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_ALTUSMETRUM_MIN 0x000a
27 #define USB_PRODUCT_ALTUSMETRUM_MAX 0x00ff
29 #define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \
30 (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \
31 (p) <= USB_PRODUCT_ALTUSMETRUM_MAX))
33 #define BLUETOOTH_PRODUCT_TELEBT "TeleBT"
40 return LIBALTOS_SUCCESS;
48 static struct altos_error last_error;
51 altos_set_last_error(int code, char *string)
53 last_error.code = code;
54 strncpy(last_error.string, string, sizeof (last_error.string) -1);
55 last_error.string[sizeof(last_error.string)-1] = '\0';
59 altos_get_last_error(struct altos_error *error)
68 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
70 altos_strndup (const char *s, size_t n)
72 size_t len = strlen (s);
84 #define altos_strndup strndup
95 #define USB_BUF_SIZE 64
104 unsigned char out_data[USB_BUF_SIZE];
106 unsigned char in_data[USB_BUF_SIZE];
112 altos_set_last_posix_error(void)
114 altos_set_last_error(errno, strerror(errno));
117 PUBLIC struct altos_file *
118 altos_open(struct altos_device *device)
120 struct altos_file *file = calloc (sizeof (struct altos_file), 1);
125 altos_set_last_posix_error();
129 // altos_set_last_error(12, "yeah yeah, failed again");
133 file->fd = open(device->path, O_RDWR | O_NOCTTY);
135 altos_set_last_posix_error();
142 file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
143 if (file->out_fd < 0) {
144 altos_set_last_posix_error();
150 ret = tcgetattr(file->fd, &term);
152 altos_set_last_posix_error();
163 term.c_cc[VTIME] = 0;
166 term.c_cc[VTIME] = 1;
168 ret = tcsetattr(file->fd, TCSAFLUSH, &term);
170 altos_set_last_posix_error();
182 altos_close(struct altos_file *file)
184 if (file->fd != -1) {
188 write(file->pipe[1], "\r", 1);
198 altos_free(struct altos_file *file)
205 altos_flush(struct altos_file *file)
207 if (file->out_used && 0) {
209 fwrite(file->out_data, 1, file->out_used, stdout);
212 while (file->out_used) {
218 ret = write (file->fd, file->out_data, file->out_used);
220 ret = write (file->out_fd, file->out_data, file->out_used);
223 altos_set_last_posix_error();
224 return -last_error.code;
227 memmove(file->out_data, file->out_data + ret,
228 file->out_used - ret);
229 file->out_used -= ret;
236 altos_putchar(struct altos_file *file, char c)
240 if (file->out_used == USB_BUF_SIZE) {
241 ret = altos_flush(file);
246 file->out_data[file->out_used++] = c;
248 if (file->out_used == USB_BUF_SIZE)
249 ret = altos_flush(file);
258 altos_fill(struct altos_file *file, int timeout)
267 while (file->in_read == file->in_used) {
269 return LIBALTOS_ERROR;
272 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
273 fd[1].fd = file->pipe[0];
274 fd[1].events = POLLIN;
275 ret = poll(fd, 2, timeout);
277 altos_set_last_posix_error();
278 return LIBALTOS_ERROR;
281 return LIBALTOS_TIMEOUT;
283 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
284 return LIBALTOS_ERROR;
285 if (fd[0].revents & POLLIN)
288 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
290 altos_set_last_posix_error();
291 return LIBALTOS_ERROR;
296 if (ret == 0 && timeout > 0)
297 return LIBALTOS_TIMEOUT;
301 if (file->in_used && 0) {
303 fwrite(file->in_data, 1, file->in_used, stdout);
310 altos_getchar(struct altos_file *file, int timeout)
313 while (file->in_read == file->in_used) {
315 return LIBALTOS_ERROR;
316 ret = altos_fill(file, timeout);
320 return file->in_data[file->in_read++];
323 #endif /* POSIX_TTY */
326 * Scan for Altus Metrum devices by looking through /sys
337 #include <bluetooth/bluetooth.h>
338 #include <bluetooth/hci.h>
339 #include <bluetooth/hci_lib.h>
340 #include <bluetooth/rfcomm.h>
343 cc_fullname (char *dir, char *file)
346 int dlen = strlen (dir);
347 int flen = strlen (file);
350 if (dir[dlen-1] != '/')
352 new = malloc (dlen + slen + flen + 1);
363 cc_basename(char *file)
367 b = strrchr(file, '/');
374 load_string(char *dir, char *file)
376 char *full = cc_fullname(dir, file);
382 f = fopen(full, "r");
386 r = fgets(line, sizeof (line), f);
391 if (r[rlen-1] == '\n')
397 load_hex(char *dir, char *file)
403 line = load_string(dir, file);
406 i = strtol(line, &end, 16);
414 load_dec(char *dir, char *file)
420 line = load_string(dir, file);
423 i = strtol(line, &end, 10);
431 dir_filter_tty_colon(const struct dirent *d)
433 return strncmp(d->d_name, "tty:", 4) == 0;
437 dir_filter_tty(const struct dirent *d)
439 return strncmp(d->d_name, "tty", 3) == 0;
442 struct altos_usbdev {
447 int serial; /* AltOS always uses simple integer serial numbers */
458 struct dirent **namelist;
461 char endpoint_base[20];
467 base = cc_basename(sys);
468 num_configs = load_hex(sys, "bNumConfigurations");
469 num_interfaces = load_hex(sys, "bNumInterfaces");
470 for (config = 1; config <= num_configs; config++) {
471 for (interface = 0; interface < num_interfaces; interface++) {
472 sprintf(endpoint_base, "%s:%d.%d",
473 base, config, interface);
474 endpoint_full = cc_fullname(sys, endpoint_base);
476 /* Check for tty:ttyACMx style names
478 ntty = scandir(endpoint_full, &namelist,
479 dir_filter_tty_colon,
483 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
488 /* Check for tty/ttyACMx style names
490 tty_dir = cc_fullname(endpoint_full, "tty");
492 ntty = scandir(tty_dir, &namelist,
497 tty = cc_fullname("/dev", namelist[0]->d_name);
506 static struct altos_usbdev *
507 usb_scan_device(char *sys)
509 struct altos_usbdev *usbdev;
511 usbdev = calloc(1, sizeof (struct altos_usbdev));
514 usbdev->sys = strdup(sys);
515 usbdev->manufacturer = load_string(sys, "manufacturer");
516 usbdev->product_name = load_string(sys, "product");
517 usbdev->serial = load_dec(sys, "serial");
518 usbdev->idProduct = load_hex(sys, "idProduct");
519 usbdev->idVendor = load_hex(sys, "idVendor");
520 usbdev->tty = usb_tty(sys);
525 usbdev_free(struct altos_usbdev *usbdev)
528 free(usbdev->manufacturer);
529 free(usbdev->product_name);
530 /* this can get used as a return value */
536 #define USB_DEVICES "/sys/bus/usb/devices"
539 dir_filter_dev(const struct dirent *d)
541 const char *n = d->d_name;
549 if (c == '.' && n != d->d_name + 1)
557 struct altos_usbdev **dev;
563 altos_list_start(void)
566 struct dirent **ents;
568 struct altos_usbdev *dev;
569 struct altos_list *devs;
572 devs = calloc(1, sizeof (struct altos_list));
576 n = scandir (USB_DEVICES, &ents,
581 for (e = 0; e < n; e++) {
582 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
583 dev = usb_scan_device(dir);
585 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
587 devs->dev = realloc(devs->dev,
588 (devs->ndev + 1) * sizeof (struct usbdev *));
590 devs->dev = malloc (sizeof (struct usbdev *));
591 devs->dev[devs->ndev++] = dev;
600 altos_list_next(struct altos_list *list, struct altos_device *device)
602 struct altos_usbdev *dev;
603 if (list->current >= list->ndev)
605 dev = list->dev[list->current];
606 strcpy(device->name, dev->product_name);
607 device->vendor = dev->idVendor;
608 device->product = dev->idProduct;
609 strcpy(device->path, dev->tty);
610 device->serial = dev->serial;
616 altos_list_finish(struct altos_list *usbdevs)
622 for (i = 0; i < usbdevs->ndev; i++)
623 usbdev_free(usbdevs->dev[i]);
627 struct altos_bt_list {
635 #define INQUIRY_MAX_RSP 255
637 struct altos_bt_list *
638 altos_bt_list_start(int inquiry_time)
640 struct altos_bt_list *bt_list;
642 bt_list = calloc(1, sizeof (struct altos_bt_list));
646 bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
649 bt_list->dev_id = hci_get_route(NULL);
650 if (bt_list->dev_id < 0)
653 bt_list->sock = hci_open_dev(bt_list->dev_id);
654 if (bt_list->sock < 0)
657 bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
663 if (bt_list->num_rsp < 0)
670 close(bt_list->sock);
681 altos_bt_list_next(struct altos_bt_list *bt_list,
682 struct altos_bt_device *device)
686 if (bt_list->rsp >= bt_list->num_rsp)
689 ii = &bt_list->ii[bt_list->rsp];
690 ba2str(&ii->bdaddr, device->addr);
691 memset(&device->name, '\0', sizeof (device->name));
692 if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
693 sizeof (device->name),
694 device->name, 0) < 0) {
695 strcpy(device->name, "[unknown]");
702 altos_bt_list_finish(struct altos_bt_list *bt_list)
704 close(bt_list->sock);
710 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
712 strncpy(device->name, name, sizeof (device->name));
713 device->name[sizeof(device->name)-1] = '\0';
714 strncpy(device->addr, addr, sizeof (device->addr));
715 device->addr[sizeof(device->addr)-1] = '\0';
719 altos_bt_open(struct altos_bt_device *device)
721 struct sockaddr_rc addr = { 0 };
723 struct altos_file *file;
725 file = calloc(1, sizeof (struct altos_file));
728 file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
730 altos_set_last_posix_error();
734 addr.rc_family = AF_BLUETOOTH;
736 str2ba(device->addr, &addr.rc_bdaddr);
738 status = connect(file->fd,
739 (struct sockaddr *)&addr,
742 altos_set_last_posix_error();
750 file->out_fd = dup(file->fd);
765 #include <IOKitLib.h>
766 #include <IOKit/usb/USBspec.h>
767 #include <sys/param.h>
769 #include <CFNumber.h>
776 io_iterator_t iterator;
780 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
782 CFTypeRef entry_as_string;
785 entry_as_string = IORegistryEntrySearchCFProperty (object,
789 kIORegistryIterateRecursively);
790 if (entry_as_string) {
791 got_string = CFStringGetCString(entry_as_string,
793 kCFStringEncodingASCII);
795 CFRelease(entry_as_string);
803 get_number(io_object_t object, CFStringRef entry, int *result)
805 CFTypeRef entry_as_number;
808 entry_as_number = IORegistryEntrySearchCFProperty (object,
812 kIORegistryIterateRecursively);
813 if (entry_as_number) {
814 got_number = CFNumberGetValue(entry_as_number,
823 PUBLIC struct altos_list *
824 altos_list_start(void)
826 struct altos_list *list = calloc (sizeof (struct altos_list), 1);
827 CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
828 io_iterator_t tdIterator;
829 io_object_t tdObject;
833 ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
834 if (ret != kIOReturnSuccess)
840 altos_list_next(struct altos_list *list, struct altos_device *device)
843 char serial_string[128];
846 object = IOIteratorNext(list->iterator);
850 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
851 !get_number (object, CFSTR(kUSBProductID), &device->product))
853 if (device->vendor != 0xfffe)
855 if (device->product < 0x000a || 0x0013 < device->product)
857 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
858 get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
859 get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
860 device->serial = atoi(serial_string);
867 altos_list_finish(struct altos_list *list)
869 IOObjectRelease (list->iterator);
873 struct altos_bt_list {
880 #define INQUIRY_MAX_RSP 255
882 struct altos_bt_list *
883 altos_bt_list_start(int inquiry_time)
889 altos_bt_list_next(struct altos_bt_list *bt_list,
890 struct altos_bt_device *device)
896 altos_bt_list_finish(struct altos_bt_list *bt_list)
901 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
903 strncpy(device->name, name, sizeof (device->name));
904 device->name[sizeof(device->name)-1] = '\0';
905 strncpy(device->addr, addr, sizeof (device->addr));
906 device->addr[sizeof(device->addr)-1] = '\0';
910 altos_bt_open(struct altos_bt_device *device)
922 #include <setupapi.h>
929 #define USB_BUF_SIZE 64
933 unsigned char out_data[USB_BUF_SIZE];
935 unsigned char in_data[USB_BUF_SIZE];
944 altos_set_last_windows_error(void)
946 DWORD error = GetLastError();
948 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
953 sizeof (message) / sizeof (TCHAR),
955 altos_set_last_error(error, message);
958 PUBLIC struct altos_list *
959 altos_list_start(void)
961 struct altos_list *list = calloc(1, sizeof (struct altos_list));
965 list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
966 DIGCF_ALLCLASSES|DIGCF_PRESENT);
967 if (list->dev_info == INVALID_HANDLE_VALUE) {
968 altos_set_last_windows_error();
977 altos_list_next(struct altos_list *list, struct altos_device *device)
979 SP_DEVINFO_DATA dev_info_data;
982 char friendlyname[256];
986 unsigned int vid, pid;
989 DWORD friendlyname_type;
990 DWORD friendlyname_len;
992 dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
993 while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
998 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
999 DICS_FLAG_GLOBAL, 0, DIREG_DEV,
1001 if (dev_key == INVALID_HANDLE_VALUE) {
1002 altos_set_last_windows_error();
1003 printf("cannot open device registry key\n");
1007 /* Fetch symbolic name for this device and parse out
1008 * the vid/pid/serial info */
1009 symbolic_len = sizeof(symbolic);
1010 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
1011 symbolic, &symbolic_len);
1013 altos_set_last_windows_error();
1014 printf("cannot find SymbolicName value\n");
1015 RegCloseKey(dev_key);
1018 vid = pid = serial = 0;
1019 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
1021 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
1023 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
1025 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
1026 RegCloseKey(dev_key);
1030 /* Fetch the com port name */
1031 port_len = sizeof (port);
1032 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
1034 RegCloseKey(dev_key);
1036 altos_set_last_windows_error();
1037 printf("failed to get PortName\n");
1041 /* Fetch the device description which is the device name,
1042 * with firmware that has unique USB ids */
1043 friendlyname_len = sizeof (friendlyname);
1044 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
1048 (BYTE *)friendlyname,
1049 sizeof(friendlyname),
1052 altos_set_last_windows_error();
1053 printf("Failed to get friendlyname\n");
1056 device->vendor = vid;
1057 device->product = pid;
1058 device->serial = serial;
1059 strcpy(device->name, friendlyname);
1061 strcpy(device->path, (char *) port);
1064 result = GetLastError();
1065 if (result != ERROR_NO_MORE_ITEMS) {
1066 altos_set_last_windows_error();
1067 printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
1073 altos_list_finish(struct altos_list *list)
1075 SetupDiDestroyDeviceInfoList(list->dev_info);
1080 altos_queue_read(struct altos_file *file)
1083 if (file->pend_read)
1084 return LIBALTOS_SUCCESS;
1086 if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
1087 if (GetLastError() != ERROR_IO_PENDING) {
1088 altos_set_last_windows_error();
1089 return LIBALTOS_ERROR;
1091 file->pend_read = TRUE;
1093 file->pend_read = FALSE;
1095 file->in_used = got;
1097 return LIBALTOS_SUCCESS;
1101 altos_wait_read(struct altos_file *file, int timeout)
1106 if (!file->pend_read)
1107 return LIBALTOS_SUCCESS;
1112 ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1115 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
1116 altos_set_last_windows_error();
1117 return LIBALTOS_ERROR;
1119 file->pend_read = FALSE;
1121 file->in_used = got;
1124 return LIBALTOS_TIMEOUT;
1127 return LIBALTOS_ERROR;
1129 return LIBALTOS_SUCCESS;
1133 altos_fill(struct altos_file *file, int timeout)
1137 if (file->in_read < file->in_used)
1138 return LIBALTOS_SUCCESS;
1140 file->in_read = file->in_used = 0;
1142 ret = altos_queue_read(file);
1145 ret = altos_wait_read(file, timeout);
1149 return LIBALTOS_SUCCESS;
1153 altos_flush(struct altos_file *file)
1156 unsigned char *data = file->out_data;
1157 int used = file->out_used;
1161 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1162 if (GetLastError() != ERROR_IO_PENDING) {
1163 altos_set_last_windows_error();
1164 return LIBALTOS_ERROR;
1166 ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1169 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
1170 altos_set_last_windows_error();
1171 return LIBALTOS_ERROR;
1175 altos_set_last_windows_error();
1176 return LIBALTOS_ERROR;
1183 return LIBALTOS_SUCCESS;
1186 PUBLIC struct altos_file *
1187 altos_open(struct altos_device *device)
1189 struct altos_file *file = calloc (1, sizeof (struct altos_file));
1191 DCB dcbSerialParams = {0};
1192 COMMTIMEOUTS timeouts;
1197 strcpy(full_name, "\\\\.\\");
1198 strcat(full_name, device->path);
1199 file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1200 0, NULL, OPEN_EXISTING,
1201 FILE_FLAG_OVERLAPPED, NULL);
1202 if (file->handle == INVALID_HANDLE_VALUE) {
1203 altos_set_last_windows_error();
1207 file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1208 file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1210 timeouts.ReadIntervalTimeout = MAXDWORD;
1211 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1212 timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */
1213 timeouts.WriteTotalTimeoutMultiplier = 0;
1214 timeouts.WriteTotalTimeoutConstant = 0;
1215 SetCommTimeouts(file->handle, &timeouts);
1217 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
1218 if (!GetCommState(file->handle, &dcbSerialParams)) {
1219 altos_set_last_windows_error();
1220 CloseHandle(file->handle);
1224 dcbSerialParams.BaudRate = CBR_9600;
1225 dcbSerialParams.ByteSize = 8;
1226 dcbSerialParams.StopBits = ONESTOPBIT;
1227 dcbSerialParams.Parity = NOPARITY;
1228 if (!SetCommState(file->handle, &dcbSerialParams)) {
1229 altos_set_last_windows_error();
1230 CloseHandle(file->handle);
1239 altos_close(struct altos_file *file)
1241 if (file->handle != INVALID_HANDLE_VALUE) {
1242 CloseHandle(file->handle);
1243 file->handle = INVALID_HANDLE_VALUE;
1248 altos_free(struct altos_file *file)
1255 altos_putchar(struct altos_file *file, char c)
1259 if (file->out_used == USB_BUF_SIZE) {
1260 ret = altos_flush(file);
1264 file->out_data[file->out_used++] = c;
1265 if (file->out_used == USB_BUF_SIZE)
1266 return altos_flush(file);
1267 return LIBALTOS_SUCCESS;
1271 altos_getchar(struct altos_file *file, int timeout)
1274 while (file->in_read == file->in_used) {
1275 if (file->handle == INVALID_HANDLE_VALUE)
1276 return LIBALTOS_ERROR;
1277 ret = altos_fill(file, timeout);
1281 return file->in_data[file->in_read++];
1284 struct altos_bt_list *
1285 altos_bt_list_start(int inquiry_time)
1291 altos_bt_list_next(struct altos_bt_list *bt_list,
1292 struct altos_bt_device *device)
1298 altos_bt_list_finish(struct altos_bt_list *bt_list)
1304 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
1306 strncpy(device->name, name, sizeof (device->name));
1307 device->name[sizeof(device->name)-1] = '\0';
1308 strncpy(device->addr, addr, sizeof (device->addr));
1309 device->addr[sizeof(device->addr)-1] = '\0';
1313 altos_bt_open(struct altos_bt_device *device)