驱动DAY4

该代码实现了一个Linux设备驱动程序,用于控制三个LED灯。通过创建设备文件`/dev/led0`、`/dev/led1`和`/dev/led2`,用户空间程序可以使用`ioctl`调用来开关这些LED灯。驱动程序在内核模块`mychrdev.c`中注册了字符设备,并进行了设备节点的创建和初始化。用户空间程序`test.c`提供了交互式界面,允许用户选择要操作的LED灯并控制其状态。
摘要由CSDN通过智能技术生成

编写LED灯的驱动,创建三个设备文件,每个设备文件和一个LED灯绑定,当操作这个设备文件时只能控制设备文件对应的这盏灯。

  • head.h
#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;
    unsigned int BSRR;
    unsigned int LCKR;
    unsigned int AFRL;
    unsigned int AFRH;
    unsigned int BRR;
    unsigned int RES;
    unsigned int SECCFGR;
}gpio_t;

#define LED1_ADDR   0X50006000
#define LED2_ADDR   0X50007000
#define BUZZER_ADDR 0X50003000
#define RCC_ADDR    0X50000A28

//开
#define LED1_ON _IOW('l',1,int)
#define LED2_ON _IOW('y',1,int)
#define LED3_ON _IOW('z',1,int)
#define MOTOR_ON _IOW('m',1,int)
#define BUZZER_ON _IOW('b',1,int)
#define FAN_ON _IOW('f',1,int)
//关
#define LED1_OFF _IOW('l',0,int)
#define LED2_OFF _IOW('y',0,int)
#define LED3_OFF _IOW('z',0,int)
#define MOTOR_OFF _IOW('m',0,int)
#define BUZZER_OFF _IOW('b',0,int)
#define FAN_OFF _IOW('f',0,int)

#endif 
  •  test.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"

int main(int argc, char const *argv[])
{
    int a, b;
    int led_fd0 = open("/dev/led0", O_RDWR);
    if (led_fd0 < 0 )
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    int led_fd1 = open("/dev/led1", O_RDWR);
    if (led_fd1 < 0 )
    {
        close(led_fd0);
        printf("打开设备文件失败\n");
        exit(-1);
    }
    int led_fd2 = open("/dev/led2", O_RDWR);
    if (led_fd2 < 0 )
    {
        close(led_fd0);
        close(led_fd1);
        printf("打开设备文件失败\n");
        exit(-1);
    }
    int motor_fd = open("/dev/motor", O_RDWR);
    if (motor_fd < 0 )
    {
        close(led_fd0);
        close(led_fd1);
        close(led_fd2);
        printf("打开设备文件失败\n");
        exit(-1);
    }
    int buzzer_fd = open("/dev/buzzer", O_RDWR);
    if (buzzer_fd < 0 )
    {
        close(led_fd0);
        close(led_fd1);
        close(led_fd2);
        close(motor_fd);
        printf("打开设备文件失败\n");
        exit(-1);
    }
    int fan_fd = open("/dev/fan", O_RDWR);
    if (fan_fd < 0 )
    {
        close(led_fd0);
        close(led_fd1);
        close(led_fd2);
        close(motor_fd);
        close(buzzer_fd);
        printf("打开设备文件失败\n");
        exit(-1);
    }
    printf("打开设备文件成功\n");
    while (1)
    {
    LOOP:
        printf("请选择设备(0(Quit)  1(Led1)  2(Led2)  3(Led3)  4(马达)  5(蜂鸣器)  6(风扇)  7(所有的灯)  8(所有设备)\n ");
        printf("请选择:");
        scanf("%d", &a);
        if (a < 0 || a > 8)
        {
            printf("输入错误,请重新输入!!!\n");
            goto LOOP;
        }
        getchar();
        if (a == 0)
        {
            close(led_fd0);
            close(led_fd1);
            close(led_fd2);
            close(motor_fd);
            close(buzzer_fd);
            close(fan_fd);
            return 0;
        }
        printf("请选择设备的状态 0(关)  1(开)\n");
        printf("请选择:");
        scanf("%d", &b);
        if (b != 1 && b != 0)
        {
            printf("输入错误,请重新输入!!!\n");
            goto LOOP;
        }
        getchar();

        switch (b)
        {
        case 0:
            switch (a)
            {
            case 1:
                ioctl(led_fd0, LED1_OFF, &a);
                break;
            case 2:
                ioctl(led_fd1, LED2_OFF, &a);
                break;
            case 3:
                ioctl(led_fd2, LED3_OFF, &a);
                break;
            case 4:
                ioctl(motor_fd, MOTOR_OFF, &a);
                break;
            case 5:
                ioctl(buzzer_fd, BUZZER_OFF, &a);
                break;
            case 6:
                ioctl(fan_fd, FAN_OFF, &a);
                break;
            case 7:
                ioctl(led_fd0, LED1_OFF, &a);
                ioctl(led_fd1, LED2_OFF, &a);
                ioctl(led_fd2, LED3_OFF, &a);
                break;
            case 8:
                ioctl(led_fd0, LED1_OFF, &a);
                ioctl(led_fd1, LED2_OFF, &a);
                ioctl(led_fd2, LED3_OFF, &a);
                ioctl(motor_fd, MOTOR_OFF, &a);
                ioctl(buzzer_fd, BUZZER_OFF, &a);
                ioctl(fan_fd, FAN_OFF, &a);
                break;
            }
            break;
        case 1:
            switch (a)
            {
            case 1:
                ioctl(led_fd0, LED1_ON, &a);
                break;
            case 2:
                ioctl(led_fd1, LED2_ON, &a);
                break;
            case 3:
                ioctl(led_fd2, LED3_ON, &a);
                break;
            case 4:
                ioctl(motor_fd, MOTOR_ON, &a);
                break;
            case 5:
                ioctl(buzzer_fd, BUZZER_ON, &a);
                break;
            case 6:
                ioctl(fan_fd, FAN_ON, &a);
                break;
            case 7:
                ioctl(led_fd0, LED1_ON, &a);
                ioctl(led_fd1, LED2_ON, &a);
                ioctl(led_fd2, LED3_ON, &a);
                break;
            case 8:
                ioctl(led_fd0, LED1_ON, &a);
                ioctl(led_fd1, LED2_ON, &a);
                ioctl(led_fd2, LED3_ON, &a);
                ioctl(motor_fd, MOTOR_ON, &a);
                ioctl(buzzer_fd, BUZZER_ON, &a);
                ioctl(fan_fd, FAN_ON, &a);
                break;
            }
            break;
        default:
            break;
        }
    }
    close(led_fd0);
    close(led_fd1);
    close(led_fd2);
    close(motor_fd);
    close(buzzer_fd);
    close(fan_fd);
    return 0;
}
  • mychrdev.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include "head.h"

struct class *cls;
struct class *cls1;
struct class *cls2;
struct class *cls3;
struct device *dev;
// 主设备号:LED、马达、蜂鸣器、风扇
unsigned int major = 0, major1 = 0, major2 = 0, major3 = 0; // 用于保存主设备号
// 次设备号:LED、马达、蜂鸣器、风扇
unsigned int minor = 0, minor1 = 0, minor2 = 0, minor3 = 0;
// 映射的地址
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
gpio_t *vir_motor;
gpio_t *vir_buzzer;
gpio_t *vir_fan;
unsigned int *vir_rcc;
// 储存设备号
dev_t devno, devno1, devno2, devno3;
// 存放字符驱动设备的地址
struct cdev *cdev;
struct cdev *cdev1;
struct cdev *cdev2;
struct cdev *cdev3;

// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    unsigned int node_t = MINOR(inode->i_rdev); //得到次设备号
    file->private_data = (void *)node_t;        //存放在file结构体中
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    unsigned int node_t = (unsigned int)file->private_data; //读出次设备号
    printk("node_t = %d\n", node_t);
    // 进行功能控制
    switch (node_t) //先根据次设备号来选择
    {
    case 0: //次设备号为0
        switch (cmd) //再根据cmd进行选择
        {
        case LED1_OFF: // LED1
            vir_led1->ODR &= (~(1 << 10));
            break;
        case MOTOR_OFF: // 马达
            vir_motor->ODR &= (~(1 << 6));
            break;
        case BUZZER_OFF: // 蜂鸣器
            vir_buzzer->ODR &= (~(1 << 6));
            break;
        case FAN_OFF: // 风扇
            vir_fan->ODR &= (~(1 << 9));
            break;
        case LED1_ON:
            vir_led1->ODR |= (1 << 10);
            break;
        case MOTOR_ON:
            vir_motor->ODR |= (1 << 6);
            break;
        case BUZZER_ON:
            vir_buzzer->ODR |= (1 << 6);
            break;
        case FAN_ON:
            vir_fan->ODR |= (1 << 9);
            break;
        }
        break;
    case 1:   //次设备号为1
        switch (cmd)
        {
        case LED2_OFF:
            vir_led2->ODR &= (~(1 << 10));
            break;
        case LED2_ON:
            vir_led2->ODR |= (1 << 10);
            break;
        }
        break;
    case 2:  //次设备号为2
        switch (cmd)
        {
        case LED3_OFF:
            vir_led3->ODR &= (~(1 << 8));
            break;
        case LED3_ON:
            vir_led3->ODR |= (1 << 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,
};

static int __init mycdev_init(void)
{
    int i = 0, ret;
    // 1.分配字符设备驱动对象空间***********************
    // cdev_alloc
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("分配字符设备驱动对象空间失败%d\n", __LINE__);
        ret = EFAULT;
        goto out1;
    }
    cdev1 = cdev_alloc();
    if (cdev == NULL)
    {
        printk("分配字符设备驱动对象空间失败%d\n", __LINE__);
        ret = EFAULT;
        goto out2;
    }
    cdev2 = cdev_alloc();
    if (cdev == NULL)
    {
        printk("分配字符设备驱动对象空间失败%d\n", __LINE__);
        ret = EFAULT;
        goto out3;
    }
    cdev3 = cdev_alloc();
    if (cdev == NULL)
    {
        printk("分配字符设备驱动对象空间失败%d\n", __LINE__);
        ret = EFAULT;
        goto out4;
    }

    printk("分配字符设备驱动对象空间成功\n");

    // 2.字符设备驱动对象部分初始化************************
    // cdev_init
    cdev_init(cdev, &fops);
    cdev_init(cdev1, &fops);
    cdev_init(cdev2, &fops);
    cdev_init(cdev3, &fops);

    // 3.申请设备号***************************************
    // register_chrdev_region/alloc_chrdev_region
    if (major > 0)
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "led");
        if (ret)
        {
            printk("静态指定设备号失败\n");
            goto out5;
        }
    }
    else
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "led");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out5;
        }
        // 根据设备号得到主设备号
        major = MAJOR(devno);
        // 根据设备号得到此设备号
        minor = MINOR(devno);
    }
    if (major1 > 0)
    {
        ret = register_chrdev_region(MKDEV(major1, minor1), 1, "motor");
        if (ret)
        {
            printk("静态指定设备号失败\n");
            goto out6;
        }
    }
    else
    {
        ret = alloc_chrdev_region(&devno1, minor1, 1, "motor");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out6;
        }
        // 根据设备号得到主设备号
        major1 = MAJOR(devno1);
        // 根据设备号得到此设备号
        minor1 = MINOR(devno1);
    }
    if (major2 > 0)
    {
        ret = register_chrdev_region(MKDEV(major2, minor2), 1, "buzzer");
        if (ret)
        {
            printk("静态指定设备号失败\n");
            goto out7;
        }
    }
    else
    {
        ret = alloc_chrdev_region(&devno2, minor2, 1, "buzzer");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out7;
        }
        // 根据设备号得到主设备号
        major2 = MAJOR(devno2);
        // 根据设备号得到此设备号
        minor2 = MINOR(devno2);
    }
    if (major3 > 0)
    {
        ret = register_chrdev_region(MKDEV(major3, minor3), 1, "fan");
        if (ret)
        {
            printk("静态指定设备号失败\n");
            goto out8;
        }
    }
    else
    {
        ret = alloc_chrdev_region(&devno3, minor3, 1, "fan");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out8;
        }
        // 根据设备号得到主设备号
        major3 = MAJOR(devno3);
        // 根据设备号得到此设备号
        minor3 = MINOR(devno3);
    }
    printk("申请设备号成功\n");
    // 4.注册字符设备驱动对象*************************
    // cdev_add()
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out9;
    }
    ret = cdev_add(cdev1, MKDEV(major1, minor1), 1);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out10;
    }
    ret = cdev_add(cdev2, MKDEV(major2, minor2), 1);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out11;
    }
    ret = cdev_add(cdev3, MKDEV(major3, minor3), 1);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out12;
    }
    printk("注册字符设备驱动对象成功\n");
    // 5.向上提交目录********************************
    cls = class_create(THIS_MODULE, "led");
    if (IS_ERR(cls))
    {
        printk("向上提交led目录失败\n");
        ret = PTR_ERR(cls);
        goto out13;
    }
    cls1 = class_create(THIS_MODULE, "motor");
    if (IS_ERR(cls1))
    {
        printk("向上提交马达目录失败\n");
        ret = PTR_ERR(cls1);
        goto out14;
    }
    cls2 = class_create(THIS_MODULE, "buzzer");
    if (IS_ERR(cls2))
    {
        printk("向上提交目录蜂鸣器失败\n");
        ret = PTR_ERR(cls2);
        goto out15;
    }
    cls3 = class_create(THIS_MODULE, "fan");
    if (IS_ERR(cls3))
    {
        printk("向上提交风扇目录失败\n");
        ret = PTR_ERR(cls3);
        goto out16;
    }

    // 6.向上提交设备节点*****************************
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "led%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交led%d设备节点信息失败\n", i);
            ret = PTR_ERR(dev);
            goto out16;
        }
        printk("向上提交led%d设备节点信息成功\n", i);
    }
    dev = device_create(cls1, NULL, MKDEV(major1, minor1), NULL, "motor");
    if (IS_ERR(dev))
    {
        printk("向上提交马达设备节点信息失败\n");
        ret = PTR_ERR(dev);
        goto out17;
    }
    printk("向上提交马达设备节点信息成功\n");
    dev = device_create(cls2, NULL, MKDEV(major2, minor2), NULL, "buzzer");
    if (IS_ERR(dev))
    {
        printk("向上提交蜂鸣器设备节点信息失败\n");
        ret = PTR_ERR(dev);
        goto out18;
    }
    printk("向上提交蜂鸣器设备节点信息成功\n");
    dev = device_create(cls3, NULL, MKDEV(major3, minor3), NULL, "fan");
    if (IS_ERR(dev))
    {
        printk("向上提交风扇设备节点信息失败\n");
        ret = PTR_ERR(dev);
        goto out19;
    }
    printk("向上提交风扇设备节点信息成功\n");

    // 映射物理寄存器*********************************
    vir_led1 = ioremap(LED1_ADDR, sizeof(gpio_t));
    vir_led2 = ioremap(LED2_ADDR, sizeof(gpio_t));
    vir_led3 = vir_led1;
    vir_motor = vir_led2;
    vir_buzzer = ioremap(BUZZER_ADDR, sizeof(gpio_t));
    vir_fan = vir_led1;
    if (vir_led1 == NULL || vir_led2 == NULL || vir_buzzer == NULL)
    {
        printk("gpio寄存器地址映射表失败\n");
        return -EFAULT;
    }
    printk("gpio寄存器地址映射表成功\n");

    vir_rcc = ioremap(RCC_ADDR, 4);
    if (vir_rcc == NULL)
    {
        printk("RCC寄存器地址映射表失败\n");
        return -EFAULT;
    }
    printk("RCC寄存器地址映射成功\n");

    // rcc
    (*vir_rcc) |= (0x19 << 1);
    // 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));
    // 马达
    vir_motor->MODER &= (~(3 << 12));
    vir_motor->MODER |= (1 << 12);
    vir_motor->ODR &= (~(0x1 << 6));
    // 蜂鸣器
    vir_buzzer->MODER &= (~(3 << 12));
    vir_buzzer->MODER |= (1 << 12);
    vir_buzzer->ODR &= (~(1 << 6));
    // 风扇
    vir_fan->MODER &= (~(3 << 18));
    vir_fan->MODER |= (1 << 18);
    vir_fan->ODR &= (~(1 << 9));
    printk("寄存器初始化成功\n");
    return 0;

out19:
    device_destroy(cls2, MKDEV(major2, minor2));
out18:
    device_destroy(cls1, MKDEV(major1, minor1));

out17:
    for (--i; i >= 0; i--)
    {
        // 销毁上面提交的设备信息
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls3);
out16:
    class_destroy(cls2);
out15:
    class_destroy(cls1);
out14:
    class_destroy(cls);

out13:
    cdev_del(cdev);
out12:
    cdev_del(cdev1);
out11:
    cdev_del(cdev2);
out10:
    cdev_del(cdev3);

out9:
    unregister_chrdev_region(MKDEV(major3, minor3), 1);
out8:
    unregister_chrdev_region(MKDEV(major2, minor2), 1);
out7:
    unregister_chrdev_region(MKDEV(major1, minor1), 1);
out6:
    unregister_chrdev_region(MKDEV(major, minor), 3);

out5:
    kfree(cdev3);
out4:
    kfree(cdev2);
out3:
    kfree(cdev1);
out2:
    kfree(cdev);

out1:

    return -ret;
}

static void __exit mycdev_exit(void)
{
    int i;
    // 1.销毁设备信息  device_destroy
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    device_destroy(cls1, MKDEV(major1, minor1));
    device_destroy(cls2, MKDEV(major2, minor2));
    device_destroy(cls3, MKDEV(major3, minor3));
    printk("销毁设备节点信息成功\n");
    // 2.销毁目录  class_destroy
    class_destroy(cls);
    class_destroy(cls1);
    class_destroy(cls2);
    class_destroy(cls3);
    printk("销毁目录信息成功\n");
    // 3.注销对象  cdev_del()
    cdev_del(cdev);
    cdev_del(cdev1);
    cdev_del(cdev2);
    cdev_del(cdev3);
    printk("注销对象成功\n");
    // 4.释放设备号   unregister_chrdev_region()
    unregister_chrdev_region(MKDEV(major3, minor3), 1);
    unregister_chrdev_region(MKDEV(major2, minor2), 1);
    unregister_chrdev_region(MKDEV(major1, minor1), 1);
    unregister_chrdev_region(MKDEV(major, minor), 3);
    printk("释放设备号成功\n");
    // 5.释放对象空间  kfree()
    kfree(cdev3);
    kfree(cdev2);
    kfree(cdev1);
    kfree(cdev);
    printk("释放对象空间成功\n");
    // 取消寄存器地址映射
    iounmap(vir_led1);
    iounmap(vir_led2);
    iounmap(vir_buzzer);
    iounmap(vir_rcc);
    printk("取消寄存器地址映射成功\n");
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值