diff options
author | Hans J. Koch <hjk@linutronix.de> | 2008-05-23 07:50:14 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-22 00:54:55 -0400 |
commit | 328a14e70e7f46997cb50d4258dd93d5377f98c6 (patch) | |
tree | 7cc64b122fc45af454d4dde9f561b329d9f2b0c4 | |
parent | 934da4766e5f72797118f7c014efaef567a812fc (diff) |
UIO: Add write function to allow irq masking
Sometimes it is necessary to enable/disable the interrupt of a UIO device
from the userspace part of the driver. With this patch, the UIO kernel driver
can implement an "irqcontrol()" function that does this. Userspace can write
an s32 value to /dev/uioX (usually 0 or 1 to turn the irq off or on). The
UIO core will then call the driver's irqcontrol function.
Signed-off-by: Hans J. Koch <hjk@linutronix.de>
Acked-by: Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
Acked-by: Magnus Damm <damm@igel.co.jp>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | Documentation/DocBook/uio-howto.tmpl | 40 | ||||
-rw-r--r-- | drivers/uio/uio.c | 26 | ||||
-rw-r--r-- | include/linux/uio_driver.h | 2 |
3 files changed, 67 insertions, 1 deletions
diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index fdd7f4f887b7..c4d187313963 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl | |||
@@ -30,6 +30,12 @@ | |||
30 | 30 | ||
31 | <revhistory> | 31 | <revhistory> |
32 | <revision> | 32 | <revision> |
33 | <revnumber>0.5</revnumber> | ||
34 | <date>2008-05-22</date> | ||
35 | <authorinitials>hjk</authorinitials> | ||
36 | <revremark>Added description of write() function.</revremark> | ||
37 | </revision> | ||
38 | <revision> | ||
33 | <revnumber>0.4</revnumber> | 39 | <revnumber>0.4</revnumber> |
34 | <date>2007-11-26</date> | 40 | <date>2007-11-26</date> |
35 | <authorinitials>hjk</authorinitials> | 41 | <authorinitials>hjk</authorinitials> |
@@ -64,7 +70,7 @@ | |||
64 | <?dbhtml filename="copyright.html"?> | 70 | <?dbhtml filename="copyright.html"?> |
65 | <title>Copyright and License</title> | 71 | <title>Copyright and License</title> |
66 | <para> | 72 | <para> |
67 | Copyright (c) 2006 by Hans-Jürgen Koch.</para> | 73 | Copyright (c) 2006-2008 by Hans-Jürgen Koch.</para> |
68 | <para> | 74 | <para> |
69 | This documentation is Free Software licensed under the terms of the | 75 | This documentation is Free Software licensed under the terms of the |
70 | GPL version 2. | 76 | GPL version 2. |
@@ -189,6 +195,30 @@ interested in translating it, please email me | |||
189 | represents the total interrupt count. You can use this number | 195 | represents the total interrupt count. You can use this number |
190 | to figure out if you missed some interrupts. | 196 | to figure out if you missed some interrupts. |
191 | </para> | 197 | </para> |
198 | <para> | ||
199 | For some hardware that has more than one interrupt source internally, | ||
200 | but not separate IRQ mask and status registers, there might be | ||
201 | situations where userspace cannot determine what the interrupt source | ||
202 | was if the kernel handler disables them by writing to the chip's IRQ | ||
203 | register. In such a case, the kernel has to disable the IRQ completely | ||
204 | to leave the chip's register untouched. Now the userspace part can | ||
205 | determine the cause of the interrupt, but it cannot re-enable | ||
206 | interrupts. Another cornercase is chips where re-enabling interrupts | ||
207 | is a read-modify-write operation to a combined IRQ status/acknowledge | ||
208 | register. This would be racy if a new interrupt occurred | ||
209 | simultaneously. | ||
210 | </para> | ||
211 | <para> | ||
212 | To address these problems, UIO also implements a write() function. It | ||
213 | is normally not used and can be ignored for hardware that has only a | ||
214 | single interrupt source or has separate IRQ mask and status registers. | ||
215 | If you need it, however, a write to <filename>/dev/uioX</filename> | ||
216 | will call the <function>irqcontrol()</function> function implemented | ||
217 | by the driver. You have to write a 32-bit value that is usually either | ||
218 | 0 or 1 to disable or enable interrupts. If a driver does not implement | ||
219 | <function>irqcontrol()</function>, <function>write()</function> will | ||
220 | return with <varname>-ENOSYS</varname>. | ||
221 | </para> | ||
192 | 222 | ||
193 | <para> | 223 | <para> |
194 | To handle interrupts properly, your custom kernel module can | 224 | To handle interrupts properly, your custom kernel module can |
@@ -362,6 +392,14 @@ device is actually used. | |||
362 | <function>open()</function>, you will probably also want a custom | 392 | <function>open()</function>, you will probably also want a custom |
363 | <function>release()</function> function. | 393 | <function>release()</function> function. |
364 | </para></listitem> | 394 | </para></listitem> |
395 | |||
396 | <listitem><para> | ||
397 | <varname>int (*irqcontrol)(struct uio_info *info, s32 irq_on) | ||
398 | </varname>: Optional. If you need to be able to enable or disable | ||
399 | interrupts from userspace by writing to <filename>/dev/uioX</filename>, | ||
400 | you can implement this function. The parameter <varname>irq_on</varname> | ||
401 | will be 0 to disable interrupts and 1 to enable them. | ||
402 | </para></listitem> | ||
365 | </itemizedlist> | 403 | </itemizedlist> |
366 | 404 | ||
367 | <para> | 405 | <para> |
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 5a7ca2e6094d..3a6934bf7131 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c | |||
@@ -427,6 +427,31 @@ static ssize_t uio_read(struct file *filep, char __user *buf, | |||
427 | return retval; | 427 | return retval; |
428 | } | 428 | } |
429 | 429 | ||
430 | static ssize_t uio_write(struct file *filep, const char __user *buf, | ||
431 | size_t count, loff_t *ppos) | ||
432 | { | ||
433 | struct uio_listener *listener = filep->private_data; | ||
434 | struct uio_device *idev = listener->dev; | ||
435 | ssize_t retval; | ||
436 | s32 irq_on; | ||
437 | |||
438 | if (idev->info->irq == UIO_IRQ_NONE) | ||
439 | return -EIO; | ||
440 | |||
441 | if (count != sizeof(s32)) | ||
442 | return -EINVAL; | ||
443 | |||
444 | if (!idev->info->irqcontrol) | ||
445 | return -ENOSYS; | ||
446 | |||
447 | if (copy_from_user(&irq_on, buf, count)) | ||
448 | return -EFAULT; | ||
449 | |||
450 | retval = idev->info->irqcontrol(idev->info, irq_on); | ||
451 | |||
452 | return retval ? retval : sizeof(s32); | ||
453 | } | ||
454 | |||
430 | static int uio_find_mem_index(struct vm_area_struct *vma) | 455 | static int uio_find_mem_index(struct vm_area_struct *vma) |
431 | { | 456 | { |
432 | int mi; | 457 | int mi; |
@@ -546,6 +571,7 @@ static const struct file_operations uio_fops = { | |||
546 | .open = uio_open, | 571 | .open = uio_open, |
547 | .release = uio_release, | 572 | .release = uio_release, |
548 | .read = uio_read, | 573 | .read = uio_read, |
574 | .write = uio_write, | ||
549 | .mmap = uio_mmap, | 575 | .mmap = uio_mmap, |
550 | .poll = uio_poll, | 576 | .poll = uio_poll, |
551 | .fasync = uio_fasync, | 577 | .fasync = uio_fasync, |
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 973386d439da..cf65e964102b 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h | |||
@@ -53,6 +53,7 @@ struct uio_device; | |||
53 | * @mmap: mmap operation for this uio device | 53 | * @mmap: mmap operation for this uio device |
54 | * @open: open operation for this uio device | 54 | * @open: open operation for this uio device |
55 | * @release: release operation for this uio device | 55 | * @release: release operation for this uio device |
56 | * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX | ||
56 | */ | 57 | */ |
57 | struct uio_info { | 58 | struct uio_info { |
58 | struct uio_device *uio_dev; | 59 | struct uio_device *uio_dev; |
@@ -66,6 +67,7 @@ struct uio_info { | |||
66 | int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); | 67 | int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); |
67 | int (*open)(struct uio_info *info, struct inode *inode); | 68 | int (*open)(struct uio_info *info, struct inode *inode); |
68 | int (*release)(struct uio_info *info, struct inode *inode); | 69 | int (*release)(struct uio_info *info, struct inode *inode); |
70 | int (*irqcontrol)(struct uio_info *info, s32 irq_on); | ||
69 | }; | 71 | }; |
70 | 72 | ||
71 | extern int __must_check | 73 | extern int __must_check |