我是靠谱客的博主 背后荔枝,这篇文章主要介绍Linux驱动—编写流程,现在分享给大家,希望可以做个参考。

一、编写驱动流程
①确认主设备号
查询LINUX系统中已经被使用过的主设备号

cat /proc/devices

②编写file_operations结构体

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, 
	  						 unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,loff_t len);
	int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

③file_operations结构体中包含驱动入口函数
在函数中注册驱动,注册至chardev[ ]数组中

int __register_chrdev(unsigned int major, 主设备号
                      const char *name,   模块名
                      const struct file_operations *fops 结构体)

④自动生成设备和设备文件
设备和设备文件区别:设备包含多个设备文件,都是同一个主设备号,利用次设备号来区分。

class_create(THIS_MODULE,XXX); //相当于创建一个名为XXX的文件夹

class_device_create(创建的设备名, NULL, MKDEV(major_num, 0), NULL, “模块名”);

二、例子

#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <asm/io.h>

static struct class *pin4_class;
static struct device *pin4_class_dev;      

static dev_t devno;                                      //设备号
static int major =231;                                   //主设备号
static int minor =0;                                    //次设备号
static char *module_name="pin4";                        //模块名


//初始化寄存器的地址(volatile作用:确保指令不会因编译器的优化而省略,且每次直接读值)
volatile unsigned int* GPFSEL0=NULL;                    //GPIO功能选择0                                                                                        
volatile unsigned int* GPSET0=NULL;                     //GPIO引脚输出设置为0
volatile unsigned int* GPCLR0=NULL;                     //GPIO引脚输出清除0


//led_read函数
static int pin4_read(struct file *file,char __user *buf, size_t size, loff_t *ppos)
{

    printk("pin4_readn");
    return 0;

}

//led_open函数
static int pin4_open(struct inode *inode,struct file *file)

{

    printk("pin4_openn");                  //内核的打印函数和printf类似

    //配置Pin4引脚为输出引脚(将bit的12-14配置成100)
    *GPFSEL0 &=~(0x6<<12);                 //0x6  0110左移12位,取反后结果为把bit1314配置成0
    *GPFSEL0 |=(0x1<<12);                 //把第12位配置成1 
   
    return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{

    int userCmd;
    printk("pin4_writen");

    //1.获取上层write函数的值,函数:copy_from_user
    //copy_from_user函数参数第一个参数是一个char类型的指针const char __user *buf,可用char和int类型
    copy_from_user(&userCmd,buf,count);                                                                               

    //2.根据值来操作io口(高电平或低电平)
    if(userCmd==1){
    printk("set 1n");
    *GPSET0 |=0x1<<4;           //拉高电平置0(第4个引脚)
    }
    else if(userCmd==0){
    printk("set 0n");
    *GPCLR0 |=0x1<<4;           //拉高电平清0(第4个引脚)
    }
    else{
    printk("undon");
    }
    return 0;

}

static struct file_operations pin4_fops = {

   .owner = THIS_MODULE,
   .open  = pin4_open,
   .write = pin4_write,
   .read  = pin4_read,

};

int __init pin4_drv_init(void)                  // 真实驱动入口
{
    int ret;
    printk("insmod driver pin4 successn");
    devno = MKDEV(major,minor);                                                 // 2.创建设备号 
    ret   = register_chrdev(major, module_name,&pin4_fops);                     //3.注册驱动 告诉内核,把这个驱动加入到内核的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");                         // 让代码在dev自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);      //创建设备文件

    //将物理地址转换成虚拟地址:ioremap(真正的物理地址,映射的大小),io口寄存器映射成普通的内存单元进行访问
    GPFSEL0=(volatile unsigned int *)ioremap(0x3f200000,4);                     //ioremap函数参数:第一个参数真正的物理地址,第二个参数映射的大小
    GPSET0=(volatile unsigned int*)ioremap(0x3f20001c,4);                       
    GPCLR0=(volatile unsigned int*)ioremap(0x3f200028,4);                      

    return 0;

}

void __exit pin4_drv_exit(void)
{
   //解绑映射iounmap
   iounmap(GPFSEL0);                                                                                                                        
   iounmap(GPSET0);
   iounmap(GPCLR0);

   device_destroy(pin4_class,devno);
   class_destroy(pin4_class);
   unregister_chrdev(major, module_name);                                       //卸载驱动

}

module_init(pin4_drv_init);                                                 //入口,内核加载驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

注:为什么需要使用虚拟地址???
答:在LINUX系统中,可以同时运行多个程序,会走多个main函数。找到物理地址后会利用ioremap()函数来映射出虚拟地址供程序运行

最后

以上就是背后荔枝最近收集整理的关于Linux驱动—编写流程的全部内容,更多相关Linux驱动—编写流程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(93)

评论列表共有 0 条评论

立即
投稿
返回
顶部