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