2008-11-18 23:02:19 +03:00
/*
* ARAnyM block device driver
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive
* for more details .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/genhd.h>
# include <linux/blkdev.h>
# include <linux/hdreg.h>
# include <linux/slab.h>
# include <asm/natfeat.h>
static long nfhd_id ;
enum {
/* emulation entry points */
NFHD_READ_WRITE = 10 ,
NFHD_GET_CAPACITY = 14 ,
/* skip ACSI devices */
NFHD_DEV_OFFSET = 8 ,
} ;
static inline s32 nfhd_read_write ( u32 major , u32 minor , u32 rwflag , u32 recno ,
u32 count , u32 buf )
{
return nf_call ( nfhd_id + NFHD_READ_WRITE , major , minor , rwflag , recno ,
count , buf ) ;
}
static inline s32 nfhd_get_capacity ( u32 major , u32 minor , u32 * blocks ,
u32 * blocksize )
{
return nf_call ( nfhd_id + NFHD_GET_CAPACITY , major , minor , blocks ,
blocksize ) ;
}
static LIST_HEAD ( nfhd_list ) ;
static int major_num ;
module_param ( major_num , int , 0 ) ;
struct nfhd_device {
struct list_head list ;
int id ;
u32 blocks , bsize ;
int bshift ;
struct request_queue * queue ;
struct gendisk * disk ;
} ;
2011-09-12 14:12:01 +04:00
static void nfhd_make_request ( struct request_queue * queue , struct bio * bio )
2008-11-18 23:02:19 +03:00
{
struct nfhd_device * dev = queue - > queuedata ;
struct bio_vec * bvec ;
int i , dir , len , shift ;
sector_t sec = bio - > bi_sector ;
dir = bio_data_dir ( bio ) ;
shift = dev - > bshift ;
bio_for_each_segment ( bvec , bio , i ) {
len = bvec - > bv_len ;
len > > = 9 ;
nfhd_read_write ( dev - > id , 0 , dir , sec > > shift , len > > shift ,
bvec_to_phys ( bvec ) ) ;
sec + = len ;
}
bio_endio ( bio , 0 ) ;
}
static int nfhd_getgeo ( struct block_device * bdev , struct hd_geometry * geo )
{
struct nfhd_device * dev = bdev - > bd_disk - > private_data ;
geo - > cylinders = dev - > blocks > > ( 6 - dev - > bshift ) ;
geo - > heads = 4 ;
geo - > sectors = 16 ;
return 0 ;
}
static const struct block_device_operations nfhd_ops = {
. owner = THIS_MODULE ,
. getgeo = nfhd_getgeo ,
} ;
static int __init nfhd_init_one ( int id , u32 blocks , u32 bsize )
{
struct nfhd_device * dev ;
int dev_id = id - NFHD_DEV_OFFSET ;
pr_info ( " nfhd%u: found device with %u blocks (%u bytes) \n " , dev_id ,
blocks , bsize ) ;
if ( bsize < 512 | | ( bsize & ( bsize - 1 ) ) ) {
pr_warn ( " nfhd%u: invalid block size \n " , dev_id ) ;
return - EINVAL ;
}
dev = kmalloc ( sizeof ( struct nfhd_device ) , GFP_KERNEL ) ;
if ( ! dev )
goto out ;
dev - > id = id ;
dev - > blocks = blocks ;
dev - > bsize = bsize ;
dev - > bshift = ffs ( bsize ) - 10 ;
dev - > queue = blk_alloc_queue ( GFP_KERNEL ) ;
if ( dev - > queue = = NULL )
goto free_dev ;
dev - > queue - > queuedata = dev ;
blk_queue_make_request ( dev - > queue , nfhd_make_request ) ;
blk_queue_logical_block_size ( dev - > queue , bsize ) ;
dev - > disk = alloc_disk ( 16 ) ;
if ( ! dev - > disk )
goto free_queue ;
dev - > disk - > major = major_num ;
dev - > disk - > first_minor = dev_id * 16 ;
dev - > disk - > fops = & nfhd_ops ;
dev - > disk - > private_data = dev ;
sprintf ( dev - > disk - > disk_name , " nfhd%u " , dev_id ) ;
set_capacity ( dev - > disk , ( sector_t ) blocks * ( bsize / 512 ) ) ;
dev - > disk - > queue = dev - > queue ;
add_disk ( dev - > disk ) ;
list_add_tail ( & dev - > list , & nfhd_list ) ;
return 0 ;
free_queue :
blk_cleanup_queue ( dev - > queue ) ;
free_dev :
kfree ( dev ) ;
out :
return - ENOMEM ;
}
static int __init nfhd_init ( void )
{
u32 blocks , bsize ;
int i ;
nfhd_id = nf_get_id ( " XHDI " ) ;
if ( ! nfhd_id )
return - ENODEV ;
major_num = register_blkdev ( major_num , " nfhd " ) ;
if ( major_num < = 0 ) {
pr_warn ( " nfhd: unable to get major number \n " ) ;
return major_num ;
}
for ( i = NFHD_DEV_OFFSET ; i < 24 ; i + + ) {
if ( nfhd_get_capacity ( i , 0 , & blocks , & bsize ) )
continue ;
nfhd_init_one ( i , blocks , bsize ) ;
}
return 0 ;
}
static void __exit nfhd_exit ( void )
{
struct nfhd_device * dev , * next ;
list_for_each_entry_safe ( dev , next , & nfhd_list , list ) {
list_del ( & dev - > list ) ;
del_gendisk ( dev - > disk ) ;
put_disk ( dev - > disk ) ;
blk_cleanup_queue ( dev - > queue ) ;
kfree ( dev ) ;
}
unregister_blkdev ( major_num , " nfhd " ) ;
}
module_init ( nfhd_init ) ;
module_exit ( nfhd_exit ) ;
MODULE_LICENSE ( " GPL " ) ;