1 /* 2 * ubtbcmfw.c 3 * 4 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $Id: ubtbcmfw.c,v 1.1 2003/04/27 00:20:15 max Exp $ 29 * $FreeBSD$ 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/conf.h> 36 #include <sys/filio.h> 37 #include <sys/fcntl.h> 38 #include <sys/kernel.h> 39 #include <sys/poll.h> 40 #include <sys/proc.h> 41 #include <sys/sysctl.h> 42 #include <sys/vnode.h> 43 44 #include <dev/usb/usb.h> 45 #include <dev/usb/usbdi.h> 46 #include <dev/usb/usbdi_util.h> 47 #include <dev/usb/usbdevs.h> 48 49 /* 50 * Download firmware to BCM2033. 51 */ 52 53 #define UBTBCMFW_CONFIG_NO 1 /* Config number */ 54 #define UBTBCMFW_IFACE_IDX 0 /* Control interface */ 55 #define UBTBCMFW_INTR_IN_EP 0x81 /* Fixed endpoint */ 56 #define UBTBCMFW_BULK_OUT_EP 0x02 /* Fixed endpoint */ 57 #define UBTBCMFW_INTR_IN UE_GET_ADDR(UBTBCMFW_INTR_IN_EP) 58 #define UBTBCMFW_BULK_OUT UE_GET_ADDR(UBTBCMFW_BULK_OUT_EP) 59 60 struct ubtbcmfw_softc { 61 USBBASEDEVICE sc_dev; /* base device */ 62 usbd_device_handle sc_udev; /* USB device handle */ 63 dev_t sc_ctrl_dev; /* control device */ 64 dev_t sc_intr_in_dev; /* interrupt device */ 65 dev_t sc_bulk_out_dev; /* bulk device */ 66 usbd_pipe_handle sc_intr_in_pipe; /* interrupt pipe */ 67 usbd_pipe_handle sc_bulk_out_pipe; /* bulk out pipe */ 68 int sc_flags; 69 #define UBTBCMFW_CTRL_DEV (1 << 0) 70 #define UBTBCMFW_INTR_IN_DEV (1 << 1) 71 #define UBTBCMFW_BULK_OUT_DEV (1 << 2) 72 int sc_refcnt; 73 int sc_dying; 74 }; 75 76 typedef struct ubtbcmfw_softc *ubtbcmfw_softc_p; 77 78 /* 79 * Device methods 80 */ 81 82 #define UBTBCMFW_UNIT(n) ((minor(n) >> 4) & 0xf) 83 #define UBTBCMFW_ENDPOINT(n) (minor(n) & 0xf) 84 #define UBTBCMFW_MINOR(u, e) (((u) << 4) | (e)) 85 #define UBTBCMFW_BSIZE 1024 86 87 Static d_open_t ubtbcmfw_open; 88 Static d_close_t ubtbcmfw_close; 89 Static d_read_t ubtbcmfw_read; 90 Static d_write_t ubtbcmfw_write; 91 Static d_ioctl_t ubtbcmfw_ioctl; 92 Static d_poll_t ubtbcmfw_poll; 93 94 #if __FreeBSD_version < 500104 95 #define CDEV_MAJOR 223 96 #else 97 #define CDEV_MAJOR MAJOR_AUTO 98 #endif 99 100 Static struct cdevsw ubtbcmfw_cdevsw = { 101 .d_open = ubtbcmfw_open, 102 .d_close = ubtbcmfw_close, 103 .d_read = ubtbcmfw_read, 104 .d_write = ubtbcmfw_write, 105 .d_ioctl = ubtbcmfw_ioctl, 106 .d_poll = ubtbcmfw_poll, 107 .d_name = "ubtbcmfw", 108 .d_maj = CDEV_MAJOR, 109 }; 110 111 /* 112 * Module 113 */ 114 115 USB_DECLARE_DRIVER(ubtbcmfw); 116 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, 117 usbd_driver_load, 0); 118 119 /* 120 * Probe for a USB Bluetooth device 121 */ 122 123 USB_MATCH(ubtbcmfw) 124 { 125 #define USB_PRODUCT_BROADCOM_BCM2033NF 0x2033 126 127 USB_MATCH_START(ubtbcmfw, uaa); 128 129 if (uaa->iface != NULL) 130 return (UMATCH_NONE); 131 132 /* Match the boot device. */ 133 if (uaa->vendor == USB_VENDOR_BROADCOM && 134 uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF) 135 return (UMATCH_VENDOR_PRODUCT); 136 137 return (UMATCH_NONE); 138 } 139 140 /* 141 * Attach the device 142 */ 143 144 USB_ATTACH(ubtbcmfw) 145 { 146 USB_ATTACH_START(ubtbcmfw, sc, uaa); 147 usbd_interface_handle iface; 148 usbd_status err; 149 char devinfo[1024]; 150 151 sc->sc_udev = uaa->device; 152 usbd_devinfo(sc->sc_udev, 0, devinfo); 153 USB_ATTACH_SETUP; 154 printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); 155 156 sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NODEV; 157 sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL; 158 sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0; 159 160 err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1); 161 if (err) { 162 printf("%s: setting config no failed. %s\n", 163 USBDEVNAME(sc->sc_dev), usbd_errstr(err)); 164 goto bad; 165 } 166 167 err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX, 168 &iface); 169 if (err) { 170 printf("%s: getting interface handle failed. %s\n", 171 USBDEVNAME(sc->sc_dev), usbd_errstr(err)); 172 goto bad; 173 } 174 175 /* Will be used as a bulk pipe */ 176 err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0, 177 &sc->sc_intr_in_pipe); 178 if (err) { 179 printf("%s: open intr in failed. %s\n", 180 USBDEVNAME(sc->sc_dev), usbd_errstr(err)); 181 goto bad; 182 } 183 184 err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0, 185 &sc->sc_bulk_out_pipe); 186 if (err) { 187 printf("%s: open bulk out failed. %s\n", 188 USBDEVNAME(sc->sc_dev), usbd_errstr(err)); 189 goto bad; 190 } 191 192 /* Create device nodes */ 193 sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw, 194 UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), 0), 195 UID_ROOT, GID_OPERATOR, 0644, 196 "%s", USBDEVNAME(sc->sc_dev)); 197 198 sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw, 199 UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_INTR_IN), 200 UID_ROOT, GID_OPERATOR, 0644, 201 "%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_INTR_IN); 202 203 sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw, 204 UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_BULK_OUT), 205 UID_ROOT, GID_OPERATOR, 0644, 206 "%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_BULK_OUT); 207 208 USB_ATTACH_SUCCESS_RETURN; 209 bad: 210 ubtbcmfw_detach(self); 211 212 USB_ATTACH_ERROR_RETURN; 213 } 214 215 /* 216 * Detach the device 217 */ 218 219 USB_DETACH(ubtbcmfw) 220 { 221 USB_DETACH_START(ubtbcmfw, sc); 222 223 struct vnode *vp = NULL; 224 225 sc->sc_dying = 1; 226 227 if (-- sc->sc_refcnt >= 0) { 228 if (sc->sc_intr_in_pipe != NULL) 229 usbd_abort_pipe(sc->sc_intr_in_pipe); 230 231 if (sc->sc_bulk_out_pipe != NULL) 232 usbd_abort_pipe(sc->sc_bulk_out_pipe); 233 234 usb_detach_wait(USBDEV(sc->sc_dev)); 235 } 236 237 /* Destroy device nodes */ 238 if (sc->sc_bulk_out_dev != NODEV) { 239 vp = SLIST_FIRST(&sc->sc_bulk_out_dev->si_hlist); 240 if (vp != NULL) 241 VOP_REVOKE(vp, REVOKEALL); 242 243 destroy_dev(sc->sc_bulk_out_dev); 244 sc->sc_bulk_out_dev = NODEV; 245 } 246 247 if (sc->sc_intr_in_dev != NODEV) { 248 vp = SLIST_FIRST(&sc->sc_intr_in_dev->si_hlist); 249 if (vp != NULL) 250 VOP_REVOKE(vp, REVOKEALL); 251 252 destroy_dev(sc->sc_intr_in_dev); 253 sc->sc_intr_in_dev = NODEV; 254 } 255 256 if (sc->sc_ctrl_dev != NODEV) { 257 vp = SLIST_FIRST(&sc->sc_ctrl_dev->si_hlist); 258 if (vp != NULL) 259 VOP_REVOKE(vp, REVOKEALL); 260 261 destroy_dev(sc->sc_ctrl_dev); 262 sc->sc_ctrl_dev = NODEV; 263 } 264 265 /* Close pipes */ 266 if (sc->sc_intr_in_pipe != NULL) { 267 usbd_close_pipe(sc->sc_intr_in_pipe); 268 sc->sc_intr_in_pipe = NULL; 269 } 270 271 if (sc->sc_bulk_out_pipe != NULL) { 272 usbd_close_pipe(sc->sc_bulk_out_pipe); 273 sc->sc_intr_in_pipe = NULL; 274 } 275 276 return (0); 277 } 278 279 /* 280 * Open endpoint device 281 * XXX FIXME softc locking 282 */ 283 284 Static int 285 ubtbcmfw_open(dev_t dev, int flag, int mode, usb_proc_ptr p) 286 { 287 ubtbcmfw_softc_p sc = NULL; 288 int error = 0; 289 290 /* checks for sc != NULL */ 291 USB_GET_SC_OPEN(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 292 if (sc->sc_dying) 293 return (ENXIO); 294 295 switch (UBTBCMFW_ENDPOINT(dev)) { 296 case USB_CONTROL_ENDPOINT: 297 if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV)) 298 sc->sc_flags |= UBTBCMFW_CTRL_DEV; 299 else 300 error = EBUSY; 301 break; 302 303 case UBTBCMFW_INTR_IN: 304 if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) { 305 if (sc->sc_intr_in_pipe != NULL) 306 sc->sc_flags |= UBTBCMFW_INTR_IN_DEV; 307 else 308 error = ENXIO; 309 } else 310 error = EBUSY; 311 break; 312 313 case UBTBCMFW_BULK_OUT: 314 if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) { 315 if (sc->sc_bulk_out_pipe != NULL) 316 sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV; 317 else 318 error = ENXIO; 319 } else 320 error = EBUSY; 321 break; 322 323 default: 324 error = ENXIO; 325 break; 326 } 327 328 return (error); 329 } 330 331 /* 332 * Close endpoint device 333 * XXX FIXME softc locking 334 */ 335 336 Static int 337 ubtbcmfw_close(dev_t dev, int flag, int mode, usb_proc_ptr p) 338 { 339 ubtbcmfw_softc_p sc = NULL; 340 341 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 342 if (sc == NULL) 343 return (ENXIO); 344 345 switch (UBTBCMFW_ENDPOINT(dev)) { 346 case USB_CONTROL_ENDPOINT: 347 sc->sc_flags &= ~UBTBCMFW_CTRL_DEV; 348 break; 349 350 case UBTBCMFW_INTR_IN: 351 if (sc->sc_intr_in_pipe != NULL) 352 usbd_abort_pipe(sc->sc_intr_in_pipe); 353 354 sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV; 355 break; 356 357 case UBTBCMFW_BULK_OUT: 358 if (sc->sc_bulk_out_pipe != NULL) 359 usbd_abort_pipe(sc->sc_bulk_out_pipe); 360 361 sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV; 362 break; 363 } 364 365 return (0); 366 } 367 368 /* 369 * Read from the endpoint device 370 * XXX FIXME softc locking 371 */ 372 373 Static int 374 ubtbcmfw_read(dev_t dev, struct uio *uio, int flag) 375 { 376 ubtbcmfw_softc_p sc = NULL; 377 u_int8_t buf[UBTBCMFW_BSIZE]; 378 usbd_xfer_handle xfer; 379 usbd_status err; 380 int n, tn, error = 0; 381 382 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 383 if (sc == NULL || sc->sc_dying) 384 return (ENXIO); 385 386 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN) 387 return (EOPNOTSUPP); 388 if (sc->sc_intr_in_pipe == NULL) 389 return (ENXIO); 390 391 xfer = usbd_alloc_xfer(sc->sc_udev); 392 if (xfer == NULL) 393 return (ENOMEM); 394 395 sc->sc_refcnt ++; 396 397 while ((n = min(sizeof(buf), uio->uio_resid)) != 0) { 398 tn = n; 399 err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe, 400 USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT, 401 buf, &tn, "bcmrd"); 402 switch (err) { 403 case USBD_NORMAL_COMPLETION: 404 error = uiomove(buf, tn, uio); 405 break; 406 407 case USBD_INTERRUPTED: 408 error = EINTR; 409 break; 410 411 case USBD_TIMEOUT: 412 error = ETIMEDOUT; 413 break; 414 415 default: 416 error = EIO; 417 break; 418 } 419 420 if (error != 0 || tn < n) 421 break; 422 } 423 424 usbd_free_xfer(xfer); 425 426 if (-- sc->sc_refcnt < 0) 427 usb_detach_wakeup(USBDEV(sc->sc_dev)); 428 429 return (error); 430 } 431 432 /* 433 * Write into the endpoint device 434 * XXX FIXME softc locking 435 */ 436 437 Static int 438 ubtbcmfw_write(dev_t dev, struct uio *uio, int flag) 439 { 440 ubtbcmfw_softc_p sc = NULL; 441 u_int8_t buf[UBTBCMFW_BSIZE]; 442 usbd_xfer_handle xfer; 443 usbd_status err; 444 int n, error = 0; 445 446 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 447 if (sc == NULL || sc->sc_dying) 448 return (ENXIO); 449 450 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT) 451 return (EOPNOTSUPP); 452 if (sc->sc_bulk_out_pipe == NULL) 453 return (ENXIO); 454 455 xfer = usbd_alloc_xfer(sc->sc_udev); 456 if (xfer == NULL) 457 return (ENOMEM); 458 459 sc->sc_refcnt ++; 460 461 while ((n = min(sizeof(buf), uio->uio_resid)) != 0) { 462 error = uiomove(buf, n, uio); 463 if (error != 0) 464 break; 465 466 err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe, 467 0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr"); 468 switch (err) { 469 case USBD_NORMAL_COMPLETION: 470 break; 471 472 case USBD_INTERRUPTED: 473 error = EINTR; 474 break; 475 476 case USBD_TIMEOUT: 477 error = ETIMEDOUT; 478 break; 479 480 default: 481 error = EIO; 482 break; 483 } 484 485 if (error != 0) 486 break; 487 } 488 489 usbd_free_xfer(xfer); 490 491 if (-- sc->sc_refcnt < 0) 492 usb_detach_wakeup(USBDEV(sc->sc_dev)); 493 494 return (error); 495 } 496 497 /* 498 * Process ioctl on the endpoint device 499 * XXX FIXME softc locking 500 */ 501 502 Static int 503 ubtbcmfw_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) 504 { 505 ubtbcmfw_softc_p sc = NULL; 506 int error = 0; 507 508 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 509 if (sc == NULL || sc->sc_dying) 510 return (ENXIO); 511 512 if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT) 513 return (EOPNOTSUPP); 514 515 sc->sc_refcnt ++; 516 517 switch (cmd) { 518 case USB_GET_DEVICE_DESC: 519 *(usb_device_descriptor_t *) data = 520 *usbd_get_device_descriptor(sc->sc_udev); 521 break; 522 523 default: 524 error = EINVAL; 525 break; 526 } 527 528 if (-- sc->sc_refcnt < 0) 529 usb_detach_wakeup(USBDEV(sc->sc_dev)); 530 531 return (error); 532 } 533 534 /* 535 * Poll the endpoint device 536 * XXX FIXME softc locking 537 */ 538 539 Static int 540 ubtbcmfw_poll(dev_t dev, int events, usb_proc_ptr p) 541 { 542 ubtbcmfw_softc_p sc = NULL; 543 int revents = 0; 544 545 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc); 546 if (sc == NULL) 547 return (ENXIO); 548 549 switch (UBTBCMFW_ENDPOINT(dev)) { 550 case UBTBCMFW_INTR_IN: 551 if (sc->sc_intr_in_pipe != NULL) 552 revents |= events & (POLLIN | POLLRDNORM); 553 else 554 revents = ENXIO; 555 break; 556 557 case UBTBCMFW_BULK_OUT: 558 if (sc->sc_bulk_out_pipe != NULL) 559 revents |= events & (POLLOUT | POLLWRNORM); 560 else 561 revents = ENXIO; 562 break; 563 564 default: 565 revents = EOPNOTSUPP; 566 break; 567 } 568 569 return (revents); 570 } 571 572