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