diff options
-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 |