server.c revision 0d526038144d38375bccfd8a21502b5176f64b35
1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <unistd.h>
29
30#include <bluetooth/bluetooth.h>
31#include <bluetooth/sdp.h>
32
33#include <glib.h>
34#include <dbus/dbus.h>
35
36#include "logging.h"
37
38#include "adapter.h"
39#include "device.h"
40#include "glib-helper.h"
41
42static GSList *servers = NULL;
43struct server {
44	bdaddr_t src;
45	GIOChannel *ctrl;
46	GIOChannel *intr;
47};
48
49struct authorization_data {
50	bdaddr_t src;
51	bdaddr_t dst;
52};
53
54static gint server_cmp(gconstpointer s, gconstpointer user_data)
55{
56	const struct server *server = s;
57	const bdaddr_t *src = user_data;
58
59	return bacmp(&server->src, src);
60}
61
62static void auth_callback(DBusError *derr, void *user_data)
63{
64	struct authorization_data *auth = user_data;
65
66	if (derr) {
67		error("Access denied: %s", derr->message);
68		if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
69			btd_cancel_authorization(&auth->src, &auth->dst);
70
71		input_device_close_channels(&auth->src, &auth->dst);
72	} else
73		input_device_connadd(&auth->src, &auth->dst);
74
75	g_free(auth);
76}
77
78static int authorize_device(const bdaddr_t *src, const bdaddr_t *dst)
79{
80	struct authorization_data *auth;
81
82	auth = g_new0(struct authorization_data, 1);
83	bacpy(&auth->src, src);
84	bacpy(&auth->dst, dst);
85
86	return btd_request_authorization(src, dst, HID_UUID,
87				auth_callback, auth);
88}
89
90static void connect_event_cb(GIOChannel *chan, int err, const bdaddr_t *src,
91				const bdaddr_t *dst, gpointer data)
92{
93	int sk, psm = GPOINTER_TO_UINT(data);
94
95	if (err < 0) {
96		error("accept: %s (%d)", strerror(-err), -err);
97		return;
98	}
99
100	sk = g_io_channel_unix_get_fd(chan);
101
102	debug("Incoming connection on PSM %d", psm);
103
104	if (input_device_set_channel(src, dst, psm, sk) < 0) {
105		/* Send unplug virtual cable to unknown devices */
106		if (psm == L2CAP_PSM_HIDP_CTRL) {
107			unsigned char unplug[] = { 0x15 };
108			int err;
109			err = write(sk, unplug, sizeof(unplug));
110		}
111		close(sk);
112		return;
113	}
114
115	if ((psm == L2CAP_PSM_HIDP_INTR) && (authorize_device(src, dst) < 0))
116		input_device_close_channels(src, dst);
117
118	return;
119}
120
121int server_start(const bdaddr_t *src)
122{
123	struct server *server;
124	GIOChannel *ctrl_io, *intr_io;
125
126	ctrl_io = bt_l2cap_listen(src, L2CAP_PSM_HIDP_CTRL, 0, 0,
127				connect_event_cb,
128				GUINT_TO_POINTER(L2CAP_PSM_HIDP_CTRL));
129	if (!ctrl_io) {
130		error("Failed to listen on control channel");
131		return -1;
132	}
133	g_io_channel_set_close_on_unref(ctrl_io, TRUE);
134
135	intr_io = bt_l2cap_listen(src, L2CAP_PSM_HIDP_INTR, 0, 0,
136				connect_event_cb,
137				GUINT_TO_POINTER(L2CAP_PSM_HIDP_INTR));
138	if (!intr_io) {
139		error("Failed to listen on interrupt channel");
140		g_io_channel_unref(ctrl_io);
141		return -1;
142	}
143	g_io_channel_set_close_on_unref(intr_io, TRUE);
144
145	server = g_new0(struct server, 1);
146	bacpy(&server->src, src);
147	server->ctrl = ctrl_io;
148	server->intr = intr_io;
149
150	servers = g_slist_append(servers, server);
151
152	return 0;
153}
154
155void server_stop(const bdaddr_t *src)
156{
157	struct server *server;
158	GSList *l;
159
160	l = g_slist_find_custom(servers, src, server_cmp);
161	if (!l)
162		return;
163
164	server = l->data;
165
166	g_io_channel_unref(server->intr);
167	g_io_channel_unref(server->ctrl);
168
169	servers = g_slist_remove(servers, server);
170	g_free(server);
171}
172