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