1 /* 2 * client.c 3 */ 4 5 /*- 6 * SPDX-License-Identifier: BSD-2-Clause 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 */ 34 35 #include <sys/queue.h> 36 #include <assert.h> 37 #define L2CAP_SOCKET_CHECKED 38 #include <bluetooth.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <syslog.h> 45 #include <unistd.h> 46 #include <usbhid.h> 47 #include "bthid_config.h" 48 #include "bthidd.h" 49 50 static int32_t client_socket(bdaddr_p bdaddr, uint16_t psm); 51 52 /* 53 * Get next config entry and create outbound connection (if required) 54 * 55 * XXX Do only one device at a time. At least one of my devices (3COM 56 * Bluetooth PCCARD) rejects Create_Connection command if another 57 * Create_Connection command is still pending. Weird... 58 */ 59 60 static int32_t connect_in_progress = 0; 61 62 int32_t 63 client_rescan(bthid_server_p srv) 64 { 65 static hid_device_p d; 66 bthid_session_p s; 67 68 assert(srv != NULL); 69 70 if (connect_in_progress) 71 return (0); /* another connect is still pending */ 72 73 d = get_next_hid_device(d); 74 if (d == NULL) 75 return (0); /* XXX should not happen? empty config? */ 76 77 if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) 78 return (0); /* session already active */ 79 80 if (!d->new_device) { 81 if (d->reconnect_initiate) 82 return (0); /* device will initiate reconnect */ 83 } 84 85 syslog(LOG_NOTICE, "Opening outbound session for %s " \ 86 "(new_device=%d, reconnect_initiate=%d)", 87 bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate); 88 89 if ((s = session_open(srv, d)) == NULL) { 90 syslog(LOG_CRIT, "Could not create outbound session for %s", 91 bt_ntoa(&d->bdaddr, NULL)); 92 return (-1); 93 } 94 95 /* Open control channel */ 96 s->ctrl = client_socket(&s->bdaddr, d->control_psm); 97 if (s->ctrl < 0) { 98 syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", 99 bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 100 session_close(s); 101 return (-1); 102 } 103 104 s->state = W4CTRL; 105 106 FD_SET(s->ctrl, &srv->wfdset); 107 if (s->ctrl > srv->maxfd) 108 srv->maxfd = s->ctrl; 109 110 connect_in_progress = 1; 111 112 return (0); 113 } 114 115 /* 116 * Process connect on the socket 117 */ 118 119 int32_t 120 client_connect(bthid_server_p srv, int32_t fd) 121 { 122 bthid_session_p s; 123 hid_device_p d; 124 int32_t error; 125 socklen_t len; 126 127 assert(srv != NULL); 128 assert(fd >= 0); 129 130 s = session_by_fd(srv, fd); 131 assert(s != NULL); 132 133 d = get_hid_device(&s->bdaddr); 134 assert(d != NULL); 135 136 error = 0; 137 len = sizeof(error); 138 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 139 syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", 140 bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 141 session_close(s); 142 connect_in_progress = 0; 143 144 return (-1); 145 } 146 147 if (error != 0) { 148 syslog(LOG_ERR, "Could not connect to %s. %s (%d)", 149 bt_ntoa(&s->bdaddr, NULL), strerror(error), error); 150 session_close(s); 151 connect_in_progress = 0; 152 153 return (0); 154 } 155 156 switch (s->state) { 157 case W4CTRL: /* Control channel is open */ 158 assert(s->ctrl == fd); 159 assert(s->intr == -1); 160 161 /* Open interrupt channel */ 162 s->intr = client_socket(&s->bdaddr, d->interrupt_psm); 163 if (s->intr < 0) { 164 syslog(LOG_ERR, "Could not open interrupt channel " \ 165 "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 166 strerror(errno), errno); 167 session_close(s); 168 connect_in_progress = 0; 169 170 return (-1); 171 } 172 173 s->state = W4INTR; 174 175 FD_SET(s->intr, &srv->wfdset); 176 if (s->intr > srv->maxfd) 177 srv->maxfd = s->intr; 178 179 d->new_device = 0; /* reset new device flag */ 180 write_hids_file(); 181 break; 182 183 case W4INTR: /* Interrupt channel is open */ 184 assert(s->ctrl != -1); 185 assert(s->intr == fd); 186 187 s->state = OPEN; 188 connect_in_progress = 0; 189 190 /* Create kbd/mouse after both channels are established */ 191 if (session_run(s) < 0) { 192 session_close(s); 193 return (-1); 194 } 195 break; 196 197 default: 198 assert(0); 199 break; 200 } 201 202 /* Move fd to from the write fd set into read fd set */ 203 FD_CLR(fd, &srv->wfdset); 204 FD_SET(fd, &srv->rfdset); 205 206 return (0); 207 } 208 209 /* 210 * Create bound non-blocking socket and initiate connect 211 */ 212 213 static int 214 client_socket(bdaddr_p bdaddr, uint16_t psm) 215 { 216 struct sockaddr_l2cap l2addr; 217 int32_t s, m; 218 219 s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); 220 if (s < 0) 221 return (-1); 222 223 m = fcntl(s, F_GETFL); 224 if (m < 0) { 225 close(s); 226 return (-1); 227 } 228 229 if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { 230 close(s); 231 return (-1); 232 } 233 234 l2addr.l2cap_len = sizeof(l2addr); 235 l2addr.l2cap_family = AF_BLUETOOTH; 236 memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr)); 237 l2addr.l2cap_psm = 0; 238 l2addr.l2cap_bdaddr_type = BDADDR_BREDR; 239 l2addr.l2cap_cid = 0; 240 241 if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { 242 close(s); 243 return (-1); 244 } 245 246 memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); 247 l2addr.l2cap_psm = htole16(psm); 248 249 if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && 250 errno != EINPROGRESS) { 251 close(s); 252 return (-1); 253 } 254 255 return (s); 256 } 257 258