service: Simplify nameserver route adding and removing
[framework/connectivity/connman.git] / plugins / iwmxsdk.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 <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <net/if.h>
31
32 #include <glib.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/device.h>
36 #include <connman/inet.h>
37 #include <connman/log.h>
38
39 #include <WiMaxType.h>
40 #include <WiMaxAPI.h>
41 #include <WiMaxAPIEx.h>
42
43 #include "iwmx.h"
44
45 /* Yes, this is dirty; see above on IWMX_SDK_DEV_MAX*/
46 static struct wmxsdk *g_iwmx_sdk_devs[IWMX_SDK_DEV_MAX];
47
48 static struct wmxsdk *deviceid_to_wmxsdk(WIMAX_API_DEVICE_ID *device_id)
49 {
50         unsigned cnt;
51         for (cnt = 0; cnt < IWMX_SDK_DEV_MAX; cnt++) {
52                 struct wmxsdk *wmxsdk = g_iwmx_sdk_devs[cnt];
53                 if (wmxsdk &&
54                     wmxsdk->device_id.deviceIndex == device_id->deviceIndex)
55                         return wmxsdk;
56         }
57         return NULL;
58 }
59
60 static WIMAX_API_DEVICE_ID g_api;
61
62
63 /*
64  * FIXME: pulled it it out of some hole
65  *
66  * the cinr to percentage computation comes from the L3/L4 doc
67  *
68  * But some other places (L4 code) have a more complex, seemingly
69  * logarithmical computation.
70  *
71  * Oh well...
72  *
73  */
74 static int cinr_to_percentage(int cinr)
75 {
76         int strength;
77         if (cinr <= -5)
78                 strength = 0;
79         else if (cinr >= 25)
80                 strength = 100;
81         else    /* Calc percentage on the value from -5 to 25 */
82                 strength = ((100UL * (cinr - -5)) / (25 - -5));
83         return strength;
84 }
85
86 /*
87  * Convert a WiMAX API status to an string.
88  */
89 const char *iwmx_sdk_dev_status_to_str(WIMAX_API_DEVICE_STATUS status)
90 {
91         switch (status) {
92         case WIMAX_API_DEVICE_STATUS_UnInitialized:
93                 return "Uninitialized";
94                 break;
95         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
96                 return "Device RF Off(both H/W and S/W)";
97                 break;
98         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
99                 return "Device RF Off(via H/W switch)";
100         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
101                 return "Device RF Off(via S/W switch)";
102         case WIMAX_API_DEVICE_STATUS_Ready:
103                 return "Device is ready";
104         case WIMAX_API_DEVICE_STATUS_Scanning:
105                 return "Device is scanning";
106         case WIMAX_API_DEVICE_STATUS_Connecting:
107                 return "Connection in progress";
108         case WIMAX_API_DEVICE_STATUS_Data_Connected:
109                 return "Layer 2 connected";
110 #if HAVE_IWMXSDK_STATUS_IDLE
111         case WIMAX_API_DEVICE_STATUS_Connection_Idle:
112                 return "Idle connection";
113 #endif /* #if HAVE_IWMXSDK_STATUS_IDLE */
114         default:
115                 return "unknown state";
116         }
117 }
118
119 /*
120  * Get the device's status from the device
121  *
122  * Does NOT cache the result
123  * Does NOT trigger a state change in connman
124  *
125  * Returns < 0 errno code on error, status code if ok.
126  */
127 WIMAX_API_DEVICE_STATUS iwmx_sdk_get_device_status(struct wmxsdk *wmxsdk)
128 {
129         WIMAX_API_RET r;
130         char errstr[512];
131         UINT32 errstr_size = sizeof(errstr);
132
133         WIMAX_API_DEVICE_STATUS dev_status;
134         WIMAX_API_CONNECTION_PROGRESS_INFO pi;
135
136         r = GetDeviceStatus(&wmxsdk->device_id, &dev_status, &pi);
137         if (r != WIMAX_API_RET_SUCCESS) {
138                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
139                 connman_error("wmxsdk: Cannot read device state: %d (%s)\n",
140                         r, errstr);
141                 dev_status = -EIO;
142         }
143         return dev_status;
144 }
145
146 /*
147  * Get the device's status from the device but return a string describing it
148  *
149  * Same conditions as iwmx_sdk_get_device_status().
150  */
151 static const char *iwmx_sdk_get_device_status_str(struct wmxsdk *wmxsdk)
152 {
153         const char *result;
154         WIMAX_API_DEVICE_STATUS dev_status;
155
156         dev_status = iwmx_sdk_get_device_status(wmxsdk);
157         if ((int) dev_status < 0)
158                 result = "cannot read device state";
159         else
160                 result = iwmx_sdk_dev_status_to_str(dev_status);
161         return result;
162 }
163
164 /*
165  * Translate a WiMAX network type to a readable name.
166  */
167 static const char *iwmx_sdk_network_type_name(enum _WIMAX_API_NETWORK_TYPE network_type)
168 {
169         static char *network_type_name[] = {
170                 [WIMAX_API_HOME] = "",
171                 [WIMAX_API_PARTNER] = " (partner network)",
172                 [WIMAX_API_ROAMING_PARTNER] = " (roaming partner network)",
173                 [WIMAX_API_UNKNOWN] = " (unknown network)",
174         };
175         if (network_type > WIMAX_API_UNKNOWN)
176                 return "(BUG! UNKNOWN NETWORK_TYPE MODE)";
177         else
178                 return network_type_name[network_type];
179 }
180
181 /*
182  * If the device is connected but we don't know about the network,
183  * create the knowledge of it.
184  *
185  * Asks the WiMAX API to report which NSP we are connected to and we
186  * create/update a network_el in the device's network list. Then
187  * return it.
188  *
189  * Returns NULL on error.
190  *
191  * NOTE: wmxsdk->network_mutex has to be taken
192  */
193 struct connman_network *__iwmx_sdk_get_connected_network(struct wmxsdk *wmxsdk)
194 {
195         struct connman_network *nw;
196
197         WIMAX_API_CONNECTED_NSP_INFO nsp_info;
198         WIMAX_API_RET r;
199         char errstr[512];
200         UINT32 errstr_size = sizeof(errstr);
201
202         /* The device is getting connected due to an external (to
203          * connman) event; find which is the nw we are getting
204          * connected to. if we don't have it, add it */
205         r = GetConnectedNSP(&wmxsdk->device_id, &nsp_info);
206         if (r != WIMAX_API_RET_SUCCESS) {
207                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
208                 connman_error(
209                         "wmxsdk: Cannot get connected NSP info: %d (%s)\n",
210                         r, errstr);
211                 strcpy((char *) nsp_info.NSPName, "unknown");
212                 nw = iwmx_cm_network_available(
213                         wmxsdk, "unknown",
214                         nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
215                         cinr_to_percentage(nsp_info.CINR - 10));
216         } else {
217                 nw = iwmx_cm_network_available(
218                         wmxsdk, (char *) nsp_info.NSPName,
219                         nsp_info.NSPName, strlen((char *) nsp_info.NSPName) + 1,
220                         cinr_to_percentage(nsp_info.CINR - 10));
221         }
222         return nw;
223 }
224
225 /*
226  * Callback for a RF State command
227  *
228  * Called by the WiMAX API when a command sent to change the RF state
229  * is completed. This is just a confirmation of what happened with the
230  * command.
231  *
232  * We don't do anything, as when the device changes state, the state
233  * change callback is called and that will fiddle with the connman
234  * internals.
235  */
236 static void __iwmx_sdk_rf_state_cb(WIMAX_API_DEVICE_ID *device_id,
237                                    WIMAX_API_RF_STATE rf_state)
238 {
239         DBG("rf_state changed to %d\n", rf_state);
240 }
241
242 /*
243  * Turn the radio on or off
244  *
245  * First it checks that we are in the right state before doing
246  * anything; there might be no need to do anything.
247  *
248  * Issue a command to the WiMAX API, wait for a callback confirming it
249  * is done. Sometimes the callback is missed -- in that case, do force
250  * a state change evaluation.
251  *
252  * Frustration note:
253  *
254  *      Geezoos efing Xist, they make difficult even the most simple
255  *      of the operations
256  *
257  *      This thing is definitely a pain. If the radio is ON already
258  *      and you switch it on again...well, there is no way to tell
259  *      because you don't get a callback saying it basically
260  *      suceeded. But on the other hand, if the thing was in a
261  *      different state and action needs to be taken, you have to wait
262  *      for a callback to confirm it's done. However, there is also an
263  *      state change callback, which is almost the same, so now you
264  *      have to handle things in two "unrelated" threads of execution.
265  *
266  *      How the shpx are you expected to tell the difference? Check
267  *      status first? On timeout? Nice gap (eighteen wheeler size) for
268  *      race conditions.
269  */
270 int iwmx_sdk_rf_state_set(struct wmxsdk *wmxsdk, WIMAX_API_RF_STATE rf_state)
271 {
272         int result;
273
274         WIMAX_API_RET r;
275         char errstr[512];
276         UINT32 errstr_size = sizeof(errstr);
277         WIMAX_API_DEVICE_STATUS dev_status;
278
279         g_assert(rf_state == WIMAX_API_RF_ON || rf_state == WIMAX_API_RF_OFF);
280
281         /* Guess what the current radio state is; if it is ON
282          * already, don't redo it. */
283         dev_status = iwmx_sdk_get_device_status(wmxsdk);
284         if ((int) dev_status < 0) {
285                 result = dev_status;
286                 goto error_get_status;
287         }
288         switch (dev_status) {
289         case WIMAX_API_DEVICE_STATUS_UnInitialized:
290                 result = -EINVAL;
291                 goto error_cant_do;
292         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
293         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
294                 connman_error(
295                         "wmxsdk: cannot turn on radio: hw switch is off\n");
296                 result = -EPERM;
297                 goto error_cant_do;
298                 break;
299         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
300                 if (rf_state == WIMAX_API_RF_OFF) {
301                         result = 0;
302                         DBG("radio is already off\n");
303                         goto out_done;
304                 }
305                 break;
306         case WIMAX_API_DEVICE_STATUS_Ready:
307         case WIMAX_API_DEVICE_STATUS_Scanning:
308         case WIMAX_API_DEVICE_STATUS_Connecting:
309         case WIMAX_API_DEVICE_STATUS_Data_Connected:
310 #if HAVE_IWMXSDK_STATUS_IDLE
311         case WIMAX_API_DEVICE_STATUS_Connection_Idle:
312 #endif
313                 if (rf_state == WIMAX_API_RF_ON) {
314                         result = 0;
315                         DBG("radio is already on\n");
316                         goto out_done;
317                 }
318                 break;
319         default:
320                 g_assert(1);
321         }
322         /* Ok, flip the radio */
323         r = CmdControlPowerManagement(&wmxsdk->device_id, rf_state);
324         if (r != WIMAX_API_RET_SUCCESS) {
325                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
326                 connman_error("wmxsdk: Cannot flip radio to %d: %d (%s) "
327                               "[device is in state %s]\n",
328                               rf_state, r, errstr,
329                               iwmx_sdk_get_device_status_str(wmxsdk));
330                 result = -EIO;
331         } else
332                 result = -EINPROGRESS;
333 out_done:
334 error_cant_do:
335 error_get_status:
336         return result;
337 }
338
339 /*
340  * Callback for a Connect command
341  *
342  * Called by the WiMAX API when a command sent to connect is
343  * completed. This is just a confirmation of what happened with the
344  * command.
345  *
346  * WE DON'T DO MUCH HERE -- the real meat happens when a state change
347  * callback is sent, where we detect we move to connected state (or
348  * from disconnecting to something else); the state change callback is
349  * called and that will fiddle with the connman internals.
350  */
351 static void __iwmx_sdk_connect_cb(WIMAX_API_DEVICE_ID *device_id,
352                                   WIMAX_API_NETWORK_CONNECTION_RESP resp)
353 {
354         WIMAX_API_DEVICE_STATUS status;
355         struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
356
357         status = iwmx_cm_status_get(wmxsdk);
358         if (resp == WIMAX_API_CONNECTION_SUCCESS) {
359                 if (status != WIMAX_API_DEVICE_STATUS_Data_Connected
360 #if HAVE_IWMXSDK_STATUS_IDLE
361                     && status != WIMAX_API_DEVICE_STATUS_Connection_Idle
362 #endif
363                         )
364                         connman_error("wmxsdk: error: connect worked, but state"
365                                       " didn't change (now it is %d [%s])\n",
366                                       status,
367                                       iwmx_sdk_dev_status_to_str(status));
368         } else
369                 connman_error("wmxsdk: failed to connect (status %d: %s)\n",
370                               status, iwmx_sdk_dev_status_to_str(status));
371 }
372
373 /*
374  * Connect to a network
375  *
376  * This function starts the connection process to a given network;
377  * when the device changes status, the status change callback will
378  * tell connman if the network is finally connected or not.
379  *
380  * One of the reasons it is done like that is to allow external tools
381  * to control the device and the plugin just passing the status so
382  * connman displays the right info.
383  */
384 int iwmx_sdk_connect(struct wmxsdk *wmxsdk, struct connman_network *nw)
385 {
386         int result;
387
388         WIMAX_API_RET r;
389         char errstr[512];
390         UINT32 errstr_size = sizeof(errstr);
391         WIMAX_API_DEVICE_STATUS dev_status;
392         const char *station_name = connman_network_get_identifier(nw);
393         const void *sdk_nspname;
394         unsigned int sdk_nspname_size;
395
396         g_mutex_lock(wmxsdk->connect_mutex);
397         /* Guess what the current radio state is; if it is ON
398          * already, don't redo it. */
399         dev_status = iwmx_cm_status_get(wmxsdk);
400         if ((int) dev_status < 0) {
401                 result = dev_status;
402                 goto error_get_status;
403         }
404         switch (dev_status) {
405         case WIMAX_API_DEVICE_STATUS_UnInitialized:
406                 connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
407                 result = -EINVAL;
408                 goto error_cant_do;
409         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
410         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
411         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
412                 connman_error("wmxsdk: Cannot connect: radio is off\n");
413                 result = -EPERM;
414                 goto error_cant_do;
415         case WIMAX_API_DEVICE_STATUS_Ready:
416         case WIMAX_API_DEVICE_STATUS_Scanning:
417                 break;
418         case WIMAX_API_DEVICE_STATUS_Connecting:
419                 DBG("Connect already pending, waiting for it\n");
420                 result = -EINPROGRESS;
421                 goto error_cant_do;
422         case WIMAX_API_DEVICE_STATUS_Data_Connected:
423 #if HAVE_IWMXSDK_STATUS_IDLE
424         case WIMAX_API_DEVICE_STATUS_Connection_Idle:
425 #endif
426                 connman_error("wmxsdk: BUG? need to disconnect?\n");
427                 result = -EINVAL;
428                 goto error_cant_do;
429         default:
430                 g_assert(1);
431         }
432
433         /* Ok, do the connection, wait for a callback */
434         wmxsdk->connecting_nw = connman_network_ref(nw);
435         sdk_nspname = connman_network_get_blob(nw, "WiMAX.NSP.name",
436                                                         &sdk_nspname_size);
437         g_assert(sdk_nspname != NULL);
438         r = CmdConnectToNetwork(&wmxsdk->device_id, (void *) sdk_nspname, 0, 0);
439         if (r != WIMAX_API_RET_SUCCESS) {
440                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
441                 connman_error("wmxsdk: Cannot connect to network %s: %d (%s)"
442                               " - device is in state '%s'\n",
443                               station_name, r, errstr,
444                               iwmx_sdk_get_device_status_str(wmxsdk));
445                 result = -EIO;
446                 connman_network_unref(nw);
447                 wmxsdk->connecting_nw = NULL;
448         } else
449                 result = -EINPROGRESS;
450 error_cant_do:
451 error_get_status:
452         g_mutex_unlock(wmxsdk->connect_mutex);
453         return result;
454 }
455
456 /*
457  * Callback for a Disconnect command
458  *
459  * Called by the WiMAX API when a command sent to connect is
460  * completed. This is just a confirmation of what happened with the
461  * command.
462  *
463  * When the device changes state, the state change callback is called
464  * and that will fiddle with the connman internals.
465  *
466  * We just update the result of the command and wake up anybody who is
467  * waiting for this conditional variable.
468  */
469 static void __iwmx_sdk_disconnect_cb(WIMAX_API_DEVICE_ID *device_id,
470                                      WIMAX_API_NETWORK_CONNECTION_RESP resp)
471 {
472         struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
473         WIMAX_API_DEVICE_STATUS status;
474
475         status = iwmx_cm_status_get(wmxsdk);
476         if (resp == WIMAX_API_CONNECTION_SUCCESS) {
477                 if (status == WIMAX_API_DEVICE_STATUS_Data_Connected
478 #if HAVE_IWMXSDK_STATUS_IDLE
479                     || status == WIMAX_API_DEVICE_STATUS_Connection_Idle
480 #endif
481                         )
482                         connman_error("wmxsdk: error: disconnect worked, "
483                                       "but state didn't change (now it is "
484                                       "%d [%s])\n", status,
485                                       iwmx_sdk_dev_status_to_str(status));
486         } else
487                 connman_error("wmxsdk: failed to disconnect (status %d: %s)\n",
488                               status, iwmx_sdk_dev_status_to_str(status));
489 }
490
491 /*
492  * Disconnect from a network
493  *
494  * This function tells the device to disconnect; the state change
495  * callback will take care of inform connman's internals.
496  */
497 int iwmx_sdk_disconnect(struct wmxsdk *wmxsdk)
498 {
499         int result;
500
501         WIMAX_API_RET r;
502         char errstr[512];
503         UINT32 errstr_size = sizeof(errstr);
504         WIMAX_API_DEVICE_STATUS dev_status;
505
506         g_mutex_lock(wmxsdk->connect_mutex);
507         /* Guess what the current radio state is; if it is ON
508          * already, don't redo it. */
509         dev_status = iwmx_sdk_get_device_status(wmxsdk);
510         if ((int) dev_status < 0) {
511                 result = dev_status;
512                 goto error_get_status;
513         }
514         switch (dev_status) {
515         case WIMAX_API_DEVICE_STATUS_UnInitialized:
516                 connman_error("wmxsdk: SW BUG? HW is uninitialized\n");
517                 result = -EINVAL;
518                 goto error_cant_do;
519         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW:
520         case WIMAX_API_DEVICE_STATUS_RF_OFF_HW:
521         case WIMAX_API_DEVICE_STATUS_RF_OFF_SW:
522                 DBG("Cannot disconnect, radio is off; ignoring\n");
523                 result = 0;
524                 goto error_cant_do;
525         case WIMAX_API_DEVICE_STATUS_Ready:
526         case WIMAX_API_DEVICE_STATUS_Scanning:
527                 DBG("Cannot disconnect, already disconnected; ignoring\n");
528                 result = 0;
529                 goto error_cant_do;
530         case WIMAX_API_DEVICE_STATUS_Connecting:
531         case WIMAX_API_DEVICE_STATUS_Data_Connected:
532 #if HAVE_IWMXSDK_STATUS_IDLE
533         case WIMAX_API_DEVICE_STATUS_Connection_Idle:
534 #endif
535                 break;
536         default:
537                 g_assert(1);
538         }
539         /* Ok, flip the radio */
540         r = CmdDisconnectFromNetwork(&wmxsdk->device_id);
541         if (r != WIMAX_API_RET_SUCCESS) {
542                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
543                 connman_error("wmxsdk: Cannot disconnect from network: "
544                               "%d (%s)\n", r, errstr);
545                 result = -EIO;
546         } else
547                 result = -EINPROGRESS;
548 error_cant_do:
549 error_get_status:
550         g_mutex_unlock(wmxsdk->connect_mutex);
551         return result;
552 }
553
554 /*
555  * Callback for state change messages
556  *
557  * Just pass them to the state transition handler
558  */
559 static void __iwmx_sdk_state_change_cb(WIMAX_API_DEVICE_ID *device_id,
560                                         WIMAX_API_DEVICE_STATUS status,
561                                         WIMAX_API_STATUS_REASON reason,
562                                         WIMAX_API_CONNECTION_PROGRESS_INFO pi)
563 {
564         struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
565         iwmx_cm_state_change(wmxsdk, status);
566 }
567
568 /*
569  * Called by _iwmx_sdk_*scan_cb() when [wide or preferred] scan results
570  * are available.
571  *
572  * From here we update the connman core idea of which networks are
573  * available.
574  */
575 static void __iwmx_sdk_scan_common_cb(WIMAX_API_DEVICE_ID *device_id,
576                                       WIMAX_API_NSP_INFO_EX *nsp_list,
577                                       UINT32 nsp_list_size)
578 {
579         struct wmxsdk *wmxsdk = deviceid_to_wmxsdk(device_id);
580         unsigned itr;
581         char station_name[256];
582
583         g_static_mutex_lock(&wmxsdk->network_mutex);
584         for (itr = 0; itr < nsp_list_size; itr++) {
585                 int strength;
586                 WIMAX_API_NSP_INFO_EX *nsp_info = &nsp_list[itr];
587                 snprintf(station_name, sizeof(station_name),
588                          "%s", (char *)nsp_info->NSPName);
589                 /* CAPI is reporing link quality as zero -- if it is
590                  * zero, check if it is a bug by computing it based on
591                  * CINR. If it is different, use the computed one. */
592                 strength = nsp_info->linkQuality;
593                 if (strength == 0) {    /* huh */
594                         int linkq_expected =
595                                 cinr_to_percentage(nsp_info->CINR - 10);
596                         if (linkq_expected != strength)
597                                 strength = linkq_expected;
598                 }
599
600                 __iwmx_cm_network_available(
601                         wmxsdk, station_name,
602                         nsp_info->NSPName,
603                         strlen((char *) nsp_info->NSPName) + 1,
604                         strength);
605         }
606         g_static_mutex_unlock(&wmxsdk->network_mutex);
607 }
608
609 /*
610  * Called by the WiMAX API when we get a wide scan result
611  *
612  * We treat them same as wide, so we just call that.
613  */
614 static void __iwmx_sdk_wide_scan_cb(WIMAX_API_DEVICE_ID *device_id,
615                                     WIMAX_API_NSP_INFO_EX *nsp_list,
616                                     UINT32 nsp_list_size)
617 {
618         __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
619 }
620
621 /*
622  * Called by the WiMAX API when we get a normal (non wide) scan result
623  *
624  * We treat them same as wide, so we just call that.
625  */
626 static void __iwmx_sdk_scan_cb(WIMAX_API_DEVICE_ID *device_id,
627                                 WIMAX_API_NSP_INFO_EX *nsp_list,
628                                 UINT32 nsp_list_size, UINT32 searchProgress)
629 {
630         __iwmx_sdk_scan_common_cb(device_id, nsp_list, nsp_list_size);
631 }
632
633 /*
634  * Called to ask the device to scan for networks
635  *
636  * We don't really scan as the WiMAX SDK daemon scans in the
637  * background for us. We just get the results. See iwmx_sdk_setup().
638  */
639 int iwmx_sdk_scan(struct wmxsdk *wmxsdk)
640 {
641         int result;
642
643         UINT32 nsp_list_length = 10;
644         WIMAX_API_NSP_INFO_EX nsp_list[10];     /* FIXME: up to 32? */
645
646         WIMAX_API_RET r;
647         char errstr[512];
648         UINT32 errstr_size = sizeof(errstr);
649
650         r = GetNetworkListEx(&wmxsdk->device_id, nsp_list, &nsp_list_length);
651         if (r != WIMAX_API_RET_SUCCESS) {
652                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
653                 connman_error("wmxsdk: Cannot get network list: %d (%s)\n",
654                               r, errstr);
655                 result = -EIO;
656                 goto error_scan;
657         }
658
659         if (nsp_list_length == 0)
660                 DBG("no networks\n");
661         else
662                 __iwmx_sdk_scan_common_cb(&wmxsdk->device_id, nsp_list,
663                                         nsp_list_length);
664         result = 0;
665 error_scan:
666         return result;
667 }
668
669 /*
670  * Initialize the WiMAX API, register with it, setup callbacks
671  *
672  * Called through
673  *
674  * iwmx_sdk_dev_add
675  *   connman_inet_create_device
676  *      connman_register
677  *         iwmx_cm_probe()
678  */
679 int iwmx_sdk_setup(struct wmxsdk *wmxsdk)
680 {
681         int result;
682
683         WIMAX_API_RET r;
684
685         char errstr[512];
686         UINT32 errstr_size = sizeof(errstr);
687
688         result = -ENFILE;
689
690         /* device_id initialized by iwmx_sdk_dev_add */
691
692         r = WiMaxDeviceOpen(&wmxsdk->device_id);
693         if (r != WIMAX_API_RET_SUCCESS) {
694                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
695                 connman_error("wmxsdk: Cannot open device: %d (%s)\n",
696                               r, errstr);
697                 goto error_wimaxdeviceopen;
698         }
699
700         /*
701          * We scan in auto mode (in the background)
702          *
703          * Otherwise is messy -- if we have connman triggering a scan
704          * when we call iwmx_cm_scan() -> iwmx_sdk_scan(), most of the
705          * times that causes a race condition when the UI asks for a
706          * scan right before displaying the network menu. As there is
707          * no way to cancel an ongoing scan before connecting, we are
708          * stuck. So we do auto bg and have iwmx_sdk_scan() just return
709          * the current network list.
710          */
711         r = SetConnectionMode(&wmxsdk->device_id,
712                               WIMAX_API_CONNECTION_AUTO_SCAN_MANUAL_CONNECT);
713         if (r != WIMAX_API_RET_SUCCESS) {
714                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
715                 connman_error("wmxsdk: Cannot set connectin mode to manual: "
716                               "%d (%s)\n", r, errstr);
717                 goto error_connection_mode;
718         }
719
720         r = SubscribeControlPowerManagement(&wmxsdk->device_id,
721                                             __iwmx_sdk_rf_state_cb);
722         if (r != WIMAX_API_RET_SUCCESS) {
723                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
724                 connman_error("wmxsdk: Cannot subscribe to radio change "
725                               "events: %u (%s)\n", r, errstr);
726                 result = -EIO;
727                 goto error_subscribe_rf_state;
728         }
729
730         r = SubscribeDeviceStatusChange(&wmxsdk->device_id,
731                                         __iwmx_sdk_state_change_cb);
732         if (r != WIMAX_API_RET_SUCCESS) {
733                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
734                 connman_error("wmxsdk: Cannot subscribe to state chaneg events:"
735                               "%d (%s)\n", r, errstr);
736                 goto error_subscribe_state_change;
737         }
738
739         r = SubscribeNetworkSearchWideScanEx(&wmxsdk->device_id,
740                                              __iwmx_sdk_wide_scan_cb);
741         if (r != WIMAX_API_RET_SUCCESS) {
742                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
743                 connman_error("wmxsdk: Cannot subscribe to wide scan events: "
744                               "%d (%s)\n", r, errstr);
745                 goto error_subscribe_wide_scan;
746         }
747         r = SubscribeNetworkSearchEx(&wmxsdk->device_id, __iwmx_sdk_scan_cb);
748         if (r != WIMAX_API_RET_SUCCESS) {
749                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
750                 connman_error("wmxsdk: Cannot subscribe to scan events: "
751                               "%d (%s)\n", r, errstr);
752                 goto error_subscribe_scan;
753         }
754
755         r = SubscribeConnectToNetwork(&wmxsdk->device_id,
756                                       __iwmx_sdk_connect_cb);
757         if (r != WIMAX_API_RET_SUCCESS) {
758                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
759                 connman_error("wmxsdk: Cannot subscribe to connect events: "
760                               "%d (%s)\n", r, errstr);
761                 goto error_subscribe_connect;
762         }
763
764         r = SubscribeDisconnectToNetwork(&wmxsdk->device_id,
765                                          __iwmx_sdk_disconnect_cb);
766         if (r != WIMAX_API_RET_SUCCESS) {
767                 GetErrorString(&wmxsdk->device_id, r, errstr, &errstr_size);
768                 connman_error("wmxsdk: Cannot subscribe to disconnect events: "
769                               "%d (%s)\n", r, errstr);
770                 goto error_subscribe_disconnect;
771         }
772         result = 0;
773 out:
774         return result;
775
776         UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
777 error_subscribe_disconnect:
778         UnsubscribeConnectToNetwork(&wmxsdk->device_id);
779 error_subscribe_connect:
780         UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
781 error_subscribe_scan:
782         UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
783 error_subscribe_wide_scan:
784         UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
785 error_subscribe_state_change:
786         UnsubscribeControlPowerManagement(&wmxsdk->device_id);
787 error_subscribe_rf_state:
788 error_connection_mode:
789         WiMaxDeviceClose(&wmxsdk->device_id);
790 error_wimaxdeviceopen:
791         goto out;
792 }
793
794 /*
795  * Called when a device is removed from connman
796  *
797  * Cleanup all that is done in iwmx_sdk_setup(). Remove callbacks,
798  * unregister from the WiMAX API.
799  */
800 void iwmx_sdk_remove(struct wmxsdk *wmxsdk)
801 {
802         UnsubscribeDisconnectToNetwork(&wmxsdk->device_id);
803         UnsubscribeConnectToNetwork(&wmxsdk->device_id);
804         UnsubscribeNetworkSearchEx(&wmxsdk->device_id);
805         UnsubscribeNetworkSearchWideScanEx(&wmxsdk->device_id);
806         UnsubscribeDeviceStatusChange(&wmxsdk->device_id);
807         UnsubscribeControlPowerManagement(&wmxsdk->device_id);
808         WiMaxDeviceClose(&wmxsdk->device_id);
809 }
810
811 static void iwmx_sdk_dev_add(unsigned idx, unsigned api_idx, const char *name)
812 {
813         int result, ifindex;
814         struct wmxsdk *wmxsdk;
815         const char *s;
816
817         if (idx >= IWMX_SDK_DEV_MAX) {
818                 connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
819                               idx, IWMX_SDK_DEV_MAX);
820                 goto error_bug;
821         }
822         if (g_iwmx_sdk_devs[idx] != NULL) {
823                 connman_error("BUG! device index %u already enumerated?\n",
824                               idx);
825                 goto error_bug;
826         }
827
828         wmxsdk = malloc(sizeof(*wmxsdk));
829         if (wmxsdk == NULL) {
830                 connman_error("Can't allocate %zu bytes\n",
831                               sizeof(*wmxsdk));
832                 goto error_bug;
833         }
834
835         memset(wmxsdk, 0, sizeof(*wmxsdk));
836         wmxsdk_init(wmxsdk);
837         /*
838          * This depends on a hack in the WiMAX Network Service; it has
839          * to return, as part of the device name, a string "if:IFNAME"
840          * where the OS's device name is stored.
841          */
842         s = strstr(name, "if:");
843         if (s == NULL
844             || sscanf(s, "if:%15[^ \f\n\r\t\v]", wmxsdk->ifname) != 1) {
845                 connman_error("Cannot extract network interface name off '%s'",
846                               name);
847                 goto error_noifname;
848         }
849         DBG("network interface name: '%s'", wmxsdk->ifname);
850
851         ifindex = if_nametoindex(wmxsdk->ifname);
852         if (ifindex <= 0) {
853                 result = -ENFILE;
854                 connman_error("wxmsdk: %s: cannot find interface index\n",
855                               wmxsdk->ifname);
856                 goto error_noifname;
857         }
858
859         wmxsdk->dev = connman_inet_create_device(ifindex);
860         if (wmxsdk->dev == NULL) {
861                 connman_error("wmxsdk: %s: failed to create connman_device\n",
862                               name);
863                 goto error_create;
864         }
865         strncpy(wmxsdk->name, name, sizeof(wmxsdk->name));
866         connman_device_set_data(wmxsdk->dev, wmxsdk);
867
868         wmxsdk->device_id.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
869         wmxsdk->device_id.deviceIndex = api_idx;
870
871         result = connman_device_register(wmxsdk->dev);
872         if (result < 0) {
873                 connman_error("wmxsdk: %s: failed to register: %d\n",
874                               wmxsdk->ifname, result);
875                 goto error_dev_add;
876         }
877         g_iwmx_sdk_devs[idx] = wmxsdk;
878         return;
879
880 error_dev_add:
881         wmxsdk->name[0] = 0;
882         connman_device_unref(wmxsdk->dev);
883         wmxsdk->dev = NULL;
884 error_noifname:
885 error_create:
886 error_bug:
887         return;
888 }
889
890 static void iwmx_sdk_dev_rm(unsigned idx)
891 {
892         struct wmxsdk *wmxsdk;
893
894         if (idx >= IWMX_SDK_DEV_MAX) {
895                 connman_error("BUG! idx (%u) >= IWMX_SDK_DEV_MAX (%u)\n",
896                               idx, IWMX_SDK_DEV_MAX);
897                 goto error_bug;
898         }
899         wmxsdk = g_iwmx_sdk_devs[idx];
900         if (wmxsdk->dev == NULL) {
901                 DBG("device index %u not enumerated? ignoring\n", idx);
902                 goto error_bug;
903         }
904
905         connman_device_unregister(wmxsdk->dev);
906         wmxsdk->name[0] = 0;
907         connman_device_unref(wmxsdk->dev);
908         memset(wmxsdk, 0, sizeof(*wmxsdk));
909         g_iwmx_sdk_devs[idx] = NULL;
910         free(wmxsdk);
911 error_bug:
912         return;
913 }
914
915 static void iwmx_sdk_addremove_cb(WIMAX_API_DEVICE_ID *devid,
916                                   BOOL presence)
917 {
918         unsigned int cnt;
919         WIMAX_API_RET r;
920         WIMAX_API_HW_DEVICE_ID device_id_list[5];
921         UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
922
923         char errstr[512];
924         UINT32 errstr_size = sizeof(errstr);
925
926         DBG("cb: handle %u index #%u is %d\n", devid->sdkHandle,
927             devid->deviceIndex, presence);
928
929         r = GetListDevice(devid, device_id_list, &device_id_list_size);
930         if (r != WIMAX_API_RET_SUCCESS) {
931                 GetErrorString(devid, r, errstr, &errstr_size);
932                 connman_error("wmxsdk: Cannot obtain list "
933                               "of devices: %d (%s)\n", r, errstr);
934                 return;
935         }
936
937         if (device_id_list_size == 0)
938                 DBG("No WiMAX devices reported\n");
939         else
940                 for (cnt = 0; cnt < device_id_list_size; cnt++) {
941                         WIMAX_API_HW_DEVICE_ID *dev =
942                                 device_id_list + cnt;
943                         DBG("#%u index #%u device %s\n",
944                             cnt, dev->deviceIndex, dev->deviceName);
945                 }
946         if (device_id_list_size < devid->deviceIndex) {
947                 connman_error("wmxsdk: changed device (%u) not in the list? "
948                               "(%u items)\n",
949                               devid->deviceIndex, device_id_list_size);
950                 return;
951         }
952
953         if (presence) {
954                 WIMAX_API_HW_DEVICE_ID *dev =
955                         device_id_list + devid->deviceIndex;
956                 iwmx_sdk_dev_add(devid->deviceIndex, dev->deviceIndex,
957                                dev->deviceName);
958         } else {
959                 iwmx_sdk_dev_rm(devid->deviceIndex);
960         }
961 }
962
963 /*
964  * Initialize the WiMAX API, register with it, setup callbacks for
965  * device coming up / dissapearing
966  */
967 int iwmx_sdk_api_init(void)
968 {
969         int result;
970         unsigned int cnt;
971         WIMAX_API_RET r;
972         char errstr[512];
973         UINT32 errstr_size = sizeof(errstr);
974
975         WIMAX_API_HW_DEVICE_ID device_id_list[5];
976         UINT32 device_id_list_size = ARRAY_SIZE(device_id_list);
977
978         memset(&g_api, 0, sizeof(g_api));
979         g_api.privilege = WIMAX_API_PRIVILEGE_READ_WRITE;
980
981         result = -EIO;
982         r = WiMaxAPIOpen(&g_api);
983         if (r != WIMAX_API_RET_SUCCESS) {
984                 GetErrorString(&g_api, r, errstr, &errstr_size);
985                 connman_error("wmxsdk: WiMaxAPIOpen failed with %d (%s)\n",
986                               r, errstr);
987                 goto error_wimaxapiopen;
988         }
989
990         r = SubscribeDeviceInsertRemove(&g_api, iwmx_sdk_addremove_cb);
991         if (r != WIMAX_API_RET_SUCCESS) {
992                 GetErrorString(&g_api, r, errstr, &errstr_size);
993                 connman_error("wmxsdk: insert/remove subscribe failed with "
994                               "%d (%s)\n", r, errstr);
995                 goto error_close;
996         }
997
998         r = GetListDevice(&g_api, device_id_list, &device_id_list_size);
999         if (r != WIMAX_API_RET_SUCCESS) {
1000                 GetErrorString(&g_api, r, errstr, &errstr_size);
1001                 connman_error("wmxsdk: Cannot obtain list "
1002                               "of devices: %d (%s)\n", r, errstr);
1003                 goto error_close;
1004         }
1005         if (device_id_list_size < g_api.deviceIndex) {
1006                 connman_error("wmxsdk: changed device (%u) not in the list? "
1007                               "(%u items)\n",
1008                               g_api.deviceIndex, device_id_list_size);
1009         }
1010
1011         if (device_id_list_size == 0)
1012                 DBG("No WiMAX devices reported\n");
1013         else
1014                 for (cnt = 0; cnt < device_id_list_size; cnt++) {
1015                         WIMAX_API_HW_DEVICE_ID *dev =
1016                                 device_id_list + cnt;
1017                         DBG("#%u index #%u device %s\n",
1018                             cnt, dev->deviceIndex, dev->deviceName);
1019                         iwmx_sdk_dev_add(cnt, dev->deviceIndex,
1020                                          dev->deviceName);
1021                 }
1022         return 0;
1023
1024 error_close:
1025         WiMaxAPIClose(&g_api);
1026 error_wimaxapiopen:
1027         return result;
1028 }
1029
1030 void iwmx_sdk_api_exit(void)
1031 {
1032         WIMAX_API_RET r;
1033
1034         char errstr[512];
1035         UINT32 errstr_size = sizeof(errstr);
1036
1037         r = WiMaxAPIClose(&g_api);
1038         if (r != WIMAX_API_RET_SUCCESS) {
1039                 GetErrorString(&g_api, r, errstr, &errstr_size);
1040                 connman_error("wmxsdk: WiMaxAPIClose failed with %d (%s)\n",
1041                               r, errstr);
1042         }
1043         return;
1044 }