Merge branch 'master' of git://git.gag.com/fw/altos
[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
452 #define USB_BUF_SIZE    64
453
454 struct altos_file {
455         int                             fd;
456         unsigned char                   out_data[USB_BUF_SIZE];
457         int                             out_used;
458         unsigned char                   in_data[USB_BUF_SIZE];
459         int                             in_used;
460         int                             in_read;
461 };
462
463 struct altos_file *
464 altos_open(struct altos_device *device)
465 {
466         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
467         int                     ret;
468         struct termios          term;
469
470         if (!file)
471                 return NULL;
472
473         file->fd = open(device->path, O_RDWR | O_NOCTTY);
474         if (file->fd < 0) {
475                 perror(device->path);
476                 free(file);
477                 return NULL;
478         }
479         ret = tcgetattr(file->fd, &term);
480         if (ret < 0) {
481                 perror("tcgetattr");
482                 close(file->fd);
483                 free(file);
484                 return NULL;
485         }
486         cfmakeraw(&term);
487         term.c_cc[VMIN] = 0;
488         term.c_cc[VTIME] = 1;
489         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
490         if (ret < 0) {
491                 perror("tcsetattr");
492                 close(file->fd);
493                 free(file);
494                 return NULL;
495         }
496         return file;
497 }
498
499 void
500 altos_close(struct altos_file *file)
501 {
502         if (file->fd != -1) {
503                 close(file->fd);
504                 file->fd = -1;
505         }
506 }
507
508 void
509 altos_free(struct altos_file *file)
510 {
511         altos_close(file);
512         free(file);
513 }
514
515 int
516 altos_putchar(struct altos_file *file, char c)
517 {
518         int     ret;
519
520         if (file->out_used == USB_BUF_SIZE) {
521                 ret = altos_flush(file);
522                 if (ret)
523                         return ret;
524         }
525         file->out_data[file->out_used++] = c;
526         if (file->out_used == USB_BUF_SIZE)
527                 return altos_flush(file);
528         return 0;
529 }
530
531 int
532 altos_flush(struct altos_file *file)
533 {
534         while (file->out_used) {
535                 int     ret;
536
537                 if (file->fd < 0)
538                         return -EBADF;
539                 ret = write (file->fd, file->out_data, file->out_used);
540                 if (ret < 0)
541                         return -errno;
542                 if (ret) {
543                         memmove(file->out_data, file->out_data + ret,
544                                 file->out_used - ret);
545                         file->out_used -= ret;
546                 }
547         }
548 }
549
550 #include <poll.h>
551
552 int
553 altos_getchar(struct altos_file *file, int timeout)
554 {
555         while (file->in_read == file->in_used) {
556                 int     ret;
557
558                 altos_flush(file);
559                 if (file->fd < 0)
560                         return -EBADF;
561                 if (timeout) {
562                         struct pollfd fd;
563                         int ret;
564                         fd.fd = file->fd;
565                         fd.events = POLLIN;
566                         ret = poll(&fd, 1, timeout);
567                         if (ret == 0)
568                                 return LIBALTOS_TIMEOUT;
569                 }
570                 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
571                 if (ret < 0)
572                         return LIBALTOS_ERROR;
573                 file->in_read = 0;
574                 file->in_used = ret;
575         }
576         return file->in_data[file->in_read++];
577 }
578
579 #endif /* POSIX_TTY */
580
581 #ifdef WINDOWS
582
583 #include <windows.h>
584 #include <setupapi.h>
585
586 struct altos_list {
587         HDEVINFO        dev_info;
588         int             index;
589 };
590
591 #define USB_BUF_SIZE    64
592
593 struct altos_file {
594         HANDLE                          handle;
595         unsigned char                   out_data[USB_BUF_SIZE];
596         int                             out_used;
597         unsigned char                   in_data[USB_BUF_SIZE];
598         int                             in_used;
599         int                             in_read;
600 };
601
602
603 PUBLIC struct altos_list *
604 altos_list_start(void)
605 {
606         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
607
608         if (!list)
609                 return NULL;
610         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
611                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
612         if (list->dev_info == INVALID_HANDLE_VALUE) {
613                 printf("SetupDiGetClassDevs failed %d\n", GetLastError());
614                 free(list);
615                 return NULL;
616         }
617         list->index = 0;
618         return list;
619 }
620
621 PUBLIC int
622 altos_list_next(struct altos_list *list, struct altos_device *device)
623 {
624         SP_DEVINFO_DATA dev_info_data;
625         char            port[128];
626         DWORD           port_len;
627         char            location[256];
628         char            symbolic[256];
629         DWORD           symbolic_len;
630         HKEY            dev_key;
631         int             vid, pid;
632         int             serial;
633         HRESULT         result;
634         DWORD           location_type;
635         DWORD           location_len;
636
637         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
638         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
639                                     &dev_info_data))
640         {
641                 list->index++;
642
643                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
644                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
645                                                KEY_READ);
646                 if (dev_key == INVALID_HANDLE_VALUE) {
647                         printf("cannot open device registry key\n");
648                         continue;
649                 }
650
651                 /* Fetch symbolic name for this device and parse out
652                  * the vid/pid/serial info */
653                 symbolic_len = sizeof(symbolic);
654                 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
655                                          symbolic, &symbolic_len);
656                 if (result != 0) {
657                         printf("cannot find SymbolicName value\n");
658                         RegCloseKey(dev_key);
659                         continue;
660                 }
661                 vid = pid = serial = 0;
662                 sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1,
663                        "%04X", &vid);
664                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
665                        "%04X", &pid);
666                 sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
667                        "%d", &serial);
668                 if (!USB_IS_ALTUSMETRUM(vid, pid)) {
669                         printf("Not Altus Metrum symbolic name: %s\n",
670                                symbolic);
671                         RegCloseKey(dev_key);
672                         continue;
673                 }
674
675                 /* Fetch the com port name */
676                 port_len = sizeof (port);
677                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
678                                          port, &port_len);
679                 RegCloseKey(dev_key);
680                 if (result != 0) {
681                         printf("failed to get PortName\n");
682                         continue;
683                 }
684
685                 /* Fetch the 'location information' which is the device name,
686                  * at least on XP */
687                 location_len = sizeof (location);
688                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
689                                                      &dev_info_data,
690                                                      SPDRP_LOCATION_INFORMATION,
691                                                      &location_type,
692                                                      (BYTE *)location,
693                                                      sizeof(location),
694                                                      &location_len))
695                 {
696                         printf("Failed to get location\n");
697                         continue;
698                 }
699                 device->vendor = vid;
700                 device->product = pid;
701                 device->serial = serial;
702
703                 if (strcasestr(location, "tele"))
704                         strcpy(device->name, location);
705                 else
706                         strcpy(device->name, "");
707
708                 strcpy(device->path, port);
709                 printf ("product: %04x:%04x (%s)  path: %s serial %d\n",
710                         device->vendor, device->product, device->name,
711                         device->path, device->serial);
712                 return 1;
713         }
714         result = GetLastError();
715         if (result != ERROR_NO_MORE_ITEMS)
716                 printf ("SetupDiEnumDeviceInfo failed error %d\n", result);
717         return 0;
718 }
719
720 PUBLIC void
721 altos_list_finish(struct altos_list *list)
722 {
723         SetupDiDestroyDeviceInfoList(list->dev_info);
724         free(list);
725 }
726
727 static int
728 altos_fill(struct altos_file *file, int timeout)
729 {
730         DWORD   result;
731         DWORD   got;
732         COMMTIMEOUTS timeouts;
733
734         if (file->in_read < file->in_used)
735                 return LIBALTOS_SUCCESS;
736         file->in_read = file->in_used = 0;
737
738         if (timeout) {
739                 timeouts.ReadIntervalTimeout = MAXDWORD;
740                 timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
741                 timeouts.ReadTotalTimeoutConstant = timeout;
742         } else {
743                 timeouts.ReadIntervalTimeout = 0;
744                 timeouts.ReadTotalTimeoutMultiplier = 0;
745                 timeouts.ReadTotalTimeoutConstant = 0;
746         }
747         timeouts.WriteTotalTimeoutMultiplier = 0;
748         timeouts.WriteTotalTimeoutConstant = 0;
749
750         if (!SetCommTimeouts(file->handle, &timeouts)) {
751                 printf("SetCommTimeouts failed %d\n", GetLastError());
752         }
753
754         if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) {
755                 result = GetLastError();
756                 printf ("read failed %d\n", result);
757                 return LIBALTOS_ERROR;
758                 got = 0;
759         }
760         if (got)
761                 return LIBALTOS_SUCCESS;
762         return LIBALTOS_TIMEOUT;
763 }
764
765 PUBLIC int
766 altos_flush(struct altos_file *file)
767 {
768         DWORD   put;
769         char    *data = file->out_data;
770         char    used = file->out_used;
771         DWORD   result;
772
773         while (used) {
774                 if (!WriteFile(file->handle, data, used, &put, NULL)) {
775                         result = GetLastError();
776                         printf ("write failed %d\n", result);
777                         return LIBALTOS_ERROR;
778                 }
779                 data += put;
780                 used -= put;
781         }
782         file->out_used = 0;
783         return LIBALTOS_SUCCESS;
784 }
785
786 PUBLIC struct altos_file *
787 altos_open(struct altos_device *device)
788 {
789         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
790         char    full_name[64];
791
792         if (!file)
793                 return NULL;
794
795         strcpy(full_name, "\\\\.\\");
796         strcat(full_name, device->path);
797         file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
798                                   0, NULL, OPEN_EXISTING,
799                                   FILE_ATTRIBUTE_NORMAL, NULL);
800         if (file->handle == INVALID_HANDLE_VALUE) {
801                 free(file);
802                 return NULL;
803         }
804
805         timeouts.ReadIntervalTimeout = MAXDWORD;
806         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
807         timeouts.ReadTotalTimeoutConstant = 100;
808         timeouts.WriteTotalTimeoutMultiplier = 0;
809         timeouts.WriteTotalTimeoutConstant = 10000;
810         if (!SetCommTimeouts(file->handle, &timeouts)) {
811                 printf("SetCommTimeouts failed %d\n", GetLastError());
812         }
813
814         return file;
815 }
816
817 PUBLIC void
818 altos_close(struct altos_file *file)
819 {
820         if (file->handle != INVALID_HANDLE_VALUE) {
821                 CloseHandle(file->handle);
822                 file->handle = INVALID_HANDLE_VALUE;
823         }
824 }
825
826 PUBLIC void
827 altos_free(struct altos_file *file)
828 {
829         altos_close(file);
830         free(file);
831 }
832
833 int
834 altos_putchar(struct altos_file *file, char c)
835 {
836         int     ret;
837
838         if (file->out_used == USB_BUF_SIZE) {
839                 ret = altos_flush(file);
840                 if (ret)
841                         return ret;
842         }
843         file->out_data[file->out_used++] = c;
844         if (file->out_used == USB_BUF_SIZE)
845                 return altos_flush(file);
846         return LIBALTOS_SUCCESS;
847 }
848
849 int
850 altos_getchar(struct altos_file *file, int timeout)
851 {
852         int     ret;
853         while (file->in_read == file->in_used) {
854                 ret = altos_flush(file);
855                 if (ret)
856                         return ret;
857                 if (file->handle == INVALID_HANDLE_VALUE)
858                         return LIBALTOS_ERROR;
859                 ret = altos_fill(file, timeout);
860                 if (ret)
861                         return ret;
862         }
863         return file->in_data[file->in_read++];
864 }
865
866 #endif