14eb513daSJulian Elischer /* 24eb513daSJulian Elischer * bcmfw.c 34eb513daSJulian Elischer * 44eb513daSJulian Elischer * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 54eb513daSJulian Elischer * All rights reserved. 64eb513daSJulian Elischer * 74eb513daSJulian Elischer * Redistribution and use in source and binary forms, with or without 84eb513daSJulian Elischer * modification, are permitted provided that the following conditions 94eb513daSJulian Elischer * are met: 104eb513daSJulian Elischer * 1. Redistributions of source code must retain the above copyright 114eb513daSJulian Elischer * notice, this list of conditions and the following disclaimer. 124eb513daSJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright 134eb513daSJulian Elischer * notice, this list of conditions and the following disclaimer in the 144eb513daSJulian Elischer * documentation and/or other materials provided with the distribution. 154eb513daSJulian Elischer * 164eb513daSJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 174eb513daSJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 184eb513daSJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 194eb513daSJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 204eb513daSJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 214eb513daSJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 224eb513daSJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 234eb513daSJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 244eb513daSJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 254eb513daSJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 264eb513daSJulian Elischer * SUCH DAMAGE. 274eb513daSJulian Elischer * 284eb513daSJulian Elischer * $Id: bcmfw.c,v 1.4 2003/04/27 19:28:09 max Exp $ 294eb513daSJulian Elischer * $FreeBSD$ 304eb513daSJulian Elischer * 314eb513daSJulian Elischer * Based on Linux BlueZ BlueFW-0.9 package 324eb513daSJulian Elischer * 334eb513daSJulian Elischer */ 344eb513daSJulian Elischer 354eb513daSJulian Elischer #include <sys/types.h> 364eb513daSJulian Elischer #include <sys/ioctl.h> 374eb513daSJulian Elischer #include <dev/usb/usb.h> 384eb513daSJulian Elischer #include <errno.h> 394eb513daSJulian Elischer #include <fcntl.h> 404eb513daSJulian Elischer #include <netgraph.h> 414eb513daSJulian Elischer #include <stdarg.h> 424eb513daSJulian Elischer #include <stdio.h> 434eb513daSJulian Elischer #include <stdlib.h> 444eb513daSJulian Elischer #include <string.h> 454eb513daSJulian Elischer #include <syslog.h> 464eb513daSJulian Elischer #include <unistd.h> 474eb513daSJulian Elischer 484eb513daSJulian Elischer #define BCMFW "bcmfw" 494eb513daSJulian Elischer #define BCMFW_INTR_EP 1 504eb513daSJulian Elischer #define BCMFW_BULK_EP 2 514eb513daSJulian Elischer #define BCMFW_BSIZE 4096 524eb513daSJulian Elischer 53e3ce1a4aSWarner Losh #define USB_VENDOR_BROADCOM 0x0a5c 54e3ce1a4aSWarner Losh #define USB_PRODUCT_BROADCOM_BCM2033 0x2033 55e3ce1a4aSWarner Losh 564eb513daSJulian Elischer static int bcmfw_check_device 574eb513daSJulian Elischer (char const *name); 584eb513daSJulian Elischer static int bcmfw_load_firmware 594eb513daSJulian Elischer (char const *name, char const *md, char const *fw); 604eb513daSJulian Elischer static void bcmfw_usage 614eb513daSJulian Elischer (void); 624eb513daSJulian Elischer 634eb513daSJulian Elischer /* 644eb513daSJulian Elischer * Main 654eb513daSJulian Elischer */ 664eb513daSJulian Elischer 674eb513daSJulian Elischer int 684eb513daSJulian Elischer main(int argc, char *argv[]) 694eb513daSJulian Elischer { 704eb513daSJulian Elischer char *name = NULL, *md = NULL, *fw = NULL; 714eb513daSJulian Elischer int x; 724eb513daSJulian Elischer 734eb513daSJulian Elischer while ((x = getopt(argc, argv, "f:hn:m:")) != -1) { 744eb513daSJulian Elischer switch (x) { 754eb513daSJulian Elischer case 'f': /* firmware file */ 764eb513daSJulian Elischer fw = optarg; 774eb513daSJulian Elischer break; 784eb513daSJulian Elischer 794eb513daSJulian Elischer case 'n': /* name */ 804eb513daSJulian Elischer name = optarg; 814eb513daSJulian Elischer break; 824eb513daSJulian Elischer 834eb513daSJulian Elischer case 'm': /* Mini-driver */ 844eb513daSJulian Elischer md = optarg; 854eb513daSJulian Elischer break; 864eb513daSJulian Elischer 874eb513daSJulian Elischer case 'h': 884eb513daSJulian Elischer default: 894eb513daSJulian Elischer bcmfw_usage(); 904eb513daSJulian Elischer /* NOT REACHED */ 914eb513daSJulian Elischer } 924eb513daSJulian Elischer } 934eb513daSJulian Elischer 944eb513daSJulian Elischer if (name == NULL || md == NULL || fw == NULL) 954eb513daSJulian Elischer bcmfw_usage(); 964eb513daSJulian Elischer /* NOT REACHED */ 974eb513daSJulian Elischer 984eb513daSJulian Elischer openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 994eb513daSJulian Elischer 1004eb513daSJulian Elischer if (bcmfw_check_device(name) < 0) 1014eb513daSJulian Elischer exit(1); 1024eb513daSJulian Elischer 1034eb513daSJulian Elischer if (bcmfw_load_firmware(name, md, fw) < 0) 1044eb513daSJulian Elischer exit(1); 1054eb513daSJulian Elischer 1064eb513daSJulian Elischer closelog(); 1074eb513daSJulian Elischer 1084eb513daSJulian Elischer return (0); 1094eb513daSJulian Elischer } /* main */ 1104eb513daSJulian Elischer 1114eb513daSJulian Elischer /* 1124eb513daSJulian Elischer * Check device VendorID/ProductID 1134eb513daSJulian Elischer */ 1144eb513daSJulian Elischer 1154eb513daSJulian Elischer static int 1164eb513daSJulian Elischer bcmfw_check_device(char const *name) 1174eb513daSJulian Elischer { 1184eb513daSJulian Elischer usb_device_descriptor_t desc; 1194eb513daSJulian Elischer char path[BCMFW_BSIZE]; 1204eb513daSJulian Elischer int fd = -1, error = -1; 1214eb513daSJulian Elischer 1224eb513daSJulian Elischer snprintf(path, sizeof(path), "/dev/%s", name); 1234eb513daSJulian Elischer 1244eb513daSJulian Elischer if ((fd = open(path, O_WRONLY)) < 0) { 1254eb513daSJulian Elischer syslog(LOG_ERR, "Could not open(%s). %s (%d)", 1264eb513daSJulian Elischer path, strerror(errno), errno); 1274eb513daSJulian Elischer goto out; 1284eb513daSJulian Elischer } 1294eb513daSJulian Elischer 1304eb513daSJulian Elischer if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) { 1314eb513daSJulian Elischer syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)", 1324eb513daSJulian Elischer fd, USB_GET_DEVICE_DESC, &desc, 1334eb513daSJulian Elischer strerror(errno), errno); 1344eb513daSJulian Elischer goto out; 1354eb513daSJulian Elischer } 1364eb513daSJulian Elischer 1374eb513daSJulian Elischer if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM || 138e3ce1a4aSWarner Losh UGETW(desc.idProduct) != USB_PRODUCT_BROADCOM_BCM2033) { 1394eb513daSJulian Elischer syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \ 1404eb513daSJulian Elischer "ProductID=%#x", UGETW(desc.idVendor), 1414eb513daSJulian Elischer UGETW(desc.idProduct)); 1424eb513daSJulian Elischer error = -1; 1434eb513daSJulian Elischer } else 1444eb513daSJulian Elischer error = 0; 1454eb513daSJulian Elischer out: 1464eb513daSJulian Elischer if (fd != -1) 1474eb513daSJulian Elischer close(fd); 1484eb513daSJulian Elischer 1494eb513daSJulian Elischer return (error); 1504eb513daSJulian Elischer } /* bcmfw_check_device */ 1514eb513daSJulian Elischer 1524eb513daSJulian Elischer /* 1534eb513daSJulian Elischer * Download minidriver and firmware 1544eb513daSJulian Elischer */ 1554eb513daSJulian Elischer 1564eb513daSJulian Elischer static int 1574eb513daSJulian Elischer bcmfw_load_firmware(char const *name, char const *md, char const *fw) 1584eb513daSJulian Elischer { 1594eb513daSJulian Elischer char buf[BCMFW_BSIZE]; 1604eb513daSJulian Elischer int intr = -1, bulk = -1, fd = -1, error = -1, len; 1614eb513daSJulian Elischer 1624eb513daSJulian Elischer /* Open interrupt endpoint device */ 1634eb513daSJulian Elischer snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP); 1644eb513daSJulian Elischer if ((intr = open(buf, O_RDONLY)) < 0) { 1654eb513daSJulian Elischer syslog(LOG_ERR, "Could not open(%s). %s (%d)", 1664eb513daSJulian Elischer buf, strerror(errno), errno); 1674eb513daSJulian Elischer goto out; 1684eb513daSJulian Elischer } 1694eb513daSJulian Elischer 1704eb513daSJulian Elischer /* Open bulk endpoint device */ 1714eb513daSJulian Elischer snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP); 1724eb513daSJulian Elischer if ((bulk = open(buf, O_WRONLY)) < 0) { 1734eb513daSJulian Elischer syslog(LOG_ERR, "Could not open(%s). %s (%d)", 1744eb513daSJulian Elischer buf, strerror(errno), errno); 1754eb513daSJulian Elischer goto out; 1764eb513daSJulian Elischer } 1774eb513daSJulian Elischer 1784eb513daSJulian Elischer /* 1794eb513daSJulian Elischer * Load mini-driver 1804eb513daSJulian Elischer */ 1814eb513daSJulian Elischer 1824eb513daSJulian Elischer if ((fd = open(md, O_RDONLY)) < 0) { 1834eb513daSJulian Elischer syslog(LOG_ERR, "Could not open(%s). %s (%d)", 1844eb513daSJulian Elischer md, strerror(errno), errno); 1854eb513daSJulian Elischer goto out; 1864eb513daSJulian Elischer } 1874eb513daSJulian Elischer 1884eb513daSJulian Elischer for (;;) { 1894eb513daSJulian Elischer len = read(fd, buf, sizeof(buf)); 1904eb513daSJulian Elischer if (len < 0) { 1914eb513daSJulian Elischer syslog(LOG_ERR, "Could not read(%s). %s (%d)", 1924eb513daSJulian Elischer md, strerror(errno), errno); 1934eb513daSJulian Elischer goto out; 1944eb513daSJulian Elischer } 1954eb513daSJulian Elischer if (len == 0) 1964eb513daSJulian Elischer break; 1974eb513daSJulian Elischer 1984eb513daSJulian Elischer len = write(bulk, buf, len); 1994eb513daSJulian Elischer if (len < 0) { 2004eb513daSJulian Elischer syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 2014eb513daSJulian Elischer name, BCMFW_BULK_EP, strerror(errno), 2024eb513daSJulian Elischer errno); 2034eb513daSJulian Elischer goto out; 2044eb513daSJulian Elischer } 2054eb513daSJulian Elischer } 2064eb513daSJulian Elischer 2074eb513daSJulian Elischer close(fd); 2084eb513daSJulian Elischer fd = -1; 2094eb513daSJulian Elischer 2104eb513daSJulian Elischer usleep(10); 2114eb513daSJulian Elischer 2124eb513daSJulian Elischer /* 2134eb513daSJulian Elischer * Memory select 2144eb513daSJulian Elischer */ 2154eb513daSJulian Elischer 2164eb513daSJulian Elischer if (write(bulk, "#", 1) < 0) { 2174eb513daSJulian Elischer syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 2184eb513daSJulian Elischer name, BCMFW_BULK_EP, strerror(errno), errno); 2194eb513daSJulian Elischer goto out; 2204eb513daSJulian Elischer } 2214eb513daSJulian Elischer 2224eb513daSJulian Elischer if (read(intr, buf, sizeof(buf)) < 0) { 2234eb513daSJulian Elischer syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 2244eb513daSJulian Elischer name, BCMFW_INTR_EP, strerror(errno), errno); 2254eb513daSJulian Elischer goto out; 2264eb513daSJulian Elischer } 2274eb513daSJulian Elischer 2284eb513daSJulian Elischer if (buf[0] != '#') { 2294eb513daSJulian Elischer syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]); 2304eb513daSJulian Elischer goto out; 2314eb513daSJulian Elischer } 2324eb513daSJulian Elischer 2334eb513daSJulian Elischer /* 2344eb513daSJulian Elischer * Load firmware 2354eb513daSJulian Elischer */ 2364eb513daSJulian Elischer 2374eb513daSJulian Elischer if ((fd = open(fw, O_RDONLY)) < 0) { 2384eb513daSJulian Elischer syslog(LOG_ERR, "Could not open(%s). %s (%d)", 2394eb513daSJulian Elischer fw, strerror(errno), errno); 2404eb513daSJulian Elischer goto out; 2414eb513daSJulian Elischer } 2424eb513daSJulian Elischer 2434eb513daSJulian Elischer for (;;) { 2444eb513daSJulian Elischer len = read(fd, buf, sizeof(buf)); 2454eb513daSJulian Elischer if (len < 0) { 2464eb513daSJulian Elischer syslog(LOG_ERR, "Could not read(%s). %s (%d)", 2474eb513daSJulian Elischer fw, strerror(errno), errno); 2484eb513daSJulian Elischer goto out; 2494eb513daSJulian Elischer } 2504eb513daSJulian Elischer if (len == 0) 2514eb513daSJulian Elischer break; 2524eb513daSJulian Elischer 2534eb513daSJulian Elischer len = write(bulk, buf, len); 2544eb513daSJulian Elischer if (len < 0) { 2554eb513daSJulian Elischer syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 2564eb513daSJulian Elischer name, BCMFW_BULK_EP, strerror(errno), 2574eb513daSJulian Elischer errno); 2584eb513daSJulian Elischer goto out; 2594eb513daSJulian Elischer } 2604eb513daSJulian Elischer } 2614eb513daSJulian Elischer 2624eb513daSJulian Elischer close(fd); 2634eb513daSJulian Elischer fd = -1; 2644eb513daSJulian Elischer 2654eb513daSJulian Elischer if (read(intr, buf, sizeof(buf)) < 0) { 2664eb513daSJulian Elischer syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 2674eb513daSJulian Elischer name, BCMFW_INTR_EP, strerror(errno), errno); 2684eb513daSJulian Elischer goto out; 2694eb513daSJulian Elischer } 2704eb513daSJulian Elischer 2714eb513daSJulian Elischer if (buf[0] != '.') { 2724eb513daSJulian Elischer syslog(LOG_ERR, "%s: Could not load firmware (%c)", 2734eb513daSJulian Elischer name, buf[0]); 2744eb513daSJulian Elischer goto out; 2754eb513daSJulian Elischer } 2764eb513daSJulian Elischer 2774eb513daSJulian Elischer usleep(500000); 2784eb513daSJulian Elischer error = 0; 2794eb513daSJulian Elischer out: 2804eb513daSJulian Elischer if (fd != -1) 2814eb513daSJulian Elischer close(fd); 2824eb513daSJulian Elischer if (bulk != -1) 2834eb513daSJulian Elischer close(bulk); 2844eb513daSJulian Elischer if (intr != -1) 2854eb513daSJulian Elischer close(intr); 2864eb513daSJulian Elischer 2874eb513daSJulian Elischer return (error); 2884eb513daSJulian Elischer } /* bcmfw_load_firmware */ 2894eb513daSJulian Elischer 2904eb513daSJulian Elischer /* 2914eb513daSJulian Elischer * Display usage message and quit 2924eb513daSJulian Elischer */ 2934eb513daSJulian Elischer 2944eb513daSJulian Elischer static void 2954eb513daSJulian Elischer bcmfw_usage(void) 2964eb513daSJulian Elischer { 2974eb513daSJulian Elischer fprintf(stdout, 2984eb513daSJulian Elischer "Usage: %s -n name -m md_file -f fw_file\n" 2994eb513daSJulian Elischer "Where:\n" \ 3004eb513daSJulian Elischer "\t-n name device name\n" \ 3014eb513daSJulian Elischer "\t-m mini-driver image mini-driver image file name for download\n" \ 3024eb513daSJulian Elischer "\t-f firmware image firmware image file name for download\n" \ 3034eb513daSJulian Elischer "\t-h display this message\n", BCMFW); 3044eb513daSJulian Elischer 3054eb513daSJulian Elischer exit(255); 3064eb513daSJulian Elischer } /* bcmfw_usage */ 3074eb513daSJulian Elischer 308