Switch to libusb-1.0 and use async interface.
[fw/altos] / lib / cp-usb-async.c
1 /*
2  * Copyright © 2008 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include <stdio.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include "cp-usb-async.h"
24 #include "ccdbg-debug.h"
25
26 #define MAX_OUTSTANDING         256
27 #define CP_TIMEOUT              1000    /* ms */
28
29 struct cp_usb_packet {
30         struct libusb_transfer  *transfer;
31         enum { packet_read, packet_write } direction;
32         unsigned char           data[9];
33         uint8_t                 *valuep;
34 };
35
36 struct cp_usb_async {
37         libusb_context          *ctx;
38         libusb_device_handle    *handle;
39         struct cp_usb_packet    packet[MAX_OUTSTANDING];
40         int                     p, ack;
41 };
42
43 struct cp_usb_async *
44 cp_usb_async_open(void)
45 {
46         struct cp_usb_async *cp;
47         int ret;
48
49         cp = calloc(sizeof (struct cp_usb_async), 1);
50         if (!cp)
51                 return NULL;
52         ret = libusb_init(&cp->ctx);
53         if (ret) {
54                 free(cp);
55                 return NULL;
56         }
57         cp->handle = libusb_open_device_with_vid_pid(cp->ctx,
58                                                      0x10c4, 0xea60);
59         if (!cp->handle) {
60                 libusb_exit(cp->ctx);
61                 free(cp);
62                 return NULL;
63         }
64         return cp;
65 }
66
67 void
68 cp_usb_async_close(struct cp_usb_async *cp)
69 {
70         libusb_close(cp->handle);
71         libusb_exit(cp->ctx);
72         free(cp);
73 }
74
75 static void
76 cp_usb_async_transfer_callback(struct libusb_transfer *transfer)
77 {
78         struct cp_usb_async *cp = transfer->user_data;
79         int p;
80
81         for (p = 0; p < cp->p; p++)
82                 if (cp->packet[p].transfer == transfer)
83                         break;
84         if (p == cp->p) {
85                 fprintf(stderr, "unknown transfer\n");
86                 return;
87         }
88         switch (cp->packet[p].direction) {
89         case packet_read:
90                 ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack read %d 0x%02x\n",
91                             p, cp->packet[p].data[8]);
92                 *cp->packet[p].valuep = cp->packet[p].data[8];
93                 break;
94         case packet_write:
95                 ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack write %d\n", p);
96                 break;
97         }
98         if (p > cp->ack)
99                 cp->ack = p;
100 }
101
102 void
103 cp_usb_async_write(struct cp_usb_async *cp, uint8_t mask, uint8_t value)
104 {
105         int     p;
106         uint16_t gpio_set = ((uint16_t) value << 8) | mask;
107         int     ret;
108
109         if (cp->p == MAX_OUTSTANDING)
110                 cp_usb_async_sync(cp);
111         p = cp->p;
112         if (!cp->packet[p].transfer)
113                 cp->packet[p].transfer = libusb_alloc_transfer(0);
114         cp->packet[p].direction = packet_write;
115         libusb_fill_control_setup(cp->packet[p].data,
116                                   0x40,                 /* request */
117                                   0xff,                 /* request type */
118                                   0x37e1,               /* value */
119                                   gpio_set,             /* index */
120                                   0);                   /* length */
121         
122         libusb_fill_control_transfer(cp->packet[p].transfer,
123                                      cp->handle,
124                                      cp->packet[p].data,
125                                      cp_usb_async_transfer_callback,
126                                      cp,
127                                      CP_TIMEOUT);
128         ccdbg_debug(CC_DEBUG_USB_ASYNC, "Write packet %d 0x%x 0x%x\n", p, mask, value);
129         ret = libusb_submit_transfer(cp->packet[p].transfer);
130         if (ret)
131                 fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
132         cp->p++;
133 }
134
135 void
136 cp_usb_async_read(struct cp_usb_async *cp, uint8_t *valuep)
137 {
138         int     p;
139         int     ret;
140
141         if (cp->p == MAX_OUTSTANDING)
142                 cp_usb_async_sync(cp);
143         p = cp->p;
144         if (!cp->packet[p].transfer)
145                 cp->packet[p].transfer = libusb_alloc_transfer(0);
146         cp->packet[p].valuep = valuep;
147         cp->packet[p].direction = packet_read;
148         libusb_fill_control_setup(cp->packet[p].data,
149                                   0xc0,                 /* request */
150                                   0xff,                 /* request type */
151                                   0x00c2,               /* value */
152                                   0,                    /* index */
153                                   1);                   /* length */
154         
155         libusb_fill_control_transfer(cp->packet[p].transfer,
156                                      cp->handle,
157                                      cp->packet[p].data,
158                                      cp_usb_async_transfer_callback,
159                                      cp,
160                                      CP_TIMEOUT);
161         ccdbg_debug(CC_DEBUG_USB_ASYNC, "Read packet %d\n", p);
162         ret = libusb_submit_transfer(cp->packet[p].transfer);
163         if (ret)
164                 fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
165         cp->p++;
166 }
167
168 void
169 cp_usb_async_sync(struct cp_usb_async *cp)
170 {
171         while (cp->ack < cp->p - 1) {
172                 libusb_handle_events(cp->ctx);
173         }
174         cp->p = 0;
175         cp->ack = 0;
176 }