/*********************RTC驱动***************************/
/*
* RTC借助电池供电,它通常还有产生周期性中断以及产生闹钟中断的能力,是一种典型的字符设备。/drivers/rtc/rtc-dev.c中实现了通用的字符设备驱动层,它实现
* 了file_operations的成员函数以及一些通用的控制代码,并向底层导出了注册和注销函数rtc_class_ops结构体用于描述底层的RTC硬件操作drivers/rtc/rtc-s3c.c
* 实现了RTC驱动,其注册RTC以及绑定了rtc_class_ops相关数据结构和接口函数在rtc.h、rtc.c
*/
#include <linux/rtc.h>
#include <plat/regs-rtc.h>
struct rtc_device
{
struct device dev;
struct module *owner;
int id;
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops;
struct mutex ops_lock;
struct cdev char_dev;
unsigned long flags;
unsigned long irq_data;
spinlock_t irq_lock;
wait_queue_head_t irq_queue;
struct fasync_struct *async_queue;
struct rtc_task *irq_task;
spinlock_t irq_task_lock;
int irq_freq;
int max_user_freq;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int uie_irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};
struct rtc_class_ops { //用于描述底层的RTC硬件操作
int (*open)(struct device *);
void (*release)(struct device *);
int (*ioctl)(struct device *, unsigned int, unsigned long);
int (*read_time)(struct device *, struct rtc_time *);
int (*set_time)(struct device *, struct rtc_time *);
int (*read_alarm)(struct device *, struct rtc_wkalrm *);
int (*set_alarm)(struct device *, struct rtc_wkalrm *);
int (*proc)(struct device *, struct seq_file *);
int (*set_mmss)(struct device *, unsigned long secs);
int (*irq_set_state)(struct device *, int enabled);
int (*irq_set_freq)(struct device *, int freq);
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
int (*update_irq_enable)(struct device *, unsigned int enabled);
};
struct rtc_device *rtc_device_register(const char *name,struct device *dev,
const struct rtc_class_ops *ops,struct module *owner); //注册RTC
extern void rtc_device_unregister(struct rtc_device *rtc); //注销RTC
一般设计步骤:
1:struct rtc_device *rtc;
2:硬件相关的操作
3:static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.irq_set_freq = s3c_rtc_setfreq,
.irq_set_state = s3c_rtc_setpie,
.proc = s3c_rtc_proc,
};
4:注册
5:上述函数的实现
/**************************end***********************/
#ifndef __LED_H__
#define __LED_H__
#define LED_ON _IO(‘k‘, 0)
#define LED_OFF _IO(‘k‘, 1)
#endif // __LED_H__
/*
* io内存资源放到BSP linux-2.6.35-farsight\arch\arm\mach-s5pc100\mach-smdkc100
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
// ioremap
#include <asm/io.h>
#include "led.h"
struct led_t {
// 设备类型(字符设备、块设备、网络设备)
struct cdev led_cdev;
// 设备号
dev_t devno;
// 设备类
struct class *class;
// 设备
struct device *dev;
// 设备本身特征
// 1. 描述led设备本身
int on; // 0 - turn off 1-turn on
// lock
// 操作设备的寄存器的虚拟地址
unsigned long* GPG3CON;
unsigned long* GPG3DAT;
};
int led_open(struct inode *inode, struct file *filp)
{
filp->private_data = (void *)container_of(inode->i_cdev, struct led_t, led_cdev);
printk("char open\n");
return 0;
}
long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct led_t *led = (struct led_t *)filp->private_data;
long ret = 0;
unsigned long reg;
printk("led_ioctl: %p\n", led);
switch (cmd) {
// 5. 实现led开关
case LED_ON:
reg = readl(led->GPG3DAT);
reg |= 1;
writel(reg, led->GPG3DAT);
break;
case LED_OFF:
reg = readl(led->GPG3DAT);
reg &= ~1;
writel(reg, led->GPG3DAT);
break;
default:
ret = -ENOTTY;
break;
}
return 0;
}
int led_release(struct inode *inode, struct file *filp)
{
printk("char release\n");
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = led_ioctl,
.release = led_release,
};
int led_probe(struct platform_device *dev)
{
int ret = 0;
struct led_t *led;
struct resource * res;
unsigned long reg;
printk("platform: match ok!\n");
led = (struct led_t*)kmalloc(sizeof(struct led_t), GFP_KERNEL);
if (NULL == led) {
ret = -ENOMEM;
goto exit;
}
memset(led, 0, sizeof(struct led_t));
platform_set_drvdata(dev, led);
// 1. 获取资源
/*
* @brief 从bsp获取平台设备资源
* @param[in] dev 平台设备
* @param[in] type 资源类型(flags)
* @param[in] num 资源编号
* @return 资源
*/
// struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (NULL == res) {
ret = -ENOMEM;
goto platfrom_get_resource_err;
}
printk("res: %08x - %08x, size= %d\n", res->start, res->end, res->end - res->start + 1);
/* 如果是在bsp中设置io内存,不需要再申请
if (NULL == request_mem_region(res->start, res->start - res->end + 1, "led")) {
ret = -ENOMEM;
goto request_mem_region_err;
}*/
led->GPG3CON = (unsigned long *)ioremap(res->start, res->end - res->start + 1);
if (NULL == led->GPG3CON) {
printk("ioremap\n");
ret = -EBUSY;
goto ioremap_err;
}
led->GPG3DAT = led->GPG3CON + 1;
reg = readl(led->GPG3CON);
reg &= ~0xf;
reg |= 1;
writel(reg, led->GPG3CON);
reg = readl(led->GPG3DAT);
reg &= ~1;
writel(reg, led->GPG3DAT);
cdev_init(&led->led_cdev, &fops);
led->led_cdev.owner = THIS_MODULE;
ret = alloc_chrdev_region(&led->devno, 0, 1, "led device");
if (ret) {
printk("alloc_chrdev_region\n");
goto alloc_chrdev_region_err;
}
ret = cdev_add(&led->led_cdev, led->devno, 1);
if (ret) {
printk("cdev_add\n");
goto cdev_add_err;
}
led->class = class_create(THIS_MODULE, "led");
if (IS_ERR(led->class)) {
ret = PTR_ERR(led->class);
goto class_create_err;
}
led->dev = device_create(led->class, NULL, led->devno, NULL, "led");
if (IS_ERR(led->dev)) {
ret = PTR_ERR(led->dev);
goto device_create_err;
}
goto exit;
device_create_err:
class_destroy(led->class);
class_create_err:
cdev_del(&led->led_cdev);
cdev_add_err:
unregister_chrdev_region(led->devno, 1);
alloc_chrdev_region_err:
iounmap(led->GPG3CON);
ioremap_err:
// 如果是在bsp中设置io内存,不需要再申请
// release_mem_region(0xE03001C0, 8);
//request_mem_region_err:
platfrom_get_resource_err:
kfree(led);
exit:
return ret;
}
int led_remove(struct platform_device *dev)
{
struct led_t *led = (struct led_t *)platform_get_drvdata(dev);
printk("platform: driver remove\n");
device_destroy(led->class, led->devno);
class_destroy(led->class);
cdev_del(&led->led_cdev);
unregister_chrdev_region(led->devno, 1);
iounmap(led->GPG3CON);
// release_mem_region(0xE03001C0, 8);
kfree(led);
return 0;
}
struct platform_driver led_driver = {
.probe = led_probe,
.remove = __devexit_p(led_remove),
.driver = {
.name = "led_device",
},
};
int __init led_init(void)
{
platform_driver_register(&led_driver);
return 0;
}
void __exit led_exit(void)
{
platform_driver_unregister(&led_driver);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include "led.h"
int main (void)
{
int fd;
fd = open ("/dev/led", O_RDWR);
if (fd < 0) {
printf ("fd open failed\n");
exit(0);
}
printf ("\n/dev/chardev opened, fd=%d\n",fd);
ioctl(fd, LED_ON);
sleep(10);
ioctl(fd, LED_OFF);
close (fd);
printf ("/dev/hello closed :)\n");
return 0;
}
在BSP linux-2.6.35-farsight\arch\arm\mach-s5pc100\mach-smdkc100中添加:
// 1. 新建平台总线设备
struct resource led_resources[] = {
[0] = {
.start = 0xE03001C0, // io内存开始
.end = 0xE03001C0 + 7, // io内存结束(包含结束地址本身)
.flags = IORESOURCE_MEM,
}
};
static struct platform_device led_device = {
.name = "led_device", // 跟驱动里面的name相同
.id = -1, // 用于多个相近设备的设备id
// 添加IO内存资源
.num_resources = ARRAY_SIZE(led_resources),
.resource = led_resources,
};
* 中断例子(cat /proc/interrupts 命令查看中断是否申请成,并且中断次数)
*/
#include <linux/init.h>
#include <linux/module.h>
// 1. 头文件
#include <linux/interrupt.h>
#include <linux/irq.h>
// 设备结构体
struct key_t {
} key;
// 2.实现中断isr
/*
* @brief 中断处理函数
* @param[in] irq 中断号
* @param[in] dev 设备结构体指针
* @return 处理结果
* @li IRQ_NONE 没有处理中断(中断共享)
* @li IRQ_HANDLED 已经发生中断
* @li IRQ_WAKE_THREAD 唤醒irq处理线程
* @notes 本函数是内核调用
*/
irqreturn_t key_hander(int irq, void *dev)
{
printk("key_hander\n");
// 1. 不能休眠
// 2. 时间不能太长
// 3. 如果需要,调度中断下半部
return IRQ_HANDLED;
}
int __init int_demo_init(void)
{
int ret = 0;
// 3. 申请中断
/*
* @brief 申请中断
* @param[in] irq 中断号
* @param[in] handler 中断处理函数
* @param[in] flags 中断处理标志(中断促发方式,中断是否共享,中断是否可以嵌套)
* @param[in] name 中断名字(和设备名字相同)
* @param[in] dev 设备结构体
* @return 申请结果
* @li 0 申请成功
* @li < 0 错误码
*/
// int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
// IRQ_EINT(1) 外部中断1,不一定是中断1
// IRQF_DISABLED 执行中断处理函数期间,禁止中断
ret = request_irq(IRQ_EINT(1), key_hander, IRQF_TRIGGER_FALLING | IRQF_DISABLED, "key", &key);
return 0;
}
void __exit int_demo_exit(void)
{
// 3.
/*
* @brief 释放中断
* @param[in] irq 中断号
* @param[in] dev 设备结构体
*/
// void free_irq(unsigned int irq, void *dev);
free_irq(IRQ_EINT(1), &key);
}
module_init(int_demo_init);
module_exit(int_demo_exit);