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