rk3399平台下的字符设备驱动

最近在搞一款瑞芯微平台的板子,型号:rk3399 ,这几天刚刚上手,前几天一直在搞环境,熟悉这个板子,这个板子跑的Android7.1的系统。在网上开到这块的东西不是很多,自己也想做个记录。然后,开始吧:我目前用的板子是葡萄雨科技的,因为这家公司给的资料

很少,之前一直在熟悉这个板子的目录结构,设备树文件再哪儿,以及这个公司给的编译脚本,关于编译源码这一块,瑞芯微的开源论坛也有讲,这一块就不多说,先说说我干的事情吧:写了一个led驱动,很简单,目的当然就是点灯,体验一下这个平台的驱动与我之前接触的有什么不同,所以先测试一下,接下来是我用的几种方式:

  1. 编译进内核,这种方式 系统一上电,在一定时候会自动加载模块,进入驱动程序的probe,这个是两种方式的唯一不同之处
  2. 区别:
    module_platform_driver(led_driver);
    module_exit(led_exit);
    MODULE_LICENSE("GPL");

     3.不编译进内核

驱动编写方式:1.字符设备注册 2.以杂项设备注册

直接上代码:

#include <linux/kernel.h>
#include <linux/gpio.h>

#include <linux/init.h>
#include <linux/module.h> 
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>


#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
//#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define GPIO_LOW 0
#define GPIO_HIGH 1
int gpio;
int major;
static struct class *cls;
static int led_open(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);
    
    return 0;    
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
    int ret;
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);

    ret = copy_from_user(&val, buf, count); //    copy_to_user();

    if (val == 1)
    {
        gpio_set_value(gpio,GPIO_LOW);
    }
    else
    {
        gpio_set_value(gpio,GPIO_HIGH);
    }
    
    return 0;
}
static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,   
    .open   =   led_open,     
    .write    =    led_write,       
};

static int led_probe(struct platform_device *pdev)
{
    int ret ;
    int i;

    enum of_gpio_flags flag;
    struct device_node *led_node = pdev->dev.of_node;

    
    
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);

    gpio = of_get_named_gpio_flags(led_node,"led-gpio", 0,&flag);
    if (!gpio_is_valid(gpio)){
        printk(KERN_INFO "hello: invalid gpio : %d\n",gpio);
        return -1;
    } 
    ret = gpio_request(gpio, "led");
    if (ret) {
        gpio_free(gpio);
        return -EIO;
    }

    gpio_direction_output(gpio, GPIO_HIGH);

    for(i=0; i < 10; i++)
    {
        gpio_set_value(gpio,GPIO_LOW);
        mdelay(500);
        gpio_set_value(gpio,GPIO_HIGH);
        mdelay(500);
    }
    
    
    major = register_chrdev(0, "myled", &led_fops);

    cls = class_create(THIS_MODULE, "myled");

    device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); 
    
    
    gpio_set_value(gpio,GPIO_LOW);
        
    printk(KERN_INFO "%s-%d: exit\n",__FUNCTION__,__LINE__);
    
    
    
    return 0;  //return Ok
}


static int led_remove(struct platform_device *pdev)
{ 
    printk(KERN_INFO "Enter %s\n", __FUNCTION__);
    gpio_free(gpio);
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "myled");
    return 0;
}
static const struct of_device_id of_led_match[] = {
    { .compatible = "led_test" },
    { /* Sentinel */ }
};
static struct platform_driver led_driver = {
    .probe        = led_probe,
    .remove        = led_remove,
    .driver        = {
        .name    = "led",
        .owner    = THIS_MODULE,
        .of_match_table    = of_led_match,
    },

};

static int __init led_init(void)
{
    printk(KERN_INFO "Enter %s\n", __FUNCTION__);
    return platform_driver_register(&led_driver);
    return 0;
}

static void __exit led_exit(void)
{
    platform_driver_unregister(&led_driver);
    printk(KERN_INFO "Exit Hello world\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

另外一种

#include <linux/kernel.h>
#include <linux/gpio.h>

#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>


#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
//#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define GPIO_LOW 0
#define GPIO_HIGH 1
int gpio;
int major;
static struct class *cls;
static int led_open(struct inode *inode, struct file *file)
{
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);
    
    return 0;    
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
    int ret;
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);

    ret = copy_from_user(&val, buf, count); //    copy_to_user();

    if (val == 1)
    {
        // 点灯
        gpio_set_value(gpio,GPIO_LOW);
    }
    else
    {
        // 灭灯
        gpio_set_value(gpio,GPIO_HIGH);
    }
    
    return 0;
}
static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,   
    .open   =   led_open,    
    .write    =    led_write,      
};

static int led_probe(struct platform_device *pdev)
{
    int ret ;
    int i;

    enum of_gpio_flags flag;
    struct device_node *led_node = pdev->dev.of_node;

    
    
    printk(KERN_EMERG "%s-%d: enter\n",__FUNCTION__,__LINE__);

    gpio = of_get_named_gpio_flags(led_node,"led-gpio", 0,&flag);
    if (!gpio_is_valid(gpio)){
        printk(KERN_INFO "hello: invalid gpio : %d\n",gpio);
        return -1;
    }
    ret = gpio_request(gpio, "led");
    if (ret) {
        gpio_free(gpio);
        return -EIO;
    }

    gpio_direction_output(gpio, GPIO_HIGH);

    for(i=0; i < 10; i++)
    {
        gpio_set_value(gpio,GPIO_LOW);
        mdelay(500);
        gpio_set_value(gpio,GPIO_HIGH);
        mdelay(500);
    }
    
    
    major = register_chrdev(0, "myled", &led_fops);

    cls = class_create(THIS_MODULE, "myled");

    device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    
    
    gpio_set_value(gpio,GPIO_LOW);
        
    printk(KERN_INFO "%s-%d: exit\n",__FUNCTION__,__LINE__);
    
    
    
    return 0;  //return Ok
}


static int led_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "Enter %s\n", __FUNCTION__);
    gpio_free(gpio);
    device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "myled");
    return 0;
}
static const struct of_device_id of_led_match[] = {
    { .compatible = "led_test" },
    { /* Sentinel */ }
};
static struct platform_driver led_driver = {
    .probe        = led_probe,
    .remove        = led_remove,
    .driver        = {
        .name    = "led",
        .owner    = THIS_MODULE,
        .of_match_table    = of_led_match,
    },

};

static int __init led_init(void)
{
    printk(KERN_INFO "Enter %s\n", __FUNCTION__);
    return platform_driver_register(&led_driver);
    return 0;
}

static void __exit led_exit(void)
{
    platform_driver_unregister(&led_driver);
    printk(KERN_INFO "Exit Hello world\n");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

另外,该平台下的驱动,在内核需要修改一些东西,这个板子涉及到了设备树,需要修改设备树文件:

我的设备树文件路径:/rk3399/source/g3399-v7-1-2-20180529/kernel/arch/arm64/boot/dts/rockchip

可能不同的板子会有些微差别,设备树文件:g3399-baseboard.dtsi(这个具体看自己的),设备树的东西不描述,自行研究

添加内容:(位置随便)

hello-led{
                compatible = "led_test";
                led-gpio = <&gpio0 8 GPIO_ACTIVE_LOW>;
                status = "okay";
        };

接下来是增加Kconfig 和Makefile 新建一个目录 放进去 目录位置/path/xxxxxxx/kernel/driver/

该目录下的Makefile新增:obj-y             += test/

Kconfig新增:

source "drivers/test/Kconfig"

上面的test为文件夹名字

接下来进入test文件夹添加自己的Makefile 和Kconfig

具体内容如下:Makefile:

obj-$(CONFIG_HELLO)     += hello.o

hello是驱动程序.c文件的名字 CONFIG_HELLO这个一般命名格式就是CONFIG_xxx

Kconfig:

config HELLO
        tristate "led test"
        help
          Hello for test

这个地方注意 这里的HELLO和Makefile里面CONFIG_xxx中的xxx一致,tristate "led test" 双引号里面的内容可以自己修改,help是帮助信息,自己修改。

关于程序,论坛有一些demo,可以参考,不过怎么样就看自己了

关于驱动程序运行结果:

g3399:/storage/0000-0000 # insmod test
test.ko         test_char.ko
g3399:/storage/0000-0000 # insmod test.ko                                      
[  355.467922] Exit Hello world
[ 3372.497665] Enter led_init
[ 3372.498181] led_probe-77: enter
[ 3382.500052] led_probe-104: exit
g3399:/storage/0000-0000 # rmmod test
[ 3385.692121]  remove
g3399:/storage/0000-0000 # 

目前写了个测试程序,一执行,结果很无奈(这个是测试程序,不是驱动程序)

g3399:/storage/0000-0000 # ./cmd_test                                          
/system/bin/sh: ./cmd_test: can't execute: Permission denied

关于它的权限:-rwxrwx--x 1 root sdcard_rw   8502 2018-11-05 00:01 cmd_test

感觉没毛病啊,居然无法访问,另外:发现开发板的文件系统使用cp命令时,出现下面的问题:

g3399:/storage/0000-0000 # cp -rf test.ko ../../                             
cp: ../..//test.ko: Read-only file system

可能由于文件系统的原因导致的,不知道怎么弄,有没有哪位高手遇到过,求解决

Note:上面的一切命令均在root权限下运行

最后的最后补充一点;驱动程序创建的设备结点可以用 ls /dev 查看

 

猜你喜欢

转载自blog.csdn.net/qq_33166886/article/details/83752935