482257acec634a357a8e65eef0ee8bb1dcc2208d
[tridge/junkcode.git] / trd / trd.c
1 /*
2   A trivial ramdisk
3   tridge@valinux.com, 2001
4 */
5
6 #define MAJOR_NR   240
7
8 #define MAX_DEVS 32
9
10 #define DEVICE_NAME "trd"
11 #define DEVICE_REQUEST do_trd_request
12 #define DEVICE_NR(device) (MINOR(device))
13
14 #include <linux/major.h>
15 #include <linux/slab.h>
16 #include <linux/vmalloc.h>
17 #include <linux/blk.h>
18 #include <linux/init.h>
19 #include <linux/module.h>
20 #include <asm/setup.h>
21 #include <asm/uaccess.h>
22
23 #define TRD_BLOCK_SIZE 1024
24 #define TRD_SIZE (trd_size<<10)
25
26 static int trd_blocksizes[MAX_DEVS];
27 static int trd_blk_sizes[MAX_DEVS];
28 static void **trd_base[MAX_DEVS];
29 static int trd_pages;
30 static unsigned trd_size = 4096;
31
32 MODULE_PARM (trd_size, "1i");
33
34 static void do_trd_request(request_queue_t * q)
35 {
36         u_long start, ofs, len, len1;
37         void *addr;
38         int page, minor;
39
40         while (1) {
41                 INIT_REQUEST;
42
43                 minor = MINOR(CURRENT->rq_dev);
44                 start = CURRENT->sector << 9;
45                 len  = CURRENT->current_nr_sectors << 9;
46                 
47                 if ((start + len) > TRD_SIZE) {
48                         printk(KERN_ERR DEVICE_NAME ": bad access: block=%ld, count=%ld\n",
49                                CURRENT->sector,
50                                CURRENT->current_nr_sectors);
51                         end_request(0);
52                         continue;
53                 }
54                 
55                 if ((CURRENT->cmd != READ) && (CURRENT->cmd != WRITE)) {
56                         printk(KERN_ERR DEVICE_NAME ": bad command: %d\n", CURRENT->cmd);
57                         end_request(0);
58                         continue;
59                 }
60
61                 while (len) {
62                         page = start / PAGE_SIZE;
63                         ofs = start % PAGE_SIZE;
64                         addr = (void *) (((char *)(trd_base[minor][page])) + ofs);
65
66                         len1 = len;
67                         if (ofs + len1 > PAGE_SIZE) {
68                                 len1 = PAGE_SIZE - ofs;
69                         }
70
71                         if (CURRENT->cmd == READ) {
72                                 memcpy(CURRENT->buffer, (char *)addr, len1);
73                         } else {
74                                 memcpy((char *)addr, CURRENT->buffer, len1);
75                         }
76
77                         len -= len1;
78                         start += len1;
79                 }
80             
81                 end_request(1);
82         }
83 }
84
85 static int trd_allocate(int minor)
86 {
87         int i;
88
89         /* it might be already allocated */
90         if (trd_base[minor]) return 0;
91
92         trd_base[minor] = (void **)vmalloc(sizeof(void *)*trd_pages);
93         if (!trd_base[minor]) goto nomem;
94         memset(trd_base[minor], 0, sizeof(void *)*trd_pages);
95
96         for (i=0;i<trd_pages;i++) {
97                 trd_base[minor][i] = (void *)__get_free_page(GFP_USER);
98                 if (!trd_base[minor][i]) goto nomem;
99                 /* we have to zero it to ensure private data doesn't leak */
100                 memset(trd_base[minor][i], 0, PAGE_SIZE);
101         }
102
103         printk(KERN_INFO DEVICE_NAME ": Allocated %dk to minor %d\n",
104                TRD_SIZE>>10, minor);
105
106         return 0;
107
108 nomem:
109         if (trd_base[minor]) {
110                 for (i=0;i<trd_pages;i++) {
111                         if (trd_base[minor][i]) 
112                                 free_page((unsigned long)trd_base[minor][i]);
113                 }
114                 vfree(trd_base[minor]);
115                 trd_base[minor] = NULL;
116         }       
117
118         printk(KERN_ERR DEVICE_NAME ": Unable to allocate trd of size %d\n",
119                TRD_SIZE);
120         return -ENOMEM;
121 }
122
123
124 static int trd_open(struct inode *inode, struct file *filp)
125 {
126         int ret;
127         int minor = MINOR(inode->i_rdev);
128
129         if (minor >= MAX_DEVS) return -ENODEV;
130
131         ret = trd_allocate(minor);
132         if (ret != 0) return ret;
133
134         MOD_INC_USE_COUNT;
135
136         return 0;
137 }
138
139 static int trd_release(struct inode *inode, struct file *filp)
140 {
141         MOD_DEC_USE_COUNT;
142         return 0;     
143 }
144
145 static int trd_ioctl(struct inode *inode, struct file *file,
146                      unsigned int cmd, unsigned long arg)
147 {
148         if (!capable(CAP_SYS_ADMIN))
149                 return -EPERM;
150         if (!inode)
151                 return -EINVAL;
152
153         switch (cmd) {
154         case BLKGETSIZE:
155                 return put_user(TRD_SIZE >> 9, (long *) arg);
156         default:
157                 printk(KERN_ERR DEVICE_NAME ": unknown ioctl 0x%x\n", cmd);
158                 break;
159         }
160         return -EINVAL;
161 }
162
163 static struct block_device_operations trd_fops =
164 {
165         open:           trd_open,
166         release:        trd_release,
167         ioctl:          trd_ioctl,
168 };
169
170
171 static int trd_init(void)
172 {
173         int i;
174
175         trd_pages = (TRD_SIZE + (PAGE_SIZE-1)) / PAGE_SIZE;
176
177         if (register_blkdev(MAJOR_NR, DEVICE_NAME, &trd_fops)) {
178                 printk(KERN_ERR DEVICE_NAME ": Unable to register_blkdev(%d)\n", MAJOR_NR);
179                 return -EBUSY;
180         }
181
182         for (i=0;i<MAX_DEVS;i++) {
183                 trd_blocksizes[i] = TRD_BLOCK_SIZE;
184                 trd_blk_sizes[i] = trd_size;
185         }
186
187         blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
188         blksize_size[MAJOR_NR] = trd_blocksizes;
189         blk_size[MAJOR_NR] = trd_blk_sizes;
190
191         printk(KERN_DEBUG DEVICE_NAME ": trd initialised size=%dk\n", 
192                trd_size);
193
194         return 0;
195 }
196
197 static void __exit trd_cleanup(void)
198 {
199         int minor, i;
200
201         unregister_blkdev(MAJOR_NR, DEVICE_NAME);
202
203         for (minor=0;minor<MAX_DEVS;minor++) {
204                 if (!trd_base[minor]) continue;
205                 for (i=0;i<trd_pages;i++) {
206                         free_page((unsigned long)trd_base[minor][i]);
207                 }
208                 vfree(trd_base[minor]);
209                 trd_base[minor] = NULL;
210         }
211         printk(KERN_DEBUG DEVICE_NAME ": trd released\n");
212 }
213
214 module_init(trd_init);
215 module_exit(trd_cleanup);
216 MODULE_DESCRIPTION("trivial ramdisk");