荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: autodotcom (cpu8086), 信区: Linux
标 题: 设备分析2
发信站: 荔园晨风BBS站 (Sat Apr 27 13:11:51 2002), 转信
发信人: fist (星仔迷), 信区: SysInternals
标 题: Linux 设备分析(2)
发信站: 武汉白云黄鹤站 (Wed Dec 13 10:22:06 2000), 站内信件
五. 添加一个字符设备
作为对linux设备管理的分析的总结,我们介绍一下如何添加一个设备,首先介绍
如何添
加一个字符设备。在后面的文章中,我们将新添加的设备称为新设备,说明以我们
实现
的虚拟的字符设备为例,步骤基本如下:
1. 确定设备的设备名称和主设备号:
我们必须找一个还没有被使用的主设备号,分配给自己的字符设备。假设主设备号
为30
(在2.0.34的内核中还没有以30作为主设备号的字符设备)。
2. 确定编写需要的file_operations中的操作函数,包括:
static int my_open(struct inode * inode,struct file * file)
//通过宏指令MINOR()提取inode参数的I_rdev字段,确定辅助设备号,然后
检查相应的读写忙标志,看新设备是否已经打开。如果是,返回错误信息;
否则置读写忙标志为true,阻止再次打开新设备。
static void my_release(struct inode * inode,struct file * file)
//同my_open类似,只是置读写忙标志为false,允许再次打开新设备。
static int my _write(struct inode * inode,struct file * file,const
char * bu
ffer,int count)
//用于对该设备的写
static int my _read(struct inode * inode , struct file * file,char *
buffer,
int count)
//用于对该设备的读
static int my_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
//用于传送特殊的控制信息给设备驱动程序,或者长设备驱动程
序取得状
态信息,
在我们实现的虚拟字符设备中,这个函数的功能是用来打开和关闭跟踪功
能。
3. 确定编写需要的初始化函数:
void my_init(void)
//首先需要将上述的file_operations中的操作函数的地址赋给某个
file_operations
的结构变量my_fops中的相应域;
然后调用标准内核函数登记该设备:register_chrdev(30,"mychd",&my_fops);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
最后对必要的变量(例如读写忙标志、跟踪标志等)赋初值。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4. 在drivers/char/mem.c中添加相应语句;
在chr_dev_init函数之前添加drgn_init的原型说明:
void my_init (void);
在chr_dev_init函数的return语句之前添加以下语句:
my_init (); //用于在字符设备初始化时初始化新设备
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. 修改drivers/char/Makefile;
~~~~~~~~~~~~~~~~~~~~~~~~~~
假设我们把所以必要的函数写mychd.c中,则找到"L_OBJS := tty_io.o n_tty.o
con
sole.o \"行,将"mychd.o"加到其中。
6. 将该设备私有的*.c,*.h复制到目录drivers/char下。
7. 用命令:make clean;make dep;make zImage重新编译内核。
8. 用mknod命令在目录/dev下建立相应主设备号的用于读写的特殊文件。
完成了上述步骤,你在linux环境下编程时就可以使用新设备了。
六. 添加一个块设备
接下来我们将介绍如何添加一个块设备。在后面的文章中,我们将新添加的块设备
称为
新设备,块设备的添加过程和字符设备有相似之处,我们将主要介绍其不同点,步
骤基
本如下:
1. 确定设备的设备名称和主设备号
我们必须找一个还没有被使用的主设备号,分配给自己的新设备。假设主设备号为
30(
在2.0.34的内核中还没有以30作为主设备号的块设备),则需要在
include/linux/majo
r.h中加入如下句:
#define MY_MAJOR 30
这样我们可以通过MY_MAJOR来确定设备为新设备,保证通用性。
2. 确定编写需要的file_operations中的操作函数:
static int my_open(struct inode * inode,struct file * file)
static void my_release(struct inode * inode,struct file * file)
static int my_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
由于使用了高速缓存,块设备驱动程序就不需要包含自己的read()、write()和
fsync()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
函数,但必须使用自己的open()、 release()和 ioctl()函数,这些函数的作用和
字符
设备的相应函数类似。
3. 确定编写需要的输入/输出函数:
static int my _read(void) //正确处理时返回值为1,错误时返
回值为0
static int my _write(void) //正确处理时返回值为1,错误时返回值为0
值得注意的是这两个函数和字符设备中的my read()、mywrite()函数不同:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 参数不同:字符设备中的函数是带参数的,用于对其工作空间(也可以看成是简
单的
~~~~~~~~~~~`
缓冲区)中的一定长度(长度也是参数传递的)的字符进行读写;而块设备的函数
是没
有参数的,它通过当前请求中的信息访问高速缓存中的相应的块,因此不需要参数
输入
。
2 调用形式不同:字符设备中的函数地址是存放在file_operations中的,在对字
符设备
~~~~~~~~~~~~~~
进行读写时就调用了;而块设备的读写函数是在需要进行实际I/O时在request中调
用。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
这在后面可以看到。
4. 确定编写需要的请求处理函数:
~~~~~~~~~~~~~~~~~~~~~~~~~
static void my_request(void)
在块设备驱动程序中,不带中断服务子程序的请求处理函数是简单的,典型的格式
如下
:
static void my_request(void)
{
loop:
INIT_REQUEST;
if (MINOR(CURRENT->dev)>MY_MINOR_MAX)
{
end_request(0);
goto loop;
}
if (CURRENT ->cmd==READ)
//CUREENT是指向请求队列头的request结构指针
{
end_request(my_read()); //my_read()在前面已经定
义
goto loop;
}
if (CURRENT ->cmd==WRITE)
{
end_request(my_write()); //my_write()在前面已经定
义
goto loop;
}
end_request(0);
goto loop;
}
实际上,一个真正的块设备一般不可能没有中断服务子程序,另外设备驱动程序是
在系
统调用中被调用的,这时由内核程序控制CPU,因此不能抢占,只能自愿放弃;因
此驱动
程序必须调用sleep_on()函数,释放对CPU的占用;在中断服务子程序将所需的数
据复制
到内核内存后,再由它来发出wake_up()调用,
5. 如果需要,编写中断服务子程序
实际上,一个真正的块设备一般不可能没有中断服务子程序,另外设备驱动程序是
在系
统调用中被调用的,这时由内核程序控制CPU,因此不能抢占,只能自愿放弃;因
此驱动
程序必须调用sleep_on()函数,释放对CPU的占用;在中断服务子程序将所需的数
据复制
到内核内存后,再由它来发出wake_up()调用。
另外两段中断服务子程序都要访问和修改特定的内核数据结构时,必须要仔细协调
,以
防止出现灾难性的后果。
2 首先,在必要时可以禁止中断,这可以通过sti()和cli()来允许和禁止中断请求
。
2 其次,修改特定的内核数据结构的程序段要尽可能的短,使中断不至于延时过长
。
含有中断服务子程序的块设备驱动程序的编写相对比较复杂,我们还没有完全实现
,主
要问题是在中断处理之间的协调。因为这些程序是要加入内核的,系统默认为你是
完全
正确的,如果引起循环或者中断长时间不响应,结果非常严重。我们正在努力实现
这一
程序。
6. 确定编写需要的初始化函数:
void my_init(void)
需要将的file_operations中的操作函数的地址赋给某个file_operations的结构变
量my
_fops中的相应域;一个典型的形式是:
struct file_operations my_fops=
{
0,
block_read,
block_write,
0,
0,
my_ioctl,
0,
my_open,
my_release,
block_fsync,
0,
0,
0,
}
my_init中需要作的工作有:
2 首先调用标准内核函数登记该设备:
register_chrdev(MY_MOJOR,"my--bdev",&my_fops);
2 将request()函数的地址告诉内核:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
blk_dev[MY_MAJOR].request_fn=DEVICE_REQUEST;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DEVICE_REQUEST是请求处理函数的地址,它的定义将在稍后可以看到。
2 告诉新设备的高速缓存的数据块的块大小:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
my_block_size=512; //也可以是1024等
blksize_size[MY_MAJOR]=& my_block_size;
为了系统在初始化时能够对新设备进行初始化,需要在blk_dev_init()中添加一行
代码
,可以插在blk_dev_init()中return 0的前面,格式为:
my_init();
7. 在include/linux/blk.h中添加相应语句;
到目前为止,除了DEVICE_REQUEST符合外,还没有告诉内核到那里去找你的
request()函
数,为此需要将一些宏定义加到blk.h中。在blk.h中找到类似的一行:
#endif /*MAJOR_NR==whatever */
在这行前面加入如下宏定义:
#elif (MAJOR_NR==whatever)
static void my_request(void);
#define DEVICE_NAME "MY_BLK_DEV" //驱动程序名称
#define DEVICE_REQUEST my_request //request()函数指针
#define DEVIEC_NR(device) (MINOR(device)) //计算实际设备号
#define DEVIEC_ON(device) //用于需
要打开的
设备
#define DEVIEC_OFF(device) //用于需要关闭的
设备
8. 修改drivers/block/Makefile;
假设我们把所以必要的函数写mybd.c中,则找到"L_OBJS := tty_io.o n_tty.
o cons
ole.o \"行,将"mybd.o"加到其中。
10. 用命令:make clean;make dep;make zImage重新编译内核。
11. 用mknod命令在目录/dev下建立相应主设备号的用于读写的特殊文件。
完成了上述步骤,你在linux环境下编程时就可以使用新设备了。
七. 一个虚拟的字符设备驱动程序
以下是一个虚拟的字符设备驱动程序,该程序是我和潘刚同学的试验结果,本来我
们还
打算写一个虚拟的块设备驱动程序,由于时间关系,没有能够完全明白中断中断服
务子
程序的编写方法,因此没有没有能够实现一个可以所有的虚拟的块设备,非常遗憾
。不
过主要步骤已经在上文中进行了介绍,我想再有一段时间应该能够完成,到时候一
定交
给李老师看一下。
虚拟的字符设备驱动程序如下,在潘刚同学的试验报告中也有介绍:
/* drgn.h */
#ifdef KERNEL
#define TRACE_TXT(text) {if(drgn_trace) {console_print(text);
console_print("
\n");}}
#define TRACE_CHR(chr) {if(drgn_trace) console_print(chr);}
#define DRGN_READ 1
#define DRGN_WRITE 0
#endif
#define FALSE 0
#define TRUE 1
#define MAX_BUF 120
#define DRGN_TRON (('M' << 8)|0x01)
#define DRGN_TROFF (('M' << 8)|0x02)
struct drgn_buf
{
int buf_size;
char buffer[MAX_BUF];
struct drgn_buf *link;
};
/* drgn.c */
#define KERNEL
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/irq.h>
#include "drgn.h"
static int drgn_trace;
static int write_busy;
static int read_busy;
static struct drgn_buf * qhead;
static struct drgn_buf * qtail;
static int drgn_read(struct inode * , struct file * , char * , int );
static int drgn_write(struct inode * , struct file * , const char *,
int );
static int drgn_ioctl(struct inode * , struct file * , unsigned int ,
unsig
ned long );
static int drgn_open(struct inode *,struct file *);
static void drgn_release(struct inode *,struct file *);
/* extern void console_print(char *);*/
struct file_operations drgn_fops=
{
NULL,
drgn_read,
drgn_write,
NULL,
NULL,
drgn_ioctl,
NULL,
drgn_open,
drgn_release,
NULL,
NULL,
NULL,
NULL
};
void drgn_init(void)
{
drgn_trace=TRUE;
if(register_chrdev(30,"drgn",&drgn_fops))
TRACE_TXT("Cannot register drgn driver as major device 30.")
else
TRACE_TXT("Tiny devie driver registered successfully.")
qhead=0;
write_busy=FALSE;
read_busy=FALSE;
/* drgn_trace=FALSE;*/
return;
}
static int drgn_open(struct inode * inode,struct file * file)
{
TRACE_TXT("drgn_open")
switch (MINOR(inode->i_rdev))
{
case DRGN_WRITE:
if(write_busy)
return -EBUSY;
else{
write_busy=TRUE;
return 0;
}
case DRGN_READ:
if(read_busy)
return -EBUSY;
else{
read_busy=TRUE;
return 0;
}
default:
return -ENXIO;
}
}
static void drgn_release(struct inode * inode,struct file * file)
{
TRACE_TXT("drgn_release")
switch (MINOR(inode->i_rdev))
{
case DRGN_WRITE:
write_busy=FALSE;
return;
case DRGN_READ:
read_busy=FALSE;
return;
}
}
static int drgn_write(struct inode * inode,struct file * file,
const char * buffer,int count)
{
int i,len;
struct drgn_buf * ptr;
TRACE_TXT("drgn_write")
if (MINOR(inode->i_rdev)!=DRGN_WRITE)
return -EINVAL;
if ((ptr=kmalloc(sizeof(struct drgn_buf),GFP_KERNEL))==0)
return -ENOMEM;
len=count < MAX_BUF?count:MAX_BUF;
if (verify_area(VERIFY_READ,buffer,len))
return -EFAULT;
for(i=0;i < count && i<MAX_BUF;++i)
{
ptr->buffer[i]=(char) get_user((char*)(buffer+i));
TRACE_CHR("w")
}
ptr->link=0;
if(qhead==0)
qhead=ptr;
else
qtail->link=ptr;
qtail=ptr;
TRACE_CHR("\n")
ptr->buf_size=i;
return i;
}
static int drgn_read(struct inode * inode , struct file * file,
char * buffer, int count)
{
int i,len;
struct drgn_buf * ptr;
TRACE_TXT("drgn_read")
if(MINOR(inode->i_rdev)!=DRGN_READ)
return -EINVAL;
if (qhead==0)
return -ENODATA;
ptr=qhead;
qhead=qhead->link;
len=count < ptr->buf_size?count:ptr->buf_size;
if (verify_area(VERIFY_WRITE,buffer,len))
return -EFAULT;
for (i=0; i<count && i<ptr->buf_size; ++i)
{
put_user((char) ptr->buffer[i],(char *)(buffer+i));
TRACE_CHR("r")
}
TRACE_CHR("\n")
kfree_s(ptr,sizeof(struct drgn_buf));
return i;
}
static int drgn_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
TRACE_TXT("drgn_ioctl")
/* if (cmd==DRGN_TRON){
drgn_trace=TRUE;
return 0;
}
else
if (cmd==DRGN_TROFF){
drgn_trace=FALSE;
return 0;
}
else
return -EINVAL;*/
switch(cmd)
{
case DRGN_TRON:
drgn_trace=TRUE;
return 0;
case DRGN_TROFF:
drgn_trace=FALSE;
return 0;
default:
return -EINVAL;
}
else
if (cmd==DRGN_TROFF){
drgn_trace=FALSE;
return 0;
}
else
return -EINVAL;*/
switch(cmd)
{
case DRGN_TRON:
drgn_trace=TRUE;
return 0;
case DRGN_TROFF:
drgn_trace=FALSE;
return 0;
default:
return -EINVAL;
}
}
--
※ 来源:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: 202.114.1.60]
--
-再见了
-我爱的那个人
-从此,我将与DDK独行
※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.55.48]
[回到开始]
[上一篇][下一篇]
荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店