Copy xhci functions first. We should redevelop xhci functions step by step
This commit is contained in:
parent
01a92a269c
commit
303737e590
|
@ -91,4 +91,448 @@ static inline struct xhci_host* xhci_get_inst_of_port(struct usbh_hubport *hport
|
|||
return (struct xhci_host*)usb->priv;
|
||||
}
|
||||
|
||||
static struct xhci_host xhci_host[CONFIG_USBHOST_XHCI_NUM];
|
||||
|
||||
/* xhci hardware init */
|
||||
int usb_hc_init(uint32_t id)
|
||||
{
|
||||
USB_ASSERT(id < CONFIG_USBHOST_XHCI_NUM);
|
||||
int rc = 0;
|
||||
struct usbh_bus *bus = usbh_get_bus_of_index(id);
|
||||
USB_ASSERT(bus);
|
||||
struct xhci_host *xhci = &(xhci_host[id]);
|
||||
|
||||
size_t flag = usb_osal_enter_critical_section(); /* no interrupt when init hc */
|
||||
|
||||
usb_hc_low_level_init(id); /* set gic and memp */
|
||||
|
||||
memset(xhci, 0, sizeof(*xhci));
|
||||
xhci->bus = bus;
|
||||
bus->priv = xhci;
|
||||
if (rc = xhci_probe(xhci, usb_hc_get_register_base(id)) != 0) {
|
||||
goto err_open;
|
||||
}
|
||||
|
||||
if (rc = xhci_open(xhci) != 0 ) {
|
||||
goto err_open;
|
||||
}
|
||||
|
||||
err_open:
|
||||
usb_osal_leave_critical_section(flag);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int usbh_roothub_control(struct usbh_bus *usb, struct usb_setup_packet *setup, uint8_t *buf)
|
||||
{
|
||||
uint8_t nports;
|
||||
uint8_t port;
|
||||
uint32_t portsc;
|
||||
uint32_t status;
|
||||
int ret = 0;
|
||||
struct xhci_host *xhci = usb->priv;
|
||||
nports = CONFIG_USBHOST_MAX_RHPORTS;
|
||||
|
||||
port = setup->wIndex;
|
||||
|
||||
/*
|
||||
bmRequestType bit[4:0], define whether the request is directed to the device (0000b),
|
||||
specific interface (00001b), endpoint (00010b), or other element (00011b)
|
||||
bRequest, identifies the request
|
||||
wValue, pass the request-specific info to the device
|
||||
*/
|
||||
if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) /* request is directed to device */
|
||||
{
|
||||
switch (setup->bRequest)
|
||||
{
|
||||
case HUB_REQUEST_CLEAR_FEATURE: /* disable the feature */
|
||||
switch (setup->wValue)
|
||||
{
|
||||
case HUB_FEATURE_HUB_C_LOCALPOWER:
|
||||
USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n");
|
||||
break;
|
||||
case HUB_FEATURE_HUB_C_OVERCURRENT:
|
||||
USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n");
|
||||
break;
|
||||
default:
|
||||
return -EPIPE;
|
||||
}
|
||||
break;
|
||||
case HUB_REQUEST_SET_FEATURE: /* set a value reported in the hub status */
|
||||
switch (setup->wValue)
|
||||
{
|
||||
case HUB_FEATURE_HUB_C_LOCALPOWER:
|
||||
USB_LOG_ERR("HUB_FEATURE_HUB_C_LOCALPOWER not implmented.\n");
|
||||
break;
|
||||
case HUB_FEATURE_HUB_C_OVERCURRENT:
|
||||
USB_LOG_ERR("HUB_FEATURE_HUB_C_OVERCURRENT not implmented.\n");
|
||||
break;
|
||||
default:
|
||||
return -EPIPE;
|
||||
}
|
||||
break;
|
||||
case HUB_REQUEST_GET_DESCRIPTOR:
|
||||
USB_LOG_ERR("HUB_REQUEST_GET_DESCRIPTOR not implmented.\n");
|
||||
break;
|
||||
case HUB_REQUEST_GET_STATUS:
|
||||
USB_ASSERT(buf);
|
||||
memset(buf, 0, 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER)
|
||||
{
|
||||
switch (setup->bRequest)
|
||||
{
|
||||
case HUB_REQUEST_CLEAR_FEATURE:
|
||||
if (!port || port > nports)
|
||||
{
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) );
|
||||
switch (setup->wValue)
|
||||
{
|
||||
case HUB_PORT_FEATURE_ENABLE:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_SUSPEND:
|
||||
case HUB_PORT_FEATURE_C_SUSPEND:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_POWER:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_C_CONNECTION:
|
||||
portsc |= XHCI_PORTSC_CSC;
|
||||
break;
|
||||
case HUB_PORT_FEATURE_C_ENABLE:
|
||||
portsc |= XHCI_PORTSC_PEC;
|
||||
break;
|
||||
case HUB_PORT_FEATURE_C_OVER_CURREN:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_C_RESET:
|
||||
break;
|
||||
default:
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
uint32_t pclear = portsc & XHCI_PORTSC_RW_MASK;
|
||||
/* clear port status */
|
||||
writel(pclear, xhci->op + XHCI_OP_PORTSC ( port ));
|
||||
|
||||
break;
|
||||
case HUB_REQUEST_SET_FEATURE:
|
||||
if (!port || port > nports)
|
||||
{
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
switch (setup->wValue)
|
||||
{
|
||||
case HUB_PORT_FEATURE_SUSPEND:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_POWER:
|
||||
break;
|
||||
case HUB_PORT_FEATURE_RESET:
|
||||
ret = xhci_port_enable(xhci, port);
|
||||
break;
|
||||
default:
|
||||
return -EPIPE;
|
||||
}
|
||||
break;
|
||||
case HUB_REQUEST_GET_STATUS:
|
||||
if (!port || port > nports)
|
||||
{
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) );
|
||||
status = 0;
|
||||
|
||||
if (portsc & XHCI_PORTSC_CSC)
|
||||
{
|
||||
/* Port connection status changed */
|
||||
status |= (1 << HUB_PORT_FEATURE_C_CONNECTION);
|
||||
|
||||
/* always clear all the status change bits */
|
||||
uint32_t temp = portsc & ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE );
|
||||
writel ( temp, xhci->op + XHCI_OP_PORTSC ( port ) );
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_PEC)
|
||||
{
|
||||
/* Port enabled status changed */
|
||||
status |= (1 << HUB_PORT_FEATURE_C_ENABLE);
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_OCC)
|
||||
{
|
||||
/* Port status changed due to over-current */
|
||||
status |= (1 << HUB_PORT_FEATURE_C_OVER_CURREN);
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_CCS)
|
||||
{
|
||||
/* Port connected */
|
||||
status |= (1 << HUB_PORT_FEATURE_CONNECTION);
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_PED)
|
||||
{
|
||||
/* Port enabled */
|
||||
status |= (1 << HUB_PORT_FEATURE_ENABLE);
|
||||
|
||||
const unsigned int speed = xhci_root_speed(xhci, port);
|
||||
USB_LOG_DBG("Port-%d speed = %d \r\n", port, speed);
|
||||
if (speed == USB_SPEED_LOW)
|
||||
{
|
||||
status |= (1 << HUB_PORT_FEATURE_LOWSPEED);
|
||||
}
|
||||
else if (speed == USB_SPEED_HIGH)
|
||||
{
|
||||
status |= (1 << HUB_PORT_FEATURE_HIGHSPEED);
|
||||
}
|
||||
/* else is full-speed */
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_OCA)
|
||||
{
|
||||
/* Over-current condition */
|
||||
status |= (1 << HUB_PORT_FEATURE_OVERCURRENT);
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_PR)
|
||||
{
|
||||
/* Reset is in progress */
|
||||
status |= (1 << HUB_PORT_FEATURE_RESET);
|
||||
}
|
||||
|
||||
if (portsc & XHCI_PORTSC_PP)
|
||||
{
|
||||
/* Port is not power off */
|
||||
status |= (1 << HUB_PORT_FEATURE_POWER);
|
||||
}
|
||||
memcpy(buf, &status, 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint8_t usbh_get_port_speed(struct usbh_hub *hub, const uint8_t port)
|
||||
{
|
||||
USB_ASSERT(hub);
|
||||
struct usbh_bus *usb = hub->usb;
|
||||
USB_ASSERT(usb && usb->priv);
|
||||
struct xhci_host *xhci = usb->priv;
|
||||
|
||||
if (hub->is_roothub) {
|
||||
return xhci_root_speed(xhci, port);
|
||||
} else {
|
||||
struct usbh_hubport *roothub_port = usbh_root_hub_port(&(hub->child[port]));
|
||||
/* TODO, hanlde TT for low-speed device on high-speed hub, but it doesn't matter, since
|
||||
we haven't well handle hub yet */
|
||||
USB_ASSERT(roothub_port);
|
||||
return xhci_root_speed(xhci, roothub_port->port);
|
||||
}
|
||||
}
|
||||
|
||||
int usbh_ep_pipe_reconfigure(struct usbh_bus *usb, usbh_pipe_t pipe, uint8_t dev_addr, uint8_t mtu, uint8_t speed)
|
||||
{
|
||||
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
|
||||
size_t old_mtu = ppipe->mtu;
|
||||
int rc = 0;
|
||||
|
||||
if (mtu != old_mtu) {
|
||||
ppipe->mtu = mtu;
|
||||
rc = xhci_endpoint_mtu(ppipe);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg)
|
||||
{
|
||||
int rc = 0;
|
||||
int slot_id = 0;
|
||||
struct xhci_host *xhci = xhci_get_inst_of_port(ep_cfg->hport);
|
||||
struct usbh_hubport *hport = ep_cfg->hport;
|
||||
struct xhci_endpoint *ppipe = usb_align(XHCI_RING_SIZE, sizeof(struct xhci_endpoint));
|
||||
struct xhci_slot *slot;
|
||||
|
||||
if (NULL == ppipe) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(ppipe, 0, sizeof(struct xhci_endpoint));
|
||||
|
||||
ppipe->waitsem = usb_osal_sem_create(0);
|
||||
ppipe->waiter = false;
|
||||
ppipe->urb = NULL;
|
||||
ppipe->hport = hport;
|
||||
|
||||
ppipe->address = ep_cfg->ep_addr;
|
||||
ppipe->mtu = ep_cfg->ep_mps;
|
||||
ppipe->interval = ep_cfg->ep_interval;
|
||||
ppipe->ep_type = ep_cfg->ep_type;
|
||||
ppipe->burst = 0U;
|
||||
|
||||
if (ppipe->address == 0) { /* if try to allocate ctrl ep, open device first */
|
||||
USB_LOG_DBG("allocate device for port-%d \r\n", hport->port);
|
||||
rc = xhci_device_open(xhci, ppipe, &slot_id);
|
||||
if (rc) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
slot = xhci->slot[slot_id];
|
||||
USB_ASSERT(slot);
|
||||
rc = xhci_ctrl_endpoint_open(xhci, slot, ppipe);
|
||||
if (rc) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = xhci_device_address(xhci, slot, ppipe);
|
||||
if (rc) {
|
||||
goto failed;
|
||||
}
|
||||
} else {
|
||||
slot_id = hport->dev_addr;
|
||||
slot = xhci->slot[slot_id];
|
||||
|
||||
rc = xhci_work_endpoint_open(xhci, slot, ppipe);
|
||||
if (rc) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
*pipe = (usbh_pipe_t)ppipe;
|
||||
return rc;
|
||||
failed:
|
||||
usb_free(ppipe);
|
||||
*pipe = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int usbh_get_xhci_devaddr(usbh_pipe_t *pipe)
|
||||
{
|
||||
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
|
||||
USB_ASSERT(ppipe && (ppipe->slot));
|
||||
return ppipe->slot->id;
|
||||
}
|
||||
|
||||
int usbh_pipe_free(usbh_pipe_t pipe)
|
||||
{
|
||||
int ret = 0;
|
||||
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)pipe;
|
||||
|
||||
if (!ppipe) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct usbh_urb *urb = ppipe->urb;
|
||||
if (urb) {
|
||||
usbh_kill_urb(urb);
|
||||
}
|
||||
|
||||
size_t flags = usb_osal_enter_critical_section();
|
||||
xhci_endpoint_close(ppipe);
|
||||
usb_osal_leave_critical_section(flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbh_submit_urb(struct usbh_urb *urb)
|
||||
{
|
||||
int ret = 0;
|
||||
if (!urb || !urb->pipe) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct xhci_endpoint *ppipe = (struct xhci_endpoint *)urb->pipe;
|
||||
struct xhci_host *xhci = ppipe->xhci;
|
||||
struct usb_setup_packet *setup = urb->setup;
|
||||
size_t flags = usb_osal_enter_critical_section();
|
||||
|
||||
urb->errorcode = -EBUSY;
|
||||
urb->actual_length = 0U;
|
||||
|
||||
ppipe->urb = urb;
|
||||
ppipe->timeout = urb->timeout;
|
||||
if (ppipe->timeout > 0) {
|
||||
ppipe->waiter = true;
|
||||
} else {
|
||||
ppipe->waiter = false;
|
||||
}
|
||||
|
||||
usb_osal_leave_critical_section(flags);
|
||||
switch (ppipe->ep_type) {
|
||||
case USB_ENDPOINT_TYPE_CONTROL:
|
||||
USB_ASSERT(setup);
|
||||
if (setup->bRequest == USB_REQUEST_SET_ADDRESS) {
|
||||
/* Set address command sent during xhci_alloc_pipe. */
|
||||
goto skip_req;
|
||||
}
|
||||
|
||||
USB_LOG_DBG("%s request-%d.\n", __func__, setup->bRequest);
|
||||
xhci_endpoint_message(ppipe, setup, urb->transfer_buffer, urb->transfer_buffer_length);
|
||||
break;
|
||||
case USB_ENDPOINT_TYPE_INTERRUPT:
|
||||
case USB_ENDPOINT_TYPE_BULK:
|
||||
xhci_endpoint_stream(ppipe, urb->transfer_buffer, urb->transfer_buffer_length);
|
||||
break;
|
||||
default:
|
||||
USB_ASSERT(0U);
|
||||
break;
|
||||
}
|
||||
|
||||
/* wait all ring handled by xHc */
|
||||
int cc = xhci_event_wait(xhci, ppipe, &ppipe->reqs);
|
||||
if ((cc != XHCI_CMPLT_SUCCESS) &&
|
||||
!((cc == XHCI_CMPLT_TIMEOUT) && (ppipe->ep_type == USB_ENDPOINT_TYPE_INTERRUPT)))
|
||||
{
|
||||
/* ignore transfer timeout for interrupt type */
|
||||
USB_LOG_ERR("%s: xfer failed (cc %d).\n", __func__, cc);
|
||||
ret = -1;
|
||||
urb->errorcode = cc;
|
||||
goto errout_timeout;
|
||||
}
|
||||
|
||||
skip_req:
|
||||
errout_timeout:
|
||||
/* Timeout will run here */
|
||||
usbh_kill_urb(urb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_kill_urb(struct usbh_urb *urb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void USBH_IRQHandler(void *param)
|
||||
{
|
||||
USB_ASSERT(param);
|
||||
struct usbh_bus *bus = (struct usbh_bus *)param;
|
||||
struct xhci_host *xhci = bus->priv;
|
||||
USB_ASSERT(xhci);
|
||||
uint32_t usbsts;
|
||||
uint32_t runtime;
|
||||
|
||||
/* clear interrupt status */
|
||||
usbsts = readl ( xhci->op + XHCI_OP_USBSTS );
|
||||
usbsts |= XHCI_USBSTS_EINT;
|
||||
writel(usbsts, xhci->op + XHCI_OP_USBSTS);
|
||||
|
||||
/* ack interrupt */
|
||||
runtime = readl(xhci->run + XHCI_RUN_IR_IMAN ( 0 ));
|
||||
runtime |= XHCI_RUN_IR_IMAN_IP;
|
||||
writel (runtime, xhci->run + XHCI_RUN_IR_IMAN ( 0 ));
|
||||
|
||||
(void)xhci_event_process(xhci);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue