uboot usb ehci 控制传输

来源:互联网 发布:成都java平均工资 编辑:程序博客网 时间:2024/05/22 14:53
1.问题陈述
在添加uboot中增加一个function后,uboot中usb driver出现无法发送控制命令的问题。
2.如何追述问题原因
通过usb 分析仪进行usb总线上发包分析,分析仪显示在设备初始化阶段并没有检测到有效的packet发送。
直接追究其原因是有没有正确将qh于qtd传递到内存中, 接下来贴上一些有关qh意识qtd初始化的代码
1.
int
submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
  int length, struct devrequest *setup)
{


if (usb_pipetype(pipe) != PIPE_CONTROL) {
debug("non-control pipe (type=%lu)", usb_pipetype(pipe));
return -1;
}


if (usb_pipedevice(pipe) == rootdev) {
if (rootdev == 0)
dev->speed = USB_SPEED_HIGH;
return ehci_submit_root(dev, pipe, buffer, length, setup);
}
return ehci_submit_async(dev, pipe, buffer, length, setup);
}


这个函数很简单只是判定是否是root hub,现在我们分析的设备并不是root hub dev所以我先直接进入ehci_submit_async进行分析,随后我们在回头看看如果是root dev的情况
这个函数非常长,但是还是比较容易读懂的,接下来我们逐行进行分析
static int
ehci_submit_async(struct usb_device *dev, unsigned long pipe, void *buffer,
  int length, struct devrequest *req)
{
struct QH *qh;
struct qTD *td;
struct qTD *tdp;
volatile struct qTD *vtd;
volatile struct qTD *pvtd;
unsigned long ts;
//uint32_t *tdp;
uint32_t endpt, token, usbsts;
uint32_t c, toggle;
uint32_t cmd;
int ret = 0;


// TODO - revert printf to debug
debug("dev=%p, pipe=%lx, buffer=%p, length=%d, req=%p\n", dev, pipe,
     buffer, length, req);
if (req != NULL)
debug("req=%u (%#x), type=%u (%#x), value=%u (%#x), index=%u\n",
     req->request, req->request,
     req->requesttype, req->requesttype,
     le16_to_cpu(req->value), le16_to_cpu(req->value),
     le16_to_cpu(req->index));


qh = ehci_alloc(sizeof(struct QH), 32); // 分配一个qh
if (qh == NULL) {
debug("unable to allocate QH\n");
return -1;
}


/* Add it to the little chain */
qh_list.qh_list = qh; //qh_list 是一个全局变量,async执行的起始地址是指向该qh_ist,这是一个无效的qh,这里我们将qh_list视为一个链表头指针

qh->qh_list = NULL;
qh->qtd_list = NULL;
/*初始化qh*/
qh->qh_link = cpu_to_hc32((uint32_t)qh_list.qh_dma | QH_LINK_TYPE_QH); //qh 指向qh链表头指针
c = (usb_pipespeed(pipe) != USB_SPEED_HIGH &&
    usb_pipeendpoint(pipe) == 0) ? 1 : 0;
endpt = (8 << 28) |
   (c << 27) |
   (usb_maxpacket(dev, pipe) << 16) |
   (0 << 15) |
   (1 << 14) |
   (usb_pipespeed(pipe) << 12) |
   (usb_pipeendpoint(pipe) << 8) |
   (0 << 7) | (usb_pipedevice(pipe) << 0);
qh->qh_endpt1 = cpu_to_hc32(endpt);
endpt = (1 << 30) |
   (dev->portnr << 23) |
   (dev->parent->devnum << 16) | (0 << 8) | (0 << 0);
qh->qh_endpt2 = cpu_to_hc32(endpt);
qh->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
qh->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);


tdp = NULL;

toggle = usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
/*control msg 至少有2个包  第一个是setup,第二个是data (如果有data的话)第三个握手包 */
if (req != NULL) {
td = ehci_alloc(sizeof(struct qTD), 32);
if (td == NULL) {
debug("unable to allocate SETUP td\n");
goto fail;
}

if (qh->qtd_list == NULL) {
qh->qtd_list = td;
qh->qh_overlay.qt_next = td->qt_dma;//qh 的overlay部分指向第一个setup qtd
}


if (tdp != NULL) {
tdp->qt_next = cpu_to_hc32(td->qt_dma);
tdp->qtd_list = td;
}

tdp = td; //这个是qtd的指针chain
td->qtd_list = NULL;

td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (0 << 31) |
   (sizeof(*req) << 16) |
   (0 << 15) | (0 << 12) | (3 << 10) | (2 << 8) | (0x80 << 0);
td->qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(td, req, sizeof(*req)) != 0) {
debug("unable construct SETUP td\n");
ehci_free(td, sizeof(*td));
goto fail;
}


toggle = 1;
}




if (length > 0 || req == NULL) {
td = ehci_alloc(sizeof(struct qTD), 32);
if (td == NULL) {
printf("unable to allocate DATA td\n"); // TODO puts -> debug
goto fail;
}


if (qh->qtd_list == NULL) {
qh->qtd_list = td;
qh->qh_overlay.qt_next = td->qt_dma;
}

if (tdp != NULL) {
tdp->qt_next = cpu_to_hc32(td->qt_dma);  //tpd 现在是指向的是setup 包 ,赋值操作将data qtd的地址存放在set up qtd中
tdp->qtd_list = td;
}

tdp = td;
td->qtd_list = NULL;

td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
   (length << 16) |
   ((req == NULL ? 1 : 0) << 15) |
   (0 << 12) |
   (3 << 10) |
   ((usb_pipein(pipe) ? 1 : 0) << 8) | (0x80 << 0);
td->qt_token = cpu_to_hc32(token);
if (ehci_td_buffer(td, buffer, length) != 0) {
puts("unable construct DATA td\n"); // TODO puts -> debug
ehci_free(td, sizeof(*td));
goto fail;
}
}


if (req != NULL) {
td = ehci_alloc(sizeof(struct qTD), 32);
if (td == NULL) {
puts("unable to allocate ACK td\n"); // TODO puts -> debug
goto fail;
}


if (qh->qtd_list == NULL) {
qh->qtd_list = td;
qh->qh_overlay.qt_next = td->qt_dma;
}


if (tdp != NULL) {
tdp->qt_next = cpu_to_hc32(td->qt_dma);
tdp->qtd_list = td;
}

tdp = td;
td->qtd_list = NULL;

td->qt_next = cpu_to_hc32(QT_NEXT_TERMINATE);
td->qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE);
token = (toggle << 31) |
   (0 << 16) |
   (1 << 15) |
   (0 << 12) |
   (3 << 10) |
   ((usb_pipein(pipe) ? 0 : 1) << 8) | (0x80 << 0);
td->qt_token = cpu_to_hc32(token);

td->qt_buffer[0] = 0;
td->qt_buffer[1] = 0;
td->qt_buffer[2] = 0;
td->qt_buffer[3] = 0;
td->qt_buffer[4] = 0;

}


qh_list.qh_link = cpu_to_hc32((uint32_t)qh->qh_dma | QH_LINK_TYPE_QH); //qh链表头指针的next 物理地址指针指向当前qh的物理地址


ehci_flush_qh(&qh_list); // flush cache

dump_qh (&qh_list, 0);


usbsts = ehci_readl(&hcor->or_usbsts);
ehci_writel(&hcor->or_usbsts, (usbsts & 0x3f)); //清除状态寄存器


/* Enable async. schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd |= CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);


ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, STD_ASS,200 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS set\n");
goto fail;
}


//printf("ip9028: asynclistaddr=%08x\n", ehci_readl(&hcor->or_asynclistaddr));


/* Wait for TDs to be processed. */
ts = get_timer(0);


vtd = td;
pvtd = td->qt_dma;

do { //轮询等待 数据包发送完毕
/* Invalidate dcache */
ehci_inv_qh(&qh_list);
#ifdef DEBUG
printf("TOKEN=%#x %x %#x %x\n", vtd->qt_token, vtd, pvtd->qt_token, pvtd);
#endif


if (!(vtd->qt_token & 0x80)) {
#ifdef DEBUG
printf("END-TOKEN=%#x\n", vtd->qt_token);
#endif
break;
}
} while (get_timer(ts) < CONFIG_SYS_HZ);


/* Disable async schedule. */
cmd = ehci_readl(&hcor->or_usbcmd);
cmd &= ~CMD_ASE;
ehci_writel(&hcor->or_usbcmd, cmd);


//printf("ip9028: asynclistaddr=%08x\n", ehci_readl(&hcor->or_asynclistaddr));


ret = handshake((uint32_t *)&hcor->or_usbsts, STD_ASS, 0, 200 * 1000);
if (ret < 0) {
printf("EHCI fail timeout STD_ASS reset\n");
goto fail;
}


//printf("ip9028: asynclistaddr=%08x\n", ehci_readl(&hcor->or_asynclistaddr));


dump_qh (&qh_list, 0);


qh_list.qh_link = cpu_to_hc32((uint32_t)qh_list.qh_dma | QH_LINK_TYPE_QH);


token = hc32_to_cpu(qh->qh_overlay.qt_token);
if (!(token & 0x80)) { //分析当前的的stat 
debug("TOKEN=%#x\n", token);
switch (token & 0xfc) {
case 0:
toggle = token >> 31;
usb_settoggle(dev, usb_pipeendpoint(pipe),
      usb_pipeout(pipe), toggle);
dev->status = 0;
break;
case 0x40:
//debug("USB_ST_STALLED\n");
puts("USB_ST_STALLED\n");
dev->status = USB_ST_STALLED;
break;
case 0xa0:
case 0x20:
//debug("USB_ST_BUF_ERR\n");
puts("USB_ST_BUF_ERR\n");
dev->status = USB_ST_BUF_ERR;
break;
case 0x50:
case 0x10:
//debug("USB_ST_BABBLE_DET\n");
puts("USB_ST_BABBLE_DET\n");
dev->status = USB_ST_BABBLE_DET;
break;
default:
//debug("USB_ST_CRC_ERR\n");
puts("USB_ST_CRC_ERR\n");
dev->status = USB_ST_CRC_ERR;
break;
}
dev->act_len = length - ((token >> 16) & 0x7fff);
} else {
dev->act_len = 0;
debug("dev=%u, usbsts=%#x, p[1]=%#x, p[2]=%#x\n",
     dev->devnum, ehci_readl(&hcor->or_usbsts),
     ehci_readl(&hcor->or_portsc[0]),
     ehci_readl(&hcor->or_portsc[1]));
}


return (dev->status != USB_ST_NOT_PROC) ? 0 : -1;


fail:
td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
while (td != (void *)QT_NEXT_TERMINATE) {
qh->qh_overlay.qt_next = td->qt_next;
ehci_free(td, sizeof(*td));
td = (void *)hc32_to_cpu(qh->qh_overlay.qt_next);
}
ehci_free(qh, sizeof(*qh));

return -1;
}


代码解析完毕,在实验当中发现,在启动异步传输之前,有一段时间延时便可以成功的发送数据包,
对于当前情况唯一的解释是:qh和qtd flush 在启动异步传输的时候没有及时的flush到ddr中。在flush data 中增加一定的延时问题则不再出现
原创粉丝点击