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