Linux驱动注册轮询设备

一般情况下很少需要使用注册轮询设备的,
因为轮询设备需要高频率调用获取外设的状态,增加CPU的负担.
但是碰到过2次需要使用注册轮询的情况:
1 外设IO口非常紧张的情况下,没有空出多余的IO口作为中断脚.
2 所使用的IO口没有带有中断功能.芯片中没有设计其对应的中断号

#include <linux/input-polldev.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>

struct pollgpio_key_data {
	int m_id;
	struct input_polled_dev *poll_dev;
};

static void adgpiokeys_poll(struct input_polled_dev *dev){
	struct timex  txc;
	struct rtc_time tm;
	do_gettimeofday(&(txc.time));
	rtc_time_to_tm(txc.time.tv_sec,&tm);
	printk("[%d-%02d-%02d %02d:%02d:%02d][%s,pid=%d]\n",
			tm.tm_year+1900,tm.tm_mon+1, tm.tm_mday,
			tm.tm_hour,tm.tm_min,tm.tm_sec,
			__FUNCTION__, current->pid);
}
	
static int adgpiokeys_probe(struct platform_device *pdev){
	struct pollgpio_key_data *adkey;
	struct input_polled_dev *poll_dev;
	int err;
	printk("%s, pid=%d\n", __func__, current->pid);
	adkey = kzalloc(sizeof(struct pollgpio_key_data), GFP_KERNEL);
	if (!adkey){
		return -ENOMEM;
	}
	
	poll_dev = input_allocate_polled_device();
	if (!poll_dev) {
		err = -ENOMEM;
		goto fail;
	}

	platform_set_drvdata(pdev, adkey);

	// 不设置名字会警告 input: Unspecified device as /devices/virtual/input/input10
	poll_dev->input->name = "demo-name01";
	poll_dev->input->phys = "demopoll-key/input0";
	
	poll_dev->poll = adgpiokeys_poll;
	poll_dev->poll_interval = 200; // 设置轮询的频率为200毫秒, default = 500
	adkey->poll_dev = poll_dev; // for set_drvdata and get_drvdata
	adkey->m_id = 0x8008;
	err = input_register_polled_device(poll_dev);
	if (err)
		goto fail;
	
	return 0;
	
fail:
	printk(KERN_ERR "Adkey: failed to register driver, error: %d\n", err);
	platform_set_drvdata(pdev, NULL);
	input_free_polled_device(poll_dev);
	kfree(adkey);
	return err;
}

static int adgpiokeys_remove(struct platform_device *pdev){
	struct pollgpio_key_data *adkey = platform_get_drvdata(pdev);
	printk("%s,m_id=0x%x\n", __func__, adkey->m_id);
	input_unregister_polled_device(adkey->poll_dev);
	input_free_polled_device(adkey->poll_dev);
	kfree(adkey);	
	return 0;
}

static struct platform_driver analog_ops = {
	.probe = adgpiokeys_probe,
	.remove = __devexit_p(adgpiokeys_remove),
	.suspend = NULL,
	.resume = NULL,
	.driver = {
		.owner = THIS_MODULE,
		.name = "analog-key",
	},
};

static int __init ad_gpio_key_init(void){
	return platform_driver_register(&analog_ops);
}

static void __exit ad_gpio_key_exit(void){
	printk("analog_gpio_key_exit\n");
	platform_driver_unregister(&analog_ops);
}

//late_initcall(ad_gpio_key_init);
module_init(ad_gpio_key_init);
module_exit(ad_gpio_key_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mr.linux.debug");
MODULE_DESCRIPTION("Demo ad gpio poll for kernel module");

相关函数接口的原型分析

/// 向内核申请一个轮询设备
struct input_polled_dev *input_allocate_polled_device(void)
{
		struct input_polled_dev *dev;

		dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL);
		if (!dev)
				return NULL;

		dev->input = input_allocate_device();
		if (!dev->input) {
				kfree(dev);
				return NULL;
		}

		return dev;
}

 向输入子系统注册一个轮询设备
int input_register_polled_device(struct input_polled_dev *dev)
{
		struct input_dev *input = dev->input;

		input_set_drvdata(input, dev);
		INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
		if (!dev->poll_interval)
				dev->poll_interval = 500;
		input->open = input_open_polled_device;
		input->close = input_close_polled_device;

		return input_register_device(input);
}

///  轮询设备的结构体
struct input_polled_dev {
		void *private; // 私有的驱动数据
		void (*flush)(struct input_polled_dev *dev); // 驱动程序提供的方法,刷新设备的状态(可选)
		void (*poll)(struct input_polled_dev *dev); // 轮询调用的处理函数
		unsigned int poll_interval; /* msec */  // 轮询的间隔时间, 以毫秒为单位
		struct input_dev *input; // 普通的输入子设备
		struct delayed_work work; // 包含一个延时工作队列
};

可以看出来两个接口的处理原理

input_allocate_polled_device向内核申请一个轮询设备
就是对申请输入子系统设备(input_allocate_device)二次封装
input_register_polled_device向输入子系统注册一个轮询设备
就是对注册输入子系统设备(input_register_device)二次封装
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值