diff --git a/plugins/bluetooth.h b/plugins/bluetooth.h index 936659c8..7e94f961 100644 --- a/plugins/bluetooth.h +++ b/plugins/bluetooth.h @@ -45,7 +45,8 @@ struct bluetooth_profile { struct bluetooth_sap_driver { const char *name; - int (*enable) (struct ofono_modem *modem); + int (*enable) (struct ofono_modem *modem, struct ofono_modem *sap_modem, + int bt_fd); }; struct server; diff --git a/plugins/telit.c b/plugins/telit.c index 1f950aa3..2e422e7b 100644 --- a/plugins/telit.c +++ b/plugins/telit.c @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -62,8 +66,14 @@ static const char *rsen_prefix[]= { "#RSEN:", NULL }; struct telit_data { GAtChat *chat; + GAtChat *aux; struct ofono_sim *sim; guint sim_inserted_source; + struct ofono_modem *sap_modem; + GIOChannel *bt_io; + GIOChannel *hw_io; + guint bt_watch; + guint hw_watch; }; static void telit_debug(const char *str, void *user_data) @@ -73,6 +83,104 @@ static void telit_debug(const char *str, void *user_data) ofono_info("%s%s", prefix, str); } +static void sap_close_io(struct ofono_modem *modem) +{ + struct telit_data *data = ofono_modem_get_data(modem); + + if (data->bt_io != NULL) { + int sk = g_io_channel_unix_get_fd(data->bt_io); + shutdown(sk, SHUT_RDWR); + + g_io_channel_unref(data->bt_io); + data->bt_io = NULL; + } + + if (data->bt_watch > 0) + g_source_remove(data->bt_watch); + + if (data->hw_io != NULL) { + g_io_channel_unref(data->hw_io); + data->hw_io = NULL; + } + + if (data->hw_watch > 0) + g_source_remove(data->hw_watch); +} + +static void bt_watch_remove(gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct telit_data *data = ofono_modem_get_data(modem); + + ofono_modem_set_powered(modem, FALSE); + + data->bt_watch = 0; +} + +static gboolean bt_event_cb(GIOChannel *bt_io, GIOCondition condition, + gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct telit_data *data = ofono_modem_get_data(modem); + + if (condition & G_IO_IN) { + GIOStatus status; + gsize bytes_read, bytes_written; + gchar buf[300]; + + status = g_io_channel_read_chars(bt_io, buf, 300, + &bytes_read, NULL); + + if (bytes_read > 0) + g_io_channel_write_chars(data->hw_io, buf, + bytes_read, &bytes_written, NULL); + + if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) + return FALSE; + + return TRUE; + } + + return FALSE; +} + +static void hw_watch_remove(gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct telit_data *data = ofono_modem_get_data(modem); + + ofono_modem_set_powered(modem, FALSE); + + data->hw_watch = 0; +} + +static gboolean hw_event_cb(GIOChannel *hw_io, GIOCondition condition, + gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct telit_data *data = ofono_modem_get_data(modem); + + if (condition & G_IO_IN) { + GIOStatus status; + gsize bytes_read, bytes_written; + gchar buf[300]; + + status = g_io_channel_read_chars(hw_io, buf, 300, + &bytes_read, NULL); + + if (bytes_read > 0) + g_io_channel_write_chars(data->bt_io, buf, + bytes_read, &bytes_written, NULL); + + if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN) + return FALSE; + + return TRUE; + } + + return FALSE; +} + static GAtChat *open_device(struct ofono_modem *modem, const char *key, char *debug) { @@ -129,31 +237,96 @@ static void rsen_enable_cb(gboolean ok, GAtResult *result, gpointer user_data) DBG("%p", modem); if (!ok) { - g_at_chat_unref(data->chat); - data->chat = NULL; - ofono_modem_set_powered(modem, FALSE); + g_at_chat_unref(data->aux); + data->aux = NULL; + ofono_modem_set_powered(data->sap_modem, FALSE); + sap_close_io(modem); return; } - } -static int telit_sap_enable(struct ofono_modem *modem) +static int telit_sap_open() +{ + const char *device = "/dev/ttyUSB4"; + struct termios ti; + int fd; + + DBG("%s", device); + + fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) + return -EINVAL; + + /* Switch TTY to raw mode */ + memset(&ti, 0, sizeof(ti)); + cfmakeraw(&ti); + + ti.c_cflag |= (B115200 | CLOCAL | CREAD); + + tcflush(fd, TCIOFLUSH); + if (tcsetattr(fd, TCSANOW, &ti) < 0) { + close(fd); + return -EBADF; + } + + return fd; +} + +static int telit_sap_enable(struct ofono_modem *modem, + struct ofono_modem *sap_modem, + int bt_fd) { struct telit_data *data = ofono_modem_get_data(modem); + int fd; DBG("%p", modem); - data->chat = open_device(modem, "Data", "Aux: "); - if (data->chat == NULL) + data->aux = open_device(modem, "Data", "Aux: "); + if (data->aux == NULL) return -EINVAL; - g_at_chat_register(data->chat, "#RSEN:", telit_rsen_notify, + fd = telit_sap_open(); + if (fd < 0) + return fd; + + data->hw_io = g_io_channel_unix_new(fd); + if (data->hw_io == NULL) { + close(fd); + return -ENOMEM; + } + + g_io_channel_set_encoding(data->hw_io, NULL, NULL); + g_io_channel_set_buffered(data->hw_io, FALSE); + g_io_channel_set_close_on_unref(data->hw_io, TRUE); + + data->hw_watch = g_io_add_watch_full(data->hw_io, G_PRIORITY_DEFAULT, + G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN, + hw_event_cb, modem, hw_watch_remove); + + data->bt_io = g_io_channel_unix_new(bt_fd); + if (data->bt_io == NULL) { + sap_close_io(modem); + return -ENOMEM; + } + + g_io_channel_set_encoding(data->bt_io, NULL, NULL); + g_io_channel_set_buffered(data->bt_io, FALSE); + g_io_channel_set_close_on_unref(data->bt_io, TRUE); + + data->bt_watch = g_io_add_watch_full(data->bt_io, G_PRIORITY_DEFAULT, + G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN, + bt_event_cb, modem, bt_watch_remove); + + + data->sap_modem = sap_modem; + + g_at_chat_register(data->aux, "#RSEN:", telit_rsen_notify, FALSE, modem, NULL); - g_at_chat_send(data->chat, "AT#NOPT=3", NULL, NULL, NULL, NULL); + g_at_chat_send(data->aux, "AT#NOPT=0", NULL, NULL, NULL, NULL); /* Set SAP functionality */ - g_at_chat_send(data->chat, "AT#RSEN=1,1,0,2,0", rsen_prefix, + g_at_chat_send(data->aux, "AT#RSEN=1,1,0,2,0", rsen_prefix, rsen_enable_cb, modem, NULL); return -EINPROGRESS;