Chapter 3. Writing your own kernel module

Table of Contents

struct uio_info
Adding an interrupt handler
Using uio_pdrv for platform devices
Using uio_pdrv_genirq for platform devices
Using uio_dmem_genirq for platform devices

Please have a look at uio_cif.c as an example. The following paragraphs explain the different sections of this file.

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 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 request_irq().

  • int (*mmap)(struct uio_info *info, struct vm_area_struct *vma): Optional. If you need a special mmap() function, you can set it here. If this pointer is not NULL, your 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 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 open(), you will probably also want a custom 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 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 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.