diff --git a/buildroot_config_fragment b/buildroot_config_fragment index 598ed87..740f192 100644 --- a/buildroot_config_fragment +++ b/buildroot_config_fragment @@ -79,6 +79,7 @@ BR2_PACKAGE_DEVMEM2=y # # hexdump /sys/bus/pci/devices/0000:00:05.0/config # +# and /dev/mem can of course do both reads and writes, # but setpci provies nice human readable register names, e.g.: # # setpci --dumpregs diff --git a/kernel_module/pci.c b/kernel_module/pci.c index f10990b..862ea7a 100644 --- a/kernel_module/pci.c +++ b/kernel_module/pci.c @@ -60,6 +60,10 @@ Class: pure magic: https://www-s.acm.illinois.edu/sigops/2007/roll_your_own/7.c. TODO: does it have any side effects? Set in the edu device at: k->class_id = PCI_CLASS_OTHERS + +## Play with registers from the CLI + +Use setpci, devmem2 and /sys. */ #include /* put_user */ diff --git a/kernel_module/user/uio_read.c b/kernel_module/user/uio_read.c index bfac6e1..84ce544 100644 --- a/kernel_module/user/uio_read.c +++ b/kernel_module/user/uio_read.c @@ -1,19 +1,32 @@ /* -Adapted from: https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt/ +TODO get working. Currently I don't understand the behaviour. - modprobe uio_pci_generic - echo '1234 11e9' > /sys/bus/pci/drivers/uio_pci_generic/new_id - /uio_read.out & +TODO how to ACK interrupts? How to ensure that every interrupt gets handled separately? -TODO get working. The problem now is how to generate IRQs to test our IRQ handling: - -- a synchronous IRQ needs regwrites to be kicked off, but mmap does not seem to work with this driver -- a custom PCI periodic timer device would also need to be initiazed by some regwrite, otherwise - early interrupts before the OS is setup would crash everything? +TODO write to registers. Currently using /dev/mem and lspci. Handle interrupts from userland and print a message to stdout. + +- Userland driver +- https://stackoverflow.com/questions/15286772/userspace-vs-kernel-space-driver +- https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html +- https://stackoverflow.com/questions/7986260/linux-interrupt-handling-in-user-space +- https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt/ +- https://github.com/bmartini/zynq-axis/blob/65a3a448fda1f0ea4977adfba899eb487201853d/dev/axis.c +- https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt/ +- http://nairobi-embedded.org/uio_example.html that website has QEMU examples for everything as usual. The example has a kernel-side which creates the memory mappings and is used by the user. + +Userland driver stability questions: + +- https://stackoverflow.com/questions/8030758/getting-kernel-version-from-linux-kernel-module-at-runtime/45430233#45430233 +- https://stackoverflow.com/questions/37098482/how-to-build-a-linux-kernel-module-so-that-it-is-compatible-with-all-kernel-rele/45429681#45429681 +- https://liquidat.wordpress.com/2007/07/21/linux-kernel-2623-to-have-stable-userspace-driver-api/ */ +#if 1 + +/* Adapted from: https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt */ + #define _XOPEN_SOURCE 700 #include /* open */ #include @@ -28,7 +41,6 @@ Handle interrupts from userland and print a message to stdout. int main(int argc, char **argv) { - char *dev = "/dev/uio0"; if (argc > 1) { dev = argv[1]; @@ -58,9 +70,65 @@ int main(int argc, char **argv) } nb = read(fd, &info, sizeof(info)); if (nb == sizeof(info)) { - printf("Interrupt #%u\n", info); + printf(__FILE__ " read = %u\n", info); } } - close(fd); - exit(EXIT_SUCCESS); } + +#else + +/* Ripped from the kernel docs. */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include +#include + +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; + } + err = pread(configfd, &command_high, 1, 5); + if (err != 1) { + perror("command config read:"); + return errno; + } + command_high &= ~0x4; + for(i = 0;; ++i) { + fprintf(stderr, "Interrupts: %d\n", icount); + err = pwrite(configfd, &command_high, 1, 5); + if (err != 1) { + perror("config write:"); + break; + } + err = read(uiofd, &icount, 4); + if (err != 4) { + perror("uio read:"); + break; + } + + } + return errno; +} + +#endif diff --git a/rootfs_overlay/uio_read.sh b/rootfs_overlay/uio_read.sh new file mode 100755 index 0000000..5cecb26 --- /dev/null +++ b/rootfs_overlay/uio_read.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -ex +modprobe uio_pci_generic +# pci_min device +echo '1234 11e9' > /sys/bus/pci/drivers/uio_pci_generic/new_id +/uio_read.out & +# Helper to observe interrupts. +insmod /irq.ko +base="$(setpci -d 1234:11e9 BASE_ADDRESS_0)" +# Start generating interrupt. +/devmem3.out "0x${base}" w 0x12345678 +# Stop generating interrupt. +/devmem3.out "0x$(($base + 4))" w 0x12345678 +/devmem3.out "0x${base}" w 0x12345678 +/devmem3.out "0x$(($base + 4))" w 0x12345678