平台总线驱动设备驱动模型

时间:2022-04-11 17:32:32



         总线只是负责找到设备和驱动匹配,并调用驱动里的probe函数,在匹配后的probe函数里该注册混杂设备驱动还得注册。

代码如下:

专题2-总线设备驱动模型---第2课-平台设备驱动设计
key_dev.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

#define GPFCON 0x56000050

static struct resource key_resource[] = {
[0] = {
.start = GPFCON,
.end = GPFCON + 8,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_EINT0,
.end = IRQ_EINT2,
.flags = IORESOURCE_IRQ,
},
};

struct platform_device key_device = {
.name = "my-key",
.id = 0,
.num_resources = ARRAY_SIZE(key_resource),
.resource = key_resource,
};

static int button_init()
{
platform_device_register(&key_device);

return 0;
}


static void button_exit()
{
platform_device_unregister(&key_device);
}


module_init(button_init);
module_exit(button_exit);
key_driver.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>

MODULE_LICENSE("GPL");

struct work_struct *work;

struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t key_q;

struct resource *res;
struct resource *res_irq;
unsigned int *key_base;

void work_func(struct work_struct *work)
{
mod_timer(&buttons_timer, jiffies + (HZ /10));
}

void buttons_timer_function(unsigned long data)
{
unsigned int key_val;

key_val = readw(key_base+1)&0x1;
if (key_val == 0)
key_num = 4;

key_val = readw(key_base+1)&0x4;
if (key_val == 0)
key_num = 3;

wake_up(&key_q);
}


irqreturn_t key_int(int irq, void *dev_id)
{
//1. 检测是否发生了按键中断

//2. 清除已经发生的按键中断

//3. 提交下半部
schedule_work(work);

//return 0;
return IRQ_HANDLED;

}

void key_hw_init()
{
unsigned short data;

data = readw(key_base);
data &= ~0b110011;
data |= 0b100010;

writew(data,key_base);
}


int key_open(struct inode *node,struct file *filp)
{
return 0;
}

ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
wait_event(key_q,key_num);

copy_to_user(buf, &key_num, 4);

key_num = 0;

return 4;
}

struct file_operations key_fops =
{
.open = key_open,
.read = key_read,
};

struct miscdevice key_miscdev = {
.minor = 200,
.name = "key",
.fops = &key_fops,
};

int key_probe(struct platform_device *pdev)
{
int ret,size;


ret = misc_register(&key_miscdev);

if (ret !=0)
printk("register fail!\n");

//注册中断处理程序
//取出设备里的资源,取出IORESOURCE_IRQ类型的资源的第0个资源
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);

//按键初始化,按键物理地址映射为虚拟地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = (res->end - res->start) + 1;
key_base = ioremap(res->start, size);

key_hw_init();

//. 创建工作
work = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
INIT_WORK(work, work_func);

/* 初始化定时器 */
init_timer(&buttons_timer);
buttons_timer.function = buttons_timer_function;

/* 向内核注册一个定时器 */
add_timer(&buttons_timer);

/*初始化等待队列*/
init_waitqueue_head(&key_q);

return 0;
}
//内核代码里搜索platform_driver模仿内核里的写法
int key_remove(struct platform_device *dev)
{ //设备移除时注销设备
free_irq(res_irq->start, (void *)4);
free_irq(res_irq->end, (void *)3);

iounmap(key_base);
misc_deregister(&key_miscdev);
return 0;
}
//内核代码里搜索platform_driver模仿内核里的写法
static struct platform_driver key_driver = {
.probe= key_probe,
.remove= key_remove,
.driver= {
.owner= THIS_MODULE,
.name= "my-key",//设备的名字必须和驱动的名字一样用于总线key_drv.c的math匹配函数
},
};

static int button_init()
{
return platform_driver_register(&key_driver);
}

static void button_exit()
{
platform_driver_unregister(&key_driver);
}

module_init(button_init);
module_exit(button_exit);

Makefile
obj-m := key_dev.o key_driver.o
KDIR := /home/S5-driver/lesson7/linux-tq2440/
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order