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