姓名:薛紹宏? ? ?學(xué)號:19020100016? ? 學(xué)院:電子工程學(xué)院
【嵌牛導(dǎo)讀】本文介紹了嵌入式Linux驅(qū)動(dòng)程序開發(fā)的一些實(shí)例和剖析
【嵌牛鼻子】嵌入式Linux驅(qū)動(dòng)程序開發(fā)
【嵌牛提問】嵌入式Linux驅(qū)動(dòng)程序開發(fā)實(shí)例如何剖析?
【嵌牛正文】
9.實(shí)例剖析
/******************************
*LED_Driver 2007/09/20
*****************************/
#include<linux/config.h>
#include<linux/kernel.h>
#include<linux/sched.h>
#include<linux/timer.h>
#include<linux/init.h>
#include<linux/module.h>
#include<asm/hardware.h>
#defineGPIO_LED_MAJOR 97
#defineARM_GPIO_LED_DEBUG
#defineARM_GPIO_LED (GPIO96)
#defineLED_ON 0
#defineLED_OFF 1
#definectl_GPIO_LED1 1
#defineVERSION "ARM_GPIO_LED_2007/09/20"
voidshowversion(void)
{
printk("*********************************************\n");
printk("\t %s \t\n",VERSION);
printk("********************************************\n\n");
}
//------------------- READ ------------------------
ssize_tGPIO_LED_read (struct file * file ,char * buf, size_t count, loff_t * f_ops)
{
#ifdef ARM_GPIO_LED_DEBUG
printk ("GPIO_LED_read [--kernel--]\n");
#endif
return count;
}
//------------------- WRITE -----------------------
ssize_tGPIO_LED_write (struct file * file ,const char * buf, size_t count, loff_t *f_ops)
{
#ifdef ARM_GPIO_LED_DEBUG
printk("GPIO_LED_write [ --kernel--]\n");
#endif
return count;
}
//------------------- IOCTL -----------------------
ssize_tGPIO_LED_ioctl (struct inode * inode ,struct file * file, unsigned int cmd,long data)
{
#ifdef ARM_GPIO_LED_DEBUG
printk("GPIO_LED_ioctl [ --kernel--]\n");
#endif
switch (cmd)
{
case LED_ON : {GPCR3 |= 0x1;break;}
case LED_OFF: {GPSR3 |= 0x1;break;}
default :
{printk ("lcdcontrol : no cmd run [ --kernel--]\n"); return (-EINVAL);}
}
return 0;
}
//------------------- OPEN ------------------------
ssize_tGPIO_LED_open (struct inode * inode ,struct file * file)
{
#ifdef ARM_GPIO_LED_DEBUG
printk("GPIO_LED_open [ --kernel--]\n");
#endif
MOD_INC_USE_COUNT;
return 0;
}
//------------------- RELEASE/CLOSE ---------------
ssize_tGPIO_LED_release (struct inode * inode,struct file * file)
{
#ifdef ARM_GPIO_LED_DEBUG
printk ("GPIO_LED_release [ --kernel--]\n");
#endif
MOD_DEC_USE_COUNT;
return 0;
}
//-------------------------------------------------
structfile_operations GPIO_LED_ctl_ops ={
open: GPIO_LED_open,
read: GPIO_LED_read,
write: GPIO_LED_write,
ioctl: GPIO_LED_ioctl,
release: GPIO_LED_release,
};
//------------------- INIT ------------------------
staticint __init HW_GPIO_LED_CTL_init(void)
{
int ret = -ENODEV;
printk("Driver Loding.....................\n\n");
showversion();
// init GPIO
GPDR3 |= 0x00000001; // SET GPIO96 OUTPUTMODE
GPSR3 |= 0x00000001; // OFF THE LED
#ifdef ARM_GPIO_LED_DEBUG
printk (" GPLR3=%x\n",GPLR3);
printk (" GPDR3=%x \n",GPDR3);
#endif
ret = devfs_register_chrdev(GPIO_LED_MAJOR,"led_drv", &GPIO_LED_ctl_ops);
if( ret < 0 )
{
printk (" ARM: init_module failedwith %d\n [ --kernel--]", ret);
return ret;
}
else
{
printk(" ARM gpio_led_driverregister success!!! [ --kernel--]\n");
}
return ret;
}
staticint __init ARM_GPIO_LED_CTL_init(void)
{
intret = -ENODEV;
#ifdef ARM_GPIO_LED_DEBUG
printk("ARM_GPIO_LED_CTL_init [ --kernel--]\n");
#endif
ret = HW_GPIO_LED_CTL_init();
if (ret)
return ret;
return 0;
}
staticvoid __exit cleanup_GPIO_LED_ctl(void)
{
#ifdef ARM_GPIO_LED_DEBUG
printk("cleanup_GPIO_LED_ctl [ --kernel--]\n");
#endif
devfs_unregister_chrdev (GPIO_LED_MAJOR,"gpio_led_ctl" );
}
MODULE_DESCRIPTION("GPIO_led driver module");
MODULE_AUTHOR("zsm");
MODULE_LICENSE("GPL");//GPL協(xié)議證書信息
module_init(ARM_GPIO_LED_CTL_init);
module_exit(cleanup_GPIO_LED_ctl);
在編寫用戶應(yīng)用程序過程中,考慮通過接口open()函數(shù)打開設(shè)備,再通過接口ioctl()函數(shù)來實(shí)現(xiàn)對LED的控制功能。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> // open() close()
#include <unistd.h> // read() write()
#define DEVICE_NAME "/dev/led_drv"
#define LED_ON0
#define LED_OFF 1
int main(void)
{
intfd;
int ret;
char *i;
printf("\nstart GPIO_led_driver test\n\n");
fd= open(DEVICE_NAME, O_RDWR);
printf("fd= %d\n",fd);
if(fd == -1)
{
printf("open device %s error\n",DEVICE_NAME);
}
else
{
while(1)
{ ioctl(fd,LED_OFF);
sleep(1);
ioctl(fd,LED_ON);
sleep(1);
}
ret =close(fd);
printf("ret=%d\n",ret);
printf("close led_driver test\n");
}
return 0;
}
(1)Create:
[root@OURSELEC usb]# mknod /dev/led_drv c 97 0
(2)view:
[root@OURSELEC usb]# ls -l /dev/led_drv
crw-r--r--1 root root 97,0 Jan 1 01:29 /dev/led_drv
(3)insmod:
[root@OURSELEC usb]# insmod led_drv.o
Using led_drv.o
ARM_GPIO_LED_CTL_init [ --kernel--]
Driver Loding .....................
*********************************************
ARM_GPIO_LED_2007/09/20
*********************************************
GPLR3=73e7fd
GPDR3=1efffc3
ARMgpio_led_driver register success!!! [ --kernel--]
(4)./test_led:
[root@OURSELEC usb]# ./test_led
startGPIO_led_driver test
GPIO_LED_open[ --kernel--]
fd = 3
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_ioctl [ --kernel--]
GPIO_LED_release [ --kernel--]
(5) remove led_drv mode:
[root@OURSELEC usb]# rmmod led_drv
cleanup_GPIO_LED_ctl [ --kernel--]
/*************************************
NAME:gt2440_leds.c
COPYRIGHT:www.e-online.cc
*************************************/
#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"
/* 應(yīng)用程序執(zhí)行ioctl(fd, cmd, arg)時(shí)的第2個(gè)參數(shù) */
#define IOCTL_LED_ON 1
#define IOCTL_LED_OFF 0
/* 用來指定LED所用的GPIO引腳 */
static unsigned long led_table [] =
{
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
/* 用來指定GPIO引腳的功能:輸出 */
static unsigned int led_cfg_table [] =
{
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
static int gt2440_leds_ioctl(
structinode *inode,
structfile *file,
unsignedint cmd,
unsignedlong arg)
{
if(arg > 4)
{
return-EINVAL;
}
switch(cmd)
{
caseIOCTL_LED_ON:
//設(shè)置指定引腳的輸出電平為0
s3c2410_gpio_setpin(led_table[arg], 0);
return0;
caseIOCTL_LED_OFF:
//設(shè)置指定引腳的輸出電平為1
s3c2410_gpio_setpin(led_table[arg], 1);
return0;
default:
return-EINVAL;
}
}
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = gt2440_leds_ioctl,
};
static struct miscdevice misc = {
.minor= MISC_DYNAMIC_MINOR,
.name= DEVICE_NAME,
.fops= &dev_fops,
};
static int __init dev_init(void)
{
intret;
inti;
for(i = 0; i < 4; i++)
{
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}
ret= misc_register(&misc);
printk(DEVICE_NAME" initialized\n");
returnret;
}
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.e-online.cc");
MODULE_DESCRIPTION("LEDS control forGT2440 Board");
塊設(shè)備文件通常指一些需要以塊(如512字節(jié))的方式寫入的設(shè)備,如IDE硬盤、SCSI硬盤、光驅(qū)等。它的驅(qū)動(dòng)程序的編寫過程與字符型設(shè)備驅(qū)動(dòng)程序的編寫有很大的區(qū)別。
為了把各種塊設(shè)備的操作請求隊(duì)列有效地組織起來,內(nèi)核中設(shè)置了一個(gè)結(jié)構(gòu)數(shù)組blk_dev,該數(shù)組中的元素類型是blk_dev_struct結(jié)構(gòu)。這個(gè)結(jié)構(gòu)由三個(gè)成分組成,其主體是執(zhí)行操作的請求隊(duì)列request_queue,還有一個(gè)函數(shù)指針queue。當(dāng)這個(gè)指針不為0時(shí),就調(diào)用這個(gè)函數(shù)來找到具體設(shè)備的請求隊(duì)列。塊設(shè)備驅(qū)動(dòng)程序描述符是一個(gè)包含在<linux/blkdev.h>中的blk_dev_struct類型的數(shù)據(jù)結(jié)構(gòu),其定義如下所示:
struct blk_dev_struct {
request_queue_t request_queue;
queue_proc *queue;
void *date;
};
在這個(gè)結(jié)構(gòu)中,請求隊(duì)列request_queue是主體,包含了初始化之后的I/O 請求隊(duì)列。
所有塊設(shè)備的描述符都存放在blk_dev表struct blk_dev_structblk_dev[MAX_BLKDEV]中;每個(gè)塊設(shè)備都對應(yīng)著數(shù)組中的一項(xiàng),可以使用主設(shè)備號進(jìn)行檢索。每當(dāng)用戶進(jìn)程對一個(gè)塊設(shè)備發(fā)出一個(gè)讀寫請求時(shí),首先調(diào)用塊設(shè)備所公用的函數(shù)generic_file_read(),generic_file_write()。如果數(shù)據(jù)存在在緩沖區(qū)中或緩沖區(qū)還可以存放數(shù)據(jù),那么就同緩沖區(qū)進(jìn)行數(shù)據(jù)交換。否則,系統(tǒng)會(huì)將相應(yīng)的請求隊(duì)列結(jié)構(gòu)添加到其對應(yīng)項(xiàng)的blk_dev_struct中,如下圖所示:
塊設(shè)備驅(qū)動(dòng)程序的編寫流程同字符設(shè)備驅(qū)動(dòng)程序的編寫流程很類似,也包括了注冊和使
用兩部分。但與字符驅(qū)動(dòng)設(shè)備所不同的是,塊設(shè)備驅(qū)動(dòng)程序包括一個(gè)request請求隊(duì)列。它是
當(dāng)內(nèi)核安排一次數(shù)據(jù)傳輸時(shí)在列表中的一個(gè)請求隊(duì)列,用以最大化系統(tǒng)性能為原則進(jìn)行排序。
塊設(shè)備驅(qū)動(dòng)程序流程圖
Linux系統(tǒng)中有一個(gè)名為blkdevs的結(jié)構(gòu)數(shù)組,它描述了一系列在系統(tǒng)中登記的塊設(shè)備。數(shù)組blkdevs也使用設(shè)備的主設(shè)備號作為索引,其元素類型是device_struct結(jié)構(gòu)。該結(jié)構(gòu)中包括指向已登記的設(shè)備驅(qū)動(dòng)程序名的指針和指向block_device_operations結(jié)構(gòu)的指針。在block_device_operations結(jié)構(gòu)中包含指向有關(guān)操作的函數(shù)指針。所以,該結(jié)構(gòu)就是連接抽象的塊設(shè)備操作與具體塊設(shè)備類型的操作之間的樞紐。
一個(gè)bio結(jié)構(gòu)體是在通用塊層或更底層對塊設(shè)備i/o操作的的表示單位。通常1個(gè)bio對應(yīng)1個(gè)I/O請求.
struct bio {
sector_tbi_sector; /* device address in 512 byte
sectors */
structbio *bi_next; /*request queue link */
structblock_device *bi_bdev;
unsignedlongbi_flags; /* status, command, etc */
unsignedlongbi_rw; /* bottom bits READ/WRITE,
* top bits priority
*/
unsignedshort bi_vcnt; /*how many bio_vec's */
unsignedshort bi_idx;/* current index into bvl_vec */
/* Numberofsegments in this BIO after
*physical address coalescing is performed.
*/
unsignedint bi_phys_segments;
unsignedint bi_size; /*residual I/O count */
/*
* Tokeep track of the max segment size, weaccount for the
*sizes of the first and last mergeablesegments in this bio.
*/
unsignedint bi_seg_front_size;
unsignedint bi_seg_back_size;
unsignedintbi_max_vecs; /* max bvl_vecs we can hold */
unsignedintbi_comp_cpu; /* completion CPU */
atomic_tbi_cnt; /*pin count*/
structbio_vec*bi_io_vec; /* the actual vec list */
bio_end_io_t*bi_end_io;
void*bi_private;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
structbio_integrity_payload *bi_integrity; /*data integrity */
#endif
bio_destructor_t *bi_destructor; /* destructor */
/*
* Wecan inline a number of vecs at the end ofthe bio, to avoid
*double allocations for a small number ofbio_vecs. This member
* MUSTobviously be kept at the very end ofthe bio.
*/
structbio_vec bi_inline_vecs[0];
};
struct gendisk { //表示一個(gè)獨(dú)立的磁盤設(shè)備或分區(qū)
intmajor;/* major number of driver */
intfirst_minor; /*starting minor number*/
intminors; /* maximumnumber ofminors, =1 for
*disks that can't be partitioned. 每一個(gè)分區(qū)都有一個(gè)minor號*/
chardisk_name[DISK_NAME_LEN]; /* name ofmajor driver */
structdisk_part_tbl *part_tbl;
structhd_struct part0;
structblock_device_operations *fops;
structrequest_queue*queue;
void*private_data;
int flags;
struct device*driverfs_dev; // FIXME: remove
struct kobject*slave_dir;
structtimer_rand_state *random;
atomic_tsync_io; /* RAID */
structwork_struct async_notify;
#ifdefCONFIG_BLK_DEV_INTEGRITY
structblk_integrity *integrity;
#endif
int node_id;
};
struct device_struct {
const char *name;
struct file_operations *chops;
};
static struct device_structblkdevs[MAX_BLKDEV];
struct sbull_dev {
void **data;
int quantum;// thecurrent quantum size
int qset;// the current array size
unsigned long size;
unsigned int access_key;// used by sbulluid and sbullpriv
unsigned int usage;// lock the device while using it
unsigned int new_msg;
struct sbull_dev *next;// next listitem
};
與字符設(shè)備驅(qū)動(dòng)程序一樣,塊設(shè)備驅(qū)動(dòng)程序也包含一個(gè)file_operation結(jié)構(gòu),其結(jié)構(gòu)定義一般如下所示:
struct file_operation blk_fops = {
NULL,//seek
block_read,//內(nèi)核函數(shù)
block_write,//內(nèi)核函數(shù)
NULL,//readdir
NULL,//poll
sbull_ioctl,// ioctl
NULL,//mmap
sbull_open,//open
NULL,//flush
sbull_release,//release
block_fsync,//內(nèi)核函數(shù)
NULL,//fasync
sbull_check_media_change,//check media change
NULL,//revalidate
NULL,//lock
};
所有的塊驅(qū)動(dòng)程序都調(diào)用內(nèi)核函數(shù)block_read()、block_write(),block_fsync()函數(shù),所以在塊設(shè)備驅(qū)動(dòng)程序入口中不包含這些函數(shù),只需包括ioctl()、open()
和release()函數(shù)即可。
塊設(shè)備的初始化過程要比字符設(shè)備復(fù)雜,它既需要像字符設(shè)備一樣在引導(dǎo)內(nèi)核時(shí)完成一定的
工作,還需要在內(nèi)核編譯時(shí)增加一些內(nèi)容。塊設(shè)備驅(qū)動(dòng)程序初始化時(shí),由驅(qū)動(dòng)程序的init()完成。
塊設(shè)備驅(qū)動(dòng)程序初始化的工作主要包括:
· 檢查硬件是否存在;
· 登記主設(shè)備號;
· 將fops結(jié)構(gòu)的指針傳遞給內(nèi)核;
· 利用register_blkdev()函數(shù)對設(shè)備進(jìn)行注冊:
if(register_blkdev(sbull_MAJOR,“sbull”,&sbull_fops)) {
printk(“Registering block device major:%d failed\n”,sbull_MAJOR);
return-EIO;
};
· 將request()函數(shù)的地址傳遞給內(nèi)核:
blk_dev[sbull_MAJOR].request_fn= DEVICE_REQUEST;
· 將塊設(shè)備驅(qū)動(dòng)程序的數(shù)據(jù)容量傳遞給緩沖區(qū):
#define sbull_HARDS_SIZE 512
#define sbull_BLOCK_SIZE 1024
static int sbull_hard = sbull_HARDS_SIZE;
static int sbull_soft = sbull_BLOCK_SIZE;
hardsect_size[sbull_MAJOR] = &sbull_hard;
blksize_size[sbull_MAJOR] = &sbull_soft;
在塊設(shè)備驅(qū)動(dòng)程序內(nèi)核編譯時(shí),應(yīng)把下列宏加到blk.h文件中:
#define MAJOR_NR sbull_MAJOR
#define DEVICE_NAME “sbull”
#define DEVICE_REQUEST sbull_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
Request操作涉及一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)如下。
struct request {
kdev_t rq_dev;
int cmd; // 讀或?qū)?/p>
int errors;
unsigned long sector;
char *buffer;
struct request *next;
};
對于具體的塊設(shè)備,函數(shù)指針request_fn當(dāng)然是不同的。塊設(shè)備的讀寫操作都是由request()函數(shù)完成。所有的讀寫請求都存儲在request結(jié)構(gòu)的鏈表中。request()函數(shù)利用CURRENT宏
檢查當(dāng)前的請求。request()函數(shù)從INIT_REQUEST宏命令開始(它也在blk.h中定義),它對請求隊(duì)列進(jìn)行檢查,保證請求隊(duì)列中至少有一個(gè)請求在等待處理。如果沒有請求(即CURRENT = 0),則INIT_REQUEST宏命令將使request()函數(shù)返回,任務(wù)結(jié)束。
假定隊(duì)列中至少有一個(gè)請求,request()函數(shù)現(xiàn)在應(yīng)處理隊(duì)列中的第一個(gè)請求,當(dāng)處理完
請求后,request()函數(shù)將調(diào)用end_request()函數(shù)。如果成功地完成了讀寫操作,那么應(yīng)該用參數(shù)值1 調(diào)用end_request()函數(shù);如果讀寫操作不成功,那么以參數(shù)值0 調(diào)用end_request()函數(shù)。如果隊(duì)列中還有其他請求,那么將CURRENT 指針設(shè)為指向下一個(gè)請求。執(zhí)行end_request()函數(shù)后,request()函數(shù)回到循環(huán)的起點(diǎn),對下一個(gè)請求重復(fù)上面的處理過程。
很多Linux 的驅(qū)動(dòng)都是通過中斷的方式來進(jìn)行內(nèi)核和硬件的交互。
這是驅(qū)動(dòng)程序申請中斷和釋放中斷的調(diào)用。在include/linux/sched.h里聲明。
request_irq()調(diào)用的定義:
int request_irq(unsigned int irq,
void (*handler)(int irq, void*dev_id, struct pt_regs *regs),
unsigned long irqflags,const char* devname,oid *dev_id);
irq 是要申請的硬件中斷號。在Intel平臺,范圍是0~15。handler 是向系統(tǒng)登記的中斷處理函數(shù)。這是一個(gè)回調(diào)函數(shù),中斷發(fā)生時(shí),系統(tǒng)調(diào)用這個(gè)函數(shù),傳入的參數(shù)包括硬件中斷號,device id,寄存器值。dev_id就是下面的request_irq時(shí)傳遞給系統(tǒng)的參數(shù)dev_id。irqflags是中斷處理的一些屬性。比較重要的有SA_INTERRUPT,標(biāo)明中斷處理程序是快速處理程序(設(shè)置SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)。快速處理程序
被調(diào)用時(shí)屏蔽所有中斷。慢速處理程序不屏蔽。還有一個(gè)SA_SHIRQ 屬性,設(shè)置了以后運(yùn)行多個(gè)設(shè)備共享中斷。dev_id在中斷共享時(shí)會(huì)用到。一般設(shè)置為這個(gè)設(shè)備的device結(jié)構(gòu)本身或者NULL。中斷處理程序可以用dev_id找到相應(yīng)的控制這個(gè)中斷的設(shè)備,或者用irq2dev_map
找到中斷對應(yīng)的設(shè)備。void free_irq(unsigned int irq,void *dev_id);
10.4一個(gè)簡單的塊設(shè)備驅(qū)動(dòng)
通過寫一個(gè)建立在內(nèi)存中的塊設(shè)備驅(qū)動(dòng),來學(xué)習(xí)linux內(nèi)核和相關(guān)設(shè)備驅(qū)動(dòng)知識
#defineSIMP_BLKDEV_DISKNAME "simp_blkdev"
#defineSIMP_BLKDEV_BYTES (16*1024*1024)// 使用宏定義了塊設(shè)備的大小,定為16M
#defineSIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
structblock_device_operations simp_blkdev_fops = {
.owner = THIS_MODULE,
};// gendisk結(jié)構(gòu)需要設(shè)置fops指針,雖然我們用不到,但該設(shè)還是要設(shè)的
static structgendisk *simp_blkdev_disk;
static structrequest_queue *simp_blkdev_queue;// 指向塊設(shè)備需要的請求隊(duì)列
unsigned charsimp_blkdev_data[SIMP_BLKDEV_BYTES];
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
module_init(simp_blkdev_init);//然后申明模塊的入口和出口
module_exit(simp_blkdev_exit);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int __initsimp_blkdev_init(void) //在入口處添加這個(gè)設(shè)備、出口處刪除這個(gè)設(shè)備
{
simp_blkdev_disk = alloc_disk(1); //在添加設(shè)備之前我們需要申請這個(gè)設(shè)備的資源,這用到了alloc_disk()函數(shù)
strcpy(simp_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME); //設(shè)備有關(guān)的屬性也是需要設(shè)置
simp_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR;
simp_blkdev_disk->first_minor = 0;
simp_blkdev_disk->fops =&simp_blkdev_fops;
simp_blkdev_disk->queue = simp_blkdev_queue;
set_capacity(simp_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
add_disk(simp_blkdev_disk);
if (!simp_blkdev_disk) {
ret = -ENOMEM;
goto err_alloc_disk;
}
simp_blkdev_queue =blk_init_queue(simp_blkdev_do_request, NULL);//初始化請求隊(duì)列
if (!simp_blkdev_queue) {
ret = -ENOMEM;
goto err_init_queue;
}//在加載模塊時(shí)用simp_blkdev_do_request()函數(shù)的地址作參數(shù)
調(diào)用blk_init_queue()初始化一個(gè)請求隊(duì)列
//用來從一個(gè)請求隊(duì)列中拿出一條請求(其實(shí)嚴(yán)格來說,拿出的可能是請求中的一段)。
隨后的處理請求本質(zhì)上是根據(jù)rq_data_dir(req)返回的該請求的方向(讀/寫),把塊設(shè)備中的數(shù)據(jù)裝入req->buffer、或是把req->buffer中的數(shù)據(jù)寫入塊設(shè)備。
static voidsimp_blkdev_do_request(struct request_queue *q)//請求隊(duì)列的處理函數(shù)。
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {
if ((req->sector +req->current_nr_sectors) << 9
> SIMP_BLKDEV_BYTES) {
printk(KERN_ERR SIMP_BLKDEV_DISKNAME
": bad request: block=%llu,count=%u\n",
(unsigned long long)req->sector,
req->current_nr_sectors);
end_request(req, 0);
continue;
}
switch (rq_data_dir(req)){
case READ:
memcpy(req->buffer,
simp_blkdev_data + (req->sector <<9),
req->current_nr_sectors << 9);
end_request(req, 1);
break;
case WRITE:
memcpy(simp_blkdev_data + (req->sector << 9),
req->buffer, req->current_nr_sectors<< 9);
end_request(req, 1);
break;
default:
/* No default because rq_data_dir(req) is 1 bit */
break;
}
}
}
return 0;
err_alloc_disk:
blk_cleanup_queue(simp_blkdev_queue);
err_init_queue:
return ret;}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static void __exit simp_blkdev_exit(void)
{
del_gendisk(simp_blkdev_disk);
put_disk(simp_blkdev_disk);
blk_cleanup_queue(simp_blkdev_queue);
}