从kernel到app系列之---kernel篇

0.kernel module

Modules are pieces of code that can be loaded and unloaded into the kernel upon demand. HAL implementations are packaged into modules and loaded by the Android system at the appropriate time. A kernel module can be a device driver that handles or manages a hardware.

1.Loadable Kernel Module

As part of the module kernel requirements introduced in Android 8.0, all system-on-chip (SoC) kernels must support loadable kernel modules.

To support loadable kernel modules, android-base.cfg in all common kernels includes the following kernel-config options (or their kernel-version equivalent):

CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y

All device kernels must enable these options. Kernel modules should also support unloading and reloading whenever possible.

2.实例编写

说明:我们这里会编写一个实例驱动,将大小写字母进行转换。如:AbC -> aBc.

其目录结构如下:

 

 我们在drivers 目录下新建invcase目录,并创建相关文件。

2.1 invcase.c 文件

#include <linux/module.h>    // all kernel modules
#include <linux/kernel.h>    // KERN_INFO
#include <linux/errno.h>     // EFAULT
#include <linux/device.h>    // device register
#include <linux/fs.h>        // file_operations
#include <linux/types.h>     // size_t
#include <linux/uaccess.h>   // copy_from/to_user

MODULE_LICENSE("GPL");
MODULE_AUTHOR("VQTRONG");
MODULE_DESCRIPTION("Inverse Case");

/* DEVICE NAME */
#define DEVICE_NAME     "invcase"   // The device will appear at /dev/invcase
#define CLASS_NAME      "invcase"
#define DEVICE_DEBUG    "invcase: "

/* Global variable */
static int majorNumber = 0;
static struct class*  invcaseClass  = NULL;
static struct device* invcaseDevice = NULL;

#define MAX_SIZE 1024
static char __buffer[MAX_SIZE] = {0};

/* Function declaration */
static int invcase_init(void);
static void invcase_exit(void);
static ssize_t invcase_receive(
    struct file *filp, char *buf, size_t count, loff_t *f_pos
);
static ssize_t invcase_send(
    struct file *filp, const char *buf, size_t count, loff_t *f_pos
);

/* Device operations */
static struct file_operations __fops = 
{
    .owner  = THIS_MODULE,
    .read   = invcase_receive,
    .write  = invcase_send,
};

static int invcase_init(void){
    // Try to dynamically allocate a major number for the device -- more difficult but worth it
    majorNumber = register_chrdev(0, DEVICE_NAME, &__fops);
    if (majorNumber<0){
        printk(KERN_ERR DEVICE_DEBUG "Failed to register a major number\n");
        return majorNumber;
    }
    printk(KERN_INFO DEVICE_DEBUG "Registered with major number %d\n", majorNumber);

    // Register the device class
    invcaseClass = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(invcaseClass)) // Check for error and clean up if there is
    {
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ERR DEVICE_DEBUG "Failed to register device class\n");
        return PTR_ERR(invcaseClass); // Correct way to return an error on a pointer
    }
    printk(KERN_INFO DEVICE_DEBUG "Device class registered correctly\n");

    // Register the device driver
    invcaseDevice = device_create(invcaseClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);
    if (IS_ERR(invcaseDevice)) // Clean up if there is an error
    {
        class_destroy(invcaseClass);
        unregister_chrdev(majorNumber, DEVICE_NAME);
        printk(KERN_ERR DEVICE_DEBUG "Failed to create the device\n");
        return PTR_ERR(invcaseDevice);
    }

    // clear buffer
    memset(__buffer, 0, MAX_SIZE);
    printk(KERN_INFO DEVICE_DEBUG "Init!\n");
    return 0; // Zero means OK
}

static void invcase_exit(void){
    device_destroy(invcaseClass, MKDEV(majorNumber, 0)); // remove the device
    class_unregister(invcaseClass); // unregister the device class
    class_destroy(invcaseClass); // remove the device class
    unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
    printk(KERN_INFO DEVICE_DEBUG "Exit\n");
}

static ssize_t invcase_receive(
    struct file *filp, char *buf, size_t count, loff_t *f_pos) {
    ssize_t remain = MAX_SIZE - *f_pos;
    ssize_t len = count > remain ? remain : count;

    printk(KERN_INFO DEVICE_DEBUG "Read from device, remain=%ld, *f_pos= %lld, count= %ld\n", remain, *f_pos, count);
    if(remain <= 0) return 0;

    if (copy_to_user(buf, __buffer+*f_pos, len)) {
        printk(KERN_ERR DEVICE_DEBUG "Can not copy to user\n");
        return -EFAULT;
    }
    printk(KERN_INFO DEVICE_DEBUG "Read from device: %s\n", __buffer);

    *f_pos += len;
    return len;
}

static ssize_t invcase_send(
    struct file *filp, const char *buf, size_t count, loff_t *f_pos) {
    int i;
    ssize_t remain = MAX_SIZE - *f_pos;
    ssize_t len = count > remain ? remain : count;

    printk(KERN_INFO DEVICE_DEBUG "Write to device, remain=%ld, *f_pos= %lld, count= %ld\n", remain, *f_pos, count);

    if(*f_pos == 0) memset(__buffer, 0, MAX_SIZE);

    if(remain <= 0) return count; // ignore all requested bytes

    if(copy_from_user(__buffer+*f_pos, buf, len)) {
        printk(KERN_ERR DEVICE_DEBUG "Can not copy from user\n");
        return -EFAULT;
    }
    printk(KERN_INFO DEVICE_DEBUG "Write to device: %s\n", __buffer);

    for(i=*f_pos; i<*f_pos+len; i++) {
        if( __buffer[i] >= 'A' && __buffer[i] <= 'Z') {
            __buffer[i] += 32;
        }
        else if( __buffer[i] >= 'a' && __buffer[i] <= 'z') {
        __buffer[i] -= 32;
        }
    }
    printk(KERN_INFO DEVICE_DEBUG "Convert to: %s\n", __buffer);

    *f_pos += len;
    return len;
}

module_init(invcase_init);
module_exit(invcase_exit);

2.2 Module implementation:

  1. invcase_init: register a character device at /dev/invcase
  2. invcase_exit: remove the registered device
  3. invcase_receive: aka read, copy from an internal buffer to the user buffer

    • Use f_pos to know how many bytes have been read
    • Return the number of bytes for current read command
    • Return 0 to indicate end of data
  4. invcase_send: aka write, copy from the user buffer to the internal buffer

    • Use f_pos to know how many bytes have been written
    • Return the number of bytes for current write command
    • System write expects that total returned bytes must be equal to the total requested bytes, therefore

2.3 Kconfig

# Invcase Device configuration

menuconfig INVCASE
    tristate "Inverse Characters Case"
    default y
    help
      Say Y to include this module
      Say N will not build this module
      Say M to build this module but not include to kernel yet

在上一级Kconfig中添加:

source "drivers/invcase/Kconfig"

2.4 Makfile

obj-$(CONFIG_INVCASE) += invcase.o

在上一级Makefile 中添加:

obj-$(CONFIG_INVCASE)     += invcase/

2.5 将驱动添加到系统

上面,我们可以通过将驱动编译到系统中,如Android 的boot.img 中,来使用驱动,但是我们的目标是,能将驱动加载和卸载。下面我们来看看是如何操作的呢?

2.5.1 将ko文件放到system中

在mk文件中添加:

PRODUCT_COPY_FILES += \
        path/to/invcase.ko:system/lib/modules/invcase.ko

在 rc文件中,加载驱动:

on boot
    insmod /system/lib/modules/invcase.ko

修改权限:

on boot
    chown system system /dev/invcase
    chmod 0600 /dev/invcase

重新编译system 即可。

2.5.2 实验测试

向节点写入字符串,然后读取出来后,发现大小写反转了。

 

2.6 额外编译ko

上面ko是依赖源码进行编译的,那么我们是否可以不依赖特定源码进行编译呢,我们只需要根据kernel的版本,编译出所需的ko文件呢?下面,我们来看看。

From AOSP 11, Vendor modules are recommended to built separately in common-modules and are built with option EXT_MODULES.

For example: build.config.virtual_device.x86_64 → build.config.virtual_device which declares:

EXT_MODULES="common-modules/virtual-device"

External modules do not listed in Kernel Config Menu. You have to change them manually in build command or in common-modules/virtual-device/Kbuild file.

2.6.1 Appendix

A simple device can be implemented and tested in Linux.

Install Kernel Headers

sudo apt install linux-headers-$(uname -r)

Makefile:

BINARY := invcase
BUILD := /lib/modules/$(shell uname -r)/build
obj-m := $(BINARY).o

all:
    make -C $(BUILD) M=$(PWD) modules

install:
    echo 'KERNEL=="invcase", SUBSYSTEM=="invcase", MODE="0777"' | sudo tee /etc/udev/rules.d/99-invcase.rules
    sudo insmod $(BINARY).ko

remove:
    sudo rmmod $(BINARY)

clean:
    make -C $(BUILD) M=$(PWD) clean

这里暂时还没有实验过,先记录一下。

 

参考链接:

https://www.codeinsideout.com/blog/android/kernel-module/

posted @ 2023-04-03 10:13  皓然123  阅读(142)  评论(0)    收藏  举报