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