Commit 075718e6 authored by Slava Monich's avatar Slava Monich

[ofono] SIM7100 support. JB#39679

parent d438dbc8
......@@ -232,6 +232,16 @@ builtin_sources += drivers/rilmodem/rilmodem.h \
endif
endif
if SIM7100
builtin_modules += sim7100
builtin_sources += drivers/sim7100/sim7100_devinfo.c \
drivers/sim7100/sim7100_modem.c \
drivers/sim7100/sim7100_plugin.c \
drivers/sim7100/sim7100_radio_settings.c \
drivers/sim7100/sim7100_syntax.c \
drivers/sim7100/sim7100_uicc.c
endif
if ISIMODEM
builtin_modules += isimodem
builtin_sources += $(gisi_sources) \
......
......@@ -177,6 +177,11 @@ AC_ARG_ENABLE(rilmodem, AC_HELP_STRING([--disable-rilmodem],
[enable_rilmodem=${enableval}])
AM_CONDITIONAL(RILMODEM, test "${enable_rilmodem}" != "no")
AC_ARG_ENABLE(sim7100, AC_HELP_STRING([--disable-sim7100],
[disable Sim7100 modem support]),
[enable_sim7100=${enableval}])
AM_CONDITIONAL(SIM7100, test "${enable_sim7100}" != "no")
AC_ARG_ENABLE(sailfish-rilmodem, AC_HELP_STRING([--enable-sailfish-rilmodem],
[enable Sailfish RIL modem]),
[enable_sailfish_rilmodem=${enableval}],
......
# Sample config file for sim7100 driver
[Settings]
StartTimeout=20000
[Options]
Baud=115200
Parity=none
StopBits=1
DataBits=8
XonXoff=off
Local=off
RtsCts=off
Read=on
[sim7100_0]
UsbDevice=3-1:1.2
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd.
*
* 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.
*/
#ifndef SIMTECH_CONSTANTS_H
#define SIMTECH_CONSTANTS_H
#define CME_ERROR_SIM_NOT_INSERTED (10) /* SIM not inserted */
#define CME_ERROR_SIM_BUSY (14) /* SIM busy */
#endif /* SIMTECH_CONSTANTS_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd.
*
* 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.
*/
#include "sim7100_driver.h"
#include <ofono/devinfo.h>
#include <ofono/log.h>
#include <gutil_log.h>
struct sim7100_devinfo {
struct ofono_devinfo *devinfo;
const struct sim7100_slot_pub *slot;
guint register_id;
};
#define DBG_(self,fmt,args...) DBG("%s " fmt, (self)->slot->name, ##args)
static struct sim7100_devinfo *sim7100_devinfo_data(struct ofono_devinfo *info)
{
return ofono_devinfo_get_data(info);
}
const struct sim7100_slot_pub *sim7100_devinfo_slot(struct ofono_devinfo *info)
{
return sim7100_devinfo_data(info)->slot;
}
static void sim7100_query(ofono_devinfo_query_cb_t cb, const char *info,
void *data)
{
struct ofono_error error;
memset(&error, 0, sizeof(error));
if (info) {
error.type = OFONO_ERROR_TYPE_NO_ERROR;
cb(&error, info, data);
} else {
error.type = OFONO_ERROR_TYPE_FAILURE;
cb(&error, NULL, data);
}
}
static gboolean sim7100_devinfo_register_cb(gpointer user_data)
{
struct sim7100_devinfo *self = user_data;
DBG_(self, "");
GASSERT(self->register_id);
self->register_id = 0;
ofono_devinfo_register(self->devinfo);
return G_SOURCE_REMOVE;
}
/* ofono_devinfo_driver callbacks */
static void sim7100_query_manufacturer(struct ofono_devinfo *devinfo,
ofono_devinfo_query_cb_t cb, void *data)
{
sim7100_query(cb, sim7100_devinfo_slot(devinfo)->manufacturer, data);
}
static void sim7100_query_model(struct ofono_devinfo *devinfo,
ofono_devinfo_query_cb_t cb, void *data)
{
sim7100_query(cb, sim7100_devinfo_slot(devinfo)->model, data);
}
static void sim7100_query_revision(struct ofono_devinfo *devinfo,
ofono_devinfo_query_cb_t cb, void *data)
{
sim7100_query(cb, sim7100_devinfo_slot(devinfo)->revision, data);
}
static void sim7100_query_serial(struct ofono_devinfo *devinfo,
ofono_devinfo_query_cb_t cb, void *data)
{
sim7100_query(cb, sim7100_devinfo_slot(devinfo)->imei, data);
}
static int sim7100_devinfo_probe(struct ofono_devinfo *devinfo,
unsigned int vendor, void *data)
{
const struct sim7100_slot_pub *slot = data;
struct sim7100_devinfo *self = g_new0(struct sim7100_devinfo, 1);
self->slot = slot;
self->devinfo = devinfo;
DBG_(self, "");
self->register_id = g_idle_add(sim7100_devinfo_register_cb, self);
ofono_devinfo_set_data(devinfo, self);
return 0;
}
static void sim7100_devinfo_remove(struct ofono_devinfo *devinfo)
{
struct sim7100_devinfo *self = ofono_devinfo_get_data(devinfo);
ofono_devinfo_set_data(devinfo, NULL);
if (self) {
DBG_(self, "");
if (self->register_id) {
g_source_remove(self->register_id);
}
g_free(self);
}
}
const struct ofono_devinfo_driver sim7100_devinfo_driver = {
.name = SIM7100_DRIVER,
.probe = sim7100_devinfo_probe,
.remove = sim7100_devinfo_remove,
.query_manufacturer = sim7100_query_manufacturer,
.query_model = sim7100_query_model,
.query_revision = sim7100_query_revision,
.query_serial = sim7100_query_serial
};
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd.
*
* 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.
*/
#ifndef SIM7100_DRIVER_H
#define SIM7100_DRIVER_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "sim7100_constants.h"
#include <ofono/types.h>
#include <glib.h>
#include <gatsyntax.h>
#include <gatchat.h>
#include <gatmux.h>
#include <string.h>
#include <errno.h>
#define SIM7100_DRIVER "sim7100"
struct ofono_modem;
#define FOREACH_DLC(dlc) \
dlc(SIM,sim) \
dlc(NETREG,netreg) \
dlc(SMS,sms) \
dlc(GPRS,gprs)
enum sim7100_dlc {
#define SIM7100_DLC_ID(X) SIM7100_DLC_##X
#define _SIM7100_DLC_ENUM(X,x) SIM7100_DLC_ID(X),
FOREACH_DLC(_SIM7100_DLC_ENUM)
SIM7100_DLC_COUNT
};
#define SIM7100_DLC_USSD SIM7100_DLC_SIM
#define SIM7100_DLC_RADIO_SETTINGS SIM7100_DLC_NETREG
struct sim7100_slot_pub {
const char *name;
const char *path;
const char *manufacturer;
const char *model;
const char *revision;
const char *imei;
};
typedef void (*sim7100_modem_cb_t)(struct ofono_modem *modem);
GAtChat *sim7100_dlc_new(GAtMux *mux, gboolean sim7100_syntax);
GAtSyntax *sim7100_syntax_new(void);
void sim7100_foreach_modem(sim7100_modem_cb_t cb);
void sim7100_modem_init(struct ofono_modem *modem,
GAtChat *dlc[/* SIM7100_DLC_COUNT */],
struct sim7100_slot_pub *slot);
extern const struct ofono_radio_settings_driver sim7100_radio_settings_driver;
extern const struct ofono_devinfo_driver sim7100_devinfo_driver;
extern const struct ofono_modem_driver sim7100_modem_driver;
#endif /* SIM7100_DRIVER_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017-2019 Jolla Ltd.
*
* 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.
*/
#include "sim7100_driver.h"
#include <ofono/watch.h>
#include <ofono/devinfo.h>
#include <ofono/modem.h>
#include <ofono/netreg.h>
#include <ofono/sim.h>
#include <ofono/sms.h>
#include <ofono/ussd.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/voicecall.h>
#include <ofono/call-volume.h>
#include <ofono/radio-settings.h>
#include <ofono/log.h>
#include <gutil_log.h>
#include <gatresult.h>
#include "drivers/atmodem/vendor.h"
#define ATMODEM_DRIVER "atmodem"
#ifdef REALLY_ENABLE_DISABLE_MODEM
#define SIM7100_DLC_MODEM SIM7100_DLC_SIM
enum sim7100_modem_request {
REQ_ENABLE,
REQ_DISABLE,
REQ_COUNT
};
#endif
struct sim7100_modem {
struct ofono_modem *modem;
struct ofono_watch *watch;
struct sim7100_slot_pub *slot;
guint simcard_not_available_id;
GAtChat *dlc[SIM7100_DLC_COUNT];
#ifdef REALLY_ENABLE_DISABLE_MODEM
guint req_id[REQ_COUNT];
#endif /* REALLY_ENABLE_DISABLE_MODEM */
};
#define DBG_(self,fmt,args...) \
DBG("%s " fmt, (self)->slot->name, ##args)
static struct sim7100_modem *sim7100_modem_data(struct ofono_modem *modem)
{
return ofono_modem_get_data(modem);
}
static void sim7100_modem_simcard_notify(GAtResult *result, gpointer user_data)
{
struct sim7100_modem *self = user_data;
struct ofono_sim *sim = self->watch->sim;
if (sim) {
GAtResultIter iter;
g_at_result_iter_init(&iter, result);
if (g_at_result_iter_next(&iter, "+SIMCARD:")) {
const char *line = g_at_result_iter_raw_line(&iter);
DBG("%s", line);
if (!g_strcmp0(line, "NOT AVAILABLE")) {
ofono_sim_inserted_notify(sim, FALSE);
}
}
}
}
void sim7100_modem_init(struct ofono_modem *modem,
GAtChat *dlc[/* SIM7100_DLC_COUNT */],
struct sim7100_slot_pub *slot)
{
guint i;
struct sim7100_modem *self = g_new0(struct sim7100_modem, 1);
self->modem = modem;
self->slot = slot;
self->watch = ofono_watch_new(slot->path);
ofono_modem_set_data(modem, self);
/* Create the DLCs */
for (i = 0; i < G_N_ELEMENTS(self->dlc); i++) {
self->dlc[i] = g_at_chat_ref(dlc[i]);
}
/* Register to receive SIM card removal notification */
g_at_chat_register(self->dlc[SIM7100_DLC_SIM], "+SIMCARD:",
sim7100_modem_simcard_notify, FALSE, self, NULL);
}
#ifdef REALLY_ENABLE_DISABLE_MODEM
static void sim7100_modem_enable_cb(gboolean ok, GAtResult *res,
gpointer data)
{
struct sim7100_modem *self = data;
DBG_(self, "%s", ok ? "ok" : g_at_result_final_response(res));
GASSERT(self->req_id[REQ_ENABLE]);
self->req_id[REQ_ENABLE] = 0;
ofono_modem_set_powered(self->modem, ok);
}
static void sim7100_modem_disable_cb(gboolean ok, GAtResult *res,
gpointer data)
{
struct sim7100_modem *self = data;
DBG_(self, "%s", ok ? "ok" : g_at_result_final_response(res));
GASSERT(self->req_id[REQ_DISABLE]);
self->req_id[REQ_DISABLE] = 0;
ofono_modem_set_powered(self->modem, FALSE);
}
#endif /* REALLY_ENABLE_DISABLE_MODEM */
/* ofono_modem_driver callbacks */
static int sim7100_modem_probe(struct ofono_modem *modem)
{
DBG_(sim7100_modem_data(modem), "");
return 0;
}
static int sim7100_modem_enable(struct ofono_modem *modem)
{
#ifdef REALLY_ENABLE_DISABLE_MODEM
struct sim7100_modem *self = sim7100_modem_data(modem);
GAtChat *modem_dlc = self->dlc[SIM7100_DLC_MODEM];
DBG_(self, "");
/* Enable full modem functionality */
g_at_chat_cancel(modem_dlc, self->req_id[REQ_ENABLE]);
self->req_id[REQ_ENABLE] = g_at_chat_send(modem_dlc, "AT+CFUN=1",
NULL, sim7100_modem_enable_cb, self, NULL);
return (-EINPROGRESS);
#else
DBG("");
return 0;
#endif /* REALLY_ENABLE_DISABLE_MODEM */
}
static int sim7100_modem_disable(struct ofono_modem *modem)
{
#ifdef REALLY_ENABLE_DISABLE_MODEM
struct sim7100_modem *self = sim7100_modem_data(modem);
GAtChat *modem_dlc = self->dlc[SIM7100_DLC_MODEM];
DBG_(self, "");
/* Switch to offline mode */
g_at_chat_cancel(modem_dlc, self->req_id[REQ_DISABLE]);
self->req_id[REQ_DISABLE] = g_at_chat_send(modem_dlc, "AT+CFUN=4",
NULL, sim7100_modem_disable_cb, self, NULL);
return (-EINPROGRESS);
#else
DBG("");
return 0;
#endif /* REALLY_ENABLE_DISABLE_MODEM */
}
static void sim7100_modem_pre_sim(struct ofono_modem *modem)
{
struct sim7100_modem *self = sim7100_modem_data(modem);
struct sim7100_slot_pub *slot = self->slot;
DBG_(self, "");
ofono_devinfo_create(modem, 0, SIM7100_DRIVER, slot);
ofono_sim_create(modem, OFONO_VENDOR_SIMCOM,
ATMODEM_DRIVER, self->dlc[SIM7100_DLC_SIM]);
}
static void sim7100_modem_post_sim(struct ofono_modem *modem)
{
struct sim7100_modem *self = sim7100_modem_data(modem);
GAtChat *gprs_dlc = self->dlc[SIM7100_DLC_GPRS];
struct ofono_gprs *gprs;
DBG_(self, "");
ofono_radio_settings_create(modem, OFONO_VENDOR_SIMCOM_SIM7100,
SIM7100_DRIVER, self->dlc[SIM7100_DLC_RADIO_SETTINGS]);
ofono_sms_create(modem, OFONO_VENDOR_SIMCOM_SIM7100,
ATMODEM_DRIVER, self->dlc[SIM7100_DLC_SMS]);
gprs = ofono_gprs_create(modem, OFONO_VENDOR_SIMCOM_SIM7100,
ATMODEM_DRIVER, gprs_dlc);
if (gprs) {
int i;
for (i = 0; i < 2; i++) {
struct ofono_gprs_context *gc =
ofono_gprs_context_create(modem,
OFONO_VENDOR_SIMCOM_SIM7100,
ATMODEM_DRIVER, gprs_dlc);
if (gc) {
ofono_gprs_add_context(gprs, gc);
}
}
}
}
static void sim7100_modem_post_online(struct ofono_modem *modem)
{
struct sim7100_modem *self = sim7100_modem_data(modem);
DBG_(self, "");
ofono_netreg_create(modem, OFONO_VENDOR_SIMCOM,
ATMODEM_DRIVER, self->dlc[SIM7100_DLC_NETREG]);
ofono_ussd_create(modem, OFONO_VENDOR_SIMCOM,
ATMODEM_DRIVER, self->dlc[SIM7100_DLC_USSD]);
}
static void sim7100_modem_remove(struct ofono_modem *modem)
{
struct sim7100_modem *self = sim7100_modem_data(modem);
DBG_(self, "");
ofono_modem_set_data(modem, NULL);
if (self) {
guint i;
#ifdef REALLY_ENABLE_DISABLE_MODEM
GAtChat *modem_dlc = self->dlc[SIM7100_DLC_MODEM];
if (modem_dlc) {
for (i = 0; i < G_N_ELEMENTS(self->req_id); i++) {
guint id = self->req_id[i];
if (id) {
g_at_chat_cancel(modem_dlc, id);
}
}
}
#endif /* REALLY_ENABLE_DISABLE_MODEM */
g_at_chat_cancel(self->dlc[SIM7100_DLC_SIM],
self->simcard_not_available_id);
for (i = 0; i < G_N_ELEMENTS(self->dlc); i++) {
g_at_chat_unref(self->dlc[i]);
}
ofono_watch_unref(self->watch);
g_free(self);
}
}
const struct ofono_modem_driver sim7100_modem_driver = {
.name = SIM7100_DRIVER,
.probe = sim7100_modem_probe,
.remove = sim7100_modem_remove,
.enable = sim7100_modem_enable,
.disable = sim7100_modem_disable,
.pre_sim = sim7100_modem_pre_sim,
.post_sim = sim7100_modem_post_sim,
.post_online = sim7100_modem_post_online
};
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
This diff is collapsed.
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017-2019 Jolla Ltd.
*
* 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.
*/
#include "sim7100_driver.h"
#include <ofono/modem.h>
#include <ofono/radio-settings.h>
#include <ofono/log.h>
#include "gatresult.h"
#include "drivers/atmodem/atutil.h"
struct sim7100_radio_settings {
struct ofono_radio_settings *rs;
GAtChat *chat;
int *modes;
};
struct sim7100_radio_settings_cbd {
struct sim7100_radio_settings *self;
union _ofono_radio_settings_cb {
ofono_radio_settings_rat_mode_set_cb_t rat_mode_set;
ofono_radio_settings_rat_mode_query_cb_t rat_mode_query;
gpointer ptr;
} cb;
gpointer data;
};
#define SIM7100_MODE_AUTO (2)
#define SIM7100_MODE_GSM (13)
#define SIM7100_MODE_WCDMA (14)
#define SIM7100_MODE_LTE (38)
#define SIM7100_MODE_GSM_WCDMA (19)
#define SIM7100_MODE_ANY_BUT_LTE (48)
static const char *cnmp_prefix[] = { "+CNMP:", NULL };
static struct sim7100_radio_settings_cbd *sim7100_radio_settings_cbd_new
(struct sim7100_radio_settings *self, void *cb, void *data)
{
struct sim7100_radio_settings_cbd *cbd =
g_new(struct sim7100_radio_settings_cbd, 1);
cbd->self = self;
cbd->cb.ptr = cb;
cbd->data = data;
return cbd;
}
static void sim7100_radio_settings_mode_append
(struct sim7100_radio_settings *self, int mode)
{
int n = 0;
int *new_modes;
if (self->modes) {
while (self->modes[n]) {
if (self->modes[n] == mode) {
/* Already have this one */
return;
}
n++;
}
}
DBG("%d", mode);
new_modes = g_new(int, n + 2);
new_modes[n + 1] = 0;
new_modes[n] = mode;
if (self->modes) {
memcpy(new_modes, self->modes, n * sizeof(int));
g_free(self->modes);
}
self->modes = new_modes;
}
static int sim7100_radio_settings_mode(enum ofono_radio_access_mode am)
{
if (am & OFONO_RADIO_ACCESS_MODE_LTE) {
return SIM7100_MODE_LTE;
} else if (am & OFONO_RADIO_ACCESS_MODE_UMTS) {
return SIM7100_MODE_GSM_WCDMA;
} else if (am & OFONO_RADIO_ACCESS_MODE_GSM) {
return SIM7100_MODE_GSM;
} else {
return SIM7100_MODE_AUTO;
}
}
static enum ofono_radio_access_mode sim7100_radio_settings_access_mode(int m)
{
/* Returns single bit */
switch (m) {
case SIM7100_MODE_GSM:
return OFONO_RADIO_ACCESS_MODE_GSM;
case SIM7100_MODE_WCDMA:
case SIM7100_MODE_GSM_WCDMA:
case SIM7100_MODE_ANY_BUT_LTE:
return OFONO_RADIO_ACCESS_MODE_UMTS;
case SIM7100_MODE_LTE:
return OFONO_RADIO_ACCESS_MODE_LTE;
default:
return 0;
}
}
static enum ofono_radio_access_mode sim7100_radio_settings_all_access_modes
(struct sim7100_radio_settings *self)
{
enum ofono_radio_access_mode mask = 0;
if (self->modes) {
const int* ptr = self->modes;
while (*ptr) {
mask |= sim7100_radio_settings_access_mode(*ptr++);
}
}
return mask;
}
static void sim7100_radio_settings_cnmp_query_cb(gboolean ok,
GAtResult *result, gpointer param)
{
struct sim7100_radio_settings_cbd *cbd = param;
ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb.rat_mode_query;
DBG("%s", ok ? "ok" : g_at_result_final_response(result));
if (ok) {