---- 整理自 王利涛老师 课程
文章目录
- 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 时释放: