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