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