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