aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am2
-rw-r--r--callouts/77-mm-modem-probe-capabilities.rules23
-rw-r--r--callouts/Makefile.am13
-rw-r--r--callouts/mm-modem-probe.c639
-rw-r--r--configure.in7
-rw-r--r--introspection/mm-modem.xml10
-rw-r--r--marshallers/mm-marshal.list1
-rw-r--r--plugins/Makefile.am17
-rw-r--r--plugins/mm-plugin-generic.c350
-rw-r--r--plugins/mm-plugin-generic.h27
-rw-r--r--src/Makefile.am22
-rw-r--r--src/main.c93
-rw-r--r--src/mm-generic-cdma.c217
-rw-r--r--src/mm-generic-cdma.h17
-rw-r--r--src/mm-generic-gsm.c457
-rw-r--r--src/mm-generic-gsm.h17
-rw-r--r--src/mm-manager.c376
-rw-r--r--src/mm-manager.h6
-rw-r--r--src/mm-modem.c97
-rw-r--r--src/mm-modem.h54
-rw-r--r--src/mm-plugin.c48
-rw-r--r--src/mm-plugin.h58
-rw-r--r--src/mm-serial-port.c1107
-rw-r--r--src/mm-serial-port.h114
-rw-r--r--src/mm-serial.c1024
-rw-r--r--src/mm-serial.h80
27 files changed, 3280 insertions, 1598 deletions
diff --git a/.gitignore b/.gitignore
index 21592d41..c6c7fdab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ ModemManager.pc
marshallers/mm-marshal.[ch]
src/modem-manager
docs/spec.html
+callouts/mm-modem-probe
+
diff --git a/Makefile.am b/Makefile.am
index f7e7f7de..0964e1bb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,7 +20,7 @@ clean:
rm -f $(GENERATED_FILES)
endif
-SUBDIRS = marshallers src plugins introspection test
+SUBDIRS = marshallers callouts src plugins introspection test
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.ModemManager.conf
diff --git a/callouts/77-mm-modem-probe-capabilities.rules b/callouts/77-mm-modem-probe-capabilities.rules
new file mode 100644
index 00000000..ba86cc2c
--- /dev/null
+++ b/callouts/77-mm-modem-probe-capabilities.rules
@@ -0,0 +1,23 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change", GOTO="mm_modem_probe_end"
+SUBSYSTEM!="tty", GOTO="mm_modem_probe_end"
+
+DRIVERS=="serial_cs|nozomi", IMPORT{program}="mm-modem-probe --delay 3000 --export $tempnode", GOTO="mm_modem_probe_end"
+
+# Only probe known mobile broadband drivers
+DRIVERS=="option|sierra|hso|cdc_acm|qcserial|moto-modem", GOTO="probe"
+
+GOTO="mm_modem_probe_end"
+
+LABEL="probe"
+
+# Don't probe new-style beagleboard cdc-acm ports
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0525", GOTO="mm_modem_probe_end"
+
+SUBSYSTEM=="tty", SUBSYSTEMS=="usb", DRIVERS=="?*", ENV{MM_MODEM_DRIVER}="$attr{driver}"
+SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{MM_MODEM_USB_INTERFACE_NUMBER}="$attr{bInterfaceNumber}"
+SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="?*", ATTRS{idProduct}=="?*", IMPORT{program}="mm-modem-probe --vid 0x$attr{idVendor} --pid 0x$attr{idProduct} --usb-interface $env{MM_MODEM_USB_INTERFACE_NUMBER} --driver $env{MM_MODEM_DRIVER} --delay 3000 --export $tempnode", GOTO="mm_modem_probe_end"
+
+LABEL="mm_modem_probe_end"
+
diff --git a/callouts/Makefile.am b/callouts/Makefile.am
new file mode 100644
index 00000000..6a3f3b24
--- /dev/null
+++ b/callouts/Makefile.am
@@ -0,0 +1,13 @@
+udevdir = $(UDEV_BASE_DIR)
+udev_PROGRAMS = mm-modem-probe
+
+mm_modem_probe_SOURCES = mm-modem-probe.c
+mm_modem_probe_CPPFLAGS = $(MM_CFLAGS)
+mm_modem_probe_LDADD = $(MM_LIBS)
+
+udevrulesdir = $(UDEV_BASE_DIR)/rules.d
+udevrules_DATA = 77-mm-modem-probe-capabilities.rules
+
+EXTRA_DIST = \
+ $(udevrules_DATA)
+
diff --git a/callouts/mm-modem-probe.c b/callouts/mm-modem-probe.c
new file mode 100644
index 00000000..93f84f8d
--- /dev/null
+++ b/callouts/mm-modem-probe.c
@@ -0,0 +1,639 @@
+/*
+ * modem_caps - probe Hayes-compatible modem capabilities
+ *
+ * Copyright (C) 2008 Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <glib.h>
+
+#define HUAWEI_VENDOR_ID 0x12D1
+#define SIERRA_VENDOR_ID 0x1199
+
+#define MODEM_CAP_GSM 0x0001 /* GSM */
+#define MODEM_CAP_IS707_A 0x0002 /* CDMA Circuit Switched Data */
+#define MODEM_CAP_IS707_P 0x0004 /* CDMA Packet Switched Data */
+#define MODEM_CAP_DS 0x0008 /* Data compression selection (v.42bis) */
+#define MODEM_CAP_ES 0x0010 /* Error control selection (v.42) */
+#define MODEM_CAP_FCLASS 0x0020 /* Group III Fax */
+#define MODEM_CAP_MS 0x0040 /* Modulation selection */
+#define MODEM_CAP_W 0x0080 /* Wireless commands */
+#define MODEM_CAP_IS856 0x0100 /* CDMA 3G EVDO rev 0 */
+#define MODEM_CAP_IS856_A 0x0200 /* CDMA 3G EVDO rev A */
+
+static gboolean verbose = FALSE;
+static gboolean quiet = FALSE;
+static FILE *logfile = NULL;
+
+struct modem_caps {
+ char *name;
+ guint32 bits;
+};
+
+static struct modem_caps modem_caps[] = {
+ {"+CGSM", MODEM_CAP_GSM},
+ {"+CIS707-A", MODEM_CAP_IS707_A},
+ {"+CIS707A", MODEM_CAP_IS707_A}, /* Cmotech */
+ {"+CIS707", MODEM_CAP_IS707_A},
+ {"CIS707", MODEM_CAP_IS707_A}, /* Qualcomm Gobi */
+ {"+CIS707P", MODEM_CAP_IS707_P},
+ {"CIS-856", MODEM_CAP_IS856},
+ {"+IS-856", MODEM_CAP_IS856}, /* Cmotech */
+ {"CIS-856-A", MODEM_CAP_IS856_A},
+ {"CIS-856A", MODEM_CAP_IS856_A}, /* Kyocera KPC680 */
+ {"+DS", MODEM_CAP_DS},
+ {"+ES", MODEM_CAP_ES},
+ {"+MS", MODEM_CAP_MS},
+ {"+FCLASS", MODEM_CAP_FCLASS},
+ {NULL}
+};
+
+static void
+printerr_handler (const char *string)
+{
+ if (logfile)
+ fprintf (logfile, "E: %s", string);
+ if (!quiet)
+ fprintf (stderr, "E: %s", string);
+}
+
+static void
+print_handler (const char *string)
+{
+ if (logfile)
+ fprintf (logfile, "L: %s", string);
+ if (!quiet)
+ fprintf (stdout, "L: %s", string);
+}
+
+#define verbose(fmt, args...) \
+if (verbose) { \
+ g_print ("%s(): " fmt "\n", G_STRFUNC, ##args); \
+}
+
+static gboolean
+modem_send_command (int fd, const char *cmd)
+{
+ int eagain_count = 1000;
+ guint32 i;
+ ssize_t written;
+
+ verbose ("Sending: '%s'", cmd);
+
+ for (i = 0; i < strlen (cmd) && eagain_count > 0;) {
+ written = write (fd, cmd + i, 1);
+
+ if (written > 0)
+ i += written;
+ else {
+ /* Treat written == 0 as EAGAIN to ensure we break out of the
+ * for() loop eventually.
+ */
+ if ((written < 0) && (errno != EAGAIN)) {
+ g_printerr ("error writing command: %d\n", errno);
+ return FALSE;
+ }
+ eagain_count--;
+ g_usleep (G_USEC_PER_SEC / 10000);
+ }
+ }
+
+ return eagain_count <= 0 ? FALSE : TRUE;
+}
+
+static int
+find_terminator (const char *line, const char **terminators)
+{
+ int i;
+
+ for (i = 0; terminators[i]; i++) {
+ if (!strncasecmp (line, terminators[i], strlen (terminators[i])))
+ return i;
+ }
+ return -1;
+}
+
+static const char *
+find_response (const char *line, const char **responses, int *idx)
+{
+ int i;
+
+ /* Don't look for a result again if we got one previously */
+ for (i = 0; responses[i]; i++) {
+ if (strstr (line, responses[i])) {
+ *idx = i;
+ return line;
+ }
+ }
+ return NULL;
+}
+
+#define RESPONSE_LINE_MAX 128
+#define SERIAL_BUF_SIZE 2048
+
+/* Return values:
+ *
+ * -2: timeout
+ * -1: read error or response not found
+ * 0...N: response index in **needles array
+ */
+static int
+modem_wait_reply (int fd,
+ guint32 timeout_secs,
+ const char **needles,
+ const char **terminators,
+ int *out_terminator,
+ char **out_response)
+{
+ char buf[SERIAL_BUF_SIZE + 1];
+ int reply_index = -1, bytes_read;
+ GString *result = g_string_sized_new (RESPONSE_LINE_MAX * 2);
+ time_t end;
+ const char *response = NULL;
+ gboolean done = FALSE;
+
+ *out_terminator = -1;
+ end = time (NULL) + timeout_secs;
+ do {
+ bytes_read = read (fd, buf, SERIAL_BUF_SIZE);
+ if (bytes_read < 0 && errno != EAGAIN) {
+ g_string_free (result, TRUE);
+ g_printerr ("read error: %d\n", errno);
+ return -1;
+ }
+
+ if (bytes_read == 0)
+ break; /* EOF */
+ else if (bytes_read > 0) {
+ char **lines, **iter, *tmp;
+
+ buf[bytes_read] = 0;
+ g_string_append (result, buf);
+
+ verbose ("Got: '%s'", result->str);
+
+ lines = g_strsplit_set (result->str, "\n\r", 0);
+
+ /* Find response terminators */
+ for (iter = lines; *iter && !done; iter++) {
+ tmp = g_strstrip (*iter);
+ if (tmp && strlen (tmp)) {
+ *out_terminator = find_terminator (tmp, terminators);
+ if (*out_terminator >= 0)
+ done = TRUE;
+ }
+ }
+
+ /* If the terminator is found, look for expected responses */
+ if (done) {
+ for (iter = lines; *iter && (reply_index < 0); iter++) {
+ tmp = g_strstrip (*iter);
+ if (tmp && strlen (tmp)) {
+ response = find_response (tmp, needles, &reply_index);
+ if (response) {
+ g_free (*out_response);
+ *out_response = g_strdup (response);
+ }
+ }
+ }
+ }
+ g_strfreev (lines);
+ }
+
+ if (!done)
+ g_usleep (1000);
+ } while (!done && (time (NULL) < end) && (result->len <= SERIAL_BUF_SIZE));
+
+ /* Handle timeout */
+ if (*out_terminator < 0 && !*out_response)
+ reply_index = -2;
+
+ g_string_free (result, TRUE);
+ return reply_index;
+}
+
+#define GCAP_TAG "+GCAP:"
+#define CGMM_TAG "+CGMM:"
+#define HUAWEI_EC121_TAG "+CIS707-A"
+
+static int
+parse_gcap (const char *tag, gboolean strip_tag, const char *buf)
+{
+ const char *p = buf;
+ char **caps, **iter;
+ int ret = 0;
+
+ if (strip_tag)
+ p += strlen (tag);
+
+ caps = g_strsplit_set (p, " ,\t", 0);
+ if (!caps)
+ return 0;
+
+ for (iter = caps; *iter; iter++) {
+ struct modem_caps *cap = modem_caps;
+
+ while (cap->name) {
+ if (!strcmp(cap->name, *iter)) {
+ ret |= cap->bits;
+ break;
+ }
+ cap++;
+ }
+ }
+
+ g_strfreev (caps);
+ return ret;
+}
+
+static int
+parse_cgmm (const char *buf)
+{
+ const char *p = buf + strlen (CGMM_TAG);
+ char **cgmm, **iter;
+ gboolean gsm = FALSE;
+
+ cgmm = g_strsplit_set (p, " ,\t", 0);
+ if (!cgmm)
+ return 0;
+
+ /* BUSlink SCWi275u USB GPRS modem and some Motorola phones */
+ for (iter = cgmm; *iter && !gsm; iter++) {
+ if (strstr (*iter, "GSM900") || strstr (*iter, "GSM1800") ||
+ strstr (*iter, "GSM1900") || strstr (*iter, "GSM850"))
+ gsm = TRUE;
+ }
+
+ g_strfreev (cgmm);
+ return gsm ? MODEM_CAP_GSM : 0;
+}
+
+static int
+g_timeval_subtract (GTimeVal *result, GTimeVal *x, GTimeVal *y)
+{
+ int nsec;
+
+ /* Perform the carry for the later subtraction by updating y. */
+ if (x->tv_usec < y->tv_usec) {
+ nsec = (y->tv_usec - x->tv_usec) / G_USEC_PER_SEC + 1;
+ y->tv_usec -= G_USEC_PER_SEC * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_usec - y->tv_usec > G_USEC_PER_SEC) {
+ nsec = (x->tv_usec - y->tv_usec) / G_USEC_PER_SEC;
+ y->tv_usec += G_USEC_PER_SEC * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ tv_usec is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_usec = x->tv_usec - y->tv_usec;
+
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
+}
+
+static int modem_probe_caps(int fd, glong timeout_ms)
+{
+ const char *gcap_responses[] = { GCAP_TAG, HUAWEI_EC121_TAG, NULL };
+ const char *terminators[] = { "OK", "ERROR", "ERR", "+CME ERROR", NULL };
+ char *reply = NULL;
+ int idx = -1, term_idx = -1, ret = 0;
+ gboolean try_ati = FALSE;
+ GTimeVal start, end;
+ gboolean send_success;
+
+ /* If a delay was specified, start a bit later */
+ if (timeout_ms > 500) {
+ g_usleep (500000);
+ timeout_ms -= 500;
+ }
+
+ /* Standard response timeout case */
+ timeout_ms += 3000;
+
+ while (timeout_ms > 0) {
+ GTimeVal diff;
+ gulong sleep_time = 100000;
+
+ g_get_current_time (&start);
+
+ idx = term_idx = 0;
+ send_success = modem_send_command (fd, "AT+GCAP\r\n");
+ if (send_success)
+ idx = modem_wait_reply (fd, 2, gcap_responses, terminators, &term_idx, &reply);
+ else
+ sleep_time = 300000;
+
+ g_get_current_time (&end);
+ g_timeval_subtract (&diff, &end, &start);
+ timeout_ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
+
+ if (send_success) {
+ if (0 == term_idx && 0 == idx) {
+ /* Success */
+ verbose ("GCAP response: %s", reply);
+ ret = parse_gcap (gcap_responses[idx], TRUE, reply);
+ break;
+ } else if (0 == term_idx && 1 == idx) {
+ /* Stupid Huawei EC121 that doesn't prefix response with +GCAP: */
+ verbose ("GCAP response: %s", reply);
+ ret = parse_gcap (gcap_responses[idx], FALSE, reply);
+ break;
+ } else if (0 == term_idx && -1 == idx) {
+ /* Just returned "OK" but no GCAP (Sierra) */
+ try_ati = TRUE;
+ break;
+ } else if (3 == term_idx && -1 == idx) {
+ /* No SIM (Huawei) */
+ try_ati = TRUE;
+ break;
+ } else if (1 == term_idx || 2 == term_idx) {
+ try_ati = TRUE;
+ } else
+ verbose ("timed out waiting for GCAP reply (idx %d, term_idx %d)", idx, term_idx);
+ g_free (reply);
+ reply = NULL;
+ }
+
+ g_usleep (sleep_time);
+ timeout_ms -= sleep_time / 1000;
+ }
+
+ if (!ret && try_ati) {
+ const char *ati_responses[] = { GCAP_TAG, HUAWEI_EC121_TAG, NULL };
+
+ /* Many cards (ex Sierra 860 & 875) won't accept AT+GCAP but
+ * accept ATI when the SIM is missing. Often the GCAP info is
+ * in the ATI response too.
+ */
+ g_free (reply);
+ reply = NULL;
+
+ verbose ("GCAP failed, trying ATI...");
+ if (modem_send_command (fd, "ATI\r\n")) {
+ idx = modem_wait_reply (fd, 3, ati_responses, terminators, &term_idx, &reply);
+ if (0 == term_idx && 0 == idx) {
+ verbose ("ATI response: %s", reply);
+ ret = parse_gcap (ati_responses[idx], TRUE, reply);
+ } else if (0 == term_idx && 1 == idx) {
+ verbose ("ATI response: %s", reply);
+ ret = parse_gcap (ati_responses[idx], FALSE, reply);
+ }
+ }
+ }
+
+ g_free (reply);
+ reply = NULL;
+
+ /* Try an alternate method on some hardware (ex BUSlink SCWi275u) */
+ if ((idx != -2) && !(ret & MODEM_CAP_GSM) && !(ret & MODEM_CAP_IS707_A)) {
+ const char *cgmm_responses[] = { CGMM_TAG, NULL };
+
+ if (modem_send_command (fd, "AT+CGMM\r\n")) {
+ idx = modem_wait_reply (fd, 5, cgmm_responses, terminators, &term_idx, &reply);
+ if (0 == term_idx && 0 == idx) {
+ verbose ("CGMM response: %s", reply);
+ ret |= parse_cgmm (reply);
+ }
+ g_free (reply);
+ }
+ }
+
+ return ret;
+}
+
+static void
+print_usage (void)
+{
+ printf("Usage: probe-modem [options] <device>\n"
+ " --export export key/value pairs\n"
+ " --delay <ms> delay before probing (1 to 3000 ms inclusive)\n"
+ " --verbose print verbose debugging output\n"
+ " --quiet suppress logging to stdout (does not affect logfile output)\n"
+ " --log <file> log all output\n"
+ " --vid <vid> USB Vendor ID (optional)\n"
+ " --pid <pid> USB Product ID (optional)\n"
+ " --usb-interface <num> USB device interface number (optional)\n"
+ " --driver <name> Linux kernel device driver (optional)\n"
+ " --help\n\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ static const struct option options[] = {
+ { "export", 0, NULL, 'x' },
+ { "delay", required_argument, NULL, 'a' },
+ { "verbose", 0, NULL, 'v' },
+ { "quiet", 0, NULL, 'q' },
+ { "log", required_argument, NULL, 'l' },
+ { "vid", required_argument, NULL, 'e' },
+ { "pid", required_argument, NULL, 'p' },
+ { "usb-interface", required_argument, NULL, 'i' },
+ { "driver", required_argument, NULL, 'd' },
+ { "help", 0, NULL, 'h' },
+ {}
+ };
+
+ const char *device = NULL;
+ const char *logpath = NULL;
+ const char *driver = NULL;
+ gboolean export = 0;
+ struct termios orig, attrs;
+ int fd = -1, caps, ret = 0;
+ guint32 delay_ms = 0;
+ unsigned int vid = 0, pid = 0, usbif = 0, last_err = 0;
+ unsigned long int tmp;
+
+ while (1) {
+ int option;
+
+ option = getopt_long (argc, argv, "xvl:qh", options, NULL);
+ if (option == -1)
+ break;
+
+ switch (option) {
+ case 'x':
+ export = TRUE;
+ break;
+ case 'a':
+ tmp = strtoul (optarg, NULL, 10);
+ if (tmp < 1 || tmp > 3000) {
+ fprintf (stderr, "Invalid delay: %s\n", optarg);
+ return 1;
+ }
+ delay_ms = (guint32) tmp;
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ case 'l':
+ logpath = optarg;
+ break;
+ case 'e':
+ vid = strtoul (optarg, NULL, 0);
+ if (vid == 0) {
+ fprintf (stderr, "Could not parse USB Vendor ID '%s'", optarg);
+ return 1;
+ }
+ break;
+ case 'p':
+ pid = strtoul (optarg, NULL, 0);
+ if (pid > G_MAXUINT32) {
+ fprintf (stderr, "Could not parse USB Product ID '%s'", optarg);
+ return 1;
+ }
+ break;
+ case 'i':
+ usbif = strtoul (optarg, NULL, 0);
+ if (usbif > 50) {
+ fprintf (stderr, "Could not parse USB interface number '%s'", optarg);
+ return 1;
+ }
+ break;
+ case 'd':
+ driver = optarg;
+ break;
+ case 'q':
+ quiet = TRUE;
+ break;
+ case 'h':
+ print_usage ();
+ return 0;
+ default:
+ return 1;
+ }
+ }
+
+ if (logpath) {
+ time_t t = time (NULL);
+
+ logfile = fopen (logpath, "a+");
+ if (!logfile) {
+ fprintf (stderr, "Couldn't open/create logfile %s", logpath);
+ return 2;
+ }
+
+ fprintf (logfile, "\n**** Started: %s\n", ctime (&t));
+ g_set_printerr_handler (printerr_handler);
+ }
+
+ g_set_print_handler (print_handler);
+
+ device = argv[optind];
+ if (device == NULL) {
+ g_printerr ("no node specified\n");
+ ret = 3;
+ goto exit;
+ }
+
+ verbose ("(%s): usb-vid 0x%04x usb-pid 0x%04x usb-intf %d driver '%s'",
+ device, vid, pid, usbif, driver);
+
+ /* Some devices just shouldn't be touched */
+ if (vid == HUAWEI_VENDOR_ID && usbif != 0) {
+ verbose ("(%s) ignoring Huawei USB interface #1", device);
+ if (export)
+ printf ("ID_MM_MODEM_PROBED=1\n");
+ goto exit;
+ }
+
+ verbose ("probing %s", device);
+
+ /* If a delay was specified, retry opening the serial port for that
+ * amount of time. Some devices (nozomi) aren't ready to be opened
+ * even though their device node is created by udev already.
+ */
+ do {
+ fd = open (device, O_RDWR | O_EXCL | O_NONBLOCK);
+ if (fd < 0) {
+ last_err = errno;
+ g_usleep (500000);
+ delay_ms -= 500;
+ }
+ } while (fd < 0 && delay_ms > 0);
+
+ if (fd < 0) {
+ g_printerr ("open(%s) failed: %d\n", device, last_err);
+ ret = 4;
+ goto exit;
+ }
+
+ if (tcgetattr (fd, &orig)) {
+ g_printerr ("tcgetattr(%s): failed %d\n", device, errno);
+ ret = 5;
+ goto exit;
+ }
+
+ memcpy (&attrs, &orig, sizeof (attrs));
+ attrs.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR);
+ attrs.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
+ attrs.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
+ attrs.c_lflag &= ~(ECHO | ECHOE);
+ attrs.c_cc[VMIN] = 1;
+ attrs.c_cc[VTIME] = 0;
+ attrs.c_cc[VEOF] = 1;
+
+ attrs.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
+ attrs.c_cflag |= (B9600 | CS8 | CREAD | PARENB);
+
+ tcsetattr (fd, TCSANOW, &attrs);
+ caps = modem_probe_caps (fd, delay_ms);
+ tcsetattr (fd, TCSANOW, &orig);
+
+ if (caps < 0) {
+ g_printerr ("%s: couldn't get modem capabilities\n", device);
+ if (export)
+ printf ("ID_MM_MODEM_PROBED=1\n");
+ goto exit;
+ }
+
+ if (export) {
+ if (caps & MODEM_CAP_GSM)
+ printf ("ID_MM_MODEM_GSM=1\n");
+ if (caps & MODEM_CAP_IS707_A)
+ printf ("ID_MM_MODEM_IS707_A=1\n");
+ if (caps & MODEM_CAP_IS707_P)
+ printf ("ID_MM_MODEM_IS707P=1\n");
+ if (caps & MODEM_CAP_IS856)
+ printf ("ID_MM_MODEM_IS856=1\n");
+ if (caps & MODEM_CAP_IS856_A)
+ printf ("ID_MM_MODEM_IS856_A=1\n");
+ printf ("ID_MM_MODEM_PROBED=1\n");
+ }
+
+ verbose ("%s: caps (0x%X)%s%s%s%s\n", device, caps,
+ caps & MODEM_CAP_GSM ? " GSM" : "",
+ caps & (MODEM_CAP_IS707_A | MODEM_CAP_IS707_P) ? " CDMA-1x" : "",
+ caps & MODEM_CAP_IS856 ? " EVDOr0" : "",
+ caps & MODEM_CAP_IS856_A ? " EVDOrA" : "");
+
+exit:
+ if (fd >= 0)
+ close (fd);
+ if (logfile)
+ fclose (logfile);
+ return ret;
+}
+
diff --git a/configure.in b/configure.in
index 5da50126..55884c96 100644
--- a/configure.in
+++ b/configure.in
@@ -12,7 +12,11 @@ AM_PROG_CC_C_O
AC_PROG_INSTALL
AC_PROG_LIBTOOL
-PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.75 glib-2.0 >= 2.14 gmodule-2.0 gobject-2.0 hal)
+PKG_CHECK_MODULES(MM, dbus-glib-1 >= 0.75 glib-2.0 >= 2.14 gmodule-2.0 gobject-2.0)
+
+PKG_CHECK_MODULES(GUDEV, gudev-1.0)
+AC_SUBST(GUDEV_CFLAGS)
+AC_SUBST(GUDEV_LIBS)
AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is]))
@@ -67,6 +71,7 @@ esac
AC_CONFIG_FILES([
Makefile
marshallers/Makefile
+callouts/Makefile
src/Makefile
plugins/Makefile
test/Makefile
diff --git a/introspection/mm-modem.xml b/introspection/mm-modem.xml
index ecf5ec8b..470b4a3d 100644
--- a/introspection/mm-modem.xml
+++ b/introspection/mm-modem.xml
@@ -51,7 +51,15 @@
<property name="Device" type="s" access="read">
<tp:docstring>
- The device to use for IP configuration and traffic.
+ The modem port to use for IP configuration and traffic.
+ </tp:docstring>
+ </property>
+
+ <property name="MasterDevice" type="s" access="read">
+ <tp:docstring>
+ The physical modem device reference (ie, USB, PCI, PCMCIA device), which
+ may be dependent upon the operating system. In Linux for example, this
+ points to a sysfs path of the usb_device object.
</tp:docstring>
</property>
diff --git a/marshallers/mm-marshal.list b/marshallers/mm-marshal.list
index 0ef23b37..48b99135 100644
--- a/marshallers/mm-marshal.list
+++ b/marshallers/mm-marshal.list
@@ -1 +1,2 @@
VOID:UINT,STRING,STRING
+VOID:STRING,STRING,UINT
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index ed8b2240..10b0ca25 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,4 +1,5 @@
pkglib_LTLIBRARIES = \
+ libmm-plugin-generic.la \
libmm-plugin-huawei.la \
libmm-plugin-hso.la \
libmm-plugin-mbm.la \
@@ -8,6 +9,22 @@ pkglib_LTLIBRARIES = \
libmm-plugin-nokia.la \
libmm-plugin-zte.la
+# Generic
+
+libmm_plugin_generic_la_SOURCES = \
+ mm-plugin-generic.c \
+ mm-plugin-generic.h
+
+libmm_plugin_generic_la_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ $(GUDEV_CFLAGS) \
+ -I$(top_srcdir)/src
+
+libmm_plugin_generic_la_LDFLAGS = \
+ $(GUDEV_LDFLAGS) \
+ -module \
+ -avoid-version
+
# Huawei
libmm_plugin_huawei_la_SOURCES = \
diff --git a/plugins/mm-plugin-generic.c b/plugins/mm-plugin-generic.c
new file mode 100644
index 00000000..f504a6ee
--- /dev/null
+++ b/plugins/mm-plugin-generic.c
@@ -0,0 +1,350 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2008 - 2009 Dan Williams <dcbw@redhat.com>
+ */
+
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <gmodule.h>
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+
+#include "mm-plugin-generic.h"
+#include "mm-generic-gsm.h"
+#include "mm-generic-cdma.h"
+#include "mm-serial-port.h"
+
+static void plugin_init (MMPlugin *plugin_class);
+
+G_DEFINE_TYPE_EXTENDED (MMPluginGeneric, mm_plugin_generic, G_TYPE_OBJECT,
+ 0, G_IMPLEMENT_INTERFACE (MM_TYPE_PLUGIN, plugin_init))
+
+int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
+#define MM_PLUGIN_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericPrivate))
+
+typedef struct {
+ GUdevClient *client;
+ GHashTable *modems;
+} MMPluginGenericPrivate;
+
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_GENERIC, NULL));
+}
+
+/*****************************************************************************/
+
+static char *
+get_driver_name (GUdevDevice *device)
+{
+ GUdevDevice *parent = NULL;
+ const char *driver;
+ char *ret;
+
+ driver = g_udev_device_get_driver (device);
+ if (!driver) {
+ parent = g_udev_device_get_parent (device);
+ if (parent)
+ driver = g_udev_device_get_driver (parent);
+ }
+
+ if (driver)
+ ret = g_strdup (driver);
+ if (parent)
+ g_object_unref (parent);
+
+ return ret;
+}
+
+static GUdevDevice *
+find_physical_device (GUdevDevice *child)
+{
+ GUdevDevice *iter, *old = NULL;
+ const char *bus, *type;
+
+ g_return_val_if_fail (child != NULL, NULL);
+
+ bus = g_udev_device_get_property (child, "ID_BUS");
+ if (!bus)
+ return NULL;
+
+ if (!strcmp (bus, "usb")) {
+ /* Walk the parents to find the 'usb_device' for this device. */
+ iter = g_object_ref (child);
+ while (iter) {
+ type = g_udev_device_get_devtype (iter);
+ if (type && !strcmp (type, "usb_device"))
+ return iter;
+
+ old = iter;
+ iter = g_udev_device_get_parent (old);
+ g_object_unref (old);
+ }
+ g_object_unref (child);
+ } else if (!strcmp (bus, "pci")) {
+ return g_udev_device_get_parent (child);
+ }
+
+ // FIXME: pci and pcmcia/cardbus? (like Sierra 850/860)
+ return NULL;
+}
+
+#define PROP_GSM "ID_MM_MODEM_GSM"
+#define PROP_CDMA "ID_MM_MODEM_IS707_A"
+#define PROP_EVDO1 "ID_MM_MODEM_IS856"
+#define PROP_EVDOA "ID_MM_MODEM_IS856_A"
+
+static GUdevDevice *
+get_device_type (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ gboolean *gsm,
+ gboolean *cdma)
+{
+ MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (plugin);
+ GUdevDevice *device;
+
+ g_return_val_if_fail (plugin != NULL, NULL);
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
+ g_return_val_if_fail (subsys != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ device = g_udev_client_query_by_subsystem_and_name (priv->client, subsys, name);
+ if (!device)
+ return NULL;
+
+ if (g_udev_device_get_property_as_boolean (device, PROP_GSM))
+ *gsm = TRUE;
+ if ( g_udev_device_get_property_as_boolean (device, PROP_CDMA)
+ || g_udev_device_get_property_as_boolean (device, PROP_EVDO1)
+ || g_udev_device_get_property_as_boolean (device, PROP_EVDOA))
+ *cdma = TRUE;
+
+ return device;
+}
+
+static guint32
+supports_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name)
+{
+ GUdevDevice *device, *physdev = NULL;
+ gboolean gsm = FALSE, cdma = FALSE;
+ guint32 level = 0;
+
+ g_return_val_if_fail (plugin != NULL, 0);
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), 0);
+ g_return_val_if_fail (subsys != NULL, 0);
+ g_return_val_if_fail (name != NULL, 0);
+
+ /* Can't do anything with non-serial ports */
+ if (strcmp (subsys, "tty"))
+ return 0;
+
+ device = get_device_type (plugin, subsys, name, &gsm, &cdma);
+ if (!device)
+ return 0;
+
+ if (!gsm && !cdma)
+ goto out;
+
+ physdev = find_physical_device (device);
+ if (!physdev)
+ goto out;
+ g_object_unref (physdev);
+ level = 5;
+
+out:
+ return level;
+}
+
+typedef struct {
+ char *key;
+ gpointer modem;
+} FindInfo;
+
+static void
+find_modem (gpointer key, gpointer data, gpointer user_data)
+{
+ FindInfo *info = user_data;
+
+ if (!info->key && data == info->modem)
+ info->key = g_strdup ((const char *) key);
+}
+
+static void
+modem_destroyed (gpointer data, GObject *modem)
+{
+ MMPluginGeneric *self = MM_PLUGIN_GENERIC (data);
+ MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (self);
+ FindInfo info = { NULL, modem };
+
+ g_hash_table_foreach (priv->modems, find_modem, &info);
+ if (info.key)
+ g_hash_table_remove (priv->modems, info.key);
+ g_free (info.key);
+}
+
+static MMModem *
+grab_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ GError **error)
+{
+ MMPluginGeneric *self = MM_PLUGIN_GENERIC (plugin);
+ MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (plugin);
+ GUdevDevice *device = NULL, *physdev = NULL;
+ const char *devfile, *sysfs_path;
+ char *driver = NULL;
+ MMModem *modem = NULL;
+ gboolean gsm = FALSE, cdma = FALSE;
+
+ g_return_val_if_fail (subsys != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ /* Can't do anything with non-serial ports */
+ if (strcmp (subsys, "tty"))
+ return NULL;
+
+ device = get_device_type (plugin, subsys, name, &gsm, &cdma);
+ if (!device) {
+ g_set_error (error, 0, 0, "Could not get port's udev device.");
+ return NULL;
+ }
+
+ if (!gsm && !cdma) {
+ g_set_error (error, 0, 0, "Modem unsupported (not GSM or CDMA).");
+ goto out;
+ }
+
+ physdev = find_physical_device (device);
+ if (!physdev) {
+ g_set_error (error, 0, 0, "Could not get ports's physical device.");
+ goto out;
+ }
+
+ devfile = g_udev_device_get_device_file (device);
+ if (!devfile) {
+ g_set_error (error, 0, 0, "Could not get port's sysfs file.");
+ goto out;
+ }
+
+ driver = get_driver_name (device);
+ if (!devfile) {
+ g_set_error (error, 0, 0, "Could not get port's driver name.");
+ goto out;
+ }
+
+ sysfs_path = g_udev_device_get_sysfs_path (physdev);
+ if (!devfile) {
+ g_set_error (error, 0, 0, "Could not get port's physical device sysfs path.");
+ goto out;
+ }
+
+ modem = g_hash_table_lookup (priv->modems, sysfs_path);
+ if (!modem) {
+ if (gsm) {
+ modem = mm_generic_gsm_new (sysfs_path,
+ driver,
+ mm_plugin_get_name (plugin));
+ } else if (cdma) {
+ modem = mm_generic_cdma_new (sysfs_path,
+ driver,
+ mm_plugin_get_name (plugin));
+ }
+
+ if (modem) {
+ if (!mm_modem_grab_port (modem, subsys, name, error)) {
+ g_object_unref (modem);
+ modem = NULL;
+ }
+ }
+
+ if (modem) {
+ g_object_weak_ref (G_OBJECT (modem), modem_destroyed, self);
+ g_hash_table_insert (priv->modems, g_strdup (sysfs_path), modem);
+ }
+ } else {
+ if (gsm || cdma) {
+ if (!mm_modem_grab_port (modem, subsys, name, error))
+ modem = NULL;
+ }
+ }
+
+out:
+ g_free (driver);
+ g_object_unref (device);
+ g_object_unref (physdev);
+ return modem;
+}
+
+static const char *
+get_name (MMPlugin *plugin)
+{
+ return "Generic";
+}
+
+/*****************************************************************************/
+
+static void
+plugin_init (MMPlugin *plugin_class)
+{
+ /* interface implementation */
+ plugin_class->get_name = get_name;
+ plugin_class->supports_port = supports_port;
+ plugin_class->grab_port = grab_port;
+}
+
+static void
+mm_plugin_generic_init (MMPluginGeneric *self)
+{
+ MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (self);
+ const char *subsys[2] = { "tty", NULL };
+
+ priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ priv->client = g_udev_client_new (subsys);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMPluginGenericPrivate *priv = MM_PLUGIN_GENERIC_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->modems);
+ g_object_unref (priv->client);
+}
+
+static void
+mm_plugin_generic_class_init (MMPluginGenericClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMPluginGenericPrivate));
+
+ object_class->dispose = dispose;
+}
+
diff --git a/plugins/mm-plugin-generic.h b/plugins/mm-plugin-generic.h
new file mode 100644
index 00000000..991784db
--- /dev/null
+++ b/plugins/mm-plugin-generic.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifndef MM_PLUGIN_GENERIC_H
+#define MM_PLUGIN_GENERIC_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_GENERIC (mm_plugin_generic_get_type ())
+#define MM_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGeneric))
+#define MM_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
+#define MM_IS_PLUGIN_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GENERIC))
+#define MM_IS_PLUGIN_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GENERIC))
+#define MM_PLUGIN_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GENERIC, MMPluginGenericClass))
+
+typedef struct {
+ GObject parent;
+} MMPluginGeneric;
+
+typedef struct {
+ GObjectClass parent;
+} MMPluginGenericClass;
+
+GType mm_plugin_generic_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_GENERIC_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index d6d4e73f..e1557341 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,27 +2,35 @@ sbin_PROGRAMS = modem-manager
modem_manager_CPPFLAGS = \
$(MM_CFLAGS) \
+ $(GUDEV_CFLAGS) \
-I${top_builddir}/marshallers \
-DPLUGINDIR=\"$(pkglibdir)\"
modem_manager_LDADD = \
$(MM_LIBS) \
+ $(GUDEV_LIBS) \
$(top_builddir)/marshallers/libmarshallers.la
modem_manager_SOURCES = \
main.c \
mm-callback-info.c \
mm-callback-info.h \
- mm-generic-cdma.c \
- mm-generic-cdma.h \
- mm-generic-gsm.c \
- mm-generic-gsm.h \
mm-errors.c \
mm-errors.h \
mm-manager.c \
mm-manager.h \
mm-modem.c \
mm-modem.h \
+ mm-serial.c \
+ mm-serial.h \
+ mm-serial-port.c \
+ mm-serial-port.h \
+ mm-serial-parsers.c \
+ mm-serial-parsers.h \
+ mm-generic-cdma.c \
+ mm-generic-cdma.h \
+ mm-generic-gsm.c \
+ mm-generic-gsm.h \
mm-modem-cdma.c \
mm-modem-cdma.h \
mm-modem-gsm-card.c \
@@ -36,11 +44,7 @@ modem_manager_SOURCES = \
mm-options.c \
mm-options.h \
mm-plugin.c \
- mm-plugin.h \
- mm-serial.c \
- mm-serial.h \
- mm-serial-parsers.c \
- mm-serial-parsers.h
+ mm-plugin.h
mm-manager-glue.h: $(top_srcdir)/introspection/mm-manager.xml
dbus-binding-tool --prefix=mm_manager --mode=glib-server --output=$@ $<
diff --git a/src/main.c b/src/main.c
index 629b10ed..99257067 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,7 +5,6 @@
#include <string.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
-#include <libhal.h>
#include "mm-manager.h"
#include "mm-options.h"
@@ -135,87 +134,11 @@ create_dbus_proxy (DBusGConnection *bus)
return proxy;
}
-static void
-hal_init (MMManager *manager)
-{
- LibHalContext *hal_ctx;
- DBusError dbus_error;
-
- hal_ctx = libhal_ctx_new ();
- if (!hal_ctx) {
- g_warning ("Could not get connection to the HAL service.");
- }
-
- libhal_ctx_set_dbus_connection (hal_ctx, dbus_g_connection_get_connection (mm_manager_get_bus (manager)));
-
- dbus_error_init (&dbus_error);
- if (!libhal_ctx_init (hal_ctx, &dbus_error)) {
- g_warning ("libhal_ctx_init() failed: %s", dbus_error.message);
- dbus_error_free (&dbus_error);
- }
-
- mm_manager_set_hal_ctx (manager, hal_ctx);
-}
-
-static void
-hal_deinit (MMManager *manager)
-{
- LibHalContext *hal_ctx;
-
- hal_ctx = mm_manager_get_hal_ctx (manager);
- if (hal_ctx) {
- libhal_ctx_shutdown (hal_ctx, NULL);
- libhal_ctx_free (hal_ctx);
- mm_manager_set_hal_ctx (manager, NULL);
- }
-}
-
static gboolean
-hal_on_bus (DBusGProxy *proxy)
+start_manager (gpointer user_data)
{
- GError *err = NULL;
- gboolean has_owner = FALSE;
-
- if (!dbus_g_proxy_call (proxy,
- "NameHasOwner", &err,
- G_TYPE_STRING, HAL_DBUS_SERVICE,
- G_TYPE_INVALID,
- G_TYPE_BOOLEAN, &has_owner,
- G_TYPE_INVALID)) {
- g_warning ("Error on NameHasOwner DBUS call: %s", err->message);
- g_error_free (err);
- }
-
- return has_owner;
-}
-
-static void
-name_owner_changed (DBusGProxy *proxy,
- const char *name,
- const char *old_owner,
- const char *new_owner,
- gpointer user_data)
-{
- MMManager *manager;
- gboolean old_owner_good;
- gboolean new_owner_good;
-
- /* Only care about signals from HAL */
- if (strcmp (name, HAL_DBUS_SERVICE))
- return;
-
- manager = MM_MANAGER (user_data);
- old_owner_good = (old_owner && (strlen (old_owner) > 0));
- new_owner_good = (new_owner && (strlen (new_owner) > 0));
-
- if (!old_owner_good && new_owner_good) {
- g_message ("HAL appeared");
- hal_init (manager);
- } else if (old_owner_good && !new_owner_good) {
- /* HAL went away. Bad HAL. */
- g_message ("HAL disappeared");
- hal_deinit (manager);
- }
+ mm_manager_start (MM_MANAGER (user_data));
+ return FALSE;
}
int
@@ -249,21 +172,13 @@ main (int argc, char *argv[])
return -1;
manager = mm_manager_new (bus);
-
- dbus_g_proxy_connect_signal (proxy,
- "NameOwnerChanged",
- G_CALLBACK (name_owner_changed),
- manager, NULL);
-
- if (hal_on_bus (proxy))
- hal_init (manager);
+ g_idle_add (start_manager, manager);
loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (proxy, "destroy", G_CALLBACK (destroy_cb), loop);
g_main_loop_run (loop);
- hal_deinit (manager);
g_object_unref (manager);
g_object_unref (proxy);
dbus_g_connection_unref (bus);
diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c
index de8c916a..984b8aa8 100644
--- a/src/mm-generic-cdma.c
+++ b/src/mm-generic-cdma.c
@@ -16,25 +16,124 @@ static gpointer mm_generic_cdma_parent_class = NULL;
typedef struct {
char *driver;
+ char *plugin;
+ char *device;
+
+ guint32 ip_method;
+ gboolean valid;
+
+ MMSerialPort *primary;
+ MMSerialPort *secondary;
} MMGenericCdmaPrivate;
MMModem *
-mm_generic_cdma_new (const char *serial_device, const char *driver)
+mm_generic_cdma_new (const char *device,
+ const char *driver,
+ const char *plugin)
{
- g_return_val_if_fail (serial_device != NULL, NULL);
+ g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
return MM_MODEM (g_object_new (MM_TYPE_GENERIC_CDMA,
- MM_SERIAL_DEVICE, serial_device,
+ MM_MODEM_MASTER_DEVICE, device,
MM_MODEM_DRIVER, driver,
- MM_MODEM_TYPE, MM_MODEM_TYPE_CDMA,
+ MM_MODEM_PLUGIN, plugin,
NULL));
}
/*****************************************************************************/
static void
-enable_error_reporting_done (MMSerial *serial,
+check_valid (MMGenericCdma *self)
+{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+ gboolean new_valid = FALSE;
+
+ if (priv->primary)
+ new_valid = TRUE;
+
+ if (priv->valid != new_valid) {
+ priv->valid = new_valid;
+ g_object_notify (G_OBJECT (self), MM_MODEM_VALID);
+ }
+}
+
+static gboolean
+owns_port (MMModem *modem, const char *subsys, const char *name)
+{
+ MMGenericCdma *self = MM_GENERIC_CDMA (modem);
+
+ if (strcmp (subsys, "tty"))
+ return FALSE;
+
+ return !!mm_serial_get_port (MM_SERIAL (self), name);
+}
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ GError **error)
+{
+ MMGenericCdma *self = MM_GENERIC_CDMA (modem);
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (self);
+ MMSerialPortType ptype = MM_SERIAL_PORT_TYPE_IGNORED;
+ MMSerialPort *port;
+
+ if (strcmp (subsys, "tty"))
+ return FALSE;
+
+ if (!priv->primary)
+ ptype = MM_SERIAL_PORT_TYPE_PRIMARY;
+ else if (!priv->secondary)
+ ptype = MM_SERIAL_PORT_TYPE_SECONDARY;
+
+ port = mm_serial_add_port (MM_SERIAL (self), name, ptype);
+ g_object_set (G_OBJECT (port), MM_SERIAL_PORT_CARRIER_DETECT, FALSE, NULL);
+ mm_serial_port_set_response_parser (port,
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
+
+ if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) {
+ priv->primary = port;
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+ check_valid (self);
+ } else if (ptype == MM_SERIAL_PORT_TYPE_SECONDARY)
+ priv->secondary = port;
+
+ return TRUE;
+}
+
+static void
+release_port (MMModem *modem, const char *subsys, const char *name)
+{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
+ MMSerialPort *port;
+
+ if (strcmp (subsys, "tty"))
+ return;
+
+ port = mm_serial_get_port (MM_SERIAL (modem), name);
+ if (!port)
+ return;
+
+ if (port == priv->primary) {
+ mm_serial_remove_port (MM_SERIAL (modem), port);
+ priv->primary = NULL;
+ }
+
+ if (port == priv->secondary) {
+ mm_serial_remove_port (MM_SERIAL (modem), port);
+ priv->secondary = NULL;
+ }
+
+ check_valid (MM_GENERIC_CDMA (modem));
+}
+
+static void
+enable_error_reporting_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -49,7 +148,7 @@ enable_error_reporting_done (MMSerial *serial,
}
static void
-init_done (MMSerial *serial,
+init_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -65,14 +164,14 @@ init_done (MMSerial *serial,
FIXME: It's mandatory by spec, so it really shouldn't be optional. Figure
out which CDMA modems have problems with it and implement plugin for them.
*/
- mm_serial_queue_command (serial, "+CMEE=1", 3, enable_error_reporting_done, user_data);
+ mm_serial_port_queue_command (port, "+CMEE=1", 3, enable_error_reporting_done, user_data);
}
}
static void
-flash_done (MMSerial *serial, gpointer user_data)
+flash_done (MMSerialPort *port, gpointer user_data)
{
- mm_serial_queue_command (serial, "Z E0 V1 X4 &C1", 3, init_done, user_data);
+ mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1", 3, init_done, user_data);
}
static void
@@ -81,25 +180,26 @@ enable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_new (modem, callback, user_data);
if (!do_enable) {
- mm_serial_close (MM_SERIAL (modem));
+ mm_serial_port_close (priv->primary);
mm_callback_info_schedule (info);
return;
}
- if (mm_serial_open (MM_SERIAL (modem), &info->error))
- mm_serial_flash (MM_SERIAL (modem), 100, flash_done, info);
+ if (mm_serial_port_open (priv->primary, &info->error))
+ mm_serial_port_flash (priv->primary, 100, flash_done, info);
if (info->error)
mm_callback_info_schedule (info);
}
static void
-dial_done (MMSerial *serial,
+dial_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -118,17 +218,18 @@ connect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (modem, callback, user_data);
command = g_strconcat ("DT", number, NULL);
- mm_serial_queue_command (MM_SERIAL (modem), command, 60, dial_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 60, dial_done, info);
g_free (command);
}
static void
-disconnect_flash_done (MMSerial *serial, gpointer user_data)
+disconnect_flash_done (MMSerialPort *port, gpointer user_data)
{
mm_callback_info_schedule ((MMCallbackInfo *) user_data);
}
@@ -138,14 +239,17 @@ disconnect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
+ g_return_if_fail (priv->primary != NULL);
+
info = mm_callback_info_new (modem, callback, user_data);
- mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info);
+ mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
}
static void
-get_signal_quality_done (MMSerial *serial,
+get_signal_quality_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -182,10 +286,15 @@ get_signal_quality (MMModemCdma *modem,
MMModemUIntFn callback,
gpointer user_data)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info);
+ /* Prefer secondary port for signal strength */
+ mm_serial_port_queue_command (priv->secondary ? priv->secondary : priv->primary,
+ "+CSQ",
+ 3,
+ get_signal_quality_done, info);
}
/*****************************************************************************/
@@ -261,6 +370,7 @@ simple_connect (MMModemSimple *simple,
gpointer user_data)
{
MMCallbackInfo *info;
+ GError *error = NULL;
info = mm_callback_info_new (MM_MODEM (simple), callback, user_data);
mm_callback_info_set_data (info, "simple-connect-properties",
@@ -268,12 +378,12 @@ simple_connect (MMModemSimple *simple,
(GDestroyNotify) g_hash_table_unref);
/* At least number must be present */
- if (!simple_get_string_property (info, "number", &info->error)) {
- if (!info->error)
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Missing number property");
+ if (!simple_get_string_property (info, "number", &error)) {
+ if (!error)
+ error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Missing number property");
}
- simple_state_machine (MM_MODEM (simple), NULL, info);
+ simple_state_machine (MM_MODEM (simple), error, info);
}
static void
@@ -340,6 +450,9 @@ simple_get_status (MMModemSimple *simple,
static void
modem_init (MMModem *modem_class)
{
+ modem_class->owns_port = owns_port;
+ modem_class->grab_port = grab_port;
+ modem_class->release_port = release_port;
modem_class->enable = enable;
modem_class->connect = connect;
modem_class->disconnect = disconnect;
@@ -361,24 +474,32 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_cdma_init (MMGenericCdma *self)
{
- mm_serial_set_response_parser (MM_SERIAL (self),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object);
+
switch (prop_id) {
case MM_MODEM_PROP_DRIVER:
/* Construct only */
- MM_GENERIC_CDMA_GET_PRIVATE (object)->driver = g_value_dup_string (value);
+ priv->driver = g_value_dup_string (value);
+ break;
+ case MM_MODEM_PROP_PLUGIN:
+ /* Construct only */
+ priv->plugin = g_value_dup_string (value);
+ break;
+ case MM_MODEM_PROP_MASTER_DEVICE:
+ /* Constrcut only */
+ priv->device = g_value_dup_string (value);
break;
- case MM_MODEM_PROP_DEVICE:
- case MM_MODEM_PROP_TYPE:
case MM_MODEM_PROP_IP_METHOD:
+ priv->ip_method = g_value_get_uint (value);
+ break;
+ case MM_MODEM_PROP_TYPE:
+ case MM_MODEM_PROP_VALID:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -390,18 +511,32 @@ static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
+ MMGenericCdmaPrivate *priv = MM_GENERIC_CDMA_GET_PRIVATE (object);
+
switch (prop_id) {
- case MM_MODEM_PROP_DEVICE:
- g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object)));
+ case MM_MODEM_PROP_DATA_DEVICE:
+ if (priv->primary)
+ g_value_set_string (value, mm_serial_port_get_device (priv->primary));
+ else
+ g_value_set_string (value, NULL);
+ break;
+ case MM_MODEM_PROP_MASTER_DEVICE:
+ g_value_set_string (value, priv->device);
break;
case MM_MODEM_PROP_DRIVER:
- g_value_set_string (value, MM_GENERIC_CDMA_GET_PRIVATE (object)->driver);
+ g_value_set_string (value, priv->driver);
+ break;
+ case MM_MODEM_PROP_PLUGIN:
+ g_value_set_string (value, priv->plugin);
break;
case MM_MODEM_PROP_TYPE:
g_value_set_uint (value, MM_MODEM_TYPE_CDMA);
break;
case MM_MODEM_PROP_IP_METHOD:
- g_value_set_uint (value, MM_MODEM_IP_METHOD_PPP);
+ g_value_set_uint (value, priv->ip_method);
+ break;
+ case MM_MODEM_PROP_VALID:
+ g_value_set_boolean (value, priv->valid);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -434,20 +569,32 @@ mm_generic_cdma_class_init (MMGenericCdmaClass *klass)
/* Properties */
g_object_class_override_property (object_class,
- MM_MODEM_PROP_DEVICE,
- MM_MODEM_DEVICE);
+ MM_MODEM_PROP_DATA_DEVICE,
+ MM_MODEM_DATA_DEVICE);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_MASTER_DEVICE,
+ MM_MODEM_MASTER_DEVICE);
g_object_class_override_property (object_class,
MM_MODEM_PROP_DRIVER,
MM_MODEM_DRIVER);
g_object_class_override_property (object_class,
+ MM_MODEM_PROP_PLUGIN,
+ MM_MODEM_PLUGIN);
+
+ g_object_class_override_property (object_class,
MM_MODEM_PROP_TYPE,
MM_MODEM_TYPE);
g_object_class_override_property (object_class,
MM_MODEM_PROP_IP_METHOD,
MM_MODEM_IP_METHOD);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_VALID,
+ MM_MODEM_VALID);
}
GType
diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h
index 125b540a..c8d25518 100644
--- a/src/mm-generic-cdma.h
+++ b/src/mm-generic-cdma.h
@@ -6,12 +6,12 @@
#include "mm-modem.h"
#include "mm-serial.h"
-#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ())
-#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma))
-#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
-#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA))
-#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA))
-#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
+#define MM_TYPE_GENERIC_CDMA (mm_generic_cdma_get_type ())
+#define MM_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdma))
+#define MM_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
+#define MM_IS_GENERIC_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_CDMA))
+#define MM_IS_GENERIC_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_CDMA))
+#define MM_GENERIC_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_CDMA, MMGenericCdmaClass))
typedef struct {
MMSerial parent;
@@ -23,7 +23,8 @@ typedef struct {
GType mm_generic_cdma_get_type (void);
-MMModem *mm_generic_cdma_new (const char *serial_device,
- const char *driver);
+MMModem *mm_generic_cdma_new (const char *device,
+ const char *driver,
+ const char *plugin);
#endif /* MM_GENERIC_CDMA_H */
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index a566fb6e..3cf8ab8c 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -18,10 +18,14 @@ static gpointer mm_generic_gsm_parent_class = NULL;
typedef struct {
char *driver;
+ char *plugin;
+ char *device;
+
+ gboolean valid;
+
char *data_device;
char *oper_code;
char *oper_name;
- guint32 modem_type;
guint32 ip_method;
gboolean unsolicited_registration;
@@ -30,24 +34,39 @@ typedef struct {
guint32 signal_quality;
guint32 cid;
+
+ MMSerialPort *primary;
+ MMSerialPort *secondary;
} MMGenericGsmPrivate;
-static void get_registration_status (MMSerial *serial, MMCallbackInfo *info);
-static void read_operator_done (MMSerial *serial,
- GString *response,
- GError *error,
- gpointer user_data);
+static void get_registration_status (MMSerialPort *port, MMCallbackInfo *info);
+static void read_operator_code_done (MMSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static void read_operator_name_done (MMSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static void reg_state_changed (MMSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data);
MMModem *
-mm_generic_gsm_new (const char *serial_device, const char *driver)
+mm_generic_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin)
{
- g_return_val_if_fail (serial_device != NULL, NULL);
+ g_return_val_if_fail (device != NULL, NULL);
g_return_val_if_fail (driver != NULL, NULL);
+ g_return_val_if_fail (plugin != NULL, NULL);
return MM_MODEM (g_object_new (MM_TYPE_GENERIC_GSM,
- MM_SERIAL_DEVICE, serial_device,
+ MM_MODEM_MASTER_DEVICE, device,
MM_MODEM_DRIVER, driver,
- MM_MODEM_TYPE, MM_MODEM_TYPE_GSM,
+ MM_MODEM_PLUGIN, plugin,
NULL));
}
@@ -101,8 +120,8 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
- mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,2;+COPS?", 3, read_operator_done, GINT_TO_POINTER (0));
- mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,0;+COPS?", 3, read_operator_done, GINT_TO_POINTER (1));
+ mm_serial_port_queue_command (priv->primary, "+COPS=3,2;+COPS?", 3, read_operator_code_done, modem);
+ mm_serial_port_queue_command (priv->primary, "+COPS=3,0;+COPS?", 3, read_operator_name_done, modem);
mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL);
} else {
g_free (priv->oper_code);
@@ -116,7 +135,7 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
}
static void
-pin_check_done (MMSerial *serial,
+pin_check_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -150,18 +169,113 @@ mm_generic_gsm_check_pin (MMGenericGsm *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- mm_serial_queue_command (MM_SERIAL (modem), "+CPIN?", 3, pin_check_done, info);
+ mm_serial_port_queue_command (priv->primary, "+CPIN?", 3, pin_check_done, info);
}
/*****************************************************************************/
static void
-enable_done (MMSerial *serial,
+check_valid (MMGenericGsm *self)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ gboolean new_valid = FALSE;
+
+ if (priv->primary)
+ new_valid = TRUE;
+
+ if (priv->valid != new_valid) {
+ priv->valid = new_valid;
+ g_object_notify (G_OBJECT (self), MM_MODEM_VALID);
+ }
+}
+
+static gboolean
+owns_port (MMModem *modem, const char *subsys, const char *name)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+
+ if (strcmp (subsys, "tty"))
+ return FALSE;
+
+ return !!mm_serial_get_port (MM_SERIAL (self), name);
+}
+
+static gboolean
+grab_port (MMModem *modem,
+ const char *subsys,
+ const char *name,
+ GError **error)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMSerialPortType ptype = MM_SERIAL_PORT_TYPE_IGNORED;
+ MMSerialPort *port;
+ GRegex *regex;
+
+ if (strcmp (subsys, "tty"))
+ return FALSE;
+
+ if (!priv->primary)
+ ptype = MM_SERIAL_PORT_TYPE_PRIMARY;
+ else if (!priv->secondary)
+ ptype = MM_SERIAL_PORT_TYPE_SECONDARY;
+
+ port = mm_serial_add_port (MM_SERIAL (self), name, ptype);
+ mm_serial_port_set_response_parser (port,
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
+
+ regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ mm_serial_port_add_unsolicited_msg_handler (port, regex, reg_state_changed, self, NULL);
+ g_regex_unref (regex);
+
+
+ if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) {
+ priv->primary = port;
+ g_object_notify (G_OBJECT (self), MM_MODEM_DATA_DEVICE);
+ check_valid (self);
+ } else if (ptype == MM_SERIAL_PORT_TYPE_SECONDARY)
+ priv->secondary = port;
+
+ return TRUE;
+}
+
+static void
+release_port (MMModem *modem, const char *subsys, const char *name)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMSerialPort *port;
+
+ if (strcmp (subsys, "tty"))
+ return;
+
+ port = mm_serial_get_port (MM_SERIAL (modem), name);
+ if (!port)
+ return;
+
+ if (port == priv->primary) {
+ mm_serial_remove_port (MM_SERIAL (modem), port);
+ priv->primary = NULL;
+ check_valid (MM_GENERIC_GSM (modem));
+ }
+
+ if (port == priv->secondary) {
+ mm_serial_remove_port (MM_SERIAL (modem), port);
+ priv->secondary = NULL;
+ check_valid (MM_GENERIC_GSM (modem));
+ }
+}
+
+static void
+enable_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -175,7 +289,7 @@ enable_done (MMSerial *serial,
}
static void
-init_done (MMSerial *serial,
+init_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -186,35 +300,35 @@ init_done (MMSerial *serial,
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else {
- if (MM_GENERIC_GSM_GET_PRIVATE (serial)->unsolicited_registration)
- mm_serial_queue_command (serial, "+CREG=1", 5, NULL, NULL);
+ if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration)
+ mm_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL);
else
- mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL);
+ mm_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL);
- mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data);
+ mm_serial_port_queue_command (port, "+CFUN=1", 5, enable_done, user_data);
}
}
static void
-enable_flash_done (MMSerial *serial, gpointer user_data)
+enable_flash_done (MMSerialPort *port, gpointer user_data)
{
- mm_serial_queue_command (serial, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data);
+ mm_serial_port_queue_command (port, "Z E0 V1 X4 &C1 +CMEE=1", 3, init_done, user_data);
}
static void
-disable_done (MMSerial *serial,
+disable_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
- mm_serial_close (serial);
+ mm_serial_port_close (port);
mm_callback_info_schedule ((MMCallbackInfo *) user_data);
}
static void
-disable_flash_done (MMSerial *serial, gpointer user_data)
+disable_flash_done (MMSerialPort *port, gpointer user_data)
{
- mm_serial_queue_command (serial, "+CFUN=0", 5, disable_done, user_data);
+ mm_serial_port_queue_command (port, "+CFUN=0", 5, disable_done, user_data);
}
static void
@@ -223,6 +337,7 @@ enable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
/* First, reset the previously used CID */
@@ -233,13 +348,13 @@ enable (MMModem *modem,
if (!do_enable) {
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
- if (mm_serial_is_connected (MM_SERIAL (modem)))
- mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info);
+ if (mm_serial_port_is_connected (priv->primary))
+ mm_serial_port_flash (priv->primary, 1000, disable_flash_done, info);
else
- disable_flash_done (MM_SERIAL (modem), info);
+ disable_flash_done (priv->primary, info);
} else {
- if (mm_serial_open (MM_SERIAL (modem), &info->error))
- mm_serial_flash (MM_SERIAL (modem), 100, enable_flash_done, info);
+ if (mm_serial_port_open (priv->primary, &info->error))
+ mm_serial_port_flash (priv->primary, 100, enable_flash_done, info);
if (info->error)
mm_callback_info_schedule (info);
@@ -247,7 +362,7 @@ enable (MMModem *modem,
}
static void
-get_string_done (MMSerial *serial,
+get_string_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -267,10 +382,11 @@ get_imei (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_queue_command_cached (MM_SERIAL (modem), "+CGSN", 3, get_string_done, info);
+ mm_serial_port_queue_command_cached (priv->primary, "+CGSN", 3, get_string_done, info);
}
static void
@@ -278,10 +394,11 @@ get_imsi (MMModemGsmCard *modem,
MMModemStringFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_string_new (MM_MODEM (modem), callback, user_data);
- mm_serial_queue_command_cached (MM_SERIAL (modem), "+CIMI", 3, get_string_done, info);
+ mm_serial_port_queue_command_cached (priv->primary, "+CIMI", 3, get_string_done, info);
}
static void
@@ -297,7 +414,7 @@ gsm_card_info_invoke (MMCallbackInfo *info)
}
static void
-get_version_done (MMSerial *serial,
+get_version_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -313,7 +430,7 @@ get_version_done (MMSerial *serial,
}
static void
-get_model_done (MMSerial *serial,
+get_model_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -327,7 +444,7 @@ get_model_done (MMSerial *serial,
}
static void
-get_manufacturer_done (MMSerial *serial,
+get_manufacturer_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -345,21 +462,21 @@ get_card_info (MMModemGsmCard *modem,
MMModemGsmCardInfoFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
- MMSerial *serial = MM_SERIAL (modem);
info = mm_callback_info_new_full (MM_MODEM (modem),
gsm_card_info_invoke,
G_CALLBACK (callback),
user_data);
- mm_serial_queue_command_cached (serial, "+CGMI", 3, get_manufacturer_done, info);
- mm_serial_queue_command_cached (serial, "+CGMM", 3, get_model_done, info);
- mm_serial_queue_command_cached (serial, "+CGMR", 3, get_version_done, info);
+ mm_serial_port_queue_command_cached (priv->primary, "+CGMI", 3, get_manufacturer_done, info);
+ mm_serial_port_queue_command_cached (priv->primary, "+CGMM", 3, get_model_done, info);
+ mm_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info);
}
static void
-send_puk_done (MMSerial *serial,
+send_puk_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -378,17 +495,18 @@ send_puk (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin);
- mm_serial_queue_command (MM_SERIAL (modem), command, 3, send_puk_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 3, send_puk_done, info);
g_free (command);
}
static void
-send_pin_done (MMSerial *serial,
+send_pin_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -406,17 +524,18 @@ send_pin (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CPIN=\"%s\"", pin);
- mm_serial_queue_command (MM_SERIAL (modem), command, 3, send_pin_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 3, send_pin_done, info);
g_free (command);
}
static void
-enable_pin_done (MMSerial *serial,
+enable_pin_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -435,17 +554,18 @@ enable_pin (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"", enabled ? 1 : 0, pin);
- mm_serial_queue_command (MM_SERIAL (modem), command, 3, enable_pin_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 3, enable_pin_done, info);
g_free (command);
}
static void
-change_pin_done (MMSerial *serial,
+change_pin_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -464,12 +584,13 @@ change_pin (MMModemGsmCard *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"", old_pin, new_pin);
- mm_serial_queue_command (MM_SERIAL (modem), command, 3, change_pin_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 3, change_pin_done, info);
g_free (command);
}
@@ -500,30 +621,48 @@ parse_operator (const char *reply)
}
static void
-read_operator_done (MMSerial *serial,
- GString *response,
- GError *error,
- gpointer user_data)
+read_operator_code_done (MMSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
{
- if (!error) {
- char *oper;
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ char *oper;
- oper = parse_operator (response->str);
- if (oper) {
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
+ if (error)
+ return;
- if (GPOINTER_TO_INT (user_data) == 0) {
- g_free (priv->oper_code);
- priv->oper_code = oper;
- } else {
- g_free (priv->oper_name);
- priv->oper_name = oper;
+ oper = parse_operator (response->str);
+ if (!oper)
+ return;
- mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (serial), priv->reg_status,
- priv->oper_code, priv->oper_name);
- }
- }
- }
+ g_free (priv->oper_code);
+ priv->oper_code = oper;
+}
+
+static void
+read_operator_name_done (MMSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (user_data);
+ char *oper;
+
+ if (error)
+ return;
+
+ oper = parse_operator (response->str);
+ if (!oper)
+ return;
+
+ g_free (priv->oper_name);
+ priv->oper_name = oper;
+
+ mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (user_data),
+ priv->reg_status,
+ priv->oper_code,
+ priv->oper_name);
}
/* Registration */
@@ -618,24 +757,27 @@ reg_status_updated (MMGenericGsm *self, int new_value)
}
static void
-reg_state_changed (MMSerial *serial,
+reg_state_changed (MMSerialPort *port,
GMatchInfo *match_info,
gpointer user_data)
{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
char *str;
str = g_match_info_fetch (match_info, 1);
- reg_status_updated (MM_GENERIC_GSM (serial), atoi (str));
+ reg_status_updated (self, atoi (str));
g_free (str);
}
static gboolean
reg_status_again (gpointer data)
{
+ MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) data;
- if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration)
- get_registration_status (MM_SERIAL (info->modem), info);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ if (priv->pending_registration)
+ get_registration_status (priv->primary, info);
return FALSE;
}
@@ -647,13 +789,13 @@ reg_status_again_remove (gpointer data)
}
static void
-get_reg_status_done (MMSerial *serial,
+get_reg_status_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
if (error) {
info->error = g_error_copy (error);
@@ -666,7 +808,7 @@ get_reg_status_done (MMSerial *serial,
int unsolicited, stat;
if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) {
- reg_status_updated (MM_GENERIC_GSM (serial), stat);
+ reg_status_updated (MM_GENERIC_GSM (info->modem), stat);
if (!unsolicited && priv->pending_registration) {
guint id;
@@ -687,19 +829,19 @@ get_reg_status_done (MMSerial *serial,
}
static void
-get_registration_status (MMSerial *serial, MMCallbackInfo *info)
+get_registration_status (MMSerialPort *port, MMCallbackInfo *info)
{
- mm_serial_queue_command (serial, "+CREG?", 3, get_reg_status_done, info);
+ mm_serial_port_queue_command (port, "+CREG?", 3, get_reg_status_done, info);
}
static void
-register_done (MMSerial *serial,
+register_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
/* Ignore errors here, get the actual registration status */
- get_registration_status (serial, (MMCallbackInfo *) user_data);
+ get_registration_status (port, (MMCallbackInfo *) user_data);
}
static void
@@ -708,12 +850,13 @@ do_register (MMModemGsmNetwork *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
- MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_registration =
+ priv->pending_registration =
g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60,
pending_registration_timed_out,
info,
@@ -724,7 +867,7 @@ do_register (MMModemGsmNetwork *modem,
else
command = g_strdup ("+COPS=0,,");
- mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 5, register_done, info);
g_free (command);
}
@@ -758,7 +901,7 @@ get_registration_info (MMModemGsmNetwork *self,
}
static void
-connect_report_done (MMSerial *serial,
+connect_report_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -774,17 +917,19 @@ connect_report_done (MMSerial *serial,
}
static void
-connect_done (MMSerial *serial,
+connect_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
if (error) {
info->error = g_error_copy (error);
/* Try to get more information why it failed */
- mm_serial_queue_command (serial, "+CEER", 3, connect_report_done, info);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ mm_serial_port_queue_command (priv->primary, "+CEER", 3, connect_report_done, info);
} else
/* Done */
mm_callback_info_schedule (info);
@@ -796,6 +941,7 @@ connect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
char *command;
guint32 cid = mm_generic_gsm_get_cid (MM_GENERIC_GSM (modem));
@@ -816,12 +962,12 @@ connect (MMModem *modem,
} else
command = g_strconcat ("DT", number, NULL);
- mm_serial_queue_command (MM_SERIAL (modem), command, 60, connect_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 60, connect_done, info);
g_free (command);
}
static void
-disconnect_flash_done (MMSerial *serial, gpointer user_data)
+disconnect_flash_done (MMSerialPort *port, gpointer user_data)
{
mm_callback_info_schedule ((MMCallbackInfo *) user_data);
}
@@ -831,13 +977,14 @@ disconnect (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
/* First, reset the previously used CID */
mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
info = mm_callback_info_new (modem, callback, user_data);
- mm_serial_flash (MM_SERIAL (modem), 1000, disconnect_flash_done, info);
+ mm_serial_port_flash (priv->primary, 1000, disconnect_flash_done, info);
}
static void
@@ -861,7 +1008,7 @@ destroy_scan_data (gpointer data)
}
static void
-scan_done (MMSerial *serial,
+scan_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -880,8 +1027,13 @@ scan_done (MMSerial *serial,
reply += 7;
- /* Pattern without crazy escaping using | for matching: (|\d|,"|.+|","|.+|","|.+|",|\d|) */
- r = g_regex_new ("\\((\\d),\"(.+)\",\"(.+)\",\"(.+)\",(\\d)\\)", G_REGEX_UNGREEDY, 0, &err);
+ /* Pattern without crazy escaping using | for matching: (|\d|,"|.+|","|.+|","|.+|"\)?,|\d|) */
+
+ /* Quirk: Sony-Ericsson TM-506 sometimes includes a stray ')' like so:
+ *
+ * +COPS: (2,"","T-Mobile","31026",0),(1,"AT&T","AT&T","310410"),0)
+ */
+ r = g_regex_new ("\\((\\d),\"(.*)\",\"(.*)\",\"(.*)\"\\)?,(\\d)\\)", G_REGEX_UNGREEDY, 0, &err);
if (err) {
g_error ("Invalid regular expression: %s", err->message);
g_error_free (err);
@@ -922,6 +1074,7 @@ scan (MMModemGsmNetwork *modem,
MMModemGsmNetworkScanFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_new_full (MM_MODEM (modem),
@@ -929,13 +1082,13 @@ scan (MMModemGsmNetwork *modem,
G_CALLBACK (callback),
user_data);
- mm_serial_queue_command (MM_SERIAL (modem), "+COPS=?", 60, scan_done, info);
+ mm_serial_port_queue_command (priv->primary, "+COPS=?", 60, scan_done, info);
}
/* SetApn */
static void
-set_apn_done (MMSerial *serial,
+set_apn_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -945,14 +1098,14 @@ set_apn_done (MMSerial *serial,
if (error)
info->error = g_error_copy (error);
else
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (serial),
+ mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem),
GPOINTER_TO_UINT (mm_callback_info_get_data (info, "cid")));
mm_callback_info_schedule (info);
}
static void
-cid_range_read (MMSerial *serial,
+cid_range_read (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1012,13 +1165,13 @@ cid_range_read (MMSerial *serial,
mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL);
command = g_strdup_printf ("+CGDCONT=%d, \"IP\", \"%s\"", cid, apn);
- mm_serial_queue_command (serial, command, 3, set_apn_done, info);
+ mm_serial_port_queue_command (port, command, 3, set_apn_done, info);
g_free (command);
}
}
static void
-existing_apns_read (MMSerial *serial,
+existing_apns_read (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1051,7 +1204,7 @@ existing_apns_read (MMSerial *serial,
apn = g_match_info_fetch (match_info, 3);
if (!strcmp (apn, new_apn)) {
- mm_generic_gsm_set_cid (MM_GENERIC_GSM (serial), (guint32) num_cid);
+ mm_generic_gsm_set_cid (MM_GENERIC_GSM (info->modem), (guint32) num_cid);
found = TRUE;
}
@@ -1083,7 +1236,7 @@ existing_apns_read (MMSerial *serial,
mm_callback_info_schedule (info);
else
/* APN not configured on the card. Get the allowed CID range */
- mm_serial_queue_command_cached (serial, "+CGDCONT=?", 3, cid_range_read, info);
+ mm_serial_port_queue_command_cached (port, "+CGDCONT=?", 3, cid_range_read, info);
}
static void
@@ -1092,23 +1245,25 @@ set_apn (MMModemGsmNetwork *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
mm_callback_info_set_data (info, "apn", g_strdup (apn), g_free);
/* Start by searching if the APN is already in card */
- mm_serial_queue_command (MM_SERIAL (modem), "+CGDCONT?", 3, existing_apns_read, info);
+ mm_serial_port_queue_command (priv->primary, "+CGDCONT?", 3, existing_apns_read, info);
}
/* GetSignalQuality */
static void
-get_signal_quality_done (MMSerial *serial,
+get_signal_quality_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
char *reply = response->str;
@@ -1127,7 +1282,8 @@ get_signal_quality_done (MMSerial *serial,
/* Normalize the quality */
quality = quality * 100 / 31;
- MM_GENERIC_GSM_GET_PRIVATE (serial)->signal_quality = quality;
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ priv->signal_quality = quality;
mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL);
} else
info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
@@ -1142,23 +1298,26 @@ get_signal_quality (MMModemGsmNetwork *modem,
MMModemUIntFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
MMCallbackInfo *info;
+ gboolean connected;
- if (mm_serial_is_connected (MM_SERIAL (modem))) {
- g_message ("Returning saved signal quality %d", MM_GENERIC_GSM_GET_PRIVATE (modem)->signal_quality);
- callback (MM_MODEM (modem), MM_GENERIC_GSM_GET_PRIVATE (modem)->signal_quality, NULL, user_data);
+ connected = mm_serial_port_is_connected (priv->primary);
+ if (connected && !priv->secondary) {
+ g_message ("Returning saved signal quality %d", priv->signal_quality);
+ callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data);
return;
}
info = mm_callback_info_uint_new (MM_MODEM (modem), callback, user_data);
- mm_serial_queue_command (MM_SERIAL (modem), "+CSQ", 3, get_signal_quality_done, info);
+ mm_serial_port_queue_command (connected ? priv->secondary : priv->primary, "+CSQ", 3, get_signal_quality_done, info);
}
/*****************************************************************************/
/* MMModemGsmSms interface */
static void
-sms_send_done (MMSerial *serial,
+sms_send_done (MMSerialPort *port,
GString *response,
GError *error,
gpointer user_data)
@@ -1181,16 +1340,33 @@ sms_send (MMModemGsmSms *modem,
MMModemFn callback,
gpointer user_data)
{
+ MMGenericGsmPrivate *priv;
MMCallbackInfo *info;
char *command;
+ gboolean connected;
+ MMSerialPort *port = NULL;
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ connected = mm_serial_port_is_connected (priv->primary);
+ if (connected)
+ port = priv->secondary;
+ else
+ port = priv->primary;
+
+ if (!port) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Cannot send SMS while connected");
+ mm_callback_info_schedule (info);
+ return;
+ }
+
/* FIXME: use the PDU mode instead */
- mm_serial_queue_command (MM_SERIAL (modem), "AT+CMGF=1", 3, NULL, NULL);
+ mm_serial_port_queue_command (port, "AT+CMGF=1", 3, NULL, NULL);
command = g_strdup_printf ("+CMGS=\"%s\"\r%s\x1a", number, text);
- mm_serial_queue_command (MM_SERIAL (modem), command, 10, sms_send_done, info);
+ mm_serial_port_queue_command (port, command, 10, sms_send_done, info);
g_free (command);
}
@@ -1441,6 +1617,9 @@ simple_get_status (MMModemSimple *simple,
static void
modem_init (MMModem *modem_class)
{
+ modem_class->owns_port = owns_port;
+ modem_class->grab_port = grab_port;
+ modem_class->release_port = release_port;
modem_class->enable = enable;
modem_class->connect = connect;
modem_class->disconnect = disconnect;
@@ -1484,16 +1663,6 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
- GRegex *regex;
-
- mm_serial_set_response_parser (MM_SERIAL (self),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
-
- regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, reg_state_changed, NULL, NULL);
- g_regex_unref (regex);
}
static void
@@ -1507,16 +1676,20 @@ set_property (GObject *object, guint prop_id,
/* Construct only */
priv->driver = g_value_dup_string (value);
break;
- case MM_MODEM_PROP_DEVICE:
- g_free (priv->data_device);
- priv->data_device = g_value_dup_string (value);
+ case MM_MODEM_PROP_PLUGIN:
+ /* Construct only */
+ priv->plugin = g_value_dup_string (value);
break;
- case MM_MODEM_PROP_TYPE:
- priv->modem_type = g_value_get_uint (value);
+ case MM_MODEM_PROP_MASTER_DEVICE:
+ /* Constrcut only */
+ priv->device = g_value_dup_string (value);
break;
case MM_MODEM_PROP_IP_METHOD:
priv->ip_method = g_value_get_uint (value);
break;
+ case MM_MODEM_PROP_TYPE:
+ case MM_MODEM_PROP_VALID:
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1530,21 +1703,30 @@ get_property (GObject *object, guint prop_id,
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
switch (prop_id) {
- case MM_MODEM_PROP_DEVICE:
- if (priv->data_device)
- g_value_set_string (value, priv->data_device);
+ case MM_MODEM_PROP_DATA_DEVICE:
+ if (priv->primary)
+ g_value_set_string (value, mm_serial_port_get_device (priv->primary));
else
- g_value_set_string (value, mm_serial_get_device (MM_SERIAL (object)));
+ g_value_set_string (value, NULL);
+ break;
+ case MM_MODEM_PROP_MASTER_DEVICE:
+ g_value_set_string (value, priv->device);
break;
case MM_MODEM_PROP_DRIVER:
- g_value_set_string (value, MM_GENERIC_GSM_GET_PRIVATE (object)->driver);
+ g_value_set_string (value, priv->driver);
+ break;
+ case MM_MODEM_PROP_PLUGIN:
+ g_value_set_string (value, priv->plugin);
break;
case MM_MODEM_PROP_TYPE:
- g_value_set_uint (value, priv->modem_type);
+ g_value_set_uint (value, MM_MODEM_TYPE_GSM);
break;
case MM_MODEM_PROP_IP_METHOD:
g_value_set_uint (value, priv->ip_method);
break;
+ case MM_MODEM_PROP_VALID:
+ g_value_set_boolean (value, priv->valid);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1581,14 +1763,22 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
/* Properties */
g_object_class_override_property (object_class,
- MM_MODEM_PROP_DEVICE,
- MM_MODEM_DEVICE);
+ MM_MODEM_PROP_DATA_DEVICE,
+ MM_MODEM_DATA_DEVICE);
+
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_MASTER_DEVICE,
+ MM_MODEM_MASTER_DEVICE);
g_object_class_override_property (object_class,
MM_MODEM_PROP_DRIVER,
MM_MODEM_DRIVER);
g_object_class_override_property (object_class,
+ MM_MODEM_PROP_PLUGIN,
+ MM_MODEM_PLUGIN);
+
+ g_object_class_override_property (object_class,
MM_MODEM_PROP_TYPE,
MM_MODEM_TYPE);
@@ -1596,6 +1786,9 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
MM_MODEM_PROP_IP_METHOD,
MM_MODEM_IP_METHOD);
+ g_object_class_override_property (object_class,
+ MM_MODEM_PROP_VALID,
+ MM_MODEM_VALID);
}
GType
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index 3f857365..a98afefa 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -6,12 +6,12 @@
#include "mm-modem-gsm-network.h"
#include "mm-serial.h"
-#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ())
-#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm))
-#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
-#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM))
-#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM))
-#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
+#define MM_TYPE_GENERIC_GSM (mm_generic_gsm_get_type ())
+#define MM_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsm))
+#define MM_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
+#define MM_IS_GENERIC_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_GENERIC_GSM))
+#define MM_IS_GENERIC_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_GENERIC_GSM))
+#define MM_GENERIC_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_GENERIC_GSM, MMGenericGsmClass))
typedef struct {
MMSerial parent;
@@ -23,8 +23,9 @@ typedef struct {
GType mm_generic_gsm_get_type (void);
-MMModem *mm_generic_gsm_new (const char *serial_device,
- const char *driver);
+MMModem *mm_generic_gsm_new (const char *device,
+ const char *driver,
+ const char *plugin);
void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
gboolean enabled);
diff --git a/src/mm-manager.c b/src/mm-manager.c
index a19c9446..beed08cc 100644
--- a/src/mm-manager.c
+++ b/src/mm-manager.c
@@ -2,12 +2,12 @@
#include <string.h>
#include <gmodule.h>
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include "mm-manager.h"
#include "mm-errors.h"
-#include "mm-generic-gsm.h"
-#include "mm-generic-cdma.h"
#include "mm-plugin.h"
static gboolean impl_manager_enumerate_devices (MMManager *manager,
@@ -29,9 +29,11 @@ static guint signals[LAST_SIGNAL] = { 0 };
#define MM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MANAGER, MMManagerPrivate))
+#define DBUS_PATH_TAG "dbus-path"
+
typedef struct {
DBusGConnection *connection;
- LibHalContext *hal_ctx;
+ GUdevClient *udev;
GSList *plugins;
GHashTable *modems;
} MMManagerPrivate;
@@ -147,169 +149,78 @@ mm_manager_new (DBusGConnection *bus)
return manager;
}
-static char *
-get_driver_name (LibHalContext *ctx, const char *udi)
-{
- char *parent_udi;
- char *driver = NULL;
-
- parent_udi = libhal_device_get_property_string (ctx, udi, "info.parent", NULL);
- if (parent_udi) {
- driver = libhal_device_get_property_string (ctx, parent_udi, "info.linux.driver", NULL);
- libhal_free_string (parent_udi);
- }
-
- return driver;
-}
-
-static MMModem *
-create_generic_modem (MMManager *manager, const char *udi)
-{
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- MMModem *modem;
- char **capabilities;
- char **iter;
- char *serial_device;
- char *driver;
- gboolean type_gsm = FALSE;
- gboolean type_cdma = FALSE;
-
- capabilities = libhal_device_get_property_strlist (priv->hal_ctx, udi, "modem.command_sets", NULL);
- for (iter = capabilities; iter && *iter; iter++) {
- if (!strcmp (*iter, "GSM-07.07")) {
- type_gsm = TRUE;
- break;
- }
- if (!strcmp (*iter, "IS-707-A")) {
- type_cdma = TRUE;
- break;
- }
- }
- g_strfreev (capabilities);
-
- if (!type_gsm && !type_cdma)
- return NULL;
-
- serial_device = libhal_device_get_property_string (priv->hal_ctx, udi, "serial.device", NULL);
- g_return_val_if_fail (serial_device != NULL, NULL);
-
- driver = get_driver_name (priv->hal_ctx, udi);
- g_return_val_if_fail (driver != NULL, NULL);
-
- if (type_gsm)
- modem = mm_generic_gsm_new (serial_device, driver);
- else
- modem = mm_generic_cdma_new (serial_device, driver);
-
- g_free (serial_device);
- g_free (driver);
-
- if (modem)
- g_debug ("Created new generic modem (%s)", udi);
- else
- g_warning ("Failed to create generic modem (%s)", udi);
-
- return modem;
-}
-
static void
-add_modem (MMManager *manager, const char *udi, MMModem *modem)
+remove_modem (MMManager *manager, MMModem *modem)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ char *device;
- g_hash_table_insert (priv->modems, g_strdup (udi), modem);
- dbus_g_connection_register_g_object (priv->connection, udi, G_OBJECT (modem));
+ device = mm_modem_get_device (modem);
+ g_assert (device);
+ g_debug ("Removed modem %s", device);
- g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
+ g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem);
+ g_hash_table_remove (priv->modems, device);
+ g_free (device);
}
-static MMModem *
-modem_exists (MMManager *manager, const char *udi)
+static void
+modem_valid (MMModem *modem, GParamSpec *pspec, gpointer user_data)
{
+ MMManager *manager = MM_MANAGER (user_data);
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ static guint32 id = 0;
+ char *path, *device;
- return (MMModem *) g_hash_table_lookup (priv->modems, udi);
-}
+ if (mm_modem_get_valid (modem)) {
+ path = g_strdup_printf (MM_DBUS_PATH"/Modems/%d", id++);
+ dbus_g_connection_register_g_object (priv->connection, path, G_OBJECT (modem));
+ g_object_set_data_full (G_OBJECT (modem), DBUS_PATH_TAG, path, (GDestroyNotify) g_free);
-static void
-create_initial_modems_from_plugins (MMManager *manager)
-{
- MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- GSList *iter;
+ device = mm_modem_get_device (modem);
+ g_assert (device);
+ g_debug ("Exported modem %s", device);
+ g_free (device);
- for (iter = priv->plugins; iter; iter = iter->next) {
- MMPlugin *plugin = MM_PLUGIN (iter->data);
- char **udis;
- int i;
-
- udis = mm_plugin_list_supported_udis (plugin, priv->hal_ctx);
- if (udis) {
- for (i = 0; udis[i]; i++) {
- char *udi = udis[i];
- MMModem *modem;
-
- if (modem_exists (manager, udi)) {
- g_warning ("Modem for UDI %s already exists, ignoring", udi);
- continue;
- }
-
- modem = mm_plugin_create_modem (plugin, priv->hal_ctx, udi);
- if (modem)
- add_modem (manager, udi, modem);
- }
-
- g_strfreev (udis);
- }
- }
+ g_signal_emit (manager, signals[DEVICE_ADDED], 0, modem);
+ } else
+ remove_modem (manager, modem);
}
static void
-create_initial_modems_generic (MMManager *manager)
+add_modem (MMManager *manager, MMModem *modem)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
- char **devices;
- int num_devices;
- int i;
- DBusError err;
-
- dbus_error_init (&err);
- devices = libhal_find_device_by_capability (priv->hal_ctx, "modem", &num_devices, &err);
- if (dbus_error_is_set (&err)) {
- g_warning ("Could not list HAL devices: %s", err.message);
- dbus_error_free (&err);
- }
-
- if (devices) {
- for (i = 0; i < num_devices; i++) {
- char *udi = devices[i];
- MMModem *modem;
-
- if (modem_exists (manager, udi))
- /* Already exists, most likely handled by a plugin */
- continue;
-
- modem = create_generic_modem (manager, udi);
- if (modem)
- add_modem (manager, g_strdup (udi), modem);
- }
+ char *device;
+ gboolean valid = FALSE;
+
+ device = mm_modem_get_device (modem);
+ g_assert (device);
+ if (!g_hash_table_lookup (priv->modems, device)) {
+ g_hash_table_insert (priv->modems, g_strdup (device), modem);
+ g_debug ("Added modem %s", device);
+ g_signal_connect (modem, "notify::valid", G_CALLBACK (modem_valid), manager);
+ g_object_get (modem, MM_MODEM_VALID, &valid, NULL);
+ if (valid)
+ modem_valid (modem, NULL, manager);
}
-
- g_strfreev (devices);
-}
-
-static void
-create_initial_modems (MMManager *manager)
-{
- create_initial_modems_from_plugins (manager);
- create_initial_modems_generic (manager);
+ g_free (device);
}
static void
enumerate_devices_cb (gpointer key, gpointer val, gpointer user_data)
{
+ MMModem *modem = MM_MODEM (val);
GPtrArray **devices = (GPtrArray **) user_data;
-
- g_ptr_array_add (*devices, g_strdup ((char *) key));
+ const char *path;
+ gboolean valid = FALSE;
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_VALID, &valid, NULL);
+ if (valid) {
+ path = g_object_get_data (G_OBJECT (modem), DBUS_PATH_TAG);
+ g_return_if_fail (path != NULL);
+ g_ptr_array_add (*devices, g_strdup (path));
+ }
}
static gboolean
@@ -325,118 +236,164 @@ impl_manager_enumerate_devices (MMManager *manager,
return TRUE;
}
+typedef struct {
+ MMModem *modem;
+ const char *subsys;
+ const char *name;
+} FindPortInfo;
+
+static void
+find_port (gpointer key, gpointer data, gpointer user_data)
+{
+ FindPortInfo *info = user_data;
+ MMModem *modem = MM_MODEM (data);
+
+ if (!info->modem && mm_modem_owns_port (modem, info->subsys, info->name))
+ info->modem = modem;
+}
+
+static MMModem *
+find_modem_for_port (MMManager *manager, const char *subsys, const char *name)
+{
+ MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ FindPortInfo info = { NULL, subsys, name };
+
+ g_hash_table_foreach (priv->modems, find_port, &info);
+ return info.modem;
+}
+
static void
-device_added (LibHalContext *ctx, const char *udi)
+device_added (MMManager *manager, GUdevDevice *device)
{
- MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
GSList *iter;
MMModem *modem = NULL;
+ const char *subsys, *name;
+ MMPlugin *best_plugin = NULL;
+ guint32 best_level = 0;
+ GError *error = NULL;
- if (modem_exists (manager, udi))
- /* Shouldn't happen */
+ g_return_if_fail (device != NULL);
+
+ subsys = g_udev_device_get_subsystem (device);
+ name = g_udev_device_get_name (device);
+
+ if (find_modem_for_port (manager, subsys, name))
return;
- for (iter = priv->plugins; iter && modem == NULL; iter = iter->next) {
+ /* Build up the list of plugins that support this port */
+ for (iter = priv->plugins; iter; iter = iter->next) {
MMPlugin *plugin = MM_PLUGIN (iter->data);
+ guint32 level;
- if (mm_plugin_supports_udi (plugin, ctx, udi)) {
- modem = mm_plugin_create_modem (plugin, ctx, udi);
- if (modem)
- break;
+ level = mm_plugin_supports_port (plugin, subsys, name);
+ if (level > best_level) {
+ best_plugin = plugin;
+ best_level = level;
}
}
- if (!modem)
- /* None of the plugins supported the udi, try generic devices */
- modem = create_generic_modem (manager, udi);
+ /* Let the best plugin handle this port */
+ if (!best_plugin)
+ return;
- if (modem)
- add_modem (manager, udi, modem);
+ modem = mm_plugin_grab_port (best_plugin, subsys, name, &error);
+ if (modem) {
+ guint32 modem_type = MM_MODEM_TYPE_UNKNOWN;
+ const char *type_name = "UNKNOWN";
+
+ g_object_get (G_OBJECT (modem), MM_MODEM_TYPE, &modem_type, NULL);
+ if (modem_type == MM_MODEM_TYPE_GSM)
+ type_name = "GSM";
+ else if (modem_type == MM_MODEM_TYPE_CDMA)
+ type_name = "CDMA";
+
+ g_message ("(%s): %s modem %s claimed port %s",
+ mm_plugin_get_name (best_plugin),
+ type_name,
+ mm_modem_get_device (modem),
+ name);
+ } else {
+ g_warning ("%s: plugin '%s' claimed to support %s/%s but couldn't: (%d) %s",
+ __func__, mm_plugin_get_name (best_plugin), subsys, name,
+ error ? error->code : -1,
+ (error && error->message) ? error->message : "(unknown)");
+ return;
+ }
+
+ add_modem (manager, modem);
}
static void
-device_removed (LibHalContext *ctx, const char *udi)
+device_removed (MMManager *manager, GUdevDevice *device)
{
- MMManager *manager = MM_MANAGER (libhal_ctx_get_user_data (ctx));
MMModem *modem;
+ const char *subsys, *name;
- modem = modem_exists (manager, udi);
- if (modem) {
- g_debug ("Removed modem %s", udi);
- g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem);
- g_hash_table_remove (MM_MANAGER_GET_PRIVATE (manager)->modems, udi);
- }
+ g_return_if_fail (device != NULL);
+
+ subsys = g_udev_device_get_subsystem (device);
+ name = g_udev_device_get_name (device);
+ modem = find_modem_for_port (manager, subsys, name);
+ if (modem)
+ mm_modem_release_port (modem, subsys, name);
}
static void
-device_new_capability (LibHalContext *ctx, const char *udi, const char *capability)
+handle_uevent (GUdevClient *client,
+ const char *action,
+ GUdevDevice *device,
+ gpointer user_data)
{
- device_added (ctx, udi);
-}
+ MMManager *self = MM_MANAGER (user_data);
+ const char *subsys;
+ g_return_if_fail (action != NULL);
-DBusGConnection *
-mm_manager_get_bus (MMManager *manager)
-{
- g_return_val_if_fail (MM_IS_MANAGER (manager), NULL);
-
- return MM_MANAGER_GET_PRIVATE (manager)->connection;
-}
+ /* A bit paranoid */
+ subsys = g_udev_device_get_subsystem (device);
+ g_return_if_fail (subsys != NULL);
-static gboolean
-remove_one (gpointer key,
- gpointer value,
- gpointer user_data)
-{
- const char *udi = (char *) key;
- MMModem *modem = MM_MODEM (value);
- MMManager *manager = MM_MANAGER (user_data);
+ g_return_if_fail (!strcmp (subsys, "tty") || !strcmp (subsys, "net"));
- g_debug ("Removed modem %s", udi);
- g_signal_emit (manager, signals[DEVICE_REMOVED], 0, modem);
-
- return TRUE;
+ if (!strcmp (action, "add"))
+ device_added (self, device);
+ else if (!strcmp (action, "remove"))
+ device_removed (self, device);
}
void
-mm_manager_set_hal_ctx (MMManager *manager,
- LibHalContext *hal_ctx)
+mm_manager_start (MMManager *manager)
{
MMManagerPrivate *priv;
+ GList *devices, *iter;
+ g_return_if_fail (manager != NULL);
g_return_if_fail (MM_IS_MANAGER (manager));
priv = MM_MANAGER_GET_PRIVATE (manager);
- priv->hal_ctx = hal_ctx;
- if (hal_ctx) {
- libhal_ctx_set_user_data (hal_ctx, manager);
- libhal_ctx_set_device_added (hal_ctx, device_added);
- libhal_ctx_set_device_removed (hal_ctx, device_removed);
- libhal_ctx_set_device_new_capability (hal_ctx, device_new_capability);
+ devices = g_udev_client_query_by_subsystem (priv->udev, "tty");
+ for (iter = devices; iter; iter = g_list_next (iter))
+ device_added (manager, G_UDEV_DEVICE (iter->data));
- create_initial_modems (manager);
- } else {
- g_hash_table_foreach_remove (priv->modems, remove_one, manager);
- }
-}
-
-LibHalContext *
-mm_manager_get_hal_ctx (MMManager *manager)
-{
- g_return_val_if_fail (MM_IS_MANAGER (manager), NULL);
-
- return MM_MANAGER_GET_PRIVATE (manager)->hal_ctx;
+ devices = g_udev_client_query_by_subsystem (priv->udev, "net");
+ for (iter = devices; iter; iter = g_list_next (iter))
+ device_added (manager, G_UDEV_DEVICE (iter->data));
}
static void
mm_manager_init (MMManager *manager)
{
MMManagerPrivate *priv = MM_MANAGER_GET_PRIVATE (manager);
+ const char *subsys[3] = { "tty", "net", NULL };
priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
load_plugins (manager);
+
+ priv->udev = g_udev_client_new (subsys);
+ g_assert (priv->udev);
+ g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), manager);
}
static void
@@ -449,11 +406,8 @@ finalize (GObject *object)
g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
g_slist_free (priv->plugins);
- if (priv->hal_ctx) {
- mm_manager_set_hal_ctx (MM_MANAGER (object), NULL);
- libhal_ctx_shutdown (priv->hal_ctx, NULL);
- libhal_ctx_free (priv->hal_ctx);
- }
+ if (priv->udev)
+ g_object_unref (priv->udev);
if (priv->connection)
dbus_g_connection_unref (priv->connection);
diff --git a/src/mm-manager.h b/src/mm-manager.h
index 4426d0f1..0e7564ff 100644
--- a/src/mm-manager.h
+++ b/src/mm-manager.h
@@ -6,7 +6,6 @@
#include <glib/gtypes.h>
#include <glib-object.h>
#include <dbus/dbus-glib.h>
-#include <libhal.h>
#include "mm-modem.h"
#define MM_TYPE_MANAGER (mm_manager_get_type ())
@@ -34,10 +33,7 @@ typedef struct {
GType mm_manager_get_type (void);
MMManager *mm_manager_new (DBusGConnection *bus);
-DBusGConnection *mm_manager_get_bus (MMManager *manager);
-void mm_manager_set_hal_ctx (MMManager *manager,
- LibHalContext *hal_ctx);
-LibHalContext *mm_manager_get_hal_ctx (MMManager *manager);
+void mm_manager_start (MMManager *manager);
#endif /* MM_MANAGER_H */
diff --git a/src/mm-modem.c b/src/mm-modem.c
index 7bf913ec..be87b79f 100644
--- a/src/mm-modem.c
+++ b/src/mm-modem.c
@@ -199,6 +199,75 @@ impl_modem_disconnect (MMModem *modem,
/*****************************************************************************/
+gboolean
+mm_modem_owns_port (MMModem *self,
+ const char *subsys,
+ const char *name)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (subsys, FALSE);
+ g_return_val_if_fail (name, FALSE);
+
+ g_assert (MM_MODEM_GET_INTERFACE (self)->owns_port);
+ return MM_MODEM_GET_INTERFACE (self)->owns_port (self, subsys, name);
+}
+
+gboolean
+mm_modem_grab_port (MMModem *self,
+ const char *subsys,
+ const char *name,
+ GError **error)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (subsys, FALSE);
+ g_return_val_if_fail (name, FALSE);
+
+ g_assert (MM_MODEM_GET_INTERFACE (self)->grab_port);
+ return MM_MODEM_GET_INTERFACE (self)->grab_port (self, subsys, name, error);
+}
+
+void
+mm_modem_release_port (MMModem *self,
+ const char *subsys,
+ const char *name)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_MODEM (self));
+ g_return_if_fail (subsys);
+ g_return_if_fail (name);
+
+ g_assert (MM_MODEM_GET_INTERFACE (self)->release_port);
+ MM_MODEM_GET_INTERFACE (self)->release_port (self, subsys, name);
+}
+
+gboolean
+mm_modem_get_valid (MMModem *self)
+{
+ gboolean valid = FALSE;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ g_object_get (G_OBJECT (self), MM_MODEM_VALID, &valid, NULL);
+ return valid;
+}
+
+char *
+mm_modem_get_device (MMModem *self)
+{
+ char *device;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ g_object_get (G_OBJECT (self), MM_MODEM_MASTER_DEVICE, &device, NULL);
+ return device;
+}
+
+/*****************************************************************************/
+
static void
mm_modem_init (gpointer g_iface)
{
@@ -210,9 +279,17 @@ mm_modem_init (gpointer g_iface)
/* Properties */
g_object_interface_install_property
(g_iface,
- g_param_spec_string (MM_MODEM_DEVICE,
- "Device",
+ g_param_spec_string (MM_MODEM_DATA_DEVICE,
"Device",
+ "Data device",
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_MODEM_MASTER_DEVICE,
+ "MasterDevice",
+ "Master modem parent device of all the modem's ports",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
@@ -226,6 +303,14 @@ mm_modem_init (gpointer g_iface)
g_object_interface_install_property
(g_iface,
+ g_param_spec_string (MM_MODEM_PLUGIN,
+ "Plugin",
+ "Plugin name",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
g_param_spec_uint (MM_MODEM_TYPE,
"Type",
"Type",
@@ -242,6 +327,14 @@ mm_modem_init (gpointer g_iface)
MM_MODEM_IP_METHOD_PPP,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_MODEM_VALID,
+ "Valid",
+ "Modem is valid",
+ FALSE,
+ G_PARAM_READABLE));
+
initialized = TRUE;
}
diff --git a/src/mm-modem.h b/src/mm-modem.h
index 914b86a3..19ec284d 100644
--- a/src/mm-modem.h
+++ b/src/mm-modem.h
@@ -10,13 +10,17 @@
#define MM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM))
#define MM_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM, MMModem))
-#define MM_MODEM_DEVICE "device"
-#define MM_MODEM_DRIVER "driver"
-#define MM_MODEM_TYPE "type"
-#define MM_MODEM_IP_METHOD "ip-method"
-
-#define MM_MODEM_TYPE_GSM 1
-#define MM_MODEM_TYPE_CDMA 2
+#define MM_MODEM_DATA_DEVICE "device"
+#define MM_MODEM_MASTER_DEVICE "master-device"
+#define MM_MODEM_DRIVER "driver"
+#define MM_MODEM_TYPE "type"
+#define MM_MODEM_IP_METHOD "ip-method"
+#define MM_MODEM_VALID "valid" /* not exported */
+#define MM_MODEM_PLUGIN "plugin" /* not exported */
+
+#define MM_MODEM_TYPE_UNKNOWN 0
+#define MM_MODEM_TYPE_GSM 1
+#define MM_MODEM_TYPE_CDMA 2
#define MM_MODEM_IP_METHOD_PPP 0
#define MM_MODEM_IP_METHOD_STATIC 1
@@ -25,10 +29,13 @@
typedef enum {
MM_MODEM_PROP_FIRST = 0x1000,
- MM_MODEM_PROP_DEVICE = MM_MODEM_PROP_FIRST,
+ MM_MODEM_PROP_DATA_DEVICE = MM_MODEM_PROP_FIRST,
+ MM_MODEM_PROP_MASTER_DEVICE,
MM_MODEM_PROP_DRIVER,
MM_MODEM_PROP_TYPE,
MM_MODEM_PROP_IP_METHOD,
+ MM_MODEM_PROP_VALID, /* Not exported */
+ MM_MODEM_PROP_PLUGIN, /* Not exported */
} MMModemProp;
typedef struct _MMModem MMModem;
@@ -57,6 +64,19 @@ struct _MMModem {
GTypeInterface g_iface;
/* Methods */
+ gboolean (*owns_port) (MMModem *self,
+ const char *subsys,
+ const char *name);
+
+ gboolean (*grab_port) (MMModem *self,
+ const char *subsys,
+ const char *name,
+ GError **error);
+
+ void (*release_port) (MMModem *self,
+ const char *subsys,
+ const char *name);
+
void (*enable) (MMModem *self,
gboolean enable,
MMModemFn callback,
@@ -78,6 +98,19 @@ struct _MMModem {
GType mm_modem_get_type (void);
+gboolean mm_modem_owns_port (MMModem *self,
+ const char *subsys,
+ const char *name);
+
+gboolean mm_modem_grab_port (MMModem *self,
+ const char *subsys,
+ const char *name,
+ GError **error);
+
+void mm_modem_release_port (MMModem *self,
+ const char *subsys,
+ const char *name);
+
void mm_modem_enable (MMModem *self,
gboolean enable,
MMModemFn callback,
@@ -96,4 +129,9 @@ void mm_modem_disconnect (MMModem *self,
MMModemFn callback,
gpointer user_data);
+gboolean mm_modem_get_valid (MMModem *self);
+
+char *mm_modem_get_device (MMModem *self);
+
#endif /* MM_MODEM_H */
+
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index e1fd21c1..c3425538 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -10,49 +10,31 @@ mm_plugin_get_name (MMPlugin *plugin)
return MM_PLUGIN_GET_INTERFACE (plugin)->get_name (plugin);
}
-char **
-mm_plugin_list_supported_udis (MMPlugin *plugin,
- LibHalContext *hal_ctx)
-{
- g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
- g_return_val_if_fail (hal_ctx != NULL, NULL);
-
- return MM_PLUGIN_GET_INTERFACE (plugin)->list_supported_udis (plugin, hal_ctx);
-}
-
-gboolean
-mm_plugin_supports_udi (MMPlugin *plugin,
- LibHalContext *hal_ctx,
- const char *udi)
+guint32
+mm_plugin_supports_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name)
{
g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
- g_return_val_if_fail (hal_ctx != NULL, FALSE);
- g_return_val_if_fail (udi != NULL, FALSE);
+ g_return_val_if_fail (subsys != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
- return MM_PLUGIN_GET_INTERFACE (plugin)->supports_udi (plugin, hal_ctx, udi);
+ return MM_PLUGIN_GET_INTERFACE (plugin)->supports_port (plugin, subsys, name);
}
MMModem *
-mm_plugin_create_modem (MMPlugin *plugin,
- LibHalContext *hal_ctx,
- const char *udi)
+mm_plugin_grab_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ GError **error)
{
- MMModem *modem;
-
- g_return_val_if_fail (MM_IS_PLUGIN (plugin), NULL);
- g_return_val_if_fail (hal_ctx != NULL, NULL);
- g_return_val_if_fail (udi != NULL, NULL);
-
- modem = MM_PLUGIN_GET_INTERFACE (plugin)->create_modem (plugin, hal_ctx, udi);
- if (modem)
- g_debug ("Created new %s modem (%s)", mm_plugin_get_name (plugin), udi);
- else
- g_warning ("Failed to create %s modem (%s)", mm_plugin_get_name (plugin), udi);
+ g_return_val_if_fail (MM_IS_PLUGIN (plugin), FALSE);
+ g_return_val_if_fail (subsys != NULL, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
- return modem;
+ return MM_PLUGIN_GET_INTERFACE (plugin)->grab_port (plugin, subsys, name, error);
}
-
/*****************************************************************************/
static void
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index 0f9a600b..07f7f60c 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -4,10 +4,9 @@
#define MM_PLUGIN_H
#include <glib-object.h>
-#include <libhal.h>
#include <mm-modem.h>
-#define MM_PLUGIN_MAJOR_VERSION 1
+#define MM_PLUGIN_MAJOR_VERSION 2
#define MM_PLUGIN_MINOR_VERSION 0
#define MM_TYPE_PLUGIN (mm_plugin_get_type ())
@@ -23,33 +22,44 @@ struct _MMPlugin {
GTypeInterface g_iface;
/* Methods */
- const char *(*get_name) (MMPlugin *self);
-
- char **(*list_supported_udis) (MMPlugin *self,
- LibHalContext *hal_ctx);
-
- gboolean (*supports_udi) (MMPlugin *self,
- LibHalContext *hal_ctx,
- const char *udi);
-
- MMModem *(*create_modem) (MMPlugin *self,
- LibHalContext *hal_ctx,
- const char *udi);
+ const char *(*get_name) (MMPlugin *self);
+
+ /* Check whether a plugin supports a particular modem port, and what level
+ * of support the plugin has for the device. The plugin should return a
+ * value between 0 and 100 inclusive, where 0 means the plugin has no
+ * support for the device, and 100 means the plugin has full support for the
+ * device.
+ */
+ guint32 (*supports_port) (MMPlugin *self,
+ const char *subsys,
+ const char *name);
+
+ /* Will only be called if the plugin returns a value greater than 0 for
+ * the supports_port() method for this port. The plugin should create and
+ * return a new modem for the port's device if there is no existing modem
+ * to handle the port's hardware device, or should add the port to an
+ * existing modem and return that modem object. If an error is encountered
+ * while claiming the port, the error information should be returned in the
+ * error argument, and the plugin should return NULL.
+ */
+ MMModem * (*grab_port) (MMPlugin *self,
+ const char *subsys,
+ const char *name,
+ GError **error);
};
GType mm_plugin_get_type (void);
-const char *mm_plugin_get_name (MMPlugin *plugin);
+const char *mm_plugin_get_name (MMPlugin *plugin);
-char **mm_plugin_list_supported_udis (MMPlugin *plugin,
- LibHalContext *hal_ctx);
+guint32 mm_plugin_supports_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name);
-gboolean mm_plugin_supports_udi (MMPlugin *plugin,
- LibHalContext *hal_ctx,
- const char *udi);
-
-MMModem *mm_plugin_create_modem (MMPlugin *plugin,
- LibHalContext *hal_ctx,
- const char *udi);
+MMModem *mm_plugin_grab_port (MMPlugin *plugin,
+ const char *subsys,
+ const char *name,
+ GError **error);
#endif /* MM_PLUGIN_H */
+
diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c
new file mode 100644
index 00000000..e825d597
--- /dev/null
+++ b/src/mm-serial-port.c
@@ -0,0 +1,1107 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#define _GNU_SOURCE /* for strcasestr() */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <termio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#include "mm-serial-port.h"
+#include "mm-errors.h"
+#include "mm-options.h"
+
+static gboolean mm_serial_port_queue_process (gpointer data);
+
+G_DEFINE_TYPE (MMSerialPort, mm_serial_port, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ PROP_BAUD,
+ PROP_BITS,
+ PROP_PARITY,
+ PROP_STOPBITS,
+ PROP_SEND_DELAY,
+ PROP_CARRIER_DETECT,
+
+ LAST_PROP
+};
+
+#define SERIAL_BUF_SIZE 2048
+
+#define MM_SERIAL_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL_PORT, MMSerialPortPrivate))
+
+typedef struct {
+ int fd;
+ GHashTable *reply_cache;
+ GIOChannel *channel;
+ GQueue *queue;
+ GString *command;
+ GString *response;
+
+ /* Response parser data */
+ MMSerialResponseParserFn response_parser_fn;
+ gpointer response_parser_user_data;
+ GDestroyNotify response_parser_notify;
+ GSList *unsolicited_msg_handlers;
+
+ struct termios old_t;
+
+ char *device;
+ guint baud;
+ guint bits;
+ char parity;
+ guint stopbits;
+ guint64 send_delay;
+ gboolean carrier_detect;
+
+ guint queue_schedule;
+ guint watch_id;
+ guint timeout_id;
+} MMSerialPortPrivate;
+
+typedef struct {
+ GRegex *regex;
+ MMSerialUnsolicitedMsgFn callback;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMUnsolicitedMsgHandler;
+
+const char *
+mm_serial_port_get_device (MMSerialPort *self)
+{
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), NULL);
+
+ return MM_SERIAL_PORT_GET_PRIVATE (self)->device;
+}
+
+static void
+mm_serial_port_set_cached_reply (MMSerialPort *self,
+ const char *command,
+ const char *reply)
+{
+ if (reply)
+ g_hash_table_insert (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache,
+ g_strdup (command),
+ g_strdup (reply));
+ else
+ g_hash_table_remove (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
+}
+
+static const char *
+mm_serial_port_get_cached_reply (MMSerialPort *self,
+ const char *command)
+{
+ return (char *) g_hash_table_lookup (MM_SERIAL_PORT_GET_PRIVATE (self)->reply_cache, command);
+}
+
+static int
+parse_baudrate (guint i)
+{
+ int speed;
+
+ switch (i) {
+ case 0:
+ speed = B0;
+ break;
+ case 50:
+ speed = B50;
+ break;
+ case 75:
+ speed = B75;
+ break;
+ case 110:
+ speed = B110;
+ break;
+ case 150:
+ speed = B150;
+ break;
+ case 300:
+ speed = B300;
+ break;
+ case 600:
+ speed = B600;
+ break;
+ case 1200:
+ speed = B1200;
+ break;
+ case 2400:
+ speed = B2400;
+ break;
+ case 4800:
+ speed = B4800;
+ break;
+ case 9600:
+ speed = B9600;
+ break;
+ case 19200:
+ speed = B19200;
+ break;
+ case 38400:
+ speed = B38400;
+ break;
+ case 57600:
+ speed = B57600;
+ break;
+ case 115200:
+ speed = B115200;
+ break;
+ case 460800:
+ speed = B460800;
+ break;
+ default:
+ g_warning ("Invalid baudrate '%d'", i);
+ speed = B9600;
+ }
+
+ return speed;
+}
+
+static int
+parse_bits (guint i)
+{
+ int bits;
+
+ switch (i) {
+ case 5:
+ bits = CS5;
+ break;
+ case 6:
+ bits = CS6;
+ break;
+ case 7:
+ bits = CS7;
+ break;
+ case 8:
+ bits = CS8;
+ break;
+ default:
+ g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
+ bits = CS8;
+ }
+
+ return bits;
+}
+
+static int
+parse_parity (char c)
+{
+ int parity;
+
+ switch (c) {
+ case 'n':
+ case 'N':
+ parity = 0;
+ break;
+ case 'e':
+ case 'E':
+ parity = PARENB;
+ break;
+ case 'o':
+ case 'O':
+ parity = PARENB | PARODD;
+ break;
+ default:
+ g_warning ("Invalid parity (%c). Valid values are n, e, o", c);
+ parity = 0;
+ }
+
+ return parity;
+}
+
+static int
+parse_stopbits (guint i)
+{
+ int stopbits;
+
+ switch (i) {
+ case 1:
+ stopbits = 0;
+ break;
+ case 2:
+ stopbits = CSTOPB;
+ break;
+ default:
+ g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
+ stopbits = 0;
+ }
+
+ return stopbits;
+}
+
+static gboolean
+config_fd (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ struct termio stbuf;
+ int speed;
+ int bits;
+ int parity;
+ int stopbits;
+
+ speed = parse_baudrate (priv->baud);
+ bits = parse_bits (priv->bits);
+ parity = parse_parity (priv->parity);
+ stopbits = parse_stopbits (priv->stopbits);
+
+ ioctl (priv->fd, TCGETA, &stbuf);
+
+ stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
+ stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
+ stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
+ stbuf.c_lflag &= ~(ECHO | ECHOE);
+ stbuf.c_cc[VMIN] = 1;
+ stbuf.c_cc[VTIME] = 0;
+ stbuf.c_cc[VEOF] = 1;
+
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
+ stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
+
+ if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
+ g_warning ("(%s) cannot control device (errno %d)", priv->device, errno);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+serial_debug (const char *prefix, const char *buf, int len)
+{
+ static GString *debug = NULL;
+ const char *s;
+
+ if (!mm_options_debug ())
+ return;
+
+ if (len < 0)
+ len = strlen (buf);
+
+ if (!debug)
+ debug = g_string_sized_new (256);
+
+ g_string_append (debug, prefix);
+ g_string_append (debug, " '");
+
+ s = buf;
+ while (len--) {
+ if (g_ascii_isprint (*s))
+ g_string_append_c (debug, *s);
+ else if (*s == '\r')
+ g_string_append (debug, "<CR>");
+ else if (*s == '\n')
+ g_string_append (debug, "<LF>");
+ else
+ g_string_append_printf (debug, "\\%d", *s);
+
+ s++;
+ }
+
+ g_string_append_c (debug, '\'');
+ g_debug ("%s", debug->str);
+ g_string_truncate (debug, 0);
+}
+
+static gboolean
+mm_serial_port_send_command (MMSerialPort *self,
+ const char *command,
+ GError **error)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ const char *s;
+ int status;
+ int eagain_count = 1000;
+
+ if (priv->fd < 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ "%s", "Sending command failed: device is not enabled");
+ return FALSE;
+ }
+
+ if (mm_serial_port_is_connected (self)) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ "%s", "Sending command failed: device is connected");
+ return FALSE;
+ }
+
+ g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2);
+ g_string_append (priv->command, command);
+
+ if (command[strlen (command)] != '\r')
+ g_string_append_c (priv->command, '\r');
+
+ serial_debug ("-->", priv->command->str, -1);
+
+ s = priv->command->str;
+ while (*s) {
+ status = write (priv->fd, s, 1);
+ if (status < 0) {
+ if (errno == EAGAIN) {
+ eagain_count--;
+ if (eagain_count <= 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ "Sending command failed: '%s'", strerror (errno));
+ break;
+ }
+ } else {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
+ "Sending command failed: '%s'", strerror (errno));
+ break;
+ }
+ } else
+ s++;
+
+ if (priv->send_delay)
+ usleep (priv->send_delay);
+ }
+
+ return *s == '\0';
+}
+
+typedef struct {
+ char *command;
+ MMSerialResponseFn callback;
+ gpointer user_data;
+ guint32 timeout;
+ gboolean cached;
+} MMQueueData;
+
+static void
+mm_serial_port_schedule_queue_process (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ GSource *source;
+
+ if (priv->queue_schedule)
+ /* Already scheduled */
+ return;
+
+ source = g_idle_source_new ();
+ g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self)));
+ g_source_attach (source, NULL);
+ priv->queue_schedule = g_source_get_id (source);
+ g_source_unref (source);
+}
+
+static void
+mm_serial_port_got_response (MMSerialPort *self, GError *error)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ MMQueueData *info;
+
+ if (priv->timeout_id)
+ g_source_remove (priv->timeout_id);
+
+ info = (MMQueueData *) g_queue_pop_head (priv->queue);
+ if (info) {
+ if (info->cached && !error)
+ mm_serial_port_set_cached_reply (self, info->command, priv->response->str);
+
+ if (info->callback)
+ info->callback (self, priv->response, error, info->user_data);
+
+ g_free (info->command);
+ g_slice_free (MMQueueData, info);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ g_string_truncate (priv->response, 0);
+ if (!g_queue_is_empty (priv->queue))
+ mm_serial_port_schedule_queue_process (self);
+}
+
+static gboolean
+mm_serial_port_timed_out (gpointer data)
+{
+ MMSerialPort *self = MM_SERIAL_PORT (data);
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ GError *error;
+
+ priv->timeout_id = 0;
+
+ error = g_error_new_literal (MM_SERIAL_ERROR,
+ MM_SERIAL_RESPONSE_TIMEOUT,
+ "Serial command timed out");
+ /* FIXME: This is not completely correct - if the response finally arrives and there's
+ some other command waiting for response right now, the other command will
+ get the output of the timed out command. Not sure what to do here. */
+ mm_serial_port_got_response (self, error);
+
+ return FALSE;
+}
+
+static gboolean
+mm_serial_port_queue_process (gpointer data)
+{
+ MMSerialPort *self = MM_SERIAL_PORT (data);
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ MMQueueData *info;
+ GError *error = NULL;
+
+ priv->queue_schedule = 0;
+
+ info = (MMQueueData *) g_queue_peek_head (priv->queue);
+ if (!info)
+ return FALSE;
+
+ if (info->cached) {
+ const char *cached = mm_serial_port_get_cached_reply (self, info->command);
+
+ if (cached) {
+ g_string_append (priv->response, cached);
+ mm_serial_port_got_response (self, NULL);
+ return FALSE;
+ }
+ }
+
+ if (mm_serial_port_send_command (self, info->command, &error)) {
+ GSource *source;
+
+ source = g_timeout_source_new (info->timeout);
+ g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self)));
+ g_source_attach (source, NULL);
+ priv->timeout_id = g_source_get_id (source);
+ g_source_unref (source);
+ } else {
+ mm_serial_port_got_response (self, error);
+ }
+
+ return FALSE;
+}
+
+void
+mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
+ GRegex *regex,
+ MMSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMUnsolicitedMsgHandler *handler;
+ MMSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+ g_return_if_fail (regex != NULL);
+
+ handler = g_slice_new (MMUnsolicitedMsgHandler);
+ handler->regex = g_regex_ref (regex);
+ handler->callback = callback;
+ handler->user_data = user_data;
+ handler->notify = notify;
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
+}
+
+void
+mm_serial_port_set_response_parser (MMSerialPort *self,
+ MMSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+
+ if (priv->response_parser_notify)
+ priv->response_parser_notify (priv->response_parser_user_data);
+
+ priv->response_parser_fn = fn;
+ priv->response_parser_user_data = user_data;
+ priv->response_parser_notify = notify;
+}
+
+static gboolean
+remove_eval_cb (const GMatchInfo *match_info,
+ GString *result,
+ gpointer user_data)
+{
+ int *result_len = (int *) user_data;
+ int start;
+ int end;
+
+ if (g_match_info_fetch_pos (match_info, 0, &start, &end))
+ *result_len -= (end - start);
+
+ return FALSE;
+}
+
+static void
+parse_unsolicited_messages (MMSerialPort *self,
+ GString *response)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data;
+ GMatchInfo *match_info;
+ gboolean matches;
+
+ matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL);
+ if (handler->callback) {
+ while (g_match_info_matches (match_info)) {
+ handler->callback (self, match_info, handler->user_data);
+ g_match_info_next (match_info, NULL);
+ }
+ }
+
+ g_match_info_free (match_info);
+
+ if (matches) {
+ /* Remove matches */
+ char *str;
+ int result_len = response->len;
+
+ str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0,
+ remove_eval_cb, &result_len, NULL);
+
+ g_string_truncate (response, 0);
+ g_string_append_len (response, str, result_len);
+ g_free (str);
+ }
+ }
+}
+
+static gboolean
+parse_response (MMSerialPort *self,
+ GString *response,
+ GError **error)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
+
+ parse_unsolicited_messages (self, response);
+
+ return priv->response_parser_fn (priv->response_parser_user_data, response, error);
+}
+
+static gboolean
+data_available (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ MMSerialPort *self = MM_SERIAL_PORT (data);
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ char buf[SERIAL_BUF_SIZE + 1];
+ gsize bytes_read;
+ GIOStatus status;
+
+ if (condition & G_IO_HUP) {
+ g_string_truncate (priv->response, 0);
+ mm_serial_port_close (self);
+ return FALSE;
+ }
+
+ if (condition & G_IO_ERR) {
+ g_string_truncate (priv->response, 0);
+ return TRUE;
+ }
+
+ do {
+ GError *err = NULL;
+
+ status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
+ if (status == G_IO_STATUS_ERROR) {
+ g_warning ("%s", err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+
+ if (bytes_read > 0) {
+ serial_debug ("<--", buf, bytes_read);
+ g_string_append_len (priv->response, buf, bytes_read);
+ }
+
+ /* Make sure the string doesn't grow too long */
+ if (priv->response->len > SERIAL_BUF_SIZE) {
+ g_warning ("%s (%s): response buffer filled before repsonse received",
+ G_STRFUNC, mm_serial_port_get_device (self));
+ g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2));
+ }
+
+ if (parse_response (self, priv->response, &err))
+ mm_serial_port_got_response (self, err);
+ } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
+
+ return TRUE;
+}
+
+gboolean
+mm_serial_port_open (MMSerialPort *self, GError **error)
+{
+ MMSerialPortPrivate *priv;
+ char *devfile;
+
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ if (priv->fd >= 0)
+ /* Already open */
+ return TRUE;
+
+ g_debug ("(%s) opening serial device...", priv->device);
+ devfile = g_strdup_printf ("/dev/%s", priv->device);
+ priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
+ g_free (devfile);
+
+ if (priv->fd < 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ "Could not open serial device %s: %s", priv->device, strerror (errno));
+ return FALSE;
+ }
+
+ if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ "Could not open serial device %s: %s", priv->device, strerror (errno));
+ close (priv->fd);
+ priv->fd = -1;
+ return FALSE;
+ }
+
+ if (!config_fd (self)) {
+ g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
+ "Could not open serial device %s: %s", priv->device, strerror (errno));
+ close (priv->fd);
+ priv->fd = -1;
+ return FALSE;
+ }
+
+ priv->channel = g_io_channel_unix_new (priv->fd);
+ priv->watch_id = g_io_add_watch (priv->channel,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ data_available, self);
+
+ return TRUE;
+}
+
+void
+mm_serial_port_close (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv;
+
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ if (priv->fd >= 0) {
+ g_message ("Closing device '%s'", priv->device);
+
+ if (priv->channel) {
+ g_source_remove (priv->watch_id);
+ g_io_channel_shutdown (priv->channel, TRUE, NULL);
+ g_io_channel_unref (priv->channel);
+ priv->channel = NULL;
+ }
+
+ ioctl (priv->fd, TCSETA, &priv->old_t);
+ close (priv->fd);
+ priv->fd = -1;
+ }
+}
+
+static void
+internal_queue_command (MMSerialPort *self,
+ const char *command,
+ gboolean cached,
+ guint32 timeout_seconds,
+ MMSerialResponseFn callback,
+ gpointer user_data)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+ MMQueueData *info;
+
+ g_return_if_fail (MM_IS_SERIAL_PORT (self));
+ g_return_if_fail (command != NULL);
+
+ info = g_slice_new0 (MMQueueData);
+ info->command = g_strdup (command);
+ info->cached = cached;
+ info->timeout = timeout_seconds * 1000;
+ info->callback = callback;
+ info->user_data = user_data;
+
+ /* Clear the cached value for this command if not asking for cached value */
+ if (!cached)
+ mm_serial_port_set_cached_reply (self, command, NULL);
+
+ g_queue_push_tail (priv->queue, info);
+
+ if (g_queue_get_length (priv->queue) == 1)
+ mm_serial_port_schedule_queue_process (self);
+}
+
+void
+mm_serial_port_queue_command (MMSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMSerialResponseFn callback,
+ gpointer user_data)
+{
+ internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data);
+}
+
+void
+mm_serial_port_queue_command_cached (MMSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMSerialResponseFn callback,
+ gpointer user_data)
+{
+ internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data);
+}
+
+typedef struct {
+ MMSerialPort *port;
+ speed_t current_speed;
+ MMSerialFlashFn callback;
+ gpointer user_data;
+} FlashInfo;
+
+static speed_t
+get_speed (MMSerialPort *self)
+{
+ struct termios options;
+
+ tcgetattr (MM_SERIAL_PORT_GET_PRIVATE (self)->fd, &options);
+
+ return cfgetospeed (&options);
+}
+
+static void
+set_speed (MMSerialPort *self, speed_t speed)
+{
+ struct termios options;
+ int fd;
+
+ fd = MM_SERIAL_PORT_GET_PRIVATE (self)->fd;
+ tcgetattr (fd, &options);
+
+ cfsetispeed (&options, speed);
+ cfsetospeed (&options, speed);
+
+ options.c_cflag |= (CLOCAL | CREAD);
+ tcsetattr (fd, TCSANOW, &options);
+}
+
+static void
+flash_done (gpointer data)
+{
+ FlashInfo *info = (FlashInfo *) data;
+
+ info->callback (info->port, info->user_data);
+
+ g_slice_free (FlashInfo, info);
+}
+
+static gboolean
+flash_do (gpointer data)
+{
+ FlashInfo *info = (FlashInfo *) data;
+
+ set_speed (info->port, info->current_speed);
+
+ return FALSE;
+}
+
+guint
+mm_serial_port_flash (MMSerialPort *self,
+ guint32 flash_time,
+ MMSerialFlashFn callback,
+ gpointer user_data)
+{
+ FlashInfo *info;
+ guint id;
+
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), 0);
+ g_return_val_if_fail (callback != NULL, 0);
+
+ info = g_slice_new0 (FlashInfo);
+ info->port = self;
+ info->current_speed = get_speed (self);
+ info->callback = callback;
+ info->user_data = user_data;
+
+ set_speed (self, B0);
+
+ id = g_timeout_add_full (G_PRIORITY_DEFAULT,
+ flash_time,
+ flash_do,
+ info,
+ flash_done);
+
+ return id;
+}
+
+gboolean
+mm_serial_port_is_connected (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv;
+ int mcs = 0;
+
+ g_return_val_if_fail (MM_IS_SERIAL_PORT (self), FALSE);
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ if (!priv->carrier_detect)
+ return FALSE;
+
+ if (priv->fd < 0)
+ return FALSE;
+
+ if (ioctl (priv->fd, TIOCMGET, &mcs) < 0)
+ return FALSE;
+
+ return mcs & TIOCM_CAR ? TRUE : FALSE;
+}
+
+/*****************************************************************************/
+
+MMSerialPort *
+mm_serial_port_new (const char *name)
+{
+ return MM_SERIAL_PORT (g_object_new (MM_TYPE_SERIAL_PORT,
+ MM_SERIAL_PORT_DEVICE, name,
+ NULL));
+}
+
+static void
+mm_serial_port_init (MMSerialPort *self)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ priv->fd = -1;
+ priv->baud = 57600;
+ priv->bits = 8;
+ priv->parity = 'n';
+ priv->stopbits = 1;
+ priv->send_delay = 1000;
+ priv->carrier_detect = TRUE;
+
+ priv->queue = g_queue_new ();
+ priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE);
+ priv->response = g_string_sized_new (SERIAL_BUF_SIZE);
+}
+
+static GObject*
+constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ MMSerialPortPrivate *priv;
+
+ object = G_OBJECT_CLASS (mm_serial_port_parent_class)->constructor (type,
+ n_construct_params,
+ construct_params);
+ if (!object)
+ return NULL;
+
+ priv = MM_SERIAL_PORT_GET_PRIVATE (object);
+
+ if (!priv->device) {
+ g_warning ("No device provided");
+ g_object_unref (object);
+ return NULL;
+ }
+
+ return object;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ /* Construct only */
+ priv->device = g_path_get_basename (g_value_get_string (value));
+ break;
+ case PROP_BAUD:
+ priv->baud = g_value_get_uint (value);
+ break;
+ case PROP_BITS:
+ priv->bits = g_value_get_uint (value);
+ break;
+ case PROP_PARITY:
+ priv->parity = g_value_get_char (value);
+ break;
+ case PROP_STOPBITS:
+ priv->stopbits = g_value_get_uint (value);
+ break;
+ case PROP_SEND_DELAY:
+ priv->send_delay = g_value_get_uint64 (value);
+ break;
+ case PROP_CARRIER_DETECT:
+ priv->carrier_detect = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE:
+ g_value_set_string (value, priv->device);
+ break;
+ case PROP_BAUD:
+ g_value_set_uint (value, priv->baud);
+ break;
+ case PROP_BITS:
+ g_value_set_uint (value, priv->bits);
+ break;
+ case PROP_PARITY:
+ g_value_set_char (value, priv->parity);
+ break;
+ case PROP_STOPBITS:
+ g_value_set_uint (value, priv->stopbits);
+ break;
+ case PROP_SEND_DELAY:
+ g_value_set_uint64 (value, priv->send_delay);
+ break;
+ case PROP_CARRIER_DETECT:
+ g_value_set_boolean (value, priv->carrier_detect);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSerialPort *self = MM_SERIAL_PORT (object);
+ MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self);
+
+ mm_serial_port_close (self);
+
+ g_hash_table_destroy (priv->reply_cache);
+ g_queue_free (priv->queue);
+ g_string_free (priv->command, TRUE);
+ g_string_free (priv->response, TRUE);
+ g_free (priv->device);
+
+ while (priv->unsolicited_msg_handlers) {
+ MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_regex_unref (handler->regex);
+ g_slice_free (MMUnsolicitedMsgHandler, handler);
+ priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
+ priv->unsolicited_msg_handlers);
+ }
+
+ if (priv->response_parser_notify)
+ priv->response_parser_notify (priv->response_parser_user_data);
+
+ G_OBJECT_CLASS (mm_serial_port_parent_class)->finalize (object);
+}
+
+static void
+mm_serial_port_class_init (MMSerialPortClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMSerialPortPrivate));
+
+ /* Virtual methods */
+ object_class->constructor = constructor;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ /* Properties */
+ g_object_class_install_property
+ (object_class, PROP_DEVICE,
+ g_param_spec_string (MM_SERIAL_PORT_DEVICE,
+ "Device",
+ "Serial device",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_BAUD,
+ g_param_spec_uint (MM_SERIAL_PORT_BAUD,
+ "Baud",
+ "Baud rate",
+ 0, G_MAXUINT, 57600,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_BITS,
+ g_param_spec_uint (MM_SERIAL_PORT_BITS,
+ "Bits",
+ "Bits",
+ 5, 8, 8,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_PARITY,
+ g_param_spec_char (MM_SERIAL_PORT_PARITY,
+ "Parity",
+ "Parity",
+ 'E', 'o', 'n',
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_STOPBITS,
+ g_param_spec_uint (MM_SERIAL_PORT_STOPBITS,
+ "Stopbits",
+ "Stopbits",
+ 1, 2, 1,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_SEND_DELAY,
+ g_param_spec_uint64 (MM_SERIAL_PORT_SEND_DELAY,
+ "SendDelay",
+ "Send delay",
+ 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_CARRIER_DETECT,
+ g_param_spec_boolean (MM_SERIAL_PORT_CARRIER_DETECT,
+ "CarrierDetect",
+ "Has carrier detect",
+ TRUE,
+ G_PARAM_READWRITE));
+}
diff --git a/src/mm-serial-port.h b/src/mm-serial-port.h
new file mode 100644
index 00000000..7056527a
--- /dev/null
+++ b/src/mm-serial-port.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef MM_SERIAL_PORT_H
+#define MM_SERIAL_PORT_H
+
+#include <glib.h>
+#include <glib/gtypes.h>
+#include <glib-object.h>
+
+typedef enum {
+ MM_SERIAL_PORT_TYPE_UNKNOWN = 0x0,
+ MM_SERIAL_PORT_TYPE_PRIMARY,
+ MM_SERIAL_PORT_TYPE_SECONDARY,
+ MM_SERIAL_PORT_TYPE_IGNORED
+} MMSerialPortType;
+
+#define MM_TYPE_SERIAL_PORT (mm_serial_port_get_type ())
+#define MM_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL_PORT, MMSerialPort))
+#define MM_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL_PORT, MMSerialPortClass))
+#define MM_IS_SERIAL_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SERIAL_PORT))
+#define MM_IS_SERIAL_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL_PORT))
+#define MM_SERIAL_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL_PORT, MMSerialPortClass))
+
+#define MM_SERIAL_PORT_DEVICE "serial-device"
+#define MM_SERIAL_PORT_BAUD "baud"
+#define MM_SERIAL_PORT_BITS "bits"
+#define MM_SERIAL_PORT_PARITY "parity"
+#define MM_SERIAL_PORT_STOPBITS "stopbits"
+#define MM_SERIAL_PORT_SEND_DELAY "send-delay"
+#define MM_SERIAL_PORT_CARRIER_DETECT "carrier-detect"
+
+typedef struct _MMSerialPort MMSerialPort;
+typedef struct _MMSerialPortClass MMSerialPortClass;
+
+typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
+ GString *response,
+ GError **error);
+
+typedef void (*MMSerialUnsolicitedMsgFn) (MMSerialPort *port,
+ GMatchInfo *match_info,
+ gpointer user_data);
+
+typedef void (*MMSerialResponseFn) (MMSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+typedef void (*MMSerialFlashFn) (MMSerialPort *port,
+ gpointer user_data);
+
+struct _MMSerialPort {
+ GObject parent;
+};
+
+struct _MMSerialPortClass {
+ GObjectClass parent;
+};
+
+GType mm_serial_port_get_type (void);
+
+MMSerialPort *mm_serial_port_new (const char *name);
+
+void mm_serial_port_add_unsolicited_msg_handler (MMSerialPort *self,
+ GRegex *regex,
+ MMSerialUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_serial_port_set_response_parser (MMSerialPort *self,
+ MMSerialResponseParserFn fn,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+gboolean mm_serial_port_open (MMSerialPort *self,
+ GError **error);
+
+void mm_serial_port_close (MMSerialPort *self);
+void mm_serial_port_queue_command (MMSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMSerialResponseFn callback,
+ gpointer user_data);
+
+void mm_serial_port_queue_command_cached (MMSerialPort *self,
+ const char *command,
+ guint32 timeout_seconds,
+ MMSerialResponseFn callback,
+ gpointer user_data);
+
+guint mm_serial_port_flash (MMSerialPort *self,
+ guint32 flash_time,
+ MMSerialFlashFn callback,
+ gpointer user_data);
+
+gboolean mm_serial_port_is_connected (MMSerialPort *self);
+
+const char *mm_serial_port_get_device (MMSerialPort *self);
+
+#endif /* MM_SERIAL_PORT_H */
+
diff --git a/src/mm-serial.c b/src/mm-serial.c
index 0301a592..48492ce4 100644
--- a/src/mm-serial.c
+++ b/src/mm-serial.c
@@ -1,864 +1,100 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-
-#define _GNU_SOURCE /* for strcasestr() */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
#include <stdio.h>
#include <stdlib.h>
-#include <termio.h>
#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/ioctl.h>
#include <string.h>
#include "mm-serial.h"
#include "mm-errors.h"
#include "mm-options.h"
-static gboolean mm_serial_queue_process (gpointer data);
+#define TYPE_TAG "type"
G_DEFINE_TYPE (MMSerial, mm_serial, G_TYPE_OBJECT)
enum {
PROP_0,
- PROP_DEVICE,
- PROP_BAUD,
- PROP_BITS,
- PROP_PARITY,
- PROP_STOPBITS,
- PROP_SEND_DELAY,
- PROP_CARRIER_DETECT,
LAST_PROP
};
-#define SERIAL_BUF_SIZE 2048
-
#define MM_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_SERIAL, MMSerialPrivate))
typedef struct {
- int fd;
- GHashTable *reply_cache;
- GIOChannel *channel;
- GQueue *queue;
- GString *command;
- GString *response;
-
- /* Response parser data */
- MMSerialResponseParserFn response_parser_fn;
- gpointer response_parser_user_data;
- GDestroyNotify response_parser_notify;
- GSList *unsolicited_msg_handlers;
-
- struct termios old_t;
-
- char *device;
- guint baud;
- guint bits;
- char parity;
- guint stopbits;
- guint64 send_delay;
- gboolean carrier_detect;
-
- guint queue_schedule;
- guint watch_id;
- guint timeout_id;
+ GHashTable *ports;
} MMSerialPrivate;
-typedef struct {
- GRegex *regex;
- MMSerialUnsolicitedMsgFn callback;
- gpointer user_data;
- GDestroyNotify notify;
-} MMUnsolicitedMsgHandler;
-
-const char *
-mm_serial_get_device (MMSerial *serial)
-{
- g_return_val_if_fail (MM_IS_SERIAL (serial), NULL);
-
- return MM_SERIAL_GET_PRIVATE (serial)->device;
-}
-
-static void
-mm_serial_set_cached_reply (MMSerial *self,
- const char *command,
- const char *reply)
-{
- if (reply)
- g_hash_table_insert (MM_SERIAL_GET_PRIVATE (self)->reply_cache,
- g_strdup (command),
- g_strdup (reply));
- else
- g_hash_table_remove (MM_SERIAL_GET_PRIVATE (self)->reply_cache, command);
-}
-
-static const char *
-mm_serial_get_cached_reply (MMSerial *self,
- const char *command)
-{
- return (char *) g_hash_table_lookup (MM_SERIAL_GET_PRIVATE (self)->reply_cache, command);
-}
-
-static int
-parse_baudrate (guint i)
-{
- int speed;
-
- switch (i) {
- case 0:
- speed = B0;
- break;
- case 50:
- speed = B50;
- break;
- case 75:
- speed = B75;
- break;
- case 110:
- speed = B110;
- break;
- case 150:
- speed = B150;
- break;
- case 300:
- speed = B300;
- break;
- case 600:
- speed = B600;
- break;
- case 1200:
- speed = B1200;
- break;
- case 2400:
- speed = B2400;
- break;
- case 4800:
- speed = B4800;
- break;
- case 9600:
- speed = B9600;
- break;
- case 19200:
- speed = B19200;
- break;
- case 38400:
- speed = B38400;
- break;
- case 57600:
- speed = B57600;
- break;
- case 115200:
- speed = B115200;
- break;
- case 460800:
- speed = B460800;
- break;
- default:
- g_warning ("Invalid baudrate '%d'", i);
- speed = B9600;
- }
-
- return speed;
-}
-
-static int
-parse_bits (guint i)
-{
- int bits;
-
- switch (i) {
- case 5:
- bits = CS5;
- break;
- case 6:
- bits = CS6;
- break;
- case 7:
- bits = CS7;
- break;
- case 8:
- bits = CS8;
- break;
- default:
- g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
- bits = CS8;
- }
-
- return bits;
-}
-
-static int
-parse_parity (char c)
-{
- int parity;
-
- switch (c) {
- case 'n':
- case 'N':
- parity = 0;
- break;
- case 'e':
- case 'E':
- parity = PARENB;
- break;
- case 'o':
- case 'O':
- parity = PARENB | PARODD;
- break;
- default:
- g_warning ("Invalid parity (%c). Valid values are n, e, o", c);
- parity = 0;
- }
-
- return parity;
-}
-
-static int
-parse_stopbits (guint i)
-{
- int stopbits;
-
- switch (i) {
- case 1:
- stopbits = 0;
- break;
- case 2:
- stopbits = CSTOPB;
- break;
- default:
- g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
- stopbits = 0;
- }
-
- return stopbits;
-}
-
-static gboolean
-config_fd (MMSerial *self)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- struct termio stbuf;
- int speed;
- int bits;
- int parity;
- int stopbits;
-
- speed = parse_baudrate (priv->baud);
- bits = parse_bits (priv->bits);
- parity = parse_parity (priv->parity);
- stopbits = parse_stopbits (priv->stopbits);
-
- ioctl (priv->fd, TCGETA, &stbuf);
-
- stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
- stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
- stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
- stbuf.c_lflag &= ~(ECHO | ECHOE);
- stbuf.c_cc[VMIN] = 1;
- stbuf.c_cc[VTIME] = 0;
- stbuf.c_cc[VEOF] = 1;
-
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
- stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
-
- if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
- g_warning ("(%s) cannot control device (errno %d)", priv->device, errno);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static void
-serial_debug (const char *prefix, const char *buf, int len)
-{
- static GString *debug = NULL;
- const char *s;
-
- if (!mm_options_debug ())
- return;
-
- if (len < 0)
- len = strlen (buf);
-
- if (!debug)
- debug = g_string_sized_new (256);
-
- g_string_append (debug, prefix);
- g_string_append (debug, " '");
-
- s = buf;
- while (len--) {
- if (g_ascii_isprint (*s))
- g_string_append_c (debug, *s);
- else if (*s == '\r')
- g_string_append (debug, "<CR>");
- else if (*s == '\n')
- g_string_append (debug, "<LF>");
- else
- g_string_append_printf (debug, "\\%d", *s);
-
- s++;
- }
-
- g_string_append_c (debug, '\'');
- g_debug ("%s", debug->str);
- g_string_truncate (debug, 0);
-}
-
-static gboolean
-mm_serial_send_command (MMSerial *self,
- const char *command,
- GError **error)
+MMSerialPort *
+mm_serial_get_port (MMSerial *self, const char *name)
{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- const char *s;
- int status;
- int eagain_count = 1000;
-
- if (priv->fd == 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
- "%s", "Sending command failed: device is not enabled");
- return FALSE;
- }
-
- if (mm_serial_is_connected (self)) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
- "%s", "Sending command failed: device is connected");
- return FALSE;
- }
-
- g_string_truncate (priv->command, g_str_has_prefix (command, "AT") ? 0 : 2);
- g_string_append (priv->command, command);
-
- if (command[strlen (command)] != '\r')
- g_string_append_c (priv->command, '\r');
-
- serial_debug ("-->", priv->command->str, -1);
-
- s = priv->command->str;
- while (*s) {
- status = write (priv->fd, s, 1);
- if (status < 0) {
- if (errno == EAGAIN) {
- eagain_count--;
- if (eagain_count <= 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
- break;
- }
- } else {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
- break;
- }
- } else
- s++;
-
- if (priv->send_delay)
- usleep (priv->send_delay);
- }
-
- return *s == '\0';
+ return g_hash_table_lookup (MM_SERIAL_GET_PRIVATE (self)->ports, name);
}
-typedef struct {
- char *command;
- MMSerialResponseFn callback;
- gpointer user_data;
- guint32 timeout;
- gboolean cached;
-} MMQueueData;
-
static void
-mm_serial_schedule_queue_process (MMSerial *self)
+find_primary (gpointer key, gpointer data, gpointer user_data)
{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- GSource *source;
+ MMSerialPort **found = user_data;
+ MMSerialPort *port = MM_SERIAL_PORT (data);
+ MMSerialPortType ptype;
- if (priv->queue_schedule)
- /* Already scheduled */
+ if (*found)
return;
- source = g_idle_source_new ();
- g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_queue_process), G_OBJECT (self)));
- g_source_attach (source, NULL);
- priv->queue_schedule = g_source_get_id (source);
- g_source_unref (source);
-}
-
-static void
-mm_serial_got_response (MMSerial *self, GError *error)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- MMQueueData *info;
-
- if (priv->timeout_id)
- g_source_remove (priv->timeout_id);
-
- info = (MMQueueData *) g_queue_pop_head (priv->queue);
- if (info) {
- if (info->cached && !error)
- mm_serial_set_cached_reply (self, info->command, priv->response->str);
-
- if (info->callback)
- info->callback (self, priv->response, error, info->user_data);
-
- g_free (info->command);
- g_slice_free (MMQueueData, info);
- }
-
- if (error)
- g_error_free (error);
-
- g_string_truncate (priv->response, 0);
- if (!g_queue_is_empty (priv->queue))
- mm_serial_schedule_queue_process (self);
-}
-
-static gboolean
-mm_serial_timed_out (gpointer data)
-{
- MMSerial *self = MM_SERIAL (data);
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- GError *error;
-
- priv->timeout_id = 0;
-
- error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_RESPONSE_TIMEOUT,
- "Serial command timed out");
- /* FIXME: This is not completely correct - if the response finally arrives and there's
- some other command waiting for response right now, the other command will
- get the output of the timed out command. Not sure what to do here. */
- mm_serial_got_response (self, error);
-
- return FALSE;
-}
-
-static gboolean
-mm_serial_queue_process (gpointer data)
-{
- MMSerial *self = MM_SERIAL (data);
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- MMQueueData *info;
- GError *error = NULL;
-
- priv->queue_schedule = 0;
-
- info = (MMQueueData *) g_queue_peek_head (priv->queue);
- if (!info)
- return FALSE;
-
- if (info->cached) {
- const char *cached = mm_serial_get_cached_reply (self, info->command);
-
- if (cached) {
- g_string_append (priv->response, cached);
- mm_serial_got_response (self, NULL);
- return FALSE;
- }
- }
-
- if (mm_serial_send_command (self, info->command, &error)) {
- GSource *source;
-
- source = g_timeout_source_new (info->timeout);
- g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_timed_out), G_OBJECT (self)));
- g_source_attach (source, NULL);
- priv->timeout_id = g_source_get_id (source);
- g_source_unref (source);
- } else {
- mm_serial_got_response (self, error);
- }
-
- return FALSE;
-}
-
-void
-mm_serial_add_unsolicited_msg_handler (MMSerial *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMUnsolicitedMsgHandler *handler;
- MMSerialPrivate *priv;
-
- g_return_if_fail (MM_IS_SERIAL (self));
- g_return_if_fail (regex != NULL);
-
- handler = g_slice_new (MMUnsolicitedMsgHandler);
- handler->regex = g_regex_ref (regex);
- handler->callback = callback;
- handler->user_data = user_data;
- handler->notify = notify;
-
- priv = MM_SERIAL_GET_PRIVATE (self);
- priv->unsolicited_msg_handlers = g_slist_append (priv->unsolicited_msg_handlers, handler);
-}
-
-void
-mm_serial_set_response_parser (MMSerial *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
-
- g_return_if_fail (MM_IS_SERIAL (self));
-
- if (priv->response_parser_notify)
- priv->response_parser_notify (priv->response_parser_user_data);
-
- priv->response_parser_fn = fn;
- priv->response_parser_user_data = user_data;
- priv->response_parser_notify = notify;
-}
-
-static gboolean
-remove_eval_cb (const GMatchInfo *match_info,
- GString *result,
- gpointer user_data)
-{
- int *result_len = (int *) user_data;
- int start;
- int end;
-
- if (g_match_info_fetch_pos (match_info, 0, &start, &end))
- *result_len -= (end - start);
-
- return FALSE;
-}
-
-static void
-parse_unsolicited_messages (MMSerial *self,
- GString *response)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- GSList *iter;
-
- for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
- MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) iter->data;
- GMatchInfo *match_info;
- gboolean matches;
-
- matches = g_regex_match_full (handler->regex, response->str, response->len, 0, 0, &match_info, NULL);
- if (handler->callback) {
- while (g_match_info_matches (match_info)) {
- handler->callback (self, match_info, handler->user_data);
- g_match_info_next (match_info, NULL);
- }
- }
-
- g_match_info_free (match_info);
-
- if (matches) {
- /* Remove matches */
- char *str;
- int result_len = response->len;
-
- str = g_regex_replace_eval (handler->regex, response->str, response->len, 0, 0,
- remove_eval_cb, &result_len, NULL);
-
- g_string_truncate (response, 0);
- g_string_append_len (response, str, result_len);
- g_free (str);
- }
- }
-}
-
-static gboolean
-parse_response (MMSerial *self,
- GString *response,
- GError **error)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
-
- g_return_val_if_fail (priv->response_parser_fn != NULL, FALSE);
-
- parse_unsolicited_messages (self, response);
-
- return priv->response_parser_fn (priv->response_parser_user_data, response, error);
+ ptype = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (port), TYPE_TAG));
+ if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY)
+ *found = port;
}
-static gboolean
-data_available (GIOChannel *source,
- GIOCondition condition,
- gpointer data)
+MMSerialPort *
+mm_serial_add_port (MMSerial *self,
+ const char *name,
+ MMSerialPortType ptype)
{
- MMSerial *self = MM_SERIAL (data);
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- char buf[SERIAL_BUF_SIZE + 1];
- gsize bytes_read;
- GIOStatus status;
-
- if (condition & G_IO_HUP) {
- g_string_truncate (priv->response, 0);
- mm_serial_close (self);
- return FALSE;
- }
-
- if (condition & G_IO_ERR) {
- g_string_truncate (priv->response, 0);
- return TRUE;
- }
-
- do {
- GError *err = NULL;
-
- status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
- if (status == G_IO_STATUS_ERROR) {
- g_warning ("%s", err->message);
- g_error_free (err);
- err = NULL;
- }
-
- if (bytes_read > 0) {
- serial_debug ("<--", buf, bytes_read);
- g_string_append_len (priv->response, buf, bytes_read);
- }
-
- /* Make sure the string doesn't grow too long */
- if (priv->response->len > SERIAL_BUF_SIZE) {
- g_warning ("%s (%s): response buffer filled before repsonse received",
- G_STRFUNC, mm_serial_get_device (self));
- g_string_erase (priv->response, 0, (SERIAL_BUF_SIZE / 2));
- }
-
- if (parse_response (self, priv->response, &err))
- mm_serial_got_response (self, err);
- } while (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
-
- return TRUE;
-}
-
-gboolean
-mm_serial_open (MMSerial *self, GError **error)
-{
- MMSerialPrivate *priv;
+ MMSerialPort *port = NULL;
g_return_val_if_fail (MM_IS_SERIAL (self), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+ g_return_val_if_fail (ptype != MM_SERIAL_PORT_TYPE_UNKNOWN, FALSE);
- priv = MM_SERIAL_GET_PRIVATE (self);
-
- if (priv->fd)
- /* Already open */
- return TRUE;
-
- g_debug ("(%s) opening serial device...", priv->device);
- priv->fd = open (priv->device, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
-
- if (priv->fd < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
- "Could not open serial device %s: %s", priv->device, strerror (errno));
- return FALSE;
- }
-
- if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
- "Could not open serial device %s: %s", priv->device, strerror (errno));
- close (priv->fd);
- return FALSE;
- }
-
- if (!config_fd (self)) {
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED,
- "Could not open serial device %s: %s", priv->device, strerror (errno));
- close (priv->fd);
- priv->fd = 0;
- return FALSE;
- }
-
- priv->channel = g_io_channel_unix_new (priv->fd);
- priv->watch_id = g_io_add_watch (priv->channel,
- G_IO_IN | G_IO_ERR | G_IO_HUP,
- data_available, self);
-
- return TRUE;
-}
-
-void
-mm_serial_close (MMSerial *self)
-{
- MMSerialPrivate *priv;
-
- g_return_if_fail (MM_IS_SERIAL (self));
-
- priv = MM_SERIAL_GET_PRIVATE (self);
-
- if (priv->fd) {
- g_message ("Closing device '%s'", priv->device);
+ g_return_val_if_fail (g_hash_table_lookup (priv->ports, name) == NULL, FALSE);
- if (priv->channel) {
- g_source_remove (priv->watch_id);
- g_io_channel_shutdown (priv->channel, TRUE, NULL);
- g_io_channel_unref (priv->channel);
- priv->channel = NULL;
- }
-
- ioctl (priv->fd, TCSETA, &priv->old_t);
- close (priv->fd);
- priv->fd = 0;
+ if (ptype == MM_SERIAL_PORT_TYPE_PRIMARY) {
+ g_hash_table_foreach (priv->ports, find_primary, &port);
+ g_return_val_if_fail (port == NULL, FALSE);
}
-}
-
-static void
-internal_queue_command (MMSerial *self,
- const char *command,
- gboolean cached,
- guint32 timeout_seconds,
- MMSerialResponseFn callback,
- gpointer user_data)
-{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- MMQueueData *info;
-
- g_return_if_fail (MM_IS_SERIAL (self));
- g_return_if_fail (command != NULL);
-
- info = g_slice_new0 (MMQueueData);
- info->command = g_strdup (command);
- info->cached = cached;
- info->timeout = timeout_seconds * 1000;
- info->callback = callback;
- info->user_data = user_data;
-
- /* Clear the cached value for this command if not asking for cached value */
- if (!cached)
- mm_serial_set_cached_reply (self, command, NULL);
-
- g_queue_push_tail (priv->queue, info);
-
- if (g_queue_get_length (priv->queue) == 1)
- mm_serial_schedule_queue_process (self);
-}
-
-void
-mm_serial_queue_command (MMSerial *self,
- const char *command,
- guint32 timeout_seconds,
- MMSerialResponseFn callback,
- gpointer user_data)
-{
- internal_queue_command (self, command, FALSE, timeout_seconds, callback, user_data);
-}
-
-void
-mm_serial_queue_command_cached (MMSerial *self,
- const char *command,
- guint32 timeout_seconds,
- MMSerialResponseFn callback,
- gpointer user_data)
-{
- internal_queue_command (self, command, TRUE, timeout_seconds, callback, user_data);
-}
-
-typedef struct {
- MMSerial *serial;
- speed_t current_speed;
- MMSerialFlashFn callback;
- gpointer user_data;
-} FlashInfo;
-
-static speed_t
-get_speed (MMSerial *self)
-{
- struct termios options;
-
- tcgetattr (MM_SERIAL_GET_PRIVATE (self)->fd, &options);
-
- return cfgetospeed (&options);
-}
-
-static void
-set_speed (MMSerial *self, speed_t speed)
-{
- struct termios options;
- int fd;
-
- fd = MM_SERIAL_GET_PRIVATE (self)->fd;
- tcgetattr (fd, &options);
-
- cfsetispeed (&options, speed);
- cfsetospeed (&options, speed);
-
- options.c_cflag |= (CLOCAL | CREAD);
- tcsetattr (fd, TCSANOW, &options);
-}
-
-static void
-flash_done (gpointer data)
-{
- FlashInfo *info = (FlashInfo *) data;
-
- info->callback (info->serial, info->user_data);
-
- g_slice_free (FlashInfo, info);
-}
-static gboolean
-flash_do (gpointer data)
-{
- FlashInfo *info = (FlashInfo *) data;
-
- set_speed (info->serial, info->current_speed);
-
- return FALSE;
-}
-
-guint
-mm_serial_flash (MMSerial *self,
- guint32 flash_time,
- MMSerialFlashFn callback,
- gpointer user_data)
-{
- FlashInfo *info;
- guint id;
-
- g_return_val_if_fail (MM_IS_SERIAL (self), 0);
- g_return_val_if_fail (callback != NULL, 0);
-
- info = g_slice_new0 (FlashInfo);
- info->serial = self;
- info->current_speed = get_speed (self);
- info->callback = callback;
- info->user_data = user_data;
-
- set_speed (self, B0);
-
- id = g_timeout_add_full (G_PRIORITY_DEFAULT,
- flash_time,
- flash_do,
- info,
- flash_done);
+ port = mm_serial_port_new (name);
+ if (!port)
+ return NULL;
- return id;
+ g_object_set_data (G_OBJECT (port), TYPE_TAG, GUINT_TO_POINTER (ptype));
+ g_hash_table_insert (priv->ports, g_strdup (name), port);
+ return port;
}
gboolean
-mm_serial_is_connected (MMSerial *self)
+mm_serial_remove_port (MMSerial *self, MMSerialPort *port)
{
- MMSerialPrivate *priv;
- int mcs = 0;
-
g_return_val_if_fail (MM_IS_SERIAL (self), FALSE);
+ g_return_val_if_fail (port != NULL, FALSE);
- priv = MM_SERIAL_GET_PRIVATE (self);
-
- if (!priv->carrier_detect)
- return FALSE;
-
- if (priv->fd == 0)
- return FALSE;
-
- if (ioctl (priv->fd, TIOCMGET, &mcs) < 0)
- return FALSE;
-
- return mcs & TIOCM_CAR ? TRUE : FALSE;
+ return g_hash_table_remove (MM_SERIAL_GET_PRIVATE (self)->ports, port);
}
/*****************************************************************************/
@@ -868,74 +104,14 @@ mm_serial_init (MMSerial *self)
{
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- priv->reply_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
- priv->baud = 57600;
- priv->bits = 8;
- priv->parity = 'n';
- priv->stopbits = 1;
- priv->send_delay = 1000;
- priv->carrier_detect = TRUE;
-
- priv->queue = g_queue_new ();
- priv->command = g_string_new_len ("AT", SERIAL_BUF_SIZE);
- priv->response = g_string_sized_new (SERIAL_BUF_SIZE);
-}
-
-static GObject*
-constructor (GType type,
- guint n_construct_params,
- GObjectConstructParam *construct_params)
-{
- GObject *object;
- MMSerialPrivate *priv;
-
- object = G_OBJECT_CLASS (mm_serial_parent_class)->constructor (type,
- n_construct_params,
- construct_params);
- if (!object)
- return NULL;
-
- priv = MM_SERIAL_GET_PRIVATE (object);
-
- if (!priv->device) {
- g_warning ("No device provided");
- g_object_unref (object);
- return NULL;
- }
-
- return object;
+ priv->ports = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object);
-
switch (prop_id) {
- case PROP_DEVICE:
- /* Construct only */
- priv->device = g_value_dup_string (value);
- break;
- case PROP_BAUD:
- priv->baud = g_value_get_uint (value);
- break;
- case PROP_BITS:
- priv->bits = g_value_get_uint (value);
- break;
- case PROP_PARITY:
- priv->parity = g_value_get_char (value);
- break;
- case PROP_STOPBITS:
- priv->stopbits = g_value_get_uint (value);
- break;
- case PROP_SEND_DELAY:
- priv->send_delay = g_value_get_uint64 (value);
- break;
- case PROP_CARRIER_DETECT:
- priv->carrier_detect = g_value_get_boolean (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -946,30 +122,7 @@ static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
- MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (object);
-
switch (prop_id) {
- case PROP_DEVICE:
- g_value_set_string (value, priv->device);
- break;
- case PROP_BAUD:
- g_value_set_uint (value, priv->baud);
- break;
- case PROP_BITS:
- g_value_set_uint (value, priv->bits);
- break;
- case PROP_PARITY:
- g_value_set_char (value, priv->parity);
- break;
- case PROP_STOPBITS:
- g_value_set_uint (value, priv->stopbits);
- break;
- case PROP_SEND_DELAY:
- g_value_set_uint64 (value, priv->send_delay);
- break;
- case PROP_CARRIER_DETECT:
- g_value_set_boolean (value, priv->carrier_detect);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -982,28 +135,7 @@ finalize (GObject *object)
MMSerial *self = MM_SERIAL (object);
MMSerialPrivate *priv = MM_SERIAL_GET_PRIVATE (self);
- mm_serial_close (self);
-
- g_hash_table_destroy (priv->reply_cache);
- g_queue_free (priv->queue);
- g_string_free (priv->command, TRUE);
- g_string_free (priv->response, TRUE);
- g_free (priv->device);
-
- while (priv->unsolicited_msg_handlers) {
- MMUnsolicitedMsgHandler *handler = (MMUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;
-
- if (handler->notify)
- handler->notify (handler->user_data);
-
- g_regex_unref (handler->regex);
- g_slice_free (MMUnsolicitedMsgHandler, handler);
- priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
- priv->unsolicited_msg_handlers);
- }
-
- if (priv->response_parser_notify)
- priv->response_parser_notify (priv->response_parser_user_data);
+ g_hash_table_destroy (priv->ports);
G_OBJECT_CLASS (mm_serial_parent_class)->finalize (object);
}
@@ -1016,65 +148,7 @@ mm_serial_class_init (MMSerialClass *klass)
g_type_class_add_private (object_class, sizeof (MMSerialPrivate));
/* Virtual methods */
- object_class->constructor = constructor;
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->finalize = finalize;
-
- /* Properties */
- g_object_class_install_property
- (object_class, PROP_DEVICE,
- g_param_spec_string (MM_SERIAL_DEVICE,
- "Device",
- "Serial device",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property
- (object_class, PROP_BAUD,
- g_param_spec_uint (MM_SERIAL_BAUD,
- "Baud",
- "Baud rate",
- 0, G_MAXUINT, 57600,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_BITS,
- g_param_spec_uint (MM_SERIAL_BITS,
- "Bits",
- "Bits",
- 5, 8, 8,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_PARITY,
- g_param_spec_char (MM_SERIAL_PARITY,
- "Parity",
- "Parity",
- 'E', 'o', 'n',
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_STOPBITS,
- g_param_spec_uint (MM_SERIAL_STOPBITS,
- "Stopbits",
- "Stopbits",
- 1, 2, 1,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_SEND_DELAY,
- g_param_spec_uint64 (MM_SERIAL_SEND_DELAY,
- "SendDelay",
- "Send delay",
- 0, G_MAXUINT64, 0,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
- (object_class, PROP_CARRIER_DETECT,
- g_param_spec_boolean (MM_SERIAL_CARRIER_DETECT,
- "CarrierDetect",
- "Has carrier detect",
- TRUE,
- G_PARAM_READWRITE));
}
diff --git a/src/mm-serial.h b/src/mm-serial.h
index e3479eed..b6c2b5ec 100644
--- a/src/mm-serial.h
+++ b/src/mm-serial.h
@@ -1,4 +1,18 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
#ifndef MM_SERIAL_H
#define MM_SERIAL_H
@@ -7,6 +21,8 @@
#include <glib/gtypes.h>
#include <glib-object.h>
+#include "mm-serial-port.h"
+
#define MM_TYPE_SERIAL (mm_serial_get_type ())
#define MM_SERIAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SERIAL, MMSerial))
#define MM_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SERIAL, MMSerialClass))
@@ -14,33 +30,9 @@
#define MM_IS_SERIAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SERIAL))
#define MM_SERIAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SERIAL, MMSerialClass))
-#define MM_SERIAL_DEVICE "serial-device"
-#define MM_SERIAL_BAUD "baud"
-#define MM_SERIAL_BITS "bits"
-#define MM_SERIAL_PARITY "parity"
-#define MM_SERIAL_STOPBITS "stopbits"
-#define MM_SERIAL_SEND_DELAY "send-delay"
-#define MM_SERIAL_CARRIER_DETECT "carrier-detect"
-
typedef struct _MMSerial MMSerial;
typedef struct _MMSerialClass MMSerialClass;
-typedef gboolean (*MMSerialResponseParserFn) (gpointer user_data,
- GString *response,
- GError **error);
-
-typedef void (*MMSerialUnsolicitedMsgFn) (MMSerial *serial,
- GMatchInfo *match_info,
- gpointer user_data);
-
-typedef void (*MMSerialResponseFn) (MMSerial *serial,
- GString *response,
- GError *error,
- gpointer user_data);
-
-typedef void (*MMSerialFlashFn) (MMSerial *serial,
- gpointer user_data);
-
struct _MMSerial {
GObject parent;
};
@@ -51,39 +43,15 @@ struct _MMSerialClass {
GType mm_serial_get_type (void);
-void mm_serial_add_unsolicited_msg_handler (MMSerial *self,
- GRegex *regex,
- MMSerialUnsolicitedMsgFn callback,
- gpointer user_data,
- GDestroyNotify notify);
+MMSerialPort *mm_serial_get_port (MMSerial *self,
+ const char *name);
-void mm_serial_set_response_parser (MMSerial *self,
- MMSerialResponseParserFn fn,
- gpointer user_data,
- GDestroyNotify notify);
+MMSerialPort *mm_serial_add_port (MMSerial *self,
+ const char *name,
+ MMSerialPortType ptype);
-gboolean mm_serial_open (MMSerial *self,
- GError **error);
-
-void mm_serial_close (MMSerial *self);
-void mm_serial_queue_command (MMSerial *self,
- const char *command,
- guint32 timeout_seconds,
- MMSerialResponseFn callback,
- gpointer user_data);
-
-void mm_serial_queue_command_cached (MMSerial *self,
- const char *command,
- guint32 timeout_seconds,
- MMSerialResponseFn callback,
- gpointer user_data);
-
-guint mm_serial_flash (MMSerial *self,
- guint32 flash_time,
- MMSerialFlashFn callback,
- gpointer user_data);
-
-gboolean mm_serial_is_connected (MMSerial *self);
-const char *mm_serial_get_device (MMSerial *self);
+gboolean mm_serial_remove_port (MMSerial *self,
+ MMSerialPort *port);
#endif /* MM_SERIAL_H */
+