驱动入门笔记
admin
2024-02-04 01:28:31

基于正点原子IMX6UL开发板的笔记

文章目录

        • 40 字符设备驱动开发
          • c_cpp_properties.json
          • chrdevbase.c
          • chrdevbaseApp.c
          • Makefile
          • 复制chrdevbase.ko chrdevbaseApp 到 lib/modules/4.1.15/
          • 启动板卡测试
        • 41 Linux LED驱动开发实验
          • ioremap iounmap 物理地址映射到虚拟地址
          • led.c
          • ledApp.c
          • Makefile
          • 复制led.ko ledApp 到 lib/modules/4.1.15/
          • 启动板卡测试

40 字符设备驱动开发

c_cpp_properties.json
{ "configurations": [ { "name": "Linux", "includePath": [ "${workspaceFolder}/**", "/home/yinliang/linux/kernel/nxp_linux/include","/home/yinliang/linux/kernel/nxp_linux/arch/arm/include","/home/yinliang/linux/kernel/nxp_linux/arch/arm/include/generated"], "defines": [], "compilerPath": "/usr/bin/gcc", "cStandard": "c11", "cppStandard": "c++17", "intelliSenseMode": "clang-x64" } ], "version": 4 
} 
chrdevbase.c
#include 
#include 
#include 
#include 
#include 
#include 
/***************************************************************  
文件名  : chrdevbase.c 
作者    : junluoyu 
版本    : V1.0 
描述    : chrdevbase驱动文件。 
其他    : 无 
日志    : 初版 V1.0 2022/11/28 
***************************************************************/#define CHRDEVBASE_MAJOR  200             /* 主设备号 */
#define CHRDEVBASE_NAME  "chrdevbase"     /* 设备名   */static char readbuf[100];                 /* 读缓存   */
static char writebuf[100];                /* 写缓存   */ 
static char kerneldata[] = {"kernel data!"};/** @description    : 打开设备* @param - inode  :传递给驱动的inode * @param - filp   :设备文件,file结构体有给叫做private_data的成员变量*                   ,一般再open的时候将private_data指向设备的结构体 * @return         :0成功;其他失败*/static int chrdevbase_open(struct inode *inode, struct file *filp){printk("chrdevbase open!\r\n");return 0;}/** @description    : 从设备读取数据* @param - buf    :返回给用户空间的数据缓冲区* @param - filp   :要打开的设备文件(文件描述符)* @param - cnt    :要读取的数据长度* @param - offt   :相对于文件首地址的偏移* @return         :读取的字节数,负值表示读取失败*/static ssize_t chrdevbase_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){int retvalue = 0;/*向用户空间发送数据 */memcpy(readbuf, kerneldata, sizeof(kerneldata));retvalue = copy_to_user(buf, readbuf, cnt);if(retvalue == 0) {printk("kernel senddata ok!\r\n");}else{printk("kernel senddata failed!\r\n");}//printk("chrdevbase read!\r\n");return 0;}/** @description    : 从设备写数据* @param - buf    :要写给设备写入的数据* @param - filp   :设备文件,表示打开的文件描述符* @param - cnt    :要写入的数据长度* @param - offt   :相对于文件首地址的偏移* @return         :写入的字节数,负值表示读取失败*/static ssize_t chrdevbase_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt){int retvalue = 0;/*接收用户空间传递给内核的数据并打印出来*/retvalue = copy_from_user(writebuf, buf, cnt);if(retvalue == 0) {printk("kernel recvdata: %s\r\n", writebuf);}else{printk("kernel recvdata failed!\r\n");}//printk("chrdevbase write!\r\n");return 0;}/** @description    : 关闭、释放设备* @param - filp   :要关闭的设备文件* @return         :0成功;其他 失败*/static int chrdevbase_release(struct inode *inode, struct file *filp){// printk("chrdevbase release! \r\n");return 0;}/** 设备操作结构体*/
static struct file_operations chrdevbase_fops = {.owner = THIS_MODULE,.open  = chrdevbase_open,.read  = chrdevbase_read,.write = chrdevbase_write,.release = chrdevbase_release,
};/** @description    : 驱动入口函数* @param - filp   :无* @return         :0成功; 其他 失败*/static int __init chrdevbase_init(void){int retvalue = 0;/* 注册字符设备驱动 */retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME,&chrdevbase_fops);if (retvalue < 0){printk("chrdevbase driver register failed\r\n");}printk("chrdevbase_init()\r\n"); return 0;}/** @description    : 驱动出口函数* @param - filp   :无* @return         :无*/void __exit chrdevbase_exit(void){/* 注销字符设备驱动 */unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);printk("chrdevbase_exit()\r\n"); }/** 将上面两个函数指定为驱动的入口和出口函数*/module_init(chrdevbase_init);module_exit(chrdevbase_exit);/** LICENSE和作者信息*/MODULE_LICENSE("GPL");MODULE_AUTHOR("junluoyu");
chrdevbaseApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include  
/***************************************************************  
文件名  : chrdevbaseApp.c 
作者    : junluoyu 
版本    : V1.0 
描述    : chrdevbase驱动测试APP 
其他    : 无 
日志    : 初版 V1.0 2022/11/28 
其他    : ./chrdevbaseApp /dev/chrdevbase 1(读文件)|2(写文件)
***************************************************************/
static char usrdata[] = {"usr data!"};/** @description  : main主程序* @param -argc  :argv数组元素长度* @param -argv  :具体参数*/ 
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;char readbuf[100], writebuf[100];if (argc != 3) {printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动文件 */fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s \r\n", filename);return -1;}if (atoi(argv[2]) == 1) { /* 从驱动文件读取数据 */retvalue = read(fd, readbuf, 50);if (retvalue < 0) {printf("read file %s failed!\r\n", filename);} else {/* 读取成功,打印出读取成功的数据*/printf("read data:%s \r\n", readbuf);}}if (atoi(argv[2]) == 2) { /* 向驱动文件写数据 */memcpy(writebuf, usrdata, sizeof(usrdata));retvalue = write(fd, writebuf, 50);if (retvalue < 0) {printf("write file %s failed!\r\n", filename);} }/* 关闭设备 */retvalue = close(fd);if (retvalue < 0) {printf("Can't close file %s failed!\r\n", filename);return -1;} return 0;
}
Makefile
KERNELDIR := /home/yinliang/linux/kernel/nxp_linux
CURERENT_PATH := $(shell pwd)
obj-m := chrdevbase.obuild: kernel_modulskernel_moduls:$(MAKE) -C $(KERNELDIR) M=$(CURERENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURERENT_PATH) clean
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
file chrdevbaseApp
复制chrdevbase.ko chrdevbaseApp 到 lib/modules/4.1.15/
sudo cp chrdevbase.ko chrdevbaseApp /home/yinliang/linux/nfs/rootfs/lib/modules/4.1.15/ -f 
启动板卡测试
cd /lib/modules/4.1.151.安装驱动模块
insmod chrdevbase.ko  modprobe chrdevbase.ko          #会安装依赖文件2.挂载
cat /proc/devices               #看有没有设备mknod /dev/chrdevbase c 200 0   #mknod创建节点命令,/dev/chrdevbase是创建的节点文件,c表示是字符设备,200是主设备号,0表示设备的次设备号ls /dev/chrdevbase -l3.测试
chmod 777 /dev/chrdevbase./chrdevbaseApp /dev/chedevbase 1./chrdevbaseApp /dev/chedevbase 24.卸载驱动模块
rmmod chrdevbase.ko  modprobe -r chrdevbase.ko    #会卸载依赖文件

41 Linux LED驱动开发实验

ioremap iounmap 物理地址映射到虚拟地址
IMX6U_CCM_CCGR1    = ioremap(CCM_CCGR1_BASE   ,4);
// 物理地址映射到虚拟地址  4代表4个字节
iounmap(IMX6U_CCM_CCGR1   );
// 取消映射关系
led.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include /***************************************************************  
文件名  : led.c 
作者    : junluoyu 
版本    : V1.0 
描述    : led驱动文件。 
其他    : 无 
日志    : 初版 V1.0 2022/11/20 
***************************************************************//* 寄存器物理地址 */
#define CCM_CCGR1_BASE         (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_DR_BASE          (0X0209C000)
#define GPIO1_GDIR_BASE        (0X0209C004)/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;#define LED_MAJOR  200            /* 主设备号 */
#define LED_NAME  "led"            /* 设备名   */#define LEDOFF 0 //关灯
#define LEDON  1 //开灯/** @description    : led打开/关闭* @param - sta    :LED0N 0 打开LED  LEDOFF 1 关闭LED* @return         :无*/void led_switch(u8 sta){u32 val = 0;if(sta == LEDON) {val = readl(GPIO1_DR);val &= ~(1<<3);writel(val,GPIO1_DR);} else if (sta == LEDOFF) {val = readl(GPIO1_DR);val |= (1<<3);writel(val,GPIO1_DR);}}/** @description    : 打开设备* @param - inode  :传递给驱动的inode * @param - filp   :设备文件,file结构体有给叫做private_data的成员变量*                   ,一般再open的时候将private_data指向设备的结构体 * @return         :0成功;其他失败*/static int led_open(struct inode *inode, struct file *filp){return 0;}/** @description    : 从设备读取数据* @param - buf    :返回给用户空间的数据缓冲区* @param - filp   :要打开的设备文件(文件描述符)* @param - cnt    :要读取的数据长度* @param - offt   :相对于文件首地址的偏移* @return         :读取的字节数,负值表示读取失败*/static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){return 0;}/** @description    : 从设备写数据* @param - buf    :要写给设备写入的数据* @param - filp   :设备文件,表示打开的文件描述符* @param - cnt    :要写入的数据长度* @param - offt   :相对于文件首地址的偏移* @return         :写入的字节数,负值表示读取失败*/static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt){int retvalue = 0;unsigned char databuf[1];unsigned char ledstat;/*接收用户空间传递给内核的数据并打印出来*/retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed: \r\n");return -EFAULT;}ledstat = databuf[0];        //读取状态值if(ledstat == LEDON) {led_switch(LEDON);       //打开LED灯}else if(ledstat == LEDOFF) {led_switch(LEDOFF);      //关闭LED灯}return 0;}/** @description    : 关闭、释放设备* @param - filp   :要关闭的设备文件* @return         :0成功;其他 失败*/static int led_release(struct inode *inode, struct file *filp){// printk("led release! \r\n");return 0;}/** 设备操作结构体*/
static struct file_operations led_fops = {.owner = THIS_MODULE,.open  = led_open,.read  = led_read,.write = led_write,.release = led_release,
};/** @description    : 驱动入口函数* @param - filp   :无* @return         :0成功; 其他 失败*/static int __init led_init(void){int retvalue = 0;u32 val = 0;/* 初始化LED *//* 1.寄存器地址映射 */IMX6U_CCM_CCGR1      = ioremap(CCM_CCGR1_BASE   ,4);SW_MUX_GPIO1_IO03    = ioremap(SW_MUX_GPIO1_IO03_BASE ,4);SW_PAD_GPIO1_IO03    = ioremap(SW_PAD_GPIO1_IO03_BASE ,4);GPIO1_DR             = ioremap(GPIO1_DR_BASE          ,4);GPIO1_GDIR           = ioremap(GPIO1_GDIR_BASE        ,4);/* 2.使能GPIO1时钟 */val = readl(IMX6U_CCM_CCGR1);val &= ~(3<<26);  /* 清除以前的设置 */val |= (3<<26);   /* 设置新值      */writel(val, IMX6U_CCM_CCGR1);/* 3.设置GPIO1_IO03的复用功能,将其复用为GPIO01_IO03,最后设置IO属性 */writel(5, SW_MUX_GPIO1_IO03);           //mux复用功能为5writel(0X10B0, SW_PAD_GPIO1_IO03);      //PAD设置IO属性/* 4.设置GPIO1_IO03为输出功能*/val = readl(GPIO1_GDIR);val &= ~(1<<3);  /* 清除以前的设置 */val |=  (1<<3);  /* 设置为输出     */writel(val,GPIO1_GDIR);/* 5.默认关闭LED*/val = readl(GPIO1_DR);val |= (1<<3); //默认为关,1电平writel(val, GPIO1_DR);/* 6.注册字符设备驱动 */retvalue = register_chrdev(LED_MAJOR, LED_NAME,&led_fops);if (retvalue < 0){printk("register led failed\r\n");return -EIO;}return 0;}/** @description    : 驱动出口函数* @param - filp   :无* @return         :无*/void __exit led_exit(void){/* 取消映射 */iounmap(IMX6U_CCM_CCGR1   );iounmap(SW_PAD_GPIO1_IO03 );      iounmap(SW_MUX_GPIO1_IO03 );      iounmap(GPIO1_DR          );      iounmap(GPIO1_GDIR        );      /* 注销字符设备驱动 */unregister_chrdev(LED_MAJOR,LED_NAME);}/** 将上面两个函数指定为驱动的入口和出口函数*/module_init(led_init);module_exit(led_exit);/** LICENSE和作者信息*/MODULE_LICENSE("GPL");MODULE_AUTHOR("junluoyu");
ledApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************  
文件名  : ledApp.c 
作者    : junluoyu 
版本    : V1.0 
描述    : led驱动测试APP 
其他    : 无 
日志    : 初版 V1.0 2022/11/28 
其他    : ./ledApp /dev/led 1(打开LED)|0(关闭LED)
***************************************************************/
#define LEDOFF 0 //关灯
#define LEDON  1 //开灯/** @description  : main主程序* @param -argc  :argv数组元素长度* @param -argv  :具体参数*/ 
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char databuf[1];if (argc != 3) {printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开驱动文件 */fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s \r\n", filename);return -1;}databuf[0] = atoi(argv[2]);/* 向/dev/led文件写入数据 */retvalue = write(fd, databuf,sizeof(databuf));if (retvalue < 0) {printf("led control failed!\r\n");close(fd);return -1;} /* 关闭led设备 */retvalue = close(fd);if (retvalue < 0) {printf("file %s close failed!\r\n", argv[1]);return -1;} return 0;
}
Makefile
KERNELDIR := /home/yinliang/linux/kernel/nxp_linux
CURERENT_PATH := $(shell pwd)
obj-m := led.obuild: kernel_modulskernel_moduls:$(MAKE) -C $(KERNELDIR) M=$(CURERENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURERENT_PATH) clean
make -j16
arm-linux-gnueabihf-gcc led.c -o ledApp
file chrdevbaseApp
复制led.ko ledApp 到 lib/modules/4.1.15/

sudo cp led.ko ledApp /home/yinliang/linux/nfs/rootfs/lib/modules/4.1.15/ -f 
启动板卡测试
cd /lib/modules/4.1.151.安装驱动模块  第一次加载先使用depmod
insmod led.ko  modprobe led.ko          #会安装依赖文件2.挂载
cat /proc/devices               #看有没有设备mknod /dev/led c 200 0   #mknod创建节点命令,/dev/led是创建的节点文件,c表示是字符设备,200是主设备号,0表示设备的次设备号ls /dev/led -l3.测试
chmod 777 /dev/led./ledApp /dev/led 1  //开灯./ledApp /dev/led 2  //关灯4.卸载驱动模块
rmmod ledbase.ko  modprobe -r ledbase.ko    #会卸载依赖文件

相关内容

热门资讯

国家电投集团发布全球首套超高温... 转自:新华财经新华财经北京12月26日电(记者沈寅飞)国家电投集团25日在北京正式发布全球首套超高温...
大盘7连阳留下3个不足 建议不...   长沙晚报掌上长沙12月25日讯(全媒体记者 刘军)A股三大指数25日集体小幅上扬,沪指日线7连阳...
五大维度回顾2025年14款年... 在政策托底与竞争加剧的双重拉扯下,2025年的新车市场比以往更“现实”。一方面,“以旧换新”继续加力...
中国铝业股价涨5.66%,中银... 12月26日,中国铝业涨5.66%,截至发稿,报11.58元/股,成交29.48亿元,换手率1.99...
中国铝业股价涨5.66%,国泰... 12月26日,中国铝业涨5.66%,截至发稿,报11.58元/股,成交29.53亿元,换手率2.00...