diff --git a/linux/gpio.h b/linux/gpio.h new file mode 100644 index 000000000..778ebc356 --- /dev/null +++ b/linux/gpio.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * - userspace ABI for the GPIO character devices + * + * Copyright (C) 2016 Linus Walleij + * + * 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. + */ +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include +#include + +/** + * struct gpiochip_info - Information about a certain GPIO chip + * @name: the Linux kernel name of this GPIO chip + * @label: a functional name for this GPIO chip, such as a product + * number, may be NULL + * @lines: number of GPIO lines on this chip + */ +struct gpiochip_info { + char name[32]; + char label[32]; + __u32 lines; +}; + +/* Informational flags */ +#define GPIOLINE_FLAG_KERNEL (1UL << 0) /* Line used by the kernel */ +#define GPIOLINE_FLAG_IS_OUT (1UL << 1) +#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2) +#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3) +#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) + +/** + * struct gpioline_info - Information about a certain GPIO line + * @line_offset: the local offset on this GPIO device, fill this in when + * requesting the line information from the kernel + * @flags: various flags for this line + * @name: the name of this GPIO line, such as the output pin of the line on the + * chip, a rail or a pin header name on a board, as specified by the gpio + * chip, may be NULL + * @consumer: a functional name for the consumer of this GPIO line as set by + * whatever is using it, will be NULL if there is no current user but may + * also be NULL if the consumer doesn't set this up + */ +struct gpioline_info { + __u32 line_offset; + __u32 flags; + char name[32]; + char consumer[32]; +}; + +/* Maximum number of requested handles */ +#define GPIOHANDLES_MAX 64 + +/* Linerequest flags */ +#define GPIOHANDLE_REQUEST_INPUT (1UL << 0) +#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1) +#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) +#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) +#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) + +/** + * struct gpiohandle_request - Information about a GPIO handle request + * @lineoffsets: an array of desired lines, specified by offset index for the + * associated GPIO device + * @flags: desired flags for the desired GPIO lines, such as + * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed + * together. Note that even if multiple lines are requested, the same flags + * must be applicable to all of them, if you want lines with individual + * flags set, request them one by one. It is possible to select + * a batch of input or output lines, but they must all have the same + * characteristics, i.e. all inputs or all outputs, all active low etc + * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set for a requested + * line, this specifies the default output value, should be 0 (low) or + * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high) + * @consumer_label: a desired consumer label for the selected GPIO line(s) + * such as "my-bitbanged-relay" + * @lines: number of lines requested in this request, i.e. the number of + * valid fields in the above arrays, set to 1 to request a single line + * @fd: if successful this field will contain a valid anonymous file handle + * after a GPIO_GET_LINEHANDLE_IOCTL operation, zero or negative value + * means error + */ +struct gpiohandle_request { + __u32 lineoffsets[GPIOHANDLES_MAX]; + __u32 flags; + __u8 default_values[GPIOHANDLES_MAX]; + char consumer_label[32]; + __u32 lines; + int fd; +}; + +/** + * struct gpiohandle_data - Information of values on a GPIO handle + * @values: when getting the state of lines this contains the current + * state of a line, when setting the state of lines these should contain + * the desired target state + */ +struct gpiohandle_data { + __u8 values[GPIOHANDLES_MAX]; +}; + +#define GPIOHANDLE_GET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x08, struct gpiohandle_data) +#define GPIOHANDLE_SET_LINE_VALUES_IOCTL _IOWR(0xB4, 0x09, struct gpiohandle_data) + +/* Eventrequest flags */ +#define GPIOEVENT_REQUEST_RISING_EDGE (1UL << 0) +#define GPIOEVENT_REQUEST_FALLING_EDGE (1UL << 1) +#define GPIOEVENT_REQUEST_BOTH_EDGES ((1UL << 0) | (1UL << 1)) + +/** + * struct gpioevent_request - Information about a GPIO event request + * @lineoffset: the desired line to subscribe to events from, specified by + * offset index for the associated GPIO device + * @handleflags: desired handle flags for the desired GPIO line, such as + * GPIOHANDLE_REQUEST_ACTIVE_LOW or GPIOHANDLE_REQUEST_OPEN_DRAIN + * @eventflags: desired flags for the desired GPIO event line, such as + * GPIOEVENT_REQUEST_RISING_EDGE or GPIOEVENT_REQUEST_FALLING_EDGE + * @consumer_label: a desired consumer label for the selected GPIO line(s) + * such as "my-listener" + * @fd: if successful this field will contain a valid anonymous file handle + * after a GPIO_GET_LINEEVENT_IOCTL operation, zero or negative value + * means error + */ +struct gpioevent_request { + __u32 lineoffset; + __u32 handleflags; + __u32 eventflags; + char consumer_label[32]; + int fd; +}; + +/** + * GPIO event types + */ +#define GPIOEVENT_EVENT_RISING_EDGE 0x01 +#define GPIOEVENT_EVENT_FALLING_EDGE 0x02 + +/** + * struct gpioevent_data - The actual event being pushed to userspace + * @timestamp: best estimate of time of event occurrence, in nanoseconds + * @id: event identifier + */ +struct gpioevent_data { + __u64 timestamp; + __u32 id; +}; + +#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info) +#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info) +#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request) +#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request) + +#endif /* _GPIO_H_ */ diff --git a/linux/gsmmux.h b/linux/gsmmux.h new file mode 100644 index 000000000..101d3c469 --- /dev/null +++ b/linux/gsmmux.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_GSMMUX_H +#define _LINUX_GSMMUX_H + +#include +#include +#include + +struct gsm_config +{ + unsigned int adaption; + unsigned int encapsulation; + unsigned int initiator; + unsigned int t1; + unsigned int t2; + unsigned int t3; + unsigned int n2; + unsigned int mru; + unsigned int mtu; + unsigned int k; + unsigned int i; + unsigned int unused[8]; /* Padding for expansion without + breaking stuff */ +}; + +#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config) +#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config) + +struct gsm_netconfig { + unsigned int adaption; /* Adaption to use in network mode */ + unsigned short protocol;/* Protocol to use - only ETH_P_IP supported */ + unsigned short unused2; + char if_name[IFNAMSIZ]; /* interface name format string */ + __u8 unused[28]; /* For future use */ +}; + +#define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_netconfig) +#define GSMIOC_DISABLE_NET _IO('G', 3) + + +#endif diff --git a/ofono/AUTHORS b/ofono/AUTHORS index 758d571e7..796b2680f 100644 --- a/ofono/AUTHORS +++ b/ofono/AUTHORS @@ -126,6 +126,7 @@ Lukasz Nowak Jonas Bonn Matthijs Kooijman Clayton Craft +Alexander Couzens Joey Hewitt Richard Röjfors Philippe De Swert @@ -139,5 +140,8 @@ Martin Hundebøll Julien Tournier Nandini Rebello Giacinto Cifelli +Pau Espin Pedrol Khaled Romdhani Pavel Machek +Tom Nguyen +Stefan Herbrechtsmeier diff --git a/ofono/ChangeLog b/ofono/ChangeLog index 2a426bce9..283f3b5b2 100644 --- a/ofono/ChangeLog +++ b/ofono/ChangeLog @@ -1,3 +1,9 @@ +ver 1.30: + Fix issue with handling of IPv6 address and xmm7xxx modems. + Fix issue with default context creation with an empty APN. + Fix issue with activation of context with CID zero. + Add support for Quectel MC60 modems. + ver 1.29: Fix issue with QMI and SIM initialized notification. Add support for multiple PDP contexts and xmm7xxx modems. diff --git a/ofono/Makefile.am b/ofono/Makefile.am index d067817db..088932e62 100644 --- a/ofono/Makefile.am +++ b/ofono/Makefile.am @@ -267,6 +267,7 @@ builtin_sources += drivers/atmodem/atmodem.h \ drivers/atmodem/cbs.c \ drivers/atmodem/call-forwarding.c \ drivers/atmodem/call-meter.c \ + drivers/atmodem/network-registration.h \ drivers/atmodem/network-registration.c \ drivers/atmodem/sim.c \ drivers/atmodem/stk.c \ @@ -404,6 +405,7 @@ builtin_sources += drivers/atmodem/atutil.h \ drivers/ubloxmodem/ubloxmodem.h \ drivers/ubloxmodem/ubloxmodem.c \ drivers/ubloxmodem/gprs-context.c \ + drivers/ubloxmodem/network-registration.c \ drivers/ubloxmodem/netmon.c \ drivers/ubloxmodem/lte.c @@ -695,7 +697,8 @@ endif sbin_PROGRAMS = src/ofonod -src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \ +src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) \ + linux/gsmmux.h linux/gpio.h src/ofono.ver \ src/main.c src/ofono.h src/log.c src/plugin.c \ src/modem.c src/common.h src/common.c \ src/manager.c src/dbus.c src/util.h src/util.c \ diff --git a/ofono/configure.ac b/ofono/configure.ac index 397983972..05efb3503 100644 --- a/ofono/configure.ac +++ b/ofono/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(ofono, 1.29) +AC_INIT(ofono, 1.30) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests]) AC_CONFIG_HEADERS(config.h) @@ -55,6 +55,8 @@ AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], fi ]) +AC_CHECK_FUNCS(explicit_bzero) + AC_CHECK_FUNC(signalfd, dummy=yes, AC_MSG_ERROR(signalfd support is required)) diff --git a/ofono/doc/networkmonitor-api.txt b/ofono/doc/networkmonitor-api.txt index f8cc1c2e9..1cc73d2ca 100644 --- a/ofono/doc/networkmonitor-api.txt +++ b/ofono/doc/networkmonitor-api.txt @@ -22,6 +22,23 @@ Methods a{sv} GetServingCellInformation() are available, their valid value ranges and applicability to different cell types. + a{a{sv}} GetNeighbouringCellsInformation() + + Requests the neighbouring cells information and basic + measurements from oFono. The returned value is a + dictionary with the possible key / values documented + below. The type of cell is given by the 'Technology' + property. + + Based on the type of cell, the dictionary will contain + additional key/value pairs. If a given key/value pair + is not present, then it is not known or unsupported + by the underlying driver. + + Refer to the sections below for which property types + are available, their valid value ranges and + applicability to different cell types. + void RegisterAgent(object path) Registers an agent which will be called whenever the diff --git a/ofono/doc/quectel-hardware-api.txt b/ofono/doc/quectel-hardware-api.txt new file mode 100644 index 000000000..139a0dc1d --- /dev/null +++ b/ofono/doc/quectel-hardware-api.txt @@ -0,0 +1,51 @@ +Hardware hierarchy +================== + +Service org.ofono +Interface org.ofono.quectel.Hardware +Object path /{device0,device1,...} + +Methods array{string,variant} GetProperties + + Returns hardware properties for the modem object. See + the properties section for available properties. + +Signals PowerDown(string reason) + + This signal is emitted on gracefull shutdowns initiated + by the modem. + + Possible reasons: + "VoltageLow" The supply voltage is too low + "Normal" The PWRKEY pin was asserted + "VoltageHigh" The supply voltage is too high + + PowerWarning(string reason) + + This signal is emitted when the modem detects its supply + voltage is close to its supported limits. + + Possible reasons: + "VoltageLow" The supply voltage is low + "VoltageHigh" The supply voltage is high + +Properties int32 Voltage [readonly] + + Integer with the modem supply voltage in mV. + + int32 ChargeStatus [readonly,optional] + + Integer with one of either: + + 0: Modem is not charging + 1: Modem is charging + 2: Charging is finished + + This property is available on UC15 + + int32 ChargeLevel [readonly,optional] + + Integer representing the battery charge level in + percent (from 0 to 100). + + This property is available on UC15 diff --git a/ofono/doc/radio-settings-api.txt b/ofono/doc/radio-settings-api.txt index 03868a9b1..9663b07f9 100644 --- a/ofono/doc/radio-settings-api.txt +++ b/ofono/doc/radio-settings-api.txt @@ -44,6 +44,12 @@ Properties string TechnologyPreference [readwrite] "gsm" Only GSM used for radio access. "umts" Only UMTS used for radio access. "lte" Only LTE used for radio access. + "umts,gsm" Dual mode operation with UMTS + and GSM radio access whith preference + for UMTS. + "lte,umts" Dual mode operation with LTE + and UMTS radio access with preference + for LTE. array{string} AvailableTechnologies [readonly, optional] diff --git a/ofono/drivers/atmodem/atutil.c b/ofono/drivers/atmodem/atutil.c index fa9ed72f7..073f86b31 100644 --- a/ofono/drivers/atmodem/atutil.c +++ b/ofono/drivers/atmodem/atutil.c @@ -24,15 +24,17 @@ #include #endif -#include -#include #include #include #include +#include +#include + #define OFONO_API_SUBJECT_TO_CHANGE #include #include +#include #include "atutil.h" #include "vendor.h" @@ -656,6 +658,45 @@ int at_util_get_ipv4_address_and_netmask(const char *addrnetmask, return ret; } +/* + * CGCONTRDP returns addr + netmask in the same string in the form + * of "a1.a2.a3.a4.a5.a6.a7.a8.a9.a10.a11.a12.a13.a14.a15.a16.m1.m2. + * m3.m4.m5.m6.m7.m8.m9.m10.m11.m12.m13.m14.m15.m16" for IPv6. + * address/netmask must be able to hold 64 characters. + */ +int at_util_get_ipv6_address_and_netmask(const char *addrnetmask, + char *address, char *netmask) +{ + const char *s = addrnetmask; + const char *net = NULL; + + int ret = -EINVAL; + int i; + + /* Count 31 dots for ipv6, less or more means error. */ + for (i = 0; i < 33; i++, s++) { + s = strchr(s, '.'); + + if (!s) + break; + + if (i == 15) { + /* set netmask ptr and break the string */ + net = s + 1; + } + } + + if (i == 31) { + memcpy(address, addrnetmask, net - addrnetmask); + address[net - addrnetmask - 1] = '\0'; + strcpy(netmask, net); + + ret = 0; + } + + return ret; +} + int at_util_gprs_auth_method_to_auth_prot( enum ofono_gprs_auth_method auth_method) { @@ -698,3 +739,54 @@ char *at_util_get_cgdcont_command(guint cid, enum ofono_gprs_proto proto, return g_strdup_printf("AT+CGDCONT=%u,\"%s\",\"%s\"", cid, pdp_type, apn); } + +GAtChat *at_util_open_device(struct ofono_modem *modem, const char *key, + GAtDebugFunc debug_func, char *debug_prefix, + char *tty_option, ...) +{ + const char *device; + va_list args; + GIOChannel *channel; + GAtSyntax *syntax; + GAtChat *chat; + GHashTable *options = NULL; + + device = ofono_modem_get_string(modem, key); + if (device == NULL) + return NULL; + + if (tty_option) { + options = g_hash_table_new(g_str_hash, g_str_equal); + if (options == NULL) + return NULL; + + va_start(args, tty_option); + while (tty_option) { + gpointer value = (gpointer) va_arg(args, const char *); + + g_hash_table_insert(options, tty_option, value); + tty_option = (gpointer) va_arg(args, const char *); + } + } + + channel = g_at_tty_open(device, options); + + if (options) + g_hash_table_destroy(options); + + if (channel == NULL) + return NULL; + + syntax = g_at_syntax_new_gsm_permissive(); + chat = g_at_chat_new(channel, syntax); + g_at_syntax_unref(syntax); + g_io_channel_unref(channel); + + if (chat == NULL) + return NULL; + + if (getenv("OFONO_AT_DEBUG")) + g_at_chat_set_debug(chat, debug_func, debug_prefix); + + return chat; +} diff --git a/ofono/drivers/atmodem/atutil.h b/ofono/drivers/atmodem/atutil.h index 69e8b499d..fe2acb390 100644 --- a/ofono/drivers/atmodem/atutil.h +++ b/ofono/drivers/atmodem/atutil.h @@ -20,6 +20,10 @@ * */ +#include + +struct ofono_modem; + enum at_util_sms_store { AT_UTIL_SMS_STORE_SM = 0, AT_UTIL_SMS_STORE_ME = 1, @@ -86,6 +90,9 @@ void at_util_sim_state_query_free(struct at_util_sim_state_query *req); int at_util_get_ipv4_address_and_netmask(const char *addrnetmask, char *address, char *netmask); +int at_util_get_ipv6_address_and_netmask(const char *addrnetmask, + char *address, char *netmask); + int at_util_gprs_auth_method_to_auth_prot( enum ofono_gprs_auth_method auth_method); @@ -166,3 +173,7 @@ static inline int at_util_convert_signal_strength(int strength) e.error = 0; \ f(&e, ##args); \ } while (0) + +GAtChat *at_util_open_device(struct ofono_modem *modem, const char *key, + GAtDebugFunc debug_func, char *debug_prefix, + char *tty_option, ...); diff --git a/ofono/drivers/atmodem/gprs-context.c b/ofono/drivers/atmodem/gprs-context.c index b86c63893..403a300dd 100644 --- a/ofono/drivers/atmodem/gprs-context.c +++ b/ofono/drivers/atmodem/gprs-context.c @@ -285,35 +285,36 @@ static void at_gprs_activate_primary(struct ofono_gprs_context *gc, len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); - if (ctx->apn) { - switch (gcd->vendor) { - case OFONO_VENDOR_UBLOX: - /* - * U-blox modems require a magic prefix to the APN to - * specify the authentication method to use in the - * network. See UBX-13002752 - R21. - * - * As the response of the read command omits this magic - * prefix, this is the least invasive place to set it. - */ - switch (ctx->auth_method) { - case OFONO_GPRS_AUTH_METHOD_ANY: - case OFONO_GPRS_AUTH_METHOD_NONE: - case OFONO_GPRS_AUTH_METHOD_CHAP: - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"CHAP:%s\"", ctx->apn); - break; - case OFONO_GPRS_AUTH_METHOD_PAP: - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"PAP:%s\"", ctx->apn); - break; - } + switch (gcd->vendor) { + case OFONO_VENDOR_UBLOX: + /* + * U-blox modems require a magic prefix to the APN to + * specify the authentication method to use in the + * network. See UBX-13002752 - R21. + * + * As the response of the read command omits this magic + * prefix, this is the least invasive place to set it. + */ + switch (ctx->auth_method) { + case OFONO_GPRS_AUTH_METHOD_ANY: + case OFONO_GPRS_AUTH_METHOD_CHAP: + snprintf(buf + len, sizeof(buf) - len - 3, + ",\"CHAP:%s\"", ctx->apn); break; - default: - snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", - ctx->apn); + case OFONO_GPRS_AUTH_METHOD_PAP: + snprintf(buf + len, sizeof(buf) - len - 3, + ",\"PAP:%s\"", ctx->apn); + break; + case OFONO_GPRS_AUTH_METHOD_NONE: + snprintf(buf + len, sizeof(buf) - len - 3, + ",\"%s\"", ctx->apn); break; } + break; + default: + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", + ctx->apn); + break; } if (g_at_chat_send(gcd->chat, buf, none_prefix, @@ -405,9 +406,11 @@ static void at_cgdata_test_cb(gboolean ok, GAtResult *result, goto error; } - if (!g_at_result_iter_open_list(&iter)) { - DBG("no list found"); - goto error; + if (gcd->vendor != OFONO_VENDOR_QUECTEL_SERIAL) { + if (!g_at_result_iter_open_list(&iter)) { + DBG("no list found"); + goto error; + } } while (!found && g_at_result_iter_next_string(&iter, &data_type)) { diff --git a/ofono/drivers/atmodem/gprs.c b/ofono/drivers/atmodem/gprs.c index d3fd893cc..101082817 100644 --- a/ofono/drivers/atmodem/gprs.c +++ b/ofono/drivers/atmodem/gprs.c @@ -43,12 +43,13 @@ static const char *cgreg_prefix[] = { "+CGREG:", NULL }; static const char *cgdcont_prefix[] = { "+CGDCONT:", NULL }; +static const char *cgact_prefix[] = { "+CGACT:", NULL }; static const char *none_prefix[] = { NULL }; struct gprs_data { GAtChat *chat; unsigned int vendor; - unsigned int last_auto_context_id; + int last_auto_context_id; gboolean telit_try_reattach; int attached; }; @@ -161,6 +162,11 @@ static void at_cgdcont_read_cb(gboolean ok, GAtResult *result, return; } + if (gd->last_auto_context_id == -1) { + DBG("Context got deactivated while calling CGDCONT"); + return; + } + g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+CGDCONT:")) { @@ -187,6 +193,48 @@ static void at_cgdcont_read_cb(gboolean ok, GAtResult *result, activated_cid); } +static void at_cgact_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct ofono_gprs *gprs = user_data; + struct gprs_data *gd = ofono_gprs_get_data(gprs); + GAtResultIter iter; + + DBG("ok %d", ok); + + if (!ok) { + ofono_warn("Can't read CGACT contexts."); + return; + } + + g_at_result_iter_init(&iter, result); + + while (g_at_result_iter_next(&iter, "+CGACT:")) { + int read_cid = -1; + int read_status = -1; + + if (!g_at_result_iter_next_number(&iter, &read_cid)) + break; + + if (!g_at_result_iter_next_number(&iter, &read_status)) + break; + + if (read_status != 1) + continue; + + /* Flag this as auto context as it was obviously active */ + if (gd->last_auto_context_id == 0) + gd->last_auto_context_id = read_cid; + + if (read_cid != gd->last_auto_context_id) + continue; + + g_at_chat_send(gd->chat, "AT+CGDCONT?", cgdcont_prefix, + at_cgdcont_read_cb, gprs, NULL); + + break; + } +} + static void cgreg_notify(GAtResult *result, gpointer user_data) { struct ofono_gprs *gprs = user_data; @@ -251,6 +299,12 @@ static void cgev_notify(GAtResult *result, gpointer user_data) g_at_chat_send(gd->chat, "AT+CGDCONT?", cgdcont_prefix, at_cgdcont_read_cb, gprs, NULL); + } else if (g_str_has_prefix(event, "ME PDN DEACT")) { + int context_id; + sscanf(event, "%*s %*s %*s %u", &context_id); + /* Indicate that this cid is not activated anymore */ + if (gd->last_auto_context_id == context_id) + gd->last_auto_context_id = -1; } } @@ -484,6 +538,10 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data) break; } + /* Check if there is any already activated contexts at init */ + g_at_chat_send(gd->chat, "AT+CGACT?", cgact_prefix, + at_cgact_cb, gprs, NULL); + ofono_gprs_register(gprs); } @@ -621,6 +679,7 @@ static int at_gprs_probe(struct ofono_gprs *gprs, gd->chat = g_at_chat_clone(chat); gd->vendor = vendor; + gd->last_auto_context_id = -1; ofono_gprs_set_data(gprs, gd); diff --git a/ofono/drivers/atmodem/network-registration.c b/ofono/drivers/atmodem/network-registration.c index 67380b733..cc702c2c2 100644 --- a/ofono/drivers/atmodem/network-registration.c +++ b/ofono/drivers/atmodem/network-registration.c @@ -41,6 +41,8 @@ #include "atmodem.h" #include "vendor.h" +#include "network-registration.h" + static const char *none_prefix[] = { NULL }; static const char *creg_prefix[] = { "+CREG:", NULL }; static const char *cops_prefix[] = { "+COPS:", NULL }; @@ -51,20 +53,6 @@ static const char *smoni_prefix[] = { "^SMONI:", NULL }; static const char *zpas_prefix[] = { "+ZPAS:", NULL }; static const char *option_tech_prefix[] = { "_OCTI:", "_OUWCTI:", NULL }; -struct netreg_data { - GAtChat *chat; - char mcc[OFONO_MAX_MCC_LENGTH + 1]; - char mnc[OFONO_MAX_MNC_LENGTH + 1]; - int signal_index; /* If strength is reported via CIND */ - int signal_min; /* min strength reported via CIND */ - int signal_max; /* max strength reported via CIND */ - int signal_invalid; /* invalid strength reported via CIND */ - int tech; - struct ofono_network_time time; - guint nitz_timeout; - unsigned int vendor; -}; - struct tech_query { int status; int lac; @@ -209,7 +197,7 @@ static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data) ofono_netreg_status_cb_t cb = cbd->cb; int status, lac, ci, tech; struct ofono_error error; - struct netreg_data *nd = cbd->user; + struct at_netreg_data *nd = cbd->user; decode_at_error(&error, g_at_result_final_response(result)); @@ -250,7 +238,7 @@ static void zte_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_netreg *netreg = cbd->data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); if (ok) nd->tech = zte_parse_tech(result); @@ -262,7 +250,7 @@ static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_netreg *netreg = cbd->data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); if (ok) nd->tech = option_parse_tech(result); @@ -270,11 +258,11 @@ static void option_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) nd->tech = -1; } -static void at_registration_status(struct ofono_netreg *netreg, +void at_registration_status(struct ofono_netreg *netreg, ofono_netreg_status_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = nd; @@ -337,7 +325,7 @@ static void at_registration_status(struct ofono_netreg *netreg, static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; - struct netreg_data *nd = ofono_netreg_get_data(cbd->user); + struct at_netreg_data *nd = ofono_netreg_get_data(cbd->user); ofono_netreg_operator_cb_t cb = cbd->cb; struct ofono_network_operator op; GAtResultIter iter; @@ -398,7 +386,7 @@ static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data) static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; - struct netreg_data *nd = ofono_netreg_get_data(cbd->user); + struct at_netreg_data *nd = ofono_netreg_get_data(cbd->user); ofono_netreg_operator_cb_t cb = cbd->cb; GAtResultIter iter; const char *str; @@ -450,10 +438,10 @@ static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data) g_free(cbd); } -static void at_current_operator(struct ofono_netreg *netreg, +void at_current_operator(struct ofono_netreg *netreg, ofono_netreg_operator_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); gboolean ok; @@ -589,10 +577,10 @@ static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data) g_free(list); } -static void at_list_operators(struct ofono_netreg *netreg, +void at_list_operators(struct ofono_netreg *netreg, ofono_netreg_operator_list_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(nd->chat, "AT+COPS=?", cops_prefix, @@ -615,10 +603,10 @@ static void register_cb(gboolean ok, GAtResult *result, gpointer user_data) cb(&error, cbd->data); } -static void at_register_auto(struct ofono_netreg *netreg, +void at_register_auto(struct ofono_netreg *netreg, ofono_netreg_register_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); if (g_at_chat_send(nd->chat, "AT+COPS=0", none_prefix, @@ -630,11 +618,11 @@ static void at_register_auto(struct ofono_netreg *netreg, CALLBACK_WITH_FAILURE(cb, data); } -static void at_register_manual(struct ofono_netreg *netreg, +void at_register_manual(struct ofono_netreg *netreg, const char *mcc, const char *mnc, ofono_netreg_register_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); char buf[128]; @@ -723,7 +711,7 @@ static void ifx_xhomezr_notify(GAtResult *result, gpointer user_data) static void ifx_xreg_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int state; const char *band; GAtResultIter iter; @@ -822,7 +810,7 @@ static void ifx_xcsq_notify(GAtResult *result, gpointer user_data) static void ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int strength, ind; GAtResultIter iter; @@ -851,7 +839,7 @@ static void ciev_notify(GAtResult *result, gpointer user_data) static void telit_ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); const char *signal_identifier = "rssi"; const char *ind_str; int strength; @@ -882,7 +870,7 @@ static void telit_ciev_notify(GAtResult *result, gpointer user_data) static void gemalto_ciev_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); const char *signal_identifier = "rssi"; const char *ind_str; int strength; @@ -915,7 +903,7 @@ static void gemalto_ciev_notify(GAtResult *result, gpointer user_data) static void ctzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); const char *tz; GAtResultIter iter; @@ -937,7 +925,7 @@ static void ctzv_notify(GAtResult *result, gpointer user_data) static void tlts_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; char tz[4]; const char *time; @@ -972,7 +960,7 @@ static void tlts_notify(GAtResult *result, gpointer user_data) static gboolean notify_time(gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); nd->nitz_timeout = 0; @@ -984,7 +972,7 @@ static gboolean notify_time(gpointer user_data) static void ifx_ctzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; const char *tz, *time; GAtResultIter iter; @@ -1022,7 +1010,7 @@ static void ifx_ctzv_notify(GAtResult *result, gpointer user_data) static void ifx_ctzdst_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int dst; GAtResultIter iter; @@ -1050,7 +1038,7 @@ static void cind_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; ofono_netreg_strength_cb_t cb = cbd->cb; - struct netreg_data *nd = cbd->user; + struct at_netreg_data *nd = cbd->user; int index; int strength; GAtResultIter iter; @@ -1104,7 +1092,7 @@ static void huawei_rssi_notify(GAtResult *result, gpointer user_data) static void huawei_mode_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int mode, submode; @@ -1132,7 +1120,7 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data) static void huawei_hcsq_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *mode; @@ -1153,7 +1141,7 @@ static void huawei_hcsq_notify(GAtResult *result, gpointer user_data) static void huawei_nwtime_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; char tz[4]; const char *date, *time, *dst; @@ -1228,10 +1216,10 @@ static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data) cb(&error, strength, cbd->data); } -static void at_signal_strength(struct ofono_netreg *netreg, +void at_signal_strength(struct ofono_netreg *netreg, ofono_netreg_strength_cb_t cb, void *data) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct cb_data *cbd = cb_data_new(cb, data); cbd->user = nd; @@ -1258,7 +1246,7 @@ static void at_signal_strength(struct ofono_netreg *netreg, static void mbm_etzv_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); int year, mon, mday, hour, min, sec; const char *tz, *time, *timestamp; GAtResultIter iter; @@ -1307,7 +1295,7 @@ static void mbm_etzv_notify(GAtResult *result, gpointer user_data) static void mbm_erinfo_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int mode, gsm, umts; @@ -1359,7 +1347,7 @@ static void mbm_erinfo_notify(GAtResult *result, gpointer user_data) static void icera_nwstate_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *mccmnc, *tech, *state; int rssi; @@ -1427,7 +1415,7 @@ static int cnti_to_tech(const char *cnti) static void gobi_cnti_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *tech; int option; @@ -1452,7 +1440,7 @@ static void gobi_cnti_notify(GAtResult *result, gpointer user_data) static void nw_cnti_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *tech; int option; @@ -1478,7 +1466,7 @@ static void cnti_query_tech_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct tech_query *tq = user_data; - struct netreg_data *nd = ofono_netreg_get_data(tq->netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(tq->netreg); ofono_netreg_status_notify(tq->netreg, tq->status, tq->lac, tq->ci, nd->tech); @@ -1518,7 +1506,7 @@ static void creg_notify(GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; int status, lac, ci, tech; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); struct tech_query *tq; if (at_util_parse_reg_unsolicited(result, "+CREG:", &status, @@ -1587,7 +1575,7 @@ static void at_cmer_not_supported(struct ofono_netreg *netreg) static void at_cmer_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); if (!ok) { at_cmer_not_supported(netreg); @@ -1646,7 +1634,7 @@ static inline ofono_bool_t append_cmer_element(char *buf, int *len, int cap, } static ofono_bool_t build_cmer_string(char *buf, int *cmer_opts, - struct netreg_data *nd) + struct at_netreg_data *nd) { const char *ind; int len = sprintf(buf, "AT+CMER="); @@ -1715,7 +1703,7 @@ static void at_cmer_query_cb(ofono_bool_t ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; int cmer_opts_cnt = 5; /* See 27.007 Section 8.10 */ int cmer_opts[cmer_opts_cnt]; @@ -1763,7 +1751,7 @@ static void at_cmer_query_cb(ofono_bool_t ok, GAtResult *result, static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); GAtResultIter iter; const char *str; char *signal_identifier = "signal"; @@ -1870,7 +1858,7 @@ static void cind_support_cb(gboolean ok, GAtResult *result, gpointer user_data) static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); if (!ok) { ofono_error("Unable to initialize Network Registration"); @@ -2073,7 +2061,7 @@ static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data) static void at_creg_test_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct ofono_netreg *netreg = user_data; - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); gint range[2]; GAtResultIter iter; int creg1 = 0; @@ -2121,9 +2109,9 @@ static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, void *data) { GAtChat *chat = data; - struct netreg_data *nd; + struct at_netreg_data *nd; - nd = g_new0(struct netreg_data, 1); + nd = g_new0(struct at_netreg_data, 1); nd->chat = g_at_chat_clone(chat); nd->vendor = vendor; @@ -2144,9 +2132,9 @@ static int at_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor, return 0; } -static void at_netreg_remove(struct ofono_netreg *netreg) +void at_netreg_remove(struct ofono_netreg *netreg) { - struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); if (nd->nitz_timeout) g_source_remove(nd->nitz_timeout); diff --git a/ofono/drivers/atmodem/network-registration.h b/ofono/drivers/atmodem/network-registration.h new file mode 100644 index 000000000..8a5401cf8 --- /dev/null +++ b/ofono/drivers/atmodem/network-registration.h @@ -0,0 +1,31 @@ +#pragma once + +struct at_netreg_data { + GAtChat *chat; + char mcc[OFONO_MAX_MCC_LENGTH + 1]; + char mnc[OFONO_MAX_MNC_LENGTH + 1]; + int signal_index; /* If strength is reported via CIND */ + int signal_min; /* min strength reported via CIND */ + int signal_max; /* max strength reported via CIND */ + int signal_invalid; /* invalid strength reported via CIND */ + int tech; + struct ofono_network_time time; + guint nitz_timeout; + unsigned int vendor; +}; + +void at_registration_status(struct ofono_netreg *netreg, + ofono_netreg_status_cb_t cb, + void *data); +void at_current_operator(struct ofono_netreg *netreg, + ofono_netreg_operator_cb_t cb, void *data); +void at_list_operators(struct ofono_netreg *netreg, + ofono_netreg_operator_list_cb_t cb, void *data); +void at_register_auto(struct ofono_netreg *netreg, + ofono_netreg_register_cb_t cb, void *data); +void at_register_manual(struct ofono_netreg *netreg, + const char *mcc, const char *mnc, + ofono_netreg_register_cb_t cb, void *data); +void at_signal_strength(struct ofono_netreg *netreg, + ofono_netreg_strength_cb_t cb, void *data); +void at_netreg_remove(struct ofono_netreg *netreg); diff --git a/ofono/drivers/atmodem/sim.c b/ofono/drivers/atmodem/sim.c index 520b3dbf5..dd42cac43 100644 --- a/ofono/drivers/atmodem/sim.c +++ b/ofono/drivers/atmodem/sim.c @@ -1217,7 +1217,7 @@ static void at_pin_retries_query(struct ofono_sim *sim, at_qpinc_cb, cbd, g_free) > 0) return; break; - case OFONO_VENDOR_QUECTEL_M95: + case OFONO_VENDOR_QUECTEL_SERIAL: if (g_at_chat_send(sd->chat, "AT+QTRPIN", qtrpin_prefix, at_qtrpin_cb, cbd, g_free) > 0) return; @@ -1354,7 +1354,7 @@ static void at_pin_send_cb(gboolean ok, GAtResult *result, case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_SIMCOM: case OFONO_VENDOR_SIERRA: - case OFONO_VENDOR_QUECTEL_M95: + case OFONO_VENDOR_QUECTEL_SERIAL: /* * On ZTE modems, after pin is entered, SIM state is checked * by polling CPIN as their modem doesn't provide unsolicited diff --git a/ofono/drivers/atmodem/sms.c b/ofono/drivers/atmodem/sms.c index 277d65175..de8a28059 100644 --- a/ofono/drivers/atmodem/sms.c +++ b/ofono/drivers/atmodem/sms.c @@ -338,6 +338,9 @@ static inline void at_ack_delivery(struct ofono_sms *sms) case OFONO_VENDOR_GEMALTO: snprintf(buf, sizeof(buf), "AT+CNMA=1"); break; + case OFONO_VENDOR_QUECTEL_SERIAL: + snprintf(buf, sizeof(buf), "AT+CNMA"); + break; default: snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s", data->cnma_ack_pdu_len, @@ -1237,7 +1240,7 @@ static void at_csms_status_cb(gboolean ok, GAtResult *result, if (!g_at_result_iter_next_number(&iter, &mo)) goto out; - if (service == 1) + if (service == 1 || service == 128) data->cnma_enabled = TRUE; if (mt == 1 && mo == 1) @@ -1268,10 +1271,10 @@ static void at_csms_query_cb(gboolean ok, GAtResult *result, { struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); - gboolean cnma_supported = FALSE; GAtResultIter iter; int status_min, status_max; char buf[128]; + int csms = 0; if (!ok) return at_sms_not_supported(sms); @@ -1284,14 +1287,25 @@ static void at_csms_query_cb(gboolean ok, GAtResult *result, if (!g_at_result_iter_open_list(&iter)) goto out; - while (g_at_result_iter_next_range(&iter, &status_min, &status_max)) + switch (data->vendor) { + case OFONO_VENDOR_QUECTEL_SERIAL: + g_at_result_iter_next_number(&iter, &status_min); + g_at_result_iter_next_number(&iter, &status_max); if (status_min <= 1 && 1 <= status_max) - cnma_supported = TRUE; + csms = 128; + break; + default: + while (g_at_result_iter_next_range(&iter, &status_min, + &status_max)) + if (status_min <= 1 && 1 <= status_max) + csms = 1; + break; + } DBG("CSMS query parsed successfully"); out: - snprintf(buf, sizeof(buf), "AT+CSMS=%d", cnma_supported ? 1 : 0); + snprintf(buf, sizeof(buf), "AT+CSMS=%d", csms); g_at_chat_send(data->chat, buf, csms_prefix, at_csms_set_cb, sms, NULL); } diff --git a/ofono/drivers/atmodem/stk.c b/ofono/drivers/atmodem/stk.c index f0dc5c036..b2d208159 100644 --- a/ofono/drivers/atmodem/stk.c +++ b/ofono/drivers/atmodem/stk.c @@ -191,6 +191,19 @@ static gboolean at_stk_register(gpointer user) g_at_chat_register(sd->chat, "*HCMD:", phonesim_hcmd_notify, FALSE, stk, NULL); + if (sd->vendor == OFONO_VENDOR_XMM) { + /* enabling stk */ + g_at_chat_send(sd->chat, "AT+CFUN=6", none_prefix, + NULL, NULL, NULL); + /* Here ofono has missed stk menu proactive command + * that comes after sim initialization only. Doing a + * sim reset will enable the stk driver to get the + * missed +CUSATP notifications. + */ + g_at_chat_send(sd->chat, "AT+CFUN=27,1", none_prefix, + NULL, NULL, NULL); + } + ofono_stk_register(stk); return FALSE; diff --git a/ofono/drivers/atmodem/vendor.h b/ofono/drivers/atmodem/vendor.h index 10c043159..d839d1e0f 100644 --- a/ofono/drivers/atmodem/vendor.h +++ b/ofono/drivers/atmodem/vendor.h @@ -44,7 +44,7 @@ enum ofono_vendor { OFONO_VENDOR_WAVECOM_Q2XXX, OFONO_VENDOR_ALCATEL, OFONO_VENDOR_QUECTEL, - OFONO_VENDOR_QUECTEL_M95, + OFONO_VENDOR_QUECTEL_SERIAL, OFONO_VENDOR_UBLOX, OFONO_VENDOR_XMM, OFONO_VENDOR_GEMALTO, diff --git a/ofono/drivers/hsomodem/gprs-context.c b/ofono/drivers/hsomodem/gprs-context.c index 340092fae..ca622480c 100644 --- a/ofono/drivers/hsomodem/gprs-context.c +++ b/ofono/drivers/hsomodem/gprs-context.c @@ -150,7 +150,6 @@ static void hso_gprs_activate_primary(struct ofono_gprs_context *gc, struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; - int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) @@ -173,11 +172,8 @@ static void hso_gprs_activate_primary(struct ofono_gprs_context *gc, NULL, NULL, NULL) == 0) goto error; - len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); - - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", - ctx->apn); + snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\",\"%s\"", + ctx->cid, ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, hso_cgdcont_cb, cbd, g_free) > 0) diff --git a/ofono/drivers/huaweimodem/gprs-context.c b/ofono/drivers/huaweimodem/gprs-context.c index cae401c85..7e72ad922 100644 --- a/ofono/drivers/huaweimodem/gprs-context.c +++ b/ofono/drivers/huaweimodem/gprs-context.c @@ -256,7 +256,6 @@ static void huawei_gprs_activate_primary(struct ofono_gprs_context *gc, struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[64]; - int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) @@ -265,14 +264,10 @@ static void huawei_gprs_activate_primary(struct ofono_gprs_context *gc, DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; - cbd->user = gc; - len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); - - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", - ctx->apn); + snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\",\"%s\"", + ctx->cid, ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) diff --git a/ofono/drivers/iceramodem/gprs-context.c b/ofono/drivers/iceramodem/gprs-context.c index 395a9dc56..41d9d9bf5 100644 --- a/ofono/drivers/iceramodem/gprs-context.c +++ b/ofono/drivers/iceramodem/gprs-context.c @@ -332,9 +332,7 @@ static void icera_gprs_activate_primary(struct ofono_gprs_context *gc, break; } - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"%s\"", ctx->apn); + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) diff --git a/ofono/drivers/ifxmodem/gprs-context.c b/ofono/drivers/ifxmodem/gprs-context.c index 1dc3d452e..e17beae49 100644 --- a/ofono/drivers/ifxmodem/gprs-context.c +++ b/ofono/drivers/ifxmodem/gprs-context.c @@ -44,6 +44,7 @@ #define TUN_DEV "/dev/net/tun" #define STATIC_IP_NETMASK "255.255.255.255" +#define IPV6_DEFAULT_PREFIX_LEN 8 static const char *none_prefix[] = { NULL }; static const char *xdns_prefix[] = { "+XDNS:", NULL }; @@ -352,10 +353,41 @@ static void cgcontrdp_cb(gboolean ok, GAtResult *result, gpointer user_data) DBG("DNS: %s, %s\n", gcd->dns1, gcd->dns2); - if (!laddrnetmask || at_util_get_ipv4_address_and_netmask(laddrnetmask, + if (gcd->proto == OFONO_GPRS_PROTO_IP) { + if (!laddrnetmask || + at_util_get_ipv4_address_and_netmask(laddrnetmask, gcd->address, gcd->netmask) < 0) { - failed_setup(gc, NULL, TRUE); - return; + failed_setup(gc, NULL, TRUE); + return; + } + + ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE); + + if (gcd->netmask[0]) + ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask); + + if (gcd->gateway[0]) + ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway); + + ofono_gprs_context_set_ipv4_dns_servers(gc, dns); + } + + if (gcd->proto == OFONO_GPRS_PROTO_IPV6) { + if (!laddrnetmask || + at_util_get_ipv6_address_and_netmask(laddrnetmask, + gcd->address, gcd->netmask) < 0) { + failed_setup(gc, NULL, TRUE); + return; + } + + ofono_gprs_context_set_ipv6_address(gc, gcd->address); + + if (gcd->gateway[0]) + ofono_gprs_context_set_ipv6_gateway(gc, gcd->gateway); + + ofono_gprs_context_set_ipv6_dns_servers(gc, dns); + ofono_gprs_context_set_ipv6_prefix_length(gc, + IPV6_DEFAULT_PREFIX_LEN); } if (gw) @@ -373,17 +405,7 @@ static void cgcontrdp_cb(gboolean ok, GAtResult *result, gpointer user_data) interface = ofono_gprs_context_get_interface(gc); datapath = get_datapath(modem, interface); - ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE); - - if (gcd->netmask[0]) - ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask); - - if (gcd->gateway[0]) - ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway); - - ofono_gprs_context_set_ipv4_dns_servers(gc, dns); - - snprintf(buf, sizeof(buf), "AT+XDATACHANNEL=1,1,\"%s\",\"%s\",2,%u", + snprintf(buf, sizeof(buf), "AT+XDATACHANNEL=1,1,\"%s\",\"%s\",0,%u", ctrlpath, datapath, gcd->active_context); g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL); snprintf(buf, sizeof(buf), "AT+CGDATA=\"M-RAW_IP\",%u", @@ -539,9 +561,7 @@ static void ifx_gprs_activate_primary(struct ofono_gprs_context *gc, break; } - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"%s\"", ctx->apn); + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, setup_cb, gc, NULL) > 0) diff --git a/ofono/drivers/isimodem/gprs-context.c b/ofono/drivers/isimodem/gprs-context.c index 5258cb17f..4076ed8b4 100644 --- a/ofono/drivers/isimodem/gprs-context.c +++ b/ofono/drivers/isimodem/gprs-context.c @@ -537,11 +537,15 @@ static void isi_gprs_activate_primary(struct ofono_gprs_context *gc, strncpy(cd->apn, ctx->apn, GPDS_MAX_APN_STRING_LENGTH); cd->apn[GPDS_MAX_APN_STRING_LENGTH] = '\0'; - strncpy(cd->username, ctx->username, GPDS_MAX_USERNAME_LENGTH); - cd->username[GPDS_MAX_USERNAME_LENGTH] = '\0'; - - strncpy(cd->password, ctx->password, GPDS_MAX_PASSWORD_LENGTH); - cd->username[GPDS_MAX_PASSWORD_LENGTH] = '\0'; + if (ctx->auth_method == OFONO_GPRS_AUTH_METHOD_NONE) { + memset(cd->username, 0, sizeof(cd->username)); + memset(cd->password, 0, sizeof(cd->password)); + } else { + strncpy(cd->username, ctx->username, GPDS_MAX_USERNAME_LENGTH); + cd->username[GPDS_MAX_USERNAME_LENGTH] = '\0'; + strncpy(cd->password, ctx->password, GPDS_MAX_PASSWORD_LENGTH); + cd->password[GPDS_MAX_PASSWORD_LENGTH] = '\0'; + } cd->pep = g_isi_pep_create(cd->idx, NULL, NULL); if (cd->pep == NULL) diff --git a/ofono/drivers/isimodem/voicecall.c b/ofono/drivers/isimodem/voicecall.c index 9a63f1004..3fb150f62 100644 --- a/ofono/drivers/isimodem/voicecall.c +++ b/ofono/drivers/isimodem/voicecall.c @@ -227,7 +227,7 @@ static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc, call->addr_type = type | 0x80; call->presentation = pres; - strncpy(call->address, addr, sizeof(call->address)); + strncpy(call->address, addr, sizeof(call->address) - 1); g_free(addr); } diff --git a/ofono/drivers/mbimmodem/mbim-message.c b/ofono/drivers/mbimmodem/mbim-message.c index 781aff223..cd392c703 100644 --- a/ofono/drivers/mbimmodem/mbim-message.c +++ b/ofono/drivers/mbimmodem/mbim-message.c @@ -152,8 +152,8 @@ static bool _iter_copy_string(struct mbim_message_iter *iter, uint32_t offset, uint32_t len, char **out) { - uint8_t buf[len]; - uint8_t *dest = buf; + uint16_t buf[len / 2 + 1]; + uint8_t *dest = (uint8_t *) buf; uint32_t remaining = len; uint32_t iov_start = 0; uint32_t i = 0; @@ -195,7 +195,7 @@ static bool _iter_copy_string(struct mbim_message_iter *iter, /* Strings are in UTF16-LE, so convert to UTF16-CPU first if needed */ if (L_CPU_TO_LE16(0x8000) != 0x8000) { - uint16_t *le = (uint16_t *) buf; + uint16_t *le = buf; for (i = 0; i < len / 2; i++) le[i] = __builtin_bswap16(le[i]); diff --git a/ofono/drivers/mbmmodem/gprs-context.c b/ofono/drivers/mbmmodem/gprs-context.c index c48e7260f..f873e2835 100644 --- a/ofono/drivers/mbmmodem/gprs-context.c +++ b/ofono/drivers/mbmmodem/gprs-context.c @@ -367,7 +367,6 @@ static void mbm_gprs_activate_primary(struct ofono_gprs_context *gc, struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; - int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) @@ -376,14 +375,10 @@ static void mbm_gprs_activate_primary(struct ofono_gprs_context *gc, DBG("cid %u", ctx->cid); gcd->active_context = ctx->cid; - cbd->user = gc; - len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); - - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", - ctx->apn); + snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\",\"%s\"", + ctx->cid, ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, mbm_cgdcont_cb, cbd, g_free) == 0) diff --git a/ofono/drivers/qmimodem/devinfo.c b/ofono/drivers/qmimodem/devinfo.c index af976b772..365ff02b4 100644 --- a/ofono/drivers/qmimodem/devinfo.c +++ b/ofono/drivers/qmimodem/devinfo.c @@ -36,6 +36,7 @@ struct devinfo_data { struct qmi_service *dms; + bool device_is_3gpp; }; static void string_cb(struct qmi_result *result, void *user_data) @@ -116,7 +117,12 @@ static void qmi_query_revision(struct ofono_devinfo *devinfo, static void get_ids_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; + struct ofono_devinfo *devinfo = cbd->user; + struct devinfo_data *data = ofono_devinfo_get_data(devinfo); ofono_devinfo_query_cb_t cb = cbd->cb; + char *esn; + char *imei; + char *meid; char *str; DBG(""); @@ -126,20 +132,28 @@ static void get_ids_cb(struct qmi_result *result, void *user_data) return; } - str = qmi_result_get_string(result, QMI_DMS_RESULT_ESN); - /* Telit qmi modems return a "0" string when ESN is not available. */ - if (!str || strcmp(str, "0") == 0) { - qmi_free(str); - str = qmi_result_get_string(result, QMI_DMS_RESULT_IMEI); - if (!str) { - CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); - return; - } - } + esn = qmi_result_get_string(result, QMI_DMS_RESULT_ESN); + imei = qmi_result_get_string(result, QMI_DMS_RESULT_IMEI); + meid = qmi_result_get_string(result, QMI_DMS_RESULT_MEID); - CALLBACK_WITH_SUCCESS(cb, str, cbd->data); + str = NULL; - qmi_free(str); + if (data->device_is_3gpp && imei && strcmp(imei, "0")) + str = imei; + else if (esn && strcmp(esn, "0")) + str = esn; + + if (str == NULL && meid && strcmp(meid, "0")) + str = meid; + + if (str) + CALLBACK_WITH_SUCCESS(cb, str, cbd->data); + else + CALLBACK_WITH_FAILURE(cb, NULL, cbd->data); + + qmi_free(esn); + qmi_free(imei); + qmi_free(meid); } static void qmi_query_serial(struct ofono_devinfo *devinfo, @@ -150,6 +164,8 @@ static void qmi_query_serial(struct ofono_devinfo *devinfo, DBG(""); + cbd->user = devinfo; + if (qmi_service_send(data->dms, QMI_DMS_GET_IDS, NULL, get_ids_cb, cbd, g_free) > 0) return; @@ -159,6 +175,51 @@ static void qmi_query_serial(struct ofono_devinfo *devinfo, g_free(cbd); } +static void get_caps_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_devinfo *devinfo = user_data; + struct devinfo_data *data = ofono_devinfo_get_data(devinfo); + const struct qmi_dms_device_caps *caps; + uint8_t i; + + DBG(""); + + if (qmi_result_set_error(result, NULL)) + goto error; + + caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, NULL); + if (caps == NULL) + goto error; + + data->device_is_3gpp = false; + + for (i = 0; i < caps->radio_if_count; i++) { + switch (caps->radio_if[i]) { + case QMI_DMS_RADIO_IF_GSM: + case QMI_DMS_RADIO_IF_UMTS: + case QMI_DMS_RADIO_IF_LTE: + data->device_is_3gpp = true; + break; + } + } + +error: + ofono_devinfo_register(devinfo); +} + +static void qmi_query_caps(struct ofono_devinfo *devinfo) +{ + struct devinfo_data *data = ofono_devinfo_get_data(devinfo); + + DBG(""); + + if (qmi_service_send(data->dms, QMI_DMS_GET_CAPS, NULL, + get_caps_cb, devinfo, NULL) > 0) + return; + + ofono_devinfo_register(devinfo); +} + static void create_dms_cb(struct qmi_service *service, void *user_data) { struct ofono_devinfo *devinfo = user_data; @@ -173,8 +234,9 @@ static void create_dms_cb(struct qmi_service *service, void *user_data) } data->dms = qmi_service_ref(service); + data->device_is_3gpp = false; - ofono_devinfo_register(devinfo); + qmi_query_caps(devinfo); } static int qmi_devinfo_probe(struct ofono_devinfo *devinfo, diff --git a/ofono/drivers/qmimodem/netmon.c b/ofono/drivers/qmimodem/netmon.c index 14a55632e..729879ce7 100644 --- a/ofono/drivers/qmimodem/netmon.c +++ b/ofono/drivers/qmimodem/netmon.c @@ -89,8 +89,8 @@ static void get_rssi_cb(struct qmi_result *result, void *user_data) /* RSSI */ rssi = qmi_result_get(result, 0x11, &len); - num = GUINT16_FROM_LE(rssi->count); if (rssi) { + num = GUINT16_FROM_LE(rssi->count); for (i = 0; i < num; i++) { DBG("RSSI: %hhu on RAT %hhd", rssi->info[i].rssi, @@ -126,8 +126,8 @@ static void get_rssi_cb(struct qmi_result *result, void *user_data) /* Bit error rate */ ber = qmi_result_get(result, 0x15, &len); - num = GUINT16_FROM_LE(ber->count); if (ber) { + num = GUINT16_FROM_LE(ber->count); for (i = 0; i < ber->count; i++) { DBG("Bit error rate: %hu on RAT %hhd", GUINT16_FROM_LE(ber->info[i].rate), diff --git a/ofono/drivers/qmimodem/network-registration.c b/ofono/drivers/qmimodem/network-registration.c index 1fccb5737..04f20c669 100644 --- a/ofono/drivers/qmimodem/network-registration.c +++ b/ofono/drivers/qmimodem/network-registration.c @@ -42,6 +42,8 @@ struct netreg_data { struct qmi_service *nas; struct ofono_network_operator operator; uint8_t current_rat; + int lac; + int cellid; bool is_roaming; }; @@ -166,6 +168,31 @@ static bool extract_ss_info(struct qmi_result *result, int *status, return true; } +static int remember_ss_info(struct netreg_data *data, int status, int lac, + int cellid, enum roaming_status roaming) +{ + if (roaming == ROAMING_STATUS_ON) + data->is_roaming = true; + else if (roaming == ROAMING_STATUS_OFF) + data->is_roaming = false; + + if (status == QMI_NAS_REGISTRATION_STATE_REGISTERED) { + if (lac >= 0) + data->lac = lac; + if (cellid >= 0) + data->cellid = cellid; + } else { + data->lac = -1; + data->cellid = -1; + } + + if (status == QMI_NAS_REGISTRATION_STATE_REGISTERED && + data->is_roaming) + status = NETWORK_REGISTRATION_STATUS_ROAMING; + + return status; +} + static void ss_info_notify(struct qmi_result *result, void *user_data) { struct ofono_netreg *netreg = user_data; @@ -183,16 +210,10 @@ static void ss_info_notify(struct qmi_result *result, void *user_data) &data->operator)) return; - if (roaming == ROAMING_STATUS_ON) - data->is_roaming = true; - else if (roaming == ROAMING_STATUS_OFF) - data->is_roaming = false; - - if (status == QMI_NAS_REGISTRATION_STATE_REGISTERED && - data->is_roaming) - status = NETWORK_REGISTRATION_STATUS_ROAMING; + status = remember_ss_info(data, status, lac, cellid, roaming); - ofono_netreg_status_notify(netreg, status, lac, cellid, tech); + ofono_netreg_status_notify(netreg, status, data->lac, data->cellid, + tech); } static void get_ss_info_cb(struct qmi_result *result, void *user_data) @@ -216,16 +237,10 @@ static void get_ss_info_cb(struct qmi_result *result, void *user_data) return; } - if (roaming == ROAMING_STATUS_ON) - data->is_roaming = true; - else if (roaming == ROAMING_STATUS_OFF) - data->is_roaming = false; - - if (status == QMI_NAS_REGISTRATION_STATE_REGISTERED && - data->is_roaming) - status = NETWORK_REGISTRATION_STATUS_ROAMING; + status = remember_ss_info(data, status, lac, cellid, roaming); - CALLBACK_WITH_SUCCESS(cb, status, lac, cellid, tech, cbd->data); + CALLBACK_WITH_SUCCESS(cb, status, data->lac, data->cellid, tech, + cbd->data); } static void qmi_registration_status(struct ofono_netreg *netreg, @@ -613,6 +628,8 @@ static int qmi_netreg_probe(struct ofono_netreg *netreg, data->current_rat = QMI_NAS_NETWORK_RAT_NO_CHANGE; data->is_roaming = false; + data->lac = -1; + data->cellid = -1; ofono_netreg_set_data(netreg, data); diff --git a/ofono/drivers/qmimodem/qmi.c b/ofono/drivers/qmimodem/qmi.c index 19ec13038..4cd1530f4 100644 --- a/ofono/drivers/qmimodem/qmi.c +++ b/ofono/drivers/qmimodem/qmi.c @@ -477,7 +477,8 @@ static const char *__error_to_string(uint16_t error) return NULL; } -int qmi_error_to_ofono_cme(int qmi_error) { +int qmi_error_to_ofono_cme(int qmi_error) +{ switch (qmi_error) { case 0x0019: return 4; /* Not Supported */ @@ -1209,10 +1210,10 @@ static void discover_callback(uint16_t message, uint16_t length, if (name) __debug_device(device, "found service [%s %d.%d]", - name, major, minor); + name, major, minor); else __debug_device(device, "found service [%d %d.%d]", - type, major, minor); + type, major, minor); if (type == QMI_SERVICE_CONTROL) { device->control_major = major; diff --git a/ofono/drivers/qmimodem/sim-legacy.c b/ofono/drivers/qmimodem/sim-legacy.c index 30eb46134..601e721d1 100644 --- a/ofono/drivers/qmimodem/sim-legacy.c +++ b/ofono/drivers/qmimodem/sim-legacy.c @@ -83,13 +83,13 @@ static void get_iccid_cb(struct qmi_result *result, void *user_data) len = strlen(str); if (len > 20) { + qmi_free(str); CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data); return; } sim_encode_bcd_number(str, iccid); iccid_len = len / 2; - qmi_free(str); CALLBACK_WITH_SUCCESS(cb, iccid, iccid_len, cbd->data); diff --git a/ofono/drivers/qmimodem/sms.c b/ofono/drivers/qmimodem/sms.c index 1e9303966..2e9624297 100644 --- a/ofono/drivers/qmimodem/sms.c +++ b/ofono/drivers/qmimodem/sms.c @@ -39,8 +39,17 @@ struct sms_data { struct qmi_service *wms; uint16_t major; uint16_t minor; + struct qmi_wms_read_msg_id rd_msg_id; + struct qmi_wms_result_msg_list *msg_list; + uint32_t rd_msg_num; + uint8_t msg_mode; + bool msg_mode_all; + bool msg_list_chk; }; +static void get_msg_list(struct ofono_sms *sms); +static void raw_read(struct ofono_sms *sms, uint8_t type, uint32_t ndx); + static void get_smsc_addr_cb(struct qmi_result *result, void *user_data) { struct cb_data *cbd = user_data; @@ -334,21 +343,95 @@ static void qmi_bearer_set(struct ofono_sms *sms, int bearer, g_free(cbd); } +static void delete_msg_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_sms *sms = user_data; + struct sms_data *data = ofono_sms_get_data(sms); + uint16_t err; + + DBG(""); + + if (qmi_result_set_error(result, &err)) + DBG("Err: delete %d - %s", err, qmi_result_get_error(result)); + + /* + * Continue processing msg list. If error occurred, something + * serious happened, then don't bother. + */ + if (data->msg_list && data->msg_list_chk) { + uint32_t msg = ++data->rd_msg_num; + + /* + * Get another msg. If list is empty check for more. Once query + * returns empty, rely on event indication to get new msgs. + */ + if (msg < data->msg_list->cnt) + raw_read(sms, data->msg_list->msg[msg].type, + GUINT32_FROM_LE(data->msg_list->msg[msg].ndx)); + else + get_msg_list(sms); + } +} + +static void delete_msg(struct ofono_sms *sms, uint8_t tag) +{ + struct sms_data *data = ofono_sms_get_data(sms); + struct qmi_param *param; + qmi_result_func_t func = NULL; + + DBG(""); + + param = qmi_param_new(); + if (param == NULL) + goto done; + + qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_STORE, + QMI_WMS_STORAGE_TYPE_NV); + + if (tag == QMI_WMS_MT_UNDEFINE) { + DBG("delete read msg type %d ndx %d", data->rd_msg_id.type, + data->rd_msg_id.ndx); + + /* delete 1 msg */ + qmi_param_append_uint32(param, QMI_WMS_PARAM_DEL_NDX, + data->rd_msg_id.ndx); + func = delete_msg_cb; + } else { + DBG("delete msg tag %d mode %d", tag, data->msg_mode); + + /* delete all msgs from 1 tag type */ + qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_TYPE, tag); + } + + qmi_param_append_uint8(param, QMI_WMS_PARAM_DEL_MODE, data->msg_mode); + + if (qmi_service_send(data->wms, QMI_WMS_DELETE, param, + func, sms, NULL) > 0) + return; + + qmi_param_free(param); + +done: + data->msg_list_chk = false; +} + static void raw_read_cb(struct qmi_result *result, void *user_data) { struct ofono_sms *sms = user_data; - const struct qmi_wms_raw_message* msg; - uint16_t len; - uint16_t error; + struct sms_data *data = ofono_sms_get_data(sms); + const struct qmi_wms_raw_message *msg; + uint16_t err; - if (qmi_result_set_error(result, &error)) { - DBG("Raw read error: %d (%s)", error, - qmi_result_get_error(result)); + DBG(""); + + if (qmi_result_set_error(result, &err)) { + DBG("Err: read %d - %s", err, qmi_result_get_error(result)); + data->msg_list_chk = false; return; } /* Raw message data */ - msg = qmi_result_get(result, 0x01, &len); + msg = qmi_result_get(result, QMI_WMS_RESULT_READ_MSG, NULL); if (msg) { uint16_t plen; uint16_t tpdu_len; @@ -357,9 +440,174 @@ static void raw_read_cb(struct qmi_result *result, void *user_data) tpdu_len = plen - msg->msg_data[0] - 1; ofono_sms_deliver_notify(sms, msg->msg_data, plen, tpdu_len); + } else + DBG("Err: no data in type %d ndx %d", data->rd_msg_id.type, + data->rd_msg_id.ndx); + + /* delete read msg */ + delete_msg(sms, QMI_WMS_MT_UNDEFINE); +} + +static void raw_read(struct ofono_sms *sms, uint8_t type, uint32_t ndx) +{ + struct sms_data *data = ofono_sms_get_data(sms); + struct qmi_param *param; + + DBG(""); + + param = qmi_param_new(); + if (param == NULL) + goto done; + + data->rd_msg_id.type = type; + data->rd_msg_id.ndx = ndx; + + DBG("read type %d ndx %d", data->rd_msg_id.type, data->rd_msg_id.ndx); + + qmi_param_append(param, QMI_WMS_PARAM_READ_MSG, + sizeof(data->rd_msg_id), &data->rd_msg_id); + qmi_param_append_uint8(param, QMI_WMS_PARAM_READ_MODE, data->msg_mode); + + if (qmi_service_send(data->wms, QMI_WMS_RAW_READ, param, + raw_read_cb, sms, NULL) > 0) + return; + + qmi_param_free(param); + +done: + data->msg_list_chk = false; +} + +static void get_msg_list_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_sms *sms = user_data; + struct sms_data *data = ofono_sms_get_data(sms); + const struct qmi_wms_result_msg_list *list; + uint32_t cnt = 0; + uint16_t tmp; + + DBG(""); + + if (qmi_result_set_error(result, &tmp)) { + DBG("Err: get msg list mode=%d %d=%s", data->msg_mode, tmp, + qmi_result_get_error(result)); + goto done; + } + + list = qmi_result_get(result, QMI_WMS_RESULT_MSG_LIST, NULL); + if (list == NULL) { + DBG("Err: get msg list empty"); + goto done; + } + + cnt = GUINT32_FROM_LE(list->cnt); + DBG("msgs found %d", cnt); + + for (tmp = 0; tmp < cnt; tmp++) { + DBG("unread type %d ndx %d", list->msg[tmp].type, + GUINT32_FROM_LE(list->msg[tmp].ndx)); + } + + /* free list from last time */ + if (data->msg_list) { + g_free(data->msg_list); + data->msg_list = NULL; + } + + /* save list and get 1st msg */ + if (cnt) { + int msg_size = cnt * sizeof(list->msg[0]); + + data->msg_list = g_try_malloc0(sizeof(list->cnt) + msg_size); + if (data->msg_list == NULL) + goto done; + + data->msg_list->cnt = cnt; + memcpy(data->msg_list->msg, list->msg, msg_size); + + data->rd_msg_num = 0; + raw_read(sms, data->msg_list->msg[0].type, + GUINT32_FROM_LE(data->msg_list->msg[0].ndx)); + return; + } + +done: + data->msg_list_chk = false; + + /* if both protocols supported, check the other */ + if (data->msg_mode_all) { + data->msg_mode_all = false; + data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA; + get_msg_list(sms); + } +} + +static void get_msg_list(struct ofono_sms *sms) +{ + struct sms_data *data = ofono_sms_get_data(sms); + struct qmi_param *param; + + DBG(""); + + param = qmi_param_new(); + if (param == NULL) + return; + + data->msg_list_chk = true; + + /* query NOT_READ msg list */ + qmi_param_append_uint8(param, QMI_WMS_PARAM_STORAGE_TYPE, + QMI_WMS_STORAGE_TYPE_NV); + qmi_param_append_uint8(param, QMI_WMS_PARAM_TAG_TYPE, + QMI_WMS_MT_NOT_READ); + qmi_param_append_uint8(param, QMI_WMS_PARAM_MESSAGE_MODE, + data->msg_mode); + + if (qmi_service_send(data->wms, QMI_WMS_GET_MSG_LIST, param, + get_msg_list_cb, sms, NULL) > 0) + return; + + data->msg_list_chk = false; + qmi_param_free(param); +} + +static void get_msg_protocol_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_sms *sms = user_data; + struct sms_data *data = ofono_sms_get_data(sms); + uint16_t err; + + DBG(""); + + if (qmi_result_set_error(result, &err) && + (err != QMI_ERR_OP_DEVICE_UNSUPPORTED)) { + DBG("Err: protocol %d - %s", err, qmi_result_get_error(result)); + return; + } + + if (err != QMI_ERR_OP_DEVICE_UNSUPPORTED) { + /* modem supports only 1 protocol */ + qmi_result_get_uint8(result, QMI_WMS_PARAM_PROTOCOL, + &data->msg_mode); } else { - DBG("No message data available at requested position"); + /* check both, start with 1 then switch to other */ + DBG("device supports CDMA and WCDMA msg protocol"); + data->msg_mode_all = true; + data->msg_mode = QMI_WMS_MESSAGE_MODE_CDMA; } + + /* check for messages */ + get_msg_list(sms); +} + +static void get_msg_protocol(struct ofono_sms *sms) +{ + struct sms_data *data = ofono_sms_get_data(sms); + + DBG(""); + + qmi_service_send(data->wms, QMI_WMS_GET_MSG_PROTOCOL, NULL, + get_msg_protocol_cb, sms, NULL); } static void event_notify(struct qmi_result *result, void *user_data) @@ -367,66 +615,78 @@ static void event_notify(struct qmi_result *result, void *user_data) struct ofono_sms *sms = user_data; struct sms_data *data = ofono_sms_get_data(sms); const struct qmi_wms_result_new_msg_notify *notify; - const struct qmi_wms_result_message *message; - uint16_t len; DBG(""); - notify = qmi_result_get(result, QMI_WMS_RESULT_NEW_MSG_NOTIFY, &len); + /* + * The 2 types of MT message TLVs are mutually exclusive, depending on + * how the route action is configured. If action is store and notify, + * then the MT message TLV is sent. If action is transfer only or + * transfer and ack, then the transfer route MT message TLV is sent. + */ + notify = qmi_result_get(result, QMI_WMS_RESULT_NEW_MSG_NOTIFY, NULL); if (notify) { - DBG("storage type %d index %d", notify->storage_type, - GUINT32_FROM_LE(notify->storage_index)); - } + /* route is store and notify */ + if (!qmi_result_get_uint8(result, QMI_WMS_RESULT_MSG_MODE, + &data->msg_mode)) + DBG("msg mode not found, use mode %d", data->msg_mode); + + DBG("msg type %d ndx %d mode %d", notify->storage_type, + GUINT32_FROM_LE(notify->storage_index), data->msg_mode); + + /* don't read if list is being processed, get this msg later */ + if (!data->msg_list_chk) + raw_read(sms, notify->storage_type, + GUINT32_FROM_LE(notify->storage_index)); + } else { + /* route is either transfer only or transfer and ACK */ + const struct qmi_wms_result_message *message; - message = qmi_result_get(result, QMI_WMS_RESULT_MESSAGE, &len); - if (message) { - uint16_t plen; + message = qmi_result_get(result, QMI_WMS_RESULT_MESSAGE, NULL); + if (message) { + uint16_t plen; - plen = GUINT16_FROM_LE(message->msg_length); + plen = GUINT16_FROM_LE(message->msg_length); - DBG("ack_required %d transaction id %u", message->ack_required, + DBG("ack_required %d transaction id %u", + message->ack_required, GUINT32_FROM_LE(message->transaction_id)); - DBG("msg format %d PDU length %d", message->msg_format, plen); + DBG("msg format %d PDU length %d", + message->msg_format, plen); - ofono_sms_deliver_notify(sms, message->msg_data, plen, plen); - } else { - /* The Quectel EC21, at least, does not provide the - * message data in the event notification, so a 'raw read' - * needs to be issued in order to query the message itself - */ - struct qmi_param *param; - - param = qmi_param_new(); - if (!param) - return; - - /* Message memory storage ID */ - qmi_param_append(param, 0x01, sizeof(*notify), notify); - /* The 'message mode' parameter is documented as optional, - * but the Quectel EC21 errors out with error 17 (missing - * argument) if it is not provided... we default to 3GPP - * here because that's what works for me and it's not clear - * how to actually query what this should be otherwise... - */ - /* Message mode */ - qmi_param_append_uint8(param, 0x10, - QMI_WMS_MESSAGE_MODE_GSMWCDMA); - - if (qmi_service_send(data->wms, QMI_WMS_RAW_READ, param, - raw_read_cb, sms, NULL) > 0) - return; - - qmi_param_free(param); + ofono_sms_deliver_notify(sms, message->msg_data, + plen, plen); + } } } static void set_routes_cb(struct qmi_result *result, void *user_data) { struct ofono_sms *sms = user_data; + struct sms_data *data = ofono_sms_get_data(sms); DBG(""); ofono_sms_register(sms); + + /* + * Modem storage is limited. As a fail safe, delete processed messages + * to free device memory to prevent blockage of new messages. + */ + data->msg_mode = QMI_WMS_MESSAGE_MODE_CDMA; + delete_msg(sms, QMI_WMS_MT_READ); + delete_msg(sms, QMI_WMS_MO_SENT); + data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA; + delete_msg(sms, QMI_WMS_MT_READ); + delete_msg(sms, QMI_WMS_MO_SENT); + + /* + * Subsystem initialized, now start process to check for unread + * messages. First, query msg protocol/mode. If modem supports both + * modes, then check messages for both modes since there's no way to + * query which mode is active. + */ + get_msg_protocol(sms); } static void get_routes_cb(struct qmi_result *result, void *user_data) @@ -468,8 +728,8 @@ static void get_routes_cb(struct qmi_result *result, void *user_data) new_list->count = GUINT16_TO_LE(1); new_list->route[0].msg_type = QMI_WMS_MSG_TYPE_P2P; new_list->route[0].msg_class = QMI_WMS_MSG_CLASS_NONE; - new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NONE; - new_list->route[0].action = QMI_WMS_ACTION_TRANSFER_AND_ACK; + new_list->route[0].storage_type = QMI_WMS_STORAGE_TYPE_NV; + new_list->route[0].action = QMI_WMS_ACTION_STORE_AND_NOTIFY; param = qmi_param_new(); if (!param) @@ -524,6 +784,9 @@ static void create_wms_cb(struct qmi_service *service, void *user_data) data->wms = qmi_service_ref(service); + memset(&data->rd_msg_id, 0, sizeof(data->rd_msg_id)); + data->msg_mode = QMI_WMS_MESSAGE_MODE_GSMWCDMA; + qmi_service_register(data->wms, QMI_WMS_EVENT, event_notify, sms, NULL); @@ -568,6 +831,9 @@ static void qmi_sms_remove(struct ofono_sms *sms) qmi_service_unref(data->wms); + if (data->msg_list) + g_free(data->msg_list); + g_free(data); } diff --git a/ofono/drivers/qmimodem/wms.h b/ofono/drivers/qmimodem/wms.h index 7e18ec9d4..f53fc1bdf 100644 --- a/ofono/drivers/qmimodem/wms.h +++ b/ofono/drivers/qmimodem/wms.h @@ -25,8 +25,9 @@ #define QMI_WMS_RAW_SEND 32 /* Send a raw message */ -#define QMI_WMS_RAW_READ 34 /* Read raw message from storage*/ - +#define QMI_WMS_RAW_READ 34 /* Read raw message from storage */ +#define QMI_WMS_DELETE 36 /* Delete message */ +#define QMI_WMS_GET_MSG_PROTOCOL 48 /* Get message protocol */ #define QMI_WMS_GET_MSG_LIST 49 /* Get list of messages from the device */ #define QMI_WMS_SET_ROUTES 50 /* Set routes for message memory storage */ #define QMI_WMS_GET_ROUTES 51 /* Get routes for message memory storage */ @@ -45,6 +46,17 @@ struct qmi_wms_result_new_msg_notify { uint32_t storage_index; } __attribute__((__packed__)); +#define QMI_WMS_RESULT_MESSAGE 0x11 +struct qmi_wms_result_message { + uint8_t ack_required; /* bool */ + uint32_t transaction_id; + uint8_t msg_format; + uint16_t msg_length; + uint8_t msg_data[0]; +} __attribute__((__packed__)); + +#define QMI_WMS_RESULT_MSG_MODE 0x12 + /* Set new message conditions */ #define QMI_WMS_PARAM_NEW_MSG_REPORT 0x10 /* bool */ @@ -57,23 +69,59 @@ struct qmi_wms_param_message { } __attribute__((__packed__)); #define QMI_WMS_RESULT_MESSAGE_ID 0x01 /* uint16 */ +/* Read a raw message */ +#define QMI_WMS_PARAM_READ_MSG 0x01 +struct qmi_wms_read_msg_id { + uint8_t type; + uint32_t ndx; +} __attribute__((__packed__)); + +#define QMI_WMS_PARAM_READ_MODE 0x10 + +#define QMI_WMS_RESULT_READ_MSG 0x01 +struct qmi_wms_raw_message { + uint8_t msg_tag; + uint8_t msg_format; + uint16_t msg_length; + uint8_t msg_data[0]; +} __attribute__((__packed__)); + +/* Delete messages */ +#define QMI_WMS_PARAM_DEL_STORE 0x01 +#define QMI_WMS_PARAM_DEL_NDX 0x10 +#define QMI_WMS_PARAM_DEL_TYPE 0x11 +#define QMI_WMS_PARAM_DEL_MODE 0x12 + +/* Get message protocol */ +#define QMI_WMS_PARAM_PROTOCOL 0x01 + /* Get list of messages from the device */ #define QMI_WMS_PARAM_STORAGE_TYPE 0x01 /* uint8 */ +#define QMI_WMS_PARAM_TAG_TYPE 0x10 #define QMI_WMS_PARAM_MESSAGE_MODE 0x11 /* uint8 */ +#define QMI_WMS_RESULT_MSG_LIST 0x01 +struct qmi_wms_result_msg_list { + uint32_t cnt; + struct { + uint32_t ndx; + uint8_t type; + } __attribute__((__packed__)) msg[0]; +} __attribute__((__packed__)); + #define QMI_WMS_STORAGE_TYPE_UIM 0 #define QMI_WMS_STORAGE_TYPE_NV 1 #define QMI_WMS_STORAGE_TYPE_UNKNOWN 2 #define QMI_WMS_STORAGE_TYPE_NONE 255 -#define QMI_WMS_MESSAGE_MODE_GSMWCDMA 1 +#define QMI_WMS_MT_READ 0x00 +#define QMI_WMS_MT_NOT_READ 0x01 +#define QMI_WMS_MO_SENT 0x02 +#define QMI_WMS_MO_NOT_SENT 0x03 +#define QMI_WMS_MT_UNDEFINE 0xff -struct qmi_wms_raw_message { - uint8_t msg_tag; - uint8_t msg_format; - uint16_t msg_length; - uint8_t msg_data[0]; -} __attribute__((__packed__)); +#define QMI_WMS_MESSAGE_MODE_CDMA 0x00 +#define QMI_WMS_MESSAGE_MODE_GSMWCDMA 0x01 /* Get routes for message memory storage */ #define QMI_WMS_RESULT_ROUTE_LIST 0x01 @@ -89,14 +137,6 @@ struct qmi_wms_route_list { } __attribute__((__packed__)); #define QMI_WMS_RESULT_STATUS_REPORT 0x10 /* bool */ #define QMI_WMS_PARAM_STATUS_REPORT 0x10 /* bool */ -#define QMI_WMS_RESULT_MESSAGE 0x11 -struct qmi_wms_result_message { - uint8_t ack_required; /* bool */ - uint32_t transaction_id; - uint8_t msg_format; - uint16_t msg_length; - uint8_t msg_data[0]; -} __attribute__((__packed__)); #define QMI_WMS_MSG_TYPE_P2P 0x00 #define QMI_WMS_MSG_TYPE_BROADCAST 0x01 @@ -134,3 +174,6 @@ struct qmi_wms_result_smsc_addr { #define QMI_WMS_DOMAIN_PS_PREFERRED 0x01 #define QMI_WMS_DOMAIN_CS_ONLY 0x02 #define QMI_WMS_DOMAIN_PS_ONLY 0x03 + +/* Error code */ +#define QMI_ERR_OP_DEVICE_UNSUPPORTED 0x19 diff --git a/ofono/drivers/stemodem/caif_rtnl.c b/ofono/drivers/stemodem/caif_rtnl.c index 1a42c144c..584c5a4a7 100644 --- a/ofono/drivers/stemodem/caif_rtnl.c +++ b/ofono/drivers/stemodem/caif_rtnl.c @@ -85,7 +85,7 @@ static void parse_newlink_param(struct ifinfomsg *msg, int size, if (attr->rta_type == IFLA_IFNAME && ifname != NULL) { - strncpy(ifname, RTA_DATA(attr), IF_NAMESIZE); + strncpy(ifname, RTA_DATA(attr), IF_NAMESIZE - 1); ifname[IF_NAMESIZE-1] = '\0'; break; } diff --git a/ofono/drivers/stemodem/gprs-context.c b/ofono/drivers/stemodem/gprs-context.c index 56b34388e..fb279c5a6 100644 --- a/ofono/drivers/stemodem/gprs-context.c +++ b/ofono/drivers/stemodem/gprs-context.c @@ -277,7 +277,6 @@ static void ste_gprs_activate_primary(struct ofono_gprs_context *gc, struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); struct cb_data *cbd = cb_data_new(cb, data); char buf[AUTH_BUF_LENGTH]; - int len; /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) @@ -291,11 +290,8 @@ static void ste_gprs_activate_primary(struct ofono_gprs_context *gc, goto error; } - len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid); - - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len, ",\"%s\"", - ctx->apn); + snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\",\"%s\"", + ctx->cid, ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, ste_cgdcont_cb, cbd, g_free) == 0) diff --git a/ofono/drivers/swmodem/gprs-context.c b/ofono/drivers/swmodem/gprs-context.c index 5ac9a9754..002053cf9 100644 --- a/ofono/drivers/swmodem/gprs-context.c +++ b/ofono/drivers/swmodem/gprs-context.c @@ -177,9 +177,7 @@ static void sw_gprs_activate_primary(struct ofono_gprs_context *gc, break; } - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"%s\"", ctx->apn); + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, at_cgdcont_cb, cbd, g_free) > 0) diff --git a/ofono/drivers/telitmodem/gprs-context-ncm.c b/ofono/drivers/telitmodem/gprs-context-ncm.c index 244a3dc25..e2eece77f 100644 --- a/ofono/drivers/telitmodem/gprs-context-ncm.c +++ b/ofono/drivers/telitmodem/gprs-context-ncm.c @@ -346,9 +346,7 @@ static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc, break; } - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, - ",\"%s\"", ctx->apn); + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); if (g_at_chat_send(gcd->chat, buf, none_prefix, setup_cb, gc, NULL) > 0) diff --git a/ofono/drivers/ubloxmodem/gprs-context.c b/ofono/drivers/ubloxmodem/gprs-context.c index 3b48062a7..5bc449195 100644 --- a/ofono/drivers/ubloxmodem/gprs-context.c +++ b/ofono/drivers/ubloxmodem/gprs-context.c @@ -321,7 +321,7 @@ static void ublox_send_uauthreq(struct ofono_gprs_context *gc, { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); char buf[UBLOX_MAX_USER_LEN + UBLOX_MAX_PASS_LEN + 32]; - unsigned auth; + unsigned auth = 0; switch (auth_method) { case OFONO_GPRS_AUTH_METHOD_PAP: @@ -388,6 +388,14 @@ static void ublox_gprs_activate_primary(struct ofono_gprs_context *gc, { struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc); + if (ublox_is_toby_l4(gcd->model)) { + /* TOBY L4 does not support IPv6 */ + if (ctx->proto != OFONO_GPRS_PROTO_IP) { + CALLBACK_WITH_FAILURE(cb, data); + return; + } + } + /* IPv6 support not implemented */ if (ctx->proto != OFONO_GPRS_PROTO_IP) { CALLBACK_WITH_FAILURE(cb, data); diff --git a/ofono/drivers/ubloxmodem/network-registration.c b/ofono/drivers/ubloxmodem/network-registration.c new file mode 100644 index 000000000..64ef8076b --- /dev/null +++ b/ofono/drivers/ubloxmodem/network-registration.c @@ -0,0 +1,427 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2010 ST-Ericsson AB. + * Copyright (C) 2019 Norrbonn AB + * + * 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 + +#include +#include +#include + +#include + +#include +#include +#include + +#include "gatchat.h" +#include "gatresult.h" + +#include "common.h" +#include "ubloxmodem.h" +#include "drivers/atmodem/vendor.h" + +#include "drivers/atmodem/network-registration.h" + +static const char *none_prefix[] = { NULL }; +static const char *cmer_prefix[] = { "+CMER:", NULL }; +static const char *ureg_prefix[] = { "+UREG:", NULL }; + +struct netreg_data { + struct at_netreg_data at_data; + + const struct ublox_model *model; +}; + +struct tech_query { + int status; + int lac; + int ci; + struct ofono_netreg *netreg; +}; + +static void ciev_notify(GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + int strength, ind; + GAtResultIter iter; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CIEV:")) + return; + + if (!g_at_result_iter_next_number(&iter, &ind)) + return; + + if (ind != nd->signal_index) + return; + + if (!g_at_result_iter_next_number(&iter, &strength)) + return; + + if (strength == nd->signal_invalid) + strength = -1; + else + strength = (strength * 100) / (nd->signal_max - nd->signal_min); + + ofono_netreg_strength_notify(netreg, strength); +} + +static gboolean notify_time(gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + + nd->nitz_timeout = 0; + + ofono_netreg_time_notify(netreg, &nd->time); + + return FALSE; +} + +static void ctzdst_notify(GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + int dst; + GAtResultIter iter; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CTZDST:")) + return; + + if (!g_at_result_iter_next_number(&iter, &dst)) + return; + + DBG("dst %d", dst); + + nd->time.dst = dst; + + if (nd->nitz_timeout > 0) { + g_source_remove(nd->nitz_timeout); + nd->nitz_timeout = 0; + } + + ofono_netreg_time_notify(netreg, &nd->time); +} + +static void ctzv_notify(GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + int year, mon, mday, hour, min, sec; + const char *tz, *time; + GAtResultIter iter; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CTZV:")) + return; + + if (!g_at_result_iter_next_unquoted_string(&iter, &tz)) + return; + + if (!g_at_result_iter_next_string(&iter, &time)) + return; + + DBG("tz %s time %s", tz, time); + + if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday, + &hour, &min, &sec) != 6) + return; + + nd->time.sec = sec; + nd->time.min = min; + nd->time.hour = hour; + nd->time.mday = mday; + nd->time.mon = mon; + nd->time.year = 2000 + year; + + nd->time.utcoff = atoi(tz) * 15 * 60; + + /* Delay notification in case there's a DST update coming */ + if (nd->nitz_timeout > 0) + g_source_remove(nd->nitz_timeout); + + nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data); +} + +static void ctze_notify(GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + int year, mon, mday, hour, min, sec; + int dst; + const char *tz, *time; + GAtResultIter iter; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CTZE:")) + return; + + if (!g_at_result_iter_next_unquoted_string(&iter, &tz)) + return; + + if (!g_at_result_iter_next_number(&iter, &dst)) + return; + + if (!g_at_result_iter_next_string(&iter, &time)) + return; + + DBG("tz %s dst %d time %s", tz, dst, time); + + if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday, + &hour, &min, &sec) != 6) + return; + + nd->time.sec = sec; + nd->time.min = min; + nd->time.hour = hour; + nd->time.mday = mday; + nd->time.mon = mon; + nd->time.year = 2000 + year; + + nd->time.utcoff = atoi(tz) * 15 * 60; + nd->time.dst = dst; + + ofono_netreg_time_notify(netreg, &nd->time); +} + +static void ublox_query_tech_cb(gboolean ok, GAtResult *result, + gpointer user_data) +{ + struct tech_query *tq = user_data; + GAtResultIter iter; + gint enabled, state; + int tech = -1; + + if (!ok) + goto error; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+UREG:")) + return; + + if (!g_at_result_iter_next_number(&iter, &enabled)) + return; + + if (!g_at_result_iter_next_number(&iter, &state)) + return; + + switch (state) { + case 4: + tech = 5; + break; + case 5: + tech = 4; + break; + case 8: + tech = 1; + break; + case 9: + tech = 2; + break; + default: + tech = state; + } + +error: + ofono_netreg_status_notify(tq->netreg, + tq->status, tq->lac, tq->ci, tech); +} + +static void creg_notify(GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + int status, lac, ci, tech; + struct netreg_data *nd = ofono_netreg_get_data(netreg); + struct tech_query *tq; + + if (at_util_parse_reg_unsolicited(result, "+CREG:", &status, + &lac, &ci, &tech, OFONO_VENDOR_GENERIC) == FALSE) + return; + + if (status != 1 && status != 5) + goto notify; + + if (ublox_is_toby_l4(nd->model)) { + tq = g_new0(struct tech_query, 1); + + tq->status = status; + tq->lac = lac; + tq->ci = ci; + tq->netreg = netreg; + + if (g_at_chat_send(nd->at_data.chat, "AT+UREG?", ureg_prefix, + ublox_query_tech_cb, tq, g_free) > 0) + return; + + g_free(tq); + } + + if ((status == 1 || status == 5) && tech == -1) + tech = nd->at_data.tech; + +notify: + ofono_netreg_status_notify(netreg, status, lac, ci, tech); +} + +static void at_cmer_not_supported(struct ofono_netreg *netreg) +{ + ofono_error("+CMER not supported by this modem. If this is an error" + " please submit patches to support this hardware"); + + ofono_netreg_remove(netreg); +} + +static void ublox_cmer_set_cb(gboolean ok, + GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct at_netreg_data *nd = ofono_netreg_get_data(netreg); + + if (!ok) { + at_cmer_not_supported(netreg); + return; + } + + g_at_chat_register(nd->chat, "+CIEV:", + ciev_notify, FALSE, netreg, NULL); + + g_at_chat_register(nd->chat, "+CREG:", + creg_notify, FALSE, netreg, NULL); + + ofono_netreg_register(netreg); +} + +static void ublox_creg_set_cb(gboolean ok, + GAtResult *result, gpointer user_data) +{ + struct ofono_netreg *netreg = user_data; + struct netreg_data *nd = ofono_netreg_get_data(netreg); + + if (!ok) { + ofono_error("Unable to initialize Network Registration"); + ofono_netreg_remove(netreg); + return; + } + + if (ublox_is_toby_l4(nd->model)) { + /* FIXME */ + ofono_error("TOBY L4 requires polling of ECSQ"); + ofono_error("TOBY L4 wants UREG notifications for" + " tech updates"); + } + + /* Register for network time update reports */ + if (ublox_is_toby_l2(nd->model)) { + /* TOBY L2 does not support CTZDST */ + g_at_chat_register(nd->at_data.chat, "+CTZE:", ctze_notify, + FALSE, netreg, NULL); + g_at_chat_send(nd->at_data.chat, "AT+CTZR=2", none_prefix, + NULL, NULL, NULL); + } else { + g_at_chat_register(nd->at_data.chat, "+CTZV:", ctzv_notify, + FALSE, netreg, NULL); + g_at_chat_register(nd->at_data.chat, "+CTZDST:", ctzdst_notify, + FALSE, netreg, NULL); + g_at_chat_send(nd->at_data.chat, "AT+CTZR=1", none_prefix, + NULL, NULL, NULL); + } + + /* AT+CMER NOTES: + * - For all u-blox models, mode 3 is equivalent to mode 1; + * since some models do not support setting modes 2 nor 3 + * (see UBX-13002752), we prefer mode 1 for all models. + * - The TOBY L4 does not support ind=2 + */ + g_at_chat_send(nd->at_data.chat, "AT+CMER=1,0,0,1", cmer_prefix, + ublox_cmer_set_cb, netreg, NULL); +} + +/* + * uBlox netreg atom probe. + * - takes uBlox model ID parameter instead of AT vendor ID + */ +static int ublox_netreg_probe(struct ofono_netreg *netreg, + unsigned int model_id, + void *data) +{ + GAtChat *chat = data; + struct netreg_data *nd; + + nd = g_new0(struct netreg_data, 1); + + nd->model = ublox_model_from_id(model_id); + + /* There should be no uBlox-specific quirks in the 'generic' + * AT driver + */ + nd->at_data.vendor = OFONO_VENDOR_GENERIC; + + nd->at_data.chat = g_at_chat_clone(chat); + nd->at_data.tech = -1; + nd->at_data.time.sec = -1; + nd->at_data.time.min = -1; + nd->at_data.time.hour = -1; + nd->at_data.time.mday = -1; + nd->at_data.time.mon = -1; + nd->at_data.time.year = -1; + nd->at_data.time.dst = 0; + nd->at_data.time.utcoff = 0; + ofono_netreg_set_data(netreg, nd); + + /* All uBlox devices support n=2 so no need to query this */ + g_at_chat_send(nd->at_data.chat, "AT+CREG=2", none_prefix, + ublox_creg_set_cb, netreg, NULL); + + return 0; +} + +static const struct ofono_netreg_driver driver = { + .name = "ubloxmodem", + .probe = ublox_netreg_probe, + .remove = at_netreg_remove, + .registration_status = at_registration_status, + .current_operator = at_current_operator, + .list_operators = at_list_operators, + .register_auto = at_register_auto, + .register_manual = at_register_manual, + .strength = at_signal_strength, +}; + +void ublox_netreg_init(void) +{ + ofono_netreg_driver_register(&driver); +} + +void ublox_netreg_exit(void) +{ + ofono_netreg_driver_unregister(&driver); +} diff --git a/ofono/drivers/ubloxmodem/ubloxmodem.c b/ofono/drivers/ubloxmodem/ubloxmodem.c index a52a67ea0..034f7db1a 100644 --- a/ofono/drivers/ubloxmodem/ubloxmodem.c +++ b/ofono/drivers/ubloxmodem/ubloxmodem.c @@ -77,6 +77,15 @@ const struct ublox_model ublox_models[] = { .name = "TOBY-L4906", .flags = UBLOX_F_TOBY_L4, }, + /* LARA L2 series */ + { + .name = "LARA-R202", + .flags = UBLOX_F_LARA_R2, + }, + { + .name = "LARA-R211", + .flags = UBLOX_F_LARA_R2, + }, { /* sentinel */ }, }; @@ -115,6 +124,7 @@ int ublox_is_toby_l4(const struct ublox_model *model) static int ubloxmodem_init(void) { ublox_gprs_context_init(); + ublox_netreg_init(); ublox_netmon_init(); ublox_lte_init(); @@ -124,6 +134,7 @@ static int ubloxmodem_init(void) static void ubloxmodem_exit(void) { ublox_gprs_context_exit(); + ublox_netreg_exit(); ublox_netmon_exit(); ublox_lte_exit(); } diff --git a/ofono/drivers/ubloxmodem/ubloxmodem.h b/ofono/drivers/ubloxmodem/ubloxmodem.h index 2c5b74336..0bd7ef239 100644 --- a/ofono/drivers/ubloxmodem/ubloxmodem.h +++ b/ofono/drivers/ubloxmodem/ubloxmodem.h @@ -26,7 +26,8 @@ enum ublox_flags { UBLOX_F_TOBY_L2 = (1 << 0), UBLOX_F_TOBY_L4 = (1 << 1), - UBLOX_F_HAVE_USBCONF = (1 << 2), + UBLOX_F_LARA_R2 = (1 << 2), + UBLOX_F_HAVE_USBCONF = (1 << 3), }; struct ublox_model { @@ -43,6 +44,9 @@ int ublox_is_toby_l4(const struct ublox_model *model); extern void ublox_gprs_context_init(void); extern void ublox_gprs_context_exit(void); +void ublox_netreg_init(void); +void ublox_netreg_exit(void); + extern void ublox_netmon_init(void); extern void ublox_netmon_exit(void); diff --git a/ofono/drivers/xmm7modem/netmon.c b/ofono/drivers/xmm7modem/netmon.c index ba70e2bd6..0e958abef 100644 --- a/ofono/drivers/xmm7modem/netmon.c +++ b/ofono/drivers/xmm7modem/netmon.c @@ -47,6 +47,7 @@ static const char *xmci_prefix[] = { "+XMCI:", NULL }; struct netmon_driver_data { GAtChat *chat; + int xmci_mode; }; enum xmci_ofono_type_info { @@ -85,6 +86,7 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct cb_data *cbd = user_data; struct ofono_netmon *netmon = cbd->data; + struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon); ofono_netmon_cb_t cb = cbd->cb; struct ofono_error error; GAtResultIter iter; @@ -96,6 +98,11 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) int ecn0 = -1; int rsrq = -1; int tech = -1; + int type = -1; + int ci = -1; + const char *cell_id; + char mcc[3]; + char mnc[3]; DBG("ok %d", ok); @@ -109,18 +116,23 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) g_at_result_iter_init(&iter, result); while (g_at_result_iter_next(&iter, "+XMCI:")) { - if (!g_at_result_iter_next_number(&iter, &number)) + if (!g_at_result_iter_next_number(&iter, &type)) break; - tech = xmm7modem_map_radio_access_technology(number); + tech = xmm7modem_map_radio_access_technology(type); - switch (number) { + switch (type) { + case XMCI_GSM_NEIGH_CELL: case XMCI_GSM_SERV_CELL: - /* skip ,,,, */ - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); + /* ,,,, */ + g_at_result_iter_next_number(&iter, &number); + snprintf(mcc, 3, "%d", number); + g_at_result_iter_next_number(&iter, &number); + snprintf(mnc, 3, "%d", number); g_at_result_iter_skip_next(&iter); + g_at_result_iter_next_string(&iter, &cell_id); + sscanf(&cell_id[2], "%x", &number); + ci = number != -1 ? number : 0; g_at_result_iter_skip_next(&iter); g_at_result_iter_next_number(&iter, &number); @@ -129,15 +141,20 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) g_at_result_iter_next_number(&iter, &number); ber = number != 99 ? number : ber; break; + case XMCI_UMTS_NEIGH_CELL: case XMCI_UMTS_SERV_CELL: /* - * skip ,,,,, + * ,,,,, * ,, */ + g_at_result_iter_next_number(&iter, &number); + snprintf(mcc, 3, "%d", number); + g_at_result_iter_next_number(&iter, &number); + snprintf(mnc, 3, "%d", number); g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); + g_at_result_iter_next_string(&iter, &cell_id); + sscanf(&cell_id[2], "%x", &number); + ci = number != -1 ? number : 0; g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); @@ -150,15 +167,20 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) g_at_result_iter_next_number(&iter, &number); ecn0 = number != 255 ? number : ecn0; break; + case XMCI_LTE_NEIGH_CELL: case XMCI_LTE_SERV_CELL: /* - * skip ,,,,,, + * ,,,,,, * , */ + g_at_result_iter_next_number(&iter, &number); + snprintf(mcc, 3, "%d", number); + g_at_result_iter_next_number(&iter, &number); + snprintf(mnc, 3, "%d", number); g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); - g_at_result_iter_skip_next(&iter); + g_at_result_iter_next_string(&iter, &cell_id); + sscanf(&cell_id[2], "%x", &number); + ci = number != -1 ? number : 0; g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); g_at_result_iter_skip_next(&iter); @@ -174,8 +196,15 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) break; } - ofono_netmon_serving_cell_notify(netmon, + if ((nmd->xmci_mode == 0) && + (type == XMCI_GSM_NEIGH_CELL || + type == XMCI_UMTS_NEIGH_CELL || + type == XMCI_LTE_NEIGH_CELL)) { + ofono_netmon_neighbouring_cell_notify(netmon, tech, + OFONO_NETMON_INFO_MCC, mcc, + OFONO_NETMON_INFO_MNC, mnc, + OFONO_NETMON_INFO_CI, ci, OFONO_NETMON_INFO_RXLEV, rxlev, OFONO_NETMON_INFO_BER, ber, OFONO_NETMON_INFO_RSCP, rscp, @@ -183,10 +212,25 @@ static void xmci_cb(gboolean ok, GAtResult *result, gpointer user_data) OFONO_NETMON_INFO_RSRQ, rsrq, OFONO_NETMON_INFO_RSRP, rsrp, OFONO_NETMON_INFO_INVALID); - - CALLBACK_WITH_SUCCESS(cb, cbd->data); - break; + } else if ((nmd->xmci_mode == 1) && + (type == XMCI_GSM_SERV_CELL || + type == XMCI_UMTS_SERV_CELL || + type == XMCI_LTE_SERV_CELL)) { + ofono_netmon_serving_cell_notify(netmon, + tech, + OFONO_NETMON_INFO_RXLEV, rxlev, + OFONO_NETMON_INFO_BER, ber, + OFONO_NETMON_INFO_RSCP, rscp, + OFONO_NETMON_INFO_ECN0, ecn0, + OFONO_NETMON_INFO_RSRQ, rsrq, + OFONO_NETMON_INFO_RSRP, rsrp, + OFONO_NETMON_INFO_INVALID); + break; + } } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); + nmd->xmci_mode = -1; } static void xmm7modem_netmon_request_update(struct ofono_netmon *netmon, @@ -194,6 +238,7 @@ static void xmm7modem_netmon_request_update(struct ofono_netmon *netmon, { struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon); struct cb_data *cbd = cb_data_new(cb, data); + nmd->xmci_mode = 1; DBG("xmm7modem netmon request update"); @@ -205,6 +250,23 @@ static void xmm7modem_netmon_request_update(struct ofono_netmon *netmon, CALLBACK_WITH_FAILURE(cb, data); } +static void xmm7modem_neighbouring_cell_update(struct ofono_netmon *netmon, + ofono_netmon_cb_t cb, void *data) +{ + struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon); + struct cb_data *cbd = cb_data_new(cb, data); + nmd->xmci_mode = 0; + + DBG("xmm7modem netmon request neighbouring cell update"); + + if (g_at_chat_send(nmd->chat, "AT+XMCI=0", xmci_prefix, + xmci_cb, cbd, g_free) > 0) + return; + + g_free(cbd); + CALLBACK_WITH_FAILURE(cb, data); +} + static gboolean ril_delayed_register(gpointer user_data) { struct ofono_netmon *netmon = user_data; @@ -224,6 +286,7 @@ static int xmm7modem_netmon_probe(struct ofono_netmon *netmon, nmd = g_new0(struct netmon_driver_data, 1); nmd->chat = g_at_chat_clone(chat); + nmd->xmci_mode = -1; ofono_netmon_set_data(netmon, nmd); @@ -250,6 +313,7 @@ static const struct ofono_netmon_driver driver = { .probe = xmm7modem_netmon_probe, .remove = xmm7modem_netmon_remove, .request_update = xmm7modem_netmon_request_update, + .neighbouring_cell_update = xmm7modem_neighbouring_cell_update, }; void xmm_netmon_init(void) diff --git a/ofono/drivers/xmm7modem/radio-settings.c b/ofono/drivers/xmm7modem/radio-settings.c index f1eb1aa9b..c7c2ce0e8 100644 --- a/ofono/drivers/xmm7modem/radio-settings.c +++ b/ofono/drivers/xmm7modem/radio-settings.c @@ -84,10 +84,10 @@ static void xact_query_cb(gboolean ok, GAtResult *result, gpointer user_data) mode = OFONO_RADIO_ACCESS_MODE_LTE; break; case 3: - mode = OFONO_RADIO_ACCESS_MODE_UMTS; + mode = OFONO_RADIO_ACCESS_MODE_UMTS|OFONO_RADIO_ACCESS_MODE_GSM; break; case 4: - mode = OFONO_RADIO_ACCESS_MODE_LTE; + mode = OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS; break; case 5: mode = OFONO_RADIO_ACCESS_MODE_LTE; @@ -158,7 +158,16 @@ static void xmm_set_rat_mode(struct ofono_radio_settings *rs, break; } - if (value == 6) + if (mode == + (OFONO_RADIO_ACCESS_MODE_UMTS|OFONO_RADIO_ACCESS_MODE_GSM)) { + value = 3; + preferred = 1; + } + + if (mode == (OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS)) + value = 4; + + if (value == 6 || value == 3 || value == 4) snprintf(buf, sizeof(buf), "AT+XACT=%u,%u", value, preferred); else snprintf(buf, sizeof(buf), "AT+XACT=%u", value); diff --git a/ofono/gatchat/gatmux.c b/ofono/gatchat/gatmux.c index 9660006b0..757a5123a 100644 --- a/ofono/gatchat/gatmux.c +++ b/ofono/gatchat/gatmux.c @@ -952,8 +952,7 @@ gboolean g_at_mux_setup_gsm0710(GAtChat *chat, mux_query_cb, msd, msd_free) > 0) return TRUE; - if (msd) - msd_free(msd); + msd_free(msd); return FALSE; } diff --git a/ofono/gatchat/gatresult.c b/ofono/gatchat/gatresult.c index 2659db28c..883b4105f 100644 --- a/ofono/gatchat/gatresult.c +++ b/ofono/gatchat/gatresult.c @@ -111,6 +111,7 @@ gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter, unsigned int pos; unsigned int end; unsigned int len; + unsigned int stripped; char *line; if (iter == NULL) @@ -139,7 +140,12 @@ gboolean g_at_result_iter_next_unquoted_string(GAtResultIter *iter, while (end < len && line[end] != ',' && line[end] != ')') end += 1; - iter->buf[end] = '\0'; + stripped = end; + + while (line[stripped - 1] == ' ') + stripped -= 1; + + iter->buf[stripped] = '\0'; out: iter->line_pos = skip_to_next_field(line, end, len); diff --git a/ofono/gatchat/gatresult.h b/ofono/gatchat/gatresult.h index 589dd3dd6..e92d38ba0 100644 --- a/ofono/gatchat/gatresult.h +++ b/ofono/gatchat/gatresult.h @@ -22,6 +22,8 @@ #ifndef __GATCHAT_RESULT_H #define __GATCHAT_RESULT_H +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/ofono/gatchat/gatutil.c b/ofono/gatchat/gatutil.c index a2528e13a..be691b2aa 100644 --- a/ofono/gatchat/gatutil.c +++ b/ofono/gatchat/gatutil.c @@ -67,8 +67,7 @@ void g_at_util_debug_chat(gboolean in, const char *str, gsize len, escaped_str[0] = type; escaped_str[1] = ' '; - escaped_str[2] = '\0'; - escaped_str[escaped] = '\0'; + memset(escaped_str + 2, '\0', escaped - 1); for (escaped = 2, i = 0; i < len; i++) { unsigned char c = str[i]; @@ -87,11 +86,11 @@ void g_at_util_debug_chat(gboolean in, const char *str, gsize len, escaped_str[escaped++] = 'n'; break; case 26: - strncpy(&escaped_str[escaped], ctrlz, ctrlz_size); + memcpy(escaped_str + escaped, ctrlz, ctrlz_size); escaped += ctrlz_size; break; case 25: - strncpy(&escaped_str[escaped], esc, esc_size); + memcpy(escaped_str + escaped, esc, esc_size); escaped += esc_size; break; default: diff --git a/ofono/gisi/modem.c b/ofono/gisi/modem.c index ef0c04911..2eb015dfa 100644 --- a/ofono/gisi/modem.c +++ b/ofono/gisi/modem.c @@ -33,6 +33,8 @@ #include #include +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + #include "message.h" #include "common.h" #include "modem.h" diff --git a/ofono/gisi/socket.c b/ofono/gisi/socket.c index 2428f5d58..8c4af1bd3 100644 --- a/ofono/gisi/socket.c +++ b/ofono/gisi/socket.c @@ -32,6 +32,8 @@ #include #include +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + #include "phonet.h" #include "socket.h" diff --git a/ofono/include/netmon.h b/ofono/include/netmon.h index c8fcafa62..a99d6ca92 100644 --- a/ofono/include/netmon.h +++ b/ofono/include/netmon.h @@ -43,6 +43,8 @@ struct ofono_netmon_driver { unsigned int enable, unsigned int period, ofono_netmon_cb_t cb, void *data); + void (*neighbouring_cell_update)(struct ofono_netmon *netmon, + ofono_netmon_cb_t cb, void *data); }; enum ofono_netmon_cell_type { @@ -104,6 +106,10 @@ void ofono_netmon_set_data(struct ofono_netmon *netmon, void *data); void *ofono_netmon_get_data(struct ofono_netmon *netmon); +void ofono_netmon_neighbouring_cell_notify(struct ofono_netmon *netmon, + enum ofono_netmon_cell_type type, + int info_type, ...); + #ifdef __cplusplus } #endif diff --git a/ofono/plugins/alcatel.c b/ofono/plugins/alcatel.c index fb1d1ae57..8da06192a 100644 --- a/ofono/plugins/alcatel.c +++ b/ofono/plugins/alcatel.c @@ -91,34 +91,7 @@ static void alcatel_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, alcatel_debug, debug); - - return chat; + return at_util_open_device(modem, key, alcatel_debug, debug, NULL); } static void sim_state_cb(gboolean present, gpointer user_data) diff --git a/ofono/plugins/hfp_ag_bluez5.c b/ofono/plugins/hfp_ag_bluez5.c index 7653c4df2..edba5666d 100644 --- a/ofono/plugins/hfp_ag_bluez5.c +++ b/ofono/plugins/hfp_ag_bluez5.c @@ -38,9 +38,6 @@ #include #include -typedef struct GAtChat GAtChat; -typedef struct GAtResult GAtResult; - #include "drivers/atmodem/atutil.h" #include "hfp.h" diff --git a/ofono/plugins/hso.c b/ofono/plugins/hso.c index 249bb2caa..b58bf2a53 100644 --- a/ofono/plugins/hso.c +++ b/ofono/plugins/hso.c @@ -301,34 +301,7 @@ static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, hso_debug, debug); - - return chat; + return at_util_open_device(modem, key, hso_debug, debug, NULL); } static int hso_enable(struct ofono_modem *modem) diff --git a/ofono/plugins/huawei.c b/ofono/plugins/huawei.c index 817ee66ae..bdf7bc397 100644 --- a/ofono/plugins/huawei.c +++ b/ofono/plugins/huawei.c @@ -553,36 +553,15 @@ static void gcap_support(gboolean ok, GAtResult *result, gpointer user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; + GAtChat *chat = at_util_open_device(modem, key, huawei_debug, debug, + NULL); - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) + if (!chat) return NULL; g_at_chat_add_terminator(chat, "COMMAND NOT SUPPORT", -1, FALSE); g_at_chat_add_terminator(chat, "TOO MANY PARAMETERS", -1, FALSE); - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, huawei_debug, debug); - return chat; } diff --git a/ofono/plugins/icera.c b/ofono/plugins/icera.c index 7df5ffd76..90aca221b 100644 --- a/ofono/plugins/icera.c +++ b/ofono/plugins/icera.c @@ -96,42 +96,9 @@ static void icera_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - GAtChat *chat; - GAtSyntax *syntax; - GIOChannel *channel; - GHashTable *options; - const char *device; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return NULL; - - g_hash_table_insert(options, "Baud", "115200"); - - channel = g_at_tty_open(device, options); - - g_hash_table_destroy(options); - - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, icera_debug, debug); - - return chat; + return at_util_open_device(modem, key, icera_debug, debug, + "Baud", "115200", + NULL); } static void ussdmode_query(gboolean ok, GAtResult *result, diff --git a/ofono/plugins/linktop.c b/ofono/plugins/linktop.c index bb0d7b8ea..2b8acf769 100644 --- a/ofono/plugins/linktop.c +++ b/ofono/plugins/linktop.c @@ -91,34 +91,7 @@ static void linktop_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, linktop_debug, debug); - - return chat; + return at_util_open_device(modem, key, linktop_debug, debug, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) diff --git a/ofono/plugins/mbm.c b/ofono/plugins/mbm.c index b787aeb79..38297d747 100644 --- a/ofono/plugins/mbm.c +++ b/ofono/plugins/mbm.c @@ -285,35 +285,12 @@ static void emrdy_query(gboolean ok, GAtResult *result, gpointer user_data) cfun_query, modem, NULL); } -static GAtChat *create_port(const char *device) +static GAtChat *open_device(struct ofono_modem *modem, const char *key, + char *debug) { - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - GHashTable *options; - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return NULL; - - g_hash_table_insert(options, "Baud", "115200"); - - channel = g_at_tty_open(device, options); - - g_hash_table_destroy(options); - - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - return chat; + return at_util_open_device(modem, key, mbm_debug, debug, + "Baud", "115200", + NULL); } static int mbm_enable(struct ofono_modem *modem) @@ -332,14 +309,11 @@ static int mbm_enable(struct ofono_modem *modem) if (modem_dev == NULL || data_dev == NULL) return -EINVAL; - data->modem_port = create_port(modem_dev); + data->modem_port = open_device(modem, "ModemDevice", "Modem: "); if (data->modem_port == NULL) return -EIO; - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(data->modem_port, mbm_debug, "Modem: "); - - data->data_port = create_port(data_dev); + data->data_port = open_device(modem, "DataDevice", "Data: "); if (data->data_port == NULL) { g_at_chat_unref(data->modem_port); data->modem_port = NULL; @@ -347,9 +321,6 @@ static int mbm_enable(struct ofono_modem *modem) return -EIO; } - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(data->data_port, mbm_debug, "Data: "); - g_at_chat_register(data->modem_port, "*EMRDY:", emrdy_notifier, FALSE, modem, NULL); diff --git a/ofono/plugins/nokia.c b/ofono/plugins/nokia.c index ef598fa4b..7f19ce968 100644 --- a/ofono/plugins/nokia.c +++ b/ofono/plugins/nokia.c @@ -43,6 +43,7 @@ #include #include +#include #include static const char *none_prefix[] = { NULL }; @@ -91,34 +92,7 @@ static void nokia_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, nokia_debug, debug); - - return chat; + return at_util_open_device(modem, key, nokia_debug, debug, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) diff --git a/ofono/plugins/novatel.c b/ofono/plugins/novatel.c index a64364d16..c01aa5485 100644 --- a/ofono/plugins/novatel.c +++ b/ofono/plugins/novatel.c @@ -96,32 +96,7 @@ static void novatel_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - GAtChat *chat; - GAtSyntax *syntax; - GIOChannel *channel; - const char *device; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, novatel_debug, debug); - - return chat; + return at_util_open_device(modem, key, novatel_debug, debug, NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) diff --git a/ofono/plugins/phonesim.c b/ofono/plugins/phonesim.c index 7f17c2d33..2209a35bd 100644 --- a/ofono/plugins/phonesim.c +++ b/ofono/plugins/phonesim.c @@ -161,9 +161,7 @@ static void phonesim_activate_primary(struct ofono_gprs_context *gc, break; } - if (ctx->apn) - snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", - ctx->apn); + snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"", ctx->apn); /* Assume always succeeds */ if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0) diff --git a/ofono/plugins/quectel.c b/ofono/plugins/quectel.c index ba1aa42da..037d4834b 100644 --- a/ofono/plugins/quectel.c +++ b/ofono/plugins/quectel.c @@ -25,35 +25,100 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #define OFONO_API_SUBJECT_TO_CHANGE +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include +#include + +#include #include #include static const char *cfun_prefix[] = { "+CFUN:", NULL }; static const char *cpin_prefix[] = { "+CPIN:", NULL }; +static const char *cbc_prefix[] = { "+CBC:", NULL }; +static const char *qinistat_prefix[] = { "+QINISTAT:", NULL }; +static const char *cgmm_prefix[] = { "UC15", "Quectel_M95", "Quectel_MC60", + NULL }; static const char *none_prefix[] = { NULL }; +static const uint8_t gsm0710_terminate[] = { + 0xf9, /* open flag */ + 0x03, /* channel 0 */ + 0xef, /* UIH frame */ + 0x05, /* 2 data bytes */ + 0xc3, /* terminate 1 */ + 0x01, /* terminate 2 */ + 0xf2, /* crc */ + 0xf9, /* close flag */ +}; + +enum quectel_model { + QUECTEL_UNKNOWN, + QUECTEL_UC15, + QUECTEL_M95, + QUECTEL_MC60, +}; + struct quectel_data { GAtChat *modem; GAtChat *aux; guint cpin_ready; - gboolean have_sim; + guint call_ready; + bool have_sim; + enum ofono_vendor vendor; + enum quectel_model model; + struct l_timeout *sms_ready_timer; + + /* used by quectel uart driver */ + GAtChat *uart; + int mux_ready_count; + int initial_ldisc; + struct l_gpio_writer *gpio; +}; + +struct dbus_hw { + DBusMessage *msg; + struct ofono_modem *modem; + int32_t charge_status; + int32_t charge_level; + int32_t voltage; +}; + +enum quectel_power_event { + LOW_POWER_DOWN = -2, + LOW_WARNING = -1, + NORMAL_POWER_DOWN = 0, + HIGH_WARNING = 1, + HIGH_POWER_DOWN = 2, }; +static const char dbus_hw_interface[] = OFONO_SERVICE ".quectel.Hardware"; + static void quectel_debug(const char *str, void *user_data) { const char *prefix = user_data; @@ -61,6 +126,43 @@ static void quectel_debug(const char *str, void *user_data) ofono_info("%s%s", prefix, str); } +static int quectel_probe_gpio(struct ofono_modem *modem) +{ + struct quectel_data *data = ofono_modem_get_data(modem); + struct l_gpio_chip *gpiochip; + uint32_t offset; + const char *chip_name, *offset_str; + uint32_t value = 0; + + DBG("%p", modem); + + chip_name = ofono_modem_get_string(modem, "GpioChip"); + if (!chip_name) + return 0; + + offset_str = ofono_modem_get_string(modem, "GpioOffset"); + if (!offset_str) + return -EINVAL; + + offset = strtoul(offset_str, NULL, 0); + if (!offset) + return -EINVAL; + + gpiochip = l_gpio_chip_new(chip_name); + if (!gpiochip) + return -ENODEV; + + data->gpio = l_gpio_writer_new(gpiochip, "ofono", 1, &offset, + &value); + + l_gpio_chip_free(gpiochip); + + if (!data->gpio) + return -EIO; + + return 0; +} + static int quectel_probe(struct ofono_modem *modem) { struct quectel_data *data; @@ -73,7 +175,7 @@ static int quectel_probe(struct ofono_modem *modem) ofono_modem_set_data(modem, data); - return 0; + return quectel_probe_gpio(modem); } static void quectel_remove(struct ofono_modem *modem) @@ -86,42 +188,341 @@ static void quectel_remove(struct ofono_modem *modem) g_at_chat_unregister(data->aux, data->cpin_ready); ofono_modem_set_data(modem, NULL); + l_gpio_writer_free(data->gpio); g_at_chat_unref(data->aux); g_at_chat_unref(data->modem); + g_at_chat_unref(data->uart); g_free(data); } -static GAtChat *open_device(struct ofono_modem *modem, - const char *key, char *debug) +static void close_mux_cb(struct l_timeout *timeout, void *user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + GIOChannel *device; + uint32_t gpio_value = 0; + ssize_t write_count; + int fd; + + DBG("%p", modem); + + device = g_at_chat_get_channel(data->uart); + fd = g_io_channel_unix_get_fd(device); + + /* restore initial tty line discipline */ + if (ioctl(fd, TIOCSETD, &data->initial_ldisc) < 0) + ofono_warn("Failed to restore line discipline"); + + /* terminate gsm 0710 multiplexing on the modem side */ + write_count = write(fd, gsm0710_terminate, sizeof(gsm0710_terminate)); + if (write_count != sizeof(gsm0710_terminate)) + ofono_warn("Failed to terminate gsm multiplexing"); + + g_at_chat_unref(data->uart); + data->uart = NULL; + + l_timeout_remove(timeout); + l_gpio_writer_set(data->gpio, 1, &gpio_value); + ofono_modem_set_powered(modem, FALSE); +} + +static void close_serial(struct ofono_modem *modem) +{ + struct quectel_data *data = ofono_modem_get_data(modem); + + DBG("%p", modem); + + g_at_chat_unref(data->aux); + data->aux = NULL; + + g_at_chat_unref(data->modem); + data->modem = NULL; + + /* + * if gsm0710 multiplexing is used, the aux and modem file descriptors + * must be closed before closing the underlying serial device to avoid + * an old kernel dead-lock: + * https://lists.ofono.org/pipermail/ofono/2011-March/009405.html + * + * setup a timer to iterate the mainloop once to let gatchat close the + * virtual file descriptors unreferenced above + */ + if (data->uart) + l_timeout_create_ms(1, close_mux_cb, modem, NULL); + else + ofono_modem_set_powered(modem, false); +} + +static void dbus_hw_reply_properties(struct dbus_hw *hw) +{ + struct quectel_data *data = ofono_modem_get_data(hw->modem); + DBusMessage *reply; + DBusMessageIter dbus_iter; + DBusMessageIter dbus_dict; + + DBG("%p", hw->modem); + + reply = dbus_message_new_method_return(hw->msg); + dbus_message_iter_init_append(reply, &dbus_iter); + dbus_message_iter_open_container(&dbus_iter, DBUS_TYPE_ARRAY, + OFONO_PROPERTIES_ARRAY_SIGNATURE, + &dbus_dict); + + /* + * the charge status/level received from m95 and mc60 are invalid so + * only return those for the UC15 modem. + */ + if (data->model == QUECTEL_UC15) { + ofono_dbus_dict_append(&dbus_dict, "ChargeStatus", + DBUS_TYPE_INT32, &hw->charge_status); + + ofono_dbus_dict_append(&dbus_dict, "ChargeLevel", + DBUS_TYPE_INT32, &hw->charge_level); + } + + ofono_dbus_dict_append(&dbus_dict, "Voltage", DBUS_TYPE_INT32, + &hw->voltage); + + dbus_message_iter_close_container(&dbus_iter, &dbus_dict); + + __ofono_dbus_pending_reply(&hw->msg, reply); +} + +static void cbc_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct dbus_hw *hw = user_data; + GAtResultIter iter; + + DBG("%p", hw->modem); + + if (!hw->msg) + return; + + if (!ok) + goto error; + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+CBC:")) + goto error; + + /* the returned charge status is valid only for uc15 */ + if (!g_at_result_iter_next_number(&iter, &hw->charge_status)) + goto error; + + /* the returned charge level is valid only for uc15 */ + if (!g_at_result_iter_next_number(&iter, &hw->charge_level)) + goto error; + + /* now comes the millivolts */ + if (!g_at_result_iter_next_number(&iter, &hw->voltage)) + goto error; + + dbus_hw_reply_properties(hw); + + return; + +error: + __ofono_dbus_pending_reply(&hw->msg, __ofono_error_failed(hw->msg)); +} + +static DBusMessage *dbus_hw_get_properties(DBusConnection *conn, + DBusMessage *msg, + void *user_data) +{ + struct dbus_hw *hw = user_data; + struct quectel_data *data = ofono_modem_get_data(hw->modem); + + DBG("%p", hw->modem); + + if (hw->msg != NULL) + return __ofono_error_busy(msg); + + if (!g_at_chat_send(data->aux, "AT+CBC", cbc_prefix, cbc_cb, hw, NULL)) + return __ofono_error_failed(msg); + + hw->msg = dbus_message_ref(msg); + + return NULL; +} + +static void voltage_handle(struct ofono_modem *modem, + enum quectel_power_event event) +{ + DBusConnection *conn = ofono_dbus_get_connection(); + DBusMessage *signal; + DBusMessageIter iter; + const char *path = ofono_modem_get_path(modem); + const char *name; + const char *reason; + bool close; + + DBG("%p", modem); + + switch (event) { + case LOW_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "VoltageLow"; + break; + case LOW_WARNING: + close = false; + name = "PowerWarning"; + reason = "VoltageLow"; + break; + case NORMAL_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "Normal"; + break; + case HIGH_WARNING: + close = false; + name = "PowerWarning"; + reason = "VoltageHigh"; + break; + case HIGH_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "VoltageHigh"; + break; + default: + return; + } + + signal = dbus_message_new_signal(path, dbus_hw_interface, name); + if (signal) { + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &reason); + g_dbus_send_message(conn, signal); + } + + if (close) + close_serial(modem); +} + +static void qind_notify(GAtResult *result, void *user_data) +{ + struct dbus_hw *hw = user_data; + GAtResultIter iter; + enum quectel_power_event event; + const char *type; + + DBG("%p", hw->modem); + + g_at_result_iter_init(&iter, result); + g_at_result_iter_next(&iter, "+QIND:"); + + if (!g_at_result_iter_next_string(&iter, &type)) + return; + + if (!g_at_result_iter_next_number(&iter, &event)) + return; + + voltage_handle(hw->modem, event); +} + +static void power_notify(GAtResult *result, void *user_data) +{ + struct dbus_hw *hw = user_data; + GAtResultIter iter; + const char *event; + + DBG("%p", hw->modem); + + g_at_result_iter_init(&iter, result); + g_at_result_iter_next(&iter, NULL); + + if (!g_at_result_iter_next_unquoted_string(&iter, &event)) + return; + + DBG("event: %s", event); + + if (g_strcmp0(event, "UNDER_VOLTAGE POWER DOWN") == 0) + voltage_handle(hw->modem, LOW_POWER_DOWN); + else if (g_strcmp0(event, "UNDER_VOLTAGE WARNING") == 0) + voltage_handle(hw->modem, LOW_WARNING); + else if (g_strcmp0(event, "NORMAL POWER DOWN") == 0) + voltage_handle(hw->modem, NORMAL_POWER_DOWN); + else if (g_strcmp0(event, "OVER_VOLTAGE WARNING") == 0) + voltage_handle(hw->modem, HIGH_WARNING); + else if (g_strcmp0(event, "OVER_VOLTAGE POWER DOWN") == 0) + voltage_handle(hw->modem, HIGH_POWER_DOWN); +} + +static const GDBusMethodTable dbus_hw_methods[] = { + { GDBUS_ASYNC_METHOD("GetProperties", + NULL, GDBUS_ARGS({ "properties", "a{sv}" }), + dbus_hw_get_properties) }, + {} +}; + +static const GDBusSignalTable dbus_hw_signals[] = { + { GDBUS_SIGNAL("PowerDown", + GDBUS_ARGS({ "reason", "s" })) }, + { GDBUS_SIGNAL("PowerWarning", + GDBUS_ARGS({ "reason", "s" })) }, + { } +}; + +static void dbus_hw_cleanup(void *data) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; + struct dbus_hw *hw = data; - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; + DBG("%p", hw->modem); - DBG("%s %s", key, device); + if (hw->msg) + __ofono_dbus_pending_reply(&hw->msg, + __ofono_error_canceled(hw->msg)); + + l_free(hw); +} - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; +static void dbus_hw_enable(struct ofono_modem *modem) +{ + DBusConnection *conn = ofono_dbus_get_connection(); + struct quectel_data *data = ofono_modem_get_data(modem); + const char *path = ofono_modem_get_path(modem); + struct dbus_hw *hw; - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); + DBG("%p", modem); - g_io_channel_unref(channel); + hw = l_new(struct dbus_hw, 1); + hw->modem = modem; - if (chat == NULL) - return NULL; + if (!g_dbus_register_interface(conn, path, dbus_hw_interface, + dbus_hw_methods, dbus_hw_signals, NULL, + hw, dbus_hw_cleanup)) { + ofono_error("Could not register %s interface under %s", + dbus_hw_interface, path); + l_free(hw); + return; + } - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, quectel_debug, debug); + g_at_chat_register(data->aux, "NORMAL POWER DOWN", power_notify, FALSE, + hw, NULL); + + switch (data->model) { + case QUECTEL_UC15: + g_at_chat_register(data->aux, "+QIND", qind_notify, FALSE, hw, + NULL); + break; + case QUECTEL_M95: + case QUECTEL_MC60: + g_at_chat_register(data->aux, "OVER_VOLTAGE POWER DOWN", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "UNDER_VOLTAGE POWER DOWN", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "OVER_VOLTAGE WARNING", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "UNDER_VOLTAGE WARNING", + power_notify, FALSE, hw, NULL); + break; + case QUECTEL_UNKNOWN: + break; + } - return chat; + ofono_modem_add_interface(modem, dbus_hw_interface); } static void cpin_notify(GAtResult *result, gpointer user_data) @@ -141,7 +542,7 @@ static void cpin_notify(GAtResult *result, gpointer user_data) g_at_result_iter_next_unquoted_string(&iter, &sim_inserted); if (g_strcmp0(sim_inserted, "NOT INSERTED") != 0) - data->have_sim = TRUE; + data->have_sim = true; ofono_modem_set_powered(modem, TRUE); @@ -150,11 +551,13 @@ static void cpin_notify(GAtResult *result, gpointer user_data) g_at_chat_unregister(data->aux, data->cpin_ready); data->cpin_ready = 0; + + dbus_hw_enable(modem); } static void cpin_query(gboolean ok, GAtResult *result, gpointer user_data) { - DBG("ok %d", ok); + DBG("%p ok %d", user_data, ok); if (ok) cpin_notify(result, user_data); @@ -165,21 +568,17 @@ static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); - DBG("ok %d", ok); + DBG("%p ok %d", modem, ok); if (!ok) { - g_at_chat_unref(data->aux); - data->aux = NULL; - g_at_chat_unref(data->modem); - data->modem = NULL; - ofono_modem_set_powered(modem, FALSE); + close_serial(modem); return; } data->cpin_ready = g_at_chat_register(data->aux, "+CPIN", cpin_notify, FALSE, modem, NULL); - g_at_chat_send(data->aux, "AT+CPIN?", cpin_prefix, cpin_query, - modem, NULL); + g_at_chat_send(data->aux, "AT+CPIN?", cpin_prefix, cpin_query, modem, + NULL); } static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data) @@ -187,19 +586,23 @@ static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data) struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); GAtResultIter iter; - int status; + int cfun; - DBG("ok %d", ok); + DBG("%p ok %d", modem, ok); - if (!ok) + if (!ok) { + close_serial(modem); return; + } g_at_result_iter_init(&iter, result); - if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE) + if (g_at_result_iter_next(&iter, "+CFUN:") == FALSE) { + close_serial(modem); return; + } - g_at_result_iter_next_number(&iter, &status); + g_at_result_iter_next_number(&iter, &cfun); /* * The modem firmware powers up in CFUN=1 but will respond to AT+CFUN=4 @@ -207,80 +610,362 @@ static void cfun_query(gboolean ok, GAtResult *result, gpointer user_data) * passes. Empirical evidence suggests that the firmware will report an * unsolicited +CPIN: notification when it is ready to be useful. * - * Work around this feature by only transitioning to CFUN=4 after we've - * received an unsolicited +CPIN: notification. + * Work around this feature by only transitioning to CFUN=4 if the + * modem is not in CFUN=1 or until after we've received an unsolicited + * +CPIN: notification. */ + if (cfun != 1) + g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, cfun_enable, + modem, NULL); + else + cfun_enable(TRUE, NULL, modem); +} + +static void cgmm_cb(int ok, GAtResult *result, void *user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + const char *model; + + DBG("%p ok %d", modem, ok); - if (status != 1) { - g_at_chat_send(data->aux, "AT+CFUN=4", none_prefix, - cfun_enable, modem, NULL); + if (!at_util_parse_attr(result, "", &model)) { + ofono_error("Failed to query modem model"); + close_serial(modem); return; } - cfun_enable(TRUE, NULL, modem); + if (strcmp(model, "UC15") == 0) { + DBG("%p model UC15", modem); + data->vendor = OFONO_VENDOR_QUECTEL; + data->model = QUECTEL_UC15; + } else if (strcmp(model, "Quectel_M95") == 0) { + DBG("%p model M95", modem); + data->vendor = OFONO_VENDOR_QUECTEL_SERIAL; + data->model = QUECTEL_M95; + } else if (strcmp(model, "Quectel_MC60") == 0) { + DBG("%p model MC60", modem); + data->vendor = OFONO_VENDOR_QUECTEL_SERIAL; + data->model = QUECTEL_MC60; + } else { + ofono_warn("%p unknown model: '%s'", modem, model); + data->vendor = OFONO_VENDOR_QUECTEL; + data->model = QUECTEL_UNKNOWN; + } + + g_at_chat_send(data->aux, "AT+CFUN?", cfun_prefix, cfun_query, modem, + NULL); } -static int quectel_enable(struct ofono_modem *modem) +static void qinistat_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + GAtResultIter iter; + int status; + + DBG("%p", modem); + + g_at_result_iter_init(&iter, result); + + if (!g_at_result_iter_next(&iter, "+QINISTAT:")) + return; + + if (!g_at_result_iter_next_number(&iter, &status)) + return; + + DBG("qinistat: %d", status); + + if (status != 3) { + l_timeout_modify_ms(data->sms_ready_timer, 500); + return; + } + + ofono_sms_create(modem, data->vendor, "atmodem", data->aux); + l_timeout_remove(data->sms_ready_timer); + data->sms_ready_timer = NULL; +} + +static void sms_ready_cb(struct l_timeout *timeout, void *user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + + DBG("%p", modem); + + g_at_chat_send(data->aux, "AT+QINISTAT", qinistat_prefix, qinistat_cb, + modem, NULL); +} + +static void call_ready_notify(GAtResult *result, void *user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + + DBG("%p", modem); + + g_at_chat_unregister(data->aux, data->call_ready); + data->call_ready = 0; + data->sms_ready_timer = l_timeout_create_ms(500, sms_ready_cb, modem, + NULL); + if (!data->sms_ready_timer) { + close_serial(modem); + return; + } + + ofono_phonebook_create(modem, 0, "atmodem", data->aux); + ofono_voicecall_create(modem, 0, "atmodem", data->aux); + ofono_call_volume_create(modem, 0, "atmodem", data->aux); +} + +static int open_ttys(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); DBG("%p", modem); - data->modem = open_device(modem, "Modem", "Modem: "); + data->modem = at_util_open_device(modem, "Modem", quectel_debug, + "Modem: ", NULL); if (data->modem == NULL) return -EINVAL; - data->aux = open_device(modem, "Aux", "Aux: "); + data->aux = at_util_open_device(modem, "Aux", quectel_debug, "Aux: ", + NULL); if (data->aux == NULL) { g_at_chat_unref(data->modem); data->modem = NULL; return -EIO; } - g_at_chat_set_slave(data->modem, data->aux); + data->call_ready = g_at_chat_register(data->aux, "Call Ready", + call_ready_notify, false, + modem, NULL); + if (!data->call_ready) { + close_serial(modem); + return -ENOTTY; + } - g_at_chat_send(data->modem, "ATE0 &C0 +CMEE=1", none_prefix, - NULL, NULL, NULL); - g_at_chat_send(data->aux, "ATE0 &C0 +CMEE=1", none_prefix, - NULL, NULL, NULL); + g_at_chat_set_slave(data->modem, data->aux); - g_at_chat_send(data->aux, "AT+CFUN?", cfun_prefix, - cfun_query, modem, NULL); + g_at_chat_send(data->modem, "ATE0; &C0; +CMEE=1", none_prefix, NULL, + NULL, NULL); + g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1", none_prefix, NULL, NULL, + NULL); + g_at_chat_send(data->aux, "AT+CGMM", cgmm_prefix, cgmm_cb, modem, + NULL); return -EINPROGRESS; } -static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) +static void mux_ready_cb(struct l_timeout *timeout, void *user_data) { struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); + struct stat st; + int ret; - DBG(""); + DBG("%p", modem); - g_at_chat_unref(data->aux); - data->aux = NULL; + /* check if the last (and thus all) virtual gsm tty's are created */ + ret = stat(ofono_modem_get_string(modem, "Modem"), &st); + if (ret < 0) { + if (data->mux_ready_count++ < 5) { + /* not ready yet; try again in 100 ms*/ + l_timeout_modify_ms(timeout, 100); + return; + } + + /* not ready after 500 ms; bail out */ + close_serial(modem); + return; + } - if (ok) - ofono_modem_set_powered(modem, FALSE); + /* virtual gsm tty's are ready */ + l_timeout_remove(timeout); + + if (open_ttys(modem) != -EINPROGRESS) + close_serial(modem); + + g_at_chat_set_slave(data->uart, data->modem); +} + +static void cmux_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + struct gsm_config gsm_config; + GIOChannel *device; + int ldisc = N_GSM0710; + int fd; + + DBG("%p", modem); + + device = g_at_chat_get_channel(data->uart); + fd = g_io_channel_unix_get_fd(device); + + /* get initial line discipline to restore after use */ + if (ioctl(fd, TIOCGETD, &data->initial_ldisc) < 0) { + ofono_error("Failed to get current line discipline: %s", + strerror(errno)); + close_serial(modem); + return; + } + + /* enable gsm 0710 multiplexing line discipline */ + if (ioctl(fd, TIOCSETD, &ldisc) < 0) { + ofono_error("Failed to set multiplexer line discipline: %s", + strerror(errno)); + close_serial(modem); + return; + } + + /* get n_gsm configuration */ + if (ioctl(fd, GSMIOC_GETCONF, &gsm_config) < 0) { + ofono_error("Failed to get gsm config: %s", strerror(errno)); + close_serial(modem); + return; + } + + gsm_config.initiator = 1; /* cpu side is initiating multiplexing */ + gsm_config.encapsulation = 0; /* basic transparency encoding */ + gsm_config.mru = 127; /* 127 bytes rx mtu */ + gsm_config.mtu = 127; /* 127 bytes tx mtu */ + gsm_config.t1 = 10; /* 100 ms ack timer */ + gsm_config.n2 = 3; /* 3 retries */ + gsm_config.t2 = 30; /* 300 ms response timer */ + gsm_config.t3 = 10; /* 100 ms wake up response timer */ + gsm_config.i = 1; /* subset */ + + /* set the new configuration */ + if (ioctl(fd, GSMIOC_SETCONF, &gsm_config) < 0) { + ofono_error("Failed to set gsm config: %s", strerror(errno)); + close_serial(modem); + return; + } + + /* + * the kernel does not yet support mapping the underlying serial device + * to its virtual gsm ttys, so hard-code gsmtty1 gsmtty2 for now + */ + ofono_modem_set_string(modem, "Aux", "/dev/gsmtty1"); + ofono_modem_set_string(modem, "Modem", "/dev/gsmtty2"); + + /* wait for gsmtty devices to appear */ + if (!l_timeout_create_ms(100, mux_ready_cb, modem, NULL)) { + close_serial(modem); + return; + } +} + +static void ate_cb(int ok, GAtResult *result, void *user_data) +{ + struct ofono_modem *modem = user_data; + struct quectel_data *data = ofono_modem_get_data(modem); + + DBG("%p", modem); + + g_at_chat_set_wakeup_command(data->uart, NULL, 0, 0); + g_at_chat_send(data->uart, "AT+CMUX=0,0,5,127,10,3,30,10,2", NULL, + cmux_cb, modem, NULL); +} + +static int open_serial(struct ofono_modem *modem) +{ + struct quectel_data *data = ofono_modem_get_data(modem); + const uint32_t gpio_value = 1; + const char *rts_cts; + + DBG("%p", modem); + + rts_cts = ofono_modem_get_string(modem, "RtsCts"); + + data->uart = at_util_open_device(modem, "Device", quectel_debug, + "UART: ", + "Baud", "115200", + "Parity", "none", + "StopBits", "1", + "DataBits", "8", + "XonXoff", "off", + "Local", "on", + "Read", "on", + "RtsCts", rts_cts, + NULL); + if (data->uart == NULL) + return -EINVAL; + + if (data->gpio && !l_gpio_writer_set(data->gpio, 1, &gpio_value)) { + close_serial(modem); + return -EIO; + } + + /* + * there are three different power-up scenarios: + * + * 1) the gpio has just been toggled on, so the modem is not ready + * until it prints RDY + * + * 2) the modem has been on for a while and ready to respond to + * commands, so there will be no RDY notification + * + * 3) either of the previous to scenarious is the case, but the modem + * UART is not configured to a fixed bitrate. In this case it needs + * a few 'AT' bytes to detect the host UART bitrate, but the RDY is + * lost. + * + * the wakeup command feature is (mis)used to support all three + * scenarious by sending AT commands until the modem responds with OK, + * at which point the modem is ready. + */ + g_at_chat_set_wakeup_command(data->uart, "AT\r", 500, 10000); + + if (strcmp(rts_cts, "on") == 0) + g_at_chat_send(data->uart, "AT+IFC=2,2; E0", none_prefix, + ate_cb, modem, NULL); + else + g_at_chat_send(data->uart, "ATE0", none_prefix, ate_cb, modem, + NULL); + + return -EINPROGRESS; +} + +static int quectel_enable(struct ofono_modem *modem) +{ + DBG("%p", modem); + + if (ofono_modem_get_string(modem, "Device")) + return open_serial(modem); + else + return open_ttys(modem); +} + +static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct ofono_modem *modem = user_data; + + DBG("%p", modem); + + close_serial(modem); } static int quectel_disable(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); + DBusConnection *conn = ofono_dbus_get_connection(); + const char *path = ofono_modem_get_path(modem); DBG("%p", modem); g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); - g_at_chat_unref(data->modem); - data->modem = NULL; - g_at_chat_cancel_all(data->aux); g_at_chat_unregister_all(data->aux); - g_at_chat_send(data->aux, "AT+CFUN=0", cfun_prefix, - cfun_disable, modem, NULL); + if (g_dbus_unregister_interface(conn, path, dbus_hw_interface)) + ofono_modem_remove_interface(modem, dbus_hw_interface); + + g_at_chat_send(data->aux, "AT+CFUN=0", cfun_prefix, cfun_disable, modem, + NULL); return -EINPROGRESS; } @@ -291,6 +976,8 @@ static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data) ofono_modem_online_cb_t cb = cbd->cb; struct ofono_error error; + DBG("%p", user_data); + decode_at_error(&error, g_at_result_final_response(result)); cb(&error, cbd->data); } @@ -304,8 +991,8 @@ static void quectel_set_online(struct ofono_modem *modem, ofono_bool_t online, DBG("modem %p %s", modem, online ? "online" : "offline"); - if (g_at_chat_send(data->aux, command, cfun_prefix, set_online_cb, - cbd, g_free) > 0) + if (g_at_chat_send(data->aux, command, cfun_prefix, set_online_cb, cbd, + g_free) > 0) return; CALLBACK_WITH_FAILURE(cb, cbd->data); @@ -321,10 +1008,9 @@ static void quectel_pre_sim(struct ofono_modem *modem) DBG("%p", modem); ofono_devinfo_create(modem, 0, "atmodem", data->aux); - sim = ofono_sim_create(modem, OFONO_VENDOR_QUECTEL, "atmodem", - data->aux); + sim = ofono_sim_create(modem, data->vendor, "atmodem", data->aux); - if (sim && data->have_sim == TRUE) + if (sim && data->have_sim == true) ofono_sim_inserted_notify(sim, TRUE); } @@ -336,8 +1022,9 @@ static void quectel_post_sim(struct ofono_modem *modem) DBG("%p", modem); - gprs = ofono_gprs_create(modem, 0, "atmodem", data->aux); - gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem); + gprs = ofono_gprs_create(modem, data->vendor, "atmodem", data->aux); + gc = ofono_gprs_context_create(modem, data->vendor, "atmodem", + data->modem); if (gprs && gc) ofono_gprs_add_context(gprs, gc); @@ -347,6 +1034,8 @@ static void quectel_post_online(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); + DBG("%p", modem); + ofono_netreg_create(modem, 0, "atmodem", data->aux); } diff --git a/ofono/plugins/samsung.c b/ofono/plugins/samsung.c index 68a9b0a6d..1780943be 100644 --- a/ofono/plugins/samsung.c +++ b/ofono/plugins/samsung.c @@ -149,47 +149,21 @@ static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) static int samsung_enable(struct ofono_modem *modem) { struct samsung_data *data = ofono_modem_get_data(modem); - GAtSyntax *syntax; - GIOChannel *channel; - GHashTable *options; - const char *device; - - device = ofono_modem_get_string(modem, "ControlPort"); - if (device == NULL) - return -EINVAL; - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return -ENOMEM; - - g_hash_table_insert(options, "Baud", "115200"); - g_hash_table_insert(options, "Parity", "none"); - g_hash_table_insert(options, "StopBits", "1"); - g_hash_table_insert(options, "DataBits", "8"); - g_hash_table_insert(options, "XonXoff", "off"); - g_hash_table_insert(options, "RtsCts", "on"); - g_hash_table_insert(options, "Local", "on"); - g_hash_table_insert(options, "Read", "on"); - - channel = g_at_tty_open(device, options); - - g_hash_table_destroy(options); - - if (channel == NULL) - return -EIO; - - syntax = g_at_syntax_new_gsm_permissive(); - data->chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); + data->chat = at_util_open_device(modem, "ControlPort", + samsung_debug, "Device: ", + "Baud", "115200", + "Parity", "none", + "StopBits", "1", + "DataBits", "8", + "XonXoff", "off", + "RtsCts", "on", + "Local", "on", + "Read", "on", + NULL); if (data->chat == NULL) return -ENOMEM; - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(data->chat, samsung_debug, "Device: "); - g_at_chat_send(data->chat, "ATE0", NULL, NULL, NULL, NULL); g_at_chat_send(data->chat, "AT+CMEE=1", NULL, NULL, NULL, NULL); diff --git a/ofono/plugins/sierra.c b/ofono/plugins/sierra.c index 458fec2ff..0cc1f3a7d 100644 --- a/ofono/plugins/sierra.c +++ b/ofono/plugins/sierra.c @@ -94,34 +94,7 @@ static void sierra_remove(struct ofono_modem *modem) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, sierra_debug, debug); - - return chat; + return at_util_open_device(modem, key, sierra_debug, debug, NULL); } static void sim_state_cb(gboolean present, gpointer user_data) @@ -149,6 +122,8 @@ static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) if (!ok) { g_at_chat_unref(data->modem); data->modem = NULL; + ofono_modem_set_powered(modem, FALSE); + return; } data->sim_state_query = at_util_sim_state_query_new(data->modem, diff --git a/ofono/plugins/speedup.c b/ofono/plugins/speedup.c index d1ea35a12..0b5d22892 100644 --- a/ofono/plugins/speedup.c +++ b/ofono/plugins/speedup.c @@ -109,34 +109,7 @@ static void speedup_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, speedup_debug, debug); - - return chat; + return at_util_open_device(modem, key, speedup_debug, debug, NULL); } static void sim_state_cb(gboolean present, gpointer user_data) diff --git a/ofono/plugins/speedupcdma.c b/ofono/plugins/speedupcdma.c index 8e5f32431..779c37bd1 100644 --- a/ofono/plugins/speedupcdma.c +++ b/ofono/plugins/speedupcdma.c @@ -38,6 +38,7 @@ #include #include +#include "drivers/atmodem/atutil.h" #include "drivers/atmodem/vendor.h" struct speedupcdma_data { @@ -102,34 +103,7 @@ static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, speedupcdma_debug, debug); - - return chat; + return at_util_open_device(modem, key, speedupcdma_debug, debug, NULL); } static int speedupcdma_enable(struct ofono_modem *modem) diff --git a/ofono/plugins/telit.c b/ofono/plugins/telit.c index 1a3ade070..094d762f0 100644 --- a/ofono/plugins/telit.c +++ b/ofono/plugins/telit.c @@ -116,41 +116,9 @@ static void telit_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - GHashTable *options; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return NULL; - - g_hash_table_insert(options, "Baud", "115200"); - channel = g_at_tty_open(device, options); - g_hash_table_destroy(options); - - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, telit_debug, debug); - - return chat; + return at_util_open_device(modem, key, telit_debug, debug, + "Baud", "115200", + NULL); } static void switch_sim_state_status(struct ofono_modem *modem, int status) diff --git a/ofono/plugins/ublox.c b/ofono/plugins/ublox.c index 1ca0030a2..b65bc52a7 100644 --- a/ofono/plugins/ublox.c +++ b/ofono/plugins/ublox.c @@ -40,10 +40,16 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include #include - #include static const char *uusbconf_prefix[] = { "+UUSBCONF:", NULL }; @@ -99,34 +105,8 @@ static void ublox_remove(struct ofono_modem *modem) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - channel = g_at_tty_open(device, NULL); - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, ublox_debug, debug); - - return chat; + return at_util_open_device(modem, key, ublox_debug, debug, + NULL); } static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data) @@ -383,9 +363,9 @@ static void ublox_post_sim(struct ofono_modem *modem) struct ofono_gprs *gprs; struct ofono_gprs_context *gc; GAtChat *chat = data->modem ? data->modem : data->aux; + struct ofono_message_waiting *mw; const char *driver; - /* Toby L2: Create same number of contexts as supported PDP contexts. */ - int ncontexts = data->flags & UBLOX_DEVICE_F_HIGH_THROUGHPUT_MODE ? 8 : 1; + const char *iface; int variant; DBG("%p", modem); @@ -393,40 +373,41 @@ static void ublox_post_sim(struct ofono_modem *modem) gprs = ofono_gprs_create(modem, data->vendor_family, "atmodem", data->aux); - if (ublox_is_toby_l4(data->model)) { + iface = ofono_modem_get_string(modem, "NetworkInterface"); + if (iface) { driver = "ubloxmodem"; variant = ublox_model_to_id(data->model); - } else if (ublox_is_toby_l2(data->model)) { - if (data->flags & UBLOX_DEVICE_F_HIGH_THROUGHPUT_MODE) { - driver = "ubloxmodem"; - variant = ublox_model_to_id(data->model); - } else { - driver = "atmodem"; - variant = OFONO_VENDOR_UBLOX; - } } else { driver = "atmodem"; variant = OFONO_VENDOR_UBLOX; } - while (ncontexts) { - gc = ofono_gprs_context_create(modem, variant, driver, chat); - - if (gprs && gc) - ofono_gprs_add_context(gprs, gc); - - --ncontexts; - } + gc = ofono_gprs_context_create(modem, variant, driver, chat); + if (gprs && gc) + ofono_gprs_add_context(gprs, gc); ofono_lte_create(modem, ublox_model_to_id(data->model), "ubloxmodem", data->aux); + + ofono_sms_create(modem, 0, "atmodem", data->aux); + + ofono_ussd_create(modem, 0, "atmodem", data->aux); + ofono_call_forwarding_create(modem, 0, "atmodem", data->aux); + ofono_call_settings_create(modem, 0, "atmodem", data->aux); + ofono_call_meter_create(modem, 0, "atmodem", data->aux); + ofono_call_barring_create(modem, 0, "atmodem", data->aux); + + mw = ofono_message_waiting_create(modem); + if (mw) + ofono_message_waiting_register(mw); } static void ublox_post_online(struct ofono_modem *modem) { struct ublox_data *data = ofono_modem_get_data(modem); - ofono_netreg_create(modem, data->vendor_family, "atmodem", data->aux); + ofono_netreg_create(modem, + ublox_model_to_id(data->model), "ubloxmodem", data->aux); ofono_netmon_create(modem, data->vendor_family, "ubloxmodem", data->aux); } diff --git a/ofono/plugins/udevng.c b/ofono/plugins/udevng.c index 1c0fb2bd1..928881da7 100644 --- a/ofono/plugins/udevng.c +++ b/ofono/plugins/udevng.c @@ -837,7 +837,7 @@ static gboolean setup_samsung(struct modem_info *modem) return TRUE; } -static gboolean setup_quectel(struct modem_info *modem) +static gboolean setup_quectel_usb(struct modem_info *modem) { const char *aux = NULL, *mdm = NULL; GSList *list; @@ -877,6 +877,37 @@ static gboolean setup_quectel(struct modem_info *modem) return TRUE; } +static gboolean setup_quectel_serial(struct modem_info *modem) +{ + struct serial_device_info *info = modem->serial; + const char *value; + + value = udev_device_get_property_value(info->dev, + "OFONO_QUECTEL_GPIO_CHIP"); + if (value) + ofono_modem_set_string(modem->modem, "GpioChip", value); + + value = udev_device_get_property_value(info->dev, + "OFONO_QUECTEL_GPIO_OFFSET"); + if (value) + ofono_modem_set_string(modem->modem, "GpioOffset", value); + + value = udev_device_get_property_value(info->dev, + "OFONO_QUECTEL_RTSCTS"); + ofono_modem_set_string(modem->modem, "RtsCts", value ? value : "off"); + ofono_modem_set_string(modem->modem, "Device", info->devnode); + + return TRUE; +} + +static gboolean setup_quectel(struct modem_info *modem) +{ + if (modem->serial) + return setup_quectel_serial(modem); + else + return setup_quectel_usb(modem); +} + static gboolean setup_quectelqmi(struct modem_info *modem) { const char *qmi = NULL, *net = NULL, *gps = NULL, *aux = NULL; @@ -1240,9 +1271,11 @@ static gboolean setup_xmm7xxx(struct modem_info *modem) return TRUE; } -static gboolean setup_sim7100(struct modem_info *modem) +static gboolean setup_sim7x00(struct modem_info *modem) { - const char *at = NULL, *ppp = NULL, *gps = NULL, *diag = NULL, *audio = NULL; + const char *audio = NULL, *diag = NULL, *gps = NULL; + const char *mdm = NULL, *net = NULL, *ppp = NULL; + const char *qmi = NULL; GSList *list; DBG("%s", modem->syspath); @@ -1250,10 +1283,12 @@ static gboolean setup_sim7100(struct modem_info *modem) for (list = modem->devices; list; list = list->next) { struct device_info *info = list->data; - DBG("%s %s", info->devnode, info->number); + DBG("%s %s %s %s %s %s", info->devnode, info->interface, + info->number, info->label, + info->sysattr, info->subsystem); /* - * Serial port layout: + * SIM7100 serial port layout: * 0: QCDM/DIAG * 1: NMEA * 2: AT @@ -1262,29 +1297,52 @@ static gboolean setup_sim7100(struct modem_info *modem) * * -- https://www.spinics.net/lists/linux-usb/msg135728.html */ - if (g_strcmp0(info->number, "00") == 0) - diag = info->devnode; - else if (g_strcmp0(info->number, "01") == 0) - gps = info->devnode; - else if (g_strcmp0(info->number, "02") == 0) - at = info->devnode; - else if (g_strcmp0(info->number, "03") == 0) - ppp = info->devnode; - else if (g_strcmp0(info->number, "04") == 0) - audio = info->devnode; + if (g_strcmp0(info->subsystem, "usbmisc") == 0) /* cdc-wdm */ + qmi = info->devnode; /* SIM7600 */ + else if (g_strcmp0(info->subsystem, "net") == 0) /* wwan */ + net = info->devnode; /* SIM7600 */ + else if (g_strcmp0(info->subsystem, "tty") == 0) { + if (g_strcmp0(info->interface, "255/255/255") == 0) { + if (g_strcmp0(info->number, "00") == 0) + diag = info->devnode; /* SIM7x00 */ + } else if (g_strcmp0(info->interface, "255/0/0") == 0) { + if (g_strcmp0(info->number, "01") == 0) + gps = info->devnode; /* SIM7x00 */ + else if (g_strcmp0(info->number, "02") == 0) + mdm = info->devnode; /* SIM7x00 */ + else if (g_strcmp0(info->number, "03") == 0) + ppp = info->devnode; /* SIM7100 */ + else if (g_strcmp0(info->number, "04") == 0) + audio = info->devnode; /* SIM7100 */ + } + } } - if (at == NULL) + if (mdm == NULL) return FALSE; - DBG("at=%s ppp=%s gps=%s diag=%s, audio=%s", at, ppp, gps, diag, audio); + if (qmi != NULL && net != NULL) { + DBG("qmi=%s net=%s mdm=%s gps=%s diag=%s", + qmi, net, mdm, gps, diag); + + ofono_modem_set_driver(modem->modem, "gobi"); + + ofono_modem_set_string(modem->modem, "Device", qmi); + ofono_modem_set_string(modem->modem, "Modem", mdm); + ofono_modem_set_string(modem->modem, "NetworkInterface", net); + } else { + DBG("at=%s ppp=%s gps=%s diag=%s, audio=%s", + mdm, ppp, gps, diag, audio); + + ofono_modem_set_driver(modem->modem, "sim7100"); + + ofono_modem_set_string(modem->modem, "AT", mdm); + ofono_modem_set_string(modem->modem, "PPP", ppp); + ofono_modem_set_string(modem->modem, "Audio", audio); + } - ofono_modem_set_string(modem->modem, "AT", at); - ofono_modem_set_string(modem->modem, "PPP", ppp); ofono_modem_set_string(modem->modem, "GPS", gps); ofono_modem_set_string(modem->modem, "Diag", diag); - ofono_modem_set_string(modem->modem, "Audio", audio); - return TRUE; } @@ -1308,7 +1366,7 @@ static struct { { "telit", setup_telit, "device/interface" }, { "telitqmi", setup_telitqmi }, { "simcom", setup_simcom }, - { "sim7100", setup_sim7100 }, + { "sim7x00", setup_sim7x00 }, { "zte", setup_zte }, { "icera", setup_icera }, { "samsung", setup_samsung }, @@ -1681,7 +1739,8 @@ static struct { { "novatel", "option", "1410" }, { "zte", "option", "19d2" }, { "simcom", "option", "05c6", "9000" }, - { "sim7100", "option", "1e0e", "9001" }, + { "sim7x00", "option", "1e0e", "9001" }, + { "sim7x00", "qmi_wwan", "1e0e", "9001" }, { "telit", "usbserial", "1bc7" }, { "telit", "option", "1bc7" }, { "telit", "cdc_acm", "1bc7", "0021" }, @@ -1699,6 +1758,8 @@ static struct { { "ublox", "cdc_acm", "1546", "1010" }, { "ublox", "cdc_ncm", "1546", "1010" }, { "ublox", "cdc_acm", "1546", "1102" }, + { "ublox", "cdc_acm", "1546", "110a" }, + { "ublox", "cdc_ncm", "1546", "110a" }, { "ublox", "rndis_host", "1546", "1146" }, { "ublox", "cdc_acm", "1546", "1146" }, { "gemalto", "option", "1e2d", "0053" }, diff --git a/ofono/plugins/wavecom.c b/ofono/plugins/wavecom.c index 7f24eae91..03830bf51 100644 --- a/ofono/plugins/wavecom.c +++ b/ofono/plugins/wavecom.c @@ -48,6 +48,7 @@ #include #include +#include #include @@ -70,52 +71,19 @@ static void wavecom_debug(const char *str, void *user_data) static int wavecom_enable(struct ofono_modem *modem) { GAtChat *chat; - GIOChannel *channel; - GAtSyntax *syntax; - const char *device; - GHashTable *options; DBG("%p", modem); - device = ofono_modem_get_string(modem, "Device"); - if (device == NULL) + chat = at_util_open_device(modem, "Device", wavecom_debug, "", + "Baud", "115200", + "Parity", "none", + "StopBits", "1", + "DataBits", "8", + NULL); + if (!chat) return -EINVAL; - options = g_hash_table_new(g_str_hash, g_str_equal); - - if (options == NULL) - return -ENOMEM; - - g_hash_table_insert(options, "Baud", "115200"); - g_hash_table_insert(options, "Parity", "none"); - g_hash_table_insert(options, "StopBits", "1"); - g_hash_table_insert(options, "DataBits", "8"); - - channel = g_at_tty_open(device, options); - - g_hash_table_destroy(options); - - if (channel == NULL) - return -EIO; - - /* - * Could not figure out whether it is fully compliant or not, use - * permissive for now - * */ - syntax = g_at_syntax_new_gsm_permissive(); - - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - g_io_channel_unref(channel); - - if (chat == NULL) - return -ENOMEM; - g_at_chat_add_terminator(chat, "+CPIN:", 6, TRUE); - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, wavecom_debug, ""); - ofono_modem_set_data(modem, chat); return 0; diff --git a/ofono/plugins/xmm7xxx.c b/ofono/plugins/xmm7xxx.c index 23a7d4e41..a544798aa 100644 --- a/ofono/plugins/xmm7xxx.c +++ b/ofono/plugins/xmm7xxx.c @@ -965,41 +965,9 @@ static void xmm7xxx_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GAtSyntax *syntax; - GIOChannel *channel; - GAtChat *chat; - GHashTable *options; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return NULL; - - g_hash_table_insert(options, "Baud", "115200"); - channel = g_at_tty_open(device, options); - g_hash_table_destroy(options); - - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, xmm7xxx_debug, debug); - - return chat; + return at_util_open_device(modem, key, xmm7xxx_debug, debug, + "Baud", "115200", + NULL); } static void switch_sim_state_status(struct ofono_modem *modem, int status) diff --git a/ofono/plugins/zte.c b/ofono/plugins/zte.c index 53beefe08..b6093af97 100644 --- a/ofono/plugins/zte.c +++ b/ofono/plugins/zte.c @@ -99,51 +99,16 @@ static void zte_debug(const char *str, void *user_data) static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { - const char *device; - GIOChannel *channel; - GAtSyntax *syntax; - GAtChat *chat; - GHashTable *options; - - device = ofono_modem_get_string(modem, key); - if (device == NULL) - return NULL; - - DBG("%s %s", key, device); - - options = g_hash_table_new(g_str_hash, g_str_equal); - if (options == NULL) - return NULL; - - g_hash_table_insert(options, "Baud", "115200"); - g_hash_table_insert(options, "Parity", "none"); - g_hash_table_insert(options, "StopBits", "1"); - g_hash_table_insert(options, "DataBits", "8"); - g_hash_table_insert(options, "XonXoff", "off"); - g_hash_table_insert(options, "RtsCts", "on"); - g_hash_table_insert(options, "Local", "on"); - g_hash_table_insert(options, "Read", "on"); - - channel = g_at_tty_open(device, options); - - g_hash_table_destroy(options); - - if (channel == NULL) - return NULL; - - syntax = g_at_syntax_new_gsm_permissive(); - chat = g_at_chat_new(channel, syntax); - g_at_syntax_unref(syntax); - - g_io_channel_unref(channel); - - if (chat == NULL) - return NULL; - - if (getenv("OFONO_AT_DEBUG")) - g_at_chat_set_debug(chat, zte_debug, debug); - - return chat; + return at_util_open_device(modem, key, zte_debug, debug, + "Baud", "115200", + "Parity", "none", + "StopBits", "1", + "DataBits", "8", + "XonXoff", "off", + "RtsCts", "on", + "Local", "on", + "Read", "on", + NULL); } static void zoprt_enable(gboolean ok, GAtResult *result, gpointer user_data) diff --git a/ofono/src/gprs.c b/ofono/src/gprs.c index 44099f59b..680e84f76 100644 --- a/ofono/src/gprs.c +++ b/ofono/src/gprs.c @@ -305,6 +305,8 @@ static gboolean assign_context(struct pri_context *ctx, int use_cid) return TRUE; } + ctx->context.cid = 0; + return FALSE; } @@ -820,8 +822,7 @@ static void pri_update_mms_context_settings(struct pri_context *ctx) struct ofono_gprs_context *gc = ctx->context_driver; struct context_settings *settings = gc->settings; - if (ctx->message_proxy) - settings->ipv4->proxy = g_strdup(ctx->message_proxy); + settings->ipv4->proxy = g_strdup(ctx->message_proxy); if (!pri_parse_proxy(ctx, ctx->message_proxy)) pri_parse_proxy(ctx, ctx->message_center); @@ -1459,7 +1460,7 @@ static DBusMessage *pri_set_message_proxy(struct pri_context *ctx, if (strlen(proxy) > MAX_MESSAGE_PROXY_LENGTH) return __ofono_error_invalid_format(msg); - if (ctx->message_proxy && g_str_equal(ctx->message_proxy, proxy)) + if (g_str_equal(ctx->message_proxy, proxy)) return dbus_message_new_method_return(msg); strcpy(ctx->message_proxy, proxy); @@ -1488,7 +1489,7 @@ static DBusMessage *pri_set_message_center(struct pri_context *ctx, if (strlen(center) > MAX_MESSAGE_CENTER_LENGTH) return __ofono_error_invalid_format(msg); - if (ctx->message_center && g_str_equal(ctx->message_center, center)) + if (g_str_equal(ctx->message_center, center)) return dbus_message_new_method_return(msg); strcpy(ctx->message_center, center); @@ -2077,6 +2078,15 @@ static void gprs_netreg_update(struct ofono_gprs *gprs) { ofono_bool_t attach; + /* + * This function can get called by other reasons than netreg + * updating its status. So check if we have a valid netreg status yet. + * The only reason for not having a valid status is basically during + * startup while the netreg atom is fetching the status. + */ + if (gprs->netreg_status < 0) + return; + attach = gprs->netreg_status == NETWORK_REGISTRATION_STATUS_REGISTERED; attach = attach || (gprs->roaming_allowed && @@ -2121,7 +2131,7 @@ static void netreg_status_changed(int status, int lac, int ci, int tech, { struct ofono_gprs *gprs = data; - DBG("%d", status); + DBG("%d (%s)", status, registration_status_to_string(status)); gprs->netreg_status = status; @@ -2224,7 +2234,7 @@ static DBusMessage *gprs_set_property(DBusConnection *conn, gprs->roaming_allowed = value; if (gprs->settings) { - g_key_file_set_integer(gprs->settings, SETTINGS_GROUP, + g_key_file_set_boolean(gprs->settings, SETTINGS_GROUP, "RoamingAllowed", gprs->roaming_allowed); storage_sync(gprs->imsi, SETTINGS_STORE, @@ -2321,7 +2331,7 @@ static struct pri_context *find_usable_context(struct ofono_gprs *gprs, for (l = gprs->contexts; l; l = l->next) { pri_ctx = l->data; - if (pri_ctx->context.apn == NULL) + if (pri_ctx->context.apn[0] == '\0') return pri_ctx; } @@ -3629,7 +3639,7 @@ struct ofono_gprs *ofono_gprs_create(struct ofono_modem *modem, } gprs->status = NETWORK_REGISTRATION_STATUS_UNKNOWN; - gprs->netreg_status = NETWORK_REGISTRATION_STATUS_UNKNOWN; + gprs->netreg_status = -1; gprs->pid_map = idmap_new(MAX_CONTEXTS); gprs->filters = __ofono_gprs_filter_chain_new(gprs); @@ -3641,6 +3651,7 @@ static void netreg_watch(struct ofono_atom *atom, void *data) { struct ofono_gprs *gprs = data; + int status; if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { gprs_netreg_removed(gprs); @@ -3648,7 +3659,16 @@ static void netreg_watch(struct ofono_atom *atom, } gprs->netreg = __ofono_atom_get_data(atom); - gprs->netreg_status = ofono_netreg_get_status(gprs->netreg); + status = ofono_netreg_get_status(gprs->netreg); + + /* + * If the status is known, assign it, otherwise keep the init value + * to indicate that the netreg atom is not initialised with a known + * value + */ + if (status != NETWORK_REGISTRATION_STATUS_UNKNOWN) + gprs->netreg_status = status; + gprs->status_watch = __ofono_netreg_add_status_watch(gprs->netreg, netreg_status_changed, gprs, NULL); diff --git a/ofono/src/handsfree.c b/ofono/src/handsfree.c index 921e0bd7e..aea5fa5f4 100644 --- a/ofono/src/handsfree.c +++ b/ofono/src/handsfree.c @@ -174,7 +174,7 @@ void ofono_handsfree_battchg_notify(struct ofono_handsfree *hf, unsigned char level) { DBusConnection *conn = ofono_dbus_get_connection(); - const char *path = __ofono_atom_get_path(hf->atom); + const char *path; if (hf == NULL) return; @@ -187,6 +187,7 @@ void ofono_handsfree_battchg_notify(struct ofono_handsfree *hf, if (__ofono_atom_get_registered(hf->atom) == FALSE) return; + path = __ofono_atom_get_path(hf->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_HANDSFREE_INTERFACE, "BatteryChargeLevel", DBUS_TYPE_BYTE, @@ -295,7 +296,7 @@ static void hf_cnum_callback(const struct ofono_error *error, int total, subscriber_number->type = numbers[num].type; strncpy(subscriber_number->number, numbers[num].number, - OFONO_MAX_PHONE_NUMBER_LENGTH + 1); + OFONO_MAX_PHONE_NUMBER_LENGTH); hf->subscriber_numbers = g_slist_prepend(hf->subscriber_numbers, subscriber_number); diff --git a/ofono/src/log.c b/ofono/src/log.c index 999b2d88b..a58472114 100644 --- a/ofono/src/log.c +++ b/ofono/src/log.c @@ -238,7 +238,7 @@ static void print_backtrace(unsigned int offset) if (written < 0) break; - len = read(infd[0], buf, sizeof(buf)); + len = read(infd[0], buf, sizeof(buf) - 1); if (len < 0) break; diff --git a/ofono/src/main.c b/ofono/src/main.c index 5bca6d582..dbb0a13bb 100644 --- a/ofono/src/main.c +++ b/ofono/src/main.c @@ -249,6 +249,7 @@ int main(int argc, char **argv) #ifdef HAVE_ELL l_log_set_stderr(); + l_debug(""); l_debug_enable("*"); l_main_init(); diff --git a/ofono/src/message-waiting.c b/ofono/src/message-waiting.c index a356e9016..6115d495f 100644 --- a/ofono/src/message-waiting.c +++ b/ofono/src/message-waiting.c @@ -753,8 +753,8 @@ static void mw_set_indicator(struct ofono_message_waiting *mw, int profile, efmwis[0] = mw->messages[0].indication ? 0xa : 0x5; if (mw->ef_cphs_mwis_length > 1) - efmwis[1] = mw->messages[1].indication ? 0xa : 0x5 | - mw->messages[3].indication ? 0xa0 : 0x50; + efmwis[1] = (mw->messages[1].indication ? 0xa : 0x5) | + (mw->messages[3].indication ? 0xa0 : 0x50); if (ofono_sim_write(mw->sim_context, SIM_EF_CPHS_MWIS_FILEID, mw_mwis_write_cb, diff --git a/ofono/src/netmon.c b/ofono/src/netmon.c index 62e0ec0b8..9eacb3ca5 100644 --- a/ofono/src/netmon.c +++ b/ofono/src/netmon.c @@ -50,6 +50,8 @@ struct ofono_netmon { const struct ofono_netmon_driver *driver; DBusMessage *pending; DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter arr; void *driver_data; struct ofono_atom *atom; struct netmon_agent *agent; @@ -69,174 +71,145 @@ static const char *cell_type_to_tech_name(enum ofono_netmon_cell_type type) return NULL; } -void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon, - enum ofono_netmon_cell_type type, - int info_type, ...) +static void netmon_cell_info_dict_append(DBusMessageIter *dict, + va_list *arglist, int info_type) { - va_list arglist; - DBusMessage *agent_notify = NULL; - DBusMessageIter iter; - DBusMessageIter dict; - enum ofono_netmon_info next_info_type = info_type; - const char *technology = cell_type_to_tech_name(type); char *mcc; char *mnc; int intval; - - if (netmon->pending != NULL) { - netmon->reply = dbus_message_new_method_return(netmon->pending); - dbus_message_iter_init_append(netmon->reply, &iter); - } else if (netmon->agent != NULL) { - agent_notify = netmon_agent_new_method_call(netmon->agent, - "ServingCellInformationChanged"); - - dbus_message_iter_init_append(agent_notify, &iter); - } else - return; - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - OFONO_PROPERTIES_ARRAY_SIGNATURE, - &dict); - - va_start(arglist, info_type); - - if (technology == NULL) - goto done; - - ofono_dbus_dict_append(&dict, "Technology", - DBUS_TYPE_STRING, &technology); + enum ofono_netmon_info next_info_type = info_type; while (next_info_type != OFONO_NETMON_INFO_INVALID) { switch (next_info_type) { case OFONO_NETMON_INFO_MCC: - mcc = va_arg(arglist, char *); + mcc = va_arg(*arglist, char *); if (mcc && strlen(mcc)) - ofono_dbus_dict_append(&dict, + ofono_dbus_dict_append(dict, "MobileCountryCode", DBUS_TYPE_STRING, &mcc); break; case OFONO_NETMON_INFO_MNC: - mnc = va_arg(arglist, char *); + mnc = va_arg(*arglist, char *); if (mnc && strlen(mnc)) - ofono_dbus_dict_append(&dict, + ofono_dbus_dict_append(dict, "MobileNetworkCode", DBUS_TYPE_STRING, &mnc); break; case OFONO_NETMON_INFO_LAC: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "LocationAreaCode", + CELL_INFO_DICT_APPEND(dict, "LocationAreaCode", intval, uint16_t, DBUS_TYPE_UINT16); break; case OFONO_NETMON_INFO_CI: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "CellId", + CELL_INFO_DICT_APPEND(dict, "CellId", intval, uint32_t, DBUS_TYPE_UINT32); break; case OFONO_NETMON_INFO_ARFCN: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "ARFCN", + CELL_INFO_DICT_APPEND(dict, "ARFCN", intval, uint16_t, DBUS_TYPE_UINT16); break; case OFONO_NETMON_INFO_BSIC: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "BSIC", + CELL_INFO_DICT_APPEND(dict, "BSIC", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_RXLEV: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "ReceivedSignalStrength", + CELL_INFO_DICT_APPEND(dict, "ReceivedSignalStrength", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_TIMING_ADVANCE: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "TimingAdvance", + CELL_INFO_DICT_APPEND(dict, "TimingAdvance", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_PSC: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "PrimaryScramblingCode", + CELL_INFO_DICT_APPEND(dict, "PrimaryScramblingCode", intval, uint16_t, DBUS_TYPE_UINT16); break; case OFONO_NETMON_INFO_BER: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "BitErrorRate", + CELL_INFO_DICT_APPEND(dict, "BitErrorRate", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_RSSI: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "Strength", + CELL_INFO_DICT_APPEND(dict, "Strength", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_RSCP: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "ReceivedSignalCodePower", + CELL_INFO_DICT_APPEND(dict, "ReceivedSignalCodePower", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_ECN0: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "ReceivedEnergyRatio", + CELL_INFO_DICT_APPEND(dict, "ReceivedEnergyRatio", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_RSRQ: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, + CELL_INFO_DICT_APPEND(dict, "ReferenceSignalReceivedQuality", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_RSRP: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, + CELL_INFO_DICT_APPEND(dict, "ReferenceSignalReceivedPower", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_EARFCN: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "EARFCN", + CELL_INFO_DICT_APPEND(dict, "EARFCN", intval, uint16_t, DBUS_TYPE_UINT16); break; case OFONO_NETMON_INFO_EBAND: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "EBand", + CELL_INFO_DICT_APPEND(dict, "EBand", intval, uint8_t, DBUS_TYPE_BYTE); break; case OFONO_NETMON_INFO_CQI: - intval = va_arg(arglist, int); + intval = va_arg(*arglist, int); - CELL_INFO_DICT_APPEND(&dict, "ChannelQualityIndicator", + CELL_INFO_DICT_APPEND(dict, "ChannelQualityIndicator", intval, uint8_t, DBUS_TYPE_BYTE); break; @@ -244,8 +217,44 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon, break; } - next_info_type = va_arg(arglist, int); + next_info_type = va_arg(*arglist, int); } +} + +void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon, + enum ofono_netmon_cell_type type, + int info_type, ...) +{ + va_list arglist; + DBusMessage *agent_notify = NULL; + DBusMessageIter iter; + DBusMessageIter dict; + const char *technology = cell_type_to_tech_name(type); + + if (netmon->pending != NULL) { + netmon->reply = dbus_message_new_method_return(netmon->pending); + dbus_message_iter_init_append(netmon->reply, &iter); + } else if (netmon->agent != NULL) { + agent_notify = netmon_agent_new_method_call(netmon->agent, + "ServingCellInformationChanged"); + + dbus_message_iter_init_append(agent_notify, &iter); + } else + return; + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + OFONO_PROPERTIES_ARRAY_SIGNATURE, + &dict); + + va_start(arglist, info_type); + + if (technology == NULL) + goto done; + + ofono_dbus_dict_append(&dict, "Technology", + DBUS_TYPE_STRING, &technology); + + netmon_cell_info_dict_append(&dict, &arglist, info_type); done: va_end(arglist); @@ -288,7 +297,7 @@ static DBusMessage *netmon_get_serving_cell_info(DBusConnection *conn, { struct ofono_netmon *netmon = data; - if (!netmon->driver && !netmon->driver->request_update) + if (!netmon->driver->request_update) return __ofono_error_not_implemented(msg); if (netmon->pending) @@ -403,6 +412,109 @@ static DBusMessage *netmon_unregister_agent(DBusConnection *conn, return dbus_message_new_method_return(msg); } + +void ofono_netmon_neighbouring_cell_notify(struct ofono_netmon *netmon, + enum ofono_netmon_cell_type type, + int info_type, ...) +{ + va_list arglist; + DBusMessageIter dict; + DBusMessageIter strct; + const char *tech = cell_type_to_tech_name(type); + + if (netmon->pending == NULL) + return; + + if (!netmon->reply) { + netmon->reply = dbus_message_new_method_return(netmon->pending); + dbus_message_iter_init_append(netmon->reply, &netmon->iter); + + dbus_message_iter_open_container(&netmon->iter, DBUS_TYPE_ARRAY, + DBUS_STRUCT_BEGIN_CHAR_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + DBUS_STRUCT_END_CHAR_AS_STRING, + &netmon->arr); + } + + tech = cell_type_to_tech_name(type); + + dbus_message_iter_open_container(&netmon->arr, DBUS_TYPE_STRUCT, + NULL, &strct); + dbus_message_iter_open_container(&strct, DBUS_TYPE_ARRAY, + OFONO_PROPERTIES_ARRAY_SIGNATURE, + &dict); + + va_start(arglist, info_type); + + if (tech == NULL) + goto done; + + ofono_dbus_dict_append(&dict, "Technology", + DBUS_TYPE_STRING, &tech); + + netmon_cell_info_dict_append(&dict, &arglist, info_type); + +done: + va_end(arglist); + + dbus_message_iter_close_container(&strct, &dict); + dbus_message_iter_close_container(&netmon->arr, &strct); +} + +static void neighbouring_cell_info_callback(const struct ofono_error *error, + void *data) +{ + struct ofono_netmon *netmon = data; + DBusMessage *reply = netmon->reply; + + DBG(""); + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + if (reply) + dbus_message_unref(reply); + + reply = __ofono_error_failed(netmon->pending); + } else if (!reply) { + DBusMessageIter iter; + DBusMessageIter dict; + + reply = dbus_message_new_method_return(netmon->pending); + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + OFONO_PROPERTIES_ARRAY_SIGNATURE, + &dict); + dbus_message_iter_close_container(&iter, &dict); + } else { + dbus_message_iter_close_container(&netmon->iter, &netmon->arr); + } + + netmon->reply = NULL; + __ofono_dbus_pending_reply(&netmon->pending, reply); +} + +static DBusMessage *netmon_get_neighbouring_cell_info(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_netmon *netmon = data; + + if (!netmon->driver->neighbouring_cell_update) + return __ofono_error_not_implemented(msg); + + if (netmon->pending) + return __ofono_error_busy(msg); + + netmon->pending = dbus_message_ref(msg); + + netmon->driver->neighbouring_cell_update(netmon, + neighbouring_cell_info_callback, netmon); + + return NULL; +} + static const GDBusMethodTable netmon_methods[] = { { GDBUS_ASYNC_METHOD("GetServingCellInformation", NULL, GDBUS_ARGS({ "cellinfo", "a{sv}" }), @@ -413,6 +525,9 @@ static const GDBusMethodTable netmon_methods[] = { { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "agent", "o" }), NULL, netmon_unregister_agent) }, + { GDBUS_ASYNC_METHOD("GetNeighbouringCellInformation", + NULL, GDBUS_ARGS({ "cellinfo", "a(a{sv})" }), + netmon_get_neighbouring_cell_info) }, { } }; diff --git a/ofono/src/network.c b/ofono/src/network.c index 2882e2d24..4d1775f60 100644 --- a/ofono/src/network.c +++ b/ofono/src/network.c @@ -1391,8 +1391,8 @@ void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status, if (netreg == NULL) return; - DBG("%s status %d tech %d", __ofono_atom_get_path(netreg->atom), - status, tech); + DBG("%s status %d tech %d lac %d ci %d", + __ofono_atom_get_path(netreg->atom), status, tech, lac, ci); if (netreg->status != status) { struct ofono_modem *modem; @@ -1447,6 +1447,11 @@ void ofono_netreg_time_notify(struct ofono_netreg *netreg, if (info == NULL) return; + DBG("net time %d-%02d-%02d %02d:%02d:%02d utcoff %d dst %d", + info->year, info->mon, info->mday, + info->hour, info->min, info->sec, + info->utcoff, info->dst); + __ofono_nettime_info_received(modem, info); } diff --git a/ofono/src/phonebook.c b/ofono/src/phonebook.c index 391b7d306..007cb76eb 100644 --- a/ofono/src/phonebook.c +++ b/ofono/src/phonebook.c @@ -179,7 +179,7 @@ static void vcard_printf_number(GString *vcards, const char *number, int type, if ((type == TYPE_INTERNATIONAL) && (number[0] != '+')) intl = "+"; - snprintf(buf, sizeof(buf), "TEL;TYPE=\%s%s:\%s\%s", pref, + snprintf(buf, sizeof(buf), "TEL;TYPE=%s%s:%s%s", pref, category_string, intl, number); vcard_printf(vcards, buf, number); } diff --git a/ofono/src/radio-settings.c b/ofono/src/radio-settings.c index 9063a8fa2..6d4169a5a 100644 --- a/ofono/src/radio-settings.c +++ b/ofono/src/radio-settings.c @@ -86,9 +86,15 @@ const char *ofono_radio_access_mode_to_string(enum ofono_radio_access_mode m) return "umts"; case OFONO_RADIO_ACCESS_MODE_LTE: return "lte"; - default: - return NULL; } + + if (m == (OFONO_RADIO_ACCESS_MODE_UMTS|OFONO_RADIO_ACCESS_MODE_GSM)) + return "umts,gsm"; + + if (m == (OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS)) + return "lte,umts"; + + return NULL; } #define radio_access_mode_from_string ofono_radio_access_mode_from_string @@ -110,6 +116,12 @@ ofono_bool_t ofono_radio_access_mode_from_string(const char *str, } else if (g_str_equal(str, "lte")) { *mode = OFONO_RADIO_ACCESS_MODE_LTE; return TRUE; + } else if (g_str_equal(str, "umts,gsm")) { + *mode = OFONO_RADIO_ACCESS_MODE_UMTS|OFONO_RADIO_ACCESS_MODE_GSM; + return TRUE; + } else if (g_str_equal(str, "lte,umts")) { + *mode = OFONO_RADIO_ACCESS_MODE_LTE|OFONO_RADIO_ACCESS_MODE_UMTS; + return TRUE; } return FALSE; diff --git a/ofono/src/sim-auth.c b/ofono/src/sim-auth.c index c23b44fba..269b38933 100644 --- a/ofono/src/sim-auth.c +++ b/ofono/src/sim-auth.c @@ -676,13 +676,13 @@ static char *build_nai(const char *imsi) char mnc[3]; char *nai; - strncpy(mcc, imsi, 3); + memcpy(mcc, imsi, 3); if (strlen(imsi) == 16) { - strncpy(mnc, imsi + 3, 3); + memcpy(mnc, imsi + 3, 3); } else { mnc[0] = '0'; - strncpy(mnc + 1, imsi + 3, 2); + memcpy(mnc + 1, imsi + 3, 2); } nai = g_strdup_printf("%s@ims.mnc%.3s.mcc%.3s.3gppnetwork.org", diff --git a/ofono/src/sim.c b/ofono/src/sim.c index f848b802d..a5d2535ba 100644 --- a/ofono/src/sim.c +++ b/ofono/src/sim.c @@ -2609,11 +2609,13 @@ struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim) struct ofono_sim_context *ofono_sim_context_create_isim( struct ofono_sim *sim) { - GSList *iter = sim->aid_sessions; + GSList *iter; if (sim == NULL || sim->simfs_isim == NULL) return NULL; + iter = sim->aid_sessions; + /* Find the AID */ while (iter) { struct ofono_sim_aid_session *session = iter->data; diff --git a/ofono/src/siri.c b/ofono/src/siri.c index 88888dcf9..82715d384 100644 --- a/ofono/src/siri.c +++ b/ofono/src/siri.c @@ -54,7 +54,7 @@ struct ofono_siri { void ofono_siri_set_status(struct ofono_siri *siri, int value) { DBusConnection *conn = ofono_dbus_get_connection(); - const char *path = __ofono_atom_get_path(siri->atom); + const char *path; dbus_bool_t siri_status; if (siri == NULL) @@ -70,6 +70,7 @@ void ofono_siri_set_status(struct ofono_siri *siri, int value) if (__ofono_atom_get_registered(siri->atom) == FALSE) return; + path = __ofono_atom_get_path(siri->atom); ofono_dbus_signal_property_changed(conn, path, OFONO_SIRI_INTERFACE, "Enabled", DBUS_TYPE_BOOLEAN, &siri_status); diff --git a/ofono/src/stkutil.c b/ofono/src/stkutil.c index da5ecd5ec..dfce9ec4f 100644 --- a/ofono/src/stkutil.c +++ b/ofono/src/stkutil.c @@ -5417,8 +5417,10 @@ static bool build_dataobj(struct stk_tlv_builder *tlv, const void *data = va_arg(args, const void *); bool cr = (flags & DATAOBJ_FLAG_CR) ? true : false; - if (!builder_func(tlv, data, cr)) + if (!builder_func(tlv, data, cr)) { + va_end(args); return false; + } builder_func = va_arg(args, dataobj_writer); } diff --git a/ofono/src/voicecall.c b/ofono/src/voicecall.c index afb76e417..93a3b4183 100644 --- a/ofono/src/voicecall.c +++ b/ofono/src/voicecall.c @@ -4028,7 +4028,7 @@ static void emulator_atd_cb(struct ofono_emulator *em, emulator_dial(em, vc, num); } else { - strncpy(number, str, len - 1); + memcpy(number, str, len - 1); number[len - 1] = '\0'; emulator_dial(em, vc, number); @@ -4461,6 +4461,9 @@ void __ofono_voicecall_tone_cancel(struct ofono_voicecall *vc, int id) if (entry->id == id) break; + if (!entry) + return; + tone_request_finish(vc, entry, 0, FALSE); /* diff --git a/ofono/test/process-context-settings b/ofono/test/process-context-settings index 1d30b30df..7ffb12b30 100755 --- a/ofono/test/process-context-settings +++ b/ofono/test/process-context-settings @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os +import sys import dbus bus = dbus.SystemBus() @@ -23,13 +24,14 @@ for path, properties in modems: if properties["Active"] == dbus.Boolean(0): continue - print("Configuring %s" % (path)) + print("Configuring %s" % (path), file=sys.stderr) settings = properties["Settings"] interface = settings["Interface"] if settings["Method"] == "dhcp": - print(" Run DHCP on interface %s" % (interface)) + print(" Run DHCP on interface %s" % (interface), + file=sys.stderr) else: address = settings["Address"] try: @@ -37,18 +39,22 @@ for path, properties in modems: except: gateway = "0.0.0.0"; - print(" Interface is %s" % (interface)) - print(" IP address is %s" % (address)) - print(" Gateway is %s" % (gateway)) + print(" Interface is %s" % (interface), + file=sys.stderr) + print(" IP address is %s" % (address), + file=sys.stderr) + print(" Gateway is %s" % (gateway), + file=sys.stderr) - cmd = "ifconfig " + interface + " " + address - cmd += " netmask 255.255.255.255" + cmd = "ip address add dev " + interface + " " + address + cmd += "/32" os.system(cmd); for i in settings["DomainNameServers"]: - print(" Nameserver is %s" % (i)) + print(" Nameserver is %s" % (i), + file=sys.stderr) - cmd = "route add -host " + i + cmd = "ip route add " + i cmd +=" dev " + interface os.system(cmd); - print('') + print('', file=sys.stderr)