417c8fe5688b2ff295b89ca6fc7e6afcb838b141
[fw/altos] / ao-tools / 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
20 #define USE_DARWIN
21
22 #ifdef USE_DARWIN
23
24 #include <IOKitLib.h>
25 #include <IOKit/usb/USBspec.h>
26 #include <sys/param.h>
27 #include <paths.h>
28 #include <CFNumber.h>
29 #include <IOBSD.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <poll.h>
35 #include <termios.h>
36 #include <errno.h>
37
38 struct altos_list {
39   io_iterator_t iterator;
40 };
41
42 static int
43 get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
44 {
45   CFTypeRef entry_as_string;
46   Boolean got_string;
47
48   entry_as_string = IORegistryEntrySearchCFProperty (object,
49                                                      kIOServicePlane,
50                                                      entry,
51                                                      kCFAllocatorDefault,
52                                                      kIORegistryIterateRecursively);
53   if (entry_as_string) {
54     got_string = CFStringGetCString(entry_as_string,
55                                     result, result_len,
56                                     kCFStringEncodingASCII);
57     
58     CFRelease(entry_as_string);
59     if (got_string)
60       return 1;
61   }
62   return 0;
63 }
64
65 int
66 altos_init(void)
67 {
68         return 1;
69 }
70
71 void
72 altos_fini(void)
73 {
74 }
75
76 struct altos_list *
77 altos_list_start(void)
78 {
79         struct altos_list *list = calloc (sizeof (struct altos_list), 1);
80         CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
81         UInt32 vendor = 0xfffe, product = 0x000a;
82         CFNumberRef vendor_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor);
83         CFNumberRef product_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product);
84         io_iterator_t tdIterator;
85         io_object_t tdObject;
86   
87         CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBVendorID), vendor_ref);
88         CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBProductID), product_ref);
89
90         IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
91   
92         CFRelease(vendor_ref);
93         CFRelease(product_ref);
94         return list;
95 }
96
97 int
98 altos_list_next(struct altos_list *list, struct altos_device *device)
99 {
100         io_object_t object;
101         char serial_string[128];
102
103         for (;;) {
104                 object = IOIteratorNext(list->iterator);
105                 if (!object)
106                         return 0;
107   
108                 if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
109                     get_string (object, CFSTR("USB Product Name"), device->product, sizeof (device->product)) &&
110                     get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
111                         device->serial = atoi(serial_string);
112                         return 1;
113                 }
114         }
115 }
116
117 void
118 altos_list_finish(struct altos_list *list)
119 {
120   IOObjectRelease (list->iterator);
121   free(list);
122 }
123
124
125 #define USB_BUF_SIZE    64
126
127 struct altos_file {
128         int                             fd;
129         unsigned char                   out_data[USB_BUF_SIZE];
130         int                             out_used;
131         unsigned char                   in_data[USB_BUF_SIZE];
132         int                             in_used;
133         int                             in_read;
134 };
135
136 void
137 altos_test(char *path)
138 {
139         int n;
140         char buf[16];
141         int fd;
142         struct termios term;
143
144         fd = open(path, O_RDWR | O_NOCTTY);
145         if (fd < 0) {
146                 perror(path);
147                 return;
148         }
149         if (ioctl(fd, TIOCEXCL, (char *) 0) < 0) {
150                 perror("TIOCEXCL");
151                 close (fd);
152                 return;
153         }
154
155         n = tcgetattr(fd, &term);
156         if (n < 0) {
157                 perror("tcgetattr");
158                 close(fd);
159                 return;
160         }
161         cfmakeraw(&term);
162         term.c_cc[VMIN] = 0;
163         term.c_cc[VTIME] = 1;
164         n = tcsetattr(fd, TCSAFLUSH, &term);
165         if (n < 0) {
166                 perror("tcsetattr");
167                 close(fd);
168                 return;
169         }
170         write(fd, "\n?\n", 3);
171         for (;;) {
172                 n = read(fd, buf, sizeof (buf));
173                 if (n < 0) {
174                         perror("read");
175                         break;
176                 }
177                 if (n == 0)
178                         break;
179                 write(1, buf, n);
180         }
181         close(fd);
182 }
183
184 struct altos_file *
185 altos_open(struct altos_device *device)
186 {
187         struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
188         int                     ret;
189         struct termios          term;
190
191         if (!file)
192                 return NULL;
193
194         file->fd = open(device->path, O_RDWR | O_NOCTTY);
195         if (file->fd < 0) {
196                 perror(device->path);
197                 free(file);
198                 return NULL;
199         }
200         ret = tcgetattr(file->fd, &term);
201         if (ret < 0) {
202                 perror("tcgetattr");
203                 close(file->fd);
204                 free(file);
205                 return NULL;
206         }
207         cfmakeraw(&term);
208         term.c_cc[VMIN] = 0;
209         term.c_cc[VTIME] = 1;
210         ret = tcsetattr(file->fd, TCSAFLUSH, &term);
211         if (ret < 0) {
212                 perror("tcsetattr");
213                 close(file->fd);
214                 free(file);
215                 return NULL;
216         }
217         return file;
218 }
219
220 void
221 altos_close(struct altos_file *file)
222 {
223         close(file->fd);
224         free(file);
225 }
226
227 int
228 altos_putchar(struct altos_file *file, char c)
229 {
230         int     ret;
231
232         if (file->out_used == USB_BUF_SIZE) {
233                 ret = altos_flush(file);
234                 if (ret)
235                         return ret;
236         }
237         file->out_data[file->out_used++] = c;
238         if (file->out_used == USB_BUF_SIZE)
239                 return altos_flush(file);
240         return 0;
241 }
242
243 int
244 altos_flush(struct altos_file *file)
245 {
246         while (file->out_used) {
247                 int     ret;
248
249                 ret = write (file->fd, file->out_data, file->out_used);
250                 if (ret < 0)
251                         return -errno;
252                 if (ret) {
253                         memmove(file->out_data, file->out_data + ret,
254                                 file->out_used - ret);
255                         file->out_used -= ret;
256                 }
257         }
258 }
259
260 int
261 altos_getchar(struct altos_file *file, int timeout)
262 {
263         while (file->in_read == file->in_used) {
264                 int     ret;
265
266                 altos_flush(file);
267                 ret = read(file->fd, file->in_data, USB_BUF_SIZE);
268                 if (ret < 0)
269                         return -errno;
270                 file->in_read = 0;
271                 file->in_used = ret;
272         }
273         return file->in_data[file->in_read++];
274 }
275
276 #endif /* USE_DARWIN */
277
278 #ifdef USE_LIBUSB
279 #include <libusb.h>
280 #include <stdio.h>
281 #include <stdlib.h>
282 #include <string.h>
283
284 libusb_context  *usb_context;
285
286 int altos_init(void)
287 {
288         int     ret;
289         ret = libusb_init(&usb_context);
290         if (ret)
291                 return ret;
292         libusb_set_debug(usb_context, 3);
293         return 0;
294 }
295
296 void altos_fini(void)
297 {
298         libusb_exit(usb_context);
299         usb_context = NULL;
300 }
301
302 static libusb_device **list;
303 static ssize_t num, current;
304
305 int altos_list_start(void)
306 {
307         if (list)
308                 altos_list_finish();
309         current = 0;
310         num = libusb_get_device_list(usb_context, &list);
311         if (num == 0) {
312                 current = num = 0;
313                 list = NULL;
314                 return 0;
315         }
316         return 1;
317 }
318
319 int altos_list_next(struct altos_device *device)
320 {
321         while (current < num) {
322                 struct libusb_device_descriptor descriptor;
323                 libusb_device *usb_device = list[current++];
324
325                 if (libusb_get_device_descriptor(usb_device, &descriptor) == 0) {
326                         if (descriptor.idVendor == 0xfffe)
327                         {
328                                 libusb_device_handle    *handle;
329                                 if (libusb_open(usb_device, &handle) == 0) {
330                                         char    serial_number[256];
331                                         libusb_get_string_descriptor_ascii(handle, descriptor.iProduct,
332                                                                            device->product,
333                                                                            sizeof(device->product));
334                                         libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber,
335                                                                            serial_number,
336                                                                            sizeof (serial_number));
337                                         libusb_close(handle);
338                                         device->serial = atoi(serial_number);
339                                         device->device = usb_device;
340                                         return 1;
341                                 }
342                         }
343                 }
344         }
345         return 0;
346 }
347
348 void altos_list_finish(void)
349 {
350         if (list) {
351                 libusb_free_device_list(list, 1);
352                 list = NULL;
353         }
354 }
355
356 #define USB_BUF_SIZE    64
357
358 struct altos_file {
359         struct libusb_device            *device;
360         struct libusb_device_handle     *handle;
361         int                             out_ep;
362         int                             out_size;
363         int                             in_ep;
364         int                             in_size;
365         unsigned char                   out_data[USB_BUF_SIZE];
366         int                             out_used;
367         unsigned char                   in_data[USB_BUF_SIZE];
368         int                             in_used;
369         int                             in_read;
370 };
371
372 struct altos_file *
373 altos_open(struct altos_device *device)
374 {
375         struct altos_file               *file;
376         struct libusb_device_handle     *handle;
377         if (libusb_open(device->device, &handle) == 0) {
378                 int     ret;
379
380                 ret = libusb_claim_interface(handle, 1);
381 #if 0
382                 if (ret) {
383                         libusb_close(handle);
384                         return NULL;
385                 }
386 #endif
387                 ret = libusb_detach_kernel_driver(handle, 1);
388 #if 0
389                 if (ret) {
390                         libusb_close(handle);
391                         return NULL;
392                 }
393 #endif
394
395                 file = calloc(sizeof (struct altos_file), 1);
396                 file->device = libusb_ref_device(device->device);
397                 file->handle = handle;
398                 /* XXX should get these from the endpoint descriptors */
399                 file->out_ep = 4 | LIBUSB_ENDPOINT_OUT;
400                 file->out_size = 64;
401                 file->in_ep = 5 | LIBUSB_ENDPOINT_IN;
402                 file->in_size = 64;
403
404                 return file;
405         }
406         return NULL;
407 }
408
409 void
410 altos_close(struct altos_file *file)
411 {
412         libusb_close(file->handle);
413         libusb_unref_device(file->device);
414         file->handle = NULL;
415         free(file);
416 }
417
418 int
419 altos_putchar(struct altos_file *file, char c)
420 {
421         int     ret;
422
423         if (file->out_used == file->out_size) {
424                 ret = altos_flush(file);
425                 if (ret)
426                         return ret;
427         }
428         file->out_data[file->out_used++] = c;
429         if (file->out_used == file->out_size)
430                 return altos_flush(file);
431         return 0;
432 }
433
434 int
435 altos_flush(struct altos_file *file)
436 {
437         while (file->out_used) {
438                 int     transferred;
439                 int     ret;
440
441                 ret = libusb_bulk_transfer(file->handle,
442                                            file->out_ep,
443                                            file->out_data,
444                                            file->out_used,
445                                            &transferred,
446                                            0);
447                 if (ret)
448                         return ret;
449                 if (transferred) {
450                         memmove(file->out_data, file->out_data + transferred,
451                                 file->out_used - transferred);
452                         file->out_used -= transferred;
453                 }
454         }
455 }
456
457 int
458 altos_getchar(struct altos_file *file, int timeout)
459 {
460         while (file->in_read == file->in_used) {
461                 int     ret;
462                 int     transferred;
463
464                 altos_flush(file);
465                 ret = libusb_bulk_transfer(file->handle,
466                                            file->in_ep,
467                                            file->in_data,
468                                            file->in_size,
469                                            &transferred,
470                                            (unsigned int) timeout);
471                 if (ret)
472                         return ret;
473                 file->in_read = 0;
474                 file->in_used = transferred;
475         }
476         return file->in_data[file->in_read++];
477 }
478
479 #endif /* USE_LIBUSB */