1215195Semax/* 2215195Semax * ath3kfw.c 3215195Semax */ 4215195Semax 5215195Semax/*- 6215195Semax * Copyright (c) 2010 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7215195Semax * All rights reserved. 8215195Semax * 9215195Semax * Redistribution and use in source and binary forms, with or without 10215195Semax * modification, are permitted provided that the following conditions 11215195Semax * are met: 12215195Semax * 1. Redistributions of source code must retain the above copyright 13215195Semax * notice, this list of conditions and the following disclaimer. 14215195Semax * 2. Redistributions in binary form must reproduce the above copyright 15215195Semax * notice, this list of conditions and the following disclaimer in the 16215195Semax * documentation and/or other materials provided with the distribution. 17215195Semax * 18215195Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19215195Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20215195Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21215195Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22215195Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23215195Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24215195Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25215195Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26215195Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27215195Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28215195Semax * SUCH DAMAGE. 29215195Semax * 30215195Semax * $FreeBSD$ 31215195Semax */ 32215195Semax 33215195Semax#include <sys/types.h> 34215195Semax#include <errno.h> 35215195Semax#include <fcntl.h> 36215195Semax#include <libusb20_desc.h> 37215195Semax#include <libusb20.h> 38215195Semax#include <stdarg.h> 39215195Semax#include <stdio.h> 40215195Semax#include <stdlib.h> 41215195Semax#include <string.h> 42215195Semax#include <syslog.h> 43215195Semax#include <unistd.h> 44215195Semax 45215195Semax#define ATH3KFW "ath3kfw" 46215195Semax#define ATH3KFW_VENDOR_ID 0x0cf3 47215195Semax#define ATH3KFW_PRODUCT_ID 0x3000 48215195Semax#define ATH3KFW_FW "/usr/local/etc/ath3k-1.fw" 49215195Semax#define ATH3KFW_BULK_EP 0x02 50215195Semax#define ATH3KFW_REQ_DFU_DNLOAD 1 51215195Semax#define ATH3KFW_MAX_BSIZE 4096 52215195Semax 53215195Semaxstatic int parse_ugen_name (char const *ugen, uint8_t *bus, 54215195Semax uint8_t *addr); 55215195Semaxstatic int find_device (struct libusb20_backend *be, 56215195Semax uint8_t bus, uint8_t addr, 57215195Semax struct libusb20_device **dev); 58215195Semaxstatic int download_firmware (struct libusb20_device *dev, 59215195Semax char const *firmware); 60215195Semaxstatic void usage (void); 61215195Semax 62249179Sadrianstatic int vendor_id = ATH3KFW_VENDOR_ID; 63249179Sadrianstatic int product_id = ATH3KFW_PRODUCT_ID; 64249179Sadrian 65215195Semax/* 66215195Semax * Firmware downloader for Atheros AR3011 based USB Bluetooth devices 67215195Semax */ 68215195Semax 69215195Semaxint 70215195Semaxmain(int argc, char **argv) 71215195Semax{ 72215195Semax uint8_t bus, addr; 73215195Semax char const *firmware; 74215195Semax struct libusb20_backend *be; 75215195Semax struct libusb20_device *dev; 76215195Semax int n; 77215195Semax 78215195Semax openlog(ATH3KFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 79215195Semax 80215195Semax bus = 0; 81215195Semax addr = 0; 82215195Semax firmware = ATH3KFW_FW; 83215195Semax 84249179Sadrian while ((n = getopt(argc, argv, "d:f:hp:v:")) != -1) { 85215195Semax switch (n) { 86215195Semax case 'd': /* ugen device name */ 87215195Semax if (parse_ugen_name(optarg, &bus, &addr) < 0) 88215195Semax usage(); 89215195Semax break; 90215195Semax 91215195Semax case 'f': /* firmware file */ 92215195Semax firmware = optarg; 93215195Semax break; 94249179Sadrian case 'p': /* product id */ 95249179Sadrian product_id = strtol(optarg, NULL, 0); 96249179Sadrian break; 97249179Sadrian case 'v': /* vendor id */ 98249179Sadrian vendor_id = strtol(optarg, NULL, 0); 99249179Sadrian break; 100215195Semax case 'h': 101215195Semax default: 102215195Semax usage(); 103215195Semax break; 104215195Semax /* NOT REACHED */ 105215195Semax } 106215195Semax } 107215195Semax 108215195Semax be = libusb20_be_alloc_default(); 109215195Semax if (be == NULL) { 110215195Semax syslog(LOG_ERR, "libusb20_be_alloc_default() failed"); 111215195Semax return (-1); 112215195Semax } 113215195Semax 114215195Semax if (find_device(be, bus, addr, &dev) < 0) { 115215195Semax syslog(LOG_ERR, "ugen%d.%d is not recognized as " \ 116215195Semax "Atheros AR3011 based device " \ 117215195Semax "(possibly caused by lack of permissions)", bus, addr); 118215195Semax return (-1); 119215195Semax } 120215195Semax 121215195Semax if (download_firmware(dev, firmware) < 0) { 122215195Semax syslog(LOG_ERR, "could not download %s firmare to ugen%d.%d", 123215195Semax firmware, bus, addr); 124215195Semax return (-1); 125215195Semax } 126215195Semax 127215195Semax libusb20_be_free(be); 128215195Semax closelog(); 129215195Semax 130215195Semax return (0); 131215195Semax} 132215195Semax 133215195Semax/* 134215195Semax * Parse ugen name and extract device's bus and address 135215195Semax */ 136215195Semax 137215195Semaxstatic int 138215195Semaxparse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr) 139215195Semax{ 140215195Semax char *ep; 141215195Semax 142215195Semax if (strncmp(ugen, "ugen", 4) != 0) 143215195Semax return (-1); 144215195Semax 145215195Semax *bus = (uint8_t) strtoul(ugen + 4, &ep, 10); 146215195Semax if (*ep != '.') 147215195Semax return (-1); 148215195Semax 149215195Semax *addr = (uint8_t) strtoul(ep + 1, &ep, 10); 150215195Semax if (*ep != '\0') 151215195Semax return (-1); 152215195Semax 153215195Semax return (0); 154215195Semax} 155215195Semax 156215195Semax/* 157215195Semax * Find USB device 158215195Semax */ 159215195Semax 160215195Semaxstatic int 161215195Semaxfind_device(struct libusb20_backend *be, uint8_t bus, uint8_t addr, 162215195Semax struct libusb20_device **dev) 163215195Semax{ 164215195Semax struct LIBUSB20_DEVICE_DESC_DECODED *desc; 165215195Semax 166215195Semax *dev = NULL; 167215195Semax 168215195Semax while ((*dev = libusb20_be_device_foreach(be, *dev)) != NULL) { 169215195Semax if (libusb20_dev_get_bus_number(*dev) != bus || 170215195Semax libusb20_dev_get_address(*dev) != addr) 171215195Semax continue; 172215195Semax 173215195Semax desc = libusb20_dev_get_device_desc(*dev); 174215195Semax if (desc == NULL) 175215195Semax continue; 176215195Semax 177249179Sadrian if (desc->idVendor != vendor_id || 178249179Sadrian desc->idProduct != product_id) 179215195Semax continue; 180215195Semax 181215195Semax break; 182215195Semax } 183215195Semax 184215195Semax return ((*dev == NULL)? -1 : 0); 185215195Semax} 186215195Semax 187215195Semax/* 188215195Semax * Download firmware 189215195Semax */ 190215195Semax 191215195Semaxstatic int 192215195Semaxdownload_firmware(struct libusb20_device *dev, char const *firmware) 193215195Semax{ 194215195Semax struct libusb20_transfer *bulk; 195215195Semax struct LIBUSB20_CONTROL_SETUP_DECODED req; 196215195Semax int fd, n, error; 197215195Semax uint8_t buf[ATH3KFW_MAX_BSIZE]; 198215195Semax 199215195Semax error = -1; 200215195Semax 201215195Semax if (libusb20_dev_open(dev, 1) != 0) { 202215195Semax syslog(LOG_ERR, "libusb20_dev_open() failed"); 203215195Semax return (error); 204215195Semax } 205215195Semax 206215195Semax if ((bulk = libusb20_tr_get_pointer(dev, 0)) == NULL) { 207215195Semax syslog(LOG_ERR, "libusb20_tr_get_pointer() failed"); 208215195Semax goto out; 209215195Semax } 210215195Semax 211215195Semax if (libusb20_tr_open(bulk, ATH3KFW_MAX_BSIZE, 1, ATH3KFW_BULK_EP) != 0) { 212215195Semax syslog(LOG_ERR, "libusb20_tr_open(%d, 1, %d) failed", 213215195Semax ATH3KFW_MAX_BSIZE, ATH3KFW_BULK_EP); 214215195Semax goto out; 215215195Semax } 216215195Semax 217215195Semax if ((fd = open(firmware, O_RDONLY)) < 0) { 218215195Semax syslog(LOG_ERR, "open(%s) failed. %s", 219215195Semax firmware, strerror(errno)); 220215195Semax goto out1; 221215195Semax } 222215195Semax 223215195Semax n = read(fd, buf, 20); 224215195Semax if (n != 20) { 225215195Semax syslog(LOG_ERR, "read(%s, 20) failed. %s", 226215195Semax firmware, strerror(errno)); 227215195Semax goto out2; 228215195Semax } 229215195Semax 230215195Semax LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); 231215195Semax req.bmRequestType = LIBUSB20_REQUEST_TYPE_VENDOR; 232215195Semax req.bRequest = ATH3KFW_REQ_DFU_DNLOAD; 233215195Semax req.wLength = 20; 234215195Semax 235215195Semax if (libusb20_dev_request_sync(dev, &req, buf, NULL, 5000, 0) != 0) { 236215195Semax syslog(LOG_ERR, "libusb20_dev_request_sync() failed"); 237215195Semax goto out2; 238215195Semax } 239215195Semax 240215195Semax for (;;) { 241215195Semax n = read(fd, buf, sizeof(buf)); 242215195Semax if (n < 0) { 243215195Semax syslog(LOG_ERR, "read(%s, %d) failed. %s", 244215195Semax firmware, (int) sizeof(buf), strerror(errno)); 245215195Semax goto out2; 246215195Semax } 247215195Semax if (n == 0) 248215195Semax break; 249215195Semax 250215195Semax libusb20_tr_setup_bulk(bulk, buf, n, 3000); 251215195Semax libusb20_tr_start(bulk); 252215195Semax 253215195Semax while (libusb20_dev_process(dev) == 0) { 254215195Semax if (libusb20_tr_pending(bulk) == 0) 255215195Semax break; 256215195Semax 257215195Semax libusb20_dev_wait_process(dev, -1); 258215195Semax } 259215195Semax 260215195Semax if (libusb20_tr_get_status(bulk) != 0) { 261215195Semax syslog(LOG_ERR, "bulk transfer failed with status %d", 262215195Semax libusb20_tr_get_status(bulk)); 263215195Semax goto out2; 264215195Semax } 265215195Semax } 266215195Semax 267215195Semax error = 0; 268215195Semaxout2: 269215195Semax close(fd); 270215195Semaxout1: 271215195Semax libusb20_tr_close(bulk); 272215195Semaxout: 273215195Semax libusb20_dev_close(dev); 274215195Semax 275215195Semax return (error); 276215195Semax} 277215195Semax 278215195Semax/* 279215195Semax * Display usage and exit 280215195Semax */ 281215195Semax 282215195Semaxstatic void 283215195Semaxusage(void) 284215195Semax{ 285215195Semax printf( 286215195Semax"Usage: %s -d ugenX.Y -f firmware_file\n" 287215195Semax"Usage: %s -h\n" \ 288215195Semax"Where:\n" \ 289215195Semax"\t-d ugenX.Y ugen device name\n" \ 290215195Semax"\t-f firmware image firmware image file name for download\n" \ 291249179Sadrian"\t-v vendor_id vendor id\n" \ 292249179Sadrian"\t-p vendor_id product id\n" \ 293215195Semax"\t-h display this message\n", ATH3KFW, ATH3KFW); 294215195Semax 295215195Semax exit(255); 296215195Semax} 297215195Semax 298