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