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