Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| /* | |
| * | |
| * oFono - Open Source Telephony | |
| * | |
| * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License version 2 as | |
| * published by the Free Software Foundation. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
| * | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| #include <config.h> | |
| #endif | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <inttypes.h> | |
| #include <glib.h> | |
| #include <ofono/log.h> | |
| #include <ofono/modem.h> | |
| #include <ofono/gprs.h> | |
| #include <ofono/gprs-context.h> | |
| #include <gisi/client.h> | |
| #include <gisi/iter.h> | |
| #include "isimodem.h" | |
| #include "isiutil.h" | |
| #include "gpds.h" | |
| #include "info.h" | |
| #include "debug.h" | |
| /* 27.007 Section 10.1.20 <stat> */ | |
| enum network_registration_status { | |
| GPRS_STAT_NOT_REGISTERED = 0, | |
| GPRS_STAT_REGISTERED = 1, | |
| GPRS_STAT_SEARCHING = 2, | |
| GPRS_STAT_DENIED = 3, | |
| GPRS_STAT_UNKNOWN = 4, | |
| GPRS_STAT_ROAMING = 5, | |
| }; | |
| struct gprs_data { | |
| GIsiClient *client; | |
| GIsiClient *info_client; | |
| }; | |
| static void configure_resp_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) { | |
| DBG("ISI message error: %d", g_isi_msg_error(msg)); | |
| return; | |
| } | |
| if (g_isi_msg_id(msg) != GPDS_CONFIGURE_RESP) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 1) | |
| return; | |
| if (data[0] != GPDS_OK) | |
| DBG("GPDS configure failed: %s", gpds_status_name(data[0])); | |
| } | |
| static void set_attach_mode(struct ofono_gprs *gprs, int attached) | |
| { | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| const unsigned char msg[] = { | |
| GPDS_CONFIGURE_REQ, | |
| attached ? GPDS_ATTACH_MODE_AUTOMATIC : GPDS_ATTACH_MODE_MANUAL, | |
| GPDS_MT_ACT_MODE_REJECT, | |
| GPDS_CLASSC_MODE_DEFAULT, | |
| GPDS_AOL_CTX_DEFAULT, | |
| 0x00, | |
| 0x00 | |
| }; | |
| g_isi_client_send(gd->client, msg, sizeof(msg), | |
| configure_resp_cb, gprs, NULL); | |
| } | |
| static void detach_ind_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct ofono_gprs *gprs = opaque; | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) | |
| return; | |
| if (g_isi_msg_id(msg) != GPDS_DETACH_IND) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 2) | |
| return; | |
| DBG("detached: %s (0x%02"PRIx8")", | |
| gpds_isi_cause_name(data[0]), data[0]); | |
| set_attach_mode(gprs, FALSE); | |
| ofono_gprs_detached_notify(gprs); | |
| } | |
| static void suspend_notify(struct ofono_gprs *gprs, uint8_t suspend_status, | |
| uint8_t suspend_cause) | |
| { | |
| int cause; | |
| DBG("transfer status: %s (0x%02"PRIx8") cause %s (0x%02"PRIx8")", | |
| gpds_transfer_status_name(suspend_status), suspend_status, | |
| gpds_transfer_cause_name(suspend_cause), suspend_cause); | |
| if (suspend_status == GPDS_TRANSFER_AVAIL) { | |
| ofono_gprs_resume_notify(gprs); | |
| return; | |
| } | |
| switch (suspend_cause) { | |
| case GPDS_TRANSFER_CAUSE_SUSPENDED_NO_COVERAGE: | |
| cause = GPRS_SUSPENDED_NO_COVERAGE; | |
| break; | |
| case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL: | |
| cause = GPRS_SUSPENDED_CALL; | |
| break; | |
| case GPDS_TRANSFER_CAUSE_SUSPENDED_CALL_SMS: | |
| case GPDS_TRANSFER_CAUSE_SUSPENDED_RAU: | |
| case GPDS_TRANSFER_CAUSE_SUSPENDED_LU: | |
| cause = GPRS_SUSPENDED_SIGNALLING; | |
| break; | |
| default: | |
| return; | |
| } | |
| ofono_gprs_suspend_notify(gprs, cause); | |
| } | |
| static void transfer_status_ind_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct ofono_gprs *gprs = opaque; | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) | |
| return; | |
| if (g_isi_msg_id(msg) != GPDS_TRANSFER_STATUS_IND) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 2) | |
| return; | |
| suspend_notify(gprs, data[0], data[1]); | |
| } | |
| static void create_contexts(struct ofono_gprs *gprs, int count) | |
| { | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| GIsiModem *modem = g_isi_client_modem(gd->client); | |
| struct ofono_modem *omodem = g_isi_modem_get_userdata(modem); | |
| struct ofono_gprs_context *gc; | |
| int i; | |
| for (i = 0; i < count; i++) { | |
| gc = ofono_gprs_context_create(omodem, 0, "isimodem", modem); | |
| if (gc == NULL) | |
| break; | |
| ofono_gprs_add_context(gprs, gc); | |
| } | |
| ofono_gprs_set_cid_range(gprs, 1, i); | |
| DBG("%d GPRS contexts created", count); | |
| } | |
| static void info_pp_read_resp_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct ofono_gprs *gprs = opaque; | |
| uint8_t count = GPDS_MAX_CONTEXT_COUNT; | |
| GIsiSubBlockIter iter; | |
| if (g_isi_msg_error(msg) == -ESHUTDOWN) | |
| return; | |
| if (g_isi_msg_error(msg) < 0) | |
| goto out; | |
| if (g_isi_msg_id(msg) != INFO_PP_READ_RESP) | |
| goto out; | |
| for (g_isi_sb_iter_init(&iter, msg, 2); g_isi_sb_iter_is_valid(&iter); | |
| g_isi_sb_iter_next(&iter)) { | |
| switch (g_isi_sb_iter_get_id(&iter)) { | |
| case INFO_SB_PP: { | |
| guint16 fea; | |
| guint8 n; | |
| unsigned pp; | |
| if (!g_isi_sb_iter_get_byte(&iter, &n, 1)) | |
| goto out; | |
| for (pp = 4; n--; pp += 2) { | |
| if (!g_isi_sb_iter_get_word(&iter, &fea, pp)) | |
| goto out; | |
| if ((fea >> 8) != INFO_PP_MAX_PDP_CONTEXTS) | |
| goto out; | |
| count = fea & 0xff; | |
| break; | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| } | |
| out: | |
| create_contexts(gprs, count); | |
| } | |
| static void gpds_reachable_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct ofono_gprs *gprs = opaque; | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| GIsiModem *modem = g_isi_client_modem(gd->client); | |
| const unsigned char req[] = { | |
| INFO_PP_READ_REQ, | |
| 0, /* filler */ | |
| 1, /* subblocks */ | |
| INFO_SB_PP, | |
| 8, /* subblock length */ | |
| 0, | |
| 1, /* N */ | |
| INFO_PP_MAX_PDP_CONTEXTS, /* PP feature */ | |
| 0, /* PP value */ | |
| 0, /* filler */ | |
| 0 /* filler */ | |
| }; | |
| if (g_isi_msg_error(msg) < 0) { | |
| DBG("unable to bootstrap gprs driver"); | |
| ofono_gprs_remove(gprs); | |
| return; | |
| } | |
| ISI_RESOURCE_DBG(msg); | |
| g_isi_client_ind_subscribe(gd->client, GPDS_DETACH_IND, | |
| detach_ind_cb, gprs); | |
| g_isi_client_ind_subscribe(gd->client, GPDS_TRANSFER_STATUS_IND, | |
| transfer_status_ind_cb, gprs); | |
| ofono_gprs_register(gprs); | |
| gd->info_client = g_isi_client_create(modem, PN_PHONE_INFO); | |
| if (gd->info_client == NULL) { | |
| create_contexts(gprs, GPDS_MAX_CONTEXT_COUNT); | |
| return; | |
| } | |
| g_isi_client_send(gd->info_client, req, sizeof(req), | |
| info_pp_read_resp_cb, gprs, NULL); | |
| } | |
| static int isi_gprs_probe(struct ofono_gprs *gprs, | |
| unsigned int vendor, void *user) | |
| { | |
| GIsiModem *modem = user; | |
| struct gprs_data *gd = g_try_new0(struct gprs_data, 1); | |
| if (gd == NULL) | |
| return -ENOMEM; | |
| gd->client = g_isi_client_create(modem, PN_GPDS); | |
| if (gd->client == NULL) { | |
| g_free(gd); | |
| return -ENOMEM; | |
| } | |
| ofono_gprs_set_data(gprs, gd); | |
| g_isi_client_set_timeout(gd->client, GPDS_TIMEOUT); | |
| g_isi_client_verify(gd->client, gpds_reachable_cb, gprs, NULL); | |
| return 0; | |
| } | |
| static void isi_gprs_remove(struct ofono_gprs *gprs) | |
| { | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| ofono_gprs_set_data(gprs, NULL); | |
| if (gd == NULL) | |
| return; | |
| g_isi_client_destroy(gd->client); | |
| g_isi_client_destroy(gd->info_client); | |
| g_free(gd); | |
| } | |
| static void attach_resp_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct isi_cb_data *cbd = opaque; | |
| ofono_gprs_cb_t cb = cbd->cb; | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) { | |
| DBG("ISI message error: %d", g_isi_msg_error(msg)); | |
| goto error; | |
| } | |
| if (g_isi_msg_id(msg) != GPDS_ATTACH_RESP) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 2) | |
| goto error; | |
| if (data[0] != GPDS_OK) { | |
| DBG("attach failed: %s", gpds_status_name(data[0])); | |
| goto error; | |
| } | |
| set_attach_mode(cbd->user, TRUE); | |
| CALLBACK_WITH_SUCCESS(cb, cbd->data); | |
| return; | |
| error: | |
| CALLBACK_WITH_FAILURE(cb, cbd->data); | |
| } | |
| static void detach_resp_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct isi_cb_data *cbd = opaque; | |
| ofono_gprs_cb_t cb = cbd->cb; | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) { | |
| DBG("ISI client error: %d", g_isi_msg_error(msg)); | |
| goto error; | |
| } | |
| if (g_isi_msg_id(msg) != GPDS_DETACH_RESP) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 2) | |
| goto error; | |
| if (data[0] != GPDS_OK) { | |
| DBG("detach failed: %s", gpds_status_name(data[0])); | |
| goto error; | |
| } | |
| set_attach_mode(cbd->user, FALSE); | |
| CALLBACK_WITH_SUCCESS(cb, cbd->data); | |
| return; | |
| error: | |
| CALLBACK_WITH_FAILURE(cb, cbd->data); | |
| } | |
| static void isi_gprs_set_attached(struct ofono_gprs *gprs, int attached, | |
| ofono_gprs_cb_t cb, void *data) | |
| { | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| struct isi_cb_data *cbd = isi_cb_data_new(gprs, cb, data); | |
| if (cbd == NULL || gd == NULL) | |
| goto error; | |
| if (attached) { | |
| const unsigned char msg[] = { | |
| GPDS_ATTACH_REQ, | |
| GPDS_FOLLOW_OFF | |
| }; | |
| if (g_isi_client_send_with_timeout(gd->client, | |
| msg, sizeof(msg), | |
| GPDS_ATTACH_TIMEOUT, attach_resp_cb, | |
| cbd, g_free)) | |
| return; | |
| } else { | |
| const unsigned char msg[] = { | |
| GPDS_DETACH_REQ, | |
| 0x00, /* filler */ | |
| 0x00 /* sub-blocks */ | |
| }; | |
| if (g_isi_client_send_with_timeout(gd->client, | |
| msg, sizeof(msg), | |
| GPDS_DETACH_TIMEOUT, detach_resp_cb, | |
| cbd, g_free)) | |
| return; | |
| } | |
| error: | |
| CALLBACK_WITH_FAILURE(cb, data); | |
| g_free(cbd); | |
| } | |
| static void status_resp_cb(const GIsiMessage *msg, void *opaque) | |
| { | |
| struct isi_cb_data *cbd = opaque; | |
| ofono_gprs_status_cb_t cb = cbd->cb; | |
| struct ofono_gprs *gprs = cbd->data; | |
| int status; | |
| const uint8_t *data = g_isi_msg_data(msg); | |
| if (g_isi_msg_error(msg) < 0) { | |
| DBG("ISI message error: %d", g_isi_msg_error(msg)); | |
| goto error; | |
| } | |
| if (g_isi_msg_id(msg) != GPDS_STATUS_RESP) | |
| return; | |
| if (g_isi_msg_data_len(msg) < 12) | |
| goto error; | |
| /* FIXME: the core still expects reg status, and not a boolean | |
| * attached status here.*/ | |
| switch (data[0]) { | |
| case GPDS_ATTACHED: | |
| status = GPRS_STAT_REGISTERED; | |
| break; | |
| case GPDS_DETACHED: | |
| status = GPRS_STAT_NOT_REGISTERED; | |
| break; | |
| default: | |
| status = GPRS_STAT_UNKNOWN; | |
| } | |
| suspend_notify(gprs, data[10], data[11]); | |
| CALLBACK_WITH_SUCCESS(cb, status, cbd->data); | |
| return; | |
| error: | |
| CALLBACK_WITH_FAILURE(cb, -1, cbd->data); | |
| } | |
| static void isi_gprs_attached_status(struct ofono_gprs *gprs, | |
| ofono_gprs_status_cb_t cb, | |
| void *data) | |
| { | |
| struct gprs_data *gd = ofono_gprs_get_data(gprs); | |
| struct isi_cb_data *cbd = isi_cb_data_new(NULL, cb, data); | |
| const unsigned char msg[] = { | |
| GPDS_STATUS_REQ, | |
| }; | |
| if (cbd == NULL || gd == NULL) | |
| goto error; | |
| if (g_isi_client_send(gd->client, msg, sizeof(msg), | |
| status_resp_cb, cbd, g_free)) | |
| return; | |
| error: | |
| CALLBACK_WITH_FAILURE(cb, -1, data); | |
| g_free(cbd); | |
| } | |
| static const struct ofono_gprs_driver driver = { | |
| .name = "isimodem", | |
| .probe = isi_gprs_probe, | |
| .remove = isi_gprs_remove, | |
| .set_attached = isi_gprs_set_attached, | |
| .attached_status = isi_gprs_attached_status, | |
| }; | |
| void isi_gprs_init(void) | |
| { | |
| ofono_gprs_driver_register(&driver); | |
| } | |
| void isi_gprs_exit(void) | |
| { | |
| ofono_gprs_driver_unregister(&driver); | |
| } |