Merge branch 'upstream' into tizen
[platform/upstream/connman.git] / src / rfkill.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define _GNU_SOURCE
27 #include <stdio.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdint.h>
33
34 #include "connman.h"
35
36 enum rfkill_type {
37         RFKILL_TYPE_ALL = 0,
38         RFKILL_TYPE_WLAN,
39         RFKILL_TYPE_BLUETOOTH,
40         RFKILL_TYPE_UWB,
41         RFKILL_TYPE_WWAN,
42         RFKILL_TYPE_GPS,
43         RFKILL_TYPE_FM,
44         NUM_RFKILL_TYPES,
45 };
46
47 enum rfkill_operation {
48         RFKILL_OP_ADD = 0,
49         RFKILL_OP_DEL,
50         RFKILL_OP_CHANGE,
51         RFKILL_OP_CHANGE_ALL,
52 };
53
54 struct rfkill_event {
55         uint32_t idx;
56         uint8_t  type;
57         uint8_t  op;
58         uint8_t  soft;
59         uint8_t  hard;
60 };
61
62 static enum connman_service_type convert_type(uint8_t type)
63 {
64         switch (type) {
65         case RFKILL_TYPE_WLAN:
66                 return CONNMAN_SERVICE_TYPE_WIFI;
67         case RFKILL_TYPE_BLUETOOTH:
68                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
69         case RFKILL_TYPE_WWAN:
70                 return CONNMAN_SERVICE_TYPE_CELLULAR;
71         }
72
73         return CONNMAN_SERVICE_TYPE_UNKNOWN;
74 }
75
76 #if !defined TIZEN_EXT
77 static enum rfkill_type convert_service_type(enum connman_service_type type)
78 {
79         switch (type) {
80         case CONNMAN_SERVICE_TYPE_WIFI:
81                 return RFKILL_TYPE_WLAN;
82         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
83                 return RFKILL_TYPE_BLUETOOTH;
84         case CONNMAN_SERVICE_TYPE_CELLULAR:
85                 return RFKILL_TYPE_WWAN;
86         case CONNMAN_SERVICE_TYPE_GPS:
87                 return RFKILL_TYPE_GPS;
88         case CONNMAN_SERVICE_TYPE_SYSTEM:
89         case CONNMAN_SERVICE_TYPE_ETHERNET:
90         case CONNMAN_SERVICE_TYPE_VPN:
91         case CONNMAN_SERVICE_TYPE_GADGET:
92         case CONNMAN_SERVICE_TYPE_P2P:
93         case CONNMAN_SERVICE_TYPE_UNKNOWN:
94                 return NUM_RFKILL_TYPES;
95         }
96
97         return NUM_RFKILL_TYPES;
98 }
99 #endif
100
101 static GIOStatus rfkill_process(GIOChannel *chan)
102 {
103         unsigned char buf[32];
104         struct rfkill_event *event = (void *) buf;
105         enum connman_service_type type;
106         gsize len;
107         GIOStatus status;
108
109         DBG("");
110
111         memset(buf, 0, sizeof(buf));
112
113         status = g_io_channel_read_chars(chan, (gchar *) buf,
114                         sizeof(struct rfkill_event), &len, NULL);
115
116         if (status != G_IO_STATUS_NORMAL)
117                 return status;
118
119         if (len != sizeof(struct rfkill_event))
120                 return status;
121
122         DBG("idx %u type %u op %u soft %u hard %u", event->idx,
123                                                 event->type, event->op,
124                                                 event->soft, event->hard);
125
126         type = convert_type(event->type);
127
128         switch (event->op) {
129         case RFKILL_OP_ADD:
130                 __connman_technology_add_rfkill(event->idx, type,
131                                                 event->soft, event->hard);
132                 break;
133         case RFKILL_OP_DEL:
134                 __connman_technology_remove_rfkill(event->idx, type);
135                 break;
136         case RFKILL_OP_CHANGE:
137                 __connman_technology_update_rfkill(event->idx, type,
138                                                 event->soft, event->hard);
139                 break;
140         default:
141                 break;
142         }
143
144         return status;
145 }
146
147 static gboolean rfkill_event(GIOChannel *chan, GIOCondition cond, gpointer data)
148 {
149         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
150                 return FALSE;
151
152         if (rfkill_process(chan) == G_IO_STATUS_ERROR)
153                 return FALSE;
154
155         return TRUE;
156 }
157
158 static guint watch = 0;
159
160 int __connman_rfkill_block(enum connman_service_type type, bool block)
161 {
162 #if !defined TIZEN_EXT
163         uint8_t rfkill_type;
164         struct rfkill_event event;
165         ssize_t len;
166         int fd, err = 0;
167 #endif
168
169         DBG("type %d block %d", type, block);
170
171 #if defined TIZEN_EXT
172         DBG("try to set rfkill block %d, but it's not pormitted", block);
173
174         return 0;
175 #else
176         rfkill_type = convert_service_type(type);
177         if (rfkill_type == NUM_RFKILL_TYPES)
178                 return -EINVAL;
179
180         fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
181         if (fd < 0)
182                 return -errno;
183
184         memset(&event, 0, sizeof(event));
185         event.op = RFKILL_OP_CHANGE_ALL;
186         event.type = rfkill_type;
187         event.soft = block;
188
189         len = write(fd, &event, sizeof(event));
190         if (len < 0) {
191                 err = -errno;
192                 connman_error("Failed to change RFKILL state");
193         }
194
195         close(fd);
196
197         return err;
198 #endif
199 }
200
201 int __connman_rfkill_init(void)
202 {
203         GIOChannel *channel;
204         GIOFlags flags;
205         int fd;
206
207         DBG("");
208
209         fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
210         if (fd < 0) {
211                 connman_error("Failed to open RFKILL control device");
212                 return -EIO;
213         }
214
215         channel = g_io_channel_unix_new(fd);
216         g_io_channel_set_close_on_unref(channel, TRUE);
217
218         g_io_channel_set_encoding(channel, NULL, NULL);
219         g_io_channel_set_buffered(channel, FALSE);
220
221         flags = g_io_channel_get_flags(channel);
222         flags |= G_IO_FLAG_NONBLOCK;
223         g_io_channel_set_flags(channel, flags, NULL);
224
225         /* Process current RFKILL events sent on device open */
226         while (rfkill_process(channel) == G_IO_STATUS_NORMAL);
227
228         watch = g_io_add_watch(channel,
229                                 G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
230                                 rfkill_event, NULL);
231
232         g_io_channel_unref(channel);
233
234         return watch ? 0 : -EIO;
235 }
236
237 void __connman_rfkill_cleanup(void)
238 {
239         DBG("");
240
241         if (watch) {
242                 g_source_remove(watch);
243                 watch = 0;
244         }
245 }