1/*	$NetBSD$	*/
2
3/*-
4 * Copyright (c) 2006 Itronix Inc.
5 * All rights reserved.
6 *
7 * Written by Iain Hibbert for Itronix Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of Itronix Inc. may not be used to endorse
18 *    or promote products derived from this software without specific
19 *    prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__KERNEL_RCSID(0, "$NetBSD$");
36
37#include <sys/param.h>
38#include <sys/conf.h>
39#include <sys/device.h>
40#include <sys/fcntl.h>
41#include <sys/kernel.h>
42#include <sys/queue.h>
43#include <sys/malloc.h>
44#include <sys/mbuf.h>
45#include <sys/proc.h>
46#include <sys/systm.h>
47
48#include <prop/proplib.h>
49
50#include <netbt/bluetooth.h>
51
52#include <dev/bluetooth/btdev.h>
53
54#include "ioconf.h"
55
56/*****************************************************************************
57 *
58 *	Bluetooth Device Hub
59 */
60
61/* autoconf(9) glue */
62static int	bthub_match(device_t, cfdata_t, void *);
63static void	bthub_attach(device_t, device_t, void *);
64static int	bthub_detach(device_t, int);
65
66CFATTACH_DECL_NEW(bthub, 0,
67    bthub_match, bthub_attach, bthub_detach, NULL);
68
69/* control file */
70dev_type_ioctl(bthubioctl);
71
72const struct cdevsw bthub_cdevsw = {
73	nullopen, nullclose, noread, nowrite, bthubioctl,
74	nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
75};
76
77/* bthub functions */
78static int	bthub_print(void *, const char *);
79static int	bthub_pioctl(dev_t, unsigned long, prop_dictionary_t, int, struct lwp *);
80
81/*****************************************************************************
82 *
83 *	bthub autoconf(9) routines
84 *
85 *	A Hub is attached to each Bluetooth Controller as it is enabled
86 */
87
88static int
89bthub_match(device_t self, cfdata_t cfdata, void *arg)
90{
91
92	return 1;
93}
94
95static void
96bthub_attach(device_t parent, device_t self, void *aux)
97{
98	bdaddr_t *addr = aux;
99	prop_dictionary_t dict;
100	prop_object_t obj;
101
102	dict = device_properties(self);
103	obj = prop_data_create_data(addr, sizeof(*addr));
104	prop_dictionary_set(dict, BTDEVladdr, obj);
105	prop_object_release(obj);
106
107	aprint_verbose(" %s %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
108			BTDEVladdr,
109			addr->b[5], addr->b[4], addr->b[3],
110			addr->b[2], addr->b[1], addr->b[0]);
111
112	aprint_normal("\n");
113
114	pmf_device_register(self, NULL, NULL);
115}
116
117static int
118bthub_detach(device_t self, int flags)
119{
120
121	pmf_device_deregister(self);
122
123	return config_detach_children(self, flags);
124}
125
126/*****************************************************************************
127 *
128 *	bthub access functions to control device
129 */
130
131int
132bthubioctl(dev_t devno, unsigned long cmd, void *data, int flag, struct lwp *l)
133{
134	prop_dictionary_t dict;
135	int err;
136
137	switch(cmd) {
138	case BTDEV_ATTACH:
139	case BTDEV_DETACH:
140		/* load dictionary */
141		err = prop_dictionary_copyin_ioctl(data, cmd, &dict);
142		if (err == 0) {
143			err = bthub_pioctl(devno, cmd, dict, flag, l);
144			prop_object_release(dict);
145		}
146		break;
147
148	default:
149		err = EPASSTHROUGH;
150		break;
151	}
152
153	return err;
154}
155
156static int
157bthub_pioctl(dev_t devno, unsigned long cmd, prop_dictionary_t dict,
158    int flag, struct lwp *l)
159{
160	prop_data_t laddr, raddr;
161	prop_string_t service;
162	prop_dictionary_t prop;
163	prop_object_t obj;
164	device_t dev, self;
165	deviter_t di;
166	int unit;
167
168	/* validate local address */
169	laddr = prop_dictionary_get(dict, BTDEVladdr);
170	if (prop_data_size(laddr) != sizeof(bdaddr_t))
171		return EINVAL;
172
173	/* locate the relevant bthub */
174	for (unit = 0 ; ; unit++) {
175		if (unit == bthub_cd.cd_ndevs)
176			return ENXIO;
177
178		self = device_lookup(&bthub_cd, unit);
179		if (self == NULL)
180			continue;
181
182		prop = device_properties(self);
183		obj = prop_dictionary_get(prop, BTDEVladdr);
184		if (prop_data_equals(laddr, obj))
185			break;
186	}
187
188	/* validate remote address */
189	raddr = prop_dictionary_get(dict, BTDEVraddr);
190	if (prop_data_size(raddr) != sizeof(bdaddr_t)
191	    || bdaddr_any(prop_data_data_nocopy(raddr)))
192		return EINVAL;
193
194	/* validate service name */
195	service = prop_dictionary_get(dict, BTDEVservice);
196	if (prop_object_type(service) != PROP_TYPE_STRING)
197		return EINVAL;
198
199	/* locate matching child device, if any */
200	deviter_init(&di, 0);
201	while ((dev = deviter_next(&di)) != NULL) {
202		if (device_parent(dev) != self)
203			continue;
204
205		prop = device_properties(dev);
206
207		obj = prop_dictionary_get(prop, BTDEVraddr);
208		if (!prop_object_equals(raddr, obj))
209			continue;
210
211		obj = prop_dictionary_get(prop, BTDEVservice);
212		if (!prop_object_equals(service, obj))
213			continue;
214
215		break;
216	}
217	deviter_release(&di);
218
219	switch (cmd) {
220	case BTDEV_ATTACH:	/* attach BTDEV */
221		if (dev != NULL)
222			return EADDRINUSE;
223
224		dev = config_found(self, dict, bthub_print);
225		if (dev == NULL)
226			return ENXIO;
227
228		prop = device_properties(dev);
229		prop_dictionary_set(prop, BTDEVladdr, laddr);
230		prop_dictionary_set(prop, BTDEVraddr, raddr);
231		prop_dictionary_set(prop, BTDEVservice, service);
232		break;
233
234	case BTDEV_DETACH:	/* detach BTDEV */
235		if (dev == NULL)
236			return ENXIO;
237
238		config_detach(dev, DETACH_FORCE);
239		break;
240	}
241
242	return 0;
243}
244
245static int
246bthub_print(void *aux, const char *pnp)
247{
248	prop_dictionary_t dict = aux;
249	prop_object_t obj;
250	const bdaddr_t *raddr;
251
252	if (pnp != NULL) {
253		obj = prop_dictionary_get(dict, BTDEVtype);
254		aprint_normal("%s: %s '%s',", pnp, BTDEVtype,
255					prop_string_cstring_nocopy(obj));
256	}
257
258	obj = prop_dictionary_get(dict, BTDEVraddr);
259	raddr = prop_data_data_nocopy(obj);
260
261	aprint_verbose(" %s %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
262			BTDEVraddr,
263			raddr->b[5], raddr->b[4], raddr->b[3],
264			raddr->b[2], raddr->b[1], raddr->b[0]);
265
266	return UNCONF;
267}
268