rk3399平臺下的字元裝置驅動
最近在搞一款瑞芯微平臺的板子,型號:rk3399 ,這幾天剛剛上手,前幾天一直在搞環境,熟悉這個板子,這個板子跑的Android7.1的系統。在網上開到這塊的東西不是很多,自己也想做個記錄。然後,開始吧:我目前用的板子是葡萄雨科技的,因為這家公司給的資料
很少,之前一直在熟悉這個板子的目錄結構,裝置樹檔案再哪兒,以及這個公司給的編譯指令碼,關於編譯原始碼這一塊,瑞芯微的開源論壇也有講,這一塊就不多說,先說說我乾的事情吧:寫了一個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/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 檢視