2 * Copyright © 2010 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 match_dev(char *product, int serial, struct altos_device *device)
26 struct altos_list *list;
29 list = altos_list_start();
32 while ((i = altos_list_next(list, device)) != 0) {
33 if (product && strncmp (product, device->product, strlen(product)) != 0)
35 if (serial && serial != device->serial)
39 altos_list_finish(list);
44 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
46 altos_strndup (const char *s, size_t n)
48 size_t len = strlen (s);
60 #define altos_strndup strndup
64 altos_find_by_arg(char *arg, char *default_product, struct altos_device *device)
74 /* check for <serial> */
75 serial = strtol(arg, &end, 0);
81 /* check for <product>:<serial> */
82 colon = strchr(arg, ':');
84 product = altos_strndup(arg, colon - arg);
85 serial = strtol(colon + 1, &end, 0);
97 if (!product && default_product)
98 ret = match_dev(default_product, serial, device);
100 ret = match_dev(product, serial, device);
101 if (product && product != arg)
116 cc_fullname (char *dir, char *file)
119 int dlen = strlen (dir);
120 int flen = strlen (file);
123 if (dir[dlen-1] != '/')
125 new = malloc (dlen + slen + flen + 1);
136 cc_basename(char *file)
140 b = strrchr(file, '/');
147 load_string(char *dir, char *file)
149 char *full = cc_fullname(dir, file);
155 f = fopen(full, "r");
159 r = fgets(line, sizeof (line), f);
164 if (r[rlen-1] == '\n')
170 load_hex(char *dir, char *file)
176 line = load_string(dir, file);
179 i = strtol(line, &end, 16);
187 load_dec(char *dir, char *file)
193 line = load_string(dir, file);
196 i = strtol(line, &end, 10);
204 dir_filter_tty_colon(const struct dirent *d)
206 return strncmp(d->d_name, "tty:", 4) == 0;
210 dir_filter_tty(const struct dirent *d)
212 return strncmp(d->d_name, "tty", 3) == 0;
215 struct altos_usbdev {
220 int serial; /* AltOS always uses simple integer serial numbers */
231 struct dirent **namelist;
234 char endpoint_base[20];
240 base = cc_basename(sys);
241 num_configs = load_hex(sys, "bNumConfigurations");
242 num_interfaces = load_hex(sys, "bNumInterfaces");
243 for (config = 1; config <= num_configs; config++) {
244 for (interface = 0; interface < num_interfaces; interface++) {
245 sprintf(endpoint_base, "%s:%d.%d",
246 base, config, interface);
247 endpoint_full = cc_fullname(sys, endpoint_base);
249 /* Check for tty:ttyACMx style names
251 ntty = scandir(endpoint_full, &namelist,
252 dir_filter_tty_colon,
256 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
261 /* Check for tty/ttyACMx style names
263 tty_dir = cc_fullname(endpoint_full, "tty");
265 ntty = scandir(tty_dir, &namelist,
270 tty = cc_fullname("/dev", namelist[0]->d_name);
279 static struct altos_usbdev *
280 usb_scan_device(char *sys)
282 struct altos_usbdev *usbdev;
284 usbdev = calloc(1, sizeof (struct altos_usbdev));
287 usbdev->sys = strdup(sys);
288 usbdev->manufacturer = load_string(sys, "manufacturer");
289 usbdev->product = load_string(sys, "product");
290 usbdev->serial = load_dec(sys, "serial");
291 usbdev->idProduct = load_hex(sys, "idProduct");
292 usbdev->idVendor = load_hex(sys, "idVendor");
293 usbdev->tty = usb_tty(sys);
298 usbdev_free(struct altos_usbdev *usbdev)
301 free(usbdev->manufacturer);
302 free(usbdev->product);
303 /* this can get used as a return value */
309 #define USB_DEVICES "/sys/bus/usb/devices"
312 dir_filter_dev(const struct dirent *d)
314 const char *n = d->d_name;
322 if (c == '.' && n != d->d_name + 1)
330 struct altos_usbdev **dev;
347 altos_list_start(void)
350 struct dirent **ents;
352 struct altos_usbdev *dev;
353 struct altos_list *devs;
356 devs = calloc(1, sizeof (struct altos_list));
360 n = scandir (USB_DEVICES, &ents,
365 for (e = 0; e < n; e++) {
366 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
367 dev = usb_scan_device(dir);
369 if (dev->idVendor == 0xfffe && dev->tty) {
371 devs->dev = realloc(devs->dev,
372 devs->ndev + 1 * sizeof (struct usbdev *));
374 devs->dev = malloc (sizeof (struct usbdev *));
375 devs->dev[devs->ndev++] = dev;
384 altos_list_next(struct altos_list *list, struct altos_device *device)
386 struct altos_usbdev *dev;
387 if (list->current >= list->ndev)
389 dev = list->dev[list->current];
390 strcpy(device->product, dev->product);
391 strcpy(device->path, dev->tty);
392 device->serial = dev->serial;
398 altos_list_finish(struct altos_list *usbdevs)
404 for (i = 0; i < usbdevs->ndev; i++)
405 usbdev_free(usbdevs->dev[i]);
413 #include <IOKitLib.h>
414 #include <IOKit/usb/USBspec.h>
415 #include <sys/param.h>
417 #include <CFNumber.h>
424 io_iterator_t iterator;
428 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
430 CFTypeRef entry_as_string;
433 entry_as_string = IORegistryEntrySearchCFProperty (object,
437 kIORegistryIterateRecursively);
438 if (entry_as_string) {
439 got_string = CFStringGetCString(entry_as_string,
441 kCFStringEncodingASCII);
443 CFRelease(entry_as_string);
462 altos_list_start(void)
464 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
465 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
466 UInt32 vendor = 0xfffe, product = 0x000a;
467 CFNumberRef vendor_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor);
468 CFNumberRef product_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product);
469 io_iterator_t tdIterator;
470 io_object_t tdObject;
472 CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBVendorID), vendor_ref);
473 CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBProductID), product_ref);
475 IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
477 CFRelease(vendor_ref);
478 CFRelease(product_ref);
483 altos_list_next(struct altos_list *list, struct altos_device *device)
486 char serial_string[128];
489 object = IOIteratorNext(list->iterator);
493 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
494 get_string (object, CFSTR("USB Product Name"), device->product, sizeof (device->product)) &&
495 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
496 device->serial = atoi(serial_string);
503 altos_list_finish(struct altos_list *list)
505 IOObjectRelease (list->iterator);
519 #define USB_BUF_SIZE 64
523 unsigned char out_data[USB_BUF_SIZE];
525 unsigned char in_data[USB_BUF_SIZE];
531 altos_open(struct altos_device *device)
533 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
540 file->fd = open(device->path, O_RDWR | O_NOCTTY);
542 perror(device->path);
546 ret = tcgetattr(file->fd, &term);
555 term.c_cc[VTIME] = 1;
556 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
567 altos_close(struct altos_file *file)
574 altos_free(struct altos_file *file)
582 altos_putchar(struct altos_file *file, char c)
586 if (file->out_used == USB_BUF_SIZE) {
587 ret = altos_flush(file);
591 file->out_data[file->out_used++] = c;
592 if (file->out_used == USB_BUF_SIZE)
593 return altos_flush(file);
598 altos_flush(struct altos_file *file)
600 while (file->out_used) {
605 ret = write (file->fd, file->out_data, file->out_used);
609 memmove(file->out_data, file->out_data + ret,
610 file->out_used - ret);
611 file->out_used -= ret;
617 altos_getchar(struct altos_file *file, int timeout)
619 while (file->in_read == file->in_used) {
625 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
631 return file->in_data[file->in_read++];
634 #endif /* POSIX_TTY */
642 libusb_context *usb_context;
647 ret = libusb_init(&usb_context);
650 libusb_set_debug(usb_context, 3);
654 void altos_fini(void)
656 libusb_exit(usb_context);
660 static libusb_device **list;
661 static ssize_t num, current;
663 int altos_list_start(void)
668 num = libusb_get_device_list(usb_context, &list);
677 int altos_list_next(struct altos_device *device)
679 while (current < num) {
680 struct libusb_device_descriptor descriptor;
681 libusb_device *usb_device = list[current++];
683 if (libusb_get_device_descriptor(usb_device, &descriptor) == 0) {
684 if (descriptor.idVendor == 0xfffe)
686 libusb_device_handle *handle;
687 if (libusb_open(usb_device, &handle) == 0) {
688 char serial_number[256];
689 libusb_get_string_descriptor_ascii(handle, descriptor.iProduct,
691 sizeof(device->product));
692 libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber,
694 sizeof (serial_number));
695 libusb_close(handle);
696 device->serial = atoi(serial_number);
697 device->device = usb_device;
706 void altos_list_finish(void)
709 libusb_free_device_list(list, 1);
714 #define USB_BUF_SIZE 64
717 struct libusb_device *device;
718 struct libusb_device_handle *handle;
723 unsigned char out_data[USB_BUF_SIZE];
725 unsigned char in_data[USB_BUF_SIZE];
731 altos_open(struct altos_device *device)
733 struct altos_file *file;
734 struct libusb_device_handle *handle;
735 if (libusb_open(device->device, &handle) == 0) {
738 ret = libusb_claim_interface(handle, 1);
741 libusb_close(handle);
745 ret = libusb_detach_kernel_driver(handle, 1);
748 libusb_close(handle);
753 file = calloc(sizeof (struct altos_file), 1);
754 file->device = libusb_ref_device(device->device);
755 file->handle = handle;
756 /* XXX should get these from the endpoint descriptors */
757 file->out_ep = 4 | LIBUSB_ENDPOINT_OUT;
759 file->in_ep = 5 | LIBUSB_ENDPOINT_IN;
768 altos_close(struct altos_file *file)
770 libusb_close(file->handle);
771 libusb_unref_device(file->device);
777 altos_putchar(struct altos_file *file, char c)
781 if (file->out_used == file->out_size) {
782 ret = altos_flush(file);
786 file->out_data[file->out_used++] = c;
787 if (file->out_used == file->out_size)
788 return altos_flush(file);
793 altos_flush(struct altos_file *file)
795 while (file->out_used) {
799 ret = libusb_bulk_transfer(file->handle,
808 memmove(file->out_data, file->out_data + transferred,
809 file->out_used - transferred);
810 file->out_used -= transferred;
816 altos_getchar(struct altos_file *file, int timeout)
818 while (file->in_read == file->in_used) {
823 ret = libusb_bulk_transfer(file->handle,
828 (unsigned int) timeout);
832 file->in_used = transferred;
834 return file->in_data[file->in_read++];
837 #endif /* USE_LIBUSB */