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.
23 #define USB_VENDOR_FSF 0xfffe
24 #define USB_VENDOR_ALTUSMETRUM USB_VENDOR_FSF
25 #define USB_PRODUCT_ALTUSMETRUM 0x000a
26 #define USB_PRODUCT_TELEMETRUM 0x000b
27 #define USB_PRODUCT_TELEDONGLE 0x000c
28 #define USB_PRODUCT_TELETERRA 0x000d
29 #define USB_PRODUCT_TELEBT 0x000e
30 #define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a
31 #define USB_PRODUCT_ALTUSMETRUM_MAX 0x0013
33 #define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \
34 (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \
35 (p) <= USB_PRODUCT_ALTUSMETRUM_MAX))
37 #define BLUETOOTH_PRODUCT_TELEBT "TeleBT"
44 return LIBALTOS_SUCCESS;
56 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
58 altos_strndup (const char *s, size_t n)
60 size_t len = strlen (s);
72 #define altos_strndup strndup
83 #define USB_BUF_SIZE 64
92 unsigned char out_data[USB_BUF_SIZE];
94 unsigned char in_data[USB_BUF_SIZE];
99 PUBLIC struct altos_file *
100 altos_open(struct altos_device *device)
102 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
109 file->fd = open(device->path, O_RDWR | O_NOCTTY);
111 perror(device->path);
118 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
119 if (file->out_fd < 0) {
120 perror(device->path);
126 ret = tcgetattr(file->fd, &term);
139 term.c_cc[VTIME] = 0;
142 term.c_cc[VTIME] = 1;
144 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
158 altos_close(struct altos_file *file)
160 if (file->fd != -1) {
164 write(file->pipe[1], "\r", 1);
174 altos_free(struct altos_file *file)
181 altos_flush(struct altos_file *file)
183 if (file->out_used && 0) {
185 fwrite(file->out_data, 1, file->out_used, stdout);
188 while (file->out_used) {
194 ret = write (file->fd, file->out_data, file->out_used);
196 ret = write (file->out_fd, file->out_data, file->out_used);
201 memmove(file->out_data, file->out_data + ret,
202 file->out_used - ret);
203 file->out_used -= ret;
210 altos_putchar(struct altos_file *file, char c)
214 if (file->out_used == USB_BUF_SIZE) {
215 ret = altos_flush(file);
220 file->out_data[file->out_used++] = c;
222 if (file->out_used == USB_BUF_SIZE)
223 ret = altos_flush(file);
232 altos_fill(struct altos_file *file, int timeout)
241 while (file->in_read == file->in_used) {
243 return LIBALTOS_ERROR;
246 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
247 fd[1].fd = file->pipe[0];
248 fd[1].events = POLLIN;
249 ret = poll(fd, 2, timeout);
251 perror("altos_getchar");
252 return LIBALTOS_ERROR;
255 return LIBALTOS_TIMEOUT;
257 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
258 return LIBALTOS_ERROR;
259 if (fd[0].revents & POLLIN)
262 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
264 perror("altos_getchar");
265 return LIBALTOS_ERROR;
270 if (ret == 0 && timeout > 0)
271 return LIBALTOS_TIMEOUT;
275 if (file->in_used && 0) {
277 fwrite(file->in_data, 1, file->in_used, stdout);
284 altos_getchar(struct altos_file *file, int timeout)
287 while (file->in_read == file->in_used) {
289 return LIBALTOS_ERROR;
290 ret = altos_fill(file, timeout);
294 return file->in_data[file->in_read++];
297 #endif /* POSIX_TTY */
300 * Scan for Altus Metrum devices by looking through /sys
311 #include <bluetooth/bluetooth.h>
312 #include <bluetooth/hci.h>
313 #include <bluetooth/hci_lib.h>
314 #include <bluetooth/rfcomm.h>
317 cc_fullname (char *dir, char *file)
320 int dlen = strlen (dir);
321 int flen = strlen (file);
324 if (dir[dlen-1] != '/')
326 new = malloc (dlen + slen + flen + 1);
337 cc_basename(char *file)
341 b = strrchr(file, '/');
348 load_string(char *dir, char *file)
350 char *full = cc_fullname(dir, file);
356 f = fopen(full, "r");
360 r = fgets(line, sizeof (line), f);
365 if (r[rlen-1] == '\n')
371 load_hex(char *dir, char *file)
377 line = load_string(dir, file);
380 i = strtol(line, &end, 16);
388 load_dec(char *dir, char *file)
394 line = load_string(dir, file);
397 i = strtol(line, &end, 10);
405 dir_filter_tty_colon(const struct dirent *d)
407 return strncmp(d->d_name, "tty:", 4) == 0;
411 dir_filter_tty(const struct dirent *d)
413 return strncmp(d->d_name, "tty", 3) == 0;
416 struct altos_usbdev {
421 int serial; /* AltOS always uses simple integer serial numbers */
432 struct dirent **namelist;
435 char endpoint_base[20];
441 base = cc_basename(sys);
442 num_configs = load_hex(sys, "bNumConfigurations");
443 num_interfaces = load_hex(sys, "bNumInterfaces");
444 for (config = 1; config <= num_configs; config++) {
445 for (interface = 0; interface < num_interfaces; interface++) {
446 sprintf(endpoint_base, "%s:%d.%d",
447 base, config, interface);
448 endpoint_full = cc_fullname(sys, endpoint_base);
450 /* Check for tty:ttyACMx style names
452 ntty = scandir(endpoint_full, &namelist,
453 dir_filter_tty_colon,
457 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
462 /* Check for tty/ttyACMx style names
464 tty_dir = cc_fullname(endpoint_full, "tty");
466 ntty = scandir(tty_dir, &namelist,
471 tty = cc_fullname("/dev", namelist[0]->d_name);
480 static struct altos_usbdev *
481 usb_scan_device(char *sys)
483 struct altos_usbdev *usbdev;
485 usbdev = calloc(1, sizeof (struct altos_usbdev));
488 usbdev->sys = strdup(sys);
489 usbdev->manufacturer = load_string(sys, "manufacturer");
490 usbdev->product_name = load_string(sys, "product");
491 usbdev->serial = load_dec(sys, "serial");
492 usbdev->idProduct = load_hex(sys, "idProduct");
493 usbdev->idVendor = load_hex(sys, "idVendor");
494 usbdev->tty = usb_tty(sys);
499 usbdev_free(struct altos_usbdev *usbdev)
502 free(usbdev->manufacturer);
503 free(usbdev->product_name);
504 /* this can get used as a return value */
510 #define USB_DEVICES "/sys/bus/usb/devices"
513 dir_filter_dev(const struct dirent *d)
515 const char *n = d->d_name;
523 if (c == '.' && n != d->d_name + 1)
531 struct altos_usbdev **dev;
537 altos_list_start(void)
540 struct dirent **ents;
542 struct altos_usbdev *dev;
543 struct altos_list *devs;
546 devs = calloc(1, sizeof (struct altos_list));
550 n = scandir (USB_DEVICES, &ents,
555 for (e = 0; e < n; e++) {
556 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
557 dev = usb_scan_device(dir);
559 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
561 devs->dev = realloc(devs->dev,
562 (devs->ndev + 1) * sizeof (struct usbdev *));
564 devs->dev = malloc (sizeof (struct usbdev *));
565 devs->dev[devs->ndev++] = dev;
574 altos_list_next(struct altos_list *list, struct altos_device *device)
576 struct altos_usbdev *dev;
577 if (list->current >= list->ndev)
579 dev = list->dev[list->current];
580 strcpy(device->name, dev->product_name);
581 device->vendor = dev->idVendor;
582 device->product = dev->idProduct;
583 strcpy(device->path, dev->tty);
584 device->serial = dev->serial;
590 altos_list_finish(struct altos_list *usbdevs)
596 for (i = 0; i < usbdevs->ndev; i++)
597 usbdev_free(usbdevs->dev[i]);
602 struct altos_bt_list {
610 #define INQUIRY_MAX_RSP 255
612 struct altos_bt_list *
613 altos_bt_list_start(int inquiry_time)
615 struct altos_bt_list *bt_list;
617 bt_list = calloc(1, sizeof (struct altos_bt_list));
621 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
624 bt_list->dev_id = hci_get_route(NULL);
625 if (bt_list->dev_id < 0)
628 bt_list->sock = hci_open_dev(bt_list->dev_id);
629 if (bt_list->sock < 0)
632 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
638 if (bt_list->num_rsp < 0)
645 close(bt_list->sock);
656 altos_bt_list_next(struct altos_bt_list *bt_list,
657 struct altos_bt_device *device)
661 if (bt_list->rsp >= bt_list->num_rsp)
664 ii = &bt_list->ii[bt_list->rsp];
665 ba2str(&ii->bdaddr, device->addr);
666 memset(&device->name, '\0', sizeof (device->name));
667 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
668 sizeof (device->name),
669 device->name, 0) < 0) {
670 strcpy(device->name, "[unknown]");
677 altos_bt_list_finish(struct altos_bt_list *bt_list)
679 close(bt_list->sock);
685 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
687 strncpy(device->name, name, sizeof (device->name));
688 device->name[sizeof(device->name)-1] = '\0';
689 strncpy(device->addr, addr, sizeof (device->addr));
690 device->addr[sizeof(device->addr)-1] = '\0';
694 altos_bt_open(struct altos_bt_device *device)
696 struct sockaddr_rc addr = { 0 };
698 struct altos_file *file;
700 file = calloc(1, sizeof (struct altos_file));
703 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
707 addr.rc_family = AF_BLUETOOTH;
709 str2ba(device->addr, &addr.rc_bdaddr);
711 status = connect(file->fd,
712 (struct sockaddr *)&addr,
723 file->out_fd = dup(file->fd);
733 #endif /* HAS_BLUETOOTH */
739 #include <IOKitLib.h>
740 #include <IOKit/usb/USBspec.h>
741 #include <sys/param.h>
743 #include <CFNumber.h>
750 io_iterator_t iterator;
754 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
756 CFTypeRef entry_as_string;
759 entry_as_string = IORegistryEntrySearchCFProperty (object,
763 kIORegistryIterateRecursively);
764 if (entry_as_string) {
765 got_string = CFStringGetCString(entry_as_string,
767 kCFStringEncodingASCII);
769 CFRelease(entry_as_string);
777 get_number(io_object_t object, CFStringRef entry, int *result)
779 CFTypeRef entry_as_number;
782 entry_as_number = IORegistryEntrySearchCFProperty (object,
786 kIORegistryIterateRecursively);
787 if (entry_as_number) {
788 got_number = CFNumberGetValue(entry_as_number,
797 PUBLIC struct altos_list *
798 altos_list_start(void)
800 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
801 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
802 io_iterator_t tdIterator;
803 io_object_t tdObject;
807 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
808 if (ret != kIOReturnSuccess)
814 altos_list_next(struct altos_list *list, struct altos_device *device)
817 char serial_string[128];
820 object = IOIteratorNext(list->iterator);
824 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
825 !get_number (object, CFSTR(kUSBProductID), &device->product))
827 if (device->vendor != 0xfffe)
829 if (device->product < 0x000a || 0x0013 < device->product)
831 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
832 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
833 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
834 device->serial = atoi(serial_string);
841 altos_list_finish(struct altos_list *list)
843 IOObjectRelease (list->iterator);
854 #include <setupapi.h>
861 #define USB_BUF_SIZE 64
865 unsigned char out_data[USB_BUF_SIZE];
867 unsigned char in_data[USB_BUF_SIZE];
875 PUBLIC struct altos_list *
876 altos_list_start(void)
878 struct altos_list *list = calloc(1, sizeof (struct altos_list));
882 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
883 DIGCF_ALLCLASSES|DIGCF_PRESENT);
884 if (list->dev_info == INVALID_HANDLE_VALUE) {
885 printf("SetupDiGetClassDevs failed %ld\n", GetLastError());
894 altos_list_next(struct altos_list *list, struct altos_device *device)
896 SP_DEVINFO_DATA dev_info_data;
899 char friendlyname[256];
903 unsigned int vid, pid;
906 DWORD friendlyname_type;
907 DWORD friendlyname_len;
909 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
910 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
915 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
916 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
918 if (dev_key == INVALID_HANDLE_VALUE) {
919 printf("cannot open device registry key\n");
923 /* Fetch symbolic name for this device and parse out
924 * the vid/pid/serial info */
925 symbolic_len = sizeof(symbolic);
926 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
927 symbolic, &symbolic_len);
929 printf("cannot find SymbolicName value\n");
930 RegCloseKey(dev_key);
933 vid = pid = serial = 0;
934 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
936 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
938 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
940 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
941 RegCloseKey(dev_key);
945 /* Fetch the com port name */
946 port_len = sizeof (port);
947 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
949 RegCloseKey(dev_key);
951 printf("failed to get PortName\n");
955 /* Fetch the device description which is the device name,
956 * with firmware that has unique USB ids */
957 friendlyname_len = sizeof (friendlyname);
958 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
962 (BYTE *)friendlyname,
963 sizeof(friendlyname),
966 printf("Failed to get friendlyname\n");
969 device->vendor = vid;
970 device->product = pid;
971 device->serial = serial;
972 strcpy(device->name, friendlyname);
974 strcpy(device->path, (char *) port);
977 result = GetLastError();
978 if (result != ERROR_NO_MORE_ITEMS)
979 printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
984 altos_list_finish(struct altos_list *list)
986 SetupDiDestroyDeviceInfoList(list->dev_info);
991 altos_queue_read(struct altos_file *file)
995 return LIBALTOS_SUCCESS;
997 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
998 if (GetLastError() != ERROR_IO_PENDING)
999 return LIBALTOS_ERROR;
1000 file->pend_read = TRUE;
1002 file->pend_read = FALSE;
1004 file->in_used = got;
1006 return LIBALTOS_SUCCESS;
1010 altos_wait_read(struct altos_file *file, int timeout)
1015 if (!file->pend_read)
1016 return LIBALTOS_SUCCESS;
1021 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1024 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE))
1025 return LIBALTOS_ERROR;
1026 file->pend_read = FALSE;
1028 file->in_used = got;
1031 return LIBALTOS_TIMEOUT;
1034 return LIBALTOS_ERROR;
1036 return LIBALTOS_SUCCESS;
1040 altos_fill(struct altos_file *file, int timeout)
1044 if (file->in_read < file->in_used)
1045 return LIBALTOS_SUCCESS;
1047 file->in_read = file->in_used = 0;
1049 ret = altos_queue_read(file);
1052 ret = altos_wait_read(file, timeout);
1056 return LIBALTOS_SUCCESS;
1060 altos_flush(struct altos_file *file)
1063 unsigned char *data = file->out_data;
1064 int used = file->out_used;
1068 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1069 if (GetLastError() != ERROR_IO_PENDING)
1070 return LIBALTOS_ERROR;
1071 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1074 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE))
1075 return LIBALTOS_ERROR;
1078 return LIBALTOS_ERROR;
1085 return LIBALTOS_SUCCESS;
1088 PUBLIC struct altos_file *
1089 altos_open(struct altos_device *device)
1091 struct altos_file *file = calloc (1, sizeof (struct altos_file));
1093 DCB dcbSerialParams = {0};
1094 COMMTIMEOUTS timeouts;
1099 strcpy(full_name, "\\\\.\\");
1100 strcat(full_name, device->path);
1101 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1102 0, NULL, OPEN_EXISTING,
1103 FILE_FLAG_OVERLAPPED, NULL);
1104 if (file->handle == INVALID_HANDLE_VALUE) {
1108 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1109 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1111 timeouts.ReadIntervalTimeout = MAXDWORD;
1112 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1113 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
1114 timeouts.WriteTotalTimeoutMultiplier = 0;
1115 timeouts.WriteTotalTimeoutConstant = 0;
1116 SetCommTimeouts(file->handle, &timeouts);
1118 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
1119 if (!GetCommState(file->handle, &dcbSerialParams)) {
1120 CloseHandle(file->handle);
1124 dcbSerialParams.BaudRate = CBR_9600;
1125 dcbSerialParams.ByteSize = 8;
1126 dcbSerialParams.StopBits = ONESTOPBIT;
1127 dcbSerialParams.Parity = NOPARITY;
1128 if (!SetCommState(file->handle, &dcbSerialParams)) {
1129 CloseHandle(file->handle);
1138 altos_close(struct altos_file *file)
1140 if (file->handle != INVALID_HANDLE_VALUE) {
1141 CloseHandle(file->handle);
1142 file->handle = INVALID_HANDLE_VALUE;
1147 altos_free(struct altos_file *file)
1154 altos_putchar(struct altos_file *file, char c)
1158 if (file->out_used == USB_BUF_SIZE) {
1159 ret = altos_flush(file);
1163 file->out_data[file->out_used++] = c;
1164 if (file->out_used == USB_BUF_SIZE)
1165 return altos_flush(file);
1166 return LIBALTOS_SUCCESS;
1170 altos_getchar(struct altos_file *file, int timeout)
1173 while (file->in_read == file->in_used) {
1174 if (file->handle == INVALID_HANDLE_VALUE)
1175 return LIBALTOS_ERROR;
1176 ret = altos_fill(file, timeout);
1180 return file->in_data[file->in_read++];