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;
52 static struct altos_error last_error;
55 altos_set_last_error(int code, char *string)
57 last_error.code = code;
58 strncpy(last_error.string, string, sizeof (last_error.string) -1);
59 last_error.string[sizeof(last_error.string)-1] = '\0';
63 altos_get_last_error(struct altos_error *error)
72 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
74 altos_strndup (const char *s, size_t n)
76 size_t len = strlen (s);
88 #define altos_strndup strndup
99 #define USB_BUF_SIZE 64
108 unsigned char out_data[USB_BUF_SIZE];
110 unsigned char in_data[USB_BUF_SIZE];
116 altos_set_last_posix_error(void)
118 altos_set_last_error(errno, strerror(errno));
121 PUBLIC struct altos_file *
122 altos_open(struct altos_device *device)
124 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
129 altos_set_last_posix_error();
133 // altos_set_last_error(12, "yeah yeah, failed again");
137 file->fd = open(device->path, O_RDWR | O_NOCTTY);
139 altos_set_last_posix_error();
146 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
147 if (file->out_fd < 0) {
148 altos_set_last_posix_error();
154 ret = tcgetattr(file->fd, &term);
156 altos_set_last_posix_error();
167 term.c_cc[VTIME] = 0;
170 term.c_cc[VTIME] = 1;
172 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
174 altos_set_last_posix_error();
186 altos_close(struct altos_file *file)
188 if (file->fd != -1) {
192 write(file->pipe[1], "\r", 1);
202 altos_free(struct altos_file *file)
209 altos_flush(struct altos_file *file)
211 if (file->out_used && 0) {
213 fwrite(file->out_data, 1, file->out_used, stdout);
216 while (file->out_used) {
222 ret = write (file->fd, file->out_data, file->out_used);
224 ret = write (file->out_fd, file->out_data, file->out_used);
227 altos_set_last_posix_error();
231 memmove(file->out_data, file->out_data + ret,
232 file->out_used - ret);
233 file->out_used -= ret;
240 altos_putchar(struct altos_file *file, char c)
244 if (file->out_used == USB_BUF_SIZE) {
245 ret = altos_flush(file);
250 file->out_data[file->out_used++] = c;
252 if (file->out_used == USB_BUF_SIZE)
253 ret = altos_flush(file);
262 altos_fill(struct altos_file *file, int timeout)
271 while (file->in_read == file->in_used) {
273 return LIBALTOS_ERROR;
276 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
277 fd[1].fd = file->pipe[0];
278 fd[1].events = POLLIN;
279 ret = poll(fd, 2, timeout);
281 altos_set_last_posix_error();
282 return LIBALTOS_ERROR;
285 return LIBALTOS_TIMEOUT;
287 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
288 return LIBALTOS_ERROR;
289 if (fd[0].revents & POLLIN)
292 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
294 altos_set_last_posix_error();
295 return LIBALTOS_ERROR;
300 if (ret == 0 && timeout > 0)
301 return LIBALTOS_TIMEOUT;
305 if (file->in_used && 0) {
307 fwrite(file->in_data, 1, file->in_used, stdout);
314 altos_getchar(struct altos_file *file, int timeout)
317 while (file->in_read == file->in_used) {
319 return LIBALTOS_ERROR;
320 ret = altos_fill(file, timeout);
324 return file->in_data[file->in_read++];
327 #endif /* POSIX_TTY */
330 * Scan for Altus Metrum devices by looking through /sys
341 #include <bluetooth/bluetooth.h>
342 #include <bluetooth/hci.h>
343 #include <bluetooth/hci_lib.h>
344 #include <bluetooth/rfcomm.h>
347 cc_fullname (char *dir, char *file)
350 int dlen = strlen (dir);
351 int flen = strlen (file);
354 if (dir[dlen-1] != '/')
356 new = malloc (dlen + slen + flen + 1);
367 cc_basename(char *file)
371 b = strrchr(file, '/');
378 load_string(char *dir, char *file)
380 char *full = cc_fullname(dir, file);
386 f = fopen(full, "r");
390 r = fgets(line, sizeof (line), f);
395 if (r[rlen-1] == '\n')
401 load_hex(char *dir, char *file)
407 line = load_string(dir, file);
410 i = strtol(line, &end, 16);
418 load_dec(char *dir, char *file)
424 line = load_string(dir, file);
427 i = strtol(line, &end, 10);
435 dir_filter_tty_colon(const struct dirent *d)
437 return strncmp(d->d_name, "tty:", 4) == 0;
441 dir_filter_tty(const struct dirent *d)
443 return strncmp(d->d_name, "tty", 3) == 0;
446 struct altos_usbdev {
451 int serial; /* AltOS always uses simple integer serial numbers */
462 struct dirent **namelist;
465 char endpoint_base[20];
471 base = cc_basename(sys);
472 num_configs = load_hex(sys, "bNumConfigurations");
473 num_interfaces = load_hex(sys, "bNumInterfaces");
474 for (config = 1; config <= num_configs; config++) {
475 for (interface = 0; interface < num_interfaces; interface++) {
476 sprintf(endpoint_base, "%s:%d.%d",
477 base, config, interface);
478 endpoint_full = cc_fullname(sys, endpoint_base);
480 /* Check for tty:ttyACMx style names
482 ntty = scandir(endpoint_full, &namelist,
483 dir_filter_tty_colon,
487 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
492 /* Check for tty/ttyACMx style names
494 tty_dir = cc_fullname(endpoint_full, "tty");
496 ntty = scandir(tty_dir, &namelist,
501 tty = cc_fullname("/dev", namelist[0]->d_name);
510 static struct altos_usbdev *
511 usb_scan_device(char *sys)
513 struct altos_usbdev *usbdev;
515 usbdev = calloc(1, sizeof (struct altos_usbdev));
518 usbdev->sys = strdup(sys);
519 usbdev->manufacturer = load_string(sys, "manufacturer");
520 usbdev->product_name = load_string(sys, "product");
521 usbdev->serial = load_dec(sys, "serial");
522 usbdev->idProduct = load_hex(sys, "idProduct");
523 usbdev->idVendor = load_hex(sys, "idVendor");
524 usbdev->tty = usb_tty(sys);
529 usbdev_free(struct altos_usbdev *usbdev)
532 free(usbdev->manufacturer);
533 free(usbdev->product_name);
534 /* this can get used as a return value */
540 #define USB_DEVICES "/sys/bus/usb/devices"
543 dir_filter_dev(const struct dirent *d)
545 const char *n = d->d_name;
553 if (c == '.' && n != d->d_name + 1)
561 struct altos_usbdev **dev;
567 altos_list_start(void)
570 struct dirent **ents;
572 struct altos_usbdev *dev;
573 struct altos_list *devs;
576 devs = calloc(1, sizeof (struct altos_list));
580 n = scandir (USB_DEVICES, &ents,
585 for (e = 0; e < n; e++) {
586 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
587 dev = usb_scan_device(dir);
589 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
591 devs->dev = realloc(devs->dev,
592 (devs->ndev + 1) * sizeof (struct usbdev *));
594 devs->dev = malloc (sizeof (struct usbdev *));
595 devs->dev[devs->ndev++] = dev;
604 altos_list_next(struct altos_list *list, struct altos_device *device)
606 struct altos_usbdev *dev;
607 if (list->current >= list->ndev)
609 dev = list->dev[list->current];
610 strcpy(device->name, dev->product_name);
611 device->vendor = dev->idVendor;
612 device->product = dev->idProduct;
613 strcpy(device->path, dev->tty);
614 device->serial = dev->serial;
620 altos_list_finish(struct altos_list *usbdevs)
626 for (i = 0; i < usbdevs->ndev; i++)
627 usbdev_free(usbdevs->dev[i]);
631 struct altos_bt_list {
639 #define INQUIRY_MAX_RSP 255
641 struct altos_bt_list *
642 altos_bt_list_start(int inquiry_time)
644 struct altos_bt_list *bt_list;
646 bt_list = calloc(1, sizeof (struct altos_bt_list));
650 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
653 bt_list->dev_id = hci_get_route(NULL);
654 if (bt_list->dev_id < 0)
657 bt_list->sock = hci_open_dev(bt_list->dev_id);
658 if (bt_list->sock < 0)
661 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
667 if (bt_list->num_rsp < 0)
674 close(bt_list->sock);
685 altos_bt_list_next(struct altos_bt_list *bt_list,
686 struct altos_bt_device *device)
690 if (bt_list->rsp >= bt_list->num_rsp)
693 ii = &bt_list->ii[bt_list->rsp];
694 ba2str(&ii->bdaddr, device->addr);
695 memset(&device->name, '\0', sizeof (device->name));
696 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
697 sizeof (device->name),
698 device->name, 0) < 0) {
699 strcpy(device->name, "[unknown]");
706 altos_bt_list_finish(struct altos_bt_list *bt_list)
708 close(bt_list->sock);
714 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
716 strncpy(device->name, name, sizeof (device->name));
717 device->name[sizeof(device->name)-1] = '\0';
718 strncpy(device->addr, addr, sizeof (device->addr));
719 device->addr[sizeof(device->addr)-1] = '\0';
723 altos_bt_open(struct altos_bt_device *device)
725 struct sockaddr_rc addr = { 0 };
727 struct altos_file *file;
729 file = calloc(1, sizeof (struct altos_file));
732 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
734 altos_set_last_posix_error();
738 addr.rc_family = AF_BLUETOOTH;
740 str2ba(device->addr, &addr.rc_bdaddr);
742 status = connect(file->fd,
743 (struct sockaddr *)&addr,
746 altos_set_last_posix_error();
754 file->out_fd = dup(file->fd);
769 #include <IOKitLib.h>
770 #include <IOKit/usb/USBspec.h>
771 #include <sys/param.h>
773 #include <CFNumber.h>
780 io_iterator_t iterator;
784 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
786 CFTypeRef entry_as_string;
789 entry_as_string = IORegistryEntrySearchCFProperty (object,
793 kIORegistryIterateRecursively);
794 if (entry_as_string) {
795 got_string = CFStringGetCString(entry_as_string,
797 kCFStringEncodingASCII);
799 CFRelease(entry_as_string);
807 get_number(io_object_t object, CFStringRef entry, int *result)
809 CFTypeRef entry_as_number;
812 entry_as_number = IORegistryEntrySearchCFProperty (object,
816 kIORegistryIterateRecursively);
817 if (entry_as_number) {
818 got_number = CFNumberGetValue(entry_as_number,
827 PUBLIC struct altos_list *
828 altos_list_start(void)
830 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
831 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
832 io_iterator_t tdIterator;
833 io_object_t tdObject;
837 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
838 if (ret != kIOReturnSuccess)
844 altos_list_next(struct altos_list *list, struct altos_device *device)
847 char serial_string[128];
850 object = IOIteratorNext(list->iterator);
854 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
855 !get_number (object, CFSTR(kUSBProductID), &device->product))
857 if (device->vendor != 0xfffe)
859 if (device->product < 0x000a || 0x0013 < device->product)
861 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
862 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
863 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
864 device->serial = atoi(serial_string);
871 altos_list_finish(struct altos_list *list)
873 IOObjectRelease (list->iterator);
877 struct altos_bt_list {
884 #define INQUIRY_MAX_RSP 255
886 struct altos_bt_list *
887 altos_bt_list_start(int inquiry_time)
893 altos_bt_list_next(struct altos_bt_list *bt_list,
894 struct altos_bt_device *device)
900 altos_bt_list_finish(struct altos_bt_list *bt_list)
905 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
907 strncpy(device->name, name, sizeof (device->name));
908 device->name[sizeof(device->name)-1] = '\0';
909 strncpy(device->addr, addr, sizeof (device->addr));
910 device->addr[sizeof(device->addr)-1] = '\0';
914 altos_bt_open(struct altos_bt_device *device)
926 #include <setupapi.h>
933 #define USB_BUF_SIZE 64
937 unsigned char out_data[USB_BUF_SIZE];
939 unsigned char in_data[USB_BUF_SIZE];
948 altos_set_last_windows_error(void)
950 DWORD error = GetLastError();
952 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
957 sizeof (message) / sizeof (TCHAR),
959 altos_set_last_error(error, message);
962 PUBLIC struct altos_list *
963 altos_list_start(void)
965 struct altos_list *list = calloc(1, sizeof (struct altos_list));
969 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
970 DIGCF_ALLCLASSES|DIGCF_PRESENT);
971 if (list->dev_info == INVALID_HANDLE_VALUE) {
972 altos_set_last_windows_error();
981 altos_list_next(struct altos_list *list, struct altos_device *device)
983 SP_DEVINFO_DATA dev_info_data;
986 char friendlyname[256];
990 unsigned int vid, pid;
993 DWORD friendlyname_type;
994 DWORD friendlyname_len;
996 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
997 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
1002 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
1003 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
1005 if (dev_key == INVALID_HANDLE_VALUE) {
1006 altos_set_last_windows_error();
1007 printf("cannot open device registry key\n");
1011 /* Fetch symbolic name for this device and parse out
1012 * the vid/pid/serial info */
1013 symbolic_len = sizeof(symbolic);
1014 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
1015 symbolic, &symbolic_len);
1017 altos_set_last_windows_error();
1018 printf("cannot find SymbolicName value\n");
1019 RegCloseKey(dev_key);
1022 vid = pid = serial = 0;
1023 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
1025 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
1027 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
1029 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
1030 RegCloseKey(dev_key);
1034 /* Fetch the com port name */
1035 port_len = sizeof (port);
1036 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
1038 RegCloseKey(dev_key);
1040 altos_set_last_windows_error();
1041 printf("failed to get PortName\n");
1045 /* Fetch the device description which is the device name,
1046 * with firmware that has unique USB ids */
1047 friendlyname_len = sizeof (friendlyname);
1048 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
1052 (BYTE *)friendlyname,
1053 sizeof(friendlyname),
1056 altos_set_last_windows_error();
1057 printf("Failed to get friendlyname\n");
1060 device->vendor = vid;
1061 device->product = pid;
1062 device->serial = serial;
1063 strcpy(device->name, friendlyname);
1065 strcpy(device->path, (char *) port);
1068 result = GetLastError();
1069 if (result != ERROR_NO_MORE_ITEMS) {
1070 altos_set_last_windows_error();
1071 printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
1077 altos_list_finish(struct altos_list *list)
1079 SetupDiDestroyDeviceInfoList(list->dev_info);
1084 altos_queue_read(struct altos_file *file)
1087 if (file->pend_read)
1088 return LIBALTOS_SUCCESS;
1090 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
1091 if (GetLastError() != ERROR_IO_PENDING) {
1092 altos_set_last_windows_error();
1093 return LIBALTOS_ERROR;
1095 file->pend_read = TRUE;
1097 file->pend_read = FALSE;
1099 file->in_used = got;
1101 return LIBALTOS_SUCCESS;
1105 altos_wait_read(struct altos_file *file, int timeout)
1110 if (!file->pend_read)
1111 return LIBALTOS_SUCCESS;
1116 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1119 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
1120 altos_set_last_windows_error();
1121 return LIBALTOS_ERROR;
1123 file->pend_read = FALSE;
1125 file->in_used = got;
1128 return LIBALTOS_TIMEOUT;
1131 return LIBALTOS_ERROR;
1133 return LIBALTOS_SUCCESS;
1137 altos_fill(struct altos_file *file, int timeout)
1141 if (file->in_read < file->in_used)
1142 return LIBALTOS_SUCCESS;
1144 file->in_read = file->in_used = 0;
1146 ret = altos_queue_read(file);
1149 ret = altos_wait_read(file, timeout);
1153 return LIBALTOS_SUCCESS;
1157 altos_flush(struct altos_file *file)
1160 unsigned char *data = file->out_data;
1161 int used = file->out_used;
1165 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1166 if (GetLastError() != ERROR_IO_PENDING) {
1167 altos_set_last_windows_error();
1168 return LIBALTOS_ERROR;
1170 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1173 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
1174 altos_set_last_windows_error();
1175 return LIBALTOS_ERROR;
1179 altos_set_last_windows_error();
1180 return LIBALTOS_ERROR;
1187 return LIBALTOS_SUCCESS;
1190 PUBLIC struct altos_file *
1191 altos_open(struct altos_device *device)
1193 struct altos_file *file = calloc (1, sizeof (struct altos_file));
1195 DCB dcbSerialParams = {0};
1196 COMMTIMEOUTS timeouts;
1201 strcpy(full_name, "\\\\.\\");
1202 strcat(full_name, device->path);
1203 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1204 0, NULL, OPEN_EXISTING,
1205 FILE_FLAG_OVERLAPPED, NULL);
1206 if (file->handle == INVALID_HANDLE_VALUE) {
1207 altos_set_last_windows_error();
1211 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1212 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1214 timeouts.ReadIntervalTimeout = MAXDWORD;
1215 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1216 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
1217 timeouts.WriteTotalTimeoutMultiplier = 0;
1218 timeouts.WriteTotalTimeoutConstant = 0;
1219 SetCommTimeouts(file->handle, &timeouts);
1221 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
1222 if (!GetCommState(file->handle, &dcbSerialParams)) {
1223 altos_set_last_windows_error();
1224 CloseHandle(file->handle);
1228 dcbSerialParams.BaudRate = CBR_9600;
1229 dcbSerialParams.ByteSize = 8;
1230 dcbSerialParams.StopBits = ONESTOPBIT;
1231 dcbSerialParams.Parity = NOPARITY;
1232 if (!SetCommState(file->handle, &dcbSerialParams)) {
1233 altos_set_last_windows_error();
1234 CloseHandle(file->handle);
1243 altos_close(struct altos_file *file)
1245 if (file->handle != INVALID_HANDLE_VALUE) {
1246 CloseHandle(file->handle);
1247 file->handle = INVALID_HANDLE_VALUE;
1252 altos_free(struct altos_file *file)
1259 altos_putchar(struct altos_file *file, char c)
1263 if (file->out_used == USB_BUF_SIZE) {
1264 ret = altos_flush(file);
1268 file->out_data[file->out_used++] = c;
1269 if (file->out_used == USB_BUF_SIZE)
1270 return altos_flush(file);
1271 return LIBALTOS_SUCCESS;
1275 altos_getchar(struct altos_file *file, int timeout)
1278 while (file->in_read == file->in_used) {
1279 if (file->handle == INVALID_HANDLE_VALUE)
1280 return LIBALTOS_ERROR;
1281 ret = altos_fill(file, timeout);
1285 return file->in_data[file->in_read++];
1288 struct altos_bt_list *
1289 altos_bt_list_start(int inquiry_time)
1295 altos_bt_list_next(struct altos_bt_list *bt_list,
1296 struct altos_bt_device *device)
1302 altos_bt_list_finish(struct altos_bt_list *bt_list)
1308 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
1310 strncpy(device->name, name, sizeof (device->name));
1311 device->name[sizeof(device->name)-1] = '\0';
1312 strncpy(device->addr, addr, sizeof (device->addr));
1313 device->addr[sizeof(device->addr)-1] = '\0';
1317 altos_bt_open(struct altos_bt_device *device)