最近在搞一款瑞芯微平台的板子,型号:rk3399
,这几天刚刚上手,前几天一直在搞环境,熟悉这个板子,这个板子跑的Android7.1的系统。在网上开到这块的东西不是很多,自己也想做个记录。然后,开始吧:
之前一直在熟悉这个板子的目录结构,设备树文件再哪儿,以及编译脚本,关于编译源码这一块,firefly的论坛也有讲,这一块就不多说,先说说我干的事情吧:写了一个led驱动,很简单,目的当然就是点灯,体验一下这个平台的驱动与我之前接触的有什么不同,所以先测试一下,接下来是我用的几种方式:
* 编译进内核,这种方式 系统一上电,在一定时候会自动加载模块,进入驱动程序的probe,这个是两种方式的唯一不同之处
* 区别: 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/miscdevice.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 long hello_ioctl(
struct file *files, unsigned int cmd, unsigned long arg){ //printk("cmd is
%d,arg is %d\n",cmd,arg); if(cmd > 1){ printk(KERN_EMERG "cmd is 0 or 1\n"); }
if(arg > 1){ printk(KERN_EMERG "arg is only 1\n"); } gpio_set_value(gpio,cmd);
return 0; } static int hello_release(struct inode *inode, struct file *file){
printk(KERN_EMERG "hello release\n"); return 0; } static int hello_open(struct
inode *inode, struct file *file){ printk(KERN_EMERG "hello open\n"); return 0;
} static struct file_operations hello_ops = { .owner = THIS_MODULE, .open =
hello_open, .release = hello_release, .unlocked_ioctl = hello_ioctl, }; static
struct miscdevice hello_dev = { .minor = MISC_DYNAMIC_MINOR, .name =
"hello-ctl", .fops = &hello_ops, }; 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); } misc_register(&hello_dev); 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_EMERG
"\tremove\n"); gpio_free(gpio); misc_deregister(&hello_dev); 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, }, };
module_platform_driver(led_driver); 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
上述原因已解决,原因时由于系统编译构建文件系统的时候没有给该文件目录对应的权限,解决办法,把文件复制到/data/....../...../具体目录记不住了,ls
-l 命令看一下各目录的权限就好了
Note:上面的一切命令均在root权限下运行
最后的最后补充一点;驱动程序创建的设备结点可以用 ls /dev 查看