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 BLUETOOTH_PRODUCT_TELEBT "TeleBT"
30 return LIBALTOS_SUCCESS;
38 static struct altos_error last_error;
41 altos_set_last_error(int code, char *string)
43 last_error.code = code;
44 strncpy(last_error.string, string, sizeof (last_error.string) -1);
45 last_error.string[sizeof(last_error.string)-1] = '\0';
49 altos_get_last_error(struct altos_error *error)
58 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
60 altos_strndup (const char *s, size_t n)
62 size_t len = strlen (s);
74 #define altos_strndup strndup
85 #define USB_BUF_SIZE 64
94 unsigned char out_data[USB_BUF_SIZE];
96 unsigned char in_data[USB_BUF_SIZE];
102 altos_set_last_posix_error(void)
104 altos_set_last_error(errno, strerror(errno));
107 PUBLIC struct altos_file *
108 altos_open(struct altos_device *device)
110 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
115 altos_set_last_posix_error();
120 // altos_set_last_error(12, "yeah yeah, failed again");
124 file->fd = open(device->path, O_RDWR | O_NOCTTY);
126 altos_set_last_posix_error();
133 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
134 if (file->out_fd < 0) {
135 altos_set_last_posix_error();
141 ret = tcgetattr(file->fd, &term);
143 altos_set_last_posix_error();
152 cfsetospeed(&term, B9600);
153 cfsetispeed(&term, B9600);
156 term.c_cc[VTIME] = 0;
159 term.c_cc[VTIME] = 1;
161 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
163 altos_set_last_posix_error();
175 altos_close(struct altos_file *file)
177 if (file->fd != -1) {
181 write(file->pipe[1], "\r", 1);
191 altos_free(struct altos_file *file)
198 altos_flush(struct altos_file *file)
200 if (file->out_used && 0) {
202 fwrite(file->out_data, 1, file->out_used, stdout);
205 while (file->out_used) {
211 ret = write (file->fd, file->out_data, file->out_used);
213 ret = write (file->out_fd, file->out_data, file->out_used);
216 altos_set_last_posix_error();
217 return -last_error.code;
220 memmove(file->out_data, file->out_data + ret,
221 file->out_used - ret);
222 file->out_used -= ret;
229 altos_putchar(struct altos_file *file, char c)
233 if (file->out_used == USB_BUF_SIZE) {
234 ret = altos_flush(file);
239 file->out_data[file->out_used++] = c;
241 if (file->out_used == USB_BUF_SIZE)
242 ret = altos_flush(file);
251 altos_fill(struct altos_file *file, int timeout)
260 while (file->in_read == file->in_used) {
262 return LIBALTOS_ERROR;
265 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
266 fd[1].fd = file->pipe[0];
267 fd[1].events = POLLIN;
268 ret = poll(fd, 2, timeout);
270 altos_set_last_posix_error();
271 return LIBALTOS_ERROR;
274 return LIBALTOS_TIMEOUT;
276 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
277 return LIBALTOS_ERROR;
278 if (fd[0].revents & POLLIN)
281 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
283 altos_set_last_posix_error();
284 return LIBALTOS_ERROR;
289 if (ret == 0 && timeout > 0)
290 return LIBALTOS_TIMEOUT;
294 if (file->in_used && 0) {
296 fwrite(file->in_data, 1, file->in_used, stdout);
303 altos_getchar(struct altos_file *file, int timeout)
306 while (file->in_read == file->in_used) {
308 return LIBALTOS_ERROR;
309 ret = altos_fill(file, timeout);
313 return file->in_data[file->in_read++];
316 #endif /* POSIX_TTY */
319 * Scan for Altus Metrum devices by looking through /sys
330 #include <bluetooth/bluetooth.h>
331 #include <bluetooth/hci.h>
332 #include <bluetooth/hci_lib.h>
333 #include <bluetooth/rfcomm.h>
336 cc_fullname (char *dir, char *file)
339 int dlen = strlen (dir);
340 int flen = strlen (file);
343 if (dir[dlen-1] != '/')
345 new = malloc (dlen + slen + flen + 1);
356 cc_basename(char *file)
360 b = strrchr(file, '/');
367 load_string(char *dir, char *file)
369 char *full = cc_fullname(dir, file);
375 f = fopen(full, "r");
379 r = fgets(line, sizeof (line), f);
384 if (r[rlen-1] == '\n')
390 load_hex(char *dir, char *file)
396 line = load_string(dir, file);
399 i = strtol(line, &end, 16);
407 load_dec(char *dir, char *file)
413 line = load_string(dir, file);
416 i = strtol(line, &end, 10);
424 dir_filter_tty_colon(const struct dirent *d)
426 return strncmp(d->d_name, "tty:", 4) == 0;
430 dir_filter_tty(const struct dirent *d)
432 return strncmp(d->d_name, "tty", 3) == 0;
435 struct altos_usbdev {
440 int serial; /* AltOS always uses simple integer serial numbers */
451 struct dirent **namelist;
454 char endpoint_base[20];
460 base = cc_basename(sys);
461 num_configs = load_hex(sys, "bNumConfigurations");
462 num_interfaces = load_hex(sys, "bNumInterfaces");
463 for (config = 1; config <= num_configs; config++) {
464 for (interface = 0; interface < num_interfaces; interface++) {
465 sprintf(endpoint_base, "%s:%d.%d",
466 base, config, interface);
467 endpoint_full = cc_fullname(sys, endpoint_base);
470 /* Check for tty:ttyACMx style names
472 ntty = scandir(endpoint_full, &namelist,
473 dir_filter_tty_colon,
477 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
482 /* Check for ttyACMx style names
484 ntty = scandir(endpoint_full, &namelist,
489 tty = cc_fullname("/dev", namelist[0]->d_name);
494 /* Check for tty/ttyACMx style names
496 tty_dir = cc_fullname(endpoint_full, "tty");
498 ntty = scandir(tty_dir, &namelist,
503 tty = cc_fullname("/dev", namelist[0]->d_name);
513 static struct altos_usbdev *
514 usb_scan_device(char *sys)
516 struct altos_usbdev *usbdev;
522 usbdev = calloc(1, sizeof (struct altos_usbdev));
525 usbdev->sys = strdup(sys);
526 usbdev->manufacturer = load_string(sys, "manufacturer");
527 usbdev->product_name = load_string(sys, "product");
528 usbdev->serial = load_dec(sys, "serial");
529 usbdev->idProduct = load_hex(sys, "idProduct");
530 usbdev->idVendor = load_hex(sys, "idVendor");
536 usbdev_free(struct altos_usbdev *usbdev)
539 free(usbdev->manufacturer);
540 free(usbdev->product_name);
541 /* this can get used as a return value */
547 #define USB_DEVICES "/sys/bus/usb/devices"
550 dir_filter_dev(const struct dirent *d)
552 const char *n = d->d_name;
560 if (c == '.' && n != d->d_name + 1)
568 struct altos_usbdev **dev;
574 altos_list_start(void)
577 struct dirent **ents;
579 struct altos_usbdev *dev;
580 struct altos_list *devs;
583 devs = calloc(1, sizeof (struct altos_list));
587 n = scandir (USB_DEVICES, &ents,
592 for (e = 0; e < n; e++) {
593 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
594 dev = usb_scan_device(dir);
599 devs->dev = realloc(devs->dev,
600 (devs->ndev + 1) * sizeof (struct usbdev *));
602 devs->dev = malloc (sizeof (struct usbdev *));
603 devs->dev[devs->ndev++] = dev;
611 altos_list_next(struct altos_list *list, struct altos_device *device)
613 struct altos_usbdev *dev;
614 if (list->current >= list->ndev) {
618 dev = list->dev[list->current];
619 strcpy(device->name, dev->product_name);
620 device->vendor = dev->idVendor;
621 device->product = dev->idProduct;
622 strcpy(device->path, dev->tty);
623 device->serial = dev->serial;
629 altos_list_finish(struct altos_list *usbdevs)
635 for (i = 0; i < usbdevs->ndev; i++)
636 usbdev_free(usbdevs->dev[i]);
640 struct altos_bt_list {
648 #define INQUIRY_MAX_RSP 255
650 struct altos_bt_list *
651 altos_bt_list_start(int inquiry_time)
653 struct altos_bt_list *bt_list;
655 bt_list = calloc(1, sizeof (struct altos_bt_list));
659 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
662 bt_list->dev_id = hci_get_route(NULL);
663 if (bt_list->dev_id < 0)
666 bt_list->sock = hci_open_dev(bt_list->dev_id);
667 if (bt_list->sock < 0)
670 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
676 if (bt_list->num_rsp < 0)
683 close(bt_list->sock);
694 altos_bt_list_next(struct altos_bt_list *bt_list,
695 struct altos_bt_device *device)
699 if (bt_list->rsp >= bt_list->num_rsp)
702 ii = &bt_list->ii[bt_list->rsp];
703 ba2str(&ii->bdaddr, device->addr);
704 memset(&device->name, '\0', sizeof (device->name));
705 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
706 sizeof (device->name),
707 device->name, 0) < 0) {
708 strcpy(device->name, "[unknown]");
715 altos_bt_list_finish(struct altos_bt_list *bt_list)
717 close(bt_list->sock);
723 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
725 strncpy(device->name, name, sizeof (device->name));
726 device->name[sizeof(device->name)-1] = '\0';
727 strncpy(device->addr, addr, sizeof (device->addr));
728 device->addr[sizeof(device->addr)-1] = '\0';
732 altos_bt_open(struct altos_bt_device *device)
734 struct sockaddr_rc addr = { 0 };
736 struct altos_file *file;
738 file = calloc(1, sizeof (struct altos_file));
741 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
743 altos_set_last_posix_error();
747 addr.rc_family = AF_BLUETOOTH;
749 str2ba(device->addr, &addr.rc_bdaddr);
751 status = connect(file->fd,
752 (struct sockaddr *)&addr,
755 altos_set_last_posix_error();
763 file->out_fd = dup(file->fd);
778 #include <IOKitLib.h>
779 #include <IOKit/usb/USBspec.h>
780 #include <sys/param.h>
782 #include <CFNumber.h>
789 io_iterator_t iterator;
793 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
795 CFTypeRef entry_as_string;
798 entry_as_string = IORegistryEntrySearchCFProperty (object,
802 kIORegistryIterateRecursively);
803 if (entry_as_string) {
804 got_string = CFStringGetCString(entry_as_string,
806 kCFStringEncodingASCII);
808 CFRelease(entry_as_string);
816 get_number(io_object_t object, CFStringRef entry, int *result)
818 CFTypeRef entry_as_number;
821 entry_as_number = IORegistryEntrySearchCFProperty (object,
825 kIORegistryIterateRecursively);
826 if (entry_as_number) {
827 got_number = CFNumberGetValue(entry_as_number,
836 PUBLIC struct altos_list *
837 altos_list_start(void)
839 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
840 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
841 io_iterator_t tdIterator;
842 io_object_t tdObject;
846 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
847 if (ret != kIOReturnSuccess)
853 altos_list_next(struct altos_list *list, struct altos_device *device)
856 char serial_string[128];
859 object = IOIteratorNext(list->iterator);
863 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
864 !get_number (object, CFSTR(kUSBProductID), &device->product))
866 if (device->vendor != 0xfffe)
868 if (device->product < 0x000a || 0x0013 < device->product)
870 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
871 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
872 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
873 device->serial = atoi(serial_string);
880 altos_list_finish(struct altos_list *list)
882 IOObjectRelease (list->iterator);
886 struct altos_bt_list {
893 #define INQUIRY_MAX_RSP 255
895 struct altos_bt_list *
896 altos_bt_list_start(int inquiry_time)
902 altos_bt_list_next(struct altos_bt_list *bt_list,
903 struct altos_bt_device *device)
909 altos_bt_list_finish(struct altos_bt_list *bt_list)
914 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
916 strncpy(device->name, name, sizeof (device->name));
917 device->name[sizeof(device->name)-1] = '\0';
918 strncpy(device->addr, addr, sizeof (device->addr));
919 device->addr[sizeof(device->addr)-1] = '\0';
923 altos_bt_open(struct altos_bt_device *device)
935 #include <setupapi.h>
942 #define USB_BUF_SIZE 64
946 unsigned char out_data[USB_BUF_SIZE];
948 unsigned char in_data[USB_BUF_SIZE];
957 _altos_set_last_windows_error(char *file, int line)
959 DWORD error = GetLastError();
961 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
966 sizeof (message) / sizeof (TCHAR),
968 if (error != ERROR_SUCCESS)
969 printf ("%s:%d %s\n", file, line, message);
970 altos_set_last_error(error, message);
973 #define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__)
975 PUBLIC struct altos_list *
976 altos_list_start(void)
978 struct altos_list *list = calloc(1, sizeof (struct altos_list));
982 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
983 DIGCF_ALLCLASSES|DIGCF_PRESENT);
984 if (list->dev_info == INVALID_HANDLE_VALUE) {
985 altos_set_last_windows_error();
994 altos_list_next(struct altos_list *list, struct altos_device *device)
996 SP_DEVINFO_DATA dev_info_data;
999 char friendlyname[256];
1003 unsigned int vid, pid;
1006 DWORD friendlyname_type;
1007 DWORD friendlyname_len;
1009 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
1010 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
1015 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
1016 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
1018 if (dev_key == INVALID_HANDLE_VALUE) {
1019 altos_set_last_windows_error();
1020 printf("cannot open device registry key\n");
1024 /* Fetch symbolic name for this device and parse out
1025 * the vid/pid/serial info */
1026 symbolic_len = sizeof(symbolic);
1027 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
1028 symbolic, &symbolic_len);
1030 altos_set_last_windows_error();
1031 printf("cannot find SymbolicName value\n");
1032 RegCloseKey(dev_key);
1035 vid = pid = serial = 0;
1036 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
1038 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
1040 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
1043 /* Fetch the com port name */
1044 port_len = sizeof (port);
1045 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
1047 RegCloseKey(dev_key);
1049 altos_set_last_windows_error();
1050 printf("failed to get PortName\n");
1054 /* Fetch the device description which is the device name,
1055 * with firmware that has unique USB ids */
1056 friendlyname_len = sizeof (friendlyname);
1057 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
1061 (BYTE *)friendlyname,
1062 sizeof(friendlyname),
1065 altos_set_last_windows_error();
1066 printf("Failed to get friendlyname\n");
1069 device->vendor = vid;
1070 device->product = pid;
1071 device->serial = serial;
1072 strcpy(device->name, friendlyname);
1074 strcpy(device->path, (char *) port);
1077 result = GetLastError();
1078 if (result != ERROR_NO_MORE_ITEMS) {
1079 altos_set_last_windows_error();
1080 printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
1086 altos_list_finish(struct altos_list *list)
1088 SetupDiDestroyDeviceInfoList(list->dev_info);
1093 altos_queue_read(struct altos_file *file)
1096 if (file->pend_read)
1097 return LIBALTOS_SUCCESS;
1099 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
1100 if (GetLastError() != ERROR_IO_PENDING) {
1101 altos_set_last_windows_error();
1102 return LIBALTOS_ERROR;
1104 file->pend_read = TRUE;
1106 file->pend_read = FALSE;
1108 file->in_used = got;
1110 return LIBALTOS_SUCCESS;
1114 altos_wait_read(struct altos_file *file, int timeout)
1119 if (!file->pend_read)
1120 return LIBALTOS_SUCCESS;
1125 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1128 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
1129 altos_set_last_windows_error();
1130 return LIBALTOS_ERROR;
1132 file->pend_read = FALSE;
1134 file->in_used = got;
1137 return LIBALTOS_TIMEOUT;
1140 return LIBALTOS_ERROR;
1142 return LIBALTOS_SUCCESS;
1146 altos_fill(struct altos_file *file, int timeout)
1150 if (file->in_read < file->in_used)
1151 return LIBALTOS_SUCCESS;
1153 file->in_read = file->in_used = 0;
1155 ret = altos_queue_read(file);
1158 ret = altos_wait_read(file, timeout);
1162 return LIBALTOS_SUCCESS;
1166 altos_flush(struct altos_file *file)
1169 unsigned char *data = file->out_data;
1170 int used = file->out_used;
1174 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1175 if (GetLastError() != ERROR_IO_PENDING) {
1176 altos_set_last_windows_error();
1177 printf ("\tflush write error\n");
1178 return LIBALTOS_ERROR;
1180 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1183 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
1184 altos_set_last_windows_error();
1185 printf ("\tflush result error\n");
1186 return LIBALTOS_ERROR;
1190 altos_set_last_windows_error();
1191 printf ("\tflush wait error\n");
1192 return LIBALTOS_ERROR;
1199 return LIBALTOS_SUCCESS;
1202 PUBLIC struct altos_file *
1203 altos_open(struct altos_device *device)
1205 struct altos_file *file = calloc (1, sizeof (struct altos_file));
1207 COMMTIMEOUTS timeouts;
1212 strcpy(full_name, "\\\\.\\");
1213 strcat(full_name, device->path);
1214 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1215 0, NULL, OPEN_EXISTING,
1216 FILE_FLAG_OVERLAPPED, NULL);
1217 if (file->handle == INVALID_HANDLE_VALUE) {
1218 altos_set_last_windows_error();
1219 printf ("cannot open %s\n", full_name);
1223 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1224 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1226 timeouts.ReadIntervalTimeout = MAXDWORD;
1227 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1228 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
1229 timeouts.WriteTotalTimeoutMultiplier = 0;
1230 timeouts.WriteTotalTimeoutConstant = 0;
1231 SetCommTimeouts(file->handle, &timeouts);
1237 altos_close(struct altos_file *file)
1239 if (file->handle != INVALID_HANDLE_VALUE) {
1240 CloseHandle(file->handle);
1241 file->handle = INVALID_HANDLE_VALUE;
1242 SetEvent(file->ov_read.hEvent);
1243 SetEvent(file->ov_write.hEvent);
1244 CloseHandle(file->ov_read.hEvent);
1245 CloseHandle(file->ov_write.hEvent);
1250 altos_free(struct altos_file *file)
1257 altos_putchar(struct altos_file *file, char c)
1261 if (file->out_used == USB_BUF_SIZE) {
1262 ret = altos_flush(file);
1266 file->out_data[file->out_used++] = c;
1267 if (file->out_used == USB_BUF_SIZE)
1268 return altos_flush(file);
1269 return LIBALTOS_SUCCESS;
1273 altos_getchar(struct altos_file *file, int timeout)
1276 while (file->in_read == file->in_used) {
1277 if (file->handle == INVALID_HANDLE_VALUE)
1278 return LIBALTOS_ERROR;
1279 ret = altos_fill(file, timeout);
1283 return file->in_data[file->in_read++];
1286 struct altos_bt_list *
1287 altos_bt_list_start(int inquiry_time)
1293 altos_bt_list_next(struct altos_bt_list *bt_list,
1294 struct altos_bt_device *device)
1300 altos_bt_list_finish(struct altos_bt_list *bt_list)
1306 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
1308 strncpy(device->name, name, sizeof (device->name));
1309 device->name[sizeof(device->name)-1] = '\0';
1310 strncpy(device->addr, addr, sizeof (device->addr));
1311 device->addr[sizeof(device->addr)-1] = '\0';
1315 altos_bt_open(struct altos_bt_device *device)