平台设备驱动 -- 相关函数及结构体


文件 drivers/base/platform.c
/**
 *    platform_get_irq - get an IRQ for a device
 *    @dev: platform device
 *    @num: IRQ number index
 */
int platform_get_irq(struct platform_device *dev, unsigned int num)
{
    struct resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);

    return r ? r->start : 0;
}
platform_get_resource(dev, IORESOURCE_IRQ, num)获取第num个flags与IORESOURCE_IRQ匹配的资源,rtc资源结构体如下,num为1与[2]对应,num为0与[1]对应。
文件 arch/arm/mach-s3c2410/devs.c
static struct resource s3c_rtc_resource[] = {
[0] = {
        .start = S3C2410_PA_RTC,
       .end   = S3C2410_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
    }
};

文件 include/linux/ioport.h
resource结构体的定义:
struct resource {
    const char *name;
    unsigned long start, end;
    unsigned long flags;
    struct resource *parent, *sibling, *child;
};

文件 drivers/base/platform.c
/**
 *    platform_get_resource - get a resource for a device
 *    @dev: platform device
 *    @type: resource type
 *    @num: resource index
 */
struct resource *
platform_get_resource(struct platform_device *dev, unsigned int type,
              unsigned int num)
{
    int i;

    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];

        if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
                 IORESOURCE_IRQ|IORESOURCE_DMA))
            == type)
            if (num-- == 0)
                return r;
    }
    return NULL;
}

文件 include/linux/ioport.h
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name))

文件 kernel/resource.c
struct resource iomem_resource = {
    .name    = "PCI mem",
    .start    = 0UL,
    .end    = ~0UL,
    .flags    = IORESOURCE_MEM,
};

EXPORT_SYMBOL(iomem_resource);


内核中的resource是以parent即iomem_resource为根的多叉树,其中每个节点都有若干个孩子节点,孩子节点之间前面节点的end比后面节点start小,由sibling相连;子节点的start落在父节点的start和end之间,如图:


函数__request_region()正是要查找以start为起始地址,长度为n的地址空间在资源树中的位置,并新建节点res,将其插入到资源树中这个对应的位置。

文件 kernel/resource.c
/*
 * This is compatibility stuff for IO resources.
 *
 * Note how this, unlike the above, knows about
 * the IO flag meanings (busy etc).
 *
 * Request-region creates a new busy region.
 *
 * Check-region returns non-zero if the area is already busy
 *
 * Release-region releases a matching busy region.
 */
struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);

	if (res) {
		memset(res, 0, sizeof(*res));
		res->name = name;
		res->start = start;
		res->end = start + n - 1;
		res->flags = IORESOURCE_BUSY;

		write_lock(&resource_lock);

		for (;;) {
			struct resource *conflict;

			conflict = __request_resource(parent, res);
			if (!conflict)
				break;
			if (conflict != parent) {
				parent = conflict;
				if (!(conflict->flags & IORESOURCE_BUSY))
					continue;
			}

			/* Uhhuh, that didn't work out.. */
			kfree(res);
			res = NULL;
			break;
		}
		write_unlock(&resource_lock);
	}
	return res;
}

文件 kernel/resource.c
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
	unsigned long start = new->start;
	unsigned long end = new->end;
	struct resource *tmp, **p;

	if (end < start)
		return root;
	if (start < root->start)
		return root;
	if (end > root->end)
		return root;
	p = &root->child;
	for (;;) {
		tmp = *p;
		if (!tmp || tmp->start > end) {
			new->sibling = tmp;
			*p = new;
			new->parent = root;
			return NULL;
		}
		p = &tmp->sibling;
		if (tmp->end < start)
			continue;
		return tmp;
	}
}

文件arch/arm/common/rtctime.c
int register_rtc(struct rtc_ops *ops)
{
	int ret = -EBUSY;

	down(&rtc_sem);
	if (rtc_ops == NULL) {
		rtc_ops = ops;

		ret = misc_register(&rtc_miscdev);
		if (ret == 0)
			create_proc_read_entry("driver/rtc", 0, NULL,
					       rtc_read_proc, ops);
	}
	up(&rtc_sem);

	return ret;
}


文件arch/arm/common/rtctime.c
static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	struct rtc_ops *ops = data;
	struct rtc_wkalrm alrm;
	struct rtc_time tm;
	char *p = page;

	if (rtc_read_time(ops, &tm) == 0) {
		p += sprintf(p,
			"rtc_time\t: %02d:%02d:%02d\n"
			"rtc_date\t: %04d-%02d-%02d\n"
			"rtc_epoch\t: %04lu\n",
			tm.tm_hour, tm.tm_min, tm.tm_sec,
			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
			rtc_epoch);
	}

	if (rtc_read_alarm(ops, &alrm) == 0) {
		p += sprintf(p, "alrm_time\t: ");
		if ((unsigned int)alrm.time.tm_hour <= 24)
			p += sprintf(p, "%02d:", alrm.time.tm_hour);
		else
			p += sprintf(p, "**:");
		if ((unsigned int)alrm.time.tm_min <= 59)
			p += sprintf(p, "%02d:", alrm.time.tm_min);
		else
			p += sprintf(p, "**:");
		if ((unsigned int)alrm.time.tm_sec <= 59)
			p += sprintf(p, "%02d\n", alrm.time.tm_sec);
		else
			p += sprintf(p, "**\n");

		p += sprintf(p, "alrm_date\t: ");
		if ((unsigned int)alrm.time.tm_year <= 200)
			p += sprintf(p, "%04d-", alrm.time.tm_year + 1900);
		else
			p += sprintf(p, "****-");
		if ((unsigned int)alrm.time.tm_mon <= 11)
			p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);
		else
			p += sprintf(p, "**-");
		if ((unsigned int)alrm.time.tm_mday <= 31)
			p += sprintf(p, "%02d\n", alrm.time.tm_mday);
		else
			p += sprintf(p, "**\n");
		p += sprintf(p, "alrm_wakeup\t: %s\n",
			     alrm.enabled ? "yes" : "no");
		p += sprintf(p, "alrm_pending\t: %s\n",
			     alrm.pending ? "yes" : "no");
	}

	if (ops->proc)
		p += ops->proc(p);

	return p - page;
}


注册混杂设备:

文件drivers/char/misc.c
/**
 *	misc_register	-	register a miscellaneous device
 *	@misc: device structure
 *	
 *	Register a miscellaneous device with the kernel. If the minor
 *	number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
 *	and placed in the minor field of the structure. For other cases
 *	the minor number requested is used.
 *
 *	The structure passed is linked into the kernel and may not be
 *	destroyed until it has been unregistered.
 *
 *	A zero is returned on success and a negative errno code for
 *	failure.
 */
 
int misc_register(struct miscdevice * misc)
{
	struct miscdevice *c;
	dev_t dev;
	int err;

	down(&misc_sem);
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == misc->minor) {
			up(&misc_sem);
			return -EBUSY;
		}
	}

	if (misc->minor == MISC_DYNAMIC_MINOR) {
		int i = DYNAMIC_MINORS;
		while (--i >= 0)
			if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
				break;
		if (i<0) {
			up(&misc_sem);
			return -EBUSY;
		}
		misc->minor = i;
	}

	if (misc->minor < DYNAMIC_MINORS)
		misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
	if (misc->devfs_name[0] == '\0') {
		snprintf(misc->devfs_name, sizeof(misc->devfs_name),
				"misc/%s", misc->name);
	}
	dev = MKDEV(MISC_MAJOR, misc->minor);

	misc->class = class_simple_device_add(misc_class, dev,
					      misc->dev, misc->name);
	if (IS_ERR(misc->class)) {
		err = PTR_ERR(misc->class);
		goto out;
	}

	err = devfs_mk_cdev(dev, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, 
			    misc->devfs_name);
	if (err) {
		class_simple_device_remove(dev);
		goto out;
	}

	/*
	 * Add it to the front, so that later devices can "override"
	 * earlier defaults
	 */
	list_add(&misc->list, &misc_list);
 out:
	up(&misc_sem);
	return err;
}

创建proc节点,并将节点与对应的处理函数read_proc绑定
文件include/linux/proc_fs.h
static inline struct proc_dir_entry *create_proc_read_entry(const char *name,
	mode_t mode, struct proc_dir_entry *base, 
	read_proc_t *read_proc, void * data)
{
	struct proc_dir_entry *res=create_proc_entry(name,mode,base);
	if (res) {
		res->read_proc=read_proc;
		res->data=data;
	}
	return res;
}

文件fs/proc/generic.c
struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
					 struct proc_dir_entry *parent)
{
	struct proc_dir_entry *ent;
	nlink_t nlink;

	if (S_ISDIR(mode)) {
		if ((mode & S_IALLUGO) == 0)
			mode |= S_IRUGO | S_IXUGO;
		nlink = 2;
	} else {
		if ((mode & S_IFMT) == 0)
			mode |= S_IFREG;
		if ((mode & S_IALLUGO) == 0)
			mode |= S_IRUGO;
		nlink = 1;
	}

	ent = proc_create(&parent,name,mode,nlink);
	if (ent) {
		if (S_ISDIR(mode)) {
			ent->proc_fops = &proc_dir_operations;
			ent->proc_iops = &proc_dir_inode_operations;
		}
		if (proc_register(parent, ent) < 0) {
			kfree(ent);
			ent = NULL;
		}
	}
	return ent;
}

文件fs/proc/generic.c
static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent,
					  const char *name,
					  mode_t mode,
					  nlink_t nlink)
{
	struct proc_dir_entry *ent = NULL;
	const char *fn = name;
	int len;

	/* make sure name is valid */
	if (!name || !strlen(name)) goto out;

	if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0)
		goto out;

	/* At this point there must not be any '/' characters beyond *fn */
	if (strchr(fn, '/'))
		goto out;

	len = strlen(fn);

	ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
	if (!ent) goto out;

	memset(ent, 0, sizeof(struct proc_dir_entry));
	memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
	ent->name = ((char *) ent) + sizeof(*ent);
	ent->namelen = len;
	ent->mode = mode;
	ent->nlink = nlink;
 out:
	return ent;
}

文件 fs/proc/generic.c
/*
 * This function parses a name such as "tty/driver/serial", and
 * returns the struct proc_dir_entry for "/proc/tty/driver", and
 * returns "serial" in residual.
 */
static int xlate_proc_name(const char *name,
			   struct proc_dir_entry **ret, const char **residual)
{
	const char     		*cp = name, *next;
	struct proc_dir_entry	*de;
	int			len;

	de = &proc_root;
	while (1) {
		next = strchr(cp, '/');
		if (!next)
			break;

		len = next - cp;
		for (de = de->subdir; de ; de = de->next) {
			if (proc_match(len, cp, de))
				break;
		}
		if (!de)
			return -ENOENT;
		cp += len + 1;
	}
	*residual = cp;
	*ret = de;
	return 0;
}

文件 arch/arm/common/rtctime.c
void unregister_rtc(struct rtc_ops *rtc)
{
	down(&rtc_sem);
	if (rtc == rtc_ops) {
		remove_proc_entry("driver/rtc", NULL);
		misc_deregister(&rtc_miscdev);
		rtc_ops = NULL;
	}
	up(&rtc_sem);
}

文件 fs/proc/generic.c
/*
 * Remove a /proc entry and free it if it's not currently in use.
 * If it is in use, we set the 'deleted' flag.
 */
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
	struct proc_dir_entry **p;
	struct proc_dir_entry *de;
	const char *fn = name;
	int len;

	if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
		goto out;
	len = strlen(fn);
	for (p = &parent->subdir; *p; p=&(*p)->next ) {
		if (!proc_match(len, fn, *p))
			continue;
		de = *p;
		*p = de->next;
		de->next = NULL;
		if (S_ISDIR(de->mode))
			parent->nlink--;
		proc_kill_inodes(de);
		de->nlink = 0;
		WARN_ON(de->subdir);
		if (!atomic_read(&de->count))
			free_proc_entry(de);
		else {
			de->deleted = 1;
			printk("remove_proc_entry: %s/%s busy, count=%d\n",
				parent->name, de->name, atomic_read(&de->count));
		}
		break;
	}
out:
	return;
}

drivers/char/misc.c

/**
 *	misc_deregister - unregister a miscellaneous device
 *	@misc: device to unregister
 *
 *	Unregister a miscellaneous device that was previously
 *	successfully registered with misc_register(). Success
 *	is indicated by a zero return, a negative errno code
 *	indicates an error.
 */

int misc_deregister(struct miscdevice * misc)
{
	int i = misc->minor;

	if (list_empty(&misc->list))
		return -EINVAL;

	down(&misc_sem);
	list_del(&misc->list);
	class_simple_device_remove(MKDEV(MISC_MAJOR, misc->minor));
	devfs_remove(misc->devfs_name);
	if (i < DYNAMIC_MINORS && i>0) {
		misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
	}
	up(&misc_sem);
	return 0;
}

kernel/resource.c

int release_resource(struct resource *old)
{
	int retval;

	write_lock(&resource_lock);
	retval = __release_resource(old);
	write_unlock(&resource_lock);
	return retval;
}

kernel/resource.c

static int __release_resource(struct resource *old)
{
	struct resource *tmp, **p;

	p = &old->parent->child;
	for (;;) {
		tmp = *p;
		if (!tmp)
			break;
		if (tmp == old) {
			*p = tmp->sibling;
			old->parent = NULL;
			return 0;
		}
		p = &tmp->sibling;
	}
	return -EINVAL;
}

arch/sh/kernel/cpu/rtc.c

#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)

arch/arm/common/rtctime.c

void rtc_update(unsigned long num, unsigned long events)
{
	spin_lock(&rtc_lock);
	rtc_irq_data = (rtc_irq_data + (num << 8)) | events;
	spin_unlock(&rtc_lock);

	wake_up_interruptible(&rtc_wait);
	kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值