18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * ARAnyM block device driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 58c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 68c2ecf20Sopenharmony_ci * for more details. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/genhd.h> 178c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 188c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/natfeat.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic long nfhd_id; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cienum { 268c2ecf20Sopenharmony_ci /* emulation entry points */ 278c2ecf20Sopenharmony_ci NFHD_READ_WRITE = 10, 288c2ecf20Sopenharmony_ci NFHD_GET_CAPACITY = 14, 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* skip ACSI devices */ 318c2ecf20Sopenharmony_ci NFHD_DEV_OFFSET = 8, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline s32 nfhd_read_write(u32 major, u32 minor, u32 rwflag, u32 recno, 358c2ecf20Sopenharmony_ci u32 count, u32 buf) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return nf_call(nfhd_id + NFHD_READ_WRITE, major, minor, rwflag, recno, 388c2ecf20Sopenharmony_ci count, buf); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline s32 nfhd_get_capacity(u32 major, u32 minor, u32 *blocks, 428c2ecf20Sopenharmony_ci u32 *blocksize) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return nf_call(nfhd_id + NFHD_GET_CAPACITY, major, minor, 458c2ecf20Sopenharmony_ci virt_to_phys(blocks), virt_to_phys(blocksize)); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic LIST_HEAD(nfhd_list); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int major_num; 518c2ecf20Sopenharmony_cimodule_param(major_num, int, 0); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct nfhd_device { 548c2ecf20Sopenharmony_ci struct list_head list; 558c2ecf20Sopenharmony_ci int id; 568c2ecf20Sopenharmony_ci u32 blocks, bsize; 578c2ecf20Sopenharmony_ci int bshift; 588c2ecf20Sopenharmony_ci struct request_queue *queue; 598c2ecf20Sopenharmony_ci struct gendisk *disk; 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic blk_qc_t nfhd_submit_bio(struct bio *bio) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct nfhd_device *dev = bio->bi_disk->private_data; 658c2ecf20Sopenharmony_ci struct bio_vec bvec; 668c2ecf20Sopenharmony_ci struct bvec_iter iter; 678c2ecf20Sopenharmony_ci int dir, len, shift; 688c2ecf20Sopenharmony_ci sector_t sec = bio->bi_iter.bi_sector; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci dir = bio_data_dir(bio); 718c2ecf20Sopenharmony_ci shift = dev->bshift; 728c2ecf20Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 738c2ecf20Sopenharmony_ci len = bvec.bv_len; 748c2ecf20Sopenharmony_ci len >>= 9; 758c2ecf20Sopenharmony_ci nfhd_read_write(dev->id, 0, dir, sec >> shift, len >> shift, 768c2ecf20Sopenharmony_ci page_to_phys(bvec.bv_page) + bvec.bv_offset); 778c2ecf20Sopenharmony_ci sec += len; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci bio_endio(bio); 808c2ecf20Sopenharmony_ci return BLK_QC_T_NONE; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int nfhd_getgeo(struct block_device *bdev, struct hd_geometry *geo) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct nfhd_device *dev = bdev->bd_disk->private_data; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci geo->cylinders = dev->blocks >> (6 - dev->bshift); 888c2ecf20Sopenharmony_ci geo->heads = 4; 898c2ecf20Sopenharmony_ci geo->sectors = 16; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic const struct block_device_operations nfhd_ops = { 958c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 968c2ecf20Sopenharmony_ci .submit_bio = nfhd_submit_bio, 978c2ecf20Sopenharmony_ci .getgeo = nfhd_getgeo, 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int __init nfhd_init_one(int id, u32 blocks, u32 bsize) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct nfhd_device *dev; 1038c2ecf20Sopenharmony_ci int dev_id = id - NFHD_DEV_OFFSET; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci pr_info("nfhd%u: found device with %u blocks (%u bytes)\n", dev_id, 1068c2ecf20Sopenharmony_ci blocks, bsize); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (bsize < 512 || (bsize & (bsize - 1))) { 1098c2ecf20Sopenharmony_ci pr_warn("nfhd%u: invalid block size\n", dev_id); 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci dev = kmalloc(sizeof(struct nfhd_device), GFP_KERNEL); 1148c2ecf20Sopenharmony_ci if (!dev) 1158c2ecf20Sopenharmony_ci goto out; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci dev->id = id; 1188c2ecf20Sopenharmony_ci dev->blocks = blocks; 1198c2ecf20Sopenharmony_ci dev->bsize = bsize; 1208c2ecf20Sopenharmony_ci dev->bshift = ffs(bsize) - 10; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci dev->queue = blk_alloc_queue(NUMA_NO_NODE); 1238c2ecf20Sopenharmony_ci if (dev->queue == NULL) 1248c2ecf20Sopenharmony_ci goto free_dev; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci blk_queue_logical_block_size(dev->queue, bsize); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci dev->disk = alloc_disk(16); 1298c2ecf20Sopenharmony_ci if (!dev->disk) 1308c2ecf20Sopenharmony_ci goto free_queue; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dev->disk->major = major_num; 1338c2ecf20Sopenharmony_ci dev->disk->first_minor = dev_id * 16; 1348c2ecf20Sopenharmony_ci dev->disk->fops = &nfhd_ops; 1358c2ecf20Sopenharmony_ci dev->disk->private_data = dev; 1368c2ecf20Sopenharmony_ci sprintf(dev->disk->disk_name, "nfhd%u", dev_id); 1378c2ecf20Sopenharmony_ci set_capacity(dev->disk, (sector_t)blocks * (bsize / 512)); 1388c2ecf20Sopenharmony_ci dev->disk->queue = dev->queue; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci add_disk(dev->disk); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci list_add_tail(&dev->list, &nfhd_list); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cifree_queue: 1478c2ecf20Sopenharmony_ci blk_cleanup_queue(dev->queue); 1488c2ecf20Sopenharmony_cifree_dev: 1498c2ecf20Sopenharmony_ci kfree(dev); 1508c2ecf20Sopenharmony_ciout: 1518c2ecf20Sopenharmony_ci return -ENOMEM; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int __init nfhd_init(void) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci u32 blocks, bsize; 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci nfhd_id = nf_get_id("XHDI"); 1618c2ecf20Sopenharmony_ci if (!nfhd_id) 1628c2ecf20Sopenharmony_ci return -ENODEV; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = register_blkdev(major_num, "nfhd"); 1658c2ecf20Sopenharmony_ci if (ret < 0) { 1668c2ecf20Sopenharmony_ci pr_warn("nfhd: unable to get major number\n"); 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!major_num) 1718c2ecf20Sopenharmony_ci major_num = ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci for (i = NFHD_DEV_OFFSET; i < 24; i++) { 1748c2ecf20Sopenharmony_ci if (nfhd_get_capacity(i, 0, &blocks, &bsize)) 1758c2ecf20Sopenharmony_ci continue; 1768c2ecf20Sopenharmony_ci nfhd_init_one(i, blocks, bsize); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void __exit nfhd_exit(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct nfhd_device *dev, *next; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, next, &nfhd_list, list) { 1878c2ecf20Sopenharmony_ci list_del(&dev->list); 1888c2ecf20Sopenharmony_ci del_gendisk(dev->disk); 1898c2ecf20Sopenharmony_ci put_disk(dev->disk); 1908c2ecf20Sopenharmony_ci blk_cleanup_queue(dev->queue); 1918c2ecf20Sopenharmony_ci kfree(dev); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci unregister_blkdev(major_num, "nfhd"); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cimodule_init(nfhd_init); 1978c2ecf20Sopenharmony_cimodule_exit(nfhd_exit); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 200