hci.c revision 189462
1/*
2 * hci.c
3 */
4
5/*-
6 * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
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 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD: head/lib/libbluetooth/hci.c 189462 2009-03-06 23:30:07Z emax $
31 */
32
33#include <bluetooth.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39static char * bt_dev2node (char const *devname, char *nodename, int nnlen);
40
41int
42bt_devinfo(struct bt_devinfo *di)
43{
44	union {
45		struct ng_btsocket_hci_raw_node_state		r0;
46		struct ng_btsocket_hci_raw_node_bdaddr		r1;
47		struct ng_btsocket_hci_raw_node_features	r2;
48		struct ng_btsocket_hci_raw_node_buffer		r3;
49		struct ng_btsocket_hci_raw_node_stat		r4;
50		struct ng_btsocket_hci_raw_node_link_policy_mask r5;
51		struct ng_btsocket_hci_raw_node_packet_mask	r6;
52		struct ng_btsocket_hci_raw_node_role_switch	r7;
53		struct ng_btsocket_hci_raw_node_debug		r8;
54	}						rp;
55	struct sockaddr_hci				ha;
56	int						s, rval;
57
58	if (di == NULL) {
59		errno = EINVAL;
60		return (-1);
61	}
62
63	memset(&ha, 0, sizeof(ha));
64	ha.hci_len = sizeof(ha);
65	ha.hci_family = AF_BLUETOOTH;
66
67	if (bt_aton(di->devname, &rp.r1.bdaddr)) {
68		if (!bt_devname(ha.hci_node, &rp.r1.bdaddr))
69			return (-1);
70	} else if (bt_dev2node(di->devname, ha.hci_node,
71					sizeof(ha.hci_node)) == NULL) {
72		errno = ENXIO;
73		return (-1);
74	}
75
76	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
77	if (s < 0)
78		return (-1);
79
80	rval = -1;
81
82	if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
83	    connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0)
84		goto bad;
85	strlcpy(di->devname, ha.hci_node, sizeof(di->devname));
86
87	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &rp.r0, sizeof(rp.r0)) < 0)
88		goto bad;
89	di->state = rp.r0.state;
90
91	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &rp.r1, sizeof(rp.r1)) < 0)
92		goto bad;
93	bdaddr_copy(&di->bdaddr, &rp.r1.bdaddr);
94
95	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &rp.r2, sizeof(rp.r2)) < 0)
96		goto bad;
97	memcpy(di->features, rp.r2.features, sizeof(di->features));
98
99	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &rp.r3, sizeof(rp.r3)) < 0)
100		goto bad;
101	di->cmd_free = rp.r3.buffer.cmd_free;
102	di->sco_size = rp.r3.buffer.sco_size;
103	di->sco_pkts = rp.r3.buffer.sco_pkts;
104	di->sco_free = rp.r3.buffer.sco_free;
105	di->acl_size = rp.r3.buffer.acl_size;
106	di->acl_pkts = rp.r3.buffer.acl_pkts;
107	di->acl_free = rp.r3.buffer.acl_free;
108
109	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &rp.r4, sizeof(rp.r4)) < 0)
110		goto bad;
111	di->cmd_sent = rp.r4.stat.cmd_sent;
112	di->evnt_recv = rp.r4.stat.evnt_recv;
113	di->acl_recv = rp.r4.stat.acl_recv;
114	di->acl_sent = rp.r4.stat.acl_sent;
115	di->sco_recv = rp.r4.stat.sco_recv;
116	di->sco_sent = rp.r4.stat.sco_sent;
117	di->bytes_recv = rp.r4.stat.bytes_recv;
118	di->bytes_sent = rp.r4.stat.bytes_sent;
119
120	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK,
121			&rp.r5, sizeof(rp.r5)) < 0)
122		goto bad;
123	di->link_policy_info = rp.r5.policy_mask;
124
125	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK,
126			&rp.r6, sizeof(rp.r6)) < 0)
127		goto bad;
128	di->packet_type_info = rp.r6.packet_mask;
129
130	 if (ioctl(s, SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH,
131			&rp.r7, sizeof(rp.r7)) < 0)
132		goto bad;
133	di->role_switch_info = rp.r7.role_switch;
134
135	if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &rp.r8, sizeof(rp.r8)) < 0)
136		goto bad;
137	di->debug = rp.r8.debug;
138
139	rval = 0;
140bad:
141	close(s);
142
143	return (rval);
144}
145
146int
147bt_devenum(bt_devenum_cb_t cb, void *arg)
148{
149	struct ng_btsocket_hci_raw_node_list_names	rp;
150	struct bt_devinfo				di;
151	struct sockaddr_hci				ha;
152	int						s, i, count;
153
154	rp.num_names = HCI_DEVMAX;
155	rp.names = (struct nodeinfo *) calloc(rp.num_names,
156						sizeof(struct nodeinfo));
157	if (rp.names == NULL) {
158		errno = ENOMEM;
159		return (-1);
160	}
161
162	memset(&ha, 0, sizeof(ha));
163	ha.hci_len = sizeof(ha);
164	ha.hci_family = AF_BLUETOOTH;
165	ha.hci_node[0] = 'x';
166
167	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
168	if (s < 0) {
169		free(rp.names);
170
171		return (-1);
172	}
173
174	if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
175	    connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
176	    ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &rp, sizeof(rp)) < 0) {
177		close(s);
178		free(rp.names);
179
180		return (-1);
181	}
182
183	for (count = 0, i = 0; i < rp.num_names; i ++) {
184		strlcpy(di.devname, rp.names[i].name, sizeof(di.devname));
185		if (bt_devinfo(&di) < 0)
186			continue;
187
188		count ++;
189
190		if (cb == NULL)
191			continue;
192
193		strlcpy(ha.hci_node, rp.names[i].name, sizeof(ha.hci_node));
194		if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
195		    connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0)
196			continue;
197
198		if ((*cb)(s, &di, arg) > 0)
199			break;
200	}
201
202	close (s);
203	free(rp.names);
204
205	return (count);
206}
207
208static char *
209bt_dev2node(char const *devname, char *nodename, int nnlen)
210{
211	static char const *	 bt_dev_prefix[] = {
212		"btccc",	/* 3Com Bluetooth PC-CARD */
213		"h4",		/* UART/serial Bluetooth devices */
214		"ubt",		/* Bluetooth USB devices */
215		NULL		/* should be last */
216	};
217
218	static char		_nodename[HCI_DEVNAME_SIZE];
219	char const		**p;
220	char			*ep;
221	int			plen, unit;
222
223	if (nodename == NULL) {
224		nodename = _nodename;
225		nnlen = HCI_DEVNAME_SIZE;
226	}
227
228	for (p = bt_dev_prefix; *p != NULL; p ++) {
229		plen = strlen(*p);
230		if (strncmp(devname, *p, plen) != 0)
231			continue;
232
233		unit = strtoul(devname + plen, &ep, 10);
234		if (*ep != '\0' &&
235		    strcmp(ep, "hci") != 0 &&
236		    strcmp(ep, "l2cap") != 0)
237			return (NULL);	/* can't make sense of device name */
238
239		snprintf(nodename, nnlen, "%s%uhci", *p, unit);
240
241		return (nodename);
242	}
243
244	return (NULL);
245}
246
247