]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / netgraph / bluetooth / drivers / ubtbcmfw / ubtbcmfw.c
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$
32  */
33
34 #include <sys/stdint.h>
35 #include <sys/stddef.h>
36 #include <sys/param.h>
37 #include <sys/queue.h>
38 #include <sys/types.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/bus.h>
42 #include <sys/module.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <sys/condvar.h>
46 #include <sys/sysctl.h>
47 #include <sys/sx.h>
48 #include <sys/unistd.h>
49 #include <sys/callout.h>
50 #include <sys/malloc.h>
51 #include <sys/priv.h>
52 #include <sys/conf.h>
53 #include <sys/fcntl.h>
54
55 #include "usbdevs.h"
56 #include <dev/usb/usb.h>
57 #include <dev/usb/usbdi.h>
58 #include <dev/usb/usb_ioctl.h>
59
60 #define USB_DEBUG_VAR usb_debug
61 #include <dev/usb/usb_debug.h>
62 #include <dev/usb/usb_dev.h>
63
64 /*
65  * Download firmware to BCM2033.
66  */
67
68 #define UBTBCMFW_CONFIG_NO      1       /* Config number */
69 #define UBTBCMFW_IFACE_IDX      0       /* Control interface */
70
71 #define UBTBCMFW_BSIZE          1024
72 #define UBTBCMFW_IFQ_MAXLEN     2
73
74 enum {
75         UBTBCMFW_BULK_DT_WR = 0,
76         UBTBCMFW_INTR_DT_RD,
77         UBTBCMFW_N_TRANSFER,
78 };
79
80 struct ubtbcmfw_softc {
81         struct usb_device       *sc_udev;
82         struct mtx              sc_mtx;
83         struct usb_xfer *sc_xfer[UBTBCMFW_N_TRANSFER];
84         struct usb_fifo_sc      sc_fifo;
85 };
86
87 /*
88  * Prototypes
89  */
90
91 static device_probe_t           ubtbcmfw_probe;
92 static device_attach_t          ubtbcmfw_attach;
93 static device_detach_t          ubtbcmfw_detach;
94
95 static usb_callback_t           ubtbcmfw_write_callback;
96 static usb_callback_t           ubtbcmfw_read_callback;
97
98 static usb_fifo_close_t ubtbcmfw_close;
99 static usb_fifo_cmd_t           ubtbcmfw_start_read;
100 static usb_fifo_cmd_t           ubtbcmfw_start_write;
101 static usb_fifo_cmd_t           ubtbcmfw_stop_read;
102 static usb_fifo_cmd_t           ubtbcmfw_stop_write;
103 static usb_fifo_ioctl_t ubtbcmfw_ioctl;
104 static usb_fifo_open_t          ubtbcmfw_open;
105
106 static struct usb_fifo_methods  ubtbcmfw_fifo_methods = 
107 {
108         .f_close =              &ubtbcmfw_close,
109         .f_ioctl =              &ubtbcmfw_ioctl,
110         .f_open =               &ubtbcmfw_open,
111         .f_start_read =         &ubtbcmfw_start_read,
112         .f_start_write =        &ubtbcmfw_start_write,
113         .f_stop_read =          &ubtbcmfw_stop_read,
114         .f_stop_write =         &ubtbcmfw_stop_write,
115         .basename[0] =          "ubtbcmfw",
116         .basename[1] =          "ubtbcmfw",
117         .basename[2] =          "ubtbcmfw",
118         .postfix[0] =           "",
119         .postfix[1] =           ".1",
120         .postfix[2] =           ".2",
121 };
122
123 /*
124  * Device's config structure
125  */
126
127 static const struct usb_config  ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
128 {
129         [UBTBCMFW_BULK_DT_WR] = {
130                 .type =         UE_BULK,
131                 .endpoint =     0x02,   /* fixed */
132                 .direction =    UE_DIR_OUT,
133                 .if_index =     UBTBCMFW_IFACE_IDX,
134                 .bufsize =      UBTBCMFW_BSIZE,
135                 .flags =        { .pipe_bof = 1, .force_short_xfer = 1,
136                                   .proxy_buffer = 1, },
137                 .callback =     &ubtbcmfw_write_callback,
138         },
139
140         [UBTBCMFW_INTR_DT_RD] = {
141                 .type =         UE_INTERRUPT,
142                 .endpoint =     0x01,   /* fixed */
143                 .direction =    UE_DIR_IN,
144                 .if_index =     UBTBCMFW_IFACE_IDX,
145                 .bufsize =      UBTBCMFW_BSIZE,
146                 .flags =        { .pipe_bof = 1, .short_xfer_ok = 1,
147                                   .proxy_buffer = 1, },
148                 .callback =     &ubtbcmfw_read_callback,
149         },
150 };
151
152 /*
153  * Module
154  */
155
156 static devclass_t       ubtbcmfw_devclass;
157
158 static device_method_t  ubtbcmfw_methods[] =
159 {
160         DEVMETHOD(device_probe, ubtbcmfw_probe),
161         DEVMETHOD(device_attach, ubtbcmfw_attach),
162         DEVMETHOD(device_detach, ubtbcmfw_detach),
163         {0, 0}
164 };
165
166 static driver_t         ubtbcmfw_driver =
167 {
168         .name =         "ubtbcmfw",
169         .methods =      ubtbcmfw_methods,
170         .size =         sizeof(struct ubtbcmfw_softc),
171 };
172
173 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
174 MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
175
176 /*
177  * Probe for a USB Bluetooth device
178  */
179
180 static int
181 ubtbcmfw_probe(device_t dev)
182 {
183         static const STRUCT_USB_HOST_ID devs[] = {
184         /* Broadcom BCM2033 devices only */
185         { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
186         };
187
188         struct usb_attach_arg   *uaa = device_get_ivars(dev);
189
190         if (uaa->usb_mode != USB_MODE_HOST)
191                 return (ENXIO);
192
193         if (uaa->info.bIfaceIndex != 0)
194                 return (ENXIO);
195
196         return (usbd_lookup_id_by_uaa(devs, sizeof(devs), uaa));
197 } /* ubtbcmfw_probe */
198
199 /*
200  * Attach the device
201  */
202
203 static int
204 ubtbcmfw_attach(device_t dev)
205 {
206         struct usb_attach_arg   *uaa = device_get_ivars(dev);
207         struct ubtbcmfw_softc   *sc = device_get_softc(dev);
208         uint8_t                 iface_index;
209         int                     error;
210
211         sc->sc_udev = uaa->device;
212
213         device_set_usb_desc(dev);
214
215         mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
216
217         iface_index = UBTBCMFW_IFACE_IDX;
218         error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
219                                 ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
220                                 sc, &sc->sc_mtx);
221         if (error != 0) {
222                 device_printf(dev, "allocating USB transfers failed. %s\n",
223                         usbd_errstr(error));
224                 goto detach;
225         }
226
227         error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
228                         &ubtbcmfw_fifo_methods, &sc->sc_fifo,
229                         device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
230                         UID_ROOT, GID_OPERATOR, 0644);
231         if (error != 0) {
232                 device_printf(dev, "could not attach fifo. %s\n",
233                         usbd_errstr(error));
234                 goto detach;
235         }
236
237         return (0);     /* success */
238
239 detach:
240         ubtbcmfw_detach(dev);
241
242         return (ENXIO); /* failure */
243 } /* ubtbcmfw_attach */ 
244
245 /*
246  * Detach the device
247  */
248
249 static int
250 ubtbcmfw_detach(device_t dev)
251 {
252         struct ubtbcmfw_softc   *sc = device_get_softc(dev);
253
254         usb_fifo_detach(&sc->sc_fifo);
255
256         usbd_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
257
258         mtx_destroy(&sc->sc_mtx);
259
260         return (0);
261 } /* ubtbcmfw_detach */
262
263 /*
264  * USB write callback
265  */
266
267 static void
268 ubtbcmfw_write_callback(struct usb_xfer *xfer, usb_error_t error)
269 {
270         struct ubtbcmfw_softc   *sc = usbd_xfer_softc(xfer);
271         struct usb_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX];
272         struct usb_page_cache   *pc;
273         uint32_t                actlen;
274
275         switch (USB_GET_STATE(xfer)) {
276         case USB_ST_SETUP:
277         case USB_ST_TRANSFERRED:
278 setup_next:
279                 pc = usbd_xfer_get_frame(xfer, 0);
280                 if (usb_fifo_get_data(f, pc, 0, usbd_xfer_max_len(xfer),
281                             &actlen, 0)) {
282                         usbd_xfer_set_frame_len(xfer, 0, actlen);
283                         usbd_transfer_submit(xfer);
284                 }
285                 break;
286
287         default: /* Error */
288                 if (error != USB_ERR_CANCELLED) {
289                         /* try to clear stall first */
290                         usbd_xfer_set_stall(xfer);
291                         goto setup_next;
292                 }
293                 break;
294         }
295 } /* ubtbcmfw_write_callback */
296
297 /*
298  * USB read callback
299  */
300
301 static void
302 ubtbcmfw_read_callback(struct usb_xfer *xfer, usb_error_t error)
303 {
304         struct ubtbcmfw_softc   *sc = usbd_xfer_softc(xfer);
305         struct usb_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX];
306         struct usb_page_cache   *pc;
307         int                     actlen;
308
309         usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
310
311         switch (USB_GET_STATE(xfer)) {
312         case USB_ST_TRANSFERRED:
313                 pc = usbd_xfer_get_frame(xfer, 0);
314                 usb_fifo_put_data(fifo, pc, 0, actlen, 1);
315                 /* FALLTHROUGH */
316
317         case USB_ST_SETUP:
318 setup_next:
319                 if (usb_fifo_put_bytes_max(fifo) > 0) {
320                         usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
321                         usbd_transfer_submit(xfer);
322                 }
323                 break;
324
325         default: /* Error */
326                 if (error != USB_ERR_CANCELLED) {
327                         /* try to clear stall first */
328                         usbd_xfer_set_stall(xfer);
329                         goto setup_next;
330                 }
331                 break;
332         }
333 } /* ubtbcmfw_read_callback */
334
335 /*
336  * Called when we about to start read()ing from the device
337  */
338
339 static void
340 ubtbcmfw_start_read(struct usb_fifo *fifo)
341 {
342         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
343
344         usbd_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
345 } /* ubtbcmfw_start_read */
346
347 /*
348  * Called when we about to stop reading (i.e. closing fifo)
349  */
350
351 static void
352 ubtbcmfw_stop_read(struct usb_fifo *fifo)
353 {
354         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
355
356         usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
357 } /* ubtbcmfw_stop_read */
358
359 /*
360  * Called when we about to start write()ing to the device, poll()ing
361  * for write or flushing fifo
362  */
363
364 static void
365 ubtbcmfw_start_write(struct usb_fifo *fifo)
366 {
367         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
368
369         usbd_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
370 } /* ubtbcmfw_start_write */
371
372 /*
373  * Called when we about to stop writing (i.e. closing fifo)
374  */
375
376 static void
377 ubtbcmfw_stop_write(struct usb_fifo *fifo)
378 {
379         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
380
381         usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
382 } /* ubtbcmfw_stop_write */
383
384 /*
385  * Called when fifo is open
386  */
387
388 static int
389 ubtbcmfw_open(struct usb_fifo *fifo, int fflags)
390 {
391         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
392         struct usb_xfer *xfer;
393
394         /*
395          * f_open fifo method can only be called with either FREAD
396          * or FWRITE flag set at one time.
397          */
398
399         if (fflags & FREAD)
400                 xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
401         else if (fflags & FWRITE)
402                 xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
403         else
404                 return (EINVAL);        /* should not happen */
405
406         if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(xfer),
407                         UBTBCMFW_IFQ_MAXLEN) != 0)
408                 return (ENOMEM);
409
410         return (0);
411 } /* ubtbcmfw_open */
412
413 /* 
414  * Called when fifo is closed
415  */
416
417 static void
418 ubtbcmfw_close(struct usb_fifo *fifo, int fflags)
419 {
420         if (fflags & (FREAD | FWRITE))
421                 usb_fifo_free_buffer(fifo);
422 } /* ubtbcmfw_close */
423
424 /*
425  * Process ioctl() on USB device
426  */
427
428 static int
429 ubtbcmfw_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
430     int fflags)
431 {
432         struct ubtbcmfw_softc   *sc = usb_fifo_softc(fifo);
433         int                     error = 0;
434
435         switch (cmd) {
436         case USB_GET_DEVICE_DESC:
437                 memcpy(data, usbd_get_device_descriptor(sc->sc_udev),
438                         sizeof(struct usb_device_descriptor));
439                 break;
440
441         default:
442                 error = EINVAL;
443                 break;
444         }
445
446         return (error);
447 } /* ubtbcmfw_ioctl */