65cd6c1a89bff2b2f6d3c7c0ca48388d814db0e3
[fw/altos] / ao-tools / libaltos / libaltos.c
1 /*
2  * Copyright © 2010 Keith Packard <keithp@keithp.com>
3  *
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.
7  *
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.
12  *
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.
16  */
17
18 #define BUILD_DLL
19 #include "libaltos.h"
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #define USE_POLL
25
26 PUBLIC int
27 altos_init(void)
28 {
29         return LIBALTOS_SUCCESS;
30 }
31
32 PUBLIC void
33 altos_fini(void)
34 {
35 }
36
37 #ifdef DARWIN
38
39 #undef USE_POLL
40
41 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
42 static char *
43 altos_strndup (const char *s, size_t n)
44 {
45     size_t len = strlen (s);
46     char *ret;
47
48     if (len <= n)
49        return strdup (s);
50     ret = malloc(n + 1);
51     strncpy(ret, s, n);
52     ret[n] = '\0';
53     return ret;
54 }
55
56 #else
57 #define altos_strndup strndup
58 #endif
59
60 /*
61  * Scan for Altus Metrum devices by looking through /sys
62  */
63
64 #ifdef LINUX
65
66 #define _GNU_SOURCE
67 #include <ctype.h>
68 #include <dirent.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72
73 static char *
74 cc_fullname (char *dir, char *file)
75 {
76         char    *new;
77         int     dlen = strlen (dir);
78         int     flen = strlen (file);
79         int     slen = 0;
80
81         if (dir[dlen-1] != '/')
82                 slen = 1;
83         new = malloc (dlen + slen + flen + 1);
84         if (!new)
85                 return 0;
86         strcpy(new, dir);
87         if (slen)
88                 strcat (new, "/");
89         strcat(new, file);
90         return new;
91 }
92
93 static char *
94 cc_basename(char *file)
95 {
96         char *b;
97
98         b = strrchr(file, '/');
99         if (!b)
100                 return file;
101         return b + 1;
102 }
103
104 static char *
105 load_string(char *dir, char *file)
106 {
107         char    *full = cc_fullname(dir, file);
108         char    line[4096];
109         char    *r;
110         FILE    *f;
111         int     rlen;
112
113         f = fopen(full, "r");
114         free(full);
115         if (!f)
116                 return NULL;
117         r = fgets(line, sizeof (line), f);
118         fclose(f);
119         if (!r)
120                 return NULL;
121         rlen = strlen(r);
122         if (r[rlen-1] == '\n')
123                 r[rlen-1] = '\0';
124         return strdup(r);
125 }
126
127 static int
128 load_hex(char *dir, char *file)
129 {
130         char    *line;
131         char    *end;
132         long    i;
133
134         line = load_string(dir, file);
135         if (!line)
136                 return -1;
137         i = strtol(line, &end, 16);
138         free(line);
139         if (end == line)
140                 return -1;
141         return i;
142 }
143
144 static int
145 load_dec(char *dir, char *file)
146 {
147         char    *line;
148         char    *end;
149         long    i;
150
151         line = load_string(dir, file);
152         if (!line)
153                 return -1;
154         i = strtol(line, &end, 10);
155         free(line);
156         if (end == line)
157                 return -1;
158         return i;
159 }
160
161 static int
162 dir_filter_tty_colon(const struct dirent *d)
163 {
164         return strncmp(d->d_name, "tty:", 4) == 0;
165 }
166
167 static int
168 dir_filter_tty(const struct dirent *d)
169 {
170         return strncmp(d->d_name, "tty", 3) == 0;
171 }
172
173 struct altos_usbdev {
174         char    *sys;
175         char    *tty;
176         char    *manufacturer;
177         char    *product_name;
178         int     serial; /* AltOS always uses simple integer serial numbers */
179         int     idProduct;
180         int     idVendor;
181 };
182
183 static char *
184 usb_tty(char *sys)
185 {
186         char *base;
187         int num_configs;
188         int config;
189         struct dirent **namelist;
190         int interface;
191         int num_interfaces;
192         char endpoint_base[20];
193         char *endpoint_full;
194         char *tty_dir;
195         int ntty;
196         char *tty;
197
198         base = cc_basename(sys);
199         num_configs = load_hex(sys, "bNumConfigurations");
200         num_interfaces = load_hex(sys, "bNumInterfaces");
201         for (config = 1; config <= num_configs; config++) {
202                 for (interface = 0; interface < num_interfaces; interface++) {
203                         sprintf(endpoint_base, "%s:%d.%d",
204                                 base, config, interface);
205                         endpoint_full = cc_fullname(sys, endpoint_base);
206
207                         /* Check for tty:ttyACMx style names
208                          */
209                         ntty = scandir(endpoint_full, &namelist,
210                                        dir_filter_tty_colon,
211                                        alphasort);
212                         if (ntty > 0) {
213                                 free(endpoint_full);
214                                 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
215                                 free(namelist);
216                                 return tty;
217                         }
218
219                         /* Check for tty/ttyACMx style names
220                          */
221                         tty_dir = cc_fullname(endpoint_full, "tty");
222                         free(endpoint_full);
223                         ntty = scandir(tty_dir, &namelist,
224                                        dir_filter_tty,
225                                        alphasort);
226                         free (tty_dir);
227                         if (ntty > 0) {
228                                 tty = cc_fullname("/dev", namelist[0]->d_name);
229                                 free(namelist);
230                                 return tty;
231                         }
232                 }
233         }
234         return NULL;
235 }
236
237 static struct altos_usbdev *
238 usb_scan_device(char *sys)
239 {
240         struct altos_usbdev *usbdev;
241
242         usbdev = calloc(1, sizeof (struct altos_usbdev));
243         if (!usbdev)
244                 return NULL;
245         usbdev->sys = strdup(sys);
246         usbdev->manufacturer = load_string(sys, "manufacturer");
247         usbdev->product_name = load_string(sys, "product");
248         usbdev->serial = load_dec(sys, "serial");
249         usbdev->idProduct = load_hex(sys, "idProduct");
250         usbdev->idVendor = load_hex(sys, "idVendor");
251         usbdev->tty = usb_tty(sys);
252         return usbdev;
253 }
254
255 static void
256 usbdev_free(struct altos_usbdev *usbdev)
257 {
258         free(usbdev->sys);
259         free(usbdev->manufacturer);
260         free(usbdev->product_name);
261         /* this can get used as a return value */
262         if (usbdev->tty)
263                 free(usbdev->tty);
264         free(usbdev);
265 }
266
267 #define USB_DEVICES     "/sys/bus/usb/devices"
268
269 static int
270 dir_filter_dev(const struct dirent *d)
271 {
272         const char      *n = d->d_name;
273         char    c;
274
275         while ((c = *n++)) {
276                 if (isdigit(c))
277                         continue;
278                 if (c == '-')
279                         continue;
280                 if (c == '.' && n != d->d_name + 1)
281                         continue;
282                 return 0;
283         }
284         return 1;
285 }
286
287 struct altos_list {
288         struct altos_usbdev     **dev;
289         int                     current;
290         int                     ndev;
291 };
292
293 struct altos_list *
294 altos_list_start(void)
295 {
296         int                     e;
297         struct dirent           **ents;
298         char                    *dir;
299         struct altos_usbdev     *dev;
300         struct altos_list       *devs;
301         int                     n;
302
303         devs = calloc(1, sizeof (struct altos_list));
304         if (!devs)
305                 return NULL;
306
307         n = scandir (USB_DEVICES, &ents,
308                      dir_filter_dev,
309                      alphasort);
310         if (!n)
311                 return 0;
312         for (e = 0; e < n; e++) {
313                 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
314                 dev = usb_scan_device(dir);
315                 free(dir);
316                 if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
317                         if (devs->dev)
318                                 devs->dev = realloc(devs->dev,
319                                                     devs->ndev + 1 * sizeof (struct usbdev *));
320                         else
321                                 devs->dev = malloc (sizeof (struct usbdev *));
322                         devs->dev[devs->ndev++] = dev;
323                 }
324         }
325         free(ents);
326         devs->current = 0;
327         return devs;
328 }
329
330 int
331 altos_list_next(struct altos_list *list, struct altos_device *device)
332 {
333         struct altos_usbdev *dev;
334         if (list->current >= list->ndev)
335                 return 0;
336         dev = list->dev[list->current];
337         strcpy(device->name, dev->product_name);
338         device->vendor = dev->idVendor;
339         device->product = dev->idProduct;
340         strcpy(device->path, dev->tty);
341         device->serial = dev->serial;
342         list->current++;
343         return 1;
344 }
345
346 void
347 altos_list_finish(struct altos_list *usbdevs)
348 {
349         int     i;
350
351         if (!usbdevs)
352                 return;
353         for (i = 0; i < usbdevs->ndev; i++)
354                 usbdev_free(usbdevs->dev[i]);
355         free(usbdevs);
356 }
357
358 #endif
359
360 #ifdef DARWIN
361
362 #include <IOKitLib.h>
363 #include <IOKit/usb/USBspec.h>
364 #include <sys/param.h>
365 #include <paths.h>
366 #include <CFNumber.h>
367 #include <IOBSD.h>
368 #include <string.h>
369 #include <stdio.h>
370 #include <stdlib.h>
371
372 struct altos_list {
373         io_iterator_t iterator;
374 };
375
376 static int
377 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
378 {
379         CFTypeRef entry_as_string;
380         Boolean got_string;
381
382         entry_as_string = IORegistryEntrySearchCFProperty (object,
383                                                            kIOServicePlane,
384                                                            entry,
385                                                            kCFAllocatorDefault,
386                                                            kIORegistryIterateRecursively);
387         if (entry_as_string) {
388                 got_string = CFStringGetCString(entry_as_string,
389                                                 result, result_len,
390                                                 kCFStringEncodingASCII);
391     
392                 CFRelease(entry_as_string);
393                 if (got_string)
394                         return 1;
395         }
396         return 0;
397 }
398
399 static int
400 get_number(io_object_t object, CFStringRef entry, int *result)
401 {
402         CFTypeRef entry_as_number;
403         Boolean got_number;
404         
405         entry_as_number = IORegistryEntrySearchCFProperty (object,
406                                                            kIOServicePlane,
407                                                            entry,
408                                                            kCFAllocatorDefault,
409                                                            kIORegistryIterateRecursively);
410         if (entry_as_number) {
411                 got_number = CFNumberGetValue(entry_as_number,
412                                               kCFNumberIntType,
413                                               result);
414                 if (got_number)
415                         return 1;
416         }
417         return 0;
418 }
419
420 struct altos_list *
421 altos_list_start(void)
422 {
423         struct altos_list *list = calloc (sizeof (struct altos_list), 1);
424         CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
425         io_iterator_t tdIterator;
426         io_object_t tdObject;
427         kern_return_t ret;
428         int i;
429
430         ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
431         if (ret != kIOReturnSuccess)
432                 return NULL;
433         return list;
434 }
435
436 int
437 altos_list_next(struct altos_list *list, struct altos_device *device)
438 {
439         io_object_t object;
440         char serial_string[128];
441
442         for (;;) {
443                 object = IOIteratorNext(list->iterator);
444                 if (!object)
445                         return 0;
446   
447                 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
448                     !get_number (object, CFSTR(kUSBProductID), &device->product))
449                         continue;
450                 if (device->vendor != 0xfffe)
451                         continue;
452                 if (device->product < 0x000a || 0x0013 < device->product)
453                         continue;
454                 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
455                     get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
456                     get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
457                         device->serial = atoi(serial_string);
458                         return 1;
459                 }
460         }
461 }
462
463 void
464 altos_list_finish(struct altos_list *list)
465 {
466         IOObjectRelease (list->iterator);
467         free(list);
468 }
469
470 #endif
471
472 #ifdef POSIX_TTY
473
474 #include <stdio.h>
475 #include <stdlib.h>
476 #include <fcntl.h>
477 #include <termios.h>
478 #include <errno.h>
479
480 #define USB_BUF_SIZE    64
481
482 struct altos_file {
483         int                             fd;
484 #ifdef USE_POLL
485         int                             pipe[2];
486 #else
487         int                             out_fd;
488 #endif
489         unsigned char                   out_data[USB_BUF_SIZE];
490         int                             out_used;
491         unsigned char                   in_data[USB_BUF_SIZE];
492         int                             in_used;
493         int                             in_read;
494 };
495
496 struct altos_file *
497 altos_open(struct altos_device *device)
498 {
499         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
500         int                     ret;
501         struct termios          term;
502
503         if (!file)
504                 return NULL;
505
506         printf("open %s\n", device->path);
507         file->fd = open(device->path, O_RDWR | O_NOCTTY);
508         printf("opened %d\n", file->fd);
509         if (file->fd < 0) {
510                 perror(device->path);
511                 free(file);
512                 return NULL;
513         }
514 #ifdef USE_POLL
515         pipe(file->pipe);
516 #else
517         file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
518         if (file->out_fd < 0) {
519                 perror(device->path);
520                 close(file->fd);
521                 free(file);
522                 return NULL;
523         }
524 #endif
525         ret = tcgetattr(file->fd, &term);
526         if (ret < 0) {
527                 perror("tcgetattr");
528                 close(file->fd);
529 #ifndef USE_POLL
530                 close(file->out_fd);
531 #endif
532                 free(file);
533                 return NULL;
534         }
535         cfmakeraw(&term);
536 #ifdef USE_POLL
537         term.c_cc[VMIN] = 1;
538         term.c_cc[VTIME] = 0;
539 #else
540         term.c_cc[VMIN] = 0;
541         term.c_cc[VTIME] = 1;
542 #endif
543         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
544         if (ret < 0) {
545                 perror("tcsetattr");
546                 close(file->fd);
547 #ifndef USE_POLL
548                 close(file->out_fd);
549 #endif
550                 free(file);
551                 return NULL;
552         }
553         printf("running %d\n", file->fd);
554         return file;
555 }
556
557 void
558 altos_close(struct altos_file *file)
559 {
560         if (file->fd != -1) {
561                 int     fd = file->fd;
562                 file->fd = -1;
563 #ifdef USE_POLL
564                 write(file->pipe[1], "\r", 1);
565 #else
566                 close(file->out_fd);
567                 file->out_fd = -1;
568 #endif
569                 printf("close %d\n", fd);
570                 close(fd);
571         }
572 }
573
574 void
575 altos_free(struct altos_file *file)
576 {
577         altos_close(file);
578         free(file);
579 }
580
581 int
582 altos_flush(struct altos_file *file)
583 {
584         while (file->out_used) {
585                 int     ret;
586
587                 if (file->fd < 0)
588                         return -EBADF;
589                 printf("write %d\n", file->out_used);
590 #ifdef USE_POLL
591                 ret = write (file->fd, file->out_data, file->out_used);
592 #else
593                 ret = write (file->out_fd, file->out_data, file->out_used);
594 #endif
595                 if (ret < 0)
596                         return -errno;
597                 if (ret) {
598                         memmove(file->out_data, file->out_data + ret,
599                                 file->out_used - ret);
600                         file->out_used -= ret;
601                 }
602         }
603         return 0;
604 }
605
606 int
607 altos_putchar(struct altos_file *file, char c)
608 {
609         int     ret;
610
611         if (file->out_used == USB_BUF_SIZE) {
612                 ret = altos_flush(file);
613                 if (ret) {
614                         return ret;
615                 }
616         }
617         file->out_data[file->out_used++] = c;
618         ret = 0;
619         if (file->out_used == USB_BUF_SIZE)
620                 ret = altos_flush(file);
621         return 0;
622 }
623
624 #ifdef USE_POLL
625 #include <poll.h>
626 #endif
627
628 int
629 altos_fill(struct altos_file *file, int timeout)
630 {
631         int             ret;
632 #ifdef USE_POLL
633         struct pollfd   fd[2];
634 #endif
635
636         if (timeout == 0)
637                 timeout = -1;
638         while (file->in_read == file->in_used) {
639                 if (file->fd < 0)
640                         return LIBALTOS_ERROR;
641 #ifdef USE_POLL
642                 fd[0].fd = file->fd;
643                 fd[0].events = POLLIN;
644                 fd[1].fd = file->pipe[0];
645                 fd[1].events = POLLIN;
646                 ret = poll(fd, 2, timeout);
647                 if (ret < 0) {
648                         perror("altos_getchar");
649                         return LIBALTOS_ERROR;
650                 }
651                 if (ret == 0)
652                         return LIBALTOS_TIMEOUT;
653                 if (fd[0].revents & POLLIN)
654 #endif
655                 {
656                         ret = read(file->fd, file->in_data, USB_BUF_SIZE);
657                         if (ret)
658                                 printf("read %d\n", ret);
659                         if (ret < 0) {
660                                 perror("altos_getchar");
661                                 return LIBALTOS_ERROR;
662                         }
663                         file->in_read = 0;
664                         file->in_used = ret;
665 #ifndef USE_POLL
666                         if (ret == 0 && timeout > 0)
667                                 return LIBALTOS_TIMEOUT;
668 #endif
669                 }
670         }
671         return 0;
672 }
673
674 int
675 altos_getchar(struct altos_file *file, int timeout)
676 {
677         int     ret;
678         while (file->in_read == file->in_used) {
679                 if (file->fd < 0)
680                         return LIBALTOS_ERROR;
681                 ret = altos_fill(file, timeout);
682                 if (ret)
683                         return ret;
684         }
685         return file->in_data[file->in_read++];
686 }
687
688 #endif /* POSIX_TTY */
689
690 #ifdef WINDOWS
691
692 #include <windows.h>
693 #include <setupapi.h>
694
695 struct altos_list {
696         HDEVINFO        dev_info;
697         int             index;
698 };
699
700 #define USB_BUF_SIZE    64
701
702 struct altos_file {
703         HANDLE                          handle;
704         unsigned char                   out_data[USB_BUF_SIZE];
705         int                             out_used;
706         unsigned char                   in_data[USB_BUF_SIZE];
707         int                             in_used;
708         int                             in_read;
709 };
710
711
712 PUBLIC struct altos_list *
713 altos_list_start(void)
714 {
715         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
716
717         if (!list)
718                 return NULL;
719         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
720                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
721         if (list->dev_info == INVALID_HANDLE_VALUE) {
722                 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
723                 free(list);
724                 return NULL;
725         }
726         list->index = 0;
727         return list;
728 }
729
730 PUBLIC int
731 altos_list_next(struct altos_list *list, struct altos_device *device)
732 {
733         SP_DEVINFO_DATA dev_info_data;
734         char            port[128];
735         DWORD           port_len;
736         char            location[256];
737         char            symbolic[256];
738         DWORD           symbolic_len;
739         HKEY            dev_key;
740         int             vid, pid;
741         int             serial;
742         HRESULT         result;
743         DWORD           location_type;
744         DWORD           location_len;
745
746         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
747         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
748                                     &dev_info_data))
749         {
750                 list->index++;
751
752                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
753                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
754                                                KEY_READ);
755                 if (dev_key == INVALID_HANDLE_VALUE) {
756                         printf("cannot open device registry key\n");
757                         continue;
758                 }
759
760                 /* Fetch symbolic name for this device and parse out
761                  * the vid/pid/serial info */
762                 symbolic_len = sizeof(symbolic);
763                 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
764                                          symbolic, &symbolic_len);
765                 if (result != 0) {
766                         printf("cannot find SymbolicName value\n");
767                         RegCloseKey(dev_key);
768                         continue;
769                 }
770                 vid = pid = serial = 0;
771                 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
772                        "%04X", &vid);
773                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
774                        "%04X", &pid);
775                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
776                        "%d", &serial);
777                 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
778                         printf("Not Altus Metrum symbolic name: %s\n",
779                                symbolic);
780                         RegCloseKey(dev_key);
781                         continue;
782                 }
783
784                 /* Fetch the com port name */
785                 port_len = sizeof (port);
786                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
787                                          port, &port_len);
788                 RegCloseKey(dev_key);
789                 if (result != 0) {
790                         printf("failed to get PortName\n");
791                         continue;
792                 }
793
794                 /* Fetch the 'location information' which is the device name,
795                  * at least on XP */
796                 location_len = sizeof (location);
797                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
798                                                      &dev_info_data,
799                                                      SPDRP_LOCATION_INFORMATION,
800                                                      &location_type,
801                                                      (BYTE *)location,
802                                                      sizeof(location),
803                                                      &location_len))
804                 {
805                         printf("Failed to get location\n");
806                         continue;
807                 }
808                 device->vendor = vid;
809                 device->product = pid;
810                 device->serial = serial;
811
812                 if (strcasestr(location, "tele"))
813                         strcpy(device->name, location);
814                 else
815                         strcpy(device->name, "");
816
817                 strcpy(device->path, port);
818                 printf ("product: %04x:%04x (%s)  path: %s serial %d\n",
819                         device->vendor, device->product, device->name,
820                         device->path, device->serial);
821                 return 1;
822         }
823         result = GetLastError();
824         if (result != ERROR_NO_MORE_ITEMS)
825                 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
826         return 0;
827 }
828
829 PUBLIC void
830 altos_list_finish(struct altos_list *list)
831 {
832         SetupDiDestroyDeviceInfoList(list->dev_info);
833         free(list);
834 }
835
836 static int
837 altos_fill(struct altos_file *file, int timeout)
838 {
839         DWORD   result;
840         DWORD   got;
841         COMMTIMEOUTS timeouts;
842
843         if (file->in_read < file->in_used)
844                 return LIBALTOS_SUCCESS;
845         file->in_read = file->in_used = 0;
846
847         if (timeout) {
848                 timeouts.ReadIntervalTimeout = MAXDWORD;
849                 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
850                 timeouts.ReadTotalTimeoutConstant = timeout;
851         } else {
852                 timeouts.ReadIntervalTimeout = 0;
853                 timeouts.ReadTotalTimeoutMultiplier = 0;
854                 timeouts.ReadTotalTimeoutConstant = 0;
855         }
856         timeouts.WriteTotalTimeoutMultiplier = 0;
857         timeouts.WriteTotalTimeoutConstant = 0;
858
859         if (!SetCommTimeouts(file->handle, &timeouts)) {
860                 printf("SetCommTimeouts failed %d\n", GetLastError());
861         }
862
863         if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
864                 result = GetLastError();
865                 printf ("read failed %d\n", result);
866                 return LIBALTOS_ERROR;
867                 got = 0;
868         }
869         if (got)
870                 return LIBALTOS_SUCCESS;
871         return LIBALTOS_TIMEOUT;
872 }
873
874 PUBLIC int
875 altos_flush(struct altos_file *file)
876 {
877         DWORD   put;
878         char    *data = file->out_data;
879         char    used = file->out_used;
880         DWORD   result;
881
882         while (used) {
883                 if (!WriteFile(file->handle, data, used, &put, NULL)) {
884                         result = GetLastError();
885                         printf ("write failed %d\n", result);
886                         return LIBALTOS_ERROR;
887                 }
888                 data += put;
889                 used -= put;
890         }
891         file->out_used = 0;
892         return LIBALTOS_SUCCESS;
893 }
894
895 PUBLIC struct altos_file *
896 altos_open(struct altos_device *device)
897 {
898         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
899         char    full_name[64];
900
901         if (!file)
902                 return NULL;
903
904         strcpy(full_name, "\\\\.\\");
905         strcat(full_name, device->path);
906         file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
907                                   0, NULL, OPEN_EXISTING,
908                                   FILE_ATTRIBUTE_NORMAL, NULL);
909         if (file->handle == INVALID_HANDLE_VALUE) {
910                 free(file);
911                 return NULL;
912         }
913
914         timeouts.ReadIntervalTimeout = MAXDWORD;
915         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
916         timeouts.ReadTotalTimeoutConstant = 100;
917         timeouts.WriteTotalTimeoutMultiplier = 0;
918         timeouts.WriteTotalTimeoutConstant = 10000;
919         if (!SetCommTimeouts(file->handle, &timeouts)) {
920                 printf("SetCommTimeouts failed %d\n", GetLastError());
921         }
922
923         return file;
924 }
925
926 PUBLIC void
927 altos_close(struct altos_file *file)
928 {
929         if (file->handle != INVALID_HANDLE_VALUE) {
930                 CloseHandle(file->handle);
931                 file->handle = INVALID_HANDLE_VALUE;
932         }
933 }
934
935 PUBLIC void
936 altos_free(struct altos_file *file)
937 {
938         altos_close(file);
939         free(file);
940 }
941
942 int
943 altos_putchar(struct altos_file *file, char c)
944 {
945         int     ret;
946
947         if (file->out_used == USB_BUF_SIZE) {
948                 ret = altos_flush(file);
949                 if (ret)
950                         return ret;
951         }
952         file->out_data[file->out_used++] = c;
953         if (file->out_used == USB_BUF_SIZE)
954                 return altos_flush(file);
955         return LIBALTOS_SUCCESS;
956 }
957
958 int
959 altos_getchar(struct altos_file *file, int timeout)
960 {
961         int     ret;
962         while (file->in_read == file->in_used) {
963                 if (file->handle == INVALID_HANDLE_VALUE)
964                         return LIBALTOS_ERROR;
965                 ret = altos_fill(file, timeout);
966                 if (ret)
967                         return ret;
968         }
969         return file->in_data[file->in_read++];
970 }
971
972 #endif