X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=Documentation%2FDocBook%2Fuio-howto.tmpl;h=4d4ce0e61e424708a94db666e0496d7c0f3fc253;hb=90b9a32d8f441369b2f97a765d2d957b531eb653;hp=6116b93608df87246b30a845a37da3a86c1883ce;hpb=a2ab3d30005cdce45c2c7e31ad6743ad7975609a;p=safe%2Fjmp%2Flinux-2.6 diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl index 6116b93..4d4ce0e 100644 --- a/Documentation/DocBook/uio-howto.tmpl +++ b/Documentation/DocBook/uio-howto.tmpl @@ -25,6 +25,10 @@ 2006-2008 Hans-Jürgen Koch. + + 2009 + Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) + @@ -42,6 +46,26 @@ GPL version 2. + 0.9 + 2009-07-16 + mst + Added generic pci driver + + + + 0.8 + 2008-12-24 + hjk + Added name attributes in mem and portio sysfs directories. + + + + 0.7 + 2008-12-23 + hjk + Added generic platform drivers and offset attribute. + + 0.6 2008-12-05 hjk @@ -297,12 +321,19 @@ interested in translating it, please email me appear if the size of the mapping is not 0. - Each mapX/ directory contains two read-only files - that show start address and size of the memory: + 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. @@ -312,6 +343,16 @@ interested in translating it, please email me pointed to by addr. + + + offset: The offset, in bytes, that has to be + added to the pointer returned by 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 + mmap() are always page aligned, so it is good + style to always add this offset. + + @@ -350,12 +391,19 @@ offset = N * getpagesize(); /sys/class/uio/uioX/portio/. - Each portX/ directory contains three read-only - files that show start, size, and type of the port region: + 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. @@ -393,12 +441,12 @@ offset = N * getpagesize(); -char *name: Required. The name of your driver as +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. -char *version: Required. This string appears in +const char *version: Required. This string appears in /sys/class/uio/uioX/version. @@ -594,6 +642,78 @@ framework to set up sysfs files for this region. Simply leave it alone. + +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 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 + 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 + 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. + + + @@ -700,6 +820,158 @@ framework to set up sysfs files for this region. Simply leave it alone. + + +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. + + + +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: + + 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: + + 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: + + ls -l /sys/bus/pci/devices/0000:00:19.0/driver + +Which if successful should print + + .../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: + + dmesg + +and look in the output for failure reasons + + + + +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. + + + +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. + + + +Example code using uio_pci_generic + +Here is some sample userspace driver code using uio_pci_generic: + +#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 (uiofd < 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; +} + + + + + + + Further information