7b138f6cccdb0ef63d69b1047a9ef05b28c4933e
[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 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, 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         free(device_serial);
95         if (handle)
96                 libusb_close(handle);
97         return 0;
98 }
99
100 struct chaoskey *
101 chaoskey_open(char *serial)
102 {
103         struct chaoskey *ck;
104         int             ret;
105         ssize_t         num;
106         libusb_device   **list;
107         libusb_device   *device = NULL;
108         int             d;
109
110         ck = calloc(sizeof (struct chaoskey), 1);
111         if (!ck)
112                 goto out;
113         ret = libusb_init(&ck->ctx);
114         if (ret ) {
115                 fprintf(stderr, "libusb_init failed: %s\n", libusb_strerror(ret));
116                 goto out;
117         }
118
119         num = libusb_get_device_list(ck->ctx, &list);
120         if (num < 0) {
121                 fprintf(stderr, "libusb_get_device_list failed: %s\n", libusb_strerror(num));
122                 goto out;
123         }
124
125         for (d = 0; d < num; d++) {
126                 libusb_device_handle *handle;
127
128                 handle = chaoskey_match(list[d], serial);
129                 if (handle) {
130                         ck->handle = handle;
131                         break;
132                 }
133         }
134
135         libusb_free_device_list(list, 1);
136
137         if (!ck->handle) {
138                 if (serial)
139                         fprintf (stderr, "No chaoskey matching %s\n", serial);
140                 else
141                         fprintf (stderr, "No chaoskey\n");
142                 goto out;
143         }
144
145         ck->kernel_active = libusb_kernel_driver_active(ck->handle, 0);
146         if (ck->kernel_active) {
147                 ret = libusb_detach_kernel_driver(ck->handle, 0);
148                 if (ret)
149                         goto out;
150         }
151
152         ret = libusb_claim_interface(ck->handle, 0);
153         if (ret)
154                 goto out;
155
156         return ck;
157 out:
158         if (ck->kernel_active)
159                 libusb_attach_kernel_driver(ck->handle, 0);
160         if (ck->ctx)
161                 libusb_exit(ck->ctx);
162         free(ck);
163         return NULL;
164 }
165
166 void
167 chaoskey_close(struct chaoskey *ck)
168 {
169         libusb_release_interface(ck->handle, 0);
170         if (ck->kernel_active)
171                 libusb_attach_kernel_driver(ck->handle, 0);
172         libusb_close(ck->handle);
173         libusb_exit(ck->ctx);
174         free(ck);
175 }
176
177 void
178 chaoskey_transfer_callback(struct libusb_transfer *transfer)
179 {
180         struct chaoskey *ck = transfer->user_data;
181 }
182
183 #define ENDPOINT        0x86
184
185 int
186 chaoskey_read(struct chaoskey *ck, uint8_t *buffer, int len)
187 {
188         int     total = 0;
189
190         while (len) {
191                 int     ret;
192                 int     transferred;
193
194                 ret = libusb_bulk_transfer(ck->handle, ENDPOINT, buffer, len, &transferred, 10000);
195                 if (ret) {
196                         if (total)
197                                 return total;
198                         else {
199                                 errno = EIO;
200                                 return -1;
201                         }
202                 }
203                 len -= transferred;
204                 buffer += transferred;
205         }
206 }
207
208 static const struct option options[] = {
209         { .name = "serial", .has_arg = 1, .val = 's' },
210         { .name = "length", .has_arg = 1, .val = 'l' },
211         { 0, 0, 0, 0},
212 };
213
214 static void usage(char *program)
215 {
216         fprintf(stderr, "usage: %s [--serial=<serial>] [--length=<length>[kMG]]\n", program);
217         exit(1);
218 }
219
220 int
221 main (int argc, char **argv)
222 {
223         struct chaoskey *ck;
224         char    buf[1024];
225         int     got;
226         int     c;
227         char    *serial = NULL;
228         char    *length_string;
229         char    *length_end;
230         unsigned long   length = sizeof(buf);
231         int             this_time;
232
233         while ((c = getopt_long(argc, argv, "s:l:", options, NULL)) != -1) {
234                 switch (c) {
235                 case 's':
236                         serial = optarg;
237                         break;
238                 case 'l':
239                         length_string = optarg;
240                         length = strtoul(length_string, &length_end, 10);
241                         if (!strcasecmp(length_end, "k"))
242                                 length *= 1024;
243                         else if (!strcasecmp(length_end, "m"))
244                                 length *= 1024 * 1024;
245                         else if (!strcasecmp(length_end, "g"))
246                                 length *= 1024 * 1024 * 1024;
247                         else if (strlen(length_end))
248                                  usage(argv[0]);
249                         break;
250                 default:
251                         usage(argv[0]);
252                         break;
253                 }
254         }
255
256         ck = chaoskey_open(serial);
257         if (!ck)
258                 exit(1);
259
260         while (length) {
261                 this_time = sizeof(buf);
262                 if (length < sizeof(buf))
263                         this_time = (int) length;
264                 got = chaoskey_read(ck, buf, this_time);
265                 if (got < 0) {
266                         perror("read");
267                         exit(1);
268                 }
269                 write(1, buf, got);
270                 length -= got;
271         }
272         exit(0);
273 }