嵌入式Linux驅(qū)動(dòng)程序開發(fā)(二)實(shí)例剖析

姓名:薛紹宏? ? ?學(xué)號:19020100016? ? 學(xué)院:電子工程學(xué)院

轉(zhuǎn)自:https://blog.csdn.net/iteye_2060/article/details/82089821?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162722233316780264011150%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162722233316780264011150&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-82089821.pc_search_result_control_group&utm_term=%E5%B5%8C%E5%85%A5%E5%BC%8Flinux%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91&spm=1018.2226.3001.4449

【嵌牛導(dǎo)讀】本文介紹了嵌入式Linux驅(qū)動(dòng)程序開發(fā)的一些實(shí)例和剖析

【嵌牛鼻子】嵌入式Linux驅(qū)動(dòng)程序開發(fā)

【嵌牛提問】嵌入式Linux驅(qū)動(dòng)程序開發(fā)實(shí)例如何剖析?

【嵌牛正文】

9.實(shí)例剖析

9.1實(shí)例一

9.1.1驅(qū)動(dòng)程序

/******************************

*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);

9.1.2用戶程序

在編寫用戶應(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;

}

9.1.3執(zhí)行效果

(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--]

9.2實(shí)例二

/*************************************

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");

10塊設(shè)備驅(qū)動(dòng)程序的編寫

塊設(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中,如下圖所示:

10.1塊設(shè)備驅(qū)動(dòng)編寫流程

塊設(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)程序流程圖

10.2重要數(shù)據(jù)結(jié)構(gòu)

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è)備類型的操作之間的樞紐。

10.2.1 struct bio

一個(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];

};

10.2.2 struct gendisk

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ù)即可。

(1)設(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)

(2)request操作

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ù)上面的處理過程。

(3)打開操作

(4)釋放設(shè)備操作

(5)ioctl操作

10.3中斷編程

很多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);

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容