1128080Semax/* 2128080Semax * client.c 3162128Semax */ 4162128Semax 5162128Semax/*- 6162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 7128080Semax * All rights reserved. 8128080Semax * 9128080Semax * Redistribution and use in source and binary forms, with or without 10128080Semax * modification, are permitted provided that the following conditions 11128080Semax * are met: 12128080Semax * 1. Redistributions of source code must retain the above copyright 13128080Semax * notice, this list of conditions and the following disclaimer. 14128080Semax * 2. Redistributions in binary form must reproduce the above copyright 15128080Semax * notice, this list of conditions and the following disclaimer in the 16128080Semax * documentation and/or other materials provided with the distribution. 17128080Semax * 18128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28128080Semax * SUCH DAMAGE. 29128080Semax * 30162128Semax * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $ 31128080Semax * $FreeBSD: releng/10.3/usr.sbin/bluetooth/bthidd/client.c 163918 2006-11-02 18:57:09Z emax $ 32128080Semax */ 33128080Semax 34128080Semax#include <sys/queue.h> 35128080Semax#include <assert.h> 36128080Semax#include <bluetooth.h> 37128080Semax#include <errno.h> 38128080Semax#include <fcntl.h> 39128080Semax#include <stdio.h> 40128080Semax#include <stdlib.h> 41128080Semax#include <string.h> 42128080Semax#include <syslog.h> 43128080Semax#include <unistd.h> 44128080Semax#include <usbhid.h> 45162128Semax#include "bthid_config.h" 46128080Semax#include "bthidd.h" 47128080Semax 48163918Semaxstatic int32_t client_socket(bdaddr_p bdaddr, uint16_t psm); 49128080Semax 50128080Semax/* 51128080Semax * Get next config entry and create outbound connection (if required) 52128080Semax * 53128080Semax * XXX Do only one device at a time. At least one of my devices (3COM 54128080Semax * Bluetooth PCCARD) rejects Create_Connection command if another 55128080Semax * Create_Connection command is still pending. Weird... 56128080Semax */ 57128080Semax 58162128Semaxstatic int32_t connect_in_progress = 0; 59128080Semax 60162128Semaxint32_t 61128080Semaxclient_rescan(bthid_server_p srv) 62128080Semax{ 63162128Semax static hid_device_p d; 64162128Semax bthid_session_p s; 65128080Semax 66128080Semax assert(srv != NULL); 67128080Semax 68128080Semax if (connect_in_progress) 69128080Semax return (0); /* another connect is still pending */ 70128080Semax 71128080Semax d = get_next_hid_device(d); 72128080Semax if (d == NULL) 73128080Semax return (0); /* XXX should not happen? empty config? */ 74128080Semax 75128080Semax if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) 76128080Semax return (0); /* session already active */ 77128080Semax 78128080Semax if (!d->new_device) { 79128080Semax if (d->reconnect_initiate) 80128080Semax return (0); /* device will initiate reconnect */ 81128080Semax } 82128080Semax 83128080Semax syslog(LOG_NOTICE, "Opening outbound session for %s " \ 84128080Semax "(new_device=%d, reconnect_initiate=%d)", 85128080Semax bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate); 86128080Semax 87162128Semax if ((s = session_open(srv, d)) == NULL) { 88162128Semax syslog(LOG_CRIT, "Could not create outbound session for %s", 89162128Semax bt_ntoa(&d->bdaddr, NULL)); 90128080Semax return (-1); 91128080Semax } 92128080Semax 93128080Semax /* Open control channel */ 94128080Semax s->ctrl = client_socket(&s->bdaddr, d->control_psm); 95128080Semax if (s->ctrl < 0) { 96128080Semax syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", 97128080Semax bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 98128080Semax session_close(s); 99128080Semax return (-1); 100128080Semax } 101128080Semax 102128080Semax s->state = W4CTRL; 103128080Semax 104128080Semax FD_SET(s->ctrl, &srv->wfdset); 105128080Semax if (s->ctrl > srv->maxfd) 106128080Semax srv->maxfd = s->ctrl; 107128080Semax 108128080Semax connect_in_progress = 1; 109128080Semax 110128080Semax return (0); 111128080Semax} 112128080Semax 113128080Semax/* 114128080Semax * Process connect on the socket 115128080Semax */ 116128080Semax 117162128Semaxint32_t 118162128Semaxclient_connect(bthid_server_p srv, int32_t fd) 119128080Semax{ 120162128Semax bthid_session_p s; 121162128Semax hid_device_p d; 122162494Semax int32_t error; 123162494Semax socklen_t len; 124128080Semax 125128080Semax assert(srv != NULL); 126128080Semax assert(fd >= 0); 127128080Semax 128128080Semax s = session_by_fd(srv, fd); 129128080Semax assert(s != NULL); 130128080Semax 131128080Semax d = get_hid_device(&s->bdaddr); 132128080Semax assert(d != NULL); 133128080Semax 134128080Semax error = 0; 135128080Semax len = sizeof(error); 136128080Semax if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 137128080Semax syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", 138128080Semax bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); 139128080Semax session_close(s); 140128080Semax connect_in_progress = 0; 141128080Semax 142128080Semax return (-1); 143128080Semax } 144128080Semax 145128080Semax if (error != 0) { 146128080Semax syslog(LOG_ERR, "Could not connect to %s. %s (%d)", 147128080Semax bt_ntoa(&s->bdaddr, NULL), strerror(error), error); 148128080Semax session_close(s); 149128080Semax connect_in_progress = 0; 150128080Semax 151128080Semax return (0); 152128080Semax } 153128080Semax 154128080Semax switch (s->state) { 155128080Semax case W4CTRL: /* Control channel is open */ 156128080Semax assert(s->ctrl == fd); 157128080Semax assert(s->intr == -1); 158128080Semax 159128080Semax /* Open interrupt channel */ 160128080Semax s->intr = client_socket(&s->bdaddr, d->interrupt_psm); 161128080Semax if (s->intr < 0) { 162128080Semax syslog(LOG_ERR, "Could not open interrupt channel " \ 163128080Semax "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), 164128080Semax strerror(errno), errno); 165128080Semax session_close(s); 166128080Semax connect_in_progress = 0; 167128080Semax 168128080Semax return (-1); 169128080Semax } 170128080Semax 171128080Semax s->state = W4INTR; 172128080Semax 173128080Semax FD_SET(s->intr, &srv->wfdset); 174128080Semax if (s->intr > srv->maxfd) 175128080Semax srv->maxfd = s->intr; 176128080Semax 177128080Semax d->new_device = 0; /* reset new device flag */ 178128080Semax write_hids_file(); 179128080Semax break; 180128080Semax 181128080Semax case W4INTR: /* Interrupt channel is open */ 182128080Semax assert(s->ctrl != -1); 183128080Semax assert(s->intr == fd); 184128080Semax 185128080Semax s->state = OPEN; 186128080Semax connect_in_progress = 0; 187162128Semax 188162128Semax /* Register session's vkbd descriptor (if any) for read */ 189162128Semax if (s->state == OPEN && d->keyboard) { 190162128Semax assert(s->vkbd != -1); 191162128Semax 192162128Semax FD_SET(s->vkbd, &srv->rfdset); 193162128Semax if (s->vkbd > srv->maxfd) 194162128Semax srv->maxfd = s->vkbd; 195162128Semax } 196128080Semax break; 197128080Semax 198128080Semax default: 199128080Semax assert(0); 200128080Semax break; 201128080Semax } 202128080Semax 203128080Semax /* Move fd to from the write fd set into read fd set */ 204128080Semax FD_CLR(fd, &srv->wfdset); 205128080Semax FD_SET(fd, &srv->rfdset); 206128080Semax 207128080Semax return (0); 208128080Semax} 209128080Semax 210128080Semax/* 211128080Semax * Create bound non-blocking socket and initiate connect 212128080Semax */ 213128080Semax 214128080Semaxstatic int 215163918Semaxclient_socket(bdaddr_p bdaddr, uint16_t psm) 216128080Semax{ 217128080Semax struct sockaddr_l2cap l2addr; 218162128Semax int32_t s, m; 219128080Semax 220128080Semax s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); 221128080Semax if (s < 0) 222128080Semax return (-1); 223128080Semax 224128080Semax m = fcntl(s, F_GETFL); 225128080Semax if (m < 0) { 226128080Semax close(s); 227128080Semax return (-1); 228128080Semax } 229128080Semax 230128080Semax if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { 231128080Semax close(s); 232128080Semax return (-1); 233128080Semax } 234128080Semax 235128080Semax l2addr.l2cap_len = sizeof(l2addr); 236128080Semax l2addr.l2cap_family = AF_BLUETOOTH; 237162128Semax memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr)); 238128080Semax l2addr.l2cap_psm = 0; 239128080Semax 240128080Semax if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { 241128080Semax close(s); 242128080Semax return (-1); 243128080Semax } 244128080Semax 245128080Semax memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); 246163918Semax l2addr.l2cap_psm = htole16(psm); 247128080Semax 248128080Semax if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && 249128080Semax errno != EINPROGRESS) { 250128080Semax close(s); 251128080Semax return (-1); 252128080Semax } 253128080Semax 254128080Semax return (s); 255128080Semax} 256128080Semax 257