]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bluetooth/ath3kfw/main.c
bhnd(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / usr.sbin / bluetooth / ath3kfw / main.c
1 /*-
2  * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  *
29  * $FreeBSD$
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <err.h>
38 #include <fcntl.h>
39 #include <libgen.h>
40 #include <sys/stat.h>
41 #include <sys/param.h>
42
43 #include <libusb.h>
44
45 #include "ath3k_fw.h"
46 #include "ath3k_hw.h"
47 #include "ath3k_dbg.h"
48
49 #define _DEFAULT_ATH3K_FIRMWARE_PATH    "/usr/share/firmware/ath3k/"
50
51 int     ath3k_do_debug = 0;
52 int     ath3k_do_info = 0;
53
54 struct ath3k_devid {
55         uint16_t product_id;
56         uint16_t vendor_id;
57         int is_3012;
58 };
59
60 static struct ath3k_devid ath3k_list[] = {
61
62         /* Atheros AR3012 with sflash firmware*/
63         { .vendor_id = 0x0489, .product_id = 0xe04e, .is_3012 = 1 },
64         { .vendor_id = 0x0489, .product_id = 0xe04d, .is_3012 = 1 },
65         { .vendor_id = 0x0489, .product_id = 0xe056, .is_3012 = 1 },
66         { .vendor_id = 0x0489, .product_id = 0xe057, .is_3012 = 1 },
67         { .vendor_id = 0x0489, .product_id = 0xe05f, .is_3012 = 1 },
68         { .vendor_id = 0x04c5, .product_id = 0x1330, .is_3012 = 1 },
69         { .vendor_id = 0x04ca, .product_id = 0x3004, .is_3012 = 1 },
70         { .vendor_id = 0x04ca, .product_id = 0x3005, .is_3012 = 1 },
71         { .vendor_id = 0x04ca, .product_id = 0x3006, .is_3012 = 1 },
72         { .vendor_id = 0x04ca, .product_id = 0x3008, .is_3012 = 1 },
73         { .vendor_id = 0x04ca, .product_id = 0x300b, .is_3012 = 1 },
74         { .vendor_id = 0x0930, .product_id = 0x0219, .is_3012 = 1 },
75         { .vendor_id = 0x0930, .product_id = 0x0220, .is_3012 = 1 },
76         { .vendor_id = 0x0b05, .product_id = 0x17d0, .is_3012 = 1 },
77         { .vendor_id = 0x0CF3, .product_id = 0x0036, .is_3012 = 1 },
78         { .vendor_id = 0x0cf3, .product_id = 0x3004, .is_3012 = 1 },
79         { .vendor_id = 0x0cf3, .product_id = 0x3005, .is_3012 = 1 },
80         { .vendor_id = 0x0cf3, .product_id = 0x3008, .is_3012 = 1 },
81         { .vendor_id = 0x0cf3, .product_id = 0x311D, .is_3012 = 1 },
82         { .vendor_id = 0x0cf3, .product_id = 0x311E, .is_3012 = 1 },
83         { .vendor_id = 0x0cf3, .product_id = 0x311F, .is_3012 = 1 },
84         { .vendor_id = 0x0cf3, .product_id = 0x3121, .is_3012 = 1 },
85         { .vendor_id = 0x0CF3, .product_id = 0x817a, .is_3012 = 1 },
86         { .vendor_id = 0x0cf3, .product_id = 0xe004, .is_3012 = 1 },
87         { .vendor_id = 0x0cf3, .product_id = 0xe005, .is_3012 = 1 },
88         { .vendor_id = 0x0cf3, .product_id = 0xe003, .is_3012 = 1 },
89         { .vendor_id = 0x13d3, .product_id = 0x3362, .is_3012 = 1 },
90         { .vendor_id = 0x13d3, .product_id = 0x3375, .is_3012 = 1 },
91         { .vendor_id = 0x13d3, .product_id = 0x3393, .is_3012 = 1 },
92         { .vendor_id = 0x13d3, .product_id = 0x3402, .is_3012 = 1 },
93
94         /* Atheros AR5BBU22 with sflash firmware */
95         { .vendor_id = 0x0489, .product_id = 0xE036, .is_3012 = 1 },
96         { .vendor_id = 0x0489, .product_id = 0xE03C, .is_3012 = 1 },
97 };
98
99 static int
100 ath3k_is_3012(struct libusb_device_descriptor *d)
101 {
102         int i;
103
104         /* Search looking for whether it's an AR3012 */
105         for (i = 0; i < (int) nitems(ath3k_list); i++) {
106                 if ((ath3k_list[i].product_id == d->idProduct) &&
107                     (ath3k_list[i].vendor_id == d->idVendor)) {
108                         fprintf(stderr, "%s: found AR3012\n", __func__);
109                         return (ath3k_list[i].is_3012);
110                 }
111         }
112
113         /* Not found */
114         return (0);
115 }
116
117 static libusb_device *
118 ath3k_find_device(libusb_context *ctx, int bus_id, int dev_id)
119 {
120         libusb_device **list, *dev = NULL, *found = NULL;
121         ssize_t cnt, i;
122
123         cnt = libusb_get_device_list(ctx, &list);
124         if (cnt < 0) {
125                 ath3k_err("%s: libusb_get_device_list() failed: code %lld\n",
126                     __func__,
127                     (long long int) cnt);
128                 return (NULL);
129         }
130
131         /*
132          * XXX TODO: match on the vendor/product id too!
133          */
134         for (i = 0; i < cnt; i++) {
135                 dev = list[i];
136                 if (bus_id == libusb_get_bus_number(dev) &&
137                     dev_id == libusb_get_device_address(dev)) {
138                         /*
139                          * Take a reference so it's not freed later on.
140                          */
141                         found = libusb_ref_device(dev);
142                         break;
143                 }
144         }
145
146         libusb_free_device_list(list, 1);
147         return (found);
148 }
149
150 static int
151 ath3k_init_ar3012(libusb_device_handle *hdl, const char *fw_path)
152 {
153         int ret;
154
155         ret = ath3k_load_patch(hdl, fw_path);
156         if (ret < 0) {
157                 ath3k_err("Loading patch file failed\n");
158         return (ret);
159         }
160
161         ret = ath3k_load_syscfg(hdl, fw_path);
162         if (ret < 0) {
163                 ath3k_err("Loading sysconfig file failed\n");
164                 return (ret);
165         }
166
167         ret = ath3k_set_normal_mode(hdl);
168         if (ret < 0) {
169                 ath3k_err("Set normal mode failed\n");
170                 return (ret);
171         }
172
173         ath3k_switch_pid(hdl);
174         return (0);
175 }
176
177 static int
178 ath3k_init_firmware(libusb_device_handle *hdl, const char *file_prefix)
179 {
180         struct ath3k_firmware fw;
181         char fwname[FILENAME_MAX];
182         int ret;
183
184         /* XXX path info? */
185         snprintf(fwname, FILENAME_MAX, "%s/ath3k-1.fw", file_prefix);
186
187         ath3k_debug("%s: loading ath3k-1.fw\n", __func__);
188
189         /* Read in the firmware */
190         if (ath3k_fw_read(&fw, fwname) <= 0) {
191                 fprintf(stderr, "%s: ath3k_fw_read() failed\n",
192                     __func__);
193                 return (-1);
194         }
195
196         /* Load in the firmware */
197         ret = ath3k_load_fwfile(hdl, &fw);
198
199         /* free it */
200         ath3k_fw_free(&fw);
201
202         return (0);
203 }
204
205 /*
206  * Parse ugen name and extract device's bus and address
207  */
208
209 static int
210 parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)
211 {
212         char *ep;
213
214         if (strncmp(ugen, "ugen", 4) != 0)
215                 return (-1);
216
217         *bus = (uint8_t) strtoul(ugen + 4, &ep, 10);
218         if (*ep != '.')
219                 return (-1);
220
221         *addr = (uint8_t) strtoul(ep + 1, &ep, 10);
222         if (*ep != '\0')
223                 return (-1);
224
225         return (0);
226 }
227
228 static void
229 usage(void)
230 {
231         fprintf(stderr,
232             "Usage: ath3kfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");
233         fprintf(stderr, "    -D: enable debugging\n");
234         fprintf(stderr, "    -d: device to operate upon\n");
235         fprintf(stderr, "    -f: firmware path, if not default\n");
236         fprintf(stderr, "    -I: enable informational output\n");
237         exit(127);
238 }
239
240 int
241 main(int argc, char *argv[])
242 {
243         struct libusb_device_descriptor d;
244         libusb_context *ctx;
245         libusb_device *dev;
246         libusb_device_handle *hdl;
247         unsigned char state;
248         struct ath3k_version ver;
249         int r;
250         uint8_t bus_id = 0, dev_id = 0;
251         int devid_set = 0;
252         int n;
253         char *firmware_path = NULL;
254         int is_3012 = 0;
255
256         /* libusb setup */
257         r = libusb_init(&ctx);
258         if (r != 0) {
259                 ath3k_err("%s: libusb_init failed: code %d\n",
260                     argv[0],
261                     r);
262                 exit(127);
263         }
264
265         /* Enable debugging, just because */
266         libusb_set_debug(ctx, 3);
267
268         /* Parse command line arguments */
269         while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {
270                 switch (n) {
271                 case 'd': /* ugen device name */
272                         devid_set = 1;
273                         if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)
274                                 usage();
275                         break;
276                 case 'D':
277                         ath3k_do_debug = 1;
278                         break;
279                 case 'f': /* firmware path */
280                         if (firmware_path)
281                                 free(firmware_path);
282                         firmware_path = strdup(optarg);
283                         break;
284                 case 'I':
285                         ath3k_do_info = 1;
286                         break;
287                 case 'h':
288                 default:
289                         usage();
290                         break;
291                         /* NOT REACHED */
292                 }
293         }
294
295         /* Ensure the devid was given! */
296         if (devid_set == 0) {
297                 usage();
298                 /* NOTREACHED */
299         }
300
301         ath3k_debug("%s: opening dev %d.%d\n",
302             basename(argv[0]),
303             (int) bus_id,
304             (int) dev_id);
305
306         /* Find a device based on the bus/dev id */
307         dev = ath3k_find_device(ctx, bus_id, dev_id);
308         if (dev == NULL) {
309                 ath3k_err("%s: device not found\n", __func__);
310                 /* XXX cleanup? */
311                 exit(1);
312         }
313
314         /* Get the device descriptor for this device entry */
315         r = libusb_get_device_descriptor(dev, &d);
316         if (r != 0) {
317                 warn("%s: libusb_get_device_descriptor: %s\n",
318                     __func__,
319                     libusb_strerror(r));
320                 exit(1);
321         }
322
323         /* See if its an AR3012 */
324         if (ath3k_is_3012(&d)) {
325                 is_3012 = 1;
326
327                 /* If it's bcdDevice > 1, don't attach */
328                 if (d.bcdDevice > 0x0001) {
329                         ath3k_debug("%s: AR3012; bcdDevice=%d, exiting\n",
330                             __func__,
331                             d.bcdDevice);
332                         exit(0);
333                 }
334         }
335
336         /* XXX enforce that bInterfaceNumber is 0 */
337
338         /* XXX enforce the device/product id if they're non-zero */
339
340         /* Grab device handle */
341         r = libusb_open(dev, &hdl);
342         if (r != 0) {
343                 ath3k_err("%s: libusb_open() failed: code %d\n", __func__, r);
344                 /* XXX cleanup? */
345                 exit(1);
346         }
347
348         /*
349          * Get the initial NIC state.
350          */
351         r = ath3k_get_state(hdl, &state);
352         if (r == 0) {
353                 ath3k_err("%s: ath3k_get_state() failed!\n", __func__);
354                 /* XXX cleanup? */
355                 exit(1);
356         }
357         ath3k_debug("%s: state=0x%02x\n",
358             __func__,
359             (int) state);
360
361         /* And the version */
362         r = ath3k_get_version(hdl, &ver);
363         if (r == 0) {
364                 ath3k_err("%s: ath3k_get_version() failed!\n", __func__);
365                 /* XXX cleanup? */
366                 exit(1);
367         }
368         ath3k_info("ROM version: %d, build version: %d, ram version: %d, "
369             "ref clock=%d\n",
370             ver.rom_version,
371             ver.build_version,
372             ver.ram_version,
373             ver.ref_clock);
374
375         /* Default the firmware path */
376         if (firmware_path == NULL)
377                 firmware_path = strdup(_DEFAULT_ATH3K_FIRMWARE_PATH);
378
379         if (is_3012) {
380                 (void) ath3k_init_ar3012(hdl, firmware_path);
381         } else {
382                 (void) ath3k_init_firmware(hdl, firmware_path);
383         }
384
385         /* Shutdown */
386         libusb_close(hdl);
387         hdl = NULL;
388
389         libusb_unref_device(dev);
390         dev = NULL;
391
392         libusb_exit(ctx);
393         ctx = NULL;
394 }