NetworkManager r4119 - in trunk: . src



Author: dcbw
Date: Sun Sep 28 22:55:40 2008
New Revision: 4119
URL: http://svn.gnome.org/viewvc/NetworkManager?rev=4119&view=rev

Log:
2008-09-28  Dan Williams  <dcbw redhat com>

	* src/nm-serial-device.c
	  src/nm-serial-device.h
		- (nm_serial_device_close): stop PPP manager here so that PPP gets
			cleaned at the right times when subclasses close the serial port too
		- (nm_serial_device_send_command): use a default send delay; don't
			spin forever on EAGAIN
		- (get_reply_done, get_reply_got_data, nm_serial_device_get_reply):
			remove, no longer used
		- (find_response): return the matched response if any
		- (nm_serial_device_wait_reply_blocking): wait for a reply but block
			while doing so
		- (wait_for_reply_done): pass the matched response to the callback
		- (wait_for_reply_got_data): save the matched response; simplify timeout
			handling
		- (nm_serial_device_wait_for_reply): make 'responses' and 'terminators'
			const since they never get modified
		- (cleanup_device): split out common cleanup stuff to a new function
		- (real_deactivate_quickly, finalize): use cleanup_device()

	* src/nm-gsm-device.c
		- (modem_get_reply): remove, unused
		- (set_apn): give the card a bit more time to respond
		- (manual_registration_again, schedule_manual_registration_again,
		   manual_registration_response, manual_registration): handle manual
			registration timeouts better by retrying registration a few times
			because cards are a bit slow after CFUN=1
		- (automatic_registration_get_network, get_network_response): use
			modem_wait_for_reply() because it interacts better with the serial
			buffer and does more intelligent matching; need to wait for 'OK'
			rather than just matching terminators
		- (schedule_automatic_registration_again,
		   automatic_registration_response, automatic_registration): retry
			registration a few times on timeout or "searching" because cards
			take a bit to find a network after being powered up with CFUN=1
		- (power_up_response, power_up, init_full_done, enter_pin,
		   check_pin_done): power up the card with CFUN=1 before trying to
			register with the network
		- (init_modem_full, init_modem): use more standard 3G init strings

	* src/nm-hso-gsm-device.c
		- (modem_get_reply): remove, unused
		- (hso_ip4_config_response, real_act_stage3_ip_config_start): use
			modem_wait_for_reply() to match actual responses instead of single
			termination characters; it doesn't leave stuff in the serial buffer
			that might confuse later calls
		- (real_deactivate_quickly): use nm_serial_device_wait_reply_blocking()
			to ensure that the call is really disconnected and not leave extra
			stuff in the serial buffer

	* src/nm-cdma-device.c
		- (power_up_response, power_up, init_done): try Sierra-style modem
			power up before attempting to connect



Modified:
   trunk/ChangeLog
   trunk/src/nm-cdma-device.c
   trunk/src/nm-gsm-device.c
   trunk/src/nm-hso-gsm-device.c
   trunk/src/nm-serial-device.c
   trunk/src/nm-serial-device.h

Modified: trunk/src/nm-cdma-device.c
==============================================================================
--- trunk/src/nm-cdma-device.c	(original)
+++ trunk/src/nm-cdma-device.c	Sun Sep 28 22:55:40 2008
@@ -79,6 +79,7 @@
 static void
 dial_done (NMSerialDevice *device,
            int reply_index,
+           const char *reply,
            gpointer user_data)
 {
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_UNKNOWN;
@@ -122,7 +123,7 @@
 	NMSettingCdma *setting;
 	char *command;
 	guint id = 0;
-	char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
+	const char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
 
 	setting = NM_SETTING_CDMA (cdma_device_get_setting (NM_CDMA_DEVICE (device), NM_TYPE_SETTING_CDMA));
 
@@ -138,13 +139,40 @@
 }
 
 static void
+power_up_response (NMSerialDevice *device,
+                   int reply_index,
+                   const char *reply,
+                   gpointer user_data)
+{
+	/* Ignore errors */
+	do_dial (device);
+}
+
+static void
+power_up (NMSerialDevice *device)
+{
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
+	guint id = 0;
+
+	/* Only works on Sierra cards */
+	nm_info ("(%s): powering up...", nm_device_get_iface (NM_DEVICE (device)));		
+	if (nm_serial_device_send_command_string (device, "at!pcstate=1"))
+		id = nm_serial_device_wait_for_reply (device, 10, responses, responses, power_up_response, NULL);
+
+	/* Ignore errors */
+	if (id == 0)
+		do_dial (device);
+}
+
+static void
 init_done (NMSerialDevice *device,
-		 int reply_index,
-		 gpointer user_data)
+           int reply_index,
+           const char *reply,
+           gpointer user_data)
 {
 	switch (reply_index) {
 	case 0:
-		do_dial (device);
+		power_up (device);
 		break;
 	case -1:
 		nm_warning ("Modem initialization timed out");
@@ -165,7 +193,7 @@
 init_modem (NMSerialDevice *device, gpointer user_data)
 {
 	guint id = 0;
-	char *responses[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 
 	if (nm_serial_device_send_command_string (device, "ATZ E0"))
 		id = nm_serial_device_wait_for_reply (device, 10, responses, responses, init_done, NULL);

Modified: trunk/src/nm-gsm-device.c
==============================================================================
--- trunk/src/nm-gsm-device.c	(original)
+++ trunk/src/nm-gsm-device.c	Sun Sep 28 22:55:40 2008
@@ -28,6 +28,8 @@
 	NMGsmSecret need_secret;
 	guint pending_id;
 	guint state_to_disconnected_id;
+
+	guint reg_tries;
 } NMGsmDevicePrivate;
 
 enum {
@@ -46,6 +48,7 @@
 static guint signals[LAST_SIGNAL] = { 0 };
 
 static void enter_pin (NMGsmDevice *device, gboolean retry);
+static void manual_registration (NMGsmDevice *device);
 static void automatic_registration (NMGsmDevice *device);
 
 NMGsmDevice *
@@ -70,12 +73,12 @@
 
 static void
 modem_wait_for_reply (NMGsmDevice *self,
-				  const char *command,
-				  guint timeout,
-				  char **responses,
-				  char **terminators,
-				  NMSerialWaitForReplyFn callback,
-				  gpointer user_data)
+                      const char *command,
+                      guint timeout,
+                      const char **responses,
+                      const char **terminators,
+                      NMSerialWaitForReplyFn callback,
+                      gpointer user_data)
 {
 	NMSerialDevice *serial = NM_SERIAL_DEVICE (self);
 	guint id = 0;
@@ -87,23 +90,6 @@
 		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN);
 }
 
-static void
-modem_get_reply (NMGsmDevice *self,
-			  const char *command,
-			  guint timeout,
-			  const char *terminators,
-			  NMSerialGetReplyFn callback)
-{
-	NMSerialDevice *serial = NM_SERIAL_DEVICE (self);
-	guint id = 0;
-
-	if (nm_serial_device_send_command_string (serial, command))
-		id = nm_serial_device_get_reply (serial, timeout, terminators, callback, NULL);
-
-	if (id == 0)
-		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN);
-}
-
 static NMSetting *
 gsm_device_get_setting (NMGsmDevice *device, GType setting_type)
 {
@@ -124,8 +110,9 @@
 
 static void
 dial_done (NMSerialDevice *device,
-		 int reply_index,
-		 gpointer user_data)
+           int reply_index,
+           const char *reply,
+           gpointer user_data)
 {
 	gboolean success = FALSE;
 	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_UNKNOWN;
@@ -168,7 +155,7 @@
 {
 	NMSettingGsm *setting;
 	char *command;
-	char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
+	const char *responses[] = { "CONNECT", "BUSY", "NO DIAL TONE", "NO CARRIER", NULL };
 
 	setting = NM_SETTING_GSM (gsm_device_get_setting (NM_GSM_DEVICE (device), NM_TYPE_SETTING_GSM));
 
@@ -192,8 +179,9 @@
 
 static void
 set_apn_done (NMSerialDevice *device,
-		    int reply_index,
-		    gpointer user_data)
+              int reply_index,
+              const char *reply,
+              gpointer user_data)
 {
 	switch (reply_index) {
 	case 0:
@@ -211,11 +199,14 @@
 static void
 set_apn (NMGsmDevice *device)
 {
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (device);
 	NMSettingGsm *setting;
 	char *command;
-	char *responses[] = { "OK", "ERROR", NULL };
+	const char *responses[] = { "OK", "ERROR", NULL };
 	guint cid = 1;
 
+	priv->reg_tries = 0;
+
 	setting = NM_SETTING_GSM (gsm_device_get_setting (NM_GSM_DEVICE (device), NM_TYPE_SETTING_GSM));
 	if (!setting->apn) {
 		/* APN not set, nothing to do */
@@ -224,24 +215,54 @@
 	}
 
 	command = g_strdup_printf ("AT+CGDCONT=%d, \"IP\", \"%s\"", cid, setting->apn);
-	modem_wait_for_reply (device, command, 3, responses, responses, set_apn_done, GUINT_TO_POINTER (cid));
+	modem_wait_for_reply (device, command, 7, responses, responses, set_apn_done, GUINT_TO_POINTER (cid));
 	g_free (command);
 }
 
+static gboolean
+manual_registration_again (gpointer data)
+{
+	manual_registration (NM_GSM_DEVICE (data));
+	return FALSE;
+}
+
+static void
+schedule_manual_registration_again (NMGsmDevice *self)
+{
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->pending_id)
+		g_source_remove (priv->pending_id);
+
+	priv->pending_id = g_idle_add (manual_registration_again, self);
+}
+
 static void
-manual_registration_done (NMSerialDevice *device,
-					 int reply_index,
-					 gpointer user_data)
+manual_registration_response (NMSerialDevice *device,
+                              int reply_index,
+                              const char *reply,
+                              gpointer user_data)
 {
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (device);
+
 	switch (reply_index) {
 	case 0:
 		set_apn (NM_GSM_DEVICE (device));
 		break;
 	case -1:
-		nm_warning ("Manual registration timed out");
-		nm_device_state_changed (NM_DEVICE (device),
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT);
+		/* Some cards (ex. Sierra AC860) don't immediately respond to commands
+		 * after they are powered up with CFUN=1, but take a few seconds to come
+		 * back to life.  So try registration a few times.
+		 */
+		if (priv->reg_tries++ < 6) {
+			schedule_manual_registration_again (NM_GSM_DEVICE (device));
+		} else {
+			nm_warning ("Manual registration timed out");
+			priv->reg_tries = 0;
+			nm_device_state_changed (NM_DEVICE (device),
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT);
+		}
 		break;
 	default:
 		nm_warning ("Manual registration failed");
@@ -257,24 +278,29 @@
 {
 	NMSettingGsm *setting;
 	char *command;
-	char *responses[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 
 	setting = NM_SETTING_GSM (gsm_device_get_setting (device, NM_TYPE_SETTING_GSM));
 
 	command = g_strdup_printf ("AT+COPS=1,2,\"%s\"", setting->network_id);
-	modem_wait_for_reply (device, command, 30, responses, responses, manual_registration_done, NULL);
+	modem_wait_for_reply (device, command, 15, responses, responses, manual_registration_response, NULL);
 	g_free (command);
 }
 
 static void
-get_network_done (NMSerialDevice *device,
-			   const char *response,
-			   gpointer user_data)
+get_network_response (NMSerialDevice *device,
+                      int reply_index,
+                      const char *reply,
+                      gpointer user_data)
 {
-	if (response)
-		nm_info ("Associated with network: %s", response);
-	else
+	switch (reply_index) {
+	case 0:
+		nm_info ("Associated with network: %s", reply);
+		break;
+	default:
 		nm_warning ("Couldn't read active network name");
+		break;
+	}
 
 	set_apn (NM_GSM_DEVICE (device));
 }
@@ -282,9 +308,10 @@
 static void
 automatic_registration_get_network (NMGsmDevice *device)
 {
-	const char terminators[] = { '\r', '\n', '\0' };
+	const char *responses[] = { "+COPS: ", NULL };
+	const char *terminators[] = { "OK", "ERROR", "ERR", NULL };
 
-	modem_get_reply (device, "AT+COPS?", 10, terminators, get_network_done);
+	modem_wait_for_reply (device, "AT+COPS?", 10, responses, terminators, get_network_response, NULL);
 }
 
 static gboolean
@@ -295,23 +322,49 @@
 }
 
 static void
+schedule_automatic_registration_again (NMGsmDevice *self)
+{
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->pending_id)
+		g_source_remove (priv->pending_id);
+
+	priv->pending_id = g_idle_add (automatic_registration_again, self);
+}
+
+static void
 automatic_registration_response (NMSerialDevice *device,
-						   int reply_index,
-						   gpointer user_data)
+                                 int reply_index,
+                                 const char *reply,
+                                 gpointer user_data)
 {
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (device);
+
 	switch (reply_index) {
 	case 0:
-		nm_warning ("Automatic registration failed: not registered and not searching.");
-		nm_device_state_changed (NM_DEVICE (device),
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING);
+		/* Try autoregistration a few times here because the card is actually
+		 * responding to the query and thus we aren't waiting as long for
+		 * each CREG request.  Some cards (ex. Option iCON 225) return OK
+		 * immediately from CFUN, but take a bit to start searching for a network.
+		 */
+		if (priv->reg_tries++ < 15) {
+			/* Can happen a few times while the modem is powering up */
+			schedule_automatic_registration_again (NM_GSM_DEVICE (device));
+		} else {
+			priv->reg_tries = 0;
+			nm_warning ("Automatic registration failed: not registered and not searching.");
+			nm_device_state_changed (NM_DEVICE (device),
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING);
+		}
 		break;
 	case 1:
 		nm_info ("Registered on Home network");
 		automatic_registration_get_network (NM_GSM_DEVICE (device));
 		break;
 	case 2:
-		NM_GSM_DEVICE_GET_PRIVATE (device)->pending_id = g_timeout_add (1000, automatic_registration_again, device);
+		nm_info ("Searching for a network...");
+		schedule_automatic_registration_again (NM_GSM_DEVICE (device));
 		break;
 	case 3:
 		nm_warning ("Automatic registration failed: registration denied.");
@@ -324,10 +377,19 @@
 		automatic_registration_get_network (NM_GSM_DEVICE (device));
 		break;
 	case -1:
+		/* Some cards (ex. Sierra AC860) don't immediately respond to commands
+		 * after they are powered up with CFUN=1, but take a few seconds to come
+		 * back to life.  So try registration a few times.
+		 */
 		nm_warning ("Automatic registration timed out");
-		nm_device_state_changed (NM_DEVICE (device),
-		                         NM_DEVICE_STATE_FAILED,
-		                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT);
+		if (priv->reg_tries++ < 6) {
+			schedule_automatic_registration_again (NM_GSM_DEVICE (device));
+		} else {
+			priv->reg_tries = 0;
+			nm_device_state_changed (NM_DEVICE (device),
+			                         NM_DEVICE_STATE_FAILED,
+			                         NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT);
+		}
 		break;
 	default:
 		nm_warning ("Automatic registration failed");
@@ -341,19 +403,21 @@
 static void
 automatic_registration (NMGsmDevice *device)
 {
-	char *responses[] = { "+CREG: 0,0", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", NULL };
-	char *terminators[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "+CREG: 0,0", "+CREG: 0,1", "+CREG: 0,2", "+CREG: 0,3", "+CREG: 0,5", NULL };
+	const char *terminators[] = { "OK", "ERROR", "ERR", NULL };
 
-	modem_wait_for_reply (device, "AT+CREG?", 60, responses, terminators, automatic_registration_response, NULL);
+	modem_wait_for_reply (device, "AT+CREG?", 15, responses, terminators, automatic_registration_response, NULL);
 }
 
 static void
 do_register (NMGsmDevice *device)
 {
+	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (device);
 	NMSettingGsm *setting;
 
 	setting = NM_SETTING_GSM (gsm_device_get_setting (device, NM_TYPE_SETTING_GSM));
 
+	priv->reg_tries = 0;
 	if (setting->network_id)
 		manual_registration (device);
 	else
@@ -361,13 +425,33 @@
 }
 
 static void
+power_up_response (NMSerialDevice *device,
+                   int reply_index,
+                   const char *reply,
+                   gpointer user_data)
+{
+	/* Ignore errors */
+	do_register (NM_GSM_DEVICE (device));
+}
+
+static void
+power_up (NMGsmDevice *device)
+{
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
+
+	nm_info ("(%s): powering up...", nm_device_get_iface (NM_DEVICE (device)));		
+	modem_wait_for_reply (device, "AT+CFUN=1", 10, responses, responses, power_up_response, NULL);
+}
+
+static void
 init_full_done (NMSerialDevice *device,
-			 int reply_index,
-			 gpointer user_data)
+                int reply_index,
+                const char *reply,
+                gpointer user_data)
 {
 	switch (reply_index) {
 	case 0:
-		do_register (NM_GSM_DEVICE (device));
+		power_up (NM_GSM_DEVICE (device));
 		break;
 	case -1:
 		nm_warning ("Modem second stage initialization timed out");
@@ -387,19 +471,20 @@
 static void
 init_modem_full (NMGsmDevice *device)
 {
-	char *responses[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 
 	/* Send E0 too because some devices turn echo back on after CPIN which
 	 * just breaks stuff since echo-ed commands are interpreted as replies.
 	 * rh #456770
 	 */
-	modem_wait_for_reply (device, "ATZ E0", 10, responses, responses, init_full_done, NULL);
+	modem_wait_for_reply (device, "ATZ E0 V1 X4 &C1 +FCLASS=0", 10, responses, responses, init_full_done, NULL);
 }
 
 static void
 enter_pin_done (NMSerialDevice *device,
-			 int reply_index,
-			 gpointer user_data)
+                int reply_index,
+                const char *reply,
+                gpointer user_data)
 {
 	NMSettingGsm *setting;
 
@@ -464,13 +549,13 @@
 		secret_name = NM_SETTING_GSM_PUK;
 		break;
 	default:
-		do_register (device);
+		power_up (device);
 		return;
 	}
 
 	if (secret) {
 		char *command;
-		char *responses[] = { "OK", "ERROR", "ERR", NULL };
+		const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 
 		command = g_strdup_printf ("AT+CPIN=\"%s\"", secret);
 		modem_wait_for_reply (device, command, 3, responses, responses, enter_pin_done, NULL);
@@ -491,12 +576,13 @@
 
 static void
 check_pin_done (NMSerialDevice *device,
-			 int reply_index,
-			 gpointer user_data)
+                int reply_index,
+                const char *reply,
+                gpointer user_data)
 {
 	switch (reply_index) {
 	case 0:
-		do_register (NM_GSM_DEVICE (device));
+		power_up (NM_GSM_DEVICE (device));
 		break;
 	case 1:
 		NM_GSM_DEVICE_GET_PRIVATE (device)->need_secret = NM_GSM_SECRET_PIN;
@@ -524,16 +610,17 @@
 static void
 check_pin (NMGsmDevice *self)
 {
-	char *responses[] = { "READY", "SIM PIN", "SIM PUK", "ERROR", "ERR", NULL };
-	char *terminators[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "READY", "SIM PIN", "SIM PUK", "ERROR", "ERR", NULL };
+	const char *terminators[] = { "OK", "ERROR", "ERR", NULL };
 
 	modem_wait_for_reply (self, "AT+CPIN?", 3, responses, terminators, check_pin_done, NULL);
 }
 
 static void
 init_done (NMSerialDevice *device,
-		 int reply_index,
-		 gpointer user_data)
+           int reply_index,
+           const char *reply,
+           gpointer user_data)
 {
 	switch (reply_index) {
 	case 0:
@@ -557,9 +644,9 @@
 static void
 init_modem (NMSerialDevice *device, gpointer user_data)
 {
-	char *responses[] = { "OK", "ERROR", "ERR", NULL };
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 
-	modem_wait_for_reply (NM_GSM_DEVICE (device), "AT E0", 10, responses, responses, init_done, NULL);
+	modem_wait_for_reply (NM_GSM_DEVICE (device), "ATZ E0 V1 X4 &C1 +FCLASS=0", 10, responses, responses, init_done, NULL);
 }
 
 static NMActStageReturn
@@ -680,12 +767,15 @@
 {
 	NMGsmDevicePrivate *priv = NM_GSM_DEVICE_GET_PRIVATE (device);
 
+	priv->reg_tries = 0;
+
 	if (priv->pending_id) {
 		g_source_remove (priv->pending_id);
 		priv->pending_id = 0;
 	}
 
-	NM_DEVICE_CLASS (nm_gsm_device_parent_class)->deactivate_quickly (device);
+	if (NM_DEVICE_CLASS (nm_gsm_device_parent_class)->deactivate_quickly)
+		NM_DEVICE_CLASS (nm_gsm_device_parent_class)->deactivate_quickly (device);
 }
 
 /*****************************************************************************/
@@ -793,10 +883,7 @@
 		priv->state_to_disconnected_id = 0;
 	}
 
-	/* If transitioning to UNAVAILBLE and we have a carrier, transition to
-	 * DISCONNECTED because the device is ready to use.  Otherwise the carrier-on
-	 * handler will handle the transition to DISCONNECTED when the carrier is detected.
-	 */
+	/* Transition to DISCONNECTED from an idle handler */
 	if (new_state == NM_DEVICE_STATE_UNAVAILABLE)
 		priv->state_to_disconnected_id = g_idle_add (unavailable_to_disconnected, self);
 

Modified: trunk/src/nm-hso-gsm-device.c
==============================================================================
--- trunk/src/nm-hso-gsm-device.c	(original)
+++ trunk/src/nm-hso-gsm-device.c	Sun Sep 28 22:55:40 2008
@@ -64,12 +64,12 @@
 
 static void
 modem_wait_for_reply (NMGsmDevice *self,
-				  const char *command,
-				  guint timeout,
-				  char **responses,
-				  char **terminators,
-				  NMSerialWaitForReplyFn callback,
-				  gpointer user_data)
+                      const char *command,
+                      guint timeout,
+                      const char **responses,
+                      const char **terminators,
+                      NMSerialWaitForReplyFn callback,
+                      gpointer user_data)
 {
 	NMSerialDevice *serial = NM_SERIAL_DEVICE (self);
 	guint id = 0;
@@ -81,23 +81,6 @@
 		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN);
 }
 
-static void
-modem_get_reply (NMGsmDevice *self,
-			  const char *command,
-			  guint timeout,
-			  const char *terminators,
-			  NMSerialGetReplyFn callback)
-{
-	NMSerialDevice *serial = NM_SERIAL_DEVICE (self);
-	guint id = 0;
-
-	if (nm_serial_device_send_command_string (serial, command))
-		id = nm_serial_device_get_reply (serial, timeout, terminators, callback, NULL);
-
-	if (id == 0)
-		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_UNKNOWN);
-}
-
 static NMSetting *
 gsm_device_get_setting (NMGsmDevice *device, GType setting_type)
 {
@@ -119,6 +102,7 @@
 static void
 hso_call_done (NMSerialDevice *device,
                int reply_index,
+               const char *reply,
                gpointer user_data)
 {
 	gboolean success = FALSE;
@@ -141,10 +125,11 @@
 
 static void
 hso_clear_done (NMSerialDevice *device,
-               int reply_index,
-               gpointer user_data)
+                int reply_index,
+                const char *reply,
+                gpointer user_data)
 {
-	char *responses[] = { "_OWANCALL: ", "ERROR", NULL };
+	const char *responses[] = { "_OWANCALL: ", "ERROR", NULL };
 	guint cid = GPOINTER_TO_UINT (user_data);
 	char *command;
 
@@ -157,10 +142,11 @@
 static void
 hso_auth_done (NMSerialDevice *device,
                int reply_index,
+               const char *reply,
                gpointer user_data)
 {
 	gboolean success = FALSE;
-	char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL };
+	const char *responses[] = { "_OWANCALL: ", "ERROR", "NO CARRIER", NULL };
 	guint cid = GPOINTER_TO_UINT (user_data);
 	char *command;
 
@@ -190,7 +176,7 @@
 {
 	NMSettingGsm *s_gsm;
 	NMActRequest *req;
-	char *responses[] = { "OK", "ERROR", NULL };
+	const char *responses[] = { "OK", "ERROR", "ERR", NULL };
 	char *command;
 	guint cid;
 
@@ -269,9 +255,10 @@
 #define OWANDATA_TAG "_OWANDATA: "
 
 static void
-hso_ip4_config_done (NMSerialDevice *device,
-                     const char *response,
-                     gpointer user_data)
+hso_ip4_config_response (NMSerialDevice *device,
+                         int reply_index,
+                         const char *response,
+                         gpointer user_data)
 {
 	NMHsoGsmDevicePrivate *priv = NM_HSO_GSM_DEVICE_GET_PRIVATE (device);
 	NMActRequest *req;
@@ -280,7 +267,9 @@
 	NMSettingIP4Address addr = { 0, 32, 0 };
 	guint32 dns1 = 0, dns2 = 0;
 
-	if (!response || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) {
+	if (   (reply_index < 0)
+	    || !response
+	    || strncmp (response, OWANDATA_TAG, strlen (OWANDATA_TAG))) {
 		nm_device_activate_schedule_stage4_ip_config_timeout (NM_DEVICE (device));
 		return;
 	}
@@ -338,17 +327,18 @@
 static NMActStageReturn
 real_act_stage3_ip_config_start (NMDevice *device, NMDeviceStateReason *reason)
 {
-	const char terminators[] = { '\r', '\n', '\0' };
 	NMActRequest *req;
 	char *command;
 	gint cid;
+	const char *responses[] = { "_OWANDATA: ", NULL };
+	const char *terminators[] = { "OK", "ERROR", "ERR", NULL };
 
 	req = nm_device_get_act_request (device);
 	g_assert (req);
 
 	cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID));
 	command = g_strdup_printf ("AT_OWANDATA=%d", cid);
-	modem_get_reply (NM_GSM_DEVICE (device), command, 5, terminators, hso_ip4_config_done);
+	modem_wait_for_reply (NM_GSM_DEVICE (device), command, 5, responses, terminators, hso_ip4_config_response, NULL);
 	g_free (command);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
@@ -410,18 +400,19 @@
 	if (req) {
 		cid = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (req), GSM_CID));
 		if (cid) {
-			command = g_strdup_printf ("AT_OWANCALL=%d,0,1", cid);
-			nm_serial_device_send_command_string (NM_SERIAL_DEVICE (device), command);
-			g_free (command);
+			const char *responses[] = { "OK", "ERROR", "ERR", NULL };
+			int reply;
 
-			/* FIXME: doesn't seem to take the command otherwise, perhaps since
-			 * the serial port gets closed right away
+			/* Disconnect and disable asynchonous notification to keep serial
+			 * buffer empty after the OK.
 			 */
-			g_usleep (G_USEC_PER_SEC / 3);
+			command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid);
+			nm_serial_device_send_command_string (NM_SERIAL_DEVICE (device), command);
+			reply = nm_serial_device_wait_reply_blocking (NM_SERIAL_DEVICE (device), 5, responses, responses);
+			g_free (command);
 		}
 	}
 
-
 	if (NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate_quickly)
 		NM_DEVICE_CLASS (nm_hso_gsm_device_parent_class)->deactivate_quickly (device);
 }

Modified: trunk/src/nm-serial-device.c
==============================================================================
--- trunk/src/nm-serial-device.c	(original)
+++ trunk/src/nm-serial-device.c	Sun Sep 28 22:55:40 2008
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
 #define _GNU_SOURCE  /* for strcasestr() */
 
@@ -363,7 +363,7 @@
 
 gboolean
 nm_serial_device_open (NMSerialDevice *device,
-				   NMSettingSerial *setting)
+                       NMSettingSerial *setting)
 {
 	NMSerialDevicePrivate *priv;
 	const char *iface;
@@ -414,6 +414,12 @@
 	if (priv->pending_id)
 		g_source_remove (priv->pending_id);
 
+	if (priv->ppp_manager) {
+		nm_ppp_manager_stop (priv->ppp_manager);
+		g_object_unref (priv->ppp_manager);
+		priv->ppp_manager = NULL;
+	}
+
 	if (priv->fd) {
 		nm_debug ("Closing device '%s'", nm_device_get_iface (NM_DEVICE (device)));
 
@@ -433,33 +439,41 @@
 {
 	int fd;
 	NMSettingSerial *setting;
-	int i;
-	ssize_t status;
+	int i, eagain_count = 1000;
+	ssize_t written;
+	guint32 send_delay = G_USEC_PER_SEC / 1000;
 
 	g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE);
 	g_return_val_if_fail (command != NULL, FALSE);
 
 	fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd;
 	setting = NM_SETTING_SERIAL (serial_device_get_setting (device, NM_TYPE_SETTING_SERIAL));
+	if (setting && setting->send_delay)
+		send_delay = setting->send_delay;
 
 	serial_debug ("Sending:", (char *) command->data, command->len);
 
-	for (i = 0; i < command->len; i++) {
-	again:
-		status = write (fd, command->data + i, 1);
-
-		if (status < 0) {
-			if (errno == EAGAIN)
-				goto again;
+	for (i = 0; i < command->len && eagain_count > 0;) {
+		written = write (fd, command->data + i, 1);
 
-			g_warning ("Error in writing (errno %d)", errno);
-			return FALSE;
+		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_warning ("Error in writing (errno %d)", errno);
+				return FALSE;
+			}
+			eagain_count--;
 		}
-
-		if (setting->send_delay)
-			usleep (setting->send_delay);
+		g_usleep (send_delay);
 	}
 
+	if (eagain_count <= 0)
+		serial_debug ("Error: too many retries sending:", (char *) command->data, command->len);
+
 	return TRUE;
 }
 
@@ -482,117 +496,121 @@
 	return ret;
 }
 
-typedef struct {
-	NMSerialDevice *device;
-	char *terminators;
-	GString *result;
-	NMSerialGetReplyFn callback;
-	gpointer user_data;
-} GetReplyInfo;
-
-static void
-get_reply_done (gpointer data)
+static gboolean
+find_terminator (const char *line, const char **terminators)
 {
-	GetReplyInfo *info = (GetReplyInfo *) data;
-
-	nm_serial_device_pending_done (info->device);
+	int i;
 
-	/* Call the callback */
-	info->callback (info->device, info->result->str, info->user_data);
+	for (i = 0; terminators[i]; i++) {
+		if (!strncasecmp (line, terminators[i], strlen (terminators[i])))
+			return TRUE;
+	}
+	return FALSE;
+}
 
-	/* Free info */
-	g_free (info->terminators);
-	g_string_free (info->result, TRUE);
+static const char *
+find_response (const char *line, const char **responses, gint *idx)
+{
+	int i;
 
-	g_slice_free (GetReplyInfo, info);
+	/* Don't look for a result again if we got one previously */
+	for (i = 0; responses[i]; i++) {
+		if (strcasestr (line, responses[i])) {
+			*idx = i;
+			return line;
+		}
+	}
+	return NULL;
 }
 
-static gboolean
-get_reply_got_data (GIOChannel *source,
-				GIOCondition condition,
-				gpointer data)
+#define RESPONSE_LINE_MAX 128
+
+int
+nm_serial_device_wait_reply_blocking (NMSerialDevice *device,
+                                      guint32 timeout_secs,
+                                      const char **needles,
+                                      const char **terminators)
 {
-	GetReplyInfo *info = (GetReplyInfo *) data;
-	gsize bytes_read;
 	char buf[SERIAL_BUF_SIZE + 1];
-	GIOStatus status;
+	int fd, reply_index = -1, bytes_read;
+	GString *result = NULL;
+	time_t end;
+	const char *response = NULL;
 	gboolean done = FALSE;
-	int i;
 
-	if (condition & G_IO_HUP || condition & G_IO_ERR) {
-		g_string_truncate (info->result, 0);
-		return FALSE;
-	}
+	g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), -1);
+	g_return_val_if_fail (timeout_secs <= 60, -1);
+	g_return_val_if_fail (needles != NULL, -1);
+
+	fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd;
+	if (fd < 0)
+		return -1;
 
+	end = time (NULL) + timeout_secs;
+	result = g_string_sized_new (20);
 	do {
-		GError *err = NULL;
+		bytes_read = read (fd, buf, SERIAL_BUF_SIZE);
+		if (bytes_read < 0 && errno != EAGAIN) {
+			nm_warning ("%s: read error: %d (%s)",
+			            nm_device_get_iface (NM_DEVICE (device)),
+			            errno,
+			            strerror (errno));
+			return -1;
+		}
 
-		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)
+			break; /* EOF */
+		else if (bytes_read > 0) {
+			buf[bytes_read] = 0;
+			g_string_append (result, buf);
+
+			serial_debug ("Got:", result->str, result->len);
 		}
 
-		if (bytes_read > 0) {
-			char *p;
+		/* Look for needles and terminators */
+		if ((bytes_read > 0) && result->str) {
+			char *p = result->str;
 
-			serial_debug ("Got:", buf, bytes_read);
+			/* Break the response up into lines and process each one */
+			while ((p < result->str + strlen (result->str)) && !done) {
+				char line[RESPONSE_LINE_MAX] = { '\0', };
+				char *tmp;
+				int i;
+				gboolean got_something = FALSE;
 
-			p = &buf[0];
-			for (i = 0; i < bytes_read && !done; i++, p++) {
-				int j;
-				gboolean is_terminator = FALSE;
-
-				for (j = 0; j < strlen (info->terminators); j++) {
-					if (*p == info->terminators[j]) {
-						is_terminator = TRUE;
-						break;
+				for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) {
+					/* Ignore front CR/LF */
+					if ((*p == '\n') || (*p == '\r')) {
+						if (got_something)
+							break;
+					} else {
+						line[i++] = *p;
+						got_something = TRUE;
 					}
 				}
+				line[i] = '\0';
 
-				if (is_terminator) {
-					/* Ignore terminators in the beginning of the output */
-					if (info->result->len > 0)
-						done = TRUE;
-				} else
-					g_string_append_c (info->result, *p);
+				tmp = g_strstrip (line);
+				if (tmp && strlen (tmp)) {
+					done = find_terminator (tmp, terminators);
+					if (reply_index == -1)
+						response = find_response (tmp, needles, &reply_index);
+				}
 			}
 		}
 
 		/* Limit the size of the buffer */
-		if (info->result->len > SERIAL_BUF_SIZE) {
+		if (result->len > SERIAL_BUF_SIZE) {
 			g_warning ("%s (%s): response buffer filled before repsonse received",
-			           __func__, nm_device_get_iface (NM_DEVICE (info->device)));
-			g_string_truncate (info->result, 0);
-			done = TRUE;
+			           __func__, nm_device_get_iface (NM_DEVICE (device)));
+			break;
 		}
-	} while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
-
-	return !done;
-}
 
-guint
-nm_serial_device_get_reply (NMSerialDevice *device,
-					   guint timeout,
-					   const char *terminators,
-					   NMSerialGetReplyFn callback,
-					   gpointer user_data)
-{
-	GetReplyInfo *info;
+		if (!done)
+			g_usleep (100);
+	} while (!done && (time (NULL) < end));
 
-	g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0);
-	g_return_val_if_fail (terminators != NULL, 0);
-	g_return_val_if_fail (callback != NULL, 0);
-
-	info = g_slice_new0 (GetReplyInfo);
-	info->device = device;
-	info->terminators = g_strdup (terminators);
-	info->result = g_string_new (NULL);
-	info->callback = callback;
-	info->user_data = user_data;
-
-	return nm_serial_device_set_pending (device, timeout, get_reply_got_data, info, get_reply_done);
+	return reply_index;
 }
 
 typedef struct {
@@ -603,8 +621,8 @@
 	NMSerialWaitForReplyFn callback;
 	gpointer user_data;
 	int reply_index;
-	guint timeout;
-	time_t start;
+	char *reply_line;
+	time_t end;
 } WaitForReplyInfo;
 
 static void
@@ -615,47 +633,20 @@
 	nm_serial_device_pending_done (info->device);
 
 	/* Call the callback */
-	info->callback (info->device, info->reply_index, info->user_data);
+	info->callback (info->device, info->reply_index, info->reply_line, info->user_data);
 
 	/* Free info */
 	if (info->result)
 		g_string_free (info->result, TRUE);
 
+	g_free (info->reply_line);
+
 	g_strfreev (info->str_needles);
 	g_strfreev (info->terminators);
 	g_slice_free (WaitForReplyInfo, info);
 }
 
 static gboolean
-find_terminator (const char *line, char **terminators)
-{
-	int i;
-
-	for (i = 0; terminators[i]; i++) {
-		if (!strncasecmp (line, terminators[i], strlen (terminators[i])))
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-find_response (const char *line, char **responses, gint *idx)
-{
-	int i;
-
-	/* Don't look for a result again if we got one previously */
-	for (i = 0; responses[i]; i++) {
-		if (strcasestr (line, responses[i])) {
-			*idx = i;
-			return TRUE;
-		}
-	}
-	return FALSE;
-}
-
-#define RESPONSE_LINE_MAX 128
-
-static gboolean
 wait_for_reply_got_data (GIOChannel *source,
 					GIOCondition condition,
 					gpointer data)
@@ -664,7 +655,6 @@
 	gchar buf[SERIAL_BUF_SIZE + 1];
 	gsize bytes_read;
 	GIOStatus status;
-	gboolean got_response = FALSE;
 	gboolean done = FALSE;
 
 	if (condition & G_IO_HUP || condition & G_IO_ERR)
@@ -692,8 +682,7 @@
 			char *p = info->result->str;
 
 			/* Break the response up into lines and process each one */
-			while (   (p < info->result->str + strlen (info->result->str))
-			       && !(done && got_response)) {
+			while ((p < info->result->str + strlen (info->result->str)) && !done) {
 				char line[RESPONSE_LINE_MAX] = { '\0', };
 				char *tmp;
 				int i;
@@ -713,20 +702,19 @@
 
 				tmp = g_strstrip (line);
 				if (tmp && strlen (tmp)) {
-					done = find_terminator (tmp, info->terminators);
-					if (info->reply_index == -1)
-						got_response = find_response (tmp, info->str_needles, &(info->reply_index));
+					done = find_terminator (tmp, (const char **) info->terminators);
+					if (info->reply_index == -1) {
+						if (find_response (tmp, (const char **) info->str_needles, &(info->reply_index)))
+							info->reply_line = g_strdup (tmp);
+					}
 				}
 			}
-
-			if (done && got_response)
-				break;
 		}
 
 		/* Limit the size of the buffer */
 		if (info->result->len > SERIAL_BUF_SIZE) {
-			g_warning ("%s (%s): response buffer filled before repsonse received",
-			           __func__, nm_device_get_iface (NM_DEVICE (info->device)));
+			nm_warning ("(%s): response buffer filled before repsonse received",
+			            nm_device_get_iface (NM_DEVICE (info->device)));
 			done = TRUE;
 			break;
 		}
@@ -736,10 +724,10 @@
 		 * terminate (terminator not found, whatever) then this should make
 		 * sure that NM doesn't spin the CPU forever.
 		 */
-		if (time (NULL) - info->start > info->timeout + 1) {
+		if (time (NULL) > info->end) {
 			done = TRUE;
 			break;
-		} else
+		} else if (!done)
 			g_usleep (50);
 	} while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
 
@@ -748,11 +736,11 @@
 
 guint
 nm_serial_device_wait_for_reply (NMSerialDevice *device,
-						   guint timeout,
-						   char **responses,
-						   char **terminators,
-						   NMSerialWaitForReplyFn callback,
-						   gpointer user_data)
+                                 guint timeout,
+                                 const char **responses,
+                                 const char **terminators,
+                                 NMSerialWaitForReplyFn callback,
+                                 gpointer user_data)
 {
 	WaitForReplyInfo *info;
 
@@ -762,14 +750,13 @@
 
 	info = g_slice_new0 (WaitForReplyInfo);
 	info->device = device;
-	info->str_needles = g_strdupv (responses);
-	info->terminators = g_strdupv (terminators);
+	info->str_needles = g_strdupv ((char **) responses);
+	info->terminators = g_strdupv ((char **) terminators);
 	info->result = g_string_new (NULL);
 	info->callback = callback;
 	info->user_data = user_data;
 	info->reply_index = -1;
-	info->timeout = timeout;
-	info->start = time (NULL);
+	info->end = time (NULL) + timeout;
 
 	return nm_serial_device_set_pending (device, timeout, wait_for_reply_got_data, info, wait_for_reply_done);
 }
@@ -1074,12 +1061,11 @@
 }
 
 static void
-real_deactivate_quickly (NMDevice *device)
+cleanup_device (NMSerialDevice *device)
 {
-	NMSerialDevice *self = NM_SERIAL_DEVICE (device);
 	NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
 
-	nm_device_set_ip_iface (device, NULL);
+	nm_device_set_ip_iface (NM_DEVICE (device), NULL);
 
 	if (priv->pending_ip4_config) {
 		g_object_unref (priv->pending_ip4_config);
@@ -1087,12 +1073,14 @@
 	}
 
 	priv->in_bytes = priv->out_bytes = 0;
+}
 
-	if (priv->ppp_manager) {
-		g_object_unref (priv->ppp_manager);
-		priv->ppp_manager = NULL;
-	}
+static void
+real_deactivate_quickly (NMDevice *device)
+{
+	NMSerialDevice *self = NM_SERIAL_DEVICE (device);
 
+	cleanup_device (self);
 	nm_serial_device_close (self);
 }
 
@@ -1114,6 +1102,7 @@
 {
 	NMSerialDevice *self = NM_SERIAL_DEVICE (object);
 
+	cleanup_device (self);
 	nm_serial_device_close (self);
 
 	G_OBJECT_CLASS (nm_serial_device_parent_class)->finalize (object);

Modified: trunk/src/nm-serial-device.h
==============================================================================
--- trunk/src/nm-serial-device.h	(original)
+++ trunk/src/nm-serial-device.h	Sun Sep 28 22:55:40 2008
@@ -34,8 +34,9 @@
 								gpointer user_data);
 
 typedef void (*NMSerialWaitForReplyFn) (NMSerialDevice *device,
-								int reply_index,
-								gpointer user_data);
+                                        int reply_index,
+                                        const char *reply,
+                                        gpointer user_data);
 
 typedef void (*NMSerialWaitQuietFn)    (NMSerialDevice *device,
 								gboolean timed_out,
@@ -56,16 +57,15 @@
 gboolean nm_serial_device_send_command_string (NMSerialDevice *device,
 									  const char *str);
 
-guint    nm_serial_device_get_reply           (NMSerialDevice *device,
-									  guint timeout,
-									  const char *terminators,
-									  NMSerialGetReplyFn callback,
-									  gpointer user_data);
+int nm_serial_device_wait_reply_blocking (NMSerialDevice *device,
+                                          guint32 timeout_secs,
+                                          const char **needles,
+                                          const char **terminators);
 
 guint    nm_serial_device_wait_for_reply      (NMSerialDevice *device,
 									  guint timeout,
-									  char **responses,
-									  char **terminators,
+									  const char **responses,
+									  const char **terminators,
 									  NMSerialWaitForReplyFn callback,
 									  gpointer user_data);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]