Category: 驱动

呃,内核报错

By ganquan, 2010年02月12日 19:08

剛才測試驅動程序的時候內核報錯了,呃,只是一個小問題。

我第一次造成內核錯誤,截圖紀念一下。以後仔細檢查了再加載模塊,不過我覺得做驅動的話肯定會經常造成內核報錯。。。。。


mini2440 button驱动分析

By ganquan, 2009年08月25日 01:34

基本上读懂了,有些几个地方还不明白。

由于wp-syntax插件显示代码实在让我看着眼睛难受,我决定换一种方式贴代码。点击下面的链接试试:

mini2440 button驱动注释

mini2440 leds驱动注释

mini2440 LED驱动详解

By ganquan, 2009年08月21日 21:22

从最简单的LED驱动入手,彻底细致的分析一遍mini2440的LED驱动。

官方手册上写,mini2440的四个LED与CPU的GPIO相连,LED1, LED2, LED3, LED4分别对应的
是GPB5, GPB6, GPB7, GPB8。那什么是GPIO呢?

GPIO是通用输入输出口的简称,只需要设置相应的CPU寄存器,就可以改变引脚的用途。
控制硬件,其实就是控制对应的寄存器。

四个LED的采用GPBCON寄存器上的4组2bit位来配置对应引脚的状态。4组2bit位的功能都
一样:00表示输入,01表示输出,10为特殊功能,11是保留的。
LED1对应的是GPB5, GPB5使用[11:10]位
LED2对应的是GPB6, GPB6使用[12:13]位
LED3对应的是GPB7, GPB7使用[14:15]位
LED4对应的是GPB8, GPB8使用[16:17]位
驱动需要先设置LED为输出状态,也就是要把对应的GPBX设置为01。

四个LED采用CPBDAT寄存器来对应4个LED的数值状态,GPBDAT5就对应GPB5,GPBDAT6就对
应GPB6,以此类推。手册上写低电平有效,就是说当GPBDAT寄存器位置为0时,LED就发光。

在三星官方的手册S3C2440.pdf中描述的寄存器状态如下:
GPB8 [17:16] 00 = Input 01 = Output
10 = nXDREQ1 11 = Reserved
GPB7 [15:14] 00 = Input 01 = Output
10 = nXDACK1 11 = Reserved
GPB6 [13:12] 00 = Input 01 = Output
10 = nXBREQ 11 = reserved
GPB5 [11:10] 00 = Input 01 = Output
10 = nXBACK 11 = reserved

GPBDAT Bit Description
GPB[10:0] [10:0] When the port is configured as input port, the corresponding
bit is the pin state. When the port is configured as output port, the pin state is the same
as the corresponding bit. When the port is configured as functional pin, the
undefined value will be read.

贴出添加注释后的代码,非常简单的驱动。

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
 
#define DEVICE_NAME "leds" /* /dev目录下的设备名 */
 
/* GPBX */
static unsigned long led_table [] = {
	S3C2410_GPB5,
	S3C2410_GPB6,
	S3C2410_GPB7,
	S3C2410_GPB8,
};
/* GPBX的输出状态 */
static unsigned int led_cfg_table [] = {
	S3C2410_GPB5_OUTP,
	S3C2410_GPB6_OUTP,
	S3C2410_GPB7_OUTP,
	S3C2410_GPB8_OUTP,
};
 
/* 驱动接口函数
* ioctl的内核空间版本和用户控件的版本不同
* 内核版为:
* int (*ioctl)( struct inode *inode, struct file *file, unsigned int
* cmd, unsigned long arg);
* */
static int sbc2440_leds_ioctl(
	struct inode *inode,
	struct file *file,
	unsigned int cmd,
	unsigned long arg)
{
	switch(cmd) {
	case 0:
	case 1:
		if (arg &gt; 4) {
			return -EINVAL; /* Invalid argument,非法参数 */
		}
       /* 设置数据寄存器GPBDAT
		* 低电平有效,用户程序传来的cmd取反
		* */
		s3c2410_gpio_setpin(led_table[arg], !cmd);
		return 0;
	default:
		return -EINVAL;
	}
}
 
/* 接口对象 */
static struct file_operations dev_fops = {
	.owner	=	THIS_MODULE,
	.ioctl	=	sbc2440_leds_ioctl,
};
 
/* 设备对象 */
static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR, /* 动态设备号 */
	.name = DEVICE_NAME, /* 将在/dev目录生成led设备 */
	.fops = &amp;dev_fops, /* 驱动接口 */
};
 
static int __init dev_init(void)
{
	int ret;
 
	int i;
 
	for (i = 0; i &lt; 4; i++) {
		/*设置GPIO对应的配置寄存器GPIOCON为输出状态*/
		s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
 
		/*设置GPIO对应的数据寄存器GPIODAT为低电平
		 *在模块加载结束后,四个LED应该是全部都是发光状态*/
		s3c2410_gpio_setpin(led_table[i], 0);
	}
 
	ret = misc_register(&amp;misc); /* 注册设备 */
 
	printk (DEVICE_NAME"\tinitialized\n"); /* dmesg */
 
	return ret;
}
 
static void __exit dev_exit(void)
{
	misc_deregister(&amp;misc);                     /* 注销设备 */
}
 
module_init(dev_init); /* 声明加载模块初始化函数 */
module_exit(dev_exit); /* 声明卸载模块清除函数 */
MODULE_LICENSE("GPL"); /* 许可证声明 */
MODULE_AUTHOR("FriendlyARM Inc."); /* 作者信息 */

USB驱动学习笔记(2)

By ganquan, 2009年07月1日 17:02

1.期末考终于结束了,这下可以看看USB驱动了。
上次看了几个基本的数据结构,现在看看函数都是干嘛的。驱动被加载之后调用了usb_register函数,从这个函数又转到了usb_register_driver() 函数,然后在usb_register_driver()中调用了一个名为driver_register()的函数,从名字来看,显然是为了用来注册驱动的,这个函数的传入参数就是封装在struct usbdrv_wrap里面的driver成员。
在看driver_register()函数之前,注意到usb_register_driver()函数中一句:

725          new_driver->drvwrap.driver.bus = &usb_bus_type;

这个bus就是驱动程序要去挂牌的地方。

现在进入driver_register()函数去看看。在driver_register() 中,调用了driver_find(drv->name, drv->bus)函数,这里是干啥呢?这里是先去驱动打算挂牌的地方先查找一下,这个驱动是否已经挂牌(注册)过了。在哪里查找呢?代码中写得很清楚,去bus->p->drivers_kset中查找,看到这里,我决定这里不要再继续展开细细的分析,否则永远都无法把内核看明白,有时候必须对细节很了解,有时候只要明白他的工作原理就可以,不必太细碎。简单的看,drivers_kset就是一个klist链表,链表的节点都是内核对象,也就是kobject。kset中的kobject可以是不同“类型”的(different “type”),kset只是把一类需要用进行相同处理的kobject放在一起。driver_find函数其实也不干活,它是调用kset_find_obj函数去遍历链表根据drv->name去寻找,我不管找的过程,反正如果找到了driver_find()就返回指向已经注册的驱动的指针,如果没找到就返回NULL。
回到driver_register() ,如果驱动已经注册过了,那就退出好了。如果没有注册,接下来调用bus_add_driver(struct device_driver *drv) 函数注册驱动。这个函数有点长,主要作用就是把drv放到drv->bus->p->klist_drivers链表的尾部。到此就完成注册工作。
阅读了一些老手的笔记,看了一点内核代码,看了LDD,再加上自己的猜测,现在对驱动的工作机制有了一点认识。
内核在维护两条链表,一个用来记录devices,一个用来记录drivers。系统接电开机后,总线就扫一遍有哪些设备接到计算机上了,每个设备都对应一个设备对象,然后就把设备对象填入到记录devices的链表中。接着系统继续启动,到了加载驱动的过程,把驱动对象都填入到记录drivers的链表中。每加载一个驱动,就去遍历一次devices链表,看看有没有自己要服务的设备,如果有,就把该设备对象的驱动指针指向自己,同时把该设备对象加入到驱动程序维护的设备链表中,该设备链表记录了这个驱动能够服务的设备。这是在开机之前硬件就和计算机连接好的工作方式,但是现在更实用的是另外一种工作方式,就是热插拔。
热插拔意味着设备和驱动没有谁先谁后进入系统,因为都有可能。所以工作方式就是一旦有一个设备插入,总线把对应的设备对象加入到记录devices的链表后就去查找那条记录drivers链表,看看有没有能够驱动这个设备的驱动,如果有驱动对象和设备对象就互相指向,这样驱动就能为该设备服务。如果没有找到驱动呢?很简单,设备不工作呗。这个设备就只好等待自己需要的驱动被加载,驱动会去找他。反过来说,驱动可能在设备插入之前被加载,同样的,驱动没有在记录devices的链表上找到她要服务的设备,驱动也就只好等待设备被插入的时候来找她。
那么下一步,就应该看一下驱动和设备的联系过程了。假期就这样顺着设备和驱动的工作流程,慢慢看吧。希望在暑假结束时,能够对Linux平台的USB驱动有比较深入的理解和认识,不求自己能写出实际可用的驱动来,只要能明白驱动的工作机制就算是假期的收获了。

USB驱动学习笔记(1)

By ganquan, 2009年06月17日 01:18

LDD 3rd 中文版实在是让我忍无可忍,USB驱动一章翻译得那是相当的晦涩,看得我云里雾里,索性扔下书来看代码,以后主要靠自己分析代码学习,多读网上前辈的笔记,这样不仅能加深对代码的理解,也看到了别人的思维角度,比抱着LDD 3rd啃强多了,开搞!

新手自学笔记,比较罗嗦,冗长,学到什么地方就写到什么地方,没有具体的层次和顺序。写这个笔记的目的,仅仅是当作备忘和一个过程的记录,如果高手看到了谬误之处,敬请斧正。

全部代码、笔记、实验都在Debian GNU/Linux 下进行,内核版本如下:

ganquan@debian:~$ uname -r

2.6.26-2-686

0.先从阅读usb-skeleton.c开始

ganquan@debian:~$ cat /usr/src/linux-source-2.6.26/drivers/usb/usb-skeleton.c | wc -l

525

还好,只有525行。不算吓人。

从驱动程序的Hello World开始看:

504    static int __init usb_skel_init(void)
 
505    {
 
506        int result;
 
507
 
508        /* register this driver with the USB subsystem */
 
509        result = usb_register(&skel_driver);
 
510        if (result)
 
511            err("usb_register failed. Error number %d", result);
 
512
 
513        return result;
 
514    }
 
515
 
516    static void __exit usb_skel_exit(void)
 
517    {
 
518        /* deregister this driver with the USB subsystem */
 
519        usb_deregister(&skel_driver);
 
520    }

显然的,504行的usb_skel_init(void)就是insmod之后被运行的初始化函数,它只干了一件事:调用usb_register函数,注册了一个叫skel_driver的东西,skel_driver是在usb-skeleton.c 文件67行定义的一个usb_driver类型的结构体。再来看看usb_driver到底是何方妖孽。

在linux-source-2.6.26/include/linux/usb.h里面,991行到1015行定义了这个结构体如下:

991 struct usb_driver {
 
992 const char *name;
 
993
 
994 int (*probe) (struct usb_interface *intf,
 
995 const struct usb_device_id *id);
 
996
 
997 void (*disconnect) (struct usb_interface *intf);
 
998
 
999 int (*ioctl) (struct usb_interface *intf, unsigned int code,
 
1000 void *buf);
 
1001
 
1002 int (*suspend) (struct usb_interface *intf, pm_message_t message);
 
1003 int (*resume) (struct usb_interface *intf);
 
1004 int (*reset_resume)(struct usb_interface *intf);
 
1005
 
1006 int (*pre_reset)(struct usb_interface *intf);
 
1007 int (*post_reset)(struct usb_interface *intf);
 
1008
 
1009 const struct usb_device_id *id_table;
 
1010
 
1011 struct usb_dynids dynids;
 
1012 struct usbdrv_wrap drvwrap;
 
1013 unsigned int no_dynamic_id:1;
 
1014 unsigned int supports_autosuspend:1;
 
1015 };

可见这个结构体包含了不少函数指针,通过名字大概猜了一下各个回调函数里面应该干啥,结构体中各个字段在内核代码中都有详细注释,不再一一叙述。总之,这个结构体现在看来,是用于标识我的USB驱动程序的。

搞清楚了传递给usb_register函数的参数,再看来看这个函数是干啥的。

还是在linux-source-2.6.26/include/linux/usb.h里面,有:

1075 static inline int usb_register(struct usb_driver *driver)
 
1076 {
 
1077 return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
 
1078 }

这个内联函数居然又调用了另外一个函数,内核真是一个蜘蛛网,一点也不假,继续看:

usb_register_driver函数,第一个参数是用来标识USB驱动程序的usb_driver类型的结构体,第二、三个参数都是宏,但是第二个参数其实是一个module类型的结构体,第三个参数是字符指针,这两个参数暂时不分析。先放着就可以了。

接下来看一下usb_register_driver函数又干了什么。

在linux-source-2.6.26/driver/usb/core/driver.c中:

715 int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
 
716 const char *mod_name)
 
717 {
 
718 int retval = 0;
 
719
 
720 if (usb_disabled())
 
721 return -ENODEV;
 
722
 
723 new_driver->drvwrap.for_devices = 0;
 
724 new_driver->drvwrap.driver.name = (char *) new_driver->name;
 
725 new_driver->drvwrap.driver.bus = &usb_bus_type;
 
726 new_driver->drvwrap.driver.probe = usb_probe_interface;
 
727 new_driver->drvwrap.driver.remove = usb_unbind_interface;
 
728 new_driver->drvwrap.driver.owner = owner;
 
729 new_driver->drvwrap.driver.mod_name = mod_name;
 
730 spin_lock_init(&new_driver->dynids.lock);
 
731 INIT_LIST_HEAD(&new_drive->dynids.list);
 
732
 
733 retval = driver_register(&new_driver->drvwrap.driver);
 
734
 
735 if (!retval) {
 
736 pr_info("%s: registered new interface driver %s\n",
 
737 usbcore_name, new_driver->name);
 
738 usbfs_update_special();
 
739 usb_create_newid_file(new_driver);
 
740 } else {
 
741 printk(KERN_ERR "%s: error %d registering interface "
 
742 " driver %s\n",
 
743 usbcore_name, retval, new_driver->name);
 
744 }
 
745
 
746 return retval;
 
747 }

以上可见usb_register_driver函数中对usb_driver结构体的一个成员赋值,也就是对drvwrap成员。drvwrap是一个结构体,函数中对它的driver成员的各个字段进行赋值,并且usb_register_driver函数中还调用了一个driver_register函数,传入参数是drvwap的driver字段,看来这个drvwap成员分量不轻,现在去探究一下 这个usbdrv_wrap类型的drvwrap又是什么。

在linux-source-2.6.26/include/linux/usb.h中:

934 struct usbdrv_wrap {
 
935 struct device_driver driver;
 
936 int for_devices;
 
937 };

可见,usbdrv_wrap只不过是一个对device_driver的封装,继续深入看一下device_driver又是什么。

在linux-source-2.6.26/include/linux/device.h中:

120 struct device_driver {
 
121 const char *name;
 
122 struct bus_type *bus;
 
123
 
124 struct module *owner;
 
125 const char *mod_name; /* used for built-in modules */
 
126
 
127 int (*probe) (struct device *dev);
 
128 int (*remove) (struct device *dev);
 
129 void (*shutdown) (struct device *dev);
 
130 int (*suspend) (struct device *dev, pm_message_t state);
 
131 int (*resume) (struct device *dev);
 
132 struct attribute_group **groups;
 
133
 
134 struct driver_private *p;
 
135 };

注意到device_driver的定义,134行又是一个driver_private结构体指针。非常有必要弄清楚这个结构体。

在linux-source-2.6.26/driver/base/base.h中,有:

30 struct driver_private {
 
31 struct kobject kobj;
 
32 struct klist klist_devices;
 
33 struct klist_node knode_bus;
 
34 struct module_kobject *mkobj;
 
35 struct device_driver *driver;
 
36 };

继续深入看看kobject又长什么样子。

在linux-source-2.6.26/include/linux/kobject.h中:

60 struct kobject {
 
61 const char *name;
 
62 struct kref kref;
 
63 struct list_head entry;
 
64 struct kobject *parent;
 
65 struct kset *kset;
 
66 struct kobj_type *ktype;
 
67 struct sysfs_dirent *sd;
 
68 unsigned int state_initialized:1;
 
69 unsigned int state_in_sysfs:1;
 
70 unsigned int state_add_uevent_sent:1;
 
71 unsigned int state_remove_uevent_sent:1;
 
72 };

到底了,为啥到底了。去看看linux-source-2.6.26/Document/kobject.txt就知道为啥到底了。

那么kobject到底是个啥?

kobject就是组成Linux的设备模型的基本数据结构。kobject的任务有:

(1)对内核对象的计数,对应数据结构就是struct kref

(2)sysfs表述

(3)数据结构关联

(4)热插拔处理

对usb_driver一层一层的拨开,最后走到了kobject。在内核设计中也用到了面向对象的思想,kobject就可以当作基类,其他的类都是从kobject继承的,由于C语言不能像C++那样继承,所以就一次又一次的封装。

我现在搞清楚了各个struct之间的关系:

kobject—>driver_private—>device_driver—>usbdrv_wrap—>usb_driver

总结他们之间的关系如下:

kobject是Linux设备模型的基本数据结构,它被作为“基类”,其他“子类”都是“继承”了kobject。在我分析的这个例子中,driver_private就是一个“子类”,它直接“继承”了kobject。事实上,老版本的内核(2.6.10)直接把kobject放在device_driver中,新版本的内核则是用driver_private来存放kobject,然后再把driver_private封装在device_driver中。device_driver中除了包含driver_private,还有很多函数指针,也就是驱动程序需要提供的服务。接下来,usbdrv_wrap再次封装了device_driver,这次封装很简单,没有增加过多额外的字段。最后usb_driver来封装usbdrv_wrap,usb_driver中也有很多的函数指针。

这些结构体的各个字段,具体作用是什么?尤其是最接近“基类”的那些“子类”的以k开头的字段,都用来实现什么功能?

驱动程序提供的服务又是什么样子,以及USB驱动程序在总线上的挂牌过程,这些都需要弄明白,下一篇笔记看看能不能弄清楚。

我的第一个Linux驱动程序

By ganquan, 2009年06月9日 23:02

实现一个虚拟设备“自增器”,该设备把写入的数据自增1。

ganquan@debian:~/Driver/01$ cat Add.h

#ifndef _MYDRIVER_H
#define _MYDRIVER_H

#include <linux/cdev.h>       /*cdev*/
#include <linux/types.h>    /*ssize_t*/

/*设备对象*/
struct Add
{
struct cdev MyChrDevice;    /*Char device structure*/
};

/*驱动程序要实现的函数*/
ssize_t Add_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t Add_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);

#endif    /*_MYDRIVER_H*/

ganquan@debian:~/Driver/01$ cat Add.c

#include <linux/init.h>			/*包含用于声明装载模块初始化和清除函数的宏*/
#include <linux/module.h>		/*包含装载模块需要的符号和函数定义*/
#include <linux/kernel.h>		/*printk()*/
#include <linux/fs.h>			/*申请设备号函数和数据结构,file_operation*/
#include <linux/types.h>		/*dev_t*/
#include <linux/kdev_t.h>		/*包含获取主设备号和次设备号的宏*/
#include <linux/slab.h>			/*kmalloc()*/
#include <linux/cdev.h>			/*cdev,cdev_init(),cdev_add()*/
#include <asm/uaccess.h>		/*copy_to_user(),copy_from_user()*/

#include "Add.h"

static int global_var = 0;
int Add_major = 0;	/*主设备号*/
int Add_minor = 0;	/*次设备号*/

struct Add *mydev;

struct file_operations Add_fops = {
	.owner = THIS_MODULE,
	.read = Add_read,
	.write = Add_write,
};

ssize_t Add_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	if(copy_to_user(buf,&global_var,sizeof(int)))
	{
		return -EFAULT;
	}

	return sizeof(int);
}
ssize_t Add_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
	if(copy_from_user(&global_var,buf,sizeof(int)))
	{
		return -EFAULT;
	}
	if(global_var != 0)
		global_var++;

	return sizeof(int);
}

static int MyDriver_init(void)
{
	int ret;
	int devno;
	dev_t dev;

	/*申请设备号*/
	ret = alloc_chrdev_region(&dev,0,1,"Add");
	if(ret < 0)
	{
		printk(KERN_ALERT "Can not get dev_t\n");
	}
	else
	{
		Add_major = MAJOR(dev);		/*MAJOR in <linux/kdev_t.h>*/
		Add_minor = MINOR(dev);		/*MINOR in <linux/kdev_t.h>*/
	}
	/*申请设备号结束*/

	/*分配设备内存空间*/
	mydev = kmalloc(sizeof(struct Add),GFP_KERNEL);
	if(!mydev)
	{
		printk(KERN_ALERT "kmalloc error\n");
	}
	/*分配内存空间结束*/

	/*初始化内存空间*/
	memset(mydev,0,sizeof(struct Add));

	/*注册字符设备*/
	devno = MKDEV(Add_major,Add_minor);
	cdev_init(&mydev->MyChrDevice,&Add_fops);
	mydev->MyChrDevice.owner = THIS_MODULE;
	mydev->MyChrDevice.ops = &Add_fops;
	ret = cdev_add(&mydev->MyChrDevice,devno,1);
	if(ret)
		printk(KERN_ALERT "Error %d adding Add\n",ret);
	/*注册字符设备结束*/
	return 0;
}

static void MyDriver_exit(void)
{
	dev_t devno;

    devno = MKDEV(Add_major,Add_minor);
	cdev_del(&mydev->MyChrDevice);
	unregister_chrdev_region(devno,1);
	kfree(mydev);
}

module_init(MyDriver_init);	/*声明模块加载初始化函数*/
module_exit(MyDriver_exit);	/*声明模块卸载清除函数*/

MODULE_LICENSE("GPL");		/*驱动许可证协议*/
MODULE_AUTHOR("GanQuan");	/*作者*/
MODULE_VERSION("0.0.1");	/*版本号*/

不变的Makefile:
ganquan@debian:~/Driver/01$ cat Makefile

obj-m := Add.o
	KDIR ?= /lib/modules/$(shell uname -r)/build
	PWD := $(shell pwd)
all:
	    $(MAKE) -C $(KDIR) M=$(PWD)

.PHONY:clean
clean:
	    rm -f *.mod.c *.mod.o *.ko *.o *.tmp_versions

驱动加载脚本:
ganquan@debian:~/Driver/01$ cat addload

#!/bin/bash
#author ganqan
#	imganquan@gmail.com
#load a module

module="Add"
device="Add"
mode="777"

/sbin/insmod ./${module}.ko || exit 1

major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)

mknod /dev/${module} c $major 0
chmod $mode /dev/${device}

devinfo=`ls -l /dev/${device}`

echo "insmod	:$module.ko"
echo "major	:$major"
echo "devinfo	:$devinfo"

驱动卸载脚本:
ganquan@debian:~/Driver/01$ cat addunload

#!/bin/bash
#author ganquan
#	imganquan@gmail.com
#unload a module

module="Add"
device="Add"
/sbin/rmmod $module || exit 1
rm -f /dev/${device}

很简单的测试程序:
ganquan@debian:~/Driver/01$ cat test.c

#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
int main()
{
	int fd,num;
	fd = open("/dev/Add", O_RDWR, S_IRUSR | S_IWUSR);
	if (fd != -1)
	{
		read(fd, &num, sizeof(int));
		printf("The MyDevice is %d\n", num);

		printf("Please input the num written to MyDevice\n");
		scanf("%d", &num);
		write(fd, &num, sizeof(int));
		read(fd, &num, sizeof(int));
		printf("The MyDevice is %d\n", num);
		close(fd);
	}
	else
		printf("Device open failure\n");
		return 0;
}

测试结果:

ganquan@debian:~/Driver/01$ sudo ./addload
insmod	:Add.ko
major	:249
devinfo	:crwxrwxrwx 1 root root 249, 0 2009-06-09 22:55 /dev/Add
ganquan@debian:~/Driver/01$ ./test
The MyDevice is 0
Please input the num written to MyDevice
7
The MyDevice is 8
ganquan@debian:~/Driver/01$ sudo ./addunload

Panorama Theme by Themocracy