/* * 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 #include #include #include #include #include #include #include #include #include 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, virt_to_phys(blocks), virt_to_phys(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 gendisk *disk; }; static void nfhd_submit_bio(struct bio *bio) { struct nfhd_device *dev = bio->bi_bdev->bd_disk->private_data; struct bio_vec bvec; struct bvec_iter iter; int dir, len, shift; sector_t sec = bio->bi_iter.bi_sector; dir = bio_data_dir(bio); shift = dev->bshift; bio_for_each_segment(bvec, bio, iter) { len = bvec.bv_len; len >>= 9; nfhd_read_write(dev->id, 0, dir, sec >> shift, len >> shift, page_to_phys(bvec.bv_page) + bvec.bv_offset); sec += len; } bio_endio(bio); } 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, .submit_bio = nfhd_submit_bio, .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; int err = -ENOMEM; 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->disk = blk_alloc_disk(NUMA_NO_NODE); if (!dev->disk) goto free_dev; dev->disk->major = major_num; dev->disk->first_minor = dev_id * 16; dev->disk->minors = 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)); blk_queue_logical_block_size(dev->disk->queue, bsize); err = add_disk(dev->disk); if (err) goto out_cleanup_disk; list_add_tail(&dev->list, &nfhd_list); return 0; out_cleanup_disk: put_disk(dev->disk); free_dev: kfree(dev); out: return err; } static int __init nfhd_init(void) { u32 blocks, bsize; int ret; int i; nfhd_id = nf_get_id("XHDI"); if (!nfhd_id) return -ENODEV; ret = register_blkdev(major_num, "nfhd"); if (ret < 0) { pr_warn("nfhd: unable to get major number\n"); return ret; } if (!major_num) major_num = ret; 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); kfree(dev); } unregister_blkdev(major_num, "nfhd"); } module_init(nfhd_init); module_exit(nfhd_exit); MODULE_LICENSE("GPL");