📄 sl811.c
字号:
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 int 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, clear all irq status. 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) { // 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); 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, 0)); 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); /* 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 ; /* Not me */ sl811_write(hc, SL811_INTRSTS, 0xff); if (status & SL811_INTR_INSRMV) { sl811_write(hc, SL811_INTR, 0); sl811_write(hc, SL811_CTRL1, 0); // wait for device stable mdelay(100); sl811_hc_reset(hc); return ; } spin_lock(&hc->hc_lock); if (status & SL811_INTR_DONE_A) { if (status & SL811_INTR_SOF) { sl811_transfer_done(hc, 1); PDEBUG(4, "sof in done!"); sl811_start_sof(hc); } else sl811_transfer_done(hc, 0); } else if (status & SL811_INTR_SOF) sl811_start_sof(hc); spin_unlock(&hc->hc_lock); 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; } hc->bus = bus; bus->bus_name = MODNAME; 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); // Stop interrupt handle if (hc->irq) free_irq(hc->irq, hc); hc->irq = 0; /* Stop interrupt for sharing, but only, if PatternTest ok */ if (hc->addr_io) { /* Disable Interrupts */ sl811_write(hc, SL811_INTR, 0); /* Remove all Interrupt events */ mdelay(2); sl811_write(hc, SL811_INTRSTS, 0xff); } /* free io regions */ sl811_release_regions(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 request IO memory regions, request IRQ, and * allocate all other resources. * * Input: addr_io = first IO address * data_io = second IO address * irq = interrupt number * * Return: 0 = success or error condition */static int __devinit sl811_found_hc(int addr_io, int data_io, int irq){ struct sl811_hc *hc; PDEBUG(5, "enter");// ctrl_outb(0x41,0xb80080f8); /* config CF */ hc = sl811_alloc_hc(); if (!hc) return -ENOMEM; if (sl811_request_regions (hc, addr_io, data_io, MODNAME)) { PDEBUG(1, "ioport %X,%X is in use!", addr_io, data_io); sl811_release_hc(hc); return -EBUSY; } if (sl811_reg_test(hc)) { PDEBUG(1, "SL811 register test failed!"); sl811_release_hc(hc); return -ENODEV; }#ifdef SL811_DEBUG_VERBOSE {// __u8 u = SL811Read (hci, SL11H_HWREVREG); __u8 u = sl811_read (hc, SL11H_HWREVREG); // Show the hardware revision of chip PDEBUG(1, "SL811 HW: %02Xh", u); switch (u & 0xF0) { case 0x00: PDEBUG(1, "SL11H"); break; case 0x10: PDEBUG(1, "SL811HS rev1.2"); break; case 0x20: PDEBUG(1, "SL811HS rev1.5"); break; default: PDEBUG(1, "Revision unknown!"); } }#endif // SL811_DEBUG_VERBOSE/*// Check io area// CN3 ctrl_outb(0x0e, 0xba008000); printk("address =0xba008000 "); printk("data High =%x\n", ctrl_inb(0xba008002));// ctrl_outb(0x0e, 0xba008000); printk("address =0xba008000 "); printk("data Low =%x\n", ctrl_inb(0xba008001));// CN4 ctrl_outb(0x0e, 0xba00a000); printk("address =0xba00a000 "); printk("data High =%x\n", ctrl_inb(0xba00a002));// ctrl_outb(0x0e, 0xba00a000); printk("address =0xba00a000 "); printk("data Low =%x\n", ctrl_inb(0xba00a001));////check atribute area ???// CN3 ctrl_outb(0x0e, 0xb8008000); printk("address =0xb8008000 "); printk("data High =%x\n", ctrl_inb(0xb8008002));// ctrl_outb(0x0e, 0xb8008000); printk("address =0xb8008000 "); printk("data Low =%x\n", ctrl_inb(0xb8008001));// CN4 ctrl_outb(0x0e, 0xb800a000); printk("address =0xb800a000 "); printk("data High =%x\n", ctrl_inb(0xb800a002));// ctrl_outb(0x0e, 0xb800a000); printk("address =0xb800a000 "); printk("data Low =%x\n", ctrl_inb(0xb800a001));//check common area ???// CN3 ctrl_outb(0x0e, 0xb8008800); printk("address =0xb8008800 "); printk("data High =%x\n", ctrl_inb(0xb8008802));// ctrl_outb(0x0e, 0xb8008800); printk("address =0xb8008800 "); printk("data Low =%x\n", ctrl_inb(0xb8008801));// CN4 ctrl_outb(0x0e, 0xb800a800); printk("address =0xb800a800 "); printk("data High =%x\n", ctrl_inb(0xb800a802));// ctrl_outb(0x0e, 0xb800a800); printk("address =0xb800a800 "); printk("data Low =%x\n", ctrl_inb(0xb800a801));*/ sl811_init_irq(); usb_register_bus(hc->bus); if (request_irq(irq, sl811_interrupt, SA_SHIRQ, MODNAME, hc)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -