aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/m68k/Kconfig8
-rw-r--r--arch/m68k/emu/Makefile2
-rw-r--r--arch/m68k/emu/nfblock.c195
3 files changed, 205 insertions, 0 deletions
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index f668a5802a4a..2c890b56c96e 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -247,6 +247,14 @@ config NATFEAT
247 This option enables support for ARAnyM native features, such as 247 This option enables support for ARAnyM native features, such as
248 access to a disk image as /dev/hda. 248 access to a disk image as /dev/hda.
249 249
250config NFBLOCK
251 tristate "NatFeat block device support"
252 depends on BLOCK && NATFEAT
253 help
254 Say Y to include support for the ARAnyM NatFeat block device
255 which allows direct access to the hard drives without using
256 the hardware emulation.
257
250comment "Processor type" 258comment "Processor type"
251 259
252config M68020 260config M68020
diff --git a/arch/m68k/emu/Makefile b/arch/m68k/emu/Makefile
index 34cfa348ca2b..fc4f77a3d35c 100644
--- a/arch/m68k/emu/Makefile
+++ b/arch/m68k/emu/Makefile
@@ -3,3 +3,5 @@
3# 3#
4 4
5obj-y += natfeat.o 5obj-y += natfeat.o
6
7obj-$(CONFIG_NFBLOCK) += nfblock.o
diff --git a/arch/m68k/emu/nfblock.c b/arch/m68k/emu/nfblock.c
new file mode 100644
index 000000000000..48e50f8c1c7e
--- /dev/null
+++ b/arch/m68k/emu/nfblock.c
@@ -0,0 +1,195 @@
1/*
2 * ARAnyM block device driver
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file COPYING in the main directory of this archive
6 * for more details.
7 */
8
9#include <linux/module.h>
10#include <linux/moduleparam.h>
11#include <linux/init.h>
12
13#include <linux/kernel.h>
14#include <linux/errno.h>
15#include <linux/types.h>
16#include <linux/genhd.h>
17#include <linux/blkdev.h>
18#include <linux/hdreg.h>
19#include <linux/slab.h>
20
21#include <asm/natfeat.h>
22
23static long nfhd_id;
24
25enum {
26 /* emulation entry points */
27 NFHD_READ_WRITE = 10,
28 NFHD_GET_CAPACITY = 14,
29
30 /* skip ACSI devices */
31 NFHD_DEV_OFFSET = 8,
32};
33
34static inline s32 nfhd_read_write(u32 major, u32 minor, u32 rwflag, u32 recno,
35 u32 count, u32 buf)
36{
37 return nf_call(nfhd_id + NFHD_READ_WRITE, major, minor, rwflag, recno,
38 count, buf);
39}
40
41static inline s32 nfhd_get_capacity(u32 major, u32 minor, u32 *blocks,
42 u32 *blocksize)
43{
44 return nf_call(nfhd_id + NFHD_GET_CAPACITY, major, minor, blocks,
45 blocksize);
46}
47
48static LIST_HEAD(nfhd_list);
49
50static int major_num;
51module_param(major_num, int, 0);
52
53struct nfhd_device {
54 struct list_head list;
55 int id;
56 u32 blocks, bsize;
57 int bshift;
58 struct request_queue *queue;
59 struct gendisk *disk;
60};
61
62static int nfhd_make_request(struct request_queue *queue, struct bio *bio)
63{
64 struct nfhd_device *dev = queue->queuedata;
65 struct bio_vec *bvec;
66 int i, dir, len, shift;
67 sector_t sec = bio->bi_sector;
68
69 dir = bio_data_dir(bio);
70 shift = dev->bshift;
71 bio_for_each_segment(bvec, bio, i) {
72 len = bvec->bv_len;
73 len >>= 9;
74 nfhd_read_write(dev->id, 0, dir, sec >> shift, len >> shift,
75 bvec_to_phys(bvec));
76 sec += len;
77 }
78 bio_endio(bio, 0);
79 return 0;
80}
81
82static int nfhd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
83{
84 struct nfhd_device *dev = bdev->bd_disk->private_data;
85
86 geo->cylinders = dev->blocks >> (6 - dev->bshift);
87 geo->heads = 4;
88 geo->sectors = 16;
89
90 return 0;
91}
92
93static const struct block_device_operations nfhd_ops = {
94 .owner = THIS_MODULE,
95 .getgeo = nfhd_getgeo,
96};
97
98static int __init nfhd_init_one(int id, u32 blocks, u32 bsize)
99{
100 struct nfhd_device *dev;
101 int dev_id = id - NFHD_DEV_OFFSET;
102
103 pr_info("nfhd%u: found device with %u blocks (%u bytes)\n", dev_id,
104 blocks, bsize);
105
106 if (bsize < 512 || (bsize & (bsize - 1))) {
107 pr_warn("nfhd%u: invalid block size\n", dev_id);
108 return -EINVAL;
109 }
110
111 dev = kmalloc(sizeof(struct nfhd_device), GFP_KERNEL);
112 if (!dev)
113 goto out;
114
115 dev->id = id;
116 dev->blocks = blocks;
117 dev->bsize = bsize;
118 dev->bshift = ffs(bsize) - 10;
119
120 dev->queue = blk_alloc_queue(GFP_KERNEL);
121 if (dev->queue == NULL)
122 goto free_dev;
123
124 dev->queue->queuedata = dev;
125 blk_queue_make_request(dev->queue, nfhd_make_request);
126 blk_queue_logical_block_size(dev->queue, bsize);
127
128 dev->disk = alloc_disk(16);
129 if (!dev->disk)
130 goto free_queue;
131
132 dev->disk->major = major_num;
133 dev->disk->first_minor = dev_id * 16;
134 dev->disk->fops = &nfhd_ops;
135 dev->disk->private_data = dev;
136 sprintf(dev->disk->disk_name, "nfhd%u", dev_id);
137 set_capacity(dev->disk, (sector_t)blocks * (bsize / 512));
138 dev->disk->queue = dev->queue;
139
140 add_disk(dev->disk);
141
142 list_add_tail(&dev->list, &nfhd_list);
143
144 return 0;
145
146free_queue:
147 blk_cleanup_queue(dev->queue);
148free_dev:
149 kfree(dev);
150out:
151 return -ENOMEM;
152}
153
154static int __init nfhd_init(void)
155{
156 u32 blocks, bsize;
157 int i;
158
159 nfhd_id = nf_get_id("XHDI");
160 if (!nfhd_id)
161 return -ENODEV;
162
163 major_num = register_blkdev(major_num, "nfhd");
164 if (major_num <= 0) {
165 pr_warn("nfhd: unable to get major number\n");
166 return major_num;
167 }
168
169 for (i = NFHD_DEV_OFFSET; i < 24; i++) {
170 if (nfhd_get_capacity(i, 0, &blocks, &bsize))
171 continue;
172 nfhd_init_one(i, blocks, bsize);
173 }
174
175 return 0;
176}
177
178static void __exit nfhd_exit(void)
179{
180 struct nfhd_device *dev, *next;
181
182 list_for_each_entry_safe(dev, next, &nfhd_list, list) {
183 list_del(&dev->list);
184 del_gendisk(dev->disk);
185 put_disk(dev->disk);
186 blk_cleanup_queue(dev->queue);
187 kfree(dev);
188 }
189 unregister_blkdev(major_num, "nfhd");
190}
191
192module_init(nfhd_init);
193module_exit(nfhd_exit);
194
195MODULE_LICENSE("GPL");