1 1.19 maya /* $NetBSD: l2cap_upper.c,v 1.19 2016/12/12 15:58:45 maya Exp $ */ 2 1.1 gdamore 3 1.1 gdamore /*- 4 1.1 gdamore * Copyright (c) 2005 Iain Hibbert. 5 1.1 gdamore * Copyright (c) 2006 Itronix Inc. 6 1.1 gdamore * All rights reserved. 7 1.1 gdamore * 8 1.1 gdamore * Redistribution and use in source and binary forms, with or without 9 1.1 gdamore * modification, are permitted provided that the following conditions 10 1.1 gdamore * are met: 11 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 12 1.1 gdamore * notice, this list of conditions and the following disclaimer. 13 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 gdamore * notice, this list of conditions and the following disclaimer in the 15 1.1 gdamore * documentation and/or other materials provided with the distribution. 16 1.1 gdamore * 3. The name of Itronix Inc. may not be used to endorse 17 1.1 gdamore * or promote products derived from this software without specific 18 1.1 gdamore * prior written permission. 19 1.1 gdamore * 20 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 21 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.1 gdamore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.1 gdamore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 24 1.1 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 1.1 gdamore * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 1.1 gdamore * ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.1 gdamore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.1 gdamore * POSSIBILITY OF SUCH DAMAGE. 31 1.1 gdamore */ 32 1.1 gdamore 33 1.1 gdamore #include <sys/cdefs.h> 34 1.19 maya __KERNEL_RCSID(0, "$NetBSD: l2cap_upper.c,v 1.19 2016/12/12 15:58:45 maya Exp $"); 35 1.1 gdamore 36 1.1 gdamore #include <sys/param.h> 37 1.1 gdamore #include <sys/kernel.h> 38 1.1 gdamore #include <sys/mbuf.h> 39 1.1 gdamore #include <sys/proc.h> 40 1.1 gdamore #include <sys/queue.h> 41 1.1 gdamore #include <sys/socket.h> 42 1.1 gdamore #include <sys/socketvar.h> 43 1.1 gdamore #include <sys/systm.h> 44 1.1 gdamore 45 1.1 gdamore #include <netbt/bluetooth.h> 46 1.1 gdamore #include <netbt/hci.h> 47 1.1 gdamore #include <netbt/l2cap.h> 48 1.1 gdamore 49 1.1 gdamore /******************************************************************************* 50 1.1 gdamore * 51 1.1 gdamore * L2CAP Channel - Upper Protocol API 52 1.1 gdamore */ 53 1.1 gdamore 54 1.1 gdamore /* 55 1.13 rmind * l2cap_attach_pcb(handle, btproto, upper) 56 1.1 gdamore * 57 1.1 gdamore * attach new l2cap_channel to handle, populate 58 1.8 msaitoh * with reasonable defaults 59 1.1 gdamore */ 60 1.1 gdamore int 61 1.13 rmind l2cap_attach_pcb(struct l2cap_channel **handle, 62 1.1 gdamore const struct btproto *proto, void *upper) 63 1.1 gdamore { 64 1.1 gdamore struct l2cap_channel *chan; 65 1.1 gdamore 66 1.5 plunky KASSERT(handle != NULL); 67 1.5 plunky KASSERT(proto != NULL); 68 1.5 plunky KASSERT(upper != NULL); 69 1.1 gdamore 70 1.1 gdamore chan = malloc(sizeof(struct l2cap_channel), M_BLUETOOTH, 71 1.1 gdamore M_NOWAIT | M_ZERO); 72 1.1 gdamore if (chan == NULL) 73 1.1 gdamore return ENOMEM; 74 1.1 gdamore 75 1.1 gdamore chan->lc_proto = proto; 76 1.1 gdamore chan->lc_upper = upper; 77 1.1 gdamore 78 1.1 gdamore chan->lc_state = L2CAP_CLOSED; 79 1.1 gdamore 80 1.1 gdamore chan->lc_lcid = L2CAP_NULL_CID; 81 1.1 gdamore chan->lc_rcid = L2CAP_NULL_CID; 82 1.1 gdamore 83 1.1 gdamore chan->lc_laddr.bt_len = sizeof(struct sockaddr_bt); 84 1.1 gdamore chan->lc_laddr.bt_family = AF_BLUETOOTH; 85 1.1 gdamore chan->lc_laddr.bt_psm = L2CAP_PSM_ANY; 86 1.1 gdamore 87 1.1 gdamore chan->lc_raddr.bt_len = sizeof(struct sockaddr_bt); 88 1.1 gdamore chan->lc_raddr.bt_family = AF_BLUETOOTH; 89 1.1 gdamore chan->lc_raddr.bt_psm = L2CAP_PSM_ANY; 90 1.1 gdamore 91 1.1 gdamore chan->lc_imtu = L2CAP_MTU_DEFAULT; 92 1.1 gdamore chan->lc_omtu = L2CAP_MTU_DEFAULT; 93 1.1 gdamore chan->lc_flush = L2CAP_FLUSH_TIMO_DEFAULT; 94 1.1 gdamore 95 1.1 gdamore memcpy(&chan->lc_iqos, &l2cap_default_qos, sizeof(l2cap_qos_t)); 96 1.1 gdamore memcpy(&chan->lc_oqos, &l2cap_default_qos, sizeof(l2cap_qos_t)); 97 1.1 gdamore 98 1.1 gdamore MBUFQ_INIT(&chan->lc_txq); 99 1.1 gdamore 100 1.1 gdamore *handle = chan; 101 1.1 gdamore return 0; 102 1.1 gdamore } 103 1.1 gdamore 104 1.1 gdamore /* 105 1.15 rtr * l2cap_bind_pcb(l2cap_channel, sockaddr) 106 1.1 gdamore * 107 1.1 gdamore * set local address of channel 108 1.1 gdamore */ 109 1.1 gdamore int 110 1.15 rtr l2cap_bind_pcb(struct l2cap_channel *chan, struct sockaddr_bt *addr) 111 1.1 gdamore { 112 1.1 gdamore 113 1.11 plunky if (chan->lc_lcid != L2CAP_NULL_CID) 114 1.11 plunky return EINVAL; 115 1.11 plunky 116 1.1 gdamore memcpy(&chan->lc_laddr, addr, sizeof(struct sockaddr_bt)); 117 1.1 gdamore return 0; 118 1.1 gdamore } 119 1.1 gdamore 120 1.1 gdamore /* 121 1.14 rtr * l2cap_sockaddr_pcb(l2cap_channel, sockaddr) 122 1.1 gdamore * 123 1.1 gdamore * get local address of channel 124 1.1 gdamore */ 125 1.1 gdamore int 126 1.14 rtr l2cap_sockaddr_pcb(struct l2cap_channel *chan, struct sockaddr_bt *addr) 127 1.1 gdamore { 128 1.1 gdamore 129 1.1 gdamore memcpy(addr, &chan->lc_laddr, sizeof(struct sockaddr_bt)); 130 1.1 gdamore return 0; 131 1.1 gdamore } 132 1.1 gdamore 133 1.1 gdamore /* 134 1.16 rtr * l2cap_connect_pcb(l2cap_channel, sockaddr) 135 1.1 gdamore * 136 1.1 gdamore * Initiate a connection to destination. This corresponds to 137 1.1 gdamore * "Open Channel Request" in the L2CAP specification and will 138 1.1 gdamore * result in one of the following: 139 1.1 gdamore * 140 1.1 gdamore * proto->connected(upper) 141 1.1 gdamore * proto->disconnected(upper, error) 142 1.1 gdamore * 143 1.1 gdamore * and, optionally 144 1.1 gdamore * proto->connecting(upper) 145 1.1 gdamore */ 146 1.1 gdamore int 147 1.16 rtr l2cap_connect_pcb(struct l2cap_channel *chan, struct sockaddr_bt *dest) 148 1.1 gdamore { 149 1.1 gdamore struct hci_unit *unit; 150 1.1 gdamore int err; 151 1.1 gdamore 152 1.1 gdamore memcpy(&chan->lc_raddr, dest, sizeof(struct sockaddr_bt)); 153 1.1 gdamore 154 1.1 gdamore if (L2CAP_PSM_INVALID(chan->lc_raddr.bt_psm)) 155 1.1 gdamore return EINVAL; 156 1.1 gdamore 157 1.1 gdamore if (bdaddr_any(&chan->lc_raddr.bt_bdaddr)) 158 1.1 gdamore return EDESTADDRREQ; 159 1.1 gdamore 160 1.1 gdamore /* set local address if it needs setting */ 161 1.1 gdamore if (bdaddr_any(&chan->lc_laddr.bt_bdaddr)) { 162 1.1 gdamore err = hci_route_lookup(&chan->lc_laddr.bt_bdaddr, 163 1.1 gdamore &chan->lc_raddr.bt_bdaddr); 164 1.1 gdamore if (err) 165 1.1 gdamore return err; 166 1.1 gdamore } 167 1.1 gdamore 168 1.1 gdamore unit = hci_unit_lookup(&chan->lc_laddr.bt_bdaddr); 169 1.1 gdamore if (unit == NULL) 170 1.1 gdamore return EHOSTUNREACH; 171 1.1 gdamore 172 1.1 gdamore /* attach to active list */ 173 1.1 gdamore err = l2cap_cid_alloc(chan); 174 1.1 gdamore if (err) 175 1.1 gdamore return err; 176 1.1 gdamore 177 1.1 gdamore /* open link to remote device */ 178 1.1 gdamore chan->lc_link = hci_acl_open(unit, &chan->lc_raddr.bt_bdaddr); 179 1.1 gdamore if (chan->lc_link == NULL) 180 1.1 gdamore return EHOSTUNREACH; 181 1.1 gdamore 182 1.7 plunky /* set the link mode */ 183 1.7 plunky err = l2cap_setmode(chan); 184 1.7 plunky if (err == EINPROGRESS) { 185 1.7 plunky chan->lc_state = L2CAP_WAIT_SEND_CONNECT_REQ; 186 1.7 plunky (*chan->lc_proto->connecting)(chan->lc_upper); 187 1.7 plunky return 0; 188 1.7 plunky } 189 1.7 plunky if (err) 190 1.7 plunky goto fail; 191 1.7 plunky 192 1.1 gdamore /* 193 1.7 plunky * We can queue a connect request now even though the link may 194 1.7 plunky * not yet be open; Our mode setting is assured, and the queue 195 1.7 plunky * will be started automatically at the right time. 196 1.1 gdamore */ 197 1.7 plunky chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP; 198 1.1 gdamore err = l2cap_send_connect_req(chan); 199 1.7 plunky if (err) 200 1.7 plunky goto fail; 201 1.1 gdamore 202 1.1 gdamore return 0; 203 1.7 plunky 204 1.7 plunky fail: 205 1.7 plunky chan->lc_state = L2CAP_CLOSED; 206 1.7 plunky hci_acl_close(chan->lc_link, err); 207 1.7 plunky chan->lc_link = NULL; 208 1.7 plunky return err; 209 1.1 gdamore } 210 1.1 gdamore 211 1.1 gdamore /* 212 1.14 rtr * l2cap_peeraddr_pcb(l2cap_channel, sockaddr) 213 1.1 gdamore * 214 1.1 gdamore * get remote address of channel 215 1.1 gdamore */ 216 1.1 gdamore int 217 1.14 rtr l2cap_peeraddr_pcb(struct l2cap_channel *chan, struct sockaddr_bt *addr) 218 1.1 gdamore { 219 1.1 gdamore 220 1.1 gdamore memcpy(addr, &chan->lc_raddr, sizeof(struct sockaddr_bt)); 221 1.1 gdamore return 0; 222 1.1 gdamore } 223 1.1 gdamore 224 1.1 gdamore /* 225 1.17 rtr * l2cap_disconnect_pcb(l2cap_channel, linger) 226 1.1 gdamore * 227 1.1 gdamore * Initiate L2CAP disconnection. This corresponds to 228 1.1 gdamore * "Close Channel Request" in the L2CAP specification 229 1.1 gdamore * and will result in a call to 230 1.1 gdamore * 231 1.1 gdamore * proto->disconnected(upper, error) 232 1.1 gdamore * 233 1.1 gdamore * when the disconnection is complete. If linger is set, 234 1.1 gdamore * the call will not be made until data has flushed from 235 1.1 gdamore * the queue. 236 1.1 gdamore */ 237 1.1 gdamore int 238 1.17 rtr l2cap_disconnect_pcb(struct l2cap_channel *chan, int linger) 239 1.1 gdamore { 240 1.1 gdamore int err = 0; 241 1.1 gdamore 242 1.3 plunky if (chan->lc_state == L2CAP_CLOSED 243 1.3 plunky || chan->lc_state == L2CAP_WAIT_DISCONNECT) 244 1.1 gdamore return EINVAL; 245 1.1 gdamore 246 1.1 gdamore chan->lc_flags |= L2CAP_SHUTDOWN; 247 1.1 gdamore 248 1.1 gdamore /* 249 1.1 gdamore * no need to do anything unless the queue is empty or 250 1.1 gdamore * we are not lingering.. 251 1.1 gdamore */ 252 1.1 gdamore if ((MBUFQ_FIRST(&chan->lc_txq) == NULL && chan->lc_pending == 0) 253 1.1 gdamore || linger == 0) { 254 1.1 gdamore chan->lc_state = L2CAP_WAIT_DISCONNECT; 255 1.1 gdamore err = l2cap_send_disconnect_req(chan); 256 1.1 gdamore if (err) 257 1.1 gdamore l2cap_close(chan, err); 258 1.1 gdamore } 259 1.1 gdamore return err; 260 1.1 gdamore } 261 1.1 gdamore 262 1.1 gdamore /* 263 1.13 rmind * l2cap_detach_pcb(handle) 264 1.1 gdamore * 265 1.1 gdamore * Detach l2cap channel from handle & close it down 266 1.1 gdamore */ 267 1.12 rmind void 268 1.13 rmind l2cap_detach_pcb(struct l2cap_channel **handle) 269 1.1 gdamore { 270 1.1 gdamore struct l2cap_channel *chan; 271 1.1 gdamore 272 1.1 gdamore chan = *handle; 273 1.1 gdamore *handle = NULL; 274 1.1 gdamore 275 1.1 gdamore if (chan->lc_state != L2CAP_CLOSED) 276 1.1 gdamore l2cap_close(chan, 0); 277 1.1 gdamore 278 1.1 gdamore if (chan->lc_lcid != L2CAP_NULL_CID) { 279 1.1 gdamore LIST_REMOVE(chan, lc_ncid); 280 1.1 gdamore chan->lc_lcid = L2CAP_NULL_CID; 281 1.1 gdamore } 282 1.1 gdamore 283 1.1 gdamore MBUFQ_DRAIN(&chan->lc_txq); 284 1.1 gdamore 285 1.1 gdamore /* 286 1.1 gdamore * Could implement some kind of delayed expunge to make sure that the 287 1.1 gdamore * CID is really dead before it becomes available for reuse? 288 1.1 gdamore */ 289 1.1 gdamore 290 1.1 gdamore free(chan, M_BLUETOOTH); 291 1.1 gdamore } 292 1.1 gdamore 293 1.1 gdamore /* 294 1.15 rtr * l2cap_listen_pcb(l2cap_channel) 295 1.1 gdamore * 296 1.1 gdamore * Use this channel as a listening post (until detached). This will 297 1.1 gdamore * result in calls to: 298 1.1 gdamore * 299 1.1 gdamore * proto->newconn(upper, laddr, raddr) 300 1.1 gdamore * 301 1.10 plunky * for incoming connections matching the psm and local address of 302 1.10 plunky * the channel. NULL address is permitted and matches any device. 303 1.10 plunky * If L2CAP_PSM_ANY is bound the next higher unused value from the 304 1.10 plunky * dynamic range (above 0x1001) will be selected. 305 1.1 gdamore * 306 1.1 gdamore * The upper layer should create and return a new channel. 307 1.1 gdamore * 308 1.1 gdamore * You cannot use this channel for anything else subsequent to this call 309 1.1 gdamore */ 310 1.1 gdamore int 311 1.15 rtr l2cap_listen_pcb(struct l2cap_channel *chan) 312 1.1 gdamore { 313 1.1 gdamore struct l2cap_channel *used, *prev = NULL; 314 1.10 plunky uint32_t psm; 315 1.1 gdamore 316 1.1 gdamore if (chan->lc_lcid != L2CAP_NULL_CID) 317 1.1 gdamore return EINVAL; 318 1.1 gdamore 319 1.10 plunky /* 320 1.10 plunky * This is simplistic but its not really worth spending a 321 1.10 plunky * lot of time looking for an unused PSM.. 322 1.10 plunky */ 323 1.10 plunky if (chan->lc_laddr.bt_psm == L2CAP_PSM_ANY) { 324 1.10 plunky psm = 0x1001; 325 1.10 plunky used = LIST_FIRST(&l2cap_listen_list); 326 1.10 plunky 327 1.10 plunky if (used != NULL && used->lc_laddr.bt_psm >= psm) { 328 1.10 plunky psm = used->lc_laddr.bt_psm + 0x0002; 329 1.10 plunky if ((psm & 0x0100) != 0) 330 1.10 plunky psm += 0x0100; 331 1.10 plunky 332 1.10 plunky if (psm > UINT16_MAX) 333 1.10 plunky return EADDRNOTAVAIL; 334 1.10 plunky } 335 1.10 plunky 336 1.10 plunky chan->lc_laddr.bt_psm = psm; 337 1.10 plunky } else if (L2CAP_PSM_INVALID(chan->lc_laddr.bt_psm)) 338 1.10 plunky return EINVAL; 339 1.1 gdamore 340 1.1 gdamore /* 341 1.1 gdamore * This CID is irrelevant, as the channel is not stored on the active 342 1.1 gdamore * list and the socket code does not allow operations on listening 343 1.1 gdamore * sockets, but we set it so the detach code knows to LIST_REMOVE the 344 1.1 gdamore * channel. 345 1.1 gdamore */ 346 1.1 gdamore chan->lc_lcid = L2CAP_SIGNAL_CID; 347 1.1 gdamore 348 1.1 gdamore /* 349 1.1 gdamore * The list of listening channels is stored in an order such that new 350 1.1 gdamore * listeners dont usurp current listeners, but that specific listening 351 1.1 gdamore * takes precedence over promiscuous, and the connect request code can 352 1.1 gdamore * easily use the first matching entry. 353 1.1 gdamore */ 354 1.1 gdamore LIST_FOREACH(used, &l2cap_listen_list, lc_ncid) { 355 1.1 gdamore if (used->lc_laddr.bt_psm < chan->lc_laddr.bt_psm) 356 1.1 gdamore break; 357 1.1 gdamore 358 1.1 gdamore if (used->lc_laddr.bt_psm == chan->lc_laddr.bt_psm 359 1.1 gdamore && bdaddr_any(&used->lc_laddr.bt_bdaddr) 360 1.1 gdamore && !bdaddr_any(&chan->lc_laddr.bt_bdaddr)) 361 1.1 gdamore break; 362 1.1 gdamore 363 1.1 gdamore prev = used; 364 1.1 gdamore } 365 1.1 gdamore 366 1.1 gdamore if (prev == NULL) 367 1.1 gdamore LIST_INSERT_HEAD(&l2cap_listen_list, chan, lc_ncid); 368 1.1 gdamore else 369 1.1 gdamore LIST_INSERT_AFTER(prev, chan, lc_ncid); 370 1.1 gdamore 371 1.1 gdamore return 0; 372 1.1 gdamore } 373 1.1 gdamore 374 1.1 gdamore /* 375 1.18 rtr * l2cap_send_pcb(l2cap_channel, mbuf) 376 1.1 gdamore * 377 1.1 gdamore * Output SDU on channel described by channel. This corresponds 378 1.1 gdamore * to "Send Data Request" in the L2CAP specification. The upper 379 1.1 gdamore * layer will be notified when SDU's have completed sending by a 380 1.1 gdamore * call to: 381 1.1 gdamore * 382 1.1 gdamore * proto->complete(upper, n) 383 1.1 gdamore * 384 1.1 gdamore * (currently n == 1) 385 1.1 gdamore * 386 1.1 gdamore * Note: I'm not sure how this will work out, but I think that 387 1.1 gdamore * if outgoing Retransmission Mode or Flow Control Mode is 388 1.1 gdamore * negotiated then this call will not be made until the SDU has 389 1.19 maya * been acknowledged by the peer L2CAP entity. For 'Best Effort' 390 1.1 gdamore * it will be made when the packet has cleared the controller 391 1.1 gdamore * buffers. 392 1.1 gdamore * 393 1.1 gdamore * We only support Basic mode so far, so encapsulate with a 394 1.1 gdamore * B-Frame header and start sending if we are not already 395 1.1 gdamore */ 396 1.1 gdamore int 397 1.18 rtr l2cap_send_pcb(struct l2cap_channel *chan, struct mbuf *m) 398 1.1 gdamore { 399 1.1 gdamore l2cap_hdr_t *hdr; 400 1.1 gdamore int plen; 401 1.1 gdamore 402 1.1 gdamore if (chan->lc_state == L2CAP_CLOSED) { 403 1.1 gdamore m_freem(m); 404 1.1 gdamore return ENOTCONN; 405 1.1 gdamore } 406 1.1 gdamore 407 1.1 gdamore plen = m->m_pkthdr.len; 408 1.1 gdamore 409 1.1 gdamore DPRINTFN(5, "send %d bytes on CID #%d (pending = %d)\n", 410 1.1 gdamore plen, chan->lc_lcid, chan->lc_pending); 411 1.1 gdamore 412 1.1 gdamore /* Encapsulate with B-Frame */ 413 1.1 gdamore M_PREPEND(m, sizeof(l2cap_hdr_t), M_DONTWAIT); 414 1.1 gdamore if (m == NULL) 415 1.1 gdamore return ENOMEM; 416 1.1 gdamore 417 1.1 gdamore hdr = mtod(m, l2cap_hdr_t *); 418 1.1 gdamore hdr->length = htole16(plen); 419 1.1 gdamore hdr->dcid = htole16(chan->lc_rcid); 420 1.1 gdamore 421 1.1 gdamore /* Queue it on our list */ 422 1.1 gdamore MBUFQ_ENQUEUE(&chan->lc_txq, m); 423 1.1 gdamore 424 1.1 gdamore /* If we are not sending, then start doing so */ 425 1.1 gdamore if (chan->lc_pending == 0) 426 1.1 gdamore return l2cap_start(chan); 427 1.1 gdamore 428 1.1 gdamore return 0; 429 1.1 gdamore } 430 1.1 gdamore 431 1.1 gdamore /* 432 1.9 plunky * l2cap_setopt(l2cap_channel, sopt) 433 1.1 gdamore * 434 1.1 gdamore * Apply configuration options to channel. This corresponds to 435 1.1 gdamore * "Configure Channel Request" in the L2CAP specification. 436 1.7 plunky * 437 1.7 plunky * for SO_L2CAP_LM, the settings will take effect when the 438 1.7 plunky * channel is established. If the channel is already open, 439 1.7 plunky * a call to 440 1.7 plunky * proto->linkmode(upper, new) 441 1.7 plunky * 442 1.7 plunky * will be made when the change is complete. 443 1.1 gdamore */ 444 1.1 gdamore int 445 1.9 plunky l2cap_setopt(struct l2cap_channel *chan, const struct sockopt *sopt) 446 1.1 gdamore { 447 1.7 plunky int mode, err = 0; 448 1.6 plunky uint16_t mtu; 449 1.1 gdamore 450 1.9 plunky switch (sopt->sopt_name) { 451 1.1 gdamore case SO_L2CAP_IMTU: /* set Incoming MTU */ 452 1.9 plunky err = sockopt_get(sopt, &mtu, sizeof(mtu)); 453 1.9 plunky if (err) 454 1.9 plunky break; 455 1.9 plunky 456 1.6 plunky if (mtu < L2CAP_MTU_MINIMUM) 457 1.1 gdamore err = EINVAL; 458 1.6 plunky else if (chan->lc_state == L2CAP_CLOSED) 459 1.6 plunky chan->lc_imtu = mtu; 460 1.6 plunky else 461 1.6 plunky err = EBUSY; 462 1.1 gdamore 463 1.1 gdamore break; 464 1.1 gdamore 465 1.7 plunky case SO_L2CAP_LM: /* set link mode */ 466 1.9 plunky err = sockopt_getint(sopt, &mode); 467 1.9 plunky if (err) 468 1.9 plunky break; 469 1.9 plunky 470 1.7 plunky mode &= (L2CAP_LM_SECURE | L2CAP_LM_ENCRYPT | L2CAP_LM_AUTH); 471 1.7 plunky 472 1.7 plunky if (mode & L2CAP_LM_SECURE) 473 1.7 plunky mode |= L2CAP_LM_ENCRYPT; 474 1.7 plunky 475 1.7 plunky if (mode & L2CAP_LM_ENCRYPT) 476 1.7 plunky mode |= L2CAP_LM_AUTH; 477 1.7 plunky 478 1.7 plunky chan->lc_mode = mode; 479 1.7 plunky 480 1.7 plunky if (chan->lc_state == L2CAP_OPEN) 481 1.7 plunky err = l2cap_setmode(chan); 482 1.7 plunky 483 1.7 plunky break; 484 1.7 plunky 485 1.1 gdamore case SO_L2CAP_OQOS: /* set Outgoing QoS flow spec */ 486 1.1 gdamore case SO_L2CAP_FLUSH: /* set Outgoing Flush Timeout */ 487 1.1 gdamore default: 488 1.2 plunky err = ENOPROTOOPT; 489 1.1 gdamore break; 490 1.1 gdamore } 491 1.1 gdamore 492 1.1 gdamore return err; 493 1.1 gdamore } 494 1.1 gdamore 495 1.7 plunky /* 496 1.9 plunky * l2cap_getopt(l2cap_channel, sopt) 497 1.7 plunky * 498 1.7 plunky * Return configuration parameters. 499 1.7 plunky */ 500 1.1 gdamore int 501 1.9 plunky l2cap_getopt(struct l2cap_channel *chan, struct sockopt *sopt) 502 1.1 gdamore { 503 1.1 gdamore 504 1.9 plunky switch (sopt->sopt_name) { 505 1.1 gdamore case SO_L2CAP_IMTU: /* get Incoming MTU */ 506 1.9 plunky return sockopt_set(sopt, &chan->lc_imtu, sizeof(uint16_t)); 507 1.1 gdamore 508 1.1 gdamore case SO_L2CAP_OMTU: /* get Outgoing MTU */ 509 1.9 plunky return sockopt_set(sopt, &chan->lc_omtu, sizeof(uint16_t)); 510 1.1 gdamore 511 1.1 gdamore case SO_L2CAP_IQOS: /* get Incoming QoS flow spec */ 512 1.9 plunky return sockopt_set(sopt, &chan->lc_iqos, sizeof(l2cap_qos_t)); 513 1.1 gdamore 514 1.1 gdamore case SO_L2CAP_OQOS: /* get Outgoing QoS flow spec */ 515 1.9 plunky return sockopt_set(sopt, &chan->lc_oqos, sizeof(l2cap_qos_t)); 516 1.1 gdamore 517 1.1 gdamore case SO_L2CAP_FLUSH: /* get Flush Timeout */ 518 1.9 plunky return sockopt_set(sopt, &chan->lc_flush, sizeof(uint16_t)); 519 1.1 gdamore 520 1.7 plunky case SO_L2CAP_LM: /* get link mode */ 521 1.9 plunky return sockopt_setint(sopt, chan->lc_mode); 522 1.7 plunky 523 1.1 gdamore default: 524 1.1 gdamore break; 525 1.1 gdamore } 526 1.1 gdamore 527 1.9 plunky return ENOPROTOOPT; 528 1.1 gdamore } 529