X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=ao-tools%2Flibaltos%2Flibaltos.c;h=465f0ac89475c03d61b1b89067a7b4b05ef67e4a;hp=93c483d14212799383e7e5d11bc030a5a0c507f7;hb=357826aa9c7b42c59f5d52b8eb016d73b6da0c7f;hpb=edcfb1bdf64772d3b83405ccf99385b8fea5d8e4 diff --git a/ao-tools/libaltos/libaltos.c b/ao-tools/libaltos/libaltos.c index 93c483d1..465f0ac8 100644 --- a/ao-tools/libaltos/libaltos.c +++ b/ao-tools/libaltos/libaltos.c @@ -15,12 +15,13 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#define BUILD_DLL #include "libaltos.h" #include #include #include +#define USE_POLL + PUBLIC int altos_init(void) { @@ -33,6 +34,9 @@ altos_fini(void) } #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) @@ -471,23 +475,24 @@ altos_list_finish(struct altos_list *list) #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; - pthread_mutex_t putc_mutex; - pthread_mutex_t getc_mutex; }; -struct altos_file * +PUBLIC struct altos_file * altos_open(struct altos_device *device) { struct altos_file *file = calloc (sizeof (struct altos_file), 1); @@ -497,63 +502,95 @@ altos_open(struct altos_device *device) if (!file) return NULL; - pipe(file->pipe); file->fd = open(device->path, O_RDWR | O_NOCTTY); if (file->fd < 0) { perror(device->path); 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; } - pthread_mutex_init(&file->putc_mutex,NULL); - pthread_mutex_init(&file->getc_mutex,NULL); return file; } -void +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); } } -void +PUBLIC void altos_free(struct altos_file *file) { altos_close(file); free(file); } -static int -_altos_flush(struct altos_file *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; - fflush(stdout); +#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) { @@ -562,92 +599,102 @@ _altos_flush(struct altos_file *file) file->out_used -= ret; } } + return 0; } -int +PUBLIC int altos_putchar(struct altos_file *file, char c) { int ret; - pthread_mutex_lock(&file->putc_mutex); if (file->out_used == USB_BUF_SIZE) { - ret = _altos_flush(file); + ret = altos_flush(file); if (ret) { - pthread_mutex_unlock(&file->putc_mutex); return ret; } } file->out_data[file->out_used++] = c; ret = 0; if (file->out_used == USB_BUF_SIZE) - ret = _altos_flush(file); - pthread_mutex_unlock(&file->putc_mutex); + ret = altos_flush(file); return 0; } -int -altos_flush(struct altos_file *file) -{ - int ret; - pthread_mutex_lock(&file->putc_mutex); - ret = _altos_flush(file); - pthread_mutex_unlock(&file->putc_mutex); - return ret; -} - - +#ifdef USE_POLL #include +#endif -int -altos_getchar(struct altos_file *file, int timeout) +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; - pthread_mutex_lock(&file->getc_mutex); - fd[0].fd = file->fd; - fd[0].events = POLLIN; - fd[1].fd = file->pipe[0]; - fd[1].events = POLLIN; while (file->in_read == file->in_used) { - if (file->fd < 0) { - pthread_mutex_unlock(&file->getc_mutex); + if (file->fd < 0) return LIBALTOS_ERROR; - } - altos_flush(file); - +#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) { perror("altos_getchar"); - pthread_mutex_unlock(&file->getc_mutex); return LIBALTOS_ERROR; } - if (ret == 0) { - pthread_mutex_unlock(&file->getc_mutex); + if (ret == 0) return LIBALTOS_TIMEOUT; - } - if (fd[0].revents & POLLIN) { + + 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) { perror("altos_getchar"); - pthread_mutex_unlock(&file->getc_mutex); return LIBALTOS_ERROR; } file->in_read = 0; file->in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif } } - ret = file->in_data[file->in_read++]; - pthread_mutex_unlock(&file->getc_mutex); - return ret; + 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 */ #ifdef WINDOWS +#include #include #include @@ -665,9 +712,11 @@ struct altos_file { 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) { @@ -692,15 +741,15 @@ altos_list_next(struct altos_list *list, struct altos_device *device) SP_DEVINFO_DATA dev_info_data; char port[128]; DWORD port_len; - char location[256]; + char friendlyname[256]; char symbolic[256]; DWORD symbolic_len; HKEY dev_key; int vid, pid; int serial; HRESULT result; - DWORD location_type; - DWORD location_len; + DWORD friendlyname_type; + DWORD friendlyname_len; dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); while(SetupDiEnumDeviceInfo(list->dev_info, list->index, @@ -734,8 +783,6 @@ altos_list_next(struct altos_list *list, struct altos_device *device) sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, "%d", &serial); if (!USB_IS_ALTUSMETRUM(vid, pid)) { - printf("Not Altus Metrum symbolic name: %s\n", - symbolic); RegCloseKey(dev_key); continue; } @@ -750,33 +797,26 @@ altos_list_next(struct altos_list *list, struct altos_device *device) continue; } - /* Fetch the 'location information' which is the device name, - * at least on XP */ - location_len = sizeof (location); + /* 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_LOCATION_INFORMATION, - &location_type, - (BYTE *)location, - sizeof(location), - &location_len)) + SPDRP_FRIENDLYNAME, + &friendlyname_type, + (BYTE *)friendlyname, + sizeof(friendlyname), + &friendlyname_len)) { - printf("Failed to get location\n"); + printf("Failed to get friendlyname\n"); continue; } device->vendor = vid; device->product = pid; device->serial = serial; - - if (strcasestr(location, "tele")) - strcpy(device->name, location); - else - strcpy(device->name, ""); + strcpy(device->name, friendlyname); strcpy(device->path, port); - printf ("product: %04x:%04x (%s) path: %s serial %d\n", - device->vendor, device->product, device->name, - device->path, device->serial); return 1; } result = GetLastError(); @@ -793,41 +833,72 @@ altos_list_finish(struct altos_list *list) } static int -altos_fill(struct altos_file *file, int timeout) +altos_queue_read(struct altos_file *file) { - DWORD result; DWORD got; - COMMTIMEOUTS timeouts; - - if (file->in_read < file->in_used) + if (file->pend_read) return LIBALTOS_SUCCESS; - file->in_read = file->in_used = 0; - if (timeout) { - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; - timeouts.ReadTotalTimeoutConstant = timeout; + 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 { - timeouts.ReadIntervalTimeout = 0; - timeouts.ReadTotalTimeoutMultiplier = 0; - timeouts.ReadTotalTimeoutConstant = 0; + file->pend_read = FALSE; + file->in_read = 0; + file->in_used = got; } - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; + return LIBALTOS_SUCCESS; +} - if (!SetCommTimeouts(file->handle, &timeouts)) { - printf("SetCommTimeouts failed %d\n", GetLastError()); - } +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; - if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) { - result = GetLastError(); - printf ("read failed %d\n", result); + 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; - got = 0; } - if (got) + 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; - return LIBALTOS_TIMEOUT; + + 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 @@ -836,13 +907,21 @@ altos_flush(struct altos_file *file) DWORD put; char *data = file->out_data; char used = file->out_used; - DWORD result; + DWORD ret; while (used) { - if (!WriteFile(file->handle, data, used, &put, NULL)) { - result = GetLastError(); - printf ("write failed %d\n", result); - return LIBALTOS_ERROR; + 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; + } } data += put; used -= put; @@ -854,8 +933,10 @@ altos_flush(struct altos_file *file) PUBLIC struct altos_file * altos_open(struct altos_device *device) { - struct altos_file *file = calloc (sizeof (struct altos_file), 1); + struct altos_file *file = calloc (1, sizeof (struct altos_file)); char full_name[64]; + DCB dcbSerialParams = {0}; + COMMTIMEOUTS timeouts; if (!file) return NULL; @@ -864,19 +945,35 @@ altos_open(struct altos_device *device) strcat(full_name, device->path); file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); + FILE_FLAG_OVERLAPPED, NULL); 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); timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; - timeouts.ReadTotalTimeoutConstant = 100; + timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 10000; - if (!SetCommTimeouts(file->handle, &timeouts)) { - printf("SetCommTimeouts failed %d\n", GetLastError()); + 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; @@ -919,9 +1016,6 @@ altos_getchar(struct altos_file *file, int timeout) { int ret; while (file->in_read == file->in_used) { - ret = altos_flush(file); - if (ret) - return ret; if (file->handle == INVALID_HANDLE_VALUE) return LIBALTOS_ERROR; ret = altos_fill(file, timeout);