Merge "Fix SIGSEV on freeing server domains list" 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 #include <stdio.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdint.h>
32
33 #include "connman.h"
34
35 enum rfkill_type {
36         RFKILL_TYPE_ALL = 0,
37         RFKILL_TYPE_WLAN,
38         RFKILL_TYPE_BLUETOOTH,
39         RFKILL_TYPE_UWB,
40         RFKILL_TYPE_WWAN,
41         RFKILL_TYPE_GPS,
42         RFKILL_TYPE_FM,
43         NUM_RFKILL_TYPES,
44 };
45
46 enum rfkill_operation {
47         RFKILL_OP_ADD = 0,
48         RFKILL_OP_DEL,
49         RFKILL_OP_CHANGE,
50         RFKILL_OP_CHANGE_ALL,
51 };
52
53 struct rfkill_event {
54         uint32_t idx;
55         uint8_t  type;
56         uint8_t  op;
57         uint8_t  soft;
58         uint8_t  hard;
59 };
60
61 static enum connman_service_type convert_type(uint8_t type)
62 {
63         switch (type) {
64         case RFKILL_TYPE_WLAN:
65                 return CONNMAN_SERVICE_TYPE_WIFI;
66         case RFKILL_TYPE_BLUETOOTH:
67                 return CONNMAN_SERVICE_TYPE_BLUETOOTH;
68         case RFKILL_TYPE_WWAN:
69                 return CONNMAN_SERVICE_TYPE_CELLULAR;
70         }
71
72         return CONNMAN_SERVICE_TYPE_UNKNOWN;
73 }
74
75 #if !defined TIZEN_EXT
76 static enum rfkill_type convert_service_type(enum connman_service_type type)
77 {
78         switch (type) {
79         case CONNMAN_SERVICE_TYPE_WIFI:
80                 return RFKILL_TYPE_WLAN;
81         case CONNMAN_SERVICE_TYPE_BLUETOOTH:
82                 return RFKILL_TYPE_BLUETOOTH;
83         case CONNMAN_SERVICE_TYPE_CELLULAR:
84                 return RFKILL_TYPE_WWAN;
85         case CONNMAN_SERVICE_TYPE_GPS:
86                 return RFKILL_TYPE_GPS;
87         case CONNMAN_SERVICE_TYPE_SYSTEM:
88         case CONNMAN_SERVICE_TYPE_ETHERNET:
89         case CONNMAN_SERVICE_TYPE_VPN:
90         case CONNMAN_SERVICE_TYPE_GADGET:
91         case CONNMAN_SERVICE_TYPE_P2P:
92         case CONNMAN_SERVICE_TYPE_UNKNOWN:
93                 return NUM_RFKILL_TYPES;
94         }
95
96         return NUM_RFKILL_TYPES;
97 }
98 #endif
99
100 static GIOStatus rfkill_process(GIOChannel *chan)
101 {
102         unsigned char buf[32];
103         struct rfkill_event *event = (void *) buf;
104         enum connman_service_type type;
105         gsize len;
106         GIOStatus status;
107
108         DBG("");
109
110         memset(buf, 0, sizeof(buf));
111
112         status = g_io_channel_read_chars(chan, (gchar *) buf,
113                         sizeof(struct rfkill_event), &len, NULL);
114
115         if (status != G_IO_STATUS_NORMAL)
116                 return status;
117
118         if (len != sizeof(struct rfkill_event))
119                 return status;
120
121         DBG("idx %u type %u op %u soft %u hard %u", event->idx,
122                                                 event->type, event->op,
123                                                 event->soft, event->hard);
124
125         type = convert_type(event->type);
126
127         switch (event->op) {
128         case RFKILL_OP_ADD:
129                 __connman_technology_add_rfkill(event->idx, type,
130                                                 event->soft, event->hard);
131                 break;
132         case RFKILL_OP_DEL:
133                 __connman_technology_remove_rfkill(event->idx, type);
134                 break;
135         case RFKILL_OP_CHANGE:
136                 __connman_technology_update_rfkill(event->idx, type,
137                                                 event->soft, event->hard);
138                 break;
139         default:
140                 break;
141         }
142
143         return status;
144 }
145
146 static gboolean rfkill_event(GIOChannel *chan, GIOCondition cond, gpointer data)
147 {
148         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
149                 return FALSE;
150
151         if (rfkill_process(chan) == G_IO_STATUS_ERROR)
152                 return FALSE;
153
154         return TRUE;
155 }
156
157 static guint watch = 0;
158
159 int __connman_rfkill_block(enum connman_service_type type, bool block)
160 {
161 #if !defined TIZEN_EXT
162         uint8_t rfkill_type;
163         struct rfkill_event event;
164         ssize_t len;
165         int fd, err = 0;
166 #endif
167
168         DBG("type %d block %d", type, block);
169
170 #if defined TIZEN_EXT
171         DBG("try to set rfkill block %d, but it's not permitted", block);
172
173         return 0;
174 #else
175         rfkill_type = convert_service_type(type);
176         if (rfkill_type == NUM_RFKILL_TYPES)
177                 return -EINVAL;
178
179         fd = open("/dev/rfkill", O_RDWR | O_CLOEXEC);
180         if (fd < 0)
181                 return -errno;
182
183         memset(&event, 0, sizeof(event));
184         event.op = RFKILL_OP_CHANGE_ALL;
185         event.type = rfkill_type;
186         event.soft = block;
187
188         len = write(fd, &event, sizeof(event));
189         if (len < 0) {
190                 err = -errno;
191                 connman_error("Failed to change RFKILL state");
192         }
193
194         close(fd);
195
196         return err;
197 #endif
198 }
199
200 int __connman_rfkill_init(void)
201 {
202         GIOChannel *channel;
203         GIOFlags flags;
204         int fd;
205
206         DBG("");
207
208         fd = open("/dev/rfkill", O_RDONLY | O_CLOEXEC);
209         if (fd < 0) {
210                 connman_error("Failed to open RFKILL control device");
211                 return -EIO;
212         }
213
214         channel = g_io_channel_unix_new(fd);
215         g_io_channel_set_close_on_unref(channel, TRUE);
216
217         g_io_channel_set_encoding(channel, NULL, NULL);
218         g_io_channel_set_buffered(channel, FALSE);
219
220         flags = g_io_channel_get_flags(channel);
221         flags |= G_IO_FLAG_NONBLOCK;
222         g_io_channel_set_flags(channel, flags, NULL);
223
224         /* Process current RFKILL events sent on device open */
225         while (rfkill_process(channel) == G_IO_STATUS_NORMAL);
226
227         watch = g_io_add_watch(channel,
228                                 G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
229                                 rfkill_event, NULL);
230
231         g_io_channel_unref(channel);
232
233         return watch ? 0 : -EIO;
234 }
235
236 void __connman_rfkill_cleanup(void)
237 {
238         DBG("");
239
240         if (watch) {
241                 g_source_remove(watch);
242                 watch = 0;
243         }
244 }