ubtbcmfw.c revision 184610
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 184610 2008-11-04 02:31:03Z alfred $ 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 59struct ubtbcmfw_softc { 60 struct usb2_fifo_sc sc_fifo; 61 struct mtx sc_mtx; 62 63 device_t sc_dev; 64 struct usb2_device *sc_udev; 65 struct usb2_xfer *sc_xfer[UBTBCMFW_T_MAX]; 66 67 uint8_t sc_flags; 68#define UBTBCMFW_FLAG_WRITE_STALL 0x01 69#define UBTBCMFW_FLAG_READ_STALL 0x02 70}; 71 72#define UBTBCMFW_BSIZE 1024 73#define UBTBCMFW_IFQ_MAXLEN 2 74 75/* prototypes */ 76 77static device_probe_t ubtbcmfw_probe; 78static device_attach_t ubtbcmfw_attach; 79static device_detach_t ubtbcmfw_detach; 80 81static usb2_callback_t ubtbcmfw_write_callback; 82static usb2_callback_t ubtbcmfw_write_clear_stall_callback; 83static usb2_callback_t ubtbcmfw_read_callback; 84static usb2_callback_t ubtbcmfw_read_clear_stall_callback; 85 86static usb2_fifo_close_t ubtbcmfw_close; 87static usb2_fifo_cmd_t ubtbcmfw_start_read; 88static usb2_fifo_cmd_t ubtbcmfw_start_write; 89static usb2_fifo_cmd_t ubtbcmfw_stop_read; 90static usb2_fifo_cmd_t ubtbcmfw_stop_write; 91static usb2_fifo_ioctl_t ubtbcmfw_ioctl; 92static usb2_fifo_open_t ubtbcmfw_open; 93 94static struct usb2_fifo_methods ubtbcmfw_fifo_methods = { 95 .f_close = &ubtbcmfw_close, 96 .f_ioctl = &ubtbcmfw_ioctl, 97 .f_open = &ubtbcmfw_open, 98 .f_start_read = &ubtbcmfw_start_read, 99 .f_start_write = &ubtbcmfw_start_write, 100 .f_stop_read = &ubtbcmfw_stop_read, 101 .f_stop_write = &ubtbcmfw_stop_write, 102 .basename[0] = "ubtbcmfw", 103 .basename[1] = "ubtbcmfw", 104 .basename[2] = "ubtbcmfw", 105 .postfix[0] = "", 106 .postfix[1] = ".1", 107 .postfix[2] = ".2", 108}; 109 110static const struct usb2_config ubtbcmfw_config[UBTBCMFW_T_MAX] = { 111 112 [0] = { 113 .type = UE_BULK, 114 .endpoint = 0x02, /* fixed */ 115 .direction = UE_DIR_OUT, 116 .mh.bufsize = UBTBCMFW_BSIZE, 117 .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,}, 118 .mh.callback = &ubtbcmfw_write_callback, 119 }, 120 121 [1] = { 122 .type = UE_INTERRUPT, 123 .endpoint = 0x01, /* fixed */ 124 .direction = UE_DIR_IN, 125 .mh.bufsize = UBTBCMFW_BSIZE, 126 .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,}, 127 .mh.callback = &ubtbcmfw_read_callback, 128 }, 129 130 [2] = { 131 .type = UE_CONTROL, 132 .endpoint = 0x00, /* Control pipe */ 133 .direction = UE_DIR_ANY, 134 .mh.bufsize = sizeof(struct usb2_device_request), 135 .mh.flags = {}, 136 .mh.callback = &ubtbcmfw_write_clear_stall_callback, 137 .mh.timeout = 1000, /* 1 second */ 138 .mh.interval = 50, /* 50ms */ 139 }, 140 141 [3] = { 142 .type = UE_CONTROL, 143 .endpoint = 0x00, /* Control pipe */ 144 .direction = UE_DIR_ANY, 145 .mh.bufsize = sizeof(struct usb2_device_request), 146 .mh.flags = {}, 147 .mh.callback = &ubtbcmfw_read_clear_stall_callback, 148 .mh.timeout = 1000, /* 1 second */ 149 .mh.interval = 50, /* 50ms */ 150 }, 151}; 152 153/* 154 * Module 155 */ 156 157static devclass_t ubtbcmfw_devclass; 158 159static device_method_t ubtbcmfw_methods[] = { 160 DEVMETHOD(device_probe, ubtbcmfw_probe), 161 DEVMETHOD(device_attach, ubtbcmfw_attach), 162 DEVMETHOD(device_detach, ubtbcmfw_detach), 163 {0, 0} 164}; 165 166static driver_t ubtbcmfw_driver = { 167 .name = "ubtbcmfw", 168 .methods = ubtbcmfw_methods, 169 .size = sizeof(struct ubtbcmfw_softc), 170}; 171 172DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0); 173MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1); 174MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1); 175 176/* 177 * Probe for a USB Bluetooth device 178 */ 179 180static int 181ubtbcmfw_probe(device_t dev) 182{ 183 struct usb2_attach_arg *uaa = device_get_ivars(dev); 184 185 if (uaa->usb2_mode != USB_MODE_HOST) { 186 return (ENXIO); 187 } 188 if (uaa->info.bIfaceIndex != 0) 189 return (ENXIO); 190 191 /* Match the boot device. */ 192 if (uaa->info.idVendor == USB_VENDOR_BROADCOM && 193 uaa->info.idProduct == USB_PRODUCT_BROADCOM_BCM2033) 194 return (0); 195 196 return (ENXIO); 197} 198 199/* 200 * Attach the device 201 */ 202 203static int 204ubtbcmfw_attach(device_t dev) 205{ 206 struct usb2_attach_arg *uaa = device_get_ivars(dev); 207 struct ubtbcmfw_softc *sc = device_get_softc(dev); 208 int32_t err; 209 uint8_t iface_index; 210 211 if (sc == NULL) { 212 return (ENOMEM); 213 } 214 sc->sc_dev = dev; 215 sc->sc_udev = uaa->device; 216 217 device_set_usb2_desc(dev); 218 219 mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE); 220 221 iface_index = UBTBCMFW_IFACE_IDX; 222 err = usb2_transfer_setup(uaa->device, 223 &iface_index, sc->sc_xfer, ubtbcmfw_config, 224 UBTBCMFW_T_MAX, sc, &sc->sc_mtx); 225 if (err) { 226 device_printf(dev, "allocating USB transfers " 227 "failed, err=%s\n", usb2_errstr(err)); 228 goto detach; 229 } 230 /* set interface permissions */ 231 usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, 232 UID_ROOT, GID_OPERATOR, 0644); 233 234 err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, 235 &ubtbcmfw_fifo_methods, &sc->sc_fifo, 236 device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); 237 if (err) { 238 goto detach; 239 } 240 return (0); /* success */ 241 242detach: 243 ubtbcmfw_detach(dev); 244 return (ENOMEM); /* failure */ 245} 246 247/* 248 * Detach the device 249 */ 250 251static int 252ubtbcmfw_detach(device_t dev) 253{ 254 struct ubtbcmfw_softc *sc = device_get_softc(dev); 255 256 usb2_fifo_detach(&sc->sc_fifo); 257 258 usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_T_MAX); 259 260 mtx_destroy(&sc->sc_mtx); 261 262 return (0); 263} 264 265static void 266ubtbcmfw_write_callback(struct usb2_xfer *xfer) 267{ 268 struct ubtbcmfw_softc *sc = xfer->priv_sc; 269 struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; 270 uint32_t actlen; 271 272 switch (USB_GET_STATE(xfer)) { 273 case USB_ST_TRANSFERRED: 274 case USB_ST_SETUP: 275 if (sc->sc_flags & UBTBCMFW_FLAG_WRITE_STALL) { 276 usb2_transfer_start(sc->sc_xfer[2]); 277 return; 278 } 279 if (usb2_fifo_get_data(f, xfer->frbuffers, 0, 280 UBTBCMFW_BSIZE, &actlen, 0)) { 281 282 xfer->frlengths[0] = actlen; 283 usb2_start_hardware(xfer); 284 } 285 return; 286 287 default: /* Error */ 288 if (xfer->error != USB_ERR_CANCELLED) { 289 /* try to clear stall first */ 290 sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; 291 usb2_transfer_start(sc->sc_xfer[2]); 292 } 293 return; 294 } 295} 296 297static void 298ubtbcmfw_write_clear_stall_callback(struct usb2_xfer *xfer) 299{ 300 struct ubtbcmfw_softc *sc = xfer->priv_sc; 301 struct usb2_xfer *xfer_other = sc->sc_xfer[0]; 302 303 if (usb2_clear_stall_callback(xfer, xfer_other)) { 304 DPRINTF("stall cleared\n"); 305 sc->sc_flags &= ~UBTBCMFW_FLAG_WRITE_STALL; 306 usb2_transfer_start(xfer_other); 307 } 308 return; 309} 310 311static void 312ubtbcmfw_read_callback(struct usb2_xfer *xfer) 313{ 314 struct ubtbcmfw_softc *sc = xfer->priv_sc; 315 struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX]; 316 317 switch (USB_GET_STATE(xfer)) { 318 case USB_ST_TRANSFERRED: 319 usb2_fifo_put_data(f, xfer->frbuffers, 320 0, xfer->actlen, 1); 321 322 case USB_ST_SETUP: 323 if (sc->sc_flags & UBTBCMFW_FLAG_READ_STALL) { 324 usb2_transfer_start(sc->sc_xfer[3]); 325 return; 326 } 327 if (usb2_fifo_put_bytes_max(f) != 0) { 328 xfer->frlengths[0] = xfer->max_data_length; 329 usb2_start_hardware(xfer); 330 } 331 return; 332 333 default: /* Error */ 334 if (xfer->error != USB_ERR_CANCELLED) { 335 /* try to clear stall first */ 336 sc->sc_flags |= UBTBCMFW_FLAG_READ_STALL; 337 usb2_transfer_start(sc->sc_xfer[3]); 338 } 339 return; 340 } 341} 342 343static void 344ubtbcmfw_read_clear_stall_callback(struct usb2_xfer *xfer) 345{ 346 struct ubtbcmfw_softc *sc = xfer->priv_sc; 347 struct usb2_xfer *xfer_other = sc->sc_xfer[1]; 348 349 if (usb2_clear_stall_callback(xfer, xfer_other)) { 350 DPRINTF("stall cleared\n"); 351 sc->sc_flags &= ~UBTBCMFW_FLAG_READ_STALL; 352 usb2_transfer_start(xfer_other); 353 } 354 return; 355} 356 357static void 358ubtbcmfw_start_read(struct usb2_fifo *fifo) 359{ 360 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 361 362 usb2_transfer_start(sc->sc_xfer[1]); 363 return; 364} 365 366static void 367ubtbcmfw_stop_read(struct usb2_fifo *fifo) 368{ 369 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 370 371 usb2_transfer_stop(sc->sc_xfer[3]); 372 usb2_transfer_stop(sc->sc_xfer[1]); 373 return; 374} 375 376static void 377ubtbcmfw_start_write(struct usb2_fifo *fifo) 378{ 379 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 380 381 usb2_transfer_start(sc->sc_xfer[0]); 382 return; 383} 384 385static void 386ubtbcmfw_stop_write(struct usb2_fifo *fifo) 387{ 388 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 389 390 usb2_transfer_stop(sc->sc_xfer[2]); 391 usb2_transfer_stop(sc->sc_xfer[0]); 392 return; 393} 394 395static int 396ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) 397{ 398 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 399 400 if (fflags & FREAD) { 401 if (usb2_fifo_alloc_buffer(fifo, 402 sc->sc_xfer[1]->max_data_length, 403 UBTBCMFW_IFQ_MAXLEN)) { 404 return (ENOMEM); 405 } 406 } 407 if (fflags & FWRITE) { 408 /* clear stall first */ 409 mtx_lock(&sc->sc_mtx); 410 sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL; 411 mtx_unlock(&sc->sc_mtx); 412 if (usb2_fifo_alloc_buffer(fifo, 413 sc->sc_xfer[0]->max_data_length, 414 UBTBCMFW_IFQ_MAXLEN)) { 415 return (ENOMEM); 416 } 417 } 418 return (0); 419} 420 421static void 422ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) 423{ 424 if (fflags & (FREAD | FWRITE)) { 425 usb2_fifo_free_buffer(fifo); 426 } 427 return; 428} 429 430static int 431ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, 432 int fflags, struct thread *td) 433{ 434 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 435 int error = 0; 436 437 switch (cmd) { 438 case USB_GET_DEVICE_DESC: 439 *(struct usb2_device_descriptor *)data = 440 *usb2_get_device_descriptor(sc->sc_udev); 441 break; 442 443 default: 444 error = EINVAL; 445 break; 446 } 447 return (error); 448} 449