6bc37a7b37b873f0285dafb9cd2ae4ddaa037968
[platform/upstream/connman.git] / plugins / gadget.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 #include <errno.h>
27 #include <net/if.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #ifndef IFF_LOWER_UP
32 #define IFF_LOWER_UP    0x10000
33 #endif
34
35 #include <glib.h>
36
37 #define CONNMAN_API_SUBJECT_TO_CHANGE
38 #include <connman/technology.h>
39 #include <connman/plugin.h>
40 #include <connman/device.h>
41 #include <connman/rtnl.h>
42 #include <connman/inet.h>
43 #include <connman/log.h>
44
45 static bool gadget_tethering = false;
46
47 struct gadget_data {
48         int index;
49         unsigned flags;
50         unsigned int watch;
51         struct connman_network *network;
52 };
53
54 static int gadget_network_probe(struct connman_network *network)
55 {
56         DBG("network %p", network);
57
58         return 0;
59 }
60
61 static void gadget_network_remove(struct connman_network *network)
62 {
63         DBG("network %p", network);
64 }
65
66 static int gadget_network_connect(struct connman_network *network)
67 {
68         DBG("network %p", network);
69
70         connman_network_set_connected(network, true);
71
72         return 0;
73 }
74
75 static int gadget_network_disconnect(struct connman_network *network)
76 {
77         DBG("network %p", network);
78
79         connman_network_set_connected(network, false);
80
81         return 0;
82 }
83
84 static struct connman_network_driver gadget_network_driver = {
85         .name           = "usb",
86         .type           = CONNMAN_NETWORK_TYPE_GADGET,
87         .probe          = gadget_network_probe,
88         .remove         = gadget_network_remove,
89         .connect        = gadget_network_connect,
90         .disconnect     = gadget_network_disconnect,
91 };
92
93 static void add_network(struct connman_device *device,
94                         struct gadget_data *gadget)
95 {
96         struct connman_network *network;
97         int index;
98
99         network = connman_network_create("gadget",
100                                         CONNMAN_NETWORK_TYPE_GADGET);
101         if (!network)
102                 return;
103
104         index = connman_device_get_index(device);
105         connman_network_set_index(network, index);
106
107         connman_network_set_name(network, "Wired");
108
109         if (connman_device_add_network(device, network) < 0) {
110                 connman_network_unref(network);
111                 return;
112         }
113
114         if (!gadget_tethering)
115                 /*
116                  * Prevent service from starting the reconnect
117                  * procedure as we do not want the DHCP client
118                  * to run when tethering.
119                  */
120                 connman_network_set_group(network, "usb");
121
122         gadget->network = network;
123 }
124
125 static void remove_network(struct connman_device *device,
126                                 struct gadget_data *gadget)
127 {
128         if (!gadget->network)
129                 return;
130
131         connman_device_remove_network(device, gadget->network);
132         connman_network_unref(gadget->network);
133
134         gadget->network = NULL;
135 }
136
137 static void gadget_newlink(unsigned flags, unsigned change, void *user_data)
138 {
139         struct connman_device *device = user_data;
140         struct gadget_data *gadget = connman_device_get_data(device);
141
142         DBG("index %d flags %d change %d", gadget->index, flags, change);
143
144         if ((gadget->flags & IFF_UP) != (flags & IFF_UP)) {
145                 if (flags & IFF_UP) {
146                         DBG("power on");
147                         connman_device_set_powered(device, true);
148                 } else {
149                         DBG("power off");
150                         connman_device_set_powered(device, false);
151                 }
152         }
153
154         if ((gadget->flags & IFF_LOWER_UP) != (flags & IFF_LOWER_UP)) {
155                 if (flags & IFF_LOWER_UP) {
156                         DBG("carrier on");
157                         add_network(device, gadget);
158                 } else {
159                         DBG("carrier off");
160                         remove_network(device, gadget);
161                 }
162         }
163
164         gadget->flags = flags;
165 }
166
167 static int gadget_dev_probe(struct connman_device *device)
168 {
169         struct gadget_data *gadget;
170
171         DBG("device %p", device);
172
173         gadget = g_try_new0(struct gadget_data, 1);
174         if (!gadget)
175                 return -ENOMEM;
176
177         connman_device_set_data(device, gadget);
178
179         gadget->index = connman_device_get_index(device);
180         gadget->flags = 0;
181
182         gadget->watch = connman_rtnl_add_newlink_watch(gadget->index,
183                                                 gadget_newlink, device);
184
185         return 0;
186 }
187
188 static void gadget_dev_remove(struct connman_device *device)
189 {
190         struct gadget_data *gadget = connman_device_get_data(device);
191
192         DBG("device %p", device);
193
194         connman_device_set_data(device, NULL);
195
196         connman_rtnl_remove_watch(gadget->watch);
197
198         remove_network(device, gadget);
199
200         g_free(gadget);
201 }
202
203 static int gadget_dev_enable(struct connman_device *device)
204 {
205         struct gadget_data *gadget = connman_device_get_data(device);
206
207         DBG("device %p", device);
208
209         return connman_inet_ifup(gadget->index);
210 }
211
212 static int gadget_dev_disable(struct connman_device *device)
213 {
214         struct gadget_data *gadget = connman_device_get_data(device);
215
216         DBG("device %p", device);
217
218         return connman_inet_ifdown(gadget->index);
219 }
220
221 static struct connman_device_driver gadget_dev_driver = {
222         .name           = "gadget",
223         .type           = CONNMAN_DEVICE_TYPE_GADGET,
224         .probe          = gadget_dev_probe,
225         .remove         = gadget_dev_remove,
226         .enable         = gadget_dev_enable,
227         .disable        = gadget_dev_disable,
228 };
229
230 static GList *cdc_interface_list = NULL;
231 static GHashTable *cdc_mac_hash = NULL;
232
233 static void add_station(int index)
234 {
235         char *path, line[128] = {'\0'};
236         char *ifname = connman_inet_ifname(index);
237         char *mac;
238         FILE *f;
239
240         if (ifname == NULL)
241                 return;
242
243         path = g_strdup_printf("/sys/class/usb_mode/%s/f_rndis/ethaddr",
244                                         ifname);
245
246         f = fopen(path, "re");
247
248         g_free(ifname);
249         g_free(path);
250
251         if (f == NULL)
252                 return;
253
254         if (fgets(line, sizeof(line), f) == NULL) {
255                 fclose(f);
256                 return;
257         }
258
259         fclose(f);
260
261         mac = g_ascii_strdown(line, strlen(line) - 1);
262         DBG("Add station %s in Technology %d", mac,
263                                                 CONNMAN_SERVICE_TYPE_GADGET);
264
265         g_hash_table_insert(cdc_mac_hash, GINT_TO_POINTER(index),
266                                 mac);
267
268         connman_technology_tethering_add_station(CONNMAN_SERVICE_TYPE_GADGET,
269                                 mac);
270 }
271
272 static void remove_station(int index)
273 {
274         char *mac;
275         mac = g_hash_table_lookup(cdc_mac_hash, GINT_TO_POINTER(index));
276         if (mac == NULL)
277                 return;
278
279         connman_technology_tethering_remove_station(mac);
280
281         g_hash_table_remove(cdc_mac_hash, GINT_TO_POINTER(index));
282 }
283
284 static gboolean remove_all_station(gpointer key, gpointer value, gpointer user_data)
285 {
286         char *mac;
287         mac = value;
288         if (mac == NULL)
289                 return TRUE;
290
291         connman_technology_tethering_remove_station(mac);
292
293         return TRUE;
294 }
295
296
297 static void gadget_tech_add_interface(struct connman_technology *technology,
298                         int index, const char *name, const char *ident)
299 {
300         DBG("index %d name %s ident %s", index, name, ident);
301
302         if (g_list_find(cdc_interface_list, GINT_TO_POINTER((int)index)))
303                 return;
304
305         cdc_interface_list = g_list_prepend(cdc_interface_list,
306                                         (GINT_TO_POINTER((int) index)));
307 }
308
309 static void gadget_tech_remove_interface(struct connman_technology *technology,
310                                                                 int index)
311 {
312         DBG("index %d", index);
313
314         cdc_interface_list = g_list_remove(cdc_interface_list,
315                                         GINT_TO_POINTER((int) index));
316
317         remove_station(index);
318 }
319
320 static void gadget_tech_enable_tethering(struct connman_technology *technology,
321                                                 const char *bridge)
322 {
323         GList *list;
324
325         for (list = cdc_interface_list; list; list = list->next) {
326                 int index = GPOINTER_TO_INT(list->data);
327                 struct connman_device *device =
328                         connman_device_find_by_index(index);
329                 struct gadget_data *gadget;
330
331                 if (device) {
332                         gadget = connman_device_get_data(device);
333                         if (gadget)
334                                 remove_network(device, gadget);
335                 }
336
337                 connman_technology_tethering_notify(technology, true);
338
339                 connman_inet_ifup(index);
340
341                 connman_inet_add_to_bridge(index, bridge);
342
343                 add_station(index);
344         }
345 }
346
347 static void gadget_tech_disable_tethering(struct connman_technology *technology,
348                                                 const char *bridge)
349 {
350         GList *list;
351
352         for (list = cdc_interface_list; list; list = list->next) {
353                 int index = GPOINTER_TO_INT(list->data);
354
355                 connman_inet_remove_from_bridge(index, bridge);
356
357                 remove_station(index);
358
359                 connman_inet_ifdown(index);
360
361                 connman_technology_tethering_notify(technology, false);
362         }
363 }
364
365 static int gadget_tech_set_tethering(struct connman_technology *technology,
366                                 const char *identifier, const char *passphrase,
367                                 const char *bridge, bool enabled, bool hidden)
368 {
369         DBG("bridge %s enabled %d", bridge, enabled);
370
371         if (enabled)
372                 gadget_tech_enable_tethering(technology, bridge);
373         else
374                 gadget_tech_disable_tethering(technology, bridge);
375
376         return 0;
377 }
378
379 static int gadget_tech_probe(struct connman_technology *technology)
380 {
381         DBG("tech probe %p", technology);
382
383         cdc_mac_hash = g_hash_table_new_full(g_direct_hash,
384                                          g_direct_equal, NULL, g_free);
385
386         return 0;
387 }
388
389 static void gadget_tech_remove(struct connman_technology *technology)
390 {
391         DBG("tech remove %p", technology);
392
393         g_list_free(cdc_interface_list);
394
395         cdc_interface_list = NULL;
396
397         if (cdc_mac_hash) {
398                 g_hash_table_foreach_remove(cdc_mac_hash, remove_all_station,
399                                                 NULL);
400                 g_hash_table_destroy(cdc_mac_hash);
401                 cdc_mac_hash = NULL;
402         }
403 }
404
405 static struct connman_technology_driver gadget_tech_driver = {
406         .name                   = "cdc_ethernet",
407         .type                   = CONNMAN_SERVICE_TYPE_GADGET,
408         .probe                  = gadget_tech_probe,
409         .remove                 = gadget_tech_remove,
410         .add_interface          = gadget_tech_add_interface,
411         .remove_interface       = gadget_tech_remove_interface,
412         .set_tethering          = gadget_tech_set_tethering,
413 };
414
415 static int gadget_init(void)
416 {
417         int err;
418
419         err = connman_technology_driver_register(&gadget_tech_driver);
420         if (err < 0) {
421                 return err;
422         }
423
424         err = connman_network_driver_register(&gadget_network_driver);
425         if (err < 0)
426                 return err;
427
428         err = connman_device_driver_register(&gadget_dev_driver);
429         if (err < 0) {
430                 connman_technology_driver_unregister(&gadget_tech_driver);
431                 return err;
432         }
433
434         return 0;
435 }
436
437 static void gadget_exit(void)
438 {
439         connman_technology_driver_unregister(&gadget_tech_driver);
440         connman_network_driver_unregister(&gadget_network_driver);
441         connman_device_driver_unregister(&gadget_dev_driver);
442 }
443
444 CONNMAN_PLUGIN_DEFINE(gadget, "Gadget interface plugin", VERSION,
445                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, gadget_init, gadget_exit)