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