Remove dependency on udev
[framework/connectivity/connman.git] / src / rfkill.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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_WIMAX,
41         RFKILL_TYPE_WWAN,
42         RFKILL_TYPE_GPS,
43         RFKILL_TYPE_FM,
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_WIMAX:
69                 return CONNMAN_SERVICE_TYPE_WIMAX;
70         case RFKILL_TYPE_WWAN:
71                 return CONNMAN_SERVICE_TYPE_CELLULAR;
72         }
73
74         return CONNMAN_SERVICE_TYPE_UNKNOWN;
75 }
76
77 static GIOStatus rfkill_process(GIOChannel *chan)
78 {
79         GIOStatus status = G_IO_STATUS_NORMAL;
80         unsigned char buf[32];
81         struct rfkill_event *event = (void *) buf;
82         char sysname[32];
83         enum connman_service_type type;
84         connman_bool_t blocked;
85         gsize len;
86
87         DBG("");
88
89         memset(buf, 0, sizeof(buf));
90
91         status = g_io_channel_read_chars(chan, (gchar *) buf,
92                         sizeof(struct rfkill_event), &len, NULL);
93
94         if (status != G_IO_STATUS_NORMAL)
95                 return status;
96
97         if (len != sizeof(struct rfkill_event))
98                 return status;
99
100         DBG("idx %u type %u op %u soft %u hard %u", event->idx,
101                                                 event->type, event->op,
102                                                 event->soft, event->hard);
103
104         switch (event->op) {
105         case RFKILL_OP_ADD:
106                 type = convert_type(event->type);
107                 __connman_technology_add_rfkill(event->idx, type,
108                                                 event->soft, event->hard);
109                 break;
110         case RFKILL_OP_DEL:
111                 __connman_technology_remove_rfkill(event->idx);
112                 break;
113         case RFKILL_OP_CHANGE:
114                 __connman_technology_update_rfkill(event->idx, event->soft,
115                                                                 event->hard);
116                 break;
117         default:
118                 break;
119         }
120
121         snprintf(sysname, sizeof(sysname) - 1, "rfkill%d", event->idx);
122
123         blocked = (event->soft || event->hard) ? TRUE : FALSE;
124
125         switch (event->type) {
126         case RFKILL_TYPE_ALL:
127         case RFKILL_TYPE_WLAN:
128                 /* FIXME: unblock device */
129                 break;
130         default:
131                 break;
132         }
133
134         return status;
135 }
136
137 static gboolean rfkill_event(GIOChannel *chan,
138                                 GIOCondition cond, gpointer data)
139 {
140         if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
141                 return FALSE;
142
143         if (rfkill_process(chan) == G_IO_STATUS_ERROR)
144                 return FALSE;
145
146         return TRUE;
147 }
148
149 static GIOChannel *channel = NULL;
150
151 int __connman_rfkill_init(void)
152 {
153         GIOFlags flags;
154         int fd;
155
156         DBG("");
157
158         fd = open("/dev/rfkill", O_RDWR);
159         if (fd < 0) {
160                 connman_error("Failed to open RFKILL control device");
161                 return -EIO;
162         }
163
164         channel = g_io_channel_unix_new(fd);
165         g_io_channel_set_close_on_unref(channel, TRUE);
166
167         flags = g_io_channel_get_flags(channel);
168         flags |= G_IO_FLAG_NONBLOCK;
169         g_io_channel_set_flags(channel, flags, NULL);
170
171         /* Process current RFKILL events sent on device open */
172         while (rfkill_process(channel) == G_IO_STATUS_NORMAL);
173
174         g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
175                                                         rfkill_event, NULL);
176
177         return 0;
178 }
179
180 void __connman_rfkill_cleanup(void)
181 {
182         DBG("");
183
184         if (channel == NULL)
185                 return;
186
187         g_io_channel_shutdown(channel, TRUE, NULL);
188         g_io_channel_unref(channel);
189
190         channel = NULL;
191 }