自学linux驱动从入门到放弃(八)linux字符设备驱动框架

        我也是在学习过程中,后面会过度到platform总线模型框架,以及DTS框架,最终目的是通过rk3288的I2C,GPIO,interrupt,串口资源,把6轴的驱动搞起来,并且将采集的数据通过终端显示,通过无线网卡远程登录查看。终极目标是通过一段简单的驱动读取IMU的ID,通过脚本对ID进行识别,并自动加载对应的驱动程序。

一.驱动模块基本框架

        如果只是想加载一个驱动模块,那么很简单。只需要下面一段代码

#include <linux/module.h>    
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>        //为什么这么多头文件?
#include <linux/device.h>    //我不是很关心每个头文件都是干吗的,所以我都是从kenerl代码
#include <linux/tty.h>        //里面随便找的驱动复制的头文件,多了总比少了好,用就是了。省事儿。
#include <linux/kmod.h>
#include <linux/gfp.h>    

static int charDev_init(void)    //module 入口函数,返回值为int
{
    printk(KERN_ERR "hello guys!!");
    return 0;
}

static void charDev_exit(void)    //module出口函数,无返回值
{
    printk(KERN_ERR "byebye guys!!");
}

module_init(charDev_init);    //module 入口
module_exit(charDev_exit);    //module 出口
MODULE_LICENSE("GPL");        //GPL协议声明,不然加载不了模块

二.填充file_operations结构体,注册设备。

        那么上层app需要对底层的驱动进行操作,该怎么办,这里就需要填充fileoperations结构体,并且申请设备号注册类注册设备节点操作,函数如下,分别来看。

#include<linux/fs.h>

struct file_operations {
	struct module *owner;
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	int (*open) (struct inode *, struct file *);
	int (*release) (struct inode *, struct file *);
    ...//其他我不常用的删掉了
	
}; 

static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)

static inline void unregister_chrdev(unsigned int major, const char *name)


#include<linux/device.h>

#define class_create(owner, name)

void class_destroy(struct class *cls)

struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)

void class_destroy(struct class *cls)

1.file_operations结构体

1)struct module *owner

        一般赋值为THIS_MODULE

2)int (*open)

        上层app会先打开设备节点,调用open函数,那么驱动中就会调用这个open函数

3)ssize_t (*read)

        上层app open设备节点之后,进行读操作,会调用驱动中read函数,write同理。

2.注册设备号

static inline int register_chrdev(unsigned int major, const char *name,
                  const struct file_operations *fops)

1)unsigned int major

        注册的主设备号,在分配主设备号之前,最好查看一下系统中有哪些主设备号没有被使用,保险起见,这个参数设置为'0',系统会自动分配主设备号,并返回主设备号。

2)const char *name

        驱动的名字,可以通过cat /proc/devices看到,后面这些 mem tty等,就是它的名字,这个名字自己来定。

[root@firefly-rk3288:~/mnt/02_charDev]# cat /proc/devices
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  4 ttyS

3)const struct file_operations *fops

        这个就是前面填充的file_operations结构体

4)返回值 int

        当major为0时,返回主设备号

        当major非零时,返回注册成功标志,0为成功,-1为失败。

init里面注册,那么exit的时候就要反注册,就不多说了。

3.注册类

#define class_create(owner, name)

可以看到这个宏定义实际调用的是下面的函数,它有返回值,返回值为一个class类指针。

struct class *__class_create(struct module *owner, const char *name,
                 struct lock_class_key *key)

1)owner

        THIS_MODULE

2)name

        类的名字,通过ls /sys/class可以看到,下面这些都是类的名字

[root@firefly-rk3288:~/mnt/02_charDev]# ls /sys/class/
android_usb/    devfreq-event/  gpio/           ieee80211/      misc/           ptp/            scsi_device/    spi_transport/  usbmon/
backlight/      devfreq/        graphics/       input/          mmc_host/       pwm/            scsi_disk/      spidevx/        vc/
bdi/            dht11/          hevc-service/   iommu/          net/            rc/             scsi_host/      switch/         video4linux/

3)返回值 class*

        注册类成功后的class指针,这个指针注册设备节点的时候会用。

消除类也就不多说了。

4.注册设备节点

struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)

1)class *class

        这个就是前面类注册返回的类指针

2)struct device*parent

        NULL

3)dev_t devt

        这里要提到一个宏MKDEV(major,minor),devt的值就是从这里来,major我们再前面注册设备号的时候已经得到了,minor我们可以自己指定。

4)void *drvdata

        NULL

5)const char *fmt

        设备节点的名字,自己定

设备节点的消除也就不多说了,下面是简单的框架代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

int inputPara=0;

int dev_major;
int dev_minor=0;
char *chardev_device_name="charDevice";	//设备驱动名字
struct class *class;                    //用来承接创建类的返回值
char *chardev_class_name="charClass";	//类名字
char *charDev_node_name="charTest";		//设备节点名字

module_param(inputPara, int, S_IRUSR);		//insmod的时候传入的参数,参数名字inputPara,类型int,S_IRUSR是权限


int charDev_open (struct inode *inode, struct file *file){

	return 0;
}

ssize_t charDev_read (struct file *file, char __user *buf, size_t size, loff_t *ppos){

	return 0;
}

ssize_t charDev_write (struct file *file, const char __user *buf, size_t size, loff_t *ppos){

	return 0;
}





static const struct file_operations f_op = {	//file_operations结构体
	.open = charDev_open,
	.read = charDev_read,
	.write = charDev_write,
	.owner = THIS_MODULE,
}; 

static int charDev_init(void)			//init函数,MODULE_INIT的时候会进行初始化,当前框架里面
{										//注册设备号,注册类,注册设备节点都在这里
	
	dev_major = register_chrdev(0,chardev_device_name,&f_op);	//注册设备号,f_op结构体
	if(dev_major < 0)
	{
		printk("can not regist char device!\n");
		return dev_major;
	}
	printk(KERN_ERR "dev_major = %d,chardev device name is %s\n",dev_major,chardev_device_name);
	class = class_create(THIS_MODULE, chardev_class_name);		//注册类
	if(IS_ERR(class))
	{
		printk("can not create charDev class!");
		unregister_chrdev(dev_major,chardev_device_name);
		return PTR_ERR(class);
			
	}
	printk(KERN_ERR "chardev class name is %s\n",chardev_class_name);
	device_create(class, NULL, MKDEV(dev_major,dev_minor), NULL, charDev_node_name);	//注册设备节点
	printk(KERN_ERR "chardev node create ok!\n");

	if(inputPara)			//这里是测试参数是否传入,执行insmod devDrv.ko inputPara=整数(等号前后不能有空格)。
	{
		printk(KERN_ERR "input Param is %d\n",inputPara);
	}
	


	return 0;
}

static void charDev_exit(void)
{
	device_destroy(class, MKDEV(dev_major,dev_minor));
	printk(KERN_ERR "remove chardev node!\n");
	class_destroy(class);
	
	printk(KERN_ERR "remove chardev class!\n");
	unregister_chrdev(dev_major,chardev_device_name);
	printk(KERN_ERR "remove chardev device!\n");
}



module_init(charDev_init);
module_exit(charDev_exit);
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值