1、如何把自己写的驱动添加到buildroot配置中可以参考这个文章:
https://bbs.csdn.net/topics/395088534/forums/forums/WindowsMobile
2、韦根驱动分析
1)
添加用于匹配设备树的结构体数组,跟我上一篇文章的设备树相匹配。
static const struct of_device_id gpio_wiegand_of_match[] = {
{ .compatible = "gpio-wiegand", },
{ },
};
2)
编写probe函数
//该代码进过简化方便了解流程,不能直接使用
static int gpio_wiegand_probe(struct platform_device *pdev){
//通过传入的pdev获取设备指针
struct device *dev = &pdev->dev;
struct device_node *node,*pp;
u32 gpio,ret;
//通过设备指针获取节点
node = dev->of_node;
//dat0 从子节点中找到'gpios'对应的gpio 的标志位
pp = of_get_next_child(node,NULL);//dat0
gpio = of_get_named_gpio_flags(pp, "gpios", 0, &flags);
if(!gpio_is_valid(gpio)){
return -EIO;
}
//申请占用这个管脚
ret = gpio_request(gpio,"inwieganddata0");
p_wiegand_data->gpio_dat0 = gpio;
//由管脚的标志获取该管脚对应的中断号
p_wiegand_data->irq_dat0 = gpio_to_irq(gpio);
//output dat2
pp = of_get_next_child(node,pp);//dat2
gpio = of_get_named_gpio_flags(pp, "gpios", 0, &flags);
ret = gpio_request(gpio,"inwieganddata2");
p_wiegand_data->out_gpio_dat0 = gpio;
//以下是申请设备结点,用于上层对设备的访问
devno = MKDEV(WIEGAND_MAJOR, 0);
if(WIEGAND_MAJOR)
result = register_chrdev_region(devno,1,DEVICE_NAME);
else
result = alloc_chrdev_region(&devno,0,1,DEVICE_NAME);
my_class = class_create(THIS_MODULE,"wiegand class"); //类名为hello_char_class
device_create(my_class,NULL,devno,NULL,"wiegand"); //设备名为memdev
rf_card = kmalloc(sizeof(struct wiegand_dev),GFP_KERNEL);
memset(rf_card,0,sizeof(struct wiegand_dev));
rf_card->count = 0;
//传入ops函数
cdev_init(&(rf_card->cdev), &rfcd_fops);
rf_card->cdev.owner = THIS_MODULE;
err = cdev_add(&rf_card->cdev, devno, 1);
init_completion(&(rf_card->receive_completion));
sema_init(&rf_card->sem,0);
sema_init(&rf_card->sem_output,0);
sema_init(&rf_card->sem_read,1);
rf_card->flag_wiegand_read = 0;
//设定IO的方向是输入
gpio_direction_input(DATA0);
gpio_direction_input(DATA1);
gpio_direction_output(OUTDATA0,1);
gpio_direction_output(OUTDATA1,1);
//设定IO的防抖时间
//gpio_set_debounce(DATA0,1000);
//gpio_set_debounce(DATA1,1000);
//设定定时器函数
timer_setup(&rf_card->wiegand_timer,wiegand_do_timer,0);//设定定时器函数
timer_setup(&rf_card->wiegand_timer_over,wiegand_do_timer_over,0);
hrtimer_init(&rf_card->out_wiegand_timer_td,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
rf_card->out_wiegand_timer_td.function = out_wiegand_do_timer_td;
hrtimer_init(&rf_card->out_wiegand_timer_tw,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
rf_card->out_wiegand_timer_tw.function = out_wiegand_do_timer_tw;
//申请相应的io中断,并设定中断触发方式
result = request_irqs();
return result;
}
3)fops结构体初始化
static struct file_operations rfcd_fops =
{
.owner = THIS_MODULE,
.read = rfcd_read,
.write = rfcd_write,
.open = rfcd_open,
.release = rfcd_release,
.unlocked_ioctl = rfcd_ioctl,
};
4)申请中断函数
在linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname设置中断名称 ,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
static int request_irqs(void)
{
int ret;
printk("%s:start request irqs\n",__func__);
ret = request_irq(gpio_to_irq(DATA0),wiegand_irq0,IRQF_TRIGGER_RISING,"wiegand_data0",rf_card);
if(ret)
{
printk("%s:request IRQ_EINT(17):%d,ret:%d failed!\n",__func__,gpio_to_irq(DATA0),ret);
return -1;
}
ret = request_irq(gpio_to_irq(DATA1),wiegand_irq1,IRQF_TRIGGER_RISING,"wiegand_data1",rf_card);
if(ret)
{
printk("%s:request IRQ_EINT(18):%d,ret:%d failed!\n",__func__,gpio_to_irq(DATA1),ret);
return -1;
}
printk("%s:request irqs success!\n",__func__);
return 0;
}
5)编写中断处理函数
static irqreturn_t wiegand_irq0(int irq, void *dev_id);
static irqreturn_t wiegand_irq1(int irq, void *dev_id);
在中断处理函数中完成韦根数据的接收。
未完待续