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命令