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.
28 return LIBALTOS_SUCCESS;
40 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
42 altos_strndup (const char *s, size_t n)
44 size_t len = strlen (s);
56 #define altos_strndup strndup
60 * Scan for Altus Metrum devices by looking through /sys
73 cc_fullname (char *dir, char *file)
76 int dlen = strlen (dir);
77 int flen = strlen (file);
80 if (dir[dlen-1] != '/')
82 new = malloc (dlen + slen + flen + 1);
93 cc_basename(char *file)
97 b = strrchr(file, '/');
104 load_string(char *dir, char *file)
106 char *full = cc_fullname(dir, file);
112 f = fopen(full, "r");
116 r = fgets(line, sizeof (line), f);
121 if (r[rlen-1] == '\n')
127 load_hex(char *dir, char *file)
133 line = load_string(dir, file);
136 i = strtol(line, &end, 16);
144 load_dec(char *dir, char *file)
150 line = load_string(dir, file);
153 i = strtol(line, &end, 10);
161 dir_filter_tty_colon(const struct dirent *d)
163 return strncmp(d->d_name, "tty:", 4) == 0;
167 dir_filter_tty(const struct dirent *d)
169 return strncmp(d->d_name, "tty", 3) == 0;
172 struct altos_usbdev {
177 int serial; /* AltOS always uses simple integer serial numbers */
188 struct dirent **namelist;
191 char endpoint_base[20];
197 base = cc_basename(sys);
198 num_configs = load_hex(sys, "bNumConfigurations");
199 num_interfaces = load_hex(sys, "bNumInterfaces");
200 for (config = 1; config <= num_configs; config++) {
201 for (interface = 0; interface < num_interfaces; interface++) {
202 sprintf(endpoint_base, "%s:%d.%d",
203 base, config, interface);
204 endpoint_full = cc_fullname(sys, endpoint_base);
206 /* Check for tty:ttyACMx style names
208 ntty = scandir(endpoint_full, &namelist,
209 dir_filter_tty_colon,
213 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
218 /* Check for tty/ttyACMx style names
220 tty_dir = cc_fullname(endpoint_full, "tty");
222 ntty = scandir(tty_dir, &namelist,
227 tty = cc_fullname("/dev", namelist[0]->d_name);
236 static struct altos_usbdev *
237 usb_scan_device(char *sys)
239 struct altos_usbdev *usbdev;
241 usbdev = calloc(1, sizeof (struct altos_usbdev));
244 usbdev->sys = strdup(sys);
245 usbdev->manufacturer = load_string(sys, "manufacturer");
246 usbdev->product_name = load_string(sys, "product");
247 usbdev->serial = load_dec(sys, "serial");
248 usbdev->idProduct = load_hex(sys, "idProduct");
249 usbdev->idVendor = load_hex(sys, "idVendor");
250 usbdev->tty = usb_tty(sys);
255 usbdev_free(struct altos_usbdev *usbdev)
258 free(usbdev->manufacturer);
259 free(usbdev->product_name);
260 /* this can get used as a return value */
266 #define USB_DEVICES "/sys/bus/usb/devices"
269 dir_filter_dev(const struct dirent *d)
271 const char *n = d->d_name;
279 if (c == '.' && n != d->d_name + 1)
287 struct altos_usbdev **dev;
293 altos_list_start(void)
296 struct dirent **ents;
298 struct altos_usbdev *dev;
299 struct altos_list *devs;
302 devs = calloc(1, sizeof (struct altos_list));
306 n = scandir (USB_DEVICES, &ents,
311 for (e = 0; e < n; e++) {
312 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
313 dev = usb_scan_device(dir);
315 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
317 devs->dev = realloc(devs->dev,
318 devs->ndev + 1 * sizeof (struct usbdev *));
320 devs->dev = malloc (sizeof (struct usbdev *));
321 devs->dev[devs->ndev++] = dev;
330 altos_list_next(struct altos_list *list, struct altos_device *device)
332 struct altos_usbdev *dev;
333 if (list->current >= list->ndev)
335 dev = list->dev[list->current];
336 strcpy(device->name, dev->product_name);
337 device->vendor = dev->idVendor;
338 device->product = dev->idProduct;
339 strcpy(device->path, dev->tty);
340 device->serial = dev->serial;
346 altos_list_finish(struct altos_list *usbdevs)
352 for (i = 0; i < usbdevs->ndev; i++)
353 usbdev_free(usbdevs->dev[i]);
361 #include <IOKitLib.h>
362 #include <IOKit/usb/USBspec.h>
363 #include <sys/param.h>
365 #include <CFNumber.h>
372 io_iterator_t iterator;
376 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
378 CFTypeRef entry_as_string;
381 entry_as_string = IORegistryEntrySearchCFProperty (object,
385 kIORegistryIterateRecursively);
386 if (entry_as_string) {
387 got_string = CFStringGetCString(entry_as_string,
389 kCFStringEncodingASCII);
391 CFRelease(entry_as_string);
399 get_number(io_object_t object, CFStringRef entry, int *result)
401 CFTypeRef entry_as_number;
404 entry_as_number = IORegistryEntrySearchCFProperty (object,
408 kIORegistryIterateRecursively);
409 if (entry_as_number) {
410 got_number = CFNumberGetValue(entry_as_number,
420 altos_list_start(void)
422 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
423 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
424 io_iterator_t tdIterator;
425 io_object_t tdObject;
429 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
430 if (ret != kIOReturnSuccess)
436 altos_list_next(struct altos_list *list, struct altos_device *device)
439 char serial_string[128];
442 object = IOIteratorNext(list->iterator);
446 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
447 !get_number (object, CFSTR(kUSBProductID), &device->product))
449 if (device->vendor != 0xfffe)
451 if (device->product < 0x000a || 0x0013 < device->product)
453 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
454 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
455 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
456 device->serial = atoi(serial_string);
463 altos_list_finish(struct altos_list *list)
465 IOObjectRelease (list->iterator);
479 #define USB_BUF_SIZE 64
488 unsigned char out_data[USB_BUF_SIZE];
490 unsigned char in_data[USB_BUF_SIZE];
495 PUBLIC struct altos_file *
496 altos_open(struct altos_device *device)
498 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
505 file->fd = open(device->path, O_RDWR | O_NOCTTY);
507 perror(device->path);
514 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
515 if (file->out_fd < 0) {
516 perror(device->path);
522 ret = tcgetattr(file->fd, &term);
535 term.c_cc[VTIME] = 0;
538 term.c_cc[VTIME] = 1;
540 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
554 altos_close(struct altos_file *file)
556 if (file->fd != -1) {
560 write(file->pipe[1], "\r", 1);
570 altos_free(struct altos_file *file)
577 altos_flush(struct altos_file *file)
579 if (file->out_used && 0) {
581 fwrite(file->out_data, 1, file->out_used, stdout);
584 while (file->out_used) {
590 ret = write (file->fd, file->out_data, file->out_used);
592 ret = write (file->out_fd, file->out_data, file->out_used);
597 memmove(file->out_data, file->out_data + ret,
598 file->out_used - ret);
599 file->out_used -= ret;
606 altos_putchar(struct altos_file *file, char c)
610 if (file->out_used == USB_BUF_SIZE) {
611 ret = altos_flush(file);
616 file->out_data[file->out_used++] = c;
618 if (file->out_used == USB_BUF_SIZE)
619 ret = altos_flush(file);
628 altos_fill(struct altos_file *file, int timeout)
637 while (file->in_read == file->in_used) {
639 return LIBALTOS_ERROR;
642 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
643 fd[1].fd = file->pipe[0];
644 fd[1].events = POLLIN;
645 ret = poll(fd, 2, timeout);
647 perror("altos_getchar");
648 return LIBALTOS_ERROR;
651 return LIBALTOS_TIMEOUT;
653 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
654 return LIBALTOS_ERROR;
655 if (fd[0].revents & POLLIN)
658 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
660 perror("altos_getchar");
661 return LIBALTOS_ERROR;
666 if (ret == 0 && timeout > 0)
667 return LIBALTOS_TIMEOUT;
671 if (file->in_used && 0) {
673 fwrite(file->in_data, 1, file->in_used, stdout);
680 altos_getchar(struct altos_file *file, int timeout)
683 while (file->in_read == file->in_used) {
685 return LIBALTOS_ERROR;
686 ret = altos_fill(file, timeout);
690 return file->in_data[file->in_read++];
693 #endif /* POSIX_TTY */
699 #include <setupapi.h>
706 #define USB_BUF_SIZE 64
710 unsigned char out_data[USB_BUF_SIZE];
712 unsigned char in_data[USB_BUF_SIZE];
720 PUBLIC struct altos_list *
721 altos_list_start(void)
723 struct altos_list *list = calloc(1, sizeof (struct altos_list));
727 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
728 DIGCF_ALLCLASSES|DIGCF_PRESENT);
729 if (list->dev_info == INVALID_HANDLE_VALUE) {
730 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
739 altos_list_next(struct altos_list *list, struct altos_device *device)
741 SP_DEVINFO_DATA dev_info_data;
744 char friendlyname[256];
751 DWORD friendlyname_type;
752 DWORD friendlyname_len;
754 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
755 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
760 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
761 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
763 if (dev_key == INVALID_HANDLE_VALUE) {
764 printf("cannot open device registry key\n");
768 /* Fetch symbolic name for this device and parse out
769 * the vid/pid/serial info */
770 symbolic_len = sizeof(symbolic);
771 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
772 symbolic, &symbolic_len);
774 printf("cannot find SymbolicName value\n");
775 RegCloseKey(dev_key);
778 vid = pid = serial = 0;
779 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
781 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
783 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
785 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
786 RegCloseKey(dev_key);
790 /* Fetch the com port name */
791 port_len = sizeof (port);
792 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
794 RegCloseKey(dev_key);
796 printf("failed to get PortName\n");
800 /* Fetch the device description which is the device name,
801 * with firmware that has unique USB ids */
802 friendlyname_len = sizeof (friendlyname);
803 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
807 (BYTE *)friendlyname,
808 sizeof(friendlyname),
811 printf("Failed to get friendlyname\n");
814 device->vendor = vid;
815 device->product = pid;
816 device->serial = serial;
817 strcpy(device->name, friendlyname);
819 strcpy(device->path, port);
822 result = GetLastError();
823 if (result != ERROR_NO_MORE_ITEMS)
824 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
829 altos_list_finish(struct altos_list *list)
831 SetupDiDestroyDeviceInfoList(list->dev_info);
836 altos_queue_read(struct altos_file *file)
840 return LIBALTOS_SUCCESS;
842 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
843 if (GetLastError() != ERROR_IO_PENDING)
844 return LIBALTOS_ERROR;
845 file->pend_read = TRUE;
847 file->pend_read = FALSE;
851 return LIBALTOS_SUCCESS;
855 altos_wait_read(struct altos_file *file, int timeout)
860 if (!file->pend_read)
861 return LIBALTOS_SUCCESS;
866 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
869 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE))
870 return LIBALTOS_ERROR;
871 file->pend_read = FALSE;
876 return LIBALTOS_TIMEOUT;
879 return LIBALTOS_ERROR;
881 return LIBALTOS_SUCCESS;
885 altos_fill(struct altos_file *file, int timeout)
889 if (file->in_read < file->in_used)
890 return LIBALTOS_SUCCESS;
892 file->in_read = file->in_used = 0;
894 ret = altos_queue_read(file);
897 ret = altos_wait_read(file, timeout);
901 return LIBALTOS_SUCCESS;
905 altos_flush(struct altos_file *file)
908 char *data = file->out_data;
909 char used = file->out_used;
913 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
914 if (GetLastError() != ERROR_IO_PENDING)
915 return LIBALTOS_ERROR;
916 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
919 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE))
920 return LIBALTOS_ERROR;
923 return LIBALTOS_ERROR;
930 return LIBALTOS_SUCCESS;
933 PUBLIC struct altos_file *
934 altos_open(struct altos_device *device)
936 struct altos_file *file = calloc (1, sizeof (struct altos_file));
938 DCB dcbSerialParams = {0};
939 COMMTIMEOUTS timeouts;
944 strcpy(full_name, "\\\\.\\");
945 strcat(full_name, device->path);
946 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
947 0, NULL, OPEN_EXISTING,
948 FILE_FLAG_OVERLAPPED, NULL);
949 if (file->handle == INVALID_HANDLE_VALUE) {
953 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
954 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
956 timeouts.ReadIntervalTimeout = MAXDWORD;
957 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
958 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
959 timeouts.WriteTotalTimeoutMultiplier = 0;
960 timeouts.WriteTotalTimeoutConstant = 0;
961 SetCommTimeouts(file->handle, &timeouts);
963 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
964 if (!GetCommState(file->handle, &dcbSerialParams)) {
965 CloseHandle(file->handle);
969 dcbSerialParams.BaudRate = CBR_9600;
970 dcbSerialParams.ByteSize = 8;
971 dcbSerialParams.StopBits = ONESTOPBIT;
972 dcbSerialParams.Parity = NOPARITY;
973 if (!SetCommState(file->handle, &dcbSerialParams)) {
974 CloseHandle(file->handle);
983 altos_close(struct altos_file *file)
985 if (file->handle != INVALID_HANDLE_VALUE) {
986 CloseHandle(file->handle);
987 file->handle = INVALID_HANDLE_VALUE;
992 altos_free(struct altos_file *file)
999 altos_putchar(struct altos_file *file, char c)
1003 if (file->out_used == USB_BUF_SIZE) {
1004 ret = altos_flush(file);
1008 file->out_data[file->out_used++] = c;
1009 if (file->out_used == USB_BUF_SIZE)
1010 return altos_flush(file);
1011 return LIBALTOS_SUCCESS;
1015 altos_getchar(struct altos_file *file, int timeout)
1018 while (file->in_read == file->in_used) {
1019 if (file->handle == INVALID_HANDLE_VALUE)
1020 return LIBALTOS_ERROR;
1021 ret = altos_fill(file, timeout);
1025 return file->in_data[file->in_read++];