您的位置:首页 > 文旅 > 美景 > 搭建网页的基础语言_手机版网页开发者工具_最新nba排名_西安关键词推广

搭建网页的基础语言_手机版网页开发者工具_最新nba排名_西安关键词推广

2024/10/9 11:19:45 来源:https://blog.csdn.net/weixin_36098975/article/details/142695762  浏览:    关键词:搭建网页的基础语言_手机版网页开发者工具_最新nba排名_西安关键词推广
搭建网页的基础语言_手机版网页开发者工具_最新nba排名_西安关键词推广

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

文章目录

  • 1. 第一个 platform 驱动
  • 2. platform 驱动注册过程分析
    • 2.1 platform 总线的注册过程
    • 2.2 platform 设备的注册过程
    • 2.3 platform 驱动的注册过程
  • 3. platform bus match 方法
  • 4. 注册一个字符设备驱动
  • 5. 自动创建设备节点
  • 6. platform resource
  • 7. 设备类接口的抽象与回调
  • 8. 实现自己的 RTC 子系统
  • 9. 设备的资源管理

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 0x10017000volatile 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 0x10017000volatile 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 0tm.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 0tm.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 0tm.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 0tm.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.oelse
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 0tm.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 时释放:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com