您的位置:首页 > 其它

简单的字符设备驱动模型

2016-10-13 23:46 253 查看
字符设备是指在I/O传输过程中以字符为单位进行传输的设备,这种设备没有缓存,例如键盘,打印机等。

#include< linux/module.h>
#include< linux/sche.h>
#include< linux/kernel.h>
#include< linux/init.h>

static loff_t test_llseek(struct file *filp, loff_t off, int whence);
/*
*linux 使用 llseek来改变驱动程序的llseek() 的值来修改设备(文件)的读写位置
*实质上是改变filp->f_pos的值
*它有三个参数
* 第一个是file指针,file结构是设备驱动程序最重要的一个数据结构(注意file!= FILE)
* file代表一个“打开的文件”,它由内核在open()时创建且在close()前作为参数传递给操作
*  在设备上的函数,以下是具体构成
struct file{
struct list_head f_list;
struct dentry * f_dentry;
struct vfsmount * f_vfsmnt;
struct file_operations * f_op;
atomic_t f_count;
unsigned int f_flags;
mode_t f_mode;
loff_t f_pos;
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
int f_error;

unsigned long f_version;

void * private_data;

struct kiobuf * f_iobuf;
long f_iobuf_lock;
};
其中有几个常用成员:
1.f_mode
文件模式由 FMODE_READ && FMODE_WRITE 标识,驱动程序的ioctl()方法可能查看这个域,
read()和write()在调用前已经检查,所以无需检查权限。
2.f_ops
当前读写位置,loff_t 是long long (64位整数)。如果定义了llseek()方法,就应该更新
f_pos
3.f_inode
打开文件对应的i节点(节点是内核中的一个概念,至于到底是啥我也不知道),inode指针
是内核传递给所有文件操作的第一个参数,所以驱动程序一般不需要访问file这个字段。
4.f_op
与文件对应的操作,内核在open()对这个指针赋值,以后需要分派操作时就读这些数据。
* 第二个参数loff_t 为要移动的偏移量
* 第三个参数wence 为偏移方式,0为从文件头移动, 1为从文件当前位置移动, 2为从文件尾移动
*/
static ssize_t test_read(struct file *filp, char *buf, size_t count, loff_t f_pos);
/*
*如果数据全部传输完成,则返回值为count
*如果数据部分传输完成, 则返回值为小于count的数,这种情况下一般会重新读数据
*如果已经到文件尾, 则返回值为0
*如果发生错误则返回负值
*/
static ssize_t test_write(struct file *filp, char *buf, size_t count, loff_t f_pos);
/*
*如果数据全部传输完成,则返回值为count
*如果数据部分传输完成, 则返回值为小于count的数,这种情况下一般会重新写数据
*如果已经到文件尾, 则返回值为0
*如果发生错误则返回负值
*/

static int test_open(struct inode *inode, struct file *filp);
/*
*在linux使用设备前会调用驱动程序的这个方法
*open()方法最重要的是调用 MOD_INC_USE_COUNT,这个宏用来增加驱动程序使用计数器,避免在不正确
* 的卸载驱动程序
*inode作用是打开文件对应的i节点,主要作用是获取设备号
*filp代表一个打开的文件(设备)
*/
static int test_release(struct inode *inode, struct file *filp);
/*
*/
static int test_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);
/*
*/

int test_init(void);
int test_cleanup(void);

#define MAJOR_DEV 125
#define DEVICE_NAME "my_simpledev"

module_init(test_init);
module_init(test_clenup);

static struct file_operations test_fops =
{
.owner = THIS_MODULE,
.llseek = test_llseek,
.read = test_read,
.write = test_write,
.open = test_open,
.release = test_release,
.ioctl = test_ioctl,
};

static loff_t test_llseek(struct file * filp, loff_t off, int whence)
{
printk("llseek called\n");
return 0;
}

static ssize_t test_read(struct file * filp, char * buf, size_t count, loff_t f_pos)
{
printk("read called\n");
return 0;
}

static ssize_t test_write(struct file * filp, char * buf, size_t count, loff_t f_pos)
{
printk("write called\n");
return 0;
}

static ssize_t test_open(struct inode *inode, struct file * filp)
{
printk("open called\n");
MOD_INC_USE_COUNT;
return 0;
}

static int test_release(struct inode *inode, struct file * filp)
{
printk("release called\n");
MOD_DEC_USE_COUNT;
return 0;
}

static int test_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param)
{
printk("ioctl called\n");
return 0;
}

int test_init(void)
{
int result = 0;

result = register_chrdev(MAJOR_NR, DEVICE_NAME, &test_fops)
if(result < 0)
{
printk(KERN_ERR_DEVICE_NAME ":Unable to get major %d\n", MAJOR_NR);
return result;
}
printk(KERN_ERR_DEVICE_NAME ": init OK \n");
return 0;
}

void test_cleanup(void)
{
unregister_chrdev(MAJOR_NR, DEVICE_NAME);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  io