-
相关头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#相关头文件
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/ioctl.h>
#include<asm/ioctl.h>
-
相关结构体及重要字段
//文件操作相关结构体:
//struct file :描述一个已打开的文件 linux/fs.h
//struct file_operations: vfs与设备的接口函数,linux/fs.h
//struct cdev: 描述一个字符设备
//struct inode: 描述一个文件节点
struct file{ //代表一个已经打开的文件
struct path f_path; //路径结构,以后研究 TODO
struct inode *f_inode; //当前设备的inode信息,新版本的ioctl不提供这个接口,可通过这里获取 file_inode函数
const struct file_operations *f_op; //fops操作集合
spinlock_t f_lock;
atomic_long_t f_count; //应该是文件大小,在设备驱动中未找到用处
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos; //当前pos位置,与read和write的参数lloff_t同步修改,llseek方法修改这里
u64 f_version; //默认为0
void *private_data; //存放设备私有结构体
......
}
struct cdev {
struct kobject kobj; //kobj对象,ktype只包含了release
struct module *owner; //THIS_MODULE
const struct file_operations *ops; 文件操作
struct list_head list;
dev_t dev; //cdev_add时添加绑定设备节点
unsigned int count; //设备个数,和次设备个数一致
} __randomize_layout;
struct file_operations { //文件操作接口
#重要字段
struct module *owner;
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);
loff_t (*llseek) (struct file *, loff_t, int);
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 (*fasync) (int, struct file *, int);
.....
}
这里注意:ioctl和以前实现方法不一致,
详情参考 https://blog.csdn.net/gatieme/article/details/71437163 和 https://lwn.net/Articles/119652/ 讲解的很清楚
struct inode{ //描述一个已存在的文件,
unsigned long i_ino;
dev_t i_rdev;
//文件时间相关
struct timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev; // 这个字节可用来索引自己的私有结构体
char *i_link;
unsigned i_dir_seq;
};
......
}
#file中的f_flag和f_mode
include/uapi/asm-generic/fcntl.h 中包含了filp->f_flags相关宏; 代表文件的打开属性
include/linux/fs.h 包含了filp->f_mode相关宏:代表文件本身的读写属性
/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
* to O_WRONLY and O_RDWR via the strange trick in __dentry_open()
*/
/* file is open for reading */
#define FMODE_READ ((__force fmode_t)0x1)
/* file is open for writing */
#define FMODE_WRITE ((__force fmode_t)0x2)
/* file is seekable */
#define FMODE_LSEEK ((__force fmode_t)0x4)
/* file can be accessed using pread */
#define FMODE_PREAD ((__force fmode_t)0x8)
/* file can be accessed using pwrite */
#define FMODE_PWRITE ((__force fmode_t)0x10)
/* File is opened for execution with sys_execve / sys_uselib */
#define FMODE_EXEC ((__force fmode_t)0x20)
/* File is opened with O_NDELAY (only set for block devices) */
#define FMODE_NDELAY ((__force fmode_t)0x40)
/* File is opened with O_EXCL (only set for block devices) */
#define FMODE_EXCL ((__force fmode_t)0x80)
/* File is opened using open(.., 3, ..) and is writeable only for ioctls
(specialy hack for floppy.c) */
#define FMODE_WRITE_IOCTL ((__force fmode_t)0x100)
/* 32bit hashes as llseek() offset (for directories) */
#define FMODE_32BITHASH ((__force fmode_t)0x200)
/* 64bit hashes as llseek() offset (for directories) */
#define FMODE_64BITHASH ((__force fmode_t)0x400)
if (filp->f_mode & FMODE_READ){
xxxxxx
f_flags
/*
* When introducing new O_* bits, please check its uniqueness in fcntl_init().
*/
#define O_ACCMODE 00000003
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
#ifndef O_CREAT
#define O_CREAT 00000100 /* not fcntl */
#endif
#ifndef O_EXCL
#define O_EXCL 00000200 /* not fcntl */
#endif
#ifndef O_NOCTTY
#define O_NOCTTY 00000400 /* not fcntl */
#endif
#ifndef O_TRUNC
#define O_TRUNC 00001000 /* not fcntl */
#endif
#ifndef O_APPEND
#define O_APPEND 00002000
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK 00004000
#endif
#ifndef O_DSYNC
#define O_DSYNC 00010000 /* used to be O_SYNC, see below */
#endif
#ifndef FASYNC
#define FASYNC 00020000 /* fcntl, for BSD compatibility */
#endif
#ifndef O_DIRECT
#define O_DIRECT 00040000 /* direct disk access hint */
#endif
#ifndef O_LARGEFILE
#define O_LARGEFILE 00100000
#endif
#ifndef O_DIRECTORY
#define O_DIRECTORY 00200000 /* must be a directory */
#endif
#ifndef O_NOFOLLOW
#define O_NOFOLLOW 00400000 /* don't follow links */
#endif
#ifndef O_NOATIME
#define O_NOATIME 01000000
#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 02000000 /* set close_on_exec */
#endif
-
相关函数
-
offset细节
ssize_t (*read) (struct file *filp, char __user *, size_t, loff_t *offset);
经过测试,发现offset的地址和filp->f_pos地址不一致,但在读写函数修改offset,会影响到filp->f_pos,在llseet里边修改filp->f_pos,发现下次读写时这个offset的值也会同步过来;
-
ioctl细节
应用部分一般这样调用ioctl : #include <sys/ioctl.h> ret = ioctl(filefd, (unsigned long )cmd, (void *)args);
这样args可根据需要来与内核空间交互数据;可通过cmd来区分命令
linux建议ioctl的命令cmd方式如下 [8bit设备类型] [8bit序列号][2bit方向][数据cmd]
设备类型参考Documentation/ioctl/ioctl-number.txt;里边列了部分
命令生成内核辅助宏: _IO() ; _IOR(); _IOW();和_IORW();
#define _IOC(dir,type,nr,size) \
((unsigned int) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT)))
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
使用方法如下 https://blog.csdn.net/jk198310/article/details/52662097
已经讲解的很详细了
#define GLOBALMEM_MAGIC "g"
#define GLOBALMEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)
-
自动创建设备节点和手动创建设备节点和脚本创建节点
- sysfs与proc之间相关节点描述
//1.注册字符驱动
if(major){
devno = MKDEV(major,0);
ret = register_chrdev_region(devno, 1,"globalmem");
}else{
ret = alloc_chrdev_region(&devno,0, 1,"globalmem");
major = MAJOR(devno);
}
if(ret < 0){
printk(KERN_ERR"failed region chrdev\n");
goto failed_register;
}
root@baiy-ThinkPad-T480:/home/baiy/workspace/testcode/driver/test02# cat /proc/devices
Character devices:
236 globalmem
class总线和device匹配:
//3.注册字符驱动设备节点
class = class_create(THIS_MODULE,"globalmemC");
if(!class){
printk(KERN_ERR"failed class create\n");
goto failed_class;
}
device = device_create(class, NULL, devno,NULL,"globalmem"); //mknod /dev/globalmem c xxx 0
if(!device){
printk(KERN_ERR"failed device create\n");
goto failed_device;
}
device_destroy(class,devno);
failed_device:
class_destroy(class);
root@baiy-ThinkPad-T480:/home/baiy/workspace/testcode/driver/test02# tree /sys/class/globalmemC/
/sys/class/globalmemC/
└── globalmem -> ../../devices/virtual/globalmemC/globalmem
1 directory, 0 files
root@baiy-ThinkPad-T480:/home/baiy/workspace/testcode/driver/test02# tree /sys/devices/virtual/globalmemC/
/sys/devices/virtual/globalmemC/
└── globalmem
├── dev
├── power
│ ├── async
│ ├── autosuspend_delay_ms
│ ├── control
│ ├── runtime_active_kids
│ ├── runtime_active_time
│ ├── runtime_enabled
│ ├── runtime_status
│ ├── runtime_suspended_time
│ └── runtime_usage
├── subsystem -> ../../../../class/globalmemC
└── uevent
root@baiy-ThinkPad-T480:/home/baiy/workspace/testcode/driver/test02# ls -al /dev/globalmem
crw------- 1 root root 236, 0 8月 1 10:50 /dev/globalmem
模块信息,与驱动 文件名一致
root@baiy-ThinkPad-T480:/home/baiy/workspace/testcode/driver/test02# tree /sys/module/mychar/
/sys/module/mychar/
├── coresize
├── holders
├── initsize
├── initstate
├── notes
├── parameters
│ ├── major
│ └── minor
├── refcnt
├── sections
│ ├── __mcount_loc
│ └── __param
├── srcversion
├── taint
├── uevent
└── version
脚本创建设备节点
#!/bin/sh
module="scull"
device="scull"
mode="664"
# invoke insmod with all arguments we got
# and use a pathname, as newer modutils don't look in . by default
/sbin/insmod ./$module.ko $* || exit 1
# remove stale nodes
rm -f /dev/${device}[0-3]
major=$(awk "\\$2==\"$module\" {print \\$1}" /proc/devices)
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
# give appropriate group/permissions, and change the group.
# Not all distributions have staff, some have "wheel" instead.
group="staff"
grep -q '^staff:' /etc/group || group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]