嵌入式Linux驱动学习【11】—— RTC

1 字符设备注册

1.1 注册方式

(1)2.4之前

register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

  缺点:注册字符设备,还会连续注册0~255个次设备号,使它们绑定在同一个file_operations。
(2)2.4之后
  静态注册(指定设备编号来注册)、动态分配(不指定设备编号来注册),以及有连续注册的次设备编号范围区间。

/*指定设备编号来静态注册一个字符设备*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);  
/*动态分配一个字符设备,注册成功并将分配到的主次设备号放入*dev里*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);
 /*初始化cdev结构体,并将file_operations结构体放入cdev-> ops 里*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops);

1.2 程序

  构造两个不同的file_operations操作结构体,次设备号0-1对应第一个file_operations,次设备号2-3对应第二个file_operations。
(1)驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <linux/device.h>

/* 1. 确定主设备号 */
static int major;

static int hello_open(struct inode *inode, struct file *file)
{
	printk("hello_open\n");
	return 0;
}

static int hello2_open(struct inode *inode, struct file *file)
{
	printk("hello2_open\n");
	return 0;
}


/* 2. 构造file_operations */
static struct file_operations hello_fops = {
	.owner = THIS_MODULE,
	.open  = hello_open,
};

static struct file_operations hello2_fops = {
	.owner = THIS_MODULE,
	.open  = hello2_open,
};


#define HELLO_CNT   2

static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;

static int hello_init(void)
{
	dev_t devid;
	
	/* 3. 告诉内核 */
#if 0
	major = register_chrdev(0, "hello", &hello_fops); /* (major,  0), (major, 1), ..., (major, 255)都对应hello_fops */
#else
	if (major) {
		devid = MKDEV(major, 0);
		register_chrdev_region(devid, HELLO_CNT, "hello");  /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
	} else {
		alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
		major = MAJOR(devid);                     
	}
	
	cdev_init(&hello_cdev, &hello_fops);
	cdev_add(&hello_cdev, devid, HELLO_CNT);

	devid = MKDEV(major, 2);
	register_chrdev_region(devid, 1, "hello2");
	cdev_init(&hello2_cdev, &hello2_fops);
	cdev_add(&hello2_cdev, devid, 1);
	
#endif

	cls = class_create(THIS_MODULE, "hello");
	device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */
	device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */
	device_create(cls, NULL, MKDEV(major, 2), NULL, "hello2"); /* /dev/hello2 */
	device_create(cls, NULL, MKDEV(major, 3), NULL, "hello3"); /* /dev/hello3 */
	
	
	return 0;
}

static void hello_exit(void)
{
	device_destroy(cls, MKDEV(major, 0));
	device_destroy(cls, MKDEV(major, 1));
	device_destroy(cls, MKDEV(major, 2));
	device_destroy(cls, MKDEV(major, 3));
	class_destroy(cls);

	cdev_del(&hello_cdev);
	unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);

	cdev_del(&hello2_cdev);
	unregister_chrdev_region(MKDEV(major, 2), 1);
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

(2)测试

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


/* 
 * hello_test /dev/hello0
 */

void print_usage(char *file)
{
	printf("%s <dev>\n", file);
}

int main(int argc, char **argv)
{
	int fd;
	if (argc != 2)
	{
		print_usage(argv[0]);
		return 0;
	}

	fd = open(argv[1], O_RDWR);
	if (fd < 0)
		printf("can't open %s\n", argv[1]);
	else
		printf("can open %s\n", argv[1]);

	return 0;
}

2 RTC

(1)设备
  /arch/arm/plat-s3c24xx/dev.c中定义。

static struct resource s3c_rtc_resource[] = {
	[0] = {
		.start = S3C24XX_PA_RTC,
		.end   = S3C24XX_PA_RTC + 0xff,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_RTC,
		.end   = IRQ_RTC,
		.flags = IORESOURCE_IRQ,
	},
	[2] = {
		.start = IRQ_TICK,
		.end   = IRQ_TICK,
		.flags = IORESOURCE_IRQ
	}
};

struct platform_device s3c_device_rtc = {
	.name		  = "s3c2410-rtc",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_rtc_resource),
	.resource	  = s3c_rtc_resource,
};

(2)驱动
  /drivers/rtc/rtc-s3c.c中注册。

static struct platform_driver s3c2410_rtc_driver = {
	.probe		= s3c_rtc_probe,
	.remove		= __devexit_p(s3c_rtc_remove),
	.suspend	= s3c_rtc_suspend,
	.resume		= s3c_rtc_resume,
	.driver		= {
		.name	= "s3c2410-rtc",
		.owner	= THIS_MODULE,
	},
};

static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";

static int __init s3c_rtc_init(void)
{
	printk(banner);
	return platform_driver_register(&s3c2410_rtc_driver);
}

  当与设备匹配后,调用probe。

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,
};

static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
	...
	s3c_rtc_tickno = platform_get_irq(pdev, 1);//获取IRQ_TICK节拍中断资源
	...

	s3c_rtc_alarmno = platform_get_irq(pdev, 0);//获取IRQ_RTC闹钟中断资源
	...
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取内存资源
	...

	s3c_rtc_enable(pdev, 1);//使能时钟
 	..

	rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE);//注册

	...
}

  rtc_device_register向内核注册rtc_device设备。
  rtc_device_register在drivers/rtc/Class.c文件内被定义。Class.c文件主要定义了RTC子系统,而内核初始化,便会进入Class.c,进入rtc_init()->rtc_dev_init(),来注册字符设备。

void __init rtc_dev_init(void)
{
	...
	err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
	
}

  在rtc_device_register中

struct rtc_device *rtc_device_register(const char *name, struct device *dev,
					const struct rtc_class_ops *ops,
					struct module *owner)
{
	...

	rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); //分配结构体
	if (rtc == NULL) {
		err = -ENOMEM;
		goto exit_idr;
	}

	rtc->id = id; //设置内容
	rtc->ops = ops;
	rtc->owner = owner;
	rtc->max_user_freq = 64;
	rtc->dev.parent = dev;
	rtc->dev.class = rtc_class;
	rtc->dev.release = rtc_device_release;

	...

	rtc_dev_prepare(rtc);

	err = device_register(&rtc->dev);
	if (err)
		goto exit_kfree;

	rtc_dev_add_device(rtc);
	rtc_sysfs_add_device(rtc);
	rtc_proc_add_device(rtc);

	...
}

  在rtc_dev_prepare(rtc)中调用了

cdev_init(&rtc->char_dev, &rtc_dev_fops);          //绑定file_operations

  rtc_dev_add_device(rtc)调用了:

cdev_add(&rtc->char_dev, rtc->dev.devt, 1);    //注册rtc->char_dev字符设备,添加一个从设备到系统中

  从上述流程中看出,rtc字符设备的注册与第1节流程一致。
(3)应用层
  open流程

app:    open("/dev/rtc0");
-------------------------------------------
kernel: sys_open
            rtc_dev_fops.open
            	rtc_dev_open
            	    // 根据次设备号找到以前用"rtc_device_register"注册的rtc_device
            		struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);
            		const struct rtc_class_ops *ops = rtc->ops;
            		err = ops->open ? ops->open(rtc->dev.parent) : 0;
												s3c_rtc_open

  ioctl流程

app:    ioctl(fd, RTC_RD_TIME,...)
-------------------------------------------
kernel: sys_ioctl
             rtc_dev_fops.ioctl
             	 rtc_dev_ioctl
             	 		struct rtc_device *rtc = file->private_data;
             	 		rtc_read_time(rtc, &tm);
             	 			err = rtc->ops->read_time(rtc->dev.parent, tm);
             	 						s3c_rtc_gettime

3 测试

(1)修改内核
  在arch/arm/mach-s3c2440/mach-mini2440.c中增加s3c_device_rtc。

static struct platform_device *mini2440_devices[] __initdata = {
    ...
	&s3c_device_rtc,
	...
}

(2)运行新内核后,查看ls /dev/rtc*
(3)查看时间
  在linux里有两个时钟:
  硬件时钟(2440里寄存器的时钟)、系统时钟(内核中的时钟)
  所以有两个不同的命令: date命令、hwclock命令

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值