赞
踩
USB是Universal Serial Bus的缩写,USB是一种简易、双向、快速、同步、即插即用(Plug and Play,PnP)且支持热插拔功能的串行接口
USB出现之前,计算机领域中的接口太多太繁杂,普通用户使用起来难度较大,接口之间的兼容性也较差
USB设计理念是,实现计算机设备和通讯设备的完美融合,支持即插即用不需要用户手动配置,提高接口扩展性在不同应用上使用统一的接口,总的来说,USB的出现,是希望通过此单个的USB接口,同时支持多种不同的应用,而且用户用起来也很方便,直接插上就能用了,也方便不同的设备的之间的互联
USB的优点包括,
USB设备,从物理上的逻辑结构来说,包含了主机Host端和设备Device端, 其中,主机Host端,有对应的硬件的USB的主机控制器Host Controller,而设备端,连接的是对应的USB设备
下面就来简单的介绍一下不同的USB接口类型,即各种不同的插头插座:
USB的接口类型,根据接口形状不同,主要可以分为三大类:
其中每一种大类中,又都可以分为两类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xhgOFU4T-1665644089017)(figsif_types.png)]
USB枚举,USB Emulation,对应的就是USB的Host和Device之间的对话,即Host根据Device所报告上来的参数,得知USB的device是啥类型的,具有啥功能,然后初始化相关参数,接下来,就USB Device就可以正常工作了,下面是关于windows下USB枚举的过程的总结:
USB协议中定义了四种传输(Transfer)类型
USB 描述符,USB协议本身很复杂,但方便在提供了统一的接口方式,这种方便的一个重要原因是 USB 协议规范了一套通信用的数据结构,也就是 USB 描述符,标准的USB设备有5种USB描述符:设备描述符,配置描述符,字符串描述符,接口描述符,端点描述符,USB标准化组织和各厂商根据特定的通信需要,还扩展了一系列描述符定义
USB 通信包,USB 2.0协议中,USB包由SOP,SYNC,Packet内容和EOP组成,SOP是起始包,SYNC是同步域,采用NRZI编码,Packet内容包括PID(包ID),地址,帧号,数据和CRC校验,最后EOP是结束包,不同的Packet内容构成令牌包、握手包和数据包等,多个包组合形成事务,是USB传输的基础
目录名 | 描述 |
---|---|
class | usb class 类主从驱动 |
common | usb spec 定义、常用宏、标准接口定义 |
core | usb 主从协议栈核心实现 |
demo | 示例 |
docs | 文档 |
osal | os 封装层 |
packet capture | 抓包文件(需要使用力科软件打开) |
port | usb 主从需要实现的 porting 接口 |
tools | 工具链接 |
.
├── class
├── common
├── core
├── demo
├── docs
├── osal
├── packet capture
└── port
└── tools
struct usbh_devaddr_map { /** * alloctab[0]:addr from 0~31 * alloctab[1]:addr from 32~63 * alloctab[2]:addr from 64~95 * alloctab[3]:addr from 96~127 * */ uint8_t next; /* Next device address */ uint32_t alloctab[4]; /* Bit allocation table */ }; struct usbh_bus { struct usbh_devaddr_map devgen; } g_usbh_bus; int usbh_initialize(void) { memset(&g_usbh_bus, 0, sizeof(struct usbh_bus)); (1) extern uint32_t __usbh_class_info_start__; extern uint32_t __usbh_class_info_end__; usbh_class_info_table_begin = (struct usbh_class_info *)&__usbh_class_info_start__; usbh_class_info_table_end = (struct usbh_class_info *)&__usbh_class_info_end__; (2) /* devaddr 1 is for roothub */ g_usbh_bus.devgen.next = 2; (3) usbh_hub_initialize(); return 0; }
int usbh_hub_initialize(void) { usbh_roothub_register(); hub_event_wait = usb_osal_sem_create(0); if (hub_event_wait == NULL) { return -1; } hub_thread = usb_osal_thread_create("usbh_hub", CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, NULL); if (hub_thread == NULL) { return -1; } return 0; } static void usbh_hub_thread(void *argument) { size_t flags; int ret = 0; usb_hc_init(); (1) while (1) { ret = usb_osal_sem_take(hub_event_wait, 0xffffffff); (2) if (ret < 0) { continue; } while (!usb_slist_isempty(&hub_event_head)) { (3) struct usbh_hub *hub = usb_slist_first_entry(&hub_event_head, struct usbh_hub, hub_event_list); flags = usb_osal_enter_critical_section(); usb_slist_remove(&hub_event_head, &hub->hub_event_list); usb_osal_leave_critical_section(flags); usbh_hub_events(hub); } } }
void USBH_IRQHandler(void) { uint32_t reg, status; struct xhci_s *xhci = &xhci_host; struct xhci_ring *evts = xhci->evts; struct xhci_pipe *work_pipe = NULL; ... /* check and ack event */ for (;;) { (1) uint32_t nidx = evts->nidx; /* index of dequeue trb */ uint32_t cs = evts->cs; /* cycle state toggle by xHc */ struct xhci_trb *etrb = evts->ring + nidx; /* current trb */ uint32_t control = etrb->control; /* trb control field */ if ((control & TRB_C) != (cs ? 1 : 0)) /* if cycle state not toggle, no events need to handle */ break; /* process event on etrb */ uint32_t evt_type = TRB_TYPE_GET(control); uint32_t evt_cc = TRB_CC_GET(etrb->status); /* bit[31:24] completion code */ if (ER_PORT_STATUS_CHANGE == evt_type) { (2) /* bit[31:24] port id, the port num of root hub port that generated this event */ uint32_t port = TRB_PORT_ID_GET(etrb->ptr_low) - 1; uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); /* Read status */ xhci_print_port_state(__func__, port, portsc); if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { (3) usbh_roothub_thread_wakeup(port + 1); /* wakeup when connection status changed */ }
static void usbh_hub_events(struct usbh_hub *hub) { 。。。 /* Read hub port status */ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); (1) ... /* Last, check connect status */ if (portstatus & HUB_PORT_STATUS_CONNECTION) { ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET); (2) if (ret < 0) { USB_LOG_ERR("Failed to reset port %u,errorcode:%d\r\n", port, ret); continue; } usb_osal_msleep(DELAY_TIME_AFTER_RESET); /* Read hub port status */ ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); if (ret < 0) { USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret); continue; } portstatus = port_status.wPortStatus; portchange = port_status.wPortChange; if (!(portstatus & HUB_PORT_STATUS_RESET) && (portstatus & HUB_PORT_STATUS_ENABLE)) { if (portchange & HUB_PORT_STATUS_C_RESET) { ret = usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_RESET); if (ret < 0) { USB_LOG_ERR("Failed to clear port %u reset change, errorcode: %d\r\n", port, ret); } } if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) { (3) speed = USB_SPEED_HIGH; } else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) { speed = USB_SPEED_LOW; } else { speed = USB_SPEED_FULL; } child = &hub->child[port]; memset(child, 0, sizeof(struct usbh_hubport)); child->parent = hub; child->connected = true; child->port = port + 1; child->speed = speed; USB_LOG_INFO("New %s device on Hub %u, Port %u connected\r\n", speed_table[speed], hub->index, port + 1); if (usbh_enumerate(child) < 0) { (4) USB_LOG_ERR("Port %u enumerate fail\r\n", port + 1); } } else { USB_LOG_ERR("Failed to enable port %u\r\n", port + 1); continue; } } else { ... }
int usbh_enumerate(struct usbh_hubport *hport) { ... /* Configure EP0 with the default maximum packet size */ usbh_hport_activate_ep0(hport); (1) } static int usbh_get_default_mps(int speed) { switch (speed) { case USB_SPEED_LOW: /* For low speed, we use 8 bytes */ return 8; case USB_SPEED_FULL: /* For full or high speed, we use 64 bytes */ case USB_SPEED_HIGH: return 64; case USB_SPEED_SUPER: /* For super speed , we must use 512 bytes */ case USB_SPEED_SUPER_PLUS: return 512; default: return 64; } } int usbh_hport_activate_ep0(struct usbh_hubport *hport) { struct usbh_endpoint_cfg ep0_cfg = { 0 }; ep0_cfg.ep_addr = 0x00; ep0_cfg.ep_interval = 0x00; ep0_cfg.ep_mps = usbh_get_default_mps(hport->speed); (2) ep0_cfg.ep_type = USB_ENDPOINT_TYPE_CONTROL; (3) ep0_cfg.hport = hport; (4) usbh_pipe_alloc(&hport->ep0, &ep0_cfg); (5) return 0; }
/* Read the first 8 bytes of the device descriptor */ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); setup->wIndex = 0; setup->wLength = 8; (1) ret = usbh_control_transfer(hport->ep0, setup, ep0_request_buffer); if (ret < 0) { USB_LOG_ERR("Failed to get device descriptor,errorcode:%d\r\n", ret); goto errout; } parse_device_descriptor(hport, (struct usb_device_descriptor *)ep0_request_buffer, 8); /* Extract the correct max packetsize from the device descriptor */ ep_mps = ((struct usb_device_descriptor *)ep0_request_buffer)->bMaxPacketSize0; (2) /* Reconfigure EP0 with the correct maximum packet size */ usbh_ep0_pipe_reconfigure(hport->ep0, 0, ep_mps, hport->speed); (3)
#ifdef CONFIG_USBHOST_XHCI extern int usbh_get_xhci_devaddr(usbh_pipe_t * pipe); (1) /* Assign a function address to the device connected to this port */ dev_addr = usbh_get_xhci_devaddr(hport->ep0); if (dev_addr < 0) { USB_LOG_ERR("Failed to allocate devaddr,errorcode:%d\r\n", ret); goto errout; } #else /* Assign a function address to the device connected to this port */ dev_addr = usbh_allocate_devaddr(&g_usbh_bus.devgen); (2) if (dev_addr < 0) { USB_LOG_ERR("Failed to allocate devaddr,errorcode:%d\r\n", ret); goto errout; } #endif /* Set the USB device address */ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_SET_ADDRESS; setup->wValue = dev_addr; setup->wIndex = 0; setup->wLength = 0; ret = usbh_control_transfer(hport->ep0, setup, NULL); (3) if (ret < 0) { USB_LOG_ERR("Failed to set devaddr,errorcode:%d\r\n", ret); goto errout; }
/* Read the full device descriptor */ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_DEVICE << 8) | 0); setup->wIndex = 0; setup->wLength = USB_SIZEOF_DEVICE_DESC; ret = usbh_control_transfer(hport->ep0, setup, ep0_request_buffer); (1) if (ret < 0) { USB_LOG_ERR("Failed to get full device descriptor,errorcode:%d\r\n", ret); goto errout; } ... /* Read the first 9 bytes of the config descriptor */ setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; setup->wValue = (uint16_t)((USB_DESCRIPTOR_TYPE_CONFIGURATION << 8) | 0); setup->wIndex = 0; setup->wLength = USB_SIZEOF_CONFIG_DESC; ret = usbh_control_transfer(hport->ep0, setup, ep0_request_buffer); (2) if (ret < 0) { USB_LOG_ERR("Failed to get config descriptor,errorcode:%d\r\n", ret); goto errout; } ... /* Select device configuration 1 */ setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_DEVICE; setup->bRequest = USB_REQUEST_SET_CONFIGURATION; setup->wValue = 1; (3) setup->wIndex = 0; setup->wLength = 0; ret = usbh_control_transfer(hport->ep0, setup, NULL); if (ret < 0) { USB_LOG_ERR("Failed to set configuration,errorcode:%d\r\n", ret); goto errout; }
/*search supported class driver*/ for (uint8_t i = 0; i < hport->config.config_desc.bNumInterfaces; i++) { (1) intf_desc = &hport->config.intf[i].altsetting[0].intf_desc; (2) struct usbh_class_driver *class_driver = (struct usbh_class_driver *)usbh_find_class_driver(intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol, hport->device_desc.idVendor, hport->device_desc.idProduct); 3() if (class_driver == NULL) { (4) USB_LOG_ERR("do not support Class:0x%02x,Subclass:0x%02x,Protocl:0x%02x\r\n", intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol); continue; } hport->config.intf[i].class_driver = class_driver; USB_LOG_INFO("Loading %s class driver\r\n", class_driver->driver_name); ret = CLASS_CONNECT(hport, i); (5) if (ret < 0) { ret = CLASS_DISCONNECT(hport, i); goto errout; } }
const struct usbh_class_driver msc_class_driver = {
.driver_name = "msc",
.connect = usbh_msc_connect, (1)
.disconnect = usbh_msc_disconnect(2)
};
CLASS_INFO_DEFINE const struct usbh_class_info msc_class_info = {
.match_flags = USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS | USB_CLASS_MATCH_INTF_PROTOCOL, (1)
.class = USB_DEVICE_CLASS_MASS_STORAGE,
.subclass = MSC_SUBCLASS_SCSI,
.protocol = MSC_PROTOCOL_BULK_ONLY,
.vid = 0x00,
.pid = 0x00,
.class_driver = &msc_class_driver(2)
};
#define DEV_FORMAT "/dev/sd%c" (7) int usbh_hport_activate_epx(usbh_pipe_t *pipe, struct usbh_hubport *hport, struct usb_endpoint_descriptor *ep_desc) { struct usbh_endpoint_cfg ep_cfg = { 0 }; ep_cfg.ep_addr = ep_desc->bEndpointAddress; ep_cfg.ep_type = ep_desc->bmAttributes & USB_ENDPOINT_TYPE_MASK; ep_cfg.ep_mps = ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_MASK; ep_cfg.ep_interval = ep_desc->bInterval; ep_cfg.mult = (ep_desc->wMaxPacketSize & USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_MASK) >> USB_MAXPACKETSIZE_ADDITIONAL_TRANSCATION_SHIFT; ep_cfg.hport = hport; USB_LOG_INFO("Ep=%02x Attr=%02u Mps=%d Interval=%02u Mult=%02u\r\n", ep_cfg.ep_addr, ep_desc->bmAttributes, ep_cfg.ep_mps, ep_cfg.ep_interval, ep_cfg.mult); return usbh_pipe_alloc(pipe, &ep_cfg); } static int usbh_msc_connect(struct usbh_hubport *hport, uint8_t intf) { struct usb_endpoint_descriptor *ep_desc; int ret; struct usbh_msc *msc_class = usb_malloc(sizeof(struct usbh_msc)); (1) if (msc_class == NULL) { USB_LOG_ERR("Fail to alloc msc_class\r\n"); return -ENOMEM; } memset(msc_class, 0, sizeof(struct usbh_msc)); usbh_msc_devno_alloc(msc_class); msc_class->hport = hport; (2) msc_class->intf = intf;(3) hport->config.intf[intf].priv = msc_class; ret = usbh_msc_get_maxlun(msc_class, g_msc_buf); (4) if (ret < 0) { return ret; } USB_LOG_INFO("Get max LUN:%u\r\n", g_msc_buf[0] + 1); for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) { ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc; if (ep_desc->bEndpointAddress & 0x80) { usbh_hport_activate_epx(&msc_class->bulkin, hport, ep_desc); (5) } else { usbh_hport_activate_epx(&msc_class->bulkout, hport, ep_desc); } } ret = usbh_msc_scsi_testunitready(msc_class); (6) if (ret < 0) { USB_LOG_ERR("Fail to scsi_testunitready\r\n"); return ret; } ret = usbh_msc_scsi_inquiry(msc_class); if (ret < 0) { USB_LOG_ERR("Fail to scsi_inquiry\r\n"); return ret; } ret = usbh_msc_scsi_readcapacity10(msc_class); (7) if (ret < 0) { USB_LOG_ERR("Fail to scsi_readcapacity10\r\n"); return ret; } if (msc_class->blocksize > 0) { USB_LOG_INFO("Capacity info:\r\n"); USB_LOG_INFO("Block num:%d,block size:%d\r\n", (unsigned int)msc_class->blocknum, (unsigned int)msc_class->blocksize); } else { USB_LOG_ERR("Invalid block size\r\n"); return -ERANGE; } snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, msc_class->sdchar); USB_LOG_INFO("Register MSC Class:%s\r\n", hport->config.intf[intf].devname); return ret; }
struct usbh_msc *msc_class = (struct usbh_msc *)usbh_find_class_instance("/dev/sda"); (1) if (msc_class == NULL) { USB_LOG_RAW("do not find /dev/sda\r\n"); goto err_exit; } /* get the partition table */ ret = usbh_msc_scsi_read10(msc_class, 0, partition_table, 1); (2) if (ret < 0) { USB_LOG_RAW("scsi_read10 error,ret:%d\r\n", ret); goto err_exit; } ret = usbh_msc_scsi_write10(msc_class, 0, partition_table, 1); (3) if (ret < 0) { USB_LOG_RAW("scsi_write10 error,ret:%d\r\n", ret); goto err_exit; }
XHCI,即可扩展的主机控制器接口,是英特尔公司开发的一个USB主机控制器接口,它主要是面向USB 3.0,同时也支持USB 2.0及以下的设备,是UHCI/OHCI/EHCI等接口标准的升级版本
XHCI USB协议栈中有下列组件
XHCI接口架构主要包括三大部分,
XHCI控制器用的数据结构均要求首地址按64字节对齐,部分数据结构要求处于一个4096字节页中
CherryUSB 中适配 HOST 控制器需要实现以下接口
usb_hc_init
用于初始化 usb host controller 寄存器,设置 usb 引脚、时钟、中断等等。 此函数不对用户开放。
int usb_hc_init(void);
usbh_roothub_control
用来对 roothub 发起请求, 此函数不对用户开放。 int usbh_roothub_control(struct usb_setup_packet *setup, uint8_t *buf);
-usbh_ep0_pipe_reconfigure
重新设置端点 0 的 pipe 属性。 此函数不对用户开放。
int usbh_ep0_pipe_reconfigure(usbh_pipe_t pipe, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed);
usbh_pipe_alloc
为端点分配 pipe。 此函数不对用户开放。
int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg);
usbh_pipe_free
释放端点的一些属性。 此函数不对用户开放。
int usbh_pipe_free(usbh_pipe_t pipe);
usbh_submit_urb
对某个地址上的端点进行数据请求。 此函数对用户开放。
int usbh_submit_urb(struct usbh_urb *urb);
其中, urb
结构体信息如下:
struct usbh_urb {
usbh_pipe_t pipe;
struct usb_setup_packet *setup;
uint8_t *transfer_buffer;
uint32_t transfer_buffer_length;
int transfer_flags;
uint32_t actual_length;
uint32_t timeout;
int errorcode;
uint32_t num_of_iso_packets;
usbh_complete_callback_t complete;
void *arg;
struct usbh_iso_frame_packet iso_packet[];
};
int usb_hc_init(void)
{
usb_hc_low_level_init(); (1)
memset(&xhci_host, 0, sizeof(xhci_host));
if (xhci_controller_setup(&xhci_host, CONFIG_XHCI_BASE_ADDR)) (2)
return -1;
return 0;
}
static void xhci_setup_mmio(struct xhci_s *xhci, unsigned long base_addr) { xhci->base = base_addr; xhci->caps = xhci->base; /* add to register base to find the beginning of the Operational Register Space */ xhci->op = xhci->base + readb(xhci->caps + XHCI_REG_CAP_CAPLENGTH); (2) xhci->db = xhci->base + XHCI_REG_CAP_DBOFF_GET(readl(xhci->caps + XHCI_REG_CAP_DBOFF)); xhci->ir = xhci->base + XHCI_REG_CAP_RTSOFF_GET(readl(xhci->caps + XHCI_REG_CAP_RTSOFF)) + XHCI_REG_RT_IR0; xhci->pr = xhci->op + XHCI_REG_OP_PORTS_BASE; .... } static int xhci_controller_setup(struct xhci_s *xhci, unsigned long baseaddr) { USB_LOG_INFO("%s@0x%x\n", __func__, baseaddr); /* get register offset */ xhci_setup_mmio(xhci, baseaddr); (1) if (xhci->version < 0x96 || xhci->version > 0x120) { USB_LOG_ERR("xHCI-0x%x not support\n", xhci->version); return -1; } ... USB_LOG_INFO("config XHCI ....\n"); if (xhci_controller_configure(xhci)) { (3) USB_LOG_ERR("init XHCI failed !!!\n"); return -1; } else { USB_LOG_INFO("init XHCI success !!!\n"); } }
static int xhci_controller_configure(struct xhci_s *xhci) { uint32_t reg; /* trbs */ (1) xhci->devs = usb_align(XHCI_ALIGMENT, sizeof(*xhci->devs) * (xhci->slots + 1)); /* device slot */(2) xhci->eseg = usb_align(XHCI_ALIGMENT, sizeof(*xhci->eseg)); /* event segment */(3) xhci->cmds = usb_align(XHCI_RING_SIZE, sizeof(*xhci->cmds)); /* command ring */(4) xhci->evts = usb_align(XHCI_RING_SIZE, sizeof(*xhci->evts)); /* event ring */ ... reg = readl(xhci->op + XHCI_REG_OP_USBCMD); (5) if (reg & XHCI_REG_OP_USBCMD_RUN_STOP) { /* if xHc running, stop it first */ reg &= ~XHCI_REG_OP_USBCMD_RUN_STOP; writel(xhci->op + XHCI_REG_OP_USBCMD, reg); /* stop xHc */ if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, XHCI_REG_OP_USBSTS_HCH, XHCI_REG_OP_USBSTS_HCH, 32) != 0) /* wait xHc halt */ goto fail; } USB_LOG_DBG("%s: resetting\n", __func__); writel(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST); /* reset xHc */ (6) if (wait_bit(xhci->op + XHCI_REG_OP_USBCMD, XHCI_REG_OP_USBCMD_HCRST, 0, 1000) != 0) /* wait reset process done */ goto fail; if (wait_bit(xhci->op + XHCI_REG_OP_USBSTS, XHCI_REG_OP_USBSTS_CNR, 0, 1000) != 0) /* wait until xHc ready */ goto fail;
/* enable port interrupt */ writel(xhci->ir + XHCI_REG_RT_IR_IMOD, 500U); reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); reg |= XHCI_REG_RT_IR_IMAN_IE; writel(xhci->ir + + XHCI_REG_RT_IR_IMAN, reg); (1) reg = readl(xhci->op + XHCI_REG_OP_USBCMD); reg |= XHCI_REG_OP_USBCMD_INTE; /* enable interrupt */ writel(xhci->op + XHCI_REG_OP_USBCMD, reg);(2) USB_LOG_DBG("Start xHc ....\n"); reg = readl(xhci->op + XHCI_REG_OP_USBCMD); reg |= XHCI_REG_OP_USBCMD_RUN_STOP; /* start xHc */ (3) writel(xhci->op + XHCI_REG_OP_USBCMD, reg); return 0U; /* Success */
void USBH_IRQHandler(void) { uint32_t reg, status; struct xhci_s *xhci = &xhci_host; struct xhci_ring *evts = xhci->evts; struct xhci_pipe *work_pipe = NULL; USB_LOG_DBG("%s\n", __func__); status = readl(xhci->op + XHCI_REG_OP_USBSTS); status |= XHCI_REG_OP_USBSTS_EINT; /* clear interrupt status */ writel(xhci->op + XHCI_REG_OP_USBSTS, status); (1) reg = readl(xhci->ir + XHCI_REG_RT_IR_IMAN); reg |= XHCI_REG_RT_IR_IMAN_IP; /* ack interrupt */ writel(xhci->ir + XHCI_REG_RT_IR_IMAN, reg); (2) /* check and ack event */ for (;;) { uint32_t nidx = evts->nidx; /* index of dequeue trb */ uint32_t cs = evts->cs; /* cycle state toggle by xHc */ struct xhci_trb *etrb = evts->ring + nidx; /* current trb */ uint32_t control = etrb->control; /* trb control field */ if ((control & TRB_C) != (cs ? 1 : 0)) /* if cycle state not toggle, no events need to handle */(4) break; ... if (ER_PORT_STATUS_CHANGE == evt_type) { /* bit[31:24] port id, the port num of root hub port that generated this event */ uint32_t port = TRB_PORT_ID_GET(etrb->ptr_low) - 1; uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); /* Read status */ xhci_print_port_state(__func__, port, portsc); if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) { (3) usbh_roothub_thread_wakeup(port + 1); /* wakeup when connection status changed */ } } ... /* move ring index, notify xhci */ nidx++; /* head to next trb */ (5) if (nidx == XHCI_RING_ITEMS) { nidx = 0; /* roll-back if reach end of list */ cs = cs ? 0 : 1; evts->cs = cs; /* sw toggle cycle state */ } evts->nidx = nidx; uint64_t erdp = (uint64_t)(evts->ring + nidx); writeq(xhci->ir + XHCI_REG_RT_IR_ERDP, erdp | XHCI_REG_RT_IR_ERDP_EHB); /* bit[63:4] update current event ring dequeue pointer */ }
int usbh_pipe_alloc(usbh_pipe_t *pipe, const struct usbh_endpoint_cfg *ep_cfg) { ... /* Allocate input context and initialize endpoint info. */ struct xhci_inctx *in = xhci_alloc_inctx_config_ep(xhci, hport, epid); (1) if (!in){ ret = -1; goto fail; } if (ppipe->epid == 1) { /* when allocate ep-0, allocate device first */ ... int slotid = xhci_cmd_enable_slot(xhci, ppipe); /* get slot-id */ (2) if (slotid < 0) { USB_LOG_ERR("%s: enable slot: failed\n", __func__); usb_free(dev); ret = -1; goto fail; } ... /* Send set_address command. */ int cc = xhci_cmd_address_device(xhci, ppipe, in); (3) } else { /* Send configure command. */ cc = xhci_cmd_configure_endpoint(xhci, ppipe, in, false); (4) }
void USBH_IRQHandler(void) { ... else if ((ER_COMMAND_COMPLETE == evt_type) || (ER_TRANSFER_COMPLETE == evt_type)) { struct xhci_trb *rtrb = (void*)(unsigned long)etrb->ptr_low; struct xhci_ring *ring = XHCI_RING(rtrb); /* to align addr is ring base */ struct xhci_trb *evt = &ring->evt; /* first event trb */ uint32_t eidx = rtrb - ring->ring + 1; /* calculate current evt trb index */ memcpy(evt, etrb, sizeof(*etrb)); /* copy current trb to cmd/transfer ring */ (4) ring->eidx = eidx; ... } /* Submit a command to the xhci controller ring */ static int xhci_cmd_submit(struct xhci_s *xhci, struct xhci_inctx *inctx, struct xhci_pipe *pipe, uint32_t flags) { ... usb_osal_mutex_take(xhci->cmds->lock); /* handle command one by one */ (2) pipe->timeout = 5000; pipe->waiter = true; cur_cmd_pipe = pipe; xhci_trb_queue(xhci->cmds, inctx, 0, flags); (3) /* pass command trb to hardware */ DSB(); xhci_doorbell(xhci, 0, 0); /* 0 = db host controller, 0 = db targe hc command */ int rc = xhci_event_wait(xhci, pipe, xhci->cmds); (5) usb_osal_mutex_give(xhci->cmds->lock); } static int xhci_cmd_enable_slot(struct xhci_s *xhci, struct xhci_pipe *pipe) { int cc = xhci_cmd_submit(xhci, NULL, pipe, TRB_TYPE_SET(CR_ENABLE_SLOT)); (1) if (cc != CC_SUCCESS) return cc; struct xhci_trb *evt = &(xhci->cmds->evt); return TRB_CR_SLOTID_GET(evt->control); /* bit [31:24] slot id */ (6) }
/* Signal the hardware to process events on a TRB ring */ static void xhci_doorbell(struct xhci_s *xhci, uint32_t slotid, uint32_t value) { writel(xhci->db + slotid * XHCI_REG_DB_SIZE, value); /* bit[7:0] db target, is ep_id */ (4) } /* Submit a USB transfer request to the pipe's ring */ static void xhci_xfer_normal(struct xhci_s *xhci, struct xhci_pipe *pipe, void *data, int datalen) { /* Normal trb, used in bulk and interrupt transfer */ xhci_trb_queue(&pipe->reqs, data, datalen, TRB_TYPE_SET(TR_NORMAL) | TRB_TR_IOC); (3) /* pass command trb to hardware */ DSB(); xhci_doorbell(xhci, pipe->slotid, pipe->epid); } int usbh_submit_urb(struct usbh_urb *urb) { ... switch (ppipe->eptype){ (1) case USB_ENDPOINT_TYPE_CONTROL: USB_ASSERT(setup); if (setup->bRequest == USB_REQUEST_SET_ADDRESS){ (2) /* Set address command sent during xhci_alloc_pipe. */ goto skip_req; } USB_LOG_DBG("%s request-%d\n", __func__, setup->bRequest); /* send setup in/out for command */ xhci_xfer_setup(xhci, ppipe, setup->bmRequestType & USB_EP_DIR_IN, (void*)setup, urb->transfer_buffer, urb->transfer_buffer_length); break; case USB_ENDPOINT_TYPE_INTERRUPT: case USB_ENDPOINT_TYPE_BULK: xhci_xfer_normal(xhci, ppipe, urb->transfer_buffer, urb->transfer_buffer_length); break; default: USB_ASSERT(0U); break; } ... }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。