xref: /freebsd/lib/libbluetooth/hci.c (revision c0020399a650364d0134f79f3fa319f84064372d)
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$
31  */
32 
33 #include <bluetooth.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 static char * bt_dev2node (char const *devname, char *nodename, int nnlen);
40 
41 int
42 bt_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;
140 bad:
141 	close(s);
142 
143 	return (rval);
144 }
145 
146 int
147 bt_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 
208 static char *
209 bt_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