Building generic kernels

Even if kernel sources are not provided by the vendor, it's still possible to package kernel and make the phone boot.
== Find generic kernel for the device ==
To find a needed kernel, you have to:
# Find out the information about the SoC/board used in the device. One SoC may have multiple boards. For example, Spreadtrum SC7731C has 2 boards: sc7731cea and sc7731ceb.
# Try to search for kernels on the internet. Use different queries to find as much kernels as possible. Kernel is considered matching if it has files for your board, such as device tree source (located at arch/$_carch/boot/dts). Some downstream kernels (up to 3.10.x) may have support in board files instead. They are located at arch/$_carch/mach-soc-vendor or mach-soc-name(e.g. mach-sc, mach-qcom, mach-mt6582).
== Add device support ==
Depending on the way of describing the hardware in kernel, you should choose one of these methods
=== Add vendor dts in kernel ===
# Get dts from stock boot.img
# Copy dts to linux-device-codename folder and add its filename to sources="" list in APKBUILD
# Add a copy command for dts file on prepare step, like cp "$srcdir"/dtsname.dts "$builddir/arch/$_carch"/boot/dts/
# Patch related Makefile (usually in arch/$_carch/boot/dts) to build device tree blob
=== Board files ===
If the found kernel uses board files for your SoC, then you have to change kernel config to enable/disable support for specific hardware and patch the board file if implementation of additional devices is needed (e.g. hardware keyboard).
== Adapt kernel config ==
This step is needed to make a proper kernel config which works for the device. You have to find detailed information about the hardware. Possible ways to do it are:
# Get dts from stock boot.img
# Follow instructions from tuning sysfs page to get contents of sysfs. It is recommended to do it on a rooted phone in Android, since dump made from recovery will probably have less details about hardware.
# Use strings stockboot.img > stockboot.txt to dump all possible function names and find out which kernel options should be enabled to add these functions in kernel during building.
# Find photos of device's internals on the web. If there are none, disassemble the device yourself or ask someone to do it.
After one of these steps write down all information you've found and use it to adjust kernel config. Run pmbootstrap kconfig edit and use built-in search (press / key) to find options related to found hardware.
== There is no driver for X in the kernel. What to do? ==
# Check if driver is available in other generic kernels you have found for the device. If driver is available, check step 6 and further.
# Try to find this driver in other kernels that don't match your device. It will be helpful to understand how it works, though you'll most likely have to rewrite(port) it.
# Find datasheet to have more information for porting the driver.
# Find a dummy driver for the driver's group(e.g. lcd_dummy.c for LCD panel drivers) in your kernel. Read it as well as other drivers for devices similar to yours.
# Use drivers and datasheet to make a new driver.
# Patch the kernel with driver, Makefile and Kconfig files related to your drivers group to add support. Give a proper name to driver option in Kconfig.
# Add a line to linux-device-codename's config to make the driver option appear in menuconfig. Example: # CONFIG_FB_LCD_ST7796S_MIPI is not set
# Run pmbootstrap kconfig edit and enable the option. Tap / on the keyboard to search for it.
== Additional information ==
=== How to add hardware keyboard support ===
==== Getting a list of input devices and keycodes ====
For Android devices, you may run getevent -i to get detailed information about the input devices and related keycodes. You'll get output similar to:
add device 1: /dev/input/event2
 bus: 0019
 vendor 0001
 product 0001
 version 0100
 name: "gpio-keys"
 location: "gpio-keys/input0"
 id: ""
 version: 1.0.1
 events:
 KEY (0001): 0002 0003 0005 0007 0008 0009 000a 003e
 0067 0069 006a 0161 020a
 input props:
 <none>
add device 2: /dev/input/event0
 bus: 0019
 vendor 0001
 product 0001
 version 0100
 name: "sci-keypad"
 location: "sci-key/input0"
 id: ""
 version: 1.0.1
 events:
 KEY (0001): 0004 0006 000b 003d 006c 0074 008b 009e
 00e7 020b
 input props:
 <none>
add device 3: /dev/input/event1
 bus: 0019
 vendor 0000
 product 0000
 version 0000
 name: "headset-keyboard"
 location: ""
 id: ""
 version: 1.0.1
 events:
 KEY (0001): 00e2
 input props:
 <none>
It's possible to use the -l flag to replace keycodes with key names from include/uapi/linux/kpd.h. Do note that some keys may not exist. For example, the output given above has F3 and F4 keycodes, but device doesn't have these keys.
If you're unsure about the relations of physical keys and keycodes, you may start getevent and press buttons one by one to understand.
You should also record data from dmesg, it may help later.
==== Add and modify input devices in board file ====
This task will be reviewed on the previous example. As can be seen, the phone has an input device for headset with one button, matrix keypad device with 9 keys (remember that F3 button doesn't exist) and GPIO keys device. The board file used as the base is arch/arm/mach-sc/board-sp7715ga.c. This board has headset and matrix keypad devices set, but the GPIO keys device is missing and matrix data doesn't fit the device. So, the steps needed are:
# Change matrix data to fit the device
# Add gpio keys device to the list of devices
# Create gpio keys device description
The matrix data looks like this
#define CUSTOM_KEYPAD_ROWS (SCI_ROW0 | SCI_ROW1)
#define CUSTOM_KEYPAD_COLS (SCI_COL0 | SCI_COL1)
#define ROWS	(2)
#define COLS	(2)
static const unsigned int board_keymap[] = {
	KEY(0, 0, KEY_VOLUMEDOWN),
	KEY(1, 0, KEY_VOLUMEUP),
	KEY(0, 1, KEY_CAMERA),
};
As there should be 9 matrix keys, we assume that the matrix has 3 rows and 3 columns. So, the first step is to replace existing entries with ones related to your keys. If you have access to dmesg, then you may find out the proper row and column for every key, otherwise you'll have to guess them. After that, change amount of rows and columns, patch, build and flash the kernel. Then boot to postmarketOS and run evtest. Choose the keypad device and press buttons one by one. If the keycode doesn't fit the key, then you have to make changes in board file. The final result for the device in example is:
#define CUSTOM_KEYPAD_ROWS (SCI_ROW0 | SCI_ROW1 | SCI_ROW2 )
#define CUSTOM_KEYPAD_COLS (SCI_COL0 | SCI_COL1 | SCI_COL2 )
#define ROWS	(3)
#define COLS	(3)
static const unsigned int board_keymap[] = {
 KEY(0, 0, KEY_5),
 KEY(1, 0, KEY_BACK),
 KEY(2, 0, KEY_DOWN),
 KEY(0, 1, KEY_SEND),
 KEY(1, 1, KEY_0),
 KEY(0, 2, KEY_3),
 KEY(1, 2, KEY_MENU),
 KEY(2, 2, KEY_NUMERIC_POUND),
};
The second step is to add the missing GPIO keys device. There is no template for it in this exact board file, so you have to find it. It is recommended to search for it in the same path where the board file for your device is located (arch/arm/mach-sc in this case). It doesn't mean you're limited by the kernel you're using though. It's possible to search in other kernels as well, just be sure they don't differ much. Example:
#define GPIO_KEY_HOME 172
static struct platform_device gpio_button_device;
static struct platform_device *devices[] __initdata = {
	/*
		other devices
	*/
	&gpio_button_device,
};
static struct gpio_keys_button gpio_buttons[] = {
	{
		.gpio	= GPIO_KEY_HOME,
		.ds_irqflags = IRQF_TRIGGER_LOW,
		.code = KEY_HOME,
		.desc = "Home key",
		.active_low = 1,
		.debounce_interval = 2,
	},
};
static struct gpio_keys_platform_data gpio_button_data = {
	.buttons = gpio_buttons,
	.nbuttons = ARRAY_SIZE(gpio_buttons),
};
static struct platform_device gpio_button_device = {
	.name = "gpio-keys",
	.id = -1,
	.num_resources = 0,
	.dev = {
			.platform_data = &gpio_button_data,
	}
};
The final step is to add a description of all GPIO keys to the device. In this example, we define GPIOs for keys and change the contents of gpio_buttons[] and test the changes. It is recommended to use debug-shell hook for initramfs so you get access to dmesg as fast as possible. In dmesg you may find details of GPIO keys initialization. If it failed, there will be a wrong GPIO mentioned. Then, if everything's alright, run evtest again and choose GPIO keys device. Test and edit the board file till it works.
== See also ==
- sprdfb panel tool - tool for receiving lcm driver data from Spreadtrum boot.img (3.x) or u-boot.
- MTK LCM Decoder - get panel init sequence from lk.bin
Category: Technical Reference