1. 程式人生 > >linux驅動基礎知識-白陽(四) 字元裝置

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]