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