micropeak is code complete now.
[fw/altos] / 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 BLUETOOTH_PRODUCT_TELEBT        "TeleBT"
24
25 #define USE_POLL
26
27 PUBLIC int
28 altos_init(void)
29 {
30         return LIBALTOS_SUCCESS;
31 }
32
33 PUBLIC void
34 altos_fini(void)
35 {
36 }
37
38 static struct altos_error last_error;
39
40 static void
41 altos_set_last_error(int code, char *string)
42 {
43         last_error.code = code;
44         strncpy(last_error.string, string, sizeof (last_error.string) -1);
45         last_error.string[sizeof(last_error.string)-1] = '\0';
46 }
47
48 PUBLIC void
49 altos_get_last_error(struct altos_error *error)
50 {
51         *error = last_error;
52 }
53
54 #ifdef DARWIN
55
56 #undef USE_POLL
57
58 /* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
59 static char *
60 altos_strndup (const char *s, size_t n)
61 {
62     size_t len = strlen (s);
63     char *ret;
64
65     if (len <= n)
66        return strdup (s);
67     ret = malloc(n + 1);
68     strncpy(ret, s, n);
69     ret[n] = '\0';
70     return ret;
71 }
72
73 #else
74 #define altos_strndup strndup
75 #endif
76
77 #ifdef POSIX_TTY
78
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <fcntl.h>
82 #include <termios.h>
83 #include <errno.h>
84
85 #define USB_BUF_SIZE    64
86
87 struct altos_file {
88         int                             fd;
89 #ifdef USE_POLL
90         int                             pipe[2];
91 #else
92         int                             out_fd;
93 #endif
94         unsigned char                   out_data[USB_BUF_SIZE];
95         int                             out_used;
96         unsigned char                   in_data[USB_BUF_SIZE];
97         int                             in_used;
98         int                             in_read;
99 };
100
101 static void
102 altos_set_last_posix_error(void)
103 {
104         altos_set_last_error(errno, strerror(errno));
105 }
106
107 PUBLIC struct altos_file *
108 altos_open(struct altos_device *device)
109 {
110         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
111         int                     ret;
112         struct termios          term;
113
114         if (!file) {
115                 altos_set_last_posix_error();
116                 return NULL;
117         }
118
119         printf ("open\n");
120 //      altos_set_last_error(12, "yeah yeah, failed again");
121 //      free(file);
122 //      return NULL;
123
124         file->fd = open(device->path, O_RDWR | O_NOCTTY);
125         if (file->fd < 0) {
126                 altos_set_last_posix_error();
127                 free(file);
128                 return NULL;
129         }
130 #ifdef USE_POLL
131         pipe(file->pipe);
132 #else
133         file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
134         if (file->out_fd < 0) {
135                 altos_set_last_posix_error();
136                 close(file->fd);
137                 free(file);
138                 return NULL;
139         }
140 #endif
141         ret = tcgetattr(file->fd, &term);
142         if (ret < 0) {
143                 altos_set_last_posix_error();
144                 close(file->fd);
145 #ifndef USE_POLL
146                 close(file->out_fd);
147 #endif
148                 free(file);
149                 return NULL;
150         }
151         cfmakeraw(&term);
152         cfsetospeed(&term, B9600);
153         cfsetispeed(&term, B9600);
154 #ifdef USE_POLL
155         term.c_cc[VMIN] = 1;
156         term.c_cc[VTIME] = 0;
157 #else
158         term.c_cc[VMIN] = 0;
159         term.c_cc[VTIME] = 1;
160 #endif
161         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
162         if (ret < 0) {
163                 altos_set_last_posix_error();
164                 close(file->fd);
165 #ifndef USE_POLL
166                 close(file->out_fd);
167 #endif
168                 free(file);
169                 return NULL;
170         }
171         return file;
172 }
173
174 PUBLIC void
175 altos_close(struct altos_file *file)
176 {
177         if (file->fd != -1) {
178                 int     fd = file->fd;
179                 file->fd = -1;
180 #ifdef USE_POLL
181                 write(file->pipe[1], "\r", 1);
182 #else
183                 close(file->out_fd);
184                 file->out_fd = -1;
185 #endif
186                 close(fd);
187         }
188 }
189
190 PUBLIC void
191 altos_free(struct altos_file *file)
192 {
193         altos_close(file);
194         free(file);
195 }
196
197 PUBLIC int
198 altos_flush(struct altos_file *file)
199 {
200         if (file->out_used && 0) {
201                 printf ("flush \"");
202                 fwrite(file->out_data, 1, file->out_used, stdout);
203                 printf ("\"\n");
204         }
205         while (file->out_used) {
206                 int     ret;
207
208                 if (file->fd < 0)
209                         return -EBADF;
210 #ifdef USE_POLL
211                 ret = write (file->fd, file->out_data, file->out_used);
212 #else
213                 ret = write (file->out_fd, file->out_data, file->out_used);
214 #endif
215                 if (ret < 0) {
216                         altos_set_last_posix_error();
217                         return -last_error.code;
218                 }
219                 if (ret) {
220                         memmove(file->out_data, file->out_data + ret,
221                                 file->out_used - ret);
222                         file->out_used -= ret;
223                 }
224         }
225         return 0;
226 }
227
228 PUBLIC int
229 altos_putchar(struct altos_file *file, char c)
230 {
231         int     ret;
232
233         if (file->out_used == USB_BUF_SIZE) {
234                 ret = altos_flush(file);
235                 if (ret) {
236                         return ret;
237                 }
238         }
239         file->out_data[file->out_used++] = c;
240         ret = 0;
241         if (file->out_used == USB_BUF_SIZE)
242                 ret = altos_flush(file);
243         return ret;
244 }
245
246 #ifdef USE_POLL
247 #include <poll.h>
248 #endif
249
250 static int
251 altos_fill(struct altos_file *file, int timeout)
252 {
253         int             ret;
254 #ifdef USE_POLL
255         struct pollfd   fd[2];
256 #endif
257
258         if (timeout == 0)
259                 timeout = -1;
260         while (file->in_read == file->in_used) {
261                 if (file->fd < 0)
262                         return LIBALTOS_ERROR;
263 #ifdef USE_POLL
264                 fd[0].fd = file->fd;
265                 fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
266                 fd[1].fd = file->pipe[0];
267                 fd[1].events = POLLIN;
268                 ret = poll(fd, 2, timeout);
269                 if (ret < 0) {
270                         altos_set_last_posix_error();
271                         return LIBALTOS_ERROR;
272                 }
273                 if (ret == 0)
274                         return LIBALTOS_TIMEOUT;
275
276                 if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
277                         return LIBALTOS_ERROR;
278                 if (fd[0].revents & POLLIN)
279 #endif
280                 {
281                         ret = read(file->fd, file->in_data, USB_BUF_SIZE);
282                         if (ret < 0) {
283                                 altos_set_last_posix_error();
284                                 return LIBALTOS_ERROR;
285                         }
286                         file->in_read = 0;
287                         file->in_used = ret;
288 #ifndef USE_POLL
289                         if (ret == 0 && timeout > 0)
290                                 return LIBALTOS_TIMEOUT;
291 #endif
292                 }
293         }
294         if (file->in_used && 0) {
295                 printf ("fill \"");
296                 fwrite(file->in_data, 1, file->in_used, stdout);
297                 printf ("\"\n");
298         }
299         return 0;
300 }
301
302 PUBLIC int
303 altos_getchar(struct altos_file *file, int timeout)
304 {
305         int     ret;
306         while (file->in_read == file->in_used) {
307                 if (file->fd < 0)
308                         return LIBALTOS_ERROR;
309                 ret = altos_fill(file, timeout);
310                 if (ret)
311                         return ret;
312         }
313         return file->in_data[file->in_read++];
314 }
315
316 #endif /* POSIX_TTY */
317
318 /*
319  * Scan for Altus Metrum devices by looking through /sys
320  */
321
322 #ifdef LINUX
323
324 #define _GNU_SOURCE
325 #include <ctype.h>
326 #include <dirent.h>
327 #include <stdio.h>
328 #include <stdlib.h>
329 #include <string.h>
330 #include <bluetooth/bluetooth.h>
331 #include <bluetooth/hci.h>
332 #include <bluetooth/hci_lib.h>
333 #include <bluetooth/rfcomm.h>
334
335 static char *
336 cc_fullname (char *dir, char *file)
337 {
338         char    *new;
339         int     dlen = strlen (dir);
340         int     flen = strlen (file);
341         int     slen = 0;
342
343         if (dir[dlen-1] != '/')
344                 slen = 1;
345         new = malloc (dlen + slen + flen + 1);
346         if (!new)
347                 return 0;
348         strcpy(new, dir);
349         if (slen)
350                 strcat (new, "/");
351         strcat(new, file);
352         return new;
353 }
354
355 static char *
356 cc_basename(char *file)
357 {
358         char *b;
359
360         b = strrchr(file, '/');
361         if (!b)
362                 return file;
363         return b + 1;
364 }
365
366 static char *
367 load_string(char *dir, char *file)
368 {
369         char    *full = cc_fullname(dir, file);
370         char    line[4096];
371         char    *r;
372         FILE    *f;
373         int     rlen;
374
375         f = fopen(full, "r");
376         free(full);
377         if (!f)
378                 return NULL;
379         r = fgets(line, sizeof (line), f);
380         fclose(f);
381         if (!r)
382                 return NULL;
383         rlen = strlen(r);
384         if (r[rlen-1] == '\n')
385                 r[rlen-1] = '\0';
386         return strdup(r);
387 }
388
389 static int
390 load_hex(char *dir, char *file)
391 {
392         char    *line;
393         char    *end;
394         long    i;
395
396         line = load_string(dir, file);
397         if (!line)
398                 return -1;
399         i = strtol(line, &end, 16);
400         free(line);
401         if (end == line)
402                 return -1;
403         return i;
404 }
405
406 static int
407 load_dec(char *dir, char *file)
408 {
409         char    *line;
410         char    *end;
411         long    i;
412
413         line = load_string(dir, file);
414         if (!line)
415                 return -1;
416         i = strtol(line, &end, 10);
417         free(line);
418         if (end == line)
419                 return -1;
420         return i;
421 }
422
423 static int
424 dir_filter_tty_colon(const struct dirent *d)
425 {
426         return strncmp(d->d_name, "tty:", 4) == 0;
427 }
428
429 static int
430 dir_filter_tty(const struct dirent *d)
431 {
432         return strncmp(d->d_name, "tty", 3) == 0;
433 }
434
435 struct altos_usbdev {
436         char    *sys;
437         char    *tty;
438         char    *manufacturer;
439         char    *product_name;
440         int     serial; /* AltOS always uses simple integer serial numbers */
441         int     idProduct;
442         int     idVendor;
443 };
444
445 static char *
446 usb_tty(char *sys)
447 {
448         char *base;
449         int num_configs;
450         int config;
451         struct dirent **namelist;
452         int interface;
453         int num_interfaces;
454         char endpoint_base[20];
455         char *endpoint_full;
456         char *tty_dir;
457         int ntty;
458         char *tty;
459
460         base = cc_basename(sys);
461         num_configs = load_hex(sys, "bNumConfigurations");
462         num_interfaces = load_hex(sys, "bNumInterfaces");
463         for (config = 1; config <= num_configs; config++) {
464                 for (interface = 0; interface < num_interfaces; interface++) {
465                         sprintf(endpoint_base, "%s:%d.%d",
466                                 base, config, interface);
467                         endpoint_full = cc_fullname(sys, endpoint_base);
468
469
470                         /* Check for tty:ttyACMx style names
471                          */
472                         ntty = scandir(endpoint_full, &namelist,
473                                        dir_filter_tty_colon,
474                                        alphasort);
475                         if (ntty > 0) {
476                                 free(endpoint_full);
477                                 tty = cc_fullname("/dev", namelist[0]->d_name + 4);
478                                 free(namelist);
479                                 return tty;
480                         }
481
482                         /* Check for ttyACMx style names
483                          */
484                         ntty = scandir(endpoint_full, &namelist,
485                                        dir_filter_tty,
486                                        alphasort);
487                         if (ntty > 0) {
488                                 free(endpoint_full);
489                                 tty = cc_fullname("/dev", namelist[0]->d_name);
490                                 free(namelist);
491                                 return tty;
492                         }
493
494                         /* Check for tty/ttyACMx style names
495                          */
496                         tty_dir = cc_fullname(endpoint_full, "tty");
497                         free(endpoint_full);
498                         ntty = scandir(tty_dir, &namelist,
499                                        dir_filter_tty,
500                                        alphasort);
501                         free (tty_dir);
502                         if (ntty > 0) {
503                                 tty = cc_fullname("/dev", namelist[0]->d_name);
504                                 free(namelist);
505                                 return tty;
506                         }
507
508                 }
509         }
510         return NULL;
511 }
512
513 static struct altos_usbdev *
514 usb_scan_device(char *sys)
515 {
516         struct altos_usbdev *usbdev;
517         char *tty;
518
519         tty = usb_tty(sys);
520         if (!tty)
521                 return NULL;
522         usbdev = calloc(1, sizeof (struct altos_usbdev));
523         if (!usbdev)
524                 return NULL;
525         usbdev->sys = strdup(sys);
526         usbdev->manufacturer = load_string(sys, "manufacturer");
527         usbdev->product_name = load_string(sys, "product");
528         usbdev->serial = load_dec(sys, "serial");
529         usbdev->idProduct = load_hex(sys, "idProduct");
530         usbdev->idVendor = load_hex(sys, "idVendor");
531         usbdev->tty = tty;
532         return usbdev;
533 }
534
535 static void
536 usbdev_free(struct altos_usbdev *usbdev)
537 {
538         free(usbdev->sys);
539         free(usbdev->manufacturer);
540         free(usbdev->product_name);
541         /* this can get used as a return value */
542         if (usbdev->tty)
543                 free(usbdev->tty);
544         free(usbdev);
545 }
546
547 #define USB_DEVICES     "/sys/bus/usb/devices"
548
549 static int
550 dir_filter_dev(const struct dirent *d)
551 {
552         const char      *n = d->d_name;
553         char    c;
554
555         while ((c = *n++)) {
556                 if (isdigit(c))
557                         continue;
558                 if (c == '-')
559                         continue;
560                 if (c == '.' && n != d->d_name + 1)
561                         continue;
562                 return 0;
563         }
564         return 1;
565 }
566
567 struct altos_list {
568         struct altos_usbdev     **dev;
569         int                     current;
570         int                     ndev;
571 };
572
573 struct altos_list *
574 altos_list_start(void)
575 {
576         int                     e;
577         struct dirent           **ents;
578         char                    *dir;
579         struct altos_usbdev     *dev;
580         struct altos_list       *devs;
581         int                     n;
582
583         devs = calloc(1, sizeof (struct altos_list));
584         if (!devs)
585                 return NULL;
586
587         n = scandir (USB_DEVICES, &ents,
588                      dir_filter_dev,
589                      alphasort);
590         if (!n)
591                 return 0;
592         for (e = 0; e < n; e++) {
593                 dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
594                 dev = usb_scan_device(dir);
595                 if (!dev)
596                         continue;
597                 free(dir);
598                 if (devs->dev)
599                         devs->dev = realloc(devs->dev,
600                                             (devs->ndev + 1) * sizeof (struct usbdev *));
601                 else
602                         devs->dev = malloc (sizeof (struct usbdev *));
603                 devs->dev[devs->ndev++] = dev;
604         }
605         free(ents);
606         devs->current = 0;
607         return devs;
608 }
609
610 int
611 altos_list_next(struct altos_list *list, struct altos_device *device)
612 {
613         struct altos_usbdev *dev;
614         if (list->current >= list->ndev) {
615                 printf ("end\n");
616                 return 0;
617         }
618         dev = list->dev[list->current];
619         strcpy(device->name, dev->product_name);
620         device->vendor = dev->idVendor;
621         device->product = dev->idProduct;
622         strcpy(device->path, dev->tty);
623         device->serial = dev->serial;
624         list->current++;
625         return 1;
626 }
627
628 void
629 altos_list_finish(struct altos_list *usbdevs)
630 {
631         int     i;
632
633         if (!usbdevs)
634                 return;
635         for (i = 0; i < usbdevs->ndev; i++)
636                 usbdev_free(usbdevs->dev[i]);
637         free(usbdevs);
638 }
639
640 struct altos_bt_list {
641         inquiry_info    *ii;
642         int             sock;
643         int             dev_id;
644         int             rsp;
645         int             num_rsp;
646 };
647
648 #define INQUIRY_MAX_RSP 255
649
650 struct altos_bt_list *
651 altos_bt_list_start(int inquiry_time)
652 {
653         struct altos_bt_list    *bt_list;
654
655         bt_list = calloc(1, sizeof (struct altos_bt_list));
656         if (!bt_list)
657                 goto no_bt_list;
658
659         bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
660         if (!bt_list->ii)
661                 goto no_ii;
662         bt_list->dev_id = hci_get_route(NULL);
663         if (bt_list->dev_id < 0)
664                 goto no_dev_id;
665
666         bt_list->sock = hci_open_dev(bt_list->dev_id);
667         if (bt_list->sock < 0)
668                 goto no_sock;
669
670         bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
671                                        inquiry_time,
672                                        INQUIRY_MAX_RSP,
673                                        NULL,
674                                        &bt_list->ii,
675                                        IREQ_CACHE_FLUSH);
676         if (bt_list->num_rsp < 0)
677                 goto no_rsp;
678
679         bt_list->rsp = 0;
680         return bt_list;
681
682 no_rsp:
683         close(bt_list->sock);
684 no_sock:
685 no_dev_id:
686         free(bt_list->ii);
687 no_ii:
688         free(bt_list);
689 no_bt_list:
690         return NULL;
691 }
692
693 int
694 altos_bt_list_next(struct altos_bt_list *bt_list,
695                    struct altos_bt_device *device)
696 {
697         inquiry_info    *ii;
698
699         if (bt_list->rsp >= bt_list->num_rsp)
700                 return 0;
701
702         ii = &bt_list->ii[bt_list->rsp];
703         ba2str(&ii->bdaddr, device->addr);
704         memset(&device->name, '\0', sizeof (device->name));
705         if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
706                                  sizeof (device->name),
707                                  device->name, 0) < 0) {
708                 strcpy(device->name, "[unknown]");
709         }
710         bt_list->rsp++;
711         return 1;
712 }
713
714 void
715 altos_bt_list_finish(struct altos_bt_list *bt_list)
716 {
717         close(bt_list->sock);
718         free(bt_list->ii);
719         free(bt_list);
720 }
721
722 void
723 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
724 {
725         strncpy(device->name, name, sizeof (device->name));
726         device->name[sizeof(device->name)-1] = '\0';
727         strncpy(device->addr, addr, sizeof (device->addr));
728         device->addr[sizeof(device->addr)-1] = '\0';
729 }
730
731 struct altos_file *
732 altos_bt_open(struct altos_bt_device *device)
733 {
734         struct sockaddr_rc addr = { 0 };
735         int     s, status;
736         struct altos_file *file;
737
738         file = calloc(1, sizeof (struct altos_file));
739         if (!file)
740                 goto no_file;
741         file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
742         if (file->fd < 0) {
743                 altos_set_last_posix_error();
744                 goto no_sock;
745         }
746
747         addr.rc_family = AF_BLUETOOTH;
748         addr.rc_channel = 1;
749         str2ba(device->addr, &addr.rc_bdaddr);
750
751         status = connect(file->fd,
752                          (struct sockaddr *)&addr,
753                          sizeof(addr));
754         if (status < 0) {
755                 altos_set_last_posix_error();
756                 goto no_link;
757         }
758         sleep(1);
759
760 #ifdef USE_POLL
761         pipe(file->pipe);
762 #else
763         file->out_fd = dup(file->fd);
764 #endif
765         return file;
766 no_link:
767         close(s);
768 no_sock:
769         free(file);
770 no_file:
771         return NULL;
772 }
773
774 #endif
775
776 #ifdef DARWIN
777
778 #include <IOKitLib.h>
779 #include <IOKit/usb/USBspec.h>
780 #include <sys/param.h>
781 #include <paths.h>
782 #include <CFNumber.h>
783 #include <IOBSD.h>
784 #include <string.h>
785 #include <stdio.h>
786 #include <stdlib.h>
787
788 struct altos_list {
789         io_iterator_t iterator;
790 };
791
792 static int
793 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
794 {
795         CFTypeRef entry_as_string;
796         Boolean got_string;
797
798         entry_as_string = IORegistryEntrySearchCFProperty (object,
799                                                            kIOServicePlane,
800                                                            entry,
801                                                            kCFAllocatorDefault,
802                                                            kIORegistryIterateRecursively);
803         if (entry_as_string) {
804                 got_string = CFStringGetCString(entry_as_string,
805                                                 result, result_len,
806                                                 kCFStringEncodingASCII);
807     
808                 CFRelease(entry_as_string);
809                 if (got_string)
810                         return 1;
811         }
812         return 0;
813 }
814
815 static int
816 get_number(io_object_t object, CFStringRef entry, int *result)
817 {
818         CFTypeRef entry_as_number;
819         Boolean got_number;
820         
821         entry_as_number = IORegistryEntrySearchCFProperty (object,
822                                                            kIOServicePlane,
823                                                            entry,
824                                                            kCFAllocatorDefault,
825                                                            kIORegistryIterateRecursively);
826         if (entry_as_number) {
827                 got_number = CFNumberGetValue(entry_as_number,
828                                               kCFNumberIntType,
829                                               result);
830                 if (got_number)
831                         return 1;
832         }
833         return 0;
834 }
835
836 PUBLIC struct altos_list *
837 altos_list_start(void)
838 {
839         struct altos_list *list = calloc (sizeof (struct altos_list), 1);
840         CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
841         io_iterator_t tdIterator;
842         io_object_t tdObject;
843         kern_return_t ret;
844         int i;
845
846         ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
847         if (ret != kIOReturnSuccess)
848                 return NULL;
849         return list;
850 }
851
852 PUBLIC int
853 altos_list_next(struct altos_list *list, struct altos_device *device)
854 {
855         io_object_t object;
856         char serial_string[128];
857
858         for (;;) {
859                 object = IOIteratorNext(list->iterator);
860                 if (!object)
861                         return 0;
862   
863                 if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
864                     !get_number (object, CFSTR(kUSBProductID), &device->product))
865                         continue;
866                 if (device->vendor != 0xfffe)
867                         continue;
868                 if (device->product < 0x000a || 0x0013 < device->product)
869                         continue;
870                 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
871                     get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
872                     get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
873                         device->serial = atoi(serial_string);
874                         return 1;
875                 }
876         }
877 }
878
879 PUBLIC void
880 altos_list_finish(struct altos_list *list)
881 {
882         IOObjectRelease (list->iterator);
883         free(list);
884 }
885
886 struct altos_bt_list {
887         int             sock;
888         int             dev_id;
889         int             rsp;
890         int             num_rsp;
891 };
892
893 #define INQUIRY_MAX_RSP 255
894
895 struct altos_bt_list *
896 altos_bt_list_start(int inquiry_time)
897 {
898         return NULL;
899 }
900
901 int
902 altos_bt_list_next(struct altos_bt_list *bt_list,
903                    struct altos_bt_device *device)
904 {
905         return 0;
906 }
907
908 void
909 altos_bt_list_finish(struct altos_bt_list *bt_list)
910 {
911 }
912
913 void
914 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
915 {
916         strncpy(device->name, name, sizeof (device->name));
917         device->name[sizeof(device->name)-1] = '\0';
918         strncpy(device->addr, addr, sizeof (device->addr));
919         device->addr[sizeof(device->addr)-1] = '\0';
920 }
921
922 struct altos_file *
923 altos_bt_open(struct altos_bt_device *device)
924 {
925         return NULL;
926 }
927
928 #endif
929
930
931 #ifdef WINDOWS
932
933 #include <stdlib.h>
934 #include <windows.h>
935 #include <setupapi.h>
936
937 struct altos_list {
938         HDEVINFO        dev_info;
939         int             index;
940 };
941
942 #define USB_BUF_SIZE    64
943
944 struct altos_file {
945         HANDLE                          handle;
946         unsigned char                   out_data[USB_BUF_SIZE];
947         int                             out_used;
948         unsigned char                   in_data[USB_BUF_SIZE];
949         int                             in_used;
950         int                             in_read;
951         OVERLAPPED                      ov_read;
952         BOOL                            pend_read;
953         OVERLAPPED                      ov_write;
954 };
955
956 static void
957 _altos_set_last_windows_error(char *file, int line)
958 {
959         DWORD   error = GetLastError();
960         TCHAR   message[1024];
961         FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
962                       0,
963                       error,
964                       0,
965                       message,
966                       sizeof (message) / sizeof (TCHAR),
967                       NULL);
968         if (error != ERROR_SUCCESS)
969                 printf ("%s:%d %s\n", file, line, message);
970         altos_set_last_error(error, message);
971 }
972
973 #define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__)
974
975 PUBLIC struct altos_list *
976 altos_list_start(void)
977 {
978         struct altos_list       *list = calloc(1, sizeof (struct altos_list));
979
980         if (!list)
981                 return NULL;
982         list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
983                                              DIGCF_ALLCLASSES|DIGCF_PRESENT);
984         if (list->dev_info == INVALID_HANDLE_VALUE) {
985                 altos_set_last_windows_error();
986                 free(list);
987                 return NULL;
988         }
989         list->index = 0;
990         return list;
991 }
992
993 PUBLIC int
994 altos_list_next(struct altos_list *list, struct altos_device *device)
995 {
996         SP_DEVINFO_DATA dev_info_data;
997         BYTE            port[128];
998         DWORD           port_len;
999         char            friendlyname[256];
1000         BYTE            symbolic[256];
1001         DWORD           symbolic_len;
1002         HKEY            dev_key;
1003         unsigned int    vid, pid;
1004         int             serial;
1005         HRESULT         result;
1006         DWORD           friendlyname_type;
1007         DWORD           friendlyname_len;
1008
1009         dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
1010         while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
1011                                     &dev_info_data))
1012         {
1013                 list->index++;
1014
1015                 dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
1016                                                DICS_FLAG_GLOBAL, 0, DIREG_DEV,
1017                                                KEY_READ);
1018                 if (dev_key == INVALID_HANDLE_VALUE) {
1019                         altos_set_last_windows_error();
1020                         printf("cannot open device registry key\n");
1021                         continue;
1022                 }
1023
1024                 /* Fetch symbolic name for this device and parse out
1025                  * the vid/pid/serial info */
1026                 symbolic_len = sizeof(symbolic);
1027                 result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
1028                                          symbolic, &symbolic_len);
1029                 if (result != 0) {
1030                         altos_set_last_windows_error();
1031                         printf("cannot find SymbolicName value\n");
1032                         RegCloseKey(dev_key);
1033                         continue;
1034                 }
1035                 vid = pid = serial = 0;
1036                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
1037                        "%04X", &vid);
1038                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
1039                        "%04X", &pid);
1040                 sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
1041                        "%d", &serial);
1042
1043                 /* Fetch the com port name */
1044                 port_len = sizeof (port);
1045                 result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
1046                                          port, &port_len);
1047                 RegCloseKey(dev_key);
1048                 if (result != 0) {
1049                         altos_set_last_windows_error();
1050                         printf("failed to get PortName\n");
1051                         continue;
1052                 }
1053
1054                 /* Fetch the device description which is the device name,
1055                  * with firmware that has unique USB ids */
1056                 friendlyname_len = sizeof (friendlyname);
1057                 if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
1058                                                      &dev_info_data,
1059                                                      SPDRP_FRIENDLYNAME,
1060                                                      &friendlyname_type,
1061                                                      (BYTE *)friendlyname,
1062                                                      sizeof(friendlyname),
1063                                                      &friendlyname_len))
1064                 {
1065                         altos_set_last_windows_error();
1066                         printf("Failed to get friendlyname\n");
1067                         continue;
1068                 }
1069                 device->vendor = vid;
1070                 device->product = pid;
1071                 device->serial = serial;
1072                 strcpy(device->name, friendlyname);
1073
1074                 strcpy(device->path, (char *) port);
1075                 return 1;
1076         }
1077         result = GetLastError();
1078         if (result != ERROR_NO_MORE_ITEMS) {
1079                 altos_set_last_windows_error();
1080                 printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
1081         }
1082         return 0;
1083 }
1084
1085 PUBLIC void
1086 altos_list_finish(struct altos_list *list)
1087 {
1088         SetupDiDestroyDeviceInfoList(list->dev_info);
1089         free(list);
1090 }
1091
1092 static int
1093 altos_queue_read(struct altos_file *file)
1094 {
1095         DWORD   got;
1096         if (file->pend_read)
1097                 return LIBALTOS_SUCCESS;
1098
1099         if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
1100                 if (GetLastError() != ERROR_IO_PENDING) {
1101                         altos_set_last_windows_error();
1102                         return LIBALTOS_ERROR;
1103                 }
1104                 file->pend_read = TRUE;
1105         } else {
1106                 file->pend_read = FALSE;
1107                 file->in_read = 0;
1108                 file->in_used = got;
1109         }
1110         return LIBALTOS_SUCCESS;
1111 }
1112
1113 static int
1114 altos_wait_read(struct altos_file *file, int timeout)
1115 {
1116         DWORD   ret;
1117         DWORD   got;
1118
1119         if (!file->pend_read)
1120                 return LIBALTOS_SUCCESS;
1121
1122         if (!timeout)
1123                 timeout = INFINITE;
1124
1125         ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
1126         switch (ret) {
1127         case WAIT_OBJECT_0:
1128                 if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
1129                         altos_set_last_windows_error();
1130                         return LIBALTOS_ERROR;
1131                 }
1132                 file->pend_read = FALSE;
1133                 file->in_read = 0;
1134                 file->in_used = got;
1135                 break;
1136         case WAIT_TIMEOUT:
1137                 return LIBALTOS_TIMEOUT;
1138                 break;
1139         default:
1140                 return LIBALTOS_ERROR;
1141         }
1142         return LIBALTOS_SUCCESS;
1143 }
1144
1145 static int
1146 altos_fill(struct altos_file *file, int timeout)
1147 {
1148         int     ret;
1149
1150         if (file->in_read < file->in_used)
1151                 return LIBALTOS_SUCCESS;
1152
1153         file->in_read = file->in_used = 0;
1154
1155         ret = altos_queue_read(file);
1156         if (ret)
1157                 return ret;
1158         ret = altos_wait_read(file, timeout);
1159         if (ret)
1160                 return ret;
1161
1162         return LIBALTOS_SUCCESS;
1163 }
1164
1165 PUBLIC int
1166 altos_flush(struct altos_file *file)
1167 {
1168         DWORD           put;
1169         unsigned char   *data = file->out_data;
1170         int             used = file->out_used;
1171         DWORD           ret;
1172
1173         while (used) {
1174                 if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
1175                         if (GetLastError() != ERROR_IO_PENDING) {
1176                                 altos_set_last_windows_error();
1177                                 printf ("\tflush write error\n");
1178                                 return LIBALTOS_ERROR;
1179                         }
1180                         ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
1181                         switch (ret) {
1182                         case WAIT_OBJECT_0:
1183                                 if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
1184                                         altos_set_last_windows_error();
1185                                         printf ("\tflush result error\n");
1186                                         return LIBALTOS_ERROR;
1187                                 }
1188                                 break;
1189                         default:
1190                                 altos_set_last_windows_error();
1191                                 printf ("\tflush wait error\n");
1192                                 return LIBALTOS_ERROR;
1193                         }
1194                 }
1195                 data += put;
1196                 used -= put;
1197         }
1198         file->out_used = 0;
1199         return LIBALTOS_SUCCESS;
1200 }
1201
1202 PUBLIC struct altos_file *
1203 altos_open(struct altos_device *device)
1204 {
1205         struct altos_file       *file = calloc (1, sizeof (struct altos_file));
1206         char    full_name[64];
1207         COMMTIMEOUTS timeouts;
1208
1209         if (!file)
1210                 return NULL;
1211
1212         strcpy(full_name, "\\\\.\\");
1213         strcat(full_name, device->path);
1214         file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
1215                                   0, NULL, OPEN_EXISTING,
1216                                   FILE_FLAG_OVERLAPPED, NULL);
1217         if (file->handle == INVALID_HANDLE_VALUE) {
1218                 altos_set_last_windows_error();
1219                 printf ("cannot open %s\n", full_name);
1220                 free(file);
1221                 return NULL;
1222         }
1223         file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1224         file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
1225
1226         timeouts.ReadIntervalTimeout = MAXDWORD;
1227         timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
1228         timeouts.ReadTotalTimeoutConstant = 1 << 30;    /* almost forever */
1229         timeouts.WriteTotalTimeoutMultiplier = 0;
1230         timeouts.WriteTotalTimeoutConstant = 0;
1231         SetCommTimeouts(file->handle, &timeouts);
1232
1233         return file;
1234 }
1235
1236 PUBLIC void
1237 altos_close(struct altos_file *file)
1238 {
1239         if (file->handle != INVALID_HANDLE_VALUE) {
1240                 CloseHandle(file->handle);
1241                 file->handle = INVALID_HANDLE_VALUE;
1242                 SetEvent(file->ov_read.hEvent);
1243                 SetEvent(file->ov_write.hEvent);
1244                 CloseHandle(file->ov_read.hEvent);
1245                 CloseHandle(file->ov_write.hEvent);
1246         }
1247 }
1248
1249 PUBLIC void
1250 altos_free(struct altos_file *file)
1251 {
1252         altos_close(file);
1253         free(file);
1254 }
1255
1256 PUBLIC int
1257 altos_putchar(struct altos_file *file, char c)
1258 {
1259         int     ret;
1260
1261         if (file->out_used == USB_BUF_SIZE) {
1262                 ret = altos_flush(file);
1263                 if (ret)
1264                         return ret;
1265         }
1266         file->out_data[file->out_used++] = c;
1267         if (file->out_used == USB_BUF_SIZE)
1268                 return altos_flush(file);
1269         return LIBALTOS_SUCCESS;
1270 }
1271
1272 PUBLIC int
1273 altos_getchar(struct altos_file *file, int timeout)
1274 {
1275         int     ret;
1276         while (file->in_read == file->in_used) {
1277                 if (file->handle == INVALID_HANDLE_VALUE)
1278                         return LIBALTOS_ERROR;
1279                 ret = altos_fill(file, timeout);
1280                 if (ret)
1281                         return ret;
1282         }
1283         return file->in_data[file->in_read++];
1284 }
1285
1286 struct altos_bt_list *
1287 altos_bt_list_start(int inquiry_time)
1288 {
1289         return NULL;
1290 }
1291
1292 int
1293 altos_bt_list_next(struct altos_bt_list *bt_list,
1294                    struct altos_bt_device *device)
1295 {
1296         return 0;
1297 }
1298
1299 void
1300 altos_bt_list_finish(struct altos_bt_list *bt_list)
1301 {
1302         free(bt_list);
1303 }
1304
1305 void
1306 altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
1307 {
1308         strncpy(device->name, name, sizeof (device->name));
1309         device->name[sizeof(device->name)-1] = '\0';
1310         strncpy(device->addr, addr, sizeof (device->addr));
1311         device->addr[sizeof(device->addr)-1] = '\0';
1312 }
1313
1314 struct altos_file *
1315 altos_bt_open(struct altos_bt_device *device)
1316 {
1317         return NULL;
1318 }
1319
1320 #endif