1. 程式人生 > >rk3399平臺下的字元裝置驅動

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 檢視