发现新的usb设备(八)

本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

在添加主机控制器驱动时,创建了一个定时器hcd->rh_timer,定时器的执行函数是rh_timer_func,这个定时器在register_root_hub完成后启动了。

int usb_add_hcd(struct usb_hcd *hcd, unsigned int irqnum, unsigned long irqflags)
{
	……
	if (hcd->uses_new_polling && hcd->poll_rh)
	usb_hcd_poll_rh_status(hcd);
}
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
	struct urb	*urb;
	int		length;
	unsigned long	flags;
	char		buffer[6];	/* Any root hubs with > 31 ports? */
	//root hub注册时把hcd->rh_registered设置为1
	if (unlikely(!hcd->rh_registered))
		return;
	//注册时hcd->uses_new_polling
	if (!hcd->uses_new_polling && !hcd->status_urb)
		return;

	//读端口寄存器看端口有没有变化,没有变化length = 0
	length = hcd->driver->hub_status_data(hcd, buffer);
	if (length > 0) {

		/* try to complete the status urb */
		spin_lock_irqsave(&hcd_root_hub_lock, flags);
		urb = hcd->status_urb;
		if (urb) {
			hcd->poll_pending = 0;
			hcd->status_urb = NULL;
			urb->actual_length = length;
			memcpy(urb->transfer_buffer, buffer, length);

			usb_hcd_unlink_urb_from_ep(hcd, urb);
			spin_unlock(&hcd_root_hub_lock);
			usb_hcd_giveback_urb(hcd, urb, 0);
			spin_lock(&hcd_root_hub_lock);
		} else {
			length = 0;
			hcd->poll_pending = 1;
		}
		spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
	}

	/* The USB 2.0 spec says 256 ms.  This is close enough and won't
	 * exceed that limit if HZ is 100. The math is more clunky than
	 * maybe expected, this is to make sure that all timers for USB devices
	 * fire at the same time to give the CPU a break inbetween */
	//在ohci_run函数中把hcd->uses_new_polling设置为1,在ohci_run函数中也把hcd->poll_rh设置为了1
	//启动定时器hcd->rh_timer
	if (hcd->uses_new_polling ? hcd->poll_rh :
			(length == 0 && hcd->status_urb != NULL))
		mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}

这个定时器一直读端口寄存器的状态,如果一直没有变化,这个定时器相当于空转,没做事。
如果检测到端口寄存器状态有变化,还需要 hcd->status_urb不等于NULL。

在hub初始化时,在hub_configure函数中创建了一个中断urb请求。

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_irq函数赋值给了urb->complete函数。
在hub_activate函数中提交了urb请求。

static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
    ……
	status = usb_submit_urb(hub->urb, GFP_NOIO);
}

usb_submit_urb中调用usb_hcd_submit_urb函数,将urb给HCD处理

int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
    ……
    return usb_hcd_submit_urb(urb, mem_flags);
}

usb_hcd_submit_urb函数里面,如果进来的设备是root hub,那么urb请求由rh_urb_enqueue函数处理,如果不是,调用主机控制器的urb_enqueue函数。

int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
    ……
	//root hub调用rh_urb_enqueue进行USB通信
	if (is_root_hub(urb->dev))
		status = rh_urb_enqueue(hcd, urb);
	//如果不是root hub,调用对应的控制器的urb_enqueue函数
	else
		status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);
}

我们这里还是root hub设备,所以调用rh_urb_enqueue函数。

static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
{
	//如果是中断传输
	if (usb_endpoint_xfer_int(&urb->ep->desc))
		return rh_queue_status (hcd, urb);
	//如果是控制传输
	if (usb_endpoint_xfer_control(&urb->ep->desc))
		return rh_call_control (hcd, urb);
	return -EINVAL;
}

我们这里是中断传输,所以调用rh_queue_status函数。

static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb)
{
    ……
    retval = usb_hcd_link_urb_to_ep(hcd, urb);
	if (retval)
		goto done;

	hcd->status_urb = urb;
	urb->hcpriv = hcd;	/* indicate it's queued */
}

上面把urb放入urb端口的链表中。
把urb赋值给hcd->status_urb保存起来。这样上面的hcd->status_urb就有值了。

前面说到有一个定时器hcd->rh_timer,执行函数是rh_timer_func

static void rh_timer_func (unsigned long _hcd)
{
	usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}

void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
	struct urb	*urb;
	int		length;
	unsigned long	flags;
	char		buffer[6];	/* Any root hubs with > 31 ports? */

	if (unlikely(!hcd->rh_registered))
		return;
	if (!hcd->uses_new_polling && !hcd->status_urb)
		return;

	length = hcd->driver->hub_status_data(hcd, buffer);
	if (length > 0) {

		/* try to complete the status urb */
		spin_lock_irqsave(&hcd_root_hub_lock, flags);
		urb = hcd->status_urb;
		if (urb) {
			hcd->poll_pending = 0;
			hcd->status_urb = NULL;
			urb->actual_length = length;
			memcpy(urb->transfer_buffer, buffer, length);

			usb_hcd_unlink_urb_from_ep(hcd, urb);
			spin_unlock(&hcd_root_hub_lock);
			usb_hcd_giveback_urb(hcd, urb, 0);
			spin_lock(&hcd_root_hub_lock);
		} else {
			length = 0;
			hcd->poll_pending = 1;
		}
		spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
	}

	/* The USB 2.0 spec says 256 ms.  This is close enough and won't
	 * exceed that limit if HZ is 100. The math is more clunky than
	 * maybe expected, this is to make sure that all timers for USB devices
	 * fire at the same time to give the CPU a break inbetween */
	if (hcd->uses_new_polling ? hcd->poll_rh :
			(length == 0 && hcd->status_urb != NULL))
		mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}

usb_hcd_poll_rh_status函数中,调用主机控制器的hub_status_data函数获取端口状态。如果端口的状态有变化,那么length > 0,把获取到的端口状态的数组拷贝到urb->transfer_buffer中,就是前面的hub->buffer中,同时调用usb_hcd_giveback_urb函数。
usb_hcd_giveback_urb函数中调用urb->complete (urb),而urb->complete = hub_irq,这样就返回到了hub中。
返回之前,urb->status = 0,表示urb处理成功了。
urb->actual_length = hcd->driver->hub_status_data返回的length,这个长度根据主机控制器的说明,等于1或2.
进入hub_irq函数。
将获得的端口状态的数组存入一个long型的整数hub->event_bits[0]中,它对应一个Bitmap,bit 0表示Hub有变化,而其它bit则具体表示某一个端口有没有变化,如果一个端口没有变化,对应的那一位就是0。我们通过按位与的方式可以知道哪一个端口发生了改变。

//中断传输就是只有一个urb
static void hub_irq(struct urb *urb)
{	//填充urb的时候,urb->context就是赋的hub
	struct usb_hub *hub = urb->context;
	int status = urb->status;
	unsigned i;
	unsigned long bits;
	//判断urb的状态,前三种都是出错
	switch (status) {
	case -ENOENT:		/* synchronous unlink */
	case -ECONNRESET:	/* async unlink */
	case -ESHUTDOWN:	/* hardware going away */
		return;

	default:		/* presumably an error */
		/* Cause a hub reset after 10 consecutive errors */
		dev_dbg (hub->intfdev, "transfer --> %d\n", status);
		//刚开始hub->error为0,hub->nerrors也为0
		if ((++hub->nerrors < 10) || hub->error)
			goto resubmit;
		hub->error = status;
		/* FALL THROUGH */

	/* let khubd handle things */
	//urb被顺利的处理
	case 0:			/* we got data:  port status changed */
		bits = 0;
		for (i = 0; i < urb->actual_length; ++i)
			bits |= ((unsigned long) ((*hub->buffer)[i]))
					<< (i*8);
		//event_bits[0],是一个数组,对应Bitmap,bit 0表示Hub有变化,而其它bit则具体表示
		//某一个端口有没有变化,如果一个端口没有变化,对应的那一位就是0
		hub->event_bits[0] = bits;
		break;
	}

	hub->nerrors = 0;

	/* Something happened, let khubd figure it out */
	//调用kick_khubd()函数,于是会再一次触发hub_events().
	kick_khubd(hub);

resubmit:
	//hub_suspend ,hub_pre_reset()将hub->quiescing设置为1
	//如果hub被挂起了,或者要被reset了,那么就不用重新提交urb了,hub_irq()函数直接返回
	if (hub->quiescing)
		return;

	if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
			&& status != -ENODEV && status != -EPERM)
		dev_err (hub->intfdev, "resubmit --> %d\n", status);
}

然后调用kick_khubd函数。kick_khubd函数将event_list添加到hub_event_list链表中,表示有事件产生,同时唤醒hub_thread线程。唤醒hub_thread调用的函数是wake_up(&khubd_wait)。

hub_thread线程在hub初始化时就创建好了。

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;

	/* Fall through if kernel_thread failed */
	usb_deregister(&hub_driver);
	printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);

	return -1;
}
static int hub_thread(void *__unused)
{
	/* khubd needs to be freezable to avoid intefering with USB-PERSIST
	 * port handover.  Otherwise it might see that a full-speed device
	 * was gone before the EHCI controller had handed its port over to
	 * the companion full-speed controller.
	 */
	set_freezable();

	do {
		hub_events();
		//判断event list不为空或者线程停掉了
		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;
}

hub_thread线程在没有事件时,一直在睡眠,除非event list不为空或者线程停掉了。
hub_thread线程被唤醒后,进入了hub_events函数。

在hub_events()函数中我们就会判断是不是有新的usb设备插入了。
而我们这边后面还要继续提交urb,因为我们要一直检测hub端口的状态是否有变化,但是在hcd中上次的urb处理完后就清除了,所以我们要一直提交urb。
现在进入hub_events函数,这个函数的每一步已经有注释了。

static void hub_events(void)
{
	struct list_head *tmp;
	struct usb_device *hdev;
	struct usb_interface *intf;
	struct usb_hub *hub;
	struct device *hub_dev;
	u16 hubstatus;
	u16 hubchange;
	u16 portstatus;
	u16 portchange;
	int i, ret;
	int connect_change;

	/*
	 *  We restart the list every time to avoid a deadlock with
	 * deleting hubs downstream from this one. This should be
	 * safe since we delete the hub from the event list.
	 * Not the most efficient, but avoids deadlocks.
	 */
	while (1) {

		/* Grab the first entry at the beginning of the list */
		spin_lock_irq(&hub_event_lock);
		if (list_empty(&hub_event_list)) {
			spin_unlock_irq(&hub_event_lock);
			break;
		}
		//取出event_list取出消息放入tmp, 并把tmp从队列里删除掉
		tmp = hub_event_list.next;
		list_del_init(tmp);
		//根据tmp得到usb_hub这个结构体
		hub = list_entry(tmp, struct usb_hub, event_list);
		kref_get(&hub->kref);
		spin_unlock_irq(&hub_event_lock);

		hdev = hub->hdev;
		hub_dev = hub->intfdev;
		intf = to_usb_interface(hub_dev);
		dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
				hdev->state, hub->descriptor
					? hub->descriptor->bNbrPorts
					: 0,
				/* NOTE: expects max 15 ports... */
				(u16) hub->change_bits[0],
				(u16) hub->event_bits[0]);

		/* Lock the device, then check to see if we were
		 * disconnected while waiting for the lock to succeed. */
		usb_lock_device(hdev);
		if (unlikely(hub->disconnected))
			goto loop;

		/* If the hub has died, clean up after it */
		//一下几种情况会将hub设置为USB_STATE_NOTATTACHED
		//汇报Host Controller异常死机的函数,usb_hc_died()
		//hub驱动自己提供的函数,hub_port_disable(),用于关掉一个端口的函数
		//断开设备的函数usb_disconnect()
		if (hdev->state == USB_STATE_NOTATTACHED) {
			hub->error = -ENODEV;
			hub_quiesce(hub, HUB_DISCONNECT);
			goto loop;
		}

		/* Autoresume */
		//让这个usb interface的电源引用计数加一,只要这个引用计数大于0,这个设备就不允许autosuspend
		//autosuspend就是当用户在指定的时间内没有什么活动的话,就自动挂起
		ret = usb_autopm_get_interface(intf);
		if (ret) {
			dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
			goto loop;
		}

		/* If this is an inactive hub, do nothing */
		//quiescing是停止的意思,在reset的时候我们会设置它为1,在suspend的时候我们也会把它设置为1,
		//一旦把它设置成了1,那么hub驱动程序就不会再提交任何URB
		if (hub->quiescing)
			goto loop_autopm;

		if (hub->error) {
			dev_dbg (hub_dev, "resetting for error %d\n",
				hub->error);
			//把设备reset
			ret = usb_reset_device(hdev);
			if (ret) {
				dev_dbg (hub_dev,
					"error resetting hub: %d\n", ret);
				goto loop_autopm;
			}
			//记录发生错误的次数
			hub->nerrors = 0;
			hub->error = 0;
		}

		/* deal with port status changes */
		//表示这个hub有几个端口
		for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
			//这个端口正在执行reset或者resume操作
			if (test_bit(i, hub->busy_bits))
				continue;
			//这个端口对应的change_bits没有设置
			connect_change = test_bit(i, hub->change_bits);
			//这个端口对应的event_bits没有设置
			if (!test_and_clear_bit(i, hub->event_bits) &&
					!connect_change)
				continue;
			//获取端口状态,保存在portstatus和portchange
			ret = hub_port_status(hub, i,
					&portstatus, &portchange);
			if (ret < 0)
				continue;
			//这个端口的Current Connect Status位是否有变化
			//如果有变化,发送另一个请求以清除这个flag,并且将connect_change也设置为1
			if (portchange & USB_PORT_STAT_C_CONNECTION) {
				clear_port_feature(hdev, i,
					USB_PORT_FEAT_C_CONNECTION);
				connect_change = 1;
			}
			//每个端口都有一个开关,这叫做enable或者disable一个端口
			if (portchange & USB_PORT_STAT_C_ENABLE) {
				if (!connect_change)
					dev_dbg (hub_dev,
						"port %d enable change, "
						"status %08x\n",
						i, portstatus);
				//清除USB_PORT_FEAT_C_ENABLE这个flag
				clear_port_feature(hdev, i,
					USB_PORT_FEAT_C_ENABLE);

				/*
				 * EM interference sometimes causes badly
				 * shielded USB devices to be shutdown by
				 * the hub, this hack enables them again.
				 * Works at least with mouse driver. 
				 */
				//端口被disable了,但是连接没有变化,并且hdev->children[i]还有值
				//有子设备连在端口上,可是端口却被disable了,基本上这种情况就是电磁干扰造成的,设置connect_change为1
				if (!(portstatus & USB_PORT_STAT_ENABLE)
				    && !connect_change
				    && hdev->children[i-1]) {
					dev_err (hub_dev,
					    "port %i "
					    "disabled by hub (EMI?), "
					    "re-enabling...\n",
						i);
					connect_change = 1;
				}
			}
			//连在该端口的设备的suspend状态有变化,从suspended状态出来,也就是说resume完成
			if (portchange & USB_PORT_STAT_C_SUSPEND) {
				struct usb_device *udev;
				//清除掉SUSPEND这个flag
				clear_port_feature(hdev, i,
					USB_PORT_FEAT_C_SUSPEND);
				udev = hdev->children[i-1];
				//该端口连了子设备的情况就把子设备唤醒,如果端口没有连子设备,那么就把端口disable掉
				if (udev) {
					usb_lock_device(udev);
					ret = remote_wakeup(hdev->
							children[i-1]);
					usb_unlock_device(udev);
					if (ret < 0)
						connect_change = 1;
				} else {
					ret = -ENODEV;
					hub_port_disable(hub, i, 1);
				}
				dev_dbg (hub_dev,
					"resume on port %d, status %d\n",
					i, ret);
			}
			//这个端口可能曾经存在电流过大的情况
			if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
				dev_err (hub_dev,
					"over-current change on port %d\n",
					i);
				//清除掉OVER_CURRENT这个flag
				clear_port_feature(hdev, i,
					USB_PORT_FEAT_C_OVER_CURRENT);
				//如果其它的端口电流过大,那么将会导致本端口断电,
				//即hub上一个端口出现over-current条件将有可能引起hub上其它端口陷入powered off的状态.
				//对于over-current的情况我们都把hub重新上电
				hub_power_on(hub, true);
			}
			//一个端口从Resetting状态进入到Enabled状态
			if (portchange & USB_PORT_STAT_C_RESET) {
				dev_dbg (hub_dev,
					"reset change on port %d\n",
					i);
				clear_port_feature(hdev, i,
					USB_PORT_FEAT_C_RESET);
			}
			//如果链接状态发生了变化,执行hub_port_connect_change函数
			//有三种情况会调用这个函数,一个是连接有变化,
			//一个是端口本身重新使能,即所谓的enable,这种情况通常就是为了对付电磁干扰的
			//第三种情况就是在复位一个设备的时候发现其描述符变了,这通常对应的是硬件本身有了升级
			if (connect_change)
				hub_port_connect_change(hub, i,
						portstatus, portchange);
		} /* end for i */

		/* deal with hub status changes */
		//bit 0表示Hub有变化
		if (test_and_clear_bit(0, hub->event_bits) == 0)
			;	/* do nothing */
		else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
			dev_err (hub_dev, "get_hub_status failed\n");
		else {
			//电源有变化,一个hub可以用两种供电方式,一种是自带电源.另一种是没有自带电源,由总线来供电
			if (hubchange & HUB_CHANGE_LOCAL_POWER) {
				dev_dbg (hub_dev, "power change\n");
				//先清除掉标志位
				clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
				//HUB_STATUS_LOCAL_POWER用来标志这个hub是有专门的外接电源的还是从usb总线上获取电源
				//原来是有电源的,而现在没了,把limited_power设置为1
				if (hubstatus & HUB_STATUS_LOCAL_POWER)
					/* FIXME: Is this always true? */
					hub->limited_power = 1;
				//如果是原来没有电源现在有了电源,那么可以取消limited_power了,把它设置为0
				else
					hub->limited_power = 0;
			}
			//有过流的改变
			if (hubchange & HUB_CHANGE_OVERCURRENT) {
				dev_dbg (hub_dev, "overcurrent change\n");
				msleep(500);	/* Cool down */
				clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
				//重新给它上电
                        	hub_power_on(hub, true);
			}
		}

loop_autopm:
		/* Allow autosuspend if we're not going to run again */
		if (list_empty(&hub->event_list))
			//调用了这个函数这个hub就可以被挂起
			usb_autopm_enable(intf);
loop:
		usb_unlock_device(hdev);
		//减少hub的引用计数
		kref_put(&hub->kref, hub_release);

        } /* end while (1) */
}

文章参考:https://blog.csdn.net/fudan_abc/article/category/325189

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值