ubtbcmfw.c revision 187259
1/* 2 * ubtbcmfw.c 3 */ 4 5/*- 6 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $ 31 * $FreeBSD: head/sys/dev/usb2/bluetooth/ubtbcmfw2.c 187259 2009-01-15 02:35:40Z thompsa $ 32 */ 33 34#include <dev/usb2/include/usb2_devid.h> 35#include <dev/usb2/include/usb2_standard.h> 36#include <dev/usb2/include/usb2_mfunc.h> 37#include <dev/usb2/include/usb2_error.h> 38#include <dev/usb2/include/usb2_ioctl.h> 39 40#define USB_DEBUG_VAR usb2_debug 41 42#include <dev/usb2/core/usb2_core.h> 43#include <dev/usb2/core/usb2_debug.h> 44#include <dev/usb2/core/usb2_parse.h> 45#include <dev/usb2/core/usb2_lookup.h> 46#include <dev/usb2/core/usb2_util.h> 47#include <dev/usb2/core/usb2_busdma.h> 48#include <dev/usb2/core/usb2_mbuf.h> 49#include <dev/usb2/core/usb2_dev.h> 50 51/* 52 * Download firmware to BCM2033. 53 */ 54 55#define UBTBCMFW_CONFIG_NO 1 /* Config number */ 56#define UBTBCMFW_IFACE_IDX 0 /* Control interface */ 57#define UBTBCMFW_T_MAX 4 /* units */ 58 59enum { 60 UBTBCMFW_BULK_DT_WR, 61 UBTBCMFW_BULK_DT_RD, 62 UBTBCMFW_BULK_CS_WR, 63 UBTBCMFW_BULK_CS_RD, 64 UBTBCMFW_N_TRANSFER = 4, 65}; 66 67struct ubtbcmfw_softc { 68 struct usb2_fifo_sc sc_fifo; 69 struct mtx sc_mtx; 70 71 device_t sc_dev; 72 struct usb2_device *sc_udev; 73 struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER]; 74 75 uint8_t sc_flags; 76#define UBTBCMFW_FLAG_WRITE_STALL 0x01 77#define UBTBCMFW_FLAG_READ_STALL 0x02 78}; 79 80#define UBTBCMFW_BSIZE 1024 81#define UBTBCMFW_IFQ_MAXLEN 2 82 83/* prototypes */ 84 85static device_probe_t ubtbcmfw_probe; 86static device_attach_t ubtbcmfw_attach; 87static device_detach_t ubtbcmfw_detach; 88 89static usb2_callback_t ubtbcmfw_write_callback; 90static usb2_callback_t ubtbcmfw_write_clear_stall_callback; 91static usb2_callback_t ubtbcmfw_read_callback; 92static usb2_callback_t ubtbcmfw_read_clear_stall_callback; 93 94static usb2_fifo_close_t ubtbcmfw_close; 95static usb2_fifo_cmd_t ubtbcmfw_start_read; 96static usb2_fifo_cmd_t ubtbcmfw_start_write; 97static usb2_fifo_cmd_t ubtbcmfw_stop_read; 98static usb2_fifo_cmd_t ubtbcmfw_stop_write; 99static usb2_fifo_ioctl_t ubtbcmfw_ioctl; 100static usb2_fifo_open_t ubtbcmfw_open; 101 102static struct usb2_fifo_methods ubtbcmfw_fifo_methods = { 103 .f_close = &ubtbcmfw_close, 104 .f_ioctl = &ubtbcmfw_ioctl, 105 .f_open = &ubtbcmfw_open, 106 .f_start_read = &ubtbcmfw_start_read, 107 .f_start_write = &ubtbcmfw_start_write, 108 .f_stop_read = &ubtbcmfw_stop_read, 109 .f_stop_write = &ubtbcmfw_stop_write, 110 .basename[0] = "ubtbcmfw", 111 .basename[1] = "ubtbcmfw", 112 .basename[2] = "ubtbcmfw", 113 .postfix[0] = "", 114 .postfix[1] = ".1", 115 .postfix[2] = ".2", 116}; 117 118static const struct usb2_config ubtbcmfw_config[UBTBCMFW_T_MAX] = { 119 120 [UBTBCMFW_BULK_DT_WR] = { 121 .type = UE_BULK, 122 .endpoint = 0x02, /* fixed */ 123 .direction = UE_DIR_OUT, 124 .mh.bufsize = UBTBCMFW_BSIZE, 125 .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,}, 126 .mh.callback = &ubtbcmfw_write_callback, 127 }, 128 129 [UBTBCMFW_BULK_DT_RD] = { 130 .type = UE_INTERRUPT, 131 .endpoint = 0x01, /* fixed */ 132 .direction = UE_DIR_IN, 133 .mh.bufsize = UBTBCMFW_BSIZE, 134 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, 135 .mh.callback = &ubtbcmfw_read_callback, 136 }, 137 138 [UBTBCMFW_BULK_CS_WR] = { 139 .type = UE_CONTROL, 140 .endpoint = 0x00, /* Control pipe */ 141 .direction = UE_DIR_ANY, 142 .mh.bufsize = sizeof(struct usb2_device_request), 143 .mh.flags = {}, 144 .mh.callback = &ubtbcmfw_write_clear_stall_callback, 145 .mh.timeout = 1000, /* 1 second */ 146 .mh.interval = 50, /* 50ms */ 147 }, 148 149 [UBTBCMFW_BULK_CS_RD] = { 150 .type = UE_CONTROL, 151 .endpoint = 0x00, /* Control pipe */ 152 .direction = UE_DIR_ANY, 153 .mh.bufsize = sizeof(struct usb2_device_request), 154 .mh.flags = {}, 155 .mh.callback = &ubtbcmfw_read_clear_stall_callback, 156 .mh.timeout = 1000, /* 1 second */ 157 .mh.interval = 50, /* 50ms */ 158 }, 159}; 160 161/* 162 * Module 163 */ 164 165static devclass_t ubtbcmfw_devclass; 166 167static device_method_t ubtbcmfw_methods[] = { 168 DEVMETHOD(device_probe, ubtbcmfw_probe), 169 DEVMETHOD(device_attach, ubtbcmfw_attach), 170 DEVMETHOD(device_detach, ubtbcmfw_detach), 171 {0, 0} 172}; 173 174static driver_t ubtbcmfw_driver = { 175 .name = "ubtbcmfw", 176 .methods = ubtbcmfw_methods, 177 .size = sizeof(struct ubtbcmfw_softc), 178}; 179 180DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0); 181MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1); 182MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1); 183 184/* 185 * Probe for a USB Bluetooth device 186 */ 187 188static int 189ubtbcmfw_probe(device_t dev) 190{ 191 struct usb2_attach_arg *uaa = device_get_ivars(dev); 192 193 if (uaa->usb2_mode != USB_MODE_HOST) { 194 return (ENXIO); 195 } 196 if (uaa->info.bIfaceIndex != 0) 197 return (ENXIO); 198 199 /* Match the boot device. */ 200 if (uaa->info.idVendor == USB_VENDOR_BROADCOM && 201 uaa->info.idProduct == USB_PRODUCT_BROADCOM_BCM2033) 202 return (0); 203 204 return (ENXIO); 205} 206 207/* 208 * Attach the device 209 */ 210 211static int 212ubtbcmfw_attach(device_t dev) 213{ 214 struct usb2_attach_arg *uaa = device_get_ivars(dev); 215 struct ubtbcmfw_softc *sc = device_get_softc(dev); 216 int32_t err; 217 uint8_t iface_index; 218 219 if (sc == NULL) { 220 return (ENOMEM); 221 } 222 sc->sc_dev = dev; 223 sc->sc_udev = uaa->device; 224 225 device_set_usb2_desc(dev); 226 227 mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE); 228 229 iface_index = UBTBCMFW_IFACE_IDX; 230 err = usb2_transfer_setup(uaa->device, 231 &iface_index, sc->sc_xfer, ubtbcmfw_config, 232 UBTBCMFW_T_MAX, sc, &sc->sc_mtx); 233 if (err) { 234 device_printf(dev, "allocating USB transfers " 235 "failed, err=%s\n", usb2_errstr(err)); 236 goto detach; 237 } 238 /* set interface permissions */ 239 usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, 240 UID_ROOT, GID_OPERATOR, 0644); 241 242 err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, 243 &ubtbcmfw_fifo_methods, &sc->sc_fifo, 244 device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); 245 if (err) { 246 goto detach; 247 } 248 return (0); /* success */ 249 250detach: 251 ubtbcmfw_detach(dev); 252 return (ENOMEM); /* failure */ 253} 254 255/* 256 * Detach the device 257 */ 258 259static int 260ubtbcmfw_detach(device_t dev) 261{ 262 struct ubtbcmfw_softc *sc = device_get_softc(dev); 263 264 usb2_fifo_detach(&sc->sc_fifo); 265 266 usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_T_MAX); 267 268 mtx_destroy(&sc->sc_mtx); 269 270 return (0); 271} 272 273static void 274ubtbcmfw_write_callback(struct usb2_xfer *xfer) 275{ 276 struct ubtbcmfw_softc *sc = xfer->priv_sc; 277 struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; 278 uint32_t actlen; 279 280 switch (USB_GET_STATE(xfer)) { 281 case USB_ST_TRANSFERRED: 282 case USB_ST_SETUP: 283 if (sc->sc_flags & UBTBCMFW_FLAG_WRITE_STALL) { 284 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]); 285 return; 286 } 287 if (usb2_fifo_get_data(f, xfer->frbuffers, 0, 288 UBTBCMFW_BSIZE, &actlen, 0)) { 289 290 xfer->frlengths[0] = actlen; 291 usb2_start_hardware(xfer); 292 } 293 return; 294 295 default: /* Error */ 296 if (xfer->error != USB_ERR_CANCELLED) { 297 /* try to clear stall first */ 298 sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; 299 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]); 300 } 301 return; 302 } 303} 304 305static void 306ubtbcmfw_write_clear_stall_callback(struct usb2_xfer *xfer) 307{ 308 struct ubtbcmfw_softc *sc = xfer->priv_sc; 309 struct usb2_xfer *xfer_other = sc->sc_xfer[UBTBCMFW_BULK_DT_WR]; 310 311 if (usb2_clear_stall_callback(xfer, xfer_other)) { 312 DPRINTF("stall cleared\n"); 313 sc->sc_flags &= ~UBTBCMFW_FLAG_WRITE_STALL; 314 usb2_transfer_start(xfer_other); 315 } 316} 317 318static void 319ubtbcmfw_read_callback(struct usb2_xfer *xfer) 320{ 321 struct ubtbcmfw_softc *sc = xfer->priv_sc; 322 struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; 323 324 switch (USB_GET_STATE(xfer)) { 325 case USB_ST_TRANSFERRED: 326 usb2_fifo_put_data(f, xfer->frbuffers, 327 0, xfer->actlen, 1); 328 329 case USB_ST_SETUP: 330 if (sc->sc_flags & UBTBCMFW_FLAG_READ_STALL) { 331 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]); 332 return; 333 } 334 if (usb2_fifo_put_bytes_max(f) != 0) { 335 xfer->frlengths[0] = xfer->max_data_length; 336 usb2_start_hardware(xfer); 337 } 338 return; 339 340 default: /* Error */ 341 if (xfer->error != USB_ERR_CANCELLED) { 342 /* try to clear stall first */ 343 sc->sc_flags |= UBTBCMFW_FLAG_READ_STALL; 344 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]); 345 } 346 return; 347 } 348} 349 350static void 351ubtbcmfw_read_clear_stall_callback(struct usb2_xfer *xfer) 352{ 353 struct ubtbcmfw_softc *sc = xfer->priv_sc; 354 struct usb2_xfer *xfer_other = sc->sc_xfer[UBTBCMFW_BULK_DT_RD]; 355 356 if (usb2_clear_stall_callback(xfer, xfer_other)) { 357 DPRINTF("stall cleared\n"); 358 sc->sc_flags &= ~UBTBCMFW_FLAG_READ_STALL; 359 usb2_transfer_start(xfer_other); 360 } 361} 362 363static void 364ubtbcmfw_start_read(struct usb2_fifo *fifo) 365{ 366 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 367 368 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_RD]); 369} 370 371static void 372ubtbcmfw_stop_read(struct usb2_fifo *fifo) 373{ 374 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 375 376 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]); 377 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_RD]); 378} 379 380static void 381ubtbcmfw_start_write(struct usb2_fifo *fifo) 382{ 383 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 384 385 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); 386} 387 388static void 389ubtbcmfw_stop_write(struct usb2_fifo *fifo) 390{ 391 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 392 393 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]); 394 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); 395} 396 397static int 398ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) 399{ 400 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 401 402 if (fflags & FREAD) { 403 if (usb2_fifo_alloc_buffer(fifo, 404 sc->sc_xfer[UBTBCMFW_BULK_DT_RD]->max_data_length, 405 UBTBCMFW_IFQ_MAXLEN)) { 406 return (ENOMEM); 407 } 408 } 409 if (fflags & FWRITE) { 410 /* clear stall first */ 411 mtx_lock(&sc->sc_mtx); 412 sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; 413 mtx_unlock(&sc->sc_mtx); 414 if (usb2_fifo_alloc_buffer(fifo, 415 sc->sc_xfer[UBTBCMFW_BULK_DT_WR]->max_data_length, 416 UBTBCMFW_IFQ_MAXLEN)) { 417 return (ENOMEM); 418 } 419 } 420 return (0); 421} 422 423static void 424ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) 425{ 426 if (fflags & (FREAD | FWRITE)) { 427 usb2_fifo_free_buffer(fifo); 428 } 429} 430 431static int 432ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, 433 int fflags, struct thread *td) 434{ 435 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 436 int error = 0; 437 438 switch (cmd) { 439 case USB_GET_DEVICE_DESC: 440 *(struct usb2_device_descriptor *)data = 441 *usb2_get_device_descriptor(sc->sc_udev); 442 break; 443 444 default: 445 error = EINVAL; 446 break; 447 } 448 return (error); 449} 450