首先了解一下什么是字符设备和字符设备驱动吧
在Linux系统中,字符设备以特别文件方式存在文件目录树中占据位置并拥有相应的结点。结点中的文件类型指明该文件是字符设备文件可以对字符设备文件使用文件操作命令(open、read 、write、close、ioctl)
他是一个通道,联通了 用户层和硬件,是Linux系统中最基本的一个驱动,常见的按键、i2c、SPI等都是字符驱动设备;
当用户访问 文件节点的时候,比如 open(/dev/mouse),进入内核
那么如何找到 对应的 字符设备驱动文件对象呢???
文件节点 和 字符设备驱动对象,拥有 相同的编号.u32无符号整型,
其中 高12bit称为 主设备号, 低20bit称为次设备号
练习了解: 已知设备号devno,请 求取 主设备号major 和次设备号minor
minor = devno & 0xFFFFF
major = devno >> 20;
已知 主设备号major,次设备号minor,请求取 设备号 devno
devno=major<<20 | minor
字符设备驱动(文件对象),有哪些东西:
属性: 设备号
方法: open read write close ioctl
struct inode { 表示该文件的 文件节点
struct cdev *i_cdev; 指向了 该文件节点 所对应的 字符设备驱动程序
}
struct file {
当用户open的时候,返回一个fd. 同时在内核创建一个 struct file对象
当你通过fd操作的时候,首先找到该file对象,他记录了文件的所有属性
void *private_data; 内核从来不关心这个区域,是给 驱动开发者使用的.
有什么用: 我们驱动一般在open的时候,往哪里放一个东西
在read write close的时候取出来直接使用.
更高级: open read write close ioctl 他们共享了一片空间,不在分散
}
一个文件他只有一个节点inode, 但是可以被同时打开多次,产生多个fd和file
#include //在内核的源码中
#include
#include
#include
#include
#include
#include "comm.h"
struct global_struct {
struct class *cls;
struct cdev cdev_obj; //定义了一个字符设备驱动对象
int major,minor;
};
struct global_struct gstruct; // 全局变量
/*
如何编写 字符设备驱动程序呢???
1.向系统申请一个设备号
2.实现一个 字符设备驱动对象: devno open read write close
3.创建一个 文件节点/dev/xxxx, 该节点和char dev 拥有同一个设备号.
*/
int chdev_open (struct inode *inode, struct file *file)
{
printk("%s->%d\n",__func__,__LINE__);
/*
inode->i_cdev === &gstruct.cdev_obj
问题: 已知一个大结构体的类型(struct global_struct),
和该大结构体中 某个小成员(cdev_obj)的地址(inode->i_cdev)
可否反推出 大结构体变量的地址 (&gstruct)
大结构体地址 = 结构体类型, 小成员,小成员地址
大结构体地址 = container_of(小成员地址, 大结构体类型, 小成员)
*/
struct global_struct *pt_gstruct = container_of(inode->i_cdev, struct global_struct, cdev_obj);
file->private_data = pt_gstruct;
/*返回值的问题:
0-如果成功,告知用户
negative: 标错 错误码,会回馈到 用户函数的errno
-EBUSY -EEXIST
*/
return 0;
}
int chdev_release (struct inode *inode, struct file *file)
{
struct global_struct *pt_gstruct = file->private_data;
printk("%s->%d major=%d minor=%d\n",__func__,__LINE__,
pt_gstruct->major,pt_gstruct->minor);
/*返回值的问题:
0-如果成功,告知用户
negative: 标错 错误码,会回馈到 用户函数的errno
-EBUSY -EEXIST
*/
return 0;
}
/*
int write (int fd, char *buf, int len )
ssize_t cw (struct file *file, char *usr, size_t sz , loff_t *off)
*/
ssize_t chdev_write(struct file *file, const char __user *usr,size_t sz, loff_t *off)
{
struct global_struct *pt_gstruct = file->private_data;
struct msg_struct msgkernel;
pt_gstruct = pt_gstruct;
/*思考, 该函数要干什么?????
用户为什么要写文件, 为了把数据从用户层传递到 kernel
那么 该函数 就要把用户层的数据拷贝到 内核中来
*/
/*
问题: usr来自用户层的,可能非法,不可信,一旦内核使用了
所以 usr在使用之前要审核 是否合法, 怎么审核呢
不建议直接使用 memcpy,而是使用 安全版本的拷贝函数
int copy_from_user(void * to, const void __user * from, unsigned long n)
返回值 同memcpy
memcpy 返回值是什么: 尚未拷贝的字节数
ret = memcpy(dst,src,100);
如果要求拷贝100B,但是实际上只拷贝了 40,返回值 60
如何证明拷贝成功了??? 0
*/
int ret = copy_from_user(&msgkernel, usr, sz);
if(ret){
printk("%s->%d copy_from_user err\n",__func__,__LINE__);
return -3;
}
printk("kernel write got data:tmp=%d wind=%d himudity=%ld des=%s\n",\
msgkernel.temp,msgkernel.wind,msgkernel.himudity,msgkernel.des);
/*
成功,返回实际拷贝的字节数
失败: negative err code
*/
return sz - ret ;
}