HDF驅(qū)動(dòng)框架探路:對(duì)比Linux原生驅(qū)動(dòng)開(kāi)發(fā)在Imx6ull板子點(diǎn)燈
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
老規(guī)矩還是將最終希望跑出來(lái)的效果放出來(lái)。如下:

HDF驅(qū)動(dòng)框架探路5:
前言
想要深入了解HDF框架的話,應(yīng)該繞不開(kāi)linux驅(qū)動(dòng)程序的掌握。由于是在看了韋東山老師對(duì)openharmony做的移植后,覺(jué)得linux驅(qū)動(dòng)的內(nèi)功還是必須要有的,所以本文章對(duì)比linux應(yīng)用在imx6ull中點(diǎn)亮LED燈。所以先修煉修煉內(nèi)功。
本文框架圖

本文的框架圖是最近這段時(shí)間結(jié)合了對(duì)3516測(cè)試HDF框架,以及imx6ull上linux驅(qū)動(dòng)程序的學(xué)習(xí),所得出的,是基于目前社區(qū)中所用的比較多的幾款板子和openharmony、linux對(duì)比所做的圖,大佬們覺(jué)得這個(gè)圖有任何問(wèn)題,歡迎批評(píng)指出。
1.驅(qū)動(dòng)程序
1.1 最簡(jiǎn)單的驅(qū)動(dòng)程序邏輯

- 如上圖所示,首先有個(gè)驅(qū)動(dòng)程序入口函數(shù)和出口函數(shù)分別是module_init(led_init),module_exit(led_exit);
- 然后分別實(shí)現(xiàn)led_open和led_write這兩個(gè)業(yè)務(wù)函數(shù)去填充file_operations結(jié)構(gòu)體。
- 最后把file_operations結(jié)構(gòu)體放入register_chrdev函數(shù)進(jìn)行注冊(cè),然后放入入口函數(shù)中。
- 因?yàn)轵?qū)動(dòng)程序的字符設(shè)備需要綁定IO設(shè)備去使用,所以在入口函數(shù)中調(diào)用class_create和device_create。
1.2 完成的實(shí)現(xiàn)代碼如下:
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/delay.h>
- #include <linux/poll.h>
- #include <linux/mutex.h>
- #include <linux/wait.h>
- #include <linux/uaccess.h>
- #include <linux/device.h>
- #include <asm/io.h>
- static int major;
- static struct class *led_class;
- /* registers */
- // IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
- static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
- // GPIO5_GDIR 地址:0x020AC004
- static volatile unsigned int *GPIO5_GDIR;
- //GPIO5_DR 地址:0x020AC000
- static volatile unsigned int *GPIO5_DR;
- static ssize_t led_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *ppos)
- {
- char val;
- int ret;
- /* copy_from_user : get data from app */
- ret = copy_from_user(&val, buf, 1);
- /* to set gpio register: out 1/0 */
- if (val)
- {
- /* set gpio to let led on */
- *GPIO5_DR &= ~(1<<3);
- }
- else
- {
- /* set gpio to let led off */
- *GPIO5_DR |= (1<<3);
- }
- return 1;
- }
- static int led_open(struct inode *inode, struct file *filp)
- {
- /* enable gpio5
- * configure gpio5_io3 as gpio
- * configure gpio5_io3 as output
- */
- *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
- *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
- *GPIO5_GDIR |= (1<<3);
- return 0;
- }
- static struct file_operations led_fops = {
- .owner = THIS_MODULE,
- .write = led_write,
- .open = led_open,
- };
- /* 入口函數(shù) */
- static int __init led_init(void)
- {
- printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
- major = register_chrdev(0, "hello_led", &led_fops);
- /* ioremap */
- // IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
- IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);
- // GPIO5_GDIR 地址:0x020AC004
- GPIO5_GDIR = ioremap(0x020AC004, 4);
- //GPIO5_DR 地址:0x020AC000
- GPIO5_DR = ioremap(0x020AC000, 4);
- led_class = class_create(THIS_MODULE, "helloled");
- device_create(led_class, NULL, MKDEV(major, 0), NULL, "helloled"); /* /dev/myled */
- return 0;
- }
- static void __exit led_exit(void)
- {
- iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
- iounmap(GPIO5_GDIR);
- iounmap(GPIO5_DR);
- device_destroy(led_class, MKDEV(major, 0));
- class_destroy(led_class);
- unregister_chrdev(major, "hello_led");
- }
- module_init(led_init);
- module_exit(led_exit);
- MODULE_LICENSE("GPL");
2.驅(qū)動(dòng)程序測(cè)試部分
2.1 測(cè)試模塊實(shí)現(xiàn)思路
在linux內(nèi)核中注冊(cè)相應(yīng)的驅(qū)動(dòng)模塊后,通過(guò)glibc庫(kù)函數(shù)提供的open,read,write接口訪問(wèn)驅(qū)動(dòng)程序綁定驅(qū)動(dòng)字符設(shè)備的IO文件就可以直接調(diào)用到對(duì)應(yīng)的驅(qū)動(dòng)程序了。
2.2 測(cè)試部分完成實(shí)現(xiàn)代碼
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdio.h>
- // ledtest /dev/helloled on
- // ledtest /dev/helloled off
- int main(int argc, char **argv)
- {
- int fd;
- char status = 0;
- if (argc != 3)
- {
- printf("Usage: %s <dev> <on|off>\n", argv[0]);
- printf(" eg: %s /dev/helloled on\n", argv[0]);
- printf(" eg: %s /dev/helloled off\n", argv[0]);
- return -1;
- }
- // open
- fd = open(argv[1], O_RDWR);
- if (fd < 0)
- {
- printf("can not open %s\n", argv[0]);
- return -1;
- }
- // write
- if (strcmp(argv[2], "on") == 0)
- {
- status = 1;
- }
- write(fd, &status, 1);
- return 0;
- }
3.編譯
3.1 編譯思路:
- 首先需要將驅(qū)動(dòng)程序編譯成ko文件。
- 將測(cè)試程序編譯成可執(zhí)行文件。
3.2 完成實(shí)現(xiàn)代碼如下:
- KERN_DIR = /home/qzk/code/imx6ullPro/Linux-4.9.88
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- $(CROSS_COMPILE)gcc -o ledtest ledtest.c
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- rm -rf modules.order
- rm -f ledtest
- obj-m += led_drv.o
上述代碼需要的注意的,大家在使用時(shí)候需要換掉KERN_DIR中的值,換成大家自己的內(nèi)核目錄,因?yàn)榫幾g時(shí)候會(huì)去這個(gè)目錄下找頭文件。
4.安裝驅(qū)動(dòng)進(jìn)行測(cè)試
4.1 安裝驅(qū)動(dòng)思路
通過(guò)上述的步驟,大家會(huì)發(fā)現(xiàn)驅(qū)動(dòng)程序編譯好了放在了ubuntu系統(tǒng)中,我們的目標(biāo)是需要將驅(qū)動(dòng)程序安裝進(jìn)入imx6ull中,所以我們的目標(biāo)是將驅(qū)動(dòng)程序放入imx6ull中。這里的方案是:將網(wǎng)線插入電腦,然后串口連接imx6ull,先各自寫(xiě)死ip地址,目標(biāo)是二者能夠ping通,然后搭建nfs,這樣就達(dá)到了imx6ull訪問(wèn)ubuntu下的驅(qū)動(dòng)程序的目的
4.2 搭建好環(huán)境后進(jìn)行安裝驅(qū)動(dòng)
通過(guò)insmod命令進(jìn)行安裝。

4.3 執(zhí)行測(cè)試文件去點(diǎn)亮,熄滅燈
如下圖:執(zhí)行命令

沒(méi)什么意外的話,這盞燈就在你的掌控之中了
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
https://harmonyos.51cto.com/resource/1583
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)