mbm: Remove spurious ';'
[platform/upstream/ofono.git] / plugins / mbm.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  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 <stdlib.h>
29 #include <string.h>
30
31 #include <glib.h>
32 #include <gatchat.h>
33 #include <gattty.h>
34
35 #define OFONO_API_SUBJECT_TO_CHANGE
36 #include <ofono/plugin.h>
37 #include <ofono/modem.h>
38 #include <ofono/devinfo.h>
39 #include <ofono/netreg.h>
40 #include <ofono/sim.h>
41 #include <ofono/stk.h>
42 #include <ofono/cbs.h>
43 #include <ofono/sms.h>
44 #include <ofono/ussd.h>
45 #include <ofono/gprs.h>
46 #include <ofono/gprs-context.h>
47 #include <ofono/radio-settings.h>
48 #include <ofono/log.h>
49 #include <ofono/location-reporting.h>
50
51 #include <drivers/atmodem/atutil.h>
52 #include <drivers/atmodem/vendor.h>
53
54 static const char *cfun_prefix[] = { "+CFUN:", NULL };
55 static const char *none_prefix[] = { NULL };
56
57 enum mbm_variant {
58         MBM_GENERIC,
59         MBM_DELL_D5530,         /* OEM of F3507g */
60 };
61
62 struct mbm_data {
63         GAtChat *modem_port;
64         GAtChat *data_port;
65         gboolean have_sim;
66         struct ofono_location_reporting *lr;
67         enum mbm_variant variant;
68         struct at_util_sim_state_query *sim_state_query;
69 };
70
71 static int mbm_probe(struct ofono_modem *modem)
72 {
73         struct mbm_data *data;
74
75         DBG("%p", modem);
76
77         data = g_try_new0(struct mbm_data, 1);
78         if (data == NULL)
79                 return -ENOMEM;
80
81         ofono_modem_set_data(modem, data);
82
83         return 0;
84 }
85
86 static void mbm_remove(struct ofono_modem *modem)
87 {
88         struct mbm_data *data = ofono_modem_get_data(modem);
89
90         DBG("%p", modem);
91
92         ofono_modem_set_data(modem, NULL);
93
94         /* Cleanup potential SIM state polling */
95         at_util_sim_state_query_free(data->sim_state_query);
96
97         g_at_chat_unref(data->data_port);
98         g_at_chat_unref(data->modem_port);
99
100         g_free(data);
101 }
102
103 static void mbm_debug(const char *str, void *user_data)
104 {
105         const char *prefix = user_data;
106
107         ofono_info("%s%s", prefix, str);
108 }
109
110 static void d5530_notify(GAtResult *result, gpointer user_data)
111 {
112         DBG("D5530");
113 }
114
115 static void mbm_quirk_d5530(struct ofono_modem *modem)
116 {
117         struct mbm_data *data = ofono_modem_get_data(modem);
118
119         data->variant = MBM_DELL_D5530;
120
121         /* This Dell modem sends some unsolicated messages when it boots. */
122         /* Try to ignore them. */
123         g_at_chat_register(data->modem_port, "D5530", d5530_notify,
124                                 FALSE, NULL, NULL);
125         g_at_chat_register(data->modem_port, "+GCAP:", d5530_notify,
126                                 FALSE, NULL, NULL);
127 }
128
129 static void sim_state_cb(gboolean present, gpointer user_data)
130 {
131         struct ofono_modem *modem = user_data;
132         struct mbm_data *data = ofono_modem_get_data(modem);
133
134         at_util_sim_state_query_free(data->sim_state_query);
135         data->sim_state_query = NULL;
136
137         data->have_sim = present;
138         ofono_modem_set_powered(modem, TRUE);
139 }
140
141 static void check_model(gboolean ok, GAtResult *result, gpointer user_data)
142 {
143         struct ofono_modem *modem = user_data;
144         struct mbm_data *data = ofono_modem_get_data(modem);
145         GAtResultIter iter;
146         char const *model;
147
148         DBG("");
149
150         if (!ok)
151                 goto done;
152
153         g_at_result_iter_init(&iter, result);
154
155         while (g_at_result_iter_next(&iter, NULL)) {
156                 if (!g_at_result_iter_next_unquoted_string(&iter, &model))
157                         continue;
158
159                 if (g_str_equal(model, "D5530"))
160                         mbm_quirk_d5530(modem);
161         }
162
163 done:
164         data->sim_state_query = at_util_sim_state_query_new(data->modem_port,
165                                                                 1, 5,
166                                                                 sim_state_cb,
167                                                                 modem, NULL);
168 }
169
170 static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
171 {
172         struct ofono_modem *modem = user_data;
173         struct mbm_data *data = ofono_modem_get_data(modem);
174
175         DBG("");
176
177         if (!ok) {
178                 ofono_modem_set_powered(modem, FALSE);
179                 return;
180         }
181
182         g_at_chat_send(data->modem_port, "AT+CGMM", NULL,
183                                         check_model, modem, NULL);
184 }
185
186 static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data)
187 {
188         struct ofono_modem *modem = user_data;
189         struct mbm_data *data = ofono_modem_get_data(modem);
190         GAtResultIter iter;
191         int status;
192
193         DBG("%d", ok);
194
195         if (!ok)
196                 return;
197
198         g_at_result_iter_init(&iter, result);
199
200         if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE)
201                 return;
202
203         g_at_result_iter_next_number(&iter, &status);
204
205         if (status != 4) {
206                 g_at_chat_send(data->modem_port, "AT+CFUN=4", none_prefix,
207                                 cfun_enable, modem, NULL);
208                 return;
209         }
210
211         cfun_enable(TRUE, NULL, modem);
212 }
213
214 static void emrdy_notifier(GAtResult *result, gpointer user_data)
215 {
216         struct ofono_modem *modem = user_data;
217         struct mbm_data *data = ofono_modem_get_data(modem);
218         GAtResultIter iter;
219         int status;
220
221         DBG("");
222
223         g_at_result_iter_init(&iter, result);
224
225         if (g_at_result_iter_next(&iter, "*EMRDY:") == FALSE)
226                 return;
227
228         g_at_result_iter_next_number(&iter, &status);
229
230         if (status != 1)
231                 return;
232
233         g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix,
234                                         cfun_query, modem, NULL);
235 }
236
237 static void emrdy_query(gboolean ok, GAtResult *result, gpointer user_data)
238 {
239         struct ofono_modem *modem = user_data;
240         struct mbm_data *data = ofono_modem_get_data(modem);
241
242         DBG("%d", ok);
243
244         if (ok)
245                 return;
246
247         /* On some MBM hardware the EMRDY cannot be queried, so if this fails
248          * we try to run CFUN? to check the state.  CFUN? will fail unless
249          * EMRDY: 1 has been sent, in which case the emrdy_notifier should be
250          * triggered eventually and we send CFUN? again.
251          */
252         g_at_chat_send(data->modem_port, "AT+CFUN?", cfun_prefix,
253                                         cfun_query, modem, NULL);
254 }
255
256 static GAtChat *create_port(const char *device)
257 {
258         GAtSyntax *syntax;
259         GIOChannel *channel;
260         GAtChat *chat;
261         GHashTable *options;
262
263         options = g_hash_table_new(g_str_hash, g_str_equal);
264         if (options == NULL)
265                 return NULL;
266
267         g_hash_table_insert(options, "Baud", "115200");
268
269         channel = g_at_tty_open(device, options);
270
271         g_hash_table_destroy(options);
272
273         if (channel == NULL)
274                 return NULL;
275
276         syntax = g_at_syntax_new_gsm_permissive();
277         chat = g_at_chat_new(channel, syntax);
278         g_at_syntax_unref(syntax);
279         g_io_channel_unref(channel);
280
281         if (chat == NULL)
282                 return NULL;
283
284         return chat;
285 }
286
287 static int mbm_enable(struct ofono_modem *modem)
288 {
289         struct mbm_data *data = ofono_modem_get_data(modem);
290         const char *modem_dev;
291         const char *data_dev;
292
293         DBG("%p", modem);
294
295         modem_dev = ofono_modem_get_string(modem, "ModemDevice");
296         data_dev = ofono_modem_get_string(modem, "DataDevice");
297
298         DBG("%s, %s", modem_dev, data_dev);
299
300         if (modem_dev == NULL || data_dev == NULL)
301                 return -EINVAL;
302
303         data->modem_port = create_port(modem_dev);
304         if (data->modem_port == NULL)
305                 return -EIO;
306
307         if (getenv("OFONO_AT_DEBUG"))
308                 g_at_chat_set_debug(data->modem_port, mbm_debug, "Modem: ");
309
310         data->data_port = create_port(data_dev);
311         if (data->data_port == NULL) {
312                 g_at_chat_unref(data->modem_port);
313                 data->modem_port = NULL;
314
315                 return -EIO;
316         }
317
318         if (getenv("OFONO_AT_DEBUG"))
319                 g_at_chat_set_debug(data->data_port, mbm_debug, "Data: ");
320
321         g_at_chat_register(data->modem_port, "*EMRDY:", emrdy_notifier,
322                                         FALSE, modem, NULL);
323
324         g_at_chat_send(data->modem_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL,
325                                         NULL, NULL, NULL);
326         g_at_chat_send(data->data_port, "AT&F E0 V1 X4 &C0 +CMEE=1", NULL,
327                                         NULL, NULL, NULL);
328
329         g_at_chat_send(data->modem_port, "AT*E2CFUN=1", none_prefix,
330                                         NULL, NULL, NULL);
331         g_at_chat_send(data->modem_port, "AT*EMRDY?", none_prefix,
332                                 emrdy_query, modem, NULL);
333
334         return -EINPROGRESS;
335 }
336
337 static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
338 {
339         struct ofono_modem *modem = user_data;
340         struct mbm_data *data = ofono_modem_get_data(modem);
341
342         DBG("");
343
344         g_at_chat_unref(data->modem_port);
345         data->modem_port = NULL;
346
347         g_at_chat_unref(data->data_port);
348         data->data_port = NULL;
349
350         if (ok)
351                 ofono_modem_set_powered(modem, FALSE);
352 }
353
354 static int mbm_disable(struct ofono_modem *modem)
355 {
356         struct mbm_data *data = ofono_modem_get_data(modem);
357
358         DBG("%p", modem);
359
360         if (data->modem_port == NULL)
361                 return 0;
362
363         g_at_chat_cancel_all(data->modem_port);
364         g_at_chat_unregister_all(data->modem_port);
365
366         g_at_chat_send(data->modem_port, "AT+CFUN=4", NULL,
367                                         cfun_disable, modem, NULL);
368
369         return -EINPROGRESS;
370 }
371
372 static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
373 {
374         struct cb_data *cbd = user_data;
375         ofono_modem_online_cb_t cb = cbd->cb;
376         struct ofono_error error;
377
378         decode_at_error(&error, g_at_result_final_response(result));
379         cb(&error, cbd->data);
380 }
381
382 static void mbm_set_online(struct ofono_modem *modem, ofono_bool_t online,
383                                 ofono_modem_online_cb_t cb, void *user_data)
384 {
385         struct mbm_data *data = ofono_modem_get_data(modem);
386         GAtChat *chat = data->modem_port;
387         struct cb_data *cbd = cb_data_new(cb, user_data);
388         char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
389
390         DBG("modem %p %s", modem, online ? "online" : "offline");
391
392         if (g_at_chat_send(chat, command, none_prefix,
393                                 set_online_cb, cbd, g_free) > 0)
394                 return;
395
396         CALLBACK_WITH_FAILURE(cb, cbd->data);
397
398         g_free(cbd);
399 }
400
401 static void mbm_pre_sim(struct ofono_modem *modem)
402 {
403         struct mbm_data *data = ofono_modem_get_data(modem);
404         struct ofono_sim *sim;
405
406         DBG("%p", modem);
407
408         ofono_devinfo_create(modem, 0, "atmodem", data->modem_port);
409         sim = ofono_sim_create(modem, OFONO_VENDOR_MBM,
410                                         "atmodem", data->modem_port);
411
412         if (data->have_sim && sim)
413                 ofono_sim_inserted_notify(sim, TRUE);
414 }
415
416 static void mbm_post_sim(struct ofono_modem *modem)
417 {
418         struct mbm_data *data = ofono_modem_get_data(modem);
419
420         DBG("%p", modem);
421
422         ofono_stk_create(modem, 0, "mbmmodem", data->modem_port);
423         ofono_radio_settings_create(modem, 0, "stemodem", data->modem_port);
424
425         ofono_sms_create(modem, 0, "atmodem", data->modem_port);
426 }
427
428 static void mbm_post_online(struct ofono_modem *modem)
429 {
430         struct mbm_data *data = ofono_modem_get_data(modem);
431         struct ofono_gprs *gprs;
432         struct ofono_gprs_context *gc;
433         const char *gps_dev;
434
435         DBG("%p", modem);
436
437         gps_dev = ofono_modem_get_string(modem, "GPSDevice");
438         if (gps_dev)
439                 data->lr = ofono_location_reporting_create(modem, 0,
440                                         "mbmmodem", data->modem_port);
441
442         ofono_netreg_create(modem, OFONO_VENDOR_MBM,
443                                         "atmodem", data->modem_port);
444
445         switch (data->variant) {
446         case MBM_GENERIC:
447                 ofono_cbs_create(modem, 0, "atmodem", data->modem_port);
448                 break;
449         case MBM_DELL_D5530:
450                 /* DELL D5530 crashes when it processes CBSs */
451                 break;
452         }
453
454         ofono_ussd_create(modem, 0, "atmodem", data->modem_port);
455
456         gprs = ofono_gprs_create(modem, OFONO_VENDOR_MBM,
457                                         "atmodem", data->modem_port);
458         if (gprs == NULL)
459                 return;
460
461         gc = ofono_gprs_context_create(modem, 0,
462                                         "mbmmodem", data->modem_port);
463         if (gc) {
464                 ofono_gprs_context_set_type(gc,
465                                         OFONO_GPRS_CONTEXT_TYPE_INTERNET);
466                 ofono_gprs_add_context(gprs, gc);
467         }
468
469         gc = ofono_gprs_context_create(modem, 0,
470                                         "atmodem", data->data_port);
471         if (gc) {
472                 ofono_gprs_context_set_type(gc,
473                                         OFONO_GPRS_CONTEXT_TYPE_MMS);
474                 ofono_gprs_add_context(gprs, gc);
475         }
476 }
477
478 static struct ofono_modem_driver mbm_driver = {
479         .name           = "mbm",
480         .probe          = mbm_probe,
481         .remove         = mbm_remove,
482         .enable         = mbm_enable,
483         .disable        = mbm_disable,
484         .set_online     = mbm_set_online,
485         .pre_sim        = mbm_pre_sim,
486         .post_sim       = mbm_post_sim,
487         .post_online    = mbm_post_online,
488 };
489
490 static int mbm_init(void)
491 {
492         return ofono_modem_driver_register(&mbm_driver);
493 }
494
495 static void mbm_exit(void)
496 {
497         ofono_modem_driver_unregister(&mbm_driver);
498 }
499
500 OFONO_PLUGIN_DEFINE(mbm, "Ericsson MBM modem driver", VERSION,
501                         OFONO_PLUGIN_PRIORITY_DEFAULT, mbm_init, mbm_exit)