ath3kfw.c revision 215195
1/* 2 * ath3kfw.c 3 */ 4 5/*- 6 * Copyright (c) 2010 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD: head/usr.sbin/bluetooth/ath3kfw/ath3kfw.c 215195 2010-11-12 19:43:12Z emax $ 31 */ 32 33#include <sys/types.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <libusb20_desc.h> 37#include <libusb20.h> 38#include <stdarg.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <syslog.h> 43#include <unistd.h> 44 45#define ATH3KFW "ath3kfw" 46#define ATH3KFW_VENDOR_ID 0x0cf3 47#define ATH3KFW_PRODUCT_ID 0x3000 48#define ATH3KFW_FW "/usr/local/etc/ath3k-1.fw" 49#define ATH3KFW_BULK_EP 0x02 50#define ATH3KFW_REQ_DFU_DNLOAD 1 51#define ATH3KFW_MAX_BSIZE 4096 52 53static int parse_ugen_name (char const *ugen, uint8_t *bus, 54 uint8_t *addr); 55static int find_device (struct libusb20_backend *be, 56 uint8_t bus, uint8_t addr, 57 struct libusb20_device **dev); 58static int download_firmware (struct libusb20_device *dev, 59 char const *firmware); 60static void usage (void); 61 62/* 63 * Firmware downloader for Atheros AR3011 based USB Bluetooth devices 64 */ 65 66int 67main(int argc, char **argv) 68{ 69 uint8_t bus, addr; 70 char const *firmware; 71 struct libusb20_backend *be; 72 struct libusb20_device *dev; 73 int n; 74 75 openlog(ATH3KFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 76 77 bus = 0; 78 addr = 0; 79 firmware = ATH3KFW_FW; 80 81 while ((n = getopt(argc, argv, "d:f:h")) != -1) { 82 switch (n) { 83 case 'd': /* ugen device name */ 84 if (parse_ugen_name(optarg, &bus, &addr) < 0) 85 usage(); 86 break; 87 88 case 'f': /* firmware file */ 89 firmware = optarg; 90 break; 91 92 case 'h': 93 default: 94 usage(); 95 break; 96 /* NOT REACHED */ 97 } 98 } 99 100 be = libusb20_be_alloc_default(); 101 if (be == NULL) { 102 syslog(LOG_ERR, "libusb20_be_alloc_default() failed"); 103 return (-1); 104 } 105 106 if (find_device(be, bus, addr, &dev) < 0) { 107 syslog(LOG_ERR, "ugen%d.%d is not recognized as " \ 108 "Atheros AR3011 based device " \ 109 "(possibly caused by lack of permissions)", bus, addr); 110 return (-1); 111 } 112 113 if (download_firmware(dev, firmware) < 0) { 114 syslog(LOG_ERR, "could not download %s firmare to ugen%d.%d", 115 firmware, bus, addr); 116 return (-1); 117 } 118 119 libusb20_be_free(be); 120 closelog(); 121 122 return (0); 123} 124 125/* 126 * Parse ugen name and extract device's bus and address 127 */ 128 129static int 130parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr) 131{ 132 char *ep; 133 134 if (strncmp(ugen, "ugen", 4) != 0) 135 return (-1); 136 137 *bus = (uint8_t) strtoul(ugen + 4, &ep, 10); 138 if (*ep != '.') 139 return (-1); 140 141 *addr = (uint8_t) strtoul(ep + 1, &ep, 10); 142 if (*ep != '\0') 143 return (-1); 144 145 return (0); 146} 147 148/* 149 * Find USB device 150 */ 151 152static int 153find_device(struct libusb20_backend *be, uint8_t bus, uint8_t addr, 154 struct libusb20_device **dev) 155{ 156 struct LIBUSB20_DEVICE_DESC_DECODED *desc; 157 158 *dev = NULL; 159 160 while ((*dev = libusb20_be_device_foreach(be, *dev)) != NULL) { 161 if (libusb20_dev_get_bus_number(*dev) != bus || 162 libusb20_dev_get_address(*dev) != addr) 163 continue; 164 165 desc = libusb20_dev_get_device_desc(*dev); 166 if (desc == NULL) 167 continue; 168 169 if (desc->idVendor != ATH3KFW_VENDOR_ID || 170 desc->idProduct != ATH3KFW_PRODUCT_ID) 171 continue; 172 173 break; 174 } 175 176 return ((*dev == NULL)? -1 : 0); 177} 178 179/* 180 * Download firmware 181 */ 182 183static int 184download_firmware(struct libusb20_device *dev, char const *firmware) 185{ 186 struct libusb20_transfer *bulk; 187 struct LIBUSB20_CONTROL_SETUP_DECODED req; 188 int fd, n, error; 189 uint8_t buf[ATH3KFW_MAX_BSIZE]; 190 191 error = -1; 192 193 if (libusb20_dev_open(dev, 1) != 0) { 194 syslog(LOG_ERR, "libusb20_dev_open() failed"); 195 return (error); 196 } 197 198 if ((bulk = libusb20_tr_get_pointer(dev, 0)) == NULL) { 199 syslog(LOG_ERR, "libusb20_tr_get_pointer() failed"); 200 goto out; 201 } 202 203 if (libusb20_tr_open(bulk, ATH3KFW_MAX_BSIZE, 1, ATH3KFW_BULK_EP) != 0) { 204 syslog(LOG_ERR, "libusb20_tr_open(%d, 1, %d) failed", 205 ATH3KFW_MAX_BSIZE, ATH3KFW_BULK_EP); 206 goto out; 207 } 208 209 if ((fd = open(firmware, O_RDONLY)) < 0) { 210 syslog(LOG_ERR, "open(%s) failed. %s", 211 firmware, strerror(errno)); 212 goto out1; 213 } 214 215 n = read(fd, buf, 20); 216 if (n != 20) { 217 syslog(LOG_ERR, "read(%s, 20) failed. %s", 218 firmware, strerror(errno)); 219 goto out2; 220 } 221 222 LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); 223 req.bmRequestType = LIBUSB20_REQUEST_TYPE_VENDOR; 224 req.bRequest = ATH3KFW_REQ_DFU_DNLOAD; 225 req.wLength = 20; 226 227 if (libusb20_dev_request_sync(dev, &req, buf, NULL, 5000, 0) != 0) { 228 syslog(LOG_ERR, "libusb20_dev_request_sync() failed"); 229 goto out2; 230 } 231 232 for (;;) { 233 n = read(fd, buf, sizeof(buf)); 234 if (n < 0) { 235 syslog(LOG_ERR, "read(%s, %d) failed. %s", 236 firmware, (int) sizeof(buf), strerror(errno)); 237 goto out2; 238 } 239 if (n == 0) 240 break; 241 242 libusb20_tr_setup_bulk(bulk, buf, n, 3000); 243 libusb20_tr_start(bulk); 244 245 while (libusb20_dev_process(dev) == 0) { 246 if (libusb20_tr_pending(bulk) == 0) 247 break; 248 249 libusb20_dev_wait_process(dev, -1); 250 } 251 252 if (libusb20_tr_get_status(bulk) != 0) { 253 syslog(LOG_ERR, "bulk transfer failed with status %d", 254 libusb20_tr_get_status(bulk)); 255 goto out2; 256 } 257 } 258 259 error = 0; 260out2: 261 close(fd); 262out1: 263 libusb20_tr_close(bulk); 264out: 265 libusb20_dev_close(dev); 266 267 return (error); 268} 269 270/* 271 * Display usage and exit 272 */ 273 274static void 275usage(void) 276{ 277 printf( 278"Usage: %s -d ugenX.Y -f firmware_file\n" 279"Usage: %s -h\n" \ 280"Where:\n" \ 281"\t-d ugenX.Y ugen device name\n" \ 282"\t-f firmware image firmware image file name for download\n" \ 283"\t-h display this message\n", ATH3KFW, ATH3KFW); 284 285 exit(255); 286} 287 288