void gobi_usbnet_tx_timeout_3_0_6 (struct net_device *net) { struct usbnet *dev = netdev_priv(net); #ifdef TX_URB_MONITOR int count = 0; int iRet = -1; unsigned char b_usb_if_num = 0; // Get the USB interface iRet = get_usb_interface_from_device (dev, &b_usb_if_num); count = unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); if ((URB_monitor) && (0==iRet)) { while (count) { URB_monitor(false, b_usb_if_num); count--; } } #else // TX_URB_MONITOR unlink_urbs (dev, &dev->txq); tasklet_schedule (&dev->bh); #endif // TX_URB_MONITOR // FIXME: device recovery -- reset? }
/*=========================================================================== METHOD: GobiUSBNetURBCallback (Public Method) DESCRIPTION: Write is complete, cleanup and signal that we're ready for next packet PARAMETERS pURB [ I ] - Pointer to sAutoPM struct RETURN VALUE: None ===========================================================================*/ void GobiUSBNetURBCallback( struct urb * pURB ) { unsigned long activeURBflags; sAutoPM * pAutoPM = (sAutoPM *)pURB->context; if (pAutoPM == NULL) { // Should never happen DBG( "bad context\n" ); return; } if (pURB->status != 0) { // Note that in case of an error, the behaviour is no different DBG( "urb finished with error %d\n", pURB->status ); } // Remove activeURB (memory to be freed later) spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); // EAGAIN used to signify callback is done pAutoPM->mpActiveURB = ERR_PTR( -EAGAIN ); spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); complete( &pAutoPM->mThreadDoWork ); usb_free_urb( pURB ); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif }
/*=========================================================================== METHOD: GobiUSBNetAutoPMThread (Public Method) DESCRIPTION: Handle device Auto PM state asynchronously Handle network packet transmission asynchronously PARAMETERS pData [ I ] - Pointer to sAutoPM struct RETURN VALUE: int - 0 for success Negative errno for error ===========================================================================*/ static int GobiUSBNetAutoPMThread( void * pData ) { unsigned long activeURBflags, URBListFlags; sURBList * pURBListEntry; int status; struct usb_device * pUdev; sAutoPM * pAutoPM = (sAutoPM *)pData; struct urb * pURB; if (pAutoPM == NULL) { DBG( "passed null pointer\n" ); return -EINVAL; } pUdev = interface_to_usbdev( pAutoPM->mpIntf ); DBG( "traffic thread started\n" ); while (pAutoPM->mbExit == false) { // Wait for someone to poke us wait_for_completion_interruptible( &pAutoPM->mThreadDoWork ); // Time to exit? if (pAutoPM->mbExit == true) { // Stop activeURB spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); pURB = pAutoPM->mpActiveURB; spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); if (pURB != NULL) { usb_kill_urb( pURB ); } // Will be freed in callback function #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif // Cleanup URB List spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); pURBListEntry = pAutoPM->mpURBList; while (pURBListEntry != NULL) { pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; atomic_dec( &pAutoPM->mURBListLen ); usb_free_urb( pURBListEntry->mpURB ); kfree( pURBListEntry ); pURBListEntry = pAutoPM->mpURBList; } spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif break; } // Is our URB active? spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); // EAGAIN used to signify callback is done if (IS_ERR( pAutoPM->mpActiveURB ) && PTR_ERR( pAutoPM->mpActiveURB ) == -EAGAIN ) { pAutoPM->mpActiveURB = NULL; // Restore IRQs so task can sleep spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); // URB is done, decrement the Auto PM usage count usb_autopm_put_interface( pAutoPM->mpIntf ); // Lock ActiveURB again spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); } if (pAutoPM->mpActiveURB != NULL) { // There is already a URB active, go back to sleep spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); continue; } // Is there a URB waiting to be submitted? spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); if (pAutoPM->mpURBList == NULL) { // No more URBs to submit, go back to sleep spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); continue; } // Pop an element pURBListEntry = pAutoPM->mpURBList; pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; atomic_dec( &pAutoPM->mURBListLen ); spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); // Set ActiveURB pAutoPM->mpActiveURB = pURBListEntry->mpURB; spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); // Tell autopm core we need device woken up status = usb_autopm_get_interface( pAutoPM->mpIntf ); if (status < 0) { DBG( "unable to autoresume interface: %d\n", status ); // likely caused by device going from autosuspend -> full suspend if (status == -EPERM) { #if (LINUX_VERSION_CODE < KERNEL_VERSION( 2,6,33 )) pUdev->auto_pm = 0; #endif GobiNetSuspend( pAutoPM->mpIntf, PMSG_SUSPEND ); } // Add pURBListEntry back onto pAutoPM->mpURBList spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); pURBListEntry->mpNext = pAutoPM->mpURBList; pAutoPM->mpURBList = pURBListEntry; atomic_inc( &pAutoPM->mURBListLen ); spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); pAutoPM->mpActiveURB = NULL; spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); // Go back to sleep continue; } // Submit URB status = usb_submit_urb( pAutoPM->mpActiveURB, GFP_KERNEL ); if (status < 0) { // Could happen for a number of reasons DBG( "Failed to submit URB: %d. Packet dropped\n", status ); spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); usb_free_urb( pAutoPM->mpActiveURB ); pAutoPM->mpActiveURB = NULL; spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif usb_autopm_put_interface( pAutoPM->mpIntf ); // Loop again complete( &pAutoPM->mThreadDoWork ); } kfree( pURBListEntry ); } DBG( "traffic thread exiting\n" ); pAutoPM->mpThread = NULL; return 0; }
/*=========================================================================== METHOD: GobiUSBNetTXTimeout (Public Method) DESCRIPTION: Timeout declared by the net driver. Stop all transfers PARAMETERS pNet [ I ] - Pointer to net device RETURN VALUE: None ===========================================================================*/ void GobiUSBNetTXTimeout( struct net_device * pNet ) { struct sGobiUSBNet * pGobiDev; sAutoPM * pAutoPM; sURBList * pURBListEntry; unsigned long activeURBflags, URBListFlags; struct usbnet * pDev = netdev_priv( pNet ); struct urb * pURB; if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get usbnet device\n" ); return; } pGobiDev = (sGobiUSBNet *)pDev->data[0]; if (pGobiDev == NULL) { DBG( "failed to get QMIDevice\n" ); return; } pAutoPM = &pGobiDev->mAutoPM; DBG( "\n" ); // Grab a pointer to active URB spin_lock_irqsave( &pAutoPM->mActiveURBLock, activeURBflags ); pURB = pAutoPM->mpActiveURB; spin_unlock_irqrestore( &pAutoPM->mActiveURBLock, activeURBflags ); // Stop active URB if (pURB != NULL) { usb_kill_urb( pURB ); } #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif // Cleanup URB List spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); pURBListEntry = pAutoPM->mpURBList; while (pURBListEntry != NULL) { pAutoPM->mpURBList = pAutoPM->mpURBList->mpNext; atomic_dec( &pAutoPM->mURBListLen ); usb_free_urb( pURBListEntry->mpURB ); kfree( pURBListEntry ); pURBListEntry = pAutoPM->mpURBList; } spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif complete( &pAutoPM->mThreadDoWork ); return; }
/*=========================================================================== METHOD: GobiUSBNetStartXmit (Public Method) DESCRIPTION: Convert sk_buff to usb URB and queue for transmit PARAMETERS pNet [ I ] - Pointer to net device RETURN VALUE: NETDEV_TX_OK on success NETDEV_TX_BUSY on error ===========================================================================*/ int GobiUSBNetStartXmit( struct sk_buff * pSKB, struct net_device * pNet ) { unsigned long URBListFlags; struct sGobiUSBNet * pGobiDev; sAutoPM * pAutoPM; sURBList * pURBListEntry, ** ppURBListEnd; void * pURBData; struct usbnet * pDev = netdev_priv( pNet ); struct driver_info *info = pDev->driver_info; DBG( "\n" ); if (pDev == NULL || pDev->net == NULL) { DBG( "failed to get usbnet device\n" ); return NETDEV_TX_BUSY; } pGobiDev = (sGobiUSBNet *)pDev->data[0]; if (pGobiDev == NULL) { DBG( "failed to get QMIDevice\n" ); return NETDEV_TX_BUSY; } pAutoPM = &pGobiDev->mAutoPM; if( NULL == pSKB ) { DBG( "Buffer is NULL \n" ); return NETDEV_TX_BUSY; } if (GobiTestDownReason( pGobiDev, DRIVER_SUSPENDED ) == true) { // Should not happen DBG( "device is suspended\n" ); dump_stack(); return NETDEV_TX_BUSY; } // Convert the sk_buff into a URB // Check if buffer is full pGobiDev->tx_qlen = atomic_read( &pAutoPM->mURBListLen ); if ( pGobiDev->tx_qlen >= txQueueLength) { DBG( "not scheduling request, buffer is full\n" ); return NETDEV_TX_BUSY; } #if defined(DATA_MODE_RP) || defined(QOS_MODE) if (info->tx_fixup) { pSKB = info->tx_fixup( pDev, pSKB, GFP_ATOMIC); if (pSKB == NULL) { DBG( "unable to tx_fixup skb\n" ); return NETDEV_TX_BUSY; } } #endif // Allocate URBListEntry pURBListEntry = kmalloc( sizeof( sURBList ), GFP_ATOMIC ); if (pURBListEntry == NULL) { DBG( "unable to allocate URBList memory\n" ); if (pSKB) dev_kfree_skb_any ( pSKB ); return NETDEV_TX_BUSY; } pURBListEntry->mpNext = NULL; // Allocate URB pURBListEntry->mpURB = usb_alloc_urb( 0, GFP_ATOMIC ); if (pURBListEntry->mpURB == NULL) { DBG( "unable to allocate URB\n" ); // release all memory allocated by now if (pURBListEntry) kfree( pURBListEntry ); if (pSKB) dev_kfree_skb_any ( pSKB ); return NETDEV_TX_BUSY; } // Allocate URB transfer_buffer pURBData = kmalloc( pSKB->len, GFP_ATOMIC ); if (pURBData == NULL) { DBG( "unable to allocate URB data\n" ); // release all memory allocated by now if (pURBListEntry) { usb_free_urb(pURBListEntry->mpURB); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(false); } #endif kfree( pURBListEntry ); } if (pSKB) dev_kfree_skb_any ( pSKB ); return NETDEV_TX_BUSY; } // Fill with SKB's data memcpy( pURBData, pSKB->data, pSKB->len ); usb_fill_bulk_urb( pURBListEntry->mpURB, pGobiDev->mpNetDev->udev, pGobiDev->mpNetDev->out, pURBData, pSKB->len, GobiUSBNetURBCallback, pAutoPM ); /* Handle the need to send a zero length packet and release the * transfer buffer */ pURBListEntry->mpURB->transfer_flags |= (URB_ZERO_PACKET | URB_FREE_BUFFER); // Aquire lock on URBList spin_lock_irqsave( &pAutoPM->mURBListLock, URBListFlags ); // Add URB to end of list ppURBListEnd = &pAutoPM->mpURBList; while ((*ppURBListEnd) != NULL) { ppURBListEnd = &(*ppURBListEnd)->mpNext; } *ppURBListEnd = pURBListEntry; atomic_inc( &pAutoPM->mURBListLen ); spin_unlock_irqrestore( &pAutoPM->mURBListLock, URBListFlags ); #ifdef TX_URB_MONITOR if (URB_monitor) { URB_monitor(true); } #endif complete( &pAutoPM->mThreadDoWork ); // Start transfer timer pNet->trans_start = jiffies; // Free SKB if (pSKB) dev_kfree_skb_any ( pSKB ); return NETDEV_TX_OK; }
netdev_tx_t gobi_usbnet_start_xmit_3_0_6 (struct sk_buff *skb, struct net_device *net) { struct usbnet *dev = netdev_priv(net); int length; struct urb *urb = NULL; struct skb_data *entry; struct driver_info *info = dev->driver_info; unsigned long flags; int retval; #ifdef TX_URB_MONITOR unsigned char b_usb_if_num = 0; int iRet = -1; #endif //#ifdef TX_URB_MONITOR // some devices want funky USB-level framing, for // win32 driver (usually) and/or hardware quirks if (info->tx_fixup) { skb = info->tx_fixup (dev, skb, GFP_ATOMIC); if (!skb) { if (netif_msg_tx_err(dev)) { netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); goto drop; } else { /* cdc_ncm collected packet; waits for more */ goto not_drop; } } } length = skb->len; if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { netif_dbg(dev, tx_err, dev->net, "no urb\n"); goto drop; } entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; entry->state = tx_start; entry->length = length; usb_fill_bulk_urb (urb, dev->udev, dev->out, skb->data, skb->len, tx_complete, skb); /* don't assume the hardware handles USB_ZERO_PACKET * NOTE: strictly conforming cdc-ether devices should expect * the ZLP here, but ignore the one-byte packet. * NOTE2: CDC NCM specification is different from CDC ECM when * handling ZLP/short packets, so cdc_ncm driver will make short * packet itself if needed. */ if (length % dev->maxpacket == 0) { if (!(info->flags & FLAG_SEND_ZLP)) { if (!(info->flags & FLAG_MULTI_PACKET)) { urb->transfer_buffer_length++; if (skb_tailroom(skb)) { skb->data[skb->len] = 0; __skb_put(skb, 1); } } } else urb->transfer_flags |= URB_ZERO_PACKET; } spin_lock_irqsave(&dev->txq.lock, flags); retval = usb_autopm_get_interface_async(dev->intf); if (retval < 0) { spin_unlock_irqrestore(&dev->txq.lock, flags); goto drop; } #ifdef CONFIG_PM /* if this triggers the device is still a sleep */ if (test_bit(EVENT_DEV_ASLEEP, &dev->flags)) { /* transmission will be done in resume */ usb_anchor_urb(urb, &dev->deferred); /* no use to process more packets */ netif_stop_queue(net); spin_unlock_irqrestore(&dev->txq.lock, flags); netdev_dbg(dev->net, "Delaying transmission for resumption\n"); goto deferred; } #endif #ifdef TX_URB_MONITOR iRet = get_usb_interface(urb, &b_usb_if_num); #endif //#ifdef TX_URB_MONITOR switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) { case -EPIPE: netif_stop_queue (net); usbnet_defer_kevent (dev, EVENT_TX_HALT); usb_autopm_put_interface_async(dev->intf); break; default: usb_autopm_put_interface_async(dev->intf); netif_dbg(dev, tx_err, dev->net, "tx: submit urb err %d\n", retval); break; case 0: net->trans_start = jiffies; __skb_queue_tail (&dev->txq, skb); if (dev->txq.qlen >= TX_QLEN (dev)) netif_stop_queue (net); } #ifdef TX_URB_MONITOR /* * This can be called from here or from inside the * case 0 in the above switch. There will be one less * condition to check */ /* * Call URB_monitor() with true as the URB has been successfully * submitted to the txq. */ if ((URB_monitor) && (0==iRet) && (0==retval)) { URB_monitor(true, b_usb_if_num); } #endif //#ifdef TX_URB_MONITOR spin_unlock_irqrestore (&dev->txq.lock, flags); if (retval) { netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval); drop: dev->net->stats.tx_dropped++; not_drop: if (skb) dev_kfree_skb_any (skb); usb_free_urb (urb); } else netif_dbg(dev, tx_queued, dev->net, "> tx, len %d, type 0x%x\n", length, skb->protocol); #ifdef CONFIG_PM deferred: #endif return NETDEV_TX_OK; }
static void tx_complete (struct urb *urb) { struct sk_buff *skb = (struct sk_buff *) urb->context; struct skb_data *entry = (struct skb_data *) skb->cb; struct usbnet *dev = entry->dev; #ifdef TX_URB_MONITOR unsigned char b_usb_if_num = 0; int iRet = get_usb_interface(urb, &b_usb_if_num); #endif //#ifdef TX_URB_MONITOR if (urb->status == 0) { if (!(dev->driver_info->flags & FLAG_MULTI_PACKET)) dev->net->stats.tx_packets++; dev->net->stats.tx_bytes += entry->length; } else { dev->net->stats.tx_errors++; switch (urb->status) { case -EPIPE: usbnet_defer_kevent (dev, EVENT_TX_HALT); break; /* software-driven interface shutdown */ case -ECONNRESET: // async unlink case -ESHUTDOWN: // hardware gone break; // like rx, tx gets controller i/o faults during khubd delays // and so it uses the same throttling mechanism. case -EPROTO: case -ETIME: case -EILSEQ: if (!timer_pending (&dev->delay)) { mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES); if (netif_msg_link (dev)) #if (LINUX_VERSION_CODE != KERNEL_VERSION( 3,0,6 )) devdbg (dev, "tx throttle %d", urb->status); #else netif_dbg(dev, link, dev->net, "tx throttle %d\n", urb->status); #endif } netif_stop_queue (dev->net); break; default: if (netif_msg_tx_err (dev)) #if (LINUX_VERSION_CODE != KERNEL_VERSION( 3,0,6 )) devdbg (dev, "tx err %d", entry->urb->status); #else netif_dbg(dev, tx_err, dev->net, "tx err %d\n", entry->urb->status); #endif break; } } usb_autopm_put_interface_async(dev->intf); urb->dev = NULL; entry->state = tx_done; defer_bh(dev, skb, &dev->txq); #ifdef TX_URB_MONITOR if ((URB_monitor) && (0==iRet)) { URB_monitor(false, b_usb_if_num); } #endif //#ifdef TX_URB_MONITOR }