netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] hso modem signals patch fix
@ 2008-12-08 10:37 Denis Joseph Barrow
       [not found] ` <493CF8F5.30007-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
  2008-12-08 11:34 ` Alan Cox
  0 siblings, 2 replies; 3+ messages in thread
From: Denis Joseph Barrow @ 2008-12-08 10:37 UTC (permalink / raw)
  To: Linux netdev Mailing list, Linux USB kernel mailing list

[-- Attachment #1: Type: text/plain, Size: 13620 bytes --]

Hi,
This patch is a respin of a previously sent patch,
I inherited a bug from previous code on detecting the modem
port which causes a crash on opening control ports doing
because the check
if (port & HSO_PORT_MODEM)
should be
if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
& a urb gets submitted to a non existent endpoint.

Makes TIOCM ioctls for Data Carrier Detect & related functions
work like /drivers/serial/serial-core.c potentially needed 
for pppd & similar user programs.   
Signed-off-by: Denis Joseph Barrow <D.Barow-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
---
Index: linux-2.6.28-rc6.patches/drivers/net/usb/hso.c
===================================================================
--- linux-2.6.28-rc6.patches.orig/drivers/net/usb/hso.c	2008-11-24 17:07:07.000000000 +0100
+++ linux-2.6.28-rc6.patches/drivers/net/usb/hso.c	2008-12-08 11:18:55.000000000 +0100
@@ -39,8 +39,11 @@
  *		port is opened, as this have a huge impact on the network port
  *		throughput.
  *
- * Interface 2:	Standard modem interface - circuit switched interface, should
- *		not be used.
+ * Interface 2:	Standard modem interface - circuit switched interface, this
+ *		can be used to make a standard ppp connection however it
+ *              should not be used in conjunction with the IP network interface
+ *              enabled for USB performance reasons i.e. if using this set
+ *              ideally disable_net=1.
  *
  *****************************************************************************/
 
@@ -63,6 +66,8 @@
 #include <linux/usb/cdc.h>
 #include <net/arp.h>
 #include <asm/byteorder.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
 
 
 #define DRIVER_VERSION			"1.2"
@@ -182,6 +187,41 @@
 	RX_PENDING
 };
 
+#define BM_REQUEST_TYPE (0xa1)
+#define B_NOTIFICATION  (0x20)
+#define W_VALUE         (0x0)
+#define W_INDEX         (0x2)
+#define W_LENGTH        (0x2)
+
+#define B_OVERRUN       (0x1<<6)
+#define B_PARITY        (0x1<<5)
+#define B_FRAMING       (0x1<<4)
+#define B_RING_SIGNAL   (0x1<<3)
+#define B_BREAK         (0x1<<2)
+#define B_TX_CARRIER    (0x1<<1)
+#define B_RX_CARRIER    (0x1<<0)
+
+struct hso_serial_state_notification {
+	u8 bmRequestType;
+	u8 bNotification;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+	u16 UART_state_bitmap;
+} __attribute__((packed));
+
+struct hso_tiocmget {
+	struct mutex mutex;
+	wait_queue_head_t waitq;
+	int    intr_completed;
+	struct usb_endpoint_descriptor *endp;
+	struct urb *urb;
+	struct hso_serial_state_notification serial_state_notification;
+	u16    prev_UART_state_bitmap;
+	struct uart_icount icount;
+};
+
+
 struct hso_serial {
 	struct hso_device *parent;
 	int magic;
@@ -219,6 +259,7 @@
 	spinlock_t serial_lock;
 
 	int (*write_data) (struct hso_serial *serial);
+	struct hso_tiocmget  *tiocmget;
 	/* Hacks required to get flow control
 	 * working on the serial receive buffers
 	 * so as not to drop characters on the floor.
@@ -310,7 +351,7 @@
 static void async_put_intf(struct work_struct *data);
 static int hso_put_activity(struct hso_device *hso_dev);
 static int hso_get_activity(struct hso_device *hso_dev);
-
+static void tiocmget_intr_callback(struct urb *urb);
 /*****************************************************************************/
 /* Helping functions                                                         */
 /*****************************************************************************/
@@ -1460,25 +1501,217 @@
 
 	return chars;
 }
+int tiocmget_submit_urb(struct hso_serial *serial,
+			struct hso_tiocmget  *tiocmget,
+			struct usb_device *usb)
+{
+	int result;
+
+	if (serial->parent->usb_gone)
+		return -ENODEV;
+	usb_fill_int_urb(tiocmget->urb, usb,
+			 usb_rcvintpipe(usb,
+					tiocmget->endp->
+					bEndpointAddress & 0x7F),
+			 &tiocmget->serial_state_notification,
+			 sizeof(struct hso_serial_state_notification),
+			 tiocmget_intr_callback, serial,
+			 tiocmget->endp->bInterval);
+	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
+	if (result) {
+		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
+			 result);
+	}
+	return result;
+
+}
+
+static void tiocmget_intr_callback(struct urb *urb)
+{
+	struct hso_serial *serial = urb->context;
+	struct hso_tiocmget *tiocmget;
+	int status = urb->status;
+	u16 UART_state_bitmap, prev_UART_state_bitmap;
+	struct uart_icount *icount;
+	struct hso_serial_state_notification *serial_state_notification;
+	struct usb_device *usb;
+
+	/* Sanity checks */
+	if (!serial)
+		return;
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+	tiocmget = serial->tiocmget;
+	if (!tiocmget)
+		return;
+	usb = serial->parent->usb;
+	serial_state_notification = &tiocmget->serial_state_notification;
+	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
+	    serial_state_notification->bNotification != B_NOTIFICATION ||
+	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
+	    le16_to_cpu(serial_state_notification->wIndex) != W_INDEX ||
+	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
+		dev_warn(&usb->dev,
+			 "hso received invalid serial state notification\n");
+		DUMP(serial_state_notification,
+		     sizeof(hso_serial_state_notifation))
+	} else {
+
+		UART_state_bitmap = le16_to_cpu(serial_state_notification->
+						UART_state_bitmap);
+		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
+		icount = &tiocmget->icount;
+		spin_lock(&serial->serial_lock);
+		if ((UART_state_bitmap & B_OVERRUN) !=
+		   (prev_UART_state_bitmap & B_OVERRUN))
+			icount->parity++;
+		if ((UART_state_bitmap & B_PARITY) !=
+		   (prev_UART_state_bitmap & B_PARITY))
+			icount->parity++;
+		if ((UART_state_bitmap & B_FRAMING) !=
+		   (prev_UART_state_bitmap & B_FRAMING))
+			icount->frame++;
+		if ((UART_state_bitmap & B_RING_SIGNAL) &&
+		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
+			icount->rng++;
+		if ((UART_state_bitmap & B_BREAK) !=
+		   (prev_UART_state_bitmap & B_BREAK))
+			icount->brk++;
+		if ((UART_state_bitmap & B_TX_CARRIER) !=
+		   (prev_UART_state_bitmap & B_TX_CARRIER))
+			icount->dsr++;
+		if ((UART_state_bitmap & B_RX_CARRIER) !=
+		   (prev_UART_state_bitmap & B_RX_CARRIER))
+			icount->dcd++;
+		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
+		spin_unlock(&serial->serial_lock);
+		tiocmget->intr_completed = 1;
+		wake_up_interruptible(&tiocmget->waitq);
+	}
+	memset(serial_state_notification, 0,
+	       sizeof(struct hso_serial_state_notification));
+	tiocmget_submit_urb(serial,
+			    tiocmget,
+			    serial->parent->usb);
+}
+
+/*
+ * next few functions largely stolen from drivers/serial/serial_core.c
+ */
+/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+static int
+hso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct uart_icount cprev, cnow;
+	struct hso_tiocmget  *tiocmget;
+	int ret;
+
+	tiocmget = serial->tiocmget;
+	if (!tiocmget)
+		return -ENOENT;
+	/*
+	 * note the counters on entry
+	 */
+	spin_lock_irq(&serial->serial_lock);
+	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
+	spin_unlock_irq(&serial->serial_lock);
+	add_wait_queue(&tiocmget->waitq, &wait);
+	for (;;) {
+		spin_lock_irq(&serial->serial_lock);
+		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
+		spin_unlock_irq(&serial->serial_lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
+			ret = 0;
+			break;
+		}
+		schedule();
+		/* see if a signal did it */
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		cprev = cnow;
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tiocmget->waitq, &wait);
+
+	return ret;
+}
+
+/*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ *     RI where only 0->1 is counted.
+ */
+static int hso_get_count(struct hso_serial *serial,
+			  struct serial_icounter_struct __user *icnt)
+{
+	struct serial_icounter_struct icount;
+	struct uart_icount cnow;
+	struct hso_tiocmget  *tiocmget = serial->tiocmget;
+
+	if (!tiocmget)
+		 return -ENOENT;
+	spin_lock_irq(&serial->serial_lock);
+	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
+	spin_unlock_irq(&serial->serial_lock);
+
+	icount.cts         = cnow.cts;
+	icount.dsr         = cnow.dsr;
+	icount.rng         = cnow.rng;
+	icount.dcd         = cnow.dcd;
+	icount.rx          = cnow.rx;
+	icount.tx          = cnow.tx;
+	icount.frame       = cnow.frame;
+	icount.overrun     = cnow.overrun;
+	icount.parity      = cnow.parity;
+	icount.brk         = cnow.brk;
+	icount.buf_overrun = cnow.buf_overrun;
+
+	return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+}
+
 
 static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)
 {
-	unsigned int value;
+	int retval;
 	struct hso_serial *serial = get_serial_by_tty(tty);
-	unsigned long flags;
+	struct hso_tiocmget  *tiocmget;
+	u16 UART_state_bitmap;
 
 	/* sanity check */
 	if (!serial) {
 		D1("no tty structures");
 		return -EINVAL;
 	}
-
-	spin_lock_irqsave(&serial->serial_lock, flags);
-	value = ((serial->rts_state) ? TIOCM_RTS : 0) |
+	spin_lock_irq(&serial->serial_lock);
+	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
 	    ((serial->dtr_state) ? TIOCM_DTR : 0);
-	spin_unlock_irqrestore(&serial->serial_lock, flags);
+	tiocmget = serial->tiocmget;
+	if (tiocmget) {
 
-	return value;
+		UART_state_bitmap = le16_to_cpu(
+			tiocmget->prev_UART_state_bitmap);
+		if (UART_state_bitmap & B_RING_SIGNAL)
+			retval |=  TIOCM_RNG;
+		if (UART_state_bitmap & B_RX_CARRIER)
+			retval |=  TIOCM_CD;
+		if (UART_state_bitmap & B_TX_CARRIER)
+			retval |=  TIOCM_DSR;
+	}
+	spin_unlock_irq(&serial->serial_lock);
+	return retval;
 }
 
 static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
@@ -1520,6 +1753,32 @@
 			       USB_CTRL_SET_TIMEOUT);
 }
 
+static int hso_serial_ioctl(struct tty_struct *tty, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	struct hso_serial *serial =  get_serial_by_tty(tty);
+	void __user *uarg = (void __user *)arg;
+	int ret = 0;
+	D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
+
+	if (!serial)
+		return -ENODEV;
+	switch (cmd) {
+	case TIOCMIWAIT:
+		ret = hso_wait_modem_status(serial, arg);
+		break;
+
+	case TIOCGICOUNT:
+		ret = hso_get_count(serial, uarg);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+
 /* starts a transmit */
 static void hso_kick_transmit(struct hso_serial *serial)
 {
@@ -1982,7 +2241,10 @@
 		serial->shared_int->use_count++;
 		mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
-
+	if (serial->tiocmget)
+		tiocmget_submit_urb(serial,
+				    serial->tiocmget,
+				    serial->parent->usb);
 	return result;
 }
 
@@ -1990,6 +2252,7 @@
 {
 	int i;
 	struct hso_serial *serial = dev2ser(hso_dev);
+	struct hso_tiocmget  *tiocmget;
 
 	if (!serial)
 		return -ENODEV;
@@ -2018,6 +2281,11 @@
 		}
 		mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
+	tiocmget = serial->tiocmget;
+	if (tiocmget) {
+		wake_up_interruptible(&tiocmget->waitq);
+		usb_kill_urb(tiocmget->urb);
+	}
 
 	return 0;
 }
@@ -2368,6 +2636,20 @@
 	return NULL;
 }
 
+static void hso_free_tiomget(struct hso_serial *serial)
+{
+	struct hso_tiocmget *tiocmget = serial->tiocmget;
+	if (tiocmget) {
+		kfree(tiocmget);
+		if (tiocmget->urb) {
+			usb_free_urb(tiocmget->urb);
+			tiocmget->urb = NULL;
+		}
+		serial->tiocmget = NULL;
+
+	}
+}
+
 /* Frees an AT channel ( goes for both mux and non-mux ) */
 static void hso_free_serial_device(struct hso_device *hso_dev)
 {
@@ -2386,6 +2668,7 @@
 		else
 			mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
+	hso_free_tiomget(serial);
 	kfree(serial);
 	hso_free_device(hso_dev);
 }
@@ -2397,6 +2680,7 @@
 	struct hso_device *hso_dev;
 	struct hso_serial *serial;
 	int num_urbs;
+	struct hso_tiocmget *tiocmget;
 
 	hso_dev = hso_create_device(interface, port);
 	if (!hso_dev)
@@ -2408,9 +2692,27 @@
 
 	serial->parent = hso_dev;
 	hso_dev->port_data.dev_serial = serial;
-
-	if (port & HSO_PORT_MODEM)
+	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
 		num_urbs = 2;
+		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
+					   GFP_KERNEL);
+		/* it isn't going to break our heart if serial->tiocmget
+		 *  allocation fails don't bother checking this.
+		 */
+		if (serial->tiocmget) {
+			tiocmget = serial->tiocmget;
+			tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (tiocmget->urb) {
+				mutex_init(&tiocmget->mutex);
+				init_waitqueue_head(&tiocmget->waitq);
+				tiocmget->endp = hso_get_ep(
+					interface,
+					USB_ENDPOINT_XFER_INT,
+					USB_DIR_IN);
+			} else
+				hso_free_tiomget(serial);
+		}
+	}
 	else
 		num_urbs = 1;
 
@@ -2446,6 +2748,7 @@
 exit2:
 	hso_serial_common_free(serial);
 exit:
+	hso_free_tiomget(serial);
 	kfree(serial);
 	hso_free_device(hso_dev);
 	return NULL;
@@ -2958,6 +3261,7 @@
 	.close = hso_serial_close,
 	.write = hso_serial_write,
 	.write_room = hso_serial_write_room,
+	.ioctl = hso_serial_ioctl,
 	.set_termios = hso_serial_set_termios,
 	.chars_in_buffer = hso_serial_chars_in_buffer,
 	.tiocmget = hso_serial_tiocmget,

-- 
best regards,
D.J. Barrow

[-- Attachment #2: hso_modem_signals.patch --]
[-- Type: text/x-diff, Size: 13262 bytes --]

Makes TIOCM ioctls for Data Carrier Detect & related functions
work like /drivers/serial/serial-core.c potentially needed 
for pppd & similar user programs.   
Signed-off-by: Denis Joseph Barrow <D.Barow-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
---
Index: linux-2.6.28-rc6.patches/drivers/net/usb/hso.c
===================================================================
--- linux-2.6.28-rc6.patches.orig/drivers/net/usb/hso.c	2008-11-24 17:07:07.000000000 +0100
+++ linux-2.6.28-rc6.patches/drivers/net/usb/hso.c	2008-12-08 11:18:55.000000000 +0100
@@ -39,8 +39,11 @@
  *		port is opened, as this have a huge impact on the network port
  *		throughput.
  *
- * Interface 2:	Standard modem interface - circuit switched interface, should
- *		not be used.
+ * Interface 2:	Standard modem interface - circuit switched interface, this
+ *		can be used to make a standard ppp connection however it
+ *              should not be used in conjunction with the IP network interface
+ *              enabled for USB performance reasons i.e. if using this set
+ *              ideally disable_net=1.
  *
  *****************************************************************************/
 
@@ -63,6 +66,8 @@
 #include <linux/usb/cdc.h>
 #include <net/arp.h>
 #include <asm/byteorder.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
 
 
 #define DRIVER_VERSION			"1.2"
@@ -182,6 +187,41 @@
 	RX_PENDING
 };
 
+#define BM_REQUEST_TYPE (0xa1)
+#define B_NOTIFICATION  (0x20)
+#define W_VALUE         (0x0)
+#define W_INDEX         (0x2)
+#define W_LENGTH        (0x2)
+
+#define B_OVERRUN       (0x1<<6)
+#define B_PARITY        (0x1<<5)
+#define B_FRAMING       (0x1<<4)
+#define B_RING_SIGNAL   (0x1<<3)
+#define B_BREAK         (0x1<<2)
+#define B_TX_CARRIER    (0x1<<1)
+#define B_RX_CARRIER    (0x1<<0)
+
+struct hso_serial_state_notification {
+	u8 bmRequestType;
+	u8 bNotification;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+	u16 UART_state_bitmap;
+} __attribute__((packed));
+
+struct hso_tiocmget {
+	struct mutex mutex;
+	wait_queue_head_t waitq;
+	int    intr_completed;
+	struct usb_endpoint_descriptor *endp;
+	struct urb *urb;
+	struct hso_serial_state_notification serial_state_notification;
+	u16    prev_UART_state_bitmap;
+	struct uart_icount icount;
+};
+
+
 struct hso_serial {
 	struct hso_device *parent;
 	int magic;
@@ -219,6 +259,7 @@
 	spinlock_t serial_lock;
 
 	int (*write_data) (struct hso_serial *serial);
+	struct hso_tiocmget  *tiocmget;
 	/* Hacks required to get flow control
 	 * working on the serial receive buffers
 	 * so as not to drop characters on the floor.
@@ -310,7 +351,7 @@
 static void async_put_intf(struct work_struct *data);
 static int hso_put_activity(struct hso_device *hso_dev);
 static int hso_get_activity(struct hso_device *hso_dev);
-
+static void tiocmget_intr_callback(struct urb *urb);
 /*****************************************************************************/
 /* Helping functions                                                         */
 /*****************************************************************************/
@@ -1460,25 +1501,217 @@
 
 	return chars;
 }
+int tiocmget_submit_urb(struct hso_serial *serial,
+			struct hso_tiocmget  *tiocmget,
+			struct usb_device *usb)
+{
+	int result;
+
+	if (serial->parent->usb_gone)
+		return -ENODEV;
+	usb_fill_int_urb(tiocmget->urb, usb,
+			 usb_rcvintpipe(usb,
+					tiocmget->endp->
+					bEndpointAddress & 0x7F),
+			 &tiocmget->serial_state_notification,
+			 sizeof(struct hso_serial_state_notification),
+			 tiocmget_intr_callback, serial,
+			 tiocmget->endp->bInterval);
+	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
+	if (result) {
+		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
+			 result);
+	}
+	return result;
+
+}
+
+static void tiocmget_intr_callback(struct urb *urb)
+{
+	struct hso_serial *serial = urb->context;
+	struct hso_tiocmget *tiocmget;
+	int status = urb->status;
+	u16 UART_state_bitmap, prev_UART_state_bitmap;
+	struct uart_icount *icount;
+	struct hso_serial_state_notification *serial_state_notification;
+	struct usb_device *usb;
+
+	/* Sanity checks */
+	if (!serial)
+		return;
+	if (status) {
+		log_usb_status(status, __func__);
+		return;
+	}
+	tiocmget = serial->tiocmget;
+	if (!tiocmget)
+		return;
+	usb = serial->parent->usb;
+	serial_state_notification = &tiocmget->serial_state_notification;
+	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
+	    serial_state_notification->bNotification != B_NOTIFICATION ||
+	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
+	    le16_to_cpu(serial_state_notification->wIndex) != W_INDEX ||
+	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
+		dev_warn(&usb->dev,
+			 "hso received invalid serial state notification\n");
+		DUMP(serial_state_notification,
+		     sizeof(hso_serial_state_notifation))
+	} else {
+
+		UART_state_bitmap = le16_to_cpu(serial_state_notification->
+						UART_state_bitmap);
+		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
+		icount = &tiocmget->icount;
+		spin_lock(&serial->serial_lock);
+		if ((UART_state_bitmap & B_OVERRUN) !=
+		   (prev_UART_state_bitmap & B_OVERRUN))
+			icount->parity++;
+		if ((UART_state_bitmap & B_PARITY) !=
+		   (prev_UART_state_bitmap & B_PARITY))
+			icount->parity++;
+		if ((UART_state_bitmap & B_FRAMING) !=
+		   (prev_UART_state_bitmap & B_FRAMING))
+			icount->frame++;
+		if ((UART_state_bitmap & B_RING_SIGNAL) &&
+		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
+			icount->rng++;
+		if ((UART_state_bitmap & B_BREAK) !=
+		   (prev_UART_state_bitmap & B_BREAK))
+			icount->brk++;
+		if ((UART_state_bitmap & B_TX_CARRIER) !=
+		   (prev_UART_state_bitmap & B_TX_CARRIER))
+			icount->dsr++;
+		if ((UART_state_bitmap & B_RX_CARRIER) !=
+		   (prev_UART_state_bitmap & B_RX_CARRIER))
+			icount->dcd++;
+		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
+		spin_unlock(&serial->serial_lock);
+		tiocmget->intr_completed = 1;
+		wake_up_interruptible(&tiocmget->waitq);
+	}
+	memset(serial_state_notification, 0,
+	       sizeof(struct hso_serial_state_notification));
+	tiocmget_submit_urb(serial,
+			    tiocmget,
+			    serial->parent->usb);
+}
+
+/*
+ * next few functions largely stolen from drivers/serial/serial_core.c
+ */
+/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+static int
+hso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct uart_icount cprev, cnow;
+	struct hso_tiocmget  *tiocmget;
+	int ret;
+
+	tiocmget = serial->tiocmget;
+	if (!tiocmget)
+		return -ENOENT;
+	/*
+	 * note the counters on entry
+	 */
+	spin_lock_irq(&serial->serial_lock);
+	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
+	spin_unlock_irq(&serial->serial_lock);
+	add_wait_queue(&tiocmget->waitq, &wait);
+	for (;;) {
+		spin_lock_irq(&serial->serial_lock);
+		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
+		spin_unlock_irq(&serial->serial_lock);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
+			ret = 0;
+			break;
+		}
+		schedule();
+		/* see if a signal did it */
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		cprev = cnow;
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&tiocmget->waitq, &wait);
+
+	return ret;
+}
+
+/*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ *     RI where only 0->1 is counted.
+ */
+static int hso_get_count(struct hso_serial *serial,
+			  struct serial_icounter_struct __user *icnt)
+{
+	struct serial_icounter_struct icount;
+	struct uart_icount cnow;
+	struct hso_tiocmget  *tiocmget = serial->tiocmget;
+
+	if (!tiocmget)
+		 return -ENOENT;
+	spin_lock_irq(&serial->serial_lock);
+	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
+	spin_unlock_irq(&serial->serial_lock);
+
+	icount.cts         = cnow.cts;
+	icount.dsr         = cnow.dsr;
+	icount.rng         = cnow.rng;
+	icount.dcd         = cnow.dcd;
+	icount.rx          = cnow.rx;
+	icount.tx          = cnow.tx;
+	icount.frame       = cnow.frame;
+	icount.overrun     = cnow.overrun;
+	icount.parity      = cnow.parity;
+	icount.brk         = cnow.brk;
+	icount.buf_overrun = cnow.buf_overrun;
+
+	return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+}
+
 
 static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)
 {
-	unsigned int value;
+	int retval;
 	struct hso_serial *serial = get_serial_by_tty(tty);
-	unsigned long flags;
+	struct hso_tiocmget  *tiocmget;
+	u16 UART_state_bitmap;
 
 	/* sanity check */
 	if (!serial) {
 		D1("no tty structures");
 		return -EINVAL;
 	}
-
-	spin_lock_irqsave(&serial->serial_lock, flags);
-	value = ((serial->rts_state) ? TIOCM_RTS : 0) |
+	spin_lock_irq(&serial->serial_lock);
+	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
 	    ((serial->dtr_state) ? TIOCM_DTR : 0);
-	spin_unlock_irqrestore(&serial->serial_lock, flags);
+	tiocmget = serial->tiocmget;
+	if (tiocmget) {
 
-	return value;
+		UART_state_bitmap = le16_to_cpu(
+			tiocmget->prev_UART_state_bitmap);
+		if (UART_state_bitmap & B_RING_SIGNAL)
+			retval |=  TIOCM_RNG;
+		if (UART_state_bitmap & B_RX_CARRIER)
+			retval |=  TIOCM_CD;
+		if (UART_state_bitmap & B_TX_CARRIER)
+			retval |=  TIOCM_DSR;
+	}
+	spin_unlock_irq(&serial->serial_lock);
+	return retval;
 }
 
 static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
@@ -1520,6 +1753,32 @@
 			       USB_CTRL_SET_TIMEOUT);
 }
 
+static int hso_serial_ioctl(struct tty_struct *tty, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	struct hso_serial *serial =  get_serial_by_tty(tty);
+	void __user *uarg = (void __user *)arg;
+	int ret = 0;
+	D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
+
+	if (!serial)
+		return -ENODEV;
+	switch (cmd) {
+	case TIOCMIWAIT:
+		ret = hso_wait_modem_status(serial, arg);
+		break;
+
+	case TIOCGICOUNT:
+		ret = hso_get_count(serial, uarg);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	return ret;
+}
+
+
 /* starts a transmit */
 static void hso_kick_transmit(struct hso_serial *serial)
 {
@@ -1982,7 +2241,10 @@
 		serial->shared_int->use_count++;
 		mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
-
+	if (serial->tiocmget)
+		tiocmget_submit_urb(serial,
+				    serial->tiocmget,
+				    serial->parent->usb);
 	return result;
 }
 
@@ -1990,6 +2252,7 @@
 {
 	int i;
 	struct hso_serial *serial = dev2ser(hso_dev);
+	struct hso_tiocmget  *tiocmget;
 
 	if (!serial)
 		return -ENODEV;
@@ -2018,6 +2281,11 @@
 		}
 		mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
+	tiocmget = serial->tiocmget;
+	if (tiocmget) {
+		wake_up_interruptible(&tiocmget->waitq);
+		usb_kill_urb(tiocmget->urb);
+	}
 
 	return 0;
 }
@@ -2368,6 +2636,20 @@
 	return NULL;
 }
 
+static void hso_free_tiomget(struct hso_serial *serial)
+{
+	struct hso_tiocmget *tiocmget = serial->tiocmget;
+	if (tiocmget) {
+		kfree(tiocmget);
+		if (tiocmget->urb) {
+			usb_free_urb(tiocmget->urb);
+			tiocmget->urb = NULL;
+		}
+		serial->tiocmget = NULL;
+
+	}
+}
+
 /* Frees an AT channel ( goes for both mux and non-mux ) */
 static void hso_free_serial_device(struct hso_device *hso_dev)
 {
@@ -2386,6 +2668,7 @@
 		else
 			mutex_unlock(&serial->shared_int->shared_int_lock);
 	}
+	hso_free_tiomget(serial);
 	kfree(serial);
 	hso_free_device(hso_dev);
 }
@@ -2397,6 +2680,7 @@
 	struct hso_device *hso_dev;
 	struct hso_serial *serial;
 	int num_urbs;
+	struct hso_tiocmget *tiocmget;
 
 	hso_dev = hso_create_device(interface, port);
 	if (!hso_dev)
@@ -2408,9 +2692,27 @@
 
 	serial->parent = hso_dev;
 	hso_dev->port_data.dev_serial = serial;
-
-	if (port & HSO_PORT_MODEM)
+	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
 		num_urbs = 2;
+		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
+					   GFP_KERNEL);
+		/* it isn't going to break our heart if serial->tiocmget
+		 *  allocation fails don't bother checking this.
+		 */
+		if (serial->tiocmget) {
+			tiocmget = serial->tiocmget;
+			tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
+			if (tiocmget->urb) {
+				mutex_init(&tiocmget->mutex);
+				init_waitqueue_head(&tiocmget->waitq);
+				tiocmget->endp = hso_get_ep(
+					interface,
+					USB_ENDPOINT_XFER_INT,
+					USB_DIR_IN);
+			} else
+				hso_free_tiomget(serial);
+		}
+	}
 	else
 		num_urbs = 1;
 
@@ -2446,6 +2748,7 @@
 exit2:
 	hso_serial_common_free(serial);
 exit:
+	hso_free_tiomget(serial);
 	kfree(serial);
 	hso_free_device(hso_dev);
 	return NULL;
@@ -2958,6 +3261,7 @@
 	.close = hso_serial_close,
 	.write = hso_serial_write,
 	.write_room = hso_serial_write_room,
+	.ioctl = hso_serial_ioctl,
 	.set_termios = hso_serial_set_termios,
 	.chars_in_buffer = hso_serial_chars_in_buffer,
 	.tiocmget = hso_serial_tiocmget,

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/3] hso modem signals patch fix
       [not found] ` <493CF8F5.30007-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
@ 2008-12-08 10:53   ` David Miller
  0 siblings, 0 replies; 3+ messages in thread
From: David Miller @ 2008-12-08 10:53 UTC (permalink / raw)
  To: D.Barow-x9gZzRpC1QbQT0dZR+AlfA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-usb-u79uwXL29TY76Z2rM5mHXA,
	alan-qBU/x9rampVanCEyBjwyrvXRex20P6io

From: Denis Joseph Barrow <D.Barow-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
Date: Mon, 08 Dec 2008 11:37:41 +0100

You probably want to CC: Alan Cox on patches of this nature,
which I've done here.

> This patch is a respin of a previously sent patch,
> I inherited a bug from previous code on detecting the modem
> port which causes a crash on opening control ports doing
> because the check
> if (port & HSO_PORT_MODEM)
> should be
> if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
> & a urb gets submitted to a non existent endpoint.
> 
> Makes TIOCM ioctls for Data Carrier Detect & related functions
> work like /drivers/serial/serial-core.c potentially needed 
> for pppd & similar user programs.   
> Signed-off-by: Denis Joseph Barrow <D.Barow-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
> ---
> Index: linux-2.6.28-rc6.patches/drivers/net/usb/hso.c
> ===================================================================
> --- linux-2.6.28-rc6.patches.orig/drivers/net/usb/hso.c	2008-11-24 17:07:07.000000000 +0100
> +++ linux-2.6.28-rc6.patches/drivers/net/usb/hso.c	2008-12-08 11:18:55.000000000 +0100
> @@ -39,8 +39,11 @@
>   *		port is opened, as this have a huge impact on the network port
>   *		throughput.
>   *
> - * Interface 2:	Standard modem interface - circuit switched interface, should
> - *		not be used.
> + * Interface 2:	Standard modem interface - circuit switched interface, this
> + *		can be used to make a standard ppp connection however it
> + *              should not be used in conjunction with the IP network interface
> + *              enabled for USB performance reasons i.e. if using this set
> + *              ideally disable_net=1.
>   *
>   *****************************************************************************/
>  
> @@ -63,6 +66,8 @@
>  #include <linux/usb/cdc.h>
>  #include <net/arp.h>
>  #include <asm/byteorder.h>
> +#include <linux/serial_core.h>
> +#include <linux/serial.h>
>  
>  
>  #define DRIVER_VERSION			"1.2"
> @@ -182,6 +187,41 @@
>  	RX_PENDING
>  };
>  
> +#define BM_REQUEST_TYPE (0xa1)
> +#define B_NOTIFICATION  (0x20)
> +#define W_VALUE         (0x0)
> +#define W_INDEX         (0x2)
> +#define W_LENGTH        (0x2)
> +
> +#define B_OVERRUN       (0x1<<6)
> +#define B_PARITY        (0x1<<5)
> +#define B_FRAMING       (0x1<<4)
> +#define B_RING_SIGNAL   (0x1<<3)
> +#define B_BREAK         (0x1<<2)
> +#define B_TX_CARRIER    (0x1<<1)
> +#define B_RX_CARRIER    (0x1<<0)
> +
> +struct hso_serial_state_notification {
> +	u8 bmRequestType;
> +	u8 bNotification;
> +	u16 wValue;
> +	u16 wIndex;
> +	u16 wLength;
> +	u16 UART_state_bitmap;
> +} __attribute__((packed));
> +
> +struct hso_tiocmget {
> +	struct mutex mutex;
> +	wait_queue_head_t waitq;
> +	int    intr_completed;
> +	struct usb_endpoint_descriptor *endp;
> +	struct urb *urb;
> +	struct hso_serial_state_notification serial_state_notification;
> +	u16    prev_UART_state_bitmap;
> +	struct uart_icount icount;
> +};
> +
> +
>  struct hso_serial {
>  	struct hso_device *parent;
>  	int magic;
> @@ -219,6 +259,7 @@
>  	spinlock_t serial_lock;
>  
>  	int (*write_data) (struct hso_serial *serial);
> +	struct hso_tiocmget  *tiocmget;
>  	/* Hacks required to get flow control
>  	 * working on the serial receive buffers
>  	 * so as not to drop characters on the floor.
> @@ -310,7 +351,7 @@
>  static void async_put_intf(struct work_struct *data);
>  static int hso_put_activity(struct hso_device *hso_dev);
>  static int hso_get_activity(struct hso_device *hso_dev);
> -
> +static void tiocmget_intr_callback(struct urb *urb);
>  /*****************************************************************************/
>  /* Helping functions                                                         */
>  /*****************************************************************************/
> @@ -1460,25 +1501,217 @@
>  
>  	return chars;
>  }
> +int tiocmget_submit_urb(struct hso_serial *serial,
> +			struct hso_tiocmget  *tiocmget,
> +			struct usb_device *usb)
> +{
> +	int result;
> +
> +	if (serial->parent->usb_gone)
> +		return -ENODEV;
> +	usb_fill_int_urb(tiocmget->urb, usb,
> +			 usb_rcvintpipe(usb,
> +					tiocmget->endp->
> +					bEndpointAddress & 0x7F),
> +			 &tiocmget->serial_state_notification,
> +			 sizeof(struct hso_serial_state_notification),
> +			 tiocmget_intr_callback, serial,
> +			 tiocmget->endp->bInterval);
> +	result = usb_submit_urb(tiocmget->urb, GFP_ATOMIC);
> +	if (result) {
> +		dev_warn(&usb->dev, "%s usb_submit_urb failed %d\n", __func__,
> +			 result);
> +	}
> +	return result;
> +
> +}
> +
> +static void tiocmget_intr_callback(struct urb *urb)
> +{
> +	struct hso_serial *serial = urb->context;
> +	struct hso_tiocmget *tiocmget;
> +	int status = urb->status;
> +	u16 UART_state_bitmap, prev_UART_state_bitmap;
> +	struct uart_icount *icount;
> +	struct hso_serial_state_notification *serial_state_notification;
> +	struct usb_device *usb;
> +
> +	/* Sanity checks */
> +	if (!serial)
> +		return;
> +	if (status) {
> +		log_usb_status(status, __func__);
> +		return;
> +	}
> +	tiocmget = serial->tiocmget;
> +	if (!tiocmget)
> +		return;
> +	usb = serial->parent->usb;
> +	serial_state_notification = &tiocmget->serial_state_notification;
> +	if (serial_state_notification->bmRequestType != BM_REQUEST_TYPE ||
> +	    serial_state_notification->bNotification != B_NOTIFICATION ||
> +	    le16_to_cpu(serial_state_notification->wValue) != W_VALUE ||
> +	    le16_to_cpu(serial_state_notification->wIndex) != W_INDEX ||
> +	    le16_to_cpu(serial_state_notification->wLength) != W_LENGTH) {
> +		dev_warn(&usb->dev,
> +			 "hso received invalid serial state notification\n");
> +		DUMP(serial_state_notification,
> +		     sizeof(hso_serial_state_notifation))
> +	} else {
> +
> +		UART_state_bitmap = le16_to_cpu(serial_state_notification->
> +						UART_state_bitmap);
> +		prev_UART_state_bitmap = tiocmget->prev_UART_state_bitmap;
> +		icount = &tiocmget->icount;
> +		spin_lock(&serial->serial_lock);
> +		if ((UART_state_bitmap & B_OVERRUN) !=
> +		   (prev_UART_state_bitmap & B_OVERRUN))
> +			icount->parity++;
> +		if ((UART_state_bitmap & B_PARITY) !=
> +		   (prev_UART_state_bitmap & B_PARITY))
> +			icount->parity++;
> +		if ((UART_state_bitmap & B_FRAMING) !=
> +		   (prev_UART_state_bitmap & B_FRAMING))
> +			icount->frame++;
> +		if ((UART_state_bitmap & B_RING_SIGNAL) &&
> +		   !(prev_UART_state_bitmap & B_RING_SIGNAL))
> +			icount->rng++;
> +		if ((UART_state_bitmap & B_BREAK) !=
> +		   (prev_UART_state_bitmap & B_BREAK))
> +			icount->brk++;
> +		if ((UART_state_bitmap & B_TX_CARRIER) !=
> +		   (prev_UART_state_bitmap & B_TX_CARRIER))
> +			icount->dsr++;
> +		if ((UART_state_bitmap & B_RX_CARRIER) !=
> +		   (prev_UART_state_bitmap & B_RX_CARRIER))
> +			icount->dcd++;
> +		tiocmget->prev_UART_state_bitmap = UART_state_bitmap;
> +		spin_unlock(&serial->serial_lock);
> +		tiocmget->intr_completed = 1;
> +		wake_up_interruptible(&tiocmget->waitq);
> +	}
> +	memset(serial_state_notification, 0,
> +	       sizeof(struct hso_serial_state_notification));
> +	tiocmget_submit_urb(serial,
> +			    tiocmget,
> +			    serial->parent->usb);
> +}
> +
> +/*
> + * next few functions largely stolen from drivers/serial/serial_core.c
> + */
> +/* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
> + * - mask passed in arg for lines of interest
> + *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
> + * Caller should use TIOCGICOUNT to see which one it was
> + */
> +static int
> +hso_wait_modem_status(struct hso_serial *serial, unsigned long arg)
> +{
> +	DECLARE_WAITQUEUE(wait, current);
> +	struct uart_icount cprev, cnow;
> +	struct hso_tiocmget  *tiocmget;
> +	int ret;
> +
> +	tiocmget = serial->tiocmget;
> +	if (!tiocmget)
> +		return -ENOENT;
> +	/*
> +	 * note the counters on entry
> +	 */
> +	spin_lock_irq(&serial->serial_lock);
> +	memcpy(&cprev, &tiocmget->icount, sizeof(struct uart_icount));
> +	spin_unlock_irq(&serial->serial_lock);
> +	add_wait_queue(&tiocmget->waitq, &wait);
> +	for (;;) {
> +		spin_lock_irq(&serial->serial_lock);
> +		memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
> +		spin_unlock_irq(&serial->serial_lock);
> +		set_current_state(TASK_INTERRUPTIBLE);
> +		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
> +		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
> +		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd))) {
> +			ret = 0;
> +			break;
> +		}
> +		schedule();
> +		/* see if a signal did it */
> +		if (signal_pending(current)) {
> +			ret = -ERESTARTSYS;
> +			break;
> +		}
> +		cprev = cnow;
> +	}
> +	current->state = TASK_RUNNING;
> +	remove_wait_queue(&tiocmget->waitq, &wait);
> +
> +	return ret;
> +}
> +
> +/*
> + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
> + * Return: write counters to the user passed counter struct
> + * NB: both 1->0 and 0->1 transitions are counted except for
> + *     RI where only 0->1 is counted.
> + */
> +static int hso_get_count(struct hso_serial *serial,
> +			  struct serial_icounter_struct __user *icnt)
> +{
> +	struct serial_icounter_struct icount;
> +	struct uart_icount cnow;
> +	struct hso_tiocmget  *tiocmget = serial->tiocmget;
> +
> +	if (!tiocmget)
> +		 return -ENOENT;
> +	spin_lock_irq(&serial->serial_lock);
> +	memcpy(&cnow, &tiocmget->icount, sizeof(struct uart_icount));
> +	spin_unlock_irq(&serial->serial_lock);
> +
> +	icount.cts         = cnow.cts;
> +	icount.dsr         = cnow.dsr;
> +	icount.rng         = cnow.rng;
> +	icount.dcd         = cnow.dcd;
> +	icount.rx          = cnow.rx;
> +	icount.tx          = cnow.tx;
> +	icount.frame       = cnow.frame;
> +	icount.overrun     = cnow.overrun;
> +	icount.parity      = cnow.parity;
> +	icount.brk         = cnow.brk;
> +	icount.buf_overrun = cnow.buf_overrun;
> +
> +	return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
> +}
> +
>  
>  static int hso_serial_tiocmget(struct tty_struct *tty, struct file *file)
>  {
> -	unsigned int value;
> +	int retval;
>  	struct hso_serial *serial = get_serial_by_tty(tty);
> -	unsigned long flags;
> +	struct hso_tiocmget  *tiocmget;
> +	u16 UART_state_bitmap;
>  
>  	/* sanity check */
>  	if (!serial) {
>  		D1("no tty structures");
>  		return -EINVAL;
>  	}
> -
> -	spin_lock_irqsave(&serial->serial_lock, flags);
> -	value = ((serial->rts_state) ? TIOCM_RTS : 0) |
> +	spin_lock_irq(&serial->serial_lock);
> +	retval = ((serial->rts_state) ? TIOCM_RTS : 0) |
>  	    ((serial->dtr_state) ? TIOCM_DTR : 0);
> -	spin_unlock_irqrestore(&serial->serial_lock, flags);
> +	tiocmget = serial->tiocmget;
> +	if (tiocmget) {
>  
> -	return value;
> +		UART_state_bitmap = le16_to_cpu(
> +			tiocmget->prev_UART_state_bitmap);
> +		if (UART_state_bitmap & B_RING_SIGNAL)
> +			retval |=  TIOCM_RNG;
> +		if (UART_state_bitmap & B_RX_CARRIER)
> +			retval |=  TIOCM_CD;
> +		if (UART_state_bitmap & B_TX_CARRIER)
> +			retval |=  TIOCM_DSR;
> +	}
> +	spin_unlock_irq(&serial->serial_lock);
> +	return retval;
>  }
>  
>  static int hso_serial_tiocmset(struct tty_struct *tty, struct file *file,
> @@ -1520,6 +1753,32 @@
>  			       USB_CTRL_SET_TIMEOUT);
>  }
>  
> +static int hso_serial_ioctl(struct tty_struct *tty, struct file *file,
> +			    unsigned int cmd, unsigned long arg)
> +{
> +	struct hso_serial *serial =  get_serial_by_tty(tty);
> +	void __user *uarg = (void __user *)arg;
> +	int ret = 0;
> +	D4("IOCTL cmd: %d, arg: %ld", cmd, arg);
> +
> +	if (!serial)
> +		return -ENODEV;
> +	switch (cmd) {
> +	case TIOCMIWAIT:
> +		ret = hso_wait_modem_status(serial, arg);
> +		break;
> +
> +	case TIOCGICOUNT:
> +		ret = hso_get_count(serial, uarg);
> +		break;
> +	default:
> +		ret = -ENOIOCTLCMD;
> +		break;
> +	}
> +	return ret;
> +}
> +
> +
>  /* starts a transmit */
>  static void hso_kick_transmit(struct hso_serial *serial)
>  {
> @@ -1982,7 +2241,10 @@
>  		serial->shared_int->use_count++;
>  		mutex_unlock(&serial->shared_int->shared_int_lock);
>  	}
> -
> +	if (serial->tiocmget)
> +		tiocmget_submit_urb(serial,
> +				    serial->tiocmget,
> +				    serial->parent->usb);
>  	return result;
>  }
>  
> @@ -1990,6 +2252,7 @@
>  {
>  	int i;
>  	struct hso_serial *serial = dev2ser(hso_dev);
> +	struct hso_tiocmget  *tiocmget;
>  
>  	if (!serial)
>  		return -ENODEV;
> @@ -2018,6 +2281,11 @@
>  		}
>  		mutex_unlock(&serial->shared_int->shared_int_lock);
>  	}
> +	tiocmget = serial->tiocmget;
> +	if (tiocmget) {
> +		wake_up_interruptible(&tiocmget->waitq);
> +		usb_kill_urb(tiocmget->urb);
> +	}
>  
>  	return 0;
>  }
> @@ -2368,6 +2636,20 @@
>  	return NULL;
>  }
>  
> +static void hso_free_tiomget(struct hso_serial *serial)
> +{
> +	struct hso_tiocmget *tiocmget = serial->tiocmget;
> +	if (tiocmget) {
> +		kfree(tiocmget);
> +		if (tiocmget->urb) {
> +			usb_free_urb(tiocmget->urb);
> +			tiocmget->urb = NULL;
> +		}
> +		serial->tiocmget = NULL;
> +
> +	}
> +}
> +
>  /* Frees an AT channel ( goes for both mux and non-mux ) */
>  static void hso_free_serial_device(struct hso_device *hso_dev)
>  {
> @@ -2386,6 +2668,7 @@
>  		else
>  			mutex_unlock(&serial->shared_int->shared_int_lock);
>  	}
> +	hso_free_tiomget(serial);
>  	kfree(serial);
>  	hso_free_device(hso_dev);
>  }
> @@ -2397,6 +2680,7 @@
>  	struct hso_device *hso_dev;
>  	struct hso_serial *serial;
>  	int num_urbs;
> +	struct hso_tiocmget *tiocmget;
>  
>  	hso_dev = hso_create_device(interface, port);
>  	if (!hso_dev)
> @@ -2408,9 +2692,27 @@
>  
>  	serial->parent = hso_dev;
>  	hso_dev->port_data.dev_serial = serial;
> -
> -	if (port & HSO_PORT_MODEM)
> +	if ((port & HSO_PORT_MASK) == HSO_PORT_MODEM) {
>  		num_urbs = 2;
> +		serial->tiocmget = kzalloc(sizeof(struct hso_tiocmget),
> +					   GFP_KERNEL);
> +		/* it isn't going to break our heart if serial->tiocmget
> +		 *  allocation fails don't bother checking this.
> +		 */
> +		if (serial->tiocmget) {
> +			tiocmget = serial->tiocmget;
> +			tiocmget->urb = usb_alloc_urb(0, GFP_KERNEL);
> +			if (tiocmget->urb) {
> +				mutex_init(&tiocmget->mutex);
> +				init_waitqueue_head(&tiocmget->waitq);
> +				tiocmget->endp = hso_get_ep(
> +					interface,
> +					USB_ENDPOINT_XFER_INT,
> +					USB_DIR_IN);
> +			} else
> +				hso_free_tiomget(serial);
> +		}
> +	}
>  	else
>  		num_urbs = 1;
>  
> @@ -2446,6 +2748,7 @@
>  exit2:
>  	hso_serial_common_free(serial);
>  exit:
> +	hso_free_tiomget(serial);
>  	kfree(serial);
>  	hso_free_device(hso_dev);
>  	return NULL;
> @@ -2958,6 +3261,7 @@
>  	.close = hso_serial_close,
>  	.write = hso_serial_write,
>  	.write_room = hso_serial_write_room,
> +	.ioctl = hso_serial_ioctl,
>  	.set_termios = hso_serial_set_termios,
>  	.chars_in_buffer = hso_serial_chars_in_buffer,
>  	.tiocmget = hso_serial_tiocmget,
> 
> -- 
> best regards,
> D.J. Barrow
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/3] hso modem signals patch fix
  2008-12-08 10:37 [PATCH 1/3] hso modem signals patch fix Denis Joseph Barrow
       [not found] ` <493CF8F5.30007-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
@ 2008-12-08 11:34 ` Alan Cox
  1 sibling, 0 replies; 3+ messages in thread
From: Alan Cox @ 2008-12-08 11:34 UTC (permalink / raw)
  To: Denis Joseph Barrow
  Cc: Linux netdev Mailing list, Linux USB kernel mailing list

> Makes TIOCM ioctls for Data Carrier Detect & related functions
> work like /drivers/serial/serial-core.c potentially needed 
> for pppd & similar user programs.   
> Signed-off-by: Denis Joseph Barrow <D.Barow@option.com>

Doesn't apply versus the current ttydev tree stack which has other hso
related patches in it including a previous modem patch you sent. Please
send updates versus the current ttydev tree and your previous patch.

http://zeniv.linux.org.uk/~alan has the ttydev patch stack. If the most
sensible thing to do is to drop your old patch and swap in a version of
this one then that is fine but let me know what should happen to it.

Also please send/cc tty related patches to me. I appreciate this is also
a network driver but the tty side stuff really does need to go via ttydev
as there are a lot of tty changes currently going on.

NAK this therefore

Alan

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-12-08 11:34 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-12-08 10:37 [PATCH 1/3] hso modem signals patch fix Denis Joseph Barrow
     [not found] ` <493CF8F5.30007-x9gZzRpC1QbQT0dZR+AlfA@public.gmane.org>
2008-12-08 10:53   ` David Miller
2008-12-08 11:34 ` Alan Cox

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).