altosui: Make sure packet mode is turned off when the connection fails
[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 PUBLIC 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 PUBLIC 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 PUBLIC void
570 altos_free(struct altos_file *file)
571 {
572         altos_close(file);
573         free(file);
574 }
575
576 PUBLIC int
577 altos_flush(struct altos_file *file)
578 {
579         if (file->out_used && 0) {
580                 printf ("flush \"");
581                 fwrite(file->out_data, 1, file->out_used, stdout);
582                 printf ("\"\n");
583         }
584         while (file->out_used) {
585                 int     ret;
586
587                 if (file->fd < 0)
588                         return -EBADF;
589 #ifdef USE_POLL
590                 ret = write (file->fd, file->out_data, file->out_used);
591 #else
592                 ret = write (file->out_fd, file->out_data, file->out_used);
593 #endif
594                 if (ret < 0)
595                         return -errno;
596                 if (ret) {
597                         memmove(file->out_data, file->out_data + ret,
598                                 file->out_used - ret);
599                         file->out_used -= ret;
600                 }
601         }
602         return 0;
603 }
604
605 PUBLIC int
606 altos_putchar(struct altos_file *file, char c)
607 {
608         int     ret;
609
610         if (file->out_used == USB_BUF_SIZE) {
611                 ret = altos_flush(file);
612                 if (ret) {
613                         return ret;
614                 }
615         }
616         file->out_data[file->out_used++] = c;
617         ret = 0;
618         if (file->out_used == USB_BUF_SIZE)
619                 ret = altos_flush(file);
620         return 0;
621 }
622
623 #ifdef USE_POLL
624 #include <poll.h>
625 #endif
626
627 static int
628 altos_fill(struct altos_file *file, int timeout)
629 {
630         int             ret;
631 #ifdef USE_POLL
632         struct pollfd   fd[2];
633 #endif
634
635         if (timeout == 0)
636                 timeout = -1;
637         while (file->in_read == file->in_used) {
638                 if (file->fd < 0)
639                         return LIBALTOS_ERROR;
640 #ifdef USE_POLL
641                 fd[0].fd = file->fd;
642                 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
643                 fd[1].fd = file->pipe[0];
644                 fd[1].events = POLLIN;
645                 ret = poll(fd, 2, timeout);
646                 if (ret < 0) {
647                         perror("altos_getchar");
648                         return LIBALTOS_ERROR;
649                 }
650                 if (ret == 0)
651                         return LIBALTOS_TIMEOUT;
652
653                 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
654                         return LIBALTOS_ERROR;
655                 if (fd[0].revents & POLLIN)
656 #endif
657                 {
658                         ret = read(file->fd, file->in_data, USB_BUF_SIZE);
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         if (file->in_used && 0) {
672                 printf ("fill \"");
673                 fwrite(file->in_data, 1, file->in_used, stdout);
674                 printf ("\"\n");
675         }
676         return 0;
677 }
678
679 PUBLIC int
680 altos_getchar(struct altos_file *file, int timeout)
681 {
682         int     ret;
683         while (file->in_read == file->in_used) {
684                 if (file->fd < 0)
685                         return LIBALTOS_ERROR;
686                 ret = altos_fill(file, timeout);
687                 if (ret)
688                         return ret;
689         }
690         return file->in_data[file->in_read++];
691 }
692
693 #endif /* POSIX_TTY */
694
695 #ifdef WINDOWS
696
697 #include <stdlib.h>
698 #include <windows.h>
699 #include <setupapi.h>
700
701 struct altos_list {
702         HDEVINFO        dev_info;
703         int             index;
704 };
705
706 #define USB_BUF_SIZE    64
707
708 struct altos_file {
709         HANDLE                          handle;
710         unsigned char                   out_data[USB_BUF_SIZE];
711         int                             out_used;
712         unsigned char                   in_data[USB_BUF_SIZE];
713         int                             in_used;
714         int                             in_read;
715         OVERLAPPED                      ov_read;
716         BOOL                            pend_read;
717         OVERLAPPED                      ov_write;
718 };
719
720 PUBLIC struct altos_list *
721 altos_list_start(void)
722 {
723         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
724
725         if (!list)
726                 return NULL;
727         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
728                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
729         if (list->dev_info == INVALID_HANDLE_VALUE) {
730                 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
731                 free(list);
732                 return NULL;
733         }
734         list->index = 0;
735         return list;
736 }
737
738 PUBLIC int
739 altos_list_next(struct altos_list *list, struct altos_device *device)
740 {
741         SP_DEVINFO_DATA dev_info_data;
742         char            port[128];
743         DWORD           port_len;
744         char            friendlyname[256];
745         char            symbolic[256];
746         DWORD           symbolic_len;
747         HKEY            dev_key;
748         int             vid, pid;
749         int             serial;
750         HRESULT         result;
751         DWORD           friendlyname_type;
752         DWORD           friendlyname_len;
753
754         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
755         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
756                                     &dev_info_data))
757         {
758                 list->index++;
759
760                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
761                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
762                                                KEY_READ);
763                 if (dev_key == INVALID_HANDLE_VALUE) {
764                         printf("cannot open device registry key\n");
765                         continue;
766                 }
767
768                 /* Fetch symbolic name for this device and parse out
769                  * the vid/pid/serial info */
770                 symbolic_len = sizeof(symbolic);
771                 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
772                                          symbolic, &symbolic_len);
773                 if (result != 0) {
774                         printf("cannot find SymbolicName value\n");
775                         RegCloseKey(dev_key);
776                         continue;
777                 }
778                 vid = pid = serial = 0;
779                 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
780                        "%04X", &vid);
781                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
782                        "%04X", &pid);
783                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
784                        "%d", &serial);
785                 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
786                         RegCloseKey(dev_key);
787                         continue;
788                 }
789
790                 /* Fetch the com port name */
791                 port_len = sizeof (port);
792                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
793                                          port, &port_len);
794                 RegCloseKey(dev_key);
795                 if (result != 0) {
796                         printf("failed to get PortName\n");
797                         continue;
798                 }
799
800                 /* Fetch the device description which is the device name,
801                  * with firmware that has unique USB ids */
802                 friendlyname_len = sizeof (friendlyname);
803                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
804                                                      &dev_info_data,
805                                                      SPDRP_FRIENDLYNAME,
806                                                      &friendlyname_type,
807                                                      (BYTE *)friendlyname,
808                                                      sizeof(friendlyname),
809                                                      &friendlyname_len))
810                 {
811                         printf("Failed to get friendlyname\n");
812                         continue;
813                 }
814                 device->vendor = vid;
815                 device->product = pid;
816                 device->serial = serial;
817                 strcpy(device->name, friendlyname);
818
819                 strcpy(device->path, port);
820                 return 1;
821         }
822         result = GetLastError();
823         if (result != ERROR_NO_MORE_ITEMS)
824                 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
825         return 0;
826 }
827
828 PUBLIC void
829 altos_list_finish(struct altos_list *list)
830 {
831         SetupDiDestroyDeviceInfoList(list->dev_info);
832         free(list);
833 }
834
835 static int
836 altos_queue_read(struct altos_file *file)
837 {
838         DWORD   got;
839         if (file->pend_read)
840                 return LIBALTOS_SUCCESS;
841
842         if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
843                 if (GetLastError() != ERROR_IO_PENDING)
844                         return LIBALTOS_ERROR;
845                 file->pend_read = TRUE;
846         } else {
847                 file->pend_read = FALSE;
848                 file->in_read = 0;
849                 file->in_used = got;
850         }
851         return LIBALTOS_SUCCESS;
852 }
853
854 static int
855 altos_wait_read(struct altos_file *file, int timeout)
856 {
857         DWORD   ret;
858         DWORD   got;
859
860         if (!file->pend_read)
861                 return LIBALTOS_SUCCESS;
862
863         if (!timeout)
864                 timeout = INFINITE;
865
866         ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
867         switch (ret) {
868         case WAIT_OBJECT_0:
869                 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE))
870                         return LIBALTOS_ERROR;
871                 file->pend_read = FALSE;
872                 file->in_read = 0;
873                 file->in_used = got;
874                 break;
875         case WAIT_TIMEOUT:
876                 return LIBALTOS_TIMEOUT;
877                 break;
878         default:
879                 return LIBALTOS_ERROR;
880         }
881         return LIBALTOS_SUCCESS;
882 }
883
884 static int
885 altos_fill(struct altos_file *file, int timeout)
886 {
887         int     ret;
888
889         if (file->in_read < file->in_used)
890                 return LIBALTOS_SUCCESS;
891
892         file->in_read = file->in_used = 0;
893
894         ret = altos_queue_read(file);
895         if (ret)
896                 return ret;
897         ret = altos_wait_read(file, timeout);
898         if (ret)
899                 return ret;
900
901         return LIBALTOS_SUCCESS;
902 }
903
904 PUBLIC int
905 altos_flush(struct altos_file *file)
906 {
907         DWORD   put;
908         char    *data = file->out_data;
909         char    used = file->out_used;
910         DWORD   ret;
911
912         while (used) {
913                 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
914                         if (GetLastError() != ERROR_IO_PENDING)
915                                 return LIBALTOS_ERROR;
916                         ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
917                         switch (ret) {
918                         case WAIT_OBJECT_0:
919                                 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE))
920                                         return LIBALTOS_ERROR;
921                                 break;
922                         default:
923                                 return LIBALTOS_ERROR;
924                         }
925                 }
926                 data += put;
927                 used -= put;
928         }
929         file->out_used = 0;
930         return LIBALTOS_SUCCESS;
931 }
932
933 PUBLIC struct altos_file *
934 altos_open(struct altos_device *device)
935 {
936         struct altos_file       *file = calloc (1, sizeof (struct altos_file));
937         char    full_name[64];
938         DCB dcbSerialParams = {0};
939         COMMTIMEOUTS timeouts;
940
941         if (!file)
942                 return NULL;
943
944         strcpy(full_name, "\\\\.\\");
945         strcat(full_name, device->path);
946         file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
947                                   0, NULL, OPEN_EXISTING,
948                                   FILE_FLAG_OVERLAPPED, NULL);
949         if (file->handle == INVALID_HANDLE_VALUE) {
950                 free(file);
951                 return NULL;
952         }
953         file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
954         file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
955
956         timeouts.ReadIntervalTimeout = MAXDWORD;
957         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
958         timeouts.ReadTotalTimeoutConstant = 1 << 30;    /* almost forever */
959         timeouts.WriteTotalTimeoutMultiplier = 0;
960         timeouts.WriteTotalTimeoutConstant = 0;
961         SetCommTimeouts(file->handle, &timeouts);
962
963         dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
964         if (!GetCommState(file->handle, &dcbSerialParams)) {
965                 CloseHandle(file->handle);
966                 free(file);
967                 return NULL;
968         }
969         dcbSerialParams.BaudRate = CBR_9600;
970         dcbSerialParams.ByteSize = 8;
971         dcbSerialParams.StopBits = ONESTOPBIT;
972         dcbSerialParams.Parity = NOPARITY;
973         if (!SetCommState(file->handle, &dcbSerialParams)) {
974                 CloseHandle(file->handle);
975                 free(file);
976                 return NULL;
977         }
978
979         return file;
980 }
981
982 PUBLIC void
983 altos_close(struct altos_file *file)
984 {
985         if (file->handle != INVALID_HANDLE_VALUE) {
986                 CloseHandle(file->handle);
987                 file->handle = INVALID_HANDLE_VALUE;
988         }
989 }
990
991 PUBLIC void
992 altos_free(struct altos_file *file)
993 {
994         altos_close(file);
995         free(file);
996 }
997
998 int
999 altos_putchar(struct altos_file *file, char c)
1000 {
1001         int     ret;
1002
1003         if (file->out_used == USB_BUF_SIZE) {
1004                 ret = altos_flush(file);
1005                 if (ret)
1006                         return ret;
1007         }
1008         file->out_data[file->out_used++] = c;
1009         if (file->out_used == USB_BUF_SIZE)
1010                 return altos_flush(file);
1011         return LIBALTOS_SUCCESS;
1012 }
1013
1014 int
1015 altos_getchar(struct altos_file *file, int timeout)
1016 {
1017         int     ret;
1018         while (file->in_read == file->in_used) {
1019                 if (file->handle == INVALID_HANDLE_VALUE)
1020                         return LIBALTOS_ERROR;
1021                 ret = altos_fill(file, timeout);
1022                 if (ret)
1023                         return ret;
1024         }
1025         return file->in_data[file->in_read++];
1026 }
1027
1028 #endif