<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Building Kernel Modules for the Omega2]]></title><description><![CDATA[<p dir="auto">During a new project of mine, I needed a new kernel module for a device driver. I saw that there wasn't yet a condensed write-up on how to manage, compile and run your own kernel modules for the Omega2. So I decided to write one as a side-project.  Here it is.</p>
<p dir="auto">Update 09.04.2018: Contains new section about reading hardware registers.</p>
<h1>Building Kernel Modules for the Omega2</h1>
<h3>Introduction</h3>
<p dir="auto">This is a tutorial on how to build and run Linux kernel modules (<code>.ko</code> files) for the Omega2.</p>
<p dir="auto">There are several related topics in this forum (e.g. <a href="https://community.onion.io/topic/2054/how-to-get-additional-kernel-modules" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/1005/request-for-prebuild-kernel-modules" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/921/kernel-driver-version-mismatch" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/1756/request-kernel-module-kmod-cryptodev" rel="nofollow">here</a>). However I didn't find any conclusive step-by-step tutorial on here that explains how to build a kernel module from the ground up. This is why I've created this tutorial to share what I've learned over the past few days which led me to a functioning kernel module compilation.</p>
<h3>Prerequisites</h3>
<p dir="auto">This tutorial is working with the newest firwmare version <code>b178</code>, kernel version <code>4.4.74</code>. You should also have <code>kmod</code>. Do <code>opkg update &amp;&amp; opkg install kmod</code> if it isn't already there. Other prerequisites will be installed during the tutorial.</p>
<h3>Kernel modules</h3>
<p dir="auto">What are <a href="https://wiki.archlinux.org/index.php/Kernel_module" rel="nofollow">kernel modules</a> and why should you care?</p>
<blockquote>
<p dir="auto">Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system.</p>
</blockquote>
<p dir="auto">Things like USB drivers / systems, interface drivers, input drivers, can be distributed in the form of the kernel modules. A kernel module also runs with higher priviledges (kernel space vs user space). You may only have direct hardware register access to e.g. GPIO, PWM, SPI, etc. from there. Kernel modules then expose certain functionality or device files to the user space. Examples of kernel modules are:</p>
<ul>
<li><code>kmod-spi-dev</code> - exposes linux SPI device</li>
<li><code>kmod-fs-ext4</code> - EXT4 filesystem support</li>
<li><code>kmod-usb-audio</code> - Audio over USB devices</li>
<li><code>kmod-bluetooth</code> - Bluetooth support</li>
</ul>
<p dir="auto">You can look at your loaded kernel modules by executing <code>lsmod</code> and get more info on a module by executing <code>modinfo &lt;module name&gt;</code>. The <code>.ko</code> files are stored in <code>/lib/modules/4.4.74</code>.</p>
<pre><code class="language-sh">root@Omega-17FD:~# lsmod
aead                    3489  0
bluetooth             258081  8 rfcomm,hidp,hci_uart,btusb,btintel,bnep
ext4                  319010  1
..
root@Omega-17FD:~# modinfo bluetooth
module:         /lib/modules/4.4.74/bluetooth.ko
alias:          net-pf-31
license:        GPL
depends:        crc16

root@Omega-17FD:/lib/modules/4.4.74# ls -l
-rw-r--r--    1 root     root          7192 Apr  2 23:19 aead.ko
-rw-r--r--    1 root     root         28520 Apr  2 23:19 autofs4.ko
-rw-r--r--    1 root     root        325244 Apr  2 23:19 bluetooth.ko
...
</code></pre>
<p dir="auto">For a project you might need a kernel module that is not included or distributed for the Omega2. As a first reference, you should look into Onion's <a href="http://repo.onion.io/omega2/packages/" rel="nofollow">package repository</a>. This tutorial is for the case when you don't find your wanted module there or you want to build something new (e.g., peripheral drivers, ..).</p>
<h3>Understanding the LEDE build system for kernel modules</h3>
<p dir="auto">Where can we see what stock kernel modules and libraries we can build? For this, you need the cross-compilation environment first. This includes cloning Onion's <code>source</code> <a href="https://github.com/OnionIoT/source" rel="nofollow">repository</a> for LEDE-17.01 and building it. A tutorial can be found <a href="https://docs.onion.io/omega2-docs/cross-compiling.html" rel="nofollow">here</a>. You should also have a basic idea on <a href="https://onion.io/2bt-cross-compiling-programs/" rel="nofollow">how to cross-compile a program</a> for the Omega2. I built the toolchain within a Xubuntu VM.</p>
<p dir="auto">When we execute <code>make menuconfig</code> in the <code>source</code> folder, we get a menu where we can configure stuff:</p>
<p dir="auto"><img src="https://i.stack.imgur.com/HbK2O.png" alt="kernelconfig" class="img-responsive img-markdown" /></p>
<p dir="auto">We can select a kernel module there and mark it with "M" -- meaning that this will build into a kernel module file. After exiting and saving the configuration you can build the module by executing <code>make</code> (with optional <code>-j4</code> for 4-core multithreading).</p>
<p dir="auto">Where do these packages come from? They come from the package feed. The sources are defined in the <code>feeds.conf</code> and <code>feeds.conf.default</code> file.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ cat feeds.conf
src-git packages https://git.lede-project.org/feed/packages.git;lede-17.01
src-git luci https://git.lede-project.org/project/luci.git;lede-17.01
src-git routing https://git.lede-project.org/feed/routing.git;lede-17.01
src-git telephony https://git.lede-project.org/feed/telephony.git;lede-17.01
src-git onion https://github.com/OnionIoT/OpenWRT-Packages.git
</code></pre>
<p dir="auto">So they are just a bunch of links to git repositories where packages are stored. You can visit the links to see what's inside them.</p>
<p dir="auto">There are two main steps for adding a new kernel module package:</p>
<ul>
<li>Update the index files in <code>feeds/</code> of available packages defined in <code>feeds.conf</code></li>
<li>Install symlinks to the packages in <code>package/feeds/</code> so they become available in <code>make menuconfig</code></li>
</ul>
<p dir="auto">There are scripts inside the <code>script/</code> folder to help you manage your feeds.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds -h
Usage: ./scripts/feeds &lt;command&gt; [options]

Commands:
	list [options]: List feeds, their content and revisions (if installed)
	Options:
	    -n :            List of feed names.
...
</code></pre>
<p dir="auto">As described in Onion's repository <a href="https://github.com/OnionIoT/source#lede-linux-distribution" rel="nofollow">README</a>:</p>
<blockquote>
<p dir="auto">Run <code>./scripts/feeds update -a</code> to get all the latest package definitions defined in <code>feeds.conf</code> / <code>feeds.conf.default</code> respectively and <code>./scripts/feeds install -a</code> to install symlinks of all of them into <code>package/feeds/</code>.</p>
</blockquote>
<p dir="auto">More specifically, you can also selectively update and install feeds and packages by using:</p>
<ul>
<li><code>./scripts/feeds update -i</code> to recreate the index file</li>
<li><code>./scripts/feeds update &lt;feed name&gt;</code> to update only one feed</li>
<li><code>./scripts/feeds install &lt;package name&gt;</code> to install only one package</li>
</ul>
<h3>Let's build a 'hello world' kernel module!</h3>
<p dir="auto">Time to build the first actual kernel module.</p>
<p dir="auto">For security reasons you should first comment out every other feed in your <code>feeds.conf</code> and <code>feeds.conf.default</code> to prevent accidently updating everything (and to a new kernel version), using the <code>#</code> character.</p>
<p dir="auto">We will create a new <code>src-link</code> type feed using a folder on our host system as described <a href="https://wiki.openwrt.org/doc/devel/feeds" rel="nofollow">here</a>. We will take code and modified Makefiles from a tutorial <a href="https://www.tldp.org/LDP/lkmpg/2.6/html/hello2.html" rel="nofollow">here</a>.</p>
<pre><code class="language-sh">max@max-VirtualBox:~$ mkdir omega2_kmods &amp;&amp; cd omega2_kmods/
max@max-VirtualBox:~/omega2_kmods$ mkdir hello-world &amp;&amp; cd hello-world/
max@max-VirtualBox:~/omega2_kmods/hello-world$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/hello-world$ mkdir src &amp;&amp; cd src
max@max-VirtualBox:~/omega2_kmods/hello-world/src$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/hello-world/src$ touch hello-world.c
</code></pre>
<p dir="auto">Fill in the contents of the files:</p>
<p dir="auto"><code>src/hello-world.c</code>:</p>
<pre><code class="language-c">#include &lt;linux/module.h&gt;	/* Needed by all modules */
#include &lt;linux/kernel.h&gt;	/* Needed for KERN_INFO */
#include &lt;linux/init.h&gt;		/* Needed for the macros */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maximilian Gerhardt");
MODULE_DESCRIPTION("Basic kernel module for tutorial");

static int __init hello_2_init(void)
{
	printk(KERN_INFO "Hello, world! We're inside the Omega2's kernel.\n");
	return 0;
}

static void __exit hello_2_exit(void)
{
	printk(KERN_INFO "Goodbye, world!\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);
</code></pre>
<p dir="auto"><code>Makefile</code>:</p>
<pre><code class="language-Makefile"># Copyright (C) 2006-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

# name
PKG_NAME:=hello-world
# version of what we are downloading
PKG_VERSION:=1.1
# version of this makefile
PKG_RELEASE:=4

PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)
PKG_CHECK_FORMAT_SECURITY:=0

include $(INCLUDE_DIR)/package.mk

define KernelPackage/$(PKG_NAME)
	SUBMENU:=Other modules
	DEPENDS:=@(TARGET_ramips_mt76x8||TARGET_ramips_mt7688)
	TITLE:=Hello world kernel module
	FILES:= $(PKG_BUILD_DIR)/hello-world.ko
endef

define KernelPackage/$(PKG_NAME)/description
	A "hello world" test module for a tutorial.
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

MAKE_OPTS:= \
	ARCH="$(LINUX_KARCH)" \
	CROSS_COMPILE="$(TARGET_CROSS)" \
	SUBDIRS="$(PKG_BUILD_DIR)"

define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		$(MAKE_OPTS) \
		modules
endef

$(eval $(call KernelPackage,$(PKG_NAME)))
</code></pre>
<p dir="auto"><code>src/Makefile</code>:</p>
<pre><code class="language-Makefile">obj-m := hello-world.o
</code></pre>
<p dir="auto">The top-level <code>Makefile</code> declares how to build our kernel module package. You declare the name, its files, a description, dependencies and other things there. See the documentation links at the bottom of this tutorial. The <code>src/Makefile</code> then adds all to-be compiled object files to the build process.</p>
<p dir="auto">Add a line to your <code>feeds.conf</code> declaring the new feed and the path:</p>
<pre><code>src-link omega2_kmods /home/max/omega2_kmods
</code></pre>
<p dir="auto">Update this new feed:</p>
<pre><code>max@max-VirtualBox:~/source$ ./scripts/feeds update omega2_kmods
Updating feed 'omega2_kmods' from '/home/max/omega2_kmods' ...
Create index file './feeds/omega2_kmods.index' 
Collecting package info: done
Collecting target info: done
</code></pre>
<p dir="auto">Install the new <code>kmod-hello-world</code> package. Here we just install everything:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds install -a
Installing all packages from feed omega2_kmods.
Installing package 'hello-world' from omega2_kmods
</code></pre>
<p dir="auto">The new package is installed. You can list all available modules for a feed by executing:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds list -r omega2_kmods
kmod-hello-world                	Hello world kernel module
</code></pre>
<p dir="auto">Execute <code>make menuconfig</code> and navigate to <code>Kernel Modules</code> -&gt; <code>Other Modules</code> (this submenu is declared in the <code>Makefile</code><img src="http://community.onion.io/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=ic093v0mjao" class="not-responsive emoji emoji-android emoji--disappointed" title="):" alt="😞" /></p>
<p dir="auto"><img src="https://i.stack.imgur.com/Y8yg6.png" alt="newmod" class="img-responsive img-markdown" /></p>
<p dir="auto">We see our new kernel module! Navigate to it and press <code>M</code> to mark it for compilation to a kernel module. Exit and save.</p>
<p dir="auto">Now build everything:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make -j4
 make[1] world
 make[2] package/cleanup
 make[2] target/compile
 make[3] -C target/linux compile
 make[2] package/compile
 make[3] -C package/libs/ncurses host-compile
..
 make[3] -C /home/max/omega2_kmods/hello-world compile
..
</code></pre>
<p dir="auto">You can also choose to just compile your module:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j4
 make[1] package/hello-world/compile
 make[2] -C /home/max/omega2_kmods/hello-world compile
</code></pre>
<p dir="auto">Either way, you should now have a <code>hello-world.ko</code>. Search for it:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ find . -name "*hello-world*"
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/hello-world.ko
./staging_dir/packages/ramips/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
./bin/targets/ramips/mt7688/packages/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
./build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.ko
</code></pre>
<p dir="auto">It conveniently compiled the <code>hello-world.ko</code> file and even created an <code>ipk</code> package. Unfortunetly, trying to installing that package will give you a kernel mismatch error because the kernel hashes differ (see topic links at start). Loading the <code>.ko</code> file directly will still work though.</p>
<p dir="auto">Transfer the file to your Omega2 over SSH/SCP.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/hello-world.ko root@192.168.1.202:/root/.
root@192.168.1.202's password: 
hello-world.ko                                                          100% 2504     2.5KB/s   00:00    
</code></pre>
<p dir="auto">SSH to your Omega2 and insert the module.</p>
<pre><code class="language-sh">root@Omega-17FD:~# insmod hello-world.ko
</code></pre>
<p dir="auto">The module should now be loaded and should have printed the messge to the debug kernel log:</p>
<pre><code class="language-sh">root@Omega-17FD:~# lsmod | grep "hello"
hello_world              592  0
root@Omega-17FD:~# dmesg
[...]
[11813.120524] Hello, world! We're inside the Omega2's kernel.
</code></pre>
<p dir="auto">Success! The kernel module was loaded and has executed the module init function successfully!</p>
<p dir="auto">You can unload the module by doing <code>rmmod</code>. This should trigger the module's exit/cleanup function:</p>
<pre><code class="language-sh">root@Omega-17FD:~# rmmod hello_world
root@Omega-17FD:~# dmesg
[..]
[11813.120524] Hello, world! We're inside the Omega2's kernel.
[11957.855870] Goodbye, world!
</code></pre>
<p dir="auto">Works as expected.</p>
<p dir="auto">Note that <code>modinfo</code> only seems to work when the module is inside the <code>/lib/modues/4.4.71</code> folder:</p>
<pre><code class="language-sh">root@Omega-17FD:~# cp hello-world.ko /lib/modules/4.4.74/.
root@Omega-17FD:~# modinfo hello_world
module:         /lib/modules/4.4.74/hello-world.ko
license:        GPL
depends:
</code></pre>
<p dir="auto">At the end of this section you were now able to compile a simple kernel module from scratch and load it into the Omega2.</p>
<h3>Let's read a hardware register!</h3>
<p dir="auto">For a quick additional demo, we can modify the code of the <code>hello-world</code> kernel module to read a register from the Omega2's CPU, which is a MT7688. For a reference, you will need the Mediatek MT7688 datasheet: <a href="https://labs.mediatek.com/en/chipset/MT7688" rel="nofollow">https://labs.mediatek.com/en/chipset/MT7688</a></p>
<p dir="auto">Starting at page 48, the register addresses and meanings are listed in their respective blocks (system config, timer, GPIO, SPI,..). Let's have a look at the <code>SYSCTL</code> block.</p>
<p dir="auto"><img src="https://i.stack.imgur.com/RaWWU.png" alt="datasheet" class="img-responsive img-markdown" /></p>
<p dir="auto">Starting at address <code>0x10000000</code>, there are several interesting registers we can read out. Let's read out <code>CHIPID0_3</code> and <code>CHIPID4_7</code>, which contains the ASCII chip name in these two 32-bit registers.</p>
<p dir="auto">If you were programming a microcontroller without a OS and memory management unit (MMU), you would just set a C pointer variable to that address and start reading from it. However, to read data from this physical memory address in the kernel, we first have to do a memory re-mapping. In an OS, you mostly deal with "virtual addresses", which are remapped by an MMU to real physical address within a memory chip. If we just set a C-pointer to <code>0x10000000</code> and read it, we would read this virtual address, which is no good. Virtual addresses are a whole topic on its own. You should consult a lecture on operating systems if you want to know more about this.</p>
<p dir="auto">The kernel provides in <code>linux/io.h</code> functions with which we can accomplish this:</p>
<ul>
<li><code>void* ioremap(address, size)</code> will return a pointer to the virtual memory which is now mapped to the physical address <code>address</code>. A total of <code>size</code> bytes are mapped to this location</li>
<li><code>ioread32(mem)</code>/<code>ioread16(mem)</code>/<code>ioread8(mem)</code> Reads 32/16/8 bits from specified I/O memory, with pointer previously acquired by <code>ioremap</code></li>
<li><code>iowrite32</code>/<code>iowrite16</code>/<code>iowrite8</code> writes value to memory</li>
<li><code>iounmap(mem)</code> for unmapping when you're done</li>
</ul>
<p dir="auto">Knowing these function and addresses, we can read  <code>CHIPID0_3</code>,  <code>CHIPID4_7</code> and <code>CHIP_REV_ID</code> using the following modified <code>hello-world.c</code> code:</p>
<pre><code class="language-c">#include &lt;linux/module.h&gt;	/* Needed by all modules */
#include &lt;linux/kernel.h&gt;	/* Needed for KERN_INFO */
#include &lt;linux/init.h&gt;		/* Needed for the macros */
#include &lt;linux/io.h&gt;		/* Needed for I/O operations */

/* Addresses of base registers and offsets for certain fields (see datasheet) */
#define MT7688_SYSCTL_BASE			0x10000000
#define MT7688_SYSCTL_CHIPID0_3		0x0
#define MT7688_SYSCTL_CHIPID4_7		0x4
#define MT7688_SYSCTL_CHIP_REV_ID	0xC

/* Physical address in, 32 bit integer out. */
static unsigned int ReadReg32(unsigned long addr) {
	//Remap 4 bytes starting at that address
	void* virtualMem = ioremap(addr, 4);
	if(!virtualMem) {
		printk(KERN_WARNING "Failed to remap register memory");
		return 0;
	}
	//Read 32 bits
	unsigned int val = ioread32(virtualMem);
	printk(KERN_INFO "ReadReg32(0x%08x) = 0x%08x\n", (int)addr, val);
	//Unmap memory again
	iounmap(virtualMem);
	//return read value
	return val;
}

static int __init hello_2_init(void)
{
	printk(KERN_INFO "Hello, world! We're inside the Omega2's kernel.\n");

	unsigned int chipid0_3 = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIPID0_3);
	//Recover the initial characters
	//Datasheet says leftmost bits is the last character
	char id_0 = (char)(chipid0_3 &gt;&gt; 0);
	char id_1 = (char)(chipid0_3 &gt;&gt; 8);
	char id_2 = (char)(chipid0_3 &gt;&gt; 16);
	char id_3 = (char)(chipid0_3 &gt;&gt; 24);
	//Do the same for chipid 4 to 7
	unsigned int chipid4_7 = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIPID4_7);
	char id_4 = (char)(chipid4_7 &gt;&gt; 0);
	char id_5 = (char)(chipid4_7 &gt;&gt; 8);
	char id_6 = (char)(chipid4_7 &gt;&gt; 16);
	char id_7 = (char)(chipid4_7 &gt;&gt; 24);

	//Get revision ID
	unsigned int chiprev = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIP_REV_ID);
	//Parse the bits out of the structure
	int packageId = chiprev &gt;&gt; 16;
	int verId = (chiprev &gt;&gt; 8) &amp; 0xf;
	int ecoId = chiprev &amp; 0xf;

	printk(KERN_INFO "CHIP ID IS: '%c%c%c%c%c%c%c%c'\n",
			id_0, id_1, id_2, id_3, id_4, id_5, id_6, id_7);
	printk(KERN_INFO "CHIP REV ID: PKG_ID=%d, VER_ID=%d, ECO_ID=%d\n",
			packageId, verId, ecoId);

	return 0;
}

static void __exit hello_2_exit(void)
{
	printk(KERN_INFO "Goodbye, world!\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maximilian Gerhardt");
MODULE_DESCRIPTION("Basic kernel module for tutorial with register reading");
</code></pre>
<p dir="auto">Compiling this and loading it into the kernel results in:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j4 &amp;&amp; scp ./build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.ko root@192.168.1.202:/root/.

root@Omega-17FD:~# insmod hello-world.ko
root@Omega-17FD:~# dmesg
[ 8402.279877] Hello, world! We're inside the Omega2's kernel.
[ 8402.285635] ReadReg32(0x10000000) = 0x3637544d
[ 8402.290142] ReadReg32(0x10000004) = 0x20203832
[ 8402.294669] ReadReg32(0x1000000c) = 0x00010102
[ 8402.299175] CHIP ID IS: 'MT7628  '
[ 8402.302623] CHIP REV ID: PKG_ID=1, VER_ID=1, ECO_ID=2
</code></pre>
<p dir="auto">Great, we've read out the chip ID as "MT7628", just as the datasheet said. Convert the binary string from the datasheet to ASCII to confirm this. We also read out the expected values for the <code>CHIP_REV_ID</code> fields.</p>
<p dir="auto">This simple example showed you how to read a hardware register as specified in the MT7688's datasheet from the kernel space.</p>
<h3>Let's build a XBox360 USB gamepad driver!</h3>
<p dir="auto">Now let's do some more serious stuff.</p>
<p dir="auto">I have this Logitech Wireless Gamepad F710 laying around here.</p>
<p dir="auto"><img src="https://i.stack.imgur.com/Pzdt0.png" alt="gamepad" class="img-responsive img-markdown" /></p>
<p dir="auto">Let's plug in into a PC running Linux and look at <code>dmesg</code>:</p>
<pre><code class="language-sh">[13688.633245] usb 2-2: new full-speed USB device number 3 using ohci-pci
[13689.164936] usb 2-2: New USB device found, idVendor=046d, idProduct=c21f
[13689.164939] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13689.164940] usb 2-2: Product: Wireless Gamepad F710
[13689.164941] usb 2-2: Manufacturer: Logitech
[13689.164942] usb 2-2: SerialNumber: ADED9351
[13690.978059] input: Logitech Gamepad F710 as /devices/pci0000:00/0000:00:06.0/usb2/2-2/2-2:1.0/input/input8
[13690.978517] usbcore: registered new interface driver xpad
max@max-VirtualBox:~/source$ ls /dev/input/
event0  event1  js0   mouse0 
</code></pre>
<p dir="auto">The USB device is detected, the  driver <code>xpad</code> takes over and creates new device files <code>/dev/input/event1</code> and <code>/dev/input/js0</code> (Joystick #0).  These are device files for the event-based API (new) and the older Linux Joystick API.</p>
<p dir="auto">What happens when we plug this into the Omega2?</p>
<pre><code class="language-sh">root@Omega-17FD:~# logread -f
[14602.640692] usb 2-1: new full-speed USB device number 7 using ohci-platform
[14602.889249] hid-generic 0003:046D:C22F.0009: hiddev0,hidraw0: USB HID v1.1 1 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input0
[14602.941098] hid-generic 0003:046D:C22F.000A: hiddev0,hidraw1: USB HID v1.1 1 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input1
</code></pre>
<p dir="auto">The USB device is detected as a Human Interface Device (HID). However, you will notice that there are no <code>/dev/input/*</code> devices:</p>
<pre><code class="language-sh">root@Omega-17FD:~# ls /dev/input/
root@Omega-17FD:~#
</code></pre>
<p dir="auto">We can't really use the device this way. We're missing the <code>xpad</code> driver here, as well es the entire <code>kmod-input-joydev</code> package for the Joystick API.</p>
<p dir="auto">Luckily we can use our newly gained knowledge about kernel modules to get this thing working. A search for "linux xpad driver" leads to <a href="https://github.com/paroj/xpad" rel="nofollow">https://github.com/paroj/xpad</a> and <a href="https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c" rel="nofollow">https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c</a>. I will use the code in the first repository, but the code for the newer Linux kernel should also work and support even more devices (206 of them).</p>
<p dir="auto">Following up on the structure of our first kernel module, let's create a new package folder <code>xpad</code> and add the required C files and Makefiles:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/omega2_kmods$ mkdir xpad &amp;&amp; cd xpad
max@max-VirtualBox:~/omega2_kmods/xpad$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/xpad$ mkdir src &amp;&amp; cd src
max@max-VirtualBox:~/omega2_kmods/xpad/src$ wget https://raw.githubusercontent.com/paroj/xpad/master/xpad.c
xpad.c     100%[==============================================&gt;]  61,24K   267KB/s    in 0,2s    
2018-04-08 13:03:33 (267 KB/s) - ‘xpad.c’ saved [62705/62705]
max@max-VirtualBox:~/omega2_kmods/xpad/src$ touch Makefile
</code></pre>
<p dir="auto"><code>Makefile</code>:</p>
<pre><code class="language-sh"># Copyright (C) 2006-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

# name
PKG_NAME:=xpad
# version of what we are downloading
PKG_VERSION:=1.1
# version of this makefile
PKG_RELEASE:=4

PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)
PKG_CHECK_FORMAT_SECURITY:=0

include $(INCLUDE_DIR)/package.mk

define KernelPackage/$(PKG_NAME)
	SUBMENU:=Input modules
	DEPENDS:=kmod-usb-core kmod-input-core
	TITLE:=XBox360-like gamepad support
	FILES:= $(PKG_BUILD_DIR)/xpad.ko
endef

define KernelPackage/$(PKG_NAME)/description
	Adds support for various XBox360-layout USB gamepads. Creates /dev/input/eventX devices. If kmod-input-joydev is loaded, also creates /dev/input/jsX device.
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef


MAKE_OPTS:= \
	ARCH="$(LINUX_KARCH)" \
	CROSS_COMPILE="$(TARGET_CROSS)" \
	SUBDIRS="$(PKG_BUILD_DIR)"

define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		$(MAKE_OPTS) \
		modules
endef

$(eval $(call KernelPackage,$(PKG_NAME)))
</code></pre>
<p dir="auto"><code>src/Makefile</code>:</p>
<pre><code>obj-m := xpad.o
</code></pre>
<p dir="auto">Update the feed and install new packages:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds update -a
Updating feed 'omega2_kmods' from '/home/max/omega2_kmods' ...
Create index file './feeds/omega2_kmods.index' 
Collecting package info: done
max@max-VirtualBox:~/source$ ./scripts/feeds install -a
Installing all packages from feed omega2_kmods.
Installing package 'xpad' from omega2_kmods
</code></pre>
<p dir="auto">Go into <code>make menuconfig</code> -&gt; <code>Kernel Modules</code> -&gt; <code>Input modules</code> and select <code>kmod-xpad</code> and <code>kmod-input-joydev</code> with "M".</p>
<p dir="auto"><img src="https://i.stack.imgur.com/EdM7C.png" alt="xpad" class="img-responsive img-markdown" /></p>
<p dir="auto">Exit and save, then <code>make</code>. Find the kernel modules <code>xpad.ko</code> and <code>joydev.ko</code> and transfer them to your Omega2.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make -j4
[..]
 make[3] -C /home/max/omega2_kmods/xpad compile
max@max-VirtualBox:~/source$ find . -name "xpad*" 
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/xpad.ko
max@max-VirtualBox:~/source$ find . -name "joydev*" 
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/joydev.ko
max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/xpad.ko root@192.168.1.202:/root/.
max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/ root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Let's load it in and look at the debug log when we plug it in now:</p>
<pre><code class="language-sh">root@Omega-17FD:~# insmod joydev.ko
root@Omega-17FD:~# insmod xpad.ko
root@Omega-17FD:~# dmesg
[..]
[16506.228539] usbcore: registered new interface driver xpad
[16539.408850] usb 2-1: new full-speed USB device number 9 using ohci-platform
[16539.663825] hid-generic 0003:046D:C22F.000B: hiddev0,hidraw0: USB HID v1.11 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input0
[16539.713896] hid-generic 0003:046D:C22F.000C: hiddev0,hidraw1: USB HID v1.11 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input1
[16540.890125] usb 2-1: USB disconnect, device number 9
[16541.708861] usb 2-1: new full-speed USB device number 10 using ohci-platform
[16541.941219] input: Logitech Gamepad F710 as /devices/platform/101c1000.ohci/usb2/2-1/2-1:1.0/input/input7
</code></pre>
<p dir="auto">Looks good. Let's examine <code>/dev/input</code>.</p>
<pre><code class="language-sh">root@Omega-17FD:~# ls /dev/input/
event0  js0
</code></pre>
<p dir="auto">Awesome :)! We have <code>/dev/input/js0</code> and <code>/dev/input/event0</code> now!</p>
<p dir="auto">Now we just need a small demo program to interact with the joystick device.  Let's compile <code>evtest.c</code>, which is a test program from to test <code>/dev/input/eventX</code> devices:</p>
<pre><code>max@max-VirtualBox:~/evtest$ wget https://raw.githubusercontent.com/georgeredinger/evtest/master/evtest.c
max@max-VirtualBox:~/evtest$ /home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/bin/mipsel-openwrt-linux-gcc evtest.c -o evtest
max@max-VirtualBox:~/evtest$ scp evtest root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Transfering the <code>evtest</code> to the Omega and executing it gives:</p>
<pre><code class="language-sh">root@Omega-17FD:~# ./evtest  /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0xc21f version 0x305
Input device name: "Logitech Gamepad F710"
Supported events:
  Event type 0 (Sync)
  Event type 1 (Key)
    Event code 304 (BtnA)
[...]
Testing ... (interrupt to exit)
Event: time 1523189709.996223, type 3 (Absolute), code 1 (Y), value 899
Event: time 1523189709.996223, -------------- Report Sync ------------
Event: time 1523189710.000224, type 3 (Absolute), code 1 (Y), value 1413
</code></pre>
<p dir="auto">This works and shows every gamepad event on the screen. Now lets use a library which uses the Joystick API.</p>
<p dir="auto">Let's use the library <code>libgamepad</code> made for Windows and Linux alike: <a href="https://github.com/elanthis/gamepad" rel="nofollow">https://github.com/elanthis/gamepad</a></p>
<p dir="auto">We download the <code>gamepad.c</code>, <code>main.c</code> and <code>gamepad.h</code> files in a new folder and modify the <code>Makefile</code> with which we cross-compile the program. Note that this uses <code>libncurses</code> and <code>libudev</code>. Select <code>libncurses</code> in the <code>menuconfig</code> and build. Getting <code>libudev</code> and <code>libevdev</code> is more complicated because you actually have to update to the newest LEDE feeds to select that package. Just download a binary version and from <a href="https://github.com/gamer-cndg/omega2-libs" rel="nofollow">here</a>. Also don't forget to install the dependencies on the Omega 2 by doin <code>opkg update &amp;&amp; opkg install libncurses libudev kmod-input-evdev </code>. Download the <code>Makefile</code> from <a href="https://github.com/gamer-cndg/gamepad/blob/master/Makefile" rel="nofollow">here</a>.</p>
<p dir="auto">Building gives you <code>libgamepad.so</code> (with symlink to <code>libgamepad.so.1</code>) and <code>gamepadtest</code>. Transfer them and <code>libevdev.so.2</code> to <code>/usr/lib</code> and <code>/root</code> respectively.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/gamepadlib$ scp libevdev.so.2 root@192.168.1.202:/usr/lib/.
max@max-VirtualBox:~/gamepadlib$ scp libgamepad.so root@192.168.1.202:/usr/lib/libgamepad.so.1
max@max-VirtualBox:~/gamepadlib$ scp gamepadtest root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Executing the program gives you:</p>
<p dir="auto"><img src="https://i.stack.imgur.com/CHVLO.png" alt="workinggamepad" class="img-responsive img-markdown" /></p>
<p dir="auto">Joystick 0 is correctly detected and all sticks and buttons are working flawlessly! Now we can work with this library to program an application that uses an Xbox360 USB gamepad.</p>
<p dir="auto">This concludes the practical part of the tutorial.</p>
<h3>Examples and references</h3>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/1033">@luz</a> has a repository for self-built packages and kernel modules at <a href="https://github.com/plan44/plan44-feed" rel="nofollow">https://github.com/plan44/plan44-feed</a>. The Makefile are very useful.</p>
<p dir="auto">Resources used in this tutorial:</p>
<ul>
<li><a href="https://wiki.archlinux.org/index.php/Kernel_module" rel="nofollow">https://wiki.archlinux.org/index.php/Kernel_module</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/feeds" rel="nofollow">https://wiki.openwrt.org/doc/devel/feeds</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/packages" rel="nofollow">https://wiki.openwrt.org/doc/devel/packages</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/dependencies" rel="nofollow">https://wiki.openwrt.org/doc/devel/dependencies</a></li>
<li><a href="https://www.tldp.org/LDP/lkmpg/2.6/html/" rel="nofollow">https://www.tldp.org/LDP/lkmpg/2.6/html/</a></li>
<li><a href="https://wiki.archlinux.org/index.php/Gamepad#Joystick_input_systems" rel="nofollow">https://wiki.archlinux.org/index.php/Gamepad#Joystick_input_systems</a></li>
<li><a href="https://github.com/OnionIoT/source" rel="nofollow">https://github.com/OnionIoT/source</a></li>
<li><a href="https://docs.onion.io/omega2-docs/cross-compiling.html" rel="nofollow">https://docs.onion.io/omega2-docs/cross-compiling.html</a></li>
<li><a href="http://repo.onion.io/omega2/packages/" rel="nofollow">http://repo.onion.io/omega2/packages/</a></li>
<li><a href="https://github.com/paroj/xpad" rel="nofollow">https://github.com/paroj/xpad</a></li>
<li><a href="https://elinux.org/images/9/93/Evtest.c" rel="nofollow">https://elinux.org/images/9/93/Evtest.c</a></li>
<li><a href="https://labs.mediatek.com/en/chipset/MT7688" rel="nofollow">https://labs.mediatek.com/en/chipset/MT7688</a></li>
</ul>
<p dir="auto">My Github repository with example code and libraries:</p>
<ul>
<li><a href="https://github.com/gamer-cndg/omega2-kernel-modules" rel="nofollow">https://github.com/gamer-cndg/omega2-kernel-modules</a></li>
<li><a href="https://github.com/gamer-cndg/omega2-libs" rel="nofollow">https://github.com/gamer-cndg/omega2-libs</a></li>
<li><a href="https://github.com/gamer-cndg/gamepad" rel="nofollow">https://github.com/gamer-cndg/gamepad</a></li>
</ul>
<h3>Conclusion</h3>
<p dir="auto">This tutorial showed you what kernel modules are, why we might need them and how to build them. We built a 'hello world' minimal kernel module for a start, then expanded the code to read a hardware regster. Finally, we built <code>kmod-input-joydev</code> and <code>xpad</code> to get a USB Xbox360 gamepad working. We then compiled a test program and a library to confirm that we gamepad works.</p>
]]></description><link>http://community.onion.io/topic/2830/building-kernel-modules-for-the-omega2</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 16:16:36 GMT</lastBuildDate><atom:link href="http://community.onion.io/topic/2830.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 08 Apr 2018 12:54:28 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Mon, 09 Apr 2018 19:44:00 GMT]]></title><description><![CDATA[<p dir="auto">During a new project of mine, I needed a new kernel module for a device driver. I saw that there wasn't yet a condensed write-up on how to manage, compile and run your own kernel modules for the Omega2. So I decided to write one as a side-project.  Here it is.</p>
<p dir="auto">Update 09.04.2018: Contains new section about reading hardware registers.</p>
<h1>Building Kernel Modules for the Omega2</h1>
<h3>Introduction</h3>
<p dir="auto">This is a tutorial on how to build and run Linux kernel modules (<code>.ko</code> files) for the Omega2.</p>
<p dir="auto">There are several related topics in this forum (e.g. <a href="https://community.onion.io/topic/2054/how-to-get-additional-kernel-modules" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/1005/request-for-prebuild-kernel-modules" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/921/kernel-driver-version-mismatch" rel="nofollow">here</a>, <a href="https://community.onion.io/topic/1756/request-kernel-module-kmod-cryptodev" rel="nofollow">here</a>). However I didn't find any conclusive step-by-step tutorial on here that explains how to build a kernel module from the ground up. This is why I've created this tutorial to share what I've learned over the past few days which led me to a functioning kernel module compilation.</p>
<h3>Prerequisites</h3>
<p dir="auto">This tutorial is working with the newest firwmare version <code>b178</code>, kernel version <code>4.4.74</code>. You should also have <code>kmod</code>. Do <code>opkg update &amp;&amp; opkg install kmod</code> if it isn't already there. Other prerequisites will be installed during the tutorial.</p>
<h3>Kernel modules</h3>
<p dir="auto">What are <a href="https://wiki.archlinux.org/index.php/Kernel_module" rel="nofollow">kernel modules</a> and why should you care?</p>
<blockquote>
<p dir="auto">Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system.</p>
</blockquote>
<p dir="auto">Things like USB drivers / systems, interface drivers, input drivers, can be distributed in the form of the kernel modules. A kernel module also runs with higher priviledges (kernel space vs user space). You may only have direct hardware register access to e.g. GPIO, PWM, SPI, etc. from there. Kernel modules then expose certain functionality or device files to the user space. Examples of kernel modules are:</p>
<ul>
<li><code>kmod-spi-dev</code> - exposes linux SPI device</li>
<li><code>kmod-fs-ext4</code> - EXT4 filesystem support</li>
<li><code>kmod-usb-audio</code> - Audio over USB devices</li>
<li><code>kmod-bluetooth</code> - Bluetooth support</li>
</ul>
<p dir="auto">You can look at your loaded kernel modules by executing <code>lsmod</code> and get more info on a module by executing <code>modinfo &lt;module name&gt;</code>. The <code>.ko</code> files are stored in <code>/lib/modules/4.4.74</code>.</p>
<pre><code class="language-sh">root@Omega-17FD:~# lsmod
aead                    3489  0
bluetooth             258081  8 rfcomm,hidp,hci_uart,btusb,btintel,bnep
ext4                  319010  1
..
root@Omega-17FD:~# modinfo bluetooth
module:         /lib/modules/4.4.74/bluetooth.ko
alias:          net-pf-31
license:        GPL
depends:        crc16

root@Omega-17FD:/lib/modules/4.4.74# ls -l
-rw-r--r--    1 root     root          7192 Apr  2 23:19 aead.ko
-rw-r--r--    1 root     root         28520 Apr  2 23:19 autofs4.ko
-rw-r--r--    1 root     root        325244 Apr  2 23:19 bluetooth.ko
...
</code></pre>
<p dir="auto">For a project you might need a kernel module that is not included or distributed for the Omega2. As a first reference, you should look into Onion's <a href="http://repo.onion.io/omega2/packages/" rel="nofollow">package repository</a>. This tutorial is for the case when you don't find your wanted module there or you want to build something new (e.g., peripheral drivers, ..).</p>
<h3>Understanding the LEDE build system for kernel modules</h3>
<p dir="auto">Where can we see what stock kernel modules and libraries we can build? For this, you need the cross-compilation environment first. This includes cloning Onion's <code>source</code> <a href="https://github.com/OnionIoT/source" rel="nofollow">repository</a> for LEDE-17.01 and building it. A tutorial can be found <a href="https://docs.onion.io/omega2-docs/cross-compiling.html" rel="nofollow">here</a>. You should also have a basic idea on <a href="https://onion.io/2bt-cross-compiling-programs/" rel="nofollow">how to cross-compile a program</a> for the Omega2. I built the toolchain within a Xubuntu VM.</p>
<p dir="auto">When we execute <code>make menuconfig</code> in the <code>source</code> folder, we get a menu where we can configure stuff:</p>
<p dir="auto"><img src="https://i.stack.imgur.com/HbK2O.png" alt="kernelconfig" class="img-responsive img-markdown" /></p>
<p dir="auto">We can select a kernel module there and mark it with "M" -- meaning that this will build into a kernel module file. After exiting and saving the configuration you can build the module by executing <code>make</code> (with optional <code>-j4</code> for 4-core multithreading).</p>
<p dir="auto">Where do these packages come from? They come from the package feed. The sources are defined in the <code>feeds.conf</code> and <code>feeds.conf.default</code> file.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ cat feeds.conf
src-git packages https://git.lede-project.org/feed/packages.git;lede-17.01
src-git luci https://git.lede-project.org/project/luci.git;lede-17.01
src-git routing https://git.lede-project.org/feed/routing.git;lede-17.01
src-git telephony https://git.lede-project.org/feed/telephony.git;lede-17.01
src-git onion https://github.com/OnionIoT/OpenWRT-Packages.git
</code></pre>
<p dir="auto">So they are just a bunch of links to git repositories where packages are stored. You can visit the links to see what's inside them.</p>
<p dir="auto">There are two main steps for adding a new kernel module package:</p>
<ul>
<li>Update the index files in <code>feeds/</code> of available packages defined in <code>feeds.conf</code></li>
<li>Install symlinks to the packages in <code>package/feeds/</code> so they become available in <code>make menuconfig</code></li>
</ul>
<p dir="auto">There are scripts inside the <code>script/</code> folder to help you manage your feeds.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds -h
Usage: ./scripts/feeds &lt;command&gt; [options]

Commands:
	list [options]: List feeds, their content and revisions (if installed)
	Options:
	    -n :            List of feed names.
...
</code></pre>
<p dir="auto">As described in Onion's repository <a href="https://github.com/OnionIoT/source#lede-linux-distribution" rel="nofollow">README</a>:</p>
<blockquote>
<p dir="auto">Run <code>./scripts/feeds update -a</code> to get all the latest package definitions defined in <code>feeds.conf</code> / <code>feeds.conf.default</code> respectively and <code>./scripts/feeds install -a</code> to install symlinks of all of them into <code>package/feeds/</code>.</p>
</blockquote>
<p dir="auto">More specifically, you can also selectively update and install feeds and packages by using:</p>
<ul>
<li><code>./scripts/feeds update -i</code> to recreate the index file</li>
<li><code>./scripts/feeds update &lt;feed name&gt;</code> to update only one feed</li>
<li><code>./scripts/feeds install &lt;package name&gt;</code> to install only one package</li>
</ul>
<h3>Let's build a 'hello world' kernel module!</h3>
<p dir="auto">Time to build the first actual kernel module.</p>
<p dir="auto">For security reasons you should first comment out every other feed in your <code>feeds.conf</code> and <code>feeds.conf.default</code> to prevent accidently updating everything (and to a new kernel version), using the <code>#</code> character.</p>
<p dir="auto">We will create a new <code>src-link</code> type feed using a folder on our host system as described <a href="https://wiki.openwrt.org/doc/devel/feeds" rel="nofollow">here</a>. We will take code and modified Makefiles from a tutorial <a href="https://www.tldp.org/LDP/lkmpg/2.6/html/hello2.html" rel="nofollow">here</a>.</p>
<pre><code class="language-sh">max@max-VirtualBox:~$ mkdir omega2_kmods &amp;&amp; cd omega2_kmods/
max@max-VirtualBox:~/omega2_kmods$ mkdir hello-world &amp;&amp; cd hello-world/
max@max-VirtualBox:~/omega2_kmods/hello-world$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/hello-world$ mkdir src &amp;&amp; cd src
max@max-VirtualBox:~/omega2_kmods/hello-world/src$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/hello-world/src$ touch hello-world.c
</code></pre>
<p dir="auto">Fill in the contents of the files:</p>
<p dir="auto"><code>src/hello-world.c</code>:</p>
<pre><code class="language-c">#include &lt;linux/module.h&gt;	/* Needed by all modules */
#include &lt;linux/kernel.h&gt;	/* Needed for KERN_INFO */
#include &lt;linux/init.h&gt;		/* Needed for the macros */

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maximilian Gerhardt");
MODULE_DESCRIPTION("Basic kernel module for tutorial");

static int __init hello_2_init(void)
{
	printk(KERN_INFO "Hello, world! We're inside the Omega2's kernel.\n");
	return 0;
}

static void __exit hello_2_exit(void)
{
	printk(KERN_INFO "Goodbye, world!\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);
</code></pre>
<p dir="auto"><code>Makefile</code>:</p>
<pre><code class="language-Makefile"># Copyright (C) 2006-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

# name
PKG_NAME:=hello-world
# version of what we are downloading
PKG_VERSION:=1.1
# version of this makefile
PKG_RELEASE:=4

PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)
PKG_CHECK_FORMAT_SECURITY:=0

include $(INCLUDE_DIR)/package.mk

define KernelPackage/$(PKG_NAME)
	SUBMENU:=Other modules
	DEPENDS:=@(TARGET_ramips_mt76x8||TARGET_ramips_mt7688)
	TITLE:=Hello world kernel module
	FILES:= $(PKG_BUILD_DIR)/hello-world.ko
endef

define KernelPackage/$(PKG_NAME)/description
	A "hello world" test module for a tutorial.
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

MAKE_OPTS:= \
	ARCH="$(LINUX_KARCH)" \
	CROSS_COMPILE="$(TARGET_CROSS)" \
	SUBDIRS="$(PKG_BUILD_DIR)"

define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		$(MAKE_OPTS) \
		modules
endef

$(eval $(call KernelPackage,$(PKG_NAME)))
</code></pre>
<p dir="auto"><code>src/Makefile</code>:</p>
<pre><code class="language-Makefile">obj-m := hello-world.o
</code></pre>
<p dir="auto">The top-level <code>Makefile</code> declares how to build our kernel module package. You declare the name, its files, a description, dependencies and other things there. See the documentation links at the bottom of this tutorial. The <code>src/Makefile</code> then adds all to-be compiled object files to the build process.</p>
<p dir="auto">Add a line to your <code>feeds.conf</code> declaring the new feed and the path:</p>
<pre><code>src-link omega2_kmods /home/max/omega2_kmods
</code></pre>
<p dir="auto">Update this new feed:</p>
<pre><code>max@max-VirtualBox:~/source$ ./scripts/feeds update omega2_kmods
Updating feed 'omega2_kmods' from '/home/max/omega2_kmods' ...
Create index file './feeds/omega2_kmods.index' 
Collecting package info: done
Collecting target info: done
</code></pre>
<p dir="auto">Install the new <code>kmod-hello-world</code> package. Here we just install everything:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds install -a
Installing all packages from feed omega2_kmods.
Installing package 'hello-world' from omega2_kmods
</code></pre>
<p dir="auto">The new package is installed. You can list all available modules for a feed by executing:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds list -r omega2_kmods
kmod-hello-world                	Hello world kernel module
</code></pre>
<p dir="auto">Execute <code>make menuconfig</code> and navigate to <code>Kernel Modules</code> -&gt; <code>Other Modules</code> (this submenu is declared in the <code>Makefile</code><img src="http://community.onion.io/plugins/nodebb-plugin-emoji/emoji/android/1f61e.png?v=ic093v0mjao" class="not-responsive emoji emoji-android emoji--disappointed" title="):" alt="😞" /></p>
<p dir="auto"><img src="https://i.stack.imgur.com/Y8yg6.png" alt="newmod" class="img-responsive img-markdown" /></p>
<p dir="auto">We see our new kernel module! Navigate to it and press <code>M</code> to mark it for compilation to a kernel module. Exit and save.</p>
<p dir="auto">Now build everything:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make -j4
 make[1] world
 make[2] package/cleanup
 make[2] target/compile
 make[3] -C target/linux compile
 make[2] package/compile
 make[3] -C package/libs/ncurses host-compile
..
 make[3] -C /home/max/omega2_kmods/hello-world compile
..
</code></pre>
<p dir="auto">You can also choose to just compile your module:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j4
 make[1] package/hello-world/compile
 make[2] -C /home/max/omega2_kmods/hello-world compile
</code></pre>
<p dir="auto">Either way, you should now have a <code>hello-world.ko</code>. Search for it:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ find . -name "*hello-world*"
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/hello-world.ko
./staging_dir/packages/ramips/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
./bin/targets/ramips/mt7688/packages/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
./build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.ko
</code></pre>
<p dir="auto">It conveniently compiled the <code>hello-world.ko</code> file and even created an <code>ipk</code> package. Unfortunetly, trying to installing that package will give you a kernel mismatch error because the kernel hashes differ (see topic links at start). Loading the <code>.ko</code> file directly will still work though.</p>
<p dir="auto">Transfer the file to your Omega2 over SSH/SCP.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/hello-world.ko root@192.168.1.202:/root/.
root@192.168.1.202's password: 
hello-world.ko                                                          100% 2504     2.5KB/s   00:00    
</code></pre>
<p dir="auto">SSH to your Omega2 and insert the module.</p>
<pre><code class="language-sh">root@Omega-17FD:~# insmod hello-world.ko
</code></pre>
<p dir="auto">The module should now be loaded and should have printed the messge to the debug kernel log:</p>
<pre><code class="language-sh">root@Omega-17FD:~# lsmod | grep "hello"
hello_world              592  0
root@Omega-17FD:~# dmesg
[...]
[11813.120524] Hello, world! We're inside the Omega2's kernel.
</code></pre>
<p dir="auto">Success! The kernel module was loaded and has executed the module init function successfully!</p>
<p dir="auto">You can unload the module by doing <code>rmmod</code>. This should trigger the module's exit/cleanup function:</p>
<pre><code class="language-sh">root@Omega-17FD:~# rmmod hello_world
root@Omega-17FD:~# dmesg
[..]
[11813.120524] Hello, world! We're inside the Omega2's kernel.
[11957.855870] Goodbye, world!
</code></pre>
<p dir="auto">Works as expected.</p>
<p dir="auto">Note that <code>modinfo</code> only seems to work when the module is inside the <code>/lib/modues/4.4.71</code> folder:</p>
<pre><code class="language-sh">root@Omega-17FD:~# cp hello-world.ko /lib/modules/4.4.74/.
root@Omega-17FD:~# modinfo hello_world
module:         /lib/modules/4.4.74/hello-world.ko
license:        GPL
depends:
</code></pre>
<p dir="auto">At the end of this section you were now able to compile a simple kernel module from scratch and load it into the Omega2.</p>
<h3>Let's read a hardware register!</h3>
<p dir="auto">For a quick additional demo, we can modify the code of the <code>hello-world</code> kernel module to read a register from the Omega2's CPU, which is a MT7688. For a reference, you will need the Mediatek MT7688 datasheet: <a href="https://labs.mediatek.com/en/chipset/MT7688" rel="nofollow">https://labs.mediatek.com/en/chipset/MT7688</a></p>
<p dir="auto">Starting at page 48, the register addresses and meanings are listed in their respective blocks (system config, timer, GPIO, SPI,..). Let's have a look at the <code>SYSCTL</code> block.</p>
<p dir="auto"><img src="https://i.stack.imgur.com/RaWWU.png" alt="datasheet" class="img-responsive img-markdown" /></p>
<p dir="auto">Starting at address <code>0x10000000</code>, there are several interesting registers we can read out. Let's read out <code>CHIPID0_3</code> and <code>CHIPID4_7</code>, which contains the ASCII chip name in these two 32-bit registers.</p>
<p dir="auto">If you were programming a microcontroller without a OS and memory management unit (MMU), you would just set a C pointer variable to that address and start reading from it. However, to read data from this physical memory address in the kernel, we first have to do a memory re-mapping. In an OS, you mostly deal with "virtual addresses", which are remapped by an MMU to real physical address within a memory chip. If we just set a C-pointer to <code>0x10000000</code> and read it, we would read this virtual address, which is no good. Virtual addresses are a whole topic on its own. You should consult a lecture on operating systems if you want to know more about this.</p>
<p dir="auto">The kernel provides in <code>linux/io.h</code> functions with which we can accomplish this:</p>
<ul>
<li><code>void* ioremap(address, size)</code> will return a pointer to the virtual memory which is now mapped to the physical address <code>address</code>. A total of <code>size</code> bytes are mapped to this location</li>
<li><code>ioread32(mem)</code>/<code>ioread16(mem)</code>/<code>ioread8(mem)</code> Reads 32/16/8 bits from specified I/O memory, with pointer previously acquired by <code>ioremap</code></li>
<li><code>iowrite32</code>/<code>iowrite16</code>/<code>iowrite8</code> writes value to memory</li>
<li><code>iounmap(mem)</code> for unmapping when you're done</li>
</ul>
<p dir="auto">Knowing these function and addresses, we can read  <code>CHIPID0_3</code>,  <code>CHIPID4_7</code> and <code>CHIP_REV_ID</code> using the following modified <code>hello-world.c</code> code:</p>
<pre><code class="language-c">#include &lt;linux/module.h&gt;	/* Needed by all modules */
#include &lt;linux/kernel.h&gt;	/* Needed for KERN_INFO */
#include &lt;linux/init.h&gt;		/* Needed for the macros */
#include &lt;linux/io.h&gt;		/* Needed for I/O operations */

/* Addresses of base registers and offsets for certain fields (see datasheet) */
#define MT7688_SYSCTL_BASE			0x10000000
#define MT7688_SYSCTL_CHIPID0_3		0x0
#define MT7688_SYSCTL_CHIPID4_7		0x4
#define MT7688_SYSCTL_CHIP_REV_ID	0xC

/* Physical address in, 32 bit integer out. */
static unsigned int ReadReg32(unsigned long addr) {
	//Remap 4 bytes starting at that address
	void* virtualMem = ioremap(addr, 4);
	if(!virtualMem) {
		printk(KERN_WARNING "Failed to remap register memory");
		return 0;
	}
	//Read 32 bits
	unsigned int val = ioread32(virtualMem);
	printk(KERN_INFO "ReadReg32(0x%08x) = 0x%08x\n", (int)addr, val);
	//Unmap memory again
	iounmap(virtualMem);
	//return read value
	return val;
}

static int __init hello_2_init(void)
{
	printk(KERN_INFO "Hello, world! We're inside the Omega2's kernel.\n");

	unsigned int chipid0_3 = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIPID0_3);
	//Recover the initial characters
	//Datasheet says leftmost bits is the last character
	char id_0 = (char)(chipid0_3 &gt;&gt; 0);
	char id_1 = (char)(chipid0_3 &gt;&gt; 8);
	char id_2 = (char)(chipid0_3 &gt;&gt; 16);
	char id_3 = (char)(chipid0_3 &gt;&gt; 24);
	//Do the same for chipid 4 to 7
	unsigned int chipid4_7 = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIPID4_7);
	char id_4 = (char)(chipid4_7 &gt;&gt; 0);
	char id_5 = (char)(chipid4_7 &gt;&gt; 8);
	char id_6 = (char)(chipid4_7 &gt;&gt; 16);
	char id_7 = (char)(chipid4_7 &gt;&gt; 24);

	//Get revision ID
	unsigned int chiprev = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIP_REV_ID);
	//Parse the bits out of the structure
	int packageId = chiprev &gt;&gt; 16;
	int verId = (chiprev &gt;&gt; 8) &amp; 0xf;
	int ecoId = chiprev &amp; 0xf;

	printk(KERN_INFO "CHIP ID IS: '%c%c%c%c%c%c%c%c'\n",
			id_0, id_1, id_2, id_3, id_4, id_5, id_6, id_7);
	printk(KERN_INFO "CHIP REV ID: PKG_ID=%d, VER_ID=%d, ECO_ID=%d\n",
			packageId, verId, ecoId);

	return 0;
}

static void __exit hello_2_exit(void)
{
	printk(KERN_INFO "Goodbye, world!\n");
}

module_init(hello_2_init);
module_exit(hello_2_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maximilian Gerhardt");
MODULE_DESCRIPTION("Basic kernel module for tutorial with register reading");
</code></pre>
<p dir="auto">Compiling this and loading it into the kernel results in:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j4 &amp;&amp; scp ./build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.ko root@192.168.1.202:/root/.

root@Omega-17FD:~# insmod hello-world.ko
root@Omega-17FD:~# dmesg
[ 8402.279877] Hello, world! We're inside the Omega2's kernel.
[ 8402.285635] ReadReg32(0x10000000) = 0x3637544d
[ 8402.290142] ReadReg32(0x10000004) = 0x20203832
[ 8402.294669] ReadReg32(0x1000000c) = 0x00010102
[ 8402.299175] CHIP ID IS: 'MT7628  '
[ 8402.302623] CHIP REV ID: PKG_ID=1, VER_ID=1, ECO_ID=2
</code></pre>
<p dir="auto">Great, we've read out the chip ID as "MT7628", just as the datasheet said. Convert the binary string from the datasheet to ASCII to confirm this. We also read out the expected values for the <code>CHIP_REV_ID</code> fields.</p>
<p dir="auto">This simple example showed you how to read a hardware register as specified in the MT7688's datasheet from the kernel space.</p>
<h3>Let's build a XBox360 USB gamepad driver!</h3>
<p dir="auto">Now let's do some more serious stuff.</p>
<p dir="auto">I have this Logitech Wireless Gamepad F710 laying around here.</p>
<p dir="auto"><img src="https://i.stack.imgur.com/Pzdt0.png" alt="gamepad" class="img-responsive img-markdown" /></p>
<p dir="auto">Let's plug in into a PC running Linux and look at <code>dmesg</code>:</p>
<pre><code class="language-sh">[13688.633245] usb 2-2: new full-speed USB device number 3 using ohci-pci
[13689.164936] usb 2-2: New USB device found, idVendor=046d, idProduct=c21f
[13689.164939] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13689.164940] usb 2-2: Product: Wireless Gamepad F710
[13689.164941] usb 2-2: Manufacturer: Logitech
[13689.164942] usb 2-2: SerialNumber: ADED9351
[13690.978059] input: Logitech Gamepad F710 as /devices/pci0000:00/0000:00:06.0/usb2/2-2/2-2:1.0/input/input8
[13690.978517] usbcore: registered new interface driver xpad
max@max-VirtualBox:~/source$ ls /dev/input/
event0  event1  js0   mouse0 
</code></pre>
<p dir="auto">The USB device is detected, the  driver <code>xpad</code> takes over and creates new device files <code>/dev/input/event1</code> and <code>/dev/input/js0</code> (Joystick #0).  These are device files for the event-based API (new) and the older Linux Joystick API.</p>
<p dir="auto">What happens when we plug this into the Omega2?</p>
<pre><code class="language-sh">root@Omega-17FD:~# logread -f
[14602.640692] usb 2-1: new full-speed USB device number 7 using ohci-platform
[14602.889249] hid-generic 0003:046D:C22F.0009: hiddev0,hidraw0: USB HID v1.1 1 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input0
[14602.941098] hid-generic 0003:046D:C22F.000A: hiddev0,hidraw1: USB HID v1.1 1 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input1
</code></pre>
<p dir="auto">The USB device is detected as a Human Interface Device (HID). However, you will notice that there are no <code>/dev/input/*</code> devices:</p>
<pre><code class="language-sh">root@Omega-17FD:~# ls /dev/input/
root@Omega-17FD:~#
</code></pre>
<p dir="auto">We can't really use the device this way. We're missing the <code>xpad</code> driver here, as well es the entire <code>kmod-input-joydev</code> package for the Joystick API.</p>
<p dir="auto">Luckily we can use our newly gained knowledge about kernel modules to get this thing working. A search for "linux xpad driver" leads to <a href="https://github.com/paroj/xpad" rel="nofollow">https://github.com/paroj/xpad</a> and <a href="https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c" rel="nofollow">https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c</a>. I will use the code in the first repository, but the code for the newer Linux kernel should also work and support even more devices (206 of them).</p>
<p dir="auto">Following up on the structure of our first kernel module, let's create a new package folder <code>xpad</code> and add the required C files and Makefiles:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/omega2_kmods$ mkdir xpad &amp;&amp; cd xpad
max@max-VirtualBox:~/omega2_kmods/xpad$ touch Makefile
max@max-VirtualBox:~/omega2_kmods/xpad$ mkdir src &amp;&amp; cd src
max@max-VirtualBox:~/omega2_kmods/xpad/src$ wget https://raw.githubusercontent.com/paroj/xpad/master/xpad.c
xpad.c     100%[==============================================&gt;]  61,24K   267KB/s    in 0,2s    
2018-04-08 13:03:33 (267 KB/s) - ‘xpad.c’ saved [62705/62705]
max@max-VirtualBox:~/omega2_kmods/xpad/src$ touch Makefile
</code></pre>
<p dir="auto"><code>Makefile</code>:</p>
<pre><code class="language-sh"># Copyright (C) 2006-2012 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

# name
PKG_NAME:=xpad
# version of what we are downloading
PKG_VERSION:=1.1
# version of this makefile
PKG_RELEASE:=4

PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)
PKG_CHECK_FORMAT_SECURITY:=0

include $(INCLUDE_DIR)/package.mk

define KernelPackage/$(PKG_NAME)
	SUBMENU:=Input modules
	DEPENDS:=kmod-usb-core kmod-input-core
	TITLE:=XBox360-like gamepad support
	FILES:= $(PKG_BUILD_DIR)/xpad.ko
endef

define KernelPackage/$(PKG_NAME)/description
	Adds support for various XBox360-layout USB gamepads. Creates /dev/input/eventX devices. If kmod-input-joydev is loaded, also creates /dev/input/jsX device.
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef


MAKE_OPTS:= \
	ARCH="$(LINUX_KARCH)" \
	CROSS_COMPILE="$(TARGET_CROSS)" \
	SUBDIRS="$(PKG_BUILD_DIR)"

define Build/Compile
	$(MAKE) -C "$(LINUX_DIR)" \
		$(MAKE_OPTS) \
		modules
endef

$(eval $(call KernelPackage,$(PKG_NAME)))
</code></pre>
<p dir="auto"><code>src/Makefile</code>:</p>
<pre><code>obj-m := xpad.o
</code></pre>
<p dir="auto">Update the feed and install new packages:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ ./scripts/feeds update -a
Updating feed 'omega2_kmods' from '/home/max/omega2_kmods' ...
Create index file './feeds/omega2_kmods.index' 
Collecting package info: done
max@max-VirtualBox:~/source$ ./scripts/feeds install -a
Installing all packages from feed omega2_kmods.
Installing package 'xpad' from omega2_kmods
</code></pre>
<p dir="auto">Go into <code>make menuconfig</code> -&gt; <code>Kernel Modules</code> -&gt; <code>Input modules</code> and select <code>kmod-xpad</code> and <code>kmod-input-joydev</code> with "M".</p>
<p dir="auto"><img src="https://i.stack.imgur.com/EdM7C.png" alt="xpad" class="img-responsive img-markdown" /></p>
<p dir="auto">Exit and save, then <code>make</code>. Find the kernel modules <code>xpad.ko</code> and <code>joydev.ko</code> and transfer them to your Omega2.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make -j4
[..]
 make[3] -C /home/max/omega2_kmods/xpad compile
max@max-VirtualBox:~/source$ find . -name "xpad*" 
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/xpad.ko
max@max-VirtualBox:~/source$ find . -name "joydev*" 
./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/joydev.ko
max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/xpad.ko root@192.168.1.202:/root/.
max@max-VirtualBox:~/source$ scp ./staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/lib/modules/4.4.74/ root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Let's load it in and look at the debug log when we plug it in now:</p>
<pre><code class="language-sh">root@Omega-17FD:~# insmod joydev.ko
root@Omega-17FD:~# insmod xpad.ko
root@Omega-17FD:~# dmesg
[..]
[16506.228539] usbcore: registered new interface driver xpad
[16539.408850] usb 2-1: new full-speed USB device number 9 using ohci-platform
[16539.663825] hid-generic 0003:046D:C22F.000B: hiddev0,hidraw0: USB HID v1.11 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input0
[16539.713896] hid-generic 0003:046D:C22F.000C: hiddev0,hidraw1: USB HID v1.11 Device [Logitech Logitech Cordless RumblePad 2] on usb-101c1000.ohci-1/input1
[16540.890125] usb 2-1: USB disconnect, device number 9
[16541.708861] usb 2-1: new full-speed USB device number 10 using ohci-platform
[16541.941219] input: Logitech Gamepad F710 as /devices/platform/101c1000.ohci/usb2/2-1/2-1:1.0/input/input7
</code></pre>
<p dir="auto">Looks good. Let's examine <code>/dev/input</code>.</p>
<pre><code class="language-sh">root@Omega-17FD:~# ls /dev/input/
event0  js0
</code></pre>
<p dir="auto">Awesome :)! We have <code>/dev/input/js0</code> and <code>/dev/input/event0</code> now!</p>
<p dir="auto">Now we just need a small demo program to interact with the joystick device.  Let's compile <code>evtest.c</code>, which is a test program from to test <code>/dev/input/eventX</code> devices:</p>
<pre><code>max@max-VirtualBox:~/evtest$ wget https://raw.githubusercontent.com/georgeredinger/evtest/master/evtest.c
max@max-VirtualBox:~/evtest$ /home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/bin/mipsel-openwrt-linux-gcc evtest.c -o evtest
max@max-VirtualBox:~/evtest$ scp evtest root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Transfering the <code>evtest</code> to the Omega and executing it gives:</p>
<pre><code class="language-sh">root@Omega-17FD:~# ./evtest  /dev/input/event0
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x46d product 0xc21f version 0x305
Input device name: "Logitech Gamepad F710"
Supported events:
  Event type 0 (Sync)
  Event type 1 (Key)
    Event code 304 (BtnA)
[...]
Testing ... (interrupt to exit)
Event: time 1523189709.996223, type 3 (Absolute), code 1 (Y), value 899
Event: time 1523189709.996223, -------------- Report Sync ------------
Event: time 1523189710.000224, type 3 (Absolute), code 1 (Y), value 1413
</code></pre>
<p dir="auto">This works and shows every gamepad event on the screen. Now lets use a library which uses the Joystick API.</p>
<p dir="auto">Let's use the library <code>libgamepad</code> made for Windows and Linux alike: <a href="https://github.com/elanthis/gamepad" rel="nofollow">https://github.com/elanthis/gamepad</a></p>
<p dir="auto">We download the <code>gamepad.c</code>, <code>main.c</code> and <code>gamepad.h</code> files in a new folder and modify the <code>Makefile</code> with which we cross-compile the program. Note that this uses <code>libncurses</code> and <code>libudev</code>. Select <code>libncurses</code> in the <code>menuconfig</code> and build. Getting <code>libudev</code> and <code>libevdev</code> is more complicated because you actually have to update to the newest LEDE feeds to select that package. Just download a binary version and from <a href="https://github.com/gamer-cndg/omega2-libs" rel="nofollow">here</a>. Also don't forget to install the dependencies on the Omega 2 by doin <code>opkg update &amp;&amp; opkg install libncurses libudev kmod-input-evdev </code>. Download the <code>Makefile</code> from <a href="https://github.com/gamer-cndg/gamepad/blob/master/Makefile" rel="nofollow">here</a>.</p>
<p dir="auto">Building gives you <code>libgamepad.so</code> (with symlink to <code>libgamepad.so.1</code>) and <code>gamepadtest</code>. Transfer them and <code>libevdev.so.2</code> to <code>/usr/lib</code> and <code>/root</code> respectively.</p>
<pre><code class="language-sh">max@max-VirtualBox:~/gamepadlib$ scp libevdev.so.2 root@192.168.1.202:/usr/lib/.
max@max-VirtualBox:~/gamepadlib$ scp libgamepad.so root@192.168.1.202:/usr/lib/libgamepad.so.1
max@max-VirtualBox:~/gamepadlib$ scp gamepadtest root@192.168.1.202:/root/.
</code></pre>
<p dir="auto">Executing the program gives you:</p>
<p dir="auto"><img src="https://i.stack.imgur.com/CHVLO.png" alt="workinggamepad" class="img-responsive img-markdown" /></p>
<p dir="auto">Joystick 0 is correctly detected and all sticks and buttons are working flawlessly! Now we can work with this library to program an application that uses an Xbox360 USB gamepad.</p>
<p dir="auto">This concludes the practical part of the tutorial.</p>
<h3>Examples and references</h3>
<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/1033">@luz</a> has a repository for self-built packages and kernel modules at <a href="https://github.com/plan44/plan44-feed" rel="nofollow">https://github.com/plan44/plan44-feed</a>. The Makefile are very useful.</p>
<p dir="auto">Resources used in this tutorial:</p>
<ul>
<li><a href="https://wiki.archlinux.org/index.php/Kernel_module" rel="nofollow">https://wiki.archlinux.org/index.php/Kernel_module</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/feeds" rel="nofollow">https://wiki.openwrt.org/doc/devel/feeds</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/packages" rel="nofollow">https://wiki.openwrt.org/doc/devel/packages</a></li>
<li><a href="https://wiki.openwrt.org/doc/devel/dependencies" rel="nofollow">https://wiki.openwrt.org/doc/devel/dependencies</a></li>
<li><a href="https://www.tldp.org/LDP/lkmpg/2.6/html/" rel="nofollow">https://www.tldp.org/LDP/lkmpg/2.6/html/</a></li>
<li><a href="https://wiki.archlinux.org/index.php/Gamepad#Joystick_input_systems" rel="nofollow">https://wiki.archlinux.org/index.php/Gamepad#Joystick_input_systems</a></li>
<li><a href="https://github.com/OnionIoT/source" rel="nofollow">https://github.com/OnionIoT/source</a></li>
<li><a href="https://docs.onion.io/omega2-docs/cross-compiling.html" rel="nofollow">https://docs.onion.io/omega2-docs/cross-compiling.html</a></li>
<li><a href="http://repo.onion.io/omega2/packages/" rel="nofollow">http://repo.onion.io/omega2/packages/</a></li>
<li><a href="https://github.com/paroj/xpad" rel="nofollow">https://github.com/paroj/xpad</a></li>
<li><a href="https://elinux.org/images/9/93/Evtest.c" rel="nofollow">https://elinux.org/images/9/93/Evtest.c</a></li>
<li><a href="https://labs.mediatek.com/en/chipset/MT7688" rel="nofollow">https://labs.mediatek.com/en/chipset/MT7688</a></li>
</ul>
<p dir="auto">My Github repository with example code and libraries:</p>
<ul>
<li><a href="https://github.com/gamer-cndg/omega2-kernel-modules" rel="nofollow">https://github.com/gamer-cndg/omega2-kernel-modules</a></li>
<li><a href="https://github.com/gamer-cndg/omega2-libs" rel="nofollow">https://github.com/gamer-cndg/omega2-libs</a></li>
<li><a href="https://github.com/gamer-cndg/gamepad" rel="nofollow">https://github.com/gamer-cndg/gamepad</a></li>
</ul>
<h3>Conclusion</h3>
<p dir="auto">This tutorial showed you what kernel modules are, why we might need them and how to build them. We built a 'hello world' minimal kernel module for a start, then expanded the code to read a hardware regster. Finally, we built <code>kmod-input-joydev</code> and <code>xpad</code> to get a USB Xbox360 gamepad working. We then compiled a test program and a library to confirm that we gamepad works.</p>
]]></description><link>http://community.onion.io/post/16513</link><guid isPermaLink="true">http://community.onion.io/post/16513</guid><dc:creator><![CDATA[Maximilian Gerhardt]]></dc:creator><pubDate>Mon, 09 Apr 2018 19:44:00 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Sun, 08 Apr 2018 17:09:53 GMT]]></title><description><![CDATA[<p dir="auto">Excellent!...thanks for sharing!</p>
<p dir="auto">My toolchain environment has only <strong>feeds.conf.default</strong> file but <strong>feeds.conf</strong> not exist. It's ok?.</p>
]]></description><link>http://community.onion.io/post/16514</link><guid isPermaLink="true">http://community.onion.io/post/16514</guid><dc:creator><![CDATA[Oximoron]]></dc:creator><pubDate>Sun, 08 Apr 2018 17:09:53 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Sun, 08 Apr 2018 17:17:43 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4611">@Oximoron</a> Thanks! That's weird, but should be fine too. If you cloned <a href="https://github.com/OnionIoT/source" rel="nofollow">https://github.com/OnionIoT/source</a> you <strong>should</strong> have that file. Anyways, I think I saw the update process etc. only pulling info from <code>feeds.conf</code>. I said to modify both files just to be certain you don't do an accidental upgrade and upgrade to a higher kernel. For the modules to work you should use that toolchain and kernel version. I experienced problems when compiling for a higher version kernel and running it on an older one.</p>
]]></description><link>http://community.onion.io/post/16515</link><guid isPermaLink="true">http://community.onion.io/post/16515</guid><dc:creator><![CDATA[Maximilian Gerhardt]]></dc:creator><pubDate>Sun, 08 Apr 2018 17:17:43 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Mon, 09 Apr 2018 15:09:45 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4683">@Maximilian-Gerhardt</a> you sorta out did even yourself with this post  <img src="http://community.onion.io/plugins/nodebb-plugin-emoji/emoji/android/1f642.png?v=ic093v0mjao" class="not-responsive emoji emoji-android emoji--slightly_smiling_face" title=":)" alt="🙂" /><br />
thank you. it will be very helpful.</p>
]]></description><link>http://community.onion.io/post/16522</link><guid isPermaLink="true">http://community.onion.io/post/16522</guid><dc:creator><![CDATA[Douglas Kryder]]></dc:creator><pubDate>Mon, 09 Apr 2018 15:09:45 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 24 Apr 2018 21:37:09 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4683">@Maximilian-Gerhardt</a> Awesome! Thank you so much for putting this together!</p>
]]></description><link>http://community.onion.io/post/16671</link><guid isPermaLink="true">http://community.onion.io/post/16671</guid><dc:creator><![CDATA[Bas Rijniersce]]></dc:creator><pubDate>Tue, 24 Apr 2018 21:37:09 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Wed, 25 Apr 2018 01:46:53 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4683">@Maximilian-Gerhardt</a> Outstanding work!</p>
]]></description><link>http://community.onion.io/post/16674</link><guid isPermaLink="true">http://community.onion.io/post/16674</guid><dc:creator><![CDATA[William Scott]]></dc:creator><pubDate>Wed, 25 Apr 2018 01:46:53 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 15:36:58 GMT]]></title><description><![CDATA[<p dir="auto">Great series, and great tutorial!</p>
<p dir="auto">I am trying speed up make by just compiling the hello-world module, using the command:</p>
<p dir="auto"><em>make package/hello-world/compile -j4</em></p>
<p dir="auto">However, I get the following error (when running with "-j1 V=s" for additional output):</p>
<p dir="auto"><em>make[1]: Entering directory '/root/source'<br />
make[1]: *** No rule to make target 'package/hello-world/compile'.  Stop.</em></p>
<p dir="auto">I have successfully done a full build and installed this module on my Omega2+, but I am stuck at this point.</p>
<p dir="auto">Thanks again for all the great work!</p>
]]></description><link>http://community.onion.io/post/16780</link><guid isPermaLink="true">http://community.onion.io/post/16780</guid><dc:creator><![CDATA[Jackson Wilson]]></dc:creator><pubDate>Tue, 08 May 2018 15:36:58 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 18:49:57 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/2758">@Jackson-Wilson</a> Thanks for the feedback! Unfortunately I cannot reproduce that.</p>
<p dir="auto">My full build log with <code>-j1 V=s</code>, when the package was previously compiled:</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j1 V=s
make[1]: Entering directory '/home/max/source'
make[2]: Entering directory '/home/max/omega2_kmods/hello-world'
if [ -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean ]; then rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean; fi
make[2]: Leaving directory '/home/max/omega2_kmods/hello-world'
make[1]: Leaving directory '/home/max/source'
</code></pre>
<p dir="auto">And I execute <code>make package/hello-world/clean</code> and then compile (with code from the "Read Hardware Register" Section):</p>
<pre><code class="language-sh">max@max-VirtualBox:~/source$ make package/hello-world/compile -j1 V=s
make[1]: Entering directory '/home/max/source'
make[2]: Entering directory '/home/max/omega2_kmods/hello-world'
if [ -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean ]; then rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean; fi
make[2]: Leaving directory '/home/max/omega2_kmods/hello-world'
make[1]: Leaving directory '/home/max/source'
max@max-VirtualBox:~/source$ make -C package/hello-world compile -j1 V=s
make: *** package/hello-world: No such file or directory.  Stop.
max@max-VirtualBox:~/source$ make -C /home/max/omega2_kmods/hello-world compile -j1 V=s
make: Entering directory '/home/max/omega2_kmods/hello-world'
Makefile:6: /rules.mk: No such file or directory
Makefile:7: /kernel.mk: No such file or directory
Makefile:19: /package.mk: No such file or directory
make: *** No rule to make target '/package.mk'.  Stop.
make: Leaving directory '/home/max/omega2_kmods/hello-world'
max@max-VirtualBox:~/source$ make package/hello-world/clean -j1 V=s
make[1]: Entering directory '/home/max/source'
make[2]: Entering directory '/home/max/omega2_kmods/hello-world'
rm -f /home/max/source/bin/targets/ramips/mt7688/packages/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/stamp/.hello-world_installed
rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/packages/hello-world.list /home/max/source/staging_dir/host/packages/hello-world.list
rm -rf /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world
make[2]: Leaving directory '/home/max/omega2_kmods/hello-world'
make[1]: Leaving directory '/home/max/source'
max@max-VirtualBox:~/source$ make package/hello-world/compile -j1 V=s
make[1]: Entering directory '/home/max/source'
make[2]: Entering directory '/home/max/omega2_kmods/hello-world'
mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world
cp -fpR ./src/* /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/
touch /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/.prepared_80ddc19575c0da8a918fdc92694a93eb
rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/stamp/.hello-world_installed
(cd /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/./; if [ -x ./configure ]; then find /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ -name config.guess | xargs -r chmod u+w; find /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ -name config.guess | xargs -r -n1 cp --remove-destination /home/max/source/scripts/config.guess; find /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ -name config.sub | xargs -r chmod u+w; find /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ -name config.sub | xargs -r -n1 cp --remove-destination /home/max/source/scripts/config.sub; AR="mipsel-openwrt-linux-musl-gcc-ar" AS="mipsel-openwrt-linux-musl-gcc -c -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -iremap /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world:hello-world -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro" LD=mipsel-openwrt-linux-musl-ld NM="mipsel-openwrt-linux-musl-gcc-nm" CC="mipsel-openwrt-linux-musl-gcc" GCC="mipsel-openwrt-linux-musl-gcc" CXX="mipsel-openwrt-linux-musl-g++" RANLIB="mipsel-openwrt-linux-musl-gcc-ranlib" STRIP=mipsel-openwrt-linux-musl-strip OBJCOPY=mipsel-openwrt-linux-musl-objcopy OBJDUMP=mipsel-openwrt-linux-musl-objdump SIZE=mipsel-openwrt-linux-musl-size CFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world:hello-world -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro " CXXFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -mips16 -minterlink-mips16 -iremap /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world:hello-world -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro " CPPFLAGS="-I/home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/usr/include -I/home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/include -I/home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/usr/include -I/home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/include/fortify -I/home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/include " LDFLAGS="-L/home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/usr/lib -L/home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/lib -L/home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/usr/lib -L/home/max/source/staging_dir/toolchain-mipsel_24kc_gcc-5.4.0_musl-1.1.16/lib -znow -zrelro "   ./configure --target=mipsel-openwrt-linux --host=mipsel-openwrt-linux --build=x86_64-linux-gnu --program-prefix="" --program-suffix="" --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --sysconfdir=/etc --datadir=/usr/share --localstatedir=/var --mandir=/usr/man --infodir=/usr/info --disable-nls   ; fi; )
rm -f /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/.configured_*
touch /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/.configured_yyyy
make -C "/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/linux-4.4.74" ARCH="mips" CROSS_COMPILE="mipsel-openwrt-linux-musl-" SUBDIRS="/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world" modules
make[3]: Entering directory '/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/linux-4.4.74'
  CC [M]  /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.o
/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.c: In function 'ReadReg32':
/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.c:21:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  unsigned int val = ioread32(virtualMem);
  ^
/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.c: In function 'hello_2_init':
/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.c:33:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
  unsigned int chipid0_3 = ReadReg32(MT7688_SYSCTL_BASE + MT7688_SYSCTL_CHIPID0_3);
  ^
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.mod.o
  LD [M]  /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/hello-world.ko
make[3]: Leaving directory '/home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/linux-4.4.74'
touch /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/.built
mkdir -p /home/max/source/bin/targets/ramips/mt7688/packages /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo
. /home/max/source/include/shell.sh; export modules=; probe_module() { local mods="$1"; local boot="$2"; local mod; shift 2; for mod in $mods; do mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d; echo "$mod" &gt;&gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d/hello-world; done; if [ -e /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d/hello-world ]; then if [ "$boot" = "1" -a ! -e /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d/hello-world ]; then mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d; ln -s ../modules.d/hello-world /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d/; fi; modules="${modules:+$modules }$mods"; fi; }; add_module() { local priority="$1"; local mods="$2"; local boot="$3"; local mod; shift 3; for mod in $mods; do mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d; echo "$mod" &gt;&gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d/$priority-hello-world; done; if [ -e /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d/$priority-hello-world ]; then if [ "$boot" = "1" -a ! -e /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d/$priority-hello-world ]; then mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d; ln -s ../modules.d/$priority-hello-world /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules-boot.d/; fi; modules="${modules:+$modules }$priority-hello-world"; fi; };  if [ -n "$modules" ]; then modules="$(echo "$modules" | tr ' ' '\n' | sort | uniq | paste -s -d' ' -)"; mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/etc/modules.d; mkdir -p /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL; echo "#!/bin/sh" &gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL/postinst-pkg; echo "[ -z \"\$IPKG_INSTROOT\" ] || exit 0" &gt;&gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL/postinst-pkg; echo ". /lib/functions.sh" &gt;&gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL/postinst-pkg; echo "insert_modules $modules" &gt;&gt; /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL/postinst-pkg; chmod 0755 /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL/postinst-pkg; fi
find /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| xargs -r rm -rf
export CROSS="mipsel-openwrt-linux-musl-"  NO_RENAME=1 ; NM="mipsel-openwrt-linux-musl-nm" STRIP="/home/max/source/staging_dir/host/bin/sstrip" STRIP_KMOD="/home/max/source/scripts/strip-kmod.sh" PATCHELF="/home/max/source/staging_dir/host/bin/patchelf" /home/max/source/scripts/rstrip.sh /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world
rstrip.sh: /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/lib/modules/4.4.74/hello-world.ko: relocatable
(cd /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world/CONTROL; ( echo "$CONTROL"; printf "Description: "; echo "$DESCRIPTION" | sed -e 's,^&lsqb;&lsqb;:space:&rsqb;&rsqb;*, ,g'; ) &gt; control; chmod 644 control; ( echo "#!/bin/sh"; echo "[ \"\${IPKG_NO_SCRIPT}\" = \"1\" ] &amp;&amp; exit 0"; echo "[ -x "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_postinst \$0 \$@"; ) &gt; postinst; ( echo "#!/bin/sh"; echo "[ -x "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_prerm \$0 \$@"; ) &gt; prerm; chmod 0755 postinst prerm;  )
install -d -m0755 /home/max/source/bin/targets/ramips/mt7688/packages
/home/max/source/scripts/ipkg-build -c -o 0 -g 0 /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world /home/max/source/bin/targets/ramips/mt7688/packages
Packaged contents of /home/max/source/build_dir/target-mipsel_24kc_musl-1.1.16/linux-ramips_mt7688/hello-world/ipkg-mipsel_24kc/kmod-hello-world into /home/max/source/bin/targets/ramips/mt7688/packages/kmod-hello-world_4.4.74+1.1-4_mipsel_24kc.ipk
rm -rf /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world
mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/stamp /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world
. /home/max/source/include/shell.sh; export modules=; probe_module() { local mods="$1"; local boot="$2"; local mod; shift 2; for mod in $mods; do mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d; echo "$mod" &gt;&gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d/hello-world; done; if [ -e /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d/hello-world ]; then if [ "$boot" = "1" -a ! -e /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d/hello-world ]; then mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d; ln -s ../modules.d/hello-world /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d/; fi; modules="${modules:+$modules }$mods"; fi; }; add_module() { local priority="$1"; local mods="$2"; local boot="$3"; local mod; shift 3; for mod in $mods; do mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d; echo "$mod" &gt;&gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d/$priority-hello-world; done; if [ -e /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d/$priority-hello-world ]; then if [ "$boot" = "1" -a ! -e /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d/$priority-hello-world ]; then mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d; ln -s ../modules.d/$priority-hello-world /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules-boot.d/; fi; modules="${modules:+$modules }$priority-hello-world"; fi; };  if [ -n "$modules" ]; then modules="$(echo "$modules" | tr ' ' '\n' | sort | uniq | paste -s -d' ' -)"; mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/etc/modules.d; mkdir -p /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL; echo "#!/bin/sh" &gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL/postinst-pkg; echo "[ -z \"\$IPKG_INSTROOT\" ] || exit 0" &gt;&gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL/postinst-pkg; echo ". /lib/functions.sh" &gt;&gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL/postinst-pkg; echo "insert_modules $modules" &gt;&gt; /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL/postinst-pkg; chmod 0755 /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/CONTROL/postinst-pkg; fi
SHELL= flock /home/max/source/tmp/.root-copy.flock -c 'cp -fpR /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world/. /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/'
rm -rf /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/tmp-kmod-hello-world
touch /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/root-ramips/stamp/.kmod-hello-world_installed
if [ -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean ]; then rm -f /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install /home/max/source/staging_dir/target-mipsel_24kc_musl-1.1.16/pkginfo/hello-world.default.install.clean; fi
make[2]: Leaving directory '/home/max/omega2_kmods/hello-world'
make[1]: Leaving directory '/home/max/source'
</code></pre>
<p dir="auto">Could it be that you set-up the paths the wrong way (<code>src-link</code>)? Are you executing the command from the from the right path (top-level in <code>source</code>)? Very weird that normal <code>make -j4</code> works but this doesn't.. Any differences to the OnionIoT's <code>source</code> repository setup or the package folder structure <a href="https://github.com/gamer-cndg/omega2-kernel-modules" rel="nofollow">here</a>? What does <code>make package/hello-world/prepare -j1 V=s</code> do?</p>
]]></description><link>http://community.onion.io/post/16782</link><guid isPermaLink="true">http://community.onion.io/post/16782</guid><dc:creator><![CDATA[Maximilian Gerhardt]]></dc:creator><pubDate>Tue, 08 May 2018 18:49:57 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 19:23:24 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/2758">@Jackson-Wilson</a></p>
<p dir="auto">I found the issue.  The following command works:</p>
<p dir="auto"><em>make omega2_kmods/hello_world -j4</em></p>
<p dir="auto">However, when I make a change to the source file, and try to re-build, make says the package is "up to date."</p>
<p dir="auto">Maybe someone more savvy with the innards of the build system can tell me how to get a quick re-build of this package as I make changes along with this guide.</p>
<p dir="auto">Thanks!</p>
]]></description><link>http://community.onion.io/post/16784</link><guid isPermaLink="true">http://community.onion.io/post/16784</guid><dc:creator><![CDATA[Jackson Wilson]]></dc:creator><pubDate>Tue, 08 May 2018 19:23:24 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 20:03:53 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4683">@Maximilian-Gerhardt</a></p>
<p dir="auto">Hi!</p>
<p dir="auto">I suspect the src-link, since I get different output when I issue <em>make package/hello-world/compile -j1 V=s</em>:</p>
<p dir="auto"><em>root@1fbc8ce194c0:~/source# make package/hello-world/clean -j1 V=s<br />
make[1]: Entering directory '/root/source'<br />
make[1]: *** No rule to make target 'package/hello-world/clean'.  Stop.</em></p>
<p dir="auto">My added line to feeds.conf is:</p>
<p dir="auto"><em>src-link omega2_kmods /root/source/omega2_kmods</em></p>
<p dir="auto">I'll mess around with this for a bit...</p>
]]></description><link>http://community.onion.io/post/16785</link><guid isPermaLink="true">http://community.onion.io/post/16785</guid><dc:creator><![CDATA[Jackson Wilson]]></dc:creator><pubDate>Tue, 08 May 2018 20:03:53 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 20:11:24 GMT]]></title><description><![CDATA[<p dir="auto">Good that you figured out another way.  The only difference I see is that I placed my <code>omega2_kmods</code> folder outside of <code>source</code> in some other directory (<code>/home/max/</code>).</p>
]]></description><link>http://community.onion.io/post/16786</link><guid isPermaLink="true">http://community.onion.io/post/16786</guid><dc:creator><![CDATA[Maximilian Gerhardt]]></dc:creator><pubDate>Tue, 08 May 2018 20:11:24 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 21:05:58 GMT]]></title><description><![CDATA[<p dir="auto">Well, my work-around doesn't allow me to clean and rebuild this single package, so I'm still struggling to get this to work.  I have tried to replicate the structure you describe in this guide.</p>
<p dir="auto">I get the same <em>"No rule..."</em> error for /clean and /prepare commands as well.</p>
]]></description><link>http://community.onion.io/post/16787</link><guid isPermaLink="true">http://community.onion.io/post/16787</guid><dc:creator><![CDATA[Jackson Wilson]]></dc:creator><pubDate>Tue, 08 May 2018 21:05:58 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 08 May 2018 21:13:31 GMT]]></title><description><![CDATA[<p dir="auto">Got it!  I just had to get my directories and paths right.  I was rushing a bit, and things were not lined up exactly correctly.  I think it builds for the entire project because the recursive make found my package on its own, but I wasn't specifying it exactly right when I tried to build it individually.</p>
<p dir="auto">Great fun!</p>
]]></description><link>http://community.onion.io/post/16788</link><guid isPermaLink="true">http://community.onion.io/post/16788</guid><dc:creator><![CDATA[Jackson Wilson]]></dc:creator><pubDate>Tue, 08 May 2018 21:13:31 GMT</pubDate></item><item><title><![CDATA[Reply to Building Kernel Modules for the Omega2 on Tue, 02 Jul 2019 19:29:35 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="http://community.onion.io/uid/4683">@Maximilian-Gerhardt</a> I believe we both Geophoenixed in the early 2000's. Thanks for the article, it helped me migrate a water heater control unit to a newer linux kernel (with MMU)</p>
]]></description><link>http://community.onion.io/post/19694</link><guid isPermaLink="true">http://community.onion.io/post/19694</guid><dc:creator><![CDATA[Tony Gutierrez]]></dc:creator><pubDate>Tue, 02 Jul 2019 19:29:35 GMT</pubDate></item></channel></rss>