374 lines
11 KiB
C
374 lines
11 KiB
C
/*******************************************************************************
|
|
|
|
This software file (the "File") is distributed by Marvell International Ltd.
|
|
or its affiliate(s) under the terms of the GNU General Public License Version 2,
|
|
June 1991 (the "License"). You may use, redistribute and/or modify this File
|
|
in accordance with the terms and conditions of the License, a copy of which
|
|
is available along with the File in the license.txt file or by writing to the
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
|
|
|
|
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
|
|
DISCLAIMED. The GPL License provides additional details about this warranty
|
|
disclaimer.
|
|
|
|
(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved.
|
|
(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved.
|
|
|
|
*******************************************************************************/
|
|
|
|
#include "usb/api/mvUsbDevApi.h"
|
|
#include "usb/device/mvUsbDevPrv.h"
|
|
|
|
#if defined(USB_UNDERRUN_WA)
|
|
|
|
typedef struct
|
|
{
|
|
uint_8* buff_ptr[MAX_XDS_FOR_TR_CALLS];
|
|
uint_32 size[MAX_XDS_FOR_TR_CALLS];
|
|
uint_8 ep_num[MAX_XDS_FOR_TR_CALLS];
|
|
int head;
|
|
int tail;
|
|
int tail_dma;
|
|
int num;
|
|
int num_dma;
|
|
|
|
} USB_SEND_QUEUE;
|
|
|
|
uint_8* usbSramBase;
|
|
int usbSramSize;
|
|
int usbSramPartSize;
|
|
USB_SEND_QUEUE usbSendQueue;
|
|
|
|
uint_32 usbSentSize = 0;
|
|
uint_32 usbDmaSize = 0;
|
|
|
|
#define S_FREE 0
|
|
#define S_BUSY 1
|
|
|
|
uint_32 dma_index = 0;
|
|
uint_32 sent_index = 0;
|
|
uint_32 sram_parts[USB_SRAM_MAX_PARTS];
|
|
|
|
|
|
void _usb_reset_send_queue(void)
|
|
{
|
|
int i;
|
|
|
|
usbSendQueue.num = 0;
|
|
usbSendQueue.num_dma = 0;
|
|
usbSendQueue.head = 0;
|
|
usbSendQueue.tail = 0;
|
|
usbSendQueue.tail_dma = 0;
|
|
for(i=0; i<MAX_XDS_FOR_TR_CALLS; i++)
|
|
{
|
|
usbSendQueue.size[i] = 0;
|
|
usbSendQueue.buff_ptr[i] = NULL;
|
|
usbSendQueue.ep_num[i] = 0;
|
|
}
|
|
usbSramPartSize = usbSramSize/global_wa_sram_parts;
|
|
|
|
for(i=0; i<global_wa_sram_parts; i++)
|
|
{
|
|
sram_parts[i] = S_FREE;
|
|
}
|
|
}
|
|
|
|
uint_8 _usb_prepare_to_send(void* handle)
|
|
{
|
|
XD_STRUCT_PTR xd_ptr;
|
|
USB_DEV_STATE_STRUCT_PTR usb_dev_ptr;
|
|
uint_8* buff_ptr;
|
|
uint_8* tmp_buff;
|
|
uint_32 size;
|
|
int num_dma, tail_dma, i;
|
|
uint_8 error = 0;
|
|
|
|
usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
|
|
|
|
tail_dma = usbSendQueue.tail_dma;
|
|
num_dma = usbSendQueue.num_dma;
|
|
buff_ptr = usbSendQueue.buff_ptr[tail_dma];
|
|
size = usbSendQueue.size[tail_dma];
|
|
|
|
if(num_dma == 0)
|
|
return 0;
|
|
|
|
/*
|
|
USB_printf("_usb_prepare_to_send: num=%d, tail=%d, sentSize=%d, size=%d, buff=%p\n",
|
|
num_dma, tail_dma, usbSentSize, size, buff_ptr);
|
|
*/
|
|
for(i=0; i<global_wa_sram_parts; i++)
|
|
{
|
|
if(sram_parts[dma_index] != S_FREE)
|
|
break;
|
|
|
|
if(usbDmaSize >= usbSendQueue.size[tail_dma])
|
|
{
|
|
/* Remove from the usbSendQueues */
|
|
num_dma--;
|
|
tail_dma++;
|
|
if(tail_dma == MAX_XDS_FOR_TR_CALLS)
|
|
tail_dma = 0;
|
|
|
|
usbSendQueue.tail_dma = tail_dma;
|
|
usbSendQueue.num_dma = num_dma;
|
|
usbDmaSize = 0;
|
|
|
|
if(num_dma == 0)
|
|
break;
|
|
}
|
|
|
|
buff_ptr = usbSendQueue.buff_ptr[tail_dma] + usbDmaSize;
|
|
size = MIN(usbSramPartSize, (usbSendQueue.size[tail_dma] - usbDmaSize) );
|
|
|
|
usbDmaSize += size;
|
|
|
|
if(size > global_wa_threshold)
|
|
{
|
|
tmp_buff = buff_ptr;
|
|
buff_ptr = (uint_8*)((int)usbSramBase + (dma_index * usbSramPartSize));
|
|
USB_idma_copy(buff_ptr, tmp_buff, size);
|
|
|
|
sram_parts[dma_index] = S_BUSY;
|
|
dma_index++;
|
|
if(dma_index == global_wa_sram_parts)
|
|
dma_index = 0;
|
|
}
|
|
|
|
|
|
/* Get a transfer descriptor */
|
|
USB_XD_QGET(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, xd_ptr);
|
|
|
|
usb_dev_ptr->XD_ENTRIES--;
|
|
USB_dcache_flush((pointer)buff_ptr, size);
|
|
|
|
/* Initialize the new transfer descriptor */
|
|
xd_ptr->EP_NUM = usbSendQueue.ep_num[tail_dma];
|
|
xd_ptr->BDIRECTION = ARC_USB_SEND;
|
|
xd_ptr->WTOTALLENGTH = size;
|
|
xd_ptr->WSOFAR = 0;
|
|
xd_ptr->WSTARTADDRESS = buff_ptr;
|
|
xd_ptr->BSTATUS = ARC_USB_STATUS_TRANSFER_ACCEPTED;
|
|
|
|
error = _usb_dci_vusb20_add_dTD(handle, xd_ptr);
|
|
|
|
if(error)
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*FUNCTION*-------------------------------------------------------------
|
|
*
|
|
* Function Name : usbSendComplete
|
|
* Returned Value : None
|
|
* Comments :
|
|
* Callback for send transfer complete event.
|
|
*
|
|
*END*-----------------------------------------------------------------*/
|
|
void usbSendComplete(void* handle, uint_8 type, boolean setup, uint_8 dir,
|
|
uint_8_ptr buffer, uint_32 length, uint_8 error)
|
|
{
|
|
/* Check if this complete is one from the sendQueue */
|
|
if( (usbSendQueue.ep_num[usbSendQueue.tail] == type) &&
|
|
(usbSendQueue.num > 0) )
|
|
{
|
|
USB_DEV_STATE_STRUCT_PTR usb_dev_ptr;
|
|
uint_8* buff_ptr;
|
|
uint_32 size;
|
|
int num, tail;
|
|
|
|
usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
|
|
|
|
tail = usbSendQueue.tail;
|
|
num = usbSendQueue.num;
|
|
buff_ptr = usbSendQueue.buff_ptr[tail];
|
|
size = usbSendQueue.size[tail];
|
|
/*
|
|
USB_printf("usbSendComplete: num=%d, tail=%d, usbSentSize=%d, type=%d, length=%d (%d), buff=%p (%p)\n",
|
|
num, tail, usbSentSize, type, length, usbSendQueue.size[tail],
|
|
buffer, usbSendQueue.buff_ptr[tail]);
|
|
*/
|
|
usbSentSize += length;
|
|
|
|
/* if the buffer was on the SRAM */
|
|
if( ((unsigned)buffer >= (unsigned)usbSramBase) &&
|
|
((unsigned)buffer < ((unsigned)usbSramBase + (usbSramPartSize * global_wa_sram_parts))) )
|
|
{
|
|
sram_parts[sent_index] = S_FREE;
|
|
sent_index++;
|
|
if(sent_index == global_wa_sram_parts)
|
|
sent_index = 0;
|
|
}
|
|
|
|
if(usbSentSize >= usbSendQueue.size[tail])
|
|
{
|
|
/* Remove from the usbSendQueues */
|
|
num--;
|
|
tail++;
|
|
if(tail == MAX_XDS_FOR_TR_CALLS)
|
|
tail = 0;
|
|
|
|
usbSendQueue.tail = tail;
|
|
usbSendQueue.num = num;
|
|
usbSentSize = 0;
|
|
|
|
/* Call complete callback */
|
|
_usb_device_call_service(handle, type, setup, dir,
|
|
buff_ptr, size, error);
|
|
|
|
if(num == 0)
|
|
return;
|
|
}
|
|
|
|
error = _usb_prepare_to_send(handle);
|
|
if (error)
|
|
{
|
|
USB_printf("usbSendComplete, add_dTD failed\n");
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Call complete callback */
|
|
_usb_device_call_service(handle, type, setup, dir,
|
|
buffer, length, error);
|
|
}
|
|
}
|
|
#endif /* USB_UNDERRUN_WA */
|
|
|
|
|
|
/*FUNCTION*-------------------------------------------------------------
|
|
*
|
|
* Function Name : _usb_device_send_data
|
|
* Returned Value : USB_OK or error code
|
|
* Comments :
|
|
* Sends data on a specified endpoint.
|
|
*
|
|
*END*-----------------------------------------------------------------*/
|
|
uint_8 _usb_device_send_data
|
|
(
|
|
/* [IN] the USB_USB_dev_initialize state structure */
|
|
_usb_device_handle handle,
|
|
|
|
/* [IN] the Endpoint number */
|
|
uint_8 ep_num,
|
|
|
|
/* [IN] buffer to send */
|
|
uint_8_ptr buff_ptr,
|
|
|
|
/* [IN] length of the transfer */
|
|
uint_32 size
|
|
)
|
|
{ /* Body */
|
|
int lockKey;
|
|
uint_8 error = 0;
|
|
XD_STRUCT_PTR xd_ptr;
|
|
USB_DEV_STATE_STRUCT_PTR usb_dev_ptr;
|
|
boolean toSend = TRUE;
|
|
|
|
usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle;
|
|
|
|
ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TX,
|
|
"send_data: handle=%p, ep=%d, pBuf=0x%x, size=%d, EP_QH=%p\n",
|
|
handle, ep_num, (unsigned)buff_ptr, (int)size, usb_dev_ptr->EP_QUEUE_HEAD_PTR);
|
|
|
|
ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_send_count++));
|
|
|
|
lockKey = USB_lock();
|
|
|
|
if (!usb_dev_ptr->XD_ENTRIES)
|
|
{
|
|
USB_unlock(lockKey);
|
|
USB_printf("_usb_device_send_data, transfer in progress\n");
|
|
return ARC_USB_STATUS_TRANSFER_IN_PROGRESS;
|
|
} /* Endif */
|
|
|
|
#if defined(USB_UNDERRUN_WA)
|
|
{
|
|
int head;
|
|
VUSB20_EP_QUEUE_HEAD_STRUCT* ep_queue_head_ptr;
|
|
|
|
ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR +
|
|
2*ep_num + ARC_USB_SEND;
|
|
|
|
if( ((ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF) > global_wa_threshold)
|
|
{
|
|
/* Only Endpoints with maxPktSize more than 128 bytes need special processing */
|
|
if( (size > global_wa_threshold) ||
|
|
(usbSendQueue.num != 0) )
|
|
{
|
|
/*
|
|
USB_printf("_usb_device_send_data: ep_num=%d, maxPktSize=%d, size=%d\n",
|
|
ep_num, (ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF, size);
|
|
*/
|
|
/* Check if usbSendQueue is not Full */
|
|
if(usbSendQueue.num == MAX_XDS_FOR_TR_CALLS)
|
|
{
|
|
USB_printf("ep=%d: usbSendQueue is FULL\n", ep_num);
|
|
USB_unlock(lockKey);
|
|
return USBERR_TX_FAILED;
|
|
}
|
|
|
|
/* Add to usbSendQueu */
|
|
head = usbSendQueue.head;
|
|
|
|
usbSendQueue.num++;
|
|
usbSendQueue.num_dma++;
|
|
usbSendQueue.size[head] = size;
|
|
usbSendQueue.buff_ptr[head] = buff_ptr;
|
|
usbSendQueue.ep_num[head] = ep_num;
|
|
|
|
head++;
|
|
if(head == MAX_XDS_FOR_TR_CALLS)
|
|
head = 0;
|
|
|
|
usbSendQueue.head = head;
|
|
|
|
/* Process first usbSendQueue element if possible */
|
|
if(usbSendQueue.num == 1)
|
|
{
|
|
error = _usb_prepare_to_send(handle);
|
|
}
|
|
toSend = FALSE;
|
|
}
|
|
}
|
|
}
|
|
#endif /* USB_UNDERRUN_WA */
|
|
|
|
if(toSend == TRUE)
|
|
{
|
|
/* Get a transfer descriptor */
|
|
USB_XD_QGET(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, xd_ptr);
|
|
|
|
usb_dev_ptr->XD_ENTRIES--;
|
|
|
|
if(buff_ptr != NULL)
|
|
USB_dcache_flush((pointer)buff_ptr, size);
|
|
|
|
/* Initialize the new transfer descriptor */
|
|
xd_ptr->EP_NUM = ep_num;
|
|
xd_ptr->BDIRECTION = ARC_USB_SEND;
|
|
xd_ptr->WTOTALLENGTH = size;
|
|
xd_ptr->WSOFAR = 0;
|
|
xd_ptr->WSTARTADDRESS = buff_ptr;
|
|
xd_ptr->BSTATUS = ARC_USB_STATUS_TRANSFER_ACCEPTED;
|
|
|
|
error = _usb_dci_vusb20_add_dTD(handle, xd_ptr);
|
|
}
|
|
USB_unlock(lockKey);
|
|
|
|
if (error)
|
|
{
|
|
USB_printf("_usb_device_send_data, transfer failed\n");
|
|
return USBERR_TX_FAILED;
|
|
} /* Endif */
|
|
return error;
|
|
|
|
} /* EndBody */
|
|
|