#include "gatchat.h"
#include "gatresult.h"
+#include "gatppp.h"
#include "atmodem.h"
-static const char *cgact_prefix[] = { "+CGACT:", NULL };
+#define STATIC_IP_NETMASK "255.255.255.248"
+
static const char *none_prefix[] = { NULL };
+enum state {
+ STATE_IDLE,
+ STATE_ENABLING,
+ STATE_DISABLING,
+ STATE_ACTIVE,
+};
+
struct gprs_context_data {
GAtChat *chat;
unsigned int active_context;
+ char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
+ char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
+ GAtPPP *ppp;
+ enum state state;
+ union {
+ ofono_gprs_context_cb_t down_cb; /* Down callback */
+ ofono_gprs_context_up_cb_t up_cb; /* Up callback */
+ };
+ void *cb_data; /* Callback data */
};
static void at_cgact_down_cb(gboolean ok, GAtResult *result, gpointer user_data)
cb(&error, cbd->data);
}
-static void at_cgact_up_cb(gboolean ok, GAtResult *result, gpointer user_data)
+static void ppp_connect(GAtPPPConnectStatus success,
+ const char *interface, const char *ip,
+ const char *dns1, const char *dns2,
+ gpointer user_data)
{
- struct cb_data *cbd = user_data;
- ofono_gprs_context_up_cb_t cb = cbd->cb;
- struct ofono_error error;
+ struct ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ const char *dns[3];
- decode_at_error(&error, g_at_result_final_response(result));
+ if (success != G_AT_PPP_CONNECT_SUCCESS) {
+ gcd->active_context = 0;
+ gcd->state = STATE_IDLE;
+
+ CALLBACK_WITH_FAILURE(gcd->up_cb, NULL, 0, NULL, NULL, NULL,
+ NULL, gcd->cb_data);
+ return;
+ }
- cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+ dns[0] = dns1;
+ dns[1] = dns2;
+ dns[2] = 0;
+
+ gcd->state = STATE_ACTIVE;
+ CALLBACK_WITH_SUCCESS(gcd->up_cb, interface, TRUE, ip,
+ STATIC_IP_NETMASK, NULL,
+ dns, gcd->cb_data);
}
-static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+static void ppp_disconnect(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 ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+
+ ofono_gprs_context_deactivated(gc, gcd->active_context);
+ gcd->active_context = 0;
+ gcd->state = STATE_IDLE;
+}
+
+static gboolean setup_ppp(struct ofono_gprs_context *gc)
+{
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ GIOChannel *channel;
+
+ channel = g_at_chat_get_channel(gcd->chat);
+ g_at_chat_shutdown(gcd->chat);
+
+ /* open ppp */
+ gcd->ppp = g_at_ppp_new(channel);
+
+ if (gcd->ppp == NULL)
+ return FALSE;
+
+ g_at_ppp_set_credentials(gcd->ppp, gcd->username, gcd->password);
+
+ /* set connect and disconnect callbacks */
+ g_at_ppp_set_connect_function(gcd->ppp, ppp_connect, gc);
+ g_at_ppp_set_disconnect_function(gcd->ppp, ppp_disconnect, gc);
+
+ /* open the ppp connection */
+ g_at_ppp_open(gcd->ppp);
+
+ return TRUE;
+}
+
+static void at_cgdata_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);
- struct cb_data *ncbd;
- char buf[64];
if (!ok) {
struct ofono_error error;
+ ofono_info("Unable to enter data state");
+
gcd->active_context = 0;
+ gcd->state = STATE_IDLE;
decode_at_error(&error, g_at_result_final_response(result));
- cb(&error, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+ gcd->up_cb(&error, NULL, 0, NULL, NULL, NULL, NULL,
+ gcd->cb_data);
return;
}
- ncbd = g_memdup(cbd, sizeof(struct cb_data));
+ setup_ppp(gc);
+}
- snprintf(buf, sizeof(buf), "AT+CGACT=1,%u", gcd->active_context);
+static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct ofono_gprs_context *gc = user_data;
+ struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
+ char buf[64];
- if (g_at_chat_send(gcd->chat, buf, none_prefix,
- at_cgact_up_cb, ncbd, g_free) > 0)
+ if (!ok) {
+ struct ofono_error error;
+
+ gcd->active_context = 0;
+ gcd->state = STATE_IDLE;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ gcd->up_cb(&error, NULL, 0, NULL, NULL, NULL, NULL,
+ gcd->cb_data);
return;
+ }
- if (ncbd)
- g_free(ncbd);
+ sprintf(buf, "AT+CGDATA=\"PPP\",%u", gcd->active_context);
+ if (g_at_chat_send(gcd->chat, buf, none_prefix,
+ at_cgdata_cb, gc, NULL) > 0)
+ return;
gcd->active_context = 0;
+ gcd->state = STATE_IDLE;
- CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, cbd->data);
+ CALLBACK_WITH_FAILURE(gcd->up_cb, NULL, 0, NULL, NULL, NULL, NULL,
+ gcd->cb_data);
}
static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
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->up_cb = cb;
+ gcd->cb_data = data;
+ memcpy(gcd->username, ctx->username, sizeof(ctx->username));
+ memcpy(gcd->password, ctx->password, sizeof(ctx->password));
- cbd->user = gc;
+ gcd->state = STATE_ENABLING;
- /* TODO: Handle username / password fields */
len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"", ctx->cid);
if (ctx->apn)
ctx->apn);
if (g_at_chat_send(gcd->chat, buf, none_prefix,
- at_cgdcont_cb, cbd, g_free) > 0)
+ at_cgdcont_cb, gc, NULL) > 0)
return;
-error:
- if (cbd)
- g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, 0, NULL, NULL, NULL, NULL, data);
}
CALLBACK_WITH_FAILURE(cb, data);
}
-static void at_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;
-
- if (!ok)
- return;
-
- 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;
-
- 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,
- at_cgact_read_cb, gc, NULL);
-
- return;
- }
-}
-
static int at_gprs_context_probe(struct ofono_gprs_context *gc,
unsigned int vendor, void *data)
{
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);
return 0;