当前位置: 首页 > news >正文

【驱动开发】创建设备节点、ioctl函数的使用

一、控制三盏灯的亮灭

头文件:

#ifndef __HEAD_H__
#define __HEAD_H__ 
typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
}gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR    0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR    0X50000A28
#endif 

驱动程序:

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include"head.h"int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char  *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);unsigned long ret;//向用户空间读取拷贝if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size=sizeof(kbuf);ret=copy_to_user(ubuf,kbuf,size);if(ret)//拷贝失败{printk("copy_to_user filed\n");return ret;}return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{unsigned long ret;//从用户空间读取数据if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size=sizeof(kbuf);ret=copy_from_user(kbuf,ubuf,size);if(ret)//拷贝失败{printk("copy_to_user filed\n");return ret;}switch(kbuf[0]){case '1'://LED1if(kbuf[1]=='0')//关灯vir_led1->ODR &= (~(1<<10));else//开灯vir_led1->ODR |= 1<<10;break;case '2'://LED2if(kbuf[1]=='0')//关灯vir_led2->ODR &= (~(1<<10));else//开灯vir_led2->ODR |= 1<<10;break;case '3'://LED3if(kbuf[1]=='0')//关灯vir_led3->ODR &= (~(1<<8));else//开灯vir_led3->ODR |= 1<<8;break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}//定义操作方法结构体变量并赋值
struct file_operations fops={.open=mycdev_open,.read=mycdev_read,.write=mycdev_write,.release=mycdev_close,
};int all_led_init(void)
{//寄存器地址的映射vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));if(vir_led1==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));if(vir_led2==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}vir_led3=vir_led1;vir_rcc=ioremap(PHY_RCC_ADDR,4);if(vir_rcc==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}printk("物理地址映射成功\n");//寄存器的初始化//rcc(*vir_rcc) |= (3<<4);//led1vir_led1->MODER &= (~(3<<20));vir_led1->MODER |= (1<<20);vir_led1->ODR &= (~(1<<10));//led2vir_led2->MODER &= (~(3<<20));vir_led2->MODER |= (1<<20);vir_led2->ODR &= (~(1<<10));//led3vir_led3->MODER &= (~(3<<16));vir_led1->MODER |= (1<<16);vir_led1->ODR &= (~(1<<8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{//字符设备驱动注册major=register_chrdev(0,"mychrdev",&fops);if(major<0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功:major=%d\n",major);//寄存器映射以及初始化all_led_init();return 0;
}
static void __exit mycdev_exit(void)
{//取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);//注销字符设备驱动unregister_chrdev(major,"mychrdev");}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序:
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>int main(int argc, char const *argv[])
{char buf[128]={0};int fd=open("/dev/mychrdev",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}while(1){//从终端读取printf("请输入两个字符\n");printf("第一个字符:1(LED1) 2(LED2) 3(LED3)\n");printf("第二个字符:0(关灯) 1(开灯)\n");printf("请输入>");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]='\0';//向设备文件中写write(fd,buf,sizeof(buf));}close(fd);return 0;
}

二、自动创建设备节点

1.创建设备节点的机制

  • mknod:手动创建设备节点的命令
  • devfs:linux早期创建设备节点的机制,如今由于各自原因不再使用,它创建设备节点的逻辑存在于内核空间(2.4版本之前)
  • udev机制:目前使用的自动创建设备节点的机制,创建设备节点的逻辑是在用户空间(2.6版本)
  • mdev机制:可以将mdev理解为轻量级的udev,mdev主要应用于一些嵌入式系统中

2.udev创建设备节点的原理

3.创建设备节点的相关API

**************创建设备文件相关**********
#include<linux/device.h>
1.向上提交目录
struct class *class_create(struct module *owner, const char *name)
功能:向上提交目录信息,申请了一个struct class对象并初始化
参数:owner:指向当前模块自身的一个指针,填写THIS_MODULEname:向上提交的目录名
返回值:成功返回申请到的class对象的首地址,失败返回一个指向内核顶层4K空间的指针/* 关关于返回值判断问题:只要判断指针的数值>4K预留空间起始值就说明函数调用失败bool __must_check IS_ERR(__force const void *ptr)//#define IS_ERR_VALUE(x) (unsigned long)(void *)(x) >= 0XFFFFFFF-4095功能:判断指针是否指向内核4K预留空间,如果指针指向4K预留空间返回真,否则返回假long __must_check PTR_ERR(__force const void *ptr)功能:将一个指针转换成long类型错误码返回*/ex:struct class *cls=class_create(THIS_MODULE,"mychrdev");if(IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);        }2.向上提交设备节点信息
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...)
功能:向上提交设备节点信息,申请一个struct  device对象并初始化
参数:class:class_create()得到的对象指针parent:父节点指针,不知道就填NULLdevt:设备号    主设备号<<20|次设备号/*MKDEV(主设备号,次设备号):根据主设备号和次设备号得到设备号MAJOR(dev):根据设备号得到主设备号MINOR(dev):根据设备号得到次设备号*/drvdata:当前对象的一个私有数据,填NULLfmt:填要创建的设备节点名  video%d...:不定长参数       i
返回值:成功返回创建成功的struct device对象指针,失败返回指针指向4K预留空间
************删除设备文件相关**********
销毁节点信息:
void device_destroy(struct class *class, dev_t devt)
功能:销毁节点信息
参数:class:class_create()得到的对象指针devt:向上提交设备节点是填写的设备号
销毁目录
void class_destroy(struct class *cls)
参数:class_create()得到的对象指针
返回值:无

4.实例

#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>
#include"head.h"int major;
char kbuf[128]={0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}
ssize_t mycdev_read(struct file *file, char  *ubuf, size_t size, loff_t *lof)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);unsigned long ret;//向用户空间读取拷贝if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size=sizeof(kbuf);ret=copy_to_user(ubuf,kbuf,size);if(ret)//拷贝失败{printk("copy_to_user filed\n");return ret;}return 0;
}
ssize_t mycdev_write(struct file *file, const char  *ubuf, size_t size, loff_t *lof)
{unsigned long ret;//从用户空间读取数据if(size>sizeof(kbuf))//用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小size=sizeof(kbuf);ret=copy_from_user(kbuf,ubuf,size);if(ret)//拷贝失败{printk("copy_to_user filed\n");return ret;}switch(kbuf[0]){case '1'://LED1if(kbuf[1]=='0')//关灯vir_led1->ODR &= (~(1<<10));else//开灯vir_led1->ODR |= 1<<10;break;case '2'://LED2if(kbuf[1]=='0')//关灯vir_led2->ODR &= (~(1<<10));else//开灯vir_led2->ODR |= 1<<10;break;case '3'://LED3if(kbuf[1]=='0')//关灯vir_led3->ODR &= (~(1<<8));else//开灯vir_led3->ODR |= 1<<8;break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);return 0;
}//定义操作方法结构体变量并赋值
struct file_operations fops={.open=mycdev_open,.read=mycdev_read,.write=mycdev_write,.release=mycdev_close,
};int all_led_init(void)
{//寄存器地址的映射vir_led1=ioremap(PHY_LED1_ADDR,sizeof(gpio_t));if(vir_led1==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}vir_led2=ioremap(PHY_LED2_ADDR,sizeof(gpio_t));if(vir_led2==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}vir_led3=vir_led1;vir_rcc=ioremap(PHY_RCC_ADDR,4);if(vir_rcc==NULL){printk("ioremap filed:%d\n",__LINE__);return -ENOMEM;}printk("物理地址映射成功\n");//寄存器的初始化//rcc(*vir_rcc) |= (3<<4);//led1vir_led1->MODER &= (~(3<<20));vir_led1->MODER |= (1<<20);vir_led1->ODR &= (~(1<<10));//led2vir_led2->MODER &= (~(3<<20));vir_led2->MODER |= (1<<20);vir_led2->ODR &= (~(1<<10));//led3vir_led3->MODER &= (~(3<<16));vir_led1->MODER |= (1<<16);vir_led1->ODR &= (~(1<<8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{//字符设备驱动注册major=register_chrdev(0,"mychrdev",&fops);if(major<0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功:major=%d\n",major);//向上提交目录cls=class_create(THIS_MODULE,"mychrdev");if(IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);}printk("向上提交目录成功\n");//向上提交设备节点信息int i;//向上提交三次设备节点信息for(i=0;i<3;i++){dev=device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);if(IS_ERR(dev)){printk("向上提交设备节点失败\n");return -PTR_ERR(dev);}}printk("向上提交设备节点成功\n");//寄存器映射以及初始化all_led_init();return 0;
}
static void __exit mycdev_exit(void)
{//取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);//销毁设备节点信息int i;for(i=0;i<3;i++){device_destroy(cls,MKDEV(major,i));}//销毁目录class_destroy(cls);//注销字符设备驱动unregister_chrdev(major,"mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

三、ioctl函数的使用

1.引入ioctl函数的意义

linux操作系统中有意将数据的读写和读写功能的选择分别交给不同的函数去完成。就让read/write函数只进行数据的读写即可,让一些其他功能的设置和选择交给ioctl函数来实现。比如,串口通信时,需要设置波特率,需要设置数据格式,也需要最终选择数据收发,让这些都由ioctl函数来完成。让read()write()只进行串口数据收发即可。

2.ioctl函数的分析

*********系统调用函数的分析**********
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
功能:进行io功能的设置   
参数:
fd:文件描述符
request:io控制的功能码
...:可以加,也可以不加。如果第三个参数传递数值,只能传递整型数据和指针
返回值:成功返回0,失败返回错误码*********驱动中操作方法的分析********
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long arg)
{参数分析:file:文件指针cmd:应用程序中的ioctl第二个参数传递过来arg:应用程序中的ioctl第三个参数传递过来
}

3.ioctl功能码的构建

为了让实现不同功能的功能码尽量不一样,我们对功能码进行了编码

查询内核的说明手册:~/linux-5.10.61/Documentation/userspace-api/ioctl

vi ioctl-decoding.rst//功能码的编码说明文档

====== ==================================bits   meaning====== ==================================31-30    00 - no parameters: uses _IO macro10 - read: _IOR01 - write: _IOW11 - read/write: _IOWR29-16    size of arguments15-8    ascii character supposedlyunique to each driver7-0    function #====== ==================================

#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)#define _IOC(dir,type,nr,size)          \((unsigned int)             \(((dir)  << _IOC_DIRSHIFT) |       \((type) << _IOC_TYPESHIFT) |      \((nr)   << _IOC_NRSHIFT) |        \((size) << _IOC_SIZESHIFT)))ex:构建LED开关的功能码:ioctl函数无第三个参数开灯  #define  LED_ON  _IO('l',1)关灯  #define  LED_OFF  _IO('l',0)ex:构建LED开关的功能码:ioctl函数有第三个参数开灯  #define  LED_ON  _IOW('l',1,int)关灯  #define  LED_OFF  _IOW('l',0,int)

4.ioctl实例----不传递第三个参数

头文件:
#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct
{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
} gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR 0X50000A28
// 构建开灯关灯的功能码
#define LED_ON _IO('l', 1)
#define LED_OFF _IO('l', 0)
#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd){case LED_ON: // 开灯vir_led1->ODR |= (0X1 << 10);vir_led2->ODR |= (0X1 << 10);vir_led3->ODR |= (0X1 << 8);break;case LED_OFF: // 关灯vir_led1->ODR &= (~(0X1 << 10));vir_led2->ODR &= (~(0X1 << 10));vir_led3->ODR &= (~(0X1 << 8));break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};int all_led_init(void)
{// 寄存器地址的映射vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));if (vir_led1 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));if (vir_led2 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led3 = vir_led1;vir_rcc = ioremap(PHY_RCC_ADDR, 4);if (vir_rcc == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}printk("物理地址映射成功\n");// 寄存器的初始化// rcc(*vir_rcc) |= (3 << 4);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_led3->MODER &= (~(3 << 16));vir_led1->MODER |= (1 << 16);vir_led1->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{// 字符设备驱动注册major = register_chrdev(0, "mychrdev", &fops);if (major < 0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功:major=%d\n", major);// 向上提交目录cls = class_create(THIS_MODULE, "mychrdev");if (IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);}printk("向上提交目录成功\n");// 向上提交设备节点信息int i; // 向上提交三次设备节点信息for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点失败\n");return -PTR_ERR(dev);}}printk("向上提交设备节点成功\n");// 寄存器映射以及初始化all_led_init();return 0;
}
static void __exit mycdev_exit(void)
{// 取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);// 销毁设备节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录class_destroy(cls);// 注销字符设备驱动unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "head.h"int main(int argc, char const *argv[])
{char buf[128] = {0};int a;int fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");exit(-1);}while (1){// 从终端读取printf("请输入要实现的功能 ");printf("0(关灯) 1(开灯)\n");printf("请输入>>>");scanf("%d", &a);switch (a){case 1:ioctl(fd, LED_ON);break;case 0:ioctl(fd, LED_OFF);break;}}close(fd);return 0;
}

5.ioctl实例----传递第三个参数(传递整型)

头文件:
#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct
{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
} gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR 0X50000A28
// 构建开灯关灯的功能码
#define LED_ON _IOW('l', 1, int)
#define LED_OFF _IOW('l', 0, int)#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{switch (cmd){case LED_ON: // 开灯switch (arg){case 1: // LED1vir_led1->ODR |= (0X1 << 10);break;case 2:vir_led2->ODR |= (0X1 << 10);break;case 3:vir_led3->ODR |= (0X1 << 8);break;}break;case LED_OFF: // 关灯switch (arg){case 1: // LED1vir_led1->ODR &= (~(0X1 << 10));break;case 2:vir_led2->ODR &= (~(0X1 << 10));break;case 3:vir_led3->ODR &= (~(0X1 << 8));break;}break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};int all_led_init(void)
{// 寄存器地址的映射vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));if (vir_led1 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));if (vir_led2 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led3 = vir_led1;vir_rcc = ioremap(PHY_RCC_ADDR, 4);if (vir_rcc == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}printk("物理地址映射成功\n");// 寄存器的初始化// rcc(*vir_rcc) |= (3 << 4);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_led3->MODER &= (~(3 << 16));vir_led1->MODER |= (1 << 16);vir_led1->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{// 字符设备驱动注册major = register_chrdev(0, "mychrdev", &fops);if (major < 0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功:major=%d\n", major);// 向上提交目录cls = class_create(THIS_MODULE, "mychrdev");if (IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);}printk("向上提交目录成功\n");// 向上提交设备节点信息int i; // 向上提交三次设备节点信息for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点失败\n");return -PTR_ERR(dev);}}printk("向上提交设备节点成功\n");// 寄存器映射以及初始化all_led_init();return 0;
}
static void __exit mycdev_exit(void)
{// 取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);// 销毁设备节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录class_destroy(cls);// 注销字符设备驱动unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "head.h"int main(int argc, char const *argv[])
{char buf[128] = {0};int a, b;int fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");exit(-1);}while (1){// 从终端读取printf("请输入要实现的功能 ");printf("0(关灯) 1(开灯)\n");printf("请输入>>>");scanf("%d", &a);printf("请选择要控制的灯:1(LED1)2(LED2) 3(LED3)\n");printf("请输入>>>");scanf("%d", &b);switch (a){case 1:ioctl(fd, LED_ON, b);break;case 0:ioctl(fd, LED_OFF, b);break;}}close(fd);return 0;
}

6.ioctl实例----传递第三个参数(传递地址)

头文件:
#ifndef __HEAD_H__
#define __HEAD_H__
typedef struct
{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR;
} gpio_t;
#define PHY_LED1_ADDR 0X50006000
#define PHY_LED2_ADDR 0X50007000
#define PHY_LED3_ADDR 0X50006000
#define PHY_RCC_ADDR 0X50000A28
// 构建开灯关灯的功能码
#define LED_ON _IOW('l', 1,int)
#define LED_OFF _IOW('l', 0,int)#endif
驱动程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/device.h>
#include "head.h"int major;
char kbuf[128] = {0};
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;
struct class *cls;
struct device *dev;
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int which;//获取应用程序中b的值int ret= copy_from_user(&which,(void *)arg,4);if(ret){printk("copy_from_user filed\n");return-EIO;}switch (cmd){case LED_ON: // 开灯switch (which){case 1: // LED1vir_led1->ODR |= (0X1 << 10);break;case 2:vir_led2->ODR |= (0X1 << 10);break;case 3:vir_led3->ODR |= (0X1 << 8);break;}break;case LED_OFF: // 关灯switch (which){case 1: // LED1vir_led1->ODR &= (~(0X1 << 10));break;case 2:vir_led2->ODR &= (~(0X1 << 10));break;case 3:vir_led3->ODR &= (~(0X1 << 8));break;} break;}return 0;
}
int mycdev_close(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.unlocked_ioctl = mycdev_ioctl,.release = mycdev_close,
};int all_led_init(void)
{// 寄存器地址的映射vir_led1 = ioremap(PHY_LED1_ADDR, sizeof(gpio_t));if (vir_led1 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led2 = ioremap(PHY_LED2_ADDR, sizeof(gpio_t));if (vir_led2 == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}vir_led3 = vir_led1;vir_rcc = ioremap(PHY_RCC_ADDR, 4);if (vir_rcc == NULL){printk("ioremap filed:%d\n", __LINE__);return -ENOMEM;}printk("物理地址映射成功\n");// 寄存器的初始化// rcc(*vir_rcc) |= (3 << 4);// led1vir_led1->MODER &= (~(3 << 20));vir_led1->MODER |= (1 << 20);vir_led1->ODR &= (~(1 << 10));// led2vir_led2->MODER &= (~(3 << 20));vir_led2->MODER |= (1 << 20);vir_led2->ODR &= (~(1 << 10));// led3vir_led3->MODER &= (~(3 << 16));vir_led1->MODER |= (1 << 16);vir_led1->ODR &= (~(1 << 8));printk("寄存器初始化成功\n");return 0;
}static int __init mycdev_init(void)
{// 字符设备驱动注册major = register_chrdev(0, "mychrdev", &fops);if (major < 0){printk("字符设备驱动注册失败\n");return major;}printk("字符设备驱动注册成功:major=%d\n", major);// 向上提交目录cls = class_create(THIS_MODULE, "mychrdev");if (IS_ERR(cls)){printk("向上提交目录失败\n");return -PTR_ERR(cls);}printk("向上提交目录成功\n");// 向上提交设备节点信息int i; // 向上提交三次设备节点信息for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点失败\n");return -PTR_ERR(dev);}}printk("向上提交设备节点成功\n");// 寄存器映射以及初始化all_led_init();return 0;
}
static void __exit mycdev_exit(void)
{// 取消地址映射iounmap(vir_led1);iounmap(vir_led2);iounmap(vir_rcc);// 销毁设备节点信息int i;for (i = 0; i < 3; i++){device_destroy(cls, MKDEV(major, i));}// 销毁目录class_destroy(cls);// 注销字符设备驱动unregister_chrdev(major, "mychrdev");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序:
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include "head.h"int main(int argc, char const *argv[])
{char buf[128]={0};int a,b;int fd=open("/dev/myled0",O_RDWR);if(fd<0){printf("打开设备文件失败\n");exit(-1);}while(1){//从终端读取printf("请输入要实现的功能 ");printf("0(关灯) 1(开灯)\n");printf("请输入>");scanf("%d",&a);printf("请选择要控制的灯:1(LED1)2(LED2) 3(LED3)\n");printf("请输入>");scanf("%d",&b);switch(a){case 1:ioctl(fd,LED_ON,&b);break;case 0:ioctl(fd,LED_OFF,&b);break;}}close(fd);return 0;
}

相关文章:

【驱动开发】创建设备节点、ioctl函数的使用

一、控制三盏灯的亮灭 头文件&#xff1a; #ifndef __HEAD_H__ #define __HEAD_H__ typedef struct{unsigned int MODER;unsigned int OTYPER;unsigned int OSPEEDR;unsigned int PUPDR;unsigned int IDR;unsigned int ODR; }gpio_t; #define PHY_LED1_ADDR 0X50006000 #def…...

Tomcat启动控制台乱码问题

修改Tomcat/conf/logging.properties...

学习周总结

http://t.csdnimg.cn/DKki2 http://t.csdnimg.cn/NvudJ 项目进度 做了大概的主界面&#xff0c;然后做了一个客户端和服务端的分离&#xff0c;实现了在客户端发送的信息&#xff0c;在服务端能收到&#xff1b;客户端和服务端的制作是我之前有写的一个http://t.csdnimg.cn/…...

如何在不恢复出厂设置的情况下解锁 Android 手机密码?

当您忘记 Android 手机的密码时&#xff0c;可能会有压力&#xff0c;尤其是当您不想恢复出厂设置并删除所有数据时。但是&#xff0c;有一些方法可以在不诉诸如此激烈的步骤的情况下解锁手机。我们将在这篇文章中教您如何在不恢复出厂设置的情况下解锁 Android 手机密码。我们…...

移动设备管理对企业IT 安全的增强

移动设备管理 &#xff08;MDM&#xff09; 是通过定义策略和部署安全控制&#xff08;如移动应用程序管理、移动内容管理和条件 Exchange 访问&#xff09;来管理移动设备的过程。 完整的MDM解决方案可以管理在Android&#xff0c;iOS&#xff0c;Windows&#xff0c;macOS&a…...

app分发的一些流程

应用分发的流程通常包括以下步骤&#xff1a; 开发应用程序&#xff1a;首先&#xff0c;您需要开发您的应用程序。这包括编写代码、设计用户界面、测试应用程序等等。确保您的应用程序符合各个应用商店的规范和要求&#xff0c;以确保顺利通过审核。 准备应用材料&#xff1a…...

深入浅出讲解Spring IOC和DI的区别

Spring IOC和DI的区别 一&#xff0c;介绍 前言 很多人都会把ioc和di说成同一个东西&#xff0c;其实IOC和DI虽然在概念上可以笼统地视为同一事物&#xff0c;但其本质上存在区别。IOC&#xff08;Inverse of Control&#xff0c;控制反转&#xff09;从容器的角度描述&#…...

文件操作 IO

文件(File) 狭义的文件: 指的是硬盘上的文件和目录 广义的文件: 泛指计算机中很多软硬件资源(操作系统中把很多硬件和软件资源抽象成了文件, 按照文件的方式同意管理) 本章内容只讨论狭义的文件 路径 绝对路径: 以c: , d: 盘符开头的路径相对路径: 以当前所在的目录为基准(…...

ARouter - 组件化通信方案

官网 https://github.com/alibaba/ARouter/blob/master/README_CN.md 项目简介 一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦 功能介绍 支持直接解析标准URL进行跳转&#xff0c;并自动注入参数到目标页面中支持多模块工程使用支持添…...

Linux中常见的权限问题

目录 前言1. 目录权限2. umask3. 粘滞位结语 前言 在了解完上一篇文章 Linux权限的理解与操作 之后&#xff0c;还有一些比较常见的权限问题需要我们去了解。其中包括目录的权限&#xff0c;umask 以及 粘滞位的使用。 1. 目录权限 问题一&#xff1a;进入一个目录&#xff0…...

【技术分享】RK356X Ubuntu 推流USB摄像头

本文适用与触觉智能所有RK356X ubuntu系统的主板。 IDO-SBC3566基于瑞芯微RK3566研发的一款高性能低功耗的智能主板&#xff0c;采用四核A55,主频高达1.8GHz&#xff0c;专为个人移动互联网设备和AIOT设备而设计&#xff0c;内置了多种功能强大的嵌入式硬件引擎&#xff0c;为…...

介绍一下rabbitMq应用场景

任务队列&#xff1a;RabbitMQ可以将待处理的任务放入队列中&#xff0c;再由多个工作进程异步地执行这些任务。 日志处理&#xff1a;RabbitMQ可以通过发布-订阅模式将日志消息分发到多个消费者&#xff0c;并可以灵活地控制消息的优先级和过滤条件。 实时消息处理&#xff…...

IoT 物联网共享充电桩场景中设备资产定位和地理围栏开发实践

基于经纬度的设备资产定位和地理围栏在物联网场景中应用广泛 01 物联网 GEO 场景架构方案 首先&#xff0c;IoT 终端设备通过卫星定位模块获取当前经纬度&#xff1b;然后&#xff0c;将坐标信息实时上报到物联网平台&#xff1b;最后&#xff0c;存储到 Redis GEO 数据库中。 …...

【Qt进阶之自定义控件】使用QListWidget实现自定义Item效果

目的 Q&#xff1a;如何在Qt库的基础上&#xff0c;实现自定义控件呢&#xff1f; A&#xff1a;根据官方文档回答&#xff0c;就是继承需实现的控件&#xff0c;然后实现自定义功能。 以下是实现QListWidget控件的自定义item。 先看下最终效果是如何&#xff1a; listItem 主…...

【iOS】UITableView总结(Cell的复用原理、自定义Cell、UITableViewCell协议方法)

UITableView 列表的特点&#xff1a; 数据量大样式较为统一通常需要分组垂直滚动通常可视区只有一个 -> 视图的复用 UITableViewDataSource UITableView作为视图&#xff0c;只负责展示&#xff0c;协助管理&#xff0c;不管理数据 需要开发者为UITableView提供展示所需…...

shell之常见网络命令介绍

shell之常见网络命令介绍 1&#xff09;ifconfig 用于配置网络接口。可以用于开启、关闭和设置网络接口的参数&#xff0c;如IP地址、子网掩码、MAC地址等。 ifconfig eth0 192.168.1.1 netmask 255.255.255.0 up上述命令将设置eth0网络接口的IP地址为192.168.1.1&#xff0c;子…...

Android屏幕刷新机制

基础知识 CPU运行在Android设备上的中央处理器&#xff08;Central Processing Unit&#xff09;是Android设备的核心组件之一&#xff0c;负责执行计算和控制设备的各种操作。 Android设备上的CPU通常采用ARM架构&#xff0c;如ARM Cortex-A系列处理器。这些处理器具有高性能…...

Python学习第3天-第一个Python程序

文章目录 前言一、创建项目二、创建程序总结 前言 下面给大家展示下经典的Hello World! 一、创建项目 二、创建程序 print("Hello World!")总结 回到顶部 学习网站 欢迎来到Python的世界&#xff01;...

Golang网络

golang游戏服务器框架 在Go语言中,有许多优秀的游戏服务器框架,以下是一些比较流行的框架: Leaf:一个轻量级的游戏服务器框架,支持多进程、分布式、热更新等特性。它提供了一些常用的组件,如网络层、定时器、数据库等等,可以帮助开发者快速构建游戏服务器。go-ethereum…...

[swift刷题模板] 树状数组(BIT/FenwickTree)

[TOC]([swift刷题模板] 树状数组(BIT/FenwickTree) ) 一、 算法&数据结构 1. 描述 [python刷题模板] 树状数组 二、 模板代码 1. 单点赋值(增加)&#xff0c;区间求和(PURQ) 例题: 307. 区域和检索 - 数组可修改 class BIT {var c: [Int]var n: Int init(_ n: Int){c…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解&#xff1a;由来、作用与意义**一、知识点核心内容****二、知识点的由来&#xff1a;从生活实践到数学抽象****三、知识的作用&#xff1a;解决实际问题的工具****四、学习的意义&#xff1a;培养核心素养…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

密码学基础——SM4算法

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​专栏主页&#xff1a;密码学 &#x1f4cc; 【今日更新】&#x1f4cc; 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 ​编辑…...

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…...