1 /*
2 * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3 *
4 * Copyright (C) 2001-2003
5 * David Corcoran <corcoran@musclecard.com>
6 * Copyright (C) 2002-2011
7 * Ludovic Rousseau <ludovic.rousseau@free.fr>
8 *
9 * The USB code was based partly on Johannes Erdfelt
10 * libusb code found at libusb.sourceforge.net
11 *
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15
16 1. Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18 2. Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in the
20 documentation and/or other materials provided with the distribution.
21 3. The name of the author may not be used to endorse or promote products
22 derived from this software without specific prior written permission.
23
24 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /**
37 * @file
38 * @brief This provides a search API for hot pluggble devices.
39 */
40
41 #include "config.h"
42 #include <string.h>
43
44 #if defined(__linux__) && !defined(HAVE_LIBUSB) && !defined(HAVE_LIBUDEV)
45 #include <sys/types.h>
46 #include <stdio.h>
47 #include <dirent.h>
48 #include <fcntl.h>
49 #include <time.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <pthread.h>
53
54 #include "misc.h"
55 #include "pcsclite.h"
56 #include "pcscd.h"
57 #include "debuglog.h"
58 #include "parser.h"
59 #include "readerfactory.h"
60 #include "winscard_msg.h"
61 #include "sys_generic.h"
62 #include "hotplug.h"
63 #include "utils.h"
64
65 #undef DEBUG_HOTPLUG
66 #define PCSCLITE_USB_PATH "/proc/bus/usb"
67
68 #define FALSE 0
69 #define TRUE 1
70
71 pthread_mutex_t usbNotifierMutex;
72
73 struct usb_device_descriptor
74 {
75 u_int8_t bLength;
76 u_int8_t bDescriptorType;
77 u_int16_t bcdUSB;
78 u_int8_t bDeviceClass;
79 u_int8_t bDeviceSubClass;
80 u_int8_t bDeviceProtocol;
81 u_int8_t bMaxPacketSize0;
82 u_int16_t idVendor;
83 u_int16_t idProduct;
84 u_int16_t bcdDevice;
85 u_int8_t iManufacturer;
86 u_int8_t iProduct;
87 u_int8_t iSerialNumber;
88 u_int8_t bNumConfigurations;
89 }
90 __attribute__ ((packed));
91
92 static LONG HPAddHotPluggable(int, unsigned long);
93 static LONG HPRemoveHotPluggable(int, unsigned long);
94 static LONG HPReadBundleValues(void);
95 static void HPEstablishUSBNotifications(void);
96
97 static pthread_t usbNotifyThread;
98 static int AraKiriHotPlug = FALSE;
99 static int bundleSize = 0;
100
101 /**
102 * A list to keep track of 20 simultaneous readers
103 */
104 static struct _bundleTracker
105 {
106 long manuID;
107 long productID;
108
109 struct _deviceNumber {
110 int id;
111 char status;
112 } deviceNumber[PCSCLITE_MAX_READERS_CONTEXTS];
113
114 char *bundleName;
115 char *libraryPath;
116 char *readerName;
117 }
118 bundleTracker[PCSCLITE_MAX_READERS_CONTEXTS];
119
HPReadBundleValues(void)120 static LONG HPReadBundleValues(void)
121 {
122 LONG rv;
123 DIR *hpDir;
124 struct dirent *currFP = 0;
125 char fullPath[FILENAME_MAX];
126 char fullLibPath[FILENAME_MAX];
127 unsigned int listCount = 0;
128
129 hpDir = opendir(PCSCLITE_HP_DROPDIR);
130
131 if (hpDir == NULL)
132 {
133 Log1(PCSC_LOG_INFO,
134 "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
135 Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd.");
136 return -1;
137 }
138
139 #define GET_KEY(key, values) \
140 rv = LTPBundleFindValueWithKey(&plist, key, values); \
141 if (rv) \
142 { \
143 Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \
144 fullPath); \
145 continue; \
146 }
147
148 while ((currFP = readdir(hpDir)) != 0)
149 {
150 if (strstr(currFP->d_name, ".bundle") != 0)
151 {
152 unsigned int alias;
153 list_t plist, *values;
154 list_t *manuIDs, *productIDs, *readerNames;
155 char *libraryPath;
156
157 /*
158 * The bundle exists - let's form a full path name and get the
159 * vendor and product ID's for this particular bundle
160 */
161 snprintf(fullPath, FILENAME_MAX, "%s/%s/Contents/Info.plist",
162 PCSCLITE_HP_DROPDIR, currFP->d_name);
163 fullPath[FILENAME_MAX - 1] = '\0';
164
165 rv = bundleParse(fullPath, &plist);
166 if (rv)
167 continue;
168
169 /* get CFBundleExecutable */
170 GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values)
171 libraryPath = list_get_at(values, 0);
172 (void)snprintf(fullLibPath, sizeof(fullLibPath),
173 "%s/%s/Contents/%s/%s",
174 PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
175 libraryPath);
176 fullLibPath[sizeof(fullLibPath) - 1] = '\0';
177
178 GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values)
179 GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs)
180 GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs)
181 GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames)
182
183 /* while we find a nth ifdVendorID in Info.plist */
184 for (alias=0; alias<list_size(manuIDs); alias++)
185 {
186 char *value;
187
188 /* variables entries */
189 value = list_get_at(manuIDs, alias);
190 bundleTracker[listCount].manuID = strtol(value, NULL, 16);
191
192 value = list_get_at(productIDs, alias);
193 bundleTracker[listCount].productID = strtol(value, NULL, 16);
194
195 bundleTracker[listCount].readerName = strdup(list_get_at(readerNames, alias));
196
197 /* constant entries for a same driver */
198 bundleTracker[listCount].bundleName = strdup(currFP->d_name);
199 bundleTracker[listCount].libraryPath = strdup(fullLibPath);
200
201 #ifdef DEBUG_HOTPLUG
202 Log2(PCSC_LOG_INFO, "Found driver for: %s",
203 bundleTracker[listCount].readerName);
204 #endif
205 listCount++;
206
207 if (listCount >= COUNT_OF(bundleTracker))
208 {
209 Log2(PCSC_LOG_CRITICAL, "Too many readers declared. Maximum is %zd", COUNT_OF(bundleTracker));
210 goto end;
211 }
212 }
213 bundleRelease(&plist);
214 }
215 }
216
217 end:
218 bundleSize = listCount;
219
220 if (bundleSize == 0)
221 {
222 Log1(PCSC_LOG_INFO,
223 "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
224 Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
225 }
226
227 closedir(hpDir);
228 return bundleSize;
229 }
230
HPEstablishUSBNotifications(void)231 static void HPEstablishUSBNotifications(void)
232 {
233
234 int i, j, usbDeviceStatus;
235 DIR *dir, *dirB;
236 struct dirent *entry, *entryB;
237 int deviceNumber;
238 int suspectDeviceNumber;
239 char dirpath[FILENAME_MAX];
240 char filename[FILENAME_MAX * 2];
241 int fd, ret;
242 struct usb_device_descriptor usbDescriptor;
243
244 usbDeviceStatus = 0;
245 suspectDeviceNumber = 0;
246
247 while (1)
248 {
249 for (i = 0; i < bundleSize; i++)
250 {
251 usbDeviceStatus = 0;
252 suspectDeviceNumber = 0;
253
254 for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
255 /* clear rollcall */
256 bundleTracker[i].deviceNumber[j].status = 0;
257
258 dir = NULL;
259 dir = opendir(PCSCLITE_USB_PATH);
260 if (dir == NULL)
261 {
262 Log1(PCSC_LOG_ERROR,
263 "Cannot open USB path directory: " PCSCLITE_USB_PATH);
264 return;
265 }
266
267 entry = NULL;
268 while ((entry = readdir(dir)) != 0)
269 {
270
271 /*
272 * Skip anything starting with a
273 */
274 if (entry->d_name[0] == '.')
275 continue;
276 if (!strchr("0123456789",
277 entry->d_name[strlen(entry->d_name) - 1]))
278 {
279 continue;
280 }
281
282 snprintf(dirpath, sizeof dirpath, "%s/%s",
283 PCSCLITE_USB_PATH, entry->d_name);
284
285 dirB = opendir(dirpath);
286
287 if (dirB == NULL)
288 {
289 Log2(PCSC_LOG_ERROR,
290 "USB path seems to have disappeared %s", dirpath);
291 closedir(dir);
292 return;
293 }
294
295 while ((entryB = readdir(dirB)) != NULL)
296 {
297 /*
298 * Skip anything starting with a
299 */
300 if (entryB->d_name[0] == '.')
301 continue;
302
303 /* Get the device number so we can distinguish
304 multiple readers */
305 snprintf(filename, sizeof filename, "%s/%s",
306 dirpath, entryB->d_name);
307 deviceNumber = atoi(entryB->d_name);
308
309 fd = open(filename, O_RDONLY);
310 if (fd < 0)
311 continue;
312
313 ret = read(fd, (void *) &usbDescriptor,
314 sizeof(usbDescriptor));
315
316 close(fd);
317
318 if (ret < 0)
319 continue;
320
321 /*
322 * Device is found and we don't know about it
323 */
324
325 if (usbDescriptor.idVendor == bundleTracker[i].manuID &&
326 usbDescriptor.idProduct == bundleTracker[i].productID &&
327 usbDescriptor.idVendor !=0 &&
328 usbDescriptor.idProduct != 0)
329 {
330 for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
331 {
332 if (bundleTracker[i].deviceNumber[j].id == deviceNumber &&
333 bundleTracker[i].deviceNumber[j].id != 0)
334 {
335 bundleTracker[i].deviceNumber[j].status = 1; /* i'm here */
336 break;
337 }
338 }
339
340 if (j == PCSCLITE_MAX_READERS_CONTEXTS)
341 {
342 usbDeviceStatus = 1;
343 suspectDeviceNumber = deviceNumber;
344 }
345 }
346
347 } /* End of while */
348
349 closedir(dirB);
350
351 } /* End of while */
352
353
354 if (usbDeviceStatus == 1)
355 {
356 pthread_mutex_lock(&usbNotifierMutex);
357
358 for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
359 {
360 if (bundleTracker[i].deviceNumber[j].id == 0)
361 break;
362 }
363
364 if (j == PCSCLITE_MAX_READERS_CONTEXTS)
365 Log1(PCSC_LOG_ERROR,
366 "Too many identical readers plugged in");
367 else
368 {
369 HPAddHotPluggable(i, j+1);
370 bundleTracker[i].deviceNumber[j].id = suspectDeviceNumber;
371 }
372
373 pthread_mutex_unlock(&usbNotifierMutex);
374 }
375 else
376 if (usbDeviceStatus == 0)
377 {
378
379 for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
380 {
381 if (bundleTracker[i].deviceNumber[j].id != 0 &&
382 bundleTracker[i].deviceNumber[j].status == 0)
383 {
384 pthread_mutex_lock(&usbNotifierMutex);
385 HPRemoveHotPluggable(i, j+1);
386 bundleTracker[i].deviceNumber[j].id = 0;
387 pthread_mutex_unlock(&usbNotifierMutex);
388 }
389 }
390 }
391 else
392 {
393 /*
394 * Do nothing - no USB devices found
395 */
396 }
397
398 if (dir)
399 closedir(dir);
400
401 } /* End of for..loop */
402
403 SYS_Sleep(1);
404 if (AraKiriHotPlug)
405 {
406 int retval;
407
408 Log1(PCSC_LOG_INFO, "Hotplug stopped");
409 pthread_exit(&retval);
410 }
411
412 } /* End of while loop */
413 }
414
HPSearchHotPluggables(void)415 LONG HPSearchHotPluggables(void)
416 {
417 int i, j;
418
419 for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
420 {
421 bundleTracker[i].productID = 0;
422 bundleTracker[i].manuID = 0;
423
424 for (j=0; j < PCSCLITE_MAX_READERS_CONTEXTS; j++)
425 bundleTracker[i].deviceNumber[j].id = 0;
426 }
427
428 if (HPReadBundleValues() > 0)
429 ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
430 (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, 0);
431
432 return 0;
433 }
434
HPStopHotPluggables(void)435 LONG HPStopHotPluggables(void)
436 {
437 AraKiriHotPlug = TRUE;
438
439 return 0;
440 }
441
HPAddHotPluggable(int i,unsigned long usbAddr)442 static LONG HPAddHotPluggable(int i, unsigned long usbAddr)
443 {
444 /* NOTE: The deviceName is an empty string "" until someone implements
445 * the code to get it */
446 RFAddReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr,
447 bundleTracker[i].libraryPath, "");
448
449 return 1;
450 } /* End of function */
451
HPRemoveHotPluggable(int i,unsigned long usbAddr)452 static LONG HPRemoveHotPluggable(int i, unsigned long usbAddr)
453 {
454 RFRemoveReader(bundleTracker[i].readerName, PCSCLITE_HP_BASE_PORT + usbAddr);
455
456 return 1;
457 } /* End of function */
458
459 /**
460 * Sets up callbacks for device hotplug events.
461 */
HPRegisterForHotplugEvents(void)462 ULONG HPRegisterForHotplugEvents(void)
463 {
464 (void)pthread_mutex_init(&usbNotifierMutex, NULL);
465 return 0;
466 }
467
HPReCheckSerialReaders(void)468 void HPReCheckSerialReaders(void)
469 {
470 }
471
472 #endif /* __linux__ && !HAVE_LIBUSB */
473