1100894Srwatson/*
2100894Srwatson * client.c
3100894Srwatson */
4100894Srwatson
5100894Srwatson/*-
6100894Srwatson * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7100894Srwatson * All rights reserved.
8100894Srwatson *
9100894Srwatson * Redistribution and use in source and binary forms, with or without
10106392Srwatson * modification, are permitted provided that the following conditions
11106392Srwatson * are met:
12106392Srwatson * 1. Redistributions of source code must retain the above copyright
13106392Srwatson *    notice, this list of conditions and the following disclaimer.
14100894Srwatson * 2. Redistributions in binary form must reproduce the above copyright
15100894Srwatson *    notice, this list of conditions and the following disclaimer in the
16100894Srwatson *    documentation and/or other materials provided with the distribution.
17100894Srwatson *
18100894Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19100894Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20100894Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21100894Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22100894Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23100894Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24100894Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25100894Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26100894Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27100894Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28100894Srwatson * SUCH DAMAGE.
29100894Srwatson *
30100894Srwatson * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $
31100894Srwatson * $FreeBSD: releng/11.0/usr.sbin/bluetooth/bthidd/client.c 281210 2015-04-07 16:48:23Z takawata $
32100894Srwatson */
33100894Srwatson
34100894Srwatson#include <sys/queue.h>
35100894Srwatson#include <assert.h>
36100894Srwatson#define L2CAP_SOCKET_CHECKED
37100894Srwatson#include <bluetooth.h>
38100894Srwatson#include <errno.h>
39100894Srwatson#include <fcntl.h>
40100894Srwatson#include <stdio.h>
41100894Srwatson#include <stdlib.h>
42100894Srwatson#include <string.h>
43100894Srwatson#include <syslog.h>
44100894Srwatson#include <unistd.h>
45100894Srwatson#include <usbhid.h>
46104300Sphk#include "bthid_config.h"
47101173Srwatson#include "bthidd.h"
48100894Srwatson
49100979Srwatsonstatic int32_t	client_socket(bdaddr_p bdaddr, uint16_t psm);
50106468Srwatson
51100979Srwatson/*
52100979Srwatson * Get next config entry and create outbound connection (if required)
53102949Sbde *
54100979Srwatson * XXX	Do only one device at a time. At least one of my devices (3COM
55100979Srwatson *	Bluetooth PCCARD) rejects Create_Connection command if another
56101712Srwatson *	Create_Connection command is still pending. Weird...
57100979Srwatson */
58100979Srwatson
59100894Srwatsonstatic int32_t	connect_in_progress = 0;
60100894Srwatson
61100979Srwatsonint32_t
62100979Srwatsonclient_rescan(bthid_server_p srv)
63100979Srwatson{
64100979Srwatson	static hid_device_p	d;
65100979Srwatson	bthid_session_p		s;
66100979Srwatson
67100979Srwatson	assert(srv != NULL);
68100979Srwatson
69100894Srwatson	if (connect_in_progress)
70100979Srwatson		return (0); /* another connect is still pending */
71100979Srwatson
72100979Srwatson	d = get_next_hid_device(d);
73100979Srwatson	if (d == NULL)
74100979Srwatson		return (0); /* XXX should not happen? empty config? */
75100979Srwatson
76100979Srwatson	if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL)
77100979Srwatson		return (0); /* session already active */
78100979Srwatson
79100979Srwatson	if (!d->new_device) {
80100979Srwatson		if (d->reconnect_initiate)
81100979Srwatson			return (0); /* device will initiate reconnect */
82100979Srwatson	}
83100979Srwatson
84100979Srwatson	syslog(LOG_NOTICE, "Opening outbound session for %s " \
85100979Srwatson		"(new_device=%d, reconnect_initiate=%d)",
86100979Srwatson		bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate);
87100979Srwatson
88101712Srwatson	if ((s = session_open(srv, d)) == NULL) {
89101712Srwatson		syslog(LOG_CRIT, "Could not create outbound session for %s",
90101712Srwatson			bt_ntoa(&d->bdaddr, NULL));
91101712Srwatson		return (-1);
92101712Srwatson	}
93101712Srwatson
94101712Srwatson	/* Open control channel */
95100979Srwatson	s->ctrl = client_socket(&s->bdaddr, d->control_psm);
96100979Srwatson	if (s->ctrl < 0) {
97100979Srwatson		syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)",
98100979Srwatson			bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
99104517Srwatson		session_close(s);
100100979Srwatson		return (-1);
101100979Srwatson	}
102100979Srwatson
103105497Srwatson	s->state = W4CTRL;
104100979Srwatson
105100979Srwatson	FD_SET(s->ctrl, &srv->wfdset);
106100979Srwatson	if (s->ctrl > srv->maxfd)
107100979Srwatson		srv->maxfd = s->ctrl;
108100979Srwatson
109105959Srwatson	connect_in_progress = 1;
110105959Srwatson
111105959Srwatson	return (0);
112105959Srwatson}
113105959Srwatson
114100979Srwatson/*
115100979Srwatson * Process connect on the socket
116105988Srwatson */
117105988Srwatson
118105988Srwatsonint32_t
119105988Srwatsonclient_connect(bthid_server_p srv, int32_t fd)
120105988Srwatson{
121105988Srwatson	bthid_session_p	s;
122100979Srwatson	hid_device_p	d;
123100979Srwatson	int32_t		error;
124100979Srwatson	socklen_t	len;
125100979Srwatson
126100979Srwatson	assert(srv != NULL);
127100979Srwatson	assert(fd >= 0);
128100979Srwatson
129100979Srwatson	s = session_by_fd(srv, fd);
130100979Srwatson	assert(s != NULL);
131100979Srwatson
132103513Srwatson	d = get_hid_device(&s->bdaddr);
133103513Srwatson	assert(d != NULL);
134103513Srwatson
135104236Srwatson	error = 0;
136103513Srwatson	len = sizeof(error);
137100979Srwatson	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
138100979Srwatson		syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)",
139100979Srwatson			bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
140100979Srwatson		session_close(s);
141100979Srwatson		connect_in_progress = 0;
142100979Srwatson
143100979Srwatson		return (-1);
144100979Srwatson	}
145100979Srwatson
146100979Srwatson	if (error != 0) {
147106045Srwatson		syslog(LOG_ERR, "Could not connect to %s. %s (%d)",
148106045Srwatson			bt_ntoa(&s->bdaddr, NULL), strerror(error), error);
149106045Srwatson		session_close(s);
150106045Srwatson		connect_in_progress = 0;
151106025Srwatson
152106045Srwatson		return (0);
153103514Srwatson	}
154103514Srwatson
155104236Srwatson	switch (s->state) {
156103514Srwatson	case W4CTRL: /* Control channel is open */
157103136Srwatson		assert(s->ctrl == fd);
158103136Srwatson		assert(s->intr == -1);
159103136Srwatson
160103136Srwatson		/* Open interrupt channel */
161101892Srwatson		s->intr = client_socket(&s->bdaddr, d->interrupt_psm);
162100979Srwatson		if (s->intr < 0) {
163100979Srwatson			syslog(LOG_ERR, "Could not open interrupt channel " \
164100979Srwatson				"to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
165100979Srwatson				strerror(errno), errno);
166101988Srwatson			session_close(s);
167104268Srwatson			connect_in_progress = 0;
168104268Srwatson
169104268Srwatson			return (-1);
170104268Srwatson		}
171104268Srwatson
172104268Srwatson		s->state = W4INTR;
173104268Srwatson
174104268Srwatson		FD_SET(s->intr, &srv->wfdset);
175104268Srwatson		if (s->intr > srv->maxfd)
176104268Srwatson			srv->maxfd = s->intr;
177104517Srwatson
178104517Srwatson		d->new_device = 0; /* reset new device flag */
179104517Srwatson		write_hids_file();
180100979Srwatson		break;
181100979Srwatson
182100979Srwatson	case W4INTR: /* Interrupt channel is open */
183104517Srwatson		assert(s->ctrl != -1);
184104517Srwatson		assert(s->intr == fd);
185100979Srwatson
186104517Srwatson		s->state = OPEN;
187100979Srwatson		connect_in_progress = 0;
188104517Srwatson
189100979Srwatson		/* Register session's vkbd descriptor (if any) for read */
190104517Srwatson		if (s->state == OPEN && d->keyboard) {
191100979Srwatson			assert(s->vkbd != -1);
192104517Srwatson
193100979Srwatson			FD_SET(s->vkbd, &srv->rfdset);
194104517Srwatson			if (s->vkbd > srv->maxfd)
195100979Srwatson				srv->maxfd = s->vkbd;
196104517Srwatson	        }
197100979Srwatson		break;
198104517Srwatson
199100979Srwatson	default:
200104517Srwatson		assert(0);
201100979Srwatson		break;
202104517Srwatson	}
203100979Srwatson
204104517Srwatson	/* Move fd to from the write fd set into read fd set */
205100979Srwatson	FD_CLR(fd, &srv->wfdset);
206101988Srwatson	FD_SET(fd, &srv->rfdset);
207100979Srwatson
208100979Srwatson	return (0);
209100979Srwatson}
210100979Srwatson
211100979Srwatson/*
212104546Srwatson * Create bound non-blocking socket and initiate connect
213104546Srwatson */
214100979Srwatson
215100979Srwatsonstatic int
216100979Srwatsonclient_socket(bdaddr_p bdaddr, uint16_t psm)
217104541Srwatson{
218104541Srwatson	struct sockaddr_l2cap	l2addr;
219105988Srwatson	int32_t			s, m;
220105988Srwatson
221105988Srwatson	s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
222105988Srwatson	if (s < 0)
223100979Srwatson		return (-1);
224100979Srwatson
225105694Srwatson	m = fcntl(s, F_GETFL);
226100979Srwatson	if (m < 0) {
227100979Srwatson		close(s);
228100979Srwatson		return (-1);
229100979Srwatson	}
230100979Srwatson
231100979Srwatson	if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) {
232100979Srwatson		close(s);
233100979Srwatson		return (-1);
234100979Srwatson	}
235100979Srwatson
236100979Srwatson	l2addr.l2cap_len = sizeof(l2addr);
237100979Srwatson	l2addr.l2cap_family = AF_BLUETOOTH;
238100979Srwatson	memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr));
239100979Srwatson	l2addr.l2cap_psm = 0;
240100979Srwatson	l2addr.l2cap_bdaddr_type = BDADDR_BREDR;
241100979Srwatson	l2addr.l2cap_cid = 0;
242100979Srwatson
243100979Srwatson	if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
244100979Srwatson		close(s);
245100979Srwatson		return (-1);
246100979Srwatson	}
247100979Srwatson
248100979Srwatson	memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr));
249100979Srwatson	l2addr.l2cap_psm = htole16(psm);
250100979Srwatson
251100979Srwatson	if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 &&
252100979Srwatson	    errno != EINPROGRESS) {
253100979Srwatson		close(s);
254100979Srwatson		return (-1);
255100979Srwatson	}
256100979Srwatson
257100979Srwatson	return (s);
258100979Srwatson}
259100979Srwatson
260100979Srwatson