4470
|
1 /*
|
|
2 Direct Hardware Access kernel helper
|
|
3
|
|
4 (C) 2002 Alex Beregszaszi <alex@naxine.org>
|
|
5
|
|
6 Accessing hardware from userspace as USER (no root needed!)
|
|
7
|
|
8 WARNING!
|
|
9 Don't use this on a production system! Use only at home, on a
|
|
10 "single-user" Unix system.
|
|
11
|
|
12 Tech:
|
|
13 Communication between userspace and kernelspace is going trought
|
|
14 character device using ioctl.
|
|
15
|
|
16 Usage:
|
|
17 mknod -m 666 /dev/dhahelper c 180 0
|
|
18
|
|
19 Also you can change the major number, setting the "dhahelper_major"
|
|
20 module parameter, the default is 180, specified in dhahelper.h.
|
|
21
|
|
22 Note: do not use other than minor==0, the module forbids it.
|
|
23
|
|
24 TODO:
|
|
25 * do memory mappin without fops:mmap
|
|
26 * implement unmap memory
|
|
27 * select (request?) a "valid" major number
|
|
28 * make security
|
|
29 * is pci handling needed? (libdha does this with lowlevel port funcs)
|
|
30 * test on older kernels (2.0.x (?) and 2.2.x)
|
|
31 */
|
|
32
|
|
33 #ifndef MODULE
|
|
34 #define MODULE
|
|
35 #endif
|
|
36
|
|
37 #ifndef __KERNEL__
|
|
38 #define __KERNEL__
|
|
39 #endif
|
|
40
|
|
41 #include <linux/config.h>
|
|
42 #include <linux/version.h>
|
|
43 #include <linux/module.h>
|
|
44 #include <linux/types.h>
|
|
45 #include <linux/kernel.h>
|
|
46 #include <linux/sched.h>
|
|
47 #include <linux/mm.h>
|
|
48 #include <linux/string.h>
|
|
49 #include <linux/errno.h>
|
|
50
|
|
51 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
|
|
52 #include <linux/malloc.h>
|
|
53 #else
|
|
54 #include <linux/slab.h>
|
|
55 #endif
|
|
56
|
|
57 #include <linux/pci.h>
|
|
58 #include <linux/ioport.h>
|
|
59 #include <linux/init.h>
|
|
60
|
|
61 #include <asm/uaccess.h>
|
|
62 #include <asm/system.h>
|
|
63 #include <asm/io.h>
|
|
64
|
|
65 #include <linux/mman.h>
|
|
66
|
|
67 #include <linux/fs.h>
|
|
68 #include <linux/unistd.h>
|
|
69
|
|
70 #include "dhahelper.h"
|
|
71
|
|
72 MODULE_AUTHOR("Alex Beregszaszi <alex@naxine.org>");
|
|
73 #ifdef MODULE_LICENSE
|
|
74 MODULE_LICENSE("GPL"); /* modulelicensesh*t */
|
|
75 #endif
|
|
76
|
|
77 static int dhahelper_major = DEFAULT_MAJOR;
|
|
78 MODULE_PARM(dhahelper_major, "i");
|
|
79
|
|
80 /* 0 = silent */
|
|
81 /* 1 = report errors (default) */
|
|
82 /* 2 = debug */
|
|
83 static int dhahelper_verbosity = 1;
|
|
84 MODULE_PARM(dhahelper_verbosity, "i");
|
|
85
|
|
86 static dhahelper_memory_t last_mem_request;
|
|
87
|
|
88
|
|
89 static int dhahelper_open(struct inode *inode, struct file *file)
|
|
90 {
|
|
91 if (dhahelper_verbosity > 1)
|
|
92 printk(KERN_DEBUG "dhahelper: device opened\n");
|
|
93
|
|
94 if (MINOR(inode->i_rdev) != 0)
|
|
95 return(-ENXIO);
|
|
96
|
|
97 MOD_INC_USE_COUNT;
|
|
98
|
|
99 return(0);
|
|
100 }
|
|
101
|
|
102 static int dhahelper_release(struct inode *inode, struct file *file)
|
|
103 {
|
|
104 if (dhahelper_verbosity > 1)
|
|
105 printk(KERN_DEBUG "dhahelper: device released\n");
|
|
106
|
|
107 if (MINOR(inode->i_rdev) != 0)
|
|
108 return(-ENXIO);
|
|
109
|
|
110 MOD_DEC_USE_COUNT;
|
|
111
|
|
112 return(0);
|
|
113 }
|
|
114
|
|
115 static int dhahelper_ioctl(struct inode *inode, struct file *file,
|
|
116 unsigned int cmd, unsigned long arg)
|
|
117 {
|
|
118 if (dhahelper_verbosity > 1)
|
|
119 printk(KERN_DEBUG "dhahelper: ioctl(cmd=%x, arg=%lx)\n",
|
|
120 cmd, arg);
|
|
121
|
|
122 if (MINOR(inode->i_rdev) != 0)
|
|
123 return(-ENXIO);
|
|
124
|
|
125 switch(cmd)
|
|
126 {
|
|
127 case DHAHELPER_GET_VERSION:
|
|
128 {
|
|
129 int version = API_VERSION;
|
|
130
|
|
131 if (copy_to_user((int *)arg, &version, sizeof(int)))
|
|
132 {
|
|
133 if (dhahelper_verbosity > 0)
|
|
134 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
135 return(-EFAULT);
|
|
136 }
|
|
137
|
|
138 break;
|
|
139 }
|
|
140 case DHAHELPER_PORT:
|
|
141 {
|
|
142 dhahelper_port_t port;
|
|
143
|
|
144 if (copy_from_user(&port, (dhahelper_port_t *)arg, sizeof(dhahelper_port_t)))
|
|
145 {
|
|
146 if (dhahelper_verbosity > 0)
|
|
147 printk(KERN_ERR "dhahelper: failed copy from userspace\n");
|
|
148 return(-EFAULT);
|
|
149 }
|
|
150
|
|
151 switch(port.operation)
|
|
152 {
|
|
153 case PORT_OP_READ:
|
|
154 {
|
|
155 switch(port.size)
|
|
156 {
|
|
157 case 1:
|
|
158 port.value = inb(port.addr);
|
|
159 break;
|
|
160 case 2:
|
|
161 port.value = inw(port.addr);
|
|
162 break;
|
|
163 case 4:
|
|
164 port.value = inl(port.addr);
|
|
165 break;
|
|
166 default:
|
|
167 if (dhahelper_verbosity > 0)
|
|
168 printk(KERN_ERR "dhahelper: invalid port read size (%d)\n",
|
|
169 port.size);
|
|
170 return(-EINVAL);
|
|
171 }
|
|
172 break;
|
|
173 }
|
|
174 case PORT_OP_WRITE:
|
|
175 {
|
|
176 switch(port.size)
|
|
177 {
|
|
178 case 1:
|
|
179 outb(port.value, port.addr);
|
|
180 break;
|
|
181 case 2:
|
|
182 outw(port.value, port.addr);
|
|
183 break;
|
|
184 case 4:
|
|
185 outl(port.value, port.addr);
|
|
186 break;
|
|
187 default:
|
|
188 if (dhahelper_verbosity > 0)
|
|
189 printk(KERN_ERR "dhahelper: invalid port write size (%d)\n",
|
|
190 port.size);
|
|
191 return(-EINVAL);
|
|
192 }
|
|
193 break;
|
|
194 }
|
|
195 default:
|
|
196 if (dhahelper_verbosity > 0)
|
|
197 printk(KERN_ERR "dhahelper: invalid port operation (%d)\n",
|
|
198 port.operation);
|
|
199 return(-EINVAL);
|
|
200 }
|
|
201
|
|
202 /* copy back only if read was performed */
|
|
203 if (port.operation == PORT_OP_READ)
|
|
204 if (copy_to_user((dhahelper_port_t *)arg, &port, sizeof(dhahelper_port_t)))
|
|
205 {
|
|
206 if (dhahelper_verbosity > 0)
|
|
207 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
208 return(-EFAULT);
|
|
209 }
|
|
210
|
|
211 break;
|
|
212 }
|
|
213 case DHAHELPER_MEMORY:
|
|
214 {
|
|
215 dhahelper_memory_t mem;
|
|
216
|
|
217 if (copy_from_user(&mem, (dhahelper_memory_t *)arg, sizeof(dhahelper_memory_t)))
|
|
218 {
|
|
219 if (dhahelper_verbosity > 0)
|
|
220 printk(KERN_ERR "dhahelper: failed copy from userspace\n");
|
|
221 return(-EFAULT);
|
|
222 }
|
|
223
|
|
224 switch(mem.operation)
|
|
225 {
|
|
226 case MEMORY_OP_MAP:
|
|
227 {
|
|
228 memcpy(&last_mem_request, &mem, sizeof(dhahelper_memory_t));
|
|
229
|
|
230 break;
|
|
231 }
|
|
232 case MEMORY_OP_UNMAP:
|
|
233 break;
|
|
234 default:
|
|
235 if (dhahelper_verbosity > 0)
|
|
236 printk(KERN_ERR "dhahelper: invalid memory operation (%d)\n",
|
|
237 mem.operation);
|
|
238 return(-EINVAL);
|
|
239 }
|
|
240
|
|
241 if (copy_to_user((dhahelper_memory_t *)arg, &mem, sizeof(dhahelper_memory_t)))
|
|
242 {
|
|
243 if (dhahelper_verbosity > 0)
|
|
244 printk(KERN_ERR "dhahelper: failed copy to userspace\n");
|
|
245 return(-EFAULT);
|
|
246 }
|
|
247
|
|
248 break;
|
|
249 }
|
|
250 default:
|
|
251 if (dhahelper_verbosity > 0)
|
|
252 printk(KERN_ERR "dhahelper: invalid ioctl (%x)\n", cmd);
|
|
253 return(-EINVAL);
|
|
254 }
|
|
255
|
|
256 return(0);
|
|
257 }
|
|
258
|
|
259 static int dhahelper_mmap(struct file *file, struct vm_area_struct *vma)
|
|
260 {
|
|
261 if (last_mem_request.operation != MEMORY_OP_MAP)
|
|
262 {
|
|
263 if (dhahelper_verbosity > 0)
|
|
264 printk(KERN_ERR "dhahelper: mapping not requested before mmap\n");
|
|
265 return(-EFAULT);
|
|
266 }
|
|
267
|
|
268 if (dhahelper_verbosity > 1)
|
|
269 printk(KERN_INFO "dhahelper: mapping %x (size: %x)\n",
|
|
270 last_mem_request.start+last_mem_request.offset, last_mem_request.size);
|
|
271
|
|
272 if (remap_page_range(0, last_mem_request.start + last_mem_request.offset,
|
|
273 last_mem_request.size, vma->vm_page_prot))
|
|
274 {
|
|
275 if (dhahelper_verbosity > 0)
|
|
276 printk(KERN_ERR "dhahelper: error mapping memory\n");
|
|
277 return(-EFAULT);
|
|
278 }
|
|
279
|
|
280 return(0);
|
|
281 }
|
|
282
|
|
283 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
|
|
284 static struct file_operations dhahelper_fops =
|
|
285 {
|
|
286 /*llseek*/ NULL,
|
|
287 /*read*/ NULL,
|
|
288 /*write*/ NULL,
|
|
289 /*readdir*/ NULL,
|
|
290 /*poll*/ NULL,
|
|
291 /*ioctl*/ dhahelper_ioctl,
|
|
292 /*mmap*/ dhahelper_mmap,
|
|
293 /*open*/ dhahelper_open,
|
|
294 /*flush*/ NULL,
|
|
295 /*release*/ dhahelper_release,
|
|
296 /* zero out the last 5 entries too ? */
|
|
297 };
|
|
298 #else
|
|
299 static struct file_operations dhahelper_fops =
|
|
300 {
|
|
301 owner: THIS_MODULE,
|
|
302 ioctl: dhahelper_ioctl,
|
|
303 mmap: dhahelper_mmap,
|
|
304 open: dhahelper_open,
|
|
305 release: dhahelper_release
|
|
306 };
|
|
307 #endif
|
|
308
|
|
309 static int __init init_dhahelper(void)
|
|
310 {
|
|
311 printk(KERN_INFO "Direct Hardware Access kernel helper (C) Alex Beregszaszi\n");
|
|
312
|
|
313 if(register_chrdev(dhahelper_major, "dhahelper", &dhahelper_fops))
|
|
314 {
|
|
315 if (dhahelper_verbosity > 0)
|
|
316 printk(KERN_ERR "dhahelper: unable to register character device (major: %d)\n",
|
|
317 dhahelper_major);
|
|
318 return(-EIO);
|
|
319 }
|
|
320
|
|
321 return(0);
|
|
322 }
|
|
323
|
|
324 static void __exit exit_dhahelper(void)
|
|
325 {
|
|
326 unregister_chrdev(dhahelper_major, "dhahelper");
|
|
327 }
|
|
328
|
|
329 EXPORT_NO_SYMBOLS;
|
|
330
|
|
331 module_init(init_dhahelper);
|
|
332 module_exit(exit_dhahelper);
|