blob: 3481e305460a8f14a3fd755f69e787a37c5f8a06 [file] [log] [blame]
/*******************************************************************************
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 */