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 62215195Semax/* 63215195Semax * Firmware downloader for Atheros AR3011 based USB Bluetooth devices 64215195Semax */ 65215195Semax 66215195Semaxint 67215195Semaxmain(int argc, char **argv) 68215195Semax{ 69215195Semax uint8_t bus, addr; 70215195Semax char const *firmware; 71215195Semax struct libusb20_backend *be; 72215195Semax struct libusb20_device *dev; 73215195Semax int n; 74215195Semax 75215195Semax openlog(ATH3KFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 76215195Semax 77215195Semax bus = 0; 78215195Semax addr = 0; 79215195Semax firmware = ATH3KFW_FW; 80215195Semax 81215195Semax while ((n = getopt(argc, argv, "d:f:h")) != -1) { 82215195Semax switch (n) { 83215195Semax case 'd': /* ugen device name */ 84215195Semax if (parse_ugen_name(optarg, &bus, &addr) < 0) 85215195Semax usage(); 86215195Semax break; 87215195Semax 88215195Semax case 'f': /* firmware file */ 89215195Semax firmware = optarg; 90215195Semax break; 91215195Semax 92215195Semax case 'h': 93215195Semax default: 94215195Semax usage(); 95215195Semax break; 96215195Semax /* NOT REACHED */ 97215195Semax } 98215195Semax } 99215195Semax 100215195Semax be = libusb20_be_alloc_default(); 101215195Semax if (be == NULL) { 102215195Semax syslog(LOG_ERR, "libusb20_be_alloc_default() failed"); 103215195Semax return (-1); 104215195Semax } 105215195Semax 106215195Semax if (find_device(be, bus, addr, &dev) < 0) { 107215195Semax syslog(LOG_ERR, "ugen%d.%d is not recognized as " \ 108215195Semax "Atheros AR3011 based device " \ 109215195Semax "(possibly caused by lack of permissions)", bus, addr); 110215195Semax return (-1); 111215195Semax } 112215195Semax 113215195Semax if (download_firmware(dev, firmware) < 0) { 114215195Semax syslog(LOG_ERR, "could not download %s firmare to ugen%d.%d", 115215195Semax firmware, bus, addr); 116215195Semax return (-1); 117215195Semax } 118215195Semax 119215195Semax libusb20_be_free(be); 120215195Semax closelog(); 121215195Semax 122215195Semax return (0); 123215195Semax} 124215195Semax 125215195Semax/* 126215195Semax * Parse ugen name and extract device's bus and address 127215195Semax */ 128215195Semax 129215195Semaxstatic int 130215195Semaxparse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr) 131215195Semax{ 132215195Semax char *ep; 133215195Semax 134215195Semax if (strncmp(ugen, "ugen", 4) != 0) 135215195Semax return (-1); 136215195Semax 137215195Semax *bus = (uint8_t) strtoul(ugen + 4, &ep, 10); 138215195Semax if (*ep != '.') 139215195Semax return (-1); 140215195Semax 141215195Semax *addr = (uint8_t) strtoul(ep + 1, &ep, 10); 142215195Semax if (*ep != '\0') 143215195Semax return (-1); 144215195Semax 145215195Semax return (0); 146215195Semax} 147215195Semax 148215195Semax/* 149215195Semax * Find USB device 150215195Semax */ 151215195Semax 152215195Semaxstatic int 153215195Semaxfind_device(struct libusb20_backend *be, uint8_t bus, uint8_t addr, 154215195Semax struct libusb20_device **dev) 155215195Semax{ 156215195Semax struct LIBUSB20_DEVICE_DESC_DECODED *desc; 157215195Semax 158215195Semax *dev = NULL; 159215195Semax 160215195Semax while ((*dev = libusb20_be_device_foreach(be, *dev)) != NULL) { 161215195Semax if (libusb20_dev_get_bus_number(*dev) != bus || 162215195Semax libusb20_dev_get_address(*dev) != addr) 163215195Semax continue; 164215195Semax 165215195Semax desc = libusb20_dev_get_device_desc(*dev); 166215195Semax if (desc == NULL) 167215195Semax continue; 168215195Semax 169215195Semax if (desc->idVendor != ATH3KFW_VENDOR_ID || 170215195Semax desc->idProduct != ATH3KFW_PRODUCT_ID) 171215195Semax continue; 172215195Semax 173215195Semax break; 174215195Semax } 175215195Semax 176215195Semax return ((*dev == NULL)? -1 : 0); 177215195Semax} 178215195Semax 179215195Semax/* 180215195Semax * Download firmware 181215195Semax */ 182215195Semax 183215195Semaxstatic int 184215195Semaxdownload_firmware(struct libusb20_device *dev, char const *firmware) 185215195Semax{ 186215195Semax struct libusb20_transfer *bulk; 187215195Semax struct LIBUSB20_CONTROL_SETUP_DECODED req; 188215195Semax int fd, n, error; 189215195Semax uint8_t buf[ATH3KFW_MAX_BSIZE]; 190215195Semax 191215195Semax error = -1; 192215195Semax 193215195Semax if (libusb20_dev_open(dev, 1) != 0) { 194215195Semax syslog(LOG_ERR, "libusb20_dev_open() failed"); 195215195Semax return (error); 196215195Semax } 197215195Semax 198215195Semax if ((bulk = libusb20_tr_get_pointer(dev, 0)) == NULL) { 199215195Semax syslog(LOG_ERR, "libusb20_tr_get_pointer() failed"); 200215195Semax goto out; 201215195Semax } 202215195Semax 203215195Semax if (libusb20_tr_open(bulk, ATH3KFW_MAX_BSIZE, 1, ATH3KFW_BULK_EP) != 0) { 204215195Semax syslog(LOG_ERR, "libusb20_tr_open(%d, 1, %d) failed", 205215195Semax ATH3KFW_MAX_BSIZE, ATH3KFW_BULK_EP); 206215195Semax goto out; 207215195Semax } 208215195Semax 209215195Semax if ((fd = open(firmware, O_RDONLY)) < 0) { 210215195Semax syslog(LOG_ERR, "open(%s) failed. %s", 211215195Semax firmware, strerror(errno)); 212215195Semax goto out1; 213215195Semax } 214215195Semax 215215195Semax n = read(fd, buf, 20); 216215195Semax if (n != 20) { 217215195Semax syslog(LOG_ERR, "read(%s, 20) failed. %s", 218215195Semax firmware, strerror(errno)); 219215195Semax goto out2; 220215195Semax } 221215195Semax 222215195Semax LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); 223215195Semax req.bmRequestType = LIBUSB20_REQUEST_TYPE_VENDOR; 224215195Semax req.bRequest = ATH3KFW_REQ_DFU_DNLOAD; 225215195Semax req.wLength = 20; 226215195Semax 227215195Semax if (libusb20_dev_request_sync(dev, &req, buf, NULL, 5000, 0) != 0) { 228215195Semax syslog(LOG_ERR, "libusb20_dev_request_sync() failed"); 229215195Semax goto out2; 230215195Semax } 231215195Semax 232215195Semax for (;;) { 233215195Semax n = read(fd, buf, sizeof(buf)); 234215195Semax if (n < 0) { 235215195Semax syslog(LOG_ERR, "read(%s, %d) failed. %s", 236215195Semax firmware, (int) sizeof(buf), strerror(errno)); 237215195Semax goto out2; 238215195Semax } 239215195Semax if (n == 0) 240215195Semax break; 241215195Semax 242215195Semax libusb20_tr_setup_bulk(bulk, buf, n, 3000); 243215195Semax libusb20_tr_start(bulk); 244215195Semax 245215195Semax while (libusb20_dev_process(dev) == 0) { 246215195Semax if (libusb20_tr_pending(bulk) == 0) 247215195Semax break; 248215195Semax 249215195Semax libusb20_dev_wait_process(dev, -1); 250215195Semax } 251215195Semax 252215195Semax if (libusb20_tr_get_status(bulk) != 0) { 253215195Semax syslog(LOG_ERR, "bulk transfer failed with status %d", 254215195Semax libusb20_tr_get_status(bulk)); 255215195Semax goto out2; 256215195Semax } 257215195Semax } 258215195Semax 259215195Semax error = 0; 260215195Semaxout2: 261215195Semax close(fd); 262215195Semaxout1: 263215195Semax libusb20_tr_close(bulk); 264215195Semaxout: 265215195Semax libusb20_dev_close(dev); 266215195Semax 267215195Semax return (error); 268215195Semax} 269215195Semax 270215195Semax/* 271215195Semax * Display usage and exit 272215195Semax */ 273215195Semax 274215195Semaxstatic void 275215195Semaxusage(void) 276215195Semax{ 277215195Semax printf( 278215195Semax"Usage: %s -d ugenX.Y -f firmware_file\n" 279215195Semax"Usage: %s -h\n" \ 280215195Semax"Where:\n" \ 281215195Semax"\t-d ugenX.Y ugen device name\n" \ 282215195Semax"\t-f firmware image firmware image file name for download\n" \ 283215195Semax"\t-h display this message\n", ATH3KFW, ATH3KFW); 284215195Semax 285215195Semax exit(255); 286215195Semax} 287215195Semax 288