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.
29 return LIBALTOS_SUCCESS;
41 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
43 altos_strndup (const char *s, size_t n)
45 size_t len = strlen (s);
57 #define altos_strndup strndup
61 * Scan for Altus Metrum devices by looking through /sys
74 cc_fullname (char *dir, char *file)
77 int dlen = strlen (dir);
78 int flen = strlen (file);
81 if (dir[dlen-1] != '/')
83 new = malloc (dlen + slen + flen + 1);
94 cc_basename(char *file)
98 b = strrchr(file, '/');
105 load_string(char *dir, char *file)
107 char *full = cc_fullname(dir, file);
113 f = fopen(full, "r");
117 r = fgets(line, sizeof (line), f);
122 if (r[rlen-1] == '\n')
128 load_hex(char *dir, char *file)
134 line = load_string(dir, file);
137 i = strtol(line, &end, 16);
145 load_dec(char *dir, char *file)
151 line = load_string(dir, file);
154 i = strtol(line, &end, 10);
162 dir_filter_tty_colon(const struct dirent *d)
164 return strncmp(d->d_name, "tty:", 4) == 0;
168 dir_filter_tty(const struct dirent *d)
170 return strncmp(d->d_name, "tty", 3) == 0;
173 struct altos_usbdev {
178 int serial; /* AltOS always uses simple integer serial numbers */
189 struct dirent **namelist;
192 char endpoint_base[20];
198 base = cc_basename(sys);
199 num_configs = load_hex(sys, "bNumConfigurations");
200 num_interfaces = load_hex(sys, "bNumInterfaces");
201 for (config = 1; config <= num_configs; config++) {
202 for (interface = 0; interface < num_interfaces; interface++) {
203 sprintf(endpoint_base, "%s:%d.%d",
204 base, config, interface);
205 endpoint_full = cc_fullname(sys, endpoint_base);
207 /* Check for tty:ttyACMx style names
209 ntty = scandir(endpoint_full, &namelist,
210 dir_filter_tty_colon,
214 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
219 /* Check for tty/ttyACMx style names
221 tty_dir = cc_fullname(endpoint_full, "tty");
223 ntty = scandir(tty_dir, &namelist,
228 tty = cc_fullname("/dev", namelist[0]->d_name);
237 static struct altos_usbdev *
238 usb_scan_device(char *sys)
240 struct altos_usbdev *usbdev;
242 usbdev = calloc(1, sizeof (struct altos_usbdev));
245 usbdev->sys = strdup(sys);
246 usbdev->manufacturer = load_string(sys, "manufacturer");
247 usbdev->product_name = load_string(sys, "product");
248 usbdev->serial = load_dec(sys, "serial");
249 usbdev->idProduct = load_hex(sys, "idProduct");
250 usbdev->idVendor = load_hex(sys, "idVendor");
251 usbdev->tty = usb_tty(sys);
256 usbdev_free(struct altos_usbdev *usbdev)
259 free(usbdev->manufacturer);
260 free(usbdev->product_name);
261 /* this can get used as a return value */
267 #define USB_DEVICES "/sys/bus/usb/devices"
270 dir_filter_dev(const struct dirent *d)
272 const char *n = d->d_name;
280 if (c == '.' && n != d->d_name + 1)
288 struct altos_usbdev **dev;
294 altos_list_start(void)
297 struct dirent **ents;
299 struct altos_usbdev *dev;
300 struct altos_list *devs;
303 devs = calloc(1, sizeof (struct altos_list));
307 n = scandir (USB_DEVICES, &ents,
312 for (e = 0; e < n; e++) {
313 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
314 dev = usb_scan_device(dir);
316 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
318 devs->dev = realloc(devs->dev,
319 devs->ndev + 1 * sizeof (struct usbdev *));
321 devs->dev = malloc (sizeof (struct usbdev *));
322 devs->dev[devs->ndev++] = dev;
331 altos_list_next(struct altos_list *list, struct altos_device *device)
333 struct altos_usbdev *dev;
334 if (list->current >= list->ndev)
336 dev = list->dev[list->current];
337 strcpy(device->name, dev->product_name);
338 device->vendor = dev->idVendor;
339 device->product = dev->idProduct;
340 strcpy(device->path, dev->tty);
341 device->serial = dev->serial;
347 altos_list_finish(struct altos_list *usbdevs)
353 for (i = 0; i < usbdevs->ndev; i++)
354 usbdev_free(usbdevs->dev[i]);
362 #include <IOKitLib.h>
363 #include <IOKit/usb/USBspec.h>
364 #include <sys/param.h>
366 #include <CFNumber.h>
373 io_iterator_t iterator;
377 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
379 CFTypeRef entry_as_string;
382 entry_as_string = IORegistryEntrySearchCFProperty (object,
386 kIORegistryIterateRecursively);
387 if (entry_as_string) {
388 got_string = CFStringGetCString(entry_as_string,
390 kCFStringEncodingASCII);
392 CFRelease(entry_as_string);
400 get_number(io_object_t object, CFStringRef entry, int *result)
402 CFTypeRef entry_as_number;
405 entry_as_number = IORegistryEntrySearchCFProperty (object,
409 kIORegistryIterateRecursively);
410 if (entry_as_number) {
411 got_number = CFNumberGetValue(entry_as_number,
421 altos_list_start(void)
423 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
424 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
425 io_iterator_t tdIterator;
426 io_object_t tdObject;
430 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
431 if (ret != kIOReturnSuccess)
437 altos_list_next(struct altos_list *list, struct altos_device *device)
440 char serial_string[128];
443 object = IOIteratorNext(list->iterator);
447 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
448 !get_number (object, CFSTR(kUSBProductID), &device->product))
450 if (device->vendor != 0xfffe)
452 if (device->product < 0x000a || 0x0013 < device->product)
454 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
455 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
456 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
457 device->serial = atoi(serial_string);
464 altos_list_finish(struct altos_list *list)
466 IOObjectRelease (list->iterator);
480 #define USB_BUF_SIZE 64
489 unsigned char out_data[USB_BUF_SIZE];
491 unsigned char in_data[USB_BUF_SIZE];
497 altos_open(struct altos_device *device)
499 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
506 printf("open %s\n", device->path);
507 file->fd = open(device->path, O_RDWR | O_NOCTTY);
508 printf("opened %d\n", file->fd);
510 perror(device->path);
517 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
518 if (file->out_fd < 0) {
519 perror(device->path);
525 ret = tcgetattr(file->fd, &term);
538 term.c_cc[VTIME] = 0;
541 term.c_cc[VTIME] = 1;
543 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
553 printf("running %d\n", file->fd);
558 altos_close(struct altos_file *file)
560 if (file->fd != -1) {
564 write(file->pipe[1], "\r", 1);
569 printf("close %d\n", fd);
575 altos_free(struct altos_file *file)
582 altos_flush(struct altos_file *file)
584 while (file->out_used) {
589 printf("write %d\n", file->out_used);
591 ret = write (file->fd, file->out_data, file->out_used);
593 ret = write (file->out_fd, file->out_data, file->out_used);
598 memmove(file->out_data, file->out_data + ret,
599 file->out_used - ret);
600 file->out_used -= ret;
607 altos_putchar(struct altos_file *file, char c)
611 if (file->out_used == USB_BUF_SIZE) {
612 ret = altos_flush(file);
617 file->out_data[file->out_used++] = c;
619 if (file->out_used == USB_BUF_SIZE)
620 ret = altos_flush(file);
629 altos_fill(struct altos_file *file, int timeout)
638 while (file->in_read == file->in_used) {
640 return LIBALTOS_ERROR;
643 fd[0].events = POLLIN;
644 fd[1].fd = file->pipe[0];
645 fd[1].events = POLLIN;
646 ret = poll(fd, 2, timeout);
648 perror("altos_getchar");
649 return LIBALTOS_ERROR;
652 return LIBALTOS_TIMEOUT;
653 if (fd[0].revents & POLLIN)
656 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
658 printf("read %d\n", ret);
660 perror("altos_getchar");
661 return LIBALTOS_ERROR;
666 if (ret == 0 && timeout > 0)
667 return LIBALTOS_TIMEOUT;
675 altos_getchar(struct altos_file *file, int timeout)
678 while (file->in_read == file->in_used) {
680 return LIBALTOS_ERROR;
681 ret = altos_fill(file, timeout);
685 return file->in_data[file->in_read++];
688 #endif /* POSIX_TTY */
693 #include <setupapi.h>
700 #define USB_BUF_SIZE 64
704 unsigned char out_data[USB_BUF_SIZE];
706 unsigned char in_data[USB_BUF_SIZE];
712 PUBLIC struct altos_list *
713 altos_list_start(void)
715 struct altos_list *list = calloc(1, sizeof (struct altos_list));
719 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
720 DIGCF_ALLCLASSES|DIGCF_PRESENT);
721 if (list->dev_info == INVALID_HANDLE_VALUE) {
722 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
731 altos_list_next(struct altos_list *list, struct altos_device *device)
733 SP_DEVINFO_DATA dev_info_data;
746 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
747 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
752 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
753 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
755 if (dev_key == INVALID_HANDLE_VALUE) {
756 printf("cannot open device registry key\n");
760 /* Fetch symbolic name for this device and parse out
761 * the vid/pid/serial info */
762 symbolic_len = sizeof(symbolic);
763 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
764 symbolic, &symbolic_len);
766 printf("cannot find SymbolicName value\n");
767 RegCloseKey(dev_key);
770 vid = pid = serial = 0;
771 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
773 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
775 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
777 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
778 printf("Not Altus Metrum symbolic name: %s\n",
780 RegCloseKey(dev_key);
784 /* Fetch the com port name */
785 port_len = sizeof (port);
786 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
788 RegCloseKey(dev_key);
790 printf("failed to get PortName\n");
794 /* Fetch the 'location information' which is the device name,
796 location_len = sizeof (location);
797 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
799 SPDRP_LOCATION_INFORMATION,
805 printf("Failed to get location\n");
808 device->vendor = vid;
809 device->product = pid;
810 device->serial = serial;
812 if (strcasestr(location, "tele"))
813 strcpy(device->name, location);
815 strcpy(device->name, "");
817 strcpy(device->path, port);
818 printf ("product: %04x:%04x (%s) path: %s serial %d\n",
819 device->vendor, device->product, device->name,
820 device->path, device->serial);
823 result = GetLastError();
824 if (result != ERROR_NO_MORE_ITEMS)
825 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
830 altos_list_finish(struct altos_list *list)
832 SetupDiDestroyDeviceInfoList(list->dev_info);
837 altos_fill(struct altos_file *file, int timeout)
841 COMMTIMEOUTS timeouts;
843 if (file->in_read < file->in_used)
844 return LIBALTOS_SUCCESS;
845 file->in_read = file->in_used = 0;
848 timeouts.ReadIntervalTimeout = MAXDWORD;
849 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
850 timeouts.ReadTotalTimeoutConstant = timeout;
852 timeouts.ReadIntervalTimeout = 0;
853 timeouts.ReadTotalTimeoutMultiplier = 0;
854 timeouts.ReadTotalTimeoutConstant = 0;
856 timeouts.WriteTotalTimeoutMultiplier = 0;
857 timeouts.WriteTotalTimeoutConstant = 0;
859 if (!SetCommTimeouts(file->handle, &timeouts)) {
860 printf("SetCommTimeouts failed %d\n", GetLastError());
863 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
864 result = GetLastError();
865 printf ("read failed %d\n", result);
866 return LIBALTOS_ERROR;
870 return LIBALTOS_SUCCESS;
871 return LIBALTOS_TIMEOUT;
875 altos_flush(struct altos_file *file)
878 char *data = file->out_data;
879 char used = file->out_used;
883 if (!WriteFile(file->handle, data, used, &put, NULL)) {
884 result = GetLastError();
885 printf ("write failed %d\n", result);
886 return LIBALTOS_ERROR;
892 return LIBALTOS_SUCCESS;
895 PUBLIC struct altos_file *
896 altos_open(struct altos_device *device)
898 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
904 strcpy(full_name, "\\\\.\\");
905 strcat(full_name, device->path);
906 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
907 0, NULL, OPEN_EXISTING,
908 FILE_ATTRIBUTE_NORMAL, NULL);
909 if (file->handle == INVALID_HANDLE_VALUE) {
914 timeouts.ReadIntervalTimeout = MAXDWORD;
915 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
916 timeouts.ReadTotalTimeoutConstant = 100;
917 timeouts.WriteTotalTimeoutMultiplier = 0;
918 timeouts.WriteTotalTimeoutConstant = 10000;
919 if (!SetCommTimeouts(file->handle, &timeouts)) {
920 printf("SetCommTimeouts failed %d\n", GetLastError());
927 altos_close(struct altos_file *file)
929 if (file->handle != INVALID_HANDLE_VALUE) {
930 CloseHandle(file->handle);
931 file->handle = INVALID_HANDLE_VALUE;
936 altos_free(struct altos_file *file)
943 altos_putchar(struct altos_file *file, char c)
947 if (file->out_used == USB_BUF_SIZE) {
948 ret = altos_flush(file);
952 file->out_data[file->out_used++] = c;
953 if (file->out_used == USB_BUF_SIZE)
954 return altos_flush(file);
955 return LIBALTOS_SUCCESS;
959 altos_getchar(struct altos_file *file, int timeout)
962 while (file->in_read == file->in_used) {
963 if (file->handle == INVALID_HANDLE_VALUE)
964 return LIBALTOS_ERROR;
965 ret = altos_fill(file, timeout);
969 return file->in_data[file->in_read++];