📄 uio-howto.tmpl
字号:
<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>char *name</varname>: Required. The name of your driver asit will appear in sysfs. I recommend using the name of your module for this.</para></listitem><listitem><para><varname>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 youhave memory that can be mapped with <function>mmap()</function>. For eachmapping you need to fill one of the <varname>uio_mem</varname> structures.See the description below for details.</para></listitem><listitem><para><varname>long irq</varname>: Required. If your hardware generates aninterrupt, it's your modules task to determine the irq number duringinitialization. If you don't have a hardware generated interrupt butwant 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 thisrarely 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 givenhere 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 thispointer is not NULL, your <function>mmap()</function> will be calledinstead 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 yourdevice 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></itemizedlist><para>Usually, your device will have one or more memory regions that can be mappedto 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>int memtype</varname>: Required if the mapping is used. Set this to<varname>UIO_MEM_PHYS</varname> if you you have physical memory on yourcard to be mapped. Use <varname>UIO_MEM_LOGICAL</varname> for logicalmemory (e.g. allocated with <function>kmalloc()</function>). There's also<varname>UIO_MEM_VIRTUAL</varname> for virtual memory.</para></listitem><listitem><para><varname>unsigned long addr</varname>: Required if the mapping is used.Fill in the address of your memory block. This address is the one thatappears in sysfs.</para></listitem><listitem><para><varname>unsigned long size</varname>: Fill in the size of thememory 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 forall unused mappings.</para></listitem><listitem><para><varname>void *internal_addr</varname>: If you have to access this memoryregion from within your kernel module, you will want to map it internally byusing something like <function>ioremap()</function>. Addressesreturned by this function cannot be mapped to user space, so you must notstore 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>kobj</varname> element of<varname>struct uio_mem</varname>! It is used by the UIO frameworkto set up sysfs files for this mapping. 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></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 occured. </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><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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -