【驱动开发】创建设备节点、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 
#include 
#include
#include
#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
#include
#include 
#include 
#include 
#include
#include

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创建设备节点的原理

【驱动开发】创建设备节点、ioctl函数的使用_第1张图片

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

**************创建设备文件相关**********
#include
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 
#include 
#include
#include
#include
#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函数的使用_第2张图片

三、ioctl函数的使用

1.引入ioctl函数的意义

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

2.ioctl函数的分析

*********系统调用函数的分析**********
#include 
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 #
 ====== ==================================

【驱动开发】创建设备节点、ioctl函数的使用_第3张图片

#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 
#include 
#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}

【驱动开发】创建设备节点、ioctl函数的使用_第4张图片

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 
#include 
#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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 
#include 
#include 
#include 
#include 
#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
#include
#include 
#include 
#include 
#include 
#include
#include
#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;
}

你可能感兴趣的:(驱动开发,数据结构)