美文网首页
openwrt 编写驱动控制led

openwrt 编写驱动控制led

作者: only_cherry | 来源:发表于2019-01-18 18:12 被阅读0次

    一、编写GPIO驱动

    1. 查找 GPIO 寄存器配置(gpio0为例)
      查手册获取 GPIO0 设置相关的寄存器
      GPIO#11 引脚,GPIO1_MODE[1:0] (01)来设置其为GPIO功能
      通过设置 GPIO_CTRL_0 [11号引脚]为1将 GPIO11 设置为输出模式
      通过设置 GPIO_DATA_0 [11号引脚]为0或1 将 GPIO11 输出设置为0或1

    二、创建模块文件目录:

    1、在源码目录下的/package/kernel/目录下创建chardrv目录
    2、在chardrv目录下创建Makefile文件和src目录
    3、在src目录下创建Makefile文件和chardrv.c文件

    三、编写驱动文件

    1、chardrv目录下的Makefile文件内容:

    #
    # Copyright (C) 2008 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
    
    PKG_NAME:=chardrv  #字符设备驱动模块名称
    PKG_RELEASE:=2   #版本号
    
    include $(INCLUDE_DIR)/package.mk
    
    define KernelPackage/chardrv #内核模块
      SUBMENU:=Other modules #进行归类
      TITLE:=chardrv  #标题
      FILES:=$(PKG_BUILD_DIR)/chardrv.ko #模块文件
      AUTOLOAD:=$(call AutoLoad,61,chardrv,1) #开启自动装载
      KCONFIG:=
    endef
    
    define KernelPackage/chardrv/description
     Kernel module for register chardrv. #描述
    endef
    
    EXTRA_KCONFIG:= \
        CONFIG_CHARDRV=m
    
    EXTRA_CFLAGS:= \
        $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \
        $(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \
    
    MAKE_OPTS:= \
        ARCH="$(LINUX_KARCH)" \
        CROSS_COMPILE="$(TARGET_CROSS)" \
        SUBDIRS="$(PKG_BUILD_DIR)" \
        EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \
        $(EXTRA_KCONFIG)
    
    define Build/Prepare
        mkdir -p $(PKG_BUILD_DIR)
        $(CP) ./src/* $(PKG_BUILD_DIR)/
    endef
    
    define Build/Compile
        $(MAKE) -C "$(LINUX_DIR)" \
            $(MAKE_OPTS) \
            modules
    endef
    
    $(eval $(call KernelPackage,chardrv))
    

    2、src目录下的Makefile文件内容:

    obj-${CONFIG_CHARDRV} += chardrv.o
    

    3、src目录下的chardrv.c文件内容:

    /***************************** 
    *
    *   字符设备驱动程序模板
    *
    *******************************/ 
    #include <linux/mm.h> 
    #include <linux/miscdevice.h> 
    #include <linux/slab.h> 
    #include <linux/vmalloc.h> 
    #include <linux/mman.h> 
    #include <linux/random.h> 
    #include <linux/init.h> 
    #include <linux/raw.h> 
    #include <linux/tty.h> 
    #include <linux/capability.h> 
    #include <linux/ptrace.h> 
    #include <linux/device.h> 
    #include <linux/highmem.h> 
    #include <linux/crash_dump.h> 
    #include <linux/backing-dev.h> 
    #include <linux/bootmem.h> 
    #include <linux/splice.h> 
    #include <linux/pfn.h> 
    #include <linux/export.h> 
    #include <linux/io.h> 
    #include <linux/aio.h> 
    #include <linux/kernel.h> 
    #include <linux/module.h> 
    #include <asm/uaccess.h> 
    #include <linux/ioctl.h> 
    
    /****************  基本定义 **********************/ 
    
    #define LED_ON 0
    #define LED_OFF 1
    
    //define registers 
    volatile unsigned long *GPIO_CTRL_0;// GPIO0-GPIO31 direction control register 
    volatile unsigned long *GPIO_DATA_0;// GPIO0-GPIO31 data register 
    volatile unsigned long *GPIO1_MODE;// GPIO1 purpose selection register 
    volatile unsigned long *AGPIO_CFG;// Analog GPIO configuartion, GPIO14-17 purpose
    
    //加密函数参数内容: _IOW(IOW_CHAR , IOW_NUMn , IOW_TYPE) 
    //加密函数用于chardrv_ioctl函数中 
    //使用举例:ioctl(fd , _IOW('L',0x80,long) , 0x1); 
    
    //#define NUMn chardrv 
    #define IOW_CHAR 'L' 
    #define IOW_TYPE  long 
    #define IOW_NUM1  0x80 
    
    //初始化函数必要资源定义 
    //用于初始化函数当中 
    //device number; 
        //设备号 
        dev_t dev_num; 
    //struct dev 
    
    //字符设备 
        struct cdev chardrv_cdev; 
    //auto "mknode /dev/chardrv c dev_num minor_num" 
    
    //自动创建设备对象 
    struct class *chardrv_class = NULL; 
    struct device *chardrv_device = NULL; 
    
    /**************** 结构体 file_operations 成员函数 *****************/ 
    
    //open 
    static int chardrv_open(struct inode *inode, struct file *file) 
    { 
        printk("chardrv drive open...\n"); 
          *GPIO_DATA_0&=~(1<<11);
        return 0; 
    } 
    //close 
    static int chardrv_close(struct inode *inode , struct file *file) 
    { 
        printk("chardrv drive close...\n"); 
        return 0; 
    } 
    
    //read 
    static ssize_t chardrv_read(struct file *file, char __user *buffer, size_t len, loff_t *pos) 
    { 
        int ret_v = 0; 
        printk("chardrv drive read...\n"); 
        return ret_v; 
    } 
    
    
    //write 
    static ssize_t chardrv_write( struct file *file , const char __user *buffer, size_t len , loff_t *offset ) 
    { 
        int ret_v = 0; 
        printk("chardrv drive write...\n"); 
        return ret_v; 
    } 
    
    //unlocked_ioctl 
    static int chardrv_ioctl (struct file *filp , unsigned int cmd , unsigned long arg) 
    { 
        int ret_v = 0; 
        printk("chardrv drive ioctl...\n"); 
        switch(cmd) { 
            //常规: 
            //cmd值自行进行修改 
        case LED_ON: 
            { 
                *GPIO_DATA_0 |= (1<<11);
               printk("set gpio 0 on\n"); 
        } 
        break; 
    
        case LED_OFF:
            {
                *GPIO_DATA_0 &= ~(1<<11);
                printk("set gpio 0 off\n"); 
            }
        break; 
        //带密码保护: 
        //请在"基本定义"进行必要的定义 
        case _IOW(IOW_CHAR,IOW_NUM1,IOW_TYPE): 
        { 
            if(arg == 0x1) //第二条件 
            {
    
            } 
        } 
        break;  
    
        default: 
        break; 
        } 
        return ret_v;
    
    } /***************** 结构体: file_operations ,该结构体将驱动中的函数和应用层函数关联(例如当调用应用层调用open函数时就会调用驱动中的open函数)************************/ 
    static const struct file_operations chardrv_fops = { 
        .owner = THIS_MODULE, 
        .open = chardrv_open, 
        .release = chardrv_close, 
        .read = chardrv_read, 
        .write = chardrv_write, 
        .unlocked_ioctl = chardrv_ioctl, 
    }; 
    
    unsigned char init_flag = 0;
    unsigned char add_code_flag = 0;
    
    //使用insmod挂载驱动时回调 
    static __init int chardrv_init(void) 
    { 
        int ret_v = 0; 
        printk("mydrv drive init...\n"); 
        /*
        函数alloc_chrdev_region主要参数说明:
        参数1: 自动分配的设备号
        参数2: 次设备号
        参数3: 创建多少个设备
        */ 
        if( ( ret_v = alloc_chrdev_region(&dev_num,0,1,"chardrv") ) < 0 ) //为chardrv动态分配设备号 
        { 
            goto dev_reg_error; 
        } 
    
        init_flag = 1; //标示设备创建成功   
    
        //打印主设备号和次设备号 
        printk("The drive info of chardrv:\nmajor: %d\nminor: %d\n", MAJOR(dev_num),MINOR(dev_num)); 
    
        //关联设备和操作函数 
        cdev_init(&chardrv_cdev,&chardrv_fops); 
        //注册设备 
        if( (ret_v = cdev_add(&chardrv_cdev,dev_num,1)) != 0 ) 
        { 
            goto cdev_add_error; 
        } 
    
        //创建设备类,用于自动创建设备 
        chardrv_class = class_create(THIS_MODULE,"chardrv"); 
        if( IS_ERR(chardrv_class) ) 
        { 
            goto class_c_error; 
        } 
    
        //通过设备类创建设备 
        chardrv_device = device_create(chardrv_class,NULL,dev_num,NULL,"chardrv"); 
        if( IS_ERR(chardrv_device) ) 
        { 
            goto device_c_error; 
        } 
        printk("auto mknod success!\n"); 
        //------------   请在此添加您的初始化程序  --------------// 
        //AGPIO_CFG = (volatile unsigned long *)ioremap(0x1000003c,4); //init register address 
        GPIO1_MODE = (volatile unsigned long *)ioremap(0x10000060,4); 
        GPIO_CTRL_0 = (volatile unsigned long *)ioremap(0x10000600,4); 
        GPIO_DATA_0 = (volatile unsigned long *)ioremap(0x10000620,4); 
    
        *GPIO1_MODE&=~(1<<1); //set SPIS purpose as GPIO11
        *GPIO1_MODE|=1; 
    
        *GPIO_CTRL_0|=(1<<11); //set GPIO17 as output pin
    
        //如果需要做错误处理,请:goto mydrv_error; 
        add_code_flag = 1;  
        //----------------------  END  ---------------------------// 
        goto init_success; 
        
    dev_reg_error: 
        printk("alloc_chrdev_region failed\n"); 
        return ret_v; 
    
    cdev_add_error: 
        printk("cdev_add failed\n"); 
        unregister_chrdev_region(dev_num, 1); 
    
        init_flag = 0;
        return ret_v; 
    class_c_error: 
        printk("class_create failed\n"); 
        cdev_del(&chardrv_cdev); 
        unregister_chrdev_region(dev_num, 1); 
        init_flag = 0;
        return PTR_ERR(chardrv_class); 
    device_c_error: 
        printk("device_create failed\n"); 
        cdev_del(&chardrv_cdev); 
        unregister_chrdev_region(dev_num, 1); 
        class_destroy(chardrv_class); 
        init_flag = 0;
    
        return PTR_ERR(chardrv_device); 
    //------------------ 请在此添加您的错误处理内容 ----------------// 
    chardrv_error: 
       add_code_flag = 0; 
        return -1; 
    //--------------------          END         -------------------// 
    init_success: 
        printk("chardrv init success!\n"); 
        return 0; 
    } 
    //使用rmmod卸载驱动是回调该函数 
    static __exit void chardrv_exit(void) 
    { 
      printk("chardrv drive exit...\n"); 
        if (add_code_flag == 1)
        {
            printk("free your resources...\n"); 
            iounmap(GPIO1_MODE); 
            iounmap(GPIO_CTRL_0); 
            iounmap(GPIO_DATA_0); 
            printk("free finish\n");
        }
    
        //释放初始化使用到的资源; 
        if (init_flag == 1) {
            cdev_del(&chardrv_cdev); 
            unregister_chrdev_region(dev_num, 1); 
            device_unregister(chardrv_device); 
            class_destroy(chardrv_class); 
        }
    }
    
    /**************** module operations**********************/
     //声明加载函数和卸载函数 
     module_init(chardrv_init); 
     module_exit(chardrv_exit); 
     //some infomation 
    
     MODULE_LICENSE("GPL v2"); 
     MODULE_AUTHOR("LFJ"); 
     /*********************  The End ***************************/
    

    四、编译驱动

    1、切换到源码目录的根目录:如openwrt目录
    2、make menconfig
    3、选择Kernel modules -->other modules-->kmod-chardrv,保存退出
    4、sudo make package/kernel/chardrv/compile V=99
    5、编译成功后的文件在openwrt/bin/ramips/packages/kernel/ 目录下的kmod-chardrv_4.4.7-2_ramips_24kec.ipk文件

    五、装载驱动模块到内核和卸载模块

    1、可以通过scp将kmod-chardrv_4.4.7-2_ramips_24kec.ipk文件拷贝到板子上
    2、在板子上执行opkg install kmod-chardrv_4.4.7-2_ramips_24kec.ipk 执行成功后在/lib/modules/4.4.7/目录下可以找到chardrv.ko文件
    3、insmod chardrv.ko #将装载到内核中
    4、rmmod chardrv.ko #将chardrv.ko模块从内核中卸载

    六、应用程序测试

    1、编写应用程序测试代码my_led.c

    /*************************************************************************
        > File Name: led_app.c
        > Author: 
        > Mail: LFJ
        > Created Time: 2019年01月17日 星期四 17时56分19秒
     ************************************************************************/
    #include <stdio.h> 
    #include <string.h> 
    #include <fcntl.h> 
    #include <unistd.h> 
    #include <sys/types.h> 
    #include <sys/stat.h> 
    #include <sys/ioctl.h> 
    #define LED_ON 0 
    #define LED_OFF 1 
    
    char str_dev[]="/dev/leddrv";
    int main(int argc,char* argv[]) 
    { 
        int fd;
    
        if(argc!=2) { 
            printf(" %s <on|off> to turn on or off LED on GPIO11\n",argv[0]); 
            return -1; 
        }
        fd=open(str_dev,O_RDWR|O_NONBLOCK); 
        if(fd<0) { 
            printf("can't open %s \n",str_dev); 
            return -1; 
        } if(!strcmp("on",argv[1])) {
            while(1) { 
                ioctl(fd,LED_ON); 
                sleep(1);
                ioctl(fd,LED_OFF); 
                sleep(1);
            } 
        } else if(!strcmp("off",argv[1])) {
            ioctl(fd,LED_OFF);
        } else { 
            printf(" %s <on|off> to turn on or off LED on GPIO11\n",argv[0]); 
            return -1; 
        }
    
     return 0; 
    }
    

    2、编译程序
    执行mipsel-openwrt-linux-gcc my_led.c -o my_led
    3、通过scp将my_led可执行文件拷贝到openwrt板子上./ my_led on执行,执行成功GPIO0将会循环亮、灭

    相关文章

      网友评论

          本文标题:openwrt 编写驱动控制led

          本文链接:https://www.haomeiwen.com/subject/cvgndqtx.html