以上均是IDE总线上设备的通用接口,直到do_request开始才执行特定设备的驱动,如CD,HD, floppy等IDE设备。我们来看一下ide-disk:
1、 首先是设备的初始化操作。
IDE设备接口
static ide_driver_t idedisk_driver = {
.gen_driver = {
.owner = THIS_MODULE,
.name = "ide-disk",
.bus = &ide_bus_type,
},
.probe = ide_disk_probe,
.remove = ide_disk_remove,
.shutdown = ide_device_shutdown,
.version = IDEDISK_VERSION,
.media = ide_disk,
.supports_dsc_overlap = 0,
.do_request = ide_do_rw_disk,
.end_request = ide_end_request,
.error = __ide_error,
.abort = __ide_abort,
.proc = idedisk_proc,
};
static struct block_device_operations idedisk_ops = {
.owner = THIS_MODULE,
.open = idedisk_open,
.release = idedisk_release,
.ioctl = idedisk_ioctl,
.getgeo = idedisk_getgeo,
.media_changed = idedisk_media_changed,
.revalidate_disk= idedisk_revalidate_disk
};
//设备注册
static int __init idedisk_init(void)
{
return driver_register(&idedisk_driver.gen_driver);
}
//这个probe函数是在设备注册时由驱动模型去执行
static int ide_disk_probe(ide_drive_t *drive)
{
struct ide_disk_obj *idkp;
struct gendisk *g;
idkp = kzalloc(sizeof(*idkp), GFP_KERNEL);
//分配一个gendisk结构
g = alloc_disk_node(1 << PARTN_BITS,
hwif_to_node(drive->hwif));
ide_init_disk(g, drive);
//用上面的结构注册设备
ide_register_subdriver(drive, &idedisk_driver);
kref_init(&idkp->kref);
//一些初始化操作
idkp->drive = drive;
idkp->driver = &idedisk_driver;
idkp->disk = g;
g->private_data = &idkp->driver;
drive->driver_data = idkp;
idedisk_setup(drive);
g->minors = 1 << PARTN_BITS;
g->driverfs_dev = &drive->gendev;
g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0;
set_capacity(g, idedisk_capacity(drive));
g->fops = &idedisk_ops;
add_disk(g); //插入设备,至此,该设备可用
return 0;
}
2、 处理IDE总线发来的请求
由上可以看到,IDE总线驱动调用设备的do_request()去处理这个请求,我们在上面的注册中可以看到。在ide-disk里,它是ide_do_rw_disk():
/*
* 268435455 == 137439 MB or 28bit limit
* 320173056 == 163929 MB or 48bit addressing
* 1073741822 == 549756 MB or 48bit addressing fake drive
*/
static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, sector_t block)
{
ide_hwif_t *hwif = HWIF(drive);
……
if (hwif->rw_disk)
hwif->rw_disk(drive, rq);
return __ide_do_rw_disk(drive, rq, block);
}
//以下就是特定硬盘设备的驱动了,因为我们只关心块设备驱动编程的框架,所以就不深入进去了。
/*
* __ide_do_rw_disk() issues READ and WRITE commands to a disk,
* using LBA if supported, or CHS otherwise, to address sectors.
*/
static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq, sector_t block)
{
ide_hwif_t *hwif = HWIF(drive);
unsigned int dma = drive->using_dma;
u8 lba48 = (drive->addressing == 1) ? 1 : 0;
task_ioreg_t command = WIN_NOP;
ata_nsector_t nsectors;
nsectors.all = (u16) rq->nr_sectors;
if (hwif->no_lba48_dma && lba48 && dma) {
if (block + rq->nr_sectors > 1ULL << 28)
dma = 0;
else
lba48 = 0;
}
if (drive->select.b.lba) {
if (lba48) {
……
} else {
……
}
} else {
……
}
if (dma) {
……
/* fallback to PIO */
ide_init_sg_cmd(drive, rq);
}
if (rq_data_dir(rq) == READ) {
if (drive->mult_count) {
hwif->data_phase = TASKFILE_MULTI_IN;
command = lba48 ? WIN_MULTREAD_EXT : WIN_MULTREAD;
} else {
hwif->data_phase = TASKFILE_IN;
command = lba48 ? WIN_READ_EXT : WIN_READ;
}
ide_execute_command(drive, command, &task_in_intr, WAIT_CMD, NULL);
return ide_started;
} else {
……
return pre_task_out_intr(drive, rq);
}
}
3、 块设备驱动小结
我们由上看到, 块设备驱动编程的主要工作包括分配并初始化一个gendisk结构,分配并初始化一个请求队列,请求处理函数的编写(request_fn),还有中断的处理等等。但具体到不同的设备,实现又有一些出入,我们在上面看到的IDE设备,它大部分工作都是在IDE总线级上实现了,它做了许多繁琐但必要的工作,并向下层特定设备提供统一的接口,这样就大大简化了块设备驱动的编写过程。
有兴趣可以参考一下内核ramdisk的实现。