Messages in this thread Patch in this message | | | Date | Sat, 26 Nov 2011 18:30:42 +0100 | From | Alessandro Rubini <> | Subject | [RFC PATCH 3/7] drivers/zio: core files for the ZIO input/output |
| |
From: Federico Vaga <federico.vaga@gmail.com>
This adds the core files for ZIO. The files match commit 7d37663 in git://ohwr.org/misc/zio.git .
The Makefile already includes material that is added by later patches. This doesn't imply a compilation error as drivers/zio is not yet being compiled by drivers/Makefile.
Signed-off-by: Federico Vaga <federico.vaga@gmail.com> Signed-off-by: Alessandro Rubini <rubini@gnudd.com> Acked-by: Juan David Gonzalez Cobas <dcobas@cern.ch> Acked-by: Samuel Iglesias Gonsalvez <siglesia@cern.ch> Acked-by: Manohar Vanga <manohar.vanga@cern.ch> --- drivers/zio/Makefile | 10 + drivers/zio/zio-cdev.c | 498 +++++++++++++++++ drivers/zio/zio-sys.c | 1423 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1931 insertions(+), 0 deletions(-) create mode 100644 drivers/zio/Makefile create mode 100644 drivers/zio/zio-cdev.c create mode 100644 drivers/zio/zio-sys.c
diff --git a/drivers/zio/Makefile b/drivers/zio/Makefile new file mode 100644 index 0000000..fb5dd14 --- /dev/null +++ b/drivers/zio/Makefile @@ -0,0 +1,10 @@ + +zio-core-objs := zio-cdev.o zio-sys.o +obj-$(CONFIG_ZIO) += zio-core.o + +obj-$(CONFIG_ZIO) += drivers/ +obj-$(CONFIG_ZIO) += buffers/ +obj-$(CONFIG_ZIO) += triggers/ + +hostprogs-y := zio-dump + diff --git a/drivers/zio/zio-cdev.c b/drivers/zio/zio-cdev.c new file mode 100644 index 0000000..fae5749 --- /dev/null +++ b/drivers/zio/zio-cdev.c @@ -0,0 +1,498 @@ +/* Federico Vaga and Alessandro Rubini for CERN, 2011, GNU GPLv2 or later */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + +#define __ZIO_INTERNAL__ +#include <linux/zio.h> +#include <linux/zio-buffer.h> +#include <linux/zio-trigger.h> + +static DEFINE_MUTEX(zmutex); +struct zio_status zio_global_status; +static struct zio_status *zstat = &zio_global_status; /* Always use ptr */ + +static ssize_t zio_show_version(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d.%d\n", ZIO_MAJOR_VERSION, ZIO_MINOR_VERSION); +} + +static struct class_attribute zclass_attrs[] = { + __ATTR(version, S_IRUGO, zio_show_version, NULL), + __ATTR_NULL, +}; + +static char *zio_devnode(struct device *dev, mode_t *mode) +{ + return kasprintf(GFP_KERNEL, "zio/%s", dev_name(dev)); +} + +/* + * zio_class: don't use class_create to create class because it doesn't permit + * to insert a set of class attributes. This structure is the exact + * reproduction of what class_create does but with some additional settings. + */ +static struct class zio_class = { + .name = "zio", + .owner = THIS_MODULE, + .class_attrs = zclass_attrs, + .devnode = zio_devnode, +}; + +/* Retrieve a channel from one of its minors */ +static struct zio_channel *__zio_minor_to_chan(dev_t mm) +{ + struct zio_cset *zcset; + dev_t cset_base, chan_minor; + int found = 0; + + /* Extract cset minor base */ + chan_minor = mm & (ZIO_NMAX_CSET_MINORS-1); + cset_base = mm & (~(ZIO_NMAX_CSET_MINORS-1)); + + /* Look for this minor base*/ + list_for_each_entry(zcset, &zstat->list_cset, list_cset) { + if (cset_base == zcset->basedev) { + found = 1; + break; + } + } + if (!found) + return NULL; + return &zcset->chan[chan_minor/2]; +} + +static inline int zio_device_get(dev_t devt) +{ + struct zio_channel *chan; + + /* + * FIXME there is a little concurrency; to resolve this, get the owner + * from device list by searching by minor + */ + chan = __zio_minor_to_chan(devt); + if (!chan) { + pr_err("ZIO: can't retrieve channel for minor %i\n", + MINOR(devt)); + return -EBUSY; + } + return try_module_get(chan->cset->zdev->owner); +} +static inline void zio_device_put(dev_t devt) +{ + struct zio_channel *chan; + + /* + * FIXME there is a little concurrency; to resolve this, get the owner + * from device list by searching by minor + */ + chan = __zio_minor_to_chan(devt); + /* it is impossbile chan = NULL because __zio_device_get() found it */ + module_put(chan->cset->zdev->owner); +} + +static int zio_f_open(struct inode *ino, struct file *f) +{ + struct zio_f_priv *priv = NULL; + struct zio_channel *chan; + struct zio_buffer_type *zbuf; + const struct file_operations *old_fops, *new_fops; + int ret = -EINVAL, minor; + + pr_debug("%s:%i\n", __func__, __LINE__); + if (f->f_flags & FMODE_WRITE) + goto out; + + if (!zio_device_get(ino->i_rdev)) + return -ENODEV; + + minor = iminor(ino); + chan = __zio_minor_to_chan(ino->i_rdev); + if (!chan) { + pr_err("ZIO: can't retrieve channel for minor %i\n", minor); + return -EBUSY; + } + zbuf = chan->cset->zbuf; + f->private_data = NULL; + priv = kzalloc(sizeof(struct zio_f_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* if there is no instance, then create a new one */ + if (!chan->bi) + chan->bi = zbuf->b_op->create(zbuf, chan, FMODE_READ); + priv->chan = chan; + + /* even number is control, odd number is data */ + if (minor & 0x1) + priv->type = ZIO_CDEV_DATA; + else + priv->type = ZIO_CDEV_CTRL; + f->private_data = priv; + + /* replace zio fops with buffer fops (FIXME: make it a lib function */ + mutex_lock(&zmutex); + old_fops = f->f_op; + new_fops = fops_get(zbuf->f_op); + ret = 0; + if (new_fops->open) + ret = new_fops->open(ino, f); + if (ret) { + fops_put(zbuf->f_op); + mutex_unlock(&zmutex); + goto out; + } + fops_put(old_fops); + f->f_op = new_fops; + mutex_unlock(&zmutex); + return 0; + +out: + kfree(priv); + return ret; +} + +static const struct file_operations zfops = { + .owner = THIS_MODULE, + .open = zio_f_open, +}; + +int __zio_minorbase_get(struct zio_cset *zcset) +{ + int i; + + i = find_first_zero_bit(zstat->cset_minors_mask, ZIO_CSET_MAXNUM); + if (i >= ZIO_CSET_MAXNUM) + return 1; + set_bit(i, zstat->cset_minors_mask); + /* set the base minor for a cset*/ + zcset->basedev = zstat->basedev + (i * ZIO_NMAX_CSET_MINORS); + pr_debug("%s:%i BASEMINOR 0x%x\n", __func__, __LINE__, zcset->basedev); + return 0; +} +void __zio_minorbase_put(struct zio_cset *zcset) +{ + int i; + + i = (zcset->basedev - zstat->basedev) / ZIO_NMAX_CSET_MINORS; + clear_bit(i, zstat->cset_minors_mask); +} + +/* + * create control and data char devices for a channel. The even minor + * is for control, the odd one for data. + */ +int zio_create_chan_devices(struct zio_channel *chan) +{ + int err; + dev_t devt_c, devt_d; + + + devt_c = chan->cset->basedev + chan->index * 2; + pr_debug("%s:%d dev_t=0x%x\n", __func__, __LINE__, devt_c); + chan->ctrl_dev = device_create(&zio_class, NULL, devt_c, NULL, + "%s-%i-%i-ctrl", + chan->cset->zdev->head.name, + chan->cset->index, + chan->index); + if (IS_ERR(&chan->ctrl_dev)) { + err = PTR_ERR(&chan->ctrl_dev); + goto out; + } + + devt_d = devt_c + 1; + pr_debug("%s:%d dev_t=0x%x\n", __func__, __LINE__, devt_d); + chan->data_dev = device_create(&zio_class, NULL, devt_d, NULL, + "%s-%i-%i-data", + chan->cset->zdev->head.name, + chan->cset->index, + chan->index); + if (IS_ERR(&chan->data_dev)) { + err = PTR_ERR(&chan->data_dev); + goto out_data; + } + + return 0; + +out_data: + device_destroy(&zio_class, chan->ctrl_dev->devt); +out: + return err; +} + +void zio_destroy_chan_devices(struct zio_channel *chan) +{ + pr_debug("%s\n", __func__); + device_destroy(&zio_class, chan->data_dev->devt); + device_destroy(&zio_class, chan->ctrl_dev->devt); +} + +int __zio_register_cdev() +{ + int err; + + err = class_register(&zio_class); + if (err) { + pr_err("%s: unable to register class\n", __func__); + goto out; + } + /* alloc to zio the maximum number of minors usable in ZIO */ + err = alloc_chrdev_region(&zstat->basedev, 0, + ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS, "zio"); + if (err) { + pr_err("%s: unable to allocate region for %i minors\n", + __func__, ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS); + goto out; + } + /* all ZIO's devices, buffers and triggers has zfops as f_op */ + cdev_init(&zstat->chrdev, &zfops); + zstat->chrdev.owner = THIS_MODULE; + err = cdev_add(&zstat->chrdev, zstat->basedev, + ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS); + if (err) + goto out_cdev; + INIT_LIST_HEAD(&zstat->list_cset); + return 0; +out_cdev: + unregister_chrdev_region(zstat->basedev, + ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS); +out: + class_unregister(&zio_class); + return err; +} +void __zio_unregister_cdev() +{ + cdev_del(&zstat->chrdev); + unregister_chrdev_region(zstat->basedev, + ZIO_CSET_MAXNUM * ZIO_NMAX_CSET_MINORS); + class_unregister(&zio_class); +} + + +/* + * Helper functions to check whether read and write would block. The + * return value is a poll(2) mask, so the poll method just calls them. + */ + +/* Read is quite straightforward, as blocks reack us already filled */ +static int __zio_read_allowed(struct zio_f_priv *priv) +{ + struct zio_channel *chan = priv->chan; + struct zio_bi *bi = chan->bi; + const int can_read = POLLIN | POLLRDNORM; + + if (!chan->user_block) + chan->user_block = bi->b_op->retr_block(bi); + if (!chan->user_block) + return 0; + + /* We have a block. So there is data and possibly control too */ + if (likely(priv->type == ZIO_CDEV_DATA)) + return can_read; + + if (!zio_is_cdone(chan->user_block)) + return POLLIN | POLLRDNORM; + + /* There's a block, but we want to re-read control. Get a new block */ + bi->b_op->free_block(bi, chan->user_block); + chan->user_block = bi->b_op->retr_block(bi); + if (!chan->user_block) + return 0; + return POLLIN | POLLRDNORM; +} + +/* Write is more tricky: we need control, so we may ask it to the trigger */ +static struct zio_block *__zio_write_allocblock(struct zio_bi *bi, + struct zio_control *ctrl) +{ + struct zio_block *block; + size_t datalen; + + if (!ctrl) { + ctrl = zio_alloc_control(GFP_KERNEL); + if (!ctrl) + return NULL; + memcpy(ctrl, bi->cset->ti->current_ctrl, ZIO_CONTROL_SIZE); + } + datalen = ctrl->ssize * ctrl->nsamples; + block = bi->b_op->alloc_block(bi, ctrl, datalen, GFP_KERNEL); + return block; +} + +static int __zio_write_allowed(struct zio_f_priv *priv) +{ + struct zio_channel *chan = priv->chan; + struct zio_bi *bi = chan->bi; + struct zio_block *block; + const int can_write = POLLOUT | POLLWRNORM; + + if (priv->type == ZIO_CDEV_CTRL) { + /* Control is always writeable */ + return can_write; + } + + /* We want to write data. If we have no control, retrieve one */ + if (!chan->user_block) + chan->user_block = __zio_write_allocblock(bi, NULL); + block = chan->user_block; + if (!block) + return 0; + + /* If the block is not full, user can write data */ + if (block->uoff < block->datalen) + return can_write; + + /* Block is full: try to push out to the buffer */ + if (bi->b_op->store_block(bi, block) < 0) + return 0; + + /* We sent it: get a new one for this new data */ + chan->user_block = __zio_write_allocblock(bi, NULL); + return chan->user_block ? can_write : 0; +} + +/* + * The following "generic" read and write (and poll and so on) should + * work for most buffer types, and are exported for use in their + * buffer operations. + */ +ssize_t zio_generic_read(struct file *f, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct zio_f_priv *priv = f->private_data; + struct zio_channel *chan = priv->chan; + struct zio_bi *bi = chan->bi; + struct zio_block *block; + + pr_debug("%s:%d type %s\n", __func__, __LINE__, + priv->type == ZIO_CDEV_CTRL ? "ctrl" : "data"); + + if (priv->type == ZIO_CDEV_CTRL && count < ZIO_CONTROL_SIZE) + return -EINVAL; + + if ((bi->flags & ZIO_DIR) == ZIO_DIR_OUTPUT) { + /* FIXME: read_control for output channels is missing */ + return -EINVAL; + } + + if (!__zio_read_allowed(priv)) { + if (f->f_flags & O_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(bi->q, __zio_read_allowed(priv)); + if (signal_pending(current)) + return -ERESTARTSYS; + } + block = chan->user_block; + + /* So, it's readable */ + if (unlikely(priv->type == ZIO_CDEV_CTRL)) { + zio_set_cdone(block); + if (copy_to_user(ubuf, zio_get_ctrl(block), ZIO_CONTROL_SIZE)) + return -EFAULT; + *offp += ZIO_CONTROL_SIZE; + return ZIO_CONTROL_SIZE; + } + + /* Data file, and we have data */ + if (count > block->datalen - block->uoff) + count = block->datalen - block->uoff; + if (copy_to_user(ubuf, block->data + block->uoff, count)) + return -EFAULT; + *offp += count; + block->uoff += count; + if (block->uoff == block->datalen) { + chan->user_block = NULL; + bi->b_op->free_block(bi, block); + } + return count; +} +EXPORT_SYMBOL(zio_generic_read); + +ssize_t zio_generic_write(struct file *f, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct zio_f_priv *priv = f->private_data; + struct zio_channel *chan = priv->chan; + struct zio_bi *bi = chan->bi; + struct zio_block *block; + struct zio_control *ctrl; + + pr_debug("%s:%d type %s\n", __func__, __LINE__, + priv->type == ZIO_CDEV_CTRL ? "ctrl" : "data"); + + if ((bi->flags & ZIO_DIR) == ZIO_DIR_INPUT) { + /* FIXME: write_control for input channels is missing */ + return -EINVAL; + } + + if (!__zio_write_allowed(priv)) { + if (f->f_flags & O_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(bi->q, __zio_write_allowed(priv)); + if (signal_pending(current)) + return -ERESTARTSYS; + } + + if (likely(priv->type == ZIO_CDEV_DATA)) { + /* Data is writeable, so we have space in this block */ + block = chan->user_block; + if (count > block->datalen - block->uoff) + count = block->datalen - block->uoff; + if (copy_from_user(block->data + block->uoff, ubuf, count)) + return -EFAULT; + block->uoff += count; + if (block->uoff == block->datalen) + if (bi->b_op->store_block(bi, block) == 0) + chan->user_block = NULL; + return count; + } + + /* Control: drop the current block and create a new one */ + if (priv->type == ZIO_CDEV_CTRL && count < ZIO_CONTROL_SIZE) + return -EINVAL; + count = ZIO_CONTROL_SIZE; + + if (chan->user_block) + bi->b_op->free_block(bi, chan->user_block); + chan->user_block = NULL; + ctrl = zio_alloc_control(GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + if (copy_from_user(ctrl, ubuf, count)) + return -EFAULT; + memcpy(bi->cset->ti->current_ctrl, ctrl, count); + *offp += count; + return count; +} +EXPORT_SYMBOL(zio_generic_write); + +unsigned int zio_generic_poll(struct file *f, struct poll_table_struct *w) +{ + struct zio_f_priv *priv = f->private_data; + struct zio_bi *bi = priv->chan->bi; + + poll_wait(f, &bi->q, w); + return __zio_read_allowed(priv) | __zio_write_allowed(priv); +} +EXPORT_SYMBOL(zio_generic_poll); + +int zio_generic_release(struct inode *inode, struct file *f) +{ + struct zio_f_priv *priv = f->private_data; + + /* priv is allocated by zio_f_open, must be freed */ + kfree(priv); + zio_device_put(inode->i_rdev); + return 0; +} +EXPORT_SYMBOL(zio_generic_release); + diff --git a/drivers/zio/zio-sys.c b/drivers/zio/zio-sys.c new file mode 100644 index 0000000..eef2491 --- /dev/null +++ b/drivers/zio/zio-sys.c @@ -0,0 +1,1423 @@ +/* Federico Vaga for CERN, 2011, GNU GPLv2 or later */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/spinlock.h> + +#define __ZIO_INTERNAL__ +#include <linux/zio.h> +#include <linux/zio-sysfs.h> +#include <linux/zio-buffer.h> +#include <linux/zio-trigger.h> + +static struct zio_status *zstat = &zio_global_status; /* Always use ptr */ + +const char zio_zdev_attr_names[ZATTR_STD_NUM_ZDEV][ZIO_NAME_LEN] = { + [ZATTR_GAIN] = "gain_factor", + [ZATTR_OFFSET] = "offset", + [ZATTR_NBIT] = "resolution-bits", + [ZATTR_MAXRATE] = "max-sample-rate", + [ZATTR_VREFTYPE] = "vref-src", +}; +EXPORT_SYMBOL(zio_zdev_attr_names); +const char zio_trig_attr_names[ZATTR_STD_NUM_TRIG][ZIO_NAME_LEN] = { + [ZATTR_TRIG_REENABLE] = "re-enable", + [ZATTR_TRIG_NSAMPLES] = "nsamples", +}; +EXPORT_SYMBOL(zio_trig_attr_names); +const char zio_zbuf_attr_names[ZATTR_STD_NUM_ZBUF][ZIO_NAME_LEN] = { + [ZATTR_ZBUF_MAXLEN] = "max-buffer-len", +}; +EXPORT_SYMBOL(zio_zbuf_attr_names); + +static const char *__get_sysfs_name(enum zio_object_type type, int i) +{ + const char *name; + + switch (type) { + case ZDEV: + name = zio_zdev_attr_names[i]; + break; + case ZTRIG: + case ZTI: + name = zio_trig_attr_names[i]; + break; + case ZBUF: + case ZBI: + name = zio_zbuf_attr_names[i]; + break; + default: + name = NULL; + break; + } + + return name; +} + +/* + * Top-level ZIO objects has a unique name. + * You can find a particular object by searching its name. + */ +static inline struct zio_object_list_item *__find_by_name( + struct zio_object_list *zobj_list, char *name) +{ + struct zio_object_list_item *cur; + + if (!name) + return NULL; + list_for_each_entry(cur, &zobj_list->list, list) { + pr_debug("%s:%d %s=%s\n", __func__, __LINE__, cur->name, name); + if (strcmp(cur->name, name) == 0) + return cur; /* object found */ + } + return NULL; +} + +static inline struct zio_object_list_item *__zio_object_get( + struct zio_object_list *zobj_list, char *name) +{ + struct zio_object_list_item *list_item; + + /* search for default trigger */ + list_item = __find_by_name(zobj_list, name); + if (!list_item) + return NULL; + /* increment trigger usage to prevent rmmod */ + if (!try_module_get(list_item->owner)) + return NULL; + return list_item; +} +static struct zio_buffer_type *zio_buffer_get(char *name) +{ + struct zio_object_list_item *list_item; + + list_item = __zio_object_get(&zstat->all_buffer_types, name); + if (!list_item) + return ERR_PTR(-ENODEV); + return container_of(list_item->obj_head, struct zio_buffer_type, head); +} +static inline void zio_buffer_put(struct zio_buffer_type *zbuf) +{ + pr_debug("%s:%d %p\n", __func__, __LINE__, zbuf->owner); + module_put(zbuf->owner); +} +static struct zio_trigger_type *zio_trigger_get(char *name) +{ + struct zio_object_list_item *list_item; + + list_item = __zio_object_get(&zstat->all_trigger_types, name); + if (!list_item) + return ERR_PTR(-ENODEV); + return container_of(list_item->obj_head, struct zio_trigger_type, head); +} +static inline void zio_trigger_put(struct zio_trigger_type *trig) +{ + pr_debug("%s:%d %p\n", __func__, __LINE__, trig->owner); + module_put(trig->owner); +} + +/* data_done is called by the driver, after {in,out}put_cset */ +void zio_generic_data_done(struct zio_cset *cset) +{ + struct zio_buffer_type *zbuf; + struct zio_device *zdev; + struct zio_channel *chan; + struct zio_block *block; + struct zio_ti *ti; + struct zio_bi *bi; + + pr_debug("%s:%d\n", __func__, __LINE__); + + ti = cset->ti; + zdev = cset->zdev; + zbuf = cset->zbuf; + + if (unlikely((ti->flags & ZIO_DIR) == ZIO_DIR_OUTPUT)) { + cset_for_each(cset, chan) { + bi = chan->bi; + block = chan->active_block; + if (block) + zbuf->b_op->free_block(chan->bi, block); + /* We may have a new block ready, or not */ + chan->active_block = zbuf->b_op->retr_block(chan->bi); + } + return; + } + /* DIR_INPUT */ + cset_for_each(cset, chan) { + bi = chan->bi; + block = chan->active_block; + if (!block) + continue; + if (zbuf->b_op->store_block(bi, block)) /* may fail, no prob */ + zbuf->b_op->free_block(bi, block); + } +} +EXPORT_SYMBOL(zio_generic_data_done); + +static void __zio_fire_input_trigger(struct zio_ti *ti) +{ + struct zio_buffer_type *zbuf; + struct zio_block *block; + struct zio_device *zdev; + struct zio_cset *cset; + struct zio_channel *chan; + struct zio_control *ctrl; + int errdone = 0; + + cset = ti->cset; + zdev = cset->zdev; + zbuf = cset->zbuf; + + pr_debug("%s:%d\n", __func__, __LINE__); + + /* FIXME: check if a trigger is already pending */ + + /* Allocate the buffer for the incoming sample, in active channels */ + cset_for_each(cset, chan) { + ctrl = zio_alloc_control(GFP_ATOMIC); + if (!ctrl) { + if (!errdone++) + pr_err("%s: can't alloc control\n", __func__); + continue; + } + memcpy(ctrl, ti->current_ctrl, ZIO_CONTROL_SIZE); + ctrl->chan_i = chan->index; + + block = zbuf->b_op->alloc_block(chan->bi, ctrl, + ctrl->ssize * ctrl->nsamples, + GFP_ATOMIC); + if (IS_ERR(block)) { + if (!errdone++) + pr_err("%s: can't alloc block\n", __func__); + zio_free_control(ctrl); + continue; + } + chan->active_block = block; + } + if (zdev->d_op->input_cset(cset)) { + /* It succeeded immediately */ + ti->t_op->data_done(cset); + } +} + +static void __zio_fire_output_trigger(struct zio_ti *ti) +{ + struct zio_cset *cset = ti->cset; + struct zio_device *zdev = cset->zdev; + + pr_debug("%s:%d\n", __func__, __LINE__); + + /* We are expected to already have a block in active channels */ + if (zdev->d_op->output_cset(cset)) { + /* It succeeded immediately */ + ti->t_op->data_done(cset); + } +} + +/* + * When a software trigger fires, it should call this function. Hw ones don't + */ +void zio_fire_trigger(struct zio_ti *ti) +{ + /* If the trigger runs too early, ti->cset is still NULL */ + if (!ti->cset) + return; + + /* Copy the stamp (we are software driven anyways) */ + ti->current_ctrl->tstamp.secs = ti->tstamp.tv_sec; + ti->current_ctrl->tstamp.ticks = ti->tstamp.tv_nsec; + ti->current_ctrl->tstamp.bins = ti->tstamp_extra; + /* + * And the sequence number too (first returned seq is 1). + * Sequence number is always increased to identify un-stored + * blocks or other errors in trigger activation. + */ + ti->current_ctrl->seq_num++; + + if (likely((ti->flags & ZIO_DIR) == ZIO_DIR_INPUT)) + __zio_fire_input_trigger(ti); + else + __zio_fire_output_trigger(ti); +} +EXPORT_SYMBOL(zio_fire_trigger); + +static int __has_auto_index(char *s) +{ + int i = 0; + for (i = 0; i < ZIO_NAME_LEN-1; i++) { + if (s[i] != '%') + continue; + i++; + if (s[i] == 'd') + return 1; + } + return 0; +} +static int __next_strlen(char *str) +{ + int increment = 0, i; + + for (i = strlen(str)-1; i > 0; i--) { + /* if is an ascii number */ + if (str[i] >= '0' && str[i] <= '9') { + if (str[i] == '9') + continue; + else + break; + } else { + increment++; + break; + } + } + return strlen(str) + increment; +} + +/* + * The zio device name must be unique. If it is not unique, a busy error is + * returned. + */ +static int zobj_unique_name(struct zio_object_list *zobj_list, char *name) +{ + struct zio_object_list_item *cur; + struct zio_obj_head *tmp; + unsigned int counter = 0, again, len; + char name_to_check[ZIO_NAME_LEN]; + int auto_index = __has_auto_index(name); + + pr_debug("%s\n", __func__); + + if (!name) + return -EINVAL; + + len = strlen(name); + if (!len) + return -EINVAL; + + strncpy(name_to_check, name, ZIO_NAME_LEN); + do { + again = 0; + if (auto_index) { /* TODO when zio become bus, it is useless */ + sprintf(name_to_check, name, counter++); + len = strlen(name_to_check); + } + + list_for_each_entry(cur, &zobj_list->list, list) { + tmp = cur->obj_head; + if (strcmp(tmp->name, name_to_check)) + continue; /* no conflict */ + /* conflict found */ + + /* if not auto-assigned, then error */ + if (!auto_index) { + pr_err("ZIO: name \"%s\" is already taken\n", + name); + return -EBUSY; + } + /* build sequential name */ + if (__next_strlen(name_to_check) > ZIO_NAME_LEN) { + pr_err("ZIO: invalid name \"%s\"\n", name); + return -EINVAL; + } + again = 1; + break; + } + } while (again); + strncpy(name, name_to_check, ZIO_NAME_LEN); + return 0; +} + +static struct zio_attribute *__zattr_clone(const struct zio_attribute *src, + unsigned int n) +{ + struct zio_attribute *dest = NULL; + unsigned int size; + + if (!src) + return NULL; + size = n * sizeof(struct zio_attribute); + dest = kmalloc(size, GFP_KERNEL); + if (!dest) + return NULL; + + dest = memcpy(dest, src, size); + + return dest; +} + +static void __zattr_unclone(struct zio_attribute *zattr) +{ + kfree(zattr); +} + +static int __zattr_set_copy(struct zio_attribute_set *dest, + struct zio_attribute_set *src) +{ + if (!dest || !src) + return -EINVAL; + dest->n_std_attr = src->n_std_attr; + dest->n_ext_attr = src->n_ext_attr; + dest->std_zattr = __zattr_clone(src->std_zattr, dest->n_std_attr); + dest->ext_zattr = __zattr_clone(src->ext_zattr, dest->n_ext_attr); + + return 0; +} +static void __zattr_set_free(struct zio_attribute_set *zattr_set) +{ + if (!zattr_set) + return; + __zattr_unclone(zattr_set->ext_zattr); + __zattr_unclone(zattr_set->std_zattr); +} +static int zattr_chan_pre_set(struct zio_channel *chan) +{ + struct zio_cset *cset = chan->cset; + + if (!(cset->flags & ZCSET_CHAN_ALLOC)) + return 0; /* nothing to do */ + + /* + * If the channel has been allocated by ZIO, then attributes are + * cloned from the template channel description within parent cset + */ + chan->zattr_set.std_zattr = + __zattr_clone( + cset->zattr_set_chan.std_zattr, + ZATTR_STD_NUM_ZDEV); + if (IS_ERR(chan->zattr_set.std_zattr)) + return PTR_ERR(chan->zattr_set.std_zattr); + chan->zattr_set.ext_zattr = + __zattr_clone( + cset->zattr_set.ext_zattr, + cset->zattr_set.n_ext_attr); + if (IS_ERR(chan->zattr_set.ext_zattr)) { + kfree(chan->zattr_set.std_zattr); + return PTR_ERR(chan->zattr_set.ext_zattr); + } + return 0; +} + +static void zattr_chan_post_remove(struct zio_channel *chan) +{ + if (chan->cset->flags & ZCSET_CHAN_ALLOC) { + __zattr_unclone(chan->zattr_set.std_zattr); + __zattr_unclone(chan->zattr_set.ext_zattr); + } +} + +/* When touching attributes, we always use the spinlock for the hosting dev */ +static spinlock_t *zdev_get_spinlock(struct zio_obj_head *head) +{ + spinlock_t *lock; + + switch (head->zobj_type) { + case ZDEV: + lock = &to_zio_dev(&head->kobj)->lock; + break; + case ZCSET: + lock = &to_zio_cset(&head->kobj)->zdev->lock; + break; + case ZCHAN: + lock = &to_zio_chan(&head->kobj)->cset->zdev->lock; + break; + case ZTI: /* we might not want to take a lock but... */ + lock = &to_zio_ti(&head->kobj)->cset->zdev->lock; + break; + case ZBI: + lock = &to_zio_bi(&head->kobj)->cset->zdev->lock; + break; + default: + WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type); + return NULL; + } + return lock; +} + +/* Retrieve an attribute set from an object head */ +static struct zio_attribute_set *__get_zattr_set(struct zio_obj_head *head) +{ + struct zio_attribute_set *zattr_set; + + switch (head->zobj_type) { + case ZDEV: + zattr_set = &to_zio_dev(&head->kobj)->zattr_set; + break; + case ZCSET: + zattr_set = &to_zio_cset(&head->kobj)->zattr_set; + break; + case ZCHAN: + zattr_set = &to_zio_chan(&head->kobj)->zattr_set; + break; + case ZTRIG: + zattr_set = &to_zio_chan(&head->kobj)->zattr_set; + break; + case ZBUF: + zattr_set = &to_zio_chan(&head->kobj)->zattr_set; + break; + case ZTI: + zattr_set = &to_zio_ti(&head->kobj)->zattr_set; + break; + case ZBI: + zattr_set = &to_zio_bi(&head->kobj)->zattr_set; + break; + default: + WARN(1, "ZIO: unknown zio object %i\n", head->zobj_type); + return NULL; + } + return zattr_set; +} + + /* + * Zio objects all handle uint32_t values. So the show and store + * are centralized here, and each device has its own get_info and set_conf + * which handle binary 32-bit numbers. Both the function are locked to prevent + * concurrency issue when editing device register. + */ +static ssize_t zattr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + int err = 0; + ssize_t len = 0; + spinlock_t *lock; + struct zio_attribute *zattr = to_zio_zattr(attr); + + pr_debug("%s\n", __func__); + if (unlikely(strcmp(attr->name, "name") == 0)) { + /* print device name*/ + return sprintf(buf, "%s\n", to_zio_head(kobj)->name); + } + + if (zattr->s_op->info_get) { + lock = zdev_get_spinlock(to_zio_head(kobj)); + spin_lock(lock); + err = zattr->s_op->info_get(kobj, zattr, &zattr->value); + spin_unlock(lock); + if (err) + return err; + } + len = sprintf(buf, "%i\n", zattr->value); + return len; +} +static ssize_t zattr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t size) +{ + long val; + int err = 0; + struct zio_attribute *zattr = to_zio_zattr(attr); + spinlock_t *lock; + + pr_debug("%s\n", __func__); + err = strict_strtol(buf, 0, &val); + if (err) + return -EINVAL; + if (zattr->s_op->conf_set) { + lock = zdev_get_spinlock(to_zio_head(kobj)); + spin_lock(lock); + err = zattr->s_op->conf_set(kobj, zattr, val); + spin_unlock(lock); + } + return err == 0 ? size : err; +} + +static const struct sysfs_ops zio_attribute_ktype_ops = { + .show = zattr_show, + .store = zattr_store, +}; + +static struct attribute default_attrs[] = { + { + .name = "name", /* show the name */ + .mode = 0444, /* read only */ + }, +}; +static struct attribute *def_attr_ptr[] = { + &default_attrs[0], + NULL, +}; + +static struct kobj_type zdktype = { /* For standard and extended attribute */ + .release = NULL, + .sysfs_ops = &zio_attribute_ktype_ops, + .default_attrs = def_attr_ptr, +}; + +static mode_t zattr_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + unsigned int flag1, flag2, flag3; + mode_t mode = attr->mode; + + /* + * FIXME: if it's decided that activation + * is always the first bit then is faster doing: + * flag1 & flag2 & flag3 & 0x1 + * to verify content + */ + switch (__zio_get_object_type(kobj)) { + case ZDEV: + flag1 = to_zio_dev(kobj)->flags; + if (flag1 & ZIO_DISABLED) + mode = 0; + break; + case ZCSET: + flag1 = to_zio_cset(kobj)->flags; + flag2 = to_zio_cset(kobj)->zdev->flags; + if ((flag1 | flag2) & ZIO_DISABLED) + mode = 0; + break; + case ZCHAN: + flag1 = to_zio_chan(kobj)->flags; + flag2 = to_zio_chan(kobj)->cset->flags; + flag3 = to_zio_chan(kobj)->cset->zdev->flags; + if ((flag1 | flag2 | flag3) & ZIO_DISABLED) + mode = 0; + break; + case ZBI: + break; + case ZTI: + break; + default: + WARN(1, "ZIO: unknown zio object %i\n", + __zio_get_object_type(kobj)); + } + + return mode; +} + +/* + * Verify attributes within the group, + * If they are valid register the group + */ +static int zattr_create_group(struct kobject *kobj, + struct zio_attribute *zattr, + struct attribute_group *grp, + unsigned int n_attr, + const struct zio_sys_operations *s_op, + int is_ext) +{ + int i; + + pr_debug("%s\n", __func__); + if (!zattr || !n_attr) { + grp->attrs = NULL; + return 0; /* no attributes */ + } + /* extract the attributes */ + grp->attrs = kzalloc(sizeof(struct attribute) * n_attr, GFP_KERNEL); + if (!grp->attrs) + return -ENOMEM; + + grp->is_visible = zattr_is_visible; + for (i = 0; i < n_attr; i++) { + /* Add attribute and assign show and store functions */ + grp->attrs[i] = &zattr[i].attr; + to_zio_zattr(grp->attrs[i])->s_op = s_op; + /* if not defined */ + if (!grp->attrs[i]->name) { + if (is_ext) { + pr_warning("%s: can't create ext attributes. " + "%ith attribute has not a name", __func__, i); + return -EINVAL; + } + /* + * Only standard attributes need these lines to fill + * the empty hole in the array of attributes + */ + grp->attrs[i]->name = __get_sysfs_name( + to_zio_head(kobj)->zobj_type, i); + grp->attrs[i]->mode = 0; + } + /* if write permission but no write function */ + if ((grp->attrs[i]->mode & S_IWUGO) == S_IWUGO && + !s_op->conf_set) { + pr_err("%s: %s has write permission but no write " + "function\n", __func__, grp->attrs[i]->name); + return -EINVAL; + } + } + return sysfs_create_group(kobj, grp); +} + + +/* Create a set of zio attributes: the standard one and the extended one */ +static void zattr_remove_group(struct kobject *kobj, + struct attribute_group *grp) +{ + if (!grp->attrs) + return; + sysfs_remove_group(kobj, grp); + kfree(grp->attrs); +} +/* create a set of zio attributes: the standard one and the extended one */ +static int zattr_set_create(struct zio_obj_head *head, + const struct zio_sys_operations *s_op) +{ + int err = 0; + struct zio_attribute_set *zattr_set; + + zattr_set = __get_zattr_set(head); + if (!zattr_set) + return -EINVAL; /* message already printed */ + + /* Create the standard attributes from zio attributes */ + err = zattr_create_group(&head->kobj, zattr_set->std_zattr, + &zattr_set->std_group, zattr_set->n_std_attr, s_op, 0); + if (err) + goto out; + /* Create the extended attributes from zio attributes */ + err = zattr_create_group(&head->kobj, zattr_set->ext_zattr, + &zattr_set->ext_group, zattr_set->n_ext_attr, s_op, 1); + if (err && zattr_set->std_group.attrs) + sysfs_remove_group(&head->kobj, &zattr_set->std_group); +out: + return err; +} +/* Remove an existent set of attributes */ +static void zattr_set_remove(struct zio_obj_head *head) +{ + struct zio_attribute_set *zattr_set; + + zattr_set = __get_zattr_set(head); + if (!zattr_set) + return; + + /* remove the standard attribute group */ + zattr_remove_group(&head->kobj, &zattr_set->std_group); + /* remove the extended attribute group */ + zattr_remove_group(&head->kobj, &zattr_set->ext_group); +} + +/* Create a buffer instance according to the buffer type defined in cset */ +static int __buffer_create_instance(struct zio_channel *chan) +{ + struct zio_buffer_type *zbuf = chan->cset->zbuf; + struct zio_bi *bi; + int err; + + /* create buffer */ + bi = zbuf->b_op->create(zbuf, chan, FMODE_READ); + if (IS_ERR(bi)) + return PTR_ERR(bi); + /* Now fill the trigger instance, ops, head, then the rest */ + bi->b_op = zbuf->b_op; + bi->f_op = zbuf->f_op; + bi->flags |= (chan->flags & ZIO_DIR); + bi->head.zobj_type = ZBI; + err = kobject_init_and_add(&bi->head.kobj, &zdktype, + &chan->head.kobj, "buffer"); + if (err) + goto out_kobj; + snprintf(bi->head.name, ZIO_NAME_LEN, "%s-%s-%d-%d", + zbuf->head.name, + chan->cset->zdev->head.name, + chan->cset->index, + chan->index); + + err = __zattr_set_copy(&bi->zattr_set, &zbuf->zattr_set); + if (err) + goto out_clone; + err = zattr_set_create(&bi->head, zbuf->s_op); + if (err) + goto out_sysfs; + init_waitqueue_head(&bi->q); + + /* Add to buffer instance list */ + spin_lock(&zbuf->lock); + list_add(&bi->list, &zbuf->list); + spin_unlock(&zbuf->lock); + bi->cset = chan->cset; + chan->bi = bi; + + /* Done. This cset->ti marks everything is running (FIXME?) */ + mb(); + bi->chan = chan; + + return 0; + +out_sysfs: + __zattr_set_free(&bi->zattr_set); +out_clone: + kobject_del(&bi->head.kobj); +out_kobj: + kobject_put(&bi->head.kobj); + zbuf->b_op->destroy(bi); + return err; +} + +/* Destroy a buffer instance */ +static void __buffer_destroy_instance(struct zio_channel *chan) +{ + struct zio_buffer_type *zbuf = chan->cset->zbuf; + struct zio_bi *bi = chan->bi; + + chan->bi = NULL; + + /* Remove from buffer instance list */ + spin_lock(&zbuf->lock); + list_del(&bi->list); + spin_unlock(&zbuf->lock); + /* Remove from sysfs */ + zattr_set_remove(&bi->head); + __zattr_set_free(&bi->zattr_set); + kobject_del(&bi->head.kobj); + kobject_put(&bi->head.kobj); + /* Finally destroy the instance */ + zbuf->b_op->destroy(bi); +} + +/* Create a trigger instance according to the trigger type defined in cset */ +static int __trigger_create_instance(struct zio_cset *cset) +{ + int err; + struct zio_control *ctrl; + struct zio_ti *ti; + + pr_debug("%s:%d\n", __func__, __LINE__); + /* Allocate and fill current control as much as possible*/ + ctrl = zio_alloc_control(GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + ctrl->cset_i = cset->index; + strncpy(ctrl->devname, cset->zdev->head.name, ZIO_NAME_LEN); + strncpy(ctrl->triggername, cset->trig->head.name, ZIO_NAME_LEN); + ctrl->sbits = 8; /* FIXME: retrieve from attribute */ + ctrl->ssize = cset->ssize; + + ti = cset->trig->t_op->create(cset->trig, cset, ctrl, 0/*FIXME*/); + if (IS_ERR(ti)) { + err = PTR_ERR(ti); + pr_err("%s: can't create trigger error %i\n", __func__, err); + goto out; + } + /* Now fill the trigger instance, ops, head, then the rest */ + ti->t_op = cset->trig->t_op; + ti->f_op = cset->trig->f_op; + ti->flags |= cset->flags & ZIO_DIR; + ti->head.zobj_type = ZTI; + err = kobject_init_and_add(&ti->head.kobj, &zdktype, + &cset->head.kobj, "trigger"); + if (err) + goto out_kobj; + snprintf(ti->head.name, ZIO_NAME_LEN, "%s-%s-%d", + cset->trig->head.name, + cset->zdev->head.name, + cset->index); + + err = __zattr_set_copy(&ti->zattr_set, &cset->trig->zattr_set); + if (err) + goto out_clone; + err = zattr_set_create(&ti->head, cset->trig->s_op); + if (err) + goto out_sysfs; + + /* Add to trigger instance list */ + spin_lock(&cset->trig->lock); + list_add(&ti->list, &cset->trig->list); + spin_unlock(&cset->trig->lock); + cset->ti = ti; + + /* Done. This cset->ti marks everything is running (FIXME?) */ + mb(); + ti->cset = cset; + + return 0; + +out_sysfs: + __zattr_set_free(&ti->zattr_set); +out_clone: + kobject_del(&ti->head.kobj); +out_kobj: + kobject_put(&ti->head.kobj); + ti->t_op->destroy(ti); +out: + zio_free_control(ctrl); + return err; +} + +/* Destroy a buffer instance */ +static void __trigger_destroy_instance(struct zio_cset *cset) +{ + struct zio_ti *ti = cset->ti; + struct zio_control *ctrl = ti->current_ctrl; + + cset->ti = NULL; + + /* Remove from trigger instance list */ + spin_lock(&cset->trig->lock); + list_del(&ti->list); + spin_unlock(&cset->trig->lock); + /* Remove from sysfs */ + zattr_set_remove(&ti->head); + __zattr_set_free(&ti->zattr_set); + kobject_del(&ti->head.kobj); + kobject_put(&ti->head.kobj); + /* Finally destroy the instance and free the default control*/ + cset->trig->t_op->destroy(ti); + zio_free_control(ctrl); +} + +/* + * chan_register registers one channel. It is important to register + * or unregister all the channels of a cset at the same time to prevent + * overlaps in the minors. + */ +static int chan_register(struct zio_channel *chan) +{ + int err; + + pr_debug("%s:%d\n", __func__, __LINE__); + if (!chan) + return -EINVAL; + + chan->head.zobj_type = ZCHAN; + err = kobject_init_and_add(&chan->head.kobj, &zdktype, + &chan->cset->head.kobj, "chan%i", chan->index); + if (err) + goto out_add; + + err = zattr_chan_pre_set(chan); + if (err) + goto out_pre; + + /* Create sysfs channel attributes */ + err = zattr_set_create(&chan->head, chan->cset->zdev->s_op); + if (err) + goto out_sysfs; + + /* Create buffer */ + err = __buffer_create_instance(chan); + if (err) + goto out_buf; + + /* Create channel char devices*/ + err = zio_create_chan_devices(chan); + if (err) + goto out_create; + /* + * If no name was assigned, ZIO assigns it. channel name is + * set to the kobject name. kobject name has no length limit, + * so the channel name is the first ZIO_NAME_LEN characters of + * kobject name. A duplicate channel name is not a problem + * anyways. + */ + if (!strlen(chan->head.name)) + strncpy(chan->head.name, chan->head.kobj.name, ZIO_NAME_LEN); + return 0; + +out_create: + __buffer_destroy_instance(chan); +out_buf: + zattr_set_remove(&chan->head); +out_sysfs: + zattr_chan_post_remove(chan); +out_pre: + kobject_del(&chan->head.kobj); +out_add: + /* we must _put even if it returned error */ + kobject_put(&chan->head.kobj); + return err; +} + +static void chan_unregister(struct zio_channel *chan) +{ + pr_debug("%s:%d\n", __func__, __LINE__); + if (!chan) + return; + zio_destroy_chan_devices(chan); + /* destroy buffer instance */ + __buffer_destroy_instance(chan); + /* remove sysfs cset attributes */ + zattr_set_remove(&chan->head); + zattr_chan_post_remove(chan); + kobject_del(&chan->head.kobj); + kobject_put(&chan->head.kobj); +} + +/* + * @cset_alloc_chan: low-level drivers can avoid allocating their channels, + * they say how many are there and ZIO allocates them. + * @cset_free_chan: if ZIO allocated channels, then it frees them; otherwise + * it does nothing. + */ +static struct zio_channel *cset_alloc_chan(struct zio_cset *cset) +{ + pr_debug("%s:%d\n", __func__, __LINE__); + /*if no static channels, then ZIO must alloc them */ + if (cset->chan) + return cset->chan; + /* initialize memory to zero to have correct flags and attrs */ + cset->chan = kzalloc(sizeof(struct zio_channel) * + cset->n_chan, GFP_KERNEL); + if (!cset->chan) + return ERR_PTR(-ENOMEM); + cset->flags |= ZCSET_CHAN_ALLOC; + + return cset->chan; +} +static inline void cset_free_chan(struct zio_cset *cset) +{ + pr_debug("%s:%d\n", __func__, __LINE__); + /* Only allocated channels need to be freed */ + if (cset->flags & ZCSET_CHAN_ALLOC) + kfree(cset->chan); +} + +static int cset_register(struct zio_cset *cset) +{ + int i, j, err = 0; + struct zio_buffer_type *zbuf; + struct zio_trigger_type *trig; + char *name; + + pr_debug("%s:%d\n", __func__, __LINE__); + if (!cset) + return -EINVAL; + + if (!cset->n_chan) { + pr_err("ZIO: no channels in cset%i\n", cset->index); + return -EINVAL; + } + + if (!cset->ssize) { + pr_err("ZIO: ssize can not be 0 in cset%i\n", cset->index); + return -EINVAL; + } + + /* Get an available minor base */ + err = __zio_minorbase_get(cset); + if (err) { + pr_err("ZIO: no minors available\n"); + return -EBUSY; + } + + cset->head.zobj_type = ZCSET; + err = kobject_init_and_add(&cset->head.kobj, &zdktype, + &cset->zdev->head.kobj, "cset%i", cset->index); + if (err) + goto out_add; + /* Create sysfs cset attributes */ + err = zattr_set_create(&cset->head, cset->zdev->s_op); + if (err) + goto out_sysfs; + + cset->chan = cset_alloc_chan(cset); + if (IS_ERR(cset->chan)) { + err = PTR_ERR(cset->chan); + goto out_alloc; + } + + /* + * The cset must have a buffer type. If none is associated + * to the cset, ZIO selects the preferred or default one. + */ + if (!cset->zbuf) { + name = cset->zdev->preferred_buffer; + zbuf = zio_buffer_get(name); + if (name && IS_ERR(zbuf)) + pr_warning("%s: no buffer \"%s\" (error %li), using " + "default\n", __func__, name, PTR_ERR(zbuf)); + if (IS_ERR(zbuf)) + zbuf = zio_buffer_get(ZIO_DEFAULT_BUFFER); + if (IS_ERR(zbuf)) { + err = PTR_ERR(zbuf); + goto out_buf; + } + cset->zbuf = zbuf; + } + + /* Register all child channels */ + for (i = 0; i < cset->n_chan; i++) { + cset->chan[i].index = i; + cset->chan[i].cset = cset; + cset->chan[i].flags |= cset->flags & ZIO_DIR; + err = chan_register(&cset->chan[i]); + if (err) + goto out_reg; + } + + /* + * If no name was assigned, ZIO assigns it. cset name is + * set to the kobject name. kobject name has no length limit, + * so the cset name is the first ZIO_NAME_LEN characters of + * kobject name. A duplicate cset name is not a problem + * anyways. + */ + if (!strlen(cset->head.name)) + strncpy(cset->head.name, cset->head.kobj.name, ZIO_NAME_LEN); + + /* + * The cset must have a trigger type. If none is associated + * to the cset, ZIO selects the default or preferred one. + * This is done late because each channel must be ready when + * the trigger fires. + */ + if (!cset->trig) { + name = cset->zdev->preferred_trigger; + trig = zio_trigger_get(name); + if (name && IS_ERR(trig)) + pr_warning("%s: no trigger \"%s\" (error %li), using " + "default\n", __func__, name, PTR_ERR(trig)); + if (IS_ERR(trig)) + trig = zio_trigger_get(ZIO_DEFAULT_TRIGGER); + if (IS_ERR(trig)) { + err = PTR_ERR(trig); + goto out_trig; + } + cset->trig = trig; + err = __trigger_create_instance(cset); + if (err) + goto out_trig; + } + + list_add(&cset->list_cset, &zstat->list_cset); + + /* Private initialization function */ + if (cset->init) { + err = cset->init(cset); + if (err) + goto out_init; + } + return 0; + +out_init: + __trigger_destroy_instance(cset); +out_trig: + zio_trigger_put(cset->trig); + cset->trig = NULL; +out_reg: + for (j = i-1; j >= 0; j--) + chan_unregister(&cset->chan[j]); + zio_buffer_put(cset->zbuf); +out_buf: + cset_free_chan(cset); +out_alloc: + zattr_set_remove(&cset->head); +out_sysfs: + kobject_del(&cset->head.kobj); +out_add: + /* we must _put even if it returned error */ + kobject_put(&cset->head.kobj); + return err; +} + +static void cset_unregister(struct zio_cset *cset) +{ + int i; + + pr_debug("%s:%d\n", __func__, __LINE__); + if (!cset) + return; + /* Private exit function */ + if (cset->exit) + cset->exit(cset); + + /* Remove from csets list*/ + list_del(&cset->list_cset); + /* destroy instance and decrement trigger usage */ + __trigger_destroy_instance(cset); + zio_trigger_put(cset->trig); + cset->trig = NULL; + /* Unregister all child channels */ + for (i = 0; i < cset->n_chan; i++) + chan_unregister(&cset->chan[i]); + /* decrement buffer usage */ + zio_buffer_put(cset->zbuf); + cset->zbuf = NULL; + cset_free_chan(cset); + /* Remove from sysfs */ + zattr_set_remove(&cset->head); + kobject_del(&cset->head.kobj); + kobject_put(&cset->head.kobj); + /* Release a group of minors */ + __zio_minorbase_put(cset); +} + +/* + * Register a generic zio object. It can be a device, a buffer type or + * a trigger type. + */ +static int zobj_register(struct zio_object_list *zlist, + struct zio_obj_head *head, + enum zio_object_type type, + struct module *owner, + const char *name) +{ + int err; + struct zio_object_list_item *item; + + head->zobj_type = type; + if (strlen(name) > ZIO_NAME_OBJ) + pr_warning("ZIO: name too long, cut to %d characters\n", + ZIO_NAME_OBJ); + strncpy(head->name, name, ZIO_NAME_OBJ); + + /* Name must be unique */ + err = zobj_unique_name(zlist, head->name); + if (err) + goto out; + err = kobject_init_and_add(&head->kobj, &zdktype, zlist->kobj, + head->name); + if (err) + goto out_kobj; + + /* Add to object list */ + item = kmalloc(sizeof(struct zio_object_list_item), GFP_KERNEL); + if (!item) { + err = -ENOMEM; + goto out_km; + } + item->obj_head = head; + item->owner = owner; + strncpy(item->name, head->name, ZIO_NAME_OBJ); + /* add to the object list*/ + spin_lock(&zstat->lock); + list_add(&item->list, &zlist->list); + spin_unlock(&zstat->lock); + return 0; + +out_km: + kobject_del(&head->kobj); +out_kobj: + kobject_put(&head->kobj); /* we must _put even if it returned error */ +out: + return err; +} +static void zobj_unregister(struct zio_object_list *zlist, + struct zio_obj_head *zobj) +{ + struct zio_object_list_item *item; + + pr_debug("%s:%d\n", __func__, __LINE__); + if (!zobj) + return; + list_for_each_entry(item, &zlist->list, list) { + if (item->obj_head == zobj) { + /* Remove from object list */ + spin_lock(&zstat->lock); + list_del(&item->list); + spin_unlock(&zstat->lock); + kfree(item); + break; + } + } + + kobject_del(&zobj->kobj); + kobject_put(&zobj->kobj); +} + +/* Register a zio device */ +int zio_register_dev(struct zio_device *zdev, const char *name) +{ + int err = 0, i, j; + + if (!zdev->d_op) { + pr_err("%s: new devices has no operations\n", __func__); + return -EINVAL; + } + if (!zdev->owner) { + pr_err("%s: new device has no owner\n", __func__); + return -EINVAL; + } + /* Register the device */ + err = zobj_register(&zstat->all_devices, &zdev->head, + ZDEV, zdev->owner, name); + if (err) + goto out; + zdev->zattr_set.n_std_attr = ZATTR_STD_NUM_ZDEV; + spin_lock_init(&zdev->lock); + /* Create standard and extended sysfs attribute for device */ + err = zattr_set_create(&zdev->head, zdev->s_op); + if (err) + goto out_sysfs; + + /* Register all child channel sets */ + for (i = 0; i < zdev->n_cset; i++) { + zdev->cset[i].index = i; + zdev->cset[i].zdev = zdev; + err = cset_register(&zdev->cset[i]); + if (err) + goto out_cset; + } + + return 0; + +out_cset: + for (j = i-1; j >= 0; j--) + cset_unregister(zdev->cset + j); +out_sysfs: + zobj_unregister(&zstat->all_devices, &zdev->head); +out: + return err; +} +EXPORT_SYMBOL(zio_register_dev); + +void zio_unregister_dev(struct zio_device *zdev) +{ + int i; + + pr_debug("%s:%d\n", __func__, __LINE__); + if (!zdev) + return; + + /* Unregister all child channel sets */ + for (i = 0; i < zdev->n_cset; i++) + cset_unregister(&zdev->cset[i]); + /* Remove from sysfs */ + zattr_set_remove(&zdev->head); + /* Unregister the device */ + zobj_unregister(&zstat->all_devices, &zdev->head); +} +EXPORT_SYMBOL(zio_unregister_dev); + +/* Register a buffer into the available buffer list */ +int zio_register_buf(struct zio_buffer_type *zbuf, const char *name) +{ + int err; + pr_debug("%s:%d\n", __func__, __LINE__); + if (!zbuf || !name) + return -EINVAL; + + err = zobj_register(&zstat->all_buffer_types, &zbuf->head, + ZBUF, zbuf->owner, name); + if (err) + return err; + zbuf->zattr_set.n_std_attr = ZATTR_STD_NUM_ZBUF; + INIT_LIST_HEAD(&zbuf->list); + spin_lock_init(&zbuf->lock); + + return 0; +} +EXPORT_SYMBOL(zio_register_buf); + +void zio_unregister_buf(struct zio_buffer_type *zbuf) +{ + if (!zbuf) + return; + zobj_unregister(&zstat->all_buffer_types, &zbuf->head); +} +EXPORT_SYMBOL(zio_unregister_buf); + +/* Register a trigger into the available trigger list */ +int zio_register_trig(struct zio_trigger_type *trig, const char *name) +{ + int err; + + if (!trig || !name) + return -EINVAL; + err = zobj_register(&zstat->all_trigger_types, &trig->head, + ZTRIG, trig->owner, name); + if (err) + return err; + trig->zattr_set.n_std_attr = ZATTR_STD_NUM_TRIG; + INIT_LIST_HEAD(&trig->list); + spin_lock_init(&trig->lock); + + return 0; +} +EXPORT_SYMBOL(zio_register_trig); + +void zio_unregister_trig(struct zio_trigger_type *trig) +{ + if (!trig) + return; + zobj_unregister(&zstat->all_trigger_types, &trig->head); +} +EXPORT_SYMBOL(zio_unregister_trig); + +/* Initialize a list of objects */ +static int zlist_register(struct zio_object_list *zlist, + struct kobject *parent, + enum zio_object_type type, + const char *name) +{ + int err = 0; + + /* Create a defaul kobject for the list and add it to sysfs */ + zlist->kobj = kobject_create_and_add(name, parent); + if (!zlist->kobj) + goto out_kobj; + pr_debug("%s:%d\n", __func__, __LINE__); + + /* Initialize the specific list */ + INIT_LIST_HEAD(&zlist->list); + zlist->zobj_type = type; + return 0; + +out_kobj: + kobject_put(zlist->kobj); /* we must _put even if it returned error */ + return err; +} +/* Remove a list of objects */ +static void zlist_unregister(struct zio_object_list *zlist) +{ + kobject_del(zlist->kobj); + kobject_put(zlist->kobj); +} + +static int __init zio_init(void) +{ + int err; + + /* Some compile-time checks, so developers are free to hack around */ + BUILD_BUG_ON_NOT_POWER_OF_2(ZIO_CHAN_MAXNUM); + BUILD_BUG_ON_NOT_POWER_OF_2(ZIO_CSET_MAXNUM); + BUILD_BUG_ON(ZIO_CSET_MAXNUM * ZIO_CHAN_MAXNUM * 2 > MINORMASK); + BUILD_BUG_ON(ZATTR_STD_NUM_ZDEV != ARRAY_SIZE(zio_zdev_attr_names)); + BUILD_BUG_ON(ZATTR_STD_NUM_ZBUF != ARRAY_SIZE(zio_zbuf_attr_names)); + BUILD_BUG_ON(ZATTR_STD_NUM_TRIG != ARRAY_SIZE(zio_trig_attr_names)); + + /* Initialize char device */ + err = __zio_register_cdev(); + if (err) + goto out_cdev; + + /* Create the zio container */ + zstat->kobj = kobject_create_and_add("zio", NULL); + if (!zstat->kobj) + goto out_kobj; + + /* Register the three object lists (device, buffer and trigger) */ + zlist_register(&zstat->all_devices, zstat->kobj, ZDEV, + "devices"); + zlist_register(&zstat->all_trigger_types, zstat->kobj, ZTRIG, + "triggers"); + zlist_register(&zstat->all_buffer_types, zstat->kobj, ZBUF, + "buffers"); + pr_info("zio-core had been loaded\n"); + return 0; + +out_kobj: + __zio_unregister_cdev(); +out_cdev: + return err; +} + +static void __exit zio_exit(void) +{ + /* Remove the three object lists*/ + zlist_unregister(&zstat->all_devices); + zlist_unregister(&zstat->all_buffer_types); + zlist_unregister(&zstat->all_trigger_types); + + /* Remove from sysfs */ + kobject_del(zstat->kobj); + kobject_put(zstat->kobj); + + /* Remove char device */ + __zio_unregister_cdev(); + + pr_info("zio-core had been unloaded\n"); + return; +} + +subsys_initcall(zio_init); +module_exit(zio_exit); + +MODULE_AUTHOR("Federico Vaga <federico.vaga@gmail.com>"); +MODULE_DESCRIPTION("ZIO - ZIO Input Output"); +MODULE_LICENSE("GPL"); -- 1.7.7.2
| |