162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * ARAnyM block device driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 562306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 662306a36Sopenharmony_ci * for more details. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/moduleparam.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/blkdev.h> 1762306a36Sopenharmony_ci#include <linux/hdreg.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/natfeat.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic long nfhd_id; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cienum { 2562306a36Sopenharmony_ci /* emulation entry points */ 2662306a36Sopenharmony_ci NFHD_READ_WRITE = 10, 2762306a36Sopenharmony_ci NFHD_GET_CAPACITY = 14, 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci /* skip ACSI devices */ 3062306a36Sopenharmony_ci NFHD_DEV_OFFSET = 8, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline s32 nfhd_read_write(u32 major, u32 minor, u32 rwflag, u32 recno, 3462306a36Sopenharmony_ci u32 count, u32 buf) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return nf_call(nfhd_id + NFHD_READ_WRITE, major, minor, rwflag, recno, 3762306a36Sopenharmony_ci count, buf); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic inline s32 nfhd_get_capacity(u32 major, u32 minor, u32 *blocks, 4162306a36Sopenharmony_ci u32 *blocksize) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return nf_call(nfhd_id + NFHD_GET_CAPACITY, major, minor, 4462306a36Sopenharmony_ci virt_to_phys(blocks), virt_to_phys(blocksize)); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic LIST_HEAD(nfhd_list); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int major_num; 5062306a36Sopenharmony_cimodule_param(major_num, int, 0); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct nfhd_device { 5362306a36Sopenharmony_ci struct list_head list; 5462306a36Sopenharmony_ci int id; 5562306a36Sopenharmony_ci u32 blocks, bsize; 5662306a36Sopenharmony_ci int bshift; 5762306a36Sopenharmony_ci struct gendisk *disk; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void nfhd_submit_bio(struct bio *bio) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct nfhd_device *dev = bio->bi_bdev->bd_disk->private_data; 6362306a36Sopenharmony_ci struct bio_vec bvec; 6462306a36Sopenharmony_ci struct bvec_iter iter; 6562306a36Sopenharmony_ci int dir, len, shift; 6662306a36Sopenharmony_ci sector_t sec = bio->bi_iter.bi_sector; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci dir = bio_data_dir(bio); 6962306a36Sopenharmony_ci shift = dev->bshift; 7062306a36Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 7162306a36Sopenharmony_ci len = bvec.bv_len; 7262306a36Sopenharmony_ci len >>= 9; 7362306a36Sopenharmony_ci nfhd_read_write(dev->id, 0, dir, sec >> shift, len >> shift, 7462306a36Sopenharmony_ci page_to_phys(bvec.bv_page) + bvec.bv_offset); 7562306a36Sopenharmony_ci sec += len; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci bio_endio(bio); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int nfhd_getgeo(struct block_device *bdev, struct hd_geometry *geo) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct nfhd_device *dev = bdev->bd_disk->private_data; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci geo->cylinders = dev->blocks >> (6 - dev->bshift); 8562306a36Sopenharmony_ci geo->heads = 4; 8662306a36Sopenharmony_ci geo->sectors = 16; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic const struct block_device_operations nfhd_ops = { 9262306a36Sopenharmony_ci .owner = THIS_MODULE, 9362306a36Sopenharmony_ci .submit_bio = nfhd_submit_bio, 9462306a36Sopenharmony_ci .getgeo = nfhd_getgeo, 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int __init nfhd_init_one(int id, u32 blocks, u32 bsize) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct nfhd_device *dev; 10062306a36Sopenharmony_ci int dev_id = id - NFHD_DEV_OFFSET; 10162306a36Sopenharmony_ci int err = -ENOMEM; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pr_info("nfhd%u: found device with %u blocks (%u bytes)\n", dev_id, 10462306a36Sopenharmony_ci blocks, bsize); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (bsize < 512 || (bsize & (bsize - 1))) { 10762306a36Sopenharmony_ci pr_warn("nfhd%u: invalid block size\n", dev_id); 10862306a36Sopenharmony_ci return -EINVAL; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci dev = kmalloc(sizeof(struct nfhd_device), GFP_KERNEL); 11262306a36Sopenharmony_ci if (!dev) 11362306a36Sopenharmony_ci goto out; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci dev->id = id; 11662306a36Sopenharmony_ci dev->blocks = blocks; 11762306a36Sopenharmony_ci dev->bsize = bsize; 11862306a36Sopenharmony_ci dev->bshift = ffs(bsize) - 10; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci dev->disk = blk_alloc_disk(NUMA_NO_NODE); 12162306a36Sopenharmony_ci if (!dev->disk) 12262306a36Sopenharmony_ci goto free_dev; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci dev->disk->major = major_num; 12562306a36Sopenharmony_ci dev->disk->first_minor = dev_id * 16; 12662306a36Sopenharmony_ci dev->disk->minors = 16; 12762306a36Sopenharmony_ci dev->disk->fops = &nfhd_ops; 12862306a36Sopenharmony_ci dev->disk->private_data = dev; 12962306a36Sopenharmony_ci sprintf(dev->disk->disk_name, "nfhd%u", dev_id); 13062306a36Sopenharmony_ci set_capacity(dev->disk, (sector_t)blocks * (bsize / 512)); 13162306a36Sopenharmony_ci blk_queue_logical_block_size(dev->disk->queue, bsize); 13262306a36Sopenharmony_ci err = add_disk(dev->disk); 13362306a36Sopenharmony_ci if (err) 13462306a36Sopenharmony_ci goto out_cleanup_disk; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci list_add_tail(&dev->list, &nfhd_list); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciout_cleanup_disk: 14162306a36Sopenharmony_ci put_disk(dev->disk); 14262306a36Sopenharmony_cifree_dev: 14362306a36Sopenharmony_ci kfree(dev); 14462306a36Sopenharmony_ciout: 14562306a36Sopenharmony_ci return err; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int __init nfhd_init(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci u32 blocks, bsize; 15162306a36Sopenharmony_ci int ret; 15262306a36Sopenharmony_ci int i; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci nfhd_id = nf_get_id("XHDI"); 15562306a36Sopenharmony_ci if (!nfhd_id) 15662306a36Sopenharmony_ci return -ENODEV; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci ret = register_blkdev(major_num, "nfhd"); 15962306a36Sopenharmony_ci if (ret < 0) { 16062306a36Sopenharmony_ci pr_warn("nfhd: unable to get major number\n"); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (!major_num) 16562306a36Sopenharmony_ci major_num = ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (i = NFHD_DEV_OFFSET; i < 24; i++) { 16862306a36Sopenharmony_ci if (nfhd_get_capacity(i, 0, &blocks, &bsize)) 16962306a36Sopenharmony_ci continue; 17062306a36Sopenharmony_ci nfhd_init_one(i, blocks, bsize); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void __exit nfhd_exit(void) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct nfhd_device *dev, *next; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci list_for_each_entry_safe(dev, next, &nfhd_list, list) { 18162306a36Sopenharmony_ci list_del(&dev->list); 18262306a36Sopenharmony_ci del_gendisk(dev->disk); 18362306a36Sopenharmony_ci put_disk(dev->disk); 18462306a36Sopenharmony_ci kfree(dev); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci unregister_blkdev(major_num, "nfhd"); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cimodule_init(nfhd_init); 19062306a36Sopenharmony_cimodule_exit(nfhd_exit); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 193