📄 hc_sl811.c
字号:
/* if ((td->ctrl & SL811_USB_CTRL_TOGGLE_1) != (td->status & SL811_USB_STS_TOGGLE_1)) {
PDEBUG(2, "dev %d endpoint %d unexpect data toggle!", dev, ep);
td->td_status = -EILSEQ;
}
*/
if (!(td->ctrl & SL811_USB_CTRL_DIR_OUT) && td->len > 0)
sl811_read_buf(hc, td->addr, td->buf, td->len - td->left);
if (td->left && (urb->transfer_flags & USB_DISABLE_SPD)) {
PDEBUG(2, "dev %d endpoint %d unexpect short packet! td = %p", dev, ep, td);
td->td_status = -EREMOTEIO;
} else
td->td_status = 0;
} else if (td->status & SL811_USB_STS_STALL) {
PDEBUG(2, "dev %d endpoint %d halt, td = %p", dev, ep, td);
td->td_status = -EPIPE;
if (urb->dev)
usb_endpoint_halt(td->urb->dev, usb_pipeendpoint(td->urb->pipe), usb_pipeout(td->urb->pipe));
td->done = 1;
} else if (td->status & SL811_USB_STS_OVERFLOW) {
PDEBUG(1, "dev %d endpoint %d overflow, sl811 only support packet less than %d", dev, ep, SL811_DATA_LIMIT);
td->td_status = -EOVERFLOW;
td->done = 1;
} else if (td->status & SL811_USB_STS_TIMEOUT ) {
PDEBUG(2, "dev %d endpoint %d timeout, td = %p", dev, ep, td);
td->td_status = -ETIMEDOUT;
if (--td->errcnt == 0)
td->done = 1;
} else if (td->status & SL811_USB_STS_ERROR) {
PDEBUG(2, "dev %d endpoint %d error, td = %p", dev, ep, td);
td->td_status = -EILSEQ;
if (--td->errcnt == 0)
td->done = 1;
} else if (td->status & SL811_USB_STS_NAK) {
++td->nakcnt;
PDEBUG(3, "dev %d endpoint %d nak, td = %p, count = %d", dev, ep, td, td->nakcnt);
td->td_status = -EINPROGRESS;
if (!usb_pipeslow(td->urb->pipe) && td->nakcnt > 1024) {
PDEBUG(2, "too many naks, td = %p, count = %d", td, td->nakcnt);
td->td_status = -ETIMEDOUT;
td->done = 1;
}
}
sl811_print_td(4, td);
}
/*
* This function checks the status of current urb.
*/
static int sl811_parse_cur_urb(struct urb *urb)
{
struct sl811_urb_priv *urbp = urb->hcpriv;
struct sl811_td *td = urbp->cur_td;
struct list_head *tmp;
sl811_print_td(5, td);
// this td not done yet.
if (!td->done)
return 0;
// the last ld, so the urb is done.
if (td == urbp->last_td) {
PDEBUG(4, "urb = %p is done success", td->urb);
if (usb_pipeisoc(td->urb->pipe))
PDEBUG(4, "ISO URB DONE, td = %p", td);
return 1;
}
// iso transfer, we always advance to next td
if (usb_pipeisoc(td->urb->pipe)) {
tmp = &td->td_list;
tmp = tmp->next;
urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
PDEBUG(4, "ISO NEXT, td = %p", urbp->cur_td);
return 0;
}
// some error occur, so the urb is done.
if (td->td_status) {
PDEBUG(3, "urb = %p is done error, td status is = %d", td->urb, td->td_status);
return 1;
}
// short packet.
if (td->left) {
if (usb_pipecontrol(td->urb->pipe)) {
// control packet, we advance to the last td
PDEBUG(3, "ctrl short packet, advance to last td");
urbp->cur_td = urbp->last_td;
return 0;
} else {
// interrut and bulk packet, urb is over.
PDEBUG(3, "bulk or intr short packet, urb is over");
return 1;
}
}
// we advance to next td.
tmp = &td->td_list;
tmp = tmp->next;
urbp->cur_td = list_entry(tmp, struct sl811_td, td_list);
#ifdef SL811_DEBUG
PDEBUG(4, "advance to the next td, urb = %p, td = %p", urb, urbp->cur_td);
sl811_print_td(5, urbp->cur_td);
if (td == urbp->cur_td)
PDEBUG(1, "bug!!!");
#endif
return 0;
}
/*
* Find the next td to transfer.
*/
static inline struct sl811_td* sl811_schedule_next_td(struct urb *urb, struct sl811_td *cur_td)
{
struct sl811_urb_priv *urbp = urb->hcpriv;
PDEBUG(4, "urb at %p, cur td at %p", urb, cur_td);
// iso don't schedule the td in the same frame.
if (usb_pipeisoc(cur_td->urb->pipe))
return NULL;
// cur td is not complete
if (!cur_td->done)
return NULL;
// here, urbp->cur_td is already the next td;
return urbp->cur_td;
}
/*
* Scan the list to find a active urb
*/
static inline struct urb* sl811_get_list_next_urb(struct sl811_hc *hc, struct list_head *next)
{
struct urb *urb;
int i;
if (list_empty(next))
return NULL;
if (next == hc->cur_list)
return NULL;
for (i = 0; i < 4; ++i)
if (next == &hc->urb_list[i])
return NULL;
urb = list_entry(next, struct urb, urb_list);
PDEBUG(4, "next urb in list is at %p", urb);
return urb;
}
/*
* Find the next td to transfer.
*/
static struct sl811_td* sl811_schedule_next_urb(struct sl811_hc *hc, struct list_head *next)
{
struct urb *urb = NULL;
int back_loop = 1;
struct list_head *old_list = hc->cur_list;
// try to get next urb in the same list.
if (next) {
urb = sl811_get_list_next_urb(hc, next);
if (!urb)
++hc->cur_list;
}
// try other list.
if (!urb) {
re_loop:
// try all the list.
while (hc->cur_list < &hc->urb_list[4]) {
if ((urb = sl811_get_list_next_urb(hc, hc->cur_list->next)))
return ((struct sl811_urb_priv *)urb->hcpriv)->cur_td;
++hc->cur_list;
}
// the last list is try
if (back_loop && (old_list >= &hc->ctrl_list)) {
hc->cur_list = &hc->ctrl_list;
back_loop = 0;
goto re_loop;
}
}
if (hc->cur_list > &hc->urb_list[3])
hc->cur_list = &hc->ctrl_list;
return NULL;
}
/*
* This function process the transfer rusult.
*/
static void sl811_transfer_done(struct sl811_hc *hc, int sof)
{
struct sl811_td *cur_td = hc->cur_td, *next_td = NULL;
struct urb *cur_urb = NULL;
struct list_head *next = NULL;
int done;
PDEBUG(5, "enter");
if (cur_td == NULL) {
PDEBUG(1, "in done interrupt, but td is null, be already parsed?");
return ;
}
cur_urb = cur_td->urb;
hc->cur_td = NULL;
next = &cur_urb->urb_list;
next = next->next;
spin_lock(&cur_urb->lock);
sl811_parse_cur_td(hc, cur_td);
done = sl811_parse_cur_urb(cur_urb);
spin_unlock(&cur_urb->lock);
if (done) {
list_del_init(&cur_urb->urb_list);
cur_td = NULL;
sl811_result_urb(cur_urb);
}
if (sof)
return ;
if (!done) {
next_td = sl811_schedule_next_td(cur_urb, cur_td);
if (next_td && next_td != cur_td && (sl811_calc_bus_remainder(hc) > next_td->bustime)) {
hc->cur_td = next_td;
PDEBUG(5, "ADD TD");
sl811_trans_cur_td(hc, next_td);
return ;
}
}
while (1) {
next_td = sl811_schedule_next_urb(hc, next);
if (!next_td)
return;
if (next_td == cur_td)
return;
next = &next_td->urb->urb_list;
next = next->next;
if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
hc->cur_td = next_td;
PDEBUG(5, "ADD TD");
sl811_trans_cur_td(hc, next_td);
return ;
}
}
}
/*
*
*/
static void inline sl811_dec_intr_interval(struct sl811_hc *hc)
{
struct list_head *head, *tmp;
struct urb *urb;
struct sl811_urb_priv *urbp;
if (list_empty(&hc->idle_intr_list))
return ;
head = &hc->idle_intr_list;
tmp = head->next;
while (tmp != head) {
urb = list_entry(tmp, struct urb, urb_list);
tmp = tmp->next;
spin_lock(&urb->lock);
urbp = urb->hcpriv;
if (--urbp->interval == 0) {
list_del(&urb->urb_list);
list_add(&urb->urb_list, &hc->intr_list);
PDEBUG(4, "intr urb active");
}
spin_unlock(&urb->lock);
}
}
/*
* The sof interrupt is happen.
*/
static void sl811_start_sof(struct sl811_hc *hc)
{
struct sl811_td *next_td;
#ifdef SL811_DEBUG
static struct sl811_td *repeat_td = NULL;
static repeat_cnt = 1;
#endif
if (++hc->frame_number > 1024)
hc->frame_number = 0;
if (hc->active_urbs == 0)
return ;
sl811_dec_intr_interval(hc);
if (hc->cur_td) {
if (sl811_read(hc, 0) & SL811_USB_CTRL_ARM) {
#ifdef SL811_DEBUG
if (repeat_td == hc->cur_td)
++repeat_cnt;
else {
if (repeat_cnt >= 2)
PDEBUG(2, "cur td = %p repeat %d", hc->cur_td, repeat_cnt);
repeat_cnt = 1;
repeat_td = hc->cur_td;
}
#endif
return ;
} else {
PDEBUG(2, "lost of interrupt in sof? do parse!");
sl811_transfer_done(hc, 1);
// let this frame idle
return;
}
}
hc->cur_list = &hc->iso_list;
if (hc->active_urbs == 0)
return ;
next_td = sl811_schedule_next_urb(hc, NULL);
if (!next_td) {
#ifdef SL811_DEBUG
if (list_empty(&hc->idle_intr_list))
PDEBUG(2, "not schedule a td, why? urbs = %d", hc->active_urbs);
#endif
return;
}
if (sl811_calc_bus_remainder(hc) > next_td->bustime) {
hc->cur_td = next_td;
sl811_trans_cur_td(hc, next_td);
} else
PDEBUG(2, "bus time if not enough, why?");
}
/*
* This function resets SL811HS controller and detects the speed of
* the connecting device
*
* Return: 0 = no device attached; 1 = USB device attached
*/
static int sl811_hc_reset(struct sl811_hc *hc)
{
int status ;
sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);//复位端点一
mdelay(20);
// Disable hardware SOF generation.
sl811_write(hc, SL811_CTRL1, 0);
mdelay(2);
sl811_write(hc, SL811_INTRSTS, 0xff);
status = sl811_read(hc, SL811_INTRSTS);
if (status & SL811_INTR_NOTPRESENT) {//0x40=0100 0000 没有设备
// Device is not present
PDEBUG(0, "Device not present");
hc->rh_status.wPortStatus &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE);
hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV/*0x20 = 0010 0000 */);
return 0;
}
// Send SOF to address 0, endpoint 0.
sl811_write(hc, SL811_LEN_B, 0);
sl811_write(hc, SL811_PIDEP_B, PIDEP(USB_PID_SOF/*include/linux/usb.h:0xa5*/, 0)/*PIDEP(USB_PID_SOF, 0)=0x50*/);
sl811_write(hc, SL811_DEV_B, 0x00);
sl811_write (hc, SL811_SOFLOW, SL811_12M_HI);
if (status & SL811_INTR_SPEED_FULL) {
/* full speed device connect directly to root hub */
PDEBUG (0, "Full speed Device attached");
sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
mdelay(20);
sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_12M_HI);
sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SOF);//硬件产生sof信号
/* start the SOF or EOP */
sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);//允许传送
hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION;
hc->rh_status.wPortStatus &= ~USB_PORT_STAT_LOW_SPEED;
mdelay(2);
sl811_write (hc, SL811_INTRSTS, 0xff);
} else {
/* slow speed device connect directly to root-hub */
PDEBUG(0, "Low speed Device attached");
sl811_write(hc, SL811_CTRL1, SL811_CTRL1_RESET);
mdelay(20);
sl811_write(hc, SL811_CTRL2, SL811_CTL2_HOST | SL811_CTL2_DSWAP | SL811_12M_HI);
sl811_write(hc, SL811_CTRL1, SL811_CTRL1_SPEED_LOW | SL811_CTRL1_SOF);
/* start the SOF or EOP */
sl811_write(hc, SL811_CTRL_B, SL811_USB_CTRL_ARM);
hc->rh_status.wPortStatus |= USB_PORT_STAT_CONNECTION | USB_PORT_STAT_LOW_SPEED;
mdelay(2);
sl811_write(hc, SL811_INTRSTS, 0xff);
}
hc->rh_status.wPortChange |= USB_PORT_STAT_C_CONNECTION;
sl811_write(hc, SL811_INTR, SL811_INTR_INSRMV);
return 1;
}
/*
* Interrupt service routine.
*/
static void sl811_interrupt(int irq, void *__hc, struct pt_regs * r)
{
__u8 status;
struct sl811_hc *hc = __hc;
status = sl811_read(hc, SL811_INTRSTS);
if (status == 0)
{
return ;
}
sl811_write(hc, SL811_INTRSTS, 0xff);
if(status&SL811_INTR_INSRMV)
{
info("device insort/remove");
if(wh_initsl811(hc))
{
info("found device!\n");
}
else
{
info("not device!\n");
}
}
if (status & SL811_INTR_DONE_A)
{
info("usb a interrupt occured!\n");
}
return ;
}
/*
* This function allocates all data structure and store in the
* private data structure.
*
* Return value : data structure for the host controller
*/
static struct sl811_hc* __devinit sl811_alloc_hc(void)
{
struct sl811_hc *hc;
struct usb_bus *bus;
int i;
PDEBUG(5, "enter");
hc = (struct sl811_hc *)kmalloc(sizeof(struct sl811_hc), GFP_KERNEL);
if (!hc)
return NULL;
memset(hc, 0, sizeof(struct sl811_hc));
hc->rh_status.wPortStatus = USB_PORT_STAT_POWER;
hc->rh_status.wPortChange = 0;
hc->active_urbs = 0;
INIT_LIST_HEAD(&hc->hc_hcd_list);
list_add(&hc->hc_hcd_list, &sl811_hcd_list);
init_waitqueue_head(&hc->waitq);
for (i = 0; i < 6; ++i)
INIT_LIST_HEAD(&hc->urb_list[i]);
hc->cur_list = &hc->iso_list;
bus = usb_alloc_bus(&sl811_device_operations);
if (!bus)
{
kfree (hc);
return NULL;
}
bus->bus_name = "sl811";
hc->bus = bus;
bus->hcpriv = hc;
return hc;
}
/*
* This function De-allocate all resources
*/
static void sl811_release_hc(struct sl811_hc *hc)
{
PDEBUG(5, "enter");
/* disconnect all devices */
if (hc->bus->root_hub)
usb_disconnect(&hc->bus->root_hub);
if (hc->addr_io)
release_region(hc->addr_io, 1);
if (hc->data_io)
release_region(hc->data_io, 1);
if (hc->irq)
free_irq(hc->irq, hc);
usb_deregister_bus(hc->bus);
usb_free_bus(hc->bus);
list_del(&hc->hc_hcd_list);
INIT_LIST_HEAD(&hc->hc_hcd_list);
kfree (hc);
}
/*
* This function is board specific. It sets up the in
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -