Linux下驱动开发_块设备驱动开发(硬件上采用SD卡+SPI协议)
一、前言
块设备主要为存储设备设计的框架。 在前面章节https://bbs.huaweicloud.com/blogs/380223 里介绍了块设备驱动编写思路,并且利用内存模拟了硬件存储,完成了块设备驱动开发测试。这一篇文章将采用SD卡作为存储硬件,利用SPI协议与SD卡通信,完成块设备驱动开发测试。 SD卡可以更加形象的表示块设备开发过程,明白硬件如何交互,完成数据读写。
SD卡本身支持SPI协议、SDIO协议,一般能跑Linux系统的CPU都在硬件上支持SPI协议、SDIO协议,如果要提高读写速度,肯定是采用SDIO协议最合适。这篇文章主要是介绍SD卡+块设备框架的驱动开发思路,代码里选择了SPI协议来进行通信,读写SD卡的速度比较慢(与SDIO比数据线都少了几条)。 SPI协议比较简单,学习过单片机的都比较熟悉,并且SPI协议还可以自己模拟时序,不一定要硬件上支持的,在难度上就降低了不少。
块设备驱动的思路是: 处理应用层的请求。
我们知道操块设备,都是通过文件系统读写访问比如:(U盘、SD卡、磁盘)这些设备。
读写操作块设备的常用命令:
dd、fdisk、mount
比如以下的文件操作代码:
mount /dev/sdb /mnt
fopen("/mnt/123.c","wb");
fwrite("1233445656");
fclose();
代码执行之后的请求反应到驱动层的接口就是: 读扇区(从哪里读,读多少扇区)、写扇区(从哪里写、写多少扇区)。
对于应用层而言,对块设备并发读写是经常的事情,比如:在同一个磁盘上,我们可以同时拷贝文件的同时,还可以删除其他文件,读写某个文件。 那么驱动层如何处理呢? 因为站在SD卡的角度,每一刻SPI通信肯定是只能处理一个指令,不能并发的。 所以在块设备的驱动层有一个读写IO队列,应用层的请求都会插入到队里里,然后驱动层在一个一个的进行处理。 这个队列在内核里也会涉及到算法排序,可以根据优先级进行排序的。比如:音乐文件读写要实时,不然播放器播放音乐卡顿。这些都可以进行排序,把实时性高的请求可以放在前面处理。
这是安装块设备之后,分区、格式化文件系统,挂载的测试流程: 通过这一系列操作,可以验证驱动是否正常。
[root@wbyq code]# insmod block_dev.ko
[ 20.070000] tiny4412_block_dev: unknown partition table
[ 20.075000] 块设备驱动安装成功.
[root@wbyq code]#
[root@wbyq code]# fdisk /dev/[ 34.120000] nf_conntrack: automatic helper assignment is deprecated and it will be removed soon. Use the iptables CT target to attach helpers instead.
[root@wbyq code]# fdisk /dev/tiny4412_block_dev
Device contains neither a valid DOS partition table, nor Sun, SGI, OSF or GPT disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that the previous content
won't be recoverable.
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1024, default 1): Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-1024, default 1024): Using default value 1024
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table
[ 46.200000] tiny4412_block_dev: tiny4412_block_dev1
[root@wbyq code]# mkfs.
mkfs.ext2 mkfs.minix mkfs.vfat
[root@wbyq code]# mkfs.vfat /dev/tiny4412_block_dev1
[root@wbyq code]# mount /dev/tiny4412_block_dev1 /mnt/
[ 113.755000] FAT-fs (tiny4412_block_dev1): utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!
[root@wbyq code]# cd /mnt/
[root@wbyq mnt]# ls
[root@wbyq mnt]# touch 1.c 2.c 3.c
[root@wbyq mnt]# ls
1.c 2.c 3.c
[root@wbyq mnt]# cd ..
[root@wbyq ]# umount /mnt/
二、SD卡介绍
这是SD卡的引脚功能说明:
SD卡支持两种总线方式:SD方式与SPI方式。其中SD方式采用6线制,使用CLK、CMD、DAT0~DAT3进行数据通信。而SPI方式采用4线制,使用CS、CLK、DataIn、DataOut进行数据通信。SD方式时的数据传输速度与SPI方式要快,采用单片机对SD卡进行读写时一般都采用SPI模式。采用不同的初始化方式可以使SD卡工作于SD方式或SPI方式。
SD卡协会官网: https://www.sdcard.org/
SD卡底层SPI时序:
从图上得知: SD卡在SPI模式下读写数据: 时钟的下降沿写数据、时钟的上升沿读数据。
注意: SD卡在SPI模式下数据高位先发。
SD卡的SPI方式读写:
SD卡读写一次的数据量必须为512 字节的整数倍,亦即,对SD卡读写操作的最少数据量为512 字节。我们也可以通过向SD 卡写修改扇区大小的指令CMD16(CMD16—为接下来的块操作指令设置块长度)以使每次读写的数据量变为(n×512)字节(n≥1),本文中我们使用SD卡默认的一次操作512 字节的模式对SD 卡进行读写操作。
对SD卡读操作的时序为:
(1) 写入读单数据块命令CMD17(0x11|0x40=0x51)。
(2) 写4 个地址参数,4 个字节凑成一个32 位的地址值,第一个字节是32 位地址值的最高8 位数据,第4 个字节是32 位地址值的最低8 位数据。(与写操作不同)
(3) 写CRC 校验位0xFF。
(4) 写若干个0xFF 的空操作。
(5) SD 卡发送0x00 响应。
(6) 写若干个0xFF 的空操作(等待)。
(7) SD 卡发送0x FE 数据头。
(8) SD 卡发送指令指定地址的512 字节数据块。
(9) SD 卡发送两字节的CRC 校验码,由于SPI 模式默认不需要CRC 校验,因此这两个字节的数据可丢弃不用。
(10)拉高CS,发送8 个空时钟。
至此,完成了对SD卡指定地址数据块的读操作。
三、Linux下块设备框架+SD卡驱动开发
3.1 硬件连接
这是驱动安装之后测试效果:
3.2 驱动代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/hdreg.h>
volatile unsigned int *GPBCON;
volatile unsigned int *GPBDAT;
/*----------------------------------------------
初始化SD硬件
硬件连接:
GPB_0--时钟线
GPB_3--数据线--MOSI
GPB_2--数据线--MISO
GPB_1--片选
------------------------------------------------*/
#define SDCardOut(x) {if(x){*GPBDAT|=1<<3;}else{*GPBDAT&=~(1<<3);}}
#define SDCardInput (*GPBDAT&1<<2)
#define SDCardSCLK(x) {if(x){*GPBDAT|=1<<0;}else{*GPBDAT&=~(1<<0);}}
#define SDCardCS(x) {if(x){*GPBDAT|=1<<1;}else{*GPBDAT&=~(1<<1);}}
// SD卡类型定义
#define SDCard_TYPE_ERR 0X00 //卡类型错误
#define SDCard_TYPE_MMC 0X01 //MMC卡
#define SDCard_TYPE_V1 0X02
#define SDCard_TYPE_V2 0X04
#define SDCard_TYPE_V2HC 0X06
// SD卡指令表
#define SDCard_CMD0 0 //卡复位
#define SDCard_CMD1 1
#define SDCard_CMD8 8 //命令8 ,SEND_IF_COND
#define SDCard_CMD9 9 //命令9 ,读CSD数据
#define SDCard_CMD10 10 //命令10,读CID数据
#define SDCard_CMD12 12 //命令12,停止数据传输
#define SDCard_CMD13 16 //命令16,设置扇区大小 应返回0x00
#define SDCard_CMD17 17 //命令17,读扇区
#define SDCard_CMD18 18 //命令18,读Multi 扇区
#define SDCard_CMD23 23 //命令23,设置多扇区写入前预先擦除N个block
#define SDCard_CMD24 24 //命令24,写扇区
#define SDCard_CMD25 25 //命令25,写多个扇区
#define SDCard_CMD41 41 //命令41,应返回0x00
#define SDCard_CMD55 55 //命令55,应返回0x01
#define SDCard_CMD58 58 //命令58,读OCR信息
#define SDCard_CMD59 59 //命令59,使能/禁止CRC,应返回0x00、
/*SD卡回应标记字*/
#define SDCard_RESPONSE_NO_ERROR 0x00 //正确回应
#define SDCard_SD_IN_IDLE_STATE 0x01 //闲置状态
#define SDCard_SD_ERASE_RESET 0x02 //擦除复位
#define SDCard_RESPONSE_FAILURE 0xFF //响应失败
//函数声明
u8 SDCardReadWriteOneByte(u8 data); //底层接口,SPI读写字节函数
u8 SDCardWaitBusy(void); //等待SD卡准备
u8 SDCardGetAck(u8 Response); //获得应答
u8 SDCardDeviceInit(void); //初始化
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt); //读块
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt); //写块
u32 GetSDCardSectorCount(void); //读扇区数
u8 GetSDCardCISDCardOutnfo(u8 *cid_data); //读SD卡CID
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data); //读SD卡CSD
static u8 SD_Type=0; //存放SD卡的类型
static u32 sd_size; //存放SD卡返回的容量
/*
函数功能:SD卡底层接口,通过SPI时序向SD卡读写一个字节
函数参数:data是要写入的数据
返 回 值:读到的数据
说明:时序是第二个上升沿采集数据
*/
u8 SDCardReadWriteOneByte(u8 DataTx)
{
u8 DataRx=0;
u8 i;
for(i=0;i<8;i++)
{
SDCardSCLK(0);
if(DataTx&0x80){SDCardOut(1);}
else {SDCardOut(0);}
DataTx<<=1;
SDCardSCLK(1);//第二个上升沿采集数据
DataRx<<=1;
if(SDCardInput)DataRx|=0x01;
}
return DataRx;
}
/*
函数功能:底层SD卡接口初始化
本程序SPI接口如下:
PC11 片选 SDCardCS
PC12 时钟 SDCardSCLK
PD2 输出 SPI_MOSI--主机输出从机输入
PC8 输入 SPI_MISO--主机输入从机输出
*/
void SDCardSpiInit(void)
{
/*1. 转换物理地址得到虚拟地址*/
GPBCON=ioremap(0x11400040,4);
GPBDAT=ioremap(0x11400044,4);
/*2. 配置GPIO口模式*/
*GPBCON&=0xFFFF0000;
*GPBCON|=0xFF001011;
SDCardCS(1);
}
/*
函数功能:取消选择,释放SPI总线
*/
void SDCardCancelCS(void)
{
SDCardCS(1);
SDCardReadWriteOneByte(0xff);//提供额外的8个时钟
}
/*
函数 功 能:选择sd卡,并且等待卡准备OK
函数返回值:0,成功;1,失败;
*/
u8 SDCardSelectCS(void)
{
SDCardCS(0);
if(SDCardWaitBusy()==0)return 0;//等待成功
SDCardCancelCS();
return 1;//等待失败
}
/*
函数 功 能:等待卡准备好
函数返回值:0,准备好了;其他,错误代码
*/
u8 SDCardWaitBusy(void)
{
u32 t=0;
do
{
if(SDCardReadWriteOneByte(0XFF)==0XFF)return 0;//OK
t++;
}while(t<0xFFFFFF);//等待
return 1;
}
/*
函数功能:等待SD卡回应
函数参数:
Response:要得到的回应值
返 回 值:
0,成功得到了该回应值
其他,得到回应值失败
*/
u8 SDCardGetAck(u8 Response)
{
u16 Count=0xFFFF;//等待次数
while((SDCardReadWriteOneByte(0XFF)!=Response)&&Count)Count--;//等待得到准确的回应
if(Count==0)return SDCard_RESPONSE_FAILURE;//得到回应失败
else return SDCard_RESPONSE_NO_ERROR;//正确回应
}
/*
函数功能:从sd卡读取一个数据包的内容
函数参数:
buf:数据缓存区
len:要读取的数据长度.
返回值:
0,成功;其他,失败;
*/
u8 SDCardRecvData(u8*buf,u16 len)
{
if(SDCardGetAck(0xFE))return 1;//等待SD卡发回数据起始令牌0xFE
while(len--)//开始接收数据
{
*buf=SDCardReadWriteOneByte(0xFF);
buf++;
}
//下面是2个伪CRC(dummy CRC)
SDCardReadWriteOneByte(0xFF);
SDCardReadWriteOneByte(0xFF);
return 0;//读取成功
}
/*
函数功能:向sd卡写入一个数据包的内容 512字节
函数参数:
buf 数据缓存区
cmd 指令
返 回 值:0表示成功;其他值表示失败;
*/
u8 SDCardSendData(u8*buf,u8 cmd)
{
u16 t;
if(SDCardWaitBusy())return 1; //等待准备失效
SDCardReadWriteOneByte(cmd);
if(cmd!=0XFD)//不是结束指令
{
for(t=0;t<512;t++)SDCardReadWriteOneByte(buf[t]);//提高速度,减少函数传参时间
SDCardReadWriteOneByte(0xFF); //忽略crc
SDCardReadWriteOneByte(0xFF);
t=SDCardReadWriteOneByte(0xFF); //接收响应
if((t&0x1F)!=0x05)return 2; //响应错误
}
return 0;//写入成功
}
/*
函数功能:向SD卡发送一个命令
函数参数:
u8 cmd 命令
u32 arg 命令参数
u8 crc crc校验值
返回值:SD卡返回的响应
*/
u8 SendSDCardCmd(u8 cmd, u32 arg, u8 crc)
{
u8 r1;
u8 Retry=0;
SDCardCancelCS(); //取消上次片选
if(SDCardSelectCS())return 0XFF;//片选失效
//发送数据
SDCardReadWriteOneByte(cmd | 0x40);//分别写入命令
SDCardReadWriteOneByte(arg >> 24);
SDCardReadWriteOneByte(arg >> 16);
SDCardReadWriteOneByte(arg >> 8);
SDCardReadWriteOneByte(arg);
SDCardReadWriteOneByte(crc);
if(cmd==SDCard_CMD12)SDCardReadWriteOneByte(0xff);//Skip a stuff byte when stop reading
Retry=0X1F;
do
{
r1=SDCardReadWriteOneByte(0xFF);
}while((r1&0X80) && Retry--); //等待响应,或超时退出
return r1; //返回状态值
}
/*
函数功能:获取SD卡的CID信息,包括制造商信息
函数参数:u8 *cid_data(存放CID的内存,至少16Byte)
返 回 值:
0:成功,1:错误
*/
u8 GetSDCardCISDCardOutnfo(u8 *cid_data)
{
u8 r1;
//发SDCard_CMD10命令,读CID
r1=SendSDCardCmd(SDCard_CMD10,0,0x01);
if(r1==0x00)
{
r1=SDCardRecvData(cid_data,16);//接收16个字节的数据
}
SDCardCancelCS();//取消片选
if(r1)return 1;
else return 0;
}
/*
函数说明:
获取SD卡的CSD信息,包括容量和速度信息
函数参数:
u8 *cid_data(存放CID的内存,至少16Byte)
返 回 值:
0:成功,1:错误
*/
u8 GetSDCardCSSDCardOutnfo(u8 *csd_data)
{
u8 r1;
r1=SendSDCardCmd(SDCard_CMD9,0,0x01); //发SDCard_CMD9命令,读CSD
if(r1==0)
{
r1=SDCardRecvData(csd_data, 16);//接收16个字节的数据
}
SDCardCancelCS();//取消片选
if(r1)return 1;
else return 0;
}
/*
函数功能:获取SD卡的总扇区数(扇区数)
返 回 值:
0表示容量检测出错,其他值表示SD卡的容量(扇区数/512字节)
说 明:
每扇区的字节数必为512字节,如果不是512字节,则初始化不能通过.
*/
u32 GetSDCardSectorCount(void)
{
u8 csd[16];
u32 Capacity;
u8 n;
u16 csize;
if(GetSDCardCSSDCardOutnfo(csd)!=0) return 0; //取CSD信息,如果期间出错,返回0
if((csd[0]&0xC0)==0x40) //V2.00的卡,如果为SDHC卡,按照下面方式计算
{
csize = csd[9] + ((u16)csd[8] << 8) + 1;
Capacity = (u32)csize << 10;//得到扇区数
}
else//V1.XX的卡
{
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((u16)csd[7] << 2) + ((u16)(csd[6] & 3) << 10) + 1;
Capacity= (u32)csize << (n - 9);//得到扇区数
}
return Capacity;
}
/*
函数功能: 初始化SD卡
返 回 值: 非0表示初始化失败!
*/
u8 SDCardDeviceInit(void)
{
u8 r1; // 存放SD卡的返回值
u16 retry; // 用来进行超时计数
u8 buf[4];
u16 i;
SDCardSpiInit(); //初始化底层IO口
for(i=0;i<10;i++)SDCardReadWriteOneByte(0XFF); //发送最少74个脉冲
retry=20;
do
{
r1=SendSDCardCmd(SDCard_CMD0,0,0x95);//进入IDLE状态 闲置
}while((r1!=0X01) && retry--);
SD_Type=0; //默认无卡
if(r1==0X01)
{
if(SendSDCardCmd(SDCard_CMD8,0x1AA,0x87)==1) //SD V2.0
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF); //Get trailing return value of R7 resp
if(buf[2]==0X01&&buf[3]==0XAA) //卡是否支持2.7~3.6V
{
retry=0XFFFE;
do
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //发送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0x40000000,0X01);//发送SDCard_CMD41
}while(r1&&retry--);
if(retry&&SendSDCardCmd(SDCard_CMD58,0,0X01)==0)//鉴别SD2.0卡版本开始
{
for(i=0;i<4;i++)buf[i]=SDCardReadWriteOneByte(0XFF);//得到OCR值
if(buf[0]&0x40)SD_Type=SDCard_TYPE_V2HC; //检查CCS
else SD_Type=SDCard_TYPE_V2;
}
}
}
else//SD V1.x/ MMC V3
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //发送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0,0X01); //发送SDCard_CMD41
if(r1<=1)
{
SD_Type=SDCard_TYPE_V1;
retry=0XFFFE;
do //等待退出IDLE模式
{
SendSDCardCmd(SDCard_CMD55,0,0X01); //发送SDCard_CMD55
r1=SendSDCardCmd(SDCard_CMD41,0,0X01);//发送SDCard_CMD41
}while(r1&&retry--);
}
else//MMC卡不支持SDCard_CMD55+SDCard_CMD41识别
{
SD_Type=SDCard_TYPE_MMC;//MMC V3
retry=0XFFFE;
do //等待退出IDLE模式
{
r1=SendSDCardCmd(SDCard_CMD1,0,0X01);//发送SDCard_CMD1
}while(r1&&retry--);
}
if(retry==0||SendSDCardCmd(SDCard_CMD13,512,0X01)!=0)SD_Type=SDCard_TYPE_ERR;//错误的卡
}
}
SDCardCancelCS(); //取消片选
if(SD_Type)return 0; //初始化成功返回0
else if(r1)return r1; //返回值错误值
return 0xaa; //其他错误
}
/*
函数功能:读SD卡
函数参数:
buf:数据缓存区
sector:扇区
cnt:扇区数
返回值:
0,ok;其他,失败.
说 明:
SD卡一个扇区大小512字节
*/
u8 SDCardReadData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector<<=9;//转换为字节地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD17,sector,0X01);//读命令
if(r1==0) //指令发送成功
{
r1=SDCardRecvData(buf,512); //接收512个字节
}
}else
{
r1=SendSDCardCmd(SDCard_CMD18,sector,0X01);//连续读命令
do
{
r1=SDCardRecvData(buf,512);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
SendSDCardCmd(SDCard_CMD12,0,0X01); //发送停止命令
}
SDCardCancelCS();//取消片选
return r1;//
}
/*
函数功能:向SD卡写数据
函数参数:
buf:数据缓存区
sector:起始扇区
cnt:扇区数
返回值:
0,ok;其他,失败.
说 明:
SD卡一个扇区大小512字节
*/
u8 SDCardWriteData(u8*buf,u32 sector,u32 cnt)
{
u8 r1;
if(SD_Type!=SDCard_TYPE_V2HC)sector *= 512;//转换为字节地址
if(cnt==1)
{
r1=SendSDCardCmd(SDCard_CMD24,sector,0X01);//读命令
if(r1==0)//指令发送成功
{
r1=SDCardSendData(buf,0xFE);//写512个字节
}
}
else
{
if(SD_Type!=SDCard_TYPE_MMC)
{
SendSDCardCmd(SDCard_CMD55,0,0X01);
SendSDCardCmd(SDCard_CMD23,cnt,0X01);//发送指令
}
r1=SendSDCardCmd(SDCard_CMD25,sector,0X01);//连续读命令
if(r1==0)
{
do
{
r1=SDCardSendData(buf,0xFC);//接收512个字节
buf+=512;
}while(--cnt && r1==0);
r1=SDCardSendData(0,0xFD);//接收512个字节
}
}
SDCardCancelCS();//取消片选
return r1;//
}
static unsigned int major;
static struct gendisk *tiny4412_block_gendisk;
static struct request_queue *tiny4412_block_queue;
#define DEVICE_NAME "block_dev"
static DEFINE_SPINLOCK(tiny4412_block_lock);
/*
处理请求队列
*/
static void block_request_work_func(struct request_queue *q)
{
struct request *req;
/*读取队列中一个请求*/
req=blk_fetch_request(q);
while(req)
{
int err=0;
/*得到当前起始扇区: 从哪里开始读写*/
unsigned long start = blk_rq_pos(req);
/*得到当前读写的字节单位*/
unsigned long len = blk_rq_cur_bytes(req);
/*判断当前读写方向*/
if(rq_data_dir(req) == READ)
{
SDCardReadData(req->buffer,start,len/512); //读块
}
else
{
SDCardWriteData(req->buffer,start,len/512); //写块
}
/*报告当前请求处理完毕*/
if (!__blk_end_request_cur(req, err))
/*继续读取队列里的下一个请求*/
req = blk_fetch_request(q);
}
}
/*
获取磁盘的物理结构信息
磁头、柱面(磁道)、扇区
fdisk /dev/xxx
*/
static int tiny4412_block_getgeo(struct block_device *dev, struct hd_geometry *geo)
{
/* TINY4412_DISK_SIZE 容量字节单位
每个扇区是512字节
32GB SD卡的扇区数量: 62333952
*/
geo->cylinders=sd_size/16/255; /*柱面--65535*/
geo->heads=16; /*磁头 255 */
geo->sectors=255; /*扇区 255 */
return 0;
}
static const struct block_device_operations tiny4412_block_fops =
{
.owner = THIS_MODULE,
.getgeo = tiny4412_block_getgeo,
};
static int __init tiny4412_SdCardDrv_init(void)
{
if(SDCardDeviceInit())
{
printk("SD卡初始化失败!\r\n");
}
sd_size=GetSDCardSectorCount(); //检测SD卡大小,返回值右移11位得到以M为单位的容量
printk("SD卡的容量:%d M\r\n",sd_size>>11);
printk("SD卡的扇区数量:%d\r\n",sd_size);
/*1. 注册块设备:分配主设备号*/
major=register_blkdev(0, DEVICE_NAME);
/*2. 分配根磁盘结构体*/
tiny4412_block_gendisk=alloc_disk(3);/*sdc sdc1 sdc2 ....*/
/*3. 初始化请求队列*/
tiny4412_block_queue=blk_init_queue(block_request_work_func, &tiny4412_block_lock);
/*4. 填充根磁盘参数信息*/
tiny4412_block_gendisk->major = major; /*主设备号*/
tiny4412_block_gendisk->first_minor = 0; /*起始次设备号*/
tiny4412_block_gendisk->fops = &tiny4412_block_fops; /*块设备文件操作集合*/
sprintf(tiny4412_block_gendisk->disk_name,DEVICE_NAME); /*/dev/设备节点名称*/
tiny4412_block_gendisk->queue = tiny4412_block_queue; /*绑定请求队列*/
tiny4412_block_gendisk->part0.nr_sects=sd_size; /*设置扇区数量-20480*/
/*5. 向内核添加磁盘信息*/
add_disk(tiny4412_block_gendisk);
printk("SD卡驱动安装成功.\n");
return 0;
}
static void __exit tiny4412_SdCardDrv_exit(void)
{
/*1. 注销主设备号*/
unregister_blkdev(major,DEVICE_NAME);
/*2. 删除根磁盘*/
del_gendisk(tiny4412_block_gendisk);
/*3. 减少磁盘计数*/
put_disk(tiny4412_block_gendisk);
/*4. 释放队列*/
blk_cleanup_queue(tiny4412_block_queue);
/*取消映射*/
iounmap(GPBCON);
iounmap(GPBDAT);
printk("SD卡驱动卸载成功.\n");
}
module_init(tiny4412_SdCardDrv_init);
module_exit(tiny4412_SdCardDrv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wbyq");
- 点赞
- 收藏
- 关注作者
评论(0)