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$ 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> 38188945Sthompsa#include <dev/usb/usb_ioctl.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 54131120Simp#define USB_VENDOR_BROADCOM 0x0a5c 55131120Simp#define USB_PRODUCT_BROADCOM_BCM2033 0x2033 56131120Simp 57114881Sjulianstatic int bcmfw_check_device 58114881Sjulian (char const *name); 59114881Sjulianstatic int bcmfw_load_firmware 60114881Sjulian (char const *name, char const *md, char const *fw); 61114881Sjulianstatic void bcmfw_usage 62114881Sjulian (void); 63114881Sjulian 64114881Sjulian/* 65114881Sjulian * Main 66114881Sjulian */ 67114881Sjulian 68114881Sjulianint 69114881Sjulianmain(int argc, char *argv[]) 70114881Sjulian{ 71114881Sjulian char *name = NULL, *md = NULL, *fw = NULL; 72114881Sjulian int x; 73114881Sjulian 74114881Sjulian while ((x = getopt(argc, argv, "f:hn:m:")) != -1) { 75114881Sjulian switch (x) { 76114881Sjulian case 'f': /* firmware file */ 77114881Sjulian fw = optarg; 78114881Sjulian break; 79114881Sjulian 80114881Sjulian case 'n': /* name */ 81114881Sjulian name = optarg; 82114881Sjulian break; 83114881Sjulian 84114881Sjulian case 'm': /* Mini-driver */ 85114881Sjulian md = optarg; 86114881Sjulian break; 87114881Sjulian 88114881Sjulian case 'h': 89114881Sjulian default: 90114881Sjulian bcmfw_usage(); 91114881Sjulian /* NOT REACHED */ 92114881Sjulian } 93114881Sjulian } 94114881Sjulian 95114881Sjulian if (name == NULL || md == NULL || fw == NULL) 96114881Sjulian bcmfw_usage(); 97114881Sjulian /* NOT REACHED */ 98114881Sjulian 99114881Sjulian openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER); 100114881Sjulian 101114881Sjulian if (bcmfw_check_device(name) < 0) 102114881Sjulian exit(1); 103114881Sjulian 104114881Sjulian if (bcmfw_load_firmware(name, md, fw) < 0) 105114881Sjulian exit(1); 106114881Sjulian 107114881Sjulian closelog(); 108114881Sjulian 109114881Sjulian return (0); 110114881Sjulian} /* main */ 111114881Sjulian 112114881Sjulian/* 113114881Sjulian * Check device VendorID/ProductID 114114881Sjulian */ 115114881Sjulian 116114881Sjulianstatic int 117114881Sjulianbcmfw_check_device(char const *name) 118114881Sjulian{ 119114881Sjulian usb_device_descriptor_t desc; 120114881Sjulian char path[BCMFW_BSIZE]; 121114881Sjulian int fd = -1, error = -1; 122114881Sjulian 123114881Sjulian snprintf(path, sizeof(path), "/dev/%s", name); 124114881Sjulian 125114881Sjulian if ((fd = open(path, O_WRONLY)) < 0) { 126114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 127114881Sjulian path, strerror(errno), errno); 128114881Sjulian goto out; 129114881Sjulian } 130114881Sjulian 131114881Sjulian if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) { 132114881Sjulian syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)", 133114881Sjulian fd, USB_GET_DEVICE_DESC, &desc, 134114881Sjulian strerror(errno), errno); 135114881Sjulian goto out; 136114881Sjulian } 137114881Sjulian 138114881Sjulian if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM || 139131120Simp UGETW(desc.idProduct) != USB_PRODUCT_BROADCOM_BCM2033) { 140114881Sjulian syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \ 141114881Sjulian "ProductID=%#x", UGETW(desc.idVendor), 142114881Sjulian UGETW(desc.idProduct)); 143114881Sjulian error = -1; 144114881Sjulian } else 145114881Sjulian error = 0; 146114881Sjulianout: 147114881Sjulian if (fd != -1) 148114881Sjulian close(fd); 149114881Sjulian 150114881Sjulian return (error); 151114881Sjulian} /* bcmfw_check_device */ 152114881Sjulian 153114881Sjulian/* 154114881Sjulian * Download minidriver and firmware 155114881Sjulian */ 156114881Sjulian 157114881Sjulianstatic int 158114881Sjulianbcmfw_load_firmware(char const *name, char const *md, char const *fw) 159114881Sjulian{ 160114881Sjulian char buf[BCMFW_BSIZE]; 161114881Sjulian int intr = -1, bulk = -1, fd = -1, error = -1, len; 162114881Sjulian 163114881Sjulian /* Open interrupt endpoint device */ 164114881Sjulian snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP); 165114881Sjulian if ((intr = open(buf, O_RDONLY)) < 0) { 166114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 167114881Sjulian buf, strerror(errno), errno); 168114881Sjulian goto out; 169114881Sjulian } 170114881Sjulian 171114881Sjulian /* Open bulk endpoint device */ 172114881Sjulian snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP); 173114881Sjulian if ((bulk = open(buf, O_WRONLY)) < 0) { 174114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 175114881Sjulian buf, strerror(errno), errno); 176114881Sjulian goto out; 177114881Sjulian } 178114881Sjulian 179114881Sjulian /* 180114881Sjulian * Load mini-driver 181114881Sjulian */ 182114881Sjulian 183114881Sjulian if ((fd = open(md, O_RDONLY)) < 0) { 184114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 185114881Sjulian md, strerror(errno), errno); 186114881Sjulian goto out; 187114881Sjulian } 188114881Sjulian 189114881Sjulian for (;;) { 190114881Sjulian len = read(fd, buf, sizeof(buf)); 191114881Sjulian if (len < 0) { 192114881Sjulian syslog(LOG_ERR, "Could not read(%s). %s (%d)", 193114881Sjulian md, strerror(errno), errno); 194114881Sjulian goto out; 195114881Sjulian } 196114881Sjulian if (len == 0) 197114881Sjulian break; 198114881Sjulian 199114881Sjulian len = write(bulk, buf, len); 200114881Sjulian if (len < 0) { 201114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 202114881Sjulian name, BCMFW_BULK_EP, strerror(errno), 203114881Sjulian errno); 204114881Sjulian goto out; 205114881Sjulian } 206114881Sjulian } 207114881Sjulian 208114881Sjulian close(fd); 209114881Sjulian fd = -1; 210114881Sjulian 211114881Sjulian usleep(10); 212114881Sjulian 213114881Sjulian /* 214114881Sjulian * Memory select 215114881Sjulian */ 216114881Sjulian 217114881Sjulian if (write(bulk, "#", 1) < 0) { 218114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 219114881Sjulian name, BCMFW_BULK_EP, strerror(errno), errno); 220114881Sjulian goto out; 221114881Sjulian } 222114881Sjulian 223114881Sjulian if (read(intr, buf, sizeof(buf)) < 0) { 224114881Sjulian syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 225114881Sjulian name, BCMFW_INTR_EP, strerror(errno), errno); 226114881Sjulian goto out; 227114881Sjulian } 228114881Sjulian 229114881Sjulian if (buf[0] != '#') { 230114881Sjulian syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]); 231114881Sjulian goto out; 232114881Sjulian } 233114881Sjulian 234114881Sjulian /* 235114881Sjulian * Load firmware 236114881Sjulian */ 237114881Sjulian 238114881Sjulian if ((fd = open(fw, O_RDONLY)) < 0) { 239114881Sjulian syslog(LOG_ERR, "Could not open(%s). %s (%d)", 240114881Sjulian fw, strerror(errno), errno); 241114881Sjulian goto out; 242114881Sjulian } 243114881Sjulian 244114881Sjulian for (;;) { 245114881Sjulian len = read(fd, buf, sizeof(buf)); 246114881Sjulian if (len < 0) { 247114881Sjulian syslog(LOG_ERR, "Could not read(%s). %s (%d)", 248114881Sjulian fw, strerror(errno), errno); 249114881Sjulian goto out; 250114881Sjulian } 251114881Sjulian if (len == 0) 252114881Sjulian break; 253114881Sjulian 254114881Sjulian len = write(bulk, buf, len); 255114881Sjulian if (len < 0) { 256114881Sjulian syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)", 257114881Sjulian name, BCMFW_BULK_EP, strerror(errno), 258114881Sjulian errno); 259114881Sjulian goto out; 260114881Sjulian } 261114881Sjulian } 262114881Sjulian 263114881Sjulian close(fd); 264114881Sjulian fd = -1; 265114881Sjulian 266114881Sjulian if (read(intr, buf, sizeof(buf)) < 0) { 267114881Sjulian syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)", 268114881Sjulian name, BCMFW_INTR_EP, strerror(errno), errno); 269114881Sjulian goto out; 270114881Sjulian } 271114881Sjulian 272114881Sjulian if (buf[0] != '.') { 273114881Sjulian syslog(LOG_ERR, "%s: Could not load firmware (%c)", 274114881Sjulian name, buf[0]); 275114881Sjulian goto out; 276114881Sjulian } 277114881Sjulian 278114881Sjulian usleep(500000); 279114881Sjulian error = 0; 280114881Sjulianout: 281114881Sjulian if (fd != -1) 282114881Sjulian close(fd); 283114881Sjulian if (bulk != -1) 284114881Sjulian close(bulk); 285114881Sjulian if (intr != -1) 286114881Sjulian close(intr); 287114881Sjulian 288114881Sjulian return (error); 289114881Sjulian} /* bcmfw_load_firmware */ 290114881Sjulian 291114881Sjulian/* 292114881Sjulian * Display usage message and quit 293114881Sjulian */ 294114881Sjulian 295114881Sjulianstatic void 296114881Sjulianbcmfw_usage(void) 297114881Sjulian{ 298114881Sjulian fprintf(stdout, 299114881Sjulian"Usage: %s -n name -m md_file -f fw_file\n" 300114881Sjulian"Where:\n" \ 301114881Sjulian"\t-n name device name\n" \ 302114881Sjulian"\t-m mini-driver image mini-driver image file name for download\n" \ 303114881Sjulian"\t-f firmware image firmware image file name for download\n" \ 304114881Sjulian"\t-h display this message\n", BCMFW); 305114881Sjulian 306114881Sjulian exit(255); 307114881Sjulian} /* bcmfw_usage */ 308114881Sjulian 309