Patch to nm-modem-probe
- From: Rick Jones <rick activeservice co uk>
- To: Dan Williams <dcbw redhat com>, networkmanager-list gnome org
- Subject: Patch to nm-modem-probe
- Date: Thu, 23 Jul 2009 18:06:31 +0100
Dan
As a result of keeping trying to get these darned ZTE devices to work reliably, I've come up with some tweaks to nm-modem-probe. Patch file is attached, plus text explaining the reasons for the changes.
This makes a big difference between the ZTE being still flaky and solidly reliable. I don't think these changes should have a negative impact on other modems, but there are new command options to switch the behaviour where it might be modem-specific.
I hope this is useful.
--
Cheers
Rick
--- nm-modem-probe-orig.c 2009-07-06 15:36:36.000000000 +0100
+++ nm-modem-probe.c 2009-07-21 21:02:54.000000000 +0100
@@ -313,7 +313,7 @@
return x->tv_sec < y->tv_sec;
}
-static int modem_probe_caps(int fd, glong timeout_ms)
+static int modem_probe_caps(int fd, glong timeout_ms, glong init_sec, const char* initstr)
{
const char *gcap_responses[] = { GCAP_TAG, HUAWEI_EC121_TAG, NULL };
const char *terminators[] = { "OK", "ERROR", "ERR", "+CME ERROR", NULL };
@@ -323,11 +323,24 @@
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;
+ /* wait for specified initial delay */
+ if (timeout_ms > 0) {
+ g_usleep (timeout_ms * 1000);
+ timeout_ms = 0;
+ }
+
+ /* turn off echo and (optionally) send +CPMS to stop ZTE +ZUSIMR messages
+ tries at 1-sec intervals to max of init_sec secs. from program start */
+ do {
+ g_usleep (1000000);
+ if (modem_send_command (fd, initstr)) {
+ const char *null_responses[] = { NULL };
+ modem_wait_reply (fd, 2, null_responses, terminators, &term_idx, &reply);
+ --init_sec;
}
+ else
+ init_sec = 0;
+ } while (init_sec > 0 && term_idx != 0);
/* Standard response timeout case */
timeout_ms += 3000;
@@ -339,7 +352,7 @@
g_get_current_time (&start);
idx = term_idx = 0;
- send_success = modem_send_command (fd, "AT+GCAP\r\n");
+ send_success = modem_send_command (fd, "AT+GCAP\r");
if (send_success)
idx = modem_wait_reply (fd, 2, gcap_responses, terminators, &term_idx, &reply);
else
@@ -391,7 +404,7 @@
reply = NULL;
verbose ("GCAP failed, trying ATI...");
- if (modem_send_command (fd, "ATI\r\n")) {
+ if (modem_send_command (fd, "ATI\r")) {
idx = modem_wait_reply (fd, 3, ati_responses, terminators, &term_idx, &reply);
if (0 == term_idx && 0 == idx) {
verbose ("ATI response: %s", reply);
@@ -410,7 +423,7 @@
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")) {
+ if (modem_send_command (fd, "AT+CGMM\r")) {
idx = modem_wait_reply (fd, 5, cgmm_responses, terminators, &term_idx, &reply);
if (0 == term_idx && 0 == idx) {
verbose ("CGMM response: %s", reply);
@@ -428,7 +441,9 @@
{
printf("Usage: probe-modem [options] <device>\n"
" --export export key/value pairs\n"
- " --delay <ms> delay before probing (1 to 3000 ms inclusive)\n"
+ " --delay <ms> delay before probing (1 to 5000 ms inclusive)\n"
+ " --initwait secs to wait for modem to initialise (optional, default 10)\n"
+ " --nocpms don't include +CPMS? in init string\n"
" --verbose print verbose debugging output\n"
" --quiet suppress logging to stdout (does not affect logfile output)\n"
" --log <file> log all output\n"
@@ -445,6 +460,8 @@
static const struct option options[] = {
{ "export", 0, NULL, 'x' },
{ "delay", required_argument, NULL, 'a' },
+ { "initwait", required_argument, NULL, 'w' },
+ { "nocpms", 0, NULL, 'n' },
{ "verbose", 0, NULL, 'v' },
{ "quiet", 0, NULL, 'q' },
{ "log", required_argument, NULL, 'l' },
@@ -463,8 +480,10 @@
struct termios orig, attrs;
int fd = -1, caps, ret = 0;
guint32 delay_ms = 0;
+ guint32 init_sec = 10;
unsigned int vid = 0, pid = 0, usbif = 0, last_err = 0;
unsigned long int tmp;
+ const char *initstr = "ATE0+CPMS?\r";
while (1) {
int option;
@@ -479,12 +498,23 @@
break;
case 'a':
tmp = strtoul (optarg, NULL, 10);
- if (tmp < 1 || tmp > 3000) {
+ if (tmp < 1 || tmp > 5000) {
fprintf (stderr, "Invalid delay: %s\n", optarg);
return 1;
}
delay_ms = (guint32) tmp;
break;
+ case 'w':
+ tmp = strtoul (optarg, NULL, 10);
+ if (tmp < 1 || tmp > 15) {
+ fprintf (stderr, "Invalid initwait: %s\n", optarg);
+ return 1;
+ }
+ init_sec = (guint32) tmp;
+ break;
+ case 'n':
+ initstr = "ATE0\r";
+ break;
case 'v':
verbose = TRUE;
break;
@@ -526,6 +556,9 @@
}
}
+ /* reduce initialise timeout by initial delay */
+ init_sec -= delay_ms / 1000;
+
if (logpath) {
time_t t = time (NULL);
@@ -590,7 +623,6 @@
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;
@@ -599,8 +631,8 @@
attrs.c_cflag |= (B9600 | CS8 | CREAD | PARENB);
tcsetattr (fd, TCSANOW, &attrs);
- caps = modem_probe_caps (fd, delay_ms);
- tcsetattr (fd, TCSANOW, &orig);
+ caps = modem_probe_caps (fd, delay_ms, init_sec, initstr);
+/* tcsetattr (fd, TCSANOW, &orig); don't restore the terminal attributes */
if (caps < 0) {
g_printerr ("%s: couldn't get modem capabilities\n", device);
Description and explanation of changes to nm-modem-probe, Rick Jones, 23/7/09
The attached patch to nm-modem-probe fixes various problems relating to modem
initialisation and reliable detection of port capabilities. These changes were done in
particular because of difficulties using a ZTE MF627 USB modem. However, most
changes are generic improvements, and the features that may be ZTE-specific are
switchable by command line options.
Problems causing unreliable detection.
The ZTE modem takes around 8 seconds to settle and give reliable responses to AT
commands. This is the time from when udev invokes nm-modem-probe. The original
nm-modem-probe has a --delay option which the source comments indicate is a
configurable delay before probing the modem. It was not actually implemented as
such, but was implemented as an overall timeout. The actual timeout was the
command parameter plus an arbitrary 3 secs, with an initial delay fixed at .5 secs.
The maximum value allowed was also only 3 sec (3000 msec).
Some modems may well be quick enough for these timings, but it's much too short a
time for the ZTE.
The ZTE also outputs continuous unsolicited messages (UMs) of +ZUSIMR:2 at 2 sec.
intervals. UMs cause all sorts of problems with detecting responses to commands,
and can also cause responses to get lost. These UMs can be stopped by giving any
form of +CPMS command - e.g. +CPMS? to query the status of the SMS memories.
The modem's initial state is with echo mode on. This results in unnecessary clutter for
the modem_wait_reply function to deal with, and can have other unpleasant side
effects (see below).
To deal with the issues above, the new version sends an initialisation string to the
modem, and re-tries at 1-sec intervals until an OK response is received, or a maximum
time has expired. The string is "ATE0+CPMS?\r". A command line option --nocpms
changes this to "ATE0\r", for modems that don't handle +CPMS. The default timeout is
10 secs. from program start, and can be set by the command line option --initwait= n.
The --delay option has been re-coded to be a true initial delay, and can be given a
value up to 5000 (5 secs). The actual time that the initialisation waits is thus the
--initwait value minus the --delay value.
By initialising the modem and waiting for it to settle, the responses to the actual
probe commands (AT+GCAP, ATI, etc) are much more reliable and usually produce a
good response first time. The timeout for the probe commands is still hardcoded at 3
secs.
Serial port termio (stty) modes
The original program sets the port modes to the equivalent of "raw" before probing
the modem, and then resets them before exiting. NetworkManager itself does
likewise. Although resetting a port's modes is good general practice, here it leads to
problems, even kernel panics.
The initial modes of the usb-serial port seem to be somewhat random, or if they are
consistent they correspond to the defaults for a terminal. In particular this can
include echo being on. The default state of the modem is also to have echo on. While
idle, a modem can generate UMs (the ZTE being a particularly bad case). While the
serial port is closed this data is buffered somewhere (in the USB sub-system?), and
produces a short input flood when the port is opened. Although NetworkManager
switches off echo as soon as it opens the port, there is a short window where the
buffered data can be echoed back to the modem before this happens. If the modem
is also in echo mode there is the potential for an echo race.
I have definitely experienced occasional kernel panics using standard
NetworkManager, but these do not occur if steps are taken to disable the echo loop.
This is confirmed by other users in the Ubuntu forum.
The simplest solution is simply not to reset the port modes prior to nm-modem-
probe exiting. Given that the default modes are probably to suit a terminal, they will
always be completely inappropriate for a modem anyway, and NetworkManager will
set them itself as well.
Note that initialising the modem also helps address this issue, by turning off modem
echo and stopping the major cause of UMs. However, also disabling echo at the port
completely kills any danger of an echo race.
Udev rules
This modified version of nm-modem-probe works well with the standard
77-nm-probe-modem-capabilities.rules file, when a ZTE modem is used. It has not
been tested with any other modem. I would expect the most likely compatibility
problem would be modems that do not handle +CPMS? well (i.e. don't support SMS).
It is clearly possible to branch the rules to invoke nm-modem-probe with or without
--nocpms depending on the vendor ID, 0x19d2 being ZTE. I haven't attempted to do
this.
Test results
I have posted this version of nm-modem-probe on the Ubuntu forum where some
contributors having been unable to get ZTE modems to work, either reliably or at all.
Those that have tried it have reported that it resolves the problems, especially if used
in conjunction with the recent update to NetworkManager that identifies ZTE ports
correctly (that update is not sufficient in itself).
It's also of course working well on my own machine, an Asus Eee 901 running Ubuntu
9.04.
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]