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