📄 file.c
字号:
*/ if (kobj->kset && kobj->kset->ktype) ops = kobj->kset->ktype->sysfs_ops; else if (kobj->ktype) ops = kobj->ktype->sysfs_ops; else ops = &subsys_sysfs_ops; error = -EACCES; /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ if (!ops) goto err_out; /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { if (!(inode->i_mode & S_IWUGO) || !ops->store) goto err_out; } /* File needs read support. * The inode's perms must say it's ok, and we there * must be a show method for it. */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) goto err_out; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) goto err_out; mutex_init(&buffer->mutex); buffer->needs_read_fill = 1; buffer->ops = ops; file->private_data = buffer; /* make sure we have open dirent struct */ error = sysfs_get_open_dirent(attr_sd, buffer); if (error) goto err_free; /* open succeeded, put active references */ sysfs_put_active_two(attr_sd); return 0; err_free: kfree(buffer); err_out: sysfs_put_active_two(attr_sd); return error;}static int sysfs_release(struct inode *inode, struct file *filp){ struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata; struct sysfs_buffer *buffer = filp->private_data; sysfs_put_open_dirent(sd, buffer); if (buffer->page) free_page((unsigned long)buffer->page); kfree(buffer); return 0;}/* Sysfs attribute files are pollable. The idea is that you read * the content and then you use 'poll' or 'select' to wait for * the content to change. When the content changes (assuming the * manager for the kobject supports notification), poll will * return POLLERR|POLLPRI, and select will return the fd whether * it is waiting for read, write, or exceptions. * Once poll/select indicates that the value has changed, you * need to close and re-open the file, as simply seeking and reading * again will not get new data, or reset the state of 'poll'. * Reminder: this only works for attributes which actively support * it, and it is not possible to test an attribute from userspace * to see if it supports poll (Neither 'poll' nor 'select' return * an appropriate error code). When in doubt, set a suitable timeout value. */static unsigned int sysfs_poll(struct file *filp, poll_table *wait){ struct sysfs_buffer * buffer = filp->private_data; struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct sysfs_open_dirent *od = attr_sd->s_attr.open; /* need parent for the kobj, grab both */ if (!sysfs_get_active_two(attr_sd)) goto trigger; poll_wait(filp, &od->poll, wait); sysfs_put_active_two(attr_sd); if (buffer->event != atomic_read(&od->event)) goto trigger; return 0; trigger: buffer->needs_read_fill = 1; return POLLERR|POLLPRI;}void sysfs_notify(struct kobject *k, char *dir, char *attr){ struct sysfs_dirent *sd = k->sd; mutex_lock(&sysfs_mutex); if (sd && dir) sd = sysfs_find_dirent(sd, dir); if (sd && attr) sd = sysfs_find_dirent(sd, attr); if (sd) { struct sysfs_open_dirent *od; spin_lock(&sysfs_open_dirent_lock); od = sd->s_attr.open; if (od) { atomic_inc(&od->event); wake_up_interruptible(&od->poll); } spin_unlock(&sysfs_open_dirent_lock); } mutex_unlock(&sysfs_mutex);}EXPORT_SYMBOL_GPL(sysfs_notify);const struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, .open = sysfs_open_file, .release = sysfs_release, .poll = sysfs_poll,};int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type){ umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) return -ENOMEM; sd->s_attr.attr = (void *)attr; sysfs_addrm_start(&acxt, dir_sd); rc = sysfs_add_one(&acxt, sd); sysfs_addrm_finish(&acxt); if (rc) sysfs_put(sd); return rc;}/** * sysfs_create_file - create an attribute file for an object. * @kobj: object we're creating for. * @attr: attribute descriptor. */int sysfs_create_file(struct kobject * kobj, const struct attribute * attr){ BUG_ON(!kobj || !kobj->sd || !attr); return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);}/** * sysfs_add_file_to_group - add an attribute file to a pre-existing group. * @kobj: object we're acting for. * @attr: attribute descriptor. * @group: group name. */int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group){ struct sysfs_dirent *dir_sd; int error; dir_sd = sysfs_get_dirent(kobj->sd, group); if (!dir_sd) return -ENOENT; error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); sysfs_put(dir_sd); return error;}EXPORT_SYMBOL_GPL(sysfs_add_file_to_group);/** * sysfs_chmod_file - update the modified mode value on an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * @mode: file permissions. * */int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode){ struct sysfs_dirent *victim_sd = NULL; struct dentry *victim = NULL; struct inode * inode; struct iattr newattrs; int rc; rc = -ENOENT; victim_sd = sysfs_get_dirent(kobj->sd, attr->name); if (!victim_sd) goto out; mutex_lock(&sysfs_rename_mutex); victim = sysfs_get_dentry(victim_sd); mutex_unlock(&sysfs_rename_mutex); if (IS_ERR(victim)) { rc = PTR_ERR(victim); victim = NULL; goto out; } inode = victim->d_inode; mutex_lock(&inode->i_mutex); newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; rc = notify_change(victim, &newattrs); if (rc == 0) { mutex_lock(&sysfs_mutex); victim_sd->s_mode = newattrs.ia_mode; mutex_unlock(&sysfs_mutex); } mutex_unlock(&inode->i_mutex); out: dput(victim); sysfs_put(victim_sd); return rc;}EXPORT_SYMBOL_GPL(sysfs_chmod_file);/** * sysfs_remove_file - remove an object attribute. * @kobj: object we're acting for. * @attr: attribute descriptor. * * Hash the attribute name and kill the victim. */void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr){ sysfs_hash_and_remove(kobj->sd, attr->name);}/** * sysfs_remove_file_from_group - remove an attribute file from a group. * @kobj: object we're acting for. * @attr: attribute descriptor. * @group: group name. */void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group){ struct sysfs_dirent *dir_sd; dir_sd = sysfs_get_dirent(kobj->sd, group); if (dir_sd) { sysfs_hash_and_remove(dir_sd, attr->name); sysfs_put(dir_sd); }}EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group);struct sysfs_schedule_callback_struct { struct kobject *kobj; void (*func)(void *); void *data; struct module *owner; struct work_struct work;};static void sysfs_schedule_callback_work(struct work_struct *work){ struct sysfs_schedule_callback_struct *ss = container_of(work, struct sysfs_schedule_callback_struct, work); (ss->func)(ss->data); kobject_put(ss->kobj); module_put(ss->owner); kfree(ss);}/** * sysfs_schedule_callback - helper to schedule a callback for a kobject * @kobj: object we're acting for. * @func: callback function to invoke later. * @data: argument to pass to @func. * @owner: module owning the callback code * * sysfs attribute methods must not unregister themselves or their parent * kobject (which would amount to the same thing). Attempts to do so will * deadlock, since unregistration is mutually exclusive with driver * callbacks. * * Instead methods can call this routine, which will attempt to allocate * and schedule a workqueue request to call back @func with @data as its * argument in the workqueue's process context. @kobj will be pinned * until @func returns. * * Returns 0 if the request was submitted, -ENOMEM if storage could not * be allocated, -ENODEV if a reference to @owner isn't available. */int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), void *data, struct module *owner){ struct sysfs_schedule_callback_struct *ss; if (!try_module_get(owner)) return -ENODEV; ss = kmalloc(sizeof(*ss), GFP_KERNEL); if (!ss) { module_put(owner); return -ENOMEM; } kobject_get(kobj); ss->kobj = kobj; ss->func = func; ss->data = data; ss->owner = owner; INIT_WORK(&ss->work, sysfs_schedule_callback_work); schedule_work(&ss->work); return 0;}EXPORT_SYMBOL_GPL(sysfs_schedule_callback);EXPORT_SYMBOL_GPL(sysfs_create_file);EXPORT_SYMBOL_GPL(sysfs_remove_file);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -