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