1 /*
2 * silly.c -- Simple Tool for Unloading and Printing ISA Data
3 *
4 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
5 * Copyright (C) 2001 O'Reilly & Associates
6 *
7 * The source code in this file can be freely used, adapted,
8 * and redistributed in source or binary form, so long as an
9 * acknowledgment appears in derived source files. The citation
10 * should list that the code comes from the book "Linux Device
11 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
12 * by O'Reilly & Associates. No warranty is attached;
13 * we cannot take responsibility for errors or fitness for use.
14 *
15 * $Id: silly.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $
16 */
17
18 /* =========================> BIG FAT WARNING:
19 * This will only work on architectures with an ISA memory range.
20 * It won't work on other computers.
21 */
22
23 #include <linux/module.h>
24 #include <linux/init.h>
25 #include <linux/moduleparam.h>
26 #include <linux/version.h>
27
28 #include <linux/sched.h>
29 #include <linux/kernel.h> /* printk() */
30 #include <linux/fs.h> /* everything... */
31 #include <linux/errno.h> /* error codes */
32 #include <linux/slab.h>
33 #include <linux/mm.h>
34 #include <linux/ioport.h>
35 #include <linux/poll.h>
36
37 #include <asm/io.h>
38 #include <asm/uaccess.h>
39
40 int silly_major = 0;
41 module_param(silly_major, int, 0);
42 MODULE_AUTHOR("Alessandro Rubini");
43 MODULE_LICENSE("Dual BSD/GPL");
44
45 /*
46 * The devices access the 640k-1M memory.
47 * minor 0 uses ioread8/iowrite8
48 * minor 1 uses ioread16/iowrite16
49 * minor 2 uses ioread32/iowrite32
50 * minor 3 uses memcpy_fromio()/memcpy_toio()
51 */
52
53 /*
54 * Here's our address range, and a place to store the ioremap'd base.
55 */
56 #define ISA_BASE 0xA0000
57 #define ISA_MAX 0x100000 /* for general memory access */
58
59 #define VIDEO_MAX 0xC0000 /* for vga access */
60 #define VGA_BASE 0xb8000
61 static void __iomem *io_base;
62
63
64
65 int silly_open(struct inode *inode, struct file *filp)
66 {
67 return 0;
68 }
69
70 int silly_release(struct inode *inode, struct file *filp)
71 {
72 return 0;
73 }
74
75 enum silly_modes {M_8=0, M_16, M_32, M_memcpy};
76
77 ssize_t silly_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
78 {
79 int retval;
80 int mode = iminor(filp->f_dentry->d_inode);
81 void __iomem *add;
82 unsigned long isa_addr = ISA_BASE + *f_pos;
83 unsigned char *kbuf, *ptr;
84
85 if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */
86 count = ISA_MAX - isa_addr;
87
88 /*
89 * too big an f_pos (caused by a malicious lseek())
90 * would result in a negative count
91 */
92 if (count < 0)
93 return 0;
94
95 kbuf = kmalloc(count, GFP_KERNEL);
96 if (!kbuf)
97 return -ENOMEM;
98 ptr = kbuf;
99 retval = count;
100 /*
101 * Convert our address into our remapped area.
102 */
103 add = (void __iomem *)(io_base + (isa_addr - ISA_BASE));
104 /*
105 * kbuf is aligned, but the reads might not. In order not to
106 * drive me mad with unaligned leading and trailing bytes,
107 * I downgrade the `mode' if unaligned xfers are requested.
108 */
109
110 if (mode == M_32 && ((isa_addr | count) & 3))
111 mode = M_16;
112 if (mode == M_16 && ((isa_addr | count) & 1))
113 mode = M_8;
114
115 switch(mode) {
116 case M_32:
117 while (count >= 4) {
118 *(u32 *)ptr = ioread32(add);
119 add += 4;
120 count -= 4;
121 ptr += 4;
122 }
123 break;
124
125 case M_16:
126 while (count >= 2) {
127 *(u16 *)ptr = ioread16(add);
128 add+=2;
129 count-=2;
130 ptr+=2;
131 }
132 break;
133
134 case M_8:
135 while (count) {
136 *ptr = ioread8(add);
137 add++;
138 count--;
139 ptr++;
140 }
141 break;
142
143 case M_memcpy:
144 memcpy_fromio(ptr, add, count);
145 break;
146
147 default:
148 return -EINVAL;
149 }
150 if ((retval > 0) && copy_to_user(buf, kbuf, retval))
151 retval = -EFAULT;
152 kfree(kbuf);
153 *f_pos += retval;
154 return retval;
155 }
156
157
158 ssize_t silly_write(struct file *filp, const char __user *buf, size_t count,
159 loff_t *f_pos)
160 {
161 int retval;
162 int mode = iminor(filp->f_dentry->d_inode);
163 unsigned long isa_addr = ISA_BASE + *f_pos;
164 unsigned char *kbuf, *ptr;
165 void __iomem *add;
166
167 /*
168 * Writing is dangerous.
169 * Allow root-only, independently of device permissions
170 */
171 if (!capable(CAP_SYS_RAWIO))
172 return -EPERM;
173
174 if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */
175 count = ISA_MAX - isa_addr;
176
177 /*
178 * too big an f_pos (caused by a malicious lseek())
179 * results in a negative count
180 */
181 if (count < 0)
182 return 0;
183
184 kbuf = kmalloc(count, GFP_KERNEL);
185 if (!kbuf)
186 return -ENOMEM;
187 ptr = kbuf;
188 retval=count;
189
190 /*
191 * kbuf is aligned, but the writes might not. In order not to
192 * drive me mad with unaligned leading and trailing bytes,
193 * I downgrade the `mode' if unaligned xfers are requested.
194 */
195
196 if (mode == M_32 && ((isa_addr | count) & 3))
197 mode = M_16;
198 if (mode == M_16 && ((isa_addr | count) & 1))
199 mode = M_8;
200
201 if (copy_from_user(kbuf, buf, count)) {
202 kfree(kbuf);
203 return -EFAULT;
204 }
205 ptr = kbuf;
206
207 /*
208 * Switch over to our remapped address space.
209 */
210 add = (void __iomem *)(io_base + (isa_addr - ISA_BASE));
211
212 switch(mode) {
213 case M_32:
214 while (count >= 4) {
215 iowrite8(*(u32 *)ptr, add);
216 add += 4;
217 count -= 4;
218 ptr += 4;
219 }
220 break;
221
222 case M_16:
223 while (count >= 2) {
224 iowrite8(*(u16 *)ptr, add);
225 add += 2;
226 count -= 2;
227 ptr += 2;
228 }
229 break;
230
231 case M_8:
232 while (count) {
233 iowrite8(*ptr, add);
234 add++;
235 count--;
236 ptr++;
237 }
238 break;
239
240 case M_memcpy:
241 memcpy_toio(add, ptr, count);
242 break;
243
244 default:
245 return -EINVAL;
246 }
247 *f_pos += retval;
248 kfree(kbuf);
249 return retval;
250 }
251
252
253 unsigned int silly_poll(struct file *filp, poll_table *wait)
254 {
255 return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
256 }
257
258
259 struct file_operations silly_fops = {
260 .read = silly_read,
261 .write = silly_write,
262 .poll = silly_poll,
263 .open = silly_open,
264 .release = silly_release,
265 .owner = THIS_MODULE
266 };
267
268 int silly_init(void)
269 {
270 int result = register_chrdev(silly_major, "silly", &silly_fops);
271 if (result < 0) {
272 printk(KERN_INFO "silly: can't get major number\n");
273 return result;
274 }
275 if (silly_major == 0)
276 silly_major = result; /* dynamic */
277 /*
278 * Set up our I/O range.
279 */
280
281 /* this line appears in silly_init */
282 io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE);
283 return 0;
284 }
285
286 void silly_cleanup(void)
287 {
288 iounmap(io_base);
289 unregister_chrdev(silly_major, "silly");
290 }
291
292
293 module_init(silly_init);
294 module_exit(silly_cleanup);
295
|
This page was automatically generated by the
LXR engine.
|