Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.