您的位置:首页 > 其它

uboot usb ehci 控制传输

2012-11-22 15:38 696 查看
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 中增加一定的延时问题则不再出现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: