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