您的位置:首页 > 其它

OHCI(二) - OHCI控制器驱动

2015-11-03 15:37 351 查看

注册OHCI

使用函数usb_create_hcd创建一个struct usb_hcd,usb_add_hcd将struct hc_driver注册到usb core

struct hc_driver

一个重要的结构体struct hc_driver

static 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和FSMPS

ohci->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

ClearHubFeature

ClearPortFeature

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: