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和以前实现方法不一致,

详情参考 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]

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值