altosuilib: Wait for product data while scanning
[fw/altos] / libaltos / libaltos.c
index 505b3147f0f286527f0f743479a61230d2a7cf3e..97b7b3f9bba44037206e409362824131c833bec9 100644 (file)
@@ -53,6 +53,8 @@ altos_get_last_error(struct altos_error *error)
 
 #ifdef DARWIN
 
+#include <unistd.h>
+
 #undef USE_POLL
 
 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
@@ -478,26 +480,26 @@ usb_tty(char *sys)
                                return tty;
                        }
 
-                       /* Check for ttyACMx style names
+                       /* Check for tty/ttyACMx style names
                         */
-                       ntty = scandir(endpoint_full, &namelist,
+                       tty_dir = cc_fullname(endpoint_full, "tty");
+                       ntty = scandir(tty_dir, &namelist,
                                       dir_filter_tty,
                                       alphasort);
+                       free (tty_dir);
                        if (ntty > 0) {
-                               free(endpoint_full);
                                tty = cc_fullname("/dev", namelist[0]->d_name);
+                               free(endpoint_full);
                                free(namelist);
                                return tty;
                        }
 
-                       /* Check for tty/ttyACMx style names
+                       /* Check for ttyACMx style names
                         */
-                       tty_dir = cc_fullname(endpoint_full, "tty");
-                       free(endpoint_full);
-                       ntty = scandir(tty_dir, &namelist,
+                       ntty = scandir(endpoint_full, &namelist,
                                       dir_filter_tty,
                                       alphasort);
-                       free (tty_dir);
+                       free(endpoint_full);
                        if (ntty > 0) {
                                tty = cc_fullname("/dev", namelist[0]->d_name);
                                free(namelist);
@@ -641,6 +643,49 @@ altos_list_finish(struct altos_list *usbdevs)
        free(usbdevs);
 }
 
+#include <dlfcn.h>
+
+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;
@@ -704,7 +749,8 @@ altos_bt_list_next(struct altos_bt_list *bt_list,
                return 0;
 
        ii = &bt_list->ii[bt_list->rsp];
-       ba2str(&ii->bdaddr, device->addr);
+       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),
@@ -736,30 +782,42 @@ struct altos_file *
 altos_bt_open(struct altos_bt_device *device)
 {
        struct sockaddr_rc addr = { 0 };
-       int     s, status;
+       int     status, i;
        struct altos_file *file;
 
        file = calloc(1, sizeof (struct altos_file));
-       if (!file)
+       if (!file) {
+               errno = ENOMEM;
+               altos_set_last_posix_error();
                goto no_file;
-       file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-       if (file->fd < 0) {
+       }
+       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;
        }
 
-       addr.rc_family = AF_BLUETOOTH;
-       addr.rc_channel = 1;
-       str2ba(device->addr, &addr.rc_bdaddr);
+       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));
+               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;
        }
-       sleep(1);
+       usleep(100 * 1000);
 
 #ifdef USE_POLL
        pipe(file->pipe);
@@ -768,7 +826,7 @@ altos_bt_open(struct altos_bt_device *device)
 #endif
        return file;
 no_link:
-       close(s);
+       close(file->fd);
 no_sock:
        free(file);
 no_file:
@@ -809,7 +867,7 @@ get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
                got_string = CFStringGetCString(entry_as_string,
                                                result, result_len,
                                                kCFStringEncodingASCII);
-    
+
                CFRelease(entry_as_string);
                if (got_string)
                        return 1;
@@ -822,7 +880,7 @@ get_number(io_object_t object, CFStringRef entry, int *result)
 {
        CFTypeRef entry_as_number;
        Boolean got_number;
-       
+
        entry_as_number = IORegistryEntrySearchCFProperty (object,
                                                           kIOServicePlane,
                                                           entry,
@@ -877,19 +935,10 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                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 (list->ftdi) {
-                       if (device->vendor != 0x0403)
-                               continue;
-               } else {
-                       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->name, sizeof (device->name)) &&
                    get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
@@ -977,6 +1026,29 @@ struct altos_file {
        OVERLAPPED                      ov_write;
 };
 
+#include <stdarg.h>
+
+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)
 {
@@ -990,7 +1062,7 @@ _altos_set_last_windows_error(char *file, int line)
                      sizeof (message) / sizeof (TCHAR),
                      NULL);
        if (error != ERROR_SUCCESS)
-               printf ("%s:%d %s\n", file, line, message);
+               log_message ("%s:%d %s\n", file, line, message);
        altos_set_last_error(error, message);
 }
 
@@ -1049,6 +1121,8 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
        HRESULT         result;
        DWORD           friendlyname_type;
        DWORD           friendlyname_len;
+       char            instanceid[1024];
+       DWORD           instanceid_len;
 
        dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
        while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
@@ -1061,7 +1135,6 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                                               KEY_READ);
                if (dev_key == INVALID_HANDLE_VALUE) {
                        altos_set_last_windows_error();
-                       printf("cannot open device registry key\n");
                        continue;
                }
 
@@ -1070,6 +1143,7 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                        pid = 0x6015;
                        serial = 0;
                } else {
+                       vid = pid = serial = 0;
                        /* Fetch symbolic name for this device and parse out
                         * the vid/pid/serial info */
                        symbolic_len = sizeof(symbolic);
@@ -1077,17 +1151,34 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                                                 symbolic, &symbolic_len);
                        if (result != 0) {
                                altos_set_last_windows_error();
-                               printf("cannot find SymbolicName value\n");
+                       } else {
+                               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);
+                       }
+                       if (vid == 0 || pid == 0 || serial == 0) {
+                               if (SetupDiGetDeviceInstanceId(list->dev_info,
+                                                              &dev_info_data,
+                                                              instanceid,
+                                                              sizeof (instanceid),
+                                                              &instanceid_len)) {
+                                       sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
+                                              "%04X", &vid);
+                                       sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
+                                              "%04X", &pid);
+                                       sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1,
+                                              "%d", &serial);
+                               } else {
+                                       altos_set_last_windows_error();
+                               }
+                       }
+                       if (vid == 0 || pid == 0 || serial == 0) {
                                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 */
@@ -1097,7 +1188,6 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                RegCloseKey(dev_key);
                if (result != 0) {
                        altos_set_last_windows_error();
-                       printf("failed to get PortName\n");
                        continue;
                }
 
@@ -1113,7 +1203,6 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                                                     &friendlyname_len))
                {
                        altos_set_last_windows_error();
-                       printf("Failed to get friendlyname\n");
                        continue;
                }
                device->vendor = vid;
@@ -1125,10 +1214,8 @@ altos_list_next(struct altos_list *list, struct altos_device *device)
                return 1;
        }
        result = GetLastError();
-       if (result != ERROR_NO_MORE_ITEMS) {
+       if (result != ERROR_NO_MORE_ITEMS)
                altos_set_last_windows_error();
-               printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
-       }
        return 0;
 }
 
@@ -1187,6 +1274,7 @@ altos_wait_read(struct altos_file *file, int timeout)
                return LIBALTOS_TIMEOUT;
                break;
        default:
+               altos_set_last_windows_error();
                return LIBALTOS_ERROR;
        }
        return LIBALTOS_SUCCESS;
@@ -1224,7 +1312,6 @@ altos_flush(struct altos_file *file)
                if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
                        if (GetLastError() != ERROR_IO_PENDING) {
                                altos_set_last_windows_error();
-                               printf ("\tflush write error\n");
                                return LIBALTOS_ERROR;
                        }
                        ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
@@ -1232,13 +1319,11 @@ altos_flush(struct altos_file *file)
                        case WAIT_OBJECT_0:
                                if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
                                        altos_set_last_windows_error();
-                                       printf ("\tflush result error\n");
                                        return LIBALTOS_ERROR;
                                }
                                break;
                        default:
                                altos_set_last_windows_error();
-                               printf ("\tflush wait error\n");
                                return LIBALTOS_ERROR;
                        }
                }
@@ -1249,30 +1334,98 @@ altos_flush(struct altos_file *file)
        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;
-       DCB          dcb;
+       int i;
 
        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) {
+
+       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();
-               printf ("cannot open %s\n", full_name);
+               Sleep(100);
+       }
+
+       if (file->handle == INVALID_HANDLE_VALUE) {
                free(file);
                return NULL;
        }
-       file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-       file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, 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;
@@ -1281,10 +1434,8 @@ altos_open(struct altos_device *device)
        timeouts.WriteTotalTimeoutConstant = 0;
        SetCommTimeouts(file->handle, &timeouts);
 
-       if (GetCommState(file->handle, &dcb)) {
-               dcb.BaudRate = CBR_9600;
-               (void) SetCommState(file->handle, &dcb);
-       }
+       file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
        return file;
 }
@@ -1292,13 +1443,19 @@ altos_open(struct altos_device *device)
 PUBLIC void
 altos_close(struct altos_file *file)
 {
-       if (file->handle != INVALID_HANDLE_VALUE) {
-               CloseHandle(file->handle);
+       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;
-               SetEvent(file->ov_read.hEvent);
-               SetEvent(file->ov_write.hEvent);
-               CloseHandle(file->ov_read.hEvent);
-               CloseHandle(file->ov_write.hEvent);
+               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);
        }
 }
 
@@ -1330,8 +1487,10 @@ altos_getchar(struct altos_file *file, int timeout)
 {
        int     ret;
        while (file->in_read == file->in_used) {
-               if (file->handle == INVALID_HANDLE_VALUE)
+               if (file->handle == INVALID_HANDLE_VALUE) {
+                       altos_set_last_windows_error();
                        return LIBALTOS_ERROR;
+               }
                ret = altos_fill(file, timeout);
                if (ret)
                        return ret;