File: /usr/src/linux/drivers/mtd/mtdblock_ro.c
1 /*
2 * $Id: mtdblock_ro.c,v 1.5 2001/06/10 01:41:53 dwmw2 Exp $
3 *
4 * Read-only version of the mtdblock device, without the
5 * read/erase/modify/writeback stuff
6 */
7
8 #ifdef MTDBLOCK_DEBUG
9 #define DEBUGLVL debug
10 #endif
11
12
13 #include <linux/module.h>
14 #include <linux/types.h>
15
16 #include <linux/mtd/mtd.h>
17
18 #define MAJOR_NR MTD_BLOCK_MAJOR
19 #define DEVICE_NAME "mtdblock"
20 #define DEVICE_REQUEST mtdblock_request
21 #define DEVICE_NR(device) (device)
22 #define DEVICE_ON(device)
23 #define DEVICE_OFF(device)
24 #define DEVICE_NO_RANDOM
25 #include <linux/blk.h>
26
27 #if LINUX_VERSION_CODE < 0x20300
28 #define RQFUNC_ARG void
29 #define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
30 #else
31 #define RQFUNC_ARG request_queue_t *q
32 #endif
33
34 #ifdef MTDBLOCK_DEBUG
35 static int debug = MTDBLOCK_DEBUG;
36 MODULE_PARM(debug, "i");
37 #endif
38
39
40 static int mtd_sizes[MAX_MTD_DEVICES];
41
42
43 static int mtdblock_open(struct inode *inode, struct file *file)
44 {
45 struct mtd_info *mtd = NULL;
46
47 int dev;
48
49 DEBUG(1,"mtdblock_open\n");
50
51 if (inode == 0)
52 return -EINVAL;
53
54 dev = MINOR(inode->i_rdev);
55
56 MOD_INC_USE_COUNT;
57
58 mtd = get_mtd_device(NULL, dev);
59
60 if (!mtd) {
61 MOD_DEC_USE_COUNT;
62 return -ENODEV;
63 }
64
65 mtd_sizes[dev] = mtd->size>>9;
66
67 DEBUG(1, "ok\n");
68
69 return 0;
70 }
71
72 static release_t mtdblock_release(struct inode *inode, struct file *file)
73 {
74 int dev;
75 struct mtd_info *mtd;
76
77 DEBUG(1, "mtdblock_release\n");
78
79 if (inode == NULL)
80 release_return(-ENODEV);
81
82 invalidate_device(inode->i_rdev, 1);
83
84 dev = MINOR(inode->i_rdev);
85 mtd = __get_mtd_device(NULL, dev);
86
87 if (!mtd) {
88 printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
89 MOD_DEC_USE_COUNT;
90 release_return(-ENODEV);
91 }
92
93 if (mtd->sync)
94 mtd->sync(mtd);
95
96 put_mtd_device(mtd);
97
98 DEBUG(1, "ok\n");
99
100 MOD_DEC_USE_COUNT;
101 release_return(0);
102 }
103
104
105 static void mtdblock_request(RQFUNC_ARG)
106 {
107 struct request *current_request;
108 unsigned int res = 0;
109 struct mtd_info *mtd;
110
111 while (1)
112 {
113 /* Grab the Request and unlink it from the request list, INIT_REQUEST
114 will execute a return if we are done. */
115 INIT_REQUEST;
116 current_request = CURRENT;
117
118 if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
119 {
120 printk("mtd: Unsupported device!\n");
121 end_request(0);
122 continue;
123 }
124
125 // Grab our MTD structure
126
127 mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
128 if (!mtd) {
129 printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
130 end_request(0);
131 }
132
133 if (current_request->sector << 9 > mtd->size ||
134 (current_request->sector + current_request->nr_sectors) << 9 > mtd->size)
135 {
136 printk("mtd: Attempt to read past end of device!\n");
137 printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors);
138 end_request(0);
139 continue;
140 }
141
142 /* Remove the request we are handling from the request list so nobody messes
143 with it */
144 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
145 /* Now drop the lock that the ll_rw_blk functions grabbed for us
146 and process the request. This is necessary due to the extreme time
147 we spend processing it. */
148 spin_unlock_irq(&io_request_lock);
149 #endif
150
151 // Handle the request
152 switch (current_request->cmd)
153 {
154 size_t retlen;
155
156 case READ:
157 if (MTD_READ(mtd,current_request->sector<<9,
158 current_request->nr_sectors << 9,
159 &retlen, current_request->buffer) == 0)
160 res = 1;
161 else
162 res = 0;
163 break;
164
165 case WRITE:
166
167 /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
168 current_request->nr_sectors);
169 */
170
171 // Read only device
172 if ((mtd->flags & MTD_CAP_RAM) == 0)
173 {
174 res = 0;
175 break;
176 }
177
178 // Do the write
179 if (MTD_WRITE(mtd,current_request->sector<<9,
180 current_request->nr_sectors << 9,
181 &retlen, current_request->buffer) == 0)
182 res = 1;
183 else
184 res = 0;
185 break;
186
187 // Shouldn't happen
188 default:
189 printk("mtd: unknown request\n");
190 break;
191 }
192
193 // Grab the lock and re-thread the item onto the linked list
194 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
195 spin_lock_irq(&io_request_lock);
196 #endif
197 end_request(res);
198 }
199 }
200
201
202
203 static int mtdblock_ioctl(struct inode * inode, struct file * file,
204 unsigned int cmd, unsigned long arg)
205 {
206 struct mtd_info *mtd;
207
208 mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
209
210 if (!mtd) return -EINVAL;
211
212 switch (cmd) {
213 case BLKGETSIZE: /* Return device size */
214 return put_user((mtd->size >> 9), (long *) arg);
215 case BLKGETSIZE64:
216 return put_user((u64)mtd->size, (u64 *)arg);
217
218 case BLKFLSBUF:
219 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
220 if(!capable(CAP_SYS_ADMIN)) return -EACCES;
221 #endif
222 fsync_dev(inode->i_rdev);
223 invalidate_buffers(inode->i_rdev);
224 if (mtd->sync)
225 mtd->sync(mtd);
226 return 0;
227
228 default:
229 return -ENOTTY;
230 }
231 }
232
233 #if LINUX_VERSION_CODE < 0x20326
234 static struct file_operations mtd_fops =
235 {
236 open: mtdblock_open,
237 ioctl: mtdblock_ioctl,
238 release: mtdblock_release,
239 read: block_read,
240 write: block_write
241 };
242 #else
243 static struct block_device_operations mtd_fops =
244 {
245 open: mtdblock_open,
246 release: mtdblock_release,
247 ioctl: mtdblock_ioctl
248 };
249 #endif
250
251 #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
252 #define init_mtdblock init_module
253 #define cleanup_mtdblock cleanup_module
254 #endif
255
256 int __init init_mtdblock(void)
257 {
258 int i;
259
260 if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
261 printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
262 MTD_BLOCK_MAJOR);
263 return EAGAIN;
264 }
265
266 /* We fill it in at open() time. */
267 for (i=0; i< MAX_MTD_DEVICES; i++) {
268 mtd_sizes[i] = 0;
269 }
270
271 /* Allow the block size to default to BLOCK_SIZE. */
272 blksize_size[MAJOR_NR] = NULL;
273 blk_size[MAJOR_NR] = mtd_sizes;
274
275 #if LINUX_VERSION_CODE < 0x20320
276 blk_dev[MAJOR_NR].request_fn = mtdblock_request;
277 #else
278 blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
279 #endif
280 return 0;
281 }
282
283 static void __exit cleanup_mtdblock(void)
284 {
285 unregister_blkdev(MAJOR_NR,DEVICE_NAME);
286 }
287
288 module_init(init_mtdblock);
289 module_exit(cleanup_mtdblock);
290