/* * Copyright © 2010 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "libaltos.h" #include #include #include #define BLUETOOTH_PRODUCT_TELEBT "TeleBT" #define USE_POLL PUBLIC int altos_init(void) { return LIBALTOS_SUCCESS; } PUBLIC void altos_fini(void) { } static struct altos_error last_error; static void altos_set_last_error(int code, char *string) { last_error.code = code; strncpy(last_error.string, string, sizeof (last_error.string) -1); last_error.string[sizeof(last_error.string)-1] = '\0'; } PUBLIC void altos_get_last_error(struct altos_error *error) { *error = last_error; } #ifdef DARWIN #include #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) { size_t len = strlen (s); char *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 #ifdef POSIX_TTY #include #include #include #include #include #define USB_BUF_SIZE 64 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]; int in_used; int in_read; }; static void altos_set_last_posix_error(void) { altos_set_last_error(errno, strerror(errno)); } PUBLIC struct altos_file * altos_open(struct altos_device *device) { struct altos_file *file = calloc (sizeof (struct altos_file), 1); int ret; struct termios term; if (!file) { altos_set_last_posix_error(); return NULL; } // altos_set_last_error(12, "yeah yeah, failed again"); // free(file); // return NULL; file->fd = open(device->path, O_RDWR | O_NOCTTY); if (file->fd < 0) { altos_set_last_posix_error(); 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) { altos_set_last_posix_error(); close(file->fd); free(file); return NULL; } #endif ret = tcgetattr(file->fd, &term); if (ret < 0) { altos_set_last_posix_error(); close(file->fd); #ifndef USE_POLL close(file->out_fd); #endif free(file); return NULL; } cfmakeraw(&term); cfsetospeed(&term, B9600); cfsetispeed(&term, B9600); #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) { altos_set_last_posix_error(); close(file->fd); #ifndef USE_POLL close(file->out_fd); #endif free(file); return NULL; } return file; } PUBLIC void altos_close(struct altos_file *file) { 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); } PUBLIC int altos_flush(struct altos_file *file) { if (file->out_used && 0) { printf ("flush \""); fwrite(file->out_data, 1, file->out_used, stdout); printf ("\"\n"); } 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) { altos_set_last_posix_error(); return -last_error.code; } 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) { return ret; } } file->out_data[file->out_used++] = c; ret = 0; if (file->out_used == USB_BUF_SIZE) ret = altos_flush(file); return ret; } #ifdef USE_POLL #include #endif static int altos_fill(struct altos_file *file, int timeout) { int ret; #ifdef USE_POLL struct pollfd fd[2]; #endif 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|POLLERR|POLLHUP|POLLNVAL; fd[1].fd = file->pipe[0]; fd[1].events = POLLIN; ret = poll(fd, 2, timeout); if (ret < 0) { altos_set_last_posix_error(); return LIBALTOS_ERROR; } if (ret == 0) return LIBALTOS_TIMEOUT; if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) return LIBALTOS_ERROR; if (fd[0].revents & POLLIN) #endif { ret = read(file->fd, file->in_data, USB_BUF_SIZE); if (ret < 0) { altos_set_last_posix_error(); return LIBALTOS_ERROR; } file->in_read = 0; file->in_used = ret; #ifndef USE_POLL if (ret == 0 && timeout > 0) return LIBALTOS_TIMEOUT; #endif } } if (file->in_used && 0) { printf ("fill \""); fwrite(file->in_data, 1, file->in_used, stdout); printf ("\"\n"); } return 0; } PUBLIC int altos_getchar(struct altos_file *file, int timeout) { int ret; while (file->in_read == file->in_used) { 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 */ /* * Scan for Altus Metrum devices by looking through /sys */ #ifdef LINUX #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static char * cc_fullname (char *dir, char *file) { char *new; int dlen = strlen (dir); int flen = strlen (file); int slen = 0; if (dir[dlen-1] != '/') slen = 1; new = malloc (dlen + slen + flen + 1); if (!new) return 0; strcpy(new, dir); if (slen) strcat (new, "/"); strcat(new, file); return new; } static char * cc_basename(char *file) { char *b; b = strrchr(file, '/'); if (!b) return file; return b + 1; } static char * load_string(char *dir, char *file) { char *full = cc_fullname(dir, file); char line[4096]; char *r; FILE *f; int rlen; f = fopen(full, "r"); free(full); if (!f) return NULL; r = fgets(line, sizeof (line), f); fclose(f); if (!r) return NULL; rlen = strlen(r); if (r[rlen-1] == '\n') r[rlen-1] = '\0'; return strdup(r); } static int load_hex(char *dir, char *file) { char *line; char *end; long i; line = load_string(dir, file); if (!line) return -1; i = strtol(line, &end, 16); free(line); if (end == line) return -1; return i; } static int load_dec(char *dir, char *file) { char *line; char *end; long i; line = load_string(dir, file); if (!line) return -1; i = strtol(line, &end, 10); free(line); if (end == line) return -1; return i; } static int dir_filter_tty_colon(const struct dirent *d) { return strncmp(d->d_name, "tty:", 4) == 0; } static int dir_filter_tty(const struct dirent *d) { return strncmp(d->d_name, "tty", 3) == 0; } struct altos_usbdev { char *sys; char *tty; char *manufacturer; char *product_name; int serial; /* AltOS always uses simple integer serial numbers */ int idProduct; int idVendor; }; static char * usb_tty(char *sys) { char *base; int num_configs; int config; struct dirent **namelist; int interface; int num_interfaces; char endpoint_base[20]; char *endpoint_full; char *tty_dir; int ntty; char *tty; base = cc_basename(sys); num_configs = load_hex(sys, "bNumConfigurations"); num_interfaces = load_hex(sys, "bNumInterfaces"); for (config = 1; config <= num_configs; config++) { for (interface = 0; interface < num_interfaces; interface++) { sprintf(endpoint_base, "%s:%d.%d", base, config, interface); endpoint_full = cc_fullname(sys, endpoint_base); /* Check for tty:ttyACMx style names */ ntty = scandir(endpoint_full, &namelist, dir_filter_tty_colon, alphasort); if (ntty > 0) { free(endpoint_full); tty = cc_fullname("/dev", namelist[0]->d_name + 4); free(namelist); return tty; } /* Check for tty/ttyACMx style names */ tty_dir = cc_fullname(endpoint_full, "tty"); ntty = scandir(tty_dir, &namelist, dir_filter_tty, alphasort); free (tty_dir); if (ntty > 0) { tty = cc_fullname("/dev", namelist[0]->d_name); free(endpoint_full); free(namelist); return tty; } /* Check for ttyACMx style names */ ntty = scandir(endpoint_full, &namelist, dir_filter_tty, alphasort); free(endpoint_full); if (ntty > 0) { tty = cc_fullname("/dev", namelist[0]->d_name); free(namelist); return tty; } } } return NULL; } static struct altos_usbdev * usb_scan_device(char *sys) { struct altos_usbdev *usbdev; char *tty; tty = usb_tty(sys); if (!tty) return NULL; usbdev = calloc(1, sizeof (struct altos_usbdev)); if (!usbdev) return NULL; usbdev->sys = strdup(sys); usbdev->manufacturer = load_string(sys, "manufacturer"); 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"); usbdev->tty = tty; return usbdev; } static void usbdev_free(struct altos_usbdev *usbdev) { free(usbdev->sys); free(usbdev->manufacturer); free(usbdev->product_name); /* this can get used as a return value */ if (usbdev->tty) free(usbdev->tty); free(usbdev); } #define USB_DEVICES "/sys/bus/usb/devices" static int dir_filter_dev(const struct dirent *d) { const char *n = d->d_name; char c; while ((c = *n++)) { if (isdigit(c)) continue; if (c == '-') continue; if (c == '.' && n != d->d_name + 1) continue; return 0; } return 1; } struct altos_list { struct altos_usbdev **dev; int current; int ndev; }; struct altos_list * altos_list_start(void) { int e; struct dirent **ents; char *dir; struct altos_usbdev *dev; struct altos_list *devs; int n; devs = calloc(1, sizeof (struct altos_list)); if (!devs) return NULL; n = scandir (USB_DEVICES, &ents, dir_filter_dev, alphasort); if (!n) return 0; for (e = 0; e < n; e++) { dir = cc_fullname(USB_DEVICES, ents[e]->d_name); dev = usb_scan_device(dir); if (!dev) continue; free(dir); if (devs->dev) devs->dev = realloc(devs->dev, (devs->ndev + 1) * sizeof (struct usbdev *)); else devs->dev = malloc (sizeof (struct usbdev *)); devs->dev[devs->ndev++] = dev; } free(ents); devs->current = 0; return devs; } PUBLIC struct altos_list * altos_ftdi_list_start(void) { return altos_list_start(); } int altos_list_next(struct altos_list *list, struct altos_device *device) { struct altos_usbdev *dev; if (list->current >= list->ndev) { return 0; } dev = list->dev[list->current]; 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++; return 1; } void altos_list_finish(struct altos_list *usbdevs) { int i; if (!usbdevs) return; for (i = 0; i < usbdevs->ndev; i++) usbdev_free(usbdevs->dev[i]); free(usbdevs); } #include static void *libbt; static int bt_initialized; static int init_bt(void) { if (!bt_initialized) { bt_initialized = 1; libbt = dlopen("libbluetooth.so.3", RTLD_LAZY); if (!libbt) printf("failed to find bluetooth library\n"); } return libbt != NULL; } #define join(a,b) a ## b #define bt_func(name, ret, fail, formals, actuals) \ static ret join(altos_, name) formals { \ static ret (*name) formals; \ if (!init_bt()) return fail; \ name = dlsym(libbt, #name); \ if (!name) return fail; \ return name actuals; \ } bt_func(ba2str, int, -1, (const bdaddr_t *ba, char *str), (ba, str)) #define ba2str altos_ba2str bt_func(str2ba, int, -1, (const char *str, bdaddr_t *ba), (str, ba)) #define str2ba altos_str2ba bt_func(hci_read_remote_name, int, -1, (int sock, const bdaddr_t *ba, int len, char *name, int timeout), (sock, ba, len, name, timeout)) #define hci_read_remote_name altos_hci_read_remote_name bt_func(hci_open_dev, int, -1, (int dev_id), (dev_id)) #define hci_open_dev altos_hci_open_dev bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr)) #define hci_get_route altos_hci_get_route bt_func(hci_inquiry, int, -1, (int adapter_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **devs, long flags), (adapter_id, len, max_rsp, lap, devs, flags)) #define hci_inquiry altos_hci_inquiry struct altos_bt_list { inquiry_info *ii; int sock; int dev_id; int rsp; int num_rsp; }; #define INQUIRY_MAX_RSP 255 struct altos_bt_list * altos_bt_list_start(int inquiry_time) { struct altos_bt_list *bt_list; bt_list = calloc(1, sizeof (struct altos_bt_list)); if (!bt_list) goto no_bt_list; bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); if (!bt_list->ii) goto no_ii; bt_list->dev_id = hci_get_route(NULL); if (bt_list->dev_id < 0) goto no_dev_id; bt_list->sock = hci_open_dev(bt_list->dev_id); if (bt_list->sock < 0) goto no_sock; bt_list->num_rsp = hci_inquiry(bt_list->dev_id, inquiry_time, INQUIRY_MAX_RSP, NULL, &bt_list->ii, IREQ_CACHE_FLUSH); if (bt_list->num_rsp < 0) goto no_rsp; bt_list->rsp = 0; return bt_list; no_rsp: close(bt_list->sock); no_sock: no_dev_id: free(bt_list->ii); no_ii: free(bt_list); no_bt_list: return NULL; } int altos_bt_list_next(struct altos_bt_list *bt_list, struct altos_bt_device *device) { inquiry_info *ii; if (bt_list->rsp >= bt_list->num_rsp) return 0; ii = &bt_list->ii[bt_list->rsp]; if (ba2str(&ii->bdaddr, device->addr) < 0) return 0; memset(&device->name, '\0', sizeof (device->name)); if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, sizeof (device->name), device->name, 0) < 0) { strcpy(device->name, "[unknown]"); } bt_list->rsp++; return 1; } void altos_bt_list_finish(struct altos_bt_list *bt_list) { close(bt_list->sock); free(bt_list->ii); free(bt_list); } void altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) { strncpy(device->name, name, sizeof (device->name)); device->name[sizeof(device->name)-1] = '\0'; strncpy(device->addr, addr, sizeof (device->addr)); device->addr[sizeof(device->addr)-1] = '\0'; } struct altos_file * altos_bt_open(struct altos_bt_device *device) { struct sockaddr_rc addr = { 0 }; int status, i; struct altos_file *file; file = calloc(1, sizeof (struct altos_file)); if (!file) { errno = ENOMEM; altos_set_last_posix_error(); goto no_file; } addr.rc_family = AF_BLUETOOTH; addr.rc_channel = 1; if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { altos_set_last_posix_error(); goto no_sock; } for (i = 0; i < 5; i++) { file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (file->fd < 0) { altos_set_last_posix_error(); goto no_sock; } status = connect(file->fd, (struct sockaddr *)&addr, sizeof(addr)); if (status >= 0 || errno != EBUSY) break; close(file->fd); usleep(100 * 1000); } if (status < 0) { altos_set_last_posix_error(); goto no_link; } usleep(100 * 1000); #ifdef USE_POLL pipe(file->pipe); #else file->out_fd = dup(file->fd); #endif return file; no_link: close(file->fd); no_sock: free(file); no_file: return NULL; } #endif #ifdef DARWIN #include #include #include #include #include #include #include #include #include struct altos_list { io_iterator_t iterator; int ftdi; }; static int get_string(io_object_t object, CFStringRef entry, char *result, int result_len) { CFTypeRef entry_as_string; Boolean got_string; entry_as_string = IORegistryEntrySearchCFProperty (object, kIOServicePlane, entry, kCFAllocatorDefault, kIORegistryIterateRecursively); if (entry_as_string) { got_string = CFStringGetCString(entry_as_string, result, result_len, kCFStringEncodingASCII); CFRelease(entry_as_string); if (got_string) return 1; } return 0; } 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; } PUBLIC struct altos_list * altos_list_start(void) { struct altos_list *list = calloc (sizeof (struct altos_list), 1); CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); io_iterator_t tdIterator; io_object_t tdObject; kern_return_t ret; int i; ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); if (ret != kIOReturnSuccess) { free(list); return NULL; } list->ftdi = 0; return list; } PUBLIC struct altos_list * altos_ftdi_list_start(void) { struct altos_list *list = altos_list_start(); if (list) list->ftdi = 1; return list; } PUBLIC int altos_list_next(struct altos_list *list, struct altos_device *device) { io_object_t object; char serial_string[128]; for (;;) { object = IOIteratorNext(list->iterator); if (!object) return 0; if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || !get_number (object, CFSTR(kUSBProductID), &device->product)) continue; if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && 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; } } } PUBLIC void altos_list_finish(struct altos_list *list) { IOObjectRelease (list->iterator); free(list); } struct altos_bt_list { int sock; int dev_id; int rsp; int num_rsp; }; #define INQUIRY_MAX_RSP 255 struct altos_bt_list * altos_bt_list_start(int inquiry_time) { return NULL; } int altos_bt_list_next(struct altos_bt_list *bt_list, struct altos_bt_device *device) { return 0; } void altos_bt_list_finish(struct altos_bt_list *bt_list) { } void altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) { strncpy(device->name, name, sizeof (device->name)); device->name[sizeof(device->name)-1] = '\0'; strncpy(device->addr, addr, sizeof (device->addr)); device->addr[sizeof(device->addr)-1] = '\0'; } struct altos_file * altos_bt_open(struct altos_bt_device *device) { return NULL; } #endif #ifdef WINDOWS #include #include #include struct altos_list { HDEVINFO dev_info; int index; int ftdi; }; #define USB_BUF_SIZE 64 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; }; #include static void log_message(char *fmt, ...) { static FILE *log = NULL; va_list a; if (!log) log = fopen("\\temp\\altos.txt", "w"); if (log) { SYSTEMTIME time; GetLocalTime(&time); fprintf (log, "%4d-%02d-%02d %2d:%02d:%02d. ", time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); va_start(a, fmt); vfprintf(log, fmt, a); va_end(a); fflush(log); } } static void _altos_set_last_windows_error(char *file, int line) { DWORD error = GetLastError(); TCHAR message[1024]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, error, 0, message, sizeof (message) / sizeof (TCHAR), NULL); if (error != ERROR_SUCCESS) log_message ("%s:%d %s\n", file, line, message); altos_set_last_error(error, message); } #define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__) PUBLIC struct altos_list * altos_list_start(void) { 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) { altos_set_last_windows_error(); free(list); return NULL; } list->index = 0; list->ftdi = 0; return list; } PUBLIC struct altos_list * altos_ftdi_list_start(void) { struct altos_list *list = calloc(1, sizeof (struct altos_list)); if (!list) return NULL; list->dev_info = SetupDiGetClassDevs(NULL, "FTDIBUS", NULL, DIGCF_ALLCLASSES|DIGCF_PRESENT); if (list->dev_info == INVALID_HANDLE_VALUE) { altos_set_last_windows_error(); free(list); return NULL; } list->index = 0; list->ftdi = 1; return list; } PUBLIC int altos_list_next(struct altos_list *list, struct altos_device *device) { SP_DEVINFO_DATA dev_info_data; BYTE port[128]; DWORD port_len; char friendlyname[256]; BYTE symbolic[256]; DWORD symbolic_len; HKEY dev_key; unsigned 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++; dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (dev_key == INVALID_HANDLE_VALUE) { altos_set_last_windows_error(); continue; } if (list->ftdi) { vid = 0x0403; pid = 0x6015; serial = 0; } else { /* 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) { altos_set_last_windows_error(); RegCloseKey(dev_key); continue; } vid = pid = serial = 0; sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1, "%04X", &vid); sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, "%04X", &pid); sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, "%d", &serial); } /* 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) { altos_set_last_windows_error(); 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)) { altos_set_last_windows_error(); continue; } device->vendor = vid; device->product = pid; device->serial = serial; strcpy(device->name, friendlyname); strcpy(device->path, (char *) port); return 1; } result = GetLastError(); if (result != ERROR_NO_MORE_ITEMS) altos_set_last_windows_error(); return 0; } PUBLIC void altos_list_finish(struct altos_list *list) { SetupDiDestroyDeviceInfoList(list->dev_info); free(list); } static int altos_queue_read(struct altos_file *file) { 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) { altos_set_last_windows_error(); return LIBALTOS_ERROR; } file->pend_read = TRUE; } else { file->pend_read = FALSE; file->in_read = 0; file->in_used = got; } return LIBALTOS_SUCCESS; } static int altos_wait_read(struct altos_file *file, int timeout) { DWORD ret; DWORD got; if (!file->pend_read) return LIBALTOS_SUCCESS; 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)) { altos_set_last_windows_error(); return LIBALTOS_ERROR; } file->pend_read = FALSE; file->in_read = 0; file->in_used = got; break; case WAIT_TIMEOUT: return LIBALTOS_TIMEOUT; break; default: altos_set_last_windows_error(); return LIBALTOS_ERROR; } return LIBALTOS_SUCCESS; } static int altos_fill(struct altos_file *file, int timeout) { int ret; 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; unsigned char *data = file->out_data; int used = file->out_used; DWORD ret; while (used) { if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { if (GetLastError() != ERROR_IO_PENDING) { altos_set_last_windows_error(); 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)) { altos_set_last_windows_error(); return LIBALTOS_ERROR; } break; default: altos_set_last_windows_error(); return LIBALTOS_ERROR; } } data += put; used -= put; } file->out_used = 0; return LIBALTOS_SUCCESS; } static HANDLE open_serial(char *full_name) { HANDLE handle; DCB dcb; handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { altos_set_last_windows_error(); return INVALID_HANDLE_VALUE; } if (!GetCommState(handle, &dcb)) { altos_set_last_windows_error(); CloseHandle(handle); return INVALID_HANDLE_VALUE; } dcb.BaudRate = CBR_9600; dcb.fBinary = TRUE; dcb.fParity = FALSE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fDsrSensitivity = FALSE; dcb.fTXContinueOnXoff = FALSE; dcb.fOutX = FALSE; dcb.fInX = FALSE; dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fRtsControl = RTS_CONTROL_ENABLE; dcb.fAbortOnError = FALSE; dcb.XonLim = 10; dcb.XoffLim = 10; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.XonChar = 17; dcb.XoffChar = 19; #if 0 dcb.ErrorChar = 0; dcb.EofChar = 0; dcb.EvtChar = 0; #endif if (!SetCommState(handle, &dcb)) { altos_set_last_windows_error(); CloseHandle(handle); return INVALID_HANDLE_VALUE; } return handle; } PUBLIC struct altos_file * altos_open(struct altos_device *device) { struct altos_file *file = calloc (1, sizeof (struct altos_file)); char full_name[64]; COMMTIMEOUTS timeouts; int i; if (!file) return NULL; strcpy(full_name, "\\\\.\\"); strcat(full_name, device->path); file->handle = INVALID_HANDLE_VALUE; for (i = 0; i < 5; i++) { file->handle = open_serial(full_name); if (file->handle != INVALID_HANDLE_VALUE) break; altos_set_last_windows_error(); Sleep(100); } if (file->handle == INVALID_HANDLE_VALUE) { free(file); return NULL; } /* The FTDI driver doesn't appear to work right unless you open it twice */ if (device->vendor == 0x0403) { CloseHandle(file->handle); file->handle = open_serial(full_name); if (file->handle == INVALID_HANDLE_VALUE) { free(file); return NULL; } } timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; SetCommTimeouts(file->handle, &timeouts); file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); return file; } PUBLIC void altos_close(struct altos_file *file) { HANDLE handle = file->handle; if (handle != INVALID_HANDLE_VALUE) { HANDLE ov_read = file->ov_read.hEvent; HANDLE ov_write = file->ov_write.hEvent; file->handle = INVALID_HANDLE_VALUE; file->ov_read.hEvent = INVALID_HANDLE_VALUE; file->ov_write.hEvent = INVALID_HANDLE_VALUE; PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); Sleep(100); CloseHandle(handle); file->handle = INVALID_HANDLE_VALUE; CloseHandle(ov_read); CloseHandle(ov_write); } } PUBLIC void altos_free(struct altos_file *file) { altos_close(file); free(file); } 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) return ret; } file->out_data[file->out_used++] = c; if (file->out_used == USB_BUF_SIZE) return altos_flush(file); return LIBALTOS_SUCCESS; } PUBLIC int altos_getchar(struct altos_file *file, int timeout) { int ret; while (file->in_read == file->in_used) { if (file->handle == INVALID_HANDLE_VALUE) { altos_set_last_windows_error(); return LIBALTOS_ERROR; } ret = altos_fill(file, timeout); if (ret) return ret; } return file->in_data[file->in_read++]; } struct altos_bt_list * altos_bt_list_start(int inquiry_time) { return NULL; } int altos_bt_list_next(struct altos_bt_list *bt_list, struct altos_bt_device *device) { return 0; } void altos_bt_list_finish(struct altos_bt_list *bt_list) { free(bt_list); } void altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) { strncpy(device->name, name, sizeof (device->name)); device->name[sizeof(device->name)-1] = '\0'; strncpy(device->addr, addr, sizeof (device->addr)); device->addr[sizeof(device->addr)-1] = '\0'; } struct altos_file * altos_bt_open(struct altos_bt_device *device) { return NULL; } #endif