Linux驱动中delayed workqueue用法

在驱动中,有时不能使用中断(这种情况很少遇到), 此时delayed_workqueue就可以发挥其巨大的功效了。也可以用其它同类的内核API实现:如timer。delayed_workqueue就相当于实现了中断上下半部分。

delayed_workqueue的使用过程如下:

    --> 定义workqueue: struct workqueue_struct *test_workqueue;                                      // 定义在: kernel/workqueue.c

    --> 定义workqueue要做的delayed工作:struct delayed_work test_delayed_work;       // 定义在: include/linux/workqueue.h

    --> 初始化workqueue:INIT_DELAYED_WORK(test_workqueue, test_delayed_work);    // 定义在:include/linux/workqueue.h

    --> 创建线程queue并加以名字:test_workqueue = create_singlethread_workqueue("name_of_this_queue");

    --> 运行queue:queue_delayed_work(test_workqueue, test_delayed_work, delay_time);  // 定义在:kernel/workqueue.c, 其中delay_time是延迟多少时间来运行queue。

注: 在delayed_work中一定要再次将delayed_workqueue加入queue中,即再次运行queue_delayed_work。否则,此queue只运行一次。

下面我们看个例子:

static int qcom_usb_extcon_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct qcom_usb_extcon_info *info;
	int ret;

	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->edev = devm_extcon_dev_allocate(dev, qcom_usb_extcon_cable);
	if (IS_ERR(info->edev)) {
		dev_err(dev, "failed to allocate extcon device\n");
		return -ENOMEM;
	}

	ret = devm_extcon_dev_register(dev, info->edev);
	if (ret < 0) {
		dev_err(dev, "failed to register extcon device\n");
		return ret;
	}

	ret = extcon_set_property_capability(info->edev,
			EXTCON_USB, EXTCON_PROP_USB_SS);
	ret |= extcon_set_property_capability(info->edev,
			EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
	if (ret) {
		dev_err(dev, "failed to register extcon props rc=%d\n",
						ret);
		return ret;
	}

        //这里我们初始化workqueue
	info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS);
	INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable);

	info->id_irq = platform_get_irq_byname(pdev, "usb_id");
	if (info->id_irq > 0) {
		ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
					qcom_usb_irq_handler,  //中断处理函数里会运行queue
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					pdev->name, info);
		if (ret < 0) {
			dev_err(dev, "failed to request handler for ID IRQ\n");
			return ret;
		}
	}

	info->vbus_irq = platform_get_irq_byname(pdev, "usb_vbus");
	if (info->vbus_irq > 0) {
		ret = devm_request_threaded_irq(dev, info->vbus_irq, NULL,
					qcom_usb_irq_handler,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					pdev->name, info);
		if (ret < 0) {
			dev_err(dev, "failed to request handler for VBUS IRQ\n");
			return ret;
		}
	}

	if (info->id_irq < 0 && info->vbus_irq < 0) {
		dev_err(dev, "ID and VBUS IRQ not found\n");
		return -EINVAL;
	}

	platform_set_drvdata(pdev, info);
	device_init_wakeup(dev, 1);

	/* Perform initial detection */
	qcom_usb_extcon_detect_cable(&info->wq_detcable.work);

	return 0;
}

上面我们可以看到中断处理函数里是启动queue,实现如下:

static irqreturn_t qcom_usb_irq_handler(int irq, void *dev_id)
{
	struct qcom_usb_extcon_info *info = dev_id;

	queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
			   info->debounce_jiffies);

	return IRQ_HANDLED;
}

 

你可能感兴趣的:(Linux驱动)