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.
27 return LIBALTOS_SUCCESS;
36 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
38 altos_strndup (const char *s, size_t n)
40 size_t len = strlen (s);
52 #define altos_strndup strndup
56 * Scan for Altus Metrum devices by looking through /sys
69 cc_fullname (char *dir, char *file)
72 int dlen = strlen (dir);
73 int flen = strlen (file);
76 if (dir[dlen-1] != '/')
78 new = malloc (dlen + slen + flen + 1);
89 cc_basename(char *file)
93 b = strrchr(file, '/');
100 load_string(char *dir, char *file)
102 char *full = cc_fullname(dir, file);
108 f = fopen(full, "r");
112 r = fgets(line, sizeof (line), f);
117 if (r[rlen-1] == '\n')
123 load_hex(char *dir, char *file)
129 line = load_string(dir, file);
132 i = strtol(line, &end, 16);
140 load_dec(char *dir, char *file)
146 line = load_string(dir, file);
149 i = strtol(line, &end, 10);
157 dir_filter_tty_colon(const struct dirent *d)
159 return strncmp(d->d_name, "tty:", 4) == 0;
163 dir_filter_tty(const struct dirent *d)
165 return strncmp(d->d_name, "tty", 3) == 0;
168 struct altos_usbdev {
173 int serial; /* AltOS always uses simple integer serial numbers */
184 struct dirent **namelist;
187 char endpoint_base[20];
193 base = cc_basename(sys);
194 num_configs = load_hex(sys, "bNumConfigurations");
195 num_interfaces = load_hex(sys, "bNumInterfaces");
196 for (config = 1; config <= num_configs; config++) {
197 for (interface = 0; interface < num_interfaces; interface++) {
198 sprintf(endpoint_base, "%s:%d.%d",
199 base, config, interface);
200 endpoint_full = cc_fullname(sys, endpoint_base);
202 /* Check for tty:ttyACMx style names
204 ntty = scandir(endpoint_full, &namelist,
205 dir_filter_tty_colon,
209 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
214 /* Check for tty/ttyACMx style names
216 tty_dir = cc_fullname(endpoint_full, "tty");
218 ntty = scandir(tty_dir, &namelist,
223 tty = cc_fullname("/dev", namelist[0]->d_name);
232 static struct altos_usbdev *
233 usb_scan_device(char *sys)
235 struct altos_usbdev *usbdev;
237 usbdev = calloc(1, sizeof (struct altos_usbdev));
240 usbdev->sys = strdup(sys);
241 usbdev->manufacturer = load_string(sys, "manufacturer");
242 usbdev->product_name = load_string(sys, "product");
243 usbdev->serial = load_dec(sys, "serial");
244 usbdev->idProduct = load_hex(sys, "idProduct");
245 usbdev->idVendor = load_hex(sys, "idVendor");
246 usbdev->tty = usb_tty(sys);
251 usbdev_free(struct altos_usbdev *usbdev)
254 free(usbdev->manufacturer);
255 free(usbdev->product_name);
256 /* this can get used as a return value */
262 #define USB_DEVICES "/sys/bus/usb/devices"
265 dir_filter_dev(const struct dirent *d)
267 const char *n = d->d_name;
275 if (c == '.' && n != d->d_name + 1)
283 struct altos_usbdev **dev;
289 altos_list_start(void)
292 struct dirent **ents;
294 struct altos_usbdev *dev;
295 struct altos_list *devs;
298 devs = calloc(1, sizeof (struct altos_list));
302 n = scandir (USB_DEVICES, &ents,
307 for (e = 0; e < n; e++) {
308 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
309 dev = usb_scan_device(dir);
311 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
313 devs->dev = realloc(devs->dev,
314 devs->ndev + 1 * sizeof (struct usbdev *));
316 devs->dev = malloc (sizeof (struct usbdev *));
317 devs->dev[devs->ndev++] = dev;
326 altos_list_next(struct altos_list *list, struct altos_device *device)
328 struct altos_usbdev *dev;
329 if (list->current >= list->ndev)
331 dev = list->dev[list->current];
332 strcpy(device->name, dev->product_name);
333 device->vendor = dev->idVendor;
334 device->product = dev->idProduct;
335 strcpy(device->path, dev->tty);
336 device->serial = dev->serial;
342 altos_list_finish(struct altos_list *usbdevs)
348 for (i = 0; i < usbdevs->ndev; i++)
349 usbdev_free(usbdevs->dev[i]);
357 #include <IOKitLib.h>
358 #include <IOKit/usb/USBspec.h>
359 #include <sys/param.h>
361 #include <CFNumber.h>
368 io_iterator_t iterator;
372 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
374 CFTypeRef entry_as_string;
377 entry_as_string = IORegistryEntrySearchCFProperty (object,
381 kIORegistryIterateRecursively);
382 if (entry_as_string) {
383 got_string = CFStringGetCString(entry_as_string,
385 kCFStringEncodingASCII);
387 CFRelease(entry_as_string);
395 get_number(io_object_t object, CFStringRef entry, int *result)
397 CFTypeRef entry_as_number;
400 entry_as_number = IORegistryEntrySearchCFProperty (object,
404 kIORegistryIterateRecursively);
405 if (entry_as_number) {
406 got_number = CFNumberGetValue(entry_as_number,
416 altos_list_start(void)
418 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
419 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
420 io_iterator_t tdIterator;
421 io_object_t tdObject;
425 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
426 if (ret != kIOReturnSuccess)
432 altos_list_next(struct altos_list *list, struct altos_device *device)
435 char serial_string[128];
438 object = IOIteratorNext(list->iterator);
442 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
443 !get_number (object, CFSTR(kUSBProductID), &device->product))
445 if (device->vendor != 0xfffe)
447 if (device->product < 0x000a || 0x0013 < device->product)
449 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
450 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
451 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
452 device->serial = atoi(serial_string);
459 altos_list_finish(struct altos_list *list)
461 IOObjectRelease (list->iterator);
476 #define USB_BUF_SIZE 64
481 unsigned char out_data[USB_BUF_SIZE];
483 unsigned char in_data[USB_BUF_SIZE];
486 pthread_mutex_t putc_mutex;
487 pthread_mutex_t getc_mutex;
491 altos_open(struct altos_device *device)
493 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
501 file->fd = open(device->path, O_RDWR | O_NOCTTY);
503 perror(device->path);
507 ret = tcgetattr(file->fd, &term);
516 term.c_cc[VTIME] = 0;
517 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
524 pthread_mutex_init(&file->putc_mutex,NULL);
525 pthread_mutex_init(&file->getc_mutex,NULL);
530 altos_close(struct altos_file *file)
532 if (file->fd != -1) {
535 write(file->pipe[1], "\r", 1);
541 altos_free(struct altos_file *file)
548 _altos_flush(struct altos_file *file)
550 while (file->out_used) {
556 ret = write (file->fd, file->out_data, file->out_used);
560 memmove(file->out_data, file->out_data + ret,
561 file->out_used - ret);
562 file->out_used -= ret;
568 altos_putchar(struct altos_file *file, char c)
572 pthread_mutex_lock(&file->putc_mutex);
573 if (file->out_used == USB_BUF_SIZE) {
574 ret = _altos_flush(file);
576 pthread_mutex_unlock(&file->putc_mutex);
580 file->out_data[file->out_used++] = c;
582 if (file->out_used == USB_BUF_SIZE)
583 ret = _altos_flush(file);
584 pthread_mutex_unlock(&file->putc_mutex);
589 altos_flush(struct altos_file *file)
592 pthread_mutex_lock(&file->putc_mutex);
593 ret = _altos_flush(file);
594 pthread_mutex_unlock(&file->putc_mutex);
602 altos_getchar(struct altos_file *file, int timeout)
609 pthread_mutex_lock(&file->getc_mutex);
611 fd[0].events = POLLIN;
612 fd[1].fd = file->pipe[0];
613 fd[1].events = POLLIN;
614 while (file->in_read == file->in_used) {
616 pthread_mutex_unlock(&file->getc_mutex);
617 return LIBALTOS_ERROR;
621 ret = poll(fd, 2, timeout);
623 perror("altos_getchar");
624 pthread_mutex_unlock(&file->getc_mutex);
625 return LIBALTOS_ERROR;
628 pthread_mutex_unlock(&file->getc_mutex);
629 return LIBALTOS_TIMEOUT;
631 if (fd[0].revents & POLLIN) {
632 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
634 perror("altos_getchar");
635 pthread_mutex_unlock(&file->getc_mutex);
636 return LIBALTOS_ERROR;
642 ret = file->in_data[file->in_read++];
643 pthread_mutex_unlock(&file->getc_mutex);
647 #endif /* POSIX_TTY */
652 #include <setupapi.h>
659 #define USB_BUF_SIZE 64
663 unsigned char out_data[USB_BUF_SIZE];
665 unsigned char in_data[USB_BUF_SIZE];
671 PUBLIC struct altos_list *
672 altos_list_start(void)
674 struct altos_list *list = calloc(1, sizeof (struct altos_list));
678 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
679 DIGCF_ALLCLASSES|DIGCF_PRESENT);
680 if (list->dev_info == INVALID_HANDLE_VALUE) {
681 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
690 altos_list_next(struct altos_list *list, struct altos_device *device)
692 SP_DEVINFO_DATA dev_info_data;
705 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
706 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
711 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
712 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
714 if (dev_key == INVALID_HANDLE_VALUE) {
715 printf("cannot open device registry key\n");
719 /* Fetch symbolic name for this device and parse out
720 * the vid/pid/serial info */
721 symbolic_len = sizeof(symbolic);
722 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
723 symbolic, &symbolic_len);
725 printf("cannot find SymbolicName value\n");
726 RegCloseKey(dev_key);
729 vid = pid = serial = 0;
730 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
732 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
734 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
736 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
737 printf("Not Altus Metrum symbolic name: %s\n",
739 RegCloseKey(dev_key);
743 /* Fetch the com port name */
744 port_len = sizeof (port);
745 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
747 RegCloseKey(dev_key);
749 printf("failed to get PortName\n");
753 /* Fetch the 'location information' which is the device name,
755 location_len = sizeof (location);
756 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
758 SPDRP_LOCATION_INFORMATION,
764 printf("Failed to get location\n");
767 device->vendor = vid;
768 device->product = pid;
769 device->serial = serial;
771 if (strcasestr(location, "tele"))
772 strcpy(device->name, location);
774 strcpy(device->name, "");
776 strcpy(device->path, port);
777 printf ("product: %04x:%04x (%s) path: %s serial %d\n",
778 device->vendor, device->product, device->name,
779 device->path, device->serial);
782 result = GetLastError();
783 if (result != ERROR_NO_MORE_ITEMS)
784 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
789 altos_list_finish(struct altos_list *list)
791 SetupDiDestroyDeviceInfoList(list->dev_info);
796 altos_fill(struct altos_file *file, int timeout)
800 COMMTIMEOUTS timeouts;
802 if (file->in_read < file->in_used)
803 return LIBALTOS_SUCCESS;
804 file->in_read = file->in_used = 0;
807 timeouts.ReadIntervalTimeout = MAXDWORD;
808 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
809 timeouts.ReadTotalTimeoutConstant = timeout;
811 timeouts.ReadIntervalTimeout = 0;
812 timeouts.ReadTotalTimeoutMultiplier = 0;
813 timeouts.ReadTotalTimeoutConstant = 0;
815 timeouts.WriteTotalTimeoutMultiplier = 0;
816 timeouts.WriteTotalTimeoutConstant = 0;
818 if (!SetCommTimeouts(file->handle, &timeouts)) {
819 printf("SetCommTimeouts failed %d\n", GetLastError());
822 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
823 result = GetLastError();
824 printf ("read failed %d\n", result);
825 return LIBALTOS_ERROR;
829 return LIBALTOS_SUCCESS;
830 return LIBALTOS_TIMEOUT;
834 altos_flush(struct altos_file *file)
837 char *data = file->out_data;
838 char used = file->out_used;
842 if (!WriteFile(file->handle, data, used, &put, NULL)) {
843 result = GetLastError();
844 printf ("write failed %d\n", result);
845 return LIBALTOS_ERROR;
851 return LIBALTOS_SUCCESS;
854 PUBLIC struct altos_file *
855 altos_open(struct altos_device *device)
857 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
863 strcpy(full_name, "\\\\.\\");
864 strcat(full_name, device->path);
865 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
866 0, NULL, OPEN_EXISTING,
867 FILE_ATTRIBUTE_NORMAL, NULL);
868 if (file->handle == INVALID_HANDLE_VALUE) {
873 timeouts.ReadIntervalTimeout = MAXDWORD;
874 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
875 timeouts.ReadTotalTimeoutConstant = 100;
876 timeouts.WriteTotalTimeoutMultiplier = 0;
877 timeouts.WriteTotalTimeoutConstant = 10000;
878 if (!SetCommTimeouts(file->handle, &timeouts)) {
879 printf("SetCommTimeouts failed %d\n", GetLastError());
886 altos_close(struct altos_file *file)
888 if (file->handle != INVALID_HANDLE_VALUE) {
889 CloseHandle(file->handle);
890 file->handle = INVALID_HANDLE_VALUE;
895 altos_free(struct altos_file *file)
902 altos_putchar(struct altos_file *file, char c)
906 if (file->out_used == USB_BUF_SIZE) {
907 ret = altos_flush(file);
911 file->out_data[file->out_used++] = c;
912 if (file->out_used == USB_BUF_SIZE)
913 return altos_flush(file);
914 return LIBALTOS_SUCCESS;
918 altos_getchar(struct altos_file *file, int timeout)
921 while (file->in_read == file->in_used) {
922 ret = altos_flush(file);
925 if (file->handle == INVALID_HANDLE_VALUE)
926 return LIBALTOS_ERROR;
927 ret = altos_fill(file, timeout);
931 return file->in_data[file->in_read++];