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