Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2050716
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类: LINUX

2008-04-10 23:49:03




SCULL device driver note

Learner: 山涛

Date: 2007-6-29

Reference: ldd3_example/scull/main.c

 

设备类型:

字符设备

 

驱动介绍:

SCULL (Simple Character Utility for Loading Localities). SCULL is a char driver that acts on a memory area as though it were a device.

scull0 to scull3:

Four devices, each consisting of a memory area that is both global and persistent. Global means that if the device is opened multiple times, the data contained within the device is shared by all the file descriptors that opened it. Persistent means that if the device is closed and reopened, data isn't lost. This device can be fun to work with, because it can be accessed and tested using conventional commands, such as cp, cat, and shell I/O redirection.

 

设备模型:

 

 

SCULL是对你的PC上的一片内存进行操作,而且没有限制使用内存的大小(它完全可以用光你的PC上的所有内存)。SCULL是通过kmalloc()kfree()来进行内存的获取和释放。

In scull, each device is a linked list of pointers, each of which points to a scull_dev structure. Each such structure can refer, by default, to at most four million bytes, through an array of intermediate pointers. The released source uses an array of 1000 pointers to areas of 4000 bytes. We call each memory area a quantum and the array (or its length) a quantum set.

The chosen numbers are such that writing a single byte in scull consumes 8000 or 12,000 thousand bytes of memory: 4000 for the quantum and 4000 or 8000 for the quantum set (according to whether a pointer is represented in 32 bits or 64 bits on the target platform). If, instead, you write a huge amount of data, the overhead of the linked list is not too bad. There is only one list element for every four megabytes of data, and the maximum size of the device is limited by the computer's memory size.

表示设备的数据结构:

struct scull_dev {

        struct scull_qset *data;  /* Pointer to first quantum set */

        int quantum;              /* the current quantum size */

        int qset;                 /* the current array size */

        unsigned long size;       /* amount of data stored here */

        unsigned int access_key;  /* used by sculluid and scullpriv */

        struct semaphore sem;     /* mutual exclusion semaphore     */

        struct cdev cdev;         /* Char device structure              */

};

 

驱动框架:

模块初始:module_init(scull_init_module)

模块注销:module_exit(scull_cleanup_module)

字符设备相关方法的实现:scull_llseek, scull_read, scull_write, scull_ioctl, scull_open, scull_release

 

(1)    模块初始化

1)使用alloc_chrdev_region()函数动态获取主设备号,并注册4SCULL设备为字符设备(scull0~scull3)

 

Name

alloc_chrdev_region — register a range of char device numbers

Synopsis

Int alloc_chrdev_region (

dev_t *  

dev,

 

unsigned  

baseminor,

 

unsigned  

count,

 

const char *  

name);

Arguments

dev

output parameter for first assigned number

baseminor

first of the requested range of minor numbers

count

the number of minor numbers required

name

the name of the associated device or driver

Description

Allocates a range of char device numbers. The major number will be chosen dynamically, and returned (along with the first minor number) in dev. Returns zero or a negative error code.

 

2)使用kmalloc()函数为注册的4个字符设备分配相应的数据结构scull_dev

3)初始化scull_dev结构的相关域:quantum, qset, sem

4)scull设备在内核里建立char_dev结构(调用cdev_init()cdev_add()函数);

As we mentioned, the kernel uses structures of type struct cdev to represent char devices internally. Before the kernel invokes your device's operations, you must allocate and register one or more of these structures.[6] To do so, your code should include , where the structure and its associated helper functions are defined.

 

Name

cdev_init — initialize a cdev structure

Synopsis

void cdev_init (

struct cdev *  

cdev,

 

const struct file_operations *  

fops);

Arguments

cdev

the structure to initialize

fops

the file_operations for this device

Description

Initializes cdev, remembering fops, making it ready to add to the system with cdev_add.

 

Name

cdev_add — add a char device to the system

Synopsis

int cdev_add (

struct cdev *  

p,

 

dev_t  

dev,

 

unsigned  

count);

Arguments

p

the cdev structure for the device

dev

the first device number for which this device is responsible

count

the number of consecutive minor numbers corresponding to this device

Description

cdev_add adds the device represented by p to the system, making it live immediately. A negative error code is returned on failure.

 

(2)    模块注销

1)      释放设备数据结构scull_dev以及其上链接的所有内存块;调用cdev_del(),从内核里删除scull设备相应的cdev数据结构。

Name

cdev_del — remove a cdev from the system

Synopsis

void cdev_del (

struct cdev *  

p);

Arguments

p

the cdev structure to be removed

Description

cdev_del removes p from the system, possibly freeing the structure itself.

2)      调用unregister_chrdev_region()函数释放分配的一系列设备号。

 Name

unregister_chrdev_region — return a range of device numbers

Synopsis

void unregister_chrdev_region (

dev_t  

from,

 

unsigned  

count);

Arguments

from

the first in the range of numbers to unregister

count

the number of device numbers to unregister

Description

This function will unregister a range of count device numbers, starting with from. The caller should normally be the one who allocated those numbers in the first place...

 

(3)    方法实现

 

1)      open()

a.       获取表示scull设备的数据结构scull_dev;(调用container_of()

b.       scull_dev结构保存在filp->private_data

c.       如果打开设备文件的模式为O_WRONLY,调用scull_trim()函数对数据结构进行重置。(

The device is truncating it to a length of 0 when the device is opened for writing. This is performed because, by design, overwriting a scull device with a shorter file results in a shorter device data area. This is similar to the way opening a regular file for writing truncates it to zero length. The operation does nothing if the device is opened for reading.)

 

2)      release()

N/A

 

3)      read()

 

a.       根据参数f_pos获取读数据的buffer起始地址指针;

b.       计算读数据的长度(scull设备的策略是,读取的数据长度最大不超过quantum值,这也就意味着,读取的数据在一个quantum内存块之内);

c.       调用copy_to_user()函数将数据从scull设备读取到用户空间;

d.       调整参数f_pos的值,返回实际读取数据的长度。

 

4)      write()

a.       根据参数f_pos获取写数据的buffer地址指针;(如果buffer不存在,应当使用kmalloc()分配)

b.       计算写数据的长度(scull设备的策略是,写入的数据长度最大不超过quantum值,这也就意味着,写入的数据在一个quantum内存块之内);

c.       调用copy_from_user()函数将数据从用户空间写入到scull设备;

d.       调整参数f_pos的值;调整scull_dev结构size域的值,该值表示scull设备文件当前的实际长度。

e.       返回实际写入数据的长度。

5)      ioctl()

The ioctl driver method has a prototype that differs somewhat from the user-space version:

 

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

 

The inode and filp pointers are the values corresponding to the file descriptor fd passed on by the application and are the same parameters passed to the open method. The cmd argument is passed from the user unchanged, and the optional arg argument is passed in the form of an unsigned long, regardless of whether it was given by the user as an integer or a pointer. If the invoking program doesn't pass a third argument, the arg value received by the driver operation is undefined. Because type checking is disabled on the extra argument, the compiler can't warn you if an invalid argument is passed to ioctl, and any associated bug would be difficult to spot.

 

cmd由四部分组成:type, number, direction, sizeType 8位,称为幻数;number 8位,cmd的序列数;direction 2位,表示数据传输的方向;size 14位,表示用户参数的长度。

 

Scull设定了12cmd命令,用于用户可以获取或者设置quantumqset的值。参数arg使用两种方式,一种指针,一种直接赋值。

 

/*

 * S means "Set" through a ptr,

 * T means "Tell" directly with the argument value

 * G means "Get": reply by setting through a pointer

 * Q means "Query": response is on the return value

 * X means "eXchange": switch G and S atomically

 * H means "sHift": switch T and Q atomically

 */

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,  1, int)

#define SCULL_IOCSQSET    _IOW(SCULL_IOC_MAGIC,  2, int)

#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC,   3)

#define SCULL_IOCTQSET    _IO(SCULL_IOC_MAGIC,   4)

#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC,  5, int)

#define SCULL_IOCGQSET    _IOR(SCULL_IOC_MAGIC,  6, int)

#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC,   7)

#define SCULL_IOCQQSET    _IO(SCULL_IOC_MAGIC,   8)

#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)

#define SCULL_IOCXQSET    _IOWR(SCULL_IOC_MAGIC,10, int)

#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC,  11)

#define SCULL_IOCHQSET    _IO(SCULL_IOC_MAGIC,  12)

 

6)      llseek()

修改文件的偏移量。

 

每个打开文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,

用以度量从文件开始处计算的字节数。通常,读、写操作都从当前文件位移量

处开始,并使位移量增加所读或写的字节数。按系统默认,当打开一个文件时,

除非指定O_APPEND选项,否则该位移量被设置为0

可以调用seek()显示的定位一个文件。

对参数off的解释,取决于另一个参数whence

whence=SEEK_SET,则将该文件的位移量设置为距文件开始处offset个字节;

whence=SEEK_CUR,则将该文件的位移量设置为其当前值加offsetoffset可正可负;

whence=SEEK_END,则将该文件的位移量设置为文件长度加offsetoffset可正可负。

 

阅读(998) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~