Linux内核驱动hello

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/build doesn't exist, look for /usr/src/linux-*
  • files may not be ready from board
    • apt-get install linux-source
    • apt-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)
原作者的四空格缩进好痛苦然而我又懒得改...
Comments
Write a Comment
'