[RFC] HFP support into oFono and BlueZ
by Gustavo F. Padovan
Hi,
These patches implement the new API for the Audio Gateway in BlueZ. It
follows the last version of the HandsfreeGateway and HandsfreeAgent
Intefaces API.
The first two patches is for BlueZ and the other for oFono. You can
test it with using enable-modem and test-voicecall scripts into the
test dir of oFono.
Feel free to test it and send me your comments. We have some bugs yet.
The audio part is not working yet. We are going to work on pulseaudio
this week to get this done soon.
Regards,
--
Gustavo F. Padovan
ProFUSION embedded systems - http://profusion.mobi
8 years, 5 months
[RFC] [PATCH 0/2] HFP AG integration with PulseAudio
by João Paulo Rechi Vita
Hi all,
I'm trying to add support for the Handsfree Gateway role Gustavo just added
to BlueZ and oFono. The BlueZ patches can be found on [1] and [2] and the
oFono part was just merged upstream.
But when it comes to integrate them with Pulse, I'm getting a POLLHUP when
trying to write on the fd. Also, it seems different gateways have different
behaviours regarding when they connect the SCO link. Some phone connect
them just after the RFCOMM link (some Nokia phones), when there is no call
going on yet, and others just when a call is started (Android 1.5).
Also, right now the same property (State) is beeing used to refer when the
RFCOOM link is established (State=Connected) and when the SCO link is
established (State=Playing). Shouldn't this be handled by separate props?
And last but not least, is the new Media API intended to handle the audio
part of handsfree gateways too? If so, maybe we should use all this work
as a prototype for latter integration with the new API.
Any help on testing and getting this working together or comments on the
topic would be appreciated.
[1] http://www.spinics.net/lists/linux-bluetooth/msg04250.html
[2] http://www.spinics.net/lists/linux-bluetooth/msg04251.html
--
João Paulo Rechi Vita
http://jprvita.wordpress.com/
10 years, 11 months
RFC: API for Neighbouring Cell Info
by Bastian, Waldo
Hi all,
I would like to propose an API for exposing neighbouring cell info.
Since the exact information provided is rather modem specific my thinking is to use the following API as the generic framework and then the actual per-cell information attributes that a driver provides can be different from modem to modem with the constraint that _if_ a modem provides a certain attribute that is specified by this API definition the driver must format it as specified.
Your feedback is highly appreciated, especially from those who plan to use this information for location purposes.
Once there is agreement on the API we can discuss whether it makes sense to add some skeleton support in the oFono core or leave it to each individual modem plugin to implement the DBUS service in its entire.
Cheers,
Waldo
Neighbouring Cell Info hierarchy
=================================
Service org.ofono
Interface org.ofono.NeighbouringCellInfo
Object path [variable prefix]/{modem0,modem1,...}
Methods dict GetProperties()
Returns all neighbouring cell info properties. See the
properties section for available properties.
Possible Errors: [service].Error.InvalidArguments
void SetProperty(string name, variant value)
Changes the value of the specified property. Only
properties that are listed as read-write are
changeable. On success a PropertyChanged signal
will be emitted.
Possible Errors: [service].Error.InvalidArguments
[service].Error.DoesNotExist
Signals PropertyChanged(string property, variant value)
This signal indicates a changed value of the given
property.
Properties boolean Enabled [readwrite]
Boolean representing whether the interface provides
neighbouring cell information.
array{dict} Info [readonly, only when Enabled]
List of neighbouring cells with the following information for each cell:
string Type [readonly]
Describes the type of Cell.
The possible values are: "GSM serving cell",
"GSM neighbour cell",
"UMTS serving cell",
"UMTS neighbour cell",
"UMTS detected cell"
string MobileCountryCode [readonly]
Contains the Mobile Country Code (MCC).
string MobileNetworkCode [readonly]
Contains the Mobile Network Code (MNC)
uint16 LocationAreaCode [readonly]
Contains the location area code of the cell.
uint32 CellId [readonly]
Contains the network cell id.
byte RxLevel [readonly, GSM only]
Received signal strength as defined by [3GPP TS 45.008 s8.1.4] [0-63]
uint32 TimingAdvance [readonly, GSM serving cell only]
Timing advance for serving GSM cell [0-???]
uint16 ScramblingCode [readonly, UMTS only]
Scrambling code [0-511]
uint16 DownlinkFreq [readonly, UMTS only]
Downlink frequency [0-16383]
int8 RSCP [readonly, UMTS only]
Received Signal Code Power [3GPP TS 25.133 s9.1.1.3] in dBm [-120...-25]
int8 ECNO [readonly, UMTS only]
CPICH Ec/No [3GPP TS 25.133 s9.1.2.3] in dB [-24...0]
10 years, 11 months
[PATCH] Add ability to select modem on test-voicecall
by João Paulo Rechi Vita
---
test/test-voicecall | 14 ++++++++++----
1 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/test/test-voicecall b/test/test-voicecall
index 13f371a..2da7703 100755
--- a/test/test-voicecall
+++ b/test/test-voicecall
@@ -39,11 +39,9 @@ if __name__ == "__main__":
global vcmanager
if (len(sys.argv) < 2):
- print "Useage: %s <number>" % (sys.argv[0])
+ print "Useage: %s [modem] <number>" % (sys.argv[0])
sys.exit(1)
- number = sys.argv[1]
-
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
@@ -52,9 +50,17 @@ if __name__ == "__main__":
'org.ofono.Manager')
modems = manager.GetProperties()['Modems']
+ modem = modems[0]
print modems
- vcmanager = dbus.Interface(bus.get_object('org.ofono', modems[0]),
+ if (len(sys.argv) == 3):
+ modem = sys.argv[1]
+ number = sys.argv[2]
+ else:
+ number = sys.argv[1]
+ print "Using modem %s" % modem
+
+ vcmanager = dbus.Interface(bus.get_object('org.ofono', modem),
'org.ofono.VoiceCallManager')
vcmanager.connect_to_signal("PropertyChanged",
--
1.6.3.3
10 years, 11 months
[PATCH] Add STE voice call support.
by sjur.brandeland@stericsson.com
From: Sjur Brændeland <sjur.brandeland(a)stericsson.com>
---
Makefile.am | 1 +
drivers/stemodem/stemodem.c | 2 +
drivers/stemodem/stemodem.h | 3 +
drivers/stemodem/voicecall.c | 596 ++++++++++++++++++++++++++++++++++++++++++
plugins/ste.c | 2 +-
5 files changed, 603 insertions(+), 1 deletions(-)
create mode 100644 drivers/stemodem/voicecall.c
diff --git a/Makefile.am b/Makefile.am
index ac13d73..ecd2660 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -159,6 +159,7 @@ builtin_sources += drivers/atmodem/atutil.h \
drivers/stemodem/stemodem.h \
drivers/stemodem/stemodem.c \
drivers/stemodem/gprs-context.c \
+ drivers/stemodem/voicecall.c \
drivers/stemodem/caif_socket.h \
drivers/stemodem/if_caif.h
diff --git a/drivers/stemodem/stemodem.c b/drivers/stemodem/stemodem.c
index 53207db..9184a42 100644
--- a/drivers/stemodem/stemodem.c
+++ b/drivers/stemodem/stemodem.c
@@ -37,6 +37,7 @@
static int stemodem_init(void)
{
ste_gprs_context_init();
+ ste_voicecall_init();
return 0;
}
@@ -44,6 +45,7 @@ static int stemodem_init(void)
static void stemodem_exit(void)
{
ste_gprs_context_exit();
+ ste_voicecall_exit();
}
OFONO_PLUGIN_DEFINE(stemodem, "STE modem driver", VERSION,
diff --git a/drivers/stemodem/stemodem.h b/drivers/stemodem/stemodem.h
index e55a2c3..e7c6934 100644
--- a/drivers/stemodem/stemodem.h
+++ b/drivers/stemodem/stemodem.h
@@ -25,3 +25,6 @@
extern void ste_gprs_context_init();
extern void ste_gprs_context_exit();
+extern void ste_voicecall_init();
+extern void ste_voicecall_exit();
+
diff --git a/drivers/stemodem/voicecall.c b/drivers/stemodem/voicecall.c
new file mode 100644
index 0000000..e74aa3d
--- /dev/null
+++ b/drivers/stemodem/voicecall.c
@@ -0,0 +1,596 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 ST-Ericsson 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 <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "common.h"
+
+#include "stemodem.h"
+
+enum call_status_ste {
+ STE_CALL_STATUS_IDLE = 0,
+ STE_CALL_STATUS_CALLING = 1,
+ STE_CALL_STATUS_CONNECTING = 2,
+ STE_CALL_STATUS_ACTIVE = 3,
+ STE_CALL_STATUS_HOLD = 4,
+ STE_CALL_STATUS_WAITING = 5,
+ STE_CALL_STATUS_ALERTING = 6,
+ STE_CALL_STATUS_BUSY = 7
+};
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+ GSList *calls;
+ unsigned int local_release;
+ GAtChat *chat;
+};
+
+struct release_id_req {
+ struct ofono_voicecall *vc;
+ ofono_voicecall_cb_t cb;
+ void *data;
+ int id;
+};
+
+struct change_state_req {
+ struct ofono_voicecall *vc;
+ ofono_voicecall_cb_t cb;
+ void *data;
+ int affected_types;
+};
+
+/* Translate from the ECAV-based STE-status to CLCC based status */
+static int call_status_ste_to_ofono(int status)
+{
+ switch (status) {
+ case STE_CALL_STATUS_IDLE:
+ return CALL_STATUS_DISCONNECTED;
+ case STE_CALL_STATUS_CALLING:
+ return CALL_STATUS_DIALING;
+ case STE_CALL_STATUS_CONNECTING:
+ return CALL_STATUS_ALERTING;
+ case STE_CALL_STATUS_ACTIVE:
+ return CALL_STATUS_ACTIVE;
+ case STE_CALL_STATUS_HOLD:
+ return CALL_STATUS_HELD;
+ case STE_CALL_STATUS_WAITING:
+ return CALL_STATUS_WAITING;
+ case STE_CALL_STATUS_ALERTING:
+ return CALL_STATUS_INCOMING;
+ case STE_CALL_STATUS_BUSY:
+ return CALL_STATUS_DISCONNECTED;
+ default:
+ return CALL_STATUS_DISCONNECTED;
+ }
+}
+
+static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
+ int direction, int status,
+ const char *num, int num_type, int clip)
+{
+ struct voicecall_data *d = ofono_voicecall_get_data(vc);
+ struct ofono_call *call;
+
+ /* Generate a call structure for the waiting call */
+ call = g_try_new0(struct ofono_call, 1);
+ if (!call)
+ return NULL;
+
+ call->type = type;
+ call->direction = direction;
+ call->status = status;
+
+ if (clip != 2) {
+ strncpy(call->phone_number.number, num,
+ OFONO_MAX_PHONE_NUMBER_LENGTH);
+ call->phone_number.type = num_type;
+ }
+
+ call->clip_validity = clip;
+
+ d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
+
+ return call;
+}
+
+static void ste_generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct change_state_req *req = user_data;
+ struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+ struct ofono_error error;
+
+ dump_response("ste_generic_cb", ok, result);
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ if (ok && req->affected_types) {
+ GSList *l;
+ struct ofono_call *call;
+
+ for (l = vd->calls; l; l = l->next) {
+ call = l->data;
+
+ if (req->affected_types & (0x1 << call->status))
+ vd->local_release |= (0x1 << call->id);
+ }
+ }
+
+ req->cb(&error, req->data);
+}
+
+static void release_id_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct release_id_req *req = user_data;
+ struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
+ struct ofono_error error;
+
+ dump_response("release_id_cb", ok, result);
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ if (ok)
+ vd->local_release = 0x1 << req->id;
+
+ req->cb(&error, req->data);
+}
+
+static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct ofono_error error;
+ ofono_voicecall_cb_t cb = cbd->cb;
+
+ dump_response("atd_cb", ok, result);
+
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ cb(&error, cbd->data);
+}
+
+static void ste_dial(struct ofono_voicecall *vc,
+ const struct ofono_phone_number *ph,
+ enum ofono_clir_option clir, enum ofono_cug_option cug,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ char buf[256];
+
+ if (!cbd)
+ goto error;
+
+ cbd->user = vc;
+
+ if (ph->type == 145)
+ sprintf(buf, "ATD+%s", ph->number);
+ else
+ sprintf(buf, "ATD%s", ph->number);
+
+ switch (clir) {
+ case OFONO_CLIR_OPTION_INVOCATION:
+ strcat(buf, "I");
+ break;
+ case OFONO_CLIR_OPTION_SUPPRESSION:
+ strcat(buf, "i");
+ break;
+ default:
+ break;
+ }
+
+ switch (cug) {
+ case OFONO_CUG_OPTION_INVOCATION:
+ strcat(buf, "G");
+ break;
+ default:
+ break;
+ }
+
+ strcat(buf, ";");
+
+ if (g_at_chat_send(vd->chat, buf, none_prefix,
+ atd_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_template(const char *cmd, struct ofono_voicecall *vc,
+ GAtResultFunc result_cb, unsigned int affected_types,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct change_state_req *req = g_try_new0(struct change_state_req, 1);
+
+ if (!req)
+ goto error;
+
+ req->vc = vc;
+ req->cb = cb;
+ req->data = data;
+ req->affected_types = affected_types;
+
+ if (g_at_chat_send(vd->chat, cmd, none_prefix,
+ result_cb, req, g_free) > 0)
+ return;
+
+error:
+ if (req)
+ g_free(req);
+
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_answer(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ste_template("ATA", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_hangup(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ste_template("AT+CHUP", vc, ste_generic_cb, 0x3f, cb, data);
+}
+
+static void ste_hold_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ste_template("AT+CHLD=2", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_release_all_held(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ unsigned int held_status = 0x1 << 1;
+ ste_template("AT+CHLD=0", vc, ste_generic_cb, held_status, cb, data);
+}
+
+static void ste_set_udub(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ unsigned int incoming_or_waiting = (0x1 << 4) | (0x1 << 5);
+ ste_template("AT+CHLD=0", vc, ste_generic_cb, incoming_or_waiting,
+ cb, data);
+}
+
+static void ste_release_all_active(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ste_template("AT+CHLD=1", vc, ste_generic_cb, 0x1, cb, data);
+}
+
+static void ste_release_specific(struct ofono_voicecall *vc, int id,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct release_id_req *req = g_try_new0(struct release_id_req, 1);
+ char buf[32];
+
+ if (!req)
+ goto error;
+
+ req->vc = vc;
+ req->cb = cb;
+ req->data = data;
+ req->id = id;
+
+ sprintf(buf, "AT+CHLD=1%d", id);
+
+ if (g_at_chat_send(vd->chat, buf, none_prefix,
+ release_id_cb, req, g_free) > 0)
+ return;
+
+error:
+ if (req)
+ g_free(req);
+
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_private_chat(struct ofono_voicecall *vc, int id,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ char buf[32];
+
+ sprintf(buf, "AT+CHLD=2%d", id);
+ ste_template(buf, vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_create_multiparty(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ ste_template("AT+CHLD=3", vc, ste_generic_cb, 0, cb, data);
+}
+
+static void ste_transfer(struct ofono_voicecall *vc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ /* Held & Active */
+ unsigned int transfer = 0x1 | 0x2;
+
+ /* Transfer can puts held & active calls together and disconnects
+ * from both. However, some networks support transfering of
+ * dialing/ringing calls as well.
+ */
+ transfer |= 0x4 | 0x8;
+
+ ste_template("AT+CHLD=4", vc, ste_generic_cb, transfer, cb, data);
+}
+
+static void ste_deflect(struct ofono_voicecall *vc,
+ const struct ofono_phone_number *ph,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ char buf[128];
+ unsigned int incoming_or_waiting = (0x1 << 4) | (0x1 << 5);
+
+ sprintf(buf, "AT+CTFR=\"%s\",%d", ph->number, ph->type);
+ ste_template(buf, vc, ste_generic_cb, incoming_or_waiting, cb, data);
+}
+
+static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_voicecall_cb_t cb = cbd->cb;
+ struct ofono_error error;
+
+ dump_response("vts_cb", ok, result);
+ decode_at_error(&error, g_at_result_final_response(result));
+ cb(&error, cbd->data);
+}
+
+static void ste_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ int len = strlen(dtmf);
+ int s;
+ char *buf;
+
+ if (!cbd)
+ goto error;
+
+ /* strlen("AT+VTS=) = 7 */
+ buf = g_try_new(char, len + 7);
+
+ if (!buf)
+ goto error;
+
+ sprintf(buf, "AT+VTS=%s", dtmf);
+
+ s = g_at_chat_send(vd->chat, buf, none_prefix,
+ vts_cb, cbd, g_free);
+
+ g_free(buf);
+
+ if (s > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ecav_notify(GAtResult *result, gpointer user_data)
+{
+ struct ofono_voicecall *vc = user_data;
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ GAtResultIter iter;
+ const char *num;
+ int id;
+ int status;
+ int call_type;
+ int num_type;
+ struct ofono_call *new_call;
+ struct ofono_call *existing_call = NULL;
+ GSList *l;
+
+ /* Parse ECAV */
+ dump_response("ecav_notify", TRUE, result);
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "*ECAV:"))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &id))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &status))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &call_type))
+ return;
+
+ /* Skip process id and exit cause */
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+
+ status = call_status_ste_to_ofono(status);
+ if (status == CALL_STATUS_DIALING ||
+ status == CALL_STATUS_WAITING ||
+ status == CALL_STATUS_INCOMING) {
+ if (!g_at_result_iter_next_string(&iter, &num))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &num_type))
+ return;
+ }
+
+ if (call_type != BEARER_CLASS_VOICE)
+ return;
+
+ /*
+ * Handle the call according to the status.
+ * If it doesn't exists we make a new one
+ */
+ l = g_slist_find_custom(vd->calls, GUINT_TO_POINTER(id),
+ at_util_call_compare_by_id);
+ if (l)
+ existing_call = l->data;
+
+ if (l == NULL && status != CALL_STATUS_DIALING &&
+ status != CALL_STATUS_WAITING &&
+ status != CALL_STATUS_INCOMING) {
+ ofono_error("ECAV notification for unknow call."
+ " id: %d, status: %d", id, status);
+ return;
+ }
+
+ switch (status) {
+ case CALL_STATUS_DISCONNECTED: {
+ enum ofono_disconnect_reason reason;
+
+ existing_call->status = status;
+
+ if (vd->local_release & (0x1 << existing_call->id))
+ reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+ else
+ reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+
+ ofono_voicecall_disconnected(vc, existing_call->id,
+ reason, NULL);
+
+ vd->calls = g_slist_remove(vd->calls, l->data);
+ break;
+ }
+ case CALL_STATUS_DIALING:
+ case CALL_STATUS_WAITING:
+ case CALL_STATUS_INCOMING: {
+ int clip_validity;
+ int direction;
+
+ if (status == CALL_STATUS_DIALING)
+ direction = CALL_DIRECTION_MOBILE_ORIGINATED;
+ else
+ direction = CALL_DIRECTION_MOBILE_TERMINATED;
+
+ if ((strlen(num)) > 0)
+ clip_validity = CLIP_VALIDITY_VALID;
+ else
+ clip_validity = CLIP_VALIDITY_NOT_AVAILABLE;
+
+ new_call = create_call(vc, call_type, direction, status,
+ num, num_type, clip_validity);
+ if (!new_call) {
+ ofono_error("Unable to malloc. "
+ "Call management is fubar");
+ return;
+ }
+
+ new_call->id = id;
+ ofono_voicecall_notify(vc, new_call);
+ break;
+ }
+ case CALL_STATUS_ALERTING:
+ case CALL_STATUS_ACTIVE:
+ case CALL_STATUS_HELD:
+ existing_call->status = status;
+ ofono_voicecall_notify(vc, existing_call);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static int ste_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+ void *data)
+{
+ GAtChat *chat = data;
+ struct voicecall_data *vd;
+
+ vd = g_new0(struct voicecall_data, 1);
+ vd->chat = chat;
+
+ ofono_voicecall_set_data(vc, vd);
+
+ g_at_chat_send(chat, "AT*ECAM=1", NULL, NULL, NULL, NULL);
+ g_at_chat_register(chat, "*ECAV:", ecav_notify, FALSE, vc, NULL);
+ ofono_voicecall_register(vc);
+
+ return 0;
+}
+
+static void ste_voicecall_remove(struct ofono_voicecall *vc)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+ g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
+ g_slist_free(vd->calls);
+
+ ofono_voicecall_set_data(vc, NULL);
+
+ g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+ .name = "stemodem",
+ .probe = ste_voicecall_probe,
+ .remove = ste_voicecall_remove,
+ .dial = ste_dial,
+ .answer = ste_answer,
+ .hangup = ste_hangup,
+ .hold_all_active = ste_hold_all_active,
+ .release_all_held = ste_release_all_held,
+ .set_udub = ste_set_udub,
+ .release_all_active = ste_release_all_active,
+ .release_specific = ste_release_specific,
+ .private_chat = ste_private_chat,
+ .create_multiparty = ste_create_multiparty,
+ .transfer = ste_transfer,
+ .deflect = ste_deflect,
+ .swap_without_accept = NULL,
+ .send_tones = ste_send_dtmf
+};
+
+void ste_voicecall_init()
+{
+ ofono_voicecall_driver_register(&driver);
+}
+
+void ste_voicecall_exit()
+{
+ ofono_voicecall_driver_unregister(&driver);
+}
diff --git a/plugins/ste.c b/plugins/ste.c
index 172e3bf..34550c8 100644
--- a/plugins/ste.c
+++ b/plugins/ste.c
@@ -198,7 +198,7 @@ static void ste_pre_sim(struct ofono_modem *modem)
ofono_devinfo_create(modem, 0, "atmodem", data->chat);
ofono_sim_create(modem, 0, "atmodem", data->chat);
- ofono_voicecall_create(modem, 0, "atmodem", data->chat);
+ ofono_voicecall_create(modem, 0, "stemodem", data->chat);
}
static void ste_post_sim(struct ofono_modem *modem)
--
1.6.3.3
10 years, 11 months
FW: [RFC 3/3] STE-plugin: Adding STE plugin
by Sjur Brændeland
Hi Denis.
Denis Kenzior <denkenz(a)gmail.com> wrote:
>> +static void ste_release_specific(struct ofono_voicecall *vc, int id,
>> + ofono_voicecall_cb_t cb, void *data) {
>> + struct voicecall_data *vd = ofono_voicecall_get_data(vc);
>> + struct release_id_req *req = g_try_new0(struct release_id_req, 1);
>> + char buf[32]; + struct ofono_call *call;
>> + GSList *l;
>> + int ac = 0;
>> +
>> + if (!req)
>> + goto error;
>> +
>> + req->vc = vc;
>> + req->cb = cb;
>> + req->data = data;
>> + req->id = id;
>> +
>> + /* Count active calls. If there is more than one active call
>> + * we cannot use ATH, as it will terminate all calls.
>> + * The reason for using ATH and not CHLD is that
>> + * emergency calls can not be terminated with AT+CHLD. + */
>> + for (l = vd->calls; l; l = l->next) {
>> + call = l->data;
>> +
>> + if (call->status == CALL_STATUS_ACTIVE)
>> + ac++;
>> + }
>> +
>> + l = g_slist_find_custom(vd->calls, GUINT_TO_POINTER(id),
>> + call_compare_by_id); + if (l == NULL) {
>> + ofono_error("Hangup request for unknow call");
>> + goto error;
>> + }
>> + call = l->data;
>> + /* Check the state of the call, as AT+CHLD does not terminate
>> + * calls in state Dialing, Alerting and Incoming */
>> + if (call->status == CALL_STATUS_DIALING ||
>> + call->status == CALL_STATUS_ALERTING ||
>> + call->status == CALL_STATUS_INCOMING)
>> + sprintf(buf, "ATH");
>> +
>> + /* Waiting call can not be terminated by at+chld=1x,
>> + * have to use at+chld = 0, but that will also terminate
>> + * other held calls. Bug in STE's AT module.
>> + */
>> + else if (call->status == CALL_STATUS_WAITING)
>> + sprintf(buf, "AT+CHLD=0");
>> +
>> + else {
>> + /* A held call can not be released by ATH, need to use CHLD */
>> + if (ac > 1 || call->status == CALL_STATUS_HELD)
>> + sprintf(buf, "AT+CHLD=1%d", id);
>> + else /* This is the last call, ok to use ATH. */
>> + sprintf(buf,
>> "ATH"); + }
>> +
>> + if (g_at_chat_send(vd->chat, buf, none_prefix,
>> + release_id_cb, req, g_free) > 0)
>> + return;
>> +
>> +error:
>> + if (req)
>> + g_free(req);
>> +
>> + CALLBACK_WITH_FAILURE(cb, data);
>> +}
>
> All of this logic needs to go away. The core already handles all of
> this, including selection of ATH/CHLD, waiting/held. Please review
> src/voicecall.c.
> If the core logic is not sufficient or does not properly handle a
> certain case, then lets see if we can fix the core first. Drivers
> should not concern themselves with this stuff.
OK, we can remove the state logic, but STE modem cannot terminate
calls in state DIALING and ALERTING with CHLD=1X. We need to use
ATH (or AT+CHUP) instead.
For now I think we will remove the state logic from ste_release_specific as
you suggest. Terminating calls in state DIALING and ALERTING must then be
handled by the Core part. The simplest would probably be to use hangup in
this case, but I suspect hangup work differently for different modems.
So if you cannot use hangup as the general approach, maybe it could be
implemented by adding callback functions release_dialing and release_alerting
in struct ofono_voicecall_driver. The STE driver could send ATH from
release_dialing and release_alerting, other drivers could leave them empty
and this could default to use release_specific instead?
>
> With this in mind, you might not need to track any state in this
> driver at all. See drivers/calypsomodem/voicecall.c for details.
> TI's CPI notifications are almost exactly the same as the STE ECAV.
The STE ECAV update is giving delta updates on the state information,
right now I don't see how we can get the ofono_voicall_notify right
without keeping some state information in ecav_notify.
BR/Sjur
10 years, 11 months
[PATCH 1/2] Remove unneeded code to disable the modem on hfp
by Gustavo F. Padovan
ofono_modem_remove() already disables the modem.
---
plugins/hfp.c | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 867b194..2d0faa8 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -397,9 +397,6 @@ static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, vo
{
struct ofono_modem *modem = data;
- if (ofono_modem_get_powered(modem))
- hfp_disable(modem);
-
ofono_modem_remove(modem);
return dbus_message_new_method_return(msg);
--
1.6.4.4
10 years, 11 months
[PATCH] Add copyright note to the HFP plugin
by Gustavo F. Padovan
---
plugins/hfp.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/plugins/hfp.c b/plugins/hfp.c
index 947bfc2..eb12c6a 100644
--- a/plugins/hfp.c
+++ b/plugins/hfp.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
* Copyright (C) 2009 Collabora Ltd. All rights reserved.
+ * Copyright (C) 2010 Gustavo F. Padovan <padovan(a)profusion.mobi>
*
* 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
--
1.6.4.4
10 years, 11 months
[PATCH] Add STE modem support for GPRS PDP Contexts
by sjur.brandeland@stericsson.com
From: Sjur Brændeland <sjur.brandeland(a)stericsson.com>
---
Makefile.am | 3 +-
drivers/stemodem/gprs-context.c | 610 +++++++++++++++++++++++++++++++++++++++
drivers/stemodem/stemodem.c | 3 +
drivers/stemodem/stemodem.h | 4 +
plugins/ste.c | 2 +-
5 files changed, 620 insertions(+), 2 deletions(-)
create mode 100644 drivers/stemodem/gprs-context.c
diff --git a/Makefile.am b/Makefile.am
index de7d5cf..125f1f5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -156,7 +156,8 @@ builtin_sources += drivers/atmodem/atutil.h \
drivers/stemodem/stemodem.h \
drivers/stemodem/stemodem.c \
drivers/stemodem/caif_socket.h \
- drivers/stemodem/if_caif.h
+ drivers/stemodem/if_caif.h \
+ drivers/stemodem/gprs-context.c
builtin_modules += modemconf
builtin_sources += plugins/modemconf.c
diff --git a/drivers/stemodem/gprs-context.c b/drivers/stemodem/gprs-context.c
new file mode 100644
index 0000000..99559a1
--- /dev/null
+++ b/drivers/stemodem/gprs-context.c
@@ -0,0 +1,610 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ * Copyright (C) 2010 ST-Ericsson 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 <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/gprs-context.h>
+#include <ofono/gprs.h>
+
+#include <linux/types.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+#include "stemodem.h"
+#include "caif_socket.h"
+#include "if_caif.h"
+
+#define MAX_CAIF_DEVICES 7
+#define MAX_DNS 2
+#define MAX_ELEM 20
+
+#define AUTH_BUF_LENGTH (OFONO_GPRS_MAX_USERNAME_LENGTH + \
+ OFONO_GPRS_MAX_PASSWORD_LENGTH + 128)
+
+static const char *cgact_prefix[] = { "+CGACT:", NULL };
+static const char *none_prefix[] = { NULL };
+
+static GSList *g_caif_devices;
+
+struct gprs_context_data {
+ GAtChat *chat;
+ unsigned int active_context;
+ char *username;
+ char *password;
+};
+
+struct conn_info {
+ unsigned int cid;
+ unsigned int device;
+ unsigned int channel_id;
+ char interface[10];
+};
+
+struct eppsd_response {
+ char *_current;
+ char ip_address[MAX_ELEM];
+ char subnet_mask[MAX_ELEM];
+ char mtu[MAX_ELEM];
+ char default_gateway[MAX_ELEM];
+ char dns_server1[MAX_ELEM];
+ char dns_server2[MAX_ELEM];
+ char p_cscf_server[MAX_ELEM];
+};
+
+static void start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name, const gchar **attribute_names,
+ const gchar **attribute_values, gpointer user_data,
+ GError **error)
+{
+ struct eppsd_response *rsp = user_data;
+ rsp->_current = NULL;
+
+ if (!strcmp(element_name, "ip_address"))
+ rsp->_current = rsp->ip_address;
+ else if (!strcmp(element_name, "subnet_mask"))
+ rsp->_current = rsp->subnet_mask;
+ else if (!strcmp(element_name, "mtu"))
+ rsp->_current = rsp->mtu;
+ else if (!strcmp(element_name, "default_gateway"))
+ rsp->_current = rsp->default_gateway;
+ else if (!strcmp(element_name, "dns_server") &&
+ rsp->dns_server1[0] == '\0')
+ rsp->_current = rsp->dns_server1;
+ else if (!strcmp(element_name, "dns_server"))
+ rsp->_current = rsp->dns_server2;
+ else if (!strcmp(element_name, "p_cscf_server"))
+ rsp->_current = rsp->p_cscf_server;
+}
+
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name, gpointer user_data,
+ GError **error)
+{
+ struct eppsd_response *rsp = user_data;
+ rsp->_current = NULL;
+}
+
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text, gsize text_len,
+ gpointer user_data, GError **error)
+{
+ struct eppsd_response *rsp = user_data;
+
+ if (rsp->_current) {
+ strncpy(rsp->_current, text, MAX_ELEM);
+ rsp->_current[MAX_ELEM] = 0;
+ }
+}
+
+static void error_handler (GMarkupParseContext *context,
+ GError *error, gpointer user_data)
+{
+ ofono_debug("Error parsing xml response from eppsd: %s\n",
+ error->message);
+}
+
+static GMarkupParser parser = {
+ start_element_handler,
+ end_element_handler,
+ text_handler,
+ NULL,
+ error_handler
+};
+
+static gint conn_compare_by_cid(gconstpointer a, gconstpointer b)
+{
+ const struct conn_info *conn = a;
+ unsigned int used = GPOINTER_TO_UINT(b);
+
+ if (used != conn->cid)
+ return 1;
+
+ return 0;
+}
+
+static struct conn_info *conn_info_create(unsigned int device,
+ unsigned int channel_id)
+{
+ struct conn_info *connection = g_try_new0(struct conn_info, 1);
+
+ if (!connection)
+ return NULL;
+
+ connection->cid = 0;
+ connection->device = device;
+ connection->channel_id = channel_id;
+
+ return connection;
+}
+
+/* Creates a new IP interface for CAIF.
+ *
+*/
+static gboolean caif_if_create(const char *interface,
+ unsigned int connid)
+{
+ int s;
+ static struct ifcaif_param param;
+ static struct ifreq ifr;
+
+ param.ipv4_connid = connid;
+ ifr.ifr_data = (void *) ¶m;
+ strcpy(ifr.ifr_name, interface);
+
+ s = socket(AF_CAIF, SOCK_SEQPACKET, CAIFPROTO_AT);
+ if (s < 0) {
+ ofono_debug("Failed to create socket for CAIF interface");
+ goto error;
+ }
+
+ if (ioctl(s, SIOCCAIFNETNEW, &ifr) < 0) {
+ ofono_debug("Failed to create IP interface for CAIF");
+ goto error;
+ }
+
+return TRUE;
+
+error:
+ return FALSE;
+}
+
+/* Removes IP interface for CAIF.
+ *
+*/
+static gboolean caif_if_remove(const char *interface,
+ unsigned int connid)
+{
+ int s;
+ static struct ifcaif_param param;
+ static struct ifreq ifr;
+
+ param.ipv4_connid = connid;
+ ifr.ifr_data = (void *) ¶m;
+ strcpy(ifr.ifr_name, interface);
+
+ s = socket(AF_CAIF, SOCK_SEQPACKET, CAIFPROTO_AT);
+ if (s < 0) {
+ ofono_debug("Failed to create socket for CAIF interface");
+ goto error;
+ }
+
+ if (ioctl(s, SIOCGIFINDEX, &ifr) == 0) {
+ if (ioctl(s, SIOCCAIFNETREMOVE, &ifr) < 0) {
+ ofono_debug("Failed to remove IP interface"
+ "for CAIF");
+ goto error;
+ }
+ } else {
+ ofono_debug("Did not find interface (%s) to remove",
+ interface);
+ goto error;
+ }
+
+ return TRUE;
+
+error:
+ return FALSE;
+}
+
+static void ste_eppsd_down_cb(gboolean ok,
+ GAtResult *result, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct ofono_error error;
+ struct conn_info *conn;
+ GSList *l;
+
+ dump_response("ste_eppsd_down_cb", ok, result);
+
+ if (!ok)
+ goto error;
+
+ l = g_slist_find_custom(g_caif_devices,
+ GUINT_TO_POINTER(gcd->active_context),
+ conn_compare_by_cid);
+
+ if (!l) {
+ ofono_debug("Did not find data (used caif device) for"
+ "connection with cid; %d",
+ gcd->active_context);
+ goto error;
+ }
+ conn = l->data;
+ if (!caif_if_remove(conn->interface, conn->channel_id)) {
+ ofono_debug("Failed to remove caif interface %s.",
+ conn->interface);
+ }
+ conn->cid = 0;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ cb(&error, cbd->data);
+ return;
+
+error:
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void ste_eppsd_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_up_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct conn_info *conn = NULL;
+ GAtResultIter iter;
+ GSList *l;
+ int i;
+ gsize length;
+ char *res_string;
+ const char *dns[MAX_DNS + 1];
+ struct eppsd_response rsp;
+ GMarkupParseContext *context = NULL;
+
+ dump_response("ste_eppsd_up_cb", ok, result);
+
+ l = g_slist_find_custom(g_caif_devices,
+ GUINT_TO_POINTER(gcd->active_context),
+ conn_compare_by_cid);
+
+ if (!l) {
+ ofono_debug("Did not find data (device and channel id)"
+ "for connection with cid; %d",
+ gcd->active_context);
+ goto error;
+ }
+ conn = l->data;
+
+ if (!ok)
+ goto error;
+
+ rsp._current = NULL;
+ context = g_markup_parse_context_new(&parser, 0, &rsp, NULL);
+ memset(&rsp, 0, sizeof(rsp));
+
+ g_at_result_iter_init(&iter, result);
+ for (i = 0; i < g_at_result_num_response_lines(result); i++) {
+ g_at_result_iter_next(&iter, NULL);
+ res_string = strdup(g_at_result_iter_raw_line(&iter));
+ length = strlen(res_string);
+
+ if (!g_markup_parse_context_parse(context, res_string,
+ length, NULL))
+ goto error;
+ }
+
+ if (!g_markup_parse_context_end_parse(context, NULL))
+ goto error;
+
+ g_markup_parse_context_free(context);
+
+ dns[0] = rsp.dns_server1;
+ dns[1] = rsp.dns_server2;
+ dns[2] = NULL;
+
+ sprintf(conn->interface, "caif%u", conn->device);
+
+ if (!caif_if_create(conn->interface, conn->channel_id)) {
+ ofono_error("Failed to create caif interface %s.",
+ conn->interface);
+ CALLBACK_WITH_SUCCESS(cb, NULL, FALSE, rsp.ip_address,
+ rsp.subnet_mask, rsp.default_gateway,
+ dns, cbd->data);
+ } else {
+ CALLBACK_WITH_SUCCESS(cb, conn->interface,
+ FALSE, rsp.ip_address, rsp.subnet_mask,
+ rsp.default_gateway, dns, cbd->data);
+ }
+ return;
+
+error:
+ ofono_debug("ste_eppsd_up_cb error");
+
+ if (context)
+ g_markup_parse_context_free(context);
+
+ if (conn)
+ conn->cid = 0;
+
+ CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+}
+
+static void ste_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_gprs_context_up_cb_t cb = cbd->cb;
+ struct ofono_gprs_context *gc = cbd->user;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct cb_data *ncbd = NULL;
+ struct conn_info *conn;
+ char buf[AUTH_BUF_LENGTH];
+ GSList *l;
+
+ dump_response("cgdcont_cb", ok, result);
+
+ if (!ok) {
+ struct ofono_error error;
+
+ gcd->active_context = 0;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+ return;
+ }
+
+ /* Set username and password */
+ sprintf(buf, "AT*EIAAUW=%d,1,\"%s\",\"%s\"", gcd->active_context,
+ gcd->username, gcd->password);
+
+ if (g_at_chat_send(gcd->chat, buf, none_prefix,
+ NULL, NULL, NULL) == 0)
+ goto error;
+
+ ncbd = g_memdup(cbd, sizeof(struct cb_data));
+
+ l = g_slist_find_custom(g_caif_devices, GUINT_TO_POINTER(0),
+ conn_compare_by_cid);
+
+ if (!l) {
+ ofono_debug("at_cgdcont_cb, no more available devices");
+ goto error;
+ }
+ conn = l->data;
+ conn->cid = gcd->active_context;
+ sprintf(buf, "AT*EPPSD=1,%u,%u", conn->channel_id, conn->cid);
+
+ if (g_at_chat_send(gcd->chat, buf, NULL,
+ ste_eppsd_up_cb, ncbd, g_free) > 0)
+ return;
+error:
+ if (ncbd)
+ g_free(ncbd);
+
+ gcd->active_context = 0;
+
+ CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL,
+ NULL, NULL, cbd->data);
+}
+
+static void ste_gprs_activate_primary(struct ofono_gprs_context *gc,
+ const struct ofono_gprs_primary_context *ctx,
+ ofono_gprs_context_up_cb_t cb, void *data)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
+ int len;
+
+ if (!cbd)
+ goto error;
+
+ gcd->active_context = ctx->cid;
+ gcd->username = g_strdup(ctx->username);
+ gcd->password = g_strdup(ctx->password);
+ cbd->user = gc;
+
+ len = sprintf(buf, "AT+CGDCONT=%u,\"IP\"", ctx->cid);
+
+ if (ctx->apn)
+ snprintf(buf + len, sizeof(buf) - len - 3, ",\"%s\"",
+ ctx->apn);
+
+ if (g_at_chat_send(gcd->chat, buf, none_prefix,
+ ste_cgdcont_cb, cbd, g_free) > 0)
+ return;
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data);
+}
+
+static void ste_gprs_deactivate_primary(struct ofono_gprs_context *gc,
+ unsigned int id,
+ ofono_gprs_context_cb_t cb, void *data)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ struct conn_info *conn;
+ char buf[64];
+ GSList *l;
+
+ if (!cbd)
+ goto error;
+
+ gcd->active_context = id;
+ cbd->user = gc;
+
+ l = g_slist_find_custom(g_caif_devices, GUINT_TO_POINTER(id),
+ conn_compare_by_cid);
+
+ if (!l) {
+ ofono_debug("at_gprs_deactivate_primary, did not find"
+ "data (channel id) for connection with cid; %d", id);
+ goto error;
+ }
+ conn = l->data;
+
+ sprintf(buf, "AT*EPPSD=0,%u,%u", conn->channel_id, id);
+
+ if (g_at_chat_send(gcd->chat, buf, none_prefix,
+ ste_eppsd_down_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void ste_cgact_read_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ gint cid, state;
+ GAtResultIter iter;
+
+ dump_response("cgact_read_cb", ok, result);
+
+ if (!ok)
+ return;
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, "+CGACT:")) {
+
+ if (!g_at_result_iter_next_number(&iter, &cid))
+ continue;
+
+ if ((unsigned int) cid != gcd->active_context)
+ continue;
+
+ if (!g_at_result_iter_next_number(&iter, &state))
+ continue;
+
+ if (state == 1)
+ continue;
+
+ ofono_gprs_context_deactivated(gc, gcd->active_context);
+ gcd->active_context = 0;
+
+ break;
+ }
+}
+
+static void cgev_notify(GAtResult *result, gpointer user_data)
+{
+ struct ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ GAtResultIter iter;
+ const char *event;
+
+ dump_response("cgev_notify", TRUE, result);
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+CGEV:"))
+ return;
+
+ if (!g_at_result_iter_next_unquoted_string(&iter, &event))
+ return;
+
+ if (g_str_has_prefix(event, "NW REACT ") ||
+ g_str_has_prefix(event, "NW DEACT ") ||
+ g_str_has_prefix(event, "ME DEACT ")) {
+ /* Ask what primary contexts are active now */
+
+ g_at_chat_send(gcd->chat, "AT+CGACT?", cgact_prefix,
+ ste_cgact_read_cb, gc, NULL);
+ }
+}
+
+static int ste_gprs_context_probe(struct ofono_gprs_context *gc,
+ unsigned int vendor, void *data)
+{
+ GAtChat *chat = data;
+ struct gprs_context_data *gcd;
+ struct conn_info *ci;
+ int i;
+
+ gcd = g_new0(struct gprs_context_data, 1);
+ gcd->chat = chat;
+
+ g_at_chat_register(gcd->chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
+
+ ofono_gprs_context_set_data(gc, gcd);
+
+ for (i = 0; i < MAX_CAIF_DEVICES; i++) {
+ ci = conn_info_create(i, i+1);
+ if (ci)
+ g_caif_devices = g_slist_append(g_caif_devices, ci);
+ }
+
+ return 0;
+}
+
+static void ste_gprs_context_remove(struct ofono_gprs_context *gc)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+ g_slist_foreach(g_caif_devices, (GFunc) g_free, NULL);
+ g_slist_free(g_caif_devices);
+ g_caif_devices = NULL;
+
+ ofono_gprs_context_set_data(gc, NULL);
+ g_free(gcd);
+}
+
+static struct ofono_gprs_context_driver driver = {
+ .name = "stemodem",
+ .probe = ste_gprs_context_probe,
+ .remove = ste_gprs_context_remove,
+ .activate_primary = ste_gprs_activate_primary,
+ .deactivate_primary = ste_gprs_deactivate_primary,
+};
+
+void ste_gprs_context_init()
+{
+ ofono_gprs_context_driver_register(&driver);
+}
+
+void ste_gprs_context_exit()
+{
+ ofono_gprs_context_driver_unregister(&driver);
+}
diff --git a/drivers/stemodem/stemodem.c b/drivers/stemodem/stemodem.c
index 7a49682..53207db 100644
--- a/drivers/stemodem/stemodem.c
+++ b/drivers/stemodem/stemodem.c
@@ -36,11 +36,14 @@
static int stemodem_init(void)
{
+ ste_gprs_context_init();
+
return 0;
}
static void stemodem_exit(void)
{
+ ste_gprs_context_exit();
}
OFONO_PLUGIN_DEFINE(stemodem, "STE modem driver", VERSION,
diff --git a/drivers/stemodem/stemodem.h b/drivers/stemodem/stemodem.h
index 67a8ac4..e55a2c3 100644
--- a/drivers/stemodem/stemodem.h
+++ b/drivers/stemodem/stemodem.h
@@ -21,3 +21,7 @@
*/
#include <drivers/atmodem/atutil.h>
+
+extern void ste_gprs_context_init();
+extern void ste_gprs_context_exit();
+
diff --git a/plugins/ste.c b/plugins/ste.c
index f2666eb..81853c4 100644
--- a/plugins/ste.c
+++ b/plugins/ste.c
@@ -218,7 +218,7 @@ static void ste_post_sim(struct ofono_modem *modem)
gprs = ofono_gprs_create(modem,
OFONO_VENDOR_STE, "atmodem", data->chat);
- gc = ofono_gprs_context_create(modem, 0, "atmodem", data->chat);
+ gc = ofono_gprs_context_create(modem, 0, "stemodem", data->chat);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
--
1.6.3.3
10 years, 11 months
[PATCH] enable passing of modem name via command line
by Gustavo F. Padovan
---
test/disable-modem | 14 ++++++++------
test/enable-modem | 14 ++++++++------
2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/test/disable-modem b/test/disable-modem
index dee34fd..47b8531 100755
--- a/test/disable-modem
+++ b/test/disable-modem
@@ -1,15 +1,17 @@
#!/usr/bin/python
import dbus
+import sys
bus = dbus.SystemBus()
-manager = dbus.Interface(bus.get_object('org.ofono', '/'),
- 'org.ofono.Manager')
-
-properties = manager.GetProperties()
-
-path = properties["Modems"][0]
+if len(sys.argv) == 2:
+ path = sys.argv[1]
+else:
+ manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+ 'org.ofono.Manager')
+ properties = manager.GetProperties()
+ path = properties["Modems"][0]
modem = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.Modem')
diff --git a/test/enable-modem b/test/enable-modem
index 0f9f604..86362af 100755
--- a/test/enable-modem
+++ b/test/enable-modem
@@ -1,15 +1,17 @@
#!/usr/bin/python
import dbus
+import sys
bus = dbus.SystemBus()
-manager = dbus.Interface(bus.get_object('org.ofono', '/'),
- 'org.ofono.Manager')
-
-properties = manager.GetProperties()
-
-path = properties["Modems"][0]
+if len(sys.argv) == 2:
+ path = sys.argv[1]
+else:
+ manager = dbus.Interface(bus.get_object('org.ofono', '/'),
+ 'org.ofono.Manager')
+ properties = manager.GetProperties()
+ path = properties["Modems"][0]
modem = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.Modem')
--
1.6.4.4
10 years, 11 months