| /* |
| * |
| * oFono - Open Source Telephony |
| * |
| * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. |
| * |
| * 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 |
| |
| #include <errno.h> |
| #include <ctype.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <libudev.h> |
| |
| #include <glib.h> |
| |
| #define OFONO_API_SUBJECT_TO_CHANGE |
| #include <ofono/plugin.h> |
| #include <ofono/modem.h> |
| #include <ofono/log.h> |
| |
| struct modem_info { |
| char *syspath; |
| char *devname; |
| char *driver; |
| char *vendor; |
| char *model; |
| GSList *devices; |
| struct ofono_modem *modem; |
| const char *sysattr; |
| }; |
| |
| struct device_info { |
| char *devpath; |
| char *devnode; |
| char *interface; |
| char *number; |
| char *label; |
| char *sysattr; |
| }; |
| |
| static gboolean setup_isi(struct modem_info *modem) |
| { |
| const char *node = NULL; |
| int addr = 0; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s %s", info->devnode, info->interface, |
| info->number, info->label, info->sysattr); |
| |
| if (g_strcmp0(info->sysattr, "820") == 0) { |
| if (g_strcmp0(info->interface, "2/254/0") == 0) |
| addr = 16; |
| |
| node = info->devnode; |
| } |
| } |
| |
| if (node == NULL) |
| return FALSE; |
| |
| DBG("interface=%s address=%d", node, addr); |
| |
| ofono_modem_set_string(modem->modem, "Interface", node); |
| ofono_modem_set_integer(modem->modem, "Address", addr); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_mbm(struct modem_info *modem) |
| { |
| const char *mdm = NULL, *app = NULL, *network = NULL, *gps = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s %s", info->devnode, info->interface, |
| info->number, info->label, info->sysattr); |
| |
| if (g_str_has_suffix(info->sysattr, "Modem") == TRUE || |
| g_str_has_suffix(info->sysattr, |
| "Modem 2") == TRUE) { |
| if (mdm == NULL) |
| mdm = info->devnode; |
| else |
| app = info->devnode; |
| } else if (g_str_has_suffix(info->sysattr, |
| "GPS Port") == TRUE || |
| g_str_has_suffix(info->sysattr, |
| "Module NMEA") == TRUE) { |
| gps = info->devnode; |
| } else if (g_str_has_suffix(info->sysattr, |
| "Network Adapter") == TRUE || |
| g_str_has_suffix(info->sysattr, |
| "NetworkAdapter") == TRUE) { |
| network = info->devnode; |
| } |
| } |
| |
| if (mdm == NULL || app == NULL) |
| return FALSE; |
| |
| DBG("modem=%s data=%s network=%s gps=%s", mdm, app, network, gps); |
| |
| ofono_modem_set_string(modem->modem, "ModemDevice", mdm); |
| ofono_modem_set_string(modem->modem, "DataDevice", app); |
| ofono_modem_set_string(modem->modem, "GPSDevice", gps); |
| ofono_modem_set_string(modem->modem, "NetworkInterface", network); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_hso(struct modem_info *modem) |
| { |
| const char *control = NULL, *application = NULL, *network = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s %s", info->devnode, info->interface, |
| info->number, info->label, info->sysattr); |
| |
| if (g_strcmp0(info->sysattr, "Control") == 0) |
| control = info->devnode; |
| else if (g_strcmp0(info->sysattr, "Application") == 0) |
| application = info->devnode; |
| else if (info->sysattr == NULL && |
| g_str_has_prefix(info->devnode, "hso") == TRUE) |
| network = info->devnode; |
| } |
| |
| if (control == NULL || application == NULL) |
| return FALSE; |
| |
| DBG("control=%s application=%s network=%s", |
| control, application, network); |
| |
| ofono_modem_set_string(modem->modem, "ControlPort", control); |
| ofono_modem_set_string(modem->modem, "ApplicationPort", application); |
| ofono_modem_set_string(modem->modem, "NetworkInterface", network); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_gobi(struct modem_info *modem) |
| { |
| const char *device = NULL, *gps = NULL, *qcdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "01") == 0) |
| qcdm = info->devnode; |
| else if (g_strcmp0(info->number, "02") == 0) |
| device = info->devnode; |
| else if (g_strcmp0(info->number, "03") == 0) |
| gps = info->devnode; |
| } |
| } |
| |
| if (device == NULL) |
| return FALSE; |
| |
| DBG("device=%s gps=%s qcdm=%s", device, gps, qcdm); |
| |
| ofono_modem_set_string(modem->modem, "Device", device); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_sierra(struct modem_info *modem) |
| { |
| const char *device = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->interface, "255/255/255") == 0 && |
| g_strcmp0(info->number, "03") == 0) { |
| device = info->devnode; |
| break; |
| } |
| } |
| |
| if (device == NULL) |
| return FALSE; |
| |
| DBG("device=%s", device); |
| |
| ofono_modem_set_string(modem->modem, "Device", device); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_huawei(struct modem_info *modem) |
| { |
| const char *mdm = NULL, *pcui = NULL, *diag = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "modem") == 0 || |
| g_strcmp0(info->interface, "255/1/1") == 0 || |
| g_strcmp0(info->interface, "255/2/1") == 0) { |
| mdm = info->devnode; |
| if (pcui != NULL && diag != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "pcui") == 0 || |
| g_strcmp0(info->interface, "255/1/2") == 0 || |
| g_strcmp0(info->interface, "255/2/2") == 0) { |
| pcui = info->devnode; |
| if (mdm != NULL && diag != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "diag") == 0 || |
| g_strcmp0(info->interface, "255/1/3") == 0 || |
| g_strcmp0(info->interface, "255/2/3") == 0) { |
| diag = info->devnode; |
| if (mdm != NULL && pcui != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "00") == 0) |
| mdm = info->devnode; |
| else if (g_strcmp0(info->number, "01") == 0) |
| pcui = info->devnode; |
| else if (g_strcmp0(info->number, "02") == 0) |
| pcui = info->devnode; |
| else if (g_strcmp0(info->number, "03") == 0) |
| pcui = info->devnode; |
| else if (g_strcmp0(info->number, "04") == 0) |
| pcui = info->devnode; |
| } |
| } |
| |
| if (mdm == NULL || pcui == NULL) |
| return FALSE; |
| |
| DBG("modem=%s pcui=%s diag=%s", mdm, pcui, diag); |
| |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| ofono_modem_set_string(modem->modem, "Pcui", pcui); |
| ofono_modem_set_string(modem->modem, "Diag", diag); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_speedup(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s", aux, mdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_linktop(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->interface, "2/2/1") == 0) { |
| if (g_strcmp0(info->number, "01") == 0) |
| aux = info->devnode; |
| else if (g_strcmp0(info->number, "03") == 0) |
| mdm = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s", aux, mdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_alcatel(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "03") == 0) |
| aux = info->devnode; |
| else if (g_strcmp0(info->number, "05") == 0) |
| mdm = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s", aux, mdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_novatel(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "00") == 0) |
| aux = info->devnode; |
| else if (g_strcmp0(info->number, "01") == 0) |
| mdm = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s", aux, mdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_nokia(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "10/0/0") == 0) { |
| if (g_strcmp0(info->number, "02") == 0) |
| mdm = info->devnode; |
| else if (g_strcmp0(info->number, "04") == 0) |
| aux = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s", aux, mdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_telit(struct modem_info *modem) |
| { |
| const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "00") == 0) |
| mdm = info->devnode; |
| else if (g_strcmp0(info->number, "01") == 0) |
| diag = info->devnode; |
| else if (g_strcmp0(info->number, "02") == 0) |
| gps = info->devnode; |
| else if (g_strcmp0(info->number, "03") == 0) |
| aux = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag); |
| |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| ofono_modem_set_string(modem->modem, "Data", aux); |
| ofono_modem_set_string(modem->modem, "GPS", gps); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_simcom(struct modem_info *modem) |
| { |
| const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "00") == 0) |
| diag = info->devnode; |
| else if (g_strcmp0(info->number, "01") == 0) |
| gps = info->devnode; |
| else if (g_strcmp0(info->number, "02") == 0) |
| aux = info->devnode; |
| else if (g_strcmp0(info->number, "03") == 0) |
| mdm = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag); |
| |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| ofono_modem_set_string(modem->modem, "Data", aux); |
| ofono_modem_set_string(modem->modem, "GPS", gps); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_zte(struct modem_info *modem) |
| { |
| const char *aux = NULL, *mdm = NULL, *qcdm = NULL; |
| const char *modem_intf; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| if (g_strcmp0(modem->model, "0016") == 0 || |
| g_strcmp0(modem->model, "0017") == 0 || |
| g_strcmp0(modem->model, "0117") == 0) |
| modem_intf = "02"; |
| else |
| modem_intf = "03"; |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->label, "aux") == 0) { |
| aux = info->devnode; |
| if (mdm != NULL) |
| break; |
| } else if (g_strcmp0(info->label, "modem") == 0) { |
| mdm = info->devnode; |
| if (aux != NULL) |
| break; |
| } else if (g_strcmp0(info->interface, "255/255/255") == 0) { |
| if (g_strcmp0(info->number, "00") == 0) |
| qcdm = info->devnode; |
| else if (g_strcmp0(info->number, "01") == 0) |
| aux = info->devnode; |
| else if (g_strcmp0(info->number, modem_intf) == 0) |
| mdm = info->devnode; |
| } |
| } |
| |
| if (aux == NULL || mdm == NULL) |
| return FALSE; |
| |
| DBG("aux=%s modem=%s qcdm=%s", aux, mdm, qcdm); |
| |
| ofono_modem_set_string(modem->modem, "Aux", aux); |
| ofono_modem_set_string(modem->modem, "Modem", mdm); |
| |
| return TRUE; |
| } |
| |
| static gboolean setup_samsung(struct modem_info *modem) |
| { |
| const char *control = NULL, *network = NULL; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s %s %s %s", info->devnode, info->interface, |
| info->number, info->label); |
| |
| if (g_strcmp0(info->interface, "10/0/0") == 0) |
| control = info->devnode; |
| else if (g_strcmp0(info->interface, "255/0/0") == 0) |
| network = info->devnode; |
| } |
| |
| if (control == NULL && network == NULL) |
| return FALSE; |
| |
| DBG("control=%s network=%s", control, network); |
| |
| ofono_modem_set_string(modem->modem, "ControlPort", control); |
| ofono_modem_set_string(modem->modem, "NetworkInterface", network); |
| |
| return TRUE; |
| } |
| |
| static struct { |
| const char *name; |
| gboolean (*setup)(struct modem_info *modem); |
| const char *sysattr; |
| } driver_list[] = { |
| { "isiusb", setup_isi, "type" }, |
| { "mbm", setup_mbm, "device/interface" }, |
| { "hso", setup_hso, "hsotype" }, |
| { "gobi", setup_gobi, }, |
| { "sierra", setup_sierra }, |
| { "huawei", setup_huawei }, |
| { "speedupcdma",setup_speedup }, |
| { "speedup", setup_speedup }, |
| { "linktop", setup_linktop }, |
| { "alcatel", setup_alcatel }, |
| { "novatel", setup_novatel }, |
| { "nokia", setup_nokia }, |
| { "telit", setup_telit }, |
| { "simcom", setup_simcom }, |
| { "zte", setup_zte }, |
| { "samsung", setup_samsung }, |
| { } |
| }; |
| |
| static GHashTable *modem_list; |
| |
| static const char *get_sysattr(const char *driver) |
| { |
| unsigned int i; |
| |
| for (i = 0; driver_list[i].name; i++) { |
| if (g_str_equal(driver_list[i].name, driver) == TRUE) |
| return driver_list[i].sysattr; |
| } |
| |
| return NULL; |
| } |
| |
| static void destroy_modem(gpointer data) |
| { |
| struct modem_info *modem = data; |
| GSList *list; |
| |
| DBG("%s", modem->syspath); |
| |
| ofono_modem_remove(modem->modem); |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| DBG("%s", info->devnode); |
| |
| g_free(info->devpath); |
| g_free(info->devnode); |
| g_free(info->interface); |
| g_free(info->number); |
| g_free(info->label); |
| g_free(info->sysattr); |
| g_free(info); |
| |
| list->data = NULL; |
| } |
| |
| g_slist_free(modem->devices); |
| |
| g_free(modem->syspath); |
| g_free(modem->devname); |
| g_free(modem->driver); |
| g_free(modem->vendor); |
| g_free(modem->model); |
| g_free(modem); |
| } |
| |
| static gboolean check_remove(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct modem_info *modem = value; |
| const char *devpath = user_data; |
| GSList *list; |
| |
| for (list = modem->devices; list; list = list->next) { |
| struct device_info *info = list->data; |
| |
| if (g_strcmp0(info->devpath, devpath) == 0) |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void remove_device(struct udev_device *device) |
| { |
| const char *syspath; |
| |
| syspath = udev_device_get_syspath(device); |
| if (syspath == NULL) |
| return; |
| |
| DBG("%s", syspath); |
| |
| g_hash_table_foreach_remove(modem_list, check_remove, |
| (char *) syspath); |
| } |
| |
| static gint compare_device(gconstpointer a, gconstpointer b) |
| { |
| const struct device_info *info1 = a; |
| const struct device_info *info2 = b; |
| |
| return g_strcmp0(info1->number, info2->number); |
| } |
| |
| static void add_device(const char *syspath, const char *devname, |
| const char *driver, const char *vendor, |
| const char *model, struct udev_device *device) |
| { |
| struct udev_device *intf; |
| const char *devpath, *devnode, *interface, *number, *label, *sysattr; |
| struct modem_info *modem; |
| struct device_info *info; |
| |
| devpath = udev_device_get_syspath(device); |
| if (devpath == NULL) |
| return; |
| |
| devnode = udev_device_get_devnode(device); |
| if (devnode == NULL) { |
| devnode = udev_device_get_property_value(device, "INTERFACE"); |
| if (devnode == NULL) |
| return; |
| } |
| |
| intf = udev_device_get_parent_with_subsystem_devtype(device, |
| "usb", "usb_interface"); |
| if (intf == NULL) |
| return; |
| |
| modem = g_hash_table_lookup(modem_list, syspath); |
| if (modem == NULL) { |
| modem = g_try_new0(struct modem_info, 1); |
| if (modem == NULL) |
| return; |
| |
| modem->syspath = g_strdup(syspath); |
| modem->devname = g_strdup(devname); |
| modem->driver = g_strdup(driver); |
| modem->vendor = g_strdup(vendor); |
| modem->model = g_strdup(model); |
| |
| modem->sysattr = get_sysattr(driver); |
| |
| g_hash_table_replace(modem_list, modem->syspath, modem); |
| } |
| |
| interface = udev_device_get_property_value(intf, "INTERFACE"); |
| number = udev_device_get_property_value(device, "ID_USB_INTERFACE_NUM"); |
| |
| label = udev_device_get_property_value(device, "OFONO_LABEL"); |
| |
| if (modem->sysattr != NULL) |
| sysattr = udev_device_get_sysattr_value(device, modem->sysattr); |
| else |
| sysattr = NULL; |
| |
| DBG("%s", devpath); |
| DBG("%s (%s) %s [%s] ==> %s %s", devnode, driver, |
| interface, number, label, sysattr); |
| |
| info = g_try_new0(struct device_info, 1); |
| if (info == NULL) |
| return; |
| |
| info->devpath = g_strdup(devpath); |
| info->devnode = g_strdup(devnode); |
| info->interface = g_strdup(interface); |
| info->number = g_strdup(number); |
| info->label = g_strdup(label); |
| info->sysattr = g_strdup(sysattr); |
| |
| modem->devices = g_slist_insert_sorted(modem->devices, info, |
| compare_device); |
| } |
| |
| static struct { |
| const char *driver; |
| const char *drv; |
| const char *vid; |
| const char *pid; |
| } vendor_list[] = { |
| { "isiusb", "cdc_phonet" }, |
| { "linktop", "cdc_acm", "230d" }, |
| { "mbm", "cdc_acm", "0bdb" }, |
| { "mbm" "cdc_ether", "0bdb" }, |
| { "mbm", "cdc_acm", "0fce" }, |
| { "mbm", "cdc_ether", "0fce" }, |
| { "mbm", "cdc_acm", "413c" }, |
| { "mbm", "cdc_ether", "413c" }, |
| { "mbm", "cdc_acm", "03f0" }, |
| { "mbm", "cdc_ether", "03f0" }, |
| { "mbm", "cdc_acm", "0930" }, |
| { "mbm", "cdc_ether", "0930" }, |
| { "hso", "hso" }, |
| { "gobi", "qcserial" }, |
| { "sierra", "sierra" }, |
| { "huawei", "option", "201e" }, |
| { "huawei", "cdc_ether", "12d1" }, |
| { "huawei", "option", "12d1" }, |
| { "speedupcdma","option", "1c9e", "9e00" }, |
| { "speedup", "option", "1c9e" }, |
| { "speedup", "option", "2020" }, |
| { "alcatel", "option", "1bbb", "0017" }, |
| { "novatel", "option", "1410" }, |
| { "zte", "option", "19d2" }, |
| { "simcom", "option", "05c6", "9000" }, |
| { "telit", "usbserial", "1bc7" }, |
| { "telit", "option", "1bc7" }, |
| { "nokia", "option", "0421", "060e" }, |
| { "nokia", "option", "0421", "0623" }, |
| { "samsung", "option", "04e8", "6889" }, |
| { "samsung", "kalmia" }, |
| { } |
| }; |
| |
| static void check_usb_device(struct udev_device *device) |
| { |
| struct udev_device *usb_device; |
| const char *syspath, *devname, *driver; |
| const char *vendor = NULL, *model = NULL; |
| |
| usb_device = udev_device_get_parent_with_subsystem_devtype(device, |
| "usb", "usb_device"); |
| if (usb_device == NULL) |
| return; |
| |
| syspath = udev_device_get_syspath(usb_device); |
| if (syspath == NULL) |
| return; |
| |
| devname = udev_device_get_devnode(usb_device); |
| if (devname == NULL) |
| return; |
| |
| driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER"); |
| if (driver == NULL) { |
| const char *drv, *vid, *pid; |
| unsigned int i; |
| |
| drv = udev_device_get_property_value(device, "ID_USB_DRIVER"); |
| if (drv == NULL) |
| return; |
| |
| vid = udev_device_get_property_value(device, "ID_VENDOR_ID"); |
| if (vid == NULL) |
| return; |
| |
| pid = udev_device_get_property_value(device, "ID_MODEL_ID"); |
| if (pid == NULL) |
| return; |
| |
| DBG("%s [%s:%s]", drv, vid, pid); |
| |
| for (i = 0; vendor_list[i].driver; i++) { |
| if (g_str_equal(vendor_list[i].drv, drv) == FALSE) |
| continue; |
| |
| if (vendor_list[i].vid == NULL) { |
| driver = vendor_list[i].driver; |
| vendor = vid; |
| model = pid; |
| break; |
| } |
| |
| if (g_str_equal(vendor_list[i].vid, vid) == TRUE) { |
| if (vendor_list[i].pid == NULL) { |
| if (driver == NULL) { |
| driver = vendor_list[i].driver; |
| vendor = vid; |
| model = pid; |
| } |
| continue; |
| } |
| |
| if (g_strcmp0(vendor_list[i].pid, pid) == 0) { |
| driver = vendor_list[i].driver; |
| vendor = vid; |
| model = pid; |
| break; |
| } |
| } |
| } |
| |
| if (driver == NULL) |
| return; |
| } |
| |
| add_device(syspath, devname, driver, vendor, model, device); |
| } |
| |
| static void check_device(struct udev_device *device) |
| { |
| const char *bus; |
| |
| bus = udev_device_get_property_value(device, "ID_BUS"); |
| if (bus == NULL) |
| return; |
| |
| if (g_str_equal(bus, "usb") == TRUE) |
| check_usb_device(device); |
| } |
| |
| static gboolean create_modem(gpointer key, gpointer value, gpointer user_data) |
| { |
| struct modem_info *modem = value; |
| const char *syspath = key; |
| unsigned int i; |
| |
| if (modem->modem != NULL) |
| return FALSE; |
| |
| DBG("%s", syspath); |
| |
| if (modem->devices == NULL) |
| return TRUE; |
| |
| DBG("driver=%s", modem->driver); |
| |
| modem->modem = ofono_modem_create(NULL, modem->driver); |
| if (modem->modem == NULL) |
| return TRUE; |
| |
| for (i = 0; driver_list[i].name; i++) { |
| if (g_str_equal(driver_list[i].name, modem->driver) == FALSE) |
| continue; |
| |
| if (driver_list[i].setup(modem) == TRUE) { |
| ofono_modem_register(modem->modem); |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| static void enumerate_devices(struct udev *context) |
| { |
| struct udev_enumerate *enumerate; |
| struct udev_list_entry *entry; |
| |
| DBG(""); |
| |
| enumerate = udev_enumerate_new(context); |
| if (enumerate == NULL) |
| return; |
| |
| udev_enumerate_add_match_subsystem(enumerate, "tty"); |
| udev_enumerate_add_match_subsystem(enumerate, "net"); |
| |
| udev_enumerate_scan_devices(enumerate); |
| |
| entry = udev_enumerate_get_list_entry(enumerate); |
| while (entry) { |
| const char *syspath = udev_list_entry_get_name(entry); |
| struct udev_device *device; |
| |
| device = udev_device_new_from_syspath(context, syspath); |
| if (device != NULL) { |
| check_device(device); |
| udev_device_unref(device); |
| } |
| |
| entry = udev_list_entry_get_next(entry); |
| } |
| |
| udev_enumerate_unref(enumerate); |
| |
| g_hash_table_foreach_remove(modem_list, create_modem, NULL); |
| } |
| |
| static struct udev *udev_ctx; |
| static struct udev_monitor *udev_mon; |
| static guint udev_watch = 0; |
| static guint udev_delay = 0; |
| |
| static gboolean check_modem_list(gpointer user_data) |
| { |
| udev_delay = 0; |
| |
| DBG(""); |
| |
| g_hash_table_foreach_remove(modem_list, create_modem, NULL); |
| |
| return FALSE; |
| } |
| |
| static gboolean udev_event(GIOChannel *channel, GIOCondition cond, |
| gpointer user_data) |
| { |
| struct udev_device *device; |
| const char *action; |
| |
| if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { |
| ofono_warn("Error with udev monitor channel"); |
| udev_watch = 0; |
| return FALSE; |
| } |
| |
| device = udev_monitor_receive_device(udev_mon); |
| if (device == NULL) |
| return TRUE; |
| |
| action = udev_device_get_action(device); |
| if (action == NULL) |
| return TRUE; |
| |
| if (g_str_equal(action, "add") == TRUE) { |
| if (udev_delay > 0) |
| g_source_remove(udev_delay); |
| |
| check_device(device); |
| |
| udev_delay = g_timeout_add_seconds(1, check_modem_list, NULL); |
| } else if (g_str_equal(action, "remove") == TRUE) |
| remove_device(device); |
| |
| udev_device_unref(device); |
| |
| return TRUE; |
| } |
| |
| static void udev_start(void) |
| { |
| GIOChannel *channel; |
| int fd; |
| |
| DBG(""); |
| |
| if (udev_monitor_enable_receiving(udev_mon) < 0) { |
| ofono_error("Failed to enable udev monitor"); |
| return; |
| } |
| |
| enumerate_devices(udev_ctx); |
| |
| fd = udev_monitor_get_fd(udev_mon); |
| |
| channel = g_io_channel_unix_new(fd); |
| if (channel == NULL) |
| return; |
| |
| udev_watch = g_io_add_watch(channel, |
| G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, |
| udev_event, NULL); |
| |
| g_io_channel_unref(channel); |
| } |
| |
| static int detect_init(void) |
| { |
| udev_ctx = udev_new(); |
| if (udev_ctx == NULL) { |
| ofono_error("Failed to create udev context"); |
| return -EIO; |
| } |
| |
| udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev"); |
| if (udev_mon == NULL) { |
| ofono_error("Failed to create udev monitor"); |
| udev_unref(udev_ctx); |
| udev_ctx = NULL; |
| return -EIO; |
| } |
| |
| modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, |
| NULL, destroy_modem); |
| |
| udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL); |
| udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL); |
| |
| udev_monitor_filter_update(udev_mon); |
| |
| udev_start(); |
| |
| return 0; |
| } |
| |
| static void detect_exit(void) |
| { |
| if (udev_delay > 0) |
| g_source_remove(udev_delay); |
| |
| if (udev_watch > 0) |
| g_source_remove(udev_watch); |
| |
| if (udev_ctx == NULL) |
| return; |
| |
| udev_monitor_filter_remove(udev_mon); |
| |
| g_hash_table_destroy(modem_list); |
| |
| udev_monitor_unref(udev_mon); |
| udev_unref(udev_ctx); |
| } |
| |
| OFONO_PLUGIN_DEFINE(udevng, "udev hardware detection", VERSION, |
| OFONO_PLUGIN_PRIORITY_DEFAULT, detect_init, detect_exit) |