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