aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/most/mostcore/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/most/mostcore/core.c')
-rw-r--r--drivers/staging/most/mostcore/core.c1949
1 files changed, 0 insertions, 1949 deletions
diff --git a/drivers/staging/most/mostcore/core.c b/drivers/staging/most/mostcore/core.c
deleted file mode 100644
index 069269db394c..000000000000
--- a/drivers/staging/most/mostcore/core.c
+++ /dev/null
@@ -1,1949 +0,0 @@
-/*
- * core.c - Implementation of core module of MOST Linux driver stack
- *
- * Copyright (C) 2013-2015 Microchip Technology Germany II GmbH & Co. KG
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * This file is licensed under GPLv2.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/poll.h>
-#include <linux/wait.h>
-#include <linux/kobject.h>
-#include <linux/mutex.h>
-#include <linux/completion.h>
-#include <linux/sysfs.h>
-#include <linux/kthread.h>
-#include <linux/dma-mapping.h>
-#include <linux/idr.h>
-#include "mostcore.h"
-
-#define MAX_CHANNELS 64
-#define STRING_SIZE 80
-
-static struct class *most_class;
-static struct device *core_dev;
-static struct ida mdev_id;
-static int dummy_num_buffers;
-
-struct most_c_aim_obj {
- struct most_aim *ptr;
- int refs;
- int num_buffers;
-};
-
-struct most_c_obj {
- struct kobject kobj;
- struct completion cleanup;
- atomic_t mbo_ref;
- atomic_t mbo_nq_level;
- u16 channel_id;
- bool is_poisoned;
- struct mutex start_mutex;
- struct mutex nq_mutex; /* nq thread synchronization */
- int is_starving;
- struct most_interface *iface;
- struct most_inst_obj *inst;
- struct most_channel_config cfg;
- bool keep_mbo;
- bool enqueue_halt;
- struct list_head fifo;
- spinlock_t fifo_lock;
- struct list_head halt_fifo;
- struct list_head list;
- struct most_c_aim_obj aim0;
- struct most_c_aim_obj aim1;
- struct list_head trash_fifo;
- struct task_struct *hdm_enqueue_task;
- wait_queue_head_t hdm_fifo_wq;
-};
-
-#define to_c_obj(d) container_of(d, struct most_c_obj, kobj)
-
-struct most_inst_obj {
- int dev_id;
- struct most_interface *iface;
- struct list_head channel_list;
- struct most_c_obj *channel[MAX_CHANNELS];
- struct kobject kobj;
- struct list_head list;
-};
-
-static const struct {
- int most_ch_data_type;
- const char *name;
-} ch_data_type[] = {
- { MOST_CH_CONTROL, "control\n" },
- { MOST_CH_ASYNC, "async\n" },
- { MOST_CH_SYNC, "sync\n" },
- { MOST_CH_ISOC, "isoc\n"},
- { MOST_CH_ISOC, "isoc_avp\n"},
-};
-
-#define to_inst_obj(d) container_of(d, struct most_inst_obj, kobj)
-
-/**
- * list_pop_mbo - retrieves the first MBO of the list and removes it
- * @ptr: the list head to grab the MBO from.
- */
-#define list_pop_mbo(ptr) \
-({ \
- struct mbo *_mbo = list_first_entry(ptr, struct mbo, list); \
- list_del(&_mbo->list); \
- _mbo; \
-})
-
-/* ___ ___
- * ___C H A N N E L___
- */
-
-/**
- * struct most_c_attr - to access the attributes of a channel object
- * @attr: attributes of a channel
- * @show: pointer to the show function
- * @store: pointer to the store function
- */
-struct most_c_attr {
- struct attribute attr;
- ssize_t (*show)(struct most_c_obj *d,
- struct most_c_attr *attr,
- char *buf);
- ssize_t (*store)(struct most_c_obj *d,
- struct most_c_attr *attr,
- const char *buf,
- size_t count);
-};
-
-#define to_channel_attr(a) container_of(a, struct most_c_attr, attr)
-
-/**
- * channel_attr_show - show function of channel object
- * @kobj: pointer to its kobject
- * @attr: pointer to its attributes
- * @buf: buffer
- */
-static ssize_t channel_attr_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct most_c_attr *channel_attr = to_channel_attr(attr);
- struct most_c_obj *c_obj = to_c_obj(kobj);
-
- if (!channel_attr->show)
- return -EIO;
-
- return channel_attr->show(c_obj, channel_attr, buf);
-}
-
-/**
- * channel_attr_store - store function of channel object
- * @kobj: pointer to its kobject
- * @attr: pointer to its attributes
- * @buf: buffer
- * @len: length of buffer
- */
-static ssize_t channel_attr_store(struct kobject *kobj,
- struct attribute *attr,
- const char *buf,
- size_t len)
-{
- struct most_c_attr *channel_attr = to_channel_attr(attr);
- struct most_c_obj *c_obj = to_c_obj(kobj);
-
- if (!channel_attr->store)
- return -EIO;
- return channel_attr->store(c_obj, channel_attr, buf, len);
-}
-
-static const struct sysfs_ops most_channel_sysfs_ops = {
- .show = channel_attr_show,
- .store = channel_attr_store,
-};
-
-/**
- * most_free_mbo_coherent - free an MBO and its coherent buffer
- * @mbo: buffer to be released
- *
- */
-static void most_free_mbo_coherent(struct mbo *mbo)
-{
- struct most_c_obj *c = mbo->context;
- u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len;
-
- dma_free_coherent(NULL, coherent_buf_size, mbo->virt_address,
- mbo->bus_address);
- kfree(mbo);
- if (atomic_sub_and_test(1, &c->mbo_ref))
- complete(&c->cleanup);
-}
-
-/**
- * flush_channel_fifos - clear the channel fifos
- * @c: pointer to channel object
- */
-static void flush_channel_fifos(struct most_c_obj *c)
-{
- unsigned long flags, hf_flags;
- struct mbo *mbo, *tmp;
-
- if (list_empty(&c->fifo) && list_empty(&c->halt_fifo))
- return;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- list_for_each_entry_safe(mbo, tmp, &c->fifo, list) {
- list_del(&mbo->list);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- most_free_mbo_coherent(mbo);
- spin_lock_irqsave(&c->fifo_lock, flags);
- }
- spin_unlock_irqrestore(&c->fifo_lock, flags);
-
- spin_lock_irqsave(&c->fifo_lock, hf_flags);
- list_for_each_entry_safe(mbo, tmp, &c->halt_fifo, list) {
- list_del(&mbo->list);
- spin_unlock_irqrestore(&c->fifo_lock, hf_flags);
- most_free_mbo_coherent(mbo);
- spin_lock_irqsave(&c->fifo_lock, hf_flags);
- }
- spin_unlock_irqrestore(&c->fifo_lock, hf_flags);
-
- if (unlikely((!list_empty(&c->fifo) || !list_empty(&c->halt_fifo))))
- pr_info("WARN: fifo | trash fifo not empty\n");
-}
-
-/**
- * flush_trash_fifo - clear the trash fifo
- * @c: pointer to channel object
- */
-static int flush_trash_fifo(struct most_c_obj *c)
-{
- struct mbo *mbo, *tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- list_for_each_entry_safe(mbo, tmp, &c->trash_fifo, list) {
- list_del(&mbo->list);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- most_free_mbo_coherent(mbo);
- spin_lock_irqsave(&c->fifo_lock, flags);
- }
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- return 0;
-}
-
-/**
- * most_channel_release - release function of channel object
- * @kobj: pointer to channel's kobject
- */
-static void most_channel_release(struct kobject *kobj)
-{
- struct most_c_obj *c = to_c_obj(kobj);
-
- kfree(c);
-}
-
-static ssize_t available_directions_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- strcpy(buf, "");
- if (c->iface->channel_vector[i].direction & MOST_CH_RX)
- strcat(buf, "rx ");
- if (c->iface->channel_vector[i].direction & MOST_CH_TX)
- strcat(buf, "tx ");
- strcat(buf, "\n");
- return strlen(buf);
-}
-
-static ssize_t available_datatypes_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- strcpy(buf, "");
- if (c->iface->channel_vector[i].data_type & MOST_CH_CONTROL)
- strcat(buf, "control ");
- if (c->iface->channel_vector[i].data_type & MOST_CH_ASYNC)
- strcat(buf, "async ");
- if (c->iface->channel_vector[i].data_type & MOST_CH_SYNC)
- strcat(buf, "sync ");
- if (c->iface->channel_vector[i].data_type & MOST_CH_ISOC)
- strcat(buf, "isoc ");
- strcat(buf, "\n");
- return strlen(buf);
-}
-
-static ssize_t number_of_packet_buffers_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- c->iface->channel_vector[i].num_buffers_packet);
-}
-
-static ssize_t number_of_stream_buffers_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- c->iface->channel_vector[i].num_buffers_streaming);
-}
-
-static ssize_t size_of_packet_buffer_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- c->iface->channel_vector[i].buffer_size_packet);
-}
-
-static ssize_t size_of_stream_buffer_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- unsigned int i = c->channel_id;
-
- return snprintf(buf, PAGE_SIZE, "%d\n",
- c->iface->channel_vector[i].buffer_size_streaming);
-}
-
-static ssize_t channel_starving_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving);
-}
-
-static ssize_t set_number_of_buffers_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers);
-}
-
-static ssize_t set_number_of_buffers_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- int ret = kstrtou16(buf, 0, &c->cfg.num_buffers);
-
- if (ret)
- return ret;
- return count;
-}
-
-static ssize_t set_buffer_size_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size);
-}
-
-static ssize_t set_buffer_size_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- int ret = kstrtou16(buf, 0, &c->cfg.buffer_size);
-
- if (ret)
- return ret;
- return count;
-}
-
-static ssize_t set_direction_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- if (c->cfg.direction & MOST_CH_TX)
- return snprintf(buf, PAGE_SIZE, "tx\n");
- else if (c->cfg.direction & MOST_CH_RX)
- return snprintf(buf, PAGE_SIZE, "rx\n");
- return snprintf(buf, PAGE_SIZE, "unconfigured\n");
-}
-
-static ssize_t set_direction_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- if (!strcmp(buf, "dir_rx\n")) {
- c->cfg.direction = MOST_CH_RX;
- } else if (!strcmp(buf, "rx\n")) {
- c->cfg.direction = MOST_CH_RX;
- } else if (!strcmp(buf, "dir_tx\n")) {
- c->cfg.direction = MOST_CH_TX;
- } else if (!strcmp(buf, "tx\n")) {
- c->cfg.direction = MOST_CH_TX;
- } else {
- pr_info("WARN: invalid attribute settings\n");
- return -EINVAL;
- }
- return count;
-}
-
-static ssize_t set_datatype_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) {
- if (c->cfg.data_type & ch_data_type[i].most_ch_data_type)
- return snprintf(buf, PAGE_SIZE, ch_data_type[i].name);
- }
- return snprintf(buf, PAGE_SIZE, "unconfigured\n");
-}
-
-static ssize_t set_datatype_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ch_data_type); i++) {
- if (!strcmp(buf, ch_data_type[i].name)) {
- c->cfg.data_type = ch_data_type[i].most_ch_data_type;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(ch_data_type)) {
- pr_info("WARN: invalid attribute settings\n");
- return -EINVAL;
- }
- return count;
-}
-
-static ssize_t set_subbuffer_size_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size);
-}
-
-static ssize_t set_subbuffer_size_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- int ret = kstrtou16(buf, 0, &c->cfg.subbuffer_size);
-
- if (ret)
- return ret;
- return count;
-}
-
-static ssize_t set_packets_per_xact_show(struct most_c_obj *c,
- struct most_c_attr *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact);
-}
-
-static ssize_t set_packets_per_xact_store(struct most_c_obj *c,
- struct most_c_attr *attr,
- const char *buf,
- size_t count)
-{
- int ret = kstrtou16(buf, 0, &c->cfg.packets_per_xact);
-
- if (ret)
- return ret;
- return count;
-}
-
-static struct most_c_attr most_c_attrs[] = {
- __ATTR_RO(available_directions),
- __ATTR_RO(available_datatypes),
- __ATTR_RO(number_of_packet_buffers),
- __ATTR_RO(number_of_stream_buffers),
- __ATTR_RO(size_of_stream_buffer),
- __ATTR_RO(size_of_packet_buffer),
- __ATTR_RO(channel_starving),
- __ATTR_RW(set_buffer_size),
- __ATTR_RW(set_number_of_buffers),
- __ATTR_RW(set_direction),
- __ATTR_RW(set_datatype),
- __ATTR_RW(set_subbuffer_size),
- __ATTR_RW(set_packets_per_xact),
-};
-
-/**
- * most_channel_def_attrs - array of default attributes of channel object
- */
-static struct attribute *most_channel_def_attrs[] = {
- &most_c_attrs[0].attr,
- &most_c_attrs[1].attr,
- &most_c_attrs[2].attr,
- &most_c_attrs[3].attr,
- &most_c_attrs[4].attr,
- &most_c_attrs[5].attr,
- &most_c_attrs[6].attr,
- &most_c_attrs[7].attr,
- &most_c_attrs[8].attr,
- &most_c_attrs[9].attr,
- &most_c_attrs[10].attr,
- &most_c_attrs[11].attr,
- &most_c_attrs[12].attr,
- NULL,
-};
-
-static struct kobj_type most_channel_ktype = {
- .sysfs_ops = &most_channel_sysfs_ops,
- .release = most_channel_release,
- .default_attrs = most_channel_def_attrs,
-};
-
-static struct kset *most_channel_kset;
-
-/**
- * create_most_c_obj - allocates a channel object
- * @name: name of the channel object
- * @parent: parent kobject
- *
- * This create a channel object and registers it with sysfs.
- * Returns a pointer to the object or NULL when something went wrong.
- */
-static struct most_c_obj *
-create_most_c_obj(const char *name, struct kobject *parent)
-{
- struct most_c_obj *c;
- int retval;
-
- c = kzalloc(sizeof(*c), GFP_KERNEL);
- if (!c)
- return NULL;
- c->kobj.kset = most_channel_kset;
- retval = kobject_init_and_add(&c->kobj, &most_channel_ktype, parent,
- "%s", name);
- if (retval) {
- kobject_put(&c->kobj);
- return NULL;
- }
- kobject_uevent(&c->kobj, KOBJ_ADD);
- return c;
-}
-
-/* ___ ___
- * ___I N S T A N C E___
- */
-
-static struct list_head instance_list;
-
-/**
- * struct most_inst_attribute - to access the attributes of instance object
- * @attr: attributes of an instance
- * @show: pointer to the show function
- * @store: pointer to the store function
- */
-struct most_inst_attribute {
- struct attribute attr;
- ssize_t (*show)(struct most_inst_obj *d,
- struct most_inst_attribute *attr,
- char *buf);
- ssize_t (*store)(struct most_inst_obj *d,
- struct most_inst_attribute *attr,
- const char *buf,
- size_t count);
-};
-
-#define to_instance_attr(a) \
- container_of(a, struct most_inst_attribute, attr)
-
-/**
- * instance_attr_show - show function for an instance object
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- */
-static ssize_t instance_attr_show(struct kobject *kobj,
- struct attribute *attr,
- char *buf)
-{
- struct most_inst_attribute *instance_attr;
- struct most_inst_obj *instance_obj;
-
- instance_attr = to_instance_attr(attr);
- instance_obj = to_inst_obj(kobj);
-
- if (!instance_attr->show)
- return -EIO;
-
- return instance_attr->show(instance_obj, instance_attr, buf);
-}
-
-/**
- * instance_attr_store - store function for an instance object
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- * @len: length of buffer
- */
-static ssize_t instance_attr_store(struct kobject *kobj,
- struct attribute *attr,
- const char *buf,
- size_t len)
-{
- struct most_inst_attribute *instance_attr;
- struct most_inst_obj *instance_obj;
-
- instance_attr = to_instance_attr(attr);
- instance_obj = to_inst_obj(kobj);
-
- if (!instance_attr->store)
- return -EIO;
-
- return instance_attr->store(instance_obj, instance_attr, buf, len);
-}
-
-static const struct sysfs_ops most_inst_sysfs_ops = {
- .show = instance_attr_show,
- .store = instance_attr_store,
-};
-
-/**
- * most_inst_release - release function for instance object
- * @kobj: pointer to instance's kobject
- *
- * This frees the allocated memory for the instance object
- */
-static void most_inst_release(struct kobject *kobj)
-{
- struct most_inst_obj *inst = to_inst_obj(kobj);
-
- kfree(inst);
-}
-
-static ssize_t description_show(struct most_inst_obj *instance_obj,
- struct most_inst_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n",
- instance_obj->iface->description);
-}
-
-static ssize_t interface_show(struct most_inst_obj *instance_obj,
- struct most_inst_attribute *attr,
- char *buf)
-{
- switch (instance_obj->iface->interface) {
- case ITYPE_LOOPBACK:
- return snprintf(buf, PAGE_SIZE, "loopback\n");
- case ITYPE_I2C:
- return snprintf(buf, PAGE_SIZE, "i2c\n");
- case ITYPE_I2S:
- return snprintf(buf, PAGE_SIZE, "i2s\n");
- case ITYPE_TSI:
- return snprintf(buf, PAGE_SIZE, "tsi\n");
- case ITYPE_HBI:
- return snprintf(buf, PAGE_SIZE, "hbi\n");
- case ITYPE_MEDIALB_DIM:
- return snprintf(buf, PAGE_SIZE, "mlb_dim\n");
- case ITYPE_MEDIALB_DIM2:
- return snprintf(buf, PAGE_SIZE, "mlb_dim2\n");
- case ITYPE_USB:
- return snprintf(buf, PAGE_SIZE, "usb\n");
- case ITYPE_PCIE:
- return snprintf(buf, PAGE_SIZE, "pcie\n");
- }
- return snprintf(buf, PAGE_SIZE, "unknown\n");
-}
-
-static struct most_inst_attribute most_inst_attr_description =
- __ATTR_RO(description);
-
-static struct most_inst_attribute most_inst_attr_interface =
- __ATTR_RO(interface);
-
-static struct attribute *most_inst_def_attrs[] = {
- &most_inst_attr_description.attr,
- &most_inst_attr_interface.attr,
- NULL,
-};
-
-static struct kobj_type most_inst_ktype = {
- .sysfs_ops = &most_inst_sysfs_ops,
- .release = most_inst_release,
- .default_attrs = most_inst_def_attrs,
-};
-
-static struct kset *most_inst_kset;
-
-/**
- * create_most_inst_obj - creates an instance object
- * @name: name of the object to be created
- *
- * This allocates memory for an instance structure, assigns the proper kset
- * and registers it with sysfs.
- *
- * Returns a pointer to the instance object or NULL when something went wrong.
- */
-static struct most_inst_obj *create_most_inst_obj(const char *name)
-{
- struct most_inst_obj *inst;
- int retval;
-
- inst = kzalloc(sizeof(*inst), GFP_KERNEL);
- if (!inst)
- return NULL;
- inst->kobj.kset = most_inst_kset;
- retval = kobject_init_and_add(&inst->kobj, &most_inst_ktype, NULL,
- "%s", name);
- if (retval) {
- kobject_put(&inst->kobj);
- return NULL;
- }
- kobject_uevent(&inst->kobj, KOBJ_ADD);
- return inst;
-}
-
-/**
- * destroy_most_inst_obj - MOST instance release function
- * @inst: pointer to the instance object
- *
- * This decrements the reference counter of the instance object.
- * If the reference count turns zero, its release function is called
- */
-static void destroy_most_inst_obj(struct most_inst_obj *inst)
-{
- struct most_c_obj *c, *tmp;
-
- list_for_each_entry_safe(c, tmp, &inst->channel_list, list) {
- flush_trash_fifo(c);
- flush_channel_fifos(c);
- kobject_put(&c->kobj);
- }
- kobject_put(&inst->kobj);
-}
-
-/* ___ ___
- * ___A I M___
- */
-struct most_aim_obj {
- struct kobject kobj;
- struct list_head list;
- struct most_aim *driver;
-};
-
-#define to_aim_obj(d) container_of(d, struct most_aim_obj, kobj)
-
-static struct list_head aim_list;
-
-/**
- * struct most_aim_attribute - to access the attributes of AIM object
- * @attr: attributes of an AIM
- * @show: pointer to the show function
- * @store: pointer to the store function
- */
-struct most_aim_attribute {
- struct attribute attr;
- ssize_t (*show)(struct most_aim_obj *d,
- struct most_aim_attribute *attr,
- char *buf);
- ssize_t (*store)(struct most_aim_obj *d,
- struct most_aim_attribute *attr,
- const char *buf,
- size_t count);
-};
-
-#define to_aim_attr(a) container_of(a, struct most_aim_attribute, attr)
-
-/**
- * aim_attr_show - show function of an AIM object
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- */
-static ssize_t aim_attr_show(struct kobject *kobj,
- struct attribute *attr,
- char *buf)
-{
- struct most_aim_attribute *aim_attr;
- struct most_aim_obj *aim_obj;
-
- aim_attr = to_aim_attr(attr);
- aim_obj = to_aim_obj(kobj);
-
- if (!aim_attr->show)
- return -EIO;
-
- return aim_attr->show(aim_obj, aim_attr, buf);
-}
-
-/**
- * aim_attr_store - store function of an AIM object
- * @kobj: pointer to kobject
- * @attr: pointer to attribute struct
- * @buf: buffer
- * @len: length of buffer
- */
-static ssize_t aim_attr_store(struct kobject *kobj,
- struct attribute *attr,
- const char *buf,
- size_t len)
-{
- struct most_aim_attribute *aim_attr;
- struct most_aim_obj *aim_obj;
-
- aim_attr = to_aim_attr(attr);
- aim_obj = to_aim_obj(kobj);
-
- if (!aim_attr->store)
- return -EIO;
- return aim_attr->store(aim_obj, aim_attr, buf, len);
-}
-
-static const struct sysfs_ops most_aim_sysfs_ops = {
- .show = aim_attr_show,
- .store = aim_attr_store,
-};
-
-/**
- * most_aim_release - AIM release function
- * @kobj: pointer to AIM's kobject
- */
-static void most_aim_release(struct kobject *kobj)
-{
- struct most_aim_obj *aim_obj = to_aim_obj(kobj);
-
- kfree(aim_obj);
-}
-
-static ssize_t links_show(struct most_aim_obj *aim_obj,
- struct most_aim_attribute *attr,
- char *buf)
-{
- struct most_c_obj *c;
- struct most_inst_obj *i;
- int offs = 0;
-
- list_for_each_entry(i, &instance_list, list) {
- list_for_each_entry(c, &i->channel_list, list) {
- if (c->aim0.ptr == aim_obj->driver ||
- c->aim1.ptr == aim_obj->driver) {
- offs += snprintf(buf + offs, PAGE_SIZE - offs,
- "%s:%s\n",
- kobject_name(&i->kobj),
- kobject_name(&c->kobj));
- }
- }
- }
-
- return offs;
-}
-
-/**
- * split_string - parses and changes string in the buffer buf and
- * splits it into two mandatory and one optional substrings.
- *
- * @buf: complete string from attribute 'add_channel'
- * @a: address of pointer to 1st substring (=instance name)
- * @b: address of pointer to 2nd substring (=channel name)
- * @c: optional address of pointer to 3rd substring (=user defined name)
- *
- * Examples:
- *
- * Input: "mdev0:ch6:my_channel\n" or
- * "mdev0:ch6:my_channel"
- *
- * Output: *a -> "mdev0", *b -> "ch6", *c -> "my_channel"
- *
- * Input: "mdev1:ep81\n"
- * Output: *a -> "mdev1", *b -> "ep81", *c -> ""
- *
- * Input: "mdev1:ep81"
- * Output: *a -> "mdev1", *b -> "ep81", *c == NULL
- */
-static int split_string(char *buf, char **a, char **b, char **c)
-{
- *a = strsep(&buf, ":");
- if (!*a)
- return -EIO;
-
- *b = strsep(&buf, ":\n");
- if (!*b)
- return -EIO;
-
- if (c)
- *c = strsep(&buf, ":\n");
-
- return 0;
-}
-
-/**
- * get_channel_by_name - get pointer to channel object
- * @mdev: name of the device instance
- * @mdev_ch: name of the respective channel
- *
- * This retrieves the pointer to a channel object.
- */
-static struct
-most_c_obj *get_channel_by_name(char *mdev, char *mdev_ch)
-{
- struct most_c_obj *c, *tmp;
- struct most_inst_obj *i, *i_tmp;
- int found = 0;
-
- list_for_each_entry_safe(i, i_tmp, &instance_list, list) {
- if (!strcmp(kobject_name(&i->kobj), mdev)) {
- found++;
- break;
- }
- }
- if (unlikely(!found))
- return ERR_PTR(-EIO);
-
- list_for_each_entry_safe(c, tmp, &i->channel_list, list) {
- if (!strcmp(kobject_name(&c->kobj), mdev_ch)) {
- found++;
- break;
- }
- }
- if (unlikely(found < 2))
- return ERR_PTR(-EIO);
- return c;
-}
-
-/**
- * add_link_store - store() function for add_link attribute
- * @aim_obj: pointer to AIM object
- * @attr: its attributes
- * @buf: buffer
- * @len: buffer length
- *
- * This parses the string given by buf and splits it into
- * three substrings. Note: third substring is optional. In case a cdev
- * AIM is loaded the optional 3rd substring will make up the name of
- * device node in the /dev directory. If omitted, the device node will
- * inherit the channel's name within sysfs.
- *
- * Searches for a pair of device and channel and probes the AIM
- *
- * Example:
- * (1) echo "mdev0:ch6:my_rxchannel" >add_link
- * (2) echo "mdev1:ep81" >add_link
- *
- * (1) would create the device node /dev/my_rxchannel
- * (2) would create the device node /dev/mdev1-ep81
- */
-static ssize_t add_link_store(struct most_aim_obj *aim_obj,
- struct most_aim_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct most_c_obj *c;
- struct most_aim **aim_ptr;
- char buffer[STRING_SIZE];
- char *mdev;
- char *mdev_ch;
- char *mdev_devnod;
- char devnod_buf[STRING_SIZE];
- int ret;
- size_t max_len = min_t(size_t, len + 1, STRING_SIZE);
-
- strlcpy(buffer, buf, max_len);
-
- ret = split_string(buffer, &mdev, &mdev_ch, &mdev_devnod);
- if (ret)
- return ret;
-
- if (!mdev_devnod || *mdev_devnod == 0) {
- snprintf(devnod_buf, sizeof(devnod_buf), "%s-%s", mdev,
- mdev_ch);
- mdev_devnod = devnod_buf;
- }
-
- c = get_channel_by_name(mdev, mdev_ch);
- if (IS_ERR(c))
- return -ENODEV;
-
- if (!c->aim0.ptr)
- aim_ptr = &c->aim0.ptr;
- else if (!c->aim1.ptr)
- aim_ptr = &c->aim1.ptr;
- else
- return -ENOSPC;
-
- *aim_ptr = aim_obj->driver;
- ret = aim_obj->driver->probe_channel(c->iface, c->channel_id,
- &c->cfg, &c->kobj, mdev_devnod);
- if (ret) {
- *aim_ptr = NULL;
- return ret;
- }
-
- return len;
-}
-
-/**
- * remove_link_store - store function for remove_link attribute
- * @aim_obj: pointer to AIM object
- * @attr: its attributes
- * @buf: buffer
- * @len: buffer length
- *
- * Example:
- * echo "mdev0:ep81" >remove_link
- */
-static ssize_t remove_link_store(struct most_aim_obj *aim_obj,
- struct most_aim_attribute *attr,
- const char *buf,
- size_t len)
-{
- struct most_c_obj *c;
- char buffer[STRING_SIZE];
- char *mdev;
- char *mdev_ch;
- int ret;
- size_t max_len = min_t(size_t, len + 1, STRING_SIZE);
-
- strlcpy(buffer, buf, max_len);
- ret = split_string(buffer, &mdev, &mdev_ch, NULL);
- if (ret)
- return ret;
-
- c = get_channel_by_name(mdev, mdev_ch);
- if (IS_ERR(c))
- return -ENODEV;
-
- if (aim_obj->driver->disconnect_channel(c->iface, c->channel_id))
- return -EIO;
- if (c->aim0.ptr == aim_obj->driver)
- c->aim0.ptr = NULL;
- if (c->aim1.ptr == aim_obj->driver)
- c->aim1.ptr = NULL;
- return len;
-}
-
-static struct most_aim_attribute most_aim_attrs[] = {
- __ATTR_RO(links),
- __ATTR_WO(add_link),
- __ATTR_WO(remove_link),
-};
-
-static struct attribute *most_aim_def_attrs[] = {
- &most_aim_attrs[0].attr,
- &most_aim_attrs[1].attr,
- &most_aim_attrs[2].attr,
- NULL,
-};
-
-static struct kobj_type most_aim_ktype = {
- .sysfs_ops = &most_aim_sysfs_ops,
- .release = most_aim_release,
- .default_attrs = most_aim_def_attrs,
-};
-
-static struct kset *most_aim_kset;
-
-/**
- * create_most_aim_obj - creates an AIM object
- * @name: name of the AIM
- *
- * This creates an AIM object assigns the proper kset and registers
- * it with sysfs.
- * Returns a pointer to the object or NULL if something went wrong.
- */
-static struct most_aim_obj *create_most_aim_obj(const char *name)
-{
- struct most_aim_obj *most_aim;
- int retval;
-
- most_aim = kzalloc(sizeof(*most_aim), GFP_KERNEL);
- if (!most_aim)
- return NULL;
- most_aim->kobj.kset = most_aim_kset;
- retval = kobject_init_and_add(&most_aim->kobj, &most_aim_ktype,
- NULL, "%s", name);
- if (retval) {
- kobject_put(&most_aim->kobj);
- return NULL;
- }
- kobject_uevent(&most_aim->kobj, KOBJ_ADD);
- return most_aim;
-}
-
-/**
- * destroy_most_aim_obj - AIM release function
- * @p: pointer to AIM object
- *
- * This decrements the reference counter of the AIM object. If the
- * reference count turns zero, its release function will be called.
- */
-static void destroy_most_aim_obj(struct most_aim_obj *p)
-{
- kobject_put(&p->kobj);
-}
-
-/* ___ ___
- * ___C O R E___
- */
-
-/**
- * Instantiation of the MOST bus
- */
-static struct bus_type most_bus = {
- .name = "most",
-};
-
-/**
- * Instantiation of the core driver
- */
-static struct device_driver mostcore = {
- .name = "mostcore",
- .bus = &most_bus,
-};
-
-static inline void trash_mbo(struct mbo *mbo)
-{
- unsigned long flags;
- struct most_c_obj *c = mbo->context;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- list_add(&mbo->list, &c->trash_fifo);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
-}
-
-static bool hdm_mbo_ready(struct most_c_obj *c)
-{
- bool empty;
-
- if (c->enqueue_halt)
- return false;
-
- spin_lock_irq(&c->fifo_lock);
- empty = list_empty(&c->halt_fifo);
- spin_unlock_irq(&c->fifo_lock);
-
- return !empty;
-}
-
-static void nq_hdm_mbo(struct mbo *mbo)
-{
- unsigned long flags;
- struct most_c_obj *c = mbo->context;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- list_add_tail(&mbo->list, &c->halt_fifo);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- wake_up_interruptible(&c->hdm_fifo_wq);
-}
-
-static int hdm_enqueue_thread(void *data)
-{
- struct most_c_obj *c = data;
- struct mbo *mbo;
- int ret;
- typeof(c->iface->enqueue) enqueue = c->iface->enqueue;
-
- while (likely(!kthread_should_stop())) {
- wait_event_interruptible(c->hdm_fifo_wq,
- hdm_mbo_ready(c) ||
- kthread_should_stop());
-
- mutex_lock(&c->nq_mutex);
- spin_lock_irq(&c->fifo_lock);
- if (unlikely(c->enqueue_halt || list_empty(&c->halt_fifo))) {
- spin_unlock_irq(&c->fifo_lock);
- mutex_unlock(&c->nq_mutex);
- continue;
- }
-
- mbo = list_pop_mbo(&c->halt_fifo);
- spin_unlock_irq(&c->fifo_lock);
-
- if (c->cfg.direction == MOST_CH_RX)
- mbo->buffer_length = c->cfg.buffer_size;
-
- ret = enqueue(mbo->ifp, mbo->hdm_channel_id, mbo);
- mutex_unlock(&c->nq_mutex);
-
- if (unlikely(ret)) {
- pr_err("hdm enqueue failed\n");
- nq_hdm_mbo(mbo);
- c->hdm_enqueue_task = NULL;
- return 0;
- }
- }
-
- return 0;
-}
-
-static int run_enqueue_thread(struct most_c_obj *c, int channel_id)
-{
- struct task_struct *task =
- kthread_run(hdm_enqueue_thread, c, "hdm_fifo_%d",
- channel_id);
-
- if (IS_ERR(task))
- return PTR_ERR(task);
-
- c->hdm_enqueue_task = task;
- return 0;
-}
-
-/**
- * arm_mbo - recycle MBO for further usage
- * @mbo: buffer object
- *
- * This puts an MBO back to the list to have it ready for up coming
- * tx transactions.
- *
- * In case the MBO belongs to a channel that recently has been
- * poisoned, the MBO is scheduled to be trashed.
- * Calls the completion handler of an attached AIM.
- */
-static void arm_mbo(struct mbo *mbo)
-{
- unsigned long flags;
- struct most_c_obj *c;
-
- BUG_ON((!mbo) || (!mbo->context));
- c = mbo->context;
-
- if (c->is_poisoned) {
- trash_mbo(mbo);
- return;
- }
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- ++*mbo->num_buffers_ptr;
- list_add_tail(&mbo->list, &c->fifo);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
-
- if (c->aim0.refs && c->aim0.ptr->tx_completion)
- c->aim0.ptr->tx_completion(c->iface, c->channel_id);
-
- if (c->aim1.refs && c->aim1.ptr->tx_completion)
- c->aim1.ptr->tx_completion(c->iface, c->channel_id);
-}
-
-/**
- * arm_mbo_chain - helper function that arms an MBO chain for the HDM
- * @c: pointer to interface channel
- * @dir: direction of the channel
- * @compl: pointer to completion function
- *
- * This allocates buffer objects including the containing DMA coherent
- * buffer and puts them in the fifo.
- * Buffers of Rx channels are put in the kthread fifo, hence immediately
- * submitted to the HDM.
- *
- * Returns the number of allocated and enqueued MBOs.
- */
-static int arm_mbo_chain(struct most_c_obj *c, int dir,
- void (*compl)(struct mbo *))
-{
- unsigned int i;
- int retval;
- struct mbo *mbo;
- u32 coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len;
-
- atomic_set(&c->mbo_nq_level, 0);
-
- for (i = 0; i < c->cfg.num_buffers; i++) {
- mbo = kzalloc(sizeof(*mbo), GFP_KERNEL);
- if (!mbo) {
- retval = i;
- goto _exit;
- }
- mbo->context = c;
- mbo->ifp = c->iface;
- mbo->hdm_channel_id = c->channel_id;
- mbo->virt_address = dma_alloc_coherent(NULL,
- coherent_buf_size,
- &mbo->bus_address,
- GFP_KERNEL);
- if (!mbo->virt_address) {
- pr_info("WARN: No DMA coherent buffer.\n");
- retval = i;
- goto _error1;
- }
- mbo->complete = compl;
- mbo->num_buffers_ptr = &dummy_num_buffers;
- if (dir == MOST_CH_RX) {
- nq_hdm_mbo(mbo);
- atomic_inc(&c->mbo_nq_level);
- } else {
- arm_mbo(mbo);
- }
- }
- return i;
-
-_error1:
- kfree(mbo);
-_exit:
- return retval;
-}
-
-/**
- * most_submit_mbo - submits an MBO to fifo
- * @mbo: pointer to the MBO
- */
-void most_submit_mbo(struct mbo *mbo)
-{
- if (WARN_ONCE(!mbo || !mbo->context,
- "bad mbo or missing channel reference\n"))
- return;
-
- nq_hdm_mbo(mbo);
-}
-EXPORT_SYMBOL_GPL(most_submit_mbo);
-
-/**
- * most_write_completion - write completion handler
- * @mbo: pointer to MBO
- *
- * This recycles the MBO for further usage. In case the channel has been
- * poisoned, the MBO is scheduled to be trashed.
- */
-static void most_write_completion(struct mbo *mbo)
-{
- struct most_c_obj *c;
-
- BUG_ON((!mbo) || (!mbo->context));
-
- c = mbo->context;
- if (mbo->status == MBO_E_INVAL)
- pr_info("WARN: Tx MBO status: invalid\n");
- if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE)))
- trash_mbo(mbo);
- else
- arm_mbo(mbo);
-}
-
-/**
- * get_channel_by_iface - get pointer to channel object
- * @iface: pointer to interface instance
- * @id: channel ID
- *
- * This retrieves a pointer to a channel of the given interface and channel ID.
- */
-static struct
-most_c_obj *get_channel_by_iface(struct most_interface *iface, int id)
-{
- struct most_inst_obj *i;
-
- if (unlikely(!iface)) {
- pr_err("Bad interface\n");
- return NULL;
- }
- if (unlikely((id < 0) || (id >= iface->num_channels))) {
- pr_err("Channel index (%d) out of range\n", id);
- return NULL;
- }
- i = iface->priv;
- if (unlikely(!i)) {
- pr_err("interface is not registered\n");
- return NULL;
- }
- return i->channel[id];
-}
-
-int channel_has_mbo(struct most_interface *iface, int id, struct most_aim *aim)
-{
- struct most_c_obj *c = get_channel_by_iface(iface, id);
- unsigned long flags;
- int empty;
-
- if (unlikely(!c))
- return -EINVAL;
-
- if (c->aim0.refs && c->aim1.refs &&
- ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) ||
- (aim == c->aim1.ptr && c->aim1.num_buffers <= 0)))
- return 0;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- empty = list_empty(&c->fifo);
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- return !empty;
-}
-EXPORT_SYMBOL_GPL(channel_has_mbo);
-
-/**
- * most_get_mbo - get pointer to an MBO of pool
- * @iface: pointer to interface instance
- * @id: channel ID
- *
- * This attempts to get a free buffer out of the channel fifo.
- * Returns a pointer to MBO on success or NULL otherwise.
- */
-struct mbo *most_get_mbo(struct most_interface *iface, int id,
- struct most_aim *aim)
-{
- struct mbo *mbo;
- struct most_c_obj *c;
- unsigned long flags;
- int *num_buffers_ptr;
-
- c = get_channel_by_iface(iface, id);
- if (unlikely(!c))
- return NULL;
-
- if (c->aim0.refs && c->aim1.refs &&
- ((aim == c->aim0.ptr && c->aim0.num_buffers <= 0) ||
- (aim == c->aim1.ptr && c->aim1.num_buffers <= 0)))
- return NULL;
-
- if (aim == c->aim0.ptr)
- num_buffers_ptr = &c->aim0.num_buffers;
- else if (aim == c->aim1.ptr)
- num_buffers_ptr = &c->aim1.num_buffers;
- else
- num_buffers_ptr = &dummy_num_buffers;
-
- spin_lock_irqsave(&c->fifo_lock, flags);
- if (list_empty(&c->fifo)) {
- spin_unlock_irqrestore(&c->fifo_lock, flags);
- return NULL;
- }
- mbo = list_pop_mbo(&c->fifo);
- --*num_buffers_ptr;
- spin_unlock_irqrestore(&c->fifo_lock, flags);
-
- mbo->num_buffers_ptr = num_buffers_ptr;
- mbo->buffer_length = c->cfg.buffer_size;
- return mbo;
-}
-EXPORT_SYMBOL_GPL(most_get_mbo);
-
-/**
- * most_put_mbo - return buffer to pool
- * @mbo: buffer object
- */
-void most_put_mbo(struct mbo *mbo)
-{
- struct most_c_obj *c = mbo->context;
-
- if (c->cfg.direction == MOST_CH_TX) {
- arm_mbo(mbo);
- return;
- }
- nq_hdm_mbo(mbo);
- atomic_inc(&c->mbo_nq_level);
-}
-EXPORT_SYMBOL_GPL(most_put_mbo);
-
-/**
- * most_read_completion - read completion handler
- * @mbo: pointer to MBO
- *
- * This function is called by the HDM when data has been received from the
- * hardware and copied to the buffer of the MBO.
- *
- * In case the channel has been poisoned it puts the buffer in the trash queue.
- * Otherwise, it passes the buffer to an AIM for further processing.
- */
-static void most_read_completion(struct mbo *mbo)
-{
- struct most_c_obj *c = mbo->context;
-
- if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) {
- trash_mbo(mbo);
- return;
- }
-
- if (mbo->status == MBO_E_INVAL) {
- nq_hdm_mbo(mbo);
- atomic_inc(&c->mbo_nq_level);
- return;
- }
-
- if (atomic_sub_and_test(1, &c->mbo_nq_level))
- c->is_starving = 1;
-
- if (c->aim0.refs && c->aim0.ptr->rx_completion &&
- c->aim0.ptr->rx_completion(mbo) == 0)
- return;
-
- if (c->aim1.refs && c->aim1.ptr->rx_completion &&
- c->aim1.ptr->rx_completion(mbo) == 0)
- return;
-
- most_put_mbo(mbo);
-}
-
-/**
- * most_start_channel - prepares a channel for communication
- * @iface: pointer to interface instance
- * @id: channel ID
- *
- * This prepares the channel for usage. Cross-checks whether the
- * channel's been properly configured.
- *
- * Returns 0 on success or error code otherwise.
- */
-int most_start_channel(struct most_interface *iface, int id,
- struct most_aim *aim)
-{
- int num_buffer;
- int ret;
- struct most_c_obj *c = get_channel_by_iface(iface, id);
-
- if (unlikely(!c))
- return -EINVAL;
-
- mutex_lock(&c->start_mutex);
- if (c->aim0.refs + c->aim1.refs > 0)
- goto out; /* already started by other aim */
-
- if (!try_module_get(iface->mod)) {
- pr_info("failed to acquire HDM lock\n");
- mutex_unlock(&c->start_mutex);
- return -ENOLCK;
- }
-
- c->cfg.extra_len = 0;
- if (c->iface->configure(c->iface, c->channel_id, &c->cfg)) {
- pr_info("channel configuration failed. Go check settings...\n");
- ret = -EINVAL;
- goto error;
- }
-
- init_waitqueue_head(&c->hdm_fifo_wq);
-
- if (c->cfg.direction == MOST_CH_RX)
- num_buffer = arm_mbo_chain(c, c->cfg.direction,
- most_read_completion);
- else
- num_buffer = arm_mbo_chain(c, c->cfg.direction,
- most_write_completion);
- if (unlikely(!num_buffer)) {
- pr_info("failed to allocate memory\n");
- ret = -ENOMEM;
- goto error;
- }
-
- ret = run_enqueue_thread(c, id);
- if (ret)
- goto error;
-
- c->is_starving = 0;
- c->aim0.num_buffers = c->cfg.num_buffers / 2;
- c->aim1.num_buffers = c->cfg.num_buffers - c->aim0.num_buffers;
- atomic_set(&c->mbo_ref, num_buffer);
-
-out:
- if (aim == c->aim0.ptr)
- c->aim0.refs++;
- if (aim == c->aim1.ptr)
- c->aim1.refs++;
- mutex_unlock(&c->start_mutex);
- return 0;
-
-error:
- module_put(iface->mod);
- mutex_unlock(&c->start_mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(most_start_channel);
-
-/**
- * most_stop_channel - stops a running channel
- * @iface: pointer to interface instance
- * @id: channel ID
- */
-int most_stop_channel(struct most_interface *iface, int id,
- struct most_aim *aim)
-{
- struct most_c_obj *c;
-
- if (unlikely((!iface) || (id >= iface->num_channels) || (id < 0))) {
- pr_err("Bad interface or index out of range\n");
- return -EINVAL;
- }
- c = get_channel_by_iface(iface, id);
- if (unlikely(!c))
- return -EINVAL;
-
- mutex_lock(&c->start_mutex);
- if (c->aim0.refs + c->aim1.refs >= 2)
- goto out;
-
- if (c->hdm_enqueue_task)
- kthread_stop(c->hdm_enqueue_task);
- c->hdm_enqueue_task = NULL;
-
- if (iface->mod)
- module_put(iface->mod);
-
- c->is_poisoned = true;
- if (c->iface->poison_channel(c->iface, c->channel_id)) {
- pr_err("Cannot stop channel %d of mdev %s\n", c->channel_id,
- c->iface->description);
- mutex_unlock(&c->start_mutex);
- return -EAGAIN;
- }
- flush_trash_fifo(c);
- flush_channel_fifos(c);
-
-#ifdef CMPL_INTERRUPTIBLE
- if (wait_for_completion_interruptible(&c->cleanup)) {
- pr_info("Interrupted while clean up ch %d\n", c->channel_id);
- mutex_unlock(&c->start_mutex);
- return -EINTR;
- }
-#else
- wait_for_completion(&c->cleanup);
-#endif
- c->is_poisoned = false;
-
-out:
- if (aim == c->aim0.ptr)
- c->aim0.refs--;
- if (aim == c->aim1.ptr)
- c->aim1.refs--;
- mutex_unlock(&c->start_mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(most_stop_channel);
-
-/**
- * most_register_aim - registers an AIM (driver) with the core
- * @aim: instance of AIM to be registered
- */
-int most_register_aim(struct most_aim *aim)
-{
- struct most_aim_obj *aim_obj;
-
- if (!aim) {
- pr_err("Bad driver\n");
- return -EINVAL;
- }
- aim_obj = create_most_aim_obj(aim->name);
- if (!aim_obj) {
- pr_info("failed to alloc driver object\n");
- return -ENOMEM;
- }
- aim_obj->driver = aim;
- aim->context = aim_obj;
- pr_info("registered new application interfacing module %s\n",
- aim->name);
- list_add_tail(&aim_obj->list, &aim_list);
- return 0;
-}
-EXPORT_SYMBOL_GPL(most_register_aim);
-
-/**
- * most_deregister_aim - deregisters an AIM (driver) with the core
- * @aim: AIM to be removed
- */
-int most_deregister_aim(struct most_aim *aim)
-{
- struct most_aim_obj *aim_obj;
- struct most_c_obj *c, *tmp;
- struct most_inst_obj *i, *i_tmp;
-
- if (!aim) {
- pr_err("Bad driver\n");
- return -EINVAL;
- }
-
- aim_obj = aim->context;
- if (!aim_obj) {
- pr_info("driver not registered.\n");
- return -EINVAL;
- }
- list_for_each_entry_safe(i, i_tmp, &instance_list, list) {
- list_for_each_entry_safe(c, tmp, &i->channel_list, list) {
- if (c->aim0.ptr == aim || c->aim1.ptr == aim)
- aim->disconnect_channel(
- c->iface, c->channel_id);
- if (c->aim0.ptr == aim)
- c->aim0.ptr = NULL;
- if (c->aim1.ptr == aim)
- c->aim1.ptr = NULL;
- }
- }
- list_del(&aim_obj->list);
- destroy_most_aim_obj(aim_obj);
- pr_info("deregistering application interfacing module %s\n", aim->name);
- return 0;
-}
-EXPORT_SYMBOL_GPL(most_deregister_aim);
-
-/**
- * most_register_interface - registers an interface with core
- * @iface: pointer to the instance of the interface description.
- *
- * Allocates and initializes a new interface instance and all of its channels.
- * Returns a pointer to kobject or an error pointer.
- */
-struct kobject *most_register_interface(struct most_interface *iface)
-{
- unsigned int i;
- int id;
- char name[STRING_SIZE];
- char channel_name[STRING_SIZE];
- struct most_c_obj *c;
- struct most_inst_obj *inst;
-
- if (!iface || !iface->enqueue || !iface->configure ||
- !iface->poison_channel || (iface->num_channels > MAX_CHANNELS)) {
- pr_err("Bad interface or channel overflow\n");
- return ERR_PTR(-EINVAL);
- }
-
- id = ida_simple_get(&mdev_id, 0, 0, GFP_KERNEL);
- if (id < 0) {
- pr_info("Failed to alloc mdev ID\n");
- return ERR_PTR(id);
- }
- snprintf(name, STRING_SIZE, "mdev%d", id);
-
- inst = create_most_inst_obj(name);
- if (!inst) {
- pr_info("Failed to allocate interface instance\n");
- ida_simple_remove(&mdev_id, id);
- return ERR_PTR(-ENOMEM);
- }
-
- iface->priv = inst;
- INIT_LIST_HEAD(&inst->channel_list);
- inst->iface = iface;
- inst->dev_id = id;
- list_add_tail(&inst->list, &instance_list);
-
- for (i = 0; i < iface->num_channels; i++) {
- const char *name_suffix = iface->channel_vector[i].name_suffix;
-
- if (!name_suffix)
- snprintf(channel_name, STRING_SIZE, "ch%d", i);
- else
- snprintf(channel_name, STRING_SIZE, "%s", name_suffix);
-
- /* this increments the reference count of this instance */
- c = create_most_c_obj(channel_name, &inst->kobj);
- if (!c)
- goto free_instance;
- inst->channel[i] = c;
- c->is_starving = 0;
- c->iface = iface;
- c->inst = inst;
- c->channel_id = i;
- c->keep_mbo = false;
- c->enqueue_halt = false;
- c->is_poisoned = false;
- c->cfg.direction = 0;
- c->cfg.data_type = 0;
- c->cfg.num_buffers = 0;
- c->cfg.buffer_size = 0;
- c->cfg.subbuffer_size = 0;
- c->cfg.packets_per_xact = 0;
- spin_lock_init(&c->fifo_lock);
- INIT_LIST_HEAD(&c->fifo);
- INIT_LIST_HEAD(&c->trash_fifo);
- INIT_LIST_HEAD(&c->halt_fifo);
- init_completion(&c->cleanup);
- atomic_set(&c->mbo_ref, 0);
- mutex_init(&c->start_mutex);
- mutex_init(&c->nq_mutex);
- list_add_tail(&c->list, &inst->channel_list);
- }
- pr_info("registered new MOST device mdev%d (%s)\n",
- inst->dev_id, iface->description);
- return &inst->kobj;
-
-free_instance:
- pr_info("Failed allocate channel(s)\n");
- list_del(&inst->list);
- ida_simple_remove(&mdev_id, id);
- destroy_most_inst_obj(inst);
- return ERR_PTR(-ENOMEM);
-}
-EXPORT_SYMBOL_GPL(most_register_interface);
-
-/**
- * most_deregister_interface - deregisters an interface with core
- * @iface: pointer to the interface instance description.
- *
- * Before removing an interface instance from the list, all running
- * channels are stopped and poisoned.
- */
-void most_deregister_interface(struct most_interface *iface)
-{
- struct most_inst_obj *i = iface->priv;
- struct most_c_obj *c;
-
- if (unlikely(!i)) {
- pr_info("Bad Interface\n");
- return;
- }
- pr_info("deregistering MOST device %s (%s)\n", i->kobj.name,
- iface->description);
-
- list_for_each_entry(c, &i->channel_list, list) {
- if (c->aim0.ptr)
- c->aim0.ptr->disconnect_channel(c->iface,
- c->channel_id);
- if (c->aim1.ptr)
- c->aim1.ptr->disconnect_channel(c->iface,
- c->channel_id);
- c->aim0.ptr = NULL;
- c->aim1.ptr = NULL;
- }
-
- ida_simple_remove(&mdev_id, i->dev_id);
- list_del(&i->list);
- destroy_most_inst_obj(i);
-}
-EXPORT_SYMBOL_GPL(most_deregister_interface);
-
-/**
- * most_stop_enqueue - prevents core from enqueueing MBOs
- * @iface: pointer to interface
- * @id: channel id
- *
- * This is called by an HDM that _cannot_ attend to its duties and
- * is imminent to get run over by the core. The core is not going to
- * enqueue any further packets unless the flagging HDM calls
- * most_resume enqueue().
- */
-void most_stop_enqueue(struct most_interface *iface, int id)
-{
- struct most_c_obj *c = get_channel_by_iface(iface, id);
-
- if (!c)
- return;
-
- mutex_lock(&c->nq_mutex);
- c->enqueue_halt = true;
- mutex_unlock(&c->nq_mutex);
-}
-EXPORT_SYMBOL_GPL(most_stop_enqueue);
-
-/**
- * most_resume_enqueue - allow core to enqueue MBOs again
- * @iface: pointer to interface
- * @id: channel id
- *
- * This clears the enqueue halt flag and enqueues all MBOs currently
- * sitting in the wait fifo.
- */
-void most_resume_enqueue(struct most_interface *iface, int id)
-{
- struct most_c_obj *c = get_channel_by_iface(iface, id);
-
- if (!c)
- return;
-
- mutex_lock(&c->nq_mutex);
- c->enqueue_halt = false;
- mutex_unlock(&c->nq_mutex);
-
- wake_up_interruptible(&c->hdm_fifo_wq);
-}
-EXPORT_SYMBOL_GPL(most_resume_enqueue);
-
-static int __init most_init(void)
-{
- int err;
-
- pr_info("init()\n");
- INIT_LIST_HEAD(&instance_list);
- INIT_LIST_HEAD(&aim_list);
- ida_init(&mdev_id);
-
- err = bus_register(&most_bus);
- if (err) {
- pr_info("Cannot register most bus\n");
- return err;
- }
-
- most_class = class_create(THIS_MODULE, "most");
- if (IS_ERR(most_class)) {
- pr_info("No udev support.\n");
- err = PTR_ERR(most_class);
- goto exit_bus;
- }
-
- err = driver_register(&mostcore);
- if (err) {
- pr_info("Cannot register core driver\n");
- goto exit_class;
- }
-
- core_dev = device_create(most_class, NULL, 0, NULL, "mostcore");
- if (IS_ERR(core_dev)) {
- err = PTR_ERR(core_dev);
- goto exit_driver;
- }
-
- most_aim_kset = kset_create_and_add("aims", NULL, &core_dev->kobj);
- if (!most_aim_kset) {
- err = -ENOMEM;
- goto exit_class_container;
- }
-
- most_inst_kset = kset_create_and_add("devices", NULL, &core_dev->kobj);
- if (!most_inst_kset) {
- err = -ENOMEM;
- goto exit_driver_kset;
- }
-
- return 0;
-
-exit_driver_kset:
- kset_unregister(most_aim_kset);
-exit_class_container:
- device_destroy(most_class, 0);
-exit_driver:
- driver_unregister(&mostcore);
-exit_class:
- class_destroy(most_class);
-exit_bus:
- bus_unregister(&most_bus);
- return err;
-}
-
-static void __exit most_exit(void)
-{
- struct most_inst_obj *i, *i_tmp;
- struct most_aim_obj *d, *d_tmp;
-
- pr_info("exit core module\n");
- list_for_each_entry_safe(d, d_tmp, &aim_list, list) {
- destroy_most_aim_obj(d);
- }
-
- list_for_each_entry_safe(i, i_tmp, &instance_list, list) {
- list_del(&i->list);
- destroy_most_inst_obj(i);
- }
- kset_unregister(most_inst_kset);
- kset_unregister(most_aim_kset);
- device_destroy(most_class, 0);
- driver_unregister(&mostcore);
- class_destroy(most_class);
- bus_unregister(&most_bus);
- ida_destroy(&mdev_id);
-}
-
-module_init(most_init);
-module_exit(most_exit);
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Christian Gromm <christian.gromm@microchip.com>");
-MODULE_DESCRIPTION("Core module of stacked MOST Linux driver");