1 /* 2 * client.c 3 */ 4 5 /*- 6 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 7 * 8 * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 9 * All rights reserved. 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 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $ 33 * $FreeBSD$ 34 */ 35 36 #include <sys/queue.h> 37 #include <assert.h> 38 #define L2CAP_SOCKET_CHECKED 39 #include <bluetooth.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <syslog.h> 46 #include <unistd.h> 47 #include <usbhid.h> 48 #include "bthid_config.h" 49 #include "bthidd.h" 50 51 static int32_t client_socket(bdaddr_p bdaddr, uint16_t psm); 52 53 /* 54 * Get next config entry and create outbound connection (if required) 55 * 56 * XXX Do only one device at a time. At least one of my devices (3COM 57 * Bluetooth PCCARD) rejects Create_Connection command if another 58 * Create_Connection command is still pending. Weird... 59 */ 60 61 static int32_t connect_in_progress = 0; 62 63 int32_t 64 client_rescan(bthid_server_p srv) 65 { 66 static hid_device_p d; 67 bthid_session_p s; 68 69 assert(srv != NULL); 70 71 if (connect_in_progress) 72 return (0); /* another connect is still pending */ 73 74 d = get_next_hid_device(d); 75 if (d == NULL) 76 return (0); /* XXX should not happen? empty config? */ 77 78 if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) 79 return (0); /* session already active */ 80 81 if (!d->new_device) { 82 if (d->reconnect_initiate) 83 return (0); /* device will initiate reconnect */ 84 } 85 86 syslog(LOG_NOTICE, "Opening outbound session for %s " \ 87 "(new_device=%d, reconnect_initiate=%d)", 88 bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate); 89 90 if ((s = session_open(srv, d)) == NULL) { 91 syslog(LOG_CRIT, "Could not create outbound session for %s", 92 bt_ntoa(&d->bdaddr, NULL)); 93 return (-1); 94 } 95 96 /* Open control channel */ 97 s->ctrl = client_socket(&s->bdaddr, d->control_psm); 98 if (s->ctrl < 0) { 99 syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", 100 bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 101 session_close(s); 102 return (-1); 103 } 104 105 s->state = W4CTRL; 106 107 FD_SET(s->ctrl, &srv->wfdset); 108 if (s->ctrl > srv->maxfd) 109 srv->maxfd = s->ctrl; 110 111 connect_in_progress = 1; 112 113 return (0); 114 } 115 116 /* 117 * Process connect on the socket 118 */ 119 120 int32_t 121 client_connect(bthid_server_p srv, int32_t fd) 122 { 123 bthid_session_p s; 124 hid_device_p d; 125 int32_t error; 126 socklen_t len; 127 128 assert(srv != NULL); 129 assert(fd >= 0); 130 131 s = session_by_fd(srv, fd); 132 assert(s != NULL); 133 134 d = get_hid_device(&s->bdaddr); 135 assert(d != NULL); 136 137 error = 0; 138 len = sizeof(error); 139 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 140 syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", 141 bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 142 session_close(s); 143 connect_in_progress = 0; 144 145 return (-1); 146 } 147 148 if (error != 0) { 149 syslog(LOG_ERR, "Could not connect to %s. %s (%d)", 150 bt_ntoa(&s->bdaddr, NULL), strerror(error), error); 151 session_close(s); 152 connect_in_progress = 0; 153 154 return (0); 155 } 156 157 switch (s->state) { 158 case W4CTRL: /* Control channel is open */ 159 assert(s->ctrl == fd); 160 assert(s->intr == -1); 161 162 /* Open interrupt channel */ 163 s->intr = client_socket(&s->bdaddr, d->interrupt_psm); 164 if (s->intr < 0) { 165 syslog(LOG_ERR, "Could not open interrupt channel " \ 166 "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 167 strerror(errno), errno); 168 session_close(s); 169 connect_in_progress = 0; 170 171 return (-1); 172 } 173 174 s->state = W4INTR; 175 176 FD_SET(s->intr, &srv->wfdset); 177 if (s->intr > srv->maxfd) 178 srv->maxfd = s->intr; 179 180 d->new_device = 0; /* reset new device flag */ 181 write_hids_file(); 182 break; 183 184 case W4INTR: /* Interrupt channel is open */ 185 assert(s->ctrl != -1); 186 assert(s->intr == fd); 187 188 s->state = OPEN; 189 connect_in_progress = 0; 190 191 /* Register session's vkbd descriptor (if any) for read */ 192 if (s->state == OPEN && d->keyboard) { 193 assert(s->vkbd != -1); 194 195 FD_SET(s->vkbd, &srv->rfdset); 196 if (s->vkbd > srv->maxfd) 197 srv->maxfd = s->vkbd; 198 } 199 break; 200 201 default: 202 assert(0); 203 break; 204 } 205 206 /* Move fd to from the write fd set into read fd set */ 207 FD_CLR(fd, &srv->wfdset); 208 FD_SET(fd, &srv->rfdset); 209 210 return (0); 211 } 212 213 /* 214 * Create bound non-blocking socket and initiate connect 215 */ 216 217 static int 218 client_socket(bdaddr_p bdaddr, uint16_t psm) 219 { 220 struct sockaddr_l2cap l2addr; 221 int32_t s, m; 222 223 s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); 224 if (s < 0) 225 return (-1); 226 227 m = fcntl(s, F_GETFL); 228 if (m < 0) { 229 close(s); 230 return (-1); 231 } 232 233 if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { 234 close(s); 235 return (-1); 236 } 237 238 l2addr.l2cap_len = sizeof(l2addr); 239 l2addr.l2cap_family = AF_BLUETOOTH; 240 memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr)); 241 l2addr.l2cap_psm = 0; 242 l2addr.l2cap_bdaddr_type = BDADDR_BREDR; 243 l2addr.l2cap_cid = 0; 244 245 if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { 246 close(s); 247 return (-1); 248 } 249 250 memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); 251 l2addr.l2cap_psm = htole16(psm); 252 253 if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && 254 errno != EINPROGRESS) { 255 close(s); 256 return (-1); 257 } 258 259 return (s); 260 } 261 262