Parse the USB serial number as an integer.
[fw/altos] / ao-tools / 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         uint8_t                 value;
42         uint8_t                 set;
43 };
44
45 struct cp_usb_async *
46 cp_usb_async_open(void)
47 {
48         struct cp_usb_async *cp;
49         int ret;
50
51         cp = calloc(sizeof (struct cp_usb_async), 1);
52         if (!cp)
53                 return NULL;
54         ret = libusb_init(&cp->ctx);
55         if (ret) {
56                 free(cp);
57                 return NULL;
58         }
59         cp->handle = libusb_open_device_with_vid_pid(cp->ctx,
60                                                      0x10c4, 0xea60);
61         cp->ack = -1;
62         if (!cp->handle) {
63                 fprintf(stderr, "Cannot find USB device 10c4:ea60\n");
64                 libusb_exit(cp->ctx);
65                 free(cp);
66                 return NULL;
67         }
68         cp->value = 0;
69         cp->set = 0;
70         return cp;
71 }
72
73 void
74 cp_usb_async_close(struct cp_usb_async *cp)
75 {
76         libusb_close(cp->handle);
77         libusb_exit(cp->ctx);
78         free(cp);
79 }
80
81 static void
82 cp_usb_async_transfer_callback(struct libusb_transfer *transfer)
83 {
84         struct cp_usb_async *cp = transfer->user_data;
85         int p;
86
87         for (p = 0; p < cp->p; p++)
88                 if (cp->packet[p].transfer == transfer)
89                         break;
90         if (p == cp->p) {
91                 fprintf(stderr, "unknown transfer\n");
92                 return;
93         }
94         switch (cp->packet[p].direction) {
95         case packet_read:
96                 ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack read %d 0x%02x\n",
97                             p, cp->packet[p].data[8]);
98                 *cp->packet[p].valuep = cp->packet[p].data[8];
99                 break;
100         case packet_write:
101                 ccdbg_debug(CC_DEBUG_USB_ASYNC, "ack write %d\n", p);
102                 break;
103         }
104         if (p > cp->ack)
105                 cp->ack = p;
106 }
107
108 void
109 cp_usb_async_write(struct cp_usb_async *cp, uint8_t mask, uint8_t value)
110 {
111         int     p;
112         uint16_t gpio_set;
113         int     ret;
114
115         if (cp->set) {
116                 value = (cp->value & ~mask) | (value & mask);
117                 mask = value ^ cp->value;
118         }
119         cp->set = 1;
120         cp->value = value;
121         gpio_set = ((uint16_t) value << 8) | mask;
122         if (cp->p == MAX_OUTSTANDING)
123                 cp_usb_async_sync(cp);
124         p = cp->p;
125         if (!cp->packet[p].transfer)
126                 cp->packet[p].transfer = libusb_alloc_transfer(0);
127         cp->packet[p].direction = packet_write;
128         libusb_fill_control_setup(cp->packet[p].data,
129                                   0x40,                 /* request */
130                                   0xff,                 /* request type */
131                                   0x37e1,               /* value */
132                                   gpio_set,             /* index */
133                                   0);                   /* length */
134
135         libusb_fill_control_transfer(cp->packet[p].transfer,
136                                      cp->handle,
137                                      cp->packet[p].data,
138                                      cp_usb_async_transfer_callback,
139                                      cp,
140                                      CP_TIMEOUT);
141         ccdbg_debug(CC_DEBUG_USB_ASYNC, "Write packet %d 0x%x 0x%x\n", p, mask, value);
142         ret = libusb_submit_transfer(cp->packet[p].transfer);
143         if (ret)
144                 fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
145         cp->p++;
146 }
147
148 void
149 cp_usb_async_read(struct cp_usb_async *cp, uint8_t *valuep)
150 {
151         int     p;
152         int     ret;
153
154         if (cp->p == MAX_OUTSTANDING)
155                 cp_usb_async_sync(cp);
156         p = cp->p;
157         if (!cp->packet[p].transfer)
158                 cp->packet[p].transfer = libusb_alloc_transfer(0);
159         cp->packet[p].valuep = valuep;
160         cp->packet[p].direction = packet_read;
161         libusb_fill_control_setup(cp->packet[p].data,
162                                   0xc0,                 /* request */
163                                   0xff,                 /* request type */
164                                   0x00c2,               /* value */
165                                   0,                    /* index */
166                                   1);                   /* length */
167
168         libusb_fill_control_transfer(cp->packet[p].transfer,
169                                      cp->handle,
170                                      cp->packet[p].data,
171                                      cp_usb_async_transfer_callback,
172                                      cp,
173                                      CP_TIMEOUT);
174         ccdbg_debug(CC_DEBUG_USB_ASYNC, "Read packet %d\n", p);
175         ret = libusb_submit_transfer(cp->packet[p].transfer);
176         if (ret)
177                 fprintf(stderr, "libusb_submit_transfer failed %d\n", ret);
178         cp->p++;
179 }
180
181 void
182 cp_usb_async_sync(struct cp_usb_async *cp)
183 {
184         while (cp->ack < cp->p - 1) {
185                 libusb_handle_events(cp->ctx);
186         }
187         cp->p = 0;
188         cp->ack = -1;
189 }