浏览代码

USB: g_printer bugfixes

G_PRINTER: Bug fix for blocking reads and a fix for a memory leak.

This fixes bugs in blocking IO calls. When the poll() entry point
is called receive transfers will be setup if they have not already
been. Another bug fix is that the poll() entry point now checks the
current receive buffer for data when reporting if any data had been
received. A memory leak was fixed that could have occurred when a
USB reset happened.

Signed-off-by: Craig W. Nadler <craig@nadler.us>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Craig W. Nadler 17 年之前
父节点
当前提交
cc901bbb2e
共有 1 个文件被更改,包括 54 次插入32 次删除
  1. 54 32
      drivers/usb/gadget/printer.c

+ 54 - 32
drivers/usb/gadget/printer.c

@@ -390,9 +390,12 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
 
 
 	/* normal completion */
 	/* normal completion */
 	case 0:
 	case 0:
-		list_add_tail(&req->list, &dev->rx_buffers);
-		wake_up_interruptible(&dev->rx_wait);
-		DBG(dev, "G_Printer : rx length %d\n", req->actual);
+		if (req->actual > 0) {
+			list_add_tail(&req->list, &dev->rx_buffers);
+			DBG(dev, "G_Printer : rx length %d\n", req->actual);
+		} else {
+			list_add(&req->list, &dev->rx_reqs);
+		}
 		break;
 		break;
 
 
 	/* software-driven interface shutdown */
 	/* software-driven interface shutdown */
@@ -417,6 +420,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
 		list_add(&req->list, &dev->rx_reqs);
 		list_add(&req->list, &dev->rx_reqs);
 		break;
 		break;
 	}
 	}
+
+	wake_up_interruptible(&dev->rx_wait);
 	spin_unlock_irqrestore(&dev->lock, flags);
 	spin_unlock_irqrestore(&dev->lock, flags);
 }
 }
 
 
@@ -494,6 +499,39 @@ printer_close(struct inode *inode, struct file *fd)
 	return 0;
 	return 0;
 }
 }
 
 
+/* This function must be called with interrupts turned off. */
+static void
+setup_rx_reqs(struct printer_dev *dev)
+{
+	struct usb_request              *req;
+
+	while (likely(!list_empty(&dev->rx_reqs))) {
+		int error;
+
+		req = container_of(dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del_init(&req->list);
+
+		/* The USB Host sends us whatever amount of data it wants to
+		 * so we always set the length field to the full USB_BUFSIZE.
+		 * If the amount of data is more than the read() caller asked
+		 * for it will be stored in the request buffer until it is
+		 * asked for by read().
+		 */
+		req->length = USB_BUFSIZE;
+		req->complete = rx_complete;
+
+		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+		if (error) {
+			DBG(dev, "rx submit --> %d\n", error);
+			list_add(&req->list, &dev->rx_reqs);
+			break;
+		} else {
+			list_add(&req->list, &dev->rx_reqs_active);
+		}
+	}
+}
+
 static ssize_t
 static ssize_t
 printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 {
 {
@@ -522,31 +560,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 	 */
 	 */
 	dev->reset_printer = 0;
 	dev->reset_printer = 0;
 
 
-	while (likely(!list_empty(&dev->rx_reqs))) {
-		int error;
-
-		req = container_of(dev->rx_reqs.next,
-				struct usb_request, list);
-		list_del_init(&req->list);
-
-		/* The USB Host sends us whatever amount of data it wants to
-		 * so we always set the length field to the full USB_BUFSIZE.
-		 * If the amount of data is more than the read() caller asked
-		 * for it will be stored in the request buffer until it is
-		 * asked for by read().
-		 */
-		req->length = USB_BUFSIZE;
-		req->complete = rx_complete;
-
-		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
-		if (error) {
-			DBG(dev, "rx submit --> %d\n", error);
-			list_add(&req->list, &dev->rx_reqs);
-			break;
-		} else {
-			list_add(&req->list, &dev->rx_reqs_active);
-		}
-	}
+	setup_rx_reqs(dev);
 
 
 	bytes_copied = 0;
 	bytes_copied = 0;
 	current_rx_req = dev->current_rx_req;
 	current_rx_req = dev->current_rx_req;
@@ -615,9 +629,9 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
 
 
 		spin_lock_irqsave(&dev->lock, flags);
 		spin_lock_irqsave(&dev->lock, flags);
 
 
-		/* We've disconnected or reset free the req and buffer */
+		/* We've disconnected or reset so return. */
 		if (dev->reset_printer) {
 		if (dev->reset_printer) {
-			printer_req_free(dev->out_ep, current_rx_req);
+			list_add(&current_rx_req->list, &dev->rx_reqs);
 			spin_unlock_irqrestore(&dev->lock, flags);
 			spin_unlock_irqrestore(&dev->lock, flags);
 			spin_unlock(&dev->lock_printer_io);
 			spin_unlock(&dev->lock_printer_io);
 			return -EAGAIN;
 			return -EAGAIN;
@@ -735,7 +749,7 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 
 
 		/* We've disconnected or reset so free the req and buffer */
 		/* We've disconnected or reset so free the req and buffer */
 		if (dev->reset_printer) {
 		if (dev->reset_printer) {
-			printer_req_free(dev->in_ep, req);
+			list_add(&req->list, &dev->tx_reqs);
 			spin_unlock_irqrestore(&dev->lock, flags);
 			spin_unlock_irqrestore(&dev->lock, flags);
 			spin_unlock(&dev->lock_printer_io);
 			spin_unlock(&dev->lock_printer_io);
 			return -EAGAIN;
 			return -EAGAIN;
@@ -791,6 +805,12 @@ printer_poll(struct file *fd, poll_table *wait)
 	unsigned long		flags;
 	unsigned long		flags;
 	int			status = 0;
 	int			status = 0;
 
 
+	spin_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+	setup_rx_reqs(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	spin_unlock(&dev->lock_printer_io);
+
 	poll_wait(fd, &dev->rx_wait, wait);
 	poll_wait(fd, &dev->rx_wait, wait);
 	poll_wait(fd, &dev->tx_wait, wait);
 	poll_wait(fd, &dev->tx_wait, wait);
 
 
@@ -798,7 +818,8 @@ printer_poll(struct file *fd, poll_table *wait)
 	if (likely(!list_empty(&dev->tx_reqs)))
 	if (likely(!list_empty(&dev->tx_reqs)))
 		status |= POLLOUT | POLLWRNORM;
 		status |= POLLOUT | POLLWRNORM;
 
 
-	if (likely(!list_empty(&dev->rx_buffers)))
+	if (likely(dev->current_rx_bytes) ||
+			likely(!list_empty(&dev->rx_buffers)))
 		status |= POLLIN | POLLRDNORM;
 		status |= POLLIN | POLLRDNORM;
 
 
 	spin_unlock_irqrestore(&dev->lock, flags);
 	spin_unlock_irqrestore(&dev->lock, flags);
@@ -1084,6 +1105,7 @@ static void printer_soft_reset(struct printer_dev *dev)
 	if (usb_ep_enable(dev->out_ep, dev->out))
 	if (usb_ep_enable(dev->out_ep, dev->out))
 		DBG(dev, "Failed to enable USB out_ep\n");
 		DBG(dev, "Failed to enable USB out_ep\n");
 
 
+	wake_up_interruptible(&dev->rx_wait);
 	wake_up_interruptible(&dev->tx_wait);
 	wake_up_interruptible(&dev->tx_wait);
 	wake_up_interruptible(&dev->tx_flush_wait);
 	wake_up_interruptible(&dev->tx_flush_wait);
 }
 }