¿éÉ豸Çý¶¯³ÌÐòʵÏÖ
(2007-09-04 17:42)
·ÖÀࣺ linux±à³Ìѧϰ±Ê¼Ç
¿éÉ豸Çý¶¯³ÌÐòʵÏÖ
´ÓÐéÄâÎļþϵͳ£¬Îļþϵͳ£¬Âß¼¾íһ·˳Á÷¶øÏ£¬Ò»Ö±À´µ½ÁËÓ²ÅÌ¿éÉ豸Çý¶¯³ÌÐò¡£´ÓÉϲ㿴ϲ㣬×ÜÊDZ»ÏµÍ³µÄʵÏÖ°áŪµÃÔÆÕÚÎíÕÏ£¬²»Ã÷ËùÒÔ£¬Òò´Ë£¬¸ÉÂ𲻸ɴà´Óµ×²ã¿ªÊ¼ÂýÂýÍùÉÏÅÀ¡£
OK,ÈÃÎÒÃǾʹӿéÉ豸µÄÇý¶¯³ÌÐò¿ªÊ¼°É¡£
Ê×Ïȶ¨ÒåһЩ³£ÓñäÁ¿
£¨ÎªÊ²Ã´ÊÇGK? GK stands for gingko¡£ÕâÊÇGingko Storage SystemµÄÒ»¸ö×é³É²¿·Ö¡£Ê²Ã´ÊÇGingko Storage System? -_-!!!ÊÐÃæÉÏ¿´²»µÀµÄ£¬ºÇºÇ¡££©
#define GKDD_MAJOR 0 //Ö÷É豸ºÅ
#define GKDD_SIZE 2048 //´ÅÅÌ´óС
#define GKDD_BLKSIZE 1024 //´æÈ¡¿éµÄ´óС£¬
#define GKDD_HARDSECT 512 //ÿ¸öÉÈÇøµÄ´óС
Õâ¾ÍÊÇÎÒÃǵÄÓ²Å̵Ļù±¾²ÎÊýÁË£¬ÏÂÃæ»¹ÒªÎªÎÒÃǵÄÇý¶¯É趨¼¸¸ö±äÁ¿
static int gkdd_major = 0;
module_param( gkdd_major, int, 0 );
static int hardsect_size = 512;
module_param( hardsect_size, int, 0 );
static int nsectors = 1024;
module_param( nsectors, int, 0 );
static int ndevices = 4;
module_param( ndevices, int, 0 );
Ä£¿é¿ÉÒÔ´«µÝ4¸öintÐͲÎÊý£¬4¿éÓ²ÅÌ£¬Ã¿¿éÅÌ1024¸öÉÈÇø£¬Ã¿¸öÉÈÇø512×Ö½Ú¡£
enum
{
RM_SIMPLE = 0,
RM_FULL = 1,
RM_NOQUEUE = 2,
};
static int request_mode = RM_SIMPLE;
module_param( request_mode, int, 0 );
ʹÓüòµ¥µÄÊý¾ÝÇëÇóģʽ¡£
struct gkdd_dev
{
int size;
u8 *data;
short users;
short media_change;
spinlock_t lock;
struct request_queue *queue;
struct gendisk *gd;
struct timer_list timer;
};
static struct gkdd_dev *Devices = NULL;
ºÃÁË£¬Õâ¾ÍÊÇÔÛµÄÓ²ÅÌÊý¾Ý½á¹¹£¬ÓиÃÓ²ÅÌÔÚÄں˵ıíʾgd, Ó²Å̵IJÙ×÷ÇëÇó¶ÓÁÐqueue£¬É豸²Ù×÷×ÔÐýËø£¬ÒÔ¼°²Ù×÷ÑÓʱµÄ¶¨Ê±Æ÷,ͨ¹ý¶¨Ê±Æ÷Ä£ÄâÒ»¸ö¿É¸ü»»½éÖʵĿìÉ豸£¬ÈçÄãµÄDVD¡£
Ò»ÇÐ×¼±¸¾ÍÐ÷£¬ÏòÄÚºË×¢²áÔÛÃǵÄÓ²Å̰ɡ£
static int gkdd_init_module(void)
{
int i;
gkdd_major = register_blkdev( gkdd_major, "gingko_disk" );
if( gkdd_major <= 0 )
{
printk( KERN_WARNING "GKDD-disk: unable to get major number\n" );
return -EBUSY;
}
Devices = kmalloc( ndevices*sizeof( struct gkdd_dev ), GFP_KERNEL );
if( Devices == NULL )
goto out_unregister;
for( i=0; i< ndevices; i++ )
setup_device( Devices + i, i );
printk( KERN_DEBUG "Module gkdd init\n" );
return 0;
out_unregister:
unregister_blkdev( gkdd_major, "sbd" );
return -ENOMEM;
}
module_init(gkdd_init_module);
µ±È»£¬ÕâÀïÃæÓÐÒ»¸ö¹Ø¼üµÄº¯Êý£¬¾ÍÊÇset_updevice()¡£
static void setup_device( struct gkdd_dev *dev, int which )
{
memset( dev, 0, sizeof( struct gkdd_dev ));
dev->size = nsectors*hardsect_size; //É豸µÄ´óС
dev->data = vmalloc( dev->size ); //É豸µÄÊý¾Ý¿Õ¼ä£¬ÒòΪÊÇRAMDISK£¬Ö±½ÓÉêÇëÈ«²¿µÄ¿Õ¼ä£¬·´ÕýÊÇÐé´æ£¬²»Ì«¹ý·Ö£¬×ÜÄÜÓ¦¸¶¡£
if( dev->data == NULL )
{
printk( KERN_NOTICE "vmalloc failure.\n" );
return;
}
spin_lock_init( &dev->lock );
init_timer( &dev->timer );
dev->timer.data = (unsigned long) dev;
dev->timer.function = gkdd_invalidate; //ÑÓ³Ù30Ãëºó£¬¸ÃÉ豸µÄ½éÖʾÍÈÏΪÒѾ±»ÈËÈ¥ÏÂÀ´ÁË£¬Í¨¹ý¸Ãº¯Êý£¬Ê¹ÆäʧЧ
switch( request_mode )
{
case RM_NOQUEUE:
dev->queue = blk_alloc_queue( GFP_KERNEL );
if( dev->queue == NULL )
goto out_vfree;
blk_queue_make_request( dev->queue, gkdd_make_request );
break;
case RM_FULL:
dev->queue = blk_init_queue( gkdd_full_request, &dev->lock );
if( dev->queue == NULL )
goto out_vfree;
break;
default:
printk( KERN_NOTICE "Bad request_mode %d, using simple\n", request_mode );
case RM_SIMPLE: //ÎÒÃÇ´¦ÀíµÄÊÇÕâÖÖģʽ£¬µ±È»£¬ÄãÒ²¿ÉÒÔ¸Ä³ÉÆäËûģʽ²âÊÔÒÔÏÂ
dev->queue = blk_init_queue( gkdd_request, &dev->lock );
if( dev->queue == NULL )
goto out_vfree;
break;
}
blk_queue_hardsect_size( dev->queue, hardsect_size );
dev->queue->queuedata = dev;
dev->gd = alloc_disk( GKDD_MINORS ); //»ñµÃÓ²ÅÌÔÚÄں˵ÄÊý¾Ý½á¹¹
if( !dev->gd )
{
printk( KERN_NOTICE "alloc_disk failure\n" );
goto out_vfree;
}
dev->gd->major = gkdd_major;
dev->gd->first_minor = which*GKDD_MINORS;
dev->gd->fops = &gkdd_ops; //Õâ¸öfopsºÜÊìϤ°É£¬ÔÚÄĶù¿´µ½¹ýÀàËÆµÄÄ£Ñù£¿
dev->gd->queue = dev->queue; //ÇëÇó¶ÓÁÐ
dev->gd->private_data = dev; //±£´æÎÒÃǵÄÉ豸½á¹¹
snprintf( dev->gd->disk_name, 32, "gkdd_disk_%c", which + 'a' ); //É豸Ãû£¬/dev/gkdd_disk_a, b, c, d
set_capacity( dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE) ); //ÈÝÁ¿£¬ÒÔÉÈÇøÎªµ¥Î»
add_disk( dev->gd ); //¼ÓÈëµ½ÄÚºË
return;
out_vfree:
if( dev->data )
vfree( dev->data );
}
Õâ¾ÍÊÇÎÒÃǵÄÓ²ÅÌÌṩµÄ²Ù×÷½è¿ÚÁË£º
static struct block_device_operations gkdd_ops =
{
.owner = THIS_MODULE, //Õâ¸ö¾ÍÊÇÕâ¸öÖµ£¬Ã»µÃÑ¡Ôñ
.open = gkdd_open,
.release = gkdd_release,
.media_changed = gkdd_media_changed,
.revalidate_disk = gkdd_revalidate,
.ioctl = gkdd_ioctl
};
ͨ¹ý¸Ã·½·¨£¬ÏòÄÚºËÖÐ×¢²áÁË4¿éÓ²ÅÌ£¬²»¹ýÏÖÔÚ»¹²»ÄÜfdisk -l¡£
´Óopen¿ªÊ¼£º
static int gkdd_open( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data; //ÎÒÃǵÄÉ豸½á¹¹±£´æÔÚgendiskµÄprivate_dataÀï
del_timer_sync( &dev->timer ); //Èç¹ûÉϴιرÕÉèÖÃÁ˶¨Ê±Æ÷£¬¶øÔÚ30ÃëÄÚ£¬ÄãÓÖʹÓÃÁ˸ÃÉ豸£¬¾Í²»Ê¹ËüʵЧÁË
filp->private_data = dev; //°áµ½ÐéÄâÎļþϵͳÀïÈ¥
spin_lock( &dev->lock );
if( !dev->users ) //²é¿´ÊÇ·ñ¸ü»»Á˽éÖÊ£¬Õâ¸ö´¿´âΪÁËÄ£ÄâCD,DVDµÈÀàËÆÉ豸Ìṩ
check_disk_change( inode->i_bdev );
dev->users++; //Ôö¼ÓÒýÓüÆÊý
spin_unlock( &dev->lock );
return 0;
}
release
static int gkdd_release( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data;
spin_lock( &dev->lock );
dev->users--;
if( !dev->users ) //ûÓÐÈËʹÓÃÁË£¬¾ÍÉèÖÃÒ»¸öÆÚÏÞ£¬·ñÔò£¬½éÖʾÍÈÏΪ¶ªÁË
{
dev->timer.expires = jiffies + INVALIDATE_DELAY;
add_timer( &dev->timer );
}
spin_unlock( &dev->lock );
return 0;
}
¸ü»»É豸ºó£¬ÐèҪһЩ³õʼ»¯µÄ²Ù×÷£¬
int gkdd_media_changed( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;
return dev->media_change;
}
¼¤»î³õʼ»¯ºó£¬ÄãµÄÓ²ÅÌÉϵÄÎļþϵͳʲôµÄ¾Í±»memsetΪ0ÁË¡£
int gkdd_revalidate( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;
if( dev->media_change )
{
dev->media_change = 0;
memset( dev->data, 0, dev->size );
}
return 0;
}
ΪÁËÌṩijЩӦÓÃÈçfdiskµÈ£¬ÐèÒªÖªµÀCHSÐÅÏ¢¶øÌṩ¡£ÏÖÔÚ´ó¼Ò¶¼ÓÃLBAÁË£¬ËùÒÔ»¹µÈ½«LBA»»Ëã³ÉCHS£¬Æä¼ÆËã·½·¨ÈçÏ£º
CHS2LBA
LBA = (C*number of heads per cylinder + H)*number of sectors per head + S-1
LBA2CHS
C = LBA / (heads per cylinder * sectors per track)
temp = LBA % (heads per cylinder * sectors per track)
H = temp / sectors per track
S = temp % sectors per track + 1
µ±È»£¬ÕâÖ»ÊÇÒ»¸ö¼òµ¥µÄ»»ËãÁË£¬ÏàÐÅÊÀ¼ÍµÄÓ²ÅÌʵÏÖ£¬ÎªÁË¿¼ÂÇ´ÅÅÌתËÙºÍÊý¾Ý´¦ÀíÒÔ¼°»º´æ´óС֮¼äµÄ¹ØÏµ£¬Êµ¼ÊÉϵĶÔÓ¦¹ØÏµÓ¦¸Ã²»ÊÇÕâÑùµÄ¡£¶øÎÒÃǵÄgkdd¾Í¸ü¼ÙÁË£¬Òâ˼һ϶øÒÑ¡£
int gkdd_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg )
{
long size;
struct hd_geometry geo;
struct gkdd_dev *dev = filp->private_data;
switch( cmd )
{
case HDIO_GETGEO:
size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
geo.cylinders = ( size & ~0x3f ) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if( copy_to_user( (void __user *)arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY;
}
ÏÖÔÚÓ²ÅÌ¿ÉÒÔ½ÓÊÜÓû§µÄ²Ù×÷ÇëÇóÁË£¬
//¶Áд´«ÊäÇëÇó
static void gkdd_transger( struct gkdd_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write )
{
unsigned long ffset = sector * KERNEL_SECTOR_SIZE;
unsigned long nbytes = nsect * KERNEL_SECTOR_SIZE;
if( (offset + nbytes) > dev->size )
{
printk( KERN_DEBUG "GKDD-disk: Beyond-end write (%ld %ld)\n", offset, nbytes);
return;
}
if( write )
memcpy( dev->data + offset, buffer, nbytes ); //дÇëÇ󣬴ÓÓû§¿Õ¼äдÈëÓ²ÅÌ
else
memcpy( buffer, dev->data + offset, nbytes ); //¶ÁÇëÇó£¬Ïà·´
}
static void gkdd_request( request_queue_t *q )
{
struct request *req;
while( (req = elv_next_request(q)) != NULL )
{
struct gkdd_dev *dev = req->rq_disk->private_data;
if( !blk_fs_request(req) )
{
printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
end_request( req, 0 );
continue;
}
gkdd_transger( dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir( req ));
end_request( req, 1 );
}
}
//ÐèÒª´«ÊäÒ»×éÊý¾Ý¿éʱ£¬¾ÍÒªÓõ½block I/OÊý¾Ý½á¹¹ÁË£¬¼´struct bio£¬ÕâÀï¶Ôû¿éblock»¹ÊÇÂýÂýtransfer°É¡£
static int gkdd_xfer_bio( struct gkdd_dev *dev, struct bio *bio )
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
bio_for_each_segment( bvec, bio, i )
{
char *buffer = __bio_kmap_atomic( bio, i, KM_USER0 );
gkdd_transger( dev, sector, bio_cur_sectors( bio ), buffer, bio_data_dir( bio ) == WRITE );
sector += bio_cur_sectors( bio );
__bio_kunmap_atomic( bio, KM_USER0 );
}
return 0;
}
static int gkdd_xfer_request( struct gkdd_dev *dev, struct request *req )
{
struct bio *bio;
int nsect = 0;
rq_for_each_bio( bio, req )
{
gkdd_xfer_bio( dev, bio );
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
return nsect;
}
static void gkdd_full_request( request_queue_t *q )
{
struct request *req;
int sectors_xferred;
struct gkdd_dev *dev = q->queuedata;
while( (req = elv_next_request( q )) != NULL )
{
if( !blk_fs_request( req ) )
{
printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
end_request( req, 0 );
continue;
}
sectors_xferred = gkdd_xfer_request( dev, req );
if( !end_that_request_first( req, 1, sectors_xferred ) )
{
blkdev_dequeue_request( req );
end_that_request_last( req, 0 ); //*********need to be rethink ************
}
}
}
static int gkdd_make_request( request_queue_t *q, struct bio *bio )
{
struct gkdd_dev *dev = q->queuedata;
int status;
status = gkdd_xfer_bio( dev, bio );
bio_endio( bio, bio->bi_size, status );
return 0;
}
//Í˳öÄ£¿é£¬È¥Òª×¢ÏúÉ豸£¬²¢ÊÍ·ÅÏÈǰÉêÇëµÄ×ÊÔ´¡£
static void gkdd_exit_module(void)
{
int i;
for( i=0; i<ndevices; i++ )
{
struct gkdd_dev *dev = Devices + i;
del_timer_sync( &dev->timer );
if( dev->gd )
{
del_gendisk( dev->gd );
put_disk( dev->gd );
}
if( dev->queue )
{
if( request_mode == RM_NOQUEUE )
blk_put_queue( dev->queue );
else
blk_cleanup_queue( dev->queue );
}
if( dev->data )
vfree( dev->data );
}
unregister_blkdev( gkdd_major, "gingko_disk" );
kfree( Devices );
printk( KERN_DEBUG "Module gkdd exit\n" );
}
module_exit(gkdd_exit_module);
ÍêÕûµÄ´úÂëÈçÏ£¨²Î¿¼ldd3 source£©£º
ÔËÐл·¾³linux-2.6.20, ½ÏµÍµÄ°æ±¾¿ÉÄÜ»áÓÐÎÊÌ⣬ÒòΪÄں˶ÔijЩÄÚ´æ·ÖÅäµÄÊý¾Ý½á¹¹ºÍ²Ù×÷×öÁËһЩÐ޸ġ£
´ÓÐéÄâÎļþϵͳ£¬Îļþϵͳ£¬Âß¼¾íһ·˳Á÷¶øÏ£¬Ò»Ö±À´µ½ÁËÓ²ÅÌ¿éÉ豸Çý¶¯³ÌÐò¡£´ÓÉϲ㿴ϲ㣬×ÜÊDZ»ÏµÍ³µÄʵÏÖ°áŪµÃÔÆÕÚÎíÕÏ£¬²»Ã÷ËùÒÔ£¬Òò´Ë£¬¸ÉÂ𲻸ɴà´Óµ×²ã¿ªÊ¼ÂýÂýÍùÉÏÅÀ¡£
OK,ÈÃÎÒÃǾʹӿéÉ豸µÄÇý¶¯³ÌÐò¿ªÊ¼°É¡£
Ê×Ïȶ¨ÒåһЩ³£ÓñäÁ¿
£¨ÎªÊ²Ã´ÊÇGK? GK stands for gingko¡£ÕâÊÇGingko Storage SystemµÄÒ»¸ö×é³É²¿·Ö¡£Ê²Ã´ÊÇGingko Storage System? -_-!!!ÊÐÃæÉÏ¿´²»µÀµÄ£¬ºÇºÇ¡££©
#define GKDD_MAJOR 0 //Ö÷É豸ºÅ
#define GKDD_SIZE 2048 //´ÅÅÌ´óС
#define GKDD_BLKSIZE 1024 //´æÈ¡¿éµÄ´óС£¬
#define GKDD_HARDSECT 512 //ÿ¸öÉÈÇøµÄ´óС
Õâ¾ÍÊÇÎÒÃǵÄÓ²Å̵Ļù±¾²ÎÊýÁË£¬ÏÂÃæ»¹ÒªÎªÎÒÃǵÄÇý¶¯É趨¼¸¸ö±äÁ¿
static int gkdd_major = 0;
module_param( gkdd_major, int, 0 );
static int hardsect_size = 512;
module_param( hardsect_size, int, 0 );
static int nsectors = 1024;
module_param( nsectors, int, 0 );
static int ndevices = 4;
module_param( ndevices, int, 0 );
Ä£¿é¿ÉÒÔ´«µÝ4¸öintÐͲÎÊý£¬4¿éÓ²ÅÌ£¬Ã¿¿éÅÌ1024¸öÉÈÇø£¬Ã¿¸öÉÈÇø512×Ö½Ú¡£
enum
{
RM_SIMPLE = 0,
RM_FULL = 1,
RM_NOQUEUE = 2,
};
static int request_mode = RM_SIMPLE;
module_param( request_mode, int, 0 );
ʹÓüòµ¥µÄÊý¾ÝÇëÇóģʽ¡£
struct gkdd_dev
{
int size;
u8 *data;
short users;
short media_change;
spinlock_t lock;
struct request_queue *queue;
struct gendisk *gd;
struct timer_list timer;
};
static struct gkdd_dev *Devices = NULL;
ºÃÁË£¬Õâ¾ÍÊÇÔÛµÄÓ²ÅÌÊý¾Ý½á¹¹£¬ÓиÃÓ²ÅÌÔÚÄں˵ıíʾgd, Ó²Å̵IJÙ×÷ÇëÇó¶ÓÁÐqueue£¬É豸²Ù×÷×ÔÐýËø£¬ÒÔ¼°²Ù×÷ÑÓʱµÄ¶¨Ê±Æ÷,ͨ¹ý¶¨Ê±Æ÷Ä£ÄâÒ»¸ö¿É¸ü»»½éÖʵĿìÉ豸£¬ÈçÄãµÄDVD¡£
Ò»ÇÐ×¼±¸¾ÍÐ÷£¬ÏòÄÚºË×¢²áÔÛÃǵÄÓ²Å̰ɡ£
static int gkdd_init_module(void)
{
int i;
gkdd_major = register_blkdev( gkdd_major, "gingko_disk" );
if( gkdd_major <= 0 )
{
printk( KERN_WARNING "GKDD-disk: unable to get major number\n" );
return -EBUSY;
}
Devices = kmalloc( ndevices*sizeof( struct gkdd_dev ), GFP_KERNEL );
if( Devices == NULL )
goto out_unregister;
for( i=0; i< ndevices; i++ )
setup_device( Devices + i, i );
printk( KERN_DEBUG "Module gkdd init\n" );
return 0;
out_unregister:
unregister_blkdev( gkdd_major, "sbd" );
return -ENOMEM;
}
module_init(gkdd_init_module);
µ±È»£¬ÕâÀïÃæÓÐÒ»¸ö¹Ø¼üµÄº¯Êý£¬¾ÍÊÇset_updevice()¡£
static void setup_device( struct gkdd_dev *dev, int which )
{
memset( dev, 0, sizeof( struct gkdd_dev ));
dev->size = nsectors*hardsect_size; //É豸µÄ´óС
dev->data = vmalloc( dev->size ); //É豸µÄÊý¾Ý¿Õ¼ä£¬ÒòΪÊÇRAMDISK£¬Ö±½ÓÉêÇëÈ«²¿µÄ¿Õ¼ä£¬·´ÕýÊÇÐé´æ£¬²»Ì«¹ý·Ö£¬×ÜÄÜÓ¦¸¶¡£
if( dev->data == NULL )
{
printk( KERN_NOTICE "vmalloc failure.\n" );
return;
}
spin_lock_init( &dev->lock );
init_timer( &dev->timer );
dev->timer.data = (unsigned long) dev;
dev->timer.function = gkdd_invalidate; //ÑÓ³Ù30Ãëºó£¬¸ÃÉ豸µÄ½éÖʾÍÈÏΪÒѾ±»ÈËÈ¥ÏÂÀ´ÁË£¬Í¨¹ý¸Ãº¯Êý£¬Ê¹ÆäʧЧ
switch( request_mode )
{
case RM_NOQUEUE:
dev->queue = blk_alloc_queue( GFP_KERNEL );
if( dev->queue == NULL )
goto out_vfree;
blk_queue_make_request( dev->queue, gkdd_make_request );
break;
case RM_FULL:
dev->queue = blk_init_queue( gkdd_full_request, &dev->lock );
if( dev->queue == NULL )
goto out_vfree;
break;
default:
printk( KERN_NOTICE "Bad request_mode %d, using simple\n", request_mode );
case RM_SIMPLE: //ÎÒÃÇ´¦ÀíµÄÊÇÕâÖÖģʽ£¬µ±È»£¬ÄãÒ²¿ÉÒÔ¸Ä³ÉÆäËûģʽ²âÊÔÒÔÏÂ
dev->queue = blk_init_queue( gkdd_request, &dev->lock );
if( dev->queue == NULL )
goto out_vfree;
break;
}
blk_queue_hardsect_size( dev->queue, hardsect_size );
dev->queue->queuedata = dev;
dev->gd = alloc_disk( GKDD_MINORS ); //»ñµÃÓ²ÅÌÔÚÄں˵ÄÊý¾Ý½á¹¹
if( !dev->gd )
{
printk( KERN_NOTICE "alloc_disk failure\n" );
goto out_vfree;
}
dev->gd->major = gkdd_major;
dev->gd->first_minor = which*GKDD_MINORS;
dev->gd->fops = &gkdd_ops; //Õâ¸öfopsºÜÊìϤ°É£¬ÔÚÄĶù¿´µ½¹ýÀàËÆµÄÄ£Ñù£¿
dev->gd->queue = dev->queue; //ÇëÇó¶ÓÁÐ
dev->gd->private_data = dev; //±£´æÎÒÃǵÄÉ豸½á¹¹
snprintf( dev->gd->disk_name, 32, "gkdd_disk_%c", which + 'a' ); //É豸Ãû£¬/dev/gkdd_disk_a, b, c, d
set_capacity( dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE) ); //ÈÝÁ¿£¬ÒÔÉÈÇøÎªµ¥Î»
add_disk( dev->gd ); //¼ÓÈëµ½ÄÚºË
return;
out_vfree:
if( dev->data )
vfree( dev->data );
}
Õâ¾ÍÊÇÎÒÃǵÄÓ²ÅÌÌṩµÄ²Ù×÷½è¿ÚÁË£º
static struct block_device_operations gkdd_ops =
{
.owner = THIS_MODULE, //Õâ¸ö¾ÍÊÇÕâ¸öÖµ£¬Ã»µÃÑ¡Ôñ
.open = gkdd_open,
.release = gkdd_release,
.media_changed = gkdd_media_changed,
.revalidate_disk = gkdd_revalidate,
.ioctl = gkdd_ioctl
};
ͨ¹ý¸Ã·½·¨£¬ÏòÄÚºËÖÐ×¢²áÁË4¿éÓ²ÅÌ£¬²»¹ýÏÖÔÚ»¹²»ÄÜfdisk -l¡£
´Óopen¿ªÊ¼£º
static int gkdd_open( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data; //ÎÒÃǵÄÉ豸½á¹¹±£´æÔÚgendiskµÄprivate_dataÀï
del_timer_sync( &dev->timer ); //Èç¹ûÉϴιرÕÉèÖÃÁ˶¨Ê±Æ÷£¬¶øÔÚ30ÃëÄÚ£¬ÄãÓÖʹÓÃÁ˸ÃÉ豸£¬¾Í²»Ê¹ËüʵЧÁË
filp->private_data = dev; //°áµ½ÐéÄâÎļþϵͳÀïÈ¥
spin_lock( &dev->lock );
if( !dev->users ) //²é¿´ÊÇ·ñ¸ü»»Á˽éÖÊ£¬Õâ¸ö´¿´âΪÁËÄ£ÄâCD,DVDµÈÀàËÆÉ豸Ìṩ
check_disk_change( inode->i_bdev );
dev->users++; //Ôö¼ÓÒýÓüÆÊý
spin_unlock( &dev->lock );
return 0;
}
release
static int gkdd_release( struct inode *inode, struct file *filp )
{
struct gkdd_dev *dev = inode->i_bdev->bd_disk->private_data;
spin_lock( &dev->lock );
dev->users--;
if( !dev->users ) //ûÓÐÈËʹÓÃÁË£¬¾ÍÉèÖÃÒ»¸öÆÚÏÞ£¬·ñÔò£¬½éÖʾÍÈÏΪ¶ªÁË
{
dev->timer.expires = jiffies + INVALIDATE_DELAY;
add_timer( &dev->timer );
}
spin_unlock( &dev->lock );
return 0;
}
¸ü»»É豸ºó£¬ÐèҪһЩ³õʼ»¯µÄ²Ù×÷£¬
int gkdd_media_changed( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;
return dev->media_change;
}
¼¤»î³õʼ»¯ºó£¬ÄãµÄÓ²ÅÌÉϵÄÎļþϵͳʲôµÄ¾Í±»memsetΪ0ÁË¡£
int gkdd_revalidate( struct gendisk *gd )
{
struct gkdd_dev *dev = gd->private_data;
if( dev->media_change )
{
dev->media_change = 0;
memset( dev->data, 0, dev->size );
}
return 0;
}
ΪÁËÌṩijЩӦÓÃÈçfdiskµÈ£¬ÐèÒªÖªµÀCHSÐÅÏ¢¶øÌṩ¡£ÏÖÔÚ´ó¼Ò¶¼ÓÃLBAÁË£¬ËùÒÔ»¹µÈ½«LBA»»Ëã³ÉCHS£¬Æä¼ÆËã·½·¨ÈçÏ£º
CHS2LBA
LBA = (C*number of heads per cylinder + H)*number of sectors per head + S-1
LBA2CHS
C = LBA / (heads per cylinder * sectors per track)
temp = LBA % (heads per cylinder * sectors per track)
H = temp / sectors per track
S = temp % sectors per track + 1
µ±È»£¬ÕâÖ»ÊÇÒ»¸ö¼òµ¥µÄ»»ËãÁË£¬ÏàÐÅÊÀ¼ÍµÄÓ²ÅÌʵÏÖ£¬ÎªÁË¿¼ÂÇ´ÅÅÌתËÙºÍÊý¾Ý´¦ÀíÒÔ¼°»º´æ´óС֮¼äµÄ¹ØÏµ£¬Êµ¼ÊÉϵĶÔÓ¦¹ØÏµÓ¦¸Ã²»ÊÇÕâÑùµÄ¡£¶øÎÒÃǵÄgkdd¾Í¸ü¼ÙÁË£¬Òâ˼һ϶øÒÑ¡£
int gkdd_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg )
{
long size;
struct hd_geometry geo;
struct gkdd_dev *dev = filp->private_data;
switch( cmd )
{
case HDIO_GETGEO:
size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE);
geo.cylinders = ( size & ~0x3f ) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if( copy_to_user( (void __user *)arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY;
}
ÏÖÔÚÓ²ÅÌ¿ÉÒÔ½ÓÊÜÓû§µÄ²Ù×÷ÇëÇóÁË£¬
//¶Áд´«ÊäÇëÇó
static void gkdd_transger( struct gkdd_dev *dev, unsigned long sector, unsigned long nsect, char *buffer, int write )
{
unsigned long ffset = sector * KERNEL_SECTOR_SIZE;
unsigned long nbytes = nsect * KERNEL_SECTOR_SIZE;
if( (offset + nbytes) > dev->size )
{
printk( KERN_DEBUG "GKDD-disk: Beyond-end write (%ld %ld)\n", offset, nbytes);
return;
}
if( write )
memcpy( dev->data + offset, buffer, nbytes ); //дÇëÇ󣬴ÓÓû§¿Õ¼äдÈëÓ²ÅÌ
else
memcpy( buffer, dev->data + offset, nbytes ); //¶ÁÇëÇó£¬Ïà·´
}
static void gkdd_request( request_queue_t *q )
{
struct request *req;
while( (req = elv_next_request(q)) != NULL )
{
struct gkdd_dev *dev = req->rq_disk->private_data;
if( !blk_fs_request(req) )
{
printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
end_request( req, 0 );
continue;
}
gkdd_transger( dev, req->sector, req->current_nr_sectors, req->buffer, rq_data_dir( req ));
end_request( req, 1 );
}
}
//ÐèÒª´«ÊäÒ»×éÊý¾Ý¿éʱ£¬¾ÍÒªÓõ½block I/OÊý¾Ý½á¹¹ÁË£¬¼´struct bio£¬ÕâÀï¶Ôû¿éblock»¹ÊÇÂýÂýtransfer°É¡£
static int gkdd_xfer_bio( struct gkdd_dev *dev, struct bio *bio )
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
bio_for_each_segment( bvec, bio, i )
{
char *buffer = __bio_kmap_atomic( bio, i, KM_USER0 );
gkdd_transger( dev, sector, bio_cur_sectors( bio ), buffer, bio_data_dir( bio ) == WRITE );
sector += bio_cur_sectors( bio );
__bio_kunmap_atomic( bio, KM_USER0 );
}
return 0;
}
static int gkdd_xfer_request( struct gkdd_dev *dev, struct request *req )
{
struct bio *bio;
int nsect = 0;
rq_for_each_bio( bio, req )
{
gkdd_xfer_bio( dev, bio );
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
return nsect;
}
static void gkdd_full_request( request_queue_t *q )
{
struct request *req;
int sectors_xferred;
struct gkdd_dev *dev = q->queuedata;
while( (req = elv_next_request( q )) != NULL )
{
if( !blk_fs_request( req ) )
{
printk( KERN_NOTICE "GKDD-disk: Skip non-fs request\n" );
end_request( req, 0 );
continue;
}
sectors_xferred = gkdd_xfer_request( dev, req );
if( !end_that_request_first( req, 1, sectors_xferred ) )
{
blkdev_dequeue_request( req );
end_that_request_last( req, 0 ); //*********need to be rethink ************
}
}
}
static int gkdd_make_request( request_queue_t *q, struct bio *bio )
{
struct gkdd_dev *dev = q->queuedata;
int status;
status = gkdd_xfer_bio( dev, bio );
bio_endio( bio, bio->bi_size, status );
return 0;
}
//Í˳öÄ£¿é£¬È¥Òª×¢ÏúÉ豸£¬²¢ÊÍ·ÅÏÈǰÉêÇëµÄ×ÊÔ´¡£
static void gkdd_exit_module(void)
{
int i;
for( i=0; i<ndevices; i++ )
{
struct gkdd_dev *dev = Devices + i;
del_timer_sync( &dev->timer );
if( dev->gd )
{
del_gendisk( dev->gd );
put_disk( dev->gd );
}
if( dev->queue )
{
if( request_mode == RM_NOQUEUE )
blk_put_queue( dev->queue );
else
blk_cleanup_queue( dev->queue );
}
if( dev->data )
vfree( dev->data );
}
unregister_blkdev( gkdd_major, "gingko_disk" );
kfree( Devices );
printk( KERN_DEBUG "Module gkdd exit\n" );
}
module_exit(gkdd_exit_module);
ÍêÕûµÄ´úÂëÈçÏ£¨²Î¿¼ldd3 source£©£º
ÔËÐл·¾³linux-2.6.20, ½ÏµÍµÄ°æ±¾¿ÉÄÜ»áÓÐÎÊÌ⣬ÒòΪÄں˶ÔijЩÄÚ´æ·ÖÅäµÄÊý¾Ý½á¹¹ºÍ²Ù×÷×öÁËһЩÐ޸ġ£


