Hi Denis,
On Fri, Sep 21, 2018 at 6:12 PM Denis Kenzior <denkenz(a)gmail.com> wrote:
Hi Giacinto,
So please fix your commit description in the next version.
certainly.
On 09/19/2018 10:50 PM, Giacinto Cifelli wrote:
> ---
> drivers/gemaltomodem/gemaltomodem.c | 2 +
> drivers/gemaltomodem/gemaltomodem.h | 3 +
> drivers/gemaltomodem/gprs-context-wwan.c | 446
+++++++++++++++++++++++++++++++
> 3 files changed, 451 insertions(+)
> create mode 100644 drivers/gemaltomodem/gprs-context-wwan.c
>
> diff --git a/drivers/gemaltomodem/gemaltomodem.c
b/drivers/gemaltomodem/gemaltomodem.c
> index 614ca81..7afd3c1 100644
> --- a/drivers/gemaltomodem/gemaltomodem.c
> +++ b/drivers/gemaltomodem/gemaltomodem.c
> @@ -35,6 +35,7 @@
> static int gemaltomodem_init(void)
> {
> gemalto_location_reporting_init();
> + gemaltowwan_gprs_context_init();
> gemalto_voicecall_init();
> return 0;
> }
> @@ -42,6 +43,7 @@ static int gemaltomodem_init(void)
> static void gemaltomodem_exit(void)
> {
> gemalto_location_reporting_exit();
> + gemaltowwan_gprs_context_exit();
I'm not really a fan of the name. How many of gprs_context types do you
have, and what is fundamentally different between them that they cannot
be handled by the same driver?
Ok, I will rename it.
gemalto_voicecall_exit();
> }
>
> diff --git a/drivers/gemaltomodem/gemaltomodem.h
b/drivers/gemaltomodem/gemaltomodem.h
> index 4e6ed16..f45aea9 100644
> --- a/drivers/gemaltomodem/gemaltomodem.h
> +++ b/drivers/gemaltomodem/gemaltomodem.h
> @@ -24,5 +24,8 @@
> extern void gemalto_location_reporting_init();
> extern void gemalto_location_reporting_exit();
>
> +extern void gemaltowwan_gprs_context_init();
> +extern void gemaltowwan_gprs_context_exit();
> +
> extern void gemalto_voicecall_init();
> extern void gemalto_voicecall_exit();
> diff --git a/drivers/gemaltomodem/gprs-context-wwan.c
b/drivers/gemaltomodem/gprs-context-wwan.c
> new file mode 100644
> index 0000000..12378a3
> --- /dev/null
> +++ b/drivers/gemaltomodem/gprs-context-wwan.c
> @@ -0,0 +1,446 @@
> +/*
> + *
> + * oFono - Open Source Telephony
> + *
> + * Copyright (C) 2017 Piotr Haber. All rights reserved.
> + * Copyright (C) 2018 Sebastian Arnd. All rights reserved.
> + * Copyright (C) 2018 Gemalto M2M
> + *
> + * 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.
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#define _GNU_SOURCE
> +#include <string.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <errno.h>
> +#include <sys/stat.h>
> +
> +#include <glib.h>
> +
> +#include <ofono/log.h>
> +#include <ofono/modem.h>
> +#include <ofono/gprs-context.h>
> +
> +#include "gatchat.h"
> +#include "gatresult.h"
> +
> +#include "gemaltomodem.h"
> +
> +static const char *none_prefix[] = { NULL };
> +
> +enum state {
> + STATE_IDLE,
> + STATE_ENABLING,
> + STATE_DISABLING,
> + STATE_ACTIVE,
> +};
> +
> +struct gprs_context_data {
> + GAtChat *chat;
> + unsigned int active_context;
> + char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
> + char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
> + enum ofono_gprs_auth_method auth_method;
> + enum state state;
> + enum ofono_gprs_proto proto;
> + char address[64];
> + char netmask[64];
> + char gateway[64];
> + char dns1[64];
> + char dns2[64];
> + ofono_gprs_context_cb_t cb;
> + void *cb_data;
> + int use_wwan;
> +};
> +
> +static gboolean gemalto_get_auth_command(struct ofono_modem *modem, int
cid,
> + enum ofono_gprs_auth_method auth_method,
> + const char *username, const char *password,
> + char *buf, guint buflen)
> +{
> + int gto_auth = ofono_modem_get_integer(modem, "Gemalto_Auth");
Please use a CamelCase-d variable for this. E.g. GemaltoAuthType
ok. This is new for this code, because I have always seen only variables
with underscores, but no problem.
Personally I would prefer camelCase 'gemaltoAuthType', because it is more
coherent in case of 1-part names like 'user',
but I'll do as you say.
> + int len;
> + /*
> + * 0: use cgauth
> + * 1: use sgauth(pwd, user)
> + * 2: use sgauth(user, pwd)
> + */
> +
> + int auth_type;
> +
> + switch (auth_method) {
> + case OFONO_GPRS_AUTH_METHOD_PAP:
> + auth_type = 1;
> + break;
> + case OFONO_GPRS_AUTH_METHOD_CHAP:
> + auth_type = 2;
> + break;
> + case OFONO_GPRS_AUTH_METHOD_NONE:
So until the GPRS_AUTH_METHOD_NONE is actually introduced, you might
want to just get this upstream with the existing enumeration. E.g. just
assume that if username & password is empty, the auth method is none.
I have pushed the new method NONE again, and I prefer to wait until it is
taken.
The current solution is really a non-documented hack.
Checking whether user&password are empty to change the type of
authentication goes against the caller settings.
It is better to give an error so that there is a chance to have good
settings.
> + default:
> + auth_type = 0;
> + break;
> + }
> +
> + if (auth_type != 0 && (!*username || !*password)) > +
return FALSE;
> +
> + switch (gto_auth) {
> + case 1:
> + case 2:
> + len = snprintf(buf, buflen, "AT^SGAUTH=%d", cid);
> + break;
> + case 0:
> + default:
> + len = snprintf(buf, buflen, "AT+CGAUTH=%d", cid);
> + break;
> + }
> +
> + buflen -= len;
> +
> + switch(auth_type) {
> + case 0:
> +
> + switch (gto_auth) {
> + case 2:
> + snprintf(buf+len, buflen,
",0,\"\",\"\"");
> + break;
> + case 0:
> + case 1:
> + default:
> + snprintf(buf+len, buflen, ",0");
> + break;
> + }
> + break;
This switch might be more compact as an if statement. Also the coding
style is all wrong in this function. See doc/coding-style.txt item M3
in particular.
the idea of the switch is to let a reader see the differences among the
various cases,
but in reality there are 2 or 3 flags in this variable, so I am going to
restructure the eval as a bitmap,
and then I will call if's instead of switch's.
Thank you for this review, it helped pointing out the flaw in the design.
> +
> + case 1:
> + case 2:
> +
> + switch (gto_auth) {
> + case 1:
> + snprintf(buf+len, buflen,
",%d,\"%s\",\"%s\"",
> + auth_type, password, username);
> + break;
> + case 0:
> + case 2:
> + default:
> + snprintf(buf+len, buflen,
",%d,\"%s\",\"%s\"",
> + auth_type, username, password);
> + }
> + break;
> +
> + default:
> + return FALSE;
> + }
> +
> + return TRUE;
> +}
> +
> +static void gemalto_get_cgdcont_command(struct ofono_modem *modem,
> + guint cid, enum ofono_gprs_proto proto, const char
*apn,
> + char *buf, guint
buflen)
> +{
> + int len = snprintf(buf, buflen, "AT+CGDCONT=%u", cid);
> + buflen -= len;
> +
> + if (!apn) /* it will remove the context */
> + return;
> +
> + switch (proto) {
> + case OFONO_GPRS_PROTO_IPV6:
> + snprintf(buf+len, buflen,
",\"IPV6\",\"%s\"", apn);
> + break;
> + case OFONO_GPRS_PROTO_IPV4V6:
> + snprintf(buf+len, buflen,
",\"IPV4V6\",\"%s\"", apn);
> + break;
> + case OFONO_GPRS_PROTO_IP:
> + default:
> + snprintf(buf+len, buflen,
",\"IP\",\"%s\"", apn);
> + break;
> + }
> +}
> +
> +static void failed_setup(struct ofono_gprs_context *gc,
> + GAtResult *result, gboolean deactivate)
> +{
> + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> + struct ofono_error error;
> + char buf[64];
> +
> + DBG("deactivate %d", deactivate);
> +
> + if (deactivate == TRUE) {
> +
> + if (gcd->use_wwan)
> + sprintf(buf, "AT^SWWAN=0,%u",
gcd->active_context);
> + else
> + sprintf(buf, "AT+CGACT=0,%u",
gcd->active_context);
> +
> + g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL,
NULL);
> + }
> +
> + gcd->active_context = 0;
> + gcd->state = STATE_IDLE;
> +
> + if (result == NULL) {
> + CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
> + return;
> + }
> +
> + decode_at_error(&error, g_at_result_final_response(result));
> + gcd->cb(&error, gcd->cb_data);
> +}
> +
> +static void activate_cb(gboolean ok, GAtResult *result, gpointer
user_data)
> +{
> + struct ofono_gprs_context *gc = user_data;
> + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> + DBG("ok %d", ok);
> +
> + if (!ok) {
> + ofono_error("Unable activate context");
> + /*
> + * We've reported sucess already, so can't just call
> + * failed_setup we call ofono_gprs_context_deactivated
instead.
> + * Thats not a clean solution at all, but as it seems
there is
> + * no clean way to determine whether it is possible to
activate
> + * the context before issuing AT^SWWAN. A possible
workaround
> + * might be to issue AT+CGACT=1 and AT+CGACT=0 and try if
that
> + * works, before calling CALLBACK_WITH_SUCCESS.
> + */
> + ofono_gprs_context_deactivated(gc, gcd->active_context);
> + gcd->active_context = 0;
> + gcd->state = STATE_IDLE;
> + return;
> + }
> + /* We've reported sucess already */
> +}
> +
> +static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
> +{
> + struct ofono_gprs_context *gc = user_data;
> + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> + char buf[32 + OFONO_GPRS_MAX_USERNAME_LENGTH +
> + OFONO_GPRS_MAX_PASSWORD_LENGTH +1];
> + struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
> + const char *interface;
> +
> + if (!ok) {
> + ofono_error("Failed to setup context");
> + failed_setup(gc, result, FALSE);
> + return;
> + }
> +
> + if (gemalto_get_auth_command(modem, gcd->active_context,
> + gcd->auth_method, gcd->username,
gcd->password,
> + buf, sizeof(buf)))
{
> + if (!g_at_chat_send(gcd->chat, buf, none_prefix, NULL,
NULL,
> +
NULL))
> + goto error;
indentation is wrong here?
yes, i will fix it.
> + }
> + /*
> + * note that if the auth command is not ok we skip it and continue
> + * but if the sending fails we do an error
> + */
> +
> + if (gcd->use_wwan)
> + sprintf(buf, "AT^SWWAN=1,%u", gcd->active_context);
> + else
> + sprintf(buf, "AT+CGACT=%u,1", gcd->active_context);
> +
> + if (g_at_chat_send(gcd->chat, buf, none_prefix,
> + activate_cb, gc, NULL) > 0){
> +
> + interface = ofono_modem_get_string(modem,
"NetworkInterface");
> +
> + ofono_gprs_context_set_interface(gc, interface);
> + ofono_gprs_context_set_ipv4_address(gc, NULL, FALSE);
> +
> + /*
> + * We report sucess already here because some modules need
a
> + * DHCP request to complete the AT^SWWAN command
sucessfully
> + */
> + gcd->state = STATE_ACTIVE;
> +
> + CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
So why are you calling SUCCESS even in the case of +CGACT? Also,
context activations via +CGACT need to be queried for static IPs, no?
E.g. via +CGCONTRDP? I don't see you doing that.
Fundamentally, your context activation has succeeded or it didn't. The
DHCP operation is purely local between the modem and the host. No other
vendor in the world has this weird setup that you do. So I don't really
know what to tell you here as this ^SWWAN interaction is fundamentally
broken and it can't go upstream in this form.
Can ^SWWAN support static ip reporting instead of DHCP? That would be
preferred anyway as DHCP is slow and unneeded.
This is a discussion about how Gemalto products work.
Admittedly, there are a few models, in the high-volume low-price range
(already deployed),
that won't return from AT^SWWAN until a DHCP exchange is performed, as per
Sebastian's comment above.
The rest (which are the large majority) of the modules would allow a
'standard' treatment, but also work with the code above.
As per DHCP vs static, we have both, depending on the module.
Just for information, I have stepped upon networks *requiring* DHCP:
in this kind of networks, an MBIM module that would normally return a
static settings,
returns 0x00 in the "IPv4 configuration Available" field in of the
IP_CONFIGURATION answer,
instead of the usual 0x0F (address, gateway, dns, mtu).
So, it looks like that DHCP has to be considered as a valid alternative
also in this case.
We cannot change every module and every network to fit our wishes.
Back to the code, I don't see a solution at the moment other than that one.
I can add an 'if' to filter only the models that need it, but cannot remove
it completely.
And this code would be specific for the driver, I don't see why it couldn't
go upstream.
> +
> + return;
> + }
> +
> +error:
> + failed_setup(gc, NULL, FALSE);
> +}
> +
> +static void gemaltowwan_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);
> + char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
> + struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
> +
> + DBG("cid %u", ctx->cid);
> +
> + gcd->use_wwan = ofono_modem_get_integer(modem, "Gemalto_WWAN");
> + gcd->active_context = ctx->cid;
> + gcd->cb = cb;
> + gcd->cb_data = data;
> + memcpy(gcd->username, ctx->username, sizeof(ctx->username));
> + memcpy(gcd->password, ctx->password, sizeof(ctx->password));
> + gcd->state = STATE_ENABLING;
> + gcd->proto = ctx->proto;
> + gcd->auth_method = ctx->auth_method;
> +
> + gemalto_get_cgdcont_command(modem, ctx->cid, ctx->proto, ctx->apn,
buf,
> +
sizeof(buf));
> +
> + if (g_at_chat_send(gcd->chat, buf, none_prefix,
> + setup_cb, gc, NULL) > 0)
> + return;
> +
> + CALLBACK_WITH_FAILURE(cb, data);
> +}
> +
> +static void deactivate_cb(gboolean ok, GAtResult *result, gpointer
user_data)
> +{
> + struct ofono_gprs_context *gc = user_data;
> + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> +
> + DBG("ok %d", ok);
> +
> + gcd->active_context = 0;
> + gcd->state = STATE_IDLE;
> +
> + CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
> +}
> +
> +static void gemaltowwan_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);
> + char buf[64];
> +
> + DBG("cid %u", cid);
> +
> + gcd->state = STATE_DISABLING;
> + gcd->cb = cb;
> + gcd->cb_data = data;
> +
> + if (gcd->use_wwan)
> + sprintf(buf, "AT^SWWAN=0,%u", gcd->active_context);
> + else
> + sprintf(buf, "AT+CGACT=%u,0", gcd->active_context);
> +
> + if (g_at_chat_send(gcd->chat, buf, none_prefix,
> + deactivate_cb, gc, NULL) > 0)
> + return;
> +
> + CALLBACK_WITH_SUCCESS(cb, data);
> +}
> +
> +static void cgev_notify(GAtResult *result, gpointer user_data)
> +{
> + struct ofono_gprs_context *gc = user_data;
> + struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
> + const char *event;
> + int cid = 0;
> + GAtResultIter iter;
> +
> + g_at_result_iter_init(&iter, result);
> +
> + if (!g_at_result_iter_next(&iter, "+CGEV:"))
> + return;
> +
> + if (!g_at_result_iter_next_unquoted_string(&iter, &event))
> + return;
> +
> + if (!g_str_has_prefix(event, "ME PDN DEACT"))
> + return;
> +
> + sscanf(event, "%*s %*s %*s %u", &cid);
> +
> + DBG("cid %d", cid);
> +
> + if ((unsigned int) cid != gcd->active_context)
> + return;
> +
> + ofono_gprs_context_deactivated(gc, gcd->active_context);
> +
> + gcd->active_context = 0;
> + gcd->state = STATE_IDLE;
> +}
> +
> +static int gemaltowwan_gprs_context_probe(struct ofono_gprs_context *gc,
> + unsigned int model, void *data)
> +{
> + GAtChat *chat = data;
> + struct gprs_context_data *gcd;
> + struct ofono_modem *modem = ofono_gprs_context_get_modem(gc);
> +
> + DBG("");
> +
> + gcd = g_try_new0(struct gprs_context_data, 1);
> + if (gcd == NULL)
> + return -ENOMEM;
> +
> + if (modem)
> + gcd->use_wwan = ofono_modem_get_integer(modem,
"Gemalto_WWAN");
> + gcd->chat = g_at_chat_clone(chat);
> + ofono_gprs_context_set_data(gc, gcd);
> + g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
> +
> + return 0;
> +}
> +
> +static void gemaltowwan_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);
> +
> + g_at_chat_unref(gcd->chat);
> + g_free(gcd);
> +}
> +
> +static void gemaltowwan_gprs_detach_shutdown(struct ofono_gprs_context
*gc,
> + unsigned int cid)
> +{
> + DBG("cid %u", cid);
> +
> + ofono_gprs_context_deactivated(gc, cid);
> +}
> +
> +static struct ofono_gprs_context_driver driver = {
> + .name = "gemaltowwanmodem",
> + .probe = gemaltowwan_gprs_context_probe,
> + .remove = gemaltowwan_gprs_context_remove,
> + .activate_primary = gemaltowwan_gprs_activate_primary,
> + .deactivate_primary = gemaltowwan_gprs_deactivate_primary,
> + .detach_shutdown = gemaltowwan_gprs_detach_shutdown,
> +};
> +
> +void gemaltowwan_gprs_context_init(void)
> +{
> + ofono_gprs_context_driver_register(&driver);
> +}
> +
> +void gemaltowwan_gprs_context_exit(void)
> +{
> + ofono_gprs_context_driver_unregister(&driver);
> +}
>
Regards,
-Denis
Regards,
Giacinto