AMD64 - Refactor uio_resid and size_t assumptions.
[dragonfly.git] / sys / netgraph7 / bluetooth / drivers / ubtbcmfw / ubtbcmfw.c
CommitLineData
b06ebda0
MD
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: src/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.18 2007/06/23 04:34:38 imp Exp $
5a975a3d 32 * $DragonFly: src/sys/netgraph7/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
b06ebda0
MD
33 */
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38#include <sys/conf.h>
39#include <sys/filio.h>
40#include <sys/fcntl.h>
41#include <sys/kernel.h>
42#include <sys/module.h>
43#include <sys/poll.h>
44#include <sys/proc.h>
45#include <sys/sysctl.h>
46#include <sys/uio.h>
47
48#include <dev/usb/usb.h>
49#include <dev/usb/usbdi.h>
50#include <dev/usb/usbdi_util.h>
51
52#include "usbdevs.h"
53
54/*
55 * Download firmware to BCM2033.
56 */
57
58#define UBTBCMFW_CONFIG_NO 1 /* Config number */
59#define UBTBCMFW_IFACE_IDX 0 /* Control interface */
60#define UBTBCMFW_INTR_IN_EP 0x81 /* Fixed endpoint */
61#define UBTBCMFW_BULK_OUT_EP 0x02 /* Fixed endpoint */
62#define UBTBCMFW_INTR_IN UE_GET_ADDR(UBTBCMFW_INTR_IN_EP)
63#define UBTBCMFW_BULK_OUT UE_GET_ADDR(UBTBCMFW_BULK_OUT_EP)
64
65struct ubtbcmfw_softc {
66 device_t sc_dev; /* base device */
67 usbd_device_handle sc_udev; /* USB device handle */
68 struct cdev *sc_ctrl_dev; /* control device */
69 struct cdev *sc_intr_in_dev; /* interrupt device */
70 struct cdev *sc_bulk_out_dev; /* bulk device */
71 usbd_pipe_handle sc_intr_in_pipe; /* interrupt pipe */
72 usbd_pipe_handle sc_bulk_out_pipe; /* bulk out pipe */
73 int sc_flags;
74#define UBTBCMFW_CTRL_DEV (1 << 0)
75#define UBTBCMFW_INTR_IN_DEV (1 << 1)
76#define UBTBCMFW_BULK_OUT_DEV (1 << 2)
77 int sc_refcnt;
78 int sc_dying;
79};
80
81typedef struct ubtbcmfw_softc *ubtbcmfw_softc_p;
82
83/*
84 * Device methods
85 */
86
87#define UBTBCMFW_UNIT(n) ((minor(n) >> 4) & 0xf)
88#define UBTBCMFW_ENDPOINT(n) (minor(n) & 0xf)
89#define UBTBCMFW_MINOR(u, e) (((u) << 4) | (e))
90#define UBTBCMFW_BSIZE 1024
91
92static d_open_t ubtbcmfw_open;
93static d_close_t ubtbcmfw_close;
94static d_read_t ubtbcmfw_read;
95static d_write_t ubtbcmfw_write;
96static d_ioctl_t ubtbcmfw_ioctl;
97static d_poll_t ubtbcmfw_poll;
98
99static struct cdevsw ubtbcmfw_cdevsw = {
100 .d_version = D_VERSION,
101 .d_flags = D_NEEDGIANT,
102 .d_open = ubtbcmfw_open,
103 .d_close = ubtbcmfw_close,
104 .d_read = ubtbcmfw_read,
105 .d_write = ubtbcmfw_write,
106 .d_ioctl = ubtbcmfw_ioctl,
107 .d_poll = ubtbcmfw_poll,
108 .d_name = "ubtbcmfw",
109};
110
111/*
112 * Module
113 */
114
115static device_probe_t ubtbcmfw_match;
116static device_attach_t ubtbcmfw_attach;
117static device_detach_t ubtbcmfw_detach;
118
119static device_method_t ubtbcmfw_methods[] = {
120 /* Device interface */
121 DEVMETHOD(device_probe, ubtbcmfw_match),
122 DEVMETHOD(device_attach, ubtbcmfw_attach),
123 DEVMETHOD(device_detach, ubtbcmfw_detach),
124
125 { 0, 0 }
126};
127
128static driver_t ubtbcmfw_driver = {
129 "ubtbcmfw",
130 ubtbcmfw_methods,
131 sizeof(struct ubtbcmfw_softc)
132};
133
134static devclass_t ubtbcmfw_devclass;
135
136MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
137DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
138 usbd_driver_load, 0);
139
140/*
141 * Probe for a USB Bluetooth device
142 */
143
144static int
145ubtbcmfw_match(device_t self)
146{
147#define USB_PRODUCT_BROADCOM_BCM2033NF 0x2033
148 struct usb_attach_arg *uaa = device_get_ivars(self);
149
150 if (uaa->iface != NULL)
151 return (UMATCH_NONE);
152
153 /* Match the boot device. */
154 if (uaa->vendor == USB_VENDOR_BROADCOM &&
155 uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
156 return (UMATCH_VENDOR_PRODUCT);
157
158 return (UMATCH_NONE);
159}
160
161/*
162 * Attach the device
163 */
164
165static int
166ubtbcmfw_attach(device_t self)
167{
168 struct ubtbcmfw_softc *sc = device_get_softc(self);
169 struct usb_attach_arg *uaa = device_get_ivars(self);
170 usbd_interface_handle iface;
171 usbd_status err;
172
173 sc->sc_dev = self;
174 sc->sc_udev = uaa->device;
175
176 sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NULL;
177 sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL;
178 sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0;
179
180 err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
181 if (err) {
182 printf("%s: setting config no failed. %s\n",
183 device_get_nameunit(sc->sc_dev), usbd_errstr(err));
184 goto bad;
185 }
186
187 err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
188 &iface);
189 if (err) {
190 printf("%s: getting interface handle failed. %s\n",
191 device_get_nameunit(sc->sc_dev), usbd_errstr(err));
192 goto bad;
193 }
194
195 /* Will be used as a bulk pipe */
196 err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0,
197 &sc->sc_intr_in_pipe);
198 if (err) {
199 printf("%s: open intr in failed. %s\n",
200 device_get_nameunit(sc->sc_dev), usbd_errstr(err));
201 goto bad;
202 }
203
204 err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
205 &sc->sc_bulk_out_pipe);
206 if (err) {
207 printf("%s: open bulk out failed. %s\n",
208 device_get_nameunit(sc->sc_dev), usbd_errstr(err));
209 goto bad;
210 }
211
212 /* Create device nodes */
213 sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw,
214 UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), 0),
215 UID_ROOT, GID_OPERATOR, 0644,
216 "%s", device_get_nameunit(sc->sc_dev));
217
218 sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw,
219 UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_INTR_IN),
220 UID_ROOT, GID_OPERATOR, 0644,
221 "%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_INTR_IN);
222
223 sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw,
224 UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_BULK_OUT),
225 UID_ROOT, GID_OPERATOR, 0644,
226 "%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_BULK_OUT);
227
228 return 0;
229bad:
230 ubtbcmfw_detach(self);
231 return ENXIO;
232}
233
234/*
235 * Detach the device
236 */
237
238static int
239ubtbcmfw_detach(device_t self)
240{
241 struct ubtbcmfw_softc *sc = device_get_softc(self);
242
243 sc->sc_dying = 1;
244 if (-- sc->sc_refcnt >= 0) {
245 if (sc->sc_intr_in_pipe != NULL)
246 usbd_abort_pipe(sc->sc_intr_in_pipe);
247
248 if (sc->sc_bulk_out_pipe != NULL)
249 usbd_abort_pipe(sc->sc_bulk_out_pipe);
250
251 usb_detach_wait(sc->sc_dev);
252 }
253
254 /* Destroy device nodes */
255 if (sc->sc_bulk_out_dev != NULL) {
256 destroy_dev(sc->sc_bulk_out_dev);
257 sc->sc_bulk_out_dev = NULL;
258 }
259
260 if (sc->sc_intr_in_dev != NULL) {
261 destroy_dev(sc->sc_intr_in_dev);
262 sc->sc_intr_in_dev = NULL;
263 }
264
265 if (sc->sc_ctrl_dev != NULL) {
266 destroy_dev(sc->sc_ctrl_dev);
267 sc->sc_ctrl_dev = NULL;
268 }
269
270 /* Close pipes */
271 if (sc->sc_intr_in_pipe != NULL) {
272 usbd_close_pipe(sc->sc_intr_in_pipe);
273 sc->sc_intr_in_pipe = NULL;
274 }
275
276 if (sc->sc_bulk_out_pipe != NULL) {
277 usbd_close_pipe(sc->sc_bulk_out_pipe);
278 sc->sc_intr_in_pipe = NULL;
279 }
280
281 return (0);
282}
283
284/*
285 * Open endpoint device
286 * XXX FIXME softc locking
287 */
288
289static int
290ubtbcmfw_open(struct cdev *dev, int flag, int mode, struct thread *p)
291{
292 ubtbcmfw_softc_p sc = NULL;
293 int error = 0;
294
295 /* checks for sc != NULL */
296 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
297 if (sc == NULL)
298 return (ENXIO);
299 if (sc->sc_dying)
300 return (ENXIO);
301
302 switch (UBTBCMFW_ENDPOINT(dev)) {
303 case USB_CONTROL_ENDPOINT:
304 if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV))
305 sc->sc_flags |= UBTBCMFW_CTRL_DEV;
306 else
307 error = EBUSY;
308 break;
309
310 case UBTBCMFW_INTR_IN:
311 if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) {
312 if (sc->sc_intr_in_pipe != NULL)
313 sc->sc_flags |= UBTBCMFW_INTR_IN_DEV;
314 else
315 error = ENXIO;
316 } else
317 error = EBUSY;
318 break;
319
320 case UBTBCMFW_BULK_OUT:
321 if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) {
322 if (sc->sc_bulk_out_pipe != NULL)
323 sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV;
324 else
325 error = ENXIO;
326 } else
327 error = EBUSY;
328 break;
329
330 default:
331 error = ENXIO;
332 break;
333 }
334
335 return (error);
336}
337
338/*
339 * Close endpoint device
340 * XXX FIXME softc locking
341 */
342
343static int
344ubtbcmfw_close(struct cdev *dev, int flag, int mode, struct thread *p)
345{
346 ubtbcmfw_softc_p sc = NULL;
347
348 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
349 if (sc == NULL)
350 return (ENXIO);
351
352 switch (UBTBCMFW_ENDPOINT(dev)) {
353 case USB_CONTROL_ENDPOINT:
354 sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
355 break;
356
357 case UBTBCMFW_INTR_IN:
358 if (sc->sc_intr_in_pipe != NULL)
359 usbd_abort_pipe(sc->sc_intr_in_pipe);
360
361 sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
362 break;
363
364 case UBTBCMFW_BULK_OUT:
365 if (sc->sc_bulk_out_pipe != NULL)
366 usbd_abort_pipe(sc->sc_bulk_out_pipe);
367
368 sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
369 break;
370 }
371
372 return (0);
373}
374
375/*
376 * Read from the endpoint device
377 * XXX FIXME softc locking
378 */
379
380static int
381ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
382{
383 ubtbcmfw_softc_p sc = NULL;
384 u_int8_t buf[UBTBCMFW_BSIZE];
385 usbd_xfer_handle xfer;
386 usbd_status err;
387 int n, tn, error = 0;
388
389 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
390 if (sc == NULL || sc->sc_dying)
391 return (ENXIO);
392
393 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
394 return (EOPNOTSUPP);
395 if (sc->sc_intr_in_pipe == NULL)
396 return (ENXIO);
397
398 xfer = usbd_alloc_xfer(sc->sc_udev);
399 if (xfer == NULL)
400 return (ENOMEM);
401
402 sc->sc_refcnt ++;
403
e54488bb 404 while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
b06ebda0
MD
405 tn = n;
406 err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
407 USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
408 buf, &tn, "bcmrd");
409 switch (err) {
410 case USBD_NORMAL_COMPLETION:
e54488bb 411 error = uiomove(buf, (size_t)tn, uio);
b06ebda0
MD
412 break;
413
414 case USBD_INTERRUPTED:
415 error = EINTR;
416 break;
417
418 case USBD_TIMEOUT:
419 error = ETIMEDOUT;
420 break;
421
422 default:
423 error = EIO;
424 break;
425 }
426
427 if (error != 0 || tn < n)
428 break;
429 }
430
431 usbd_free_xfer(xfer);
432
433 if (-- sc->sc_refcnt < 0)
434 usb_detach_wakeup(sc->sc_dev);
435
436 return (error);
437}
438
439/*
440 * Write into the endpoint device
441 * XXX FIXME softc locking
442 */
443
444static int
445ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
446{
447 ubtbcmfw_softc_p sc = NULL;
448 u_int8_t buf[UBTBCMFW_BSIZE];
449 usbd_xfer_handle xfer;
450 usbd_status err;
451 int n, error = 0;
452
453 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
454 if (sc == NULL || sc->sc_dying)
455 return (ENXIO);
456
457 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
458 return (EOPNOTSUPP);
459 if (sc->sc_bulk_out_pipe == NULL)
460 return (ENXIO);
461
462 xfer = usbd_alloc_xfer(sc->sc_udev);
463 if (xfer == NULL)
464 return (ENOMEM);
465
466 sc->sc_refcnt ++;
467
e54488bb
MD
468 while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
469 error = uiomove(buf, (size_t)n, uio);
b06ebda0
MD
470 if (error != 0)
471 break;
472
473 err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
474 0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
475 switch (err) {
476 case USBD_NORMAL_COMPLETION:
477 break;
478
479 case USBD_INTERRUPTED:
480 error = EINTR;
481 break;
482
483 case USBD_TIMEOUT:
484 error = ETIMEDOUT;
485 break;
486
487 default:
488 error = EIO;
489 break;
490 }
491
492 if (error != 0)
493 break;
494 }
495
496 usbd_free_xfer(xfer);
497
498 if (-- sc->sc_refcnt < 0)
499 usb_detach_wakeup(sc->sc_dev);
500
501 return (error);
502}
503
504/*
505 * Process ioctl on the endpoint device
506 * XXX FIXME softc locking
507 */
508
509static int
510ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
511 struct thread *p)
512{
513 ubtbcmfw_softc_p sc = NULL;
514 int error = 0;
515
516 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
517 if (sc == NULL || sc->sc_dying)
518 return (ENXIO);
519
520 if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
521 return (EOPNOTSUPP);
522
523 sc->sc_refcnt ++;
524
525 switch (cmd) {
526 case USB_GET_DEVICE_DESC:
527 *(usb_device_descriptor_t *) data =
528 *usbd_get_device_descriptor(sc->sc_udev);
529 break;
530
531 default:
532 error = EINVAL;
533 break;
534 }
535
536 if (-- sc->sc_refcnt < 0)
537 usb_detach_wakeup(sc->sc_dev);
538
539 return (error);
540}
541
542/*
543 * Poll the endpoint device
544 * XXX FIXME softc locking
545 */
546
547static int
548ubtbcmfw_poll(struct cdev *dev, int events, struct thread *p)
549{
550 ubtbcmfw_softc_p sc = NULL;
551 int revents = 0;
552
553 sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
554 if (sc == NULL)
555 return (ENXIO);
556
557 switch (UBTBCMFW_ENDPOINT(dev)) {
558 case UBTBCMFW_INTR_IN:
559 if (sc->sc_intr_in_pipe != NULL)
560 revents |= events & (POLLIN | POLLRDNORM);
561 else
562 revents = ENXIO;
563 break;
564
565 case UBTBCMFW_BULK_OUT:
566 if (sc->sc_bulk_out_pipe != NULL)
567 revents |= events & (POLLOUT | POLLWRNORM);
568 else
569 revents = ENXIO;
570 break;
571
572 default:
573 revents = EOPNOTSUPP;
574 break;
575 }
576
577 return (revents);
578}