首页 > 其他分享 >10 - platform 设备驱动

10 - platform 设备驱动

时间:2024-10-09 22:53:35浏览次数:3  
标签:10 return struct rtc platform tm time device 驱动

---- 整理自 王利涛老师 课程

文章目录

1. 第一个 platform 驱动

在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo", // 设备名称
    .id   = -1,         // 设备ID,-1表示自动分配
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

volatile rtc_reg_t *regs = NULL;
static unsigned long current_time = 0;

void set_rtc_alarm(rtc_reg_t *regs)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = regs->RTCDR;    /* get current time */
    current_time = tmp;
    regs->RTCMR = tmp + 1;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    tm.hour = (current_time % 86400) / 3600;
    tm.min  = (current_time % 3600) / 60;
    tm.sec  = current_time % 60;
    printk("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    set_rtc_alarm(regs);
    
    return IRQ_HANDLED;
}

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    set_rtc_alarm(regs);

    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev->name);
    free_irq(39,NULL);

    return 0;
}

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

2. platform 驱动注册过程分析

2.1 platform 总线的注册过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 platform 设备的注册过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 platform 驱动的注册过程

在这里插入图片描述
在这里插入图片描述

3. platform bus match 方法

  • 比对 device 和 driver 的 name 变量是否一样
  • of
  • ACPI
  • id_table:驱动的复用,兼容多个设备

在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo0", // 设备名称
    .id   = -1,         // 设备ID,-1表示自动分配
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

#define RTC_BASE 0x10017000

volatile rtc_reg_t *regs = NULL;
static unsigned long current_time = 0;

void set_rtc_alarm(rtc_reg_t *regs)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    tmp = regs->RTCDR;    /* get current time */
    current_time = tmp;
    regs->RTCMR = tmp + 1;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    tm.hour = (current_time % 86400) / 3600;
    tm.min  = (current_time % 3600) / 60;
    tm.sec  = current_time % 60;
    printk("%d:%d:%d\n", tm.hour, tm.min, tm.sec);

    set_rtc_alarm(regs);
    
    return IRQ_HANDLED;
}

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    set_rtc_alarm(regs);

    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    printk("%s: driver remove: %s\n", __func__, dev->name);
    free_irq(39,NULL);

    return 0;
}

// 平台驱动ID表
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
};

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, // ID表
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

4. 注册一个字符设备驱动

  • 文件 I/O 接口去读取时间、设置时间、设置闹钟
    • 在内核层面去设置一个闹钟中断
    • 去注册字符驱动,引出各种操作接口
    • 手动创建设备节点
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo0", // 设备名称
    .id   = -1,         // 设备ID,-1表示自动分配
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}
static void set_rtc_alarm(void)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(&tm); /* get alarm time */
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR;
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

static struct rtc_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }
    switch (cmd) {
    case RTC_SET_TIME:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_time(&tm);
        break;
    case RTC_SET_ALARM:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc ioctl: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_alarm();
        break;
    default:
        printk("error cmd!\n");
        return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    free_irq(39,NULL);
    printk("%s: driver remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动ID表
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
};

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, // ID表
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST   _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("current time %d:%d:%d\n", tm.hour, tm.min, tm.sec);

#if 0
    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
#endif

    /* set rtc time */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("set time %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    /* set rtc alarm */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    printf("set alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    close(fd);

    return 0;
}

ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

5. 自动创建设备节点

  • 设备模型:热插拔事件 uevent
  • mdev 实时监测,解析 uevent,创建设备文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo0", // 设备名称
    .id   = -1,         // 设备ID,-1表示自动分配
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}
static void set_rtc_alarm(void)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(&tm); /* get alarm time */
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR;
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

static struct rtc_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }
    switch (cmd) {
    case RTC_SET_TIME:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_time(&tm);
        break;
    case RTC_SET_ALARM:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc ioctl: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_alarm();
        break;
    default:
        printk("error cmd!\n");
        return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    regs = (rtc_reg_t *)ioremap(RTC_BASE, sizeof(rtc_reg_t)); 

    ret = request_irq(39, rtc_alarm_handler, 0, "rtc0-test", NULL);
    if (ret == -1) {
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }
	
    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    free_irq(39,NULL);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
	printk("%s: driver remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动ID表
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
};

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, // ID表
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST   _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("current time %d:%d:%d\n", tm.hour, tm.min, tm.sec);

#if 0
    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
#endif

    /* set rtc time */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("set time %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    /* set rtc alarm */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    printf("set alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    close(fd);

    return 0;
}

ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

6. platform resource

  • 虚拟总线,ARM SOC 芯片:集成了各种 IP
  • 每个 IP 工作依赖平台的各种的资源:时钟、总线、中断、寄存器、IO
  • platform_device 定义各种依赖的资源
  • platform_driver 获取资源,进行初始化硬件

在这里插入图片描述
在这里插入图片描述

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 资源定义,包含内存和中断资源
static struct resource rtc_resource[] = {
    [0] = {
        .start = 0x10017000,			// 内存资源起始地址
        .end   = 0x10017000 + 4 * 8,	// 内存资源结束地址
        .flags = IORESOURCE_MEM,		// 标记为内存资源
    },
    [1] = {
        .start = 39,				// 中断资源编号
        .end   = 39,				// 中断资源结束编号
        .flags = IORESOURCE_IRQ,	// 标记为中断资源
    },
};


static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo0", 		// 设备名称
    .id   = -1,         		// 设备ID,-1表示自动分配
    .num_resources = 2,			// 资源数量
    .resource = rtc_resource,	// 资源列表
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_device {
    struct cdev *rtc_cdev;
    dev_t devno;
};

//#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *rtc_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static void set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;
}
static void set_rtc_alarm(void)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(&tm);
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR; /* get current time */
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

static struct rtc_device rtc_chrdev;

static int rtc_open(struct inode *inode, struct file *fp)
{
    fp->private_data = &rtc_chrdev;
    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }
    switch (cmd) {
    case RTC_SET_TIME:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_time(&tm);
        break;
    case RTC_SET_ALARM:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc ioctl: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_alarm();
        break;
    default:
        printk("error cmd!\n");
        return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;
    struct resource *res_mem, *res_irq;
    int device_irq = -1;
	
    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start); 

    device_irq = platform_get_irq(dev, 0);
    ret = request_irq(device_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_chrdev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_chrdev.devno), \
                             MINOR(rtc_chrdev.devno));

    rtc_chrdev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_chrdev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_chrdev.rtc_cdev, rtc_chrdev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    rtc_device = device_create(rtc_class, NULL,rtc_chrdev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(rtc_device)) {
        printk("device_create failed!\n");
        return -1;
    }
	
    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    cdev_del(rtc_chrdev.rtc_cdev);
    unregister_chrdev_region(rtc_chrdev.devno, 1);
    free_irq(39,NULL);
    device_unregister(rtc_device);
    class_destroy(rtc_class);
	printk("%s: driver remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动ID表
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
};

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, // ID表
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST   _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("current time %d:%d:%d\n", tm.hour, tm.min, tm.sec);

#if 0
    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
#endif

    /* set rtc time */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("set time %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    /* set rtc alarm */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    printf("set alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    close(fd);

    return 0;
}

ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

7. 设备类接口的抽象与回调

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 资源定义,包含内存和中断资源
static struct resource rtc_resource[] = {
    [0] = {
        .start = 0x10017000,			// 内存资源起始地址
        .end   = 0x10017000 + 4 * 8,	// 内存资源结束地址
        .flags = IORESOURCE_MEM,		// 标记为内存资源
    },
    [1] = {
        .start = 39,				// 中断资源编号
        .end   = 39,				// 中断资源结束编号
        .flags = IORESOURCE_IRQ,	// 标记为中断资源
    },
};


static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device rtc_dev = {
    .name = "rtc-demo0", 		// 设备名称
    .id   = -1,         		// 设备ID,-1表示自动分配
    .num_resources = 2,			// 资源数量
    .resource = rtc_resource,	// 资源列表
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init rtc_device_init(void)
{
    return platform_device_register(&rtc_dev); // 注册平台设备
}

static void __exit rtc_device_exit(void)
{
    platform_device_unregister(&rtc_dev); // 注销平台设备
}

module_init(rtc_device_init);
module_exit(rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

typedef volatile struct{
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_class_operations {
    int (*set_time)(struct rtc_time *);
    int (*set_alarm)(struct rtc_time *);

};

struct rtc_device {
    struct cdev *rtc_cdev;
    dev_t devno;
    const struct rtc_class_operations *ops;
};

//#define RTC_BASE 0x10017000

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)

volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
static struct rtc_time tm;
static struct class  *rtc_class;
static struct device *class_device;

static void rtc_time_translate(unsigned long time)
{
    tm.hour = (time % 86400) / 3600;
    tm.min  = (time % 3600) / 60;
    tm.sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static int set_rtc_time(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->set_time) 
        ret = rtc->ops->set_time(t);

    return ret;
}

static int pl031_set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;

    return 0;
}
static void set_rtc_alarm(void)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(&tm);
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;
}

static irqreturn_t rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR; /* get current time */
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

static struct rtc_device rtc_dev;

struct rtc_class_operations pl031_rtc_ops = {
    .set_time = pl031_set_rtc_time,
};

static int rtc_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &rtc_dev;

    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    struct rtc_device *p = fp->private_data;
    printk("char device: %d:%d closed\n", MAJOR(p->devno), MINOR(p->devno));
    return 0;
}

static ssize_t rtc_read(struct file *fp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(cur_time);
    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)){
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *fp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    int len = 0;
    
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }
    cur_time = rtc_tm_to_time(&tm);
    regs->RTCLR = cur_time;

    return len;
}

static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_device *pdev = file->private_data;
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }
    switch (cmd) {
    case RTC_SET_TIME:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_time(pdev, &tm);
        break;
    case RTC_SET_ALARM:
        if (_IOC_DIR(cmd) != _IOC_WRITE) {
            printk("rtc ioctl: invalid ioclt cmd!\n");
            return -EINVAL;
        }
        if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
            printk("rtc ioctl: copy_from_user error!\n");
            return -EFAULT;
        }
        set_rtc_alarm();
        break;
    default:
        printk("error cmd!\n");
        return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};

static int rtc_driver_probe(struct platform_device *dev)
{
    irqreturn_t ret = 0;
    struct resource *res_mem;
    //struct resource *res_irq;
    int device_irq = -1;
	
    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);
    res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start); 

    device_irq = platform_get_irq(dev, 0);
    ret = request_irq(device_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }

    ret = alloc_chrdev_region(&rtc_dev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_dev.devno), \
                             MINOR(rtc_dev.devno));

    rtc_dev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_dev.rtc_cdev, &rtc_fops);

    ret = cdev_add(rtc_dev.rtc_cdev, rtc_dev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    rtc_dev.ops = &pl031_rtc_ops;

    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }

    class_device = device_create(rtc_class, NULL, rtc_dev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(class_device)) {
        printk("device_create failed!\n");
        return -1;
    }
	
    return 0;
}

static int rtc_driver_remove(struct platform_device *dev)
{
    cdev_del(rtc_dev.rtc_cdev);
    unregister_chrdev_region(rtc_dev.devno, 1);
    free_irq(39,NULL);
    device_unregister(class_device);
    class_destroy(rtc_class);
	printk("%s: driver remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动ID表
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
};

// 平台驱动结构体
static struct platform_driver rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, // ID表
    .probe  = rtc_driver_probe,
    .remove = rtc_driver_remove,
    .driver = {
        .name = "rtc-demo", // 驱动名称
    }
};

static int __init rtc_driver_init(void)
{
    return platform_driver_register(&rtc_drv); // 注册平台驱动
}

static void __exit rtc_driver_exit(void)
{
    platform_driver_unregister(&rtc_drv); // 注销平台驱动
}

module_init(rtc_driver_init);
module_exit(rtc_driver_exit);
MODULE_LICENSE("GPL");
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST   _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("current time %d:%d:%d\n", tm.hour, tm.min, tm.sec);

#if 0
    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
#endif

    /* set rtc time */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("set time %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    /* set rtc alarm */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    printf("set alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    close(fd);

    return 0;
}

ifneq ($(KERNELRELEASE),)
obj-m := rtc_device.o rtc_driver.o
else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

8. 实现自己的 RTC 子系统

  • 字符驱动注册功能与具体的寄存器操作分离
  • 抽象的 class 接口
#ifndef __RTC_H__
#define __RTC_H__

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_class_operations {
    int (*set_time)(struct rtc_time *);
    int (*read_time)(struct rtc_time *);
    int (*set_alarm)(struct rtc_time *);
    int (*read_alarm)(struct rtc_time *);
};

struct rtc_device {
    struct  cdev *rtc_cdev;
    dev_t devno;
    int   irq;
    struct rtc_class_operations *ops;
};

extern int register_rtc_device(struct rtc_class_operations *ops);
extern void unregister_rtc_device(void);

#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include "rtc.h"

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_READ_ALARM  _IOR(RTC_IOC_MAGIC, 3, struct rtc_time)

struct rtc_device rtc_dev;
EXPORT_SYMBOL_GPL(rtc_dev);

static int set_rtc_time(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->set_time) {
        ret = rtc->ops->set_time(t);
    }

    return ret;
}

static int read_rtc_time(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->read_time) {
        ret = rtc->ops->read_time(t);
    }
    
    return ret;
}

static int read_rtc_alarm(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->read_alarm) {
        ret = rtc->ops->read_alarm(t);
    }

    return ret;
}

static int set_rtc_alarm(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->set_alarm) 
        ret = rtc->ops->set_alarm(t);

    return ret;
}


static int rtc_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &rtc_dev;

    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *filp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    struct rtc_device *pdev = filp->private_data;
    struct rtc_time tm;

    read_rtc_time(pdev, &tm);

    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)) {
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *filp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    struct rtc_device *pdev = filp->private_data;
    int len = 0;
    struct rtc_time tm;
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    
    set_rtc_time(pdev, &tm);
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }

    return len;
}


static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_device *pdev = file->private_data;
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    struct rtc_time tm;

    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }

    switch (cmd) {
        case RTC_SET_TIME:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }

            set_rtc_time(pdev, &tm);
            break;

        case RTC_SET_ALARM:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc ioctl: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }

            set_rtc_alarm(pdev, &tm);
            break;

        case RTC_READ_ALARM:
            if (_IOC_DIR(cmd) != _IOC_READ) {
                printk("rtc ioctl: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            read_rtc_alarm(pdev, &tm);
            
            if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)) {
                printk("rtc_read error!\n");
                return -1;
            }

            break;

        default:
            printk("error cmd!\n");
            return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};


static struct class  *rtc_class;
static struct device *class_device;

int register_rtc_device(struct rtc_class_operations *rtc_class_ops)
{
    int ret = 0;

    ret = alloc_chrdev_region(&rtc_dev.devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }
    printk("RTC devnum:%d minornum:%d\n", MAJOR(rtc_dev.devno), \
                             MINOR(rtc_dev.devno));

    rtc_dev.ops = rtc_class_ops;
    rtc_dev.rtc_cdev = cdev_alloc();
    cdev_init(rtc_dev.rtc_cdev, &rtc_fops);
    
    ret = cdev_add(rtc_dev.rtc_cdev, rtc_dev.devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }

    class_device = device_create(rtc_class, NULL, rtc_dev.devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(class_device)) {
        printk("device_create failed!\n");
        return -1;
    }

    return 0;
}
EXPORT_SYMBOL(register_rtc_device);

void unregister_rtc_device(void)
{

    cdev_del(rtc_dev.rtc_cdev);
    unregister_chrdev_region(rtc_dev.devno, 1);
    free_irq(39,NULL);
    device_unregister(class_device);
}

EXPORT_SYMBOL(unregister_rtc_device);

static int __init rtc_dev_init(void)
{
    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }
    return 0;
}

static void __exit rtc_dev_exit(void)
{
    class_destroy(rtc_class); 
}

module_init(rtc_dev_init);
module_exit(rtc_dev_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 资源定义,包含内存和中断资源
static struct resource rtc_resource[] = {
    [0] = {
        .start = 0x10017000,			// 内存资源起始地址
        .end   = 0x10017000 + 4 * 8,	// 内存资源结束地址
        .flags = IORESOURCE_MEM,		// 标记为内存资源
    },
    [1] = {
        .start = 39,				// 中断资源编号
        .end   = 39,				// 中断资源结束编号
        .flags = IORESOURCE_IRQ,	// 标记为中断资源
    },
};

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device pl031_rtc_dev = {
    .name = "rtc-demo0", 		// 设备名称
    .id   = -1,         		// 设备ID,-1表示自动分配
    .num_resources = 2,			// 资源数量
    .resource = rtc_resource,	// 资源列表
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init pl031_rtc_device_init(void)
{
    return platform_device_register(&pl031_rtc_dev);
}

static void __exit pl031_rtc_device_exit(void)
{
    platform_device_unregister(&pl031_rtc_dev);
}

module_init(pl031_rtc_device_init);
module_exit(pl031_rtc_device_exit);
MODULE_LICENSE("GPL");
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include "rtc.h"

typedef volatile struct {
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;


volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
//static struct rtc_time tm;

static void rtc_time_translate(struct rtc_time *tm, unsigned long time)
{
    tm->hour = (time % 86400) / 3600;
    tm->min  = (time % 3600) / 60;
    tm->sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static int pl031_read_rtc_time(struct rtc_time *t)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(t, cur_time);

    return sizeof(struct rtc_time);
}

static int pl031_set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;

    return 0;
}

static int pl031_read_rtc_alarm(struct rtc_time *t)
{
    cur_time = regs->RTCMR;
    rtc_time_translate(t, cur_time);

    return sizeof(struct rtc_time);
}

static int pl031_set_rtc_alarm(struct rtc_time *t)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(t);
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;

    return 0;
}

static irqreturn_t  rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR; /* get current time */
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;      /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

struct rtc_class_operations pl031_ops = {
    .read_time  = pl031_read_rtc_time,
    .set_time  = pl031_set_rtc_time,
    .read_alarm = pl031_read_rtc_alarm,
    .set_alarm = pl031_set_rtc_alarm,
};

static int pl031_driver_probe(struct platform_device *dev)
{
    struct resource *res_mem;
    //struct resource *res_irq;
    int dev_irq = -1;
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);

    res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start); 

    //res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    dev_irq = platform_get_irq(dev, 0);
    ret = request_irq(dev_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }
    
    register_rtc_device(&pl031_ops);

    return 0;
}

static int pl031_driver_remove(struct platform_device *dev)
{
    unregister_rtc_device();
    printk("%s: remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动结构体
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
    { }
};

static struct platform_driver pl031_rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, 
    .probe  = pl031_driver_probe,
    .remove = pl031_driver_remove,
    .driver = {
        .name = "rtc-demo",
    }
};


static int __init pl031_platform_driver_init(void)
{
    return platform_driver_register(&pl031_rtc_drv);
}

static void __exit pl031_platform__driver_exit(void)
{
    platform_driver_unregister(&pl031_rtc_drv);
}

module_init(pl031_platform_driver_init);
module_exit(pl031_platform__driver_exit);
MODULE_LICENSE("GPL");
ifneq ($(KERNELRELEASE),)
obj-m += rtc_core.o 
obj-m += pl031_device.o 
obj-m += pl031_driver.o

else
EXTRA_CFLAGS += -DDEBUG 
KDIR:=/home/code_folder/uboot_linux_rootfs/kernel/linux-5.10.4
ARCH_ARGS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
all:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules
clean:
	make $(ARCH_ARGS) -C $(KDIR) M=$(PWD) modules clean
endif

在这里插入图片描述

9. 设备的资源管理

  • devm_kmalloc、devm_kfree

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_SET_TEST   _IOW('T', 2, struct rtc_time)

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

int main(void)
{
    int fd;
    int len, ret;
    struct rtc_time tm;

    fd = open("/dev/rtc-demo0", O_RDWR);
    if(fd == -1) {
        printf("connot open file..\n");
        exit(1);
    }
    
    len = sizeof(struct rtc_time);
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read error!\n");
        exit(1);
    }

    printf("current time %d:%d:%d\n", tm.hour, tm.min, tm.sec);

#if 0
    tm.hour = 11;
    tm.min  = 11;
    tm.sec  = 11;
    ret = write(fd, &tm, len);
    if (ret < len) {
        printf("write error!\n");
        return -1;
    }
    ret = read(fd, &tm, len);
    if (ret < len) {
        printf("read error!\n");
        return -1;
    }
    printf("%d:%d:%d\n", tm.hour, tm.min, tm.sec);
#endif

    /* set rtc time */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 22;
    ret = ioctl(fd, RTC_SET_TIME, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    if ((ret = read(fd, &tm, len)) < len) {
        printf("read failed!\n");
        return -1;
    }
    printf("set time %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    /* set rtc alarm */
    tm.hour = 22;
    tm.min  = 22;
    tm.sec  = 25;
    ret = ioctl(fd, RTC_SET_ALARM, (unsigned long)&tm);
    if (ret) {
        printf("ioctl failed!\n");
        return -1;
    }
    printf("set alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    close(fd);

    return 0;
}

#ifndef __RTC_H__
#define __RTC_H__

struct rtc_time{
    unsigned int year;
    unsigned int mon;
    unsigned int day;
    unsigned int hour;
    unsigned int min;
    unsigned int sec;
};

struct rtc_class_operations {
    int (*set_time)(struct rtc_time *);
    int (*read_time)(struct rtc_time *);
    int (*set_alarm)(struct rtc_time *);
    int (*read_alarm)(struct rtc_time *);
};

struct rtc_device {
    struct  cdev *rtc_cdev;
    struct device dev;
    dev_t devno;
    int   irq;
    struct rtc_class_operations *ops;
};

extern int register_rtc_device(struct rtc_class_operations *ops);
extern void unregister_rtc_device(void);

#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include "rtc.h"

#define RTC_IOC_MAGIC 'R'
#define RTC_SET_TIME   _IOW(RTC_IOC_MAGIC, 1, struct rtc_time)
#define RTC_SET_ALARM  _IOW(RTC_IOC_MAGIC, 2, struct rtc_time)
#define RTC_READ_ALARM  _IOW(RTC_IOC_MAGIC, 3, struct rtc_time)

struct rtc_device *rtc_dev;

static int set_rtc_time(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->set_time) {
        ret = rtc->ops->set_time(t);
    }

    return ret;
}

static int read_rtc_time(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->read_time) {
        ret = rtc->ops->read_time(t);
    }
    
    return ret;
}

static int read_rtc_alarm(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->read_alarm) {
        ret = rtc->ops->read_alarm(t);
    }

    return ret;
}

static int set_rtc_alarm(struct rtc_device *rtc, struct rtc_time *t)
{
    int ret;

    if(rtc->ops->set_alarm) 
        ret = rtc->ops->set_alarm(t);

    return ret;
}


static int rtc_open(struct inode *inode, struct file *filp)
{
    filp->private_data = rtc_dev;

    return 0;
}

static int rtc_release(struct inode *inode, struct file *fp)
{
    return 0;
}

static ssize_t rtc_read(struct file *filp, char __user *buf, 
                           size_t size, loff_t *pos)
{
    struct rtc_device *pdev = filp->private_data;
    struct rtc_time tm;

    read_rtc_time(pdev, &tm);

    if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)) {
        printk("rtc_read error!\n");
        return -1;
    }

    return sizeof(struct rtc_time);
}

static ssize_t rtc_write(struct file *filp, const char __user *buf, 
                            size_t size, loff_t *pos)
{
    struct rtc_device *pdev = filp->private_data;
    int len = 0;
    struct rtc_time tm;
    len = sizeof(struct rtc_time);
    if (!access_ok(buf, len))
        return -1;
    
    set_rtc_time(pdev, &tm);
    if (unlikely(copy_from_user(&tm, buf, len) != 0)) {
        printk("rtc_write error!\n");
        return -1;
    }

    return len;
}


static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct rtc_device *pdev = file->private_data;
    struct rtc_time __user *buf = (struct rtc_time __user *)arg;
    struct rtc_time tm;

    if (_IOC_TYPE(cmd) != RTC_IOC_MAGIC) {
        printk("rtc ioctl: invalid cmd!\n");
        return -EINVAL;
    }

    switch (cmd) {
        case RTC_SET_TIME:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }

            set_rtc_time(pdev, &tm);
            break;

        case RTC_SET_ALARM:
            if (_IOC_DIR(cmd) != _IOC_WRITE) {
                printk("rtc ioctl: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            if (unlikely(copy_from_user(&tm, buf, sizeof(tm)))) {
                printk("rtc ioctl: copy_from_user error!\n");
                return -EFAULT;
            }

            set_rtc_alarm(pdev, &tm);
            break;

        case RTC_READ_ALARM:
            if (_IOC_DIR(cmd) != _IOC_READ) {
                printk("rtc ioctl: invalid ioclt cmd!\n");
                return -EINVAL;
            }

            read_rtc_alarm(pdev, &tm);
            
            if (unlikely(copy_to_user(buf, &tm, sizeof(struct rtc_time)) != 0)) {
                printk("rtc_read error!\n");
                return -1;
            }

            break;

        default:
            printk("error cmd!\n");
            return -1;
    }

    return 0;
}

static const struct file_operations rtc_fops = {
    .owner   = THIS_MODULE,
    .read    = rtc_read,
    .write   = rtc_write,
    .open    = rtc_open,
    .unlocked_ioctl = rtc_ioctl,
    .release = rtc_release,
};


static struct class  *rtc_class;
static struct device *class_device;

int register_rtc_device(struct rtc_class_operations *rtc_class_ops)
{
    int ret = 0;
    int devno = -1;

    ret = alloc_chrdev_region(&devno, 0, 1, "rtc-demo");
    if (ret) {
        printk("alloc char device number failed!\n");
        return ret;
    }

    class_device = device_create(rtc_class, NULL, devno, NULL, \
                               "rtc-demo%d", 0);
    if (IS_ERR(class_device)) {
        printk("device_create failed!\n");
        return -1;
    }
    
   // rtc_dev = kmalloc(sizeof(struct rtc_device), GFP_KERNEL);
    rtc_dev = devm_kzalloc(class_device, sizeof(struct rtc_device), GFP_KERNEL);

    rtc_dev->devno = devno;
    rtc_dev->ops = rtc_class_ops;
    rtc_dev->rtc_cdev = cdev_alloc();
    cdev_init(rtc_dev->rtc_cdev, &rtc_fops);
    
    ret = cdev_add(rtc_dev->rtc_cdev, rtc_dev->devno, 1);
    if (ret < 0) {
        printk("cdev_add failed..\n");
        return -1;
    } else {
        printk("Register char module: rtc success!\n");
    }


    return 0;
}
EXPORT_SYMBOL(register_rtc_device);

void unregister_rtc_device(void)
{
    cdev_del(rtc_dev->rtc_cdev);
    unregister_chrdev_region(rtc_dev->devno, 1);
    free_irq(39,NULL);
    //devm_kfree(class_device, rtc_dev);
    device_unregister(class_device);
    // kfree(rtc_dev);
}

EXPORT_SYMBOL(unregister_rtc_device);

static int __init rtc_dev_init(void)
{
    rtc_class = class_create(THIS_MODULE, "rtc-class");
    if (IS_ERR(rtc_class)) {
        printk("class_create failed!\n");
        return -1;
    }
    return 0;
}

static void __exit rtc_dev_exit(void)
{
    class_destroy(rtc_class); 
}

module_init(rtc_dev_init);
module_exit(rtc_dev_exit);
MODULE_LICENSE("GPL");
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>

// 资源定义,包含内存和中断资源
static struct resource rtc_resource[] = {
    [0] = {
        .start = 0x10017000,			// 内存资源起始地址
        .end   = 0x10017000 + 4 * 8,	// 内存资源结束地址
        .flags = IORESOURCE_MEM,		// 标记为内存资源
    },
    [1] = {
        .start = 39,				// 中断资源编号
        .end   = 39,				// 中断资源结束编号
        .flags = IORESOURCE_IRQ,	// 标记为中断资源
    },
};

static void rtc_device_release(struct device *dev)
{
    printk("%s: %s released...\n", __func__, dev_name(dev));
}

// 定义一个平台设备
struct platform_device pl031_rtc_dev = {
    .name = "rtc-demo0", 		// 设备名称
    .id   = -1,         		// 设备ID,-1表示自动分配
    .num_resources = 2,			// 资源数量
    .resource = rtc_resource,	// 资源列表
    .dev  = {
        .release = rtc_device_release,
    },
};

static int __init pl031_rtc_device_init(void)
{
    return platform_device_register(&pl031_rtc_dev);
}

static void __exit pl031_rtc_device_exit(void)
{
    platform_device_unregister(&pl031_rtc_dev);
}

module_init(pl031_rtc_device_init);
module_exit(pl031_rtc_device_exit);
MODULE_LICENSE("GPL");

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include "rtc.h"

typedef volatile struct {
    unsigned long  RTCDR;    /* +0x00: data register */
    unsigned long  RTCMR;    /* +0x04: match register */
    unsigned long  RTCLR;    /* +0x08: load register */
    unsigned long  RTCCR;    /* +0x0C: control register */
    unsigned long  RTCIMSC;  /* +0x10: interrupt mask set and clear register*/
    unsigned long  RTCRIS;   /* +0x14: raw interrupt status register*/
    unsigned long  RTCMIS;   /* +0x18: masked interrupt status register */
    unsigned long  RTCICR;   /* +0x1C: interrupt clear register */
} rtc_reg_t;


volatile rtc_reg_t *regs = NULL;
static unsigned long cur_time = 0;
//static struct rtc_time tm;

static void rtc_time_translate(struct rtc_time *tm, unsigned long time)
{
    tm->hour = (time % 86400) / 3600;
    tm->min  = (time % 3600) / 60;
    tm->sec = time % 60;
}

static unsigned long rtc_tm_to_time(struct rtc_time *t)
{
    unsigned long time;
    time = t->hour * 3600 + t->min * 60 + t->sec;
    return time;
}

static int pl031_read_rtc_time(struct rtc_time *t)
{
    cur_time = regs->RTCDR;
    rtc_time_translate(t, cur_time);

    return sizeof(struct rtc_time);
}

static int pl031_set_rtc_time(struct rtc_time *t)
{
    cur_time = rtc_tm_to_time(t);
    regs->RTCLR = cur_time;

    return 0;
}

static int pl031_read_rtc_alarm(struct rtc_time *t)
{
    cur_time = regs->RTCMR;
    rtc_time_translate(t, cur_time);

    return sizeof(struct rtc_time);
}

static int pl031_set_rtc_alarm(struct rtc_time *t)
{
    unsigned long tmp = 0;
    tmp = regs->RTCCR;    /* write enable */
    tmp = tmp & 0xFFFFFFFE;
    regs->RTCCR = tmp;

    //tmp = regs->RTCDR;    /* get current time */
    tmp = rtc_tm_to_time(t);
    regs->RTCMR = tmp;/* set alarm time */

    regs->RTCICR = 1;     /* clear RTCINTR interrupt */ 
    regs->RTCIMSC = 1;    /* set the mask */

    tmp = regs->RTCCR;    /* write disable */
    tmp = tmp | 0x1;
    regs->RTCCR = tmp;

    return 0;
}

static irqreturn_t  rtc_alarm_handler(int irq, void *dev_id)
{
    struct rtc_time tm;
    
    cur_time = regs->RTCDR; /* get current time */
    tm.hour = (cur_time % 86400) / 3600;
    tm.min  = (cur_time % 3600) / 60;
    tm.sec  = cur_time % 60;
    printk("alarm %d:%d:%d\n", tm.hour, tm.min, tm.sec);
    
    regs->RTCICR = 1;      /* clear RTCINTR interrupt */ 

    return IRQ_HANDLED;
}

struct rtc_class_operations pl031_ops = {
    .read_time  = pl031_read_rtc_time,
    .set_time  = pl031_set_rtc_time,
    .read_alarm = pl031_read_rtc_alarm,
    .set_alarm = pl031_set_rtc_alarm,
};

static int pl031_driver_probe(struct platform_device *dev)
{
    struct resource *res_mem;
    //struct resource *res_irq;
    int dev_irq = -1;
    irqreturn_t ret = 0;

    printk("%s: probe and init hello_device: %s\n", __func__, dev->name);

    res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
    regs = (rtc_reg_t *)ioremap(res_mem->start, res_mem->end - res_mem->start); 

    //res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
    dev_irq = platform_get_irq(dev, 0);
    ret = request_irq(dev_irq, rtc_alarm_handler, 0,  "rtc0-test", NULL);
    if (ret == -1){
        printk("request_irq failed!\n");
        return -1;
    }
    
    register_rtc_device(&pl031_ops);

    return 0;
}

static int pl031_driver_remove(struct platform_device *dev)
{
    unregister_rtc_device();
    printk("%s: remove: %s\n", __func__, dev->name);

    return 0;
}

// 平台驱动结构体
static struct platform_device_id  arm_rtc_primcell_driver_ids[] = {
    {
        .name        = "rtc-demo0",
        .driver_data = 0, 
    }, 
    {
        .name        = "rtc-demo1",
        .driver_data = 1, 
    },
    { }
};

static struct platform_driver pl031_rtc_drv = {
    .id_table = arm_rtc_primcell_driver_ids, 
    .probe  = pl031_driver_probe,
    .remove = pl031_driver_remove,
    .driver = {
        .name = "rtc-demo",
    }
};


static int __init pl031_platform_driver_init(void)
{
    return platform_driver_register(&pl031_rtc_drv);
}

static void __exit pl031_platform__driver_exit(void)
{
    platform_driver_unregister(&pl031_rtc_drv);
}

module_init(pl031_platform_driver_init);
module_exit(pl031_platform__driver_exit);
MODULE_LICENSE("GPL");

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

调用 device_unregister 时释放:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

标签:10,return,struct,rtc,platform,tm,time,device,驱动
From: https://blog.csdn.net/weixin_36098975/article/details/142695762

相关文章

  • 10.9日牛客CSP-S考试总结
    10.9日牛客CSP-S考试总结T1考场上大概看了一个多小时,想了一个部分分的做法,结果变界判断错误,导致puts("-1");的分也没拿到。T2大部分时间在做这题,想了一个搜索的做法,每次枚举从哪个时刻出发,取了一个较为合适的范围,又加了一个类似于spfa容错的优化。但是因为范围开小就会导致正......
  • js学习 -2024/10/9
    今天学习了js中的一些知识DOM通过document.get...函数获取元素对象可以查阅h3school资料找对象的函数,操作对象,//根据id获取元素对象//letid=document.getElementById('back');//id.src="../img/02.png";//根据标签获取元素对象vardivss=document.getElement......
  • 10-12 ~ 10-26 停课记录
    模拟赛10-12(周六)AtCoderBeginnerContest37510-13(周日)08:30~12:00【LGR-201-Div.3】SCP2024第二轮(复赛J组)模拟14:30~18:30【LGR-202-Div.2】SCP2024第二轮(复赛S组)模拟&「KDOI」Round10noip模拟赛#7AtCoderRegularContest18510-14(周一)Oct/14/2......
  • 2024/10/9日 日志
    今天进行了离散数学中关于并包的知识,通过对之前关系内容的复习和今天知识的学习,对三种并包形式,即自反r,对称s,传递t的实现有了规律总结,即RIr即(如包含x,x),RIk即(如包含y,x),以及R*R+即(R+R2+R3+.....+Rn)。此外,在数据结构的学习中,继续学习了关于栈以及队列的内容,并对双栈公用的代码进行了实......
  • 2024/10/09 模拟赛总结
    \(100+40+20+8=168\),拿到了大众分,至少没挂分吧#A.矩阵交换一个\(m\)维偏序,可以使用\(m-1\)维树状数组解决以第\(i\)作为第\(i\)关键字,进行排序,这样一定最优。排完之后直接判断是否满足条件即可//BLuemoon_#include<bits/stdc++.h>usingnamespacestd;consti......
  • 10.9日
    .定义与语法JavaScript:函数可以使用关键字function定义,也可以使用箭头函数(ES6+):javascriptfunctionmyFunction(a,b){returna+b;}constmyArrowFunction=(a,b)=>a+b;Java:函数是类中定义的方法,必须指定返回类型,并且所有的方法都属于某个类:javapublicclas......
  • ChatGPT国内中文版镜像网站整理合集(2024-10月最新)
    一、GPT中文镜像站① yixiaai.com 支持GPT4、4o、o1、文件上传、知识库,支持MJ绘画② chat.lify.vip 支持通用全模型,支持文件读取、插件、绘画、AIPPT③ AIChat 支持GPT3.5/4,4o以及MJ绘画1.什么是镜像站镜像站(MirrorSite)是指通过复制原始网站内容和结构,创建的备用......
  • 代码随想录算法训练营day10| 232.用栈实现队列 225. 用队列实现栈 20. 有效的括
    学习资料:https://programmercarl.com/栈与队列理论基础.html栈与队列学习记录:232.用栈实现队列(两个栈(stack_in,stack_out)实现一个队列的行为)点击查看代码classMyQueue(object):def__init__(self):self.stack_in=[]self.stack_out=[]d......
  • 【笔记】杂题选讲 2024.10.5(DNF)
    十一杂题选讲-VirtualJudge(vjudge.net)/mnt/e/codes/contests/20241008/sol.md目录[AGC004F]Namori[1406E]DeletingNumbers[1081G]MergesortStrikesBack[1033E]HiddenBipartiteGraph[1254E]SendTreetoCharlie[1012E]Cyclesort[1284F]NewYearandSocialN......
  • 2024.10.9 LGJ Round
    B对于所有\(x\in[0,n],y\in[0,m]\),求执行\(x\getsx+y,y\getsx+y\)若干次后满足\(x=k\)的双元组个数。这个题充分体现我的唐氏。具体地枚举\(x,y\)分别被算了多少次,系数是斐波那契数列,所以项数很少。然后转化为求\(k_1x+k_2y=k\)的方案数,这个我非常唐不会求。只需......