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