From 2abfd90edd6200acf92850c6fe574d91e4aede0f Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Thu, 9 Nov 2017 20:52:20 -0600 Subject: [PATCH] mbimmodem: Add gprs-context driver --- Makefile.am | 3 +- drivers/mbimmodem/gprs-context.c | 464 +++++++++++++++++++++++++++++++ drivers/mbimmodem/mbimmodem.c | 2 + drivers/mbimmodem/mbimmodem.h | 4 + 4 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 drivers/mbimmodem/gprs-context.c diff --git a/Makefile.am b/Makefile.am index 32ee3dcf..903630be 100644 --- a/Makefile.am +++ b/Makefile.am @@ -624,7 +624,8 @@ builtin_sources += $(mbim_sources) \ drivers/mbimmodem/sim.c \ drivers/mbimmodem/network-registration.c \ drivers/mbimmodem/sms.c \ - drivers/mbimmodem/gprs.c + drivers/mbimmodem/gprs.c \ + drivers/mbimmodem/gprs-context.c builtin_modules += mbim builtin_sources += plugins/mbim.c diff --git a/drivers/mbimmodem/gprs-context.c b/drivers/mbimmodem/gprs-context.c new file mode 100644 index 00000000..79793c92 --- /dev/null +++ b/drivers/mbimmodem/gprs-context.c @@ -0,0 +1,464 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. + * + * 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "drivers/mbimmodem/mbim.h" +#include "drivers/mbimmodem/mbim-message.h" +#include "drivers/mbimmodem/mbimmodem.h" + +enum state { + STATE_IDLE, + STATE_ENABLING, + STATE_DISABLING, + STATE_ACTIVE, +}; + +struct gprs_context_data { + struct mbim_device *device; + unsigned int active_context; + enum ofono_gprs_proto proto; + enum state state; + ofono_gprs_context_cb_t cb; + void *cb_data; +}; + +static uint32_t proto_to_context_ip_type(enum ofono_gprs_proto proto) +{ + switch (proto) { + case OFONO_GPRS_PROTO_IP: + return 1; /* MBIMContextIPTypeIPv4 */ + case OFONO_GPRS_PROTO_IPV6: + return 2; /* MBIMContextIPTypeIPv6 */ + case OFONO_GPRS_PROTO_IPV4V6: + return 3; /* MBIMContextIPTypeIPv4v6 */ + } + + return 0; +} + +static uint32_t auth_method_to_auth_protocol(enum ofono_gprs_auth_method method) +{ + switch (method) { + case OFONO_GPRS_AUTH_METHOD_CHAP: + return 2; /* MBIMAuthProtocolChap */ + case OFONO_GPRS_AUTH_METHOD_PAP: + return 1; /* MBIMAuthProtocolPap */ + } + + return 0; +} + +static void mbim_deactivate_cb(struct mbim_message *message, void *user) +{ + struct ofono_gprs_context *gc = user; + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + + DBG(""); + + gcd->active_context = 0; + gcd->state = STATE_IDLE; + + if (!gcd->cb) + return; + + if (mbim_message_get_error(message) != 0) + CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); + else + CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); +} + +static void mbim_gprs_deactivate_primary(struct ofono_gprs_context *gc, + unsigned int cid, + ofono_gprs_context_cb_t cb, void *data) +{ + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + struct mbim_message *message; + + DBG("cid %u", cid); + + gcd->state = STATE_DISABLING; + gcd->cb = cb; + gcd->cb_data = data; + + message = mbim_message_new(mbim_uuid_basic_connect, + MBIM_CID_CONNECT, + MBIM_COMMAND_TYPE_SET); + mbim_message_set_arguments(message, "uusssuuu16y", + cid, 0, NULL, NULL, NULL, 0, 0, 0, + mbim_context_type_internet); + + if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message, + mbim_deactivate_cb, gc, NULL) > 0) + return; + + mbim_message_unref(message); + + if (cb) + CALLBACK_WITH_FAILURE(cb, data); +} + +static void mbim_ip_configuration_cb(struct mbim_message *message, void *user) +{ + struct ofono_gprs_context *gc = user; + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + struct ofono_modem *modem = ofono_gprs_context_get_modem(gc); + const char *interface; + uint32_t session_id; + uint32_t ipv4_config_available; + uint32_t ipv6_config_available; + uint32_t n_ipv4_addr; + uint32_t ipv4_addr_offset; + uint32_t n_ipv6_addr; + uint32_t ipv6_addr_offset; + uint32_t ipv4_gw_offset; + uint32_t ipv6_gw_offset; + uint32_t n_ipv4_dns; + uint32_t ipv4_dns_offset; + uint32_t n_ipv6_dns; + uint32_t ipv6_dns_offset; + uint32_t ipv4_mtu; + uint32_t ipv6_mtu; + + struct in6_addr ipv6; + struct in_addr ipv4; + char buf[INET6_ADDRSTRLEN]; + + DBG("%u", mbim_message_get_error(message)); + + if (mbim_message_get_error(message) != 0) + goto error; + + if (!mbim_message_get_arguments(message, "uuuuuuuuuuuuuuu", + &session_id, + &ipv4_config_available, &ipv6_config_available, + &n_ipv4_addr, &ipv4_addr_offset, + &n_ipv6_addr, &ipv6_addr_offset, + &ipv4_gw_offset, &ipv6_gw_offset, + &n_ipv4_dns, &ipv4_dns_offset, + &n_ipv6_dns, &ipv6_dns_offset, + &ipv4_mtu, &ipv6_mtu)) + goto error; + + if (gcd->proto == OFONO_GPRS_PROTO_IPV6) + goto ipv6; + + if (ipv4_config_available & 0x1) { /* Address Info present */ + uint32_t prefix; + + if (!mbim_message_get_ipv4_element(message, ipv4_addr_offset, + &prefix, &ipv4)) + goto error; + + inet_ntop(AF_INET, &ipv4, buf, sizeof(buf)); + ofono_gprs_context_set_ipv4_address(gc, buf, TRUE); + ofono_gprs_context_set_ipv4_prefix_length(gc, prefix); + } else + ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE); + + if (ipv4_config_available & 0x2) { /* IPv4 Gateway info */ + if (!mbim_message_get_ipv4_address(message, + ipv4_gw_offset, &ipv4)) + goto error; + + inet_ntop(AF_INET, &ipv4, buf, sizeof(buf)); + + ofono_gprs_context_set_ipv4_gateway(gc, buf); + } + + if (ipv4_config_available & 0x3) { /* IPv4 DNS Info */ + const char *dns[3]; + char dns1[INET_ADDRSTRLEN]; + char dns2[INET_ADDRSTRLEN]; + + memset(dns, 0, sizeof(dns)); + + if (n_ipv4_dns > 1) { /* Grab second DNS */ + if (!mbim_message_get_ipv4_address(message, + ipv4_dns_offset + 4, + &ipv4)) + goto error; + + inet_ntop(AF_INET, &ipv4, dns2, sizeof(dns2)); + dns[1] = dns2; + } + + if (n_ipv4_dns > 0) { /* Grab first DNS */ + if (!mbim_message_get_ipv4_address(message, + ipv4_dns_offset, + &ipv4)) + goto error; + + inet_ntop(AF_INET, &ipv4, dns1, sizeof(dns1)); + dns[0] = dns1; + + ofono_gprs_context_set_ipv4_dns_servers(gc, dns); + } + } + + if (gcd->proto == OFONO_GPRS_PROTO_IP) + goto done; +ipv6: + if (ipv6_config_available & 0x1) { /* Address Info present */ + uint32_t prefix; + + if (!mbim_message_get_ipv6_element(message, ipv6_addr_offset, + &prefix, &ipv6)) + goto error; + + inet_ntop(AF_INET6, &ipv6, buf, sizeof(buf)); + ofono_gprs_context_set_ipv6_address(gc, buf); + ofono_gprs_context_set_ipv6_prefix_length(gc, prefix); + } + + if (ipv6_config_available & 0x2) { /* IPv6 Gateway info */ + if (!mbim_message_get_ipv6_address(message, + ipv6_gw_offset, &ipv6)) + goto error; + + inet_ntop(AF_INET6, &ipv6, buf, sizeof(buf)); + + ofono_gprs_context_set_ipv6_gateway(gc, buf); + } + + if (ipv6_config_available & 0x3) { /* IPv6 DNS Info */ + const char *dns[3]; + char dns1[INET6_ADDRSTRLEN]; + char dns2[INET6_ADDRSTRLEN]; + + memset(dns, 0, sizeof(dns)); + + if (n_ipv6_dns > 1) { /* Grab second DNS */ + if (!mbim_message_get_ipv6_address(message, + ipv6_dns_offset + 16, + &ipv6)) + goto error; + + inet_ntop(AF_INET6, &ipv6, dns2, sizeof(dns2)); + dns[1] = dns2; + } + + if (n_ipv6_dns > 0) { /* Grab first DNS */ + if (!mbim_message_get_ipv6_address(message, + ipv6_dns_offset, + &ipv6)) + goto error; + + inet_ntop(AF_INET6, &ipv6, dns1, sizeof(dns1)); + dns[0] = dns1; + + ofono_gprs_context_set_ipv6_dns_servers(gc, dns); + } + } +done: + + gcd->state = STATE_ACTIVE; + interface = ofono_modem_get_string(modem, "NetworkInterface"); + ofono_gprs_context_set_interface(gc, interface); + + CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data); + gcd->cb = NULL; + gcd->cb_data = NULL; + return; + +error: + CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); + gcd->state = STATE_IDLE; + gcd->cb = NULL; + gcd->cb_data = NULL; + + message = mbim_message_new(mbim_uuid_basic_connect, + MBIM_CID_CONNECT, + MBIM_COMMAND_TYPE_SET); + mbim_message_set_arguments(message, "uusssuuu16y", + gcd->active_context, 0, + NULL, NULL, NULL, 0, 0, 0, + mbim_context_type_internet); + + if (!mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message, + NULL, NULL, NULL)) + mbim_message_unref(message); +} + +static void mbim_activate_cb(struct mbim_message *message, void *user) +{ + struct ofono_gprs_context *gc = user; + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + + DBG(""); + + if (mbim_message_get_error(message) != 0) + goto error; + + message = mbim_message_new(mbim_uuid_basic_connect, + MBIM_CID_IP_CONFIGURATION, + MBIM_COMMAND_TYPE_QUERY); + mbim_message_set_arguments(message, "uuuuuuuuuuuuuuu", + gcd->active_context, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message, + mbim_ip_configuration_cb, gc, NULL) > 0) + return; + +error: + CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data); + gcd->state = STATE_IDLE; + gcd->cb = NULL; + gcd->cb_data = NULL; +} + +static void mbim_gprs_activate_primary(struct ofono_gprs_context *gc, + const struct ofono_gprs_primary_context *ctx, + ofono_gprs_context_cb_t cb, void *data) +{ + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + struct mbim_message *message; + + DBG("cid %u", ctx->cid); + + gcd->state = STATE_ENABLING; + gcd->cb = cb; + gcd->cb_data = data; + gcd->active_context = ctx->cid; + gcd->proto = ctx->proto; + + message = mbim_message_new(mbim_uuid_basic_connect, + MBIM_CID_CONNECT, + MBIM_COMMAND_TYPE_SET); + mbim_message_set_arguments(message, "uusssuuu16y", + ctx->cid, + 1, /* MBIMActivationCommandActivate */ + ctx->apn, + ctx->username[0] ? ctx->username : NULL, + ctx->password[0] ? ctx->password : NULL, + 0, /*MBIMCompressionNone */ + auth_method_to_auth_protocol(ctx->auth_method), + proto_to_context_ip_type(ctx->proto), + mbim_context_type_internet); + + if (mbim_device_send(gcd->device, GPRS_CONTEXT_GROUP, message, + mbim_activate_cb, gc, NULL) > 0) + return; + + mbim_message_unref(message); + CALLBACK_WITH_FAILURE(cb, data); +} + +static void mbim_gprs_detach_shutdown(struct ofono_gprs_context *gc, + unsigned int cid) +{ + DBG(""); + mbim_gprs_deactivate_primary(gc, cid, NULL, NULL); +} + +static void mbim_connect_notify(struct mbim_message *message, void *user) +{ + uint32_t session_id; + uint32_t activation_state; + uint32_t voice_call_state; + uint32_t ip_type; + uint8_t context_type[16]; + uint32_t nw_error; + char uuidstr[37]; + + DBG(""); + + if (!mbim_message_get_arguments(message, "uuuu16yu", + &session_id, &activation_state, + &voice_call_state, &ip_type, + context_type, &nw_error)) + return; + + DBG("session_id: %u, activation_state: %u, ip_type: %u", + session_id, activation_state, ip_type); + l_uuid_to_string(context_type, uuidstr, sizeof(uuidstr)); + DBG("context_type: %s, nw_error: %u", uuidstr, nw_error); +} + +static int mbim_gprs_context_probe(struct ofono_gprs_context *gc, + unsigned int vendor, void *data) +{ + struct mbim_device *device = data; + struct gprs_context_data *gcd; + + DBG(""); + + if (!mbim_device_register(device, GPRS_CONTEXT_GROUP, + mbim_uuid_basic_connect, + MBIM_CID_CONNECT, + mbim_connect_notify, gc, NULL)) + return -EIO; + + gcd = l_new(struct gprs_context_data, 1); + gcd->device = mbim_device_ref(device); + + ofono_gprs_context_set_data(gc, gcd); + + return 0; +} + +static void mbim_gprs_context_remove(struct ofono_gprs_context *gc) +{ + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + + DBG(""); + + ofono_gprs_context_set_data(gc, NULL); + + mbim_device_cancel_group(gcd->device, GPRS_CONTEXT_GROUP); + mbim_device_unregister_group(gcd->device, GPRS_CONTEXT_GROUP); + mbim_device_unref(gcd->device); + gcd->device = NULL; + l_free(gcd); +} + +static struct ofono_gprs_context_driver driver = { + .name = "mbim", + .probe = mbim_gprs_context_probe, + .remove = mbim_gprs_context_remove, + .activate_primary = mbim_gprs_activate_primary, + .deactivate_primary = mbim_gprs_deactivate_primary, + .detach_shutdown = mbim_gprs_detach_shutdown +}; + +void mbim_gprs_context_init(void) +{ + ofono_gprs_context_driver_register(&driver); +} + +void mbim_gprs_context_exit(void) +{ + ofono_gprs_context_driver_unregister(&driver); +} diff --git a/drivers/mbimmodem/mbimmodem.c b/drivers/mbimmodem/mbimmodem.c index 5b8b3aca..a4c9daa1 100644 --- a/drivers/mbimmodem/mbimmodem.c +++ b/drivers/mbimmodem/mbimmodem.c @@ -35,11 +35,13 @@ static int mbimmodem_init(void) mbim_netreg_init(); mbim_sms_exit(); mbim_gprs_init(); + mbim_gprs_context_init(); return 0; } static void mbimmodem_exit(void) { + mbim_gprs_context_exit(); mbim_gprs_exit(); mbim_sms_exit(); mbim_netreg_exit(); diff --git a/drivers/mbimmodem/mbimmodem.h b/drivers/mbimmodem/mbimmodem.h index c5d8d889..7c978023 100644 --- a/drivers/mbimmodem/mbimmodem.h +++ b/drivers/mbimmodem/mbimmodem.h @@ -26,6 +26,7 @@ enum MBIM_GROUP { NETREG_GROUP = 2, SMS_GROUP = 3, GPRS_GROUP = 4, + GPRS_CONTEXT_GROUP = 101, }; extern void mbim_devinfo_init(void); @@ -42,3 +43,6 @@ extern void mbim_sms_exit(void); extern void mbim_gprs_init(void); extern void mbim_gprs_exit(void); + +extern void mbim_gprs_context_init(void); +extern void mbim_gprs_context_exit(void);