xref: /dflybsd-src/sys/netgraph7/bluetooth/drivers/bt3c/ng_bt3c_pccard.c (revision 805c8e8e4093ceca2e27510ad3a66d4de8060a55)
1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon  * ng_bt3c_pccard.c
3b06ebda0SMatthew Dillon  */
4b06ebda0SMatthew Dillon 
5b06ebda0SMatthew Dillon /*-
6b06ebda0SMatthew Dillon  * Copyright (c) 2001-2002 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: ng_bt3c_pccard.c,v 1.5 2003/04/01 18:15:21 max Exp $
31b50b93ffSSascha Wildner  * $FreeBSD: head/sys/netgraph/bluetooth/drivers/bt3c/ng_bt3c_pccard.c 243882 2012-12-05 08:04:20Z glebius $
32b06ebda0SMatthew Dillon  *
33b06ebda0SMatthew Dillon  * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
34b06ebda0SMatthew Dillon  *
35b06ebda0SMatthew Dillon  * Based on information obrained from: Jose Orlando Pereira <jop@di.uminho.pt>
36b06ebda0SMatthew Dillon  * and disassembled w2k driver.
37b06ebda0SMatthew Dillon  *
38b06ebda0SMatthew Dillon  * XXX XXX XX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
39b06ebda0SMatthew Dillon  *
40b06ebda0SMatthew Dillon  */
41b06ebda0SMatthew Dillon 
42b06ebda0SMatthew Dillon #include <sys/param.h>
43b06ebda0SMatthew Dillon #include <sys/systm.h>
44b06ebda0SMatthew Dillon 
45b06ebda0SMatthew Dillon #include <sys/bus.h>
46b06ebda0SMatthew Dillon 
47b06ebda0SMatthew Dillon #include <sys/conf.h>
48b06ebda0SMatthew Dillon #include <sys/endian.h>
49b06ebda0SMatthew Dillon #include <sys/interrupt.h>
50b06ebda0SMatthew Dillon #include <sys/kernel.h>
51*805c8e8eSzrj #include <sys/malloc.h>
52b06ebda0SMatthew Dillon #include <sys/mbuf.h>
53b06ebda0SMatthew Dillon #include <sys/module.h>
54b06ebda0SMatthew Dillon 
55b06ebda0SMatthew Dillon #include <sys/rman.h>
56b06ebda0SMatthew Dillon 
57b06ebda0SMatthew Dillon #include <sys/socket.h>
58b06ebda0SMatthew Dillon #include <net/if.h>
59b06ebda0SMatthew Dillon #include <net/if_var.h>
60b06ebda0SMatthew Dillon 
61b50b93ffSSascha Wildner #include <bus/pccard/pccardreg.h>
62b50b93ffSSascha Wildner #include <bus/pccard/pccardvar.h>
63b06ebda0SMatthew Dillon #include "pccarddevs.h"
64b06ebda0SMatthew Dillon 
65b50b93ffSSascha Wildner #include <netgraph7/ng_message.h>
66b50b93ffSSascha Wildner #include <netgraph7/netgraph.h>
67b50b93ffSSascha Wildner #include <netgraph7/ng_parse.h>
68b50b93ffSSascha Wildner #include <netgraph7/bluetooth/include/ng_bluetooth.h>
69b50b93ffSSascha Wildner #include <netgraph7/bluetooth/include/ng_hci.h>
70b50b93ffSSascha Wildner #include <netgraph7/bluetooth/include/ng_bt3c.h>
71b50b93ffSSascha Wildner #include <netgraph7/bluetooth/drivers/bt3c/ng_bt3c_var.h>
72b06ebda0SMatthew Dillon 
73b06ebda0SMatthew Dillon /* Netgraph methods */
74b06ebda0SMatthew Dillon static ng_constructor_t	ng_bt3c_constructor;
75b06ebda0SMatthew Dillon static ng_shutdown_t	ng_bt3c_shutdown;
76b06ebda0SMatthew Dillon static ng_newhook_t	ng_bt3c_newhook;
77b06ebda0SMatthew Dillon static ng_connect_t	ng_bt3c_connect;
78b06ebda0SMatthew Dillon static ng_disconnect_t	ng_bt3c_disconnect;
79b06ebda0SMatthew Dillon static ng_rcvmsg_t	ng_bt3c_rcvmsg;
80b06ebda0SMatthew Dillon static ng_rcvdata_t	ng_bt3c_rcvdata;
81b06ebda0SMatthew Dillon 
82b06ebda0SMatthew Dillon /* PCMCIA driver methods */
83b06ebda0SMatthew Dillon static int	bt3c_pccard_probe	(device_t);
84b06ebda0SMatthew Dillon static int	bt3c_pccard_attach	(device_t);
85b06ebda0SMatthew Dillon static int	bt3c_pccard_detach	(device_t);
86b06ebda0SMatthew Dillon 
87b06ebda0SMatthew Dillon static void	bt3c_intr		(void *);
88b06ebda0SMatthew Dillon static void	bt3c_receive		(bt3c_softc_p);
89b06ebda0SMatthew Dillon 
90b50b93ffSSascha Wildner static void	bt3c_swi_intr		(void *, void *);
91b06ebda0SMatthew Dillon static void	bt3c_forward		(node_p, hook_p, void *, int);
92b06ebda0SMatthew Dillon static void	bt3c_send		(node_p, hook_p, void *, int);
93b06ebda0SMatthew Dillon 
94b06ebda0SMatthew Dillon static void	bt3c_download_firmware	(bt3c_softc_p, char const *, int);
95b06ebda0SMatthew Dillon 
96b06ebda0SMatthew Dillon #define	bt3c_set_address(sc, address) \
97b06ebda0SMatthew Dillon do { \
98b06ebda0SMatthew Dillon 	bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_ADDR_L, ((address) & 0xff)); \
99b06ebda0SMatthew Dillon 	bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_ADDR_H, (((address) >> 8) & 0xff)); \
100b06ebda0SMatthew Dillon } while (0)
101b06ebda0SMatthew Dillon 
102b06ebda0SMatthew Dillon #define	bt3c_read_data(sc, data) \
103b06ebda0SMatthew Dillon do { \
104b06ebda0SMatthew Dillon 	(data)  = bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_DATA_L); \
105b06ebda0SMatthew Dillon 	(data) |= ((bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_DATA_H) & 0xff) << 8); \
106b06ebda0SMatthew Dillon } while (0)
107b06ebda0SMatthew Dillon 
108b06ebda0SMatthew Dillon #define	bt3c_write_data(sc, data) \
109b06ebda0SMatthew Dillon do { \
110b06ebda0SMatthew Dillon 	bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_DATA_L, ((data) & 0xff)); \
111b06ebda0SMatthew Dillon 	bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_DATA_H, (((data) >> 8) & 0xff)); \
112b06ebda0SMatthew Dillon } while (0)
113b06ebda0SMatthew Dillon 
114b06ebda0SMatthew Dillon #define	bt3c_read_control(sc, data) \
115b06ebda0SMatthew Dillon do { \
116b06ebda0SMatthew Dillon 	(data) = bus_space_read_1((sc)->iot, (sc)->ioh, BT3C_CONTROL); \
117b06ebda0SMatthew Dillon } while (0)
118b06ebda0SMatthew Dillon 
119b06ebda0SMatthew Dillon #define	bt3c_write_control(sc, data) \
120b06ebda0SMatthew Dillon do { \
121b06ebda0SMatthew Dillon 	bus_space_write_1((sc)->iot, (sc)->ioh, BT3C_CONTROL, (data)); \
122b06ebda0SMatthew Dillon } while (0)
123b06ebda0SMatthew Dillon 
124b06ebda0SMatthew Dillon #define bt3c_read(sc, address, data) \
125b06ebda0SMatthew Dillon do { \
126b06ebda0SMatthew Dillon 	bt3c_set_address((sc), (address)); \
127b06ebda0SMatthew Dillon 	bt3c_read_data((sc), (data)); \
128b06ebda0SMatthew Dillon } while(0)
129b06ebda0SMatthew Dillon 
130b06ebda0SMatthew Dillon #define bt3c_write(sc, address, data) \
131b06ebda0SMatthew Dillon do { \
132b06ebda0SMatthew Dillon 	bt3c_set_address((sc), (address)); \
133b06ebda0SMatthew Dillon 	bt3c_write_data((sc), (data)); \
134b06ebda0SMatthew Dillon } while(0)
135b06ebda0SMatthew Dillon 
136b06ebda0SMatthew Dillon static MALLOC_DEFINE(M_BT3C, "bt3c", "bt3c data structures");
137b06ebda0SMatthew Dillon 
138b06ebda0SMatthew Dillon /****************************************************************************
139b06ebda0SMatthew Dillon  ****************************************************************************
140b06ebda0SMatthew Dillon  **                           Netgraph specific
141b06ebda0SMatthew Dillon  ****************************************************************************
142b06ebda0SMatthew Dillon  ****************************************************************************/
143b06ebda0SMatthew Dillon 
144b06ebda0SMatthew Dillon /*
145b06ebda0SMatthew Dillon  * Netgraph node type
146b06ebda0SMatthew Dillon  */
147b06ebda0SMatthew Dillon 
148b06ebda0SMatthew Dillon /* Queue length */
149b06ebda0SMatthew Dillon static const struct ng_parse_struct_field	ng_bt3c_node_qlen_type_fields[] =
150b06ebda0SMatthew Dillon {
151b06ebda0SMatthew Dillon 	{ "queue", &ng_parse_int32_type, },
152b06ebda0SMatthew Dillon 	{ "qlen",  &ng_parse_int32_type, },
153b06ebda0SMatthew Dillon 	{ NULL, }
154b06ebda0SMatthew Dillon };
155b06ebda0SMatthew Dillon static const struct ng_parse_type		ng_bt3c_node_qlen_type = {
156b06ebda0SMatthew Dillon 	&ng_parse_struct_type,
157b06ebda0SMatthew Dillon 	&ng_bt3c_node_qlen_type_fields
158b06ebda0SMatthew Dillon };
159b06ebda0SMatthew Dillon 
160b06ebda0SMatthew Dillon /* Stat info */
161b06ebda0SMatthew Dillon static const struct ng_parse_struct_field	ng_bt3c_node_stat_type_fields[] =
162b06ebda0SMatthew Dillon {
163b06ebda0SMatthew Dillon 	{ "pckts_recv", &ng_parse_uint32_type, },
164b06ebda0SMatthew Dillon 	{ "bytes_recv", &ng_parse_uint32_type, },
165b06ebda0SMatthew Dillon 	{ "pckts_sent", &ng_parse_uint32_type, },
166b06ebda0SMatthew Dillon 	{ "bytes_sent", &ng_parse_uint32_type, },
167b06ebda0SMatthew Dillon 	{ "oerrors",    &ng_parse_uint32_type, },
168b06ebda0SMatthew Dillon 	{ "ierrors",    &ng_parse_uint32_type, },
169b06ebda0SMatthew Dillon 	{ NULL, }
170b06ebda0SMatthew Dillon };
171b06ebda0SMatthew Dillon static const struct ng_parse_type		ng_bt3c_node_stat_type = {
172b06ebda0SMatthew Dillon 	&ng_parse_struct_type,
173b06ebda0SMatthew Dillon 	&ng_bt3c_node_stat_type_fields
174b06ebda0SMatthew Dillon };
175b06ebda0SMatthew Dillon 
176b06ebda0SMatthew Dillon static const struct ng_cmdlist	ng_bt3c_cmdlist[] = {
177b06ebda0SMatthew Dillon {
178b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
179b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_GET_STATE,
180b06ebda0SMatthew Dillon 	"get_state",
181b06ebda0SMatthew Dillon 	NULL,
182b06ebda0SMatthew Dillon 	&ng_parse_uint16_type
183b06ebda0SMatthew Dillon },
184b06ebda0SMatthew Dillon {
185b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
186b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_SET_DEBUG,
187b06ebda0SMatthew Dillon 	"set_debug",
188b06ebda0SMatthew Dillon 	&ng_parse_uint16_type,
189b06ebda0SMatthew Dillon 	NULL
190b06ebda0SMatthew Dillon },
191b06ebda0SMatthew Dillon {
192b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
193b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_GET_DEBUG,
194b06ebda0SMatthew Dillon 	"get_debug",
195b06ebda0SMatthew Dillon 	NULL,
196b06ebda0SMatthew Dillon 	&ng_parse_uint16_type
197b06ebda0SMatthew Dillon },
198b06ebda0SMatthew Dillon {
199b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
200b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_GET_QLEN,
201b06ebda0SMatthew Dillon 	"get_qlen",
202b06ebda0SMatthew Dillon 	NULL,
203b06ebda0SMatthew Dillon 	&ng_bt3c_node_qlen_type
204b06ebda0SMatthew Dillon },
205b06ebda0SMatthew Dillon {
206b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
207b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_SET_QLEN,
208b06ebda0SMatthew Dillon 	"set_qlen",
209b06ebda0SMatthew Dillon 	&ng_bt3c_node_qlen_type,
210b06ebda0SMatthew Dillon 	NULL
211b06ebda0SMatthew Dillon },
212b06ebda0SMatthew Dillon {
213b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
214b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_GET_STAT,
215b06ebda0SMatthew Dillon 	"get_stat",
216b06ebda0SMatthew Dillon 	NULL,
217b06ebda0SMatthew Dillon 	&ng_bt3c_node_stat_type
218b06ebda0SMatthew Dillon },
219b06ebda0SMatthew Dillon {
220b06ebda0SMatthew Dillon 	NGM_BT3C_COOKIE,
221b06ebda0SMatthew Dillon 	NGM_BT3C_NODE_RESET_STAT,
222b06ebda0SMatthew Dillon 	"reset_stat",
223b06ebda0SMatthew Dillon 	NULL,
224b06ebda0SMatthew Dillon 	NULL
225b06ebda0SMatthew Dillon },
226b06ebda0SMatthew Dillon { 0, }
227b06ebda0SMatthew Dillon };
228b06ebda0SMatthew Dillon 
229b06ebda0SMatthew Dillon static struct ng_type	typestruct = {
230b06ebda0SMatthew Dillon 	.version =	NG_ABI_VERSION,
231b06ebda0SMatthew Dillon 	.name = 	NG_BT3C_NODE_TYPE,
232b06ebda0SMatthew Dillon 	.constructor = 	ng_bt3c_constructor,
233b06ebda0SMatthew Dillon 	.rcvmsg =	ng_bt3c_rcvmsg,
234b06ebda0SMatthew Dillon 	.shutdown = 	ng_bt3c_shutdown,
235b06ebda0SMatthew Dillon 	.newhook =	ng_bt3c_newhook,
236b06ebda0SMatthew Dillon 	.connect =	ng_bt3c_connect,
237b06ebda0SMatthew Dillon 	.rcvdata =	ng_bt3c_rcvdata,
238b06ebda0SMatthew Dillon 	.disconnect =	ng_bt3c_disconnect,
239b06ebda0SMatthew Dillon         .cmdlist =	ng_bt3c_cmdlist
240b06ebda0SMatthew Dillon };
241b06ebda0SMatthew Dillon 
242b06ebda0SMatthew Dillon /*
243b06ebda0SMatthew Dillon  * Netgraph node constructor. Do not allow to create node of this type.
244b06ebda0SMatthew Dillon  */
245b06ebda0SMatthew Dillon 
246b06ebda0SMatthew Dillon static int
ng_bt3c_constructor(node_p node)247b06ebda0SMatthew Dillon ng_bt3c_constructor(node_p node)
248b06ebda0SMatthew Dillon {
249b06ebda0SMatthew Dillon 	return (EINVAL);
250b06ebda0SMatthew Dillon } /* ng_bt3c_constructor */
251b06ebda0SMatthew Dillon 
252b06ebda0SMatthew Dillon /*
253b06ebda0SMatthew Dillon  * Netgraph node destructor. Destroy node only when device has been detached
254b06ebda0SMatthew Dillon  */
255b06ebda0SMatthew Dillon 
256b06ebda0SMatthew Dillon static int
ng_bt3c_shutdown(node_p node)257b06ebda0SMatthew Dillon ng_bt3c_shutdown(node_p node)
258b06ebda0SMatthew Dillon {
259b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
260b06ebda0SMatthew Dillon 
261b06ebda0SMatthew Dillon 	/* Let old node go */
262b06ebda0SMatthew Dillon 	NG_NODE_SET_PRIVATE(node, NULL);
263b06ebda0SMatthew Dillon 	NG_NODE_UNREF(node);
264b06ebda0SMatthew Dillon 
265b06ebda0SMatthew Dillon 	/* Create new fresh one if we are not going down */
266b06ebda0SMatthew Dillon 	if (sc == NULL)
267b06ebda0SMatthew Dillon 		goto out;
268b06ebda0SMatthew Dillon 
269b06ebda0SMatthew Dillon 	/* Create new Netgraph node */
270b06ebda0SMatthew Dillon 	if (ng_make_node_common(&typestruct, &sc->node) != 0) {
271b06ebda0SMatthew Dillon 		device_printf(sc->dev, "Could not create Netgraph node\n");
272b06ebda0SMatthew Dillon 		sc->node = NULL;
273b06ebda0SMatthew Dillon 		goto out;
274b06ebda0SMatthew Dillon 	}
275b06ebda0SMatthew Dillon 
276b06ebda0SMatthew Dillon 	/* Name new Netgraph node */
277b06ebda0SMatthew Dillon 	if (ng_name_node(sc->node,  device_get_nameunit(sc->dev)) != 0) {
278b06ebda0SMatthew Dillon 		device_printf(sc->dev, "Could not name Netgraph node\n");
279b06ebda0SMatthew Dillon 		NG_NODE_UNREF(sc->node);
280b06ebda0SMatthew Dillon 		sc->node = NULL;
281b06ebda0SMatthew Dillon 		goto out;
282b06ebda0SMatthew Dillon 	}
283b06ebda0SMatthew Dillon 
284b06ebda0SMatthew Dillon 	NG_NODE_SET_PRIVATE(sc->node, sc);
285b06ebda0SMatthew Dillon out:
286b06ebda0SMatthew Dillon 	return (0);
287b06ebda0SMatthew Dillon } /* ng_bt3c_shutdown */
288b06ebda0SMatthew Dillon 
289b06ebda0SMatthew Dillon /*
290b06ebda0SMatthew Dillon  * Create new hook. There can only be one.
291b06ebda0SMatthew Dillon  */
292b06ebda0SMatthew Dillon 
293b06ebda0SMatthew Dillon static int
ng_bt3c_newhook(node_p node,hook_p hook,char const * name)294b06ebda0SMatthew Dillon ng_bt3c_newhook(node_p node, hook_p hook, char const *name)
295b06ebda0SMatthew Dillon {
296b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
297b06ebda0SMatthew Dillon 
298b06ebda0SMatthew Dillon 	if (strcmp(name, NG_BT3C_HOOK) != 0)
299b06ebda0SMatthew Dillon 		return (EINVAL);
300b06ebda0SMatthew Dillon 
301b06ebda0SMatthew Dillon 	if (sc->hook != NULL)
302b06ebda0SMatthew Dillon 		return (EISCONN);
303b06ebda0SMatthew Dillon 
304b06ebda0SMatthew Dillon 	sc->hook = hook;
305b06ebda0SMatthew Dillon 
306b06ebda0SMatthew Dillon 	return (0);
307b06ebda0SMatthew Dillon } /* ng_bt3c_newhook */
308b06ebda0SMatthew Dillon 
309b06ebda0SMatthew Dillon /*
310b06ebda0SMatthew Dillon  * Connect hook. Say YEP, that's OK with me.
311b06ebda0SMatthew Dillon  */
312b06ebda0SMatthew Dillon 
313b06ebda0SMatthew Dillon static int
ng_bt3c_connect(hook_p hook)314b06ebda0SMatthew Dillon ng_bt3c_connect(hook_p hook)
315b06ebda0SMatthew Dillon {
316b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
317b06ebda0SMatthew Dillon 
318b06ebda0SMatthew Dillon 	if (hook != sc->hook) {
319b06ebda0SMatthew Dillon 		sc->hook = NULL;
320b06ebda0SMatthew Dillon 		return (EINVAL);
321b06ebda0SMatthew Dillon 	}
322b06ebda0SMatthew Dillon 
323b06ebda0SMatthew Dillon 	/* set the hook into queueing mode (for incoming (from wire) packets) */
324b06ebda0SMatthew Dillon 	NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
325b06ebda0SMatthew Dillon 
326b06ebda0SMatthew Dillon 	return (0);
327b06ebda0SMatthew Dillon } /* ng_bt3c_connect */
328b06ebda0SMatthew Dillon 
329b06ebda0SMatthew Dillon /*
330b06ebda0SMatthew Dillon  * Disconnect hook
331b06ebda0SMatthew Dillon  */
332b06ebda0SMatthew Dillon 
333b06ebda0SMatthew Dillon static int
ng_bt3c_disconnect(hook_p hook)334b06ebda0SMatthew Dillon ng_bt3c_disconnect(hook_p hook)
335b06ebda0SMatthew Dillon {
336b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
337b06ebda0SMatthew Dillon 
338b06ebda0SMatthew Dillon 	/*
339b06ebda0SMatthew Dillon 	 * We need to check for sc != NULL because we can be called from
340b06ebda0SMatthew Dillon 	 * bt3c_pccard_detach() via ng_rmnode_self()
341b06ebda0SMatthew Dillon 	 */
342b06ebda0SMatthew Dillon 
343b06ebda0SMatthew Dillon 	if (sc != NULL) {
344b06ebda0SMatthew Dillon 		if (hook != sc->hook)
345b06ebda0SMatthew Dillon 			return (EINVAL);
346b06ebda0SMatthew Dillon 
347b06ebda0SMatthew Dillon 		IF_DRAIN(&sc->inq);
348b06ebda0SMatthew Dillon 		IF_DRAIN(&sc->outq);
349b06ebda0SMatthew Dillon 
350b06ebda0SMatthew Dillon 		sc->hook = NULL;
351b06ebda0SMatthew Dillon 	}
352b06ebda0SMatthew Dillon 
353b06ebda0SMatthew Dillon 	return (0);
354b06ebda0SMatthew Dillon } /* ng_bt3c_disconnect */
355b06ebda0SMatthew Dillon 
356b06ebda0SMatthew Dillon /*
357b06ebda0SMatthew Dillon  * Process control message
358b06ebda0SMatthew Dillon  */
359b06ebda0SMatthew Dillon 
360b06ebda0SMatthew Dillon static int
ng_bt3c_rcvmsg(node_p node,item_p item,hook_p lasthook)361b06ebda0SMatthew Dillon ng_bt3c_rcvmsg(node_p node, item_p item, hook_p lasthook)
362b06ebda0SMatthew Dillon {
363b06ebda0SMatthew Dillon 	bt3c_softc_p	 sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
364b06ebda0SMatthew Dillon 	struct ng_mesg	*msg = NULL, *rsp = NULL;
365b06ebda0SMatthew Dillon 	int		 error = 0;
366b06ebda0SMatthew Dillon 
367b06ebda0SMatthew Dillon 	if (sc == NULL) {
368b06ebda0SMatthew Dillon 		NG_FREE_ITEM(item);
369b06ebda0SMatthew Dillon 		return (EHOSTDOWN);
370b06ebda0SMatthew Dillon 	}
371b06ebda0SMatthew Dillon 
372b06ebda0SMatthew Dillon 	NGI_GET_MSG(item, msg);
373b06ebda0SMatthew Dillon 
374b06ebda0SMatthew Dillon 	switch (msg->header.typecookie) {
375b06ebda0SMatthew Dillon 	case NGM_GENERIC_COOKIE:
376b06ebda0SMatthew Dillon 		switch (msg->header.cmd) {
377b06ebda0SMatthew Dillon 		case NGM_TEXT_STATUS:
3785a975a3dSMatthew Dillon 			NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
379b06ebda0SMatthew Dillon 			if (rsp == NULL)
380b06ebda0SMatthew Dillon 				error = ENOMEM;
381b06ebda0SMatthew Dillon 			else
382a62226e4SSascha Wildner 				ksnprintf(rsp->data, NG_TEXTRESPONSE,
383b06ebda0SMatthew Dillon 					"Hook: %s\n" \
384b06ebda0SMatthew Dillon 					"Flags: %#x\n" \
385b06ebda0SMatthew Dillon 					"Debug: %d\n"  \
386b06ebda0SMatthew Dillon 					"State: %d\n"  \
387b06ebda0SMatthew Dillon 					"IncmQ: [len:%d,max:%d]\n" \
388b06ebda0SMatthew Dillon 					"OutgQ: [len:%d,max:%d]\n",
389b06ebda0SMatthew Dillon 					(sc->hook != NULL)? NG_BT3C_HOOK : "",
390b06ebda0SMatthew Dillon 					sc->flags,
391b06ebda0SMatthew Dillon 					sc->debug,
392b06ebda0SMatthew Dillon 					sc->state,
393b50b93ffSSascha Wildner 					IF_QLEN(&sc->inq), /* XXX */
394b06ebda0SMatthew Dillon 					sc->inq.ifq_maxlen, /* XXX */
395b50b93ffSSascha Wildner 					IF_QLEN(&sc->outq), /* XXX */
396b06ebda0SMatthew Dillon 					sc->outq.ifq_maxlen /* XXX */
397b06ebda0SMatthew Dillon 					);
398b06ebda0SMatthew Dillon 			break;
399b06ebda0SMatthew Dillon 
400b06ebda0SMatthew Dillon 		default:
401b06ebda0SMatthew Dillon 			error = EINVAL;
402b06ebda0SMatthew Dillon 			break;
403b06ebda0SMatthew Dillon 		}
404b06ebda0SMatthew Dillon 		break;
405b06ebda0SMatthew Dillon 
406b06ebda0SMatthew Dillon 	case NGM_BT3C_COOKIE:
407b06ebda0SMatthew Dillon 		switch (msg->header.cmd) {
408b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_GET_STATE:
409b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_state_ep),
4105a975a3dSMatthew Dillon 				M_WAITOK | M_NULLOK);
411b06ebda0SMatthew Dillon 			if (rsp == NULL)
412b06ebda0SMatthew Dillon 				error = ENOMEM;
413b06ebda0SMatthew Dillon 			else
414b06ebda0SMatthew Dillon 				*((ng_bt3c_node_state_ep *)(rsp->data)) =
415b06ebda0SMatthew Dillon 					sc->state;
416b06ebda0SMatthew Dillon 			break;
417b06ebda0SMatthew Dillon 
418b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_SET_DEBUG:
419b06ebda0SMatthew Dillon 			if (msg->header.arglen != sizeof(ng_bt3c_node_debug_ep))
420b06ebda0SMatthew Dillon 				error = EMSGSIZE;
421b06ebda0SMatthew Dillon 			else
422b06ebda0SMatthew Dillon 				sc->debug =
423b06ebda0SMatthew Dillon 					*((ng_bt3c_node_debug_ep *)(msg->data));
424b06ebda0SMatthew Dillon 			break;
425b06ebda0SMatthew Dillon 
426b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_GET_DEBUG:
427b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_debug_ep),
4285a975a3dSMatthew Dillon 				M_WAITOK | M_NULLOK);
429b06ebda0SMatthew Dillon 			if (rsp == NULL)
430b06ebda0SMatthew Dillon 				error = ENOMEM;
431b06ebda0SMatthew Dillon 			else
432b06ebda0SMatthew Dillon 				*((ng_bt3c_node_debug_ep *)(rsp->data)) =
433b06ebda0SMatthew Dillon 					sc->debug;
434b06ebda0SMatthew Dillon 			break;
435b06ebda0SMatthew Dillon 
436b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_GET_QLEN:
437b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_qlen_ep),
4385a975a3dSMatthew Dillon 				M_WAITOK | M_NULLOK);
439b06ebda0SMatthew Dillon 			if (rsp == NULL) {
440b06ebda0SMatthew Dillon 				error = ENOMEM;
441b06ebda0SMatthew Dillon 				break;
442b06ebda0SMatthew Dillon 			}
443b06ebda0SMatthew Dillon 
444b06ebda0SMatthew Dillon 			switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
445b06ebda0SMatthew Dillon 			case NGM_BT3C_NODE_IN_QUEUE:
446b06ebda0SMatthew Dillon 				((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
447b06ebda0SMatthew Dillon 					NGM_BT3C_NODE_IN_QUEUE;
448b06ebda0SMatthew Dillon 				((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
449b06ebda0SMatthew Dillon 					sc->inq.ifq_maxlen;
450b06ebda0SMatthew Dillon 				break;
451b06ebda0SMatthew Dillon 
452b06ebda0SMatthew Dillon 			case NGM_BT3C_NODE_OUT_QUEUE:
453b06ebda0SMatthew Dillon 				((ng_bt3c_node_qlen_ep *)(rsp->data))->queue =
454b06ebda0SMatthew Dillon 					NGM_BT3C_NODE_OUT_QUEUE;
455b06ebda0SMatthew Dillon 				((ng_bt3c_node_qlen_ep *)(rsp->data))->qlen =
456b06ebda0SMatthew Dillon 					sc->outq.ifq_maxlen;
457b06ebda0SMatthew Dillon 				break;
458b06ebda0SMatthew Dillon 
459b06ebda0SMatthew Dillon 			default:
460b06ebda0SMatthew Dillon 				NG_FREE_MSG(rsp);
461b06ebda0SMatthew Dillon 				error = EINVAL;
462b06ebda0SMatthew Dillon 				break;
463b06ebda0SMatthew Dillon 			}
464b06ebda0SMatthew Dillon 			break;
465b06ebda0SMatthew Dillon 
466b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_SET_QLEN:
467b06ebda0SMatthew Dillon 			if (msg->header.arglen != sizeof(ng_bt3c_node_qlen_ep)){
468b06ebda0SMatthew Dillon 				error = EMSGSIZE;
469b06ebda0SMatthew Dillon 				break;
470b06ebda0SMatthew Dillon 			}
471b06ebda0SMatthew Dillon 
472b06ebda0SMatthew Dillon 			if (((ng_bt3c_node_qlen_ep *)(msg->data))->qlen <= 0) {
473b06ebda0SMatthew Dillon 				error = EINVAL;
474b06ebda0SMatthew Dillon 				break;
475b06ebda0SMatthew Dillon 			}
476b06ebda0SMatthew Dillon 
477b06ebda0SMatthew Dillon 			switch (((ng_bt3c_node_qlen_ep *)(msg->data))->queue) {
478b06ebda0SMatthew Dillon 			case NGM_BT3C_NODE_IN_QUEUE:
479b06ebda0SMatthew Dillon 				sc->inq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)
480b06ebda0SMatthew Dillon 					(msg->data))->qlen; /* XXX */
481b06ebda0SMatthew Dillon 				break;
482b06ebda0SMatthew Dillon 
483b06ebda0SMatthew Dillon 			case NGM_BT3C_NODE_OUT_QUEUE:
484b06ebda0SMatthew Dillon 				sc->outq.ifq_maxlen = ((ng_bt3c_node_qlen_ep *)
485b06ebda0SMatthew Dillon 					(msg->data))->qlen; /* XXX */
486b06ebda0SMatthew Dillon 				break;
487b06ebda0SMatthew Dillon 
488b06ebda0SMatthew Dillon 			default:
489b06ebda0SMatthew Dillon 				error = EINVAL;
490b06ebda0SMatthew Dillon 				break;
491b06ebda0SMatthew Dillon 			}
492b06ebda0SMatthew Dillon 			break;
493b06ebda0SMatthew Dillon 
494b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_GET_STAT:
495b06ebda0SMatthew Dillon 			NG_MKRESPONSE(rsp, msg, sizeof(ng_bt3c_node_stat_ep),
4965a975a3dSMatthew Dillon 				M_WAITOK | M_NULLOK);
497b06ebda0SMatthew Dillon 			if (rsp == NULL)
498b06ebda0SMatthew Dillon 				error = ENOMEM;
499b06ebda0SMatthew Dillon 			else
500b06ebda0SMatthew Dillon 				bcopy(&sc->stat, rsp->data,
501b06ebda0SMatthew Dillon 					sizeof(ng_bt3c_node_stat_ep));
502b06ebda0SMatthew Dillon 			break;
503b06ebda0SMatthew Dillon 
504b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_RESET_STAT:
505b06ebda0SMatthew Dillon 			NG_BT3C_STAT_RESET(sc->stat);
506b06ebda0SMatthew Dillon 			break;
507b06ebda0SMatthew Dillon 
508b06ebda0SMatthew Dillon 		case NGM_BT3C_NODE_DOWNLOAD_FIRMWARE:
509b06ebda0SMatthew Dillon 			if (msg->header.arglen <
510b06ebda0SMatthew Dillon 					sizeof(ng_bt3c_firmware_block_ep))
511b06ebda0SMatthew Dillon 				error = EMSGSIZE;
512b06ebda0SMatthew Dillon 			else
513b06ebda0SMatthew Dillon 				bt3c_download_firmware(sc, msg->data,
514b06ebda0SMatthew Dillon 							msg->header.arglen);
515b06ebda0SMatthew Dillon 			break;
516b06ebda0SMatthew Dillon 
517b06ebda0SMatthew Dillon 		default:
518b06ebda0SMatthew Dillon 			error = EINVAL;
519b06ebda0SMatthew Dillon 			break;
520b06ebda0SMatthew Dillon 		}
521b06ebda0SMatthew Dillon 		break;
522b06ebda0SMatthew Dillon 
523b06ebda0SMatthew Dillon 	default:
524b06ebda0SMatthew Dillon 		error = EINVAL;
525b06ebda0SMatthew Dillon 		break;
526b06ebda0SMatthew Dillon 	}
527b06ebda0SMatthew Dillon 
528b06ebda0SMatthew Dillon 	NG_RESPOND_MSG(error, node, item, rsp);
529b06ebda0SMatthew Dillon 	NG_FREE_MSG(msg);
530b06ebda0SMatthew Dillon 
531b06ebda0SMatthew Dillon 	return (error);
532b06ebda0SMatthew Dillon } /* ng_bt3c_rcvmsg */
533b06ebda0SMatthew Dillon 
534b06ebda0SMatthew Dillon /*
535b06ebda0SMatthew Dillon  * Process data
536b06ebda0SMatthew Dillon  */
537b06ebda0SMatthew Dillon 
538b06ebda0SMatthew Dillon static int
ng_bt3c_rcvdata(hook_p hook,item_p item)539b06ebda0SMatthew Dillon ng_bt3c_rcvdata(hook_p hook, item_p item)
540b06ebda0SMatthew Dillon {
541b06ebda0SMatthew Dillon 	bt3c_softc_p	 sc = (bt3c_softc_p)NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
542b06ebda0SMatthew Dillon 	struct mbuf	*m = NULL;
543b06ebda0SMatthew Dillon 	int		 error = 0;
544b06ebda0SMatthew Dillon 
545b06ebda0SMatthew Dillon 	if (sc == NULL) {
546b06ebda0SMatthew Dillon 		error = EHOSTDOWN;
547b06ebda0SMatthew Dillon 		goto out;
548b06ebda0SMatthew Dillon 	}
549b06ebda0SMatthew Dillon 
550b06ebda0SMatthew Dillon 	if (hook != sc->hook) {
551b06ebda0SMatthew Dillon 		error = EINVAL;
552b06ebda0SMatthew Dillon 		goto out;
553b06ebda0SMatthew Dillon 	}
554b06ebda0SMatthew Dillon 
555b06ebda0SMatthew Dillon 	NGI_GET_M(item, m);
556b06ebda0SMatthew Dillon 
557b50b93ffSSascha Wildner 	if (IF_QFULL(&sc->outq)) {
558b06ebda0SMatthew Dillon 		NG_BT3C_ERR(sc->dev,
559b06ebda0SMatthew Dillon "Outgoing queue is full. Dropping mbuf, len=%d\n", m->m_pkthdr.len);
560b06ebda0SMatthew Dillon 
561b50b93ffSSascha Wildner 		IF_DROP(&sc->outq);
562b06ebda0SMatthew Dillon 		NG_BT3C_STAT_OERROR(sc->stat);
563b06ebda0SMatthew Dillon 
564b06ebda0SMatthew Dillon 		NG_FREE_M(m);
565b06ebda0SMatthew Dillon 	} else
566b50b93ffSSascha Wildner 		IF_ENQUEUE(&sc->outq, m);
567b06ebda0SMatthew Dillon 
568b06ebda0SMatthew Dillon 	error = ng_send_fn(sc->node, NULL, bt3c_send, NULL, 0 /* new send */);
569b06ebda0SMatthew Dillon out:
570b06ebda0SMatthew Dillon         NG_FREE_ITEM(item);
571b06ebda0SMatthew Dillon 
572b06ebda0SMatthew Dillon 	return (error);
573b06ebda0SMatthew Dillon } /* ng_bt3c_rcvdata */
574b06ebda0SMatthew Dillon 
575b06ebda0SMatthew Dillon /****************************************************************************
576b06ebda0SMatthew Dillon  ****************************************************************************
577b06ebda0SMatthew Dillon  **                         PCMCIA driver specific
578b06ebda0SMatthew Dillon  ****************************************************************************
579b06ebda0SMatthew Dillon  ****************************************************************************/
580b06ebda0SMatthew Dillon 
581b06ebda0SMatthew Dillon /*
582b06ebda0SMatthew Dillon  * PC Card (PCMCIA) probe routine
583b06ebda0SMatthew Dillon  */
584b06ebda0SMatthew Dillon 
585b06ebda0SMatthew Dillon static int
bt3c_pccard_probe(device_t dev)586b06ebda0SMatthew Dillon bt3c_pccard_probe(device_t dev)
587b06ebda0SMatthew Dillon {
588b06ebda0SMatthew Dillon 	static struct pccard_product const	bt3c_pccard_products[] = {
589b50b93ffSSascha Wildner 		PCMCIA_CARD(3COM, 3CRWB609, 0),
590b06ebda0SMatthew Dillon 		{ NULL, }
591b06ebda0SMatthew Dillon 	};
592b06ebda0SMatthew Dillon 
593b06ebda0SMatthew Dillon 	struct pccard_product const	*pp = NULL;
594b06ebda0SMatthew Dillon 
595b06ebda0SMatthew Dillon 	pp = pccard_product_lookup(dev, bt3c_pccard_products,
596b06ebda0SMatthew Dillon 			sizeof(bt3c_pccard_products[0]), NULL);
597b06ebda0SMatthew Dillon 	if (pp == NULL)
598b06ebda0SMatthew Dillon 		return (ENXIO);
599b06ebda0SMatthew Dillon 
600b06ebda0SMatthew Dillon 	device_set_desc(dev, pp->pp_name);
601b06ebda0SMatthew Dillon 
602b06ebda0SMatthew Dillon 	return (0);
603b06ebda0SMatthew Dillon } /* bt3c_pccard_probe */
604b06ebda0SMatthew Dillon 
605b06ebda0SMatthew Dillon /*
606b06ebda0SMatthew Dillon  * PC Card (PCMCIA) attach routine
607b06ebda0SMatthew Dillon  */
608b06ebda0SMatthew Dillon 
609b06ebda0SMatthew Dillon static int
bt3c_pccard_attach(device_t dev)610b06ebda0SMatthew Dillon bt3c_pccard_attach(device_t dev)
611b06ebda0SMatthew Dillon {
612b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) device_get_softc(dev);
613b06ebda0SMatthew Dillon 
614b06ebda0SMatthew Dillon 	/* Allocate I/O ports */
615b06ebda0SMatthew Dillon 	sc->iobase_rid = 0;
616b06ebda0SMatthew Dillon 	sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid,
617b06ebda0SMatthew Dillon 			0, ~0, 8, RF_ACTIVE);
618b06ebda0SMatthew Dillon 	if (sc->iobase == NULL) {
619b06ebda0SMatthew Dillon 		device_printf(dev, "Could not allocate I/O ports\n");
620b06ebda0SMatthew Dillon 		goto bad;
621b06ebda0SMatthew Dillon 	}
622b06ebda0SMatthew Dillon 	sc->iot = rman_get_bustag(sc->iobase);
623b06ebda0SMatthew Dillon 	sc->ioh = rman_get_bushandle(sc->iobase);
624b06ebda0SMatthew Dillon 
625b06ebda0SMatthew Dillon 	/* Allocate IRQ */
626b06ebda0SMatthew Dillon 	sc->irq_rid = 0;
627b06ebda0SMatthew Dillon 	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
628b06ebda0SMatthew Dillon 			RF_ACTIVE);
629b06ebda0SMatthew Dillon 	if (sc->irq == NULL) {
630b06ebda0SMatthew Dillon 		device_printf(dev, "Could not allocate IRQ\n");
631b06ebda0SMatthew Dillon 		goto bad;
632b06ebda0SMatthew Dillon 	}
633b06ebda0SMatthew Dillon 
634b06ebda0SMatthew Dillon 	sc->irq_cookie = NULL;
635b50b93ffSSascha Wildner 	if (bus_setup_intr(dev, sc->irq, 0, bt3c_intr, sc,
636b50b93ffSSascha Wildner 			&sc->irq_cookie, NULL) != 0) {
637b06ebda0SMatthew Dillon 		device_printf(dev, "Could not setup ISR\n");
638b06ebda0SMatthew Dillon 		goto bad;
639b06ebda0SMatthew Dillon 	}
640b06ebda0SMatthew Dillon 
641b06ebda0SMatthew Dillon 	/* Attach handler to TTY SWI thread */
642b06ebda0SMatthew Dillon 	sc->ith = NULL;
643b50b93ffSSascha Wildner 	sc->ith = register_swi_mp(SWI_TTY, bt3c_swi_intr, sc,
644b50b93ffSSascha Wildner 	    device_get_nameunit(dev), NULL, -1);
645b50b93ffSSascha Wildner 	if (sc->ith == NULL) {
646b06ebda0SMatthew Dillon 		device_printf(dev, "Could not setup SWI ISR\n");
647b06ebda0SMatthew Dillon 		goto bad;
648b06ebda0SMatthew Dillon 	}
649b06ebda0SMatthew Dillon 
650b06ebda0SMatthew Dillon 	/* Create Netgraph node */
651b06ebda0SMatthew Dillon 	if (ng_make_node_common(&typestruct, &sc->node) != 0) {
652b06ebda0SMatthew Dillon 		device_printf(dev, "Could not create Netgraph node\n");
653b06ebda0SMatthew Dillon 		sc->node = NULL;
654b06ebda0SMatthew Dillon 		goto bad;
655b06ebda0SMatthew Dillon 	}
656b06ebda0SMatthew Dillon 
657b06ebda0SMatthew Dillon 	/* Name Netgraph node */
658b06ebda0SMatthew Dillon 	if (ng_name_node(sc->node, device_get_nameunit(dev)) != 0) {
659b06ebda0SMatthew Dillon 		device_printf(dev, "Could not name Netgraph node\n");
660b06ebda0SMatthew Dillon 		NG_NODE_UNREF(sc->node);
661b06ebda0SMatthew Dillon 		sc->node = NULL;
662b06ebda0SMatthew Dillon 		goto bad;
663b06ebda0SMatthew Dillon 	}
664b06ebda0SMatthew Dillon 
665b06ebda0SMatthew Dillon 	sc->dev = dev;
666b06ebda0SMatthew Dillon 	sc->debug = NG_BT3C_WARN_LEVEL;
667b06ebda0SMatthew Dillon 
668b06ebda0SMatthew Dillon 	sc->inq.ifq_maxlen = sc->outq.ifq_maxlen = BT3C_DEFAULTQLEN;
669b06ebda0SMatthew Dillon 
670b06ebda0SMatthew Dillon 	sc->state = NG_BT3C_W4_PKT_IND;
671b06ebda0SMatthew Dillon 	sc->want = 1;
672b06ebda0SMatthew Dillon 
673b06ebda0SMatthew Dillon 	NG_NODE_SET_PRIVATE(sc->node, sc);
674b06ebda0SMatthew Dillon 
675b06ebda0SMatthew Dillon 	return (0);
676b06ebda0SMatthew Dillon bad:
677b06ebda0SMatthew Dillon 	if (sc->ith != NULL) {
678b50b93ffSSascha Wildner 		unregister_swi(sc->ith, SWI_TTY, -1);
679b06ebda0SMatthew Dillon 		sc->ith = NULL;
680b06ebda0SMatthew Dillon 	}
681b06ebda0SMatthew Dillon 
682b06ebda0SMatthew Dillon 	if (sc->irq != NULL) {
683b06ebda0SMatthew Dillon 		if (sc->irq_cookie != NULL)
684b06ebda0SMatthew Dillon 			bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
685b06ebda0SMatthew Dillon 
686b06ebda0SMatthew Dillon 		bus_release_resource(dev, SYS_RES_IRQ,
687b06ebda0SMatthew Dillon 			sc->irq_rid, sc->irq);
688b06ebda0SMatthew Dillon 
689b06ebda0SMatthew Dillon 		sc->irq = NULL;
690b06ebda0SMatthew Dillon 		sc->irq_rid = 0;
691b06ebda0SMatthew Dillon 	}
692b06ebda0SMatthew Dillon 
693b06ebda0SMatthew Dillon 	if (sc->iobase != NULL) {
694b06ebda0SMatthew Dillon 		bus_release_resource(dev, SYS_RES_IOPORT,
695b06ebda0SMatthew Dillon 			sc->iobase_rid, sc->iobase);
696b06ebda0SMatthew Dillon 
697b06ebda0SMatthew Dillon 		sc->iobase = NULL;
698b06ebda0SMatthew Dillon 		sc->iobase_rid = 0;
699b06ebda0SMatthew Dillon 	}
700b06ebda0SMatthew Dillon 
701b06ebda0SMatthew Dillon 	return (ENXIO);
702b06ebda0SMatthew Dillon } /* bt3c_pccacd_attach */
703b06ebda0SMatthew Dillon 
704b06ebda0SMatthew Dillon /*
705b06ebda0SMatthew Dillon  * PC Card (PCMCIA) detach routine
706b06ebda0SMatthew Dillon  */
707b06ebda0SMatthew Dillon 
708b06ebda0SMatthew Dillon static int
bt3c_pccard_detach(device_t dev)709b06ebda0SMatthew Dillon bt3c_pccard_detach(device_t dev)
710b06ebda0SMatthew Dillon {
711b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) device_get_softc(dev);
712b06ebda0SMatthew Dillon 
713b06ebda0SMatthew Dillon 	if (sc == NULL)
714b06ebda0SMatthew Dillon 		return (0);
715b06ebda0SMatthew Dillon 
716b50b93ffSSascha Wildner 	unregister_swi(sc->ith, SWI_TTY, -1);
717b06ebda0SMatthew Dillon 	sc->ith = NULL;
718b06ebda0SMatthew Dillon 
719b06ebda0SMatthew Dillon 	bus_teardown_intr(dev, sc->irq, sc->irq_cookie);
720b06ebda0SMatthew Dillon 	bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
721b06ebda0SMatthew Dillon 	sc->irq_cookie = NULL;
722b06ebda0SMatthew Dillon 	sc->irq = NULL;
723b06ebda0SMatthew Dillon 	sc->irq_rid = 0;
724b06ebda0SMatthew Dillon 
725b06ebda0SMatthew Dillon 	bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase);
726b06ebda0SMatthew Dillon 	sc->iobase = NULL;
727b06ebda0SMatthew Dillon 	sc->iobase_rid = 0;
728b06ebda0SMatthew Dillon 
729b06ebda0SMatthew Dillon 	if (sc->node != NULL) {
730b06ebda0SMatthew Dillon 		NG_NODE_SET_PRIVATE(sc->node, NULL);
731b06ebda0SMatthew Dillon 		ng_rmnode_self(sc->node);
732b06ebda0SMatthew Dillon 		sc->node = NULL;
733b06ebda0SMatthew Dillon 	}
734b06ebda0SMatthew Dillon 
735b06ebda0SMatthew Dillon 	NG_FREE_M(sc->m);
736b06ebda0SMatthew Dillon 	IF_DRAIN(&sc->inq);
737b06ebda0SMatthew Dillon 	IF_DRAIN(&sc->outq);
738b06ebda0SMatthew Dillon 
739b06ebda0SMatthew Dillon 	return (0);
740b06ebda0SMatthew Dillon } /* bt3c_pccacd_detach */
741b06ebda0SMatthew Dillon 
742b06ebda0SMatthew Dillon /*
743b06ebda0SMatthew Dillon  * Interrupt service routine's
744b06ebda0SMatthew Dillon  */
745b06ebda0SMatthew Dillon 
746b06ebda0SMatthew Dillon static void
bt3c_intr(void * context)747b06ebda0SMatthew Dillon bt3c_intr(void *context)
748b06ebda0SMatthew Dillon {
749b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) context;
750b06ebda0SMatthew Dillon 	u_int16_t	control, status;
751b06ebda0SMatthew Dillon 
752b06ebda0SMatthew Dillon 	if (sc == NULL || sc->ith == NULL) {
753a62226e4SSascha Wildner 		kprintf("%s: bogus interrupt\n", NG_BT3C_NODE_TYPE);
754b06ebda0SMatthew Dillon 		return;
755b06ebda0SMatthew Dillon 	}
756b06ebda0SMatthew Dillon 
757b06ebda0SMatthew Dillon 	bt3c_read_control(sc, control);
758b06ebda0SMatthew Dillon 	if ((control & 0x80) == 0)
759b06ebda0SMatthew Dillon 		return;
760b06ebda0SMatthew Dillon 
761b06ebda0SMatthew Dillon 	bt3c_read(sc, 0x7001, status);
762b06ebda0SMatthew Dillon 	NG_BT3C_INFO(sc->dev, "control=%#x, status=%#x\n", control, status);
763b06ebda0SMatthew Dillon 
764b06ebda0SMatthew Dillon 	if ((status & 0xff) == 0x7f || (status & 0xff) == 0xff) {
765b06ebda0SMatthew Dillon 		NG_BT3C_WARN(sc->dev, "Strange status=%#x\n", status);
766b06ebda0SMatthew Dillon 		return;
767b06ebda0SMatthew Dillon 	}
768b06ebda0SMatthew Dillon 
769b06ebda0SMatthew Dillon 	/* Receive complete */
770b06ebda0SMatthew Dillon 	if (status & 0x0001)
771b06ebda0SMatthew Dillon 		bt3c_receive(sc);
772b06ebda0SMatthew Dillon 
773b06ebda0SMatthew Dillon 	/* Record status and schedule SWI */
774b06ebda0SMatthew Dillon 	sc->status |= status;
775b50b93ffSSascha Wildner 	schedsofttty();
776b06ebda0SMatthew Dillon 
777b06ebda0SMatthew Dillon 	/* Complete interrupt */
778b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x7001, 0x0000);
779b06ebda0SMatthew Dillon 	bt3c_write_control(sc, control);
780b06ebda0SMatthew Dillon } /* bt3c_intr */
781b06ebda0SMatthew Dillon 
782b06ebda0SMatthew Dillon /*
783b06ebda0SMatthew Dillon  * Receive data
784b06ebda0SMatthew Dillon  */
785b06ebda0SMatthew Dillon 
786b06ebda0SMatthew Dillon static void
bt3c_receive(bt3c_softc_p sc)787b06ebda0SMatthew Dillon bt3c_receive(bt3c_softc_p sc)
788b06ebda0SMatthew Dillon {
789b06ebda0SMatthew Dillon 	u_int16_t	i, count, c;
790b06ebda0SMatthew Dillon 
791b06ebda0SMatthew Dillon 	/* Receive data from the card */
792b06ebda0SMatthew Dillon 	bt3c_read(sc, 0x7006, count);
793b06ebda0SMatthew Dillon 	NG_BT3C_INFO(sc->dev, "The card has %d characters\n", count);
794b06ebda0SMatthew Dillon 
795b06ebda0SMatthew Dillon 	bt3c_set_address(sc, 0x7480);
796b06ebda0SMatthew Dillon 
797b06ebda0SMatthew Dillon 	for (i = 0; i < count; i++) {
798b06ebda0SMatthew Dillon 		/* Allocate new mbuf if needed */
799b06ebda0SMatthew Dillon 		if (sc->m == NULL) {
800b06ebda0SMatthew Dillon 			sc->state = NG_BT3C_W4_PKT_IND;
801b06ebda0SMatthew Dillon 			sc->want = 1;
802b06ebda0SMatthew Dillon 
803b5523eacSSascha Wildner 			MGETHDR(sc->m, M_NOWAIT, MT_DATA);
804b06ebda0SMatthew Dillon 			if (sc->m == NULL) {
805b06ebda0SMatthew Dillon 				NG_BT3C_ERR(sc->dev, "Could not get mbuf\n");
806b06ebda0SMatthew Dillon 				NG_BT3C_STAT_IERROR(sc->stat);
807b06ebda0SMatthew Dillon 
808b06ebda0SMatthew Dillon 				break; /* XXX lost of sync */
809b06ebda0SMatthew Dillon 			}
810b06ebda0SMatthew Dillon 
811b5523eacSSascha Wildner 			MCLGET(sc->m, M_NOWAIT);
812b06ebda0SMatthew Dillon 			if (!(sc->m->m_flags & M_EXT)) {
813b06ebda0SMatthew Dillon 				NG_FREE_M(sc->m);
814b06ebda0SMatthew Dillon 
815b06ebda0SMatthew Dillon 				NG_BT3C_ERR(sc->dev, "Could not get cluster\n");
816b06ebda0SMatthew Dillon 				NG_BT3C_STAT_IERROR(sc->stat);
817b06ebda0SMatthew Dillon 
818b06ebda0SMatthew Dillon 				break; /* XXX lost of sync */
819b06ebda0SMatthew Dillon 			}
820b06ebda0SMatthew Dillon 
821b06ebda0SMatthew Dillon 			sc->m->m_len = sc->m->m_pkthdr.len = 0;
822b06ebda0SMatthew Dillon 		}
823b06ebda0SMatthew Dillon 
824b06ebda0SMatthew Dillon 		/* Read and append character to mbuf */
825b06ebda0SMatthew Dillon 		bt3c_read_data(sc, c);
826b06ebda0SMatthew Dillon 		if (sc->m->m_pkthdr.len >= MCLBYTES) {
827b06ebda0SMatthew Dillon 			NG_BT3C_ERR(sc->dev, "Oversized frame\n");
828b06ebda0SMatthew Dillon 
829b06ebda0SMatthew Dillon 			NG_FREE_M(sc->m);
830b06ebda0SMatthew Dillon 			sc->state = NG_BT3C_W4_PKT_IND;
831b06ebda0SMatthew Dillon 			sc->want = 1;
832b06ebda0SMatthew Dillon 
833b06ebda0SMatthew Dillon 			break; /* XXX lost of sync */
834b06ebda0SMatthew Dillon 		}
835b06ebda0SMatthew Dillon 
836b06ebda0SMatthew Dillon 		mtod(sc->m, u_int8_t *)[sc->m->m_len ++] = (u_int8_t) c;
837b06ebda0SMatthew Dillon 		sc->m->m_pkthdr.len ++;
838b06ebda0SMatthew Dillon 
839b06ebda0SMatthew Dillon 		NG_BT3C_INFO(sc->dev,
840b06ebda0SMatthew Dillon "Got char %#x, want=%d, got=%d\n", c, sc->want, sc->m->m_pkthdr.len);
841b06ebda0SMatthew Dillon 
842b06ebda0SMatthew Dillon 		if (sc->m->m_pkthdr.len < sc->want)
843b06ebda0SMatthew Dillon 			continue; /* wait for more */
844b06ebda0SMatthew Dillon 
845b06ebda0SMatthew Dillon 		switch (sc->state) {
846b06ebda0SMatthew Dillon 		/* Got packet indicator */
847b06ebda0SMatthew Dillon 		case NG_BT3C_W4_PKT_IND:
848b06ebda0SMatthew Dillon 			NG_BT3C_INFO(sc->dev,
849b06ebda0SMatthew Dillon "Got packet indicator %#x\n", *mtod(sc->m, u_int8_t *));
850b06ebda0SMatthew Dillon 
851b06ebda0SMatthew Dillon 			sc->state = NG_BT3C_W4_PKT_HDR;
852b06ebda0SMatthew Dillon 
853b06ebda0SMatthew Dillon 			/*
854b06ebda0SMatthew Dillon 			 * Since packet indicator included in the packet
855b06ebda0SMatthew Dillon 			 * header just set sc->want to sizeof(packet header).
856b06ebda0SMatthew Dillon 			 */
857b06ebda0SMatthew Dillon 
858b06ebda0SMatthew Dillon 			switch (*mtod(sc->m, u_int8_t *)) {
859b06ebda0SMatthew Dillon 			case NG_HCI_ACL_DATA_PKT:
860b06ebda0SMatthew Dillon 				sc->want = sizeof(ng_hci_acldata_pkt_t);
861b06ebda0SMatthew Dillon 				break;
862b06ebda0SMatthew Dillon 
863b06ebda0SMatthew Dillon 			case NG_HCI_SCO_DATA_PKT:
864b06ebda0SMatthew Dillon 				sc->want = sizeof(ng_hci_scodata_pkt_t);
865b06ebda0SMatthew Dillon 				break;
866b06ebda0SMatthew Dillon 
867b06ebda0SMatthew Dillon 			case NG_HCI_EVENT_PKT:
868b06ebda0SMatthew Dillon 				sc->want = sizeof(ng_hci_event_pkt_t);
869b06ebda0SMatthew Dillon 				break;
870b06ebda0SMatthew Dillon 
871b06ebda0SMatthew Dillon 			default:
872b06ebda0SMatthew Dillon        	                	NG_BT3C_ERR(sc->dev,
873b06ebda0SMatthew Dillon "Ignoring unknown packet type=%#x\n", *mtod(sc->m, u_int8_t *));
874b06ebda0SMatthew Dillon 
875b06ebda0SMatthew Dillon 				NG_BT3C_STAT_IERROR(sc->stat);
876b06ebda0SMatthew Dillon 
877b06ebda0SMatthew Dillon 				NG_FREE_M(sc->m);
878b06ebda0SMatthew Dillon 				sc->state = NG_BT3C_W4_PKT_IND;
879b06ebda0SMatthew Dillon 				sc->want = 1;
880b06ebda0SMatthew Dillon 				break;
881b06ebda0SMatthew Dillon 			}
882b06ebda0SMatthew Dillon 			break;
883b06ebda0SMatthew Dillon 
884b06ebda0SMatthew Dillon 		/* Got packet header */
885b06ebda0SMatthew Dillon 		case NG_BT3C_W4_PKT_HDR:
886b06ebda0SMatthew Dillon 			sc->state = NG_BT3C_W4_PKT_DATA;
887b06ebda0SMatthew Dillon 
888b06ebda0SMatthew Dillon 			switch (*mtod(sc->m, u_int8_t *)) {
889b06ebda0SMatthew Dillon 			case NG_HCI_ACL_DATA_PKT:
890b06ebda0SMatthew Dillon 				c = le16toh(mtod(sc->m,
891b06ebda0SMatthew Dillon 					ng_hci_acldata_pkt_t *)->length);
892b06ebda0SMatthew Dillon 				break;
893b06ebda0SMatthew Dillon 
894b06ebda0SMatthew Dillon 			case NG_HCI_SCO_DATA_PKT:
895b06ebda0SMatthew Dillon 				c = mtod(sc->m, ng_hci_scodata_pkt_t*)->length;
896b06ebda0SMatthew Dillon 				break;
897b06ebda0SMatthew Dillon 
898b06ebda0SMatthew Dillon 			case NG_HCI_EVENT_PKT:
899b06ebda0SMatthew Dillon 				c = mtod(sc->m, ng_hci_event_pkt_t *)->length;
900b06ebda0SMatthew Dillon 				break;
901b06ebda0SMatthew Dillon 
902b06ebda0SMatthew Dillon 			default:
903b06ebda0SMatthew Dillon 				KASSERT(0,
904b06ebda0SMatthew Dillon ("Invalid packet type=%#x\n", *mtod(sc->m, u_int8_t *)));
905b06ebda0SMatthew Dillon 				break;
906b06ebda0SMatthew Dillon        	        	 }
907b06ebda0SMatthew Dillon 
908b06ebda0SMatthew Dillon 			NG_BT3C_INFO(sc->dev,
909b06ebda0SMatthew Dillon "Got packet header, packet type=%#x, got so far %d, payload size=%d\n",
910b06ebda0SMatthew Dillon 				*mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len,
911b06ebda0SMatthew Dillon 				c);
912b06ebda0SMatthew Dillon 
913b06ebda0SMatthew Dillon 			if (c > 0) {
914b06ebda0SMatthew Dillon 				sc->want += c;
915b06ebda0SMatthew Dillon 				break;
916b06ebda0SMatthew Dillon 			}
917b06ebda0SMatthew Dillon 
918b06ebda0SMatthew Dillon 			/* else FALLTHROUGH and deliver frame */
919b06ebda0SMatthew Dillon 			/* XXX is this true? should we deliver empty frame? */
920b06ebda0SMatthew Dillon 
921b06ebda0SMatthew Dillon 		/* Got packet data */
922b06ebda0SMatthew Dillon 		case NG_BT3C_W4_PKT_DATA:
923b06ebda0SMatthew Dillon 			NG_BT3C_INFO(sc->dev,
924b06ebda0SMatthew Dillon "Got full packet, packet type=%#x, packet size=%d\n",
925b06ebda0SMatthew Dillon 				*mtod(sc->m, u_int8_t *), sc->m->m_pkthdr.len);
926b06ebda0SMatthew Dillon 
927b06ebda0SMatthew Dillon 			NG_BT3C_STAT_BYTES_RECV(sc->stat, sc->m->m_pkthdr.len);
928b06ebda0SMatthew Dillon 			NG_BT3C_STAT_PCKTS_RECV(sc->stat);
929b06ebda0SMatthew Dillon 
930b50b93ffSSascha Wildner 			if (IF_QFULL(&sc->inq)) {
931b06ebda0SMatthew Dillon 				NG_BT3C_ERR(sc->dev,
932b06ebda0SMatthew Dillon "Incoming queue is full. Dropping mbuf, len=%d\n", sc->m->m_pkthdr.len);
933b06ebda0SMatthew Dillon 
934b50b93ffSSascha Wildner 				IF_DROP(&sc->inq);
935b06ebda0SMatthew Dillon 				NG_BT3C_STAT_IERROR(sc->stat);
936b06ebda0SMatthew Dillon 
937b06ebda0SMatthew Dillon 				NG_FREE_M(sc->m);
938b06ebda0SMatthew Dillon 			} else {
939b50b93ffSSascha Wildner 				IF_ENQUEUE(&sc->inq, sc->m);
940b06ebda0SMatthew Dillon 				sc->m = NULL;
941b06ebda0SMatthew Dillon 			}
942b06ebda0SMatthew Dillon 
943b06ebda0SMatthew Dillon 			sc->state = NG_BT3C_W4_PKT_IND;
944b06ebda0SMatthew Dillon 			sc->want = 1;
945b06ebda0SMatthew Dillon 			break;
946b06ebda0SMatthew Dillon 
947b06ebda0SMatthew Dillon 		default:
948b06ebda0SMatthew Dillon 			KASSERT(0,
949b06ebda0SMatthew Dillon ("Invalid node state=%d", sc->state));
950b06ebda0SMatthew Dillon 			break;
951b06ebda0SMatthew Dillon 		}
952b06ebda0SMatthew Dillon 	}
953b06ebda0SMatthew Dillon 
954b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x7006, 0x0000);
955b06ebda0SMatthew Dillon } /* bt3c_receive */
956b06ebda0SMatthew Dillon 
957b06ebda0SMatthew Dillon /*
958b06ebda0SMatthew Dillon  * SWI interrupt handler
959b06ebda0SMatthew Dillon  * Netgraph part is handled via ng_send_fn() to avoid race with hook
960b06ebda0SMatthew Dillon  * connection/disconnection
961b06ebda0SMatthew Dillon  */
962b06ebda0SMatthew Dillon 
963b06ebda0SMatthew Dillon static void
bt3c_swi_intr(void * context,void * dummy)964b50b93ffSSascha Wildner bt3c_swi_intr(void *context, void *dummy)
965b06ebda0SMatthew Dillon {
966b06ebda0SMatthew Dillon 	bt3c_softc_p	sc = (bt3c_softc_p) context;
967b06ebda0SMatthew Dillon 	u_int16_t	data;
968b06ebda0SMatthew Dillon 
969b06ebda0SMatthew Dillon 	/* Receive complete */
970b06ebda0SMatthew Dillon 	if (sc->status & 0x0001) {
971b06ebda0SMatthew Dillon 		sc->status &= ~0x0001; /* XXX is it safe? */
972b06ebda0SMatthew Dillon 
973b06ebda0SMatthew Dillon 		if (ng_send_fn(sc->node, NULL, &bt3c_forward, NULL, 0) != 0)
974b06ebda0SMatthew Dillon 			NG_BT3C_ALERT(sc->dev, "Could not forward frames!\n");
975b06ebda0SMatthew Dillon 	}
976b06ebda0SMatthew Dillon 
977b06ebda0SMatthew Dillon 	/* Send complete */
978b06ebda0SMatthew Dillon 	if (sc->status & 0x0002) {
979b06ebda0SMatthew Dillon 		sc->status &= ~0x0002; /* XXX is it safe */
980b06ebda0SMatthew Dillon 
981b06ebda0SMatthew Dillon 		if (ng_send_fn(sc->node, NULL, &bt3c_send, NULL, 1) != 0)
982b06ebda0SMatthew Dillon 			NG_BT3C_ALERT(sc->dev, "Could not send frames!\n");
983b06ebda0SMatthew Dillon 	}
984b06ebda0SMatthew Dillon 
985b06ebda0SMatthew Dillon 	/* Antenna position */
986b06ebda0SMatthew Dillon 	if (sc->status & 0x0020) {
987b06ebda0SMatthew Dillon 		sc->status &= ~0x0020; /* XXX is it safe */
988b06ebda0SMatthew Dillon 
989b06ebda0SMatthew Dillon 		bt3c_read(sc, 0x7002, data);
990b06ebda0SMatthew Dillon 		data &= 0x10;
991b06ebda0SMatthew Dillon 
992b06ebda0SMatthew Dillon 		if (data)
993b06ebda0SMatthew Dillon 			sc->flags |= BT3C_ANTENNA_OUT;
994b06ebda0SMatthew Dillon 		else
995b06ebda0SMatthew Dillon 			sc->flags &= ~BT3C_ANTENNA_OUT;
996b06ebda0SMatthew Dillon 
997b06ebda0SMatthew Dillon 		NG_BT3C_INFO(sc->dev, "Antenna %s\n", data? "OUT" : "IN");
998b06ebda0SMatthew Dillon 	}
999b06ebda0SMatthew Dillon } /* bt3c_swi_intr */
1000b06ebda0SMatthew Dillon 
1001b06ebda0SMatthew Dillon /*
1002b06ebda0SMatthew Dillon  * Send all incoming frames to the upper layer
1003b06ebda0SMatthew Dillon  */
1004b06ebda0SMatthew Dillon 
1005b06ebda0SMatthew Dillon static void
bt3c_forward(node_p node,hook_p hook,void * arg1,int arg2)1006b06ebda0SMatthew Dillon bt3c_forward(node_p node, hook_p hook, void *arg1, int arg2)
1007b06ebda0SMatthew Dillon {
1008b06ebda0SMatthew Dillon 	bt3c_softc_p	 sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
1009b06ebda0SMatthew Dillon 	struct mbuf	*m = NULL;
1010b06ebda0SMatthew Dillon 	int		 error;
1011b06ebda0SMatthew Dillon 
1012b06ebda0SMatthew Dillon 	if (sc == NULL)
1013b06ebda0SMatthew Dillon 		return;
1014b06ebda0SMatthew Dillon 
1015b06ebda0SMatthew Dillon 	if (sc->hook != NULL && NG_HOOK_IS_VALID(sc->hook)) {
1016b06ebda0SMatthew Dillon 		for (;;) {
1017b06ebda0SMatthew Dillon 			IF_DEQUEUE(&sc->inq, m);
1018b06ebda0SMatthew Dillon 			if (m == NULL)
1019b06ebda0SMatthew Dillon 				break;
1020b06ebda0SMatthew Dillon 
1021b06ebda0SMatthew Dillon 			NG_SEND_DATA_ONLY(error, sc->hook, m);
1022b06ebda0SMatthew Dillon 			if (error != 0)
1023b06ebda0SMatthew Dillon 				NG_BT3C_STAT_IERROR(sc->stat);
1024b06ebda0SMatthew Dillon 		}
1025b06ebda0SMatthew Dillon 	} else {
1026b06ebda0SMatthew Dillon 		for (;;) {
1027b50b93ffSSascha Wildner 			IF_DEQUEUE(&sc->inq, m);
1028b06ebda0SMatthew Dillon 			if (m == NULL)
1029b06ebda0SMatthew Dillon 				break;
1030b06ebda0SMatthew Dillon 
1031b06ebda0SMatthew Dillon 			NG_BT3C_STAT_IERROR(sc->stat);
1032b06ebda0SMatthew Dillon 			NG_FREE_M(m);
1033b06ebda0SMatthew Dillon 		}
1034b06ebda0SMatthew Dillon 	}
1035b06ebda0SMatthew Dillon } /* bt3c_forward */
1036b06ebda0SMatthew Dillon 
1037b06ebda0SMatthew Dillon /*
1038b06ebda0SMatthew Dillon  * Send more data to the device. Must be called when node is locked
1039b06ebda0SMatthew Dillon  */
1040b06ebda0SMatthew Dillon 
1041b06ebda0SMatthew Dillon static void
bt3c_send(node_p node,hook_p hook,void * arg,int completed)1042b06ebda0SMatthew Dillon bt3c_send(node_p node, hook_p hook, void *arg, int completed)
1043b06ebda0SMatthew Dillon {
1044b06ebda0SMatthew Dillon 	bt3c_softc_p	 sc = (bt3c_softc_p) NG_NODE_PRIVATE(node);
1045b06ebda0SMatthew Dillon 	struct mbuf	*m = NULL;
1046b06ebda0SMatthew Dillon 	int		 i, wrote, len;
1047b06ebda0SMatthew Dillon 
1048b06ebda0SMatthew Dillon 	if (sc == NULL)
1049b06ebda0SMatthew Dillon 		return;
1050b06ebda0SMatthew Dillon 
1051b06ebda0SMatthew Dillon 	if (completed)
1052b06ebda0SMatthew Dillon 		sc->flags &= ~BT3C_XMIT;
1053b06ebda0SMatthew Dillon 
1054b06ebda0SMatthew Dillon 	if (sc->flags & BT3C_XMIT)
1055b06ebda0SMatthew Dillon 		return;
1056b06ebda0SMatthew Dillon 
1057b06ebda0SMatthew Dillon 	bt3c_set_address(sc, 0x7080);
1058b06ebda0SMatthew Dillon 
1059b06ebda0SMatthew Dillon 	for (wrote = 0; wrote < BT3C_FIFO_SIZE; ) {
1060b06ebda0SMatthew Dillon 		IF_DEQUEUE(&sc->outq, m);
1061b06ebda0SMatthew Dillon 		if (m == NULL)
1062b06ebda0SMatthew Dillon 			break;
1063b06ebda0SMatthew Dillon 
1064b06ebda0SMatthew Dillon 		while (m != NULL) {
1065b06ebda0SMatthew Dillon 			len = min((BT3C_FIFO_SIZE - wrote), m->m_len);
1066b06ebda0SMatthew Dillon 
1067b06ebda0SMatthew Dillon 			for (i = 0; i < len; i++)
1068b06ebda0SMatthew Dillon 				bt3c_write_data(sc, m->m_data[i]);
1069b06ebda0SMatthew Dillon 
1070b06ebda0SMatthew Dillon 			wrote += len;
1071b06ebda0SMatthew Dillon 			m->m_data += len;
1072b06ebda0SMatthew Dillon 			m->m_len -= len;
1073b06ebda0SMatthew Dillon 
1074b06ebda0SMatthew Dillon 			if (m->m_len > 0)
1075b06ebda0SMatthew Dillon 				break;
1076b06ebda0SMatthew Dillon 
1077b06ebda0SMatthew Dillon 			m = m_free(m);
1078b06ebda0SMatthew Dillon 		}
1079b06ebda0SMatthew Dillon 
1080b06ebda0SMatthew Dillon 		if (m != NULL) {
1081b06ebda0SMatthew Dillon 			IF_PREPEND(&sc->outq, m);
1082b06ebda0SMatthew Dillon 			break;
1083b06ebda0SMatthew Dillon 		}
1084b06ebda0SMatthew Dillon 
1085b06ebda0SMatthew Dillon 		NG_BT3C_STAT_PCKTS_SENT(sc->stat);
1086b06ebda0SMatthew Dillon 	}
1087b06ebda0SMatthew Dillon 
1088b06ebda0SMatthew Dillon 	if (wrote > 0) {
1089b06ebda0SMatthew Dillon 		NG_BT3C_INFO(sc->dev, "Wrote %d bytes\n", wrote);
1090b06ebda0SMatthew Dillon 		NG_BT3C_STAT_BYTES_SENT(sc->stat, wrote);
1091b06ebda0SMatthew Dillon 
1092b06ebda0SMatthew Dillon 		bt3c_write(sc, 0x7005, wrote);
1093b06ebda0SMatthew Dillon 		sc->flags |= BT3C_XMIT;
1094b06ebda0SMatthew Dillon 	}
1095b06ebda0SMatthew Dillon } /* bt3c_send */
1096b06ebda0SMatthew Dillon 
1097b06ebda0SMatthew Dillon /*
1098b06ebda0SMatthew Dillon  * Download chip firmware
1099b06ebda0SMatthew Dillon  */
1100b06ebda0SMatthew Dillon 
1101b06ebda0SMatthew Dillon static void
bt3c_download_firmware(bt3c_softc_p sc,char const * firmware,int firmware_size)1102b06ebda0SMatthew Dillon bt3c_download_firmware(bt3c_softc_p sc, char const *firmware, int firmware_size)
1103b06ebda0SMatthew Dillon {
1104b06ebda0SMatthew Dillon 	ng_bt3c_firmware_block_ep const	*block = NULL;
1105b06ebda0SMatthew Dillon 	u_int16_t const			*data = NULL;
1106b06ebda0SMatthew Dillon 	int				 i, size;
1107b06ebda0SMatthew Dillon 	u_int8_t			 c;
1108b06ebda0SMatthew Dillon 
1109b06ebda0SMatthew Dillon 	/* Reset */
1110b06ebda0SMatthew Dillon 	device_printf(sc->dev, "Reseting the card...\n");
1111b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x8040, 0x0404);
1112b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x8040, 0x0400);
1113b06ebda0SMatthew Dillon 	DELAY(1);
1114b06ebda0SMatthew Dillon 
1115b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x8040, 0x0404);
1116b06ebda0SMatthew Dillon 	DELAY(17);
1117b06ebda0SMatthew Dillon 
1118b06ebda0SMatthew Dillon 	/* Download firmware */
1119b06ebda0SMatthew Dillon 	device_printf(sc->dev, "Starting firmware download process...\n");
1120b06ebda0SMatthew Dillon 
1121b06ebda0SMatthew Dillon 	for (size = 0; size < firmware_size; ) {
1122b06ebda0SMatthew Dillon 		block = (ng_bt3c_firmware_block_ep const *)(firmware + size);
1123b06ebda0SMatthew Dillon 		data = (u_int16_t const *)(block + 1);
1124b06ebda0SMatthew Dillon 
1125b06ebda0SMatthew Dillon 		if (bootverbose)
1126b06ebda0SMatthew Dillon 			device_printf(sc->dev, "Download firmware block, " \
1127b06ebda0SMatthew Dillon 				"address=%#08x, size=%d words, aligment=%d\n",
1128b06ebda0SMatthew Dillon 				block->block_address, block->block_size,
1129b06ebda0SMatthew Dillon 				block->block_alignment);
1130b06ebda0SMatthew Dillon 
1131b06ebda0SMatthew Dillon 		bt3c_set_address(sc, block->block_address);
1132b06ebda0SMatthew Dillon 		for (i = 0; i < block->block_size; i++)
1133b06ebda0SMatthew Dillon 			bt3c_write_data(sc, data[i]);
1134b06ebda0SMatthew Dillon 
1135b06ebda0SMatthew Dillon 		size += (sizeof(*block) + (block->block_size * 2) +
1136b06ebda0SMatthew Dillon 				block->block_alignment);
1137b06ebda0SMatthew Dillon 	}
1138b06ebda0SMatthew Dillon 
1139b06ebda0SMatthew Dillon 	DELAY(17);
1140b06ebda0SMatthew Dillon 	device_printf(sc->dev, "Firmware download process complete\n");
1141b06ebda0SMatthew Dillon 
1142b06ebda0SMatthew Dillon 	/* Boot */
1143b06ebda0SMatthew Dillon 	device_printf(sc->dev, "Starting the card...\n");
1144b06ebda0SMatthew Dillon 	bt3c_set_address(sc, 0x3000);
1145b06ebda0SMatthew Dillon 	bt3c_read_control(sc, c);
1146b06ebda0SMatthew Dillon 	bt3c_write_control(sc, (c | 0x40));
1147b06ebda0SMatthew Dillon 	DELAY(17);
1148b06ebda0SMatthew Dillon 
1149b06ebda0SMatthew Dillon 	/* Clear registers */
1150b06ebda0SMatthew Dillon 	device_printf(sc->dev, "Clearing card registers...\n");
1151b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x7006, 0x0000);
1152b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x7005, 0x0000);
1153b06ebda0SMatthew Dillon 	bt3c_write(sc, 0x7001, 0x0000);
1154b06ebda0SMatthew Dillon 	DELAY(1000);
1155b06ebda0SMatthew Dillon } /* bt3c_download_firmware */
1156b06ebda0SMatthew Dillon 
1157b06ebda0SMatthew Dillon /****************************************************************************
1158b06ebda0SMatthew Dillon  ****************************************************************************
1159b06ebda0SMatthew Dillon  **                           Driver module
1160b06ebda0SMatthew Dillon  ****************************************************************************
1161b06ebda0SMatthew Dillon  ****************************************************************************/
1162b06ebda0SMatthew Dillon 
1163b06ebda0SMatthew Dillon /*
1164b06ebda0SMatthew Dillon  * PC Card (PCMCIA) driver
1165b06ebda0SMatthew Dillon  */
1166b06ebda0SMatthew Dillon 
1167b06ebda0SMatthew Dillon static device_method_t	bt3c_pccard_methods[] = {
1168b06ebda0SMatthew Dillon 	/* Device interface */
1169b06ebda0SMatthew Dillon 	DEVMETHOD(device_probe,		bt3c_pccard_probe),
1170b06ebda0SMatthew Dillon 	DEVMETHOD(device_attach,	bt3c_pccard_attach),
1171b06ebda0SMatthew Dillon 	DEVMETHOD(device_detach,	bt3c_pccard_detach),
1172b06ebda0SMatthew Dillon 
1173d3c9c58eSSascha Wildner 	DEVMETHOD_END
1174b06ebda0SMatthew Dillon };
1175b06ebda0SMatthew Dillon 
1176b06ebda0SMatthew Dillon static driver_t		bt3c_pccard_driver = {
1177b06ebda0SMatthew Dillon 	NG_BT3C_NODE_TYPE,
1178b06ebda0SMatthew Dillon 	bt3c_pccard_methods,
1179b06ebda0SMatthew Dillon 	sizeof(bt3c_softc_t)
1180b06ebda0SMatthew Dillon };
1181b06ebda0SMatthew Dillon 
1182b06ebda0SMatthew Dillon static devclass_t	bt3c_devclass;
1183b06ebda0SMatthew Dillon 
1184b06ebda0SMatthew Dillon 
1185b06ebda0SMatthew Dillon /*
1186b06ebda0SMatthew Dillon  * Load/Unload the driver module
1187b06ebda0SMatthew Dillon  */
1188b06ebda0SMatthew Dillon 
1189b06ebda0SMatthew Dillon static int
bt3c_modevent(module_t mod,int event,void * data)1190b06ebda0SMatthew Dillon bt3c_modevent(module_t mod, int event, void *data)
1191b06ebda0SMatthew Dillon {
1192b06ebda0SMatthew Dillon 	int	error;
1193b06ebda0SMatthew Dillon 
1194b06ebda0SMatthew Dillon 	switch (event) {
1195b06ebda0SMatthew Dillon 	case MOD_LOAD:
1196b06ebda0SMatthew Dillon 		error = ng_newtype(&typestruct);
1197b06ebda0SMatthew Dillon 		if (error != 0)
1198a62226e4SSascha Wildner 			kprintf("%s: Could not register Netgraph node type, " \
1199b06ebda0SMatthew Dillon 				"error=%d\n", NG_BT3C_NODE_TYPE, error);
1200b06ebda0SMatthew Dillon 		break;
1201b06ebda0SMatthew Dillon 
1202b06ebda0SMatthew Dillon 	case MOD_UNLOAD:
1203b06ebda0SMatthew Dillon 		error = ng_rmtype(&typestruct);
1204b06ebda0SMatthew Dillon 		break;
1205b06ebda0SMatthew Dillon 
1206b06ebda0SMatthew Dillon 	default:
1207b06ebda0SMatthew Dillon 		error = EOPNOTSUPP;
1208b06ebda0SMatthew Dillon 		break;
1209b06ebda0SMatthew Dillon 	}
1210b06ebda0SMatthew Dillon 
1211b06ebda0SMatthew Dillon 	return (error);
1212b06ebda0SMatthew Dillon } /* bt3c_modevent */
1213b06ebda0SMatthew Dillon 
1214b50b93ffSSascha Wildner DRIVER_MODULE(bt3c, pccard, bt3c_pccard_driver, bt3c_devclass, bt3c_modevent,
1215b50b93ffSSascha Wildner     NULL);
1216b06ebda0SMatthew Dillon MODULE_VERSION(ng_bt3c, NG_BLUETOOTH_VERSION);
1217b06ebda0SMatthew Dillon MODULE_DEPEND(ng_bt3c, netgraph, NG_ABI_VERSION, NG_ABI_VERSION,NG_ABI_VERSION);
1218b06ebda0SMatthew Dillon 
1219