Merge remote branch 'origin/master' into new-packet-format
[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 struct altos_list *
395 altos_list_start(void)
396 {
397         struct altos_list *list = calloc (sizeof (struct altos_list), 1);
398         CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
399         UInt32 vendor = 0xfffe, product = 0x000a;
400         CFNumberRef vendor_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor);
401         CFNumberRef product_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product);
402         io_iterator_t tdIterator;
403         io_object_t tdObject;
404   
405         CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBVendorID), vendor_ref);
406         CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBProductID), product_ref);
407
408         IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
409   
410         CFRelease(vendor_ref);
411         CFRelease(product_ref);
412         return list;
413 }
414
415 int
416 altos_list_next(struct altos_list *list, struct altos_device *device)
417 {
418         io_object_t object;
419         char serial_string[128];
420
421         for (;;) {
422                 object = IOIteratorNext(list->iterator);
423                 if (!object)
424                         return 0;
425   
426                 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
427                     get_string (object, CFSTR("USB Product Name"), device->product, sizeof (device->product)) &&
428                     get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
429                         device->serial = atoi(serial_string);
430                         return 1;
431                 }
432         }
433 }
434
435 void
436 altos_list_finish(struct altos_list *list)
437 {
438         IOObjectRelease (list->iterator);
439         free(list);
440 }
441
442 #endif
443
444 #ifdef POSIX_TTY
445
446 #include <stdio.h>
447 #include <stdlib.h>
448 #include <fcntl.h>
449 #include <termios.h>
450 #include <errno.h>
451 #include <pthread.h>
452
453 #define USB_BUF_SIZE    64
454
455 struct altos_file {
456         int                             fd;
457         int                             pipe[2];
458         unsigned char                   out_data[USB_BUF_SIZE];
459         int                             out_used;
460         unsigned char                   in_data[USB_BUF_SIZE];
461         int                             in_used;
462         int                             in_read;
463         pthread_mutex_t                 putc_mutex;
464         pthread_mutex_t                 getc_mutex;
465 };
466
467 struct altos_file *
468 altos_open(struct altos_device *device)
469 {
470         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
471         int                     ret;
472         struct termios          term;
473
474         if (!file)
475                 return NULL;
476
477         pipe(file->pipe);
478         file->fd = open(device->path, O_RDWR | O_NOCTTY);
479         if (file->fd < 0) {
480                 perror(device->path);
481                 free(file);
482                 return NULL;
483         }
484         ret = tcgetattr(file->fd, &term);
485         if (ret < 0) {
486                 perror("tcgetattr");
487                 close(file->fd);
488                 free(file);
489                 return NULL;
490         }
491         cfmakeraw(&term);
492         term.c_cc[VMIN] = 1;
493         term.c_cc[VTIME] = 0;
494         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
495         if (ret < 0) {
496                 perror("tcsetattr");
497                 close(file->fd);
498                 free(file);
499                 return NULL;
500         }
501         pthread_mutex_init(&file->putc_mutex,NULL);
502         pthread_mutex_init(&file->getc_mutex,NULL);
503         return file;
504 }
505
506 void
507 altos_close(struct altos_file *file)
508 {
509         if (file->fd != -1) {
510                 int     fd = file->fd;
511                 file->fd = -1;
512                 write(file->pipe[1], "\r", 1);
513                 close(fd);
514         }
515 }
516
517 void
518 altos_free(struct altos_file *file)
519 {
520         altos_close(file);
521         free(file);
522 }
523
524 static int
525 _altos_flush(struct altos_file *file)
526 {
527         while (file->out_used) {
528                 int     ret;
529
530                 if (file->fd < 0)
531                         return -EBADF;
532                 fflush(stdout);
533                 ret = write (file->fd, file->out_data, file->out_used);
534                 if (ret < 0)
535                         return -errno;
536                 if (ret) {
537                         memmove(file->out_data, file->out_data + ret,
538                                 file->out_used - ret);
539                         file->out_used -= ret;
540                 }
541         }
542 }
543
544 int
545 altos_putchar(struct altos_file *file, char c)
546 {
547         int     ret;
548
549         pthread_mutex_lock(&file->putc_mutex);
550         if (file->out_used == USB_BUF_SIZE) {
551                 ret = _altos_flush(file);
552                 if (ret) {
553                         pthread_mutex_unlock(&file->putc_mutex);
554                         return ret;
555                 }
556         }
557         file->out_data[file->out_used++] = c;
558         ret = 0;
559         if (file->out_used == USB_BUF_SIZE)
560                 ret = _altos_flush(file);
561         pthread_mutex_unlock(&file->putc_mutex);
562         return 0;
563 }
564
565 int
566 altos_flush(struct altos_file *file)
567 {
568         int ret;
569         pthread_mutex_lock(&file->putc_mutex);
570         ret = _altos_flush(file);
571         pthread_mutex_unlock(&file->putc_mutex);
572         return ret;
573 }
574
575
576 #include <poll.h>
577
578 int
579 altos_getchar(struct altos_file *file, int timeout)
580 {
581         int             ret;
582         struct pollfd   fd[2];
583
584         if (timeout == 0)
585                 timeout = -1;
586         pthread_mutex_lock(&file->getc_mutex);
587         fd[0].fd = file->fd;
588         fd[0].events = POLLIN;
589         fd[1].fd = file->pipe[0];
590         fd[1].events = POLLIN;
591         while (file->in_read == file->in_used) {
592                 if (file->fd < 0) {
593                         pthread_mutex_unlock(&file->getc_mutex);
594                         return LIBALTOS_ERROR;
595                 }
596                 altos_flush(file);
597
598                 ret = poll(fd, 2, timeout);
599                 if (ret < 0) {
600                         perror("altos_getchar");
601                         pthread_mutex_unlock(&file->getc_mutex);
602                         return LIBALTOS_ERROR;
603                 }
604                 if (ret == 0) {
605                         pthread_mutex_unlock(&file->getc_mutex);
606                         return LIBALTOS_TIMEOUT;
607                 }
608                 if (fd[0].revents & POLLIN) {
609                         ret = read(file->fd, file->in_data, USB_BUF_SIZE);
610                         if (ret < 0) {
611                                 perror("altos_getchar");
612                                 pthread_mutex_unlock(&file->getc_mutex);
613                                 return LIBALTOS_ERROR;
614                         }
615                         file->in_read = 0;
616                         file->in_used = ret;
617                 }
618         }
619         ret = file->in_data[file->in_read++];
620         pthread_mutex_unlock(&file->getc_mutex);
621         return ret;
622 }
623
624 #endif /* POSIX_TTY */
625
626 #ifdef WINDOWS
627
628 #include <windows.h>
629 #include <setupapi.h>
630
631 struct altos_list {
632         HDEVINFO        dev_info;
633         int             index;
634 };
635
636 #define USB_BUF_SIZE    64
637
638 struct altos_file {
639         HANDLE                          handle;
640         unsigned char                   out_data[USB_BUF_SIZE];
641         int                             out_used;
642         unsigned char                   in_data[USB_BUF_SIZE];
643         int                             in_used;
644         int                             in_read;
645 };
646
647
648 PUBLIC struct altos_list *
649 altos_list_start(void)
650 {
651         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
652
653         if (!list)
654                 return NULL;
655         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
656                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
657         if (list->dev_info == INVALID_HANDLE_VALUE) {
658                 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
659                 free(list);
660                 return NULL;
661         }
662         list->index = 0;
663         return list;
664 }
665
666 PUBLIC int
667 altos_list_next(struct altos_list *list, struct altos_device *device)
668 {
669         SP_DEVINFO_DATA dev_info_data;
670         char            port[128];
671         DWORD           port_len;
672         char            location[256];
673         char            symbolic[256];
674         DWORD           symbolic_len;
675         HKEY            dev_key;
676         int             vid, pid;
677         int             serial;
678         HRESULT         result;
679         DWORD           location_type;
680         DWORD           location_len;
681
682         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
683         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
684                                     &dev_info_data))
685         {
686                 list->index++;
687
688                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
689                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
690                                                KEY_READ);
691                 if (dev_key == INVALID_HANDLE_VALUE) {
692                         printf("cannot open device registry key\n");
693                         continue;
694                 }
695
696                 /* Fetch symbolic name for this device and parse out
697                  * the vid/pid/serial info */
698                 symbolic_len = sizeof(symbolic);
699                 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
700                                          symbolic, &symbolic_len);
701                 if (result != 0) {
702                         printf("cannot find SymbolicName value\n");
703                         RegCloseKey(dev_key);
704                         continue;
705                 }
706                 vid = pid = serial = 0;
707                 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
708                        "%04X", &vid);
709                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
710                        "%04X", &pid);
711                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
712                        "%d", &serial);
713                 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
714                         printf("Not Altus Metrum symbolic name: %s\n",
715                                symbolic);
716                         RegCloseKey(dev_key);
717                         continue;
718                 }
719
720                 /* Fetch the com port name */
721                 port_len = sizeof (port);
722                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
723                                          port, &port_len);
724                 RegCloseKey(dev_key);
725                 if (result != 0) {
726                         printf("failed to get PortName\n");
727                         continue;
728                 }
729
730                 /* Fetch the 'location information' which is the device name,
731                  * at least on XP */
732                 location_len = sizeof (location);
733                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
734                                                      &dev_info_data,
735                                                      SPDRP_LOCATION_INFORMATION,
736                                                      &location_type,
737                                                      (BYTE *)location,
738                                                      sizeof(location),
739                                                      &location_len))
740                 {
741                         printf("Failed to get location\n");
742                         continue;
743                 }
744                 device->vendor = vid;
745                 device->product = pid;
746                 device->serial = serial;
747
748                 if (strcasestr(location, "tele"))
749                         strcpy(device->name, location);
750                 else
751                         strcpy(device->name, "");
752
753                 strcpy(device->path, port);
754                 printf ("product: %04x:%04x (%s)  path: %s serial %d\n",
755                         device->vendor, device->product, device->name,
756                         device->path, device->serial);
757                 return 1;
758         }
759         result = GetLastError();
760         if (result != ERROR_NO_MORE_ITEMS)
761                 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
762         return 0;
763 }
764
765 PUBLIC void
766 altos_list_finish(struct altos_list *list)
767 {
768         SetupDiDestroyDeviceInfoList(list->dev_info);
769         free(list);
770 }
771
772 static int
773 altos_fill(struct altos_file *file, int timeout)
774 {
775         DWORD   result;
776         DWORD   got;
777         COMMTIMEOUTS timeouts;
778
779         if (file->in_read < file->in_used)
780                 return LIBALTOS_SUCCESS;
781         file->in_read = file->in_used = 0;
782
783         if (timeout) {
784                 timeouts.ReadIntervalTimeout = MAXDWORD;
785                 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
786                 timeouts.ReadTotalTimeoutConstant = timeout;
787         } else {
788                 timeouts.ReadIntervalTimeout = 0;
789                 timeouts.ReadTotalTimeoutMultiplier = 0;
790                 timeouts.ReadTotalTimeoutConstant = 0;
791         }
792         timeouts.WriteTotalTimeoutMultiplier = 0;
793         timeouts.WriteTotalTimeoutConstant = 0;
794
795         if (!SetCommTimeouts(file->handle, &timeouts)) {
796                 printf("SetCommTimeouts failed %d\n", GetLastError());
797         }
798
799         if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
800                 result = GetLastError();
801                 printf ("read failed %d\n", result);
802                 return LIBALTOS_ERROR;
803                 got = 0;
804         }
805         if (got)
806                 return LIBALTOS_SUCCESS;
807         return LIBALTOS_TIMEOUT;
808 }
809
810 PUBLIC int
811 altos_flush(struct altos_file *file)
812 {
813         DWORD   put;
814         char    *data = file->out_data;
815         char    used = file->out_used;
816         DWORD   result;
817
818         while (used) {
819                 if (!WriteFile(file->handle, data, used, &put, NULL)) {
820                         result = GetLastError();
821                         printf ("write failed %d\n", result);
822                         return LIBALTOS_ERROR;
823                 }
824                 data += put;
825                 used -= put;
826         }
827         file->out_used = 0;
828         return LIBALTOS_SUCCESS;
829 }
830
831 PUBLIC struct altos_file *
832 altos_open(struct altos_device *device)
833 {
834         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
835         char    full_name[64];
836
837         if (!file)
838                 return NULL;
839
840         strcpy(full_name, "\\\\.\\");
841         strcat(full_name, device->path);
842         file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
843                                   0, NULL, OPEN_EXISTING,
844                                   FILE_ATTRIBUTE_NORMAL, NULL);
845         if (file->handle == INVALID_HANDLE_VALUE) {
846                 free(file);
847                 return NULL;
848         }
849
850         timeouts.ReadIntervalTimeout = MAXDWORD;
851         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
852         timeouts.ReadTotalTimeoutConstant = 100;
853         timeouts.WriteTotalTimeoutMultiplier = 0;
854         timeouts.WriteTotalTimeoutConstant = 10000;
855         if (!SetCommTimeouts(file->handle, &timeouts)) {
856                 printf("SetCommTimeouts failed %d\n", GetLastError());
857         }
858
859         return file;
860 }
861
862 PUBLIC void
863 altos_close(struct altos_file *file)
864 {
865         if (file->handle != INVALID_HANDLE_VALUE) {
866                 CloseHandle(file->handle);
867                 file->handle = INVALID_HANDLE_VALUE;
868         }
869 }
870
871 PUBLIC void
872 altos_free(struct altos_file *file)
873 {
874         altos_close(file);
875         free(file);
876 }
877
878 int
879 altos_putchar(struct altos_file *file, char c)
880 {
881         int     ret;
882
883         if (file->out_used == USB_BUF_SIZE) {
884                 ret = altos_flush(file);
885                 if (ret)
886                         return ret;
887         }
888         file->out_data[file->out_used++] = c;
889         if (file->out_used == USB_BUF_SIZE)
890                 return altos_flush(file);
891         return LIBALTOS_SUCCESS;
892 }
893
894 int
895 altos_getchar(struct altos_file *file, int timeout)
896 {
897         int     ret;
898         while (file->in_read == file->in_used) {
899                 ret = altos_flush(file);
900                 if (ret)
901                         return ret;
902                 if (file->handle == INVALID_HANDLE_VALUE)
903                         return LIBALTOS_ERROR;
904                 ret = altos_fill(file, timeout);
905                 if (ret)
906                         return ret;
907         }
908         return file->in_data[file->in_read++];
909 }
910
911 #endif