Search code examples
point-of-saleverifone

Sending data via Ethernet port (verifone vx520)


I want to use Ethernet port to send data with create socket.

How can I open the Ethernet port and set IP, NETMASK and GateWay? I can use these functions but I don't know how to open the Ethernet port or how to configure these functions.

ceSETNWParamValue();
ceGetNWParamValue();
ceSetDDParamValue();

Solution

  • Well, The "Verix eVo Volume II OS and Communication Programmers Guide" (esp. chapter 5) can help you out a bit, but before we get into that, there is one important thing to consider--if you are using this program that you are writing on a vx520, you will likely want it on another terminal type at some point in the future. To that end, you may consider using CommServer. CommServer is a program that helps abstract the various types of hardware out of your code so you can send the same commands to any terminal type and let it determine how to fulfill your requests based on what hardware it is using. It has been around for a while and has been ported to most of the VerFone terminal types. It is provided (and supported) by VeriFone for free. There are a few downsides to it:

    • It just adds 1 more program you have to download (and it's big)
    • It requires you to send requests via Flexi Records, which can be a bit confusing to set up at first.
    • It adds 1 more layer of moving parts that can complicate debugging
    • If you weren't using VMAC before, you'll need to use it now (which is another program and more complexity)

    However, once you get it working, it's really nice:

    • Use the same code for all hardware
    • If there are multiple applications running on the terminal that all need to use IP communication, you don't need the TCP/IP libraries in any of the applications which can often result in a smaller overall size.

    There is a good chance that at some point, you'll need to share your terminal with another 3rd party. If that's the case, you'll need to have your program running on top of VMAC, anyway, AND there is a good chance they'll be using CommServer, too. If this is ever the case, 2 of the "CON"s just disappeared and one of the "PRO"s was just emphasized.

    So now you have to choose--CommServer, or no CommServer.

    If you choose CommServer, you'll need to get very familiar with how FlexiRecords work so as a prerequisite, I suggest you read Chapter 7 of the "Verix eVo Multi-App Conductor Programmers Guide". Next, the program flow will go something like this:

    1) Initialize the event buffer with

    // Initializes the buffer provided to store a new flexi-record structure. 
    // This is a flexirecord public interface function.
    vVarInitRecord(buffer, sizeof(buffer), 0);
    
    // Assigns the buffer passed as a parameter to the global space of the Extended 
    // Flexi-record API.This is a mandatory function call for the rest of the Extended 
    // Flexi-record APIs.The buffer should be initialized using vVarInitRecord()
    // function before calling this API.
    ushInitStandardFlexi(buffer);
    
    //send the event (you'll need some other boiler plate code)
    // you'll probably want to turn this into a "SendEvent" method
    EESL_send_event("COMMSVR", VCS_EVT_INIT_REQ, buffer, bufferSize);
    
    // read the response from CommSever (again, this is a skeletal sample)
    // This will become a "ReadEvent" method
    while(strcmp(sendername, "COMMSVR") || eventID != VCS_EVT_INIT_RESP)
        eventID = EESL_read_cust_evt(buffer, sizeof(buffer), &bufferSize, sendername);
    

    2) Connect

    vVarInitRecord(eco->Buffer, sizeof(buffer), 0);
    ushInitStandardFlexi(buffer);
    
    // shVarAddData will add an un-typed field to a variable record
    // The field VCS_FLD_CONN_URL is defined as a URL or an IP address to connect to. 
    // All syntax validation of URL or IP address is performed by the TCP/IP library.
    shVarAddData(VCS_FLD_CONN_URL, hostip, strlen(hostip));
    
    // shVarAddUnsignedInt adds (unsurprisingly) an unsigned int to the Flexi-record.
    // The constant VCS_FLD_CONN_PORT defines the TCP/IP port number
    shVarAddUnsignedInt(VCS_FLD_CONN_PORT, port);
    
    // The value VCS_FLD_CONN_HOSTSSL represents a flag that indicates whether SSL
    // is supported or not: (1 - SSL supported, 0 - SSL not supported)
    shVarAddUnsignedInt(VCS_FLD_CONN_HOSTSSL, useSSL);
    // (if you are going to use SSL, you'll need to add some other values, too)
    
    //Honestly, I don't know what this is, but I don't think it works without it
    shVarAddUnsignedInt(VCS_FLD_CONN_HOSTCTX, (unsigned short)0);
    
    //send the event (you'll need some other boiler plate code)
    EESL_send_event("COMMSVR", VCS_EVT_CONN_REQ, buffer, bufferSize);
    
    // read the response from CommSever (use the same "receive" code from above, 
    // but you are looking for the response "VCS_EVT_CONN_RESP"
    EESL_read_cust_evt(buffer, sizeof(buffer), &bufferSize, sendername);
    

    3) Verify you are connected

    vVarInitRecord(buffer, sizeof(buffer), 0);
    ushInitStandardFlexi(buffer);
    // Add Status ID buffer to the parameter list
    shVarAddData(VCS_FLD_STATUS_IDS, (unsigned char*)statusIDs, (sizeof(int) * 2));
    
    // do your "sendEvent" from above with the event VCS_EVT_STATUS_REQ
    SendEvent(VCS_EVT_STATUS_REQ);
    
    // do your "ReceiveEvent" from above, looking for VCS_EVT_STATUS_RESP
    ReadEvent(VCS_EVT_STATUS_RESP);
    
    // Extract data from event buffer
    ushInitStandardFlexi(buffer);
    
    //This is the eVo (520) version:
    shVarGetUnsignedInt(VCS_FLD_CONN_STATUS, (unsigned short*)&connStatus);
    //This is the Verix/Vx version
    //shVarGetData(VCS_FLD_CONN_STATUS, (unsigned char*)&connStatus, 
                   sizeof(unsigned short), &dataLength);
    
    if (connStatus == 1) {/* you are connected */}
    

    4) Send

    vVarInitRecord(eco->Buffer, sizeof(buffer), 0);
    ushInitStandardFlexi(buffer);
    
    //Add length parameter to the data stack.
    shVarAddUnsignedInt(VCS_FLD_SEND_BUFSIZE, p_size);
    
    // Send Event to server. Note that this doesn't actually send the data, it simply
    // tells CommServer that data will be coming shortly. It also advises the length
    // of the data to be sent
    SendEvent(VCS_EVT_SEND_REQ); // this is your function, remember?
    
    
    /* Send the raw data to the host via Comm Server */
    EESL_send_event("COMMSVR", VCS_EVT_DATA_RAW, buffer, sendLength);
    
    
    // Read Comm Server's response message. Note that this isn't yet the host response
    // message. We're simply checking to make sure that our data was delivered correctly
    // to Comm Server. We'll get the actual response later...
    ReadEvent(VCS_EVT_SEND_RESP); // your function
    

    5) Receive

    vVarInitRecord(buffer, sizeof(buffer), 0);
    ushInitStandardFlexi(buffer);
    
    /* Add parameters to send to server */
    shVarAddUnsignedInt(VCS_FLD_RECV_BUFSIZE, p_size);
    shVarAddUnsignedInt(VCS_FLD_RECV_TIMEOUT, p_timeout);
    
    
    // Send Event to server. Again, note that this doesn't actually receive any data,
    // it simply prepares comm server for sending us the raw data and will
    // tell us how many bytes we should be expecting
    SendEvent(VCS_EVT_RECV_REQ);
    
    // Read event from server
    ReadEvent(VCS_EVT_RECV_RESP);
    
    // Extract data from event buffer. We're particularly interested in the number
    // total of bytes available to read. Note that we haven't actually read anything
    // at this point. We simply got the number of bytes AVAILABLE to read
    ushInitStandardFlexi(eco->Buffer);
    
    //Now do the actual read
    EESL_send_event("COMMSVR", VCS_EVT_DATA_RAW, (unsigned char *)buffer, readSize);
    

    5) Disconnect

    vVarInitRecord(buffer, sizeof(buffer), 0);
    ushInitStandardFlexi(buffer);
    SendEvent(VCS_EVT_DISC_REQ)
    ReadEvent(VCS_EVT_DISC_RESP);
    

    There's going to be more to it than that, but that's the gist of it.


    At this point, you are likely thinking to yourself, "That looks complicated--how can I do it WITHOUT CommServer?" Well, now we are venturing into a realm that I am less familiar with and I probably won't be that helpful, but I do have a code sample that makes the following calls (note that my code sample is over 1500 lines long, so I can't include it all here):

    //important includes:
    #include <ctype.h>
    #include <svc_net.h>
    #include <ceif.h>
    #include <vos_ddi_ini.h>
    
    // Init Comm Engine
    ceRegister();
    
    // Subscribe to notification events from CommEngine
    ceEnableEventNotification();
    
    
    // Check how many network interfaces (NWIF) are available and store in stNIInfo
    g_NICount=ceGetNWIFCount();
    g_NIInfo = (unsigned char*) malloc (g_NICount * sizeof (stNIInfo));
    ceGetNWIFInfo((stNIInfo*)g_NIInfo, g_NICount, &nwInfoCount);
    FillNWIF ((stNIInfo*)g_NIInfo, g_NICount);
    
    // Check the link speed
    ceGetDDParamValue (g_currMediaInfo.niHandle, "GET_LINK_SPEED",
                sizeof (szLinkSpeed), szLinkSpeed, &linkValueLen);
    

    Where FillNWIF is defined as

    void FillNWIF (stNIInfo stArray[], int arrayCount)
    {
      int cntr = 0;
      for (cntr = 0; cntr < arrayCount; cntr++)
      {
        if (strcmp (stArray [cntr].niCommTech, CE_COMM_TECH_ETH) == 0) 
          g_Devices.devEthernet.iDevHandle = stArray [cntr].niHandle;
        else if (strcmp (stArray [cntr].niCommTech, CE_COMM_TECH_PPPDIAL) == 0)
          g_Devices.devDialPPP.iDevHandle = stArray [cntr].niHandle;
        else if (strcmp (stArray [cntr].niCommTech, CE_COMM_TECH_DIALONLY) == 0)
          g_Devices.devDial.iDevHandle = stArray [cntr].niHandle;
      }
    }
    

    The actual connect and send is, in a nutshell, as follows:

    struct sockaddr_in  sockHost;
    
    memset (&sockHost, 0, sizeof (struct sockaddr_in));
    memset (&timeout, 0, sizeof (struct timeval));
    
    sockHost.sin_family = AF_INET;
    sockHost.sin_addr.s_addr = htonl (inet_addr (g_AppConfig.szHostIP));
    sockHost.sin_port = htons (g_AppConfig.iPort);
    
    sockType = SOCK_STREAM;
    sockHandle = socket (AF_INET, sockType, 0);
    
    connect (sockHandle, (struct sockaddr*)&sockHost, sizeof (struct sockaddr_in));
    
    send (iSockHandle, szSendBuff, uiSendSize, 0);
    recv (iSockHandle, szRecvBuff, sizeof (szRecvBuff), 0);
    

    Other APIs you may find useful:

    //get network interface start mode
    ceGetNWIFStartMode (g_currMediaInfo.niHandle);
    
    // Get the total number of network interface from this terminal
    g_NICount=ceGetNWIFCount();
    
    //Get network interfaces
    ceGetNWIFInfo((stNIInfo*)g_NIInfo, g_NICount, &nwInfoCount);
    
    // only open the NWIF
    ceStartNWIF (g_currMediaInfo.niHandle, CE_OPEN);
    
    //close NWIF
    ceStopNWIF (g_currMediaInfo.niHandle, CE_CLOSE);
    
    // connect
    ceStartNWIF (g_currMediaInfo.niHandle, CE_CONNECT);
    
    //get connection status
    ceGetNWParamValue (iNWIFHandle, IP_CONFIG, &ipConfig, sizeof (stNI_IPConfig), &pLen);
    
    //link up
    ceStartNWIF (g_currMediaInfo.niHandle, CE_LINK)
    
    //network up
    ceStartNWIF (g_currMediaInfo.niHandle, CE_NETWORK);
    
    //network down
    ceStopNWIF (g_currMediaInfo.niHandle, CE_NETWORK);
    
    //link down
    ceStopNWIF (g_currMediaInfo.niHandle, CE_LINK);
    
    //Disconnect
    ceStopNWIF (g_currMediaInfo.niHandle, CE_DISCONNECT);
    
    // Get the version;
    ceGetVersion (CE_VER_VXCE, sizeof (g_szVXCEVersion), g_szVXCEVersion);
    ceGetVersion (CE_VER_CEIF, sizeof (g_szCEIFVersion), g_szCEIFVersion);
    

    To address the make file comments below, your make file should have a line that is something like the following:

    $(EVOSDK)\bin\vrxcc $(COptions) $(Includes) $(AppObjects) $(Libs) -o $(OutDir)\Project.out`. 
    

    Above that, each of COptions, Includes, AppObjects, and Libs need to be defined.

    COptions

    COptions are the compiler options you want to use. Several possible options are in your "Verix eVo Volume III OS programming tools reference manual", but as an example, you might set it to:

    COptions = -p -DLOGSYS_FLAG -DLOGSYS_NEW_API_STYLE
    
    • -p indicates that it is for the ARM11 processor (i.e., this code is for the 520, not the 570)
    • -DLOGSYS_FLAG says that you want to enable LOG_PRINTF statements and
    • -DLOGSYS_NEW_API_STYLE indicates that you want to use the new macros (that don't require double parenthesis)

    Includes

    Includes is how you tell the compiler where to look for your header (.h) files. Any time you use a #include statement, you need to make sure that the directory where that .h file lives is represented in your Includes list. For example, if you want to #include <ceif.h> then you need to make sure you have -I$(EOSSDK)\include as part of your includes path. An example of how you may set your Includes might be:

    Includes = -I.\include -I$(EVOSDK)\include -I$(EOSSDK)\include -I$(EVOACT)\include -I$(EVOVCS)include -I$(EVOVMAC)include
    
    • Note that there is a -I in front of each. This tells the compiler that it is an Include and not a library file.
    • .\include points to my .h files that I made. Note that it uses a relative directory and so it assumes that my include folder is visible from the same place that my make file exists.
    • Each of EVOSDK, EOSSDK, and EVOACT will probably be used in all your projects
    • EVOVCS is if you are using commserver
    • EVOVMAC is if you are using VMAC (which you will be if you are using commserver)
    • If you are not using VMAC or CommServer, leave the last 2 off.

    AppObjects

    For each .c file you have, you need to compile it into an object (.o) file. You could technically roll this up with Libs below as it is really the same thing. The only difference is that you made these; they aren't part of the SDK or anything.

    Libs

    Libs might look something like this:

    Libs =  $(EOSSDK)\lib\svc_net.o \
            $(EOSSDK)\lib\ssl.o \
            $(EOSSDK)\lib\ceif.o \
            $(EOSSDK)\lib\elog.o \
            $(EVOACT)\Output\RV\Files\Static\Release\act2000.a
    
    • ceif.o is the object file that you'll need to use the things that are defined in ceif.h.
    • Note that there is a \ at the end of each line (except the last). That's just to tell nmake that this line continues. You could just as correctly remove each \ and put it all on 1 line. This is just for readability.

    The last part, -o $(OutDir)\Project.out is just saying where you want to output the result of your compilation.