1 1.24 rin /* $NetBSD: rfcomm_upper.c,v 1.24 2024/07/05 04:31:53 rin Exp $ */ 2 1.1 gdamore 3 1.1 gdamore /*- 4 1.1 gdamore * Copyright (c) 2006 Itronix Inc. 5 1.1 gdamore * All rights reserved. 6 1.1 gdamore * 7 1.1 gdamore * Written by Iain Hibbert for Itronix Inc. 8 1.1 gdamore * 9 1.1 gdamore * Redistribution and use in source and binary forms, with or without 10 1.1 gdamore * modification, are permitted provided that the following conditions 11 1.1 gdamore * are met: 12 1.1 gdamore * 1. Redistributions of source code must retain the above copyright 13 1.1 gdamore * notice, this list of conditions and the following disclaimer. 14 1.1 gdamore * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 gdamore * notice, this list of conditions and the following disclaimer in the 16 1.1 gdamore * documentation and/or other materials provided with the distribution. 17 1.1 gdamore * 3. The name of Itronix Inc. may not be used to endorse 18 1.1 gdamore * or promote products derived from this software without specific 19 1.1 gdamore * prior written permission. 20 1.1 gdamore * 21 1.1 gdamore * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 22 1.1 gdamore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 1.1 gdamore * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 1.1 gdamore * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 1.1 gdamore * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 1.1 gdamore * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 1.1 gdamore * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 1.1 gdamore * ON ANY THEORY OF LIABILITY, WHETHER IN 29 1.1 gdamore * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 1.1 gdamore * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 gdamore * POSSIBILITY OF SUCH DAMAGE. 32 1.1 gdamore */ 33 1.1 gdamore 34 1.1 gdamore #include <sys/cdefs.h> 35 1.24 rin __KERNEL_RCSID(0, "$NetBSD: rfcomm_upper.c,v 1.24 2024/07/05 04:31:53 rin Exp $"); 36 1.1 gdamore 37 1.1 gdamore #include <sys/param.h> 38 1.1 gdamore #include <sys/kernel.h> 39 1.1 gdamore #include <sys/mbuf.h> 40 1.14 rmind #include <sys/kmem.h> 41 1.11 plunky #include <sys/socketvar.h> 42 1.1 gdamore #include <sys/systm.h> 43 1.1 gdamore 44 1.1 gdamore #include <netbt/bluetooth.h> 45 1.1 gdamore #include <netbt/hci.h> 46 1.1 gdamore #include <netbt/l2cap.h> 47 1.1 gdamore #include <netbt/rfcomm.h> 48 1.1 gdamore 49 1.1 gdamore /**************************************************************************** 50 1.1 gdamore * 51 1.1 gdamore * RFCOMM DLC - Upper Protocol API 52 1.1 gdamore * 53 1.1 gdamore * Currently the only 'Port Emulation Entity' is the RFCOMM socket code 54 1.1 gdamore * but it is should be possible to provide a pseudo-device for a direct 55 1.1 gdamore * tty interface. 56 1.1 gdamore */ 57 1.1 gdamore 58 1.1 gdamore /* 59 1.15 rmind * rfcomm_attach_pcb(handle, proto, upper) 60 1.1 gdamore * 61 1.1 gdamore * attach a new RFCOMM DLC to handle, populate with reasonable defaults 62 1.1 gdamore */ 63 1.1 gdamore int 64 1.15 rmind rfcomm_attach_pcb(struct rfcomm_dlc **handle, 65 1.1 gdamore const struct btproto *proto, void *upper) 66 1.1 gdamore { 67 1.1 gdamore struct rfcomm_dlc *dlc; 68 1.1 gdamore 69 1.4 plunky KASSERT(handle != NULL); 70 1.4 plunky KASSERT(proto != NULL); 71 1.4 plunky KASSERT(upper != NULL); 72 1.1 gdamore 73 1.22 plunky dlc = kmem_intr_zalloc(sizeof(struct rfcomm_dlc), KM_NOSLEEP); 74 1.22 plunky if (dlc == NULL) 75 1.22 plunky return ENOMEM; 76 1.1 gdamore 77 1.1 gdamore dlc->rd_state = RFCOMM_DLC_CLOSED; 78 1.1 gdamore dlc->rd_mtu = rfcomm_mtu_default; 79 1.1 gdamore 80 1.1 gdamore dlc->rd_proto = proto; 81 1.1 gdamore dlc->rd_upper = upper; 82 1.1 gdamore 83 1.1 gdamore dlc->rd_laddr.bt_len = sizeof(struct sockaddr_bt); 84 1.1 gdamore dlc->rd_laddr.bt_family = AF_BLUETOOTH; 85 1.1 gdamore dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM; 86 1.1 gdamore 87 1.1 gdamore dlc->rd_raddr.bt_len = sizeof(struct sockaddr_bt); 88 1.1 gdamore dlc->rd_raddr.bt_family = AF_BLUETOOTH; 89 1.1 gdamore dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM; 90 1.1 gdamore 91 1.1 gdamore dlc->rd_lmodem = RFCOMM_MSC_RTC | RFCOMM_MSC_RTR | RFCOMM_MSC_DV; 92 1.1 gdamore 93 1.7 ad callout_init(&dlc->rd_timeout, 0); 94 1.1 gdamore callout_setfunc(&dlc->rd_timeout, rfcomm_dlc_timeout, dlc); 95 1.1 gdamore 96 1.1 gdamore *handle = dlc; 97 1.1 gdamore return 0; 98 1.1 gdamore } 99 1.1 gdamore 100 1.1 gdamore /* 101 1.17 rtr * rfcomm_bind_pcb(dlc, sockaddr) 102 1.1 gdamore * 103 1.1 gdamore * bind DLC to local address 104 1.1 gdamore */ 105 1.1 gdamore int 106 1.17 rtr rfcomm_bind_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) 107 1.1 gdamore { 108 1.1 gdamore 109 1.13 plunky if (dlc->rd_state != RFCOMM_DLC_CLOSED) 110 1.13 plunky return EINVAL; 111 1.13 plunky 112 1.1 gdamore memcpy(&dlc->rd_laddr, addr, sizeof(struct sockaddr_bt)); 113 1.1 gdamore return 0; 114 1.1 gdamore } 115 1.1 gdamore 116 1.1 gdamore /* 117 1.16 rtr * rfcomm_sockaddr_pcb(dlc, sockaddr) 118 1.1 gdamore * 119 1.1 gdamore * return local address 120 1.1 gdamore */ 121 1.1 gdamore int 122 1.16 rtr rfcomm_sockaddr_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) 123 1.1 gdamore { 124 1.1 gdamore 125 1.1 gdamore memcpy(addr, &dlc->rd_laddr, sizeof(struct sockaddr_bt)); 126 1.1 gdamore return 0; 127 1.1 gdamore } 128 1.1 gdamore 129 1.1 gdamore /* 130 1.18 rtr * rfcomm_connect_pcb(dlc, sockaddr) 131 1.1 gdamore * 132 1.1 gdamore * Initiate connection of RFCOMM DLC to remote address. 133 1.1 gdamore */ 134 1.1 gdamore int 135 1.18 rtr rfcomm_connect_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *dest) 136 1.1 gdamore { 137 1.1 gdamore struct rfcomm_session *rs; 138 1.1 gdamore int err = 0; 139 1.1 gdamore 140 1.1 gdamore if (dlc->rd_state != RFCOMM_DLC_CLOSED) 141 1.1 gdamore return EISCONN; 142 1.1 gdamore 143 1.1 gdamore memcpy(&dlc->rd_raddr, dest, sizeof(struct sockaddr_bt)); 144 1.1 gdamore 145 1.1 gdamore if (dlc->rd_raddr.bt_channel < RFCOMM_CHANNEL_MIN 146 1.1 gdamore || dlc->rd_raddr.bt_channel > RFCOMM_CHANNEL_MAX 147 1.1 gdamore || bdaddr_any(&dlc->rd_raddr.bt_bdaddr)) 148 1.1 gdamore return EDESTADDRREQ; 149 1.1 gdamore 150 1.1 gdamore if (dlc->rd_raddr.bt_psm == L2CAP_PSM_ANY) 151 1.1 gdamore dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM; 152 1.1 gdamore else if (dlc->rd_raddr.bt_psm != L2CAP_PSM_RFCOMM 153 1.1 gdamore && (dlc->rd_raddr.bt_psm < 0x1001 154 1.1 gdamore || L2CAP_PSM_INVALID(dlc->rd_raddr.bt_psm))) 155 1.1 gdamore return EINVAL; 156 1.1 gdamore 157 1.1 gdamore /* 158 1.1 gdamore * We are allowed only one RFCOMM session between any 2 Bluetooth 159 1.1 gdamore * devices, so see if there is a session already otherwise create 160 1.1 gdamore * one and set it connecting. 161 1.1 gdamore */ 162 1.1 gdamore rs = rfcomm_session_lookup(&dlc->rd_laddr, &dlc->rd_raddr); 163 1.1 gdamore if (rs == NULL) { 164 1.1 gdamore rs = rfcomm_session_alloc(&rfcomm_session_active, 165 1.1 gdamore &dlc->rd_laddr); 166 1.1 gdamore if (rs == NULL) 167 1.1 gdamore return ENOMEM; 168 1.1 gdamore 169 1.1 gdamore rs->rs_flags |= RFCOMM_SESSION_INITIATOR; 170 1.1 gdamore rs->rs_state = RFCOMM_SESSION_WAIT_CONNECT; 171 1.1 gdamore 172 1.18 rtr err = l2cap_connect_pcb(rs->rs_l2cap, &dlc->rd_raddr); 173 1.1 gdamore if (err) { 174 1.1 gdamore rfcomm_session_free(rs); 175 1.1 gdamore return err; 176 1.1 gdamore } 177 1.1 gdamore 178 1.1 gdamore /* 179 1.1 gdamore * This session will start up automatically when its 180 1.1 gdamore * L2CAP channel is connected. 181 1.1 gdamore */ 182 1.1 gdamore } 183 1.1 gdamore 184 1.1 gdamore /* construct DLC */ 185 1.1 gdamore dlc->rd_dlci = RFCOMM_MKDLCI(IS_INITIATOR(rs) ? 0:1, dest->bt_channel); 186 1.1 gdamore if (rfcomm_dlc_lookup(rs, dlc->rd_dlci)) 187 1.1 gdamore return EBUSY; 188 1.1 gdamore 189 1.16 rtr l2cap_sockaddr_pcb(rs->rs_l2cap, &dlc->rd_laddr); 190 1.1 gdamore 191 1.1 gdamore /* 192 1.1 gdamore * attach the DLC to the session and start it off 193 1.1 gdamore */ 194 1.1 gdamore dlc->rd_session = rs; 195 1.1 gdamore dlc->rd_state = RFCOMM_DLC_WAIT_SESSION; 196 1.1 gdamore LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next); 197 1.1 gdamore 198 1.1 gdamore if (rs->rs_state == RFCOMM_SESSION_OPEN) 199 1.1 gdamore err = rfcomm_dlc_connect(dlc); 200 1.1 gdamore 201 1.1 gdamore return err; 202 1.1 gdamore } 203 1.1 gdamore 204 1.1 gdamore /* 205 1.16 rtr * rfcomm_peeraddr_pcb(dlc, sockaddr) 206 1.1 gdamore * 207 1.1 gdamore * return remote address 208 1.1 gdamore */ 209 1.1 gdamore int 210 1.16 rtr rfcomm_peeraddr_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) 211 1.1 gdamore { 212 1.1 gdamore 213 1.1 gdamore memcpy(addr, &dlc->rd_raddr, sizeof(struct sockaddr_bt)); 214 1.1 gdamore return 0; 215 1.1 gdamore } 216 1.1 gdamore 217 1.1 gdamore /* 218 1.19 rtr * rfcomm_disconnect_pcb(dlc, linger) 219 1.1 gdamore * 220 1.1 gdamore * disconnect RFCOMM DLC 221 1.1 gdamore */ 222 1.1 gdamore int 223 1.19 rtr rfcomm_disconnect_pcb(struct rfcomm_dlc *dlc, int linger) 224 1.1 gdamore { 225 1.1 gdamore struct rfcomm_session *rs = dlc->rd_session; 226 1.1 gdamore int err = 0; 227 1.1 gdamore 228 1.1 gdamore KASSERT(dlc != NULL); 229 1.1 gdamore 230 1.1 gdamore switch (dlc->rd_state) { 231 1.1 gdamore case RFCOMM_DLC_CLOSED: 232 1.1 gdamore case RFCOMM_DLC_LISTEN: 233 1.1 gdamore return EINVAL; 234 1.1 gdamore 235 1.6 plunky case RFCOMM_DLC_WAIT_SEND_UA: 236 1.6 plunky err = rfcomm_session_send_frame(rs, 237 1.6 plunky RFCOMM_FRAME_DM, dlc->rd_dlci); 238 1.6 plunky 239 1.6 plunky /* fall through */ 240 1.1 gdamore case RFCOMM_DLC_WAIT_SESSION: 241 1.6 plunky case RFCOMM_DLC_WAIT_CONNECT: 242 1.6 plunky case RFCOMM_DLC_WAIT_SEND_SABM: 243 1.1 gdamore rfcomm_dlc_close(dlc, 0); 244 1.1 gdamore break; 245 1.1 gdamore 246 1.1 gdamore case RFCOMM_DLC_OPEN: 247 1.1 gdamore if (dlc->rd_txbuf != NULL && linger != 0) { 248 1.1 gdamore dlc->rd_flags |= RFCOMM_DLC_SHUTDOWN; 249 1.1 gdamore break; 250 1.1 gdamore } 251 1.1 gdamore 252 1.1 gdamore /* else fall through */ 253 1.6 plunky case RFCOMM_DLC_WAIT_RECV_UA: 254 1.1 gdamore dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT; 255 1.1 gdamore err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, 256 1.1 gdamore dlc->rd_dlci); 257 1.1 gdamore callout_schedule(&dlc->rd_timeout, rfcomm_ack_timeout * hz); 258 1.1 gdamore break; 259 1.1 gdamore 260 1.1 gdamore case RFCOMM_DLC_WAIT_DISCONNECT: 261 1.1 gdamore err = EALREADY; 262 1.1 gdamore break; 263 1.1 gdamore 264 1.1 gdamore default: 265 1.1 gdamore UNKNOWN(dlc->rd_state); 266 1.1 gdamore break; 267 1.1 gdamore } 268 1.1 gdamore 269 1.1 gdamore return err; 270 1.1 gdamore } 271 1.1 gdamore 272 1.1 gdamore /* 273 1.15 rmind * rfcomm_detach_pcb(handle) 274 1.1 gdamore * 275 1.1 gdamore * detach RFCOMM DLC from handle 276 1.1 gdamore */ 277 1.14 rmind void 278 1.15 rmind rfcomm_detach_pcb(struct rfcomm_dlc **handle) 279 1.1 gdamore { 280 1.1 gdamore struct rfcomm_dlc *dlc = *handle; 281 1.1 gdamore 282 1.1 gdamore if (dlc->rd_state != RFCOMM_DLC_CLOSED) 283 1.1 gdamore rfcomm_dlc_close(dlc, 0); 284 1.1 gdamore 285 1.24 rin m_freem(dlc->rd_txbuf); 286 1.24 rin dlc->rd_txbuf = NULL; 287 1.1 gdamore 288 1.1 gdamore dlc->rd_upper = NULL; 289 1.1 gdamore *handle = NULL; 290 1.1 gdamore 291 1.1 gdamore /* 292 1.1 gdamore * If callout is invoking we can't free the DLC so 293 1.1 gdamore * mark it and let the callout release it. 294 1.1 gdamore */ 295 1.1 gdamore if (callout_invoking(&dlc->rd_timeout)) 296 1.1 gdamore dlc->rd_flags |= RFCOMM_DLC_DETACH; 297 1.8 plunky else { 298 1.8 plunky callout_destroy(&dlc->rd_timeout); 299 1.22 plunky kmem_intr_free(dlc, sizeof(*dlc)); 300 1.8 plunky } 301 1.1 gdamore } 302 1.1 gdamore 303 1.1 gdamore /* 304 1.17 rtr * rfcomm_listen_pcb(dlc) 305 1.1 gdamore * 306 1.1 gdamore * This DLC is a listener. We look for an existing listening session 307 1.1 gdamore * with a matching address to attach to or else create a new one on 308 1.10 plunky * the listeners list. If the ANY channel is given, allocate the first 309 1.10 plunky * available for the session. 310 1.1 gdamore */ 311 1.1 gdamore int 312 1.17 rtr rfcomm_listen_pcb(struct rfcomm_dlc *dlc) 313 1.1 gdamore { 314 1.9 plunky struct rfcomm_session *rs; 315 1.10 plunky struct rfcomm_dlc *used; 316 1.1 gdamore struct sockaddr_bt addr; 317 1.10 plunky int err, channel; 318 1.1 gdamore 319 1.1 gdamore if (dlc->rd_state != RFCOMM_DLC_CLOSED) 320 1.1 gdamore return EISCONN; 321 1.1 gdamore 322 1.10 plunky if (dlc->rd_laddr.bt_channel != RFCOMM_CHANNEL_ANY 323 1.10 plunky && (dlc->rd_laddr.bt_channel < RFCOMM_CHANNEL_MIN 324 1.10 plunky || dlc->rd_laddr.bt_channel > RFCOMM_CHANNEL_MAX)) 325 1.1 gdamore return EADDRNOTAVAIL; 326 1.1 gdamore 327 1.1 gdamore if (dlc->rd_laddr.bt_psm == L2CAP_PSM_ANY) 328 1.1 gdamore dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM; 329 1.1 gdamore else if (dlc->rd_laddr.bt_psm != L2CAP_PSM_RFCOMM 330 1.1 gdamore && (dlc->rd_laddr.bt_psm < 0x1001 331 1.1 gdamore || L2CAP_PSM_INVALID(dlc->rd_laddr.bt_psm))) 332 1.1 gdamore return EADDRNOTAVAIL; 333 1.1 gdamore 334 1.1 gdamore LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) { 335 1.16 rtr l2cap_sockaddr_pcb(rs->rs_l2cap, &addr); 336 1.1 gdamore 337 1.1 gdamore if (addr.bt_psm != dlc->rd_laddr.bt_psm) 338 1.1 gdamore continue; 339 1.1 gdamore 340 1.1 gdamore if (bdaddr_same(&dlc->rd_laddr.bt_bdaddr, &addr.bt_bdaddr)) 341 1.9 plunky break; 342 1.1 gdamore } 343 1.1 gdamore 344 1.1 gdamore if (rs == NULL) { 345 1.1 gdamore rs = rfcomm_session_alloc(&rfcomm_session_listen, 346 1.1 gdamore &dlc->rd_laddr); 347 1.1 gdamore if (rs == NULL) 348 1.1 gdamore return ENOMEM; 349 1.1 gdamore 350 1.1 gdamore rs->rs_state = RFCOMM_SESSION_LISTEN; 351 1.1 gdamore 352 1.17 rtr err = l2cap_listen_pcb(rs->rs_l2cap); 353 1.1 gdamore if (err) { 354 1.1 gdamore rfcomm_session_free(rs); 355 1.1 gdamore return err; 356 1.1 gdamore } 357 1.1 gdamore } 358 1.1 gdamore 359 1.10 plunky if (dlc->rd_laddr.bt_channel == RFCOMM_CHANNEL_ANY) { 360 1.10 plunky channel = RFCOMM_CHANNEL_MIN; 361 1.10 plunky used = LIST_FIRST(&rs->rs_dlcs); 362 1.10 plunky 363 1.10 plunky while (used != NULL) { 364 1.10 plunky if (used->rd_laddr.bt_channel == channel) { 365 1.10 plunky if (channel++ == RFCOMM_CHANNEL_MAX) 366 1.10 plunky return EADDRNOTAVAIL; 367 1.10 plunky 368 1.10 plunky used = LIST_FIRST(&rs->rs_dlcs); 369 1.10 plunky } else { 370 1.10 plunky used = LIST_NEXT(used, rd_next); 371 1.10 plunky } 372 1.10 plunky } 373 1.10 plunky 374 1.10 plunky dlc->rd_laddr.bt_channel = channel; 375 1.10 plunky } 376 1.10 plunky 377 1.1 gdamore dlc->rd_session = rs; 378 1.1 gdamore dlc->rd_state = RFCOMM_DLC_LISTEN; 379 1.1 gdamore LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next); 380 1.1 gdamore 381 1.1 gdamore return 0; 382 1.1 gdamore } 383 1.1 gdamore 384 1.1 gdamore /* 385 1.20 rtr * rfcomm_send_pcb(dlc, mbuf) 386 1.1 gdamore * 387 1.1 gdamore * Output data on DLC. This is streamed data, so we add it 388 1.12 mbalmer * to our buffer and start the DLC, which will assemble 389 1.1 gdamore * packets and send them if it can. 390 1.1 gdamore */ 391 1.1 gdamore int 392 1.20 rtr rfcomm_send_pcb(struct rfcomm_dlc *dlc, struct mbuf *m) 393 1.1 gdamore { 394 1.1 gdamore 395 1.1 gdamore if (dlc->rd_txbuf != NULL) { 396 1.1 gdamore dlc->rd_txbuf->m_pkthdr.len += m->m_pkthdr.len; 397 1.1 gdamore m_cat(dlc->rd_txbuf, m); 398 1.1 gdamore } else { 399 1.1 gdamore dlc->rd_txbuf = m; 400 1.1 gdamore } 401 1.1 gdamore 402 1.1 gdamore if (dlc->rd_state == RFCOMM_DLC_OPEN) 403 1.1 gdamore rfcomm_dlc_start(dlc); 404 1.1 gdamore 405 1.1 gdamore return 0; 406 1.1 gdamore } 407 1.1 gdamore 408 1.1 gdamore /* 409 1.21 rtr * rfcomm_rcvd_pcb(dlc, space) 410 1.1 gdamore * 411 1.1 gdamore * Indicate space now available in receive buffer 412 1.1 gdamore * 413 1.1 gdamore * This should be used to give an initial value of the receive buffer 414 1.1 gdamore * size when the DLC is attached and anytime data is cleared from the 415 1.1 gdamore * buffer after that. 416 1.1 gdamore */ 417 1.1 gdamore int 418 1.21 rtr rfcomm_rcvd_pcb(struct rfcomm_dlc *dlc, size_t space) 419 1.1 gdamore { 420 1.1 gdamore 421 1.1 gdamore KASSERT(dlc != NULL); 422 1.1 gdamore 423 1.1 gdamore dlc->rd_rxsize = space; 424 1.1 gdamore 425 1.1 gdamore /* 426 1.1 gdamore * if we are using credit based flow control, we may 427 1.1 gdamore * want to send some credits.. 428 1.1 gdamore */ 429 1.1 gdamore if (dlc->rd_state == RFCOMM_DLC_OPEN 430 1.1 gdamore && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC)) 431 1.1 gdamore rfcomm_dlc_start(dlc); 432 1.1 gdamore 433 1.1 gdamore return 0; 434 1.1 gdamore } 435 1.1 gdamore 436 1.1 gdamore /* 437 1.11 plunky * rfcomm_setopt(dlc, sopt) 438 1.1 gdamore * 439 1.1 gdamore * set DLC options 440 1.1 gdamore */ 441 1.1 gdamore int 442 1.11 plunky rfcomm_setopt(struct rfcomm_dlc *dlc, const struct sockopt *sopt) 443 1.1 gdamore { 444 1.6 plunky int mode, err = 0; 445 1.3 plunky uint16_t mtu; 446 1.1 gdamore 447 1.11 plunky switch (sopt->sopt_name) { 448 1.1 gdamore case SO_RFCOMM_MTU: 449 1.11 plunky err = sockopt_get(sopt, &mtu, sizeof(mtu)); 450 1.11 plunky if (err) 451 1.11 plunky break; 452 1.11 plunky 453 1.3 plunky if (mtu < RFCOMM_MTU_MIN || mtu > RFCOMM_MTU_MAX) 454 1.1 gdamore err = EINVAL; 455 1.5 plunky else if (dlc->rd_state == RFCOMM_DLC_CLOSED) 456 1.5 plunky dlc->rd_mtu = mtu; 457 1.3 plunky else 458 1.5 plunky err = EBUSY; 459 1.3 plunky 460 1.1 gdamore break; 461 1.1 gdamore 462 1.6 plunky case SO_RFCOMM_LM: 463 1.11 plunky err = sockopt_getint(sopt, &mode); 464 1.11 plunky if (err) 465 1.11 plunky break; 466 1.11 plunky 467 1.6 plunky mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH); 468 1.6 plunky 469 1.6 plunky if (mode & RFCOMM_LM_SECURE) 470 1.6 plunky mode |= RFCOMM_LM_ENCRYPT; 471 1.6 plunky 472 1.6 plunky if (mode & RFCOMM_LM_ENCRYPT) 473 1.6 plunky mode |= RFCOMM_LM_AUTH; 474 1.6 plunky 475 1.6 plunky dlc->rd_mode = mode; 476 1.6 plunky 477 1.6 plunky if (dlc->rd_state == RFCOMM_DLC_OPEN) 478 1.6 plunky err = rfcomm_dlc_setmode(dlc); 479 1.6 plunky 480 1.6 plunky break; 481 1.6 plunky 482 1.1 gdamore default: 483 1.2 plunky err = ENOPROTOOPT; 484 1.1 gdamore break; 485 1.1 gdamore } 486 1.1 gdamore return err; 487 1.1 gdamore } 488 1.1 gdamore 489 1.1 gdamore /* 490 1.11 plunky * rfcomm_getopt(dlc, sopt) 491 1.1 gdamore * 492 1.1 gdamore * get DLC options 493 1.1 gdamore */ 494 1.1 gdamore int 495 1.11 plunky rfcomm_getopt(struct rfcomm_dlc *dlc, struct sockopt *sopt) 496 1.1 gdamore { 497 1.11 plunky struct rfcomm_fc_info fc; 498 1.1 gdamore 499 1.11 plunky switch (sopt->sopt_name) { 500 1.1 gdamore case SO_RFCOMM_MTU: 501 1.11 plunky return sockopt_set(sopt, &dlc->rd_mtu, sizeof(uint16_t)); 502 1.1 gdamore 503 1.1 gdamore case SO_RFCOMM_FC_INFO: 504 1.11 plunky memset(&fc, 0, sizeof(fc)); 505 1.11 plunky fc.lmodem = dlc->rd_lmodem; 506 1.11 plunky fc.rmodem = dlc->rd_rmodem; 507 1.23 riastrad fc.tx_cred = uimax(dlc->rd_txcred, 0xff); 508 1.23 riastrad fc.rx_cred = uimax(dlc->rd_rxcred, 0xff); 509 1.1 gdamore if (dlc->rd_session 510 1.1 gdamore && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC)) 511 1.11 plunky fc.cfc = 1; 512 1.1 gdamore 513 1.11 plunky return sockopt_set(sopt, &fc, sizeof(fc)); 514 1.1 gdamore 515 1.6 plunky case SO_RFCOMM_LM: 516 1.11 plunky return sockopt_setint(sopt, dlc->rd_mode); 517 1.6 plunky 518 1.1 gdamore default: 519 1.1 gdamore break; 520 1.1 gdamore } 521 1.1 gdamore 522 1.11 plunky return ENOPROTOOPT; 523 1.1 gdamore } 524