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