The migration is done with the dbxml2rst project with small additional handmade. The uio-howto is chunked along its chapters into smal parts. [1] https://return42.github.io/dbxml2rst/ Signed-off-by: Markus Heiser <markus.heiser@xxxxxxxxxxx> --- Documentation/DocBook/Makefile | 2 +- Documentation/DocBook/uio-howto.tmpl | 1050 ---------------------- Documentation/conf.py | 2 + Documentation/index.rst | 1 + Documentation/uio-howto/about_uio.rst | 150 ++++ Documentation/uio-howto/aboutthisdoc.rst | 103 +++ Documentation/uio-howto/conf.py | 10 + Documentation/uio-howto/custom_kernel_module.rst | 245 +++++ Documentation/uio-howto/index.rst | 45 + Documentation/uio-howto/uio_pci_generic.rst | 168 ++++ Documentation/uio-howto/userspace_driver.rst | 86 ++ 11 files changed, 811 insertions(+), 1051 deletions(-) delete mode 100644 Documentation/DocBook/uio-howto.tmpl create mode 100644 Documentation/uio-howto/about_uio.rst create mode 100644 Documentation/uio-howto/aboutthisdoc.rst create mode 100644 Documentation/uio-howto/conf.py create mode 100644 Documentation/uio-howto/custom_kernel_module.rst create mode 100644 Documentation/uio-howto/index.rst create mode 100644 Documentation/uio-howto/uio_pci_generic.rst create mode 100644 Documentation/uio-howto/userspace_driver.rst diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index fdf8232..26e97e2 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -11,7 +11,7 @@ DOCBOOKS := z8530book.xml \ writing_usb_driver.xml networking.xml \ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ - genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ + genericirq.xml s390-drivers.xml scsi.xml \ debugobjects.xml sh.xml regulator.xml \ alsa-driver-api.xml writing-an-alsa-driver.xml \ tracepoint.xml w1.xml \ diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl deleted file mode 100644 index cd0e452..0000000 --- a/Documentation/DocBook/uio-howto.tmpl +++ /dev/null @@ -1,1050 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" -"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> - -<book id="index"> -<bookinfo> -<title>The Userspace I/O HOWTO</title> - -<author> - <firstname>Hans-Jürgen</firstname> - <surname>Koch</surname> - <authorblurb><para>Linux developer, Linutronix</para></authorblurb> - <affiliation> - <orgname> - <ulink url="http://www.linutronix.de">Linutronix</ulink> - </orgname> - - <address> - <email>hjk@xxxxxxxxxxxx</email> - </address> - </affiliation> -</author> - -<copyright> - <year>2006-2008</year> - <holder>Hans-Jürgen Koch.</holder> -</copyright> -<copyright> - <year>2009</year> - <holder>Red Hat Inc, Michael S. Tsirkin (mst@xxxxxxxxxx)</holder> -</copyright> - -<legalnotice> -<para> -This documentation is Free Software licensed under the terms of the -GPL version 2. -</para> -</legalnotice> - -<pubdate>2006-12-11</pubdate> - -<abstract> - <para>This HOWTO describes concept and usage of Linux kernel's - Userspace I/O system.</para> -</abstract> - -<revhistory> - <revision> - <revnumber>0.9</revnumber> - <date>2009-07-16</date> - <authorinitials>mst</authorinitials> - <revremark>Added generic pci driver - </revremark> - </revision> - <revision> - <revnumber>0.8</revnumber> - <date>2008-12-24</date> - <authorinitials>hjk</authorinitials> - <revremark>Added name attributes in mem and portio sysfs directories. - </revremark> - </revision> - <revision> - <revnumber>0.7</revnumber> - <date>2008-12-23</date> - <authorinitials>hjk</authorinitials> - <revremark>Added generic platform drivers and offset attribute.</revremark> - </revision> - <revision> - <revnumber>0.6</revnumber> - <date>2008-12-05</date> - <authorinitials>hjk</authorinitials> - <revremark>Added description of portio sysfs attributes.</revremark> - </revision> - <revision> - <revnumber>0.5</revnumber> - <date>2008-05-22</date> - <authorinitials>hjk</authorinitials> - <revremark>Added description of write() function.</revremark> - </revision> - <revision> - <revnumber>0.4</revnumber> - <date>2007-11-26</date> - <authorinitials>hjk</authorinitials> - <revremark>Removed section about uio_dummy.</revremark> - </revision> - <revision> - <revnumber>0.3</revnumber> - <date>2007-04-29</date> - <authorinitials>hjk</authorinitials> - <revremark>Added section about userspace drivers.</revremark> - </revision> - <revision> - <revnumber>0.2</revnumber> - <date>2007-02-13</date> - <authorinitials>hjk</authorinitials> - <revremark>Update after multiple mappings were added.</revremark> - </revision> - <revision> - <revnumber>0.1</revnumber> - <date>2006-12-11</date> - <authorinitials>hjk</authorinitials> - <revremark>First draft.</revremark> - </revision> -</revhistory> -</bookinfo> - -<chapter id="aboutthisdoc"> -<?dbhtml filename="aboutthis.html"?> -<title>About this document</title> - -<sect1 id="translations"> -<?dbhtml filename="translations.html"?> -<title>Translations</title> - -<para>If you know of any translations for this document, or you are -interested in translating it, please email me -<email>hjk@xxxxxxxxxxxx</email>. -</para> -</sect1> - -<sect1 id="preface"> -<title>Preface</title> - <para> - For many types of devices, creating a Linux kernel driver is - overkill. All that is really needed is some way to handle an - interrupt and provide access to the memory space of the - device. The logic of controlling the device does not - necessarily have to be within the kernel, as the device does - not need to take advantage of any of other resources that the - kernel provides. One such common class of devices that are - like this are for industrial I/O cards. - </para> - <para> - To address this situation, the userspace I/O system (UIO) was - designed. For typical industrial I/O cards, only a very small - kernel module is needed. The main part of the driver will run in - user space. This simplifies development and reduces the risk of - serious bugs within a kernel module. - </para> - <para> - Please note that UIO is not an universal driver interface. Devices - that are already handled well by other kernel subsystems (like - networking or serial or USB) are no candidates for an UIO driver. - Hardware that is ideally suited for an UIO driver fulfills all of - the following: - </para> -<itemizedlist> -<listitem> - <para>The device has memory that can be mapped. The device can be - controlled completely by writing to this memory.</para> -</listitem> -<listitem> - <para>The device usually generates interrupts.</para> -</listitem> -<listitem> - <para>The device does not fit into one of the standard kernel - subsystems.</para> -</listitem> -</itemizedlist> -</sect1> - -<sect1 id="thanks"> -<title>Acknowledgments</title> - <para>I'd like to thank Thomas Gleixner and Benedikt Spranger of - Linutronix, who have not only written most of the UIO code, but also - helped greatly writing this HOWTO by giving me all kinds of background - information.</para> -</sect1> - -<sect1 id="feedback"> -<title>Feedback</title> - <para>Find something wrong with this document? (Or perhaps something - right?) I would love to hear from you. Please email me at - <email>hjk@xxxxxxxxxxxx</email>.</para> -</sect1> -</chapter> - -<chapter id="about"> -<?dbhtml filename="about.html"?> -<title>About UIO</title> - -<para>If you use UIO for your card's driver, here's what you get:</para> - -<itemizedlist> -<listitem> - <para>only one small kernel module to write and maintain.</para> -</listitem> -<listitem> - <para>develop the main part of your driver in user space, - with all the tools and libraries you're used to.</para> -</listitem> -<listitem> - <para>bugs in your driver won't crash the kernel.</para> -</listitem> -<listitem> - <para>updates of your driver can take place without recompiling - the kernel.</para> -</listitem> -</itemizedlist> - -<sect1 id="how_uio_works"> -<title>How UIO works</title> - <para> - Each UIO device is accessed through a device file and several - sysfs attribute files. The device file will be called - <filename>/dev/uio0</filename> for the first device, and - <filename>/dev/uio1</filename>, <filename>/dev/uio2</filename> - and so on for subsequent devices. - </para> - - <para><filename>/dev/uioX</filename> is used to access the - address space of the card. Just use - <function>mmap()</function> to access registers or RAM - locations of your card. - </para> - - <para> - Interrupts are handled by reading from - <filename>/dev/uioX</filename>. A blocking - <function>read()</function> from - <filename>/dev/uioX</filename> will return as soon as an - interrupt occurs. You can also use - <function>select()</function> on - <filename>/dev/uioX</filename> to wait for an interrupt. The - integer value read from <filename>/dev/uioX</filename> - represents the total interrupt count. You can use this number - to figure out if you missed some interrupts. - </para> - <para> - For some hardware that has more than one interrupt source internally, - but not separate IRQ mask and status registers, there might be - situations where userspace cannot determine what the interrupt source - was if the kernel handler disables them by writing to the chip's IRQ - register. In such a case, the kernel has to disable the IRQ completely - to leave the chip's register untouched. Now the userspace part can - determine the cause of the interrupt, but it cannot re-enable - interrupts. Another cornercase is chips where re-enabling interrupts - is a read-modify-write operation to a combined IRQ status/acknowledge - register. This would be racy if a new interrupt occurred - simultaneously. - </para> - <para> - To address these problems, UIO also implements a write() function. It - is normally not used and can be ignored for hardware that has only a - single interrupt source or has separate IRQ mask and status registers. - If you need it, however, a write to <filename>/dev/uioX</filename> - will call the <function>irqcontrol()</function> function implemented - by the driver. You have to write a 32-bit value that is usually either - 0 or 1 to disable or enable interrupts. If a driver does not implement - <function>irqcontrol()</function>, <function>write()</function> will - return with <varname>-ENOSYS</varname>. - </para> - - <para> - To handle interrupts properly, your custom kernel module can - provide its own interrupt handler. It will automatically be - called by the built-in handler. - </para> - - <para> - For cards that don't generate interrupts but need to be - polled, there is the possibility to set up a timer that - triggers the interrupt handler at configurable time intervals. - This interrupt simulation is done by calling - <function>uio_event_notify()</function> - from the timer's event handler. - </para> - - <para> - Each driver provides attributes that are used to read or write - variables. These attributes are accessible through sysfs - files. A custom kernel driver module can add its own - attributes to the device owned by the uio driver, but not added - to the UIO device itself at this time. This might change in the - future if it would be found to be useful. - </para> - - <para> - The following standard attributes are provided by the UIO - framework: - </para> -<itemizedlist> -<listitem> - <para> - <filename>name</filename>: The name of your device. It is - recommended to use the name of your kernel module for this. - </para> -</listitem> -<listitem> - <para> - <filename>version</filename>: A version string defined by your - driver. This allows the user space part of your driver to deal - with different versions of the kernel module. - </para> -</listitem> -<listitem> - <para> - <filename>event</filename>: The total number of interrupts - handled by the driver since the last time the device node was - read. - </para> -</listitem> -</itemizedlist> -<para> - These attributes appear under the - <filename>/sys/class/uio/uioX</filename> directory. Please - note that this directory might be a symlink, and not a real - directory. Any userspace code that accesses it must be able - to handle this. -</para> -<para> - Each UIO device can make one or more memory regions available for - memory mapping. This is necessary because some industrial I/O cards - require access to more than one PCI memory region in a driver. -</para> -<para> - Each mapping has its own directory in sysfs, the first mapping - appears as <filename>/sys/class/uio/uioX/maps/map0/</filename>. - Subsequent mappings create directories <filename>map1/</filename>, - <filename>map2/</filename>, and so on. These directories will only - appear if the size of the mapping is not 0. -</para> -<para> - Each <filename>mapX/</filename> directory contains four read-only files - that show attributes of the memory: -</para> -<itemizedlist> -<listitem> - <para> - <filename>name</filename>: A string identifier for this mapping. This - is optional, the string can be empty. Drivers can set this to make it - easier for userspace to find the correct mapping. - </para> -</listitem> -<listitem> - <para> - <filename>addr</filename>: The address of memory that can be mapped. - </para> -</listitem> -<listitem> - <para> - <filename>size</filename>: The size, in bytes, of the memory - pointed to by addr. - </para> -</listitem> -<listitem> - <para> - <filename>offset</filename>: The offset, in bytes, that has to be - added to the pointer returned by <function>mmap()</function> to get - to the actual device memory. This is important if the device's memory - is not page aligned. Remember that pointers returned by - <function>mmap()</function> are always page aligned, so it is good - style to always add this offset. - </para> -</listitem> -</itemizedlist> - -<para> - From userspace, the different mappings are distinguished by adjusting - the <varname>offset</varname> parameter of the - <function>mmap()</function> call. To map the memory of mapping N, you - have to use N times the page size as your offset: -</para> -<programlisting format="linespecific"> -offset = N * getpagesize(); -</programlisting> - -<para> - Sometimes there is hardware with memory-like regions that can not be - mapped with the technique described here, but there are still ways to - access them from userspace. The most common example are x86 ioports. - On x86 systems, userspace can access these ioports using - <function>ioperm()</function>, <function>iopl()</function>, - <function>inb()</function>, <function>outb()</function>, and similar - functions. -</para> -<para> - Since these ioport regions can not be mapped, they will not appear under - <filename>/sys/class/uio/uioX/maps/</filename> like the normal memory - described above. Without information about the port regions a hardware - has to offer, it becomes difficult for the userspace part of the - driver to find out which ports belong to which UIO device. -</para> -<para> - To address this situation, the new directory - <filename>/sys/class/uio/uioX/portio/</filename> was added. It only - exists if the driver wants to pass information about one or more port - regions to userspace. If that is the case, subdirectories named - <filename>port0</filename>, <filename>port1</filename>, and so on, - will appear underneath - <filename>/sys/class/uio/uioX/portio/</filename>. -</para> -<para> - Each <filename>portX/</filename> directory contains four read-only - files that show name, start, size, and type of the port region: -</para> -<itemizedlist> -<listitem> - <para> - <filename>name</filename>: A string identifier for this port region. - The string is optional and can be empty. Drivers can set it to make it - easier for userspace to find a certain port region. - </para> -</listitem> -<listitem> - <para> - <filename>start</filename>: The first port of this region. - </para> -</listitem> -<listitem> - <para> - <filename>size</filename>: The number of ports in this region. - </para> -</listitem> -<listitem> - <para> - <filename>porttype</filename>: A string describing the type of port. - </para> -</listitem> -</itemizedlist> - - -</sect1> -</chapter> - -<chapter id="custom_kernel_module" xreflabel="Writing your own kernel module"> -<?dbhtml filename="custom_kernel_module.html"?> -<title>Writing your own kernel module</title> - <para> - Please have a look at <filename>uio_cif.c</filename> as an - example. The following paragraphs explain the different - sections of this file. - </para> - -<sect1 id="uio_info"> -<title>struct uio_info</title> - <para> - This structure tells the framework the details of your driver, - Some of the members are required, others are optional. - </para> - -<itemizedlist> -<listitem><para> -<varname>const char *name</varname>: Required. The name of your driver as -it will appear in sysfs. I recommend using the name of your module for this. -</para></listitem> - -<listitem><para> -<varname>const char *version</varname>: Required. This string appears in -<filename>/sys/class/uio/uioX/version</filename>. -</para></listitem> - -<listitem><para> -<varname>struct uio_mem mem[ MAX_UIO_MAPS ]</varname>: Required if you -have memory that can be mapped with <function>mmap()</function>. For each -mapping you need to fill one of the <varname>uio_mem</varname> structures. -See the description below for details. -</para></listitem> - -<listitem><para> -<varname>struct uio_port port[ MAX_UIO_PORTS_REGIONS ]</varname>: Required -if you want to pass information about ioports to userspace. For each port -region you need to fill one of the <varname>uio_port</varname> structures. -See the description below for details. -</para></listitem> - -<listitem><para> -<varname>long irq</varname>: Required. If your hardware generates an -interrupt, it's your modules task to determine the irq number during -initialization. If you don't have a hardware generated interrupt but -want to trigger the interrupt handler in some other way, set -<varname>irq</varname> to <varname>UIO_IRQ_CUSTOM</varname>. -If you had no interrupt at all, you could set -<varname>irq</varname> to <varname>UIO_IRQ_NONE</varname>, though this -rarely makes sense. -</para></listitem> - -<listitem><para> -<varname>unsigned long irq_flags</varname>: Required if you've set -<varname>irq</varname> to a hardware interrupt number. The flags given -here will be used in the call to <function>request_irq()</function>. -</para></listitem> - -<listitem><para> -<varname>int (*mmap)(struct uio_info *info, struct vm_area_struct -*vma)</varname>: Optional. If you need a special -<function>mmap()</function> function, you can set it here. If this -pointer is not NULL, your <function>mmap()</function> will be called -instead of the built-in one. -</para></listitem> - -<listitem><para> -<varname>int (*open)(struct uio_info *info, struct inode *inode) -</varname>: Optional. You might want to have your own -<function>open()</function>, e.g. to enable interrupts only when your -device is actually used. -</para></listitem> - -<listitem><para> -<varname>int (*release)(struct uio_info *info, struct inode *inode) -</varname>: Optional. If you define your own -<function>open()</function>, you will probably also want a custom -<function>release()</function> function. -</para></listitem> - -<listitem><para> -<varname>int (*irqcontrol)(struct uio_info *info, s32 irq_on) -</varname>: Optional. If you need to be able to enable or disable -interrupts from userspace by writing to <filename>/dev/uioX</filename>, -you can implement this function. The parameter <varname>irq_on</varname> -will be 0 to disable interrupts and 1 to enable them. -</para></listitem> -</itemizedlist> - -<para> -Usually, your device will have one or more memory regions that can be mapped -to user space. For each region, you have to set up a -<varname>struct uio_mem</varname> in the <varname>mem[]</varname> array. -Here's a description of the fields of <varname>struct uio_mem</varname>: -</para> - -<itemizedlist> -<listitem><para> -<varname>const char *name</varname>: Optional. Set this to help identify -the memory region, it will show up in the corresponding sysfs node. -</para></listitem> - -<listitem><para> -<varname>int memtype</varname>: Required if the mapping is used. Set this to -<varname>UIO_MEM_PHYS</varname> if you you have physical memory on your -card to be mapped. Use <varname>UIO_MEM_LOGICAL</varname> for logical -memory (e.g. allocated with <function>kmalloc()</function>). There's also -<varname>UIO_MEM_VIRTUAL</varname> for virtual memory. -</para></listitem> - -<listitem><para> -<varname>phys_addr_t addr</varname>: Required if the mapping is used. -Fill in the address of your memory block. This address is the one that -appears in sysfs. -</para></listitem> - -<listitem><para> -<varname>resource_size_t size</varname>: Fill in the size of the -memory block that <varname>addr</varname> points to. If <varname>size</varname> -is zero, the mapping is considered unused. Note that you -<emphasis>must</emphasis> initialize <varname>size</varname> with zero for -all unused mappings. -</para></listitem> - -<listitem><para> -<varname>void *internal_addr</varname>: If you have to access this memory -region from within your kernel module, you will want to map it internally by -using something like <function>ioremap()</function>. Addresses -returned by this function cannot be mapped to user space, so you must not -store it in <varname>addr</varname>. Use <varname>internal_addr</varname> -instead to remember such an address. -</para></listitem> -</itemizedlist> - -<para> -Please do not touch the <varname>map</varname> element of -<varname>struct uio_mem</varname>! It is used by the UIO framework -to set up sysfs files for this mapping. Simply leave it alone. -</para> - -<para> -Sometimes, your device can have one or more port regions which can not be -mapped to userspace. But if there are other possibilities for userspace to -access these ports, it makes sense to make information about the ports -available in sysfs. For each region, you have to set up a -<varname>struct uio_port</varname> in the <varname>port[]</varname> array. -Here's a description of the fields of <varname>struct uio_port</varname>: -</para> - -<itemizedlist> -<listitem><para> -<varname>char *porttype</varname>: Required. Set this to one of the predefined -constants. Use <varname>UIO_PORT_X86</varname> for the ioports found in x86 -architectures. -</para></listitem> - -<listitem><para> -<varname>unsigned long start</varname>: Required if the port region is used. -Fill in the number of the first port of this region. -</para></listitem> - -<listitem><para> -<varname>unsigned long size</varname>: Fill in the number of ports in this -region. If <varname>size</varname> is zero, the region is considered unused. -Note that you <emphasis>must</emphasis> initialize <varname>size</varname> -with zero for all unused regions. -</para></listitem> -</itemizedlist> - -<para> -Please do not touch the <varname>portio</varname> element of -<varname>struct uio_port</varname>! It is used internally by the UIO -framework to set up sysfs files for this region. Simply leave it alone. -</para> - -</sect1> - -<sect1 id="adding_irq_handler"> -<title>Adding an interrupt handler</title> - <para> - What you need to do in your interrupt handler depends on your - hardware and on how you want to handle it. You should try to - keep the amount of code in your kernel interrupt handler low. - If your hardware requires no action that you - <emphasis>have</emphasis> to perform after each interrupt, - then your handler can be empty.</para> <para>If, on the other - hand, your hardware <emphasis>needs</emphasis> some action to - be performed after each interrupt, then you - <emphasis>must</emphasis> do it in your kernel module. Note - that you cannot rely on the userspace part of your driver. Your - userspace program can terminate at any time, possibly leaving - your hardware in a state where proper interrupt handling is - still required. - </para> - - <para> - There might also be applications where you want to read data - from your hardware at each interrupt and buffer it in a piece - of kernel memory you've allocated for that purpose. With this - technique you could avoid loss of data if your userspace - program misses an interrupt. - </para> - - <para> - A note on shared interrupts: Your driver should support - interrupt sharing whenever this is possible. It is possible if - and only if your driver can detect whether your hardware has - triggered the interrupt or not. This is usually done by looking - at an interrupt status register. If your driver sees that the - IRQ bit is actually set, it will perform its actions, and the - handler returns IRQ_HANDLED. If the driver detects that it was - not your hardware that caused the interrupt, it will do nothing - and return IRQ_NONE, allowing the kernel to call the next - possible interrupt handler. - </para> - - <para> - If you decide not to support shared interrupts, your card - won't work in computers with no free interrupts. As this - frequently happens on the PC platform, you can save yourself a - lot of trouble by supporting interrupt sharing. - </para> -</sect1> - -<sect1 id="using_uio_pdrv"> -<title>Using uio_pdrv for platform devices</title> - <para> - In many cases, UIO drivers for platform devices can be handled in a - generic way. In the same place where you define your - <varname>struct platform_device</varname>, you simply also implement - your interrupt handler and fill your - <varname>struct uio_info</varname>. A pointer to this - <varname>struct uio_info</varname> is then used as - <varname>platform_data</varname> for your platform device. - </para> - <para> - You also need to set up an array of <varname>struct resource</varname> - containing addresses and sizes of your memory mappings. This - information is passed to the driver using the - <varname>.resource</varname> and <varname>.num_resources</varname> - elements of <varname>struct platform_device</varname>. - </para> - <para> - You now have to set the <varname>.name</varname> element of - <varname>struct platform_device</varname> to - <varname>"uio_pdrv"</varname> to use the generic UIO platform device - driver. This driver will fill the <varname>mem[]</varname> array - according to the resources given, and register the device. - </para> - <para> - The advantage of this approach is that you only have to edit a file - you need to edit anyway. You do not have to create an extra driver. - </para> -</sect1> - -<sect1 id="using_uio_pdrv_genirq"> -<title>Using uio_pdrv_genirq for platform devices</title> - <para> - Especially in embedded devices, you frequently find chips where the - irq pin is tied to its own dedicated interrupt line. In such cases, - where you can be really sure the interrupt is not shared, we can take - the concept of <varname>uio_pdrv</varname> one step further and use a - generic interrupt handler. That's what - <varname>uio_pdrv_genirq</varname> does. - </para> - <para> - The setup for this driver is the same as described above for - <varname>uio_pdrv</varname>, except that you do not implement an - interrupt handler. The <varname>.handler</varname> element of - <varname>struct uio_info</varname> must remain - <varname>NULL</varname>. The <varname>.irq_flags</varname> element - must not contain <varname>IRQF_SHARED</varname>. - </para> - <para> - You will set the <varname>.name</varname> element of - <varname>struct platform_device</varname> to - <varname>"uio_pdrv_genirq"</varname> to use this driver. - </para> - <para> - The generic interrupt handler of <varname>uio_pdrv_genirq</varname> - will simply disable the interrupt line using - <function>disable_irq_nosync()</function>. After doing its work, - userspace can reenable the interrupt by writing 0x00000001 to the UIO - device file. The driver already implements an - <function>irq_control()</function> to make this possible, you must not - implement your own. - </para> - <para> - Using <varname>uio_pdrv_genirq</varname> not only saves a few lines of - interrupt handler code. You also do not need to know anything about - the chip's internal registers to create the kernel part of the driver. - All you need to know is the irq number of the pin the chip is - connected to. - </para> -</sect1> - -<sect1 id="using-uio_dmem_genirq"> -<title>Using uio_dmem_genirq for platform devices</title> - <para> - In addition to statically allocated memory ranges, they may also be - a desire to use dynamically allocated regions in a user space driver. - In particular, being able to access memory made available through the - dma-mapping API, may be particularly useful. The - <varname>uio_dmem_genirq</varname> driver provides a way to accomplish - this. - </para> - <para> - This driver is used in a similar manner to the - <varname>"uio_pdrv_genirq"</varname> driver with respect to interrupt - configuration and handling. - </para> - <para> - Set the <varname>.name</varname> element of - <varname>struct platform_device</varname> to - <varname>"uio_dmem_genirq"</varname> to use this driver. - </para> - <para> - When using this driver, fill in the <varname>.platform_data</varname> - element of <varname>struct platform_device</varname>, which is of type - <varname>struct uio_dmem_genirq_pdata</varname> and which contains the - following elements: - </para> - <itemizedlist> - <listitem><para><varname>struct uio_info uioinfo</varname>: The same - structure used as the <varname>uio_pdrv_genirq</varname> platform - data</para></listitem> - <listitem><para><varname>unsigned int *dynamic_region_sizes</varname>: - Pointer to list of sizes of dynamic memory regions to be mapped into - user space. - </para></listitem> - <listitem><para><varname>unsigned int num_dynamic_regions</varname>: - Number of elements in <varname>dynamic_region_sizes</varname> array. - </para></listitem> - </itemizedlist> - <para> - The dynamic regions defined in the platform data will be appended to - the <varname> mem[] </varname> array after the platform device - resources, which implies that the total number of static and dynamic - memory regions cannot exceed <varname>MAX_UIO_MAPS</varname>. - </para> - <para> - The dynamic memory regions will be allocated when the UIO device file, - <varname>/dev/uioX</varname> is opened. - Similar to static memory resources, the memory region information for - dynamic regions is then visible via sysfs at - <varname>/sys/class/uio/uioX/maps/mapY/*</varname>. - The dynamic memory regions will be freed when the UIO device file is - closed. When no processes are holding the device file open, the address - returned to userspace is ~0. - </para> -</sect1> - -</chapter> - -<chapter id="userspace_driver" xreflabel="Writing a driver in user space"> -<?dbhtml filename="userspace_driver.html"?> -<title>Writing a driver in userspace</title> - <para> - Once you have a working kernel module for your hardware, you can - write the userspace part of your driver. You don't need any special - libraries, your driver can be written in any reasonable language, - you can use floating point numbers and so on. In short, you can - use all the tools and libraries you'd normally use for writing a - userspace application. - </para> - -<sect1 id="getting_uio_information"> -<title>Getting information about your UIO device</title> - <para> - Information about all UIO devices is available in sysfs. The - first thing you should do in your driver is check - <varname>name</varname> and <varname>version</varname> to - make sure your talking to the right device and that its kernel - driver has the version you expect. - </para> - <para> - You should also make sure that the memory mapping you need - exists and has the size you expect. - </para> - <para> - There is a tool called <varname>lsuio</varname> that lists - UIO devices and their attributes. It is available here: - </para> - <para> - <ulink url="http://www.osadl.org/projects/downloads/UIO/user/"> - http://www.osadl.org/projects/downloads/UIO/user/</ulink> - </para> - <para> - With <varname>lsuio</varname> you can quickly check if your - kernel module is loaded and which attributes it exports. - Have a look at the manpage for details. - </para> - <para> - The source code of <varname>lsuio</varname> can serve as an - example for getting information about an UIO device. - The file <filename>uio_helper.c</filename> contains a lot of - functions you could use in your userspace driver code. - </para> -</sect1> - -<sect1 id="mmap_device_memory"> -<title>mmap() device memory</title> - <para> - After you made sure you've got the right device with the - memory mappings you need, all you have to do is to call - <function>mmap()</function> to map the device's memory - to userspace. - </para> - <para> - The parameter <varname>offset</varname> of the - <function>mmap()</function> call has a special meaning - for UIO devices: It is used to select which mapping of - your device you want to map. To map the memory of - mapping N, you have to use N times the page size as - your offset: - </para> -<programlisting format="linespecific"> - offset = N * getpagesize(); -</programlisting> - <para> - N starts from zero, so if you've got only one memory - range to map, set <varname>offset = 0</varname>. - A drawback of this technique is that memory is always - mapped beginning with its start address. - </para> -</sect1> - -<sect1 id="wait_for_interrupts"> -<title>Waiting for interrupts</title> - <para> - After you successfully mapped your devices memory, you - can access it like an ordinary array. Usually, you will - perform some initialization. After that, your hardware - starts working and will generate an interrupt as soon - as it's finished, has some data available, or needs your - attention because an error occurred. - </para> - <para> - <filename>/dev/uioX</filename> is a read-only file. A - <function>read()</function> will always block until an - interrupt occurs. There is only one legal value for the - <varname>count</varname> parameter of - <function>read()</function>, and that is the size of a - signed 32 bit integer (4). Any other value for - <varname>count</varname> causes <function>read()</function> - to fail. The signed 32 bit integer read is the interrupt - count of your device. If the value is one more than the value - you read the last time, everything is OK. If the difference - is greater than one, you missed interrupts. - </para> - <para> - You can also use <function>select()</function> on - <filename>/dev/uioX</filename>. - </para> -</sect1> - -</chapter> - -<chapter id="uio_pci_generic" xreflabel="Using Generic driver for PCI cards"> -<?dbhtml filename="uio_pci_generic.html"?> -<title>Generic PCI UIO driver</title> - <para> - The generic driver is a kernel module named uio_pci_generic. - It can work with any device compliant to PCI 2.3 (circa 2002) and - any compliant PCI Express device. Using this, you only need to - write the userspace driver, removing the need to write - a hardware-specific kernel module. - </para> - -<sect1 id="uio_pci_generic_binding"> -<title>Making the driver recognize the device</title> - <para> -Since the driver does not declare any device ids, it will not get loaded -automatically and will not automatically bind to any devices, you must load it -and allocate id to the driver yourself. For example: - <programlisting> - modprobe uio_pci_generic - echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id - </programlisting> - </para> - <para> -If there already is a hardware specific kernel driver for your device, the -generic driver still won't bind to it, in this case if you want to use the -generic driver (why would you?) you'll have to manually unbind the hardware -specific driver and bind the generic driver, like this: - <programlisting> - echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind - echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind - </programlisting> - </para> - <para> -You can verify that the device has been bound to the driver -by looking for it in sysfs, for example like the following: - <programlisting> - ls -l /sys/bus/pci/devices/0000:00:19.0/driver - </programlisting> -Which if successful should print - <programlisting> - .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic - </programlisting> -Note that the generic driver will not bind to old PCI 2.2 devices. -If binding the device failed, run the following command: - <programlisting> - dmesg - </programlisting> -and look in the output for failure reasons - </para> -</sect1> - -<sect1 id="uio_pci_generic_internals"> -<title>Things to know about uio_pci_generic</title> - <para> -Interrupts are handled using the Interrupt Disable bit in the PCI command -register and Interrupt Status bit in the PCI status register. All devices -compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should -support these bits. uio_pci_generic detects this support, and won't bind to -devices which do not support the Interrupt Disable Bit in the command register. - </para> - <para> -On each interrupt, uio_pci_generic sets the Interrupt Disable bit. -This prevents the device from generating further interrupts -until the bit is cleared. The userspace driver should clear this -bit before blocking and waiting for more interrupts. - </para> -</sect1> -<sect1 id="uio_pci_generic_userspace"> -<title>Writing userspace driver using uio_pci_generic</title> - <para> -Userspace driver can use pci sysfs interface, or the -libpci libray that wraps it, to talk to the device and to -re-enable interrupts by writing to the command register. - </para> -</sect1> -<sect1 id="uio_pci_generic_example"> -<title>Example code using uio_pci_generic</title> - <para> -Here is some sample userspace driver code using uio_pci_generic: -<programlisting> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> - -int main() -{ - int uiofd; - int configfd; - int err; - int i; - unsigned icount; - unsigned char command_high; - - uiofd = open("/dev/uio0", O_RDONLY); - if (uiofd < 0) { - perror("uio open:"); - return errno; - } - configfd = open("/sys/class/uio/uio0/device/config", O_RDWR); - if (configfd < 0) { - perror("config open:"); - return errno; - } - - /* Read and cache command value */ - err = pread(configfd, &command_high, 1, 5); - if (err != 1) { - perror("command config read:"); - return errno; - } - command_high &= ~0x4; - - for(i = 0;; ++i) { - /* Print out a message, for debugging. */ - if (i == 0) - fprintf(stderr, "Started uio test driver.\n"); - else - fprintf(stderr, "Interrupts: %d\n", icount); - - /****************************************/ - /* Here we got an interrupt from the - device. Do something to it. */ - /****************************************/ - - /* Re-enable interrupts. */ - err = pwrite(configfd, &command_high, 1, 5); - if (err != 1) { - perror("config write:"); - break; - } - - /* Wait for next interrupt. */ - err = read(uiofd, &icount, 4); - if (err != 4) { - perror("uio read:"); - break; - } - - } - return errno; -} - -</programlisting> - </para> -</sect1> - -</chapter> - -<appendix id="app1"> -<title>Further information</title> -<itemizedlist> - <listitem><para> - <ulink url="http://www.osadl.org"> - OSADL homepage.</ulink> - </para></listitem> - <listitem><para> - <ulink url="http://www.linutronix.de"> - Linutronix homepage.</ulink> - </para></listitem> -</itemizedlist> -</appendix> - -</book> diff --git a/Documentation/conf.py b/Documentation/conf.py index 0cc8765..867715f 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -342,6 +342,8 @@ latex_documents = [ 'The kernel development community', 'manual'), ('gpu/index', 'gpu.tex', 'Linux GPU Driver Developer\'s Guide', 'The kernel development community', 'manual'), + ('uio-howto/index', 'uio-howto.tex', 'The Userspace I/O HOWTO', + 'The kernel development community', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/Documentation/index.rst b/Documentation/index.rst index c53d089..246db7c 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -18,6 +18,7 @@ Contents: media/index gpu/index 80211/index + uio-howto/index Indices and tables ================== diff --git a/Documentation/uio-howto/about_uio.rst b/Documentation/uio-howto/about_uio.rst new file mode 100644 index 0000000..cd9ca32 --- /dev/null +++ b/Documentation/uio-howto/about_uio.rst @@ -0,0 +1,150 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _about_uio: + +********* +About UIO +********* + +If you use UIO for your card's driver, here's what you get: + +- only one small kernel module to write and maintain. + +- develop the main part of your driver in user space, with all the + tools and libraries you're used to. + +- bugs in your driver won't crash the kernel. + +- updates of your driver can take place without recompiling the kernel. + + +.. _how_uio_works: + +How UIO works +============= + +Each UIO device is accessed through a device file and several sysfs attribute +files. The device file will be called ``/dev/uio0`` for the first device, and +``/dev/uio1``, ``/dev/uio2`` and so on for subsequent devices. + +``/dev/uioX`` is used to access the address space of the card. Just use +:c:func:`mmap()` to access registers or RAM locations of your card. + +Interrupts are handled by reading from ``/dev/uioX``. A blocking +:c:func:`read()` from ``/dev/uioX`` will return as soon as an interrupt +occurs. You can also use :c:func:`select()` on ``/dev/uioX`` to wait for an +interrupt. The integer value read from ``/dev/uioX`` represents the total +interrupt count. You can use this number to figure out if you missed some +interrupts. + +For some hardware that has more than one interrupt source internally, but not +separate IRQ mask and status registers, there might be situations where +userspace cannot determine what the interrupt source was if the kernel handler +disables them by writing to the chip's IRQ register. In such a case, the kernel +has to disable the IRQ completely to leave the chip's register untouched. Now +the userspace part can determine the cause of the interrupt, but it cannot +re-enable interrupts. Another cornercase is chips where re-enabling interrupts +is a read-modify-write operation to a combined IRQ status/acknowledge +register. This would be racy if a new interrupt occurred simultaneously. + +To address these problems, UIO also implements a write() function. It is +normally not used and can be ignored for hardware that has only a single +interrupt source or has separate IRQ mask and status registers. If you need it, +however, a write to ``/dev/uioX`` will call the :c:func:`irqcontrol()` function +implemented by the driver. You have to write a 32-bit value that is usually +either 0 or 1 to disable or enable interrupts. If a driver does not implement +:c:func:`irqcontrol()`, :c:func:`write()` will return with ``-ENOSYS``. + +To handle interrupts properly, your custom kernel module can provide its own +interrupt handler. It will automatically be called by the built-in handler. + +For cards that don't generate interrupts but need to be polled, there is the +possibility to set up a timer that triggers the interrupt handler at +configurable time intervals. This interrupt simulation is done by calling +:c:func:`uio_event_notify()` from the timer's event handler. + +Each driver provides attributes that are used to read or write variables. These +attributes are accessible through sysfs files. A custom kernel driver module can +add its own attributes to the device owned by the uio driver, but not added to +the UIO device itself at this time. This might change in the future if it would +be found to be useful. + +The following standard attributes are provided by the UIO framework: + +- ``name``: The name of your device. It is recommended to use the name of your + kernel module for this. + +- ``version``: A version string defined by your driver. This allows the user + space part of your driver to deal with different versions of the kernel + module. + +- ``event``: The total number of interrupts handled by the driver since the last + time the device node was read. + +These attributes appear under the ``/sys/class/uio/uioX`` directory. Please +note that this directory might be a symlink, and not a real directory. Any +userspace code that accesses it must be able to handle this. + +Each UIO device can make one or more memory regions available for memory +mapping. This is necessary because some industrial I/O cards require access to +more than one PCI memory region in a driver. + +Each mapping has its own directory in sysfs, the first mapping appears as +``/sys/class/uio/uioX/maps/map0/``. Subsequent mappings create directories +``map1/``, ``map2/``, and so on. These directories will only appear if the size +of the mapping is not 0. + +Each ``mapX/`` directory contains four read-only files that show attributes of +the memory: + +- ``name``: A string identifier for this mapping. This is optional, the string + can be empty. Drivers can set this to make it easier for userspace to find the + correct mapping. + +- ``addr``: The address of memory that can be mapped. + +- ``size``: The size, in bytes, of the memory pointed to by addr. + +- ``offset``: The offset, in bytes, that has to be added to the pointer returned + by :c:func:`mmap()` to get to the actual device memory. This is important if + the device's memory is not page aligned. Remember that pointers returned by + :c:func:`mmap()` are always page aligned, so it is good style to always add + this offset. + +From userspace, the different mappings are distinguished by adjusting the +``offset`` parameter of the :c:func:`mmap()` call. To map the memory of mapping +N, you have to use N times the page size as your offset: + +.. code-block:: c + + offset = N * getpagesize(); + +Sometimes there is hardware with memory-like regions that can not be mapped with +the technique described here, but there are still ways to access them from +userspace. The most common example are x86 ioports. On x86 systems, userspace +can access these ioports using :c:func:`ioperm()`, :c:func:`iopl()`, +:c:func:`inb()`, :c:func:`outb()`, and similar functions. + +Since these ioport regions can not be mapped, they will not appear under +``/sys/class/uio/uioX/maps/`` like the normal memory described above. Without +information about the port regions a hardware has to offer, it becomes difficult +for the userspace part of the driver to find out which ports belong to which UIO +device. + +To address this situation, the new directory ``/sys/class/uio/uioX/portio/`` was +added. It only exists if the driver wants to pass information about one or more +port regions to userspace. If that is the case, subdirectories named ``port0``, +``port1``, and so on, will appear underneath ``/sys/class/uio/uioX/portio/``. + +Each ``portX/`` directory contains four read-only files that show name, start, +size, and type of the port region: + +- ``name``: A string identifier for this port region. The string is optional and + can be empty. Drivers can set it to make it easier for userspace to find a + certain port region. + +- ``start``: The first port of this region. + +- ``size``: The number of ports in this region. + +- ``porttype``: A string describing the type of port. diff --git a/Documentation/uio-howto/aboutthisdoc.rst b/Documentation/uio-howto/aboutthisdoc.rst new file mode 100644 index 0000000..06e9ca6 --- /dev/null +++ b/Documentation/uio-howto/aboutthisdoc.rst @@ -0,0 +1,103 @@ +.. -*- coding: utf-8; mode: rst -*- + +******************* +About this document +******************* + +Translations +============ + +If you know of any translations for this document, or you are interested in +translating it, please email me hjk@xxxxxxxxxxxx. + + +Preface +======= + +For many types of devices, creating a Linux kernel driver is overkill. All that +is really needed is some way to handle an interrupt and provide access to the +memory space of the device. The logic of controlling the device does not +necessarily have to be within the kernel, as the device does not need to take +advantage of any of other resources that the kernel provides. One such common +class of devices that are like this are for industrial I/O cards. + +To address this situation, the userspace I/O system (UIO) was designed. For +typical industrial I/O cards, only a very small kernel module is needed. The +main part of the driver will run in user space. This simplifies development and +reduces the risk of serious bugs within a kernel module. + +Please note that UIO is not an universal driver interface. Devices that are +already handled well by other kernel subsystems (like networking or serial or +USB) are no candidates for an UIO driver. Hardware that is ideally suited for an +UIO driver fulfills all of the following: + +- The device has memory that can be mapped. The device can be controlled + completely by writing to this memory. + +- The device usually generates interrupts. + +- The device does not fit into one of the standard kernel subsystems. + + +Acknowledgments +=============== + +I'd like to thank Thomas Gleixner and Benedikt Spranger of Linutronix, who have +not only written most of the UIO code, but also helped greatly writing this +HOWTO by giving me all kinds of background information. + + +Feedback +======== + +Find something wrong with this document? (Or perhaps something right?) I would +love to hear from you. Please email me at hjk@xxxxxxxxxxxx. + +Revision History +================ + +:revision: 0.9 / 2009-07-16 (*mst*) + + Added generic pci driver + + +:revision: 0.8 / 2008-12-24 (*hjk*) + + Added name attributes in mem and portio sysfs directories. + + +:revision: 0.7 / 2008-12-23 (*hjk*) + + Added generic platform drivers and offset attribute. + + +:revision: 0.6 / 2008-12-05 (*hjk*) + + Added description of portio sysfs attributes. + + +:revision: 0.5 / 2008-05-22 (*hjk*) + + Added description of write() function. + + +:revision: 0.4 / 2007-11-26 (*hjk*) + + Removed section about uio_dummy. + + +:revision: 0.3 / 2007-04-29 (*hjk*) + + Added section about userspace drivers. + + +:revision: 0.2 / 2007-02-13 (*hjk*) + + Update after multiple mappings were added. + + +:revision: 0.1 / 2006-12-11 (*hjk*) + + First draft. + + diff --git a/Documentation/uio-howto/conf.py b/Documentation/uio-howto/conf.py new file mode 100644 index 0000000..c13fea7 --- /dev/null +++ b/Documentation/uio-howto/conf.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8; mode: python -*- + +project = u'The Userspace I/O HOWTO' + +tags.add("subproject") + +latex_documents = [ + ('index', 'uio-howto.tex', 'The Userspace I/O HOWTO', + 'The kernel development community', 'manual'), +] diff --git a/Documentation/uio-howto/custom_kernel_module.rst b/Documentation/uio-howto/custom_kernel_module.rst new file mode 100644 index 0000000..320c420 --- /dev/null +++ b/Documentation/uio-howto/custom_kernel_module.rst @@ -0,0 +1,245 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _custom_kernel_module: + +****************************** +Writing your own kernel module +****************************** + +Please have a look at ``uio_cif.c`` as an example. The following paragraphs +explain the different sections of this file. + + +.. _uio_info: + +struct uio_info +=============== + +This structure tells the framework the details of your driver, Some of +the members are required, others are optional. + +- ``const char *name``: Required. The name of your driver as it will appear in + sysfs. I recommend using the name of your module for this. + +- ``const char *version``: Required. This string appears in + ``/sys/class/uio/uioX/version``. + +- ``struct uio_mem mem[ MAX_UIO_MAPS ]``: Required if you have memory that can + be mapped with :c:func:`mmap()`. For each mapping you need to fill one of the + ``uio_mem`` structures. See the description below for details. + +- ``struct uio_port port[ MAX_UIO_PORTS_REGIONS ]``: Required if you want to + pass information about ioports to userspace. For each port region you need to + fill one of the ``uio_port`` structures. See the description below for + details. + +- ``long irq``: Required. If your hardware generates an interrupt, it's your + modules task to determine the irq number during initialization. If you don't + have a hardware generated interrupt but want to trigger the interrupt handler + in some other way, set ``irq`` to ``UIO_IRQ_CUSTOM``. If you had no interrupt + at all, you could set ``irq`` to ``UIO_IRQ_NONE``, though this rarely makes + sense. + +- ``unsigned long irq_flags``: Required if you've set ``irq`` to a hardware + interrupt number. The flags given here will be used in the call to + :c:func:`request_irq()`. + +- ``int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)``: + Optional. If you need a special :c:func:`mmap()` function, you can set it + here. If this pointer is not NULL, your :c:func:`mmap()` will be called + instead of the built-in one. + +- ``int (*open)(struct uio_info *info, struct inode *inode)``: Optional. You + might want to have your own :c:func:`open()`, e.g. to enable interrupts only + when your device is actually used. + +- ``int (*release)(struct uio_info *info, struct inode *inode)``: Optional. If + you define your own :c:func:`open()`, you will probably also want a custom + :c:func:`release()` function. + +- ``int (*irqcontrol)(struct uio_info *info, s32 irq_on)``: Optional. If you + need to be able to enable or disable interrupts from userspace by writing to + ``/dev/uioX``, you can implement this function. The parameter ``irq_on`` will + be 0 to disable interrupts and 1 to enable them. + +Usually, your device will have one or more memory regions that can be mapped to +user space. For each region, you have to set up a ``struct uio_mem`` in the +``mem[]`` array. Here's a description of the fields of ``struct uio_mem``: + +- ``const char *name``: Optional. Set this to help identify the memory region, + it will show up in the corresponding sysfs node. + +- ``int memtype``: Required if the mapping is used. Set this to ``UIO_MEM_PHYS`` + if you you have physical memory on your card to be mapped. Use + ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated with + :c:func:`kmalloc()`). There's also ``UIO_MEM_VIRTUAL`` for virtual memory. + +- ``phys_addr_t addr``: Required if the mapping is used. Fill in the address of + your memory block. This address is the one that appears in sysfs. + +- ``resource_size_t size``: Fill in the size of the memory block that ``addr`` + points to. If ``size`` is zero, the mapping is considered unused. Note that + you *must* initialize ``size`` with zero for all unused mappings. + +- ``void *internal_addr``: If you have to access this memory region from within + your kernel module, you will want to map it internally by using something like + :c:func:`ioremap()`. Addresses returned by this function cannot be mapped to + user space, so you must not store it in ``addr``. Use ``internal_addr`` + instead to remember such an address. + +Please do not touch the ``map`` element of ``struct uio_mem``! It is used by the +UIO framework to set up sysfs files for this mapping. Simply leave it alone. + +Sometimes, your device can have one or more port regions which can not be mapped +to userspace. But if there are other possibilities for userspace to access these +ports, it makes sense to make information about the ports available in +sysfs. For each region, you have to set up a ``struct uio_port`` in the +``port[]`` array. Here's a description of the fields of ``struct uio_port``: + +- ``char *porttype``: Required. Set this to one of the predefined constants. Use + ``UIO_PORT_X86`` for the ioports found in x86 architectures. + +- ``unsigned long start``: Required if the port region is used. Fill in the + number of the first port of this region. + +- ``unsigned long size``: Fill in the number of ports in this region. If + ``size`` is zero, the region is considered unused. Note that you *must* + initialize ``size`` with zero for all unused regions. + +Please do not touch the ``portio`` element of ``struct uio_port``! It is used +internally by the UIO framework to set up sysfs files for this region. Simply +leave it alone. + + +.. _adding_irq_handler: + +Adding an interrupt handler +=========================== + +What you need to do in your interrupt handler depends on your hardware and on +how you want to handle it. You should try to keep the amount of code in your +kernel interrupt handler low. If your hardware requires no action that you +*have* to perform after each interrupt, then your handler can be empty. + +If, on the other hand, your hardware *needs* some action to be performed after +each interrupt, then you *must* do it in your kernel module. Note that you +cannot rely on the userspace part of your driver. Your userspace program can +terminate at any time, possibly leaving your hardware in a state where proper +interrupt handling is still required. + +There might also be applications where you want to read data from your hardware +at each interrupt and buffer it in a piece of kernel memory you've allocated for +that purpose. With this technique you could avoid loss of data if your userspace +program misses an interrupt. + +A note on shared interrupts: Your driver should support interrupt sharing +whenever this is possible. It is possible if and only if your driver can detect +whether your hardware has triggered the interrupt or not. This is usually done +by looking at an interrupt status register. If your driver sees that the IRQ bit +is actually set, it will perform its actions, and the handler returns +IRQ_HANDLED. If the driver detects that it was not your hardware that caused the +interrupt, it will do nothing and return IRQ_NONE, allowing the kernel to call +the next possible interrupt handler. + +If you decide not to support shared interrupts, your card won't work in +computers with no free interrupts. As this frequently happens on the PC +platform, you can save yourself a lot of trouble by supporting interrupt +sharing. + + +.. _using_uio_pdrv: + +Using uio_pdrv for platform devices +=================================== + +In many cases, UIO drivers for platform devices can be handled in a generic +way. In the same place where you define your ``struct platform_device``, you +simply also implement your interrupt handler and fill your ``struct +uio_info``. A pointer to this ``struct uio_info`` is then used as +``platform_data`` for your platform device. + +You also need to set up an array of ``struct resource`` containing addresses and +sizes of your memory mappings. This information is passed to the driver using +the ``.resource`` and ``.num_resources`` elements of ``struct platform_device``. + +You now have to set the ``.name`` element of ``struct platform_device`` to +``"uio_pdrv"`` to use the generic UIO platform device driver. This driver will +fill the ``mem[]`` array according to the resources given, and register the +device. + +The advantage of this approach is that you only have to edit a file you need to +edit anyway. You do not have to create an extra driver. + + +.. _using_uio_pdrv_genirq: + +Using uio_pdrv_genirq for platform devices +========================================== + +Especially in embedded devices, you frequently find chips where the irq pin is +tied to its own dedicated interrupt line. In such cases, where you can be really +sure the interrupt is not shared, we can take the concept of ``uio_pdrv`` one +step further and use a generic interrupt handler. That's what +``uio_pdrv_genirq`` does. + +The setup for this driver is the same as described above for ``uio_pdrv``, +except that you do not implement an interrupt handler. The ``.handler`` element +of ``struct uio_info`` must remain ``NULL``. The ``.irq_flags`` element must not +contain ``IRQF_SHARED``. + +You will set the ``.name`` element of ``struct platform_device`` to +``"uio_pdrv_genirq"`` to use this driver. + +The generic interrupt handler of ``uio_pdrv_genirq`` will simply disable the +interrupt line using :c:func:`disable_irq_nosync()`. After doing its work, +userspace can reenable the interrupt by writing 0x00000001 to the UIO device +file. The driver already implements an :c:func:`irq_control()` to make this +possible, you must not implement your own. + +Using ``uio_pdrv_genirq`` not only saves a few lines of interrupt handler +code. You also do not need to know anything about the chip's internal registers +to create the kernel part of the driver. All you need to know is the irq number +of the pin the chip is connected to. + + +.. _using-uio_dmem_genirq: + +Using uio_dmem_genirq for platform devices +========================================== + +In addition to statically allocated memory ranges, they may also be a desire to +use dynamically allocated regions in a user space driver. In particular, being +able to access memory made available through the dma-mapping API, may be +particularly useful. The ``uio_dmem_genirq`` driver provides a way to accomplish +this. + +This driver is used in a similar manner to the ``"uio_pdrv_genirq"`` driver with +respect to interrupt configuration and handling. + +Set the ``.name`` element of ``struct platform_device`` to ``"uio_dmem_genirq"`` +to use this driver. + +When using this driver, fill in the ``.platform_data`` element of ``struct +platform_device``, which is of type ``struct uio_dmem_genirq_pdata`` and which +contains the following elements: + +- ``struct uio_info uioinfo``: The same structure used as the + ``uio_pdrv_genirq`` platform data + +- ``unsigned int *dynamic_region_sizes``: Pointer to list of sizes of dynamic + memory regions to be mapped into user space. + +- ``unsigned int num_dynamic_regions``: Number of elements in + ``dynamic_region_sizes`` array. + +The dynamic regions defined in the platform data will be appended to the +``mem[]`` array after the platform device resources, which implies that the +total number of static and dynamic memory regions cannot exceed +``MAX_UIO_MAPS``. + +The dynamic memory regions will be allocated when the UIO device file, +``/dev/uioX`` is opened. Similar to static memory resources, the memory region +information for dynamic regions is then visible via sysfs at +``/sys/class/uio/uioX/maps/mapY/*``. The dynamic memory regions will be freed +when the UIO device file is closed. When no processes are holding the device +file open, the address returned to userspace is ~0. diff --git a/Documentation/uio-howto/index.rst b/Documentation/uio-howto/index.rst new file mode 100644 index 0000000..e010dc4 --- /dev/null +++ b/Documentation/uio-howto/index.rst @@ -0,0 +1,45 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _uio-howto: + +======================= +The Userspace I/O HOWTO +======================= + +This HOWTO describes concept and usage of Linux kernel's Userspace +I/O system. + +.. toctree:: + :maxdepth: 1 + + aboutthisdoc + about_uio + custom_kernel_module + userspace_driver + uio_pci_generic + +.. only:: subproject + + Indices + ======= + + * :ref:`genindex` + +******************* +Further information +******************* + +- `OSADL homepage. <http://www.osadl.org>`__ + +- `Linutronix homepage. <http://www.linutronix.de>`__ + +********* +Copyright +********* + +This documentation is Free Software licensed under the terms of the GPL +version 2. + +**Copyright** 2006-2008 : Hans-Jürgen Koch. + +**Copyright** 2009 : Red Hat Inc, Michael S. Tsirkin (mst@xxxxxxxxxx) diff --git a/Documentation/uio-howto/uio_pci_generic.rst b/Documentation/uio-howto/uio_pci_generic.rst new file mode 100644 index 0000000..181344e --- /dev/null +++ b/Documentation/uio-howto/uio_pci_generic.rst @@ -0,0 +1,168 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _uio_pci_generic: + +********************** +Generic PCI UIO driver +********************** + +The generic driver is a kernel module named uio_pci_generic. It can +work with any device compliant to PCI 2.3 (circa 2002) and any compliant +PCI Express device. Using this, you only need to write the userspace +driver, removing the need to write a hardware-specific kernel module. + + +.. _uio_pci_generic_binding: + +Making the driver recognize the device +====================================== + +Since the driver does not declare any device ids, it will not get loaded +automatically and will not automatically bind to any devices, you must +load it and allocate id to the driver yourself. For example: + + +.. code-block:: sh + + modprobe uio_pci_generic + echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id + +If there already is a hardware specific kernel driver for your device, +the generic driver still won't bind to it, in this case if you want to +use the generic driver (why would you?) you'll have to manually unbind +the hardware specific driver and bind the generic driver, like this: + + +.. code-block:: sh + + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind + +You can verify that the device has been bound to the driver by looking +for it in sysfs, for example like the following: + + +.. code-block:: sh + + ls -l /sys/bus/pci/devices/0000:00:19.0/driver + +Which if successful should print + + +.. code-block:: sh + + .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic + +Note that the generic driver will not bind to old PCI 2.2 devices. If +binding the device failed, run the following command: + + +.. code-block:: sh + + dmesg + +and look in the output for failure reasons + + +.. _uio_pci_generic_internals: + +Things to know about uio_pci_generic +==================================== + +Interrupts are handled using the Interrupt Disable bit in the PCI +command register and Interrupt Status bit in the PCI status register. +All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI +Express devices should support these bits. uio_pci_generic detects +this support, and won't bind to devices which do not support the +Interrupt Disable Bit in the command register. + +On each interrupt, uio_pci_generic sets the Interrupt Disable bit. +This prevents the device from generating further interrupts until the +bit is cleared. The userspace driver should clear this bit before +blocking and waiting for more interrupts. + + +.. _uio_pci_generic_userspace: + +Writing userspace driver using uio_pci_generic +============================================== + +Userspace driver can use pci sysfs interface, or the libpci libray that +wraps it, to talk to the device and to re-enable interrupts by writing +to the command register. + + +.. _uio_pci_generic_example: + +Example code using uio_pci_generic +================================== + +Here is some sample userspace driver code using uio_pci_generic: + +.. code-block:: c + + #include <stdlib.h> + #include <stdio.h> + #include <unistd.h> + #include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <errno.h> + + int main() + { + int uiofd; + int configfd; + int err; + int i; + unsigned icount; + unsigned char command_high; + + uiofd = open("/dev/uio0", O_RDONLY); + if (uiofd < 0) { + perror("uio open:"); + return errno; + } + configfd = open("/sys/class/uio/uio0/device/config", O_RDWR); + if (configfd < 0) { + perror("config open:"); + return errno; + } + + /* Read and cache command value */ + err = pread(configfd, &command_high, 1, 5); + if (err != 1) { + perror("command config read:"); + return errno; + } + command_high &= ~0x4; + + for(i = 0;; ++i) { + /* Print out a message, for debugging. */ + if (i == 0) + fprintf(stderr, "Started uio test driver.\\n"); + else + fprintf(stderr, "Interrupts: %d\\n", icount); + + /****************************************/ + /* Here we got an interrupt from the + device. Do something to it. */ + /****************************************/ + + /* Re-enable interrupts. */ + err = pwrite(configfd, &command_high, 1, 5); + if (err != 1) { + perror("config write:"); + break; + } + + /* Wait for next interrupt. */ + err = read(uiofd, &icount, 4); + if (err != 4) { + perror("uio read:"); + break; + } + + } + return errno; + } diff --git a/Documentation/uio-howto/userspace_driver.rst b/Documentation/uio-howto/userspace_driver.rst new file mode 100644 index 0000000..fc119ba --- /dev/null +++ b/Documentation/uio-howto/userspace_driver.rst @@ -0,0 +1,86 @@ +.. -*- coding: utf-8; mode: rst -*- + +.. _userspace_driver: + +***************************** +Writing a driver in userspace +***************************** + +Once you have a working kernel module for your hardware, you can write +the userspace part of your driver. You don't need any special libraries, +your driver can be written in any reasonable language, you can use +floating point numbers and so on. In short, you can use all the tools +and libraries you'd normally use for writing a userspace application. + + +.. _getting_uio_information: + +Getting information about your UIO device +========================================= + +Information about all UIO devices is available in sysfs. The first thing +you should do in your driver is check ``name`` and ``version`` to make +sure your talking to the right device and that its kernel driver has the +version you expect. + +You should also make sure that the memory mapping you need exists and +has the size you expect. + +There is a tool called ``lsuio`` that lists UIO devices and their +attributes. It is available here: + +`http://www.osadl.org/projects/downloads/UIO/user/ <http://www.osadl.org/projects/downloads/UIO/user/>`__ + +With ``lsuio`` you can quickly check if your kernel module is loaded and +which attributes it exports. Have a look at the manpage for details. + +The source code of ``lsuio`` can serve as an example for getting +information about an UIO device. The file ``uio_helper.c`` contains a +lot of functions you could use in your userspace driver code. + + +.. _mmap_device_memory: + +mmap() device memory +==================== + +After you made sure you've got the right device with the memory mappings +you need, all you have to do is to call :c:func:`mmap()` to map the +device's memory to userspace. + +The parameter ``offset`` of the :c:func:`mmap()` call has a special +meaning for UIO devices: It is used to select which mapping of your +device you want to map. To map the memory of mapping N, you have to use +N times the page size as your offset: + + +.. code-block:: c + + offset = N * getpagesize(); + +N starts from zero, so if you've got only one memory range to map, set +``offset = 0``. A drawback of this technique is that memory is always +mapped beginning with its start address. + + +.. _wait_for_interrupts: + +Waiting for interrupts +====================== + +After you successfully mapped your devices memory, you can access it +like an ordinary array. Usually, you will perform some initialization. +After that, your hardware starts working and will generate an interrupt +as soon as it's finished, has some data available, or needs your +attention because an error occurred. + +``/dev/uioX`` is a read-only file. A :c:func:`read()` will always +block until an interrupt occurs. There is only one legal value for the +``count`` parameter of :c:func:`read()`, and that is the size of a +signed 32 bit integer (4). Any other value for ``count`` causes +:c:func:`read()` to fail. The signed 32 bit integer read is the +interrupt count of your device. If the value is one more than the value +you read the last time, everything is OK. If the difference is greater +than one, you missed interrupts. + +You can also use :c:func:`select()` on ``/dev/uioX``. -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html