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