bcmfw.c revision 114881
1114881Sjulian/* 2114881Sjulian * bcmfw.c 3114881Sjulian * 4114881Sjulian * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5114881Sjulian * All rights reserved. 6114881Sjulian * 7114881Sjulian * Redistribution and use in source and binary forms, with or without 8114881Sjulian * modification, are permitted provided that the following conditions 9114881Sjulian * are met: 10114881Sjulian * 1. Redistributions of source code must retain the above copyright 11114881Sjulian * notice, this list of conditions and the following disclaimer. 12114881Sjulian * 2. Redistributions in binary form must reproduce the above copyright 13114881Sjulian * notice, this list of conditions and the following disclaimer in the 14114881Sjulian * documentation and/or other materials provided with the distribution. 15114881Sjulian * 16114881Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17114881Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18114881Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19114881Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20114881Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21114881Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22114881Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23114881Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24114881Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25114881Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26114881Sjulian * SUCH DAMAGE. 27114881Sjulian * 28114881Sjulian * $Id: bcmfw.c,v 1.4 2003/04/27 19:28:09 max Exp $ 29114881Sjulian * $FreeBSD: head/usr.sbin/bluetooth/bcmfw/bcmfw.c 114881 2003-05-10 22:03:45Z julian $ 30114881Sjulian * 31114881Sjulian * Based on Linux BlueZ BlueFW-0.9 package 32114881Sjulian * 33114881Sjulian */ 34114881Sjulian 35114881Sjulian#include <sys/types.h> 36114881Sjulian#include <sys/ioctl.h> 37114881Sjulian#include <dev/usb/usb.h> 38114881Sjulian#include <dev/usb/usbdevs.h> 39114881Sjulian#include <errno.h> 40114881Sjulian#include <fcntl.h> 41114881Sjulian#include <netgraph.h> 42114881Sjulian#include <stdarg.h> 43114881Sjulian#include <stdio.h> 44114881Sjulian#include <stdlib.h> 45114881Sjulian#include <string.h> 46114881Sjulian#include <syslog.h> 47114881Sjulian#include <unistd.h> 48114881Sjulian 49114881Sjulian#define BCMFW "bcmfw" 50114881Sjulian#define BCMFW_INTR_EP 1 51114881Sjulian#define BCMFW_BULK_EP 2 52114881Sjulian#define BCMFW_BSIZE 4096 53114881Sjulian 54114881Sjulianstatic int bcmfw_check_device 55114881Sjulian (char const *name); 56114881Sjulianstatic int bcmfw_load_firmware 57114881Sjulian (char const *name, char const *md, char const *fw); 58114881Sjulianstatic void bcmfw_usage 59114881Sjulian (void); 60114881Sjulian 61114881Sjulian/* 62114881Sjulian * Main 63114881Sjulian */ 64114881Sjulian 65114881Sjulianint 66114881Sjulianmain(int argc, char *argv[]) 67114881Sjulian{ 68114881Sjulian char *name = NULL, *md = NULL, *fw = NULL; 69114881Sjulian int x; 70114881Sjulian 71114881Sjulian while ((x = getopt(argc, argv, "f:hn:m:")) != -1) { 72114881Sjulian switch (x) { 73114881Sjulian case 'f': /* firmware file */ 74114881Sjulian fw = optarg; 75114881Sjulian break; 76114881Sjulian 77114881Sjulian case 'n': /* name */ 78114881Sjulian name = optarg; 79114881Sjulian break; 80114881Sjulian 81114881Sjulian case 'm': /* Mini-driver */ 82114881Sjulian md = optarg; 83114881Sjulian break; 84114881Sjulian 85114881Sjulian case 'h': 86114881Sjulian default: 87114881Sjulian bcmfw_usage(); 88114881Sjulian /* NOT REACHED */ 89114881Sjulian } 90114881Sjulian } 91114881Sjulian 92114881Sjulian if (name == NULL || md == NULL || fw == NULL) 93114881Sjulian bcmfw_usage(); 94114881Sjulian /* NOT REACHED */ 95114881Sjulian 96114881Sjulian openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 97114881Sjulian 98114881Sjulian if (bcmfw_check_device(name) < 0) 99114881Sjulian exit(1); 100114881Sjulian 101114881Sjulian if (bcmfw_load_firmware(name, md, fw) < 0) 102114881Sjulian exit(1); 103114881Sjulian 104114881Sjulian closelog(); 105114881Sjulian 106114881Sjulian return (0); 107114881Sjulian} /* main */ 108114881Sjulian 109114881Sjulian/* 110114881Sjulian * Check device VendorID/ProductID 111114881Sjulian */ 112114881Sjulian 113114881Sjulianstatic int 114114881Sjulianbcmfw_check_device(char const *name) 115114881Sjulian{ 116114881Sjulian usb_device_descriptor_t desc; 117114881Sjulian char path[BCMFW_BSIZE]; 118114881Sjulian int fd = -1, error = -1; 119114881Sjulian 120114881Sjulian snprintf(path, sizeof(path), "/dev/%s", name); 121114881Sjulian 122114881Sjulian if ((fd = open(path, O_WRONLY)) < 0) { 123114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 124114881Sjulian path, strerror(errno), errno); 125114881Sjulian goto out; 126114881Sjulian } 127114881Sjulian 128114881Sjulian if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) { 129114881Sjulian syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)", 130114881Sjulian fd, USB_GET_DEVICE_DESC, &desc, 131114881Sjulian strerror(errno), errno); 132114881Sjulian goto out; 133114881Sjulian } 134114881Sjulian 135114881Sjulian if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM || 136114881Sjulian UGETW(desc.idProduct) != 0x2033) { 137114881Sjulian syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \ 138114881Sjulian "ProductID=%#x", UGETW(desc.idVendor), 139114881Sjulian UGETW(desc.idProduct)); 140114881Sjulian error = -1; 141114881Sjulian } else 142114881Sjulian error = 0; 143114881Sjulianout: 144114881Sjulian if (fd != -1) 145114881Sjulian close(fd); 146114881Sjulian 147114881Sjulian return (error); 148114881Sjulian} /* bcmfw_check_device */ 149114881Sjulian 150114881Sjulian/* 151114881Sjulian * Download minidriver and firmware 152114881Sjulian */ 153114881Sjulian 154114881Sjulianstatic int 155114881Sjulianbcmfw_load_firmware(char const *name, char const *md, char const *fw) 156114881Sjulian{ 157114881Sjulian char buf[BCMFW_BSIZE]; 158114881Sjulian int intr = -1, bulk = -1, fd = -1, error = -1, len; 159114881Sjulian 160114881Sjulian /* Open interrupt endpoint device */ 161114881Sjulian snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP); 162114881Sjulian if ((intr = open(buf, O_RDONLY)) < 0) { 163114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 164114881Sjulian buf, strerror(errno), errno); 165114881Sjulian goto out; 166114881Sjulian } 167114881Sjulian 168114881Sjulian /* Open bulk endpoint device */ 169114881Sjulian snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP); 170114881Sjulian if ((bulk = open(buf, O_WRONLY)) < 0) { 171114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 172114881Sjulian buf, strerror(errno), errno); 173114881Sjulian goto out; 174114881Sjulian } 175114881Sjulian 176114881Sjulian /* 177114881Sjulian * Load mini-driver 178114881Sjulian */ 179114881Sjulian 180114881Sjulian if ((fd = open(md, O_RDONLY)) < 0) { 181114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 182114881Sjulian md, strerror(errno), errno); 183114881Sjulian goto out; 184114881Sjulian } 185114881Sjulian 186114881Sjulian for (;;) { 187114881Sjulian len = read(fd, buf, sizeof(buf)); 188114881Sjulian if (len < 0) { 189114881Sjulian syslog(LOG_ERR, "Could not read(%s). %s (%d)", 190114881Sjulian md, strerror(errno), errno); 191114881Sjulian goto out; 192114881Sjulian } 193114881Sjulian if (len == 0) 194114881Sjulian break; 195114881Sjulian 196114881Sjulian len = write(bulk, buf, len); 197114881Sjulian if (len < 0) { 198114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 199114881Sjulian name, BCMFW_BULK_EP, strerror(errno), 200114881Sjulian errno); 201114881Sjulian goto out; 202114881Sjulian } 203114881Sjulian } 204114881Sjulian 205114881Sjulian close(fd); 206114881Sjulian fd = -1; 207114881Sjulian 208114881Sjulian usleep(10); 209114881Sjulian 210114881Sjulian /* 211114881Sjulian * Memory select 212114881Sjulian */ 213114881Sjulian 214114881Sjulian if (write(bulk, "#", 1) < 0) { 215114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 216114881Sjulian name, BCMFW_BULK_EP, strerror(errno), errno); 217114881Sjulian goto out; 218114881Sjulian } 219114881Sjulian 220114881Sjulian if (read(intr, buf, sizeof(buf)) < 0) { 221114881Sjulian syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 222114881Sjulian name, BCMFW_INTR_EP, strerror(errno), errno); 223114881Sjulian goto out; 224114881Sjulian } 225114881Sjulian 226114881Sjulian if (buf[0] != '#') { 227114881Sjulian syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]); 228114881Sjulian goto out; 229114881Sjulian } 230114881Sjulian 231114881Sjulian /* 232114881Sjulian * Load firmware 233114881Sjulian */ 234114881Sjulian 235114881Sjulian if ((fd = open(fw, O_RDONLY)) < 0) { 236114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 237114881Sjulian fw, strerror(errno), errno); 238114881Sjulian goto out; 239114881Sjulian } 240114881Sjulian 241114881Sjulian for (;;) { 242114881Sjulian len = read(fd, buf, sizeof(buf)); 243114881Sjulian if (len < 0) { 244114881Sjulian syslog(LOG_ERR, "Could not read(%s). %s (%d)", 245114881Sjulian fw, strerror(errno), errno); 246114881Sjulian goto out; 247114881Sjulian } 248114881Sjulian if (len == 0) 249114881Sjulian break; 250114881Sjulian 251114881Sjulian len = write(bulk, buf, len); 252114881Sjulian if (len < 0) { 253114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 254114881Sjulian name, BCMFW_BULK_EP, strerror(errno), 255114881Sjulian errno); 256114881Sjulian goto out; 257114881Sjulian } 258114881Sjulian } 259114881Sjulian 260114881Sjulian close(fd); 261114881Sjulian fd = -1; 262114881Sjulian 263114881Sjulian if (read(intr, buf, sizeof(buf)) < 0) { 264114881Sjulian syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 265114881Sjulian name, BCMFW_INTR_EP, strerror(errno), errno); 266114881Sjulian goto out; 267114881Sjulian } 268114881Sjulian 269114881Sjulian if (buf[0] != '.') { 270114881Sjulian syslog(LOG_ERR, "%s: Could not load firmware (%c)", 271114881Sjulian name, buf[0]); 272114881Sjulian goto out; 273114881Sjulian } 274114881Sjulian 275114881Sjulian usleep(500000); 276114881Sjulian error = 0; 277114881Sjulianout: 278114881Sjulian if (fd != -1) 279114881Sjulian close(fd); 280114881Sjulian if (bulk != -1) 281114881Sjulian close(bulk); 282114881Sjulian if (intr != -1) 283114881Sjulian close(intr); 284114881Sjulian 285114881Sjulian return (error); 286114881Sjulian} /* bcmfw_load_firmware */ 287114881Sjulian 288114881Sjulian/* 289114881Sjulian * Display usage message and quit 290114881Sjulian */ 291114881Sjulian 292114881Sjulianstatic void 293114881Sjulianbcmfw_usage(void) 294114881Sjulian{ 295114881Sjulian fprintf(stdout, 296114881Sjulian"Usage: %s -n name -m md_file -f fw_file\n" 297114881Sjulian"Where:\n" \ 298114881Sjulian"\t-n name device name\n" \ 299114881Sjulian"\t-m mini-driver image mini-driver image file name for download\n" \ 300114881Sjulian"\t-f firmware image firmware image file name for download\n" \ 301114881Sjulian"\t-h display this message\n", BCMFW); 302114881Sjulian 303114881Sjulian exit(255); 304114881Sjulian} /* bcmfw_usage */ 305114881Sjulian 306