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

  • Post author:
  • Post category: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");



版权声明:本文为weixin_44948595原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。