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
67 #define USB_BUF_SIZE 64
76 unsigned char out_data[USB_BUF_SIZE];
78 unsigned char in_data[USB_BUF_SIZE];
83 PUBLIC struct altos_file *
84 altos_open(struct altos_device *device)
86 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
93 file->fd = open(device->path, O_RDWR | O_NOCTTY);
102 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
103 if (file->out_fd < 0) {
104 perror(device->path);
110 ret = tcgetattr(file->fd, &term);
123 term.c_cc[VTIME] = 0;
126 term.c_cc[VTIME] = 1;
128 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
142 altos_close(struct altos_file *file)
144 if (file->fd != -1) {
148 write(file->pipe[1], "\r", 1);
158 altos_free(struct altos_file *file)
165 altos_flush(struct altos_file *file)
167 if (file->out_used && 0) {
169 fwrite(file->out_data, 1, file->out_used, stdout);
172 while (file->out_used) {
178 ret = write (file->fd, file->out_data, file->out_used);
180 ret = write (file->out_fd, file->out_data, file->out_used);
185 memmove(file->out_data, file->out_data + ret,
186 file->out_used - ret);
187 file->out_used -= ret;
194 altos_putchar(struct altos_file *file, char c)
198 if (file->out_used == USB_BUF_SIZE) {
199 ret = altos_flush(file);
204 file->out_data[file->out_used++] = c;
206 if (file->out_used == USB_BUF_SIZE)
207 ret = altos_flush(file);
216 altos_fill(struct altos_file *file, int timeout)
225 while (file->in_read == file->in_used) {
227 return LIBALTOS_ERROR;
230 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
231 fd[1].fd = file->pipe[0];
232 fd[1].events = POLLIN;
233 ret = poll(fd, 2, timeout);
235 perror("altos_getchar");
236 return LIBALTOS_ERROR;
239 return LIBALTOS_TIMEOUT;
241 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
242 return LIBALTOS_ERROR;
243 if (fd[0].revents & POLLIN)
246 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
248 perror("altos_getchar");
249 return LIBALTOS_ERROR;
254 if (ret == 0 && timeout > 0)
255 return LIBALTOS_TIMEOUT;
259 if (file->in_used && 0) {
261 fwrite(file->in_data, 1, file->in_used, stdout);
268 altos_getchar(struct altos_file *file, int timeout)
271 while (file->in_read == file->in_used) {
273 return LIBALTOS_ERROR;
274 ret = altos_fill(file, timeout);
278 return file->in_data[file->in_read++];
281 #endif /* POSIX_TTY */
284 * Scan for Altus Metrum devices by looking through /sys
295 #include <bluetooth/bluetooth.h>
296 #include <bluetooth/hci.h>
297 #include <bluetooth/hci_lib.h>
298 #include <bluetooth/rfcomm.h>
301 cc_fullname (char *dir, char *file)
304 int dlen = strlen (dir);
305 int flen = strlen (file);
308 if (dir[dlen-1] != '/')
310 new = malloc (dlen + slen + flen + 1);
321 cc_basename(char *file)
325 b = strrchr(file, '/');
332 load_string(char *dir, char *file)
334 char *full = cc_fullname(dir, file);
340 f = fopen(full, "r");
344 r = fgets(line, sizeof (line), f);
349 if (r[rlen-1] == '\n')
355 load_hex(char *dir, char *file)
361 line = load_string(dir, file);
364 i = strtol(line, &end, 16);
372 load_dec(char *dir, char *file)
378 line = load_string(dir, file);
381 i = strtol(line, &end, 10);
389 dir_filter_tty_colon(const struct dirent *d)
391 return strncmp(d->d_name, "tty:", 4) == 0;
395 dir_filter_tty(const struct dirent *d)
397 return strncmp(d->d_name, "tty", 3) == 0;
400 struct altos_usbdev {
405 int serial; /* AltOS always uses simple integer serial numbers */
416 struct dirent **namelist;
419 char endpoint_base[20];
425 base = cc_basename(sys);
426 num_configs = load_hex(sys, "bNumConfigurations");
427 num_interfaces = load_hex(sys, "bNumInterfaces");
428 for (config = 1; config <= num_configs; config++) {
429 for (interface = 0; interface < num_interfaces; interface++) {
430 sprintf(endpoint_base, "%s:%d.%d",
431 base, config, interface);
432 endpoint_full = cc_fullname(sys, endpoint_base);
434 /* Check for tty:ttyACMx style names
436 ntty = scandir(endpoint_full, &namelist,
437 dir_filter_tty_colon,
441 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
446 /* Check for tty/ttyACMx style names
448 tty_dir = cc_fullname(endpoint_full, "tty");
450 ntty = scandir(tty_dir, &namelist,
455 tty = cc_fullname("/dev", namelist[0]->d_name);
464 static struct altos_usbdev *
465 usb_scan_device(char *sys)
467 struct altos_usbdev *usbdev;
469 usbdev = calloc(1, sizeof (struct altos_usbdev));
472 usbdev->sys = strdup(sys);
473 usbdev->manufacturer = load_string(sys, "manufacturer");
474 usbdev->product_name = load_string(sys, "product");
475 usbdev->serial = load_dec(sys, "serial");
476 usbdev->idProduct = load_hex(sys, "idProduct");
477 usbdev->idVendor = load_hex(sys, "idVendor");
478 usbdev->tty = usb_tty(sys);
483 usbdev_free(struct altos_usbdev *usbdev)
486 free(usbdev->manufacturer);
487 free(usbdev->product_name);
488 /* this can get used as a return value */
494 #define USB_DEVICES "/sys/bus/usb/devices"
497 dir_filter_dev(const struct dirent *d)
499 const char *n = d->d_name;
507 if (c == '.' && n != d->d_name + 1)
515 struct altos_usbdev **dev;
521 altos_list_start(void)
524 struct dirent **ents;
526 struct altos_usbdev *dev;
527 struct altos_list *devs;
530 devs = calloc(1, sizeof (struct altos_list));
534 n = scandir (USB_DEVICES, &ents,
539 for (e = 0; e < n; e++) {
540 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
541 dev = usb_scan_device(dir);
543 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
545 devs->dev = realloc(devs->dev,
546 devs->ndev + 1 * sizeof (struct usbdev *));
548 devs->dev = malloc (sizeof (struct usbdev *));
549 devs->dev[devs->ndev++] = dev;
558 altos_list_next(struct altos_list *list, struct altos_device *device)
560 struct altos_usbdev *dev;
561 if (list->current >= list->ndev)
563 dev = list->dev[list->current];
564 strcpy(device->name, dev->product_name);
565 device->vendor = dev->idVendor;
566 device->product = dev->idProduct;
567 strcpy(device->path, dev->tty);
568 device->serial = dev->serial;
574 altos_list_finish(struct altos_list *usbdevs)
580 for (i = 0; i < usbdevs->ndev; i++)
581 usbdev_free(usbdevs->dev[i]);
586 struct altos_bt_list {
594 #define INQUIRY_MAX_RSP 255
596 struct altos_bt_list *
597 altos_bt_list_start(int inquiry_time)
599 struct altos_bt_list *bt_list;
601 bt_list = calloc(1, sizeof (struct altos_bt_list));
605 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
608 bt_list->dev_id = hci_get_route(NULL);
609 if (bt_list->dev_id < 0)
612 bt_list->sock = hci_open_dev(bt_list->dev_id);
613 if (bt_list->sock < 0)
616 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
622 if (bt_list->num_rsp < 0)
629 close(bt_list->sock);
640 altos_bt_list_next(struct altos_bt_list *bt_list,
641 struct altos_bt_device *device)
645 if (bt_list->rsp >= bt_list->num_rsp)
648 ii = &bt_list->ii[bt_list->rsp];
649 ba2str(&ii->bdaddr, device->addr);
650 memset(&device->name, '\0', sizeof (device->name));
651 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
652 sizeof (device->name),
653 device->name, 0) < 0) {
654 strcpy(device->name, "[unknown]");
661 altos_bt_list_finish(struct altos_bt_list *bt_list)
663 close(bt_list->sock);
669 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
671 strncpy(device->name, name, sizeof (device->name));
672 device->name[sizeof(device->name)-1] = '\0';
673 strncpy(device->addr, addr, sizeof (device->addr));
674 device->addr[sizeof(device->addr)-1] = '\0';
678 altos_bt_open(struct altos_bt_device *device)
680 struct sockaddr_rc addr = { 0 };
682 struct altos_file *file;
684 file = calloc(1, sizeof (struct altos_file));
687 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
691 addr.rc_family = AF_BLUETOOTH;
693 str2ba(device->addr, &addr.rc_bdaddr);
695 status = connect(file->fd,
696 (struct sockaddr *)&addr,
707 file->out_fd = dup(file->fd);
717 #endif /* HAS_BLUETOOTH */
723 #include <IOKitLib.h>
724 #include <IOKit/usb/USBspec.h>
725 #include <sys/param.h>
727 #include <CFNumber.h>
734 io_iterator_t iterator;
738 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
740 CFTypeRef entry_as_string;
743 entry_as_string = IORegistryEntrySearchCFProperty (object,
747 kIORegistryIterateRecursively);
748 if (entry_as_string) {
749 got_string = CFStringGetCString(entry_as_string,
751 kCFStringEncodingASCII);
753 CFRelease(entry_as_string);
761 get_number(io_object_t object, CFStringRef entry, int *result)
763 CFTypeRef entry_as_number;
766 entry_as_number = IORegistryEntrySearchCFProperty (object,
770 kIORegistryIterateRecursively);
771 if (entry_as_number) {
772 got_number = CFNumberGetValue(entry_as_number,
782 altos_list_start(int time)
784 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
785 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
786 io_iterator_t tdIterator;
787 io_object_t tdObject;
791 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
792 if (ret != kIOReturnSuccess)
798 altos_list_next(struct altos_list *list, struct altos_device *device)
801 char serial_string[128];
804 object = IOIteratorNext(list->iterator);
808 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
809 !get_number (object, CFSTR(kUSBProductID), &device->product))
811 if (device->vendor != 0xfffe)
813 if (device->product < 0x000a || 0x0013 < device->product)
815 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
816 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
817 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
818 device->serial = atoi(serial_string);
825 altos_list_finish(struct altos_list *list)
827 IOObjectRelease (list->iterator);
838 #include <setupapi.h>
845 #define USB_BUF_SIZE 64
849 unsigned char out_data[USB_BUF_SIZE];
851 unsigned char in_data[USB_BUF_SIZE];
859 PUBLIC struct altos_list *
860 altos_list_start(void)
862 struct altos_list *list = calloc(1, sizeof (struct altos_list));
866 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
867 DIGCF_ALLCLASSES|DIGCF_PRESENT);
868 if (list->dev_info == INVALID_HANDLE_VALUE) {
869 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
878 altos_list_next(struct altos_list *list, struct altos_device *device)
880 SP_DEVINFO_DATA dev_info_data;
883 char friendlyname[256];
890 DWORD friendlyname_type;
891 DWORD friendlyname_len;
893 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
894 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
899 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
900 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
902 if (dev_key == INVALID_HANDLE_VALUE) {
903 printf("cannot open device registry key\n");
907 /* Fetch symbolic name for this device and parse out
908 * the vid/pid/serial info */
909 symbolic_len = sizeof(symbolic);
910 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
911 symbolic, &symbolic_len);
913 printf("cannot find SymbolicName value\n");
914 RegCloseKey(dev_key);
917 vid = pid = serial = 0;
918 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
920 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
922 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
924 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
925 RegCloseKey(dev_key);
929 /* Fetch the com port name */
930 port_len = sizeof (port);
931 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
933 RegCloseKey(dev_key);
935 printf("failed to get PortName\n");
939 /* Fetch the device description which is the device name,
940 * with firmware that has unique USB ids */
941 friendlyname_len = sizeof (friendlyname);
942 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
946 (BYTE *)friendlyname,
947 sizeof(friendlyname),
950 printf("Failed to get friendlyname\n");
953 device->vendor = vid;
954 device->product = pid;
955 device->serial = serial;
956 strcpy(device->name, friendlyname);
958 strcpy(device->path, port);
961 result = GetLastError();
962 if (result != ERROR_NO_MORE_ITEMS)
963 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
968 altos_list_finish(struct altos_list *list)
970 SetupDiDestroyDeviceInfoList(list->dev_info);
975 altos_queue_read(struct altos_file *file)
979 return LIBALTOS_SUCCESS;
981 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
982 if (GetLastError() != ERROR_IO_PENDING)
983 return LIBALTOS_ERROR;
984 file->pend_read = TRUE;
986 file->pend_read = FALSE;
990 return LIBALTOS_SUCCESS;
994 altos_wait_read(struct altos_file *file, int timeout)
999 if (!file->pend_read)
1000 return LIBALTOS_SUCCESS;
1005 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1008 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE))
1009 return LIBALTOS_ERROR;
1010 file->pend_read = FALSE;
1012 file->in_used = got;
1015 return LIBALTOS_TIMEOUT;
1018 return LIBALTOS_ERROR;
1020 return LIBALTOS_SUCCESS;
1024 altos_fill(struct altos_file *file, int timeout)
1028 if (file->in_read < file->in_used)
1029 return LIBALTOS_SUCCESS;
1031 file->in_read = file->in_used = 0;
1033 ret = altos_queue_read(file);
1036 ret = altos_wait_read(file, timeout);
1040 return LIBALTOS_SUCCESS;
1044 altos_flush(struct altos_file *file)
1047 char *data = file->out_data;
1048 char used = file->out_used;
1052 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1053 if (GetLastError() != ERROR_IO_PENDING)
1054 return LIBALTOS_ERROR;
1055 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1058 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE))
1059 return LIBALTOS_ERROR;
1062 return LIBALTOS_ERROR;
1069 return LIBALTOS_SUCCESS;
1072 PUBLIC struct altos_file *
1073 altos_open(struct altos_device *device)
1075 struct altos_file *file = calloc (1, sizeof (struct altos_file));
1077 DCB dcbSerialParams = {0};
1078 COMMTIMEOUTS timeouts;
1083 strcpy(full_name, "\\\\.\\");
1084 strcat(full_name, device->path);
1085 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1086 0, NULL, OPEN_EXISTING,
1087 FILE_FLAG_OVERLAPPED, NULL);
1088 if (file->handle == INVALID_HANDLE_VALUE) {
1092 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1093 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1095 timeouts.ReadIntervalTimeout = MAXDWORD;
1096 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1097 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
1098 timeouts.WriteTotalTimeoutMultiplier = 0;
1099 timeouts.WriteTotalTimeoutConstant = 0;
1100 SetCommTimeouts(file->handle, &timeouts);
1102 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
1103 if (!GetCommState(file->handle, &dcbSerialParams)) {
1104 CloseHandle(file->handle);
1108 dcbSerialParams.BaudRate = CBR_9600;
1109 dcbSerialParams.ByteSize = 8;
1110 dcbSerialParams.StopBits = ONESTOPBIT;
1111 dcbSerialParams.Parity = NOPARITY;
1112 if (!SetCommState(file->handle, &dcbSerialParams)) {
1113 CloseHandle(file->handle);
1122 altos_close(struct altos_file *file)
1124 if (file->handle != INVALID_HANDLE_VALUE) {
1125 CloseHandle(file->handle);
1126 file->handle = INVALID_HANDLE_VALUE;
1131 altos_free(struct altos_file *file)
1138 altos_putchar(struct altos_file *file, char c)
1142 if (file->out_used == USB_BUF_SIZE) {
1143 ret = altos_flush(file);
1147 file->out_data[file->out_used++] = c;
1148 if (file->out_used == USB_BUF_SIZE)
1149 return altos_flush(file);
1150 return LIBALTOS_SUCCESS;
1154 altos_getchar(struct altos_file *file, int timeout)
1157 while (file->in_read == file->in_used) {
1158 if (file->handle == INVALID_HANDLE_VALUE)
1159 return LIBALTOS_ERROR;
1160 ret = altos_fill(file, timeout);
1164 return file->in_data[file->in_read++];