00001
00002
00003
00004
00005
00006
00007 #include <ccid.h>
00008 #include <ctype.h>
00009 #include <errno.h>
00010
00011 #include "ccid-internal.h"
00012
00013 static libusb_context *ctx;
00014
00015 struct devid {
00016 uint16_t idVendor;
00017 uint16_t idProduct;
00018 char *name;
00019 };
00020
00021 #define DEVID_ALLOC_CHUNK (1<<5)
00022 #define DEVID_ALLOC_MASK (DEVID_ALLOC_CHUNK - 1)
00023 static struct devid *devid;
00024 static unsigned int num_devid;
00025
00026
00027 static int easy_explode(char *str, char split,
00028 char **toks, int max_toks)
00029 {
00030 char *tmp;
00031 int tok;
00032 int state;
00033
00034 for(tmp=str,state=tok=0; *tmp && tok <= max_toks; tmp++) {
00035 if ( state == 0 ) {
00036 if ( *tmp == split && (tok < max_toks)) {
00037 toks[tok++] = NULL;
00038 }else if ( !isspace(*tmp) ) {
00039 state = 1;
00040 toks[tok++] = tmp;
00041 }
00042 }else if ( state == 1 ) {
00043 if ( tok < max_toks ) {
00044 if ( *tmp == split || isspace(*tmp) ) {
00045 *tmp = '\0';
00046 state = 0;
00047 }
00048 }else if ( *tmp == '\n' )
00049 *tmp = '\0';
00050 }
00051 }
00052
00053 return tok;
00054 }
00055
00056 static int strtou16(const char *s, uint16_t *v)
00057 {
00058 unsigned long int ret;
00059 char *end = NULL;
00060
00061 ret=strtoul(s, &end, 0);
00062
00063 if ( end == s )
00064 return -1;
00065
00066 if ( ret == ULONG_MAX && errno == ERANGE )
00067 return -1;
00068
00069 if ( ret > UINT_MAX )
00070 return -1;
00071 if ( ret & ~0xffff )
00072 return 0;
00073
00074 *v = ret;
00075 if ( *end == '\0' )
00076 return 0;
00077
00078 return end - s;
00079 }
00080
00081 static int devid_assure(struct devid **d, unsigned int cnt)
00082 {
00083 static void *new;
00084
00085 if ( cnt & DEVID_ALLOC_MASK )
00086 return 1;
00087
00088 new = realloc(*d, sizeof(**d) * (cnt + DEVID_ALLOC_CHUNK));
00089 if ( new == NULL )
00090 return 0;
00091
00092 *d = new;
00093 return 1;
00094 }
00095
00096 static int append_devid(struct devid **d, unsigned int *cnt,
00097 uint16_t idProduct, uint16_t idVendor, const char *str)
00098 {
00099 char *tmp = strdup(str);
00100
00101 if ( NULL == tmp )
00102 return 0;
00103
00104 if ( !devid_assure(d, *cnt) ) {
00105 free(tmp);
00106 return 0;
00107 }
00108
00109 (*d)[*cnt].idProduct = idProduct;
00110 (*d)[*cnt].idVendor = idVendor;
00111 (*d)[*cnt].name = tmp;
00112 (*cnt)++;
00113 return 1;
00114 }
00115
00116 static struct devid *load_device_types(unsigned int *pcnt)
00117 {
00118 static const char * const fn =
00119 CCID_UTILS_DATADIR "/" PACKAGE "/usb-ccid-devices";
00120 FILE *f;
00121 char buf[512];
00122 char *tok[3];
00123 unsigned int cnt, line;
00124 struct devid *ret;
00125
00126 f = fopen(fn, "r");
00127 if ( NULL == f ) {
00128 fprintf(stderr, "%s: open: %s\n", fn, strerror(errno));
00129 return NULL;
00130 }
00131
00132 for(line = 1, cnt = 0, ret = NULL; fgets(buf, sizeof(buf), f); line++) {
00133 uint16_t idVendor, idProduct;
00134 int num_toks;
00135 char *lf;
00136
00137 if ( buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n' )
00138 continue;
00139
00140 lf = strchr(buf, '\n');
00141 if ( NULL == lf ) {
00142 fprintf(stderr,
00143 "%s:%u: line exceeded max line length (%u)",
00144 fn, line, sizeof(buf));
00145 break;
00146 }
00147
00148 *lf = '\0';
00149
00150 num_toks = easy_explode(buf, ':',
00151 tok, sizeof(tok)/sizeof(*tok));
00152 if ( num_toks < 2 ) {
00153 fprintf(stderr, "%s:%u: badly formed line", fn, line);
00154 continue;
00155 }
00156
00157 if ( strtou16(tok[0], &idVendor) ||
00158 strtou16(tok[1], &idProduct ) ) {
00159 fprintf(stderr, "%s:%u: bad ID number", fn, line);
00160 continue;
00161 }
00162
00163 append_devid(&ret, &cnt, idProduct, idVendor, tok[2]);
00164 }
00165
00166 fclose(f);
00167 *pcnt = cnt;
00168 return ret;
00169 }
00170
00171 static void zap_device_types(struct devid *d, unsigned int num)
00172 {
00173 unsigned int i;
00174 for(i = 0; i < num; i++)
00175 free(d[i].name);
00176 free(d);
00177 }
00178
00179 static void reload_device_types(void)
00180 {
00181 struct devid *tmp;
00182 unsigned int count;
00183
00184 tmp = load_device_types(&count);
00185 if ( NULL == tmp )
00186 return;
00187
00188 zap_device_types(devid, num_devid);
00189 devid = tmp;
00190 num_devid = count;
00191 }
00192
00193 static void do_init(void)
00194 {
00195 if ( NULL == ctx )
00196 libusb_init(&ctx);
00197 reload_device_types();
00198 }
00199
00200 static int check_interface(struct libusb_device *dev, int c, int i, int generic)
00201 {
00202 struct libusb_config_descriptor *conf;
00203 const struct libusb_interface *iface;
00204 int a, ret;
00205
00206 if ( libusb_get_config_descriptor(dev, c, &conf) )
00207 return -1;
00208
00209 iface = &conf->interface[i];
00210
00211
00212 for (ret = a = 0; a < iface->num_altsetting; a++) {
00213 const struct libusb_interface_descriptor *id =
00214 &iface->altsetting[a];
00215 if ( id->bInterfaceClass == 0x0b ) {
00216 return a;
00217 }
00218 }
00219
00220 if ( generic )
00221 goto out;
00222
00223
00224
00225
00226 for (ret = a = 0; a < iface->num_altsetting; a++) {
00227 const struct libusb_interface_descriptor *id =
00228 &iface->altsetting[a];
00229 if ( id->bInterfaceClass == 0xff ) {
00230 return a;
00231 }
00232 }
00233 out:
00234 return -1;
00235 }
00236
00237 static int check_vendor_dev_list(uint16_t idVendor, uint16_t idProduct)
00238 {
00239 unsigned int i;
00240
00241 for(i = 0; i < num_devid; i++) {
00242 if ( devid[i].idVendor == idVendor &&
00243 devid[i].idProduct == idProduct ) {
00244 printf("Found %s\n", devid[i].name);
00245 return 1;
00246 }
00247 }
00248
00249 return 0;
00250 }
00251
00260 int _probe_descriptors(struct libusb_device *dev, int *cp, int *ip, int *ap)
00261 {
00262 struct libusb_device_descriptor d;
00263 int c, i, a, supported;
00264
00265 c = i = a = 0;
00266
00267 if ( libusb_get_device_descriptor(dev, &d) )
00268 return 0;
00269
00270 supported = check_vendor_dev_list(d.idVendor, d.idProduct);
00271
00272 for(c = 0; c < d.bNumConfigurations; c++) {
00273 struct libusb_config_descriptor *conf;
00274 if ( libusb_get_config_descriptor(dev, c, &conf) )
00275 return 0;
00276 for(i = 0; i < conf->bNumInterfaces; i++) {
00277 a = check_interface(dev, c, i, !supported);
00278 if ( a < 0 )
00279 continue;
00280
00281 goto success;
00282 }
00283 }
00284
00285 return 0;
00286
00287 success:
00288 c++;
00289
00290 if ( cp )
00291 *cp = c;
00292 if ( ip )
00293 *ip = i;
00294 if ( ap )
00295 *ap = a;
00296 return 1;
00297 }
00298
00304 ccidev_t *ccid_get_device_list(size_t *nmemb)
00305 {
00306 libusb_device **devlist;
00307 ccidev_t *ccilist;
00308 ssize_t numdev, i, n;
00309
00310 do_init();
00311
00312 numdev = libusb_get_device_list(ctx, &devlist);
00313 if ( numdev <= 0 ) {
00314 *nmemb = 0;
00315 return NULL;
00316 }
00317
00318 ccilist = calloc(numdev + 1, sizeof(*ccilist));
00319 for(i = n = 0; i < numdev; i++) {
00320 if ( !_probe_descriptors(devlist[i], NULL, NULL, NULL) )
00321 continue;
00322 libusb_ref_device(devlist[i]);
00323 ccilist[n++] = devlist[i];
00324 }
00325 ccilist[n] = NULL;
00326
00327 libusb_free_device_list(devlist, 1);
00328
00329 *nmemb = n;
00330 return ccilist;
00331 }
00332
00333 void ccid_free_device_list(ccidev_t *list)
00334 {
00335 if ( list ) {
00336 ccidev_t *ptr;
00337 for(ptr = list; *ptr; ptr++)
00338 libusb_unref_device(*ptr);
00339 free(list);
00340 }
00341 }
00342
00343 ccidev_t ccid_device(uint8_t bus, uint8_t addr)
00344 {
00345 libusb_device **devlist;
00346 ssize_t numdev, i;
00347 ccidev_t ret;
00348
00349 do_init();
00350
00351 numdev = libusb_get_device_list(ctx, &devlist);
00352 if ( numdev <= 0 )
00353 return NULL;
00354
00355 for(ret = NULL, i = 0; i < numdev; i++) {
00356 if ( libusb_get_bus_number(devlist[i]) != bus )
00357 continue;
00358 if ( libusb_get_device_address(devlist[i]) != addr )
00359 continue;
00360 if ( _probe_descriptors(devlist[i], NULL, NULL, NULL) ) {
00361 ret = devlist[i];
00362 libusb_ref_device(ret);
00363 }
00364 break;
00365 }
00366
00367 libusb_free_device_list(devlist, 1);
00368
00369 return ret;
00370 }
00371
00372 uint8_t ccid_device_bus(ccidev_t dev)
00373 {
00374 return libusb_get_bus_number(dev);
00375 }
00376
00377 uint8_t ccid_device_addr(ccidev_t dev)
00378 {
00379 return libusb_get_device_address(dev);
00380 }