/* * Unique ID manipulation sysfs access generic functions * * Author: dmitry pervushin * * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include static int unlock; static spinlock_t u_lock; static const unsigned long UID_AUTOLOCK_TIMEOUT = HZ * 60 * 3; static struct timer_list u_timer; static void uid_timer_autolock(unsigned long param) { struct timer_list *tmr = (struct timer_list *)param; if (spin_trylock(&u_lock)) { if (unlock) pr_debug("%s: locked down.\n", __func__); unlock = 0; spin_unlock(&u_lock); } mod_timer(tmr, jiffies + UID_AUTOLOCK_TIMEOUT); } static LIST_HEAD(uid_provider_list); struct uid_provider { struct kobject *kobj; struct list_head list; struct uid_ops *ops; void *context; }; static struct uid_provider *uid_provider_find(const char *name); #define UID_FWD_SYSFS_FILE(var, file, param) \ static ssize_t var##_show(struct kobject *kobj, \ struct kobj_attribute *attr, char *buf) \ { \ struct uid_provider *p = \ uid_provider_find(kobject_name(kobj)); \ ssize_t r; \ BUG_ON(p == NULL); \ r = (p->ops && p->ops->file##_show) ? \ p->ops->file##_show(p->context, buf, param) : 0;\ return r; \ } \ \ static ssize_t var##_store(struct kobject *kobj, \ struct kobj_attribute *attr, const char *buf, \ size_t count) \ { \ struct uid_provider *p = \ uid_provider_find(kobject_name(kobj)); \ ssize_t r; \ int ul; \ BUG_ON(p == NULL); \ spin_lock(&u_lock); \ ul = unlock; \ spin_unlock(&u_lock); \ if (ul) \ r = (p->ops && p->ops->file##_store) ? \ p->ops->file##_store(p->context, buf, count, param) \ : count; \ else \ r = -EACCES; \ return r; \ } struct kobject *uid_kobj; #define UID_ATTR(_name, _varname) \ static struct kobj_attribute _varname##_attr = \ __ATTR(_name, 0644, _varname##_show, _varname##_store) UID_FWD_SYSFS_FILE(id, id, 1); UID_FWD_SYSFS_FILE(id_bin, id, 0); UID_ATTR(id, id); UID_ATTR(id.bin, id_bin); static struct attribute *uid_attrs[] = { &id_attr.attr, &id_bin_attr.attr, NULL }; static struct attribute_group uid_attr_group = { .attrs = uid_attrs, }; struct kobject *uid_provider_init(const char *name, struct uid_ops *ops, void *context) { struct uid_provider *new; int err; new = kzalloc(sizeof(*new), GFP_KERNEL); if (!new) { err = -ENOMEM; goto out; } new->kobj = kobject_create_and_add(name, uid_kobj); if (!new->kobj) { err = -ENOMEM; goto out; } new->ops = ops; new->context = context; err = sysfs_create_group(new->kobj, &uid_attr_group); if (err) goto out2; list_add_tail(&new->list, &uid_provider_list); return new->kobj; out2: kobject_del(new->kobj); out: kfree(new); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(uid_provider_init); static struct uid_provider *uid_provider_find(const char *name) { struct uid_provider *p; list_for_each_entry(p, &uid_provider_list, list) { if (strcmp(kobject_name(p->kobj), name) == 0) return p; } return NULL; } void uid_provider_remove(const char *name) { struct uid_provider *p; p = uid_provider_find(name); if (!p) return; kobject_del(p->kobj); list_del(&p->list); kfree(p); } EXPORT_SYMBOL_GPL(uid_provider_remove); static int uid_sysfs_init(void) { int error; uid_kobj = kobject_create_and_add("uid", NULL); if (!uid_kobj) { error = -ENOMEM; goto out1; } spin_lock_init(&u_lock); setup_timer(&u_timer, uid_timer_autolock, (unsigned long)&u_timer); /* try to lock each 3 minutes */ mod_timer(&u_timer, jiffies + UID_AUTOLOCK_TIMEOUT); return 0; out1: printk(KERN_ERR"%s failed, error %d.", __func__, error); return error; } module_param(unlock, int, 0600) core_initcall(uid_sysfs_init); MODULE_LICENSE("GPL"); MODULE_AUTHOR("dmitry pervushin "); MODULE_DESCRIPTION("Unique ID simple framework");