您的位置:首页 > 编程语言

基本字符设备的编写及代码分析

2014-10-10 19:08 134 查看
* 说明:用于演示一个最基本的字符设备驱动程序框架。

* 实现一个字符设备驱动的步骤:

* 1、实现模块代码框架

* 2、申请设备号,这个设备号由主、次设备号组成,是应用程序

* 通过文件访问设备的关键所在。在类unix系统中,一切设备

* 皆文件(网卡设备除外),而一个文件和一个inode对应,应用

* 层创建设备节点使用的mknod命令其实就是为了能够创建一个能

* 够代表设备的一个inode。应用程序通过路径名访问设备,但是

* 这个路径名会最终装换到对应的inode上,在创建设备节点时,

* 明确给出了主、次设备号,也即是通过路径名得到inode,通过

* inode得到了主、次设备号,最后通过主设备号查找到对应的设

* 备驱动程序,或者说是查找到了对应的cdev。

* 3、初始化一个代表字符设备的cdev对象,主要实现了cdev内部成员

* 的初始化和cdev同file_operations的关联,这样找到cdev就能

* 找到对应的操作方法集,从而实现设备的打开、关闭、读、写等

* 操作。

* 4、添加(或注册)cdev到内核,主要实现了将代表设备的cdev对象

* 的指针保存到内核中的相应数据结构中。另外,还实现了cdev和

* 主、次设备号的关联,这是通过设备号能找到对应cdev的关键所

* 在。

* 5、实现操作方法集中需要实现的操作,并非所有调用接口都要实现,

* 根据设备的具体情况而定。

*/

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/module.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/uaccess.h>

#include "ioctl.h"

#define FSDEV_MAJOR
250

#define FSDEV_MINOR
0

#define FSDEV_NR 1

#define FSDEV_NAME "fsdev"

/* 可以将该结构体看作是继承于cdev父类的一个子类 */

struct fsdev {

struct cdev cdev;
/* 代表字符设备的一个抽象父类 */

unsigned char buf[256]; /* 设备相关的一些资源,封装的概念 */

};

static struct fsdev fsdev; /* 实例化一个对象 */

/* 该函数通常用来实现对设备的前期初始化,如初始化一些关键寄存器,

* 使设备处于待工作状态。和具体的设备密切相关

*/

static int fschr_open(struct inode *inode, struct file *filp)

{

return 0;

}

/* 该函数通常用来实现和open操作相反的操作 */

static int fschr_close(struct inode *inode, struct file *filp)

{

return 0;

}

/* 从设备中获取数据,和具体的设备密切相关 */

static ssize_t fschr_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)

{

int ret;

int len;

/* 注意要对参数进行严格的检查,驱动程序的崩溃都可能会导致整个系统的崩溃 */

len = count > 256 ? 256 : count;

/* 不同层之间的拷贝,用特殊的拷贝函数,注意该函数返回的是没有拷贝完成的字节数 */

ret = copy_to_user(buf, fsdev.buf, len);

return len - ret;

}

/* 写数据到设备,和具体的设备密切相关 */

static ssize_t fschr_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)

{

int ret;

int len;

len = count > 256 ? 256 : count;

ret = copy_from_user(fsdev.buf, buf, len);

return len - ret;

}

static int fschr_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

{

int ret = 0;

/* 严格的参数检测,有利于系统的稳定 */

if (_IOC_TYPE(cmd) != FS_IOC_MAGIC)

return -ENOTTY;

if (_IOC_NR(cmd) > FS_IOC_MAXNR)

return -ENOTTY;

if (_IOC_DIR(cmd) & _IOC_READ)

/* 注意,这里的VERIFY_WRITE是针对应用层arg传递过来的指针而言的,应用层

* 的读操作恰恰是内核层对该指针所指向内存的写操作

*/

ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

else if (_IOC_DIR(cmd) & _IOC_WRITE)

ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

if (ret)

return -EFAULT;

switch (cmd) {

/* 命令字的定义推荐严格按照Linux的要求进行 */

case FS_IOC_SET_BUF:

memset(fsdev.buf, *(char *)arg, sizeof(fsdev.buf));

break;

}

return 0;

}

static struct file_operations fsops = {

/* 结构体中特定成员赋初值,前面加. */

.owner = THIS_MODULE,

.open = fschr_open,

.release = fschr_close,

.read = fschr_read,

.write = fschr_write,

.ioctl = fschr_ioctl,

};

static int __init fschr_init(void)

{

int ret;

dev_t devno;

/* 推荐使用该宏来生成设备号,因为内核对设备号的定义可能会变 */

devno = MKDEV(FSDEV_MAJOR, FSDEV_MINOR);

/* 静态的设备号申请,申请成功后可以通过cat /proc/devices命令查看 */

ret = register_chrdev_region(devno, FSDEV_NR, FSDEV_NAME);

if (ret) {

printk(KERN_ERR "fschr: register chrdev region failed\n");

goto reg_err;

}

memset(&fsdev, 0, sizeof(struct fsdev));

/* 初始化cdev对象,并和操作方法集关联 */

cdev_init(&fsdev.cdev, &fsops);

/* THIS_MODULE相当于this指针,指向驱动所在的模块,用于防止驱动在使用时,

* 模块被卸载

*/

fsdev.cdev.owner = THIS_MODULE;

/* 注册cdev到内核,并和设备号关联 */

ret = cdev_add(&fsdev.cdev, devno, FSDEV_NR);

if (ret) {

printk(KERN_ERR "fschr: add cdev failed\n");

goto add_err;

}

return 0;

add_err:

unregister_chrdev_region(devno, FSDEV_NR);

reg_err:

return ret;

}

static void __exit fschr_exit(void)

{

dev_t devno;

devno = MKDEV(FSDEV_MAJOR, FSDEV_MINOR);

cdev_del(&fsdev.cdev);

unregister_chrdev_region(devno, FSDEV_NR);

}

module_init(fschr_init);

module_exit(fschr_exit);

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("Kevin Jiang <coreteker@gmail.com>");

MODULE_DESCRIPTION("This is an example for linux char driver");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: