ao-tools: Add ao-chaosread
[fw/altos] / ao-tools / ao-chaosread / ao-chaosread.c
1 /*
2  * Copyright © 2016 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 <stdio.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <libusb.h>
23 #include <getopt.h>
24 #include <string.h>
25 #include <strings.h>
26
27 #define CHAOS_SIZE      64
28
29 #define CHAOS_VENDOR    0x1d50
30 #define CHAOS_PRODUCT   0x60c6
31
32 struct chaoskey {
33         libusb_context          *ctx;
34         libusb_device_handle    *handle;
35         int                     kernel_active;
36 };
37
38 libusb_device_handle *
39 chaoskey_match(libusb_device *dev, char *match_serial)
40 {
41         struct libusb_device_descriptor desc;
42         int ret;
43         int match_len;
44         char *device_serial = NULL;
45         libusb_device_handle *handle = NULL;
46
47         ret = libusb_get_device_descriptor(dev, &desc);
48         if (ret < 0) {
49                 fprintf(stderr, "failed to get device descriptor: %s\n", libusb_strerror(ret));
50                 return 0;
51         }
52
53         if (desc.idVendor != CHAOS_VENDOR)
54                 return NULL;
55         if (desc.idProduct != CHAOS_PRODUCT)
56                 return NULL;
57
58         ret = libusb_open(dev, &handle);
59
60         if (match_serial == NULL)
61                 return handle;
62
63         if (ret < 0) {
64                 fprintf(stderr, "failed to open device: %s\n", libusb_strerror(ret));
65                 return NULL;
66         }
67
68         match_len = strlen(match_serial);
69         device_serial = malloc(match_len + 2);
70
71         if (!device_serial) {
72                 fprintf(stderr, "malloc failed\n");
73                 goto out;
74         }
75
76         ret = libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, device_serial, match_len + 1);
77
78         if (ret < 0) {
79                 fprintf(stderr, "failed to get serial number: %s\n", libusb_strerror(ret));
80                 goto out;
81         }
82
83         device_serial[ret] = '\0';
84
85         ret = strcmp(device_serial, match_serial);
86         free(device_serial);
87         if (ret)
88                 goto out;
89
90         return handle;
91
92 out:
93         free(device_serial);
94         if (handle)
95                 libusb_close(handle);
96         return 0;
97 }
98
99 struct chaoskey *
100 chaoskey_open(char *serial)
101 {
102         struct chaoskey *ck;
103         int             ret;
104         ssize_t         num;
105         libusb_device   **list;
106         libusb_device   *device = NULL;
107         int             d;
108
109         ck = calloc(sizeof (struct chaoskey), 1);
110         if (!ck)
111                 goto out;
112         ret = libusb_init(&ck->ctx);
113         if (ret ) {
114                 fprintf(stderr, "libusb_init failed: %s\n", libusb_strerror(ret));
115                 goto out;
116         }
117
118         num = libusb_get_device_list(ck->ctx, &list);
119         if (num < 0) {
120                 fprintf(stderr, "libusb_get_device_list failed: %s\n", libusb_strerror(num));
121                 goto out;
122         }
123
124         for (d = 0; d < num; d++) {
125                 libusb_device_handle *handle;
126
127                 handle = chaoskey_match(list[d], serial);
128                 if (handle) {
129                         ck->handle = handle;
130                         break;
131                 }
132         }
133
134         libusb_free_device_list(list, 1);
135
136         if (!ck->handle) {
137                 if (serial)
138                         fprintf (stderr, "No chaoskey matching %s\n", serial);
139                 else
140                         fprintf (stderr, "No chaoskey\n");
141                 goto out;
142         }
143
144         ck->kernel_active = libusb_kernel_driver_active(ck->handle, 0);
145         if (ck->kernel_active) {
146                 ret = libusb_detach_kernel_driver(ck->handle, 0);
147                 if (ret)
148                         goto out;
149         }
150
151         ret = libusb_claim_interface(ck->handle, 0);
152         if (ret)
153                 goto out;
154
155         return ck;
156 out:
157         if (ck->kernel_active)
158                 libusb_attach_kernel_driver(ck->handle, 0);
159         if (ck->ctx)
160                 libusb_exit(ck->ctx);
161         free(ck);
162         return NULL;
163 }
164
165 void
166 chaoskey_close(struct chaoskey *ck)
167 {
168         libusb_release_interface(ck->handle, 0);
169         if (ck->kernel_active)
170                 libusb_attach_kernel_driver(ck->handle, 0);
171         libusb_close(ck->handle);
172         libusb_exit(ck->ctx);
173         free(ck);
174 }
175
176 void
177 chaoskey_transfer_callback(struct libusb_transfer *transfer)
178 {
179         struct chaoskey *ck = transfer->user_data;
180 }
181
182 #define ENDPOINT        0x86
183
184 int
185 chaoskey_read(struct chaoskey *ck, uint8_t *buffer, int len)
186 {
187         int     total = 0;
188
189         while (len) {
190                 int     ret;
191                 int     transferred;
192
193                 ret = libusb_bulk_transfer(ck->handle, ENDPOINT, buffer, len, &transferred, 10000);
194                 if (ret) {
195                         if (total)
196                                 return total;
197                         else {
198                                 errno = EIO;
199                                 return -1;
200                         }
201                 }
202                 len -= transferred;
203                 buffer += transferred;
204         }
205 }
206
207 static const struct option options[] = {
208         { .name = "serial", .has_arg = 1, .val = 's' },
209         { .name = "length", .has_arg = 1, .val = 'l' },
210         { 0, 0, 0, 0},
211 };
212
213 static void usage(char *program)
214 {
215         fprintf(stderr, "usage: %s [--serial=<serial>] [--length=<length>[kMG]]\n", program);
216         exit(1);
217 }
218
219 int
220 main (int argc, char **argv)
221 {
222         struct chaoskey *ck;
223         char    buf[1024];
224         int     got;
225         int     c;
226         char    *serial = NULL;
227         char    *length_string;
228         char    *length_end;
229         unsigned long   length = sizeof(buf);
230         int             this_time;
231
232         while ((c = getopt_long(argc, argv, "s:l:", options, NULL)) != -1) {
233                 switch (c) {
234                 case 's':
235                         serial = optarg;
236                         break;
237                 case 'l':
238                         length_string = optarg;
239                         length = strtoul(length_string, &length_end, 10);
240                         if (!strcasecmp(length_end, "k"))
241                                 length *= 1024;
242                         else if (!strcasecmp(length_end, "m"))
243                                 length *= 1024 * 1024;
244                         else if (!strcasecmp(length_end, "g"))
245                                 length *= 1024 * 1024 * 1024;
246                         else if (strlen(length_end))
247                                  usage(argv[0]);
248                         break;
249                 default:
250                         usage(argv[0]);
251                         break;
252                 }
253         }
254
255         ck = chaoskey_open(serial);
256         if (!ck)
257                 exit(1);
258
259         while (length) {
260                 this_time = sizeof(buf);
261                 if (length < sizeof(buf))
262                         this_time = (int) length;
263                 got = chaoskey_read(ck, buf, this_time);
264                 if (got < 0) {
265                         perror("read");
266                         exit(1);
267                 }
268                 write(1, buf, got);
269                 length -= got;
270         }
271         exit(0);
272 }