ubtbcmfw.c revision 187994
1/* 2 * ubtbcmfw.c 3 */ 4 5/*- 6 * Copyright (c) 2003-2009 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 187994 2009-02-02 00:49:39Z 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 58#define UBTBCMFW_BSIZE 1024 59#define UBTBCMFW_IFQ_MAXLEN 2 60 61enum { 62 UBTBCMFW_BULK_DT_WR = 0, 63 UBTBCMFW_INTR_DT_RD, 64 UBTBCMFW_N_TRANSFER, 65}; 66 67struct ubtbcmfw_softc { 68 struct usb2_device *sc_udev; 69 struct mtx sc_mtx; 70 struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER]; 71 struct usb2_fifo_sc sc_fifo; 72}; 73 74/* 75 * Prototypes 76 */ 77 78static device_probe_t ubtbcmfw_probe; 79static device_attach_t ubtbcmfw_attach; 80static device_detach_t ubtbcmfw_detach; 81 82static usb2_callback_t ubtbcmfw_write_callback; 83static usb2_callback_t ubtbcmfw_read_callback; 84 85static usb2_fifo_close_t ubtbcmfw_close; 86static usb2_fifo_cmd_t ubtbcmfw_start_read; 87static usb2_fifo_cmd_t ubtbcmfw_start_write; 88static usb2_fifo_cmd_t ubtbcmfw_stop_read; 89static usb2_fifo_cmd_t ubtbcmfw_stop_write; 90static usb2_fifo_ioctl_t ubtbcmfw_ioctl; 91static usb2_fifo_open_t ubtbcmfw_open; 92 93static struct usb2_fifo_methods ubtbcmfw_fifo_methods = 94{ 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 110/* 111 * Device's config structure 112 */ 113 114static const struct usb2_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] = 115{ 116 [UBTBCMFW_BULK_DT_WR] = { 117 .type = UE_BULK, 118 .endpoint = 0x02, /* fixed */ 119 .direction = UE_DIR_OUT, 120 .if_index = UBTBCMFW_IFACE_IDX, 121 .mh.bufsize = UBTBCMFW_BSIZE, 122 .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, 123 .proxy_buffer = 1, }, 124 .mh.callback = &ubtbcmfw_write_callback, 125 }, 126 127 [UBTBCMFW_INTR_DT_RD] = { 128 .type = UE_INTERRUPT, 129 .endpoint = 0x01, /* fixed */ 130 .direction = UE_DIR_IN, 131 .if_index = UBTBCMFW_IFACE_IDX, 132 .mh.bufsize = UBTBCMFW_BSIZE, 133 .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, 134 .proxy_buffer = 1, }, 135 .mh.callback = &ubtbcmfw_read_callback, 136 }, 137}; 138 139/* 140 * Module 141 */ 142 143static devclass_t ubtbcmfw_devclass; 144 145static device_method_t ubtbcmfw_methods[] = 146{ 147 DEVMETHOD(device_probe, ubtbcmfw_probe), 148 DEVMETHOD(device_attach, ubtbcmfw_attach), 149 DEVMETHOD(device_detach, ubtbcmfw_detach), 150 {0, 0} 151}; 152 153static driver_t ubtbcmfw_driver = 154{ 155 .name = "ubtbcmfw", 156 .methods = ubtbcmfw_methods, 157 .size = sizeof(struct ubtbcmfw_softc), 158}; 159 160DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0); 161MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1); 162MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1); 163 164/* 165 * Probe for a USB Bluetooth device 166 */ 167 168static int 169ubtbcmfw_probe(device_t dev) 170{ 171 const struct usb2_device_id devs[] = { 172 /* Broadcom BCM2033 devices only */ 173 { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) }, 174 }; 175 176 struct usb2_attach_arg *uaa = device_get_ivars(dev); 177 178 if (uaa->usb2_mode != USB_MODE_HOST) 179 return (ENXIO); 180 181 if (uaa->info.bIfaceIndex != 0) 182 return (ENXIO); 183 184 return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa)); 185} /* ubtbcmfw_probe */ 186 187/* 188 * Attach the device 189 */ 190 191static int 192ubtbcmfw_attach(device_t dev) 193{ 194 struct usb2_attach_arg *uaa = device_get_ivars(dev); 195 struct ubtbcmfw_softc *sc = device_get_softc(dev); 196 uint8_t iface_index; 197 int error; 198 199 sc->sc_udev = uaa->device; 200 201 device_set_usb2_desc(dev); 202 203 mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE); 204 205 iface_index = UBTBCMFW_IFACE_IDX; 206 error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, 207 ubtbcmfw_config, UBTBCMFW_N_TRANSFER, 208 sc, &sc->sc_mtx); 209 if (error != 0) { 210 device_printf(dev, "allocating USB transfers failed. %s\n", 211 usb2_errstr(error)); 212 goto detach; 213 } 214 215 /* Set interface permissions */ 216 usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex, 217 UID_ROOT, GID_OPERATOR, 0644); 218 219 error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx, 220 &ubtbcmfw_fifo_methods, &sc->sc_fifo, 221 device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex); 222 if (error != 0) { 223 device_printf(dev, "could not attach fifo. %s\n", 224 usb2_errstr(error)); 225 goto detach; 226 } 227 228 return (0); /* success */ 229 230detach: 231 ubtbcmfw_detach(dev); 232 233 return (ENXIO); /* failure */ 234} /* ubtbcmfw_attach */ 235 236/* 237 * Detach the device 238 */ 239 240static int 241ubtbcmfw_detach(device_t dev) 242{ 243 struct ubtbcmfw_softc *sc = device_get_softc(dev); 244 245 usb2_fifo_detach(&sc->sc_fifo); 246 247 usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER); 248 249 mtx_destroy(&sc->sc_mtx); 250 251 return (0); 252} /* ubtbcmfw_detach */ 253 254/* 255 * USB write callback 256 */ 257 258static void 259ubtbcmfw_write_callback(struct usb2_xfer *xfer) 260{ 261 struct ubtbcmfw_softc *sc = xfer->priv_sc; 262 struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX]; 263 uint32_t actlen; 264 265 switch (USB_GET_STATE(xfer)) { 266 case USB_ST_SETUP: 267 case USB_ST_TRANSFERRED: 268setup_next: 269 if (usb2_fifo_get_data(f, xfer->frbuffers, 0, 270 xfer->max_data_length, &actlen, 0)) { 271 xfer->frlengths[0] = actlen; 272 usb2_start_hardware(xfer); 273 } 274 break; 275 276 default: /* Error */ 277 if (xfer->error != USB_ERR_CANCELLED) { 278 /* try to clear stall first */ 279 xfer->flags.stall_pipe = 1; 280 goto setup_next; 281 } 282 break; 283 } 284} /* ubtbcmfw_write_callback */ 285 286/* 287 * USB read callback 288 */ 289 290static void 291ubtbcmfw_read_callback(struct usb2_xfer *xfer) 292{ 293 struct ubtbcmfw_softc *sc = xfer->priv_sc; 294 struct usb2_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX]; 295 296 switch (USB_GET_STATE(xfer)) { 297 case USB_ST_TRANSFERRED: 298 usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1); 299 /* FALLTHROUGH */ 300 301 case USB_ST_SETUP: 302setup_next: 303 if (usb2_fifo_put_bytes_max(fifo) > 0) { 304 xfer->frlengths[0] = xfer->max_data_length; 305 usb2_start_hardware(xfer); 306 } 307 break; 308 309 default: /* Error */ 310 if (xfer->error != USB_ERR_CANCELLED) { 311 /* try to clear stall first */ 312 xfer->flags.stall_pipe = 1; 313 goto setup_next; 314 } 315 break; 316 } 317} /* ubtbcmfw_read_callback */ 318 319/* 320 * Called when we about to start read()ing from the device 321 */ 322 323static void 324ubtbcmfw_start_read(struct usb2_fifo *fifo) 325{ 326 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 327 328 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); 329} /* ubtbcmfw_start_read */ 330 331/* 332 * Called when we about to stop reading (i.e. closing fifo) 333 */ 334 335static void 336ubtbcmfw_stop_read(struct usb2_fifo *fifo) 337{ 338 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 339 340 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]); 341} /* ubtbcmfw_stop_read */ 342 343/* 344 * Called when we about to start write()ing to the device, poll()ing 345 * for write or flushing fifo 346 */ 347 348static void 349ubtbcmfw_start_write(struct usb2_fifo *fifo) 350{ 351 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 352 353 usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); 354} /* ubtbcmfw_start_write */ 355 356/* 357 * Called when we about to stop writing (i.e. closing fifo) 358 */ 359 360static void 361ubtbcmfw_stop_write(struct usb2_fifo *fifo) 362{ 363 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 364 365 usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]); 366} /* ubtbcmfw_stop_write */ 367 368/* 369 * Called when fifo is open 370 */ 371 372static int 373ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td) 374{ 375 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 376 struct usb2_xfer *xfer; 377 378 /* 379 * f_open fifo method can only be called with either FREAD 380 * or FWRITE flag set at one time. 381 */ 382 383 if (fflags & FREAD) 384 xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD]; 385 else if (fflags & FWRITE) 386 xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR]; 387 else 388 return (EINVAL); /* should not happen */ 389 390 if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length, 391 UBTBCMFW_IFQ_MAXLEN) != 0) 392 return (ENOMEM); 393 394 return (0); 395} /* ubtbcmfw_open */ 396 397/* 398 * Called when fifo is closed 399 */ 400 401static void 402ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td) 403{ 404 if (fflags & (FREAD | FWRITE)) 405 usb2_fifo_free_buffer(fifo); 406} /* ubtbcmfw_close */ 407 408/* 409 * Process ioctl() on USB device 410 */ 411 412static int 413ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data, 414 int fflags, struct thread *td) 415{ 416 struct ubtbcmfw_softc *sc = fifo->priv_sc0; 417 int error = 0; 418 419 switch (cmd) { 420 case USB_GET_DEVICE_DESC: 421 memcpy(data, usb2_get_device_descriptor(sc->sc_udev), 422 sizeof(struct usb2_device_descriptor)); 423 break; 424 425 default: 426 error = EINVAL; 427 break; 428 } 429 430 return (error); 431} /* ubtbcmfw_ioctl */ 432