/* * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * File: usbpipe.c * * Purpose: Handle USB control endpoint * * Author: Warren Hsu * * Date: Mar. 29, 2005 * * Functions: * CONTROLnsRequestOut - Write variable length bytes to MEM/BB/MAC/EEPROM * CONTROLnsRequestIn - Read variable length bytes from MEM/BB/MAC/EEPROM * ControlvWriteByte - Write one byte to MEM/BB/MAC/EEPROM * ControlvReadByte - Read one byte from MEM/BB/MAC/EEPROM * ControlvMaskByte - Read one byte from MEM/BB/MAC/EEPROM and clear/set some bits in the same address * * Revision History: * 04-05-2004 Jerry Chen: Initial release * 11-24-2004 Warren Hsu: Add ControlvWriteByte,ControlvReadByte,ControlvMaskByte * */ #include "int.h" #include "rxtx.h" #include "dpc.h" #include "control.h" #include "desc.h" #include "device.h" /*--------------------- Static Definitions -------------------------*/ //endpoint def //endpoint 0: control //endpoint 1: interrupt //endpoint 2: read bulk //endpoint 3: write bulk //RequestType: //#define REQUEST_OUT (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE) // 0x40 //#define REQUEST_IN (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ) //0xc0 //static int msglevel =MSG_LEVEL_DEBUG; static int msglevel =MSG_LEVEL_INFO; #define USB_CTL_WAIT 500 //ms #ifndef URB_ASYNC_UNLINK #define URB_ASYNC_UNLINK 0 #endif /*--------------------- Static Classes ----------------------------*/ /*--------------------- Static Variables --------------------------*/ /*--------------------- Static Functions --------------------------*/ static void s_nsInterruptUsbIoCompleteRead( struct urb *urb ); static void s_nsBulkInUsbIoCompleteRead( struct urb *urb ); static void s_nsBulkOutIoCompleteWrite( struct urb *urb ); static void s_nsControlInUsbIoCompleteRead( struct urb *urb ); static void s_nsControlInUsbIoCompleteWrite( struct urb *urb ); /*--------------------- Export Variables --------------------------*/ /*--------------------- Export Functions --------------------------*/ int PIPEnsControlOutAsyn( PSDevice pDevice, BYTE byRequest, WORD wValue, WORD wIndex, WORD wLength, PBYTE pbyBuffer ) { int ntStatus; if (pDevice->Flags & fMP_DISCONNECTED) return STATUS_FAILURE; if (pDevice->Flags & fMP_CONTROL_WRITES) return STATUS_FAILURE; if (in_interrupt()) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"in_interrupt return ..byRequest %x\n", byRequest); return STATUS_FAILURE; } ntStatus = usb_control_msg( pDevice->usb, usb_sndctrlpipe(pDevice->usb , 0), byRequest, 0x40, // RequestType wValue, wIndex, (void *) pbyBuffer, wLength, HZ ); if (ntStatus >= 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"usb_sndctrlpipe ntStatus= %d\n", ntStatus); ntStatus = 0; } else { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"usb_sndctrlpipe fail, ntStatus= %d\n", ntStatus); } return ntStatus; } int PIPEnsControlOut( PSDevice pDevice, BYTE byRequest, WORD wValue, WORD wIndex, WORD wLength, PBYTE pbyBuffer ) { int ntStatus = 0; int ii; if (pDevice->Flags & fMP_DISCONNECTED) return STATUS_FAILURE; if (pDevice->Flags & fMP_CONTROL_WRITES) return STATUS_FAILURE; pDevice->sUsbCtlRequest.bRequestType = 0x40; pDevice->sUsbCtlRequest.bRequest = byRequest; pDevice->sUsbCtlRequest.wValue = cpu_to_le16p(&wValue); pDevice->sUsbCtlRequest.wIndex = cpu_to_le16p(&wIndex); pDevice->sUsbCtlRequest.wLength = cpu_to_le16p(&wLength); pDevice->pControlURB->transfer_flags |= URB_ASYNC_UNLINK; pDevice->pControlURB->actual_length = 0; // Notice, pbyBuffer limited point to variable buffer, can't be constant. usb_fill_control_urb(pDevice->pControlURB, pDevice->usb, usb_sndctrlpipe(pDevice->usb , 0), (char *) &pDevice->sUsbCtlRequest, pbyBuffer, wLength, s_nsControlInUsbIoCompleteWrite, pDevice); ntStatus = usb_submit_urb(pDevice->pControlURB, GFP_ATOMIC); if (ntStatus != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"control send request submission failed: %d\n", ntStatus); return STATUS_FAILURE; } else { MP_SET_FLAG(pDevice, fMP_CONTROL_WRITES); } spin_unlock_irq(&pDevice->lock); for (ii = 0; ii <= USB_CTL_WAIT; ii ++) { if (pDevice->Flags & fMP_CONTROL_WRITES) mdelay(1); else break; if (ii >= USB_CTL_WAIT) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "control send request submission timeout\n"); spin_lock_irq(&pDevice->lock); MP_CLEAR_FLAG(pDevice, fMP_CONTROL_WRITES); return STATUS_FAILURE; } } spin_lock_irq(&pDevice->lock); return STATUS_SUCCESS; } int PIPEnsControlIn( PSDevice pDevice, BYTE byRequest, WORD wValue, WORD wIndex, WORD wLength, PBYTE pbyBuffer ) { int ntStatus = 0; int ii; if (pDevice->Flags & fMP_DISCONNECTED) return STATUS_FAILURE; if (pDevice->Flags & fMP_CONTROL_READS) return STATUS_FAILURE; pDevice->sUsbCtlRequest.bRequestType = 0xC0; pDevice->sUsbCtlRequest.bRequest = byRequest; pDevice->sUsbCtlRequest.wValue = cpu_to_le16p(&wValue); pDevice->sUsbCtlRequest.wIndex = cpu_to_le16p(&wIndex); pDevice->sUsbCtlRequest.wLength = cpu_to_le16p(&wLength); pDevice->pControlURB->transfer_flags |= URB_ASYNC_UNLINK; pDevice->pControlURB->actual_length = 0; usb_fill_control_urb(pDevice->pControlURB, pDevice->usb, usb_rcvctrlpipe(pDevice->usb , 0), (char *) &pDevice->sUsbCtlRequest, pbyBuffer, wLength, s_nsControlInUsbIoCompleteRead, pDevice); ntStatus = usb_submit_urb(pDevice->pControlURB, GFP_ATOMIC); if (ntStatus != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"control request submission failed: %d\n", ntStatus); }else { MP_SET_FLAG(pDevice, fMP_CONTROL_READS); } spin_unlock_irq(&pDevice->lock); for (ii = 0; ii <= USB_CTL_WAIT; ii ++) { if (pDevice->Flags & fMP_CONTROL_READS) mdelay(1); else break; if (ii >= USB_CTL_WAIT) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "control rcv request submission timeout\n"); spin_lock_irq(&pDevice->lock); MP_CLEAR_FLAG(pDevice, fMP_CONTROL_READS); return STATUS_FAILURE; } } spin_lock_irq(&pDevice->lock); return ntStatus; } static void s_nsControlInUsbIoCompleteWrite( struct urb *urb ) { PSDevice pDevice; pDevice = urb->context; switch (urb->status) { case 0: break; case -EINPROGRESS: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl write urb status EINPROGRESS%d\n", urb->status); break; case -ENOENT: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl write urb status ENOENT %d\n", urb->status); break; default: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl write urb status %d\n", urb->status); } MP_CLEAR_FLAG(pDevice, fMP_CONTROL_WRITES); } /* * Description: * Complete function of usb Control callback * * Parameters: * In: * pDevice - Pointer to the adapter * * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ static void s_nsControlInUsbIoCompleteRead( struct urb *urb ) { PSDevice pDevice; pDevice = urb->context; switch (urb->status) { case 0: break; case -EINPROGRESS: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl read urb status EINPROGRESS%d\n", urb->status); break; case -ENOENT: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl read urb status = ENOENT %d\n", urb->status); break; default: DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ctrl read urb status %d\n", urb->status); } MP_CLEAR_FLAG(pDevice, fMP_CONTROL_READS); } /* * Description: * Allocates an usb interrupt in irp and calls USBD. * * Parameters: * In: * pDevice - Pointer to the adapter * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ int PIPEnsInterruptRead(PSDevice pDevice) { int ntStatus = STATUS_FAILURE; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"---->s_nsStartInterruptUsbRead()\n"); if(pDevice->intBuf.bInUse == TRUE){ return (STATUS_FAILURE); } pDevice->intBuf.bInUse = TRUE; // pDevice->bEventAvailable = FALSE; pDevice->ulIntInPosted++; // // Now that we have created the urb, we will send a // request to the USB device object. // pDevice->pInterruptURB->interval = pDevice->int_interval; usb_fill_bulk_urb(pDevice->pInterruptURB, pDevice->usb, usb_rcvbulkpipe(pDevice->usb, 1), (void *) pDevice->intBuf.pDataBuf, MAX_INTERRUPT_SIZE, s_nsInterruptUsbIoCompleteRead, pDevice); ntStatus = usb_submit_urb(pDevice->pInterruptURB, GFP_ATOMIC); if (ntStatus != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Submit int URB failed %d\n", ntStatus); } DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"<----s_nsStartInterruptUsbRead Return(%x)\n",ntStatus); return ntStatus; } /* * Description: * Complete function of usb interrupt in irp. * * Parameters: * In: * pDevice - Pointer to the adapter * * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ static void s_nsInterruptUsbIoCompleteRead( struct urb *urb ) { PSDevice pDevice; int ntStatus; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"---->s_nsInterruptUsbIoCompleteRead\n"); // // The context given to IoSetCompletionRoutine is the receive buffer object // pDevice = (PSDevice)urb->context; // // We have a number of cases: // 1) The USB read timed out and we received no data. // 2) The USB read timed out and we received some data. // 3) The USB read was successful and fully filled our irp buffer. // 4) The irp was cancelled. // 5) Some other failure from the USB device object. // ntStatus = urb->status; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"s_nsInterruptUsbIoCompleteRead Status %d\n", ntStatus); // if we were not successful, we need to free the int buffer for future use right here // otherwise interrupt data handler will free int buffer after it handle it. if (( ntStatus != STATUS_SUCCESS )) { pDevice->ulBulkInError++; pDevice->intBuf.bInUse = FALSE; // if (ntStatus == USBD_STATUS_CRC) { // pDevice->ulIntInContCRCError++; // } // if (ntStatus == STATUS_NOT_CONNECTED ) // { pDevice->fKillEventPollingThread = TRUE; // } DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"IntUSBIoCompleteControl STATUS = %d\n", ntStatus ); } else { pDevice->ulIntInBytesRead += (unsigned long) urb->actual_length; pDevice->ulIntInContCRCError = 0; pDevice->bEventAvailable = TRUE; INTnsProcessData(pDevice); } STAvUpdateUSBCounter(&pDevice->scStatistic.USB_InterruptStat, ntStatus); if (pDevice->fKillEventPollingThread != TRUE) { usb_fill_bulk_urb(pDevice->pInterruptURB, pDevice->usb, usb_rcvbulkpipe(pDevice->usb, 1), (void *) pDevice->intBuf.pDataBuf, MAX_INTERRUPT_SIZE, s_nsInterruptUsbIoCompleteRead, pDevice); ntStatus = usb_submit_urb(pDevice->pInterruptURB, GFP_ATOMIC); if (ntStatus != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Submit int URB failed %d\n", ntStatus); } } // // We return STATUS_MORE_PROCESSING_REQUIRED so that the completion // routine (IofCompleteRequest) will stop working on the irp. // return ; } /* * Description: * Allocates an usb BulkIn irp and calls USBD. * * Parameters: * In: * pDevice - Pointer to the adapter * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ int PIPEnsBulkInUsbRead(PSDevice pDevice, PRCB pRCB) { int ntStatus = 0; struct urb *pUrb; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"---->s_nsStartBulkInUsbRead\n"); if (pDevice->Flags & fMP_DISCONNECTED) return STATUS_FAILURE; pDevice->ulBulkInPosted++; pUrb = pRCB->pUrb; // // Now that we have created the urb, we will send a // request to the USB device object. // if (pRCB->skb == NULL) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"pRCB->skb is null \n"); return ntStatus; } usb_fill_bulk_urb(pUrb, pDevice->usb, usb_rcvbulkpipe(pDevice->usb, 2), (void *) (pRCB->skb->data), MAX_TOTAL_SIZE_WITH_ALL_HEADERS, s_nsBulkInUsbIoCompleteRead, pRCB); ntStatus = usb_submit_urb(pUrb, GFP_ATOMIC); if (ntStatus != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Submit Rx URB failed %d\n", ntStatus); return STATUS_FAILURE ; } pRCB->Ref = 1; pRCB->bBoolInUse= TRUE; return ntStatus; } /* * Description: * Complete function of usb BulkIn irp. * * Parameters: * In: * pDevice - Pointer to the adapter * * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ static void s_nsBulkInUsbIoCompleteRead( struct urb *urb ) { PRCB pRCB = (PRCB)urb->context; PSDevice pDevice = (PSDevice)pRCB->pDevice; unsigned long bytesRead; BOOL bIndicateReceive = FALSE; BOOL bReAllocSkb = FALSE; int status; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"---->s_nsBulkInUsbIoCompleteRead\n"); status = urb->status; bytesRead = urb->actual_length; if (status) { pDevice->ulBulkInError++; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"BULK In failed %d\n", status); pDevice->scStatistic.RxFcsErrCnt ++; //todo...xxxxxx // if (status == USBD_STATUS_CRC) { // pDevice->ulBulkInContCRCError++; // } // if (status == STATUS_DEVICE_NOT_CONNECTED ) // { // MP_SET_FLAG(pDevice, fMP_DISCONNECTED); // } } else { bIndicateReceive = TRUE; pDevice->ulBulkInContCRCError = 0; pDevice->ulBulkInBytesRead += bytesRead; pDevice->scStatistic.RxOkCnt ++; } STAvUpdateUSBCounter(&pDevice->scStatistic.USB_BulkInStat, status); if (bIndicateReceive) { spin_lock(&pDevice->lock); if (RXbBulkInProcessData(pDevice, pRCB, bytesRead) == TRUE) bReAllocSkb = TRUE; spin_unlock(&pDevice->lock); } pRCB->Ref--; if (pRCB->Ref == 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"RxvFreeNormal %d \n",pDevice->NumRecvFreeList); spin_lock(&pDevice->lock); RXvFreeRCB(pRCB, bReAllocSkb); spin_unlock(&pDevice->lock); } return; } /* * Description: * Allocates an usb BulkOut irp and calls USBD. * * Parameters: * In: * pDevice - Pointer to the adapter * Out: * none * * Return Value: STATUS_INSUFFICIENT_RESOURCES or result of IoCallDriver * */ int PIPEnsSendBulkOut( PSDevice pDevice, PUSB_SEND_CONTEXT pContext ) { int status; struct urb *pUrb; pDevice->bPWBitOn = FALSE; /* if (pDevice->pPendingBulkOutContext != NULL) { pDevice->NumContextsQueued++; EnqueueContext(pDevice->FirstTxContextQueue, pDevice->LastTxContextQueue, pContext); status = STATUS_PENDING; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Send pending!\n"); return status; } */ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"s_nsSendBulkOut\n"); if (MP_IS_READY(pDevice) && (pDevice->Flags & fMP_POST_WRITES)) { pUrb = pContext->pUrb; pDevice->ulBulkOutPosted++; // pDevice->pPendingBulkOutContext = pContext; usb_fill_bulk_urb( pUrb, pDevice->usb, usb_sndbulkpipe(pDevice->usb, 3), (void *) &(pContext->Data[0]), pContext->uBufLen, s_nsBulkOutIoCompleteWrite, pContext); status = usb_submit_urb(pUrb, GFP_ATOMIC); if (status != 0) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Submit Tx URB failed %d\n", status); return STATUS_FAILURE; } return STATUS_PENDING; } else { pContext->bBoolInUse = FALSE; return STATUS_RESOURCES; } } /* * Description: s_nsBulkOutIoCompleteWrite * 1a) Indicate to the protocol the status of the write. * 1b) Return ownership of the packet to the protocol. * * 2) If any more packets are queue for sending, send another packet * to USBD. * If the attempt to send the packet to the driver fails, * return ownership of the packet to the protocol and * try another packet (until one succeeds). * * Parameters: * In: * pdoUsbDevObj - pointer to the USB device object which * completed the irp * pIrp - the irp which was completed by the * device object * pContext - the context given to IoSetCompletionRoutine * before calling IoCallDriver on the irp * The pContext is a pointer to the USB device object. * Out: * none * * Return Value: STATUS_MORE_PROCESSING_REQUIRED - allows the completion routine * (IofCompleteRequest) to stop working on the irp. * */ static void s_nsBulkOutIoCompleteWrite( struct urb *urb ) { PSDevice pDevice; int status; CONTEXT_TYPE ContextType; unsigned long ulBufLen; PUSB_SEND_CONTEXT pContext; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"---->s_nsBulkOutIoCompleteWrite\n"); // // The context given to IoSetCompletionRoutine is an USB_CONTEXT struct // pContext = (PUSB_SEND_CONTEXT) urb->context; ASSERT( NULL != pContext ); pDevice = pContext->pDevice; ContextType = pContext->Type; ulBufLen = pContext->uBufLen; if (!netif_device_present(pDevice->dev)) return; // // Perform various IRP, URB, and buffer 'sanity checks' // status = urb->status; //we should have failed, succeeded, or cancelled, but NOT be pending STAvUpdateUSBCounter(&pDevice->scStatistic.USB_BulkOutStat, status); if(status == STATUS_SUCCESS) { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Write %d bytes\n",(int)ulBufLen); pDevice->ulBulkOutBytesWrite += ulBufLen; pDevice->ulBulkOutContCRCError = 0; pDevice->nTxDataTimeCout = 0; } else { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"BULK Out failed %d\n", status); pDevice->ulBulkOutError++; } // pDevice->ulCheckForHangCount = 0; // pDevice->pPendingBulkOutContext = NULL; if ( CONTEXT_DATA_PACKET == ContextType ) { // Indicate to the protocol the status of the sent packet and return // ownership of the packet. if (pContext->pPacket != NULL) { dev_kfree_skb_irq(pContext->pPacket); pContext->pPacket = NULL; DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"tx %d bytes\n",(int)ulBufLen); } pDevice->dev->trans_start = jiffies; if (status == STATUS_SUCCESS) { pDevice->packetsSent++; } else { DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Send USB error! [%08xh]\n", status); pDevice->packetsSentDropped++; } } if (pDevice->bLinkPass == TRUE) { if (netif_queue_stopped(pDevice->dev)) netif_wake_queue(pDevice->dev); } pContext->bBoolInUse = FALSE; return; }