Update copyright information
[framework/connectivity/connman.git] / plugins / iwmx.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 <string.h>
29
30 #include <glib.h>
31
32 #define CONNMAN_API_SUBJECT_TO_CHANGE
33 #include <connman/plugin.h>
34 #include <connman/device.h>
35 #include <connman/inet.h>
36 #include <connman/log.h>
37
38 #include <WiMaxAPI.h>
39 #include <WiMaxAPIEx.h>
40
41 #include "iwmx.h"
42
43 /*
44  * Connman plugin interface
45  *
46  * This part deals with the connman internals
47  */
48
49 /* WiMAX network driver probe/remove, nops */
50 static int iwmx_cm_network_probe(struct connman_network *nw)
51 {
52         return 0;
53 }
54
55 static void iwmx_cm_network_remove(struct connman_network *nw)
56 {
57 }
58
59 /*
60  * Called by connman when it wants us to tell the device to connect to
61  * the network @network_el; the device is @network_el->parent.
62  *
63  * We do a synchronous call to start the connection; the logic
64  * attached to the status change callback will update the connman
65  * internals once the change happens.
66  */
67 static int iwmx_cm_network_connect(struct connman_network *nw)
68 {
69         int result;
70         struct wmxsdk *wmxsdk;
71         const char *station_name = connman_network_get_identifier(nw);
72
73         wmxsdk = connman_device_get_data(connman_network_get_device(nw));
74         result = iwmx_sdk_connect(wmxsdk, nw);
75         DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
76         return result;
77 }
78
79 /*
80  * Called by connman to have the device @nw->parent
81  * disconnected from @nw.
82  *
83  * We do a synchronous call to start the disconnection; the logic
84  * attached to the status change callback will update the connman
85  * internals once the change happens.
86  */
87 static int iwmx_cm_network_disconnect(struct connman_network *nw)
88 {
89         int result;
90         struct wmxsdk *wmxsdk;
91         const char *station_name = connman_network_get_identifier(nw);
92
93         wmxsdk = connman_device_get_data(connman_network_get_device(nw));
94         result = iwmx_sdk_disconnect(wmxsdk);
95         DBG("(nw %p [%s] wmxsdk %p) = %d\n", nw, station_name, wmxsdk, result);
96         return 0;
97 }
98
99 /*
100  * "Driver" for the networks detected by a device.
101  */
102 static struct connman_network_driver iwmx_cm_network_driver = {
103         .name           = "iwmx",
104         .type           = CONNMAN_NETWORK_TYPE_WIMAX,
105         .probe          = iwmx_cm_network_probe,
106         .remove         = iwmx_cm_network_remove,
107         .connect        = iwmx_cm_network_connect,
108         .disconnect     = iwmx_cm_network_disconnect,
109 };
110
111 /*
112  * A (maybe) new network is available, create/update its data
113  *
114  * If the network is new, we create and register a new element; if it
115  * is not, we reuse the one in the list.
116  *
117  * NOTE:
118  *   wmxsdk->network_mutex has to be locked
119  */
120 struct connman_network *__iwmx_cm_network_available(
121                         struct wmxsdk *wmxsdk, const char *station_name,
122                         const char *station_type,
123                         const void *sdk_nspname, size_t sdk_nspname_size,
124                                                                 int strength)
125 {
126         struct connman_network *nw = NULL;
127         struct connman_device *dev = wmxsdk->dev;
128         char group[3 * strlen(station_name) + 1];
129         unsigned cnt;
130
131         nw = connman_device_get_network(dev, station_name);
132         if (nw == NULL) {
133                 DBG("new network %s", station_name);
134                 nw = connman_network_create(station_name,
135                                             CONNMAN_NETWORK_TYPE_WIMAX);
136                 connman_network_set_index(nw, connman_device_get_index(dev));
137                 connman_network_set_protocol(nw, CONNMAN_NETWORK_PROTOCOL_IP);
138                 connman_network_set_name(nw, station_name);
139                 connman_network_set_blob(nw, "WiMAX.NSP.name",
140                                          sdk_nspname, sdk_nspname_size);
141                 /* FIXME: add roaming info? */
142                 /* Set the group name -- this has to be a unique
143                  * [a-zA-Z0-9_] string common to all the networks that
144                  * are actually the same provider. In WiMAX each
145                  * network from the CAPI is a single provider, so we
146                  * just set this as the network name, encoded in
147                  * hex. */
148                 for (cnt = 0; station_name[cnt] != 0; cnt++)
149                         sprintf(group + 3 * cnt, "%02x", station_name[cnt]);
150                 group[3 * cnt + 1] = 0;
151                 connman_network_set_group(nw, station_name);
152                 if (connman_device_add_network(dev, nw) < 0) {
153                         connman_network_unref(nw);
154                         goto error_add;
155                 }
156         } else
157                 DBG("updating network %s nw %p\n", station_name, nw);
158         connman_network_set_available(nw, TRUE);
159         connman_network_set_strength(nw, strength);
160         connman_network_set_string(nw, "WiMAX Network Type", station_type);
161 error_add:
162         return nw;
163 }
164
165 /*
166  * A new network is available [locking version]
167  *
168  * See __iwmx_cm_network_available() for docs
169  */
170 struct connman_network *iwmx_cm_network_available(
171                         struct wmxsdk *wmxsdk, const char *station_name,
172                         const char *station_type,
173                         const void *sdk_nspname, size_t sdk_nspname_size,
174                                                                 int strength)
175 {
176         struct connman_network *nw;
177
178         g_static_mutex_lock(&wmxsdk->network_mutex);
179         nw = __iwmx_cm_network_available(wmxsdk, station_name, station_type,
180                                         sdk_nspname, sdk_nspname_size,
181                                         strength);
182         g_static_mutex_unlock(&wmxsdk->network_mutex);
183         return nw;
184 }
185
186 /*
187  * The device has been enabled, make sure connman knows
188  */
189 static void iwmx_cm_dev_enabled(struct wmxsdk *wmxsdk)
190 {
191         struct connman_device *dev = wmxsdk->dev;
192         connman_inet_ifup(connman_device_get_index(dev));
193         connman_device_set_powered(dev, TRUE);
194 }
195
196 /*
197  * The device has been disabled, make sure connman is aware of it.
198  */
199 static void iwmx_cm_dev_disabled(struct wmxsdk *wmxsdk)
200 {
201         struct connman_device *dev = wmxsdk->dev;
202         connman_inet_ifdown(connman_device_get_index(dev));
203         connman_device_set_powered(dev, FALSE);
204 }
205
206 /*
207  * The device has been (externally to connman) connnected to a
208  * network, make sure connman knows.
209  *
210  * When the device is connected to a network, this function is called
211  * to change connman's internal state to reflect the fact.
212  *
213  * If the change came from an external entity, that means that our
214  * connect code wasn't called. Our connect code sets
215  * @wmxsdk->connecting_nw to the network we were connecting
216  * to. If it is unset, it means an external entity forced the device
217  * to connect. In that case, we need to find out which network it was
218  * connected to, and create/lookup a @nw for it.
219  *
220  * Once the nw is set, then we are done.
221  */
222 static void iwmx_cm_dev_connected(struct wmxsdk *wmxsdk)
223 {
224         struct connman_network *nw;
225
226         g_mutex_lock(wmxsdk->connect_mutex);
227         nw = wmxsdk->connecting_nw;
228         if (nw == NULL) {
229                 nw = __iwmx_sdk_get_connected_network(wmxsdk);
230                 if (nw == NULL) {
231                         connman_error("wmxsdk: can't find connected network\n");
232                         goto error_nw_find;
233                 }
234         }
235         wmxsdk->nw = connman_network_ref(nw);
236         wmxsdk->connecting_nw = NULL;
237         connman_network_set_method(network, CONNMAN_IPCONFIG_METHOD_DHCP);
238         connman_network_set_connected(nw, TRUE);
239         DBG("connected to network %s\n",
240             connman_network_get_identifier(nw));
241 error_nw_find:
242         g_mutex_unlock(wmxsdk->connect_mutex);
243 }
244
245 /*
246  * The device has been (externally to connman) disconnnected, make
247  * sure connman knows
248  *
249  * We need to reverse the steps done in iwmx_cm_dev_connected().
250  * If the event was caused by an external entity and we had no record
251  * of being connected to a network...well, bad luck. We'll just
252  * pretend it happened ok.
253  */
254 static void __iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
255 {
256         struct connman_network *nw = wmxsdk->nw;
257
258         if (nw != NULL) {
259                 DBG("disconnected from network %s\n",
260                                         connman_network_get_identifier(nw));
261                 connman_network_set_connected(nw, FALSE);
262                 connman_network_unref(nw);
263                 wmxsdk->nw = NULL;
264         } else
265                 DBG("disconnected from unknown network\n");
266 }
267
268 /*
269  * The device has been disconnnected, make sure connman knows
270  *
271  * See __iwmx_cm_dev_disconnect() for more information.
272  */
273 static void iwmx_cm_dev_disconnected(struct wmxsdk *wmxsdk)
274 {
275         g_mutex_lock(wmxsdk->connect_mutex);
276         __iwmx_cm_dev_disconnected(wmxsdk);
277         g_mutex_unlock(wmxsdk->connect_mutex);
278 }
279
280 /*
281  * Handle a change in state
282  *
283  * This is were most of the action happens. When the device changes
284  * state, this will catch it (through the state change callback or an
285  * explicit call) and call iwmx_cm_dev_*ed() to indicate to connman what
286  * happened.
287  *
288  * Finally, cache the new device status.
289  */
290 void __iwmx_cm_state_change(struct wmxsdk *wmxsdk,
291                                         WIMAX_API_DEVICE_STATUS __new_status)
292 {
293         WIMAX_API_DEVICE_STATUS __old_status = wmxsdk->status;
294         WIMAX_API_DEVICE_STATUS old_status;
295         WIMAX_API_DEVICE_STATUS new_status;
296
297         /*
298          * Simplify state transition computations.
299          *
300          * For practical effects, some states are the same
301          */
302
303 #if HAVE_IWMXSDK_STATUS_IDLE
304         /* Conection_Idle is the same as Data_Connected */
305         if (__old_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
306                 old_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
307         else
308                 old_status = __old_status;
309         if (__new_status == WIMAX_API_DEVICE_STATUS_Connection_Idle)
310                 new_status = WIMAX_API_DEVICE_STATUS_Data_Connected;
311         else
312                 new_status = __new_status;
313 #endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
314         /* Radio off: all are just RF_OFF_SW (the highest) */
315         switch (__old_status) {
316         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
317         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
318         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
319                 old_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
320                 break;
321         default:
322                 old_status = __old_status;
323                 break;
324         }
325
326         switch (__new_status) {
327         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
328         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
329         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
330                 new_status = WIMAX_API_DEVICE_STATUS_RF_OFF_SW;
331                 break;
332         default:
333                 new_status = __new_status;
334                 break;
335         }
336
337         /* If no real state change, do nothing */
338         if (old_status == new_status) {
339                 DBG("no state changed\n");
340                 return;
341         } else
342                 DBG("state change from %d (%d: %s) to %d (%d: %s)\n",
343                     old_status, __old_status,
344                     iwmx_sdk_dev_status_to_str(__old_status),
345                     new_status, __new_status,
346                     iwmx_sdk_dev_status_to_str(__new_status));
347
348         /* Cleanup old state */
349         switch (old_status) {
350         case WIMAX_API_DEVICE_STATUS_UnInitialized:
351                 /* This means the plugin is starting but the device is
352                  * in some state already, so we need to update our
353                  * internal knowledge of it. */
354                 if (new_status > WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
355                         iwmx_cm_dev_enabled(wmxsdk);
356                 break;
357         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
358                 /* This means the radio is being turned on, so enable
359                  * the device ( unless going to uninitialized). */
360                 if (new_status != WIMAX_API_DEVICE_STATUS_RF_OFF_SW)
361                         iwmx_cm_dev_enabled(wmxsdk);
362                 break;
363         case WIMAX_API_DEVICE_STATUS_Ready:
364                 break;
365         case WIMAX_API_DEVICE_STATUS_Scanning:
366                 break;
367         case WIMAX_API_DEVICE_STATUS_Connecting:
368                 break;
369         case WIMAX_API_DEVICE_STATUS_Data_Connected:
370                 iwmx_cm_dev_disconnected(wmxsdk);
371                 break;
372         default:
373                 connman_error("wmxsdk: unknown old status %d\n", old_status);
374                 return;
375         };
376
377         /* Implement new state */
378         switch (new_status) {
379         case WIMAX_API_DEVICE_STATUS_UnInitialized:
380                 break;
381         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
382                 /* This means the radio is being turned off, so
383                  * disable the device unless coming from uninitialized. */
384                 if (old_status != WIMAX_API_DEVICE_STATUS_UnInitialized)
385                         iwmx_cm_dev_disabled(wmxsdk);
386                 break;
387         case WIMAX_API_DEVICE_STATUS_Ready:
388                 break;
389         case WIMAX_API_DEVICE_STATUS_Scanning:
390                 break;
391         case WIMAX_API_DEVICE_STATUS_Connecting:
392                 break;
393         case WIMAX_API_DEVICE_STATUS_Data_Connected:
394                 iwmx_cm_dev_connected(wmxsdk);
395                 break;
396         default:
397                 connman_error("wmxsdk: unknown new status %d\n", old_status);
398                 return;
399         };
400         wmxsdk->status = __new_status;
401 }
402
403 /*
404  * Implement a device state transition [locking version]
405  *
406  * See __iwmx_cm_state_change()
407  */
408 void iwmx_cm_state_change(struct wmxsdk *wmxsdk,
409                                  WIMAX_API_DEVICE_STATUS __new_status)
410 {
411         g_mutex_lock(wmxsdk->status_mutex);
412         __iwmx_cm_state_change(wmxsdk, __new_status);
413         g_mutex_unlock(wmxsdk->status_mutex);
414 }
415
416 /*
417  * Read the cached device status
418  */
419 WIMAX_API_DEVICE_STATUS iwmx_cm_status_get(struct wmxsdk *wmxsdk)
420 {
421         WIMAX_API_DEVICE_STATUS status;
422
423         g_mutex_lock(wmxsdk->status_mutex);
424         status = wmxsdk->status;
425         g_mutex_unlock(wmxsdk->status_mutex);
426         return status;
427 }
428
429 /*
430  * Called by connman when a device is enabled by the user
431  *
432  * We need to turn the radio on; the state change function will poke
433  * the internals.
434  */
435 static int iwmx_cm_enable(struct connman_device *dev)
436 {
437         int result;
438         struct wmxsdk *wmxsdk = connman_device_get_data(dev);
439
440         connman_inet_ifup(connman_device_get_index(dev));
441         result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_ON);
442         return result;
443 }
444
445 /*
446  * Called by connman when a device is disabled by the user
447  *
448  * Simple: just make sure the radio is off; the state change function
449  * will poke the internals.
450  */
451 static int iwmx_cm_disable(struct connman_device *dev)
452 {
453         int result;
454         struct wmxsdk *wmxsdk = connman_device_get_data(dev);
455
456         result = iwmx_sdk_rf_state_set(wmxsdk, WIMAX_API_RF_OFF);
457         connman_inet_ifdown(connman_device_get_index(dev));
458         return 0;
459 }
460
461 /*
462  * Probe deferred call from when the mainloop is idle
463  *
464  * probe() schedules this to be called from the mainloop when idle to
465  * do a device status evaluation. Needed because of an internal race
466  * condition in connman. FIXME: deploy into _probe() when fixed.
467  */
468 static gboolean __iwmx_cm_probe_dpc(gpointer _wmxsdk)
469 {
470         int result;
471         struct wmxsdk *wmxsdk = _wmxsdk;
472         result = iwmx_sdk_get_device_status(wmxsdk);
473         if (result < 0)
474                 connman_error("wmxsdk: can't get status: %d\n", result);
475         else
476                 iwmx_cm_state_change(wmxsdk, result);
477         return FALSE;
478 }
479
480 /*
481  * Called by connman when a new device pops in
482  *
483  * We allocate our private structure, register with the WiMAX API,
484  * open their device, subscribe to all the callbacks.
485  *
486  * At the end, we launch a deferred call (to work around current
487  * connman issues that need to be fixed in the future) and update the
488  * device's status. This allows us to pick up the current status and
489  * adapt connman's idea of the device to it.
490  */
491 static int iwmx_cm_probe(struct connman_device *dev)
492 {
493         int result;
494         struct wmxsdk *wmxsdk = NULL;
495
496         wmxsdk = connman_device_get_data(dev);
497         if (wmxsdk == NULL)
498                 /* not called from a discovery done by the WiMAX
499                  * Network Service, ignore */
500                 return -ENODEV;
501
502         result = iwmx_sdk_setup(wmxsdk);
503         if (result < 0)
504                 goto error_setup;
505
506         /* There is a race condition in the connman core that doesn't
507          * allow us to call this directly and things to work properly
508          * FIXME FIXME FIXME: merge _dpc call in here when connman is fixed */
509         g_idle_add(__iwmx_cm_probe_dpc, wmxsdk);
510         return 0;
511
512         iwmx_sdk_remove(wmxsdk);
513 error_setup:
514         return result;
515 }
516
517 /*
518  * Called when a device is removed from connman
519  *
520  * Cleanup all that is done in _probe. Remove callbacks, unregister
521  * from the WiMAX API.
522  */
523 static void iwmx_cm_remove(struct connman_device *dev)
524 {
525         struct wmxsdk *wmxsdk = connman_device_get_data(dev);
526         iwmx_sdk_remove(wmxsdk);
527 }
528
529 /*
530  * Called by connman to ask the device to scan for networks
531  *
532  * We have set in the WiMAX API the scan result callbacks, so we just
533  * start a simple scan (not a wide one).
534  *
535  * First we obtain the current list of networks and pass it to the
536  * callback processor. Then we start an scan cycle.
537  */
538 static int iwmx_cm_scan(struct connman_device *dev)
539 {
540         struct wmxsdk *wmxsdk = connman_device_get_data(dev);
541         return iwmx_sdk_scan(wmxsdk);
542 }
543
544 /*
545  * Driver for a WiMAX API based device.
546  */
547 static struct connman_device_driver iwmx_cm_device_driver = {
548         .name           = "iwmx",
549         .type           = CONNMAN_DEVICE_TYPE_WIMAX,
550         .probe          = iwmx_cm_probe,
551         .remove         = iwmx_cm_remove,
552         .enable         = iwmx_cm_enable,
553         .disable        = iwmx_cm_disable,
554         .scan           = iwmx_cm_scan,
555 };
556
557 static int iwmx_cm_init(void)
558 {
559         int result;
560
561         result = connman_device_driver_register(&iwmx_cm_device_driver);
562         if (result < 0)
563                 goto error_driver_register;
564         result = connman_network_driver_register(&iwmx_cm_network_driver);
565         if (result < 0)
566                 goto error_network_driver_register;
567         result = iwmx_sdk_api_init();
568         if (result < 0)
569                 goto error_iwmx_sdk_init;
570         return 0;
571
572 error_iwmx_sdk_init:
573         connman_network_driver_unregister(&iwmx_cm_network_driver);
574 error_network_driver_register:
575         connman_device_driver_unregister(&iwmx_cm_device_driver);
576 error_driver_register:
577         return result;
578 }
579
580 static void iwmx_cm_exit(void)
581 {
582         iwmx_sdk_api_exit();
583         connman_network_driver_unregister(&iwmx_cm_network_driver);
584         connman_device_driver_unregister(&iwmx_cm_device_driver);
585 }
586
587 CONNMAN_PLUGIN_DEFINE(iwmx, "Intel WiMAX SDK / Common API plugin",
588                         CONNMAN_VERSION, CONNMAN_PLUGIN_PRIORITY_LOW,
589                                                 iwmx_cm_init, iwmx_cm_exit);