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