<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://www.mywiki.cn/Hovercool/index.php?action=history&amp;feed=atom&amp;title=%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95</id>
	<title>一个完整的驱动程序及功能测试 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://www.mywiki.cn/Hovercool/index.php?action=history&amp;feed=atom&amp;title=%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95"/>
	<link rel="alternate" type="text/html" href="https://www.mywiki.cn/Hovercool/index.php?title=%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95&amp;action=history"/>
	<updated>2026-09-01T05:38:13Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://www.mywiki.cn/Hovercool/index.php?title=%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95&amp;diff=793&amp;oldid=prev</id>
		<title>2015年5月6日 (三) 08:19 Hovercool</title>
		<link rel="alternate" type="text/html" href="https://www.mywiki.cn/Hovercool/index.php?title=%E4%B8%80%E4%B8%AA%E5%AE%8C%E6%95%B4%E7%9A%84%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%8A%9F%E8%83%BD%E6%B5%8B%E8%AF%95&amp;diff=793&amp;oldid=prev"/>
		<updated>2015-05-06T08:19:47Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;== 驱动程序的编写 ==&lt;br /&gt;
&lt;br /&gt;
一个驱动程序通常包括3个部分：.h头文件、.c文件和Makefile。其中，.h头文件一般会定义一些宏和这个设备相关的结构体；.c文件是驱动功能的实现；Makefile则是对模块生成规则的一些定义。&lt;br /&gt;
&lt;br /&gt;
在这个例子中，我们定义了一个功能非常简单，结构却十分完整的字符型驱动程序。之所以说功能简单，是因为这个设备只能输出一些调试信息、存储一个int型的数据；而结构完整，则是因为它具备了在&amp;quot;/sys/&amp;quot;、&amp;quot;/dev/&amp;quot;、&amp;quot;/proc/&amp;quot;三个目录下生成设备文件的能力，能在&amp;quot;/dev/&amp;quot;下自动挂载，而且还提供了ioctl接口功能。&lt;br /&gt;
&lt;br /&gt;
首先给这个虚拟设备取个名称——&amp;quot;vircdev&amp;quot;，然后我们分别来说一下这三个文件：&lt;br /&gt;
&lt;br /&gt;
=== 一、头文件 ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
//vircdev.h&lt;br /&gt;
&lt;br /&gt;
#ifndef _VIRCDEV_H_&lt;br /&gt;
#define _VIRCDEV_H_&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_NUM (1)&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_CLASS_NAME  &amp;quot;vircdev_class&amp;quot;&lt;br /&gt;
#define VIRCDEV_DEVICE_NAME	&amp;quot;vircdev_device&amp;quot;&lt;br /&gt;
#define VIRCDEV_NODE_NAME   &amp;quot;vircdev_node&amp;quot;&lt;br /&gt;
#define VIRCDEV_PROC_NAME	&amp;quot;vircdev_proc&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_IOC_MAGIC   &amp;#039;k&amp;#039;&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_IOHI        _IO(VIRCDEV_IOC_MAGIC, 0)&lt;br /&gt;
#define VIRCDEV_IOWBYTE     _IOW(VIRCDEV_IOC_MAGIC, 1, int)&lt;br /&gt;
#define VIRCDEV_IORBYTE     _IOR(VIRCDEV_IOC_MAGIC, 2, int)&lt;br /&gt;
&lt;br /&gt;
struct vircdev_cntx {&lt;br /&gt;
    int r1;&lt;br /&gt;
    struct semaphore sem; &lt;br /&gt;
	struct cdev cdev;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
这里首先是定义了设备的个数，因为linux中设备是由一个主设备号和一个副设备号区分的，而系统只会区分主设备号，并在一些通用文件访问（如read、write）时转向设备对应的驱动程序，而副设备号系统则是完全漠视，直接传给驱动程序，由驱动进行处理的，这里的VIRCDEV_NUM则代表了设备的个数（自然也决定了副设备号的个数）。&lt;br /&gt;
&lt;br /&gt;
接着，是定义了一些名称，这些名称将会在创建设备文件时使用。&lt;br /&gt;
&lt;br /&gt;
而接下来这些宏，如VIRCDEV_IOHI，则是用于ioctl中的cmd命令；VIRCDEV_IOC_MAGIC是我们通常所说的幻数，这个数的定义要比较小心，一般是取系统中未使用的，以确保ioctl中cmd命令的唯一性，这样在对错误的文件使用这些命令时便不会出现不可预知的错误。&lt;br /&gt;
&lt;br /&gt;
=== 二、vircdev.c文件 ===&lt;br /&gt;
&lt;br /&gt;
1、全局变量的定义：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
/*====global====*/&lt;br /&gt;
static int g_vircdev_major = 0;&lt;br /&gt;
static int g_vircdev_minor = 0;&lt;br /&gt;
&lt;br /&gt;
struct vircdev_cntx *g_vircdev_ptr = NULL;&lt;br /&gt;
&lt;br /&gt;
struct class *g_vircdev_class = 0;&lt;br /&gt;
&lt;br /&gt;
static struct file_operations g_vircdev_fops = {&lt;br /&gt;
    .owner = THIS_MODULE,&lt;br /&gt;
    .read = vircdev_read,&lt;br /&gt;
    .write = vircdev_write, &lt;br /&gt;
    .unlocked_ioctl = vircdev_unlocked_ioctl,&lt;br /&gt;
    .open = vircdev_open,&lt;br /&gt;
    .release = vircdev_release,&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
g_vircdev_major、g_vircdev_minor为主副设备号；g_vircdev_ptr为设备结构体变量指针；g_vircdev_class是在构建/dev/设备文件时需要保留的一个struct class中间变量，在exit时要使用这个变量进行destroy；g_vircdev_fops，这个就不用过多解释了，设备的大部分操作都在这里面。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2、定义传统设备文件的访问方法，主要是定义vircdev_read、vircdev_write、vircdev_unlocked_ioctl、vircdev_open和vircdev_release：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
ssize_t vircdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {&lt;br /&gt;
    printk(KERN_ALERT &amp;quot;vircdev read.\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ssize_t vircdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) {&lt;br /&gt;
    printk(KERN_ALERT &amp;quot;vircdev write.\n&amp;quot;);&lt;br /&gt;
    return count;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
long vircdev_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {&lt;br /&gt;
    int err = 0;&lt;br /&gt;
    long retval = 0;&lt;br /&gt;
    &lt;br /&gt;
	/*&lt;br /&gt;
	 * extract the type and number bitfields, and don&amp;#039;t decode&lt;br /&gt;
	 * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()&lt;br /&gt;
	 */&lt;br /&gt;
	if (_IOC_TYPE(cmd) != VIRCDEV_IOC_MAGIC) return -ENOTTY;&lt;br /&gt;
	//if (_IOC_NR(cmd) &amp;gt; VIRCDEV_IOC_MAXNR) return -ENOTTY;&lt;br /&gt;
&lt;br /&gt;
	/*&lt;br /&gt;
	 * to verify *arg is in user space&lt;br /&gt;
	 */&lt;br /&gt;
	if (_IOC_DIR(cmd) &amp;amp; _IOC_READ)&lt;br /&gt;
		err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));&lt;br /&gt;
	else if (_IOC_DIR(cmd) &amp;amp; _IOC_WRITE)&lt;br /&gt;
		err =  !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));&lt;br /&gt;
	if (err) return -EFAULT;&lt;br /&gt;
    &lt;br /&gt;
    switch(cmd)&lt;br /&gt;
    {&lt;br /&gt;
        case VIRCDEV_IOHI:&lt;br /&gt;
            printk(KERN_ALERT &amp;quot;ioctl:you said hi.\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
            &lt;br /&gt;
        case VIRCDEV_IOWBYTE:            &lt;br /&gt;
            //retval = __get_user(g_vircdev_ptr-&amp;gt;r1, (int __user *)arg);&lt;br /&gt;
            g_vircdev_ptr-&amp;gt;r1 = (int __user *)arg;&lt;br /&gt;
            printk(KERN_ALERT &amp;quot;ioctl:write r1 to %i.\n&amp;quot;, g_vircdev_ptr-&amp;gt;r1);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case VIRCDEV_IORBYTE:                     &lt;br /&gt;
            retval = __put_user(g_vircdev_ptr-&amp;gt;r1, (int __user *)arg);&lt;br /&gt;
            printk(KERN_ALERT &amp;quot;ioctl:read r1:%i.\n&amp;quot;, g_vircdev_ptr-&amp;gt;r1);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        default:&lt;br /&gt;
            printk(KERN_ALERT &amp;quot;ioctl:you got the wrong command.\n&amp;quot;);&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    return retval;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int vircdev_open(struct inode *inode, struct file *filp) {&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int vircdev_release(struct inode *inode, struct file *filp) {&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3、动态分配设备号函数：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
//alloc device major&lt;br /&gt;
static int vircdex_alloc_major(void) {&lt;br /&gt;
    dev_t devt = 0;&lt;br /&gt;
    int result = 0;&lt;br /&gt;
    &lt;br /&gt;
    result = alloc_chrdev_region(&amp;amp;devt, g_vircdev_minor, VIRCDEV_NUM, VIRCDEV_NODE_NAME);&lt;br /&gt;
    g_vircdev_major = MAJOR(devt);&lt;br /&gt;
    &lt;br /&gt;
    return result;&lt;br /&gt;
}&lt;br /&gt;
static int vircdev_release_major(void) {&lt;br /&gt;
    dev_t devt = MKDEV(g_vircdev_major, g_vircdev_minor);&lt;br /&gt;
    unregister_chrdev_region(devt, 1);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4、字符设备安装函数：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
static int vircdev_setup_dev(struct vircdev_cntx *dev) {&lt;br /&gt;
    int err, devno = MKDEV(g_vircdev_major, g_vircdev_minor);&lt;br /&gt;
    &lt;br /&gt;
    cdev_init(&amp;amp;(dev-&amp;gt;cdev), &amp;amp;g_vircdev_fops);&lt;br /&gt;
    dev-&amp;gt;cdev.owner = THIS_MODULE;&lt;br /&gt;
    err = cdev_add(&amp;amp;dev-&amp;gt;cdev, devno, 1);&lt;br /&gt;
    if(err){   &lt;br /&gt;
        return err;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    //init_MUTEX(&amp;amp;(dev-&amp;gt;sem));&lt;br /&gt;
    sema_init(&amp;amp;(dev-&amp;gt;sem), 1);&lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
static int vircdev_unsetup_dev(struct vircdev_cntx *dev) {&lt;br /&gt;
    cdev_del(&amp;amp;(dev-&amp;gt;cdev));&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5、创建设备文件函数：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
static int vircdev_create_devfiles(dev_t devt) {//, const struct device_attribute *attr) {&lt;br /&gt;
    int err = -1;    &lt;br /&gt;
    struct device *dev = NULL;&lt;br /&gt;
                          &lt;br /&gt;
    g_vircdev_class = class_create(THIS_MODULE, VIRCDEV_CLASS_NAME);&lt;br /&gt;
    if(IS_ERR(g_vircdev_class)) {  &lt;br /&gt;
        err = PTR_ERR(g_vircdev_class);  &lt;br /&gt;
        printk(KERN_ALERT &amp;quot;Failed to create class.\n&amp;quot;);  &lt;br /&gt;
        goto CLASS_CREATE_ERR;  &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    dev = device_create(g_vircdev_class, NULL, devt, NULL, VIRCDEV_DEVICE_NAME);&lt;br /&gt;
    //dev = device_create(hello_class, NULL, dev, &amp;quot;%s&amp;quot;, HELLO_DEVICE_FILE_NAME);  &lt;br /&gt;
    //device_create( my_class, NULL, MKDEV(hello_major, 0), &amp;quot;hello&amp;quot; &amp;quot;%d&amp;quot;, 0 );&lt;br /&gt;
    //dev = device_create(g_vircdev_class, NULL, MKDEV(MYDRIVER_Major, 0), NULL, DEVICE_NAME);&lt;br /&gt;
    if(IS_ERR(dev)) {  &lt;br /&gt;
        err = PTR_ERR(dev);  &lt;br /&gt;
        printk(KERN_ALERT &amp;quot;Failed to create device.\n&amp;quot;);  &lt;br /&gt;
        goto DEVICE_CREATE_ERR;  &lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    /*err = device_create_file(dev, attr);  &lt;br /&gt;
    if(err &amp;lt; 0) {  &lt;br /&gt;
        printk(KERN_ALERT&amp;quot;Failed to create attribute file.&amp;quot;);                  &lt;br /&gt;
        goto DEVICE_CREATE_FILE_ERR;  &lt;br /&gt;
    }*/          &lt;br /&gt;
    printk(KERN_ALERT &amp;quot;seems ok.\n&amp;quot;); //zmk@@debug    &lt;br /&gt;
    &lt;br /&gt;
    return 0;&lt;br /&gt;
    &lt;br /&gt;
DEVICE_CREATE_FILE_ERR:&lt;br /&gt;
    device_destroy(g_vircdev_class, devt);&lt;br /&gt;
DEVICE_CREATE_ERR:&lt;br /&gt;
    class_destroy(g_vircdev_class);             &lt;br /&gt;
CLASS_CREATE_ERR:  &lt;br /&gt;
    return err; &lt;br /&gt;
}&lt;br /&gt;
static int vircdev_delete_devfiles(dev_t devt) {&lt;br /&gt;
    device_destroy(g_vircdev_class, devt);&lt;br /&gt;
    class_destroy(g_vircdev_class);&lt;br /&gt;
    //device_remove_file(dev, attr);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
6、创建/proc调试文件函数：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
static int vircdev_read_proc(char *buf, char **start, off_t offset,&lt;br /&gt;
                            int count, int *eof, void *data)&lt;br /&gt;
{&lt;br /&gt;
    int len =sprintf(buf, &amp;quot;vircdev read proc.\n&amp;quot;);&lt;br /&gt;
    return len;&lt;br /&gt;
}&lt;br /&gt;
static int vircdev_create_proc_file(void) {&lt;br /&gt;
    struct proc_dir_entry *entry = NULL;&lt;br /&gt;
 &lt;br /&gt;
    entry = create_proc_read_entry(VIRCDEV_PROC_NAME, 0,&lt;br /&gt;
                            NULL, vircdev_read_proc,&lt;br /&gt;
                            NULL);&lt;br /&gt;
    if(entry)&lt;br /&gt;
    {&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
    else                            &lt;br /&gt;
    {                        &lt;br /&gt;
        return -1;                       &lt;br /&gt;
    }                       &lt;br /&gt;
}&lt;br /&gt;
static int vircdev_delete_proc_file(void) {&lt;br /&gt;
    remove_proc_entry(VIRCDEV_PROC_NAME, NULL);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
7、设备驱动模块的加载和释放：&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
MODULE_LICENSE(&amp;quot;GPL&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
static int vircdev_init(void) {&lt;br /&gt;
    int err = -1;&lt;br /&gt;
    dev_t devt = 0;&lt;br /&gt;
    &lt;br /&gt;
    //[1] alloc node number&lt;br /&gt;
    err = vircdex_alloc_major();&lt;br /&gt;
    if(0 &amp;gt; err)&lt;br /&gt;
    {&lt;br /&gt;
        printk(KERN_ALERT&amp;quot;alloc major failed.\n&amp;quot;);&lt;br /&gt;
        goto ALLOC_MAJOR_ERR;&lt;br /&gt;
    }&lt;br /&gt;
    devt = MKDEV(g_vircdev_major, g_vircdev_minor);&lt;br /&gt;
    &lt;br /&gt;
    //[2] device object init    &lt;br /&gt;
    g_vircdev_ptr = kmalloc(sizeof(struct vircdev_cntx), GFP_KERNEL);  &lt;br /&gt;
    if(!g_vircdev_ptr) {  &lt;br /&gt;
        err = -ENOMEM;  &lt;br /&gt;
        printk(KERN_ALERT&amp;quot;kmalloc failed.\n&amp;quot;);  &lt;br /&gt;
        goto KMALLOC_ERR;  &lt;br /&gt;
    }&lt;br /&gt;
    memset(g_vircdev_ptr, 0, sizeof(struct vircdev_cntx));&lt;br /&gt;
    &lt;br /&gt;
    //[3] setup device&lt;br /&gt;
    err = vircdev_setup_dev(g_vircdev_ptr);&lt;br /&gt;
    if(0 &amp;gt; err)&lt;br /&gt;
    {&lt;br /&gt;
        printk(KERN_ALERT&amp;quot;device setup failed.\n&amp;quot;);&lt;br /&gt;
        goto DEVICE_SETUP_ERR;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    //[4] create files in directory &amp;quot;/dev/&amp;quot; and &amp;quot;/sys/&amp;quot; &lt;br /&gt;
    ///err = vircdev_create_devfiles(devt, attr);&lt;br /&gt;
    err = vircdev_create_devfiles(devt);&lt;br /&gt;
    if(0 &amp;gt; err)&lt;br /&gt;
    {&lt;br /&gt;
        printk(KERN_ALERT&amp;quot;devfiles create failed.\n&amp;quot;);&lt;br /&gt;
        goto DEVFILES_CREATE_ERR;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //[5] create proc file&lt;br /&gt;
    err = vircdev_create_proc_file();&lt;br /&gt;
    if(0 &amp;gt; err)&lt;br /&gt;
    {&lt;br /&gt;
        printk(KERN_ALERT&amp;quot;proc file create failed.\n&amp;quot;);&lt;br /&gt;
        goto PROC_FILE_CREATE_ERR;&lt;br /&gt;
    }&lt;br /&gt;
        &lt;br /&gt;
    return 0;&lt;br /&gt;
    &lt;br /&gt;
PROC_FILE_CREATE_ERR:&lt;br /&gt;
    vircdev_delete_devfiles(devt);&lt;br /&gt;
DEVFILES_CREATE_ERR:&lt;br /&gt;
    vircdev_unsetup_dev(g_vircdev_ptr);&lt;br /&gt;
DEVICE_SETUP_ERR:&lt;br /&gt;
    kfree(g_vircdev_ptr); &lt;br /&gt;
ALLOC_MAJOR_ERR:&lt;br /&gt;
    vircdev_release_major();&lt;br /&gt;
KMALLOC_ERR:&lt;br /&gt;
    return err;&lt;br /&gt;
}&lt;br /&gt;
static void vircdev_exit(void) {&lt;br /&gt;
    dev_t devt = MKDEV(g_vircdev_major, g_vircdev_minor);&lt;br /&gt;
    &lt;br /&gt;
    vircdev_delete_proc_file();&lt;br /&gt;
    vircdev_delete_devfiles(devt);&lt;br /&gt;
    vircdev_unsetup_dev(g_vircdev_ptr);&lt;br /&gt;
    kfree(g_vircdev_ptr); &lt;br /&gt;
    vircdev_release_major();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
module_init(vircdev_init);&lt;br /&gt;
module_exit(vircdev_exit);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 三、Makefile ===&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
 obj-m := vircdev.o&lt;br /&gt;
 KERNELDIR := /lib/modules/$(shell uname -r)/build&lt;br /&gt;
 PWD := $(shell pwd)&lt;br /&gt;
 modules:&lt;br /&gt;
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules&lt;br /&gt;
 modules_install:&lt;br /&gt;
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install&lt;br /&gt;
&lt;br /&gt;
clean:&lt;br /&gt;
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
关于Makefile，在《[[驱动模块中Makefile的自我理解]]》一文中有过说明，这里不再作过多解释。&lt;br /&gt;
&lt;br /&gt;
=== 四、验证 ===&lt;br /&gt;
&lt;br /&gt;
1.进入vircdev文件夹，编译：&lt;br /&gt;
 $:cd vircdev&lt;br /&gt;
 $:make&lt;br /&gt;
查看是否生成.ko文件：&lt;br /&gt;
 $:ls&lt;br /&gt;
结果：&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Makefile       Module.symvers  vircdev.h   vircdev.mod.c  vircdev.o&lt;br /&gt;
modules.order  vircdev.c       vircdev.ko  vircdev.mod.o&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2.载入模块：&lt;br /&gt;
 $:sudo insmod ./vircdev.ko&lt;br /&gt;
查看模块是否载入成功：&lt;br /&gt;
 $:lsmod |grep vir&lt;br /&gt;
结果：&lt;br /&gt;
 vircdev                12980  0&lt;br /&gt;
&lt;br /&gt;
3.分别使用cat、echo命令验证驱动模块的open、write、read和close功能：&lt;br /&gt;
 $:cat /dev/vircdev_device&lt;br /&gt;
 $:echo abcd &amp;gt; /dev/vircdev_device&lt;br /&gt;
 $:dmesg |grep vircdev&lt;br /&gt;
结果：&lt;br /&gt;
 [34058.091485] vircdev read.&lt;br /&gt;
 [34134.980198] vircdev write.&lt;br /&gt;
&lt;br /&gt;
4.验证/proc调试功能：&lt;br /&gt;
 $:cat /proc/vircdev_proc&lt;br /&gt;
结果：&lt;br /&gt;
 vircdev read proc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
附：驱动程序源文件下载：[[Image:vircdev.zip]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== ioctl测试程序的编写 ==&lt;br /&gt;
&lt;br /&gt;
鉴于read、write函数已经通过cat、echo验证ok了，现在这里编写的验证函数主要是为了验证ioctl函数。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;prettyprint&amp;quot;&amp;gt;&lt;br /&gt;
//vircdev_ioctl_test.c&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;asm/ioctls.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define DEVICE_NAME &amp;quot;/dev/vircdev_device&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_IOC_MAGIC   &amp;#039;k&amp;#039;&lt;br /&gt;
&lt;br /&gt;
#define VIRCDEV_IOHI        _IO(VIRCDEV_IOC_MAGIC, 0)&lt;br /&gt;
#define VIRCDEV_IOWBYTE     _IOW(VIRCDEV_IOC_MAGIC, 1, int)&lt;br /&gt;
#define VIRCDEV_IORBYTE     _IOR(VIRCDEV_IOC_MAGIC, 2, int)&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char *argv[])&lt;br /&gt;
{&lt;br /&gt;
    char cmd = *argv[1];&lt;br /&gt;
    int arg = *(argv[1]+1) - 48;&lt;br /&gt;
    int fd = 0;&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;cmd = %c, arg = %d\n&amp;quot;, cmd, arg);&lt;br /&gt;
&lt;br /&gt;
    fd = open(DEVICE_NAME, O_RDWR);&lt;br /&gt;
    switch(cmd)&lt;br /&gt;
    {&lt;br /&gt;
        case &amp;#039;h&amp;#039;:&lt;br /&gt;
            ioctl(fd, VIRCDEV_IOHI, arg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case &amp;#039;w&amp;#039;:&lt;br /&gt;
            ioctl(fd, VIRCDEV_IOWBYTE, arg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        case &amp;#039;r&amp;#039;:&lt;br /&gt;
            ioctl(fd, VIRCDEV_IORBYTE, &amp;amp;arg);&lt;br /&gt;
            printf(&amp;quot;read value:%d\n&amp;quot;, arg);&lt;br /&gt;
            break;&lt;br /&gt;
&lt;br /&gt;
        default:&lt;br /&gt;
            break;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    close(fd);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
函数首先是定义了之前在驱动模块vircdev.h中定义过的宏，这里只是拷贝过来，不过为规范起见，建议是直接将该头文件包含过来，另外由于使用了_IOW等宏，要包含&amp;quot;asm/ioctls.h&amp;quot;头文件。&lt;br /&gt;
&lt;br /&gt;
这个函数是取得用户在命令行输入的命令，然后控制对应的ioctl：&lt;br /&gt;
&lt;br /&gt;
h -- VIRCDEV_IOHI，使设备驱动打印消息&amp;quot;ioctl:you said hi.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
w -- VIRCDEV_IOWBYTE，写vircdev设备中的r1寄存器&lt;br /&gt;
&lt;br /&gt;
r -- VIRCDEV_IORBYTE，读vircdev设备中的r1寄存器&lt;br /&gt;
&lt;br /&gt;
编译：&lt;br /&gt;
 $:gcc gcc vircdev_ioctl_test.c&lt;br /&gt;
读寄存器(自然前提是vircdev驱动模块已载入到系统)：&lt;br /&gt;
 $:./a.out r1&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cmd = r, arg = 1&lt;br /&gt;
read value:9&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
写寄存器：&lt;br /&gt;
 $:./a.out w5&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cmd = w, arg = 5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
再读出当前寄存器的值：&lt;br /&gt;
 $:./a.out r1&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cmd = r, arg = 1&lt;br /&gt;
read value:5&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
与写入值相等，sucess!!!&lt;br /&gt;
&lt;br /&gt;
vircdev_ioctl_test.c下载：[[Image:vircdev_ioctl_test.c.zip]]&lt;/div&gt;</summary>
		<author><name>Hovercool</name></author>
	</entry>
</feed>