常用字符设备驱动函数总结 ----- 记录总结tool

本文总结了Linux内核设备驱动开发中的关键函数,包括打印级别、控制台打印级别查看、模块加载与卸载、内核日志查看、设备号管理以及并发控制中的锁机制。此外,还提到了内存管理模型、I/O模型和中断处理的相关概念。
摘要由CSDN通过智能技术生成

      打印等级:
        #define KERN_EMERG  "<0>"   /* system is unusable           */
        #define KERN_ALERT  "<1>"   /* action must be taken immediately */
        #define KERN_CRIT   "<2>"   /* critical conditions          */
        #define KERN_ERR    "<3>"   /* error conditions         */
        #define KERN_WARNING"<4>"   /* warning conditions           */
        #define KERN_NOTICE "<5>"   /* normal but significant condition */
        #define KERN_INFO   "<6>"   /* informational            */
        #define KERN_DEBUG  "<7>"   /* debug-level messages         */ 

    控制台相关的打印级别:
        #define console_loglevel (console_printk[0])
        #define default_message_loglevel (console_printk[1])
        #define minimum_console_loglevel (console_printk[2])
        #define default_console_loglevel (console_printk[3])

cat /proc/sys/kernel/printk  查看当前打印级别

/lib/modules ls  查看当前加载的是哪个内核  并找到makefile 的当前路径

    如何去运行相关模块:
        sudo  insmod   xxx.ko :自动执行入口函数
        查看内核打印信息:dmesg 
        
    如何删除当前模块:
        sudo rmmod xxx    :自动执行出口函数
    
    
    清除内核打印信息:
        sudo dmesg -c:清除打印信息的同时,将打印信息,显示在屏幕上
        sudo dmesg -C:清除打印信息的同时,不显示打印信息在屏幕上
        
    查看当前内核中存在的模块:
        lsmod    modinfo xxx.ko  可以查看到很多info

关于type的赋值:
        type:bool,invbool,charp,int,long,short,uint,ulong,ushort;

module_param_named(named_out,name_in,type,perm)
参数:name_out:加载模块时,参数的名字
	name_in:模块内部变量的名字
	type:参数的类型
	perm:访问权限

传递字符串到全局字符数组:
加载模块时传递字符串到一个全局字符数组module_param_string(name,string,len,perm)
参数:name:加载模块时,参数的名字
	string:模块内部字符数组的名字
	len:模块内部字符数组
	perm:访问权限

传递数组型参数	:
加载模块时传递参数到模块的数组中module_param_array(name,type,num,perm)
参数:name:加载模块时,参数的名字
	type:模块数组的数据类型
	num:传递数组成员的个数,NULL不关心用户传递的参数个数
	perm:访问权限

参数描述信息:MODULE_PARM_DESC( 参数名称,"参数描述信息");

sudo insmod module.ko num=1 module_out=8 str="hello" my_array=9,8,7,6,0

modinfo xxx.ko 查看依赖的模块

make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules

-C :获取编译该模块的内核的makefile 路径,  M:获取待编译模块所在的路径

 

EXPORT_SYMBOL(待导出的符号);

生成:Module.symvers   该文件中记录了  add的函数名 , 函数的指针

export.h-->/usr/src/linux-headers-3.5.0-23-generic/Module.symvers:   addr + symbol + module + EXPORT_SYMBOL 
     /proc/kallsyms

                    typedef void (*lpFunction)();

                    lpFunction lpReset = (lpFunction)0xF000FFF0;

                    lpReset();

1.获取到 module.symvers  insmode xxx.ko

2.完成对应的使用   extern int add(int a, int b);  extern int sub(int a, int b);

3.完成, 编译, 插入到内核


dev_t  :设备号的数据类型       
        dev_t from ;
        int major ,minor;
     获取主设备号major: major = MAJOR(from);
     获取次设备号minor: minor = MINOR(from);
     利用主次设备号获取设备号: from = MKDEV(major , minor);

 

    struct inode { ------>某一个具体的文件--->设备节点--->/dev/xxx
        const struct inode_operations    *i_op;//对文件、设备节点操作时的相关操作方法  的集合
        dev_t            i_rdev;                  //设备号
        const struct file_operations    *i_fop;    //操作方法
        union {
            struct pipe_inode_info    *i_pipe;//网络相关的
            struct block_device    *i_bdev;    //块设备的抽象结构体
            struct cdev        *i_cdev;        //字符设备的抽象结构体

        };
    };

struct cdev {//字符设备对象
        //struct kobject kobj;//linux设备模型的底层组成
        struct module *owner;//表征其所归属的模块对象
        const struct file_operations *ops;//对于字符设备对象相关操作的方法集合,open,close,read,write,llseek
        dev_t dev;//该字符设备对象对应的设备号 -------------》 对应 inode 中的 i_rdev   container_of(inode->i_rdev, xxxxx);
    };

[1]设备号的申请
[1.1]自动申请设备号
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
功能:自动申请设备号
参数:dev:对应的设备号
baseminor:起始次设备号
count:申请次设备的个数
name:名称
返回值:成功:0   失败:errno
			
			
[1.2]手动申请(可能存在设备号的冲突)
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:手动申请设备号
参数:from:对应的设备号
count:申请次设备的个数
name:名称
返回值:成功:0   失败:errno
				
[2]申请得到对应的字符设备对象
struct cdev *cdev_alloc(void)
功能:申请cdev对象
参数:无
返回值:成功:struct cdev 的指针   失败:NULL
			
			
[3]填充字符设备对象的相关属性成员   
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:初始化cdev[完成cdev与之相关联的操作方法集合的绑定]
参数:cdev:字符设备的指针对象
fops:操作方法集合
返回值:无
			
			
[4]字符设备对象的注册
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
功能:注册字符设备对象
参数:p:字符设备的指针对象
dev:该字符设备对象的设备号
count:注册的字符设备的个数
返回值:成功 : 0  失败:errno
---------------------------exit------------------------------
[1]设备号的注销
void unregister_chrdev_region(dev_t from, unsigned count)
功能:注销设备号
参数:from:对应的设备号
count:注销次设备号的个数
返回值:无
[2]void cdev_del(struct cdev *p)
功能:注销字符设备对象
参数:p:对应的字符设备对象
返回值:无
(0)字符设备驱动另一种方式
[1]字符设备驱动的简化版
static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
功能:完成字符设备框架的初始化编写
参数:major:主设备号   (major= 0 完成设备号部分的自动申请   )
name:设备对应的名称  cat /proc/devices
fops:该设备的对应的操作方法集合
返回值:成功:0  /major  失败:errno
			
static inline void unregister_chrdev(unsigned int major, const char *name)
功能:注销字符设备框架(的退出部分编写)
参数:major:主设备号   (major= 0 完成设备号部分的自动申请   )
name:设备对应的名称  cat /proc/devices
返回值:无

(1)读写
用户空间------->内核空间的访问
(1)系统调用【read,write】
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);


头文件:include <linux/uaccess.h> //read write是针对用户来讲的  copy_to_user copy_from_user是针对内核来讲的
read:将底层数据传输给用户层   copy_to_user
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
功能:传输数据到用户空间
参数:to:用户空间的数据存储地址
from:内核空间的地址
n:传输数据的大小
返回值:成功:0   失败:传输数据的个数
			
write:将用户空间数据传输给底层   copy_from_user
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
功能:将用户空间的数据写入内核空间
参数:to:内核空间的地址
from:用户空间的地址
n:传输数据的大小
返回值:成功:0   失败:传输数据的个数
(2)控制(32bits被分为四部分)
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
功能:根据不同的命令码执行不同的操作
参数:d:文件描述符
request:命令码
...:视情况再填充  ,[地址]
返回值:成功:0   失败:-1
				
头文件:#include <asm-generic/ioctl.h>
ioctl:
底层实现的机制:
(1)完成unlocked_ioctl的底层实现:switch case
(2)完成不同命令码的封装
#define _IO(type,nr)		_IOC(_IOC_NONE,(type),(nr),0)
功能:封装命令码
参数:type:命令码的一个类型 【幻数】,一般使用相关字符来标志 eg:  #define type 'a'
nr: 命令代码参数  eg:0 , 1 , 2...
/*
#define _IOR(type,nr,size)	_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
linux-3.14/Documentation/ioctl/ioctl-number.txt记录了内核中可能被占用的一些命令码 
*/
	ioctl命令码组成:	32bit		
     _________________________________________
     | 方   向   | 数据尺寸 | 设备类型  | 序列号  |
     |----------|----------|----------|--------|
     |   2 bit  | 8~14 bit |   8 bit  | 8 bit  |

幻数 一般使用字符来表示 eg:# define type ‘a’
Nr:命令代码参数

#define TYPE 'A'
#define ONE _IO(TYPE, 0)
#define TWO _IO(TYPE, 1)
(3)自动创建设备节点[/proc/sys/kernel/hotplug]--->udev
头文件:#include <linux/device.h>
			
结构体:
struct class {
	const char		*name;//类名    /sys/class/xxx
	struct module	*owner;//所归属的模块
	//struct kobject	*dev_kobj;//linux设备模型相关-->生成文件夹
};
struct device {
	struct device	*parent;//该设备的parent
	struct device_private	*p;//存放该设备的私有数据
	//struct kobject kobj;//linux设备模型相关-->生成文件夹
	const char		*init_name; /* 设备名称 */
	//const struct device_type *type;//设备类型
	//struct bus_type	*bus;		/* 该设备挂接的总线*/
	//struct device_driver *driver;	/*支持当前设备的驱动对象*/
	dev_t			devt;	/* 设备号*/
	struct class		*class;//当前设备归属于哪一个类
	//void	(*release)(struct device *dev);//当设备移除时,会被调用
};
		
(1)struct class *class_create(struct module *owner,const char *name)
	功能:创建class
	参数:owner:THIS_MODULE
	name:对应的设备类/*/sys/class/xxx*/
	返回值:成功:struct class指针对象  失败:errno指针
			
IS_ERR(errno):判断其是否申请得到了对应的指针,如果说errno该指针是正确的话,那么IS_ERR会返回0,反之非0
PTR_ERR(errno):将错误指针,转换成具体的错误码
    /*
	#define class_create(owner, name)		\
	({						\
	    static struct lock_class_key __key;	\
		__class_create(owner, name, &__key);	\
	})
	struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key)
	*/

(2)void class_destroy(struct class *cls)
功能:销毁对应的class
参数:cls:struct class的指针对象
返回值:无
			
(3)struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
功能:创建对应的device
参数:class:struct class的指针对象
parent:设备父节点  如果没有 NULL
devt:设备号
drvdata:私有数据  没有的话 NULL
fmt:对于待生成的设备节点的命名格式    /dev/demo0  /dev/demo1   "%s%d"
...:"demo"   i
返回值:成功:struct device指针对象  失败:errno指针
			
(4)void device_destroy(struct class *class, dev_t devt)
功能:销毁对应的device
参数:class:struct class的指针对象
devt:设备号
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值