diff --git a/src/gsm.c b/src/gsm.c index 52c57d9..d7e196a 100644 --- a/src/gsm.c +++ b/src/gsm.c @@ -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; +} diff --git a/src/gsm.h b/src/gsm.h index f81627e..37601f0 100644 --- a/src/gsm.h +++ b/src/gsm.h @@ -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 diff --git a/src/plugins/gprs.c b/src/plugins/gprs.c index 03e620e..9737a86 100644 --- a/src/plugins/gprs.c +++ b/src/plugins/gprs.c @@ -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); +}