Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / block / mambo_bd.c
1 /*
2  *  Bogus Block Driver for PowerPC Full System Simulator
3  *
4  *  (C) Copyright IBM Corporation 2003-2005
5  *
6  *  Bogus Disk Driver
7  *
8  *  Author: Eric Van Hensbegren <ericvh@gmail.com>
9  *
10  *    inspired by drivers/block/nbd.c
11  *    written by Pavel Machek and Steven Whitehouse
12  *
13  *  Some code is from the IBM Full System Simulator Group in ARL
14  *  Author: PAtrick Bohrer <IBM Austin Research Lab>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2, or (at your option)
19  * any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * 
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to:
28  * Free Software Foundation
29  * 51 Franklin Street, Fifth Floor
30  * Boston, MA  02111-1301  USA
31  *
32  */
33
34 #include <linux/major.h>
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/init.h>
38 #include <linux/sched.h>
39 #include <linux/fs.h>
40 #include <linux/stat.h>
41 #include <linux/errno.h>
42 #include <linux/file.h>
43 #include <linux/ioctl.h>
44 #include <linux/blkdev.h>
45 #include <net/sock.h>
46
47 #include <asm/systemsim.h>
48
49 #include <asm/uaccess.h>
50 #include <asm/types.h>
51
52 #define MAJOR_NR 112
53 #define MAX_MBD 128
54
55 #define MBD_SET_BLKSIZE _IO( 0xab, 1 )
56 #define MBD_SET_SIZE    _IO( 0xab, 2 )
57 #define MBD_SET_SIZE_BLOCKS     _IO( 0xab, 7 )
58 #define MBD_DISCONNECT  _IO( 0xab, 8 )
59
60 struct mbd_device {
61         int initialized;
62         int refcnt;
63         int flags;
64         struct gendisk *disk;
65 };
66
67 static struct mbd_device mbd_dev[MAX_MBD];
68
69 #define BD_INFO_SYNC   0
70 #define BD_INFO_STATUS 1
71 #define BD_INFO_BLKSZ  2
72 #define BD_INFO_DEVSZ  3
73 #define BD_INFO_CHANGE 4
74
75 #define BOGUS_DISK_READ  116
76 #define BOGUS_DISK_WRITE 117
77 #define BOGUS_DISK_INFO  118
78
79 static inline int
80 MamboBogusDiskRead(int devno, void *buf, ulong sect, ulong nrsect)
81 {
82         return callthru3(BOGUS_DISK_READ, (unsigned long)buf,
83                          (unsigned long)sect,
84                          (unsigned long)((nrsect << 16) | devno));
85 }
86
87 static inline int
88 MamboBogusDiskWrite(int devno, void *buf, ulong sect, ulong nrsect)
89 {
90         return callthru3(BOGUS_DISK_WRITE, (unsigned long)buf,
91                          (unsigned long)sect,
92                          (unsigned long)((nrsect << 16) | devno));
93 }
94
95 static inline int MamboBogusDiskInfo(int op, int devno)
96 {
97         return callthru2(BOGUS_DISK_INFO, (unsigned long)op,
98                          (unsigned long)devno);
99 }
100
101 static int mbd_init_disk(int devno)
102 {
103         struct gendisk *disk = mbd_dev[devno].disk;
104         unsigned int sz;
105
106         if (!__onsim())
107                 return -1;
108
109         /* check disk configured */
110         if (!MamboBogusDiskInfo(BD_INFO_STATUS, devno)) {
111                 printk(KERN_ERR
112                        "Attempting to open bogus disk before initializaiton\n");
113                 return 0;
114         }
115
116         mbd_dev[devno].initialized++;
117
118         sz = MamboBogusDiskInfo(BD_INFO_DEVSZ, devno);
119
120         if (sz == -1)
121                 return 0;
122
123         printk("Initializing disk %d with devsz %u\n", devno, sz);
124
125         set_capacity(disk, sz << 1);
126
127         return 1;
128 }
129
130 static void do_mbd_request(request_queue_t * q)
131 {
132         int result = 0;
133         struct request *req;
134
135         while ((req = elv_next_request(q)) != NULL) {
136                 int minor = req->rq_disk->first_minor;
137
138                 switch (rq_data_dir(req)) {
139                 case READ:
140                         result = MamboBogusDiskRead(minor,
141                                                     req->buffer, req->sector,
142                                                     req->current_nr_sectors);
143                         break;
144                 case WRITE:
145                         result = MamboBogusDiskWrite(minor,
146                                                      req->buffer, req->sector,
147                                                      req->current_nr_sectors);
148                 };
149
150                 if (result)
151                         end_request(req, 0);    /* failure */
152                 else
153                         end_request(req, 1);    /* success */
154         }
155 }
156
157 static int mbd_release(struct inode *inode, struct file *file)
158 {
159         struct mbd_device *lo;
160         int dev;
161
162         if (!inode)
163                 return -ENODEV;
164         dev = inode->i_bdev->bd_disk->first_minor;
165         if (dev >= MAX_MBD)
166                 return -ENODEV;
167         if (MamboBogusDiskInfo(BD_INFO_SYNC, dev) < 0) {
168                 printk(KERN_ALERT "mbd_release: unable to sync\n");
169         }
170         lo = &mbd_dev[dev];
171         if (lo->refcnt <= 0)
172                 printk(KERN_ALERT "mbd_release: refcount(%d) <= 0\n",
173                        lo->refcnt);
174         lo->refcnt--;
175         return 0;
176 }
177
178 static int mbd_revalidate(struct gendisk *disk)
179 {
180         int devno = disk->first_minor;
181
182         mbd_init_disk(devno);
183
184         return 0;
185 }
186
187 static int mbd_open(struct inode *inode, struct file *file)
188 {
189         int dev;
190
191         if (!inode)
192                 return -EINVAL;
193         dev = inode->i_bdev->bd_disk->first_minor;
194         if (dev >= MAX_MBD)
195                 return -ENODEV;
196
197         check_disk_change(inode->i_bdev);
198
199         if (!mbd_dev[dev].initialized)
200                 if (!mbd_init_disk(dev))
201                         return -ENODEV;
202
203         mbd_dev[dev].refcnt++;
204         return 0;
205 }
206
207 static struct block_device_operations mbd_fops = {
208       owner:THIS_MODULE,
209       open:mbd_open,
210       release:mbd_release,
211         /* media_changed:      mbd_check_change, */
212       revalidate_disk:mbd_revalidate,
213 };
214
215 static spinlock_t mbd_lock = SPIN_LOCK_UNLOCKED;
216
217 static int __init mbd_init(void)
218 {
219         int err = -ENOMEM;
220         int i;
221
222         for (i = 0; i < MAX_MBD; i++) {
223                 struct gendisk *disk = alloc_disk(1);
224                 if (!disk)
225                         goto out;
226                 mbd_dev[i].disk = disk;
227                 /*
228                  * The new linux 2.5 block layer implementation requires
229                  * every gendisk to have its very own request_queue struct.
230                  * These structs are big so we dynamically allocate them.
231                  */
232                 disk->queue = blk_init_queue(do_mbd_request, &mbd_lock);
233                 if (!disk->queue) {
234                         put_disk(disk);
235                         goto out;
236                 }
237         }
238
239         if (register_blkdev(MAJOR_NR, "mbd")) {
240                 err = -EIO;
241                 goto out;
242         }
243 #ifdef MODULE
244         printk("mambo bogus disk: registered device at major %d\n", MAJOR_NR);
245 #else
246         printk("mambo bogus disk: compiled in with kernel\n");
247 #endif
248
249         for (i = 0; i < MAX_MBD; i++) { /* load defaults */
250                 struct gendisk *disk = mbd_dev[i].disk;
251                 mbd_dev[i].initialized = 0;
252                 mbd_dev[i].refcnt = 0;
253                 mbd_dev[i].flags = 0;
254                 disk->major = MAJOR_NR;
255                 disk->first_minor = i;
256                 disk->fops = &mbd_fops;
257                 disk->private_data = &mbd_dev[i];
258                 sprintf(disk->disk_name, "mambobd%d", i);
259                 set_capacity(disk, 0x7ffffc00ULL << 1); /* 2 TB */
260                 add_disk(disk);
261         }
262
263         return 0;
264       out:
265         while (i--) {
266                 if (mbd_dev[i].disk->queue)
267                         blk_cleanup_queue(mbd_dev[i].disk->queue);
268                 put_disk(mbd_dev[i].disk);
269         }
270         return -EIO;
271 }
272
273 static void __exit mbd_cleanup(void)
274 {
275         if (unregister_blkdev(MAJOR_NR, "mbd") != 0)
276                 printk("mbd: cleanup_module failed\n");
277         else
278                 printk("mbd: module cleaned up.\n");
279 }
280
281 module_init(mbd_init);
282 module_exit(mbd_cleanup);
283
284 MODULE_DESCRIPTION("Mambo Block Device");
285 MODULE_LICENSE("GPL");