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