pn53x_usb.c

Go to the documentation of this file.
00001 /*-
00002  * Public platform independent Near Field Communication (NFC) library
00003  * 
00004  * Copyright (C) 2009, Roel Verdult
00005  * 
00006  * This program is free software: you can redistribute it and/or modify it
00007  * under the terms of the GNU Lesser General Public License as published by the
00008  * Free Software Foundation, either version 3 of the License, or (at your
00009  * option) any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful, but WITHOUT
00012  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00013  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
00014  * more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 
00025 #ifdef HAVE_CONFIG_H
00026   #include "config.h"
00027 #endif // HAVE_CONFIG_H
00028 
00029 /*
00030 Thanks to d18c7db and Okko for example code
00031 */
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <usb.h>
00036 #include <string.h>
00037 
00038 #include "../drivers.h"
00039 #include "../bitutils.h"
00040 
00041 #include <nfc/nfc-messages.h>
00042 
00043 #define BUFFER_LENGTH 256
00044 #define USB_TIMEOUT   30000
00045 
00046 // Find transfer endpoints for bulk transfers
00047 void get_end_points(struct usb_device *dev, usb_spec_t* pus)
00048 {
00049   uint32_t uiIndex;
00050   uint32_t uiEndPoint;
00051   struct usb_interface_descriptor* puid = dev->config->interface->altsetting;
00052 
00053   // 3 Endpoints maximum: Interrupt In, Bulk In, Bulk Out
00054   for(uiIndex = 0; uiIndex < puid->bNumEndpoints; uiIndex++)
00055   {
00056     // Only accept bulk transfer endpoints (ignore interrupt endpoints)
00057     if(puid->endpoint[uiIndex].bmAttributes != USB_ENDPOINT_TYPE_BULK) continue;
00058 
00059     // Copy the endpoint to a local var, makes it more readable code
00060     uiEndPoint = puid->endpoint[uiIndex].bEndpointAddress;
00061 
00062     // Test if we dealing with a bulk IN endpoint
00063     if((uiEndPoint & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_IN)
00064     {
00065       DBG("Bulk endpoint in  : 0x%02X", uiEndPoint);
00066       pus->uiEndPointIn = uiEndPoint;
00067     }
00068 
00069     // Test if we dealing with a bulk OUT endpoint
00070     if((uiEndPoint & USB_ENDPOINT_DIR_MASK) == USB_ENDPOINT_OUT)
00071     {
00072       DBG("Bulk endpoint in  : 0x%02X", uiEndPoint);
00073       pus->uiEndPointOut = uiEndPoint;
00074     }
00075   }
00076 }
00077 
00078 bool pn53x_usb_list_devices(nfc_device_desc_t pnddDevices[], size_t szDevices, size_t *pszDeviceFound,usb_candidate_t candidates[], int num_candidates, char * target_name)
00079 {
00080   int ret, i;
00081   
00082   struct usb_bus *bus;
00083   struct usb_device *dev;
00084   usb_dev_handle *udev;
00085   uint32_t uiBusIndex = 0;
00086   char string[256];
00087 
00088   string[0]= '\0';
00089   usb_init();
00090 
00091   if ((ret= usb_find_busses() < 0)) return NULL;
00092   DBG("%d busses",ret);
00093   if ((ret= usb_find_devices() < 0)) return NULL;
00094   DBG("%d devices",ret);
00095 
00096   *pszDeviceFound= 0;
00097 
00098   for (bus = usb_get_busses(); bus; bus = bus->next)
00099   {
00100     for (dev = bus->devices; dev; dev = dev->next, uiBusIndex++)
00101     {
00102       for(i = 0; i < num_candidates; ++i)
00103       {
00104         DBG("Checking device %04x:%04x (%04x:%04x)",dev->descriptor.idVendor,dev->descriptor.idProduct,candidates[i].idVendor,candidates[i].idProduct);
00105         if (candidates[i].idVendor==dev->descriptor.idVendor && candidates[i].idProduct==dev->descriptor.idProduct)
00106         {
00107           // Make sure there are 2 endpoints available
00108           // with libusb-win32 we got some null pointers so be robust before looking at endpoints:
00109           if (dev->config == NULL || dev->config->interface == NULL || dev->config->interface->altsetting == NULL)
00110           {
00111             // Nope, we maybe want the next one, let's try to find another
00112             continue;
00113           }
00114           if (dev->config->interface->altsetting->bNumEndpoints < 2)
00115           {
00116             // Nope, we maybe want the next one, let's try to find another
00117             continue;
00118           }
00119           if (dev->descriptor.iManufacturer || dev->descriptor.iProduct)
00120           {
00121             udev = usb_open(dev);
00122             if(udev)
00123             {
00124               usb_get_string_simple(udev, dev->descriptor.iManufacturer, string, sizeof(string));
00125               if(strlen(string) > 0)
00126                 strcpy(string + strlen(string)," / ");
00127               usb_get_string_simple(udev, dev->descriptor.iProduct, string + strlen(string), sizeof(string) - strlen(string));
00128             }
00129             usb_close(udev);
00130           }
00131           if(strlen(string) == 0)
00132             strcpy(pnddDevices[*pszDeviceFound].acDevice, target_name);
00133           else
00134             strcpy(pnddDevices[*pszDeviceFound].acDevice, string);
00135           pnddDevices[*pszDeviceFound].pcDriver = target_name;
00136           pnddDevices[*pszDeviceFound].uiBusIndex = uiBusIndex;
00137           (*pszDeviceFound)++;
00138           DBG("%s","Match!");
00139           // Test if we reach the maximum "wanted" devices
00140           if((*pszDeviceFound) == szDevices) 
00141           {
00142             DBG("Found %d devices",*pszDeviceFound);
00143             return true;
00144           }
00145         }
00146       }
00147     }
00148   }
00149   DBG("Found %d devices",*pszDeviceFound);
00150   if(*pszDeviceFound)
00151     return true;
00152   return false;
00153 }
00154 
00155 nfc_device_t* pn53x_usb_connect(const nfc_device_desc_t* pndd,const char * target_name, int target_chip)
00156 {
00157   nfc_device_t* pnd = NULL;
00158   usb_spec_t* pus;
00159   usb_spec_t us;
00160   struct usb_bus *bus;
00161   struct usb_device *dev;
00162   uint32_t uiBusIndex;
00163 
00164   us.uiEndPointIn = 0;
00165   us.uiEndPointOut = 0;
00166   us.pudh = NULL;
00167 
00168 
00169   // must specify device to connect to
00170   if(pndd == NULL) return NULL;
00171 
00172   DBG("Connecting %s device",target_name);
00173   usb_init();
00174 
00175   uiBusIndex= pndd->uiBusIndex;
00176 
00177   DBG("Skipping to device no. %d",uiBusIndex);
00178   for (bus = usb_get_busses(); bus; bus = bus->next)
00179   {
00180     for (dev = bus->devices; dev; dev = dev->next, uiBusIndex--)
00181     {
00182       DBG("Checking device %04x:%04x",dev->descriptor.idVendor,dev->descriptor.idProduct);
00183       if(uiBusIndex == 0)
00184       {
00185         DBG("Found device index %d", pndd->uiBusIndex);
00186 
00187         // Open the USB device
00188         us.pudh = usb_open(dev);
00189 
00190         get_end_points(dev,&us);
00191         if(usb_set_configuration(us.pudh,1) < 0)
00192         {
00193           DBG("%s", "Setting config failed");
00194           usb_close(us.pudh);
00195           // we failed to use the specified device
00196           return NULL;
00197         }
00198 
00199         if(usb_claim_interface(us.pudh,0) < 0)
00200         {
00201           DBG("%s", "Can't claim interface");
00202           usb_close(us.pudh);
00203           // we failed to use the specified device
00204           return NULL;
00205         }
00206         // Allocate memory for the device info and specification, fill it and return the info
00207         pus = malloc(sizeof(usb_spec_t));
00208         *pus = us;
00209         pnd = malloc(sizeof(nfc_device_t));
00210         strcpy(pnd->acName,target_name);
00211         pnd->nc = target_chip;
00212         pnd->nds = (nfc_device_spec_t)pus;
00213         pnd->bActive = true;
00214         pnd->bCrc = true;
00215         pnd->bPar = true;
00216         pnd->ui8TxBits = 0;
00217         return pnd;
00218       }
00219     }
00220   }
00221   // We ran out of devices before the index required
00222   DBG("%s","Device index not found!");
00223   return NULL;
00224 }
00225 
00226 void pn53x_usb_disconnect(nfc_device_t* pnd)
00227 {
00228   usb_spec_t* pus = (usb_spec_t*)pnd->nds;
00229   int ret;
00230 
00231   DBG("%s","resetting USB");
00232   usb_reset(pus->pudh);
00233   if((ret= usb_release_interface(pus->pudh,0)) < 0)
00234     DBG("usb_release failed %i",ret);
00235   if((ret= usb_close(pus->pudh)) < 0)
00236     DBG("usb_close failed %i",ret);
00237   free(pnd->nds);
00238   free(pnd);
00239   DBG("%s","done!");
00240 }
00241 
00242 bool pn53x_usb_transceive(const nfc_device_spec_t nds, const byte_t* pbtTx, const size_t szTxLen, byte_t* pbtRx, size_t* pszRxLen)
00243 {
00244   size_t uiPos = 0;
00245   int ret = 0;
00246   byte_t abtTx[BUFFER_LENGTH] = { 0x00, 0x00, 0xff }; // Every packet must start with "00 00 ff"
00247   byte_t abtRx[BUFFER_LENGTH];
00248   usb_spec_t* pus = (usb_spec_t*)nds;
00249 
00250   // Packet length = data length (len) + checksum (1) + end of stream marker (1)
00251   abtTx[3] = szTxLen;
00252   // Packet length checksum 
00253   abtTx[4] = BUFFER_LENGTH - abtTx[3];
00254   // Copy the PN53X command into the packet abtTx
00255   memmove(abtTx+5,pbtTx,szTxLen);
00256 
00257   // Calculate data payload checksum
00258   abtTx[szTxLen+5] = 0;
00259   for(uiPos=0; uiPos < szTxLen; uiPos++) 
00260   {
00261     abtTx[szTxLen+5] -= abtTx[uiPos+5];
00262   }
00263 
00264   // End of stream marker
00265   abtTx[szTxLen+6] = 0;
00266 
00267   DBG("%s","pn53x_usb_transceive");
00268   #ifdef DEBUG
00269     printf(" TX: ");
00270     print_hex(abtTx,szTxLen+7);
00271   #endif
00272 
00273   ret = usb_bulk_write(pus->pudh, pus->uiEndPointOut, (char*)abtTx, szTxLen+7, USB_TIMEOUT);
00274   if( ret < 0 )
00275   {
00276     DBG("usb_bulk_write failed with error %d", ret);
00277     return false;
00278   }
00279 
00280   ret = usb_bulk_read(pus->pudh, pus->uiEndPointIn, (char*)abtRx, BUFFER_LENGTH, USB_TIMEOUT);
00281   if( ret < 0 )
00282   {
00283     DBG( "usb_bulk_read failed with error %d", ret);
00284     return false;
00285   }
00286 
00287   #ifdef DEBUG
00288     printf(" RX: ");
00289     print_hex(abtRx,ret);
00290   #endif
00291 
00292   if( ret == 6 )
00293   {
00294     ret = usb_bulk_read(pus->pudh, pus->uiEndPointIn, (char*)abtRx, BUFFER_LENGTH, USB_TIMEOUT);
00295     if( ret < 0 )
00296     {
00297       DBG("usb_bulk_read failed with error %d", ret);
00298       return false;
00299     }
00300 
00301     #ifdef DEBUG
00302       printf(" RX: ");
00303       print_hex(abtRx,ret);
00304     #endif
00305   }
00306 
00307   // When the answer should be ignored, just return a succesful result
00308   if(pbtRx == NULL || pszRxLen == NULL) return true;
00309 
00310   // Only succeed when the result is at least 00 00 FF xx Fx Dx xx .. .. .. xx 00 (x = variable)
00311   if(ret < 9) 
00312   {
00313     DBG("%s","No data");
00314     return false;
00315   }
00316 
00317   // Remove the preceding and appending bytes 00 00 FF xx Fx .. .. .. xx 00 (x = variable)
00318   *pszRxLen = ret - 7 - 2;
00319 
00320   // Get register: nuke extra byte (awful hack)
00321   if ((abtRx[5]==0xd5) && (abtRx[6]==0x07) && (*pszRxLen==2)) {
00322       // printf("Got %02x %02x, keep %02x\n", abtRx[7], abtRx[8], abtRx[8]);
00323       *pszRxLen = (*pszRxLen) - 1;
00324       memcpy( pbtRx, abtRx + 8, *pszRxLen);
00325       return true;
00326   }
00327 
00328   memcpy( pbtRx, abtRx + 7, *pszRxLen);
00329 
00330   return true;
00331 }

Generated on 4 Sep 2010 for libnfc by  doxygen 1.6.1