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