OHCI(二) - OHCI控制器驱动
2015-11-03 15:37
351 查看
注册OHCI
使用函数usb_create_hcd创建一个struct usb_hcd,usb_add_hcd将struct hc_driver注册到usb corestruct hc_driver
一个重要的结构体struct hc_driverstatic const struct hc_driver ohci_platform_hc_driver = { ... .reset = ohci_platform_reset,//里面调用了ohci_init .start = ohci_platform_start,//里面调用了ohci_run .stop = ohci_stop, .shutdown = ohci_shutdown, .urb_enqueue = ohci_urb_enqueue, .urb_dequeue = ohci_urb_dequeue, .hub_status_data = ohci_hub_status_data, .hub_control = ohci_hub_control, #ifdef CONFIG_PM .bus_suspend = ohci_bus_suspend, .bus_resume = ohci_bus_resume, #endif };
reset与stop是两个相反的操作
reset对应的ohci_init函数中给ohci->hcca, ohci->td_cache, ohci_ed_cache分配了内存
stop对应的ohci_stop函数中撤销了上面分配的内存
start和shutdown也是两个相反的操作
start对应的ohci_run函数中使能中断,并初始化了ohci所有寄存器
shutdown对应的ohci_shutdown函数中关闭所有中断,software reset所有寄存器
ohci_run
ohci->regs->fminterval 设置FI和FSMPSohci->regs->control 设置usb host controller functional state(reset/resume/operational/suspend)
ohci->hcca, ohci->regs->ed_controlhead, ohci->regs->ed_bulkhead, ohci->regs->hcca
ohci->regs->cmdstatus 等待software reset完成
ohci->regs->periodicStart 设置两个frame之间从何时开始处理periodic帧,当HcFmRemaining减少到这个值后开始处理periodic帧。
ohci->regs->control = OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci->rh_state = OHCI_RH_RUNNING
清除中断标志,使能中断
以上总的来说是对ohci控制器的初始化
ohci_hub_status_data
读取roothub的状态 roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC)clear the RHSC status flag before reading the port statuses
ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus);
读取roothub_port的状态 roothub_portstatus (ohci, i);
ohci_root_hub_state_changes(ohci, changed, any_connected, rhsc_status) 这个函数不怎么理解
ohci_hub_control
ClearHubFeatureClearPortFeature
GetHubDescriptor 根据HcRhDescriptorA和HcRhDescriptorB填写描述符信息
GetHubStatus
GetPortStatus
SetHubFeature
SetPortFeature
OHCI数据传输
static int ohci_urb_enqueue (struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { //分配一块ED并初始化,然后给这个ED分配一块TD作为dummy TD,放在ED的TD链表尾 if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) return -ENOMEM; //根据不同情况分配不同数量的TD switch (ed->type) { case PIPE_CONTROL: /* td_submit_urb() doesn't yet handle these */ if (urb->transfer_buffer_length > 4096) return -EMSGSIZE; /* 1 TD for setup, 1 for ACK, plus ... */ size = 2; /* FALLTHROUGH */ // case PIPE_INTERRUPT: // case PIPE_BULK: default: /* one TD for every 4096 Bytes (can be up to 8K) */ size += urb->transfer_buffer_length / 4096; /* ... and for any remaining bytes ... */ if ((urb->transfer_buffer_length % 4096) != 0) size++; /* ... and maybe a zero length packet to wrap it up */ if (size == 0) size++; else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 && (urb->transfer_buffer_length % usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe))) == 0) size++; break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; break; } /* allocate the private part of the URB */ urb_priv = kzalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), mem_flags); /* allocate the TDs (deferring hash chain updates) */ for (i = 0; i < size; i++) { urb_priv->td [i] = td_alloc (ohci, mem_flags); } //将这个urb放到ep的链表上 retval = usb_hcd_link_urb_to_ep(hcd, urb); //使能传输(设置HcControl寄存器) retval = ed_schedule (ohci, ed); //填充多个TD并将TD挂载到ED下的TD链表 //开始传输(设置HcCommandStatus寄存器) td_submit_urb (ohci, urb); }
在ohci_urb_enqueue中主要做的事是:分配ED和TD,然后将urb中data数据的物理地址偏移填充到TD数据结构,并将TD挂载在ED下的TD链表中,然后设置HcControl和HcCommandStatus寄存器使能和开始传输。
OHCI设备插入检测
这一部分涉及到usb/core里面hub.c的代码static struct usb_driver hub_driver = { .name = "hub", .probe = hub_probe, .disconnect = hub_disconnect, .suspend = hub_suspend, .resume = hub_resume, .reset_resume = hub_reset_resume, .pre_reset = hub_pre_reset, .post_reset = hub_post_reset, .unlocked_ioctl = hub_ioctl, .id_table = hub_id_table, .supports_autosuspend = 1, }; static int hub_thread(void *__unused) { do { hub_events(); wait_event_freezable(khubd_wait, !list_empty(&hub_event_list) || kthread_should_stop()); } while (!kthread_should_stop() || !list_empty(&hub_event_list)); pr_debug("%s: khubd exiting\n", usbcore_name); return 0; } int usb_hub_init(void) { if (usb_register(&hub_driver) < 0) { printk(KERN_ERR "%s: can't register hub driver\n", usbcore_name); return -1; } khubd_task = kthread_run(hub_thread, NULL, "khubd"); if (!IS_ERR(khubd_task)) return 0; }
在usb_hub_init中注册了hub对应的struct usb_driver结构体,并创建了一个线程hub_thread,在线程hub_thread中主要做的事是循环调用hub_events,hub_events只执行一遍便进入休眠等待状态,需要wake_up(&khubd_wait)将其唤醒从而再次调用hub_events
那么这个wakeup(&khubd_wait)是在哪里被调用的呢?这就涉及到从设备插入到host去枚举设备的整个流程了
1.初始化urb并进行submit
static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { hub->urb = usb_alloc_urb(0, GFP_KERNEL); usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval); hub_activate(hub, HUB_INIT); usb_submit_urb(hub->urb, GFP_NOIO); usb_hcd_submit_urb(urb, mem_flags); rh_urb_enqueue(hcd, urb); rh_queue_status (hcd, urb); hcd->status_urb = urb;
上面创建了一个hub的urb,设置urb的complete回调函数为hub_irq,然后注册这个urb。hub_irq后面会讲到当roothub检测到roothub status change的中断函数中最终会调用hub_irq,hub_irq函数通过wakeup(&khubd_wait)将睡眠等待的hub_thread唤醒,从而进一步对设备进行枚举识别。
2.设备插入,中断来了
static irqreturn_t ohci_irq (struct usb_hcd *hcd) { if (ints & OHCI_INTR_RHSC) { ohci_vdbg(ohci, "rhsc\n"); ohci->next_statechange = jiffies + STATECHANGE_DELAY; ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, ®s->intrstatus); ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable); usb_hcd_poll_rh_status(hcd); } }
当roothub检测到OHCI_INTR_RHSC中断的时候,他会调用usb_hcd_poll_rh_status去确认roothub status是否真的有变化,并且通过urb->complete即hub_irq来通知usb/core中的hub_thread底层设备有插入。
另外在底层创建usb_hcd的usb_create_hcd->usb_create_shared_hcd函数中会创建一个hcd->rh_timer,这个timer会不断的调用usb_hcd_poll_rh_status来polling roothub的状态。
总的来说,usb_hcd_poll_rh_status一方面会在roothub的OHCI_INTR_RHSC中断中调用,另一方面又被rh_timer不断的polling。
回到hub_thread和hub_events,来看看设备的枚举,当urb->complete函数hub_irq被调用时,他会去唤醒睡眠等待的hub_thread,然后执行hub_events
OHCI电源管理suspend和resume
相关文章推荐
- IIS 发布Webservice问题
- POJ 1182 食物链
- 创建 Win32 应用程序 (C++)
- 站台查询api 据站台名称查询站台详细信息
- CocoaPods
- 项目中遇到的问题:前台 disabled 与 后台disabled
- redis 存取键值对常用的三种使用方式 - Jedis、JedisPool、Jedis分布式
- Java的入门知识和准备步骤
- Nand flash ecc校验
- ubuntu14.04开启休眠(Hibernate)
- unity 2D 正交摄像机下 uGUi 比例与自适应问题
- Android Studio系列教程四--Gradle基础
- Javascript进阶篇——( 事件响应)笔记整理
- php empty,isset,is_null比较(差异与异同)
- linux下使用读写锁
- js获得日期操作,以及日期的格式化
- PHP中的浅复制与深复制
- 设计模式
- 内部跳转(请求转发)和外部跳转(重定向)的区别?
- 演进式例解控制反转(IoC)、依赖注入(DI)之一