Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c
35065 views
1
/*
2
* ubtbcmfw.c
3
*/
4
5
/*-
6
* SPDX-License-Identifier: BSD-2-Clause
7
*
8
* Copyright (c) 2003-2009 Maksim Yevmenkin <[email protected]>
9
* All rights reserved.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*
32
* $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
33
*/
34
35
#include <sys/stdint.h>
36
#include <sys/stddef.h>
37
#include <sys/param.h>
38
#include <sys/queue.h>
39
#include <sys/types.h>
40
#include <sys/systm.h>
41
#include <sys/kernel.h>
42
#include <sys/bus.h>
43
#include <sys/module.h>
44
#include <sys/lock.h>
45
#include <sys/mutex.h>
46
#include <sys/condvar.h>
47
#include <sys/sysctl.h>
48
#include <sys/sx.h>
49
#include <sys/unistd.h>
50
#include <sys/callout.h>
51
#include <sys/malloc.h>
52
#include <sys/priv.h>
53
#include <sys/conf.h>
54
#include <sys/fcntl.h>
55
56
#include "usbdevs.h"
57
#include <dev/usb/usb.h>
58
#include <dev/usb/usbdi.h>
59
#include <dev/usb/usb_ioctl.h>
60
61
#define USB_DEBUG_VAR usb_debug
62
#include <dev/usb/usb_debug.h>
63
#include <dev/usb/usb_dev.h>
64
65
/*
66
* Download firmware to BCM2033.
67
*/
68
69
#define UBTBCMFW_CONFIG_NO 1 /* Config number */
70
#define UBTBCMFW_IFACE_IDX 0 /* Control interface */
71
72
#define UBTBCMFW_BSIZE 1024
73
#define UBTBCMFW_IFQ_MAXLEN 2
74
75
enum {
76
UBTBCMFW_BULK_DT_WR = 0,
77
UBTBCMFW_INTR_DT_RD,
78
UBTBCMFW_N_TRANSFER,
79
};
80
81
struct ubtbcmfw_softc {
82
struct usb_device *sc_udev;
83
struct mtx sc_mtx;
84
struct usb_xfer *sc_xfer[UBTBCMFW_N_TRANSFER];
85
struct usb_fifo_sc sc_fifo;
86
};
87
88
/*
89
* Prototypes
90
*/
91
92
static device_probe_t ubtbcmfw_probe;
93
static device_attach_t ubtbcmfw_attach;
94
static device_detach_t ubtbcmfw_detach;
95
96
static usb_callback_t ubtbcmfw_write_callback;
97
static usb_callback_t ubtbcmfw_read_callback;
98
99
static usb_fifo_close_t ubtbcmfw_close;
100
static usb_fifo_cmd_t ubtbcmfw_start_read;
101
static usb_fifo_cmd_t ubtbcmfw_start_write;
102
static usb_fifo_cmd_t ubtbcmfw_stop_read;
103
static usb_fifo_cmd_t ubtbcmfw_stop_write;
104
static usb_fifo_ioctl_t ubtbcmfw_ioctl;
105
static usb_fifo_open_t ubtbcmfw_open;
106
107
static struct usb_fifo_methods ubtbcmfw_fifo_methods =
108
{
109
.f_close = &ubtbcmfw_close,
110
.f_ioctl = &ubtbcmfw_ioctl,
111
.f_open = &ubtbcmfw_open,
112
.f_start_read = &ubtbcmfw_start_read,
113
.f_start_write = &ubtbcmfw_start_write,
114
.f_stop_read = &ubtbcmfw_stop_read,
115
.f_stop_write = &ubtbcmfw_stop_write,
116
.basename[0] = "ubtbcmfw",
117
.basename[1] = "ubtbcmfw",
118
.basename[2] = "ubtbcmfw",
119
.postfix[0] = "",
120
.postfix[1] = ".1",
121
.postfix[2] = ".2",
122
};
123
124
/*
125
* Device's config structure
126
*/
127
128
static const struct usb_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
129
{
130
[UBTBCMFW_BULK_DT_WR] = {
131
.type = UE_BULK,
132
.endpoint = 0x02, /* fixed */
133
.direction = UE_DIR_OUT,
134
.if_index = UBTBCMFW_IFACE_IDX,
135
.bufsize = UBTBCMFW_BSIZE,
136
.flags = { .pipe_bof = 1, .force_short_xfer = 1,
137
.proxy_buffer = 1, },
138
.callback = &ubtbcmfw_write_callback,
139
},
140
141
[UBTBCMFW_INTR_DT_RD] = {
142
.type = UE_INTERRUPT,
143
.endpoint = 0x01, /* fixed */
144
.direction = UE_DIR_IN,
145
.if_index = UBTBCMFW_IFACE_IDX,
146
.bufsize = UBTBCMFW_BSIZE,
147
.flags = { .pipe_bof = 1, .short_xfer_ok = 1,
148
.proxy_buffer = 1, },
149
.callback = &ubtbcmfw_read_callback,
150
},
151
};
152
153
/*
154
* Module
155
*/
156
157
static device_method_t ubtbcmfw_methods[] =
158
{
159
DEVMETHOD(device_probe, ubtbcmfw_probe),
160
DEVMETHOD(device_attach, ubtbcmfw_attach),
161
DEVMETHOD(device_detach, ubtbcmfw_detach),
162
{0, 0}
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
static const STRUCT_USB_HOST_ID ubtbcmfw_devs[] = {
173
/* Broadcom BCM2033 devices only */
174
{ USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
175
};
176
177
DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, NULL, NULL);
178
MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
179
USB_PNP_HOST_INFO(ubtbcmfw_devs);
180
181
/*
182
* Probe for a USB Bluetooth device
183
*/
184
185
static int
186
ubtbcmfw_probe(device_t dev)
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(ubtbcmfw_devs, sizeof(ubtbcmfw_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 */
448
449