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