forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1876 lines
49 KiB
1876 lines
49 KiB
/* ========================================================================== |
|
* Synopsys HS OTG Linux Software Driver and documentation (hereinafter, |
|
* "Software") is an Unsupported proprietary work of Synopsys, Inc. unless |
|
* otherwise expressly agreed to in writing between Synopsys and you. |
|
* |
|
* The Software IS NOT an item of Licensed Software or Licensed Product under |
|
* any End User Software License Agreement or Agreement for Licensed Product |
|
* with Synopsys or any supplement thereto. You are permitted to use and |
|
* redistribute this Software in source and binary forms, with or without |
|
* modification, provided that redistributions of source code must retain this |
|
* notice. You may not view, use, disclose, copy or distribute this file or |
|
* any information contained herein except pursuant to this license grant from |
|
* Synopsys. If you do not agree with this notice, including the disclaimer |
|
* below, then you are not authorized to use the Software. |
|
* |
|
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS |
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, |
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
|
* DAMAGE. |
|
* ========================================================================== */ |
|
|
|
/** @file |
|
* |
|
* This file contains the most of the CFI(Core Feature Interface) |
|
* implementation for the OTG. |
|
*/ |
|
|
|
#ifdef DWC_UTE_CFI |
|
|
|
#include "dwc_otg_pcd.h" |
|
#include "dwc_otg_cfi.h" |
|
|
|
/** This definition should actually migrate to the Portability Library */ |
|
#define DWC_CONSTANT_CPU_TO_LE16(x) (x) |
|
|
|
extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex); |
|
|
|
static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen); |
|
static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, |
|
struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *ctrl_req); |
|
static int cfi_set_feature_value(struct dwc_otg_pcd *pcd); |
|
static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req); |
|
static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req); |
|
static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req); |
|
static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req); |
|
static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep); |
|
|
|
static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if); |
|
static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue); |
|
static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue); |
|
|
|
static uint8_t resize_fifos(dwc_otg_core_if_t * core_if); |
|
|
|
/** This is the header of the all features descriptor */ |
|
static cfi_all_features_header_t all_props_desc_header = { |
|
.wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100), |
|
.wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG), |
|
.wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9), |
|
}; |
|
|
|
/** This is an array of statically allocated feature descriptors */ |
|
static cfi_feature_desc_header_t prop_descs[] = { |
|
|
|
/* FT_ID_DMA_MODE */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(1), |
|
}, |
|
|
|
/* FT_ID_DMA_BUFFER_SETUP */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), |
|
}, |
|
|
|
/* FT_ID_DMA_BUFF_ALIGN */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), |
|
}, |
|
|
|
/* FT_ID_DMA_CONCAT_SETUP */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
//.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), |
|
}, |
|
|
|
/* FT_ID_DMA_CIRCULAR */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), |
|
}, |
|
|
|
/* FT_ID_THRESHOLD_SETUP */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), |
|
}, |
|
|
|
/* FT_ID_DFIFO_DEPTH */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH), |
|
.bmAttributes = CFI_FEATURE_ATTR_RO, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), |
|
}, |
|
|
|
/* FT_ID_TX_FIFO_DEPTH */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), |
|
}, |
|
|
|
/* FT_ID_RX_FIFO_DEPTH */ |
|
{ |
|
.wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH), |
|
.bmAttributes = CFI_FEATURE_ATTR_RW, |
|
.wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), |
|
} |
|
}; |
|
|
|
/** The table of feature names */ |
|
cfi_string_t prop_name_table[] = { |
|
{FT_ID_DMA_MODE, "dma_mode"}, |
|
{FT_ID_DMA_BUFFER_SETUP, "buffer_setup"}, |
|
{FT_ID_DMA_BUFF_ALIGN, "buffer_align"}, |
|
{FT_ID_DMA_CONCAT_SETUP, "concat_setup"}, |
|
{FT_ID_DMA_CIRCULAR, "buffer_circular"}, |
|
{FT_ID_THRESHOLD_SETUP, "threshold_setup"}, |
|
{FT_ID_DFIFO_DEPTH, "dfifo_depth"}, |
|
{FT_ID_TX_FIFO_DEPTH, "txfifo_depth"}, |
|
{FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"}, |
|
{} |
|
}; |
|
|
|
/************************************************************************/ |
|
|
|
/** |
|
* Returns the name of the feature by its ID |
|
* or NULL if no featute ID matches. |
|
* |
|
*/ |
|
const uint8_t *get_prop_name(uint16_t prop_id, int *len) |
|
{ |
|
cfi_string_t *pstr; |
|
*len = 0; |
|
|
|
for (pstr = prop_name_table; pstr && pstr->s; pstr++) { |
|
if (pstr->id == prop_id) { |
|
*len = DWC_STRLEN(pstr->s); |
|
return pstr->s; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/** |
|
* This function handles all CFI specific control requests. |
|
* |
|
* Return a negative value to stall the DCE. |
|
*/ |
|
int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl) |
|
{ |
|
int retval = 0; |
|
dwc_otg_pcd_ep_t *ep = NULL; |
|
cfiobject_t *cfi = pcd->cfi; |
|
struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); |
|
uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength); |
|
uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue); |
|
uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex); |
|
uint32_t regaddr = 0; |
|
uint32_t regval = 0; |
|
|
|
/* Save this Control Request in the CFI object. |
|
* The data field will be assigned in the data stage completion CB function. |
|
*/ |
|
cfi->ctrl_req = *ctrl; |
|
cfi->ctrl_req.data = NULL; |
|
|
|
cfi->need_gadget_att = 0; |
|
cfi->need_status_in_complete = 0; |
|
|
|
switch (ctrl->bRequest) { |
|
case VEN_CORE_GET_FEATURES: |
|
retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN); |
|
if (retval >= 0) { |
|
//dump_msg(cfi->buf_in.buf, retval); |
|
ep = &pcd->ep0; |
|
|
|
retval = min((uint16_t) retval, wLen); |
|
/* Transfer this buffer to the host through the EP0-IN EP */ |
|
ep->dwc_ep.dma_addr = cfi->buf_in.addr; |
|
ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_len = retval; |
|
ep->dwc_ep.xfer_count = 0; |
|
ep->dwc_ep.sent_zlp = 0; |
|
ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
|
|
|
pcd->ep0_pending = 1; |
|
dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); |
|
} |
|
retval = 0; |
|
break; |
|
|
|
case VEN_CORE_GET_FEATURE: |
|
CFI_INFO("VEN_CORE_GET_FEATURE\n"); |
|
retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN, |
|
pcd, ctrl); |
|
if (retval >= 0) { |
|
ep = &pcd->ep0; |
|
|
|
retval = min((uint16_t) retval, wLen); |
|
/* Transfer this buffer to the host through the EP0-IN EP */ |
|
ep->dwc_ep.dma_addr = cfi->buf_in.addr; |
|
ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_len = retval; |
|
ep->dwc_ep.xfer_count = 0; |
|
ep->dwc_ep.sent_zlp = 0; |
|
ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
|
|
|
pcd->ep0_pending = 1; |
|
dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); |
|
} |
|
CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval); |
|
dump_msg(cfi->buf_in.buf, retval); |
|
break; |
|
|
|
case VEN_CORE_SET_FEATURE: |
|
CFI_INFO("VEN_CORE_SET_FEATURE\n"); |
|
/* Set up an XFER to get the data stage of the control request, |
|
* which is the new value of the feature to be modified. |
|
*/ |
|
ep = &pcd->ep0; |
|
ep->dwc_ep.is_in = 0; |
|
ep->dwc_ep.dma_addr = cfi->buf_out.addr; |
|
ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; |
|
ep->dwc_ep.xfer_buff = cfi->buf_out.buf; |
|
ep->dwc_ep.xfer_len = wLen; |
|
ep->dwc_ep.xfer_count = 0; |
|
ep->dwc_ep.sent_zlp = 0; |
|
ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
|
|
|
pcd->ep0_pending = 1; |
|
/* Read the control write's data stage */ |
|
dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); |
|
retval = 0; |
|
break; |
|
|
|
case VEN_CORE_RESET_FEATURES: |
|
CFI_INFO("VEN_CORE_RESET_FEATURES\n"); |
|
cfi->need_gadget_att = 1; |
|
cfi->need_status_in_complete = 1; |
|
retval = cfi_preproc_reset(pcd, ctrl); |
|
CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval); |
|
break; |
|
|
|
case VEN_CORE_ACTIVATE_FEATURES: |
|
CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n"); |
|
break; |
|
|
|
case VEN_CORE_READ_REGISTER: |
|
CFI_INFO("VEN_CORE_READ_REGISTER\n"); |
|
/* wValue optionally contains the HI WORD of the register offset and |
|
* wIndex contains the LOW WORD of the register offset |
|
*/ |
|
if (wValue == 0) { |
|
/* @TODO - MAS - fix the access to the base field */ |
|
regaddr = 0; |
|
//regaddr = (uint32_t) pcd->otg_dev->os_dep.base; |
|
//GET_CORE_IF(pcd)->co |
|
regaddr |= wIndex; |
|
} else { |
|
regaddr = (wValue << 16) | wIndex; |
|
} |
|
|
|
/* Read a 32-bit value of the memory at the regaddr */ |
|
regval = DWC_READ_REG32((uint32_t *) regaddr); |
|
|
|
ep = &pcd->ep0; |
|
dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t)); |
|
ep->dwc_ep.is_in = 1; |
|
ep->dwc_ep.dma_addr = cfi->buf_in.addr; |
|
ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_buff = cfi->buf_in.buf; |
|
ep->dwc_ep.xfer_len = wLen; |
|
ep->dwc_ep.xfer_count = 0; |
|
ep->dwc_ep.sent_zlp = 0; |
|
ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
|
|
|
pcd->ep0_pending = 1; |
|
dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); |
|
cfi->need_gadget_att = 0; |
|
retval = 0; |
|
break; |
|
|
|
case VEN_CORE_WRITE_REGISTER: |
|
CFI_INFO("VEN_CORE_WRITE_REGISTER\n"); |
|
/* Set up an XFER to get the data stage of the control request, |
|
* which is the new value of the register to be modified. |
|
*/ |
|
ep = &pcd->ep0; |
|
ep->dwc_ep.is_in = 0; |
|
ep->dwc_ep.dma_addr = cfi->buf_out.addr; |
|
ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; |
|
ep->dwc_ep.xfer_buff = cfi->buf_out.buf; |
|
ep->dwc_ep.xfer_len = wLen; |
|
ep->dwc_ep.xfer_count = 0; |
|
ep->dwc_ep.sent_zlp = 0; |
|
ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; |
|
|
|
pcd->ep0_pending = 1; |
|
/* Read the control write's data stage */ |
|
dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); |
|
retval = 0; |
|
break; |
|
|
|
default: |
|
retval = -DWC_E_NOT_SUPPORTED; |
|
break; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function prepares the core features descriptors and copies its |
|
* raw representation into the buffer <buf>. |
|
* |
|
* The buffer structure is as follows: |
|
* all_features_header (8 bytes) |
|
* features_#1 (8 bytes + feature name string length) |
|
* features_#2 (8 bytes + feature name string length) |
|
* ..... |
|
* features_#n - where n=the total count of feature descriptors |
|
*/ |
|
static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen) |
|
{ |
|
cfi_feature_desc_header_t *prop_hdr = prop_descs; |
|
cfi_feature_desc_header_t *prop; |
|
cfi_all_features_header_t *all_props_hdr = &all_props_desc_header; |
|
cfi_all_features_header_t *tmp; |
|
uint8_t *tmpbuf = buf; |
|
const uint8_t *pname = NULL; |
|
int i, j, namelen = 0, totlen; |
|
|
|
/* Prepare and copy the core features into the buffer */ |
|
CFI_INFO("%s:\n", __func__); |
|
|
|
tmp = (cfi_all_features_header_t *) tmpbuf; |
|
*tmp = *all_props_hdr; |
|
tmpbuf += CFI_ALL_FEATURES_HDR_LEN; |
|
|
|
j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t); |
|
for (i = 0; i < j; i++, prop_hdr++) { |
|
pname = get_prop_name(prop_hdr->wFeatureID, &namelen); |
|
prop = (cfi_feature_desc_header_t *) tmpbuf; |
|
*prop = *prop_hdr; |
|
|
|
prop->bNameLen = namelen; |
|
prop->wLength = |
|
DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN + |
|
namelen); |
|
|
|
tmpbuf += CFI_FEATURE_DESC_HDR_LEN; |
|
dwc_memcpy(tmpbuf, pname, namelen); |
|
tmpbuf += namelen; |
|
} |
|
|
|
totlen = tmpbuf - buf; |
|
|
|
if (totlen > 0) { |
|
tmp = (cfi_all_features_header_t *) buf; |
|
tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen); |
|
} |
|
|
|
return totlen; |
|
} |
|
|
|
/** |
|
* This function releases all the dynamic memory in the CFI object. |
|
*/ |
|
static void cfi_release(cfiobject_t * cfiobj) |
|
{ |
|
cfi_ep_t *cfiep; |
|
dwc_list_link_t *tmp; |
|
|
|
CFI_INFO("%s\n", __func__); |
|
|
|
if (cfiobj->buf_in.buf) { |
|
DWC_DMA_FREE(CFI_IN_BUF_LEN, cfiobj->buf_in.buf, |
|
cfiobj->buf_in.addr); |
|
cfiobj->buf_in.buf = NULL; |
|
} |
|
|
|
if (cfiobj->buf_out.buf) { |
|
DWC_DMA_FREE(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf, |
|
cfiobj->buf_out.addr); |
|
cfiobj->buf_out.buf = NULL; |
|
} |
|
|
|
/* Free the Buffer Setup values for each EP */ |
|
//list_for_each_entry(cfiep, &cfiobj->active_eps, lh) { |
|
DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) { |
|
cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); |
|
cfi_free_ep_bs_dyn_data(cfiep); |
|
} |
|
} |
|
|
|
/** |
|
* This function frees the dynamically allocated EP buffer setup data. |
|
*/ |
|
static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep) |
|
{ |
|
if (cfiep->bm_sg) { |
|
DWC_FREE(cfiep->bm_sg); |
|
cfiep->bm_sg = NULL; |
|
} |
|
|
|
if (cfiep->bm_align) { |
|
DWC_FREE(cfiep->bm_align); |
|
cfiep->bm_align = NULL; |
|
} |
|
|
|
if (cfiep->bm_concat) { |
|
if (NULL != cfiep->bm_concat->wTxBytes) { |
|
DWC_FREE(cfiep->bm_concat->wTxBytes); |
|
cfiep->bm_concat->wTxBytes = NULL; |
|
} |
|
DWC_FREE(cfiep->bm_concat); |
|
cfiep->bm_concat = NULL; |
|
} |
|
} |
|
|
|
/** |
|
* This function initializes the default values of the features |
|
* for a specific endpoint and should be called only once when |
|
* the EP is enabled first time. |
|
*/ |
|
static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep) |
|
{ |
|
int retval = 0; |
|
|
|
cfiep->bm_sg = DWC_ALLOC(sizeof(ddma_sg_buffer_setup_t)); |
|
if (NULL == cfiep->bm_sg) { |
|
CFI_INFO("Failed to allocate memory for SG feature value\n"); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); |
|
|
|
/* For the Concatenation feature's default value we do not allocate |
|
* memory for the wTxBytes field - it will be done in the set_feature_value |
|
* request handler. |
|
*/ |
|
cfiep->bm_concat = DWC_ALLOC(sizeof(ddma_concat_buffer_setup_t)); |
|
if (NULL == cfiep->bm_concat) { |
|
CFI_INFO |
|
("Failed to allocate memory for CONCATENATION feature value\n"); |
|
DWC_FREE(cfiep->bm_sg); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); |
|
|
|
cfiep->bm_align = DWC_ALLOC(sizeof(ddma_align_buffer_setup_t)); |
|
if (NULL == cfiep->bm_align) { |
|
CFI_INFO |
|
("Failed to allocate memory for Alignment feature value\n"); |
|
DWC_FREE(cfiep->bm_sg); |
|
DWC_FREE(cfiep->bm_concat); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t)); |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* The callback function that notifies the CFI on the activation of |
|
* an endpoint in the PCD. The following steps are done in this function: |
|
* |
|
* Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's |
|
* active endpoint) |
|
* Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP |
|
* Set the Buffer Mode to standard |
|
* Initialize the default values for all EP modes (SG, Circular, Concat, Align) |
|
* Add the cfi_ep_t object to the list of active endpoints in the CFI object |
|
*/ |
|
static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, |
|
struct dwc_otg_pcd_ep *ep) |
|
{ |
|
cfi_ep_t *cfiep; |
|
int retval = -DWC_E_NOT_SUPPORTED; |
|
|
|
CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__, |
|
"EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress); |
|
/* MAS - Check whether this endpoint already is in the list */ |
|
cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); |
|
|
|
if (NULL == cfiep) { |
|
/* Allocate a cfi_ep_t object */ |
|
cfiep = DWC_ALLOC(sizeof(cfi_ep_t)); |
|
if (NULL == cfiep) { |
|
CFI_INFO |
|
("Unable to allocate memory for <cfiep> in function %s\n", |
|
__func__); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
dwc_memset(cfiep, 0, sizeof(cfi_ep_t)); |
|
|
|
/* Save the dwc_otg_pcd_ep pointer in the cfiep object */ |
|
cfiep->ep = ep; |
|
|
|
/* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */ |
|
ep->dwc_ep.descs = |
|
DWC_DMA_ALLOC(MAX_DMA_DESCS_PER_EP * |
|
sizeof(dwc_otg_dma_desc_t), |
|
&ep->dwc_ep.descs_dma_addr); |
|
|
|
if (NULL == ep->dwc_ep.descs) { |
|
DWC_FREE(cfiep); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
|
|
DWC_LIST_INIT(&cfiep->lh); |
|
|
|
/* Set the buffer mode to BM_STANDARD. It will be modified |
|
* when building descriptors for a specific buffer mode */ |
|
ep->dwc_ep.buff_mode = BM_STANDARD; |
|
|
|
/* Create and initialize the default values for this EP's Buffer modes */ |
|
if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0) |
|
return retval; |
|
|
|
/* Add the cfi_ep_t object to the CFI object's list of active endpoints */ |
|
DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh); |
|
retval = 0; |
|
} else { /* The sought EP already is in the list */ |
|
CFI_INFO("%s: The sought EP already is in the list\n", |
|
__func__); |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function is called when the data stage of a 3-stage Control Write request |
|
* is complete. |
|
* |
|
*/ |
|
static int cfi_ctrl_write_complete(struct cfiobject *cfi, |
|
struct dwc_otg_pcd *pcd) |
|
{ |
|
uint32_t addr, reg_value; |
|
uint16_t wIndex, wValue; |
|
uint8_t bRequest; |
|
uint8_t *buf = cfi->buf_out.buf; |
|
//struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved; |
|
struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req; |
|
int retval = -DWC_E_NOT_SUPPORTED; |
|
|
|
CFI_INFO("%s\n", __func__); |
|
|
|
bRequest = ctrl_req->bRequest; |
|
wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); |
|
wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); |
|
|
|
/* |
|
* Save the pointer to the data stage in the ctrl_req's <data> field. |
|
* The request should be already saved in the command stage by now. |
|
*/ |
|
ctrl_req->data = cfi->buf_out.buf; |
|
cfi->need_status_in_complete = 0; |
|
cfi->need_gadget_att = 0; |
|
|
|
switch (bRequest) { |
|
case VEN_CORE_WRITE_REGISTER: |
|
/* The buffer contains raw data of the new value for the register */ |
|
reg_value = *((uint32_t *) buf); |
|
if (wValue == 0) { |
|
addr = 0; |
|
//addr = (uint32_t) pcd->otg_dev->os_dep.base; |
|
addr += wIndex; |
|
} else { |
|
addr = (wValue << 16) | wIndex; |
|
} |
|
|
|
//writel(reg_value, addr); |
|
|
|
retval = 0; |
|
cfi->need_status_in_complete = 1; |
|
break; |
|
|
|
case VEN_CORE_SET_FEATURE: |
|
/* The buffer contains raw data of the new value of the feature */ |
|
retval = cfi_set_feature_value(pcd); |
|
if (retval < 0) |
|
return retval; |
|
|
|
cfi->need_status_in_complete = 1; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function builds the DMA descriptors for the SG buffer mode. |
|
*/ |
|
static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, |
|
dwc_otg_pcd_request_t * req) |
|
{ |
|
struct dwc_otg_pcd_ep *ep = cfiep->ep; |
|
ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg; |
|
struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; |
|
struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; |
|
dma_addr_t buff_addr = req->dma; |
|
int i; |
|
uint32_t txsize, off; |
|
|
|
txsize = sgval->wSize; |
|
off = sgval->bOffset; |
|
|
|
// CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n", |
|
// __func__, cfiep->ep->ep.name, txsize, off); |
|
|
|
for (i = 0; i < sgval->bCount; i++) { |
|
desc->status.b.bs = BS_HOST_BUSY; |
|
desc->buf = buff_addr; |
|
desc->status.b.l = 0; |
|
desc->status.b.ioc = 0; |
|
desc->status.b.sp = 0; |
|
desc->status.b.bytes = txsize; |
|
desc->status.b.bs = BS_HOST_READY; |
|
|
|
/* Set the next address of the buffer */ |
|
buff_addr += txsize + off; |
|
desc_last = desc; |
|
desc++; |
|
} |
|
|
|
/* Set the last, ioc and sp bits on the Last DMA Descriptor */ |
|
desc_last->status.b.l = 1; |
|
desc_last->status.b.ioc = 1; |
|
desc_last->status.b.sp = ep->dwc_ep.sent_zlp; |
|
/* Save the last DMA descriptor pointer */ |
|
cfiep->dma_desc_last = desc_last; |
|
cfiep->desc_count = sgval->bCount; |
|
} |
|
|
|
/** |
|
* This function builds the DMA descriptors for the Concatenation buffer mode. |
|
*/ |
|
static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, |
|
dwc_otg_pcd_request_t * req) |
|
{ |
|
struct dwc_otg_pcd_ep *ep = cfiep->ep; |
|
ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat; |
|
struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; |
|
struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; |
|
dma_addr_t buff_addr = req->dma; |
|
int i; |
|
uint16_t *txsize; |
|
|
|
txsize = concatval->wTxBytes; |
|
|
|
for (i = 0; i < concatval->hdr.bDescCount; i++) { |
|
desc->buf = buff_addr; |
|
desc->status.b.bs = BS_HOST_BUSY; |
|
desc->status.b.l = 0; |
|
desc->status.b.ioc = 0; |
|
desc->status.b.sp = 0; |
|
desc->status.b.bytes = *txsize; |
|
desc->status.b.bs = BS_HOST_READY; |
|
|
|
txsize++; |
|
/* Set the next address of the buffer */ |
|
buff_addr += UGETW(ep->desc->wMaxPacketSize); |
|
desc_last = desc; |
|
desc++; |
|
} |
|
|
|
/* Set the last, ioc and sp bits on the Last DMA Descriptor */ |
|
desc_last->status.b.l = 1; |
|
desc_last->status.b.ioc = 1; |
|
desc_last->status.b.sp = ep->dwc_ep.sent_zlp; |
|
cfiep->dma_desc_last = desc_last; |
|
cfiep->desc_count = concatval->hdr.bDescCount; |
|
} |
|
|
|
/** |
|
* This function builds the DMA descriptors for the Circular buffer mode |
|
*/ |
|
static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, |
|
dwc_otg_pcd_request_t * req) |
|
{ |
|
/* @todo: MAS - add implementation when this feature needs to be tested */ |
|
} |
|
|
|
/** |
|
* This function builds the DMA descriptors for the Alignment buffer mode |
|
*/ |
|
static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, |
|
dwc_otg_pcd_request_t * req) |
|
{ |
|
struct dwc_otg_pcd_ep *ep = cfiep->ep; |
|
ddma_align_buffer_setup_t *alignval = cfiep->bm_align; |
|
struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; |
|
dma_addr_t buff_addr = req->dma; |
|
|
|
desc->status.b.bs = BS_HOST_BUSY; |
|
desc->status.b.l = 1; |
|
desc->status.b.ioc = 1; |
|
desc->status.b.sp = ep->dwc_ep.sent_zlp; |
|
desc->status.b.bytes = req->length; |
|
/* Adjust the buffer alignment */ |
|
desc->buf = (buff_addr + alignval->bAlign); |
|
desc->status.b.bs = BS_HOST_READY; |
|
cfiep->dma_desc_last = desc; |
|
cfiep->desc_count = 1; |
|
} |
|
|
|
/** |
|
* This function builds the DMA descriptors chain for different modes of the |
|
* buffer setup of an endpoint. |
|
*/ |
|
static void cfi_build_descriptors(struct cfiobject *cfi, |
|
struct dwc_otg_pcd *pcd, |
|
struct dwc_otg_pcd_ep *ep, |
|
dwc_otg_pcd_request_t * req) |
|
{ |
|
cfi_ep_t *cfiep; |
|
|
|
/* Get the cfiep by the dwc_otg_pcd_ep */ |
|
cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); |
|
if (NULL == cfiep) { |
|
CFI_INFO("%s: Unable to find a matching active endpoint\n", |
|
__func__); |
|
return; |
|
} |
|
|
|
cfiep->xfer_len = req->length; |
|
|
|
/* Iterate through all the DMA descriptors */ |
|
switch (cfiep->ep->dwc_ep.buff_mode) { |
|
case BM_SG: |
|
cfi_build_sg_descs(cfi, cfiep, req); |
|
break; |
|
|
|
case BM_CONCAT: |
|
cfi_build_concat_descs(cfi, cfiep, req); |
|
break; |
|
|
|
case BM_CIRCULAR: |
|
cfi_build_circ_descs(cfi, cfiep, req); |
|
break; |
|
|
|
case BM_ALIGN: |
|
cfi_build_align_descs(cfi, cfiep, req); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* Allocate DMA buffer for different Buffer modes. |
|
*/ |
|
static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, |
|
struct dwc_otg_pcd_ep *ep, dma_addr_t * dma, |
|
unsigned size, gfp_t flags) |
|
{ |
|
return DWC_DMA_ALLOC(size, dma); |
|
} |
|
|
|
/** |
|
* This function initializes the CFI object. |
|
*/ |
|
int init_cfi(cfiobject_t * cfiobj) |
|
{ |
|
CFI_INFO("%s\n", __func__); |
|
|
|
/* Allocate a buffer for IN XFERs */ |
|
cfiobj->buf_in.buf = |
|
DWC_DMA_ALLOC(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr); |
|
if (NULL == cfiobj->buf_in.buf) { |
|
CFI_INFO("Unable to allocate buffer for INs\n"); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
|
|
/* Allocate a buffer for OUT XFERs */ |
|
cfiobj->buf_out.buf = |
|
DWC_DMA_ALLOC(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr); |
|
if (NULL == cfiobj->buf_out.buf) { |
|
CFI_INFO("Unable to allocate buffer for OUT\n"); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
|
|
/* Initialize the callback function pointers */ |
|
cfiobj->ops.release = cfi_release; |
|
cfiobj->ops.ep_enable = cfi_ep_enable; |
|
cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete; |
|
cfiobj->ops.build_descriptors = cfi_build_descriptors; |
|
cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf; |
|
|
|
/* Initialize the list of active endpoints in the CFI object */ |
|
DWC_LIST_INIT(&cfiobj->active_eps); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* This function reads the required feature's current value into the buffer |
|
* |
|
* @retval: Returns negative as error, or the data length of the feature |
|
*/ |
|
static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, |
|
struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *ctrl_req) |
|
{ |
|
int retval = -DWC_E_NOT_SUPPORTED; |
|
struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); |
|
uint16_t dfifo, rxfifo, txfifo; |
|
|
|
switch (ctrl_req->wIndex) { |
|
/* Whether the DDMA is enabled or not */ |
|
case FT_ID_DMA_MODE: |
|
*buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0; |
|
retval = 1; |
|
break; |
|
|
|
case FT_ID_DMA_BUFFER_SETUP: |
|
retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req); |
|
break; |
|
|
|
case FT_ID_DMA_BUFF_ALIGN: |
|
retval = cfi_ep_get_align_val(buf, pcd, ctrl_req); |
|
break; |
|
|
|
case FT_ID_DMA_CONCAT_SETUP: |
|
retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req); |
|
break; |
|
|
|
case FT_ID_DMA_CIRCULAR: |
|
CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n"); |
|
break; |
|
|
|
case FT_ID_THRESHOLD_SETUP: |
|
CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n"); |
|
break; |
|
|
|
case FT_ID_DFIFO_DEPTH: |
|
dfifo = get_dfifo_size(coreif); |
|
*((uint16_t *) buf) = dfifo; |
|
retval = sizeof(uint16_t); |
|
break; |
|
|
|
case FT_ID_TX_FIFO_DEPTH: |
|
retval = get_txfifo_size(pcd, ctrl_req->wValue); |
|
if (retval >= 0) { |
|
txfifo = retval; |
|
*((uint16_t *) buf) = txfifo; |
|
retval = sizeof(uint16_t); |
|
} |
|
break; |
|
|
|
case FT_ID_RX_FIFO_DEPTH: |
|
retval = get_rxfifo_size(coreif, ctrl_req->wValue); |
|
if (retval >= 0) { |
|
rxfifo = retval; |
|
*((uint16_t *) buf) = rxfifo; |
|
retval = sizeof(uint16_t); |
|
} |
|
break; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function resets the SG for the specified EP to its default value |
|
*/ |
|
static int cfi_reset_sg_val(cfi_ep_t * cfiep) |
|
{ |
|
dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); |
|
return 0; |
|
} |
|
|
|
/** |
|
* This function resets the Alignment for the specified EP to its default value |
|
*/ |
|
static int cfi_reset_align_val(cfi_ep_t * cfiep) |
|
{ |
|
dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); |
|
return 0; |
|
} |
|
|
|
/** |
|
* This function resets the Concatenation for the specified EP to its default value |
|
* This function will also set the value of the wTxBytes field to NULL after |
|
* freeing the memory previously allocated for this field. |
|
*/ |
|
static int cfi_reset_concat_val(cfi_ep_t * cfiep) |
|
{ |
|
/* First we need to free the wTxBytes field */ |
|
if (cfiep->bm_concat->wTxBytes) { |
|
DWC_FREE(cfiep->bm_concat->wTxBytes); |
|
cfiep->bm_concat->wTxBytes = NULL; |
|
} |
|
|
|
dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); |
|
return 0; |
|
} |
|
|
|
/** |
|
* This function resets all the buffer setups of the specified endpoint |
|
*/ |
|
static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep) |
|
{ |
|
cfi_reset_sg_val(cfiep); |
|
cfi_reset_align_val(cfiep); |
|
cfi_reset_concat_val(cfiep); |
|
return 0; |
|
} |
|
|
|
static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr, |
|
uint8_t rx_rst, uint8_t tx_rst) |
|
{ |
|
int retval = -DWC_E_INVALID; |
|
uint16_t tx_siz[15]; |
|
uint16_t rx_siz = 0; |
|
dwc_otg_pcd_ep_t *ep = NULL; |
|
dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); |
|
dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; |
|
|
|
if (rx_rst) { |
|
rx_siz = params->dev_rx_fifo_size; |
|
params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz; |
|
} |
|
|
|
if (tx_rst) { |
|
if (ep_addr == 0) { |
|
int i; |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
tx_siz[i] = |
|
core_if->core_params->dev_tx_fifo_size[i]; |
|
core_if->core_params->dev_tx_fifo_size[i] = |
|
core_if->init_txfsiz[i]; |
|
} |
|
} else { |
|
|
|
ep = get_ep_by_addr(pcd, ep_addr); |
|
|
|
if (NULL == ep) { |
|
CFI_INFO |
|
("%s: Unable to get the endpoint addr=0x%02x\n", |
|
__func__, ep_addr); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
tx_siz[0] = |
|
params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - |
|
1]; |
|
params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = |
|
GET_CORE_IF(pcd)->init_txfsiz[ep-> |
|
dwc_ep.tx_fifo_num - |
|
1]; |
|
} |
|
} |
|
|
|
if (resize_fifos(GET_CORE_IF(pcd))) { |
|
retval = 0; |
|
} else { |
|
CFI_INFO |
|
("%s: Error resetting the feature Reset All(FIFO size)\n", |
|
__func__); |
|
if (rx_rst) { |
|
params->dev_rx_fifo_size = rx_siz; |
|
} |
|
|
|
if (tx_rst) { |
|
if (ep_addr == 0) { |
|
int i; |
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; |
|
i++) { |
|
core_if-> |
|
core_params->dev_tx_fifo_size[i] = |
|
tx_siz[i]; |
|
} |
|
} else { |
|
params->dev_tx_fifo_size[ep-> |
|
dwc_ep.tx_fifo_num - |
|
1] = tx_siz[0]; |
|
} |
|
} |
|
retval = -DWC_E_INVALID; |
|
} |
|
return retval; |
|
} |
|
|
|
static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr) |
|
{ |
|
int retval = 0; |
|
cfi_ep_t *cfiep; |
|
cfiobject_t *cfi = pcd->cfi; |
|
dwc_list_link_t *tmp; |
|
|
|
retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1); |
|
if (retval < 0) { |
|
return retval; |
|
} |
|
|
|
/* If the EP address is known then reset the features for only that EP */ |
|
if (addr) { |
|
cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == cfiep) { |
|
CFI_INFO("%s: Error getting the EP address 0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
retval = cfi_ep_reset_all_setup_vals(cfiep); |
|
cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; |
|
} |
|
/* Otherwise (wValue == 0), reset all features of all EP's */ |
|
else { |
|
/* Traverse all the active EP's and reset the feature(s) value(s) */ |
|
//list_for_each_entry(cfiep, &cfi->active_eps, lh) { |
|
DWC_LIST_FOREACH(tmp, &cfi->active_eps) { |
|
cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); |
|
retval = cfi_ep_reset_all_setup_vals(cfiep); |
|
cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; |
|
if (retval < 0) { |
|
CFI_INFO |
|
("%s: Error resetting the feature Reset All\n", |
|
__func__); |
|
return retval; |
|
} |
|
} |
|
} |
|
return retval; |
|
} |
|
|
|
static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd, |
|
uint8_t addr) |
|
{ |
|
int retval = 0; |
|
cfi_ep_t *cfiep; |
|
cfiobject_t *cfi = pcd->cfi; |
|
dwc_list_link_t *tmp; |
|
|
|
/* If the EP address is known then reset the features for only that EP */ |
|
if (addr) { |
|
cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == cfiep) { |
|
CFI_INFO("%s: Error getting the EP address 0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
retval = cfi_reset_sg_val(cfiep); |
|
} |
|
/* Otherwise (wValue == 0), reset all features of all EP's */ |
|
else { |
|
/* Traverse all the active EP's and reset the feature(s) value(s) */ |
|
//list_for_each_entry(cfiep, &cfi->active_eps, lh) { |
|
DWC_LIST_FOREACH(tmp, &cfi->active_eps) { |
|
cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); |
|
retval = cfi_reset_sg_val(cfiep); |
|
if (retval < 0) { |
|
CFI_INFO |
|
("%s: Error resetting the feature Buffer Setup\n", |
|
__func__); |
|
return retval; |
|
} |
|
} |
|
} |
|
return retval; |
|
} |
|
|
|
static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr) |
|
{ |
|
int retval = 0; |
|
cfi_ep_t *cfiep; |
|
cfiobject_t *cfi = pcd->cfi; |
|
dwc_list_link_t *tmp; |
|
|
|
/* If the EP address is known then reset the features for only that EP */ |
|
if (addr) { |
|
cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == cfiep) { |
|
CFI_INFO("%s: Error getting the EP address 0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
retval = cfi_reset_concat_val(cfiep); |
|
} |
|
/* Otherwise (wValue == 0), reset all features of all EP's */ |
|
else { |
|
/* Traverse all the active EP's and reset the feature(s) value(s) */ |
|
//list_for_each_entry(cfiep, &cfi->active_eps, lh) { |
|
DWC_LIST_FOREACH(tmp, &cfi->active_eps) { |
|
cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); |
|
retval = cfi_reset_concat_val(cfiep); |
|
if (retval < 0) { |
|
CFI_INFO |
|
("%s: Error resetting the feature Concatenation Value\n", |
|
__func__); |
|
return retval; |
|
} |
|
} |
|
} |
|
return retval; |
|
} |
|
|
|
static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr) |
|
{ |
|
int retval = 0; |
|
cfi_ep_t *cfiep; |
|
cfiobject_t *cfi = pcd->cfi; |
|
dwc_list_link_t *tmp; |
|
|
|
/* If the EP address is known then reset the features for only that EP */ |
|
if (addr) { |
|
cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == cfiep) { |
|
CFI_INFO("%s: Error getting the EP address 0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
retval = cfi_reset_align_val(cfiep); |
|
} |
|
/* Otherwise (wValue == 0), reset all features of all EP's */ |
|
else { |
|
/* Traverse all the active EP's and reset the feature(s) value(s) */ |
|
//list_for_each_entry(cfiep, &cfi->active_eps, lh) { |
|
DWC_LIST_FOREACH(tmp, &cfi->active_eps) { |
|
cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); |
|
retval = cfi_reset_align_val(cfiep); |
|
if (retval < 0) { |
|
CFI_INFO |
|
("%s: Error resetting the feature Aliignment Value\n", |
|
__func__); |
|
return retval; |
|
} |
|
} |
|
} |
|
return retval; |
|
|
|
} |
|
|
|
static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req) |
|
{ |
|
int retval = 0; |
|
|
|
switch (req->wIndex) { |
|
case 0: |
|
/* Reset all features */ |
|
retval = cfi_handle_reset_all(pcd, req->wValue & 0xff); |
|
break; |
|
|
|
case FT_ID_DMA_BUFFER_SETUP: |
|
/* Reset the SG buffer setup */ |
|
retval = |
|
cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff); |
|
break; |
|
|
|
case FT_ID_DMA_CONCAT_SETUP: |
|
/* Reset the Concatenation buffer setup */ |
|
retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff); |
|
break; |
|
|
|
case FT_ID_DMA_BUFF_ALIGN: |
|
/* Reset the Alignment buffer setup */ |
|
retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff); |
|
break; |
|
|
|
case FT_ID_TX_FIFO_DEPTH: |
|
retval = |
|
cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1); |
|
pcd->cfi->need_gadget_att = 0; |
|
break; |
|
|
|
case FT_ID_RX_FIFO_DEPTH: |
|
retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0); |
|
pcd->cfi->need_gadget_att = 0; |
|
break; |
|
default: |
|
break; |
|
} |
|
return retval; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the SG buffer setup. |
|
*/ |
|
static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd) |
|
{ |
|
uint8_t inaddr, outaddr; |
|
cfi_ep_t *epin, *epout; |
|
ddma_sg_buffer_setup_t *psgval; |
|
uint32_t desccount, size; |
|
|
|
CFI_INFO("%s\n", __func__); |
|
|
|
psgval = (ddma_sg_buffer_setup_t *) buf; |
|
desccount = (uint32_t) psgval->bCount; |
|
size = (uint32_t) psgval->wSize; |
|
|
|
/* Check the DMA descriptor count */ |
|
if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) { |
|
CFI_INFO |
|
("%s: The count of DMA Descriptors should be between 1 and %d\n", |
|
__func__, MAX_DMA_DESCS_PER_EP); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
/* Check the DMA descriptor count */ |
|
|
|
if (size == 0) { |
|
|
|
CFI_INFO("%s: The transfer size should be at least 1 byte\n", |
|
__func__); |
|
|
|
return -DWC_E_INVALID; |
|
|
|
} |
|
|
|
inaddr = psgval->bInEndpointAddress; |
|
outaddr = psgval->bOutEndpointAddress; |
|
|
|
epin = get_cfi_ep_by_addr(pcd->cfi, inaddr); |
|
epout = get_cfi_ep_by_addr(pcd->cfi, outaddr); |
|
|
|
if (NULL == epin || NULL == epout) { |
|
CFI_INFO |
|
("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n", |
|
__func__, inaddr, outaddr); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
epin->ep->dwc_ep.buff_mode = BM_SG; |
|
dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); |
|
|
|
epout->ep->dwc_ep.buff_mode = BM_SG; |
|
dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the buffer Alignment setup. |
|
*/ |
|
static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd) |
|
{ |
|
cfi_ep_t *ep; |
|
uint8_t addr; |
|
ddma_align_buffer_setup_t *palignval; |
|
|
|
palignval = (ddma_align_buffer_setup_t *) buf; |
|
addr = palignval->bEndpointAddress; |
|
|
|
ep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
|
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
ep->ep->dwc_ep.buff_mode = BM_ALIGN; |
|
dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t)); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the Concatenation buffer setup. |
|
*/ |
|
static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd) |
|
{ |
|
uint8_t addr; |
|
cfi_ep_t *ep; |
|
struct _ddma_concat_buffer_setup_hdr *pConcatValHdr; |
|
uint16_t *pVals; |
|
uint32_t desccount; |
|
int i; |
|
uint16_t mps; |
|
|
|
pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf; |
|
desccount = (uint32_t) pConcatValHdr->bDescCount; |
|
pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN); |
|
|
|
/* Check the DMA descriptor count */ |
|
if (desccount > MAX_DMA_DESCS_PER_EP) { |
|
CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n", |
|
__func__, MAX_DMA_DESCS_PER_EP); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
addr = pConcatValHdr->bEndpointAddress; |
|
ep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", |
|
__func__, addr); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
mps = UGETW(ep->ep->desc->wMaxPacketSize); |
|
|
|
#if 0 |
|
for (i = 0; i < desccount; i++) { |
|
CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]); |
|
} |
|
CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps); |
|
#endif |
|
|
|
/* Check the wTxSizes to be less than or equal to the mps */ |
|
for (i = 0; i < desccount; i++) { |
|
if (pVals[i] > mps) { |
|
CFI_INFO |
|
("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n", |
|
__func__, i, pVals[i]); |
|
return -DWC_E_INVALID; |
|
} |
|
} |
|
|
|
ep->ep->dwc_ep.buff_mode = BM_CONCAT; |
|
dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN); |
|
|
|
/* Free the previously allocated storage for the wTxBytes */ |
|
if (ep->bm_concat->wTxBytes) { |
|
DWC_FREE(ep->bm_concat->wTxBytes); |
|
} |
|
|
|
/* Allocate a new storage for the wTxBytes field */ |
|
ep->bm_concat->wTxBytes = |
|
DWC_ALLOC(sizeof(uint16_t) * pConcatValHdr->bDescCount); |
|
if (NULL == ep->bm_concat->wTxBytes) { |
|
CFI_INFO("%s: Unable to allocate memory\n", __func__); |
|
return -DWC_E_NO_MEMORY; |
|
} |
|
|
|
/* Copy the new values into the wTxBytes filed */ |
|
dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN, |
|
sizeof(uint16_t) * pConcatValHdr->bDescCount); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* This function calculates the total of all FIFO sizes |
|
* |
|
* @param core_if Programming view of DWC_otg controller |
|
* |
|
* @return The total of data FIFO sizes. |
|
* |
|
*/ |
|
static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if) |
|
{ |
|
dwc_otg_core_params_t *params = core_if->core_params; |
|
uint16_t dfifo_total = 0; |
|
int i; |
|
|
|
/* The shared RxFIFO size */ |
|
dfifo_total = |
|
params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; |
|
|
|
/* Add up each TxFIFO size to the total */ |
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
dfifo_total += params->dev_tx_fifo_size[i]; |
|
} |
|
|
|
return dfifo_total; |
|
} |
|
|
|
/** |
|
* This function returns Rx FIFO size |
|
* |
|
* @param core_if Programming view of DWC_otg controller |
|
* |
|
* @return The total of data FIFO sizes. |
|
* |
|
*/ |
|
static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue) |
|
{ |
|
switch (wValue >> 8) { |
|
case 0: |
|
return (core_if->pwron_rxfsiz < |
|
32768) ? core_if->pwron_rxfsiz : 32768; |
|
break; |
|
case 1: |
|
return core_if->core_params->dev_rx_fifo_size; |
|
break; |
|
default: |
|
return -DWC_E_INVALID; |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* This function returns Tx FIFO size for IN EP |
|
* |
|
* @param core_if Programming view of DWC_otg controller |
|
* |
|
* @return The total of data FIFO sizes. |
|
* |
|
*/ |
|
static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue) |
|
{ |
|
dwc_otg_pcd_ep_t *ep; |
|
|
|
ep = get_ep_by_addr(pcd, wValue & 0xff); |
|
|
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", |
|
__func__, wValue & 0xff); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
if (!ep->dwc_ep.is_in) { |
|
CFI_INFO |
|
("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n", |
|
__func__, wValue & 0xff); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
switch (wValue >> 8) { |
|
case 0: |
|
return (GET_CORE_IF(pcd)->pwron_txfsiz |
|
[ep->dwc_ep.tx_fifo_num - 1] < |
|
768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep-> |
|
dwc_ep.tx_fifo_num |
|
- 1] : 32768; |
|
break; |
|
case 1: |
|
return GET_CORE_IF(pcd)->core_params-> |
|
dev_tx_fifo_size[ep->dwc_ep.num - 1]; |
|
break; |
|
default: |
|
return -DWC_E_INVALID; |
|
break; |
|
} |
|
} |
|
|
|
/** |
|
* This function checks if the submitted combination of |
|
* device mode FIFO sizes is possible or not. |
|
* |
|
* @param core_if Programming view of DWC_otg controller |
|
* |
|
* @return 1 if possible, 0 otherwise. |
|
* |
|
*/ |
|
static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if) |
|
{ |
|
uint16_t dfifo_actual = 0; |
|
dwc_otg_core_params_t *params = core_if->core_params; |
|
uint16_t start_addr = 0; |
|
int i; |
|
|
|
dfifo_actual = |
|
params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
dfifo_actual += params->dev_tx_fifo_size[i]; |
|
} |
|
|
|
if (dfifo_actual > core_if->total_fifo_size) { |
|
return 0; |
|
} |
|
|
|
if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16) |
|
return 0; |
|
|
|
if (params->dev_nperio_tx_fifo_size > 32768 |
|
|| params->dev_nperio_tx_fifo_size < 16) |
|
return 0; |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
|
|
if (params->dev_tx_fifo_size[i] > 768 |
|
|| params->dev_tx_fifo_size[i] < 4) |
|
return 0; |
|
} |
|
|
|
if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz) |
|
return 0; |
|
start_addr = params->dev_rx_fifo_size; |
|
|
|
if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz) |
|
return 0; |
|
start_addr += params->dev_nperio_tx_fifo_size; |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
|
|
if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i]) |
|
return 0; |
|
start_addr += params->dev_tx_fifo_size[i]; |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
/** |
|
* This function resizes Device mode FIFOs |
|
* |
|
* @param core_if Programming view of DWC_otg controller |
|
* |
|
* @return 1 if successful, 0 otherwise |
|
* |
|
*/ |
|
static uint8_t resize_fifos(dwc_otg_core_if_t * core_if) |
|
{ |
|
int i = 0; |
|
dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; |
|
dwc_otg_core_params_t *params = core_if->core_params; |
|
uint32_t rx_fifo_size; |
|
fifosize_data_t nptxfifosize; |
|
fifosize_data_t txfifosize[15]; |
|
|
|
uint32_t rx_fsz_bak; |
|
uint32_t nptxfsz_bak; |
|
uint32_t txfsz_bak[15]; |
|
|
|
uint16_t start_address; |
|
uint8_t retval = 1; |
|
|
|
if (!check_fifo_sizes(core_if)) { |
|
return 0; |
|
} |
|
|
|
/* Configure data FIFO sizes */ |
|
if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { |
|
rx_fsz_bak = DWC_READ_REG32(&global_regs->grxfsiz); |
|
rx_fifo_size = params->dev_rx_fifo_size; |
|
DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fifo_size); |
|
|
|
/* |
|
* Tx FIFOs These FIFOs are numbered from 1 to 15. |
|
* Indexes of the FIFO size module parameters in the |
|
* dev_tx_fifo_size array and the FIFO size registers in |
|
* the dtxfsiz array run from 0 to 14. |
|
*/ |
|
|
|
/* Non-periodic Tx FIFO */ |
|
nptxfsz_bak = DWC_READ_REG32(&global_regs->gnptxfsiz); |
|
nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; |
|
start_address = params->dev_rx_fifo_size; |
|
nptxfifosize.b.startaddr = start_address; |
|
|
|
DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfifosize.d32); |
|
|
|
start_address += nptxfifosize.b.depth; |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
txfsz_bak[i] = DWC_READ_REG32(&global_regs->dtxfsiz[i]); |
|
|
|
txfifosize[i].b.depth = params->dev_tx_fifo_size[i]; |
|
txfifosize[i].b.startaddr = start_address; |
|
DWC_WRITE_REG32(&global_regs->dtxfsiz[i], |
|
txfifosize[i].d32); |
|
|
|
start_address += txfifosize[i].b.depth; |
|
} |
|
|
|
/** Check if register values are set correctly */ |
|
if (rx_fifo_size != DWC_READ_REG32(&global_regs->grxfsiz)) { |
|
retval = 0; |
|
} |
|
|
|
if (nptxfifosize.d32 != DWC_READ_REG32(&global_regs->gnptxfsiz)) { |
|
retval = 0; |
|
} |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
if (txfifosize[i].d32 != |
|
DWC_READ_REG32(&global_regs->dtxfsiz[i])) { |
|
retval = 0; |
|
} |
|
} |
|
|
|
/** If register values are not set correctly, reset old values */ |
|
if (retval == 0) { |
|
DWC_WRITE_REG32(&global_regs->grxfsiz, rx_fsz_bak); |
|
|
|
/* Non-periodic Tx FIFO */ |
|
DWC_WRITE_REG32(&global_regs->gnptxfsiz, nptxfsz_bak); |
|
|
|
for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { |
|
DWC_WRITE_REG32(&global_regs->dtxfsiz[i], |
|
txfsz_bak[i]); |
|
} |
|
} |
|
} else { |
|
return 0; |
|
} |
|
|
|
/* Flush the FIFOs */ |
|
dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ |
|
dwc_otg_flush_rx_fifo(core_if); |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the buffer Alignment setup. |
|
*/ |
|
static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) |
|
{ |
|
int retval; |
|
uint32_t fsiz; |
|
uint16_t size; |
|
uint16_t ep_addr; |
|
dwc_otg_pcd_ep_t *ep; |
|
dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; |
|
tx_fifo_size_setup_t *ptxfifoval; |
|
|
|
ptxfifoval = (tx_fifo_size_setup_t *) buf; |
|
ep_addr = ptxfifoval->bEndpointAddress; |
|
size = ptxfifoval->wDepth; |
|
|
|
ep = get_ep_by_addr(pcd, ep_addr); |
|
|
|
CFI_INFO |
|
("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n", |
|
__func__, ep_addr, size, ep->dwc_ep.tx_fifo_num); |
|
|
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", |
|
__func__, ep_addr); |
|
return -DWC_E_INVALID; |
|
} |
|
|
|
fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1]; |
|
params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size; |
|
|
|
if (resize_fifos(GET_CORE_IF(pcd))) { |
|
retval = 0; |
|
} else { |
|
CFI_INFO |
|
("%s: Error setting the feature Tx FIFO Size for EP%d\n", |
|
__func__, ep_addr); |
|
params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz; |
|
retval = -DWC_E_INVALID; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the buffer Alignment setup. |
|
*/ |
|
static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) |
|
{ |
|
int retval; |
|
uint32_t fsiz; |
|
uint16_t size; |
|
dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; |
|
rx_fifo_size_setup_t *prxfifoval; |
|
|
|
prxfifoval = (rx_fifo_size_setup_t *) buf; |
|
size = prxfifoval->wDepth; |
|
|
|
fsiz = params->dev_rx_fifo_size; |
|
params->dev_rx_fifo_size = size; |
|
|
|
if (resize_fifos(GET_CORE_IF(pcd))) { |
|
retval = 0; |
|
} else { |
|
CFI_INFO("%s: Error setting the feature Rx FIFO Size\n", |
|
__func__); |
|
params->dev_rx_fifo_size = fsiz; |
|
retval = -DWC_E_INVALID; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function reads the SG of an EP's buffer setup into the buffer buf |
|
*/ |
|
static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req) |
|
{ |
|
int retval = -DWC_E_INVALID; |
|
uint8_t addr; |
|
cfi_ep_t *ep; |
|
|
|
/* The Low Byte of the wValue contains a non-zero address of the endpoint */ |
|
addr = req->wValue & 0xFF; |
|
if (addr == 0) /* The address should be non-zero */ |
|
return retval; |
|
|
|
ep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", |
|
__func__, addr); |
|
return retval; |
|
} |
|
|
|
dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN); |
|
retval = BS_SG_VAL_DESC_LEN; |
|
return retval; |
|
} |
|
|
|
/** |
|
* This function reads the Concatenation value of an EP's buffer mode into |
|
* the buffer buf |
|
*/ |
|
static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req) |
|
{ |
|
int retval = -DWC_E_INVALID; |
|
uint8_t addr; |
|
cfi_ep_t *ep; |
|
uint8_t desc_count; |
|
|
|
/* The Low Byte of the wValue contains a non-zero address of the endpoint */ |
|
addr = req->wValue & 0xFF; |
|
if (addr == 0) /* The address should be non-zero */ |
|
return retval; |
|
|
|
ep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", |
|
__func__, addr); |
|
return retval; |
|
} |
|
|
|
/* Copy the header to the buffer */ |
|
dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN); |
|
/* Advance the buffer pointer by the header size */ |
|
buf += BS_CONCAT_VAL_HDR_LEN; |
|
|
|
desc_count = ep->bm_concat->hdr.bDescCount; |
|
/* Copy alll the wTxBytes to the buffer */ |
|
dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count); |
|
|
|
retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count; |
|
return retval; |
|
} |
|
|
|
/** |
|
* This function reads the buffer Alignment value of an EP's buffer mode into |
|
* the buffer buf |
|
* |
|
* @return The total number of bytes copied to the buffer or negative error code. |
|
*/ |
|
static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, |
|
struct cfi_usb_ctrlrequest *req) |
|
{ |
|
int retval = -DWC_E_INVALID; |
|
uint8_t addr; |
|
cfi_ep_t *ep; |
|
|
|
/* The Low Byte of the wValue contains a non-zero address of the endpoint */ |
|
addr = req->wValue & 0xFF; |
|
if (addr == 0) /* The address should be non-zero */ |
|
return retval; |
|
|
|
ep = get_cfi_ep_by_addr(pcd->cfi, addr); |
|
if (NULL == ep) { |
|
CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", |
|
__func__, addr); |
|
return retval; |
|
} |
|
|
|
dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN); |
|
retval = BS_ALIGN_VAL_HDR_LEN; |
|
|
|
return retval; |
|
} |
|
|
|
/** |
|
* This function sets a new value for the specified feature |
|
* |
|
* @param pcd A pointer to the PCD object |
|
* |
|
* @return 0 if successful, negative error code otherwise to stall the DCE. |
|
*/ |
|
static int cfi_set_feature_value(struct dwc_otg_pcd *pcd) |
|
{ |
|
int retval = -DWC_E_NOT_SUPPORTED; |
|
uint16_t wIndex, wValue; |
|
uint8_t bRequest; |
|
struct dwc_otg_core_if *coreif; |
|
cfiobject_t *cfi = pcd->cfi; |
|
struct cfi_usb_ctrlrequest *ctrl_req; |
|
uint8_t *buf; |
|
ctrl_req = &cfi->ctrl_req; |
|
|
|
buf = pcd->cfi->ctrl_req.data; |
|
|
|
coreif = GET_CORE_IF(pcd); |
|
bRequest = ctrl_req->bRequest; |
|
wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); |
|
wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); |
|
|
|
/* See which feature is to be modified */ |
|
switch (wIndex) { |
|
case FT_ID_DMA_BUFFER_SETUP: |
|
/* Modify the feature */ |
|
if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0) |
|
return retval; |
|
|
|
/* And send this request to the gadget */ |
|
cfi->need_gadget_att = 1; |
|
break; |
|
|
|
case FT_ID_DMA_BUFF_ALIGN: |
|
if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0) |
|
return retval; |
|
cfi->need_gadget_att = 1; |
|
break; |
|
|
|
case FT_ID_DMA_CONCAT_SETUP: |
|
/* Modify the feature */ |
|
if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0) |
|
return retval; |
|
cfi->need_gadget_att = 1; |
|
break; |
|
|
|
case FT_ID_DMA_CIRCULAR: |
|
CFI_INFO("FT_ID_DMA_CIRCULAR\n"); |
|
break; |
|
|
|
case FT_ID_THRESHOLD_SETUP: |
|
CFI_INFO("FT_ID_THRESHOLD_SETUP\n"); |
|
break; |
|
|
|
case FT_ID_DFIFO_DEPTH: |
|
CFI_INFO("FT_ID_DFIFO_DEPTH\n"); |
|
break; |
|
|
|
case FT_ID_TX_FIFO_DEPTH: |
|
CFI_INFO("FT_ID_TX_FIFO_DEPTH\n"); |
|
if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0) |
|
return retval; |
|
cfi->need_gadget_att = 0; |
|
break; |
|
|
|
case FT_ID_RX_FIFO_DEPTH: |
|
CFI_INFO("FT_ID_RX_FIFO_DEPTH\n"); |
|
if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0) |
|
return retval; |
|
cfi->need_gadget_att = 0; |
|
break; |
|
} |
|
|
|
return retval; |
|
} |
|
|
|
#endif //DWC_UTE_CFI
|
|
|