静安网站建设关键词优化seo,找装修公司上什么网站,php做网站需要学的东西,个人做网站需要多少钱目录 一、多节点概念1、所用到的结构体说明2、函数接口主要是read和write函数2.1、把应用层的数据拷贝给底层2.2、把应用层的数据拷贝给底层 3、应用层的read和write函数4、底层的read和write函数二、ioctl控制命令接口1、概念2、函数介绍应用层和驱动层 三、代码与现象1.编写L… 目录 一、多节点概念1、所用到的结构体说明2、函数接口主要是read和write函数2.1、把应用层的数据拷贝给底层2.2、把应用层的数据拷贝给底层 3、应用层的read和write函数4、底层的read和write函数二、ioctl控制命令接口1、概念2、函数介绍应用层和驱动层 三、代码与现象1.编写LED灯的多节点驱动实现流水灯2.编写KEY按键驱动实现底层按键按下反馈给应用层3.编写beep的驱动代码可以使用ioctl控制 学习目标 1.编写LED灯的多节点驱动实现流水灯 2.编写KEY按键驱动实现底层按键按下反馈给应用层 3.编写beep的驱动代码可以使用ioctl控制 一、多节点概念
这里所谓的多节点驱动其实指的就是一个设备对应一个设备节点。比如我现在有 4 个 led 等你怎么做到单独的去控制每一盏灯。此时如果你想单独的去操控一个 LED 灯那么你就需要单独给他们每一个灯去申请注册一个设备节点。多节点对于一类设备他们的主设备号必然是一样的只不过是一类设备当中的不同的子设备。 比如有四个灯那么他们的设备编号如下 LED1 crwx----------- 350 0 /dev/led1 LED2 crwx----------- 350 1 /dev/led2 LED3 crwx----------- 350 2 /dev/led3 LED4 crwx----------- 350 3 /dev/led4 此时你在去使用应用层的 open 去打开单独的一个硬件设备节点这样你就可以单独的操作这个设备。
1、所用到的结构体说明
这里内核是通过你 open 打开对应节点的设备号去区分不同的设备的靠的就是函数的参数。 int (*open) (struct inode *, struct file *); struct inode { i_rdev如果索引节点代表设备文件则表示设备的主设备号和次设备号 }; struct file{ void *private_data; } private_data他是一个私有数据他主要就是给其他函数使用的
2、函数接口主要是read和write函数
函数接口里主要讲解的就是 read 和 write是 对数据进行读写操作的。内核是不允许让应用层和底层进行直接数据交互的就是为了保护内核的安全。那么内核层和应用能不能进行数据交互呢肯定是可以的但是必须要使用内核提供的函数进行数据的交互。所谓的数据交互就是应用层把数据拷贝给内核层内核层把数据拷贝给应用层这就是所谓的数据交互。上层的写函数和读函数是不能直接给底层的写函数和读函数进行数据若想直接交互的话这里就需要使用以下两个函数
2.1、把应用层的数据拷贝给底层
函数功能把应用层的数据拷贝给底层 函数原型unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) 函数头文件:#include linux/uaccess.h 函数参数to他就是你要保存应用层数据的位置把要接受的数据保存到哪里 from他就是底层写函数的里的 buf n就是数据的大小 — from 函数返回值成功返回 0 失败返回负数
2.2、把应用层的数据拷贝给底层
函数功能把内核层的数据拷贝给应用层 函数原型unsigned long copy_to_user(void __user *to, const void *from, unsigned long n) 函数头文件:#include linux/uaccess.h 函数参数to就是底层读函数里的 buf from: 要拷贝底层的具体数据 n就是数据的大小 即from大小 函数返回值成功返回 0 失败返回负数
3、应用层的read和write函数
ssize_t read(int fd, void *buf, size_t count) 参数fd: — 打开文件的描述符 buf: — 读取数据保存的位置 count---- 读取数据的大小
ssize_t write(int fd, const void *buf, size_t count) 参数fd: — 打开文件的描述符 buf: — 你要写入文件的数据的保存位置 count---- 写入数据的大小
4、底层的read和write函数
**ssize_t (*read) (struct file *fp, char __user buf, size_t size, loff_t offset) 参数fp:保存文件信息的核心结构体 buf:保存底层给应用层读取数据的保存位置 里面就保存内核要给用层的数据。 size就数据的大小 — 就是 buf 的大小 offset当前文件光标的位置 **ssize_t (write) (struct file fp, const char __user buf, size_t size,loff_toffset) 参数fp:保存文件信息的核心结构体 buf:保存应用层给底层写人数据保存位置 size就数据的大小 — 就是 buf 的大小 offset当前文件光标的位置
二、ioctl控制命令接口
1、概念
ioctl用来做一些控制命令接口 类似msgctl、semctl、shmctl 等(ioctl 是设备 驱动程序中对设备的 I/O 通道进行管理的函数。所谓对 I/O 通道进行管理就 是对设备的一些特性进行控制例如串口的传输波特率、马达的转速等等)
2、函数介绍应用层和驱动层
函数功能做一些控制命令的接口 函数原型int ioctl(int fd, unsigned long request, …) 函数头文件:#include sys/ioctl.h 函数参数fd就是 open 打开的文件描述符 requestcmd 就是控制的命令 函数返回值成功返回 0 失败返回负数 函数功能做控制的命令 函数原型**long (unlocked_ioctl) (struct file fp, unsigned int cmd, unsigned long arg); 函数头文件:#include linux/fs.h 函数参数fp:保存文件信息结构体 cmd:就是你应用层传递过来的命令 — request arg:暂时不用管 函数返回值成功返回 0 失败负数
三、代码与现象
1.编写LED灯的多节点驱动实现流水灯
内核层
#include linux/module.h
#include linux/kernel.h
#include linux/of.h
#include linux/of_gpio.h
#include linux/cdev.h
#include linux/gpio.h
#include linux/device.h
#include linux/fs.h
#include linux/platform_device.h
int led[2]{0};
const char *devs_name[2]{myqxjled1,myqxjled2};
int i;
dev_t dev[2]{0};
struct cdev mydev;
struct class *myclassNULL;int myled_open (struct inode *inode, struct file *fp)
{if(inode-i_rdevdev[0]){gpio_set_value(led[0],1);}if(inode-i_rdevdev[1]){gpio_set_value(led[1],1);}printk(myled open 正确打开\n);return 0;
}int myled_close (struct inode *inode, struct file *fp)
{if(inode-i_rdevdev[0]){gpio_set_value(led[0],0);}if(inode-i_rdevdev[1]){gpio_set_value(led[1],0);}printk(myled close 关闭正确\n);return 0;
}
struct file_operations myfops{.open myled_open,.release myled_close,
};int myled_probe(struct platform_device *pdev)
{printk(探测函数设备端和驱动端匹配成功\n);//led[0] led[1]返回的是gpio编口号led[0]of_get_named_gpio(pdev-dev.of_node,leds-gpios,0);//获得设备树的属性led[1]of_get_named_gpio(pdev-dev.of_node,leds-gpios,1);gpio_request(led[0], led1 pc5);//21 申请你要使用 gpio 口的资源gpio_request(led[1], led2 pc6);//22gpio_direction_output(led[0],0);//配置 gpio 口的工作模式gpio_direction_output(led[1],0); alloc_chrdev_region(dev,0,2,led);//动态申请设备号 linux2.6或杂项类型dev[1]dev[0]1;cdev_init(mydev,myfops);//初始化核心结构体cdev_add(mydev,dev[0],2);//向内核去申请 linux2.6 字符设备myclassclass_create(THIS_MODULE,class_led);//创建类if(myclass NULL){printk(class_create error\n);printk(class_create 类创建失败\n);return -1;}for(i0;i1;i){device_create(myclass,NULL,dev[i],NULL,devs_name[i]);//自动创建设备节点}return 0;
}int myled_remove (struct platform_device *pdev)
{printk(移除函数成功\n);device_destroy(myclass,dev[0]);//销毁设备节点 在/dev/name ---device_createdevice_destroy(myclass,dev[1]);class_destroy(myclass);//销毁类 --class_createcdev_del(mydev);//释放申请的字符设备 --cdev_addunregister_chrdev_region(dev[0],2);//释放申请的设备号 ---alloc_chrdev_regiongpio_free(led[0]);// 释放 gpio 口资源 ----gpio_requestgpio_free(led[1]);return 0;
}
struct of_device_id mydev_node{.compatiblexyd-led,
};struct platform_driver drv{.probe myled_probe,.remove myled_remove,.driver {.name myxyd_leds,//与设备端必须保持一致.of_match_table mydev_node,},
};
static int __init myled_init(void)
{ platform_driver_register(drv);return 0;
}
static void __exit myled_exit(void)
{platform_driver_unregister(drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE(GPL);应用层
#include stdio.h
#include fcntl.h
#include unistd.h
int main(int argc,char *argv[])
{int fd 0;while(1){fd open(/dev/myqxjled1,O_RDWR); // --- 底层的open函数sleep(1);close(fd);//底层的closesleep(1);fdopen(/dev/myqxjled2,O_RDWR); // --- 底层的open函数sleep(1);close(fd);//底层的closesleep(1); }return 0;
}编译
obj-m led_driver.o #最终生成模块的名字就是 led.ko KDIR:/home/stephen/RK3588S/kernel #他就是你现在rk3588s里内核的路径 CROSS_COMPILE_FLAG/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-#这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:make -C $(KDIR) M$(PWD) modules ARCHarm64 CROSS_COMPILE$(CROSS_COMPILE_FLAG)aarch64-none-linux-gnu-gcc app.c -o app#调用内核层 Makefile 编译目标为 modules-模块 文件在当前路径# 架构 ARCHarm64
clean:rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.order app *.mod灯现象
2.编写KEY按键驱动实现底层按键按下反馈给应用层
内核层
#include linux/module.h
#include linux/kernel.h
#include linux/of.h
#include linux/of_gpio.h
#include linux/cdev.h
#include linux/gpio.h
#include linux/device.h
#include linux/fs.h
#include linux/platform_device.h
#include linux/uaccess.h
int key[2]{0};
int beep 0;
int key_value 0;
int beep_value[2] {0};
dev_t dev;
struct cdev mydev;
struct class *myclassNULL;int myled_open (struct inode *inode, struct file *fp)
{printk(myled open 正确打开\n);return 0;
}int myled_close (struct inode *inode, struct file *fp)
{printk(myled close 关闭正确\n);return 0;
}
ssize_t mykey_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{unsigned long ret0;if(gpio_get_value(key[0])0){key_value1;printk(按键1被按下\n);}else if(gpio_get_value(key[1])0){key_value2;printk(按键2被按下\n);}retcopy_to_user(buf,key_value,4);if(ret0){printk(copy_to_user 错误\n);return -1;}key_value0;return 0;
}
ssize_t mykey_write (struct file *fp, const char __user *buf, size_t size, loff_t *offset)
{ unsigned long ret0;retcopy_from_user(beep_value,buf,4);if(ret0){printk(copy_from_user 错误\n);return -1;}if(beep_value[0]1){gpio_set_value(beep,beep_value[0]);}else if(beep_value[1]0){gpio_set_value(beep,beep_value[1]);}return 0;
}
struct file_operations myfops{.open myled_open,.release myled_close,.read mykey_read,.write mykey_write,
};int myled_probe(struct platform_device *pdev)
{printk(探测函数设备端和驱动端匹配成功\n);//led[0] led[1]返回的是gpio编口号key[0]of_get_named_gpio(pdev-dev.of_node,devices-gpios,2);//获得设备树的属性key[1]of_get_named_gpio(pdev-dev.of_node,devices-gpios,3);beep of_get_named_gpio(pdev-dev.of_node,devices-gpios,4);gpio_request(key[0], key1 pa7);//21 申请你要使用 gpio 口的资源gpio_request(key[1], key2 pb1);//22gpio_request(beep, beep pa4);//36gpio_direction_input(key[0]);//配置 gpio 口的工作模式gpio_direction_input(key[1]); gpio_direction_output(beep,0);//高电平叫alloc_chrdev_region(dev,0,1,led);//动态申请设备号 linux2.6或杂项类型cdev_init(mydev,myfops);//初始化核心结构体cdev_add(mydev,dev,1);//向内核去申请 linux2.6 字符设备myclassclass_create(THIS_MODULE,class_led);//创建类if(myclass NULL){printk(class_create error\n);printk(class_create 类创建失败\n);return -1;}device_create(myclass,NULL,dev,NULL,mykey);//自动创建设备节点return 0;
}int myled_remove (struct platform_device *pdev)
{printk(移除函数成功\n);device_destroy(myclass,dev);//销毁设备节点 在/dev/name ---device_createclass_destroy(myclass);//销毁类 --class_createcdev_del(mydev);//释放申请的字符设备 --cdev_addunregister_chrdev_region(dev,1);//释放申请的设备号 ---alloc_chrdev_regiongpio_free(key[0]);// 释放 gpio 口资源 ----gpio_requestgpio_free(key[1]);gpio_free(beep);return 0;
}
struct of_device_id mydev_node{.compatiblexyd-device,
};struct platform_driver drv{.probe myled_probe,.remove myled_remove,.driver {.name xyd-device,//与设备端必须保持一致.of_match_table mydev_node,},
};
static int __init myled_init(void)
{ platform_driver_register(drv);return 0;
}
static void __exit myled_exit(void)
{platform_driver_unregister(drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE(GPL);应用层
#include stdio.h
#include fcntl.h
#include unistd.h
int main(int argc,char *argv[])
{int fd 0;int beep_value[2]{1,0};int key_value0;fd open(/dev/mykey,O_RDWR); // --- 底层的open函数while(1){write(fd,beep_value[0],4);usleep(500000);write(fd,beep_value[1],4);usleep(500000);read(fd,key_value,4);if(key_value1){printf(第%d个按键按下\n,key_value);}else if(key_value2){printf(第%d个按键按下\n,key_value);}usleep(500000);}close(fd);//底层的close return 0;
}编译
obj-m led_driver.o #最终生成模块的名字就是 led.ko KDIR:/home/stephen/RK3588S/kernel #他就是你现在rk3588s里内核的路径 CROSS_COMPILE_FLAG/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-#这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:make -C $(KDIR) M$(PWD) modules ARCHarm64 CROSS_COMPILE$(CROSS_COMPILE_FLAG)aarch64-none-linux-gnu-gcc app.c -o app#调用内核层 Makefile 编译目标为 modules-模块 文件在当前路径# 架构 ARCHarm64
clean:rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.order app *.mod3.编写beep的驱动代码可以使用ioctl控制
驱动层
#include linux/module.h
#include linux/kernel.h
#include linux/of.h
#include linux/of_gpio.h
#include linux/cdev.h
#include linux/gpio.h
#include linux/device.h
#include linux/fs.h
#include linux/platform_device.h
#include linux/uaccess.h
int key[2]{0};
int beep 0;
int key_value 0;
int beep_value[2] {0};
dev_t dev;
struct cdev mydev;
struct class *myclassNULL;int myled_open (struct inode *inode, struct file *fp)
{printk(myled open 正确打开\n);return 0;
}int myled_close (struct inode *inode, struct file *fp)
{printk(myled close 关闭正确\n);return 0;
}
ssize_t mykey_read (struct file *fp, char __user *buf, size_t size, loff_t *offset)
{unsigned long ret0;if(gpio_get_value(key[0])0){key_value1;printk(按键1被按下\n);}else if(gpio_get_value(key[1])0){key_value2;printk(按键2被按下\n);}retcopy_to_user(buf,key_value,4);if(ret0){printk(copy_to_user 错误\n);return -1;}key_value0;return 0;
}
ssize_t mykey_write (struct file *fp, const char __user *buf, size_t size, loff_t *offset)
{ unsigned long ret0;retcopy_from_user(beep_value,buf,4);if(ret0){printk(copy_from_user 错误\n);return -1;}if(beep_value[0]1){gpio_set_value(beep,beep_value[0]);}else if(beep_value[1]0){gpio_set_value(beep,beep_value[1]);}return 0;
}
long mybeep_ioctl (struct file *fp, unsigned int cmd, unsigned long arg)
{if(cmd1){gpio_set_value(beep,1);}if(cmd0){gpio_set_value(beep,0);}return 0;
}
struct file_operations myfops{.open myled_open,.release myled_close,.read mykey_read,.write mykey_write,.unlocked_ioctlmybeep_ioctl,
};int myled_probe(struct platform_device *pdev)
{printk(探测函数设备端和驱动端匹配成功\n);//led[0] led[1]返回的是gpio编口号key[0]of_get_named_gpio(pdev-dev.of_node,devices-gpios,2);//获得设备树的属性key[1]of_get_named_gpio(pdev-dev.of_node,devices-gpios,3);beep of_get_named_gpio(pdev-dev.of_node,devices-gpios,4);gpio_request(key[0], key1 pa7);//21 申请你要使用 gpio 口的资源gpio_request(key[1], key2 pb1);//22gpio_request(beep, beep pa4);//36gpio_direction_input(key[0]);//配置 gpio 口的工作模式gpio_direction_input(key[1]); gpio_direction_output(beep,0);//高电平叫alloc_chrdev_region(dev,0,1,led);//动态申请设备号 linux2.6或杂项类型cdev_init(mydev,myfops);//初始化核心结构体cdev_add(mydev,dev,1);//向内核去申请 linux2.6 字符设备myclassclass_create(THIS_MODULE,class_led);//创建类if(myclass NULL){printk(class_create error\n);printk(class_create 类创建失败\n);return -1;}device_create(myclass,NULL,dev,NULL,mykey);//自动创建设备节点return 0;
}int myled_remove (struct platform_device *pdev)
{printk(移除函数成功\n);device_destroy(myclass,dev);//销毁设备节点 在/dev/name ---device_createclass_destroy(myclass);//销毁类 --class_createcdev_del(mydev);//释放申请的字符设备 --cdev_addunregister_chrdev_region(dev,1);//释放申请的设备号 ---alloc_chrdev_regiongpio_free(key[0]);// 释放 gpio 口资源 ----gpio_requestgpio_free(key[1]);gpio_free(beep);return 0;
}
struct of_device_id mydev_node{.compatiblexyd-device,
};struct platform_driver drv{.probe myled_probe,.remove myled_remove,.driver {.name xyd-device,//与设备端必须保持一致.of_match_table mydev_node,},
};
static int __init myled_init(void)
{ platform_driver_register(drv);return 0;
}
static void __exit myled_exit(void)
{platform_driver_unregister(drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE(GPL);应用层
#include stdio.h
#include fcntl.h
#include unistd.h
#include sys/ioctl.h
int main(int argc,char *argv[])
{int fd 0;fd open(/dev/mykey,O_RDWR); // --- 底层的open函数while(1){ioctl(fd,1);usleep(500000);ioctl(fd,1);usleep(500000);}close(fd);//底层的close return 0;
}编译
obj-m led_driver.o #最终生成模块的名字就是 led.ko KDIR:/home/stephen/RK3588S/kernel #他就是你现在rk3588s里内核的路径 CROSS_COMPILE_FLAG/home/stephen/RK3588S/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-#这是你的交叉编译器路径 --- 这里你也要替换成你自己的交叉编译工具的路径
all:make -C $(KDIR) M$(PWD) modules ARCHarm64 CROSS_COMPILE$(CROSS_COMPILE_FLAG)aarch64-none-linux-gnu-gcc app.c -o app#调用内核层 Makefile 编译目标为 modules-模块 文件在当前路径# 架构 ARCHarm64
clean:rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.order app *.mod