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

来源:互联网 发布:淘宝原单店铺推荐 编辑:程序博客网 时间:2024/05/21 05:22


文件 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.cstatic 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.hresource结构体的定义: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.cstruct 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.cint 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.cstatic 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);elsep += sprintf(p, "**:");if ((unsigned int)alrm.time.tm_min <= 59)p += sprintf(p, "%02d:", alrm.time.tm_min);elsep += sprintf(p, "**:");if ((unsigned int)alrm.time.tm_sec <= 59)p += sprintf(p, "%02d\n", alrm.time.tm_sec);elsep += 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);elsep += sprintf(p, "****-");if ((unsigned int)alrm.time.tm_mon <= 11)p += sprintf(p, "%02d-", alrm.time.tm_mon + 1);elsep += sprintf(p, "**-");if ((unsigned int)alrm.time.tm_mday <= 31)p += sprintf(p, "%02d\n", alrm.time.tm_mday);elsep += 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.hstatic 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.cstruct 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.cstatic 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;intlen;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.cvoid 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.cint release_resource(struct resource *old){int retval;write_lock(&resource_lock);retval = __release_resource(old);write_unlock(&resource_lock);return retval;}

kernel/resource.cstatic 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.cvoid 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);}


原创粉丝点击