linux驅動基礎知識-白陽(四) 字元裝置
-
相關標頭檔案
#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和以前實現方法不一致,
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))
已經講解的很詳細了
#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;
}
[email protected]:/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);
[email protected]:/home/baiy/workspace/testcode/driver/test02# tree /sys/class/globalmemC/
/sys/class/globalmemC/
└── globalmem -> ../../devices/virtual/globalmemC/globalmem
1 directory, 0 files
[email protected]:/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
[email protected]:/home/baiy/workspace/testcode/driver/test02# ls -al /dev/globalmem
crw------- 1 root root 236, 0 8月 1 10:50 /dev/globalmem
模組資訊,與驅動 檔名一致
[email protected]:/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]