xref: /dragonfly/sys/netbt/sco_upper.c (revision f746689a)
1 /* $DragonFly: src/sys/netbt/sco_upper.c,v 1.2 2008/03/18 13:41:42 hasso Exp $ */
2 /* $OpenBSD: sco_upper.c,v 1.2 2007/10/01 16:39:30 krw Exp $ */
3 /* $NetBSD: sco_upper.c,v 1.6 2007/03/30 20:47:03 plunky Exp $ */
4 
5 /*-
6  * Copyright (c) 2006 Itronix Inc.
7  * All rights reserved.
8  *
9  * Written by Iain Hibbert for Itronix Inc.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. The name of Itronix Inc. may not be used to endorse
20  *    or promote products derived from this software without specific
21  *    prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
27  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  * ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 
38 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/mbuf.h>
41 #include <sys/proc.h>
42 #include <sys/systm.h>
43 #include <sys/endian.h>
44 #include <sys/bus.h>
45 
46 #include <netbt/bluetooth.h>
47 #include <netbt/hci.h>
48 #include <netbt/sco.h>
49 
50 /****************************************************************************
51  *
52  *	SCO - Upper Protocol API
53  */
54 
55 struct sco_pcb_list sco_pcb = LIST_HEAD_INITIALIZER(sco_pcb);
56 
57 /*
58  * sco_attach(handle, proto, upper)
59  *
60  *	Attach a new instance of SCO pcb to handle
61  */
62 int
63 sco_attach(struct sco_pcb **handle,
64 		const struct btproto *proto, void *upper)
65 {
66 	struct sco_pcb *pcb;
67 
68 	KKASSERT(handle != NULL);
69 	KKASSERT(proto != NULL);
70 	KKASSERT(upper != NULL);
71 
72 	pcb = kmalloc(sizeof(*pcb), M_BLUETOOTH, M_NOWAIT | M_ZERO);
73 	if (pcb == NULL)
74 		return ENOMEM;
75 
76 	pcb->sp_proto = proto;
77 	pcb->sp_upper = upper;
78 
79 	LIST_INSERT_HEAD(&sco_pcb, pcb, sp_next);
80 
81 	*handle = pcb;
82 	return 0;
83 }
84 
85 /*
86  * sco_bind(pcb, sockaddr)
87  *
88  *	Bind SCO pcb to local address
89  */
90 int
91 sco_bind(struct sco_pcb *pcb, struct sockaddr_bt *addr)
92 {
93 
94 	bdaddr_copy(&pcb->sp_laddr, &addr->bt_bdaddr);
95 	return 0;
96 }
97 
98 /*
99  * sco_sockaddr(pcb, sockaddr)
100  *
101  *	Copy local address of PCB to sockaddr
102  */
103 int
104 sco_sockaddr(struct sco_pcb *pcb, struct sockaddr_bt *addr)
105 {
106 
107 	memset(addr, 0, sizeof(struct sockaddr_bt));
108 	addr->bt_len = sizeof(struct sockaddr_bt);
109 	addr->bt_family = AF_BLUETOOTH;
110 	bdaddr_copy(&addr->bt_bdaddr, &pcb->sp_laddr);
111 	return 0;
112 }
113 
114 /*
115  * sco_connect(pcb, sockaddr)
116  *
117  *	Initiate a SCO connection to the destination address.
118  */
119 int
120 sco_connect(struct sco_pcb *pcb, struct sockaddr_bt *dest)
121 {
122 	hci_add_sco_con_cp cp;
123 	struct hci_unit *unit;
124 	struct hci_link *acl, *sco;
125 	int err;
126 
127 	if (pcb->sp_flags & SP_LISTENING)
128 		return EINVAL;
129 
130 	bdaddr_copy(&pcb->sp_raddr, &dest->bt_bdaddr);
131 
132 	if (bdaddr_any(&pcb->sp_raddr))
133 		return EDESTADDRREQ;
134 
135 	if (bdaddr_any(&pcb->sp_laddr)) {
136 		err = hci_route_lookup(&pcb->sp_laddr, &pcb->sp_raddr);
137 		if (err)
138 			return err;
139 	}
140 
141 	unit = hci_unit_lookup(&pcb->sp_laddr);
142 	if (unit == NULL)
143 		return ENETDOWN;
144 
145 	/*
146 	 * We must have an already open ACL connection before we open the SCO
147 	 * connection, and since SCO connections dont happen on their own we
148 	 * will not open one, the application wanting this should have opened
149 	 * it previously.
150 	 */
151 	acl = hci_link_lookup_bdaddr(unit, &pcb->sp_raddr, HCI_LINK_ACL);
152 	if (acl == NULL || acl->hl_state != HCI_LINK_OPEN)
153 		return EHOSTUNREACH;
154 
155 	sco = hci_link_alloc(unit);
156 	if (sco == NULL)
157 		return ENOMEM;
158 
159 	sco->hl_type = HCI_LINK_SCO;
160 	bdaddr_copy(&sco->hl_bdaddr, &pcb->sp_raddr);
161 
162 	sco->hl_link = hci_acl_open(unit, &pcb->sp_raddr);
163 	KKASSERT(sco->hl_link == acl);
164 
165 	cp.con_handle = htole16(acl->hl_handle);
166 	cp.pkt_type = htole16(0x00e0);		/* HV1, HV2, HV3 */
167 	err = hci_send_cmd(unit, HCI_CMD_ADD_SCO_CON, &cp, sizeof(cp));
168 	if (err) {
169 		hci_link_free(sco, err);
170 		return err;
171 	}
172 
173 	sco->hl_sco = pcb;
174 	pcb->sp_link = sco;
175 
176 	pcb->sp_mtu = unit->hci_max_sco_size;
177 	return 0;
178 }
179 
180 /*
181  * sco_peeraddr(pcb, sockaddr)
182  *
183  *	Copy remote address of SCO pcb to sockaddr
184  */
185 int
186 sco_peeraddr(struct sco_pcb *pcb, struct sockaddr_bt *addr)
187 {
188 
189 	memset(addr, 0, sizeof(struct sockaddr_bt));
190 	addr->bt_len = sizeof(struct sockaddr_bt);
191 	addr->bt_family = AF_BLUETOOTH;
192 	bdaddr_copy(&addr->bt_bdaddr, &pcb->sp_raddr);
193 	return 0;
194 }
195 
196 /*
197  * sco_disconnect(pcb, linger)
198  *
199  *	Initiate disconnection of connected SCO pcb
200  */
201 int
202 sco_disconnect(struct sco_pcb *pcb, int linger)
203 {
204 	hci_discon_cp cp;
205 	struct hci_link *sco;
206 	int err;
207 
208 	sco = pcb->sp_link;
209 	if (sco == NULL)
210 		return EINVAL;
211 
212 	cp.con_handle = htole16(sco->hl_handle);
213 	cp.reason = 0x13;	/* "Remote User Terminated Connection" */
214 
215 	err = hci_send_cmd(sco->hl_unit, HCI_CMD_DISCONNECT, &cp, sizeof(cp));
216 	if (err || linger == 0) {
217 		sco->hl_sco = NULL;
218 		pcb->sp_link = NULL;
219 		hci_link_free(sco, err);
220 	}
221 
222 	return err;
223 }
224 
225 /*
226  * sco_detach(handle)
227  *
228  *	Detach SCO pcb from handle and clear up
229  */
230 int
231 sco_detach(struct sco_pcb **handle)
232 {
233 	struct sco_pcb *pcb;
234 
235 	KKASSERT(handle != NULL);
236 	pcb = *handle;
237 	*handle = NULL;
238 
239 	if (pcb == NULL)
240 		return EINVAL;
241 
242 	if (pcb->sp_link != NULL) {
243 		sco_disconnect(pcb, 0);
244 		pcb->sp_link = NULL;
245 	}
246 
247 	LIST_REMOVE(pcb, sp_next);
248 	kfree(pcb, M_BLUETOOTH);
249 	return 0;
250 }
251 
252 /*
253  * sco_listen(pcb)
254  *
255  *	Mark pcb as a listener.
256  */
257 int
258 sco_listen(struct sco_pcb *pcb)
259 {
260 
261 	if (pcb->sp_link != NULL)
262 		return EINVAL;
263 
264 	pcb->sp_flags |= SP_LISTENING;
265 	return 0;
266 }
267 
268 /*
269  * sco_send(pcb, mbuf)
270  *
271  *	Send data on SCO pcb.
272  *
273  * Gross hackage, we just output the packet directly onto the unit queue.
274  * This will work fine for one channel per unit, but for more channels it
275  * really needs fixing. We set the context so that when the packet is sent,
276  * we can drop a record from the socket buffer.
277  */
278 int
279 sco_send(struct sco_pcb *pcb, struct mbuf *m)
280 {
281 	hci_scodata_hdr_t *hdr;
282 	int plen;
283 
284 	if (pcb->sp_link == NULL) {
285 		m_freem(m);
286 		return EINVAL;
287 	}
288 
289 	plen = m->m_pkthdr.len;
290 	DPRINTFN(10, "%d bytes\n", plen);
291 
292 	/*
293 	 * This is a temporary limitation, as USB devices cannot
294 	 * handle SCO packet sizes that are not an integer number
295 	 * of Isochronous frames. See ubt(4)
296 	 */
297 	if (plen != pcb->sp_mtu) {
298 		m_freem(m);
299 		return EMSGSIZE;
300 	}
301 
302 	M_PREPEND(m, sizeof(hci_scodata_hdr_t), MB_DONTWAIT);
303 	if (m == NULL)
304 		return ENOMEM;
305 
306 	hdr = mtod(m, hci_scodata_hdr_t *);
307 	hdr->type = HCI_SCO_DATA_PKT;
308 	hdr->con_handle = htole16(pcb->sp_link->hl_handle);
309 	hdr->length = plen;
310 
311 	pcb->sp_pending++;
312 	M_SETCTX(m, pcb->sp_link);
313 	hci_output_sco(pcb->sp_link->hl_unit, m);
314 
315 	return 0;
316 }
317 
318 /*
319  * sco_setopt(pcb, option, addr)
320  *
321  *	Set SCO pcb options
322  */
323 int
324 sco_setopt(struct sco_pcb *pcb, int opt, void *addr)
325 {
326 	int err = 0;
327 
328 	switch (opt) {
329 	default:
330 		err = ENOPROTOOPT;
331 		break;
332 	}
333 
334 	return err;
335 }
336 
337 /*
338  * sco_getopt(pcb, option, addr)
339  *
340  *	Get SCO pcb options
341  */
342 int
343 sco_getopt(struct sco_pcb *pcb, int opt, void *addr)
344 {
345 
346 	switch (opt) {
347 	case SO_SCO_MTU:
348 		*(uint16_t *)addr = pcb->sp_mtu;
349 		return sizeof(uint16_t);
350 
351 	case SO_SCO_HANDLE:
352 		if (pcb->sp_link) {
353 			*(uint16_t *)addr = pcb->sp_link->hl_handle;
354 			return sizeof(uint16_t);
355 		}
356 		break;
357 
358 	default:
359 		break;
360 	}
361 	return 0;
362 }
363