Am Dienstag, 15. Februar 2005 22:10 schrieb Jeroen Vreeken:
> Thanks for the tips Oliver.
> Here is the third attempt.

+/* Incomming data */
+void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)

[..]
+       switch(urb->status) {
+               case -EILSEQ:
+               case -ENODEV:
+               case -ETIMEDOUT:
+               case -ENOENT:
+               case -EPIPE:

ESHUTDOWN

+       if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
+               memcpy(zd->rxdata, data, urb->actual_length);
+               zd->rxlen = urb->actual_length;
+               zd->rxdatas = 1;
+               wake_up(&zd->rxdataq);
+       }

What will happen to the waiting task if the error path is taken
or resubmission fails?

+       if (wait) {
+               wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+               if (!zd->rxlen || zd2host16(*(short*)&zd->rxdata[6]) != rid) {
+                       dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
+               }
+       }

You could avoid some GFP_ATOMIC allocations here checking the flag.

+int zd1201_drvr_start(struct zd1201 *zd)
+{
+       int err, i;
+       short max;
+       unsigned char *buffer;
+       
+       buffer = kmalloc(ZD1201_RXSIZE, GFP_ATOMIC);

This is called from probe() only. Use GFP_KERNEL.

+err_urb:
+       zd->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+       usb_unlink_urb(zd->rx_urb);
+       return err;

You must make sure that the descriptor referenced in the irq handler is not
freed before this operation completes. Best use usb_kill_urb(). You don't need
to be atomic.

+static void zd1201_tx_timeout(struct net_device *dev)
+{
+       struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+       if (!zd)
+               return;
+       dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
+           dev->name);
+       zd->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+       usb_unlink_urb(zd->tx_urb);
+       zd->stats.tx_errors++;
+       /* Restart the timeout to quiet the watchdog: */
+       dev->trans_start = jiffies;
+       usb_free_urb(zd->tx_urb);
+       /* Restart the net queue: */
+       zd->tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (zd->tx_urb)
+               netif_wake_queue(dev);
+}

This is very ingenious, but it has another race. The interrupt handler
will activate the queue at some random time in the future.
May I suggest that you use a work queue and a synchronous usb_kill_urb()
in that?

+       zd->endp_in = 1;
+       zd->endp_out = 1;
+       zd->endp_out2 = 2;
+       zd->rx_urb = usb_alloc_urb(0, GFP_ATOMIC);
+       zd->tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!zd->rx_urb || !zd->tx_urb)
+               goto err_zd;

Use GFP_KERNEL

+void zd1201_disconnect(struct usb_interface *interface)

You should use usb_kill_urb() on all URBs in question.

        Regards
                Oliver


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to