X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Flibaltos%2Flibaltos.c;h=059d2ae93b7e8c7d66776f28931f22b2e25e24ea;hp=7d471f38f756b9bac70947a2c2d5edc413ebfa5f;hb=35d9a8214252dbe79aeb69ae47d2e5c58a654702;hpb=b51497597868a40df039dd3ca11b35a6258bbbb3 diff --git a/ao-tools/libaltos/libaltos.c b/ao-tools/libaltos/libaltos.c index 7d471f38..059d2ae9 100644 --- a/ao-tools/libaltos/libaltos.c +++ b/ao-tools/libaltos/libaltos.c @@ -20,69 +20,46 @@ #include #include -static int -match_dev(char *product, int serial, struct altos_device *device) +#define USE_POLL + +PUBLIC int +altos_init(void) { - struct altos_list *list; - int i; + return LIBALTOS_SUCCESS; +} - list = altos_list_start(); - if (!list) - return 0; - while ((i = altos_list_next(list, device)) != 0) { - if (product && strncmp (product, device->product, strlen(product)) != 0) - continue; - if (serial && serial != device->serial) - continue; - break; - } - altos_list_finish(list); - return i; +PUBLIC void +altos_fini(void) +{ } -int -altos_find_by_arg(char *arg, char *default_product, struct altos_device *device) +#ifdef DARWIN + +#undef USE_POLL + +/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ +static char * +altos_strndup (const char *s, size_t n) { - char *product; - int serial; - char *end; - char *colon; - int ret; + size_t len = strlen (s); + char *ret; - if (arg) - { - /* check for */ - serial = strtol(arg, &end, 0); - if (end != arg) { - if (*end != '\0') - return 0; - product = NULL; - } else { - /* check for : */ - colon = strchr(arg, ':'); - if (colon) { - product = strndup(arg, colon - arg); - serial = strtol(colon + 1, &end, 0); - if (*end != '\0') - return 0; - } else { - product = arg; - serial = 0; - } - } - } else { - product = NULL; - serial = 0; - } - if (!product && default_product) - ret = match_dev(default_product, serial, device); - if (!ret) - ret = match_dev(product, serial, device); - if (product && product != arg) - free(product); - return ret; + if (len <= n) + return strdup (s); + ret = malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; } +#else +#define altos_strndup strndup +#endif + +/* + * Scan for Altus Metrum devices by looking through /sys + */ + #ifdef LINUX #define _GNU_SOURCE @@ -196,7 +173,7 @@ struct altos_usbdev { char *sys; char *tty; char *manufacturer; - char *product; + char *product_name; int serial; /* AltOS always uses simple integer serial numbers */ int idProduct; int idVendor; @@ -266,7 +243,7 @@ usb_scan_device(char *sys) return NULL; usbdev->sys = strdup(sys); usbdev->manufacturer = load_string(sys, "manufacturer"); - usbdev->product = load_string(sys, "product"); + usbdev->product_name = load_string(sys, "product"); usbdev->serial = load_dec(sys, "serial"); usbdev->idProduct = load_hex(sys, "idProduct"); usbdev->idVendor = load_hex(sys, "idVendor"); @@ -279,7 +256,7 @@ usbdev_free(struct altos_usbdev *usbdev) { free(usbdev->sys); free(usbdev->manufacturer); - free(usbdev->product); + free(usbdev->product_name); /* this can get used as a return value */ if (usbdev->tty) free(usbdev->tty); @@ -312,17 +289,6 @@ struct altos_list { int ndev; }; -int -altos_init(void) -{ - return 1; -} - -void -altos_fini(void) -{ -} - struct altos_list * altos_list_start(void) { @@ -346,7 +312,7 @@ altos_list_start(void) dir = cc_fullname(USB_DEVICES, ents[e]->d_name); dev = usb_scan_device(dir); free(dir); - if (dev->idVendor == 0xfffe && dev->tty) { + if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) { if (devs->dev) devs->dev = realloc(devs->dev, devs->ndev + 1 * sizeof (struct usbdev *)); @@ -367,7 +333,9 @@ altos_list_next(struct altos_list *list, struct altos_device *device) if (list->current >= list->ndev) return 0; dev = list->dev[list->current]; - strcpy(device->product, dev->product); + strcpy(device->name, dev->product_name); + device->vendor = dev->idVendor; + device->product = dev->idProduct; strcpy(device->path, dev->tty); device->serial = dev->serial; list->current++; @@ -427,15 +395,25 @@ get_string(io_object_t object, CFStringRef entry, char *result, int result_len) return 0; } -int -altos_init(void) -{ - return 1; -} - -void -altos_fini(void) +static int +get_number(io_object_t object, CFStringRef entry, int *result) { + CFTypeRef entry_as_number; + Boolean got_number; + + entry_as_number = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_number) { + got_number = CFNumberGetValue(entry_as_number, + kCFNumberIntType, + result); + if (got_number) + return 1; + } + return 0; } struct altos_list * @@ -443,19 +421,14 @@ altos_list_start(void) { struct altos_list *list = calloc (sizeof (struct altos_list), 1); CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); - UInt32 vendor = 0xfffe, product = 0x000a; - CFNumberRef vendor_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor); - CFNumberRef product_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product); io_iterator_t tdIterator; io_object_t tdObject; - - CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBVendorID), vendor_ref); - CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBProductID), product_ref); + kern_return_t ret; + int i; - IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); - - CFRelease(vendor_ref); - CFRelease(product_ref); + ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); + if (ret != kIOReturnSuccess) + return NULL; return list; } @@ -470,8 +443,15 @@ altos_list_next(struct altos_list *list, struct altos_device *device) if (!object) return 0; + if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || + !get_number (object, CFSTR(kUSBProductID), &device->product)) + continue; + if (device->vendor != 0xfffe) + continue; + if (device->product < 0x000a || 0x0013 < device->product) + continue; if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && - get_string (object, CFSTR("USB Product Name"), device->product, sizeof (device->product)) && + get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { device->serial = atoi(serial_string); return 1; @@ -500,6 +480,11 @@ altos_list_finish(struct altos_list *list) struct altos_file { int fd; +#ifdef USE_POLL + int pipe[2]; +#else + int out_fd; +#endif unsigned char out_data[USB_BUF_SIZE]; int out_used; unsigned char in_data[USB_BUF_SIZE]; @@ -507,7 +492,7 @@ struct altos_file { int in_read; }; -struct altos_file * +PUBLIC struct altos_file * altos_open(struct altos_device *device) { struct altos_file *file = calloc (sizeof (struct altos_file), 1); @@ -523,221 +508,477 @@ altos_open(struct altos_device *device) free(file); return NULL; } +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->out_fd < 0) { + perror(device->path); + close(file->fd); + free(file); + return NULL; + } +#endif ret = tcgetattr(file->fd, &term); if (ret < 0) { perror("tcgetattr"); close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif free(file); return NULL; } cfmakeraw(&term); +#ifdef USE_POLL + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; +#else term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 1; +#endif ret = tcsetattr(file->fd, TCSAFLUSH, &term); if (ret < 0) { perror("tcsetattr"); close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif free(file); return NULL; } return file; } -void +PUBLIC void altos_close(struct altos_file *file) { - close(file->fd); + if (file->fd != -1) { + int fd = file->fd; + file->fd = -1; +#ifdef USE_POLL + write(file->pipe[1], "\r", 1); +#else + close(file->out_fd); + file->out_fd = -1; +#endif + close(fd); + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); free(file); } -int +PUBLIC int +altos_flush(struct altos_file *file) +{ + while (file->out_used) { + int ret; + + if (file->fd < 0) + return -EBADF; +#ifdef USE_POLL + ret = write (file->fd, file->out_data, file->out_used); +#else + ret = write (file->out_fd, file->out_data, file->out_used); +#endif + if (ret < 0) + return -errno; + if (ret) { + memmove(file->out_data, file->out_data + ret, + file->out_used - ret); + file->out_used -= ret; + } + } + return 0; +} + +PUBLIC int altos_putchar(struct altos_file *file, char c) { int ret; if (file->out_used == USB_BUF_SIZE) { ret = altos_flush(file); - if (ret) + if (ret) { return ret; + } } file->out_data[file->out_used++] = c; + ret = 0; if (file->out_used == USB_BUF_SIZE) - return altos_flush(file); + ret = altos_flush(file); return 0; } -int -altos_flush(struct altos_file *file) +#ifdef USE_POLL +#include +#endif + +static int +altos_fill(struct altos_file *file, int timeout) { - while (file->out_used) { - int ret; + int ret; +#ifdef USE_POLL + struct pollfd fd[2]; +#endif - ret = write (file->fd, file->out_data, file->out_used); - if (ret < 0) - return -errno; - if (ret) { - memmove(file->out_data, file->out_data + ret, - file->out_used - ret); - file->out_used -= ret; + if (timeout == 0) + timeout = -1; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; +#ifdef USE_POLL + fd[0].fd = file->fd; + fd[0].events = POLLIN; + fd[1].fd = file->pipe[0]; + fd[1].events = POLLIN; + ret = poll(fd, 2, timeout); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + if (ret == 0) + return LIBALTOS_TIMEOUT; + if (fd[0].revents & POLLIN) +#endif + { + ret = read(file->fd, file->in_data, USB_BUF_SIZE); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + file->in_read = 0; + file->in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif } } + return 0; } -int +PUBLIC int altos_getchar(struct altos_file *file, int timeout) { + int ret; while (file->in_read == file->in_used) { - int ret; - - altos_flush(file); - ret = read(file->fd, file->in_data, USB_BUF_SIZE); - if (ret < 0) - return -errno; - file->in_read = 0; - file->in_used = ret; + if (file->fd < 0) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; } return file->in_data[file->in_read++]; } #endif /* POSIX_TTY */ -#ifdef USE_LIBUSB -#include -#include +#ifdef WINDOWS + #include -#include +#include +#include -libusb_context *usb_context; +struct altos_list { + HDEVINFO dev_info; + int index; +}; + +#define USB_BUF_SIZE 64 -int altos_init(void) +struct altos_file { + HANDLE handle; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; + OVERLAPPED ov_read; + BOOL pend_read; + OVERLAPPED ov_write; +}; + +PUBLIC struct altos_list * +altos_list_start(void) { - int ret; - ret = libusb_init(&usb_context); - if (ret) - return ret; - libusb_set_debug(usb_context, 3); - return 0; + struct altos_list *list = calloc(1, sizeof (struct altos_list)); + + if (!list) + return NULL; + list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, + DIGCF_ALLCLASSES|DIGCF_PRESENT); + if (list->dev_info == INVALID_HANDLE_VALUE) { + printf("SetupDiGetClassDevs failed %d\n", GetLastError()); + free(list); + return NULL; + } + list->index = 0; + return list; } -void altos_fini(void) +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) { - libusb_exit(usb_context); - usb_context = NULL; -} + SP_DEVINFO_DATA dev_info_data; + char port[128]; + DWORD port_len; + char friendlyname[256]; + char symbolic[256]; + DWORD symbolic_len; + HKEY dev_key; + int vid, pid; + int serial; + HRESULT result; + DWORD friendlyname_type; + DWORD friendlyname_len; + + dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); + while(SetupDiEnumDeviceInfo(list->dev_info, list->index, + &dev_info_data)) + { + list->index++; -static libusb_device **list; -static ssize_t num, current; + dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, + DICS_FLAG_GLOBAL, 0, DIREG_DEV, + KEY_READ); + if (dev_key == INVALID_HANDLE_VALUE) { + printf("cannot open device registry key\n"); + continue; + } -int altos_list_start(void) -{ - if (list) - altos_list_finish(); - current = 0; - num = libusb_get_device_list(usb_context, &list); - if (num == 0) { - current = num = 0; - list = NULL; - return 0; - } - return 1; -} + /* Fetch symbolic name for this device and parse out + * the vid/pid/serial info */ + symbolic_len = sizeof(symbolic); + result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, + symbolic, &symbolic_len); + if (result != 0) { + printf("cannot find SymbolicName value\n"); + RegCloseKey(dev_key); + continue; + } + vid = pid = serial = 0; + sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1, + "%04X", &vid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, + "%d", &serial); + if (!USB_IS_ALTUSMETRUM(vid, pid)) { + RegCloseKey(dev_key); + continue; + } -int altos_list_next(struct altos_device *device) -{ - while (current < num) { - struct libusb_device_descriptor descriptor; - libusb_device *usb_device = list[current++]; - - if (libusb_get_device_descriptor(usb_device, &descriptor) == 0) { - if (descriptor.idVendor == 0xfffe) - { - libusb_device_handle *handle; - if (libusb_open(usb_device, &handle) == 0) { - char serial_number[256]; - libusb_get_string_descriptor_ascii(handle, descriptor.iProduct, - device->product, - sizeof(device->product)); - libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber, - serial_number, - sizeof (serial_number)); - libusb_close(handle); - device->serial = atoi(serial_number); - device->device = usb_device; - return 1; - } - } + /* Fetch the com port name */ + port_len = sizeof (port); + result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, + port, &port_len); + RegCloseKey(dev_key); + if (result != 0) { + printf("failed to get PortName\n"); + continue; + } + + /* Fetch the device description which is the device name, + * with firmware that has unique USB ids */ + friendlyname_len = sizeof (friendlyname); + if(!SetupDiGetDeviceRegistryProperty(list->dev_info, + &dev_info_data, + SPDRP_FRIENDLYNAME, + &friendlyname_type, + (BYTE *)friendlyname, + sizeof(friendlyname), + &friendlyname_len)) + { + printf("Failed to get friendlyname\n"); + continue; } + device->vendor = vid; + device->product = pid; + device->serial = serial; + strcpy(device->name, friendlyname); + + strcpy(device->path, port); + return 1; } + result = GetLastError(); + if (result != ERROR_NO_MORE_ITEMS) + printf ("SetupDiEnumDeviceInfo failed error %d\n", result); return 0; } -void altos_list_finish(void) +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + SetupDiDestroyDeviceInfoList(list->dev_info); + free(list); +} + +static int +altos_queue_read(struct altos_file *file) { - if (list) { - libusb_free_device_list(list, 1); - list = NULL; + DWORD got; + if (file->pend_read) + return LIBALTOS_SUCCESS; + + if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { + if (GetLastError() != ERROR_IO_PENDING) + return LIBALTOS_ERROR; + file->pend_read = TRUE; + } else { + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; } + return LIBALTOS_SUCCESS; } -#define USB_BUF_SIZE 64 +static int +altos_wait_read(struct altos_file *file, int timeout) +{ + DWORD ret; + DWORD got; -struct altos_file { - struct libusb_device *device; - struct libusb_device_handle *handle; - int out_ep; - int out_size; - int in_ep; - int in_size; - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; -}; + if (!file->pend_read) + return LIBALTOS_SUCCESS; -struct altos_file * -altos_open(struct altos_device *device) + if (!timeout) + timeout = INFINITE; + + ret = WaitForSingleObject(file->ov_read.hEvent, timeout); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) + return LIBALTOS_ERROR; + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; + break; + case WAIT_TIMEOUT: + return LIBALTOS_TIMEOUT; + break; + default: + return LIBALTOS_ERROR; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_fill(struct altos_file *file, int timeout) { - struct altos_file *file; - struct libusb_device_handle *handle; - if (libusb_open(device->device, &handle) == 0) { - int ret; + int ret; - ret = libusb_claim_interface(handle, 1); -#if 0 - if (ret) { - libusb_close(handle); - return NULL; - } -#endif - ret = libusb_detach_kernel_driver(handle, 1); -#if 0 - if (ret) { - libusb_close(handle); - return NULL; + if (file->in_read < file->in_used) + return LIBALTOS_SUCCESS; + + file->in_read = file->in_used = 0; + + ret = altos_queue_read(file); + if (ret) + return ret; + ret = altos_wait_read(file, timeout); + if (ret) + return ret; + + return LIBALTOS_SUCCESS; +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + DWORD put; + char *data = file->out_data; + char used = file->out_used; + DWORD ret; + + while (used) { + if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { + if (GetLastError() != ERROR_IO_PENDING) + return LIBALTOS_ERROR; + ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) + return LIBALTOS_ERROR; + break; + default: + return LIBALTOS_ERROR; + } } -#endif + data += put; + used -= put; + } + file->out_used = 0; + return LIBALTOS_SUCCESS; +} - file = calloc(sizeof (struct altos_file), 1); - file->device = libusb_ref_device(device->device); - file->handle = handle; - /* XXX should get these from the endpoint descriptors */ - file->out_ep = 4 | LIBUSB_ENDPOINT_OUT; - file->out_size = 64; - file->in_ep = 5 | LIBUSB_ENDPOINT_IN; - file->in_size = 64; +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (1, sizeof (struct altos_file)); + char full_name[64]; + DCB dcbSerialParams = {0}; + COMMTIMEOUTS timeouts; - return file; + if (!file) + return NULL; + + strcpy(full_name, "\\\\.\\"); + strcat(full_name, device->path); + file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + if (file->handle == INVALID_HANDLE_VALUE) { + free(file); + return NULL; } - return NULL; + file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(file->handle, &timeouts); + + dcbSerialParams.DCBlength = sizeof(dcbSerialParams); + if (!GetCommState(file->handle, &dcbSerialParams)) { + CloseHandle(file->handle); + free(file); + return NULL; + } + dcbSerialParams.BaudRate = CBR_9600; + dcbSerialParams.ByteSize = 8; + dcbSerialParams.StopBits = ONESTOPBIT; + dcbSerialParams.Parity = NOPARITY; + if (!SetCommState(file->handle, &dcbSerialParams)) { + CloseHandle(file->handle); + free(file); + return NULL; + } + + return file; } -void +PUBLIC void altos_close(struct altos_file *file) { - libusb_close(file->handle); - libusb_unref_device(file->device); - file->handle = NULL; + if (file->handle != INVALID_HANDLE_VALUE) { + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); free(file); } @@ -746,60 +987,29 @@ altos_putchar(struct altos_file *file, char c) { int ret; - if (file->out_used == file->out_size) { + if (file->out_used == USB_BUF_SIZE) { ret = altos_flush(file); if (ret) return ret; } file->out_data[file->out_used++] = c; - if (file->out_used == file->out_size) + if (file->out_used == USB_BUF_SIZE) return altos_flush(file); - return 0; -} - -int -altos_flush(struct altos_file *file) -{ - while (file->out_used) { - int transferred; - int ret; - - ret = libusb_bulk_transfer(file->handle, - file->out_ep, - file->out_data, - file->out_used, - &transferred, - 0); - if (ret) - return ret; - if (transferred) { - memmove(file->out_data, file->out_data + transferred, - file->out_used - transferred); - file->out_used -= transferred; - } - } + return LIBALTOS_SUCCESS; } int altos_getchar(struct altos_file *file, int timeout) { + int ret; while (file->in_read == file->in_used) { - int ret; - int transferred; - - altos_flush(file); - ret = libusb_bulk_transfer(file->handle, - file->in_ep, - file->in_data, - file->in_size, - &transferred, - (unsigned int) timeout); + if (file->handle == INVALID_HANDLE_VALUE) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); if (ret) return ret; - file->in_read = 0; - file->in_used = transferred; } return file->in_data[file->in_read++]; } -#endif /* USE_LIBUSB */ +#endif