Implemented pppd pass-through

This commit is contained in:
Pierre Pronchery 2011-02-02 03:56:38 +00:00
parent 0f03769f71
commit 9c6b35783f
3 changed files with 249 additions and 31 deletions

172
src/gsm.c
View File

@ -78,6 +78,10 @@ struct _GSM
char * wr_buf;
size_t wr_buf_cnt;
guint wr_source;
GIOChannel * rd_ppp_channel;
guint rd_ppp_source;
GIOChannel * wr_ppp_channel;
guint wr_ppp_source;
/* temporary buffers */
char number[32];
@ -338,8 +342,12 @@ static gboolean _on_reset(gpointer data);
static gboolean _on_timeout(gpointer data);
static gboolean _on_watch_can_read(GIOChannel * source, GIOCondition condition,
gpointer data);
static gboolean _on_watch_can_read_ppp(GIOChannel * source,
GIOCondition condition, gpointer data);
static gboolean _on_watch_can_write(GIOChannel * source, GIOCondition condition,
gpointer data);
static gboolean _on_watch_can_write_ppp(GIOChannel * source,
GIOCondition condition, gpointer data);
/* public */
@ -378,6 +386,10 @@ GSM * gsm_new(char const * device, unsigned int baudrate, unsigned int hwflow)
gsm->wr_buf = NULL;
gsm->wr_buf_cnt = 0;
gsm->wr_source = 0;
gsm->rd_ppp_channel = NULL;
gsm->rd_ppp_source = 0;
gsm->wr_ppp_channel = NULL;
gsm->wr_ppp_source = 0;
/* error checking */
if(gsm->device == NULL || gsm->baudrate == 0 || gsm->modem == NULL)
{
@ -1086,6 +1098,12 @@ int gsm_stop(GSM * gsm)
if(gsm->source != 0)
g_source_remove(gsm->source);
gsm->source = 0;
if(gsm->rd_ppp_source != 0)
g_source_remove(gsm->rd_ppp_source);
gsm->rd_ppp_source = 0;
if(gsm->wr_ppp_source != 0)
g_source_remove(gsm->wr_ppp_source);
gsm->wr_ppp_source = 0;
_gsm_event_send(gsm, GSM_EVENT_TYPE_SUSPEND);
return 0;
}
@ -1158,9 +1176,12 @@ static int _gsm_parse(GSM * gsm)
#endif
while(i < gsm->rd_buf_cnt)
{
if(gsm->rd_buf[i++] != '\r' && gsm->rd_buf[i - 1] != '\n')
if(gsm->rd_buf[i] != '\r' && gsm->rd_buf[i] != '\n')
{
i++;
continue;
gsm->rd_buf[i - 1] = '\0';
}
gsm->rd_buf[i++] = '\0';
if(i < gsm->rd_buf_cnt && gsm->rd_buf[i] == '\n')
i++;
if(gsm->rd_buf[0] != '\0')
@ -1937,14 +1958,35 @@ static int _gsm_trigger_cmut(GSM * gsm, char const * result)
static int _gsm_trigger_connect(GSM * gsm, char const * result,
gboolean * answered)
{
char * argv[] = { "/usr/sbin/pppd", "pppd", "call", "phone", NULL };
GSpawnFlags flags = G_SPAWN_FILE_AND_ARGV_ZERO;
int wfd;
int rfd;
GError * error = NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, result);
#endif
if(answered != NULL)
*answered = TRUE;
/* FIXME implement pass-through */
/* FIXME reset is probably not enough (send "+++"?) */
return gsm_reset(gsm, gsm->retry, NULL);
gsm->mode = GSM_MODE_DATA;
if(g_spawn_async_with_pipes(NULL, argv, NULL, flags, NULL, NULL, NULL,
&wfd, &rfd, NULL, &error)
== FALSE)
{
gsm_reset(gsm, 0, NULL);
return 1;
}
gsm->rd_ppp_channel = g_io_channel_unix_new(rfd);
g_io_channel_set_encoding(gsm->rd_ppp_channel, NULL, &error);
g_io_channel_set_buffered(gsm->rd_ppp_channel, FALSE);
gsm->rd_ppp_source = g_io_add_watch(gsm->rd_ppp_channel, G_IO_IN,
_on_watch_can_read_ppp, gsm);
gsm->wr_ppp_channel = g_io_channel_unix_new(wfd);
g_io_channel_set_encoding(gsm->wr_ppp_channel, NULL, &error);
g_io_channel_set_buffered(gsm->wr_ppp_channel, FALSE);
gsm->wr_ppp_source = 0;
return gsm_event(gsm, GSM_EVENT_TYPE_GPRS_ATTACHMENT, 1);
}
@ -2240,6 +2282,7 @@ static int _gsm_trigger_no_dialtone(GSM * gsm, char const * result,
/* callbacks */
/* on_reset */
static void _reset_channel(GIOChannel * channel);
static int _reset_do(GSM * gsm, int fd);
static gboolean _reset_settle(gpointer data);
@ -2254,13 +2297,12 @@ static gboolean _on_reset(gpointer data)
#endif
if(gsm->source != 0)
g_source_remove(gsm->source);
if(gsm->channel != NULL)
{
/* XXX should the file descriptor also be freed? */
g_io_channel_shutdown(gsm->channel, TRUE, &error);
g_io_channel_unref(gsm->channel);
gsm->channel = NULL;
}
_reset_channel(gsm->channel);
gsm->channel = NULL;
_reset_channel(gsm->rd_ppp_channel);
gsm->rd_ppp_channel = NULL;
_reset_channel(gsm->wr_ppp_channel);
gsm->wr_ppp_channel = NULL;
if((fd = open(gsm->device, O_RDWR | O_NONBLOCK)) < 0
|| _reset_do(gsm, fd) != 0)
{
@ -2287,6 +2329,17 @@ static gboolean _on_reset(gpointer data)
return FALSE;
}
static void _reset_channel(GIOChannel * channel)
{
GError * error = NULL;
if(channel == NULL)
return;
/* XXX should the file descriptor also be freed? */
g_io_channel_shutdown(channel, TRUE, &error);
g_io_channel_unref(channel);
}
static int _reset_do(GSM * gsm, int fd)
{
struct stat st;
@ -2411,7 +2464,58 @@ static gboolean _on_watch_can_read(GIOChannel * source, GIOCondition condition,
gsm->rd_source = 0;
return FALSE;
}
_gsm_parse(gsm);
switch(gsm->mode)
{
case GSM_MODE_INIT:
case GSM_MODE_COMMAND:
case GSM_MODE_PDU: /* XXX really parse? */
_gsm_parse(gsm);
break;
case GSM_MODE_DATA:
if(gsm->wr_ppp_channel == NULL
|| gsm->wr_ppp_source != 0)
break;
gsm->wr_ppp_source = g_io_add_watch(gsm->wr_ppp_channel,
G_IO_OUT, _on_watch_can_write_ppp, gsm);
break;
}
return TRUE;
}
/* on_watch_can_read_ppp */
static gboolean _on_watch_can_read_ppp(GIOChannel * source,
GIOCondition condition, gpointer data)
{
GSM * gsm = data;
gsize cnt = 0;
GError * error = NULL;
GIOStatus status;
char * p;
if(condition != G_IO_IN || source != gsm->rd_ppp_channel)
return FALSE; /* should not happen */
if((p = realloc(gsm->wr_buf, gsm->wr_buf_cnt + 256)) == NULL)
return TRUE; /* XXX retries immediately (delay?) */
gsm->wr_buf = p;
status = g_io_channel_read_chars(source,
&gsm->wr_buf[gsm->wr_buf_cnt], 256, &cnt, &error);
gsm->wr_buf_cnt += cnt;
switch(status)
{
case G_IO_STATUS_NORMAL:
break;
case G_IO_STATUS_ERROR:
error_set("%s", error->message); /* XXX really print */
case G_IO_STATUS_EOF:
default:
gsm->rd_ppp_source = 0;
gsm_reset(gsm, 0, NULL);
return FALSE;
}
if(gsm->channel != NULL && gsm->wr_source == 0)
gsm->wr_source = g_io_add_watch(gsm->channel, G_IO_OUT,
_on_watch_can_write, gsm);
return TRUE;
}
@ -2481,3 +2585,45 @@ static gboolean _on_watch_can_write(GIOChannel * source, GIOCondition condition,
}
return FALSE;
}
/* on_watch_can_write_ppp */
static gboolean _on_watch_can_write_ppp(GIOChannel * source,
GIOCondition condition, gpointer data)
{
GSM * gsm = data;
gsize cnt = 0;
GError * error = NULL;
GIOStatus status;
char * p;
if(condition != G_IO_OUT || source != gsm->wr_ppp_channel)
return FALSE; /* should not happen */
status = g_io_channel_write_chars(source, gsm->rd_buf, gsm->rd_buf_cnt,
&cnt, &error);
if(cnt != 0) /* some data may have been written anyway */
{
gsm->rd_buf_cnt -= cnt;
memmove(gsm->rd_buf, &gsm->rd_buf[cnt], gsm->rd_buf_cnt);
if((p = realloc(gsm->rd_buf, gsm->rd_buf_cnt)) != NULL)
gsm->rd_buf = p; /* we can ignore errors... */
else if(gsm->rd_buf_cnt == 0)
gsm->rd_buf = NULL; /* ...except when it's not one */
}
switch(status)
{
case G_IO_STATUS_NORMAL:
break;
case G_IO_STATUS_ERROR:
error_set("%s", error->message); /* XXX really print */
case G_IO_STATUS_EOF:
default:
gsm->wr_ppp_source = 0;
gsm_reset(gsm, 0, NULL);
return FALSE;
}
if(gsm->rd_buf_cnt > 0) /* there is more data to write */
return TRUE;
gsm->wr_ppp_source = 0;
return FALSE;
}

View File

@ -137,7 +137,7 @@ typedef enum _GSMMessageMode
typedef enum _GSMMode
{
GSM_MODE_INIT = 0, GSM_MODE_COMMAND, GSM_MODE_PDU
GSM_MODE_INIT = 0, GSM_MODE_COMMAND, GSM_MODE_DATA, GSM_MODE_PDU
} GSMMode;
typedef enum _GSMOperatorFormat

View File

@ -30,6 +30,8 @@ typedef struct _GPRS
GtkWidget * window;
GtkWidget * attach;
GtkWidget * apn;
GtkWidget * username;
GtkWidget * password;
} GPRS;
@ -42,6 +44,8 @@ static void _gprs_settings(PhonePlugin * plugin);
static int _gprs_access_point(PhonePlugin * plugin);
static int _gprs_attach(PhonePlugin * plugin);
static int _gprs_connect(PhonePlugin * plugin);
static int _gprs_disconnect(PhonePlugin * plugin);
/* public */
@ -109,6 +113,8 @@ static int _gprs_event_functional(PhonePlugin * plugin)
/* gprs_settings */
static void _on_settings_cancel(gpointer data);
static gboolean _on_settings_closex(gpointer data);
static void _on_settings_disconnect(gpointer data);
static void _on_settings_connect(gpointer data);
static void _on_settings_ok(gpointer data);
static void _gprs_settings(PhonePlugin * plugin)
@ -127,20 +133,43 @@ static void _gprs_settings(PhonePlugin * plugin)
gtk_container_set_border_width(GTK_CONTAINER(gprs->window), 4);
gtk_window_set_default_size(GTK_WINDOW(gprs->window), 200, 300);
#if GTK_CHECK_VERSION(2, 6, 0)
gtk_window_set_icon_name(GTK_WINDOW(gprs->window), "network-wireless");
gtk_window_set_icon_name(GTK_WINDOW(gprs->window), "stock_internet");
#endif
gtk_window_set_title(GTK_WINDOW(gprs->window), "GPRS preferences");
g_signal_connect_swapped(G_OBJECT(gprs->window), "delete-event",
G_CALLBACK(_on_settings_closex), plugin);
vbox = gtk_vbox_new(FALSE, 0);
vbox = gtk_vbox_new(FALSE, 4);
/* attachment */
gprs->attach = gtk_check_button_new_with_label("Always on");
gtk_box_pack_start(GTK_BOX(vbox), gprs->attach, FALSE, TRUE, 0);
/* access point */
widget = gtk_label_new("Access point:");
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
gprs->apn = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), gprs->apn, FALSE, TRUE, 0);
/* credentials */
widget = gtk_label_new("Username:");
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
gprs->username = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), gprs->username, FALSE, TRUE, 0);
widget = gtk_label_new("Password:");
gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
gprs->password = gtk_entry_new();
gtk_entry_set_visibility(GTK_ENTRY(gprs->password), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), gprs->password, FALSE, TRUE, 0);
/* connect */
widget = gtk_button_new_from_stock(GTK_STOCK_CONNECT);
g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
_on_settings_connect), plugin);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
/* disconnect */
widget = gtk_button_new_from_stock(GTK_STOCK_DISCONNECT);
g_signal_connect_swapped(G_OBJECT(widget), "clicked", G_CALLBACK(
_on_settings_disconnect), plugin);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
/* button box */
bbox = gtk_hbutton_box_new();
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
@ -165,10 +194,7 @@ static void _on_settings_cancel(gpointer data)
GPRS * gprs = plugin->priv;
char const * p;
if((p = plugin->helper->config_get(plugin->helper->phone, "gprs",
"apn")) == NULL)
p = "";
gtk_entry_set_text(GTK_ENTRY(gprs->apn), p);
gtk_widget_hide(gprs->window);
if((p = plugin->helper->config_get(plugin->helper->phone, "gprs",
"attach")) != NULL
&& strtoul(p, NULL, 10) != 0)
@ -177,7 +203,18 @@ static void _on_settings_cancel(gpointer data)
else
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gprs->attach),
FALSE);
gtk_widget_hide(gprs->window);
if((p = plugin->helper->config_get(plugin->helper->phone, "gprs",
"apn")) == NULL)
p = "";
gtk_entry_set_text(GTK_ENTRY(gprs->apn), p);
if((p = plugin->helper->config_get(plugin->helper->phone, "gprs",
"username")) == NULL)
p = "";
gtk_entry_set_text(GTK_ENTRY(gprs->username), p);
if((p = plugin->helper->config_get(plugin->helper->phone, "gprs",
"password")) == NULL)
p = "";
gtk_entry_set_text(GTK_ENTRY(gprs->password), p);
}
static gboolean _on_settings_closex(gpointer data)
@ -188,6 +225,20 @@ static gboolean _on_settings_closex(gpointer data)
return TRUE;
}
static void _on_settings_connect(gpointer data)
{
PhonePlugin * plugin = data;
_gprs_connect(plugin);
}
static void _on_settings_disconnect(gpointer data)
{
PhonePlugin * plugin = data;
_gprs_disconnect(plugin);
}
static void _on_settings_ok(gpointer data)
{
PhonePlugin * plugin = data;
@ -195,17 +246,20 @@ static void _on_settings_ok(gpointer data)
char const * p;
gboolean active;
if((p = gtk_entry_get_text(GTK_ENTRY(gprs->apn))) != NULL
&& strlen(p) != 0
&& plugin->helper->config_set(plugin->helper->phone,
"gprs", "apn", p) == 0)
_gprs_access_point(plugin);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
gprs->attach));
if(plugin->helper->config_set(plugin->helper->phone, "gprs", "attach",
active ? "1" : "0") == 0)
_gprs_attach(plugin);
gtk_widget_hide(gprs->window);
active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gprs->attach));
plugin->helper->config_set(plugin->helper->phone, "gprs", "attach",
active ? "1" : "0");
_gprs_attach(plugin);
p = gtk_entry_get_text(GTK_ENTRY(gprs->apn));
plugin->helper->config_set(plugin->helper->phone, "gprs", "apn", p);
_gprs_access_point(plugin);
p = gtk_entry_get_text(GTK_ENTRY(gprs->username));
plugin->helper->config_set(plugin->helper->phone, "gprs", "username",
p);
p = gtk_entry_get_text(GTK_ENTRY(gprs->password));
plugin->helper->config_set(plugin->helper->phone, "gprs", "password",
p);
}
@ -242,3 +296,21 @@ static int _gprs_attach(PhonePlugin * plugin)
return 1;
return plugin->helper->queue(plugin->helper->phone, "AT+CGATT?");
}
/* gprs_connect */
static int _gprs_connect(PhonePlugin * plugin)
{
char const * cmd = "ATD*99***1#";
return plugin->helper->queue(plugin->helper->phone, cmd);
}
/* gprs_disconnect */
static int _gprs_disconnect(PhonePlugin * plugin)
{
char const * cmd = "ATH"; /* XXX requires interpretation from Phone */
return plugin->helper->queue(plugin->helper->phone, cmd);
}