1/*
2 *
3 *  BlueZ - Bluetooth protocol stack for Linux
4 *
5 *  Copyright (C) 2004-2009  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 <errno.h>
29#include <stdio.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <stdint.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <glib.h>
37
38#include "logging.h"
39#include "manager.h"
40#include "adapter.h"
41#include "hcid.h"
42
43enum rfkill_type {
44	RFKILL_TYPE_ALL = 0,
45	RFKILL_TYPE_WLAN,
46	RFKILL_TYPE_BLUETOOTH,
47	RFKILL_TYPE_UWB,
48	RFKILL_TYPE_WIMAX,
49	RFKILL_TYPE_WWAN,
50};
51
52enum rfkill_operation {
53	RFKILL_OP_ADD = 0,
54	RFKILL_OP_DEL,
55	RFKILL_OP_CHANGE,
56	RFKILL_OP_CHANGE_ALL,
57};
58
59struct rfkill_event {
60	uint32_t idx;
61	uint8_t  type;
62	uint8_t  op;
63	uint8_t  soft;
64	uint8_t  hard;
65};
66
67static gboolean rfkill_event(GIOChannel *chan,
68				GIOCondition cond, gpointer data)
69{
70	unsigned char buf[32];
71	struct rfkill_event *event = (void *) buf;
72	struct btd_adapter *adapter;
73	char sysname[PATH_MAX];
74	gsize len;
75	GIOError err;
76	int fd, id;
77
78	if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
79		return FALSE;
80
81	memset(buf, 0, sizeof(buf));
82
83	err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
84	if (err) {
85		if (err == G_IO_ERROR_AGAIN)
86			return TRUE;
87		return FALSE;
88	}
89
90	if (len != sizeof(struct rfkill_event))
91		return TRUE;
92
93	debug("RFKILL event idx %u type %u op %u soft %u hard %u",
94					event->idx, event->type, event->op,
95						event->soft, event->hard);
96
97	if (event->soft || event->hard)
98		return TRUE;
99
100	if (event->op != RFKILL_OP_CHANGE)
101		return TRUE;
102
103	if (event->type != RFKILL_TYPE_BLUETOOTH &&
104					event->type != RFKILL_TYPE_ALL)
105		return TRUE;
106
107	snprintf(sysname, sizeof(sysname) - 1,
108			"/sys/class/rfkill/rfkill%u/name", event->idx);
109
110	fd = open(sysname, O_RDONLY);
111	if (fd < 0)
112		return TRUE;
113
114	memset(sysname, 0, sizeof(sysname));
115
116	if (read(fd, sysname, sizeof(sysname)) < 4) {
117		close(fd);
118		return TRUE;
119	}
120
121	close(fd);
122
123	if (g_str_has_prefix(sysname, "hci") == FALSE)
124		return TRUE;
125
126	id = atoi(sysname + 3);
127	if (id < 0)
128		return TRUE;
129
130	adapter = manager_find_adapter_by_id(id);
131	if (!adapter)
132		return TRUE;
133
134	debug("RFKILL unblock for hci%d", id);
135
136	btd_adapter_restore_powered(adapter);
137
138	return TRUE;
139}
140
141static GIOChannel *channel = NULL;
142
143void rfkill_init(void)
144{
145	int fd;
146
147	if (!main_opts.remember_powered)
148		return;
149
150	fd = open("/dev/rfkill", O_RDWR);
151	if (fd < 0) {
152		error("Failed to open RFKILL control device");
153		return;
154	}
155
156	channel = g_io_channel_unix_new(fd);
157	g_io_channel_set_close_on_unref(channel, TRUE);
158
159	g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
160							rfkill_event, NULL);
161}
162
163void rfkill_exit(void)
164{
165	if (!channel)
166		return;
167
168	g_io_channel_shutdown(channel, TRUE, NULL);
169	g_io_channel_unref(channel);
170
171	channel = NULL;
172}
173