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

news2024/12/22 18:23:08

一、控制三盏灯的亮灭

头文件:

#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'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(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);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_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_MODULE
    name:向上提交的目录名
返回值:成功返回申请到的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:父节点指针,不知道就填NULL
    devt:设备号    主设备号<<20|次设备号
        /*
            MKDEV(主设备号,次设备号):根据主设备号和次设备号得到设备号
            MAJOR(dev):根据设备号得到主设备号
            MINOR(dev):根据设备号得到次设备号
        */
        drvdata:当前对象的一个私有数据,填NULL
        fmt:填要创建的设备节点名  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'://LED1
            if(kbuf[1]=='0')//关灯
                vir_led1->ODR &= (~(1<<10));
            else//开灯
                vir_led1->ODR |= 1<<10;
            break;
        case '2'://LED2
            if(kbuf[1]=='0')//关灯
                vir_led2->ODR &= (~(1<<10));
            else//开灯
                vir_led2->ODR |= 1<<10;
            break;
        case '3'://LED3
            if(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);
    //led1
    vir_led1->MODER &= (~(3<<20));
    vir_led1->MODER |= (1<<20);
    vir_led1->ODR &= (~(1<<10));
    //led2
    vir_led2->MODER &= (~(3<<20));
    vir_led2->MODER |= (1<<20);
    vir_led2->ODR &= (~(1<<10));
    //led3
    vir_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 macro
    10 - read: _IOR
    01 - write: _IOW
    11 - read/write: _IOWR

 29-16    size of arguments

 15-8    ascii character supposedly
    unique to each driver

 7-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);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_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: // LED1
            vir_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: // LED1
            vir_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);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_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: // LED1
            vir_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: // LED1
            vir_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);
    // led1
    vir_led1->MODER &= (~(3 << 20));
    vir_led1->MODER |= (1 << 20);
    vir_led1->ODR &= (~(1 << 10));
    // led2
    vir_led2->MODER &= (~(3 << 20));
    vir_led2->MODER |= (1 << 20);
    vir_led2->ODR &= (~(1 << 10));
    // led3
    vir_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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java读取指定文件夹下的全部文件,并输出文件名,文件大小,文件创建时间

import java.io.IOException; import java.nio.file.*; import java.nio.file.attribute.*; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { try { Path startingDir Paths.get("你的目…

带你了解如何防御DDoS攻击

DDoS攻击的类型和方法 分布式拒绝服务攻击&#xff08;简称DDoS&#xff09;是一种协同攻击&#xff0c;旨在使受害者的资源无法使用。它可以由一个黑客组织协同行动&#xff0c;也可以借助连接到互联网的多个受破坏设备来执行。这些在攻击者控制下的设备通常称为僵尸网络。 …

人工智能、机器学习、深度学习的区别

人工智能涵盖范围最广&#xff0c;它包含了机器学习&#xff1b;而机器学习是人工智能的重要研究内容&#xff0c;它又包含了深度学习。 人工智能&#xff08;AI&#xff09; 人工智能是一门以计算机科学为基础&#xff0c;融合了数学、神经学、心理学、控制学等多个科目的交…

表白墙完善(数据库,前端,后端Servlet),再谈Cookie和Session。以及一个关于Cookie的练习小程序

目录 表白墙引入数据库 再谈Cookie和session 得到Cookie ​编辑 设置Cooie 使用Cookie编写一个登入的小界面 表白墙引入数据库 1.先引入数据库的依赖&#xff08;驱动包&#xff09;&#xff0c;5.1.49 pom.xml中&#xff0c;在之前的两个之前&#xff0c;再去添加一个 &…

百度松果20231022作业

越狱 盒子与球 斯特林第二类数&#xff08;用dp求&#xff09;*盒子的阶乘 int dp[11][11]; //n>k int A(int x){int res1;fer(i,2,x1)res*i;return res; } signed main(){IOS;dp[2][1]dp[2][2]dp[1][1]1;fer(i,3,11){dp[i][1]1;fer(j,2,i){dp[i][j]j*dp[i-1][j]dp[i-1][j-…

搜一搜文章排名上首页无压力,优化这5点助你稳步上升

随着微信搜一搜的普及,如何提高文章在搜一搜中的排名已经成为许多公众号运营者关注的重点。高质量的搜一搜排名不仅可以带来更多的阅读量,也是提升公众号影响力的关键。那么,如何做才能轻松实现搜一搜文章上首页排名呢?更多搜一搜文章排名相关&#xff0c;可某薇找我名字。只需…

大规模语言模型人类反馈对齐--PPO算法代码实践

在前面的章节我们已经知道&#xff0c;人类反馈强化学习机制主要包括策略模型、奖励模型、评论模型以及参考模型等部分。需要考 虑奖励模型设计、环境交互以及代理训练的挑战&#xff0c; 同时叠加大语言模型的高昂的试错成本。对于研究人员来说&#xff0c; 使用人类反馈强化学…

数字孪生智慧建筑可视化系统,提高施工效率和建造质量

随着科技的不断进步和数字化的快速发展&#xff0c;数字孪生成为了建筑行业的一个重要的概念&#xff0c;被广泛应用于智能化建筑的开发与管理中。数字孪生是将现实世界的实体与数字世界的虚拟模型进行连接和同步&#xff0c;从而实现实时的数据交互和模拟仿真。数字孪生在建筑…

【Java 进阶篇】深入了解 Bootstrap 插件

Bootstrap 是一个流行的前端框架&#xff0c;提供了各种强大的插件&#xff0c;用于增强网页和应用程序的功能和交互性。本篇博客将深入介绍 Bootstrap 插件&#xff0c;适用于那些刚刚开始学习前端开发的小白。 什么是 Bootstrap&#xff1f; 在深入探讨 Bootstrap 插件之前…

QListWiget和QToolButton

1.简介 Qt 中用于项&#xff08;Item&#xff09;处理的组件有两类&#xff0c;一类是 Item Views&#xff0c;包括 QListView、QTreeView、 QTableView、QColumnView 等&#xff1b;另一类是 Item Widgets&#xff0c;包括 QListWidget、QTreeWidget 和 QTable Widget。 Ite…

手机知识:安卓内存都卷到24GB了,为何iPhone还在固守8GB

目录 一、系统机制 二、生态差异 三、总结 在刚刚过去的9月&#xff0c;年货iPhone 15系列正式发布&#xff0c;标准版不出意外还是挤药膏&#xff0c;除了镜头、屏幕有些升级&#xff0c;芯片用iPhone 14 Pro系列的&#xff0c;内存只有6GB&#xff1b;即使是集钛合金机身、…

Json数据上传—>对象转换—>存入MongoDB(SpringData提供的规范)

上传json 代码实现 RestController RequestMapping("/api/hosp") public class ApiController{Autowiredprivate HospitalService hospitalService;PostMapping("saveHospital")public Result saveHosp(HttpServletRequest request){Map<String,String…

Jetpack:015-Jetpack的是脚手架

文章目录 1. 概念介绍2. 使用方法2.1 核心思想2.2 具体内容 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中小红点相关的内容&#xff0c;本章回中将介绍 脚手架。闲话休提&#xff0c;让我们一起Talk Android Jetpack吧&#xff01; 1. 概念介绍 我们在本章回中介…

【试题038】 逻辑与和赋值表达式例题

1.题目&#xff1a;设int n;&#xff0c;执行表达式(n2)&&(n1)&&(n0)后&#xff0c;n的值是&#xff1f; 2.代码分析&#xff1a; //设int n;&#xff0c;执行表达式(n2)&&(n1)&&(n0)后&#xff0c;n的值是? int main() {int n;printf("…

Java中的static关键字

一、static关键字的用途 在《Java编程思想》P86页有这样一段话&#xff1a; “static方法就是没有this的方法。在static方法内部不能调用非静态方法&#xff0c;反过来是可以的。而且可以在没有创建任何对象的前提下&#xff0c;仅仅通过类本身来调用static方法。这实际上正是s…

C语言 ——宽字符

前言&#xff1a; 过去C语⾔并不适合⾮英语国家&#xff08;地区&#xff09;使⽤。 C语⾔最初假定字符都是单字节的。但是这些假定并不是在世界的任何地⽅都适⽤。 C语⾔字符默认是采⽤ASCII编码的&#xff0c;ASCII字符集采⽤的是单字节编码&#xff0c;且只使⽤了单字节中…

YOLOv8改进实战 | 更换主干网络Backbone之2023最新模型LSKNet,旋转目标检测SOTA

前言 传统的YOLOv8系列中,Backbone采用的是较为复杂的C2f网络结构,这使得模型计算量大幅度的增加,检测速度较慢,应用受限,在某些真实的应用场景如移动或者嵌入式设备,如此大而复杂的模型时难以被应用的。为了解决这个问题,本章节通过采用LSKNet轻量化主干网络作为Backb…

【LangChain系列 11】Prompt模版——拼装组合

原文地址&#xff1a;【LangChain系列 11】Prompt模版——拼装组合 本文速读&#xff1a; 多prompt模版组合 单prompt模版拼装 在平常业务开发中&#xff0c;我们常常需要把一些公共模块提取出来作为一个独立的部分&#xff0c;然后将业务中去将这些模块进行组合。在LLM应用…

有哪些比较好用的协同办公软件

在疫情期间&#xff0c;协同办公大放异彩&#xff0c;解决了很多公司线上办公的问题&#xff0c;所以在后疫情时代&#xff0c;协同办公软件成为了提高工作效率和团队协作的重要工具。 随着科技的不断进步&#xff0c;越来越多的协同办公软件涌现出来。在本文中&#xff0c;我们…

YOLOv7-PTQ量化部署

目录 前言一、PTQ量化浅析二、YOLOv7模型训练1. 项目的克隆和必要的环境依赖1.1 项目的克隆1.2 项目代码结构整体介绍1.3 环境安装 2. 数据集和预训练权重的准备2.1 数据集2.2 预训练权重准备 3. 训练模型3.1 修改模型配置文件3.2 修改数据配置文件3.3 训练模型3.4 mAP测试 三、…