The above are general interfaces of devices on the IDE bus. Drivers of specific devices, such as CD, HD, floppy, and other ide devices, are not executed until do_request starts. Let's take a look at ide-Disk:
1. device initialization.
IDE Device Interface
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
};
// Register the device
Static int _ init idedisk_init (void)
{
Return driver_register (& idedisk_driver.gen_driver );
}
// This probe function is executed by the driver model during device registration
Static int ide_disk_probe (ide_drive_t * drive)
{
Struct ide_disk_obj * idkp;
Struct gendisk * g;
Idkp = kzarloc (sizeof (* idkp), gfp_kernel );
// Allocate a gendisk Structure
G = alloc_disk_node (1 <partn_bits,
Hwif_to_node (drive-> hwif ));
Ide_init_disk (G, drive );
// Register the device with the above structure
Ide_register_subdriver (drive, & idedisk_driver );
Kref_init (& idkp-> kref );
// Some initialization operations
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); // insert a device. This device is available now.
Return 0;
}
2. Process requests sent from the IDE Bus
As you can see, the IDE bus driver calls the device's do_request () to process this request. We can see it in the above registration. In ide-disk, It is 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 );
}
// The following figure shows the driver of a specific hard drive device. Because we only care about the block device driver programming framework, we will not go deep into it.
/*
* _ 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 = (002) 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. Summary of block Device Drivers
From the above we can see that the main work of block device driver programming includes allocating and initializing a gendisk structure, allocating and initializing a request queue, and writing request processing functions (request_fn ), and interrupt handling. However, there are some differences between the implementations of different devices. Most of the IDE devices we see above are implemented at the IDE bus level, it does a lot of tedious but necessary work and provides unified interfaces to the lower-layer specific devices, which greatly simplifies the compilation process of block device drivers.
If you are interested, refer to the implementation of kernel ramdisk.