- Published on
 
Linux内核驱动hello
- Authors
 - Name
 - wellsleep (Liu Zheng)
 
first linux kernel driver
Reference: LED驱动/dev/led
preparation
- get kernel version from 
uname -r. - find real KERNELDIR (in Makefile), for kernel module programming.
 - if 
/lib/module/builddoesn't exist, look for/usr/src/linux-* - files may not be ready from board
apt-get install linux-sourceapt-get install linux-headers
 
development
- Kirin 620 SoC uses address-select to enable pin.
 - write 0xF to GPIODIR to confirm 4-pin output mode.
 - modify Makefile according to environment
 - after successfully generate *.ko, look for the device in 
/dev, and its privileges if complaining "can't find device",sudo chmod 777 device. - "MODULE_LICENSE("GPL");" the different license may differ the compilation result.
 
demo:
#define BASE_ADDR 0xF7020000
#define OFFSET 0x03FC (to enable GPIO4_0 to GPIO 4_7)
unsigned long *paddr = NULL;
unsigned long *vaddr = NULL;
paddr = (unsigned long*)BASE_ADDR + OFFSET;
vaddr = (long *)(volatile unsigned long *)ioremap(paddr, 0x10);
int temp = 0xF;(to pull GPIO4_0 to GPIO4_3 to HIGH)
iowrite32(vaddr, temp); 
/* 
* *vaddr = temp might be complained "segmentation fault" in ARMv8 64bit compiler. 
* to be safe, use I/O function instead.
*/
source code
hikey_led(.ko)
#include <linux/module.h>  
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
static struct class *sun8i_opizero_led_class;
//STATUS-LED:LED901-904, USER_LED4_GPIO4_0 to GPIO4_3
#define PIO_BASE 0xF7020000
#define PIO_OFFSET 0x3FC
#define PIO_DIR 0xF7020400
volatile unsigned long *pacfg[2] = {NULL};
static int sun8i_opizero_led_open(struct inode *inode, struct file *file)
{
    //configure GPIO4_0 to GPIO4_3 to output mode
    *pacfg[1] |= 0xF;
    return 0;
}
static ssize_t sun8i_opizero_led_write(struct file *file, const int __user *buf, size_t count, loff_t *ppos)
{
    int val = 0;
    unsigned int temp = 0;
    //get user input
    copy_from_user(&val, buf, count);
    if (val == 1) {
	//*pacfg[0] |= 0xF;
        temp = ioread32(pacfg[0]);
        temp |= 0xF;
        iowrite32(temp, pacfg[0]);
    }
    else {
        //*pacfg[0] &= 0;
        temp = ioread32(pacfg[0]);
        temp &= 0xF0;
        iowrite32(temp, pacfg[0]);
    }
    
    return 0;
}
static struct file_operations sun8i_opizero_led_fops = {
    .owner = THIS_MODULE,
    .open = sun8i_opizero_led_open,
    .write = sun8i_opizero_led_write,
};
int major;
int sun8i_opizero_led_init(void)
{
    major = register_chrdev(0, "hikey_led_4_3", &sun8i_opizero_led_fops);
    sun8i_opizero_led_class = class_create(THIS_MODULE, "hikey_led_4_3");
    device_create(sun8i_opizero_led_class, NULL, MKDEV(major, 0), NULL, "hikey_led_4_3");
    pacfg[0] = (volatile unsigned long *)ioremap(PIO_BASE+PIO_OFFSET, 0x10);
    pacfg[1] = (volatile unsigned long *)ioremap(PIO_DIR, 0x10);
    printk(KERN_ALERT "HELLO LOADED!\n");
    return 0;
}
static void sun8i_opizero_led_exit(void)
{
    unregister_chrdev(major, "hikey_led_4_3");
    device_destroy(sun8i_opizero_led_class, MKDEV(major, 0));
    class_destroy(sun8i_opizero_led_class);
    iounmap(pacfg[0]);
    iounmap(pacfg[1]);
    printk(KERN_ALERT "BYE MODULE!\n");
}
module_init(sun8i_opizero_led_init);
module_exit(sun8i_opizero_led_exit);
MODULE_DESCRIPTION("LED driver");
MODULE_AUTHOR("Anonymous");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:hikey-620");
led_test
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
    int fd, val = 1;
    fd = open("/dev/hikey_led_4_3", O_RDWR);
    if (fd < 0)
        printf("can't open led device\n");
    if (argc != 2) {
        printf("Usage:\n");
		
        printf("%s <on|off>\n", argv[0]);
        return 1;
    }
    if (strcmp(argv[1], "on") == 0)
        val = 1;
    else
        val = 0;
    write(fd, &val, 4);
    return 0;
}
Makefile
obj-m := hikey_led.o #编译进模块
#KERNELDIR := /lib/modules/3.18.0-linaro-hikey/build #此处为linux内核库目录
KERNELDIR := /usr/src/linux-headers-3.18.0-linaro-hikey
PWD := $(shell pwd) #获取当前目录
OUTPUT := $(obj-m) $(obj-m:.o=.ko) $(obj-m:.o=.mod.o) $(obj-m:.o=.mod.c) modules.order Module.symvers
modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
	rm -rf $(OUTPUT)
environment
- Hikey 620 board
 - Linaro Debian image (hikey-jessie_alip_20151130-387-8g.emmc.img), kernel: 3.18.0
 - on-board compile and program
 - A53 power is great but the board sucks (HDMI unstable, wifi unstable)