Commit f13f6981 authored by Christian Gromm's avatar Christian Gromm Committed by Greg Kroah-Hartman

staging: most: fix channel operation in multi-aim context

This patch fixes the opening and closing process of a physical channel
when used by different AIMs.
Signed-off-by: default avatarAndrey Shvetsov <andrey.shvetsov@k2l.de>
Signed-off-by: default avatarChristian Gromm <christian.gromm@microchip.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9161e931
...@@ -27,6 +27,7 @@ static dev_t aim_devno; ...@@ -27,6 +27,7 @@ static dev_t aim_devno;
static struct class *aim_class; static struct class *aim_class;
static struct ida minor_id; static struct ida minor_id;
static unsigned int major; static unsigned int major;
static struct most_aim cdev_aim;
struct aim_channel { struct aim_channel {
wait_queue_head_t wq; wait_queue_head_t wq;
...@@ -96,7 +97,7 @@ static int aim_open(struct inode *inode, struct file *filp) ...@@ -96,7 +97,7 @@ static int aim_open(struct inode *inode, struct file *filp)
return -EBUSY; return -EBUSY;
} }
ret = most_start_channel(channel->iface, channel->channel_id); ret = most_start_channel(channel->iface, channel->channel_id, &cdev_aim);
if (ret) if (ret)
atomic_dec(&channel->access_ref); atomic_dec(&channel->access_ref);
return ret; return ret;
...@@ -134,7 +135,7 @@ static int aim_close(struct inode *inode, struct file *filp) ...@@ -134,7 +135,7 @@ static int aim_close(struct inode *inode, struct file *filp)
most_put_mbo(mbo); most_put_mbo(mbo);
if (channel->keep_mbo) if (channel->keep_mbo)
most_put_mbo(channel->stacked_mbo); most_put_mbo(channel->stacked_mbo);
ret = most_stop_channel(channel->iface, channel->channel_id); ret = most_stop_channel(channel->iface, channel->channel_id, &cdev_aim);
atomic_dec(&channel->access_ref); atomic_dec(&channel->access_ref);
wake_up_interruptible(&channel->wq); wake_up_interruptible(&channel->wq);
return ret; return ret;
......
...@@ -79,6 +79,7 @@ struct net_dev_context { ...@@ -79,6 +79,7 @@ struct net_dev_context {
static struct list_head net_devices = LIST_HEAD_INIT(net_devices); static struct list_head net_devices = LIST_HEAD_INIT(net_devices);
static struct spinlock list_lock; static struct spinlock list_lock;
static struct most_aim aim;
static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo) static int skb_to_mamac(const struct sk_buff *skb, struct mbo *mbo)
...@@ -194,14 +195,14 @@ static int most_nd_open(struct net_device *dev) ...@@ -194,14 +195,14 @@ static int most_nd_open(struct net_device *dev)
BUG_ON(!nd->tx.linked || !nd->rx.linked); BUG_ON(!nd->tx.linked || !nd->rx.linked);
if (most_start_channel(nd->iface, nd->rx.ch_id)) { if (most_start_channel(nd->iface, nd->rx.ch_id, &aim)) {
netdev_err(dev, "most_start_channel() failed\n"); netdev_err(dev, "most_start_channel() failed\n");
return -EBUSY; return -EBUSY;
} }
if (most_start_channel(nd->iface, nd->tx.ch_id)) { if (most_start_channel(nd->iface, nd->tx.ch_id, &aim)) {
netdev_err(dev, "most_start_channel() failed\n"); netdev_err(dev, "most_start_channel() failed\n");
most_stop_channel(nd->iface, nd->rx.ch_id); most_stop_channel(nd->iface, nd->rx.ch_id, &aim);
return -EBUSY; return -EBUSY;
} }
...@@ -227,8 +228,8 @@ static int most_nd_stop(struct net_device *dev) ...@@ -227,8 +228,8 @@ static int most_nd_stop(struct net_device *dev)
netif_stop_queue(dev); netif_stop_queue(dev);
if (nd->channels_opened) { if (nd->channels_opened) {
most_stop_channel(nd->iface, nd->rx.ch_id); most_stop_channel(nd->iface, nd->rx.ch_id, &aim);
most_stop_channel(nd->iface, nd->tx.ch_id); most_stop_channel(nd->iface, nd->tx.ch_id, &aim);
nd->channels_opened = false; nd->channels_opened = false;
} }
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#define DRIVER_NAME "sound" #define DRIVER_NAME "sound"
static struct list_head dev_list; static struct list_head dev_list;
static struct most_aim audio_aim;
/** /**
* struct channel - private structure to keep channel specific data * struct channel - private structure to keep channel specific data
...@@ -298,7 +299,7 @@ static int pcm_open(struct snd_pcm_substream *substream) ...@@ -298,7 +299,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
return PTR_ERR(channel->playback_task); return PTR_ERR(channel->playback_task);
} }
if (most_start_channel(channel->iface, channel->id)) { if (most_start_channel(channel->iface, channel->id, &audio_aim)) {
pr_err("most_start_channel() failed!\n"); pr_err("most_start_channel() failed!\n");
if (cfg->direction == MOST_CH_TX) if (cfg->direction == MOST_CH_TX)
kthread_stop(channel->playback_task); kthread_stop(channel->playback_task);
...@@ -333,7 +334,7 @@ static int pcm_close(struct snd_pcm_substream *substream) ...@@ -333,7 +334,7 @@ static int pcm_close(struct snd_pcm_substream *substream)
if (channel->cfg->direction == MOST_CH_TX) if (channel->cfg->direction == MOST_CH_TX)
kthread_stop(channel->playback_task); kthread_stop(channel->playback_task);
most_stop_channel(channel->iface, channel->id); most_stop_channel(channel->iface, channel->id, &audio_aim);
return 0; return 0;
} }
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define V4L2_AIM_MAX_INPUT 1 #define V4L2_AIM_MAX_INPUT 1
static struct most_aim aim_info;
struct most_video_dev { struct most_video_dev {
struct most_interface *iface; struct most_interface *iface;
...@@ -107,7 +108,7 @@ static int aim_vdev_open(struct file *filp) ...@@ -107,7 +108,7 @@ static int aim_vdev_open(struct file *filp)
v4l2_fh_add(&fh->fh); v4l2_fh_add(&fh->fh);
ret = most_start_channel(mdev->iface, mdev->ch_idx); ret = most_start_channel(mdev->iface, mdev->ch_idx, &aim_info);
if (ret) { if (ret) {
pr_err("most_start_channel() failed\n"); pr_err("most_start_channel() failed\n");
goto err_rm; goto err_rm;
...@@ -151,7 +152,7 @@ static int aim_vdev_close(struct file *filp) ...@@ -151,7 +152,7 @@ static int aim_vdev_close(struct file *filp)
spin_lock(&mdev->list_lock); spin_lock(&mdev->list_lock);
} }
spin_unlock(&mdev->list_lock); spin_unlock(&mdev->list_lock);
most_stop_channel(mdev->iface, mdev->ch_idx); most_stop_channel(mdev->iface, mdev->ch_idx, &aim_info);
mdev->mute = false; mdev->mute = false;
v4l2_fh_del(&fh->fh); v4l2_fh_del(&fh->fh);
......
...@@ -44,7 +44,7 @@ struct most_c_obj { ...@@ -44,7 +44,7 @@ struct most_c_obj {
atomic_t mbo_nq_level; atomic_t mbo_nq_level;
uint16_t channel_id; uint16_t channel_id;
bool is_poisoned; bool is_poisoned;
bool is_started; struct mutex start_mutex;
int is_starving; int is_starving;
struct most_interface *iface; struct most_interface *iface;
struct most_inst_obj *inst; struct most_inst_obj *inst;
...@@ -57,6 +57,8 @@ struct most_c_obj { ...@@ -57,6 +57,8 @@ struct most_c_obj {
struct list_head list; struct list_head list;
struct most_aim *first_aim; struct most_aim *first_aim;
struct most_aim *second_aim; struct most_aim *second_aim;
int first_aim_refs;
int second_aim_refs;
struct list_head trash_fifo; struct list_head trash_fifo;
struct task_struct *hdm_enqueue_task; struct task_struct *hdm_enqueue_task;
struct mutex stop_task_mutex; struct mutex stop_task_mutex;
...@@ -1234,10 +1236,11 @@ static void arm_mbo(struct mbo *mbo) ...@@ -1234,10 +1236,11 @@ static void arm_mbo(struct mbo *mbo)
list_add_tail(&mbo->list, &c->fifo); list_add_tail(&mbo->list, &c->fifo);
spin_unlock_irqrestore(&c->fifo_lock, flags); spin_unlock_irqrestore(&c->fifo_lock, flags);
if (c->second_aim && c->second_aim->tx_completion) if (c->first_aim_refs && c->first_aim->tx_completion)
c->second_aim->tx_completion(c->iface, c->channel_id);
if (c->first_aim && c->first_aim->tx_completion)
c->first_aim->tx_completion(c->iface, c->channel_id); c->first_aim->tx_completion(c->iface, c->channel_id);
if (c->second_aim_refs && c->second_aim->tx_completion)
c->second_aim->tx_completion(c->iface, c->channel_id);
} }
/** /**
...@@ -1441,11 +1444,12 @@ EXPORT_SYMBOL_GPL(most_put_mbo); ...@@ -1441,11 +1444,12 @@ EXPORT_SYMBOL_GPL(most_put_mbo);
*/ */
static void most_read_completion(struct mbo *mbo) static void most_read_completion(struct mbo *mbo)
{ {
struct most_c_obj *c; struct most_c_obj *c = mbo->context;
c = mbo->context; if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) {
if (unlikely(c->is_poisoned || (mbo->status == MBO_E_CLOSE))) trash_mbo(mbo);
goto release_mbo; return;
}
if (mbo->status == MBO_E_INVAL) { if (mbo->status == MBO_E_INVAL) {
nq_hdm_mbo(mbo); nq_hdm_mbo(mbo);
...@@ -1458,16 +1462,15 @@ static void most_read_completion(struct mbo *mbo) ...@@ -1458,16 +1462,15 @@ static void most_read_completion(struct mbo *mbo)
c->is_starving = 1; c->is_starving = 1;
} }
if (c->first_aim && c->first_aim->rx_completion && if (c->first_aim_refs && c->first_aim->rx_completion &&
c->first_aim->rx_completion(mbo) == 0) c->first_aim->rx_completion(mbo) == 0)
return; return;
if (c->second_aim && c->second_aim->rx_completion &&
if (c->second_aim_refs && c->second_aim->rx_completion &&
c->second_aim->rx_completion(mbo) == 0) c->second_aim->rx_completion(mbo) == 0)
return; return;
pr_info("WARN: no driver linked with this channel\n");
mbo->status = MBO_E_CLOSE; most_put_mbo(mbo);
release_mbo:
trash_mbo(mbo);
} }
/** /**
...@@ -1480,7 +1483,8 @@ static void most_read_completion(struct mbo *mbo) ...@@ -1480,7 +1483,8 @@ static void most_read_completion(struct mbo *mbo)
* *
* Returns 0 on success or error code otherwise. * Returns 0 on success or error code otherwise.
*/ */
int most_start_channel(struct most_interface *iface, int id) int most_start_channel(struct most_interface *iface, int id,
struct most_aim *aim)
{ {
int num_buffer; int num_buffer;
int ret; int ret;
...@@ -1489,11 +1493,13 @@ int most_start_channel(struct most_interface *iface, int id) ...@@ -1489,11 +1493,13 @@ int most_start_channel(struct most_interface *iface, int id)
if (unlikely(!c)) if (unlikely(!c))
return -EINVAL; return -EINVAL;
if (c->is_started) mutex_lock(&c->start_mutex);
return -EBUSY; if (c->first_aim_refs + c->second_aim_refs > 0)
goto out; /* already started by other aim */
if (!try_module_get(iface->mod)) { if (!try_module_get(iface->mod)) {
pr_info("failed to acquire HDM lock\n"); pr_info("failed to acquire HDM lock\n");
mutex_unlock(&c->start_mutex);
return -ENOLCK; return -ENOLCK;
} }
modref++; modref++;
...@@ -1523,14 +1529,22 @@ int most_start_channel(struct most_interface *iface, int id) ...@@ -1523,14 +1529,22 @@ int most_start_channel(struct most_interface *iface, int id)
if (ret) if (ret)
goto error; goto error;
c->is_started = true;
c->is_starving = 0; c->is_starving = 0;
atomic_set(&c->mbo_ref, num_buffer); atomic_set(&c->mbo_ref, num_buffer);
out:
if (aim == c->first_aim)
c->first_aim_refs++;
if (aim == c->second_aim)
c->second_aim_refs++;
mutex_unlock(&c->start_mutex);
return 0; return 0;
error: error:
if (iface->mod) if (iface->mod)
module_put(iface->mod); module_put(iface->mod);
modref--; modref--;
mutex_unlock(&c->start_mutex);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(most_start_channel); EXPORT_SYMBOL_GPL(most_start_channel);
...@@ -1540,7 +1554,8 @@ EXPORT_SYMBOL_GPL(most_start_channel); ...@@ -1540,7 +1554,8 @@ EXPORT_SYMBOL_GPL(most_start_channel);
* @iface: pointer to interface instance * @iface: pointer to interface instance
* @id: channel ID * @id: channel ID
*/ */
int most_stop_channel(struct most_interface *iface, int id) int most_stop_channel(struct most_interface *iface, int id,
struct most_aim *aim)
{ {
struct most_c_obj *c; struct most_c_obj *c;
...@@ -1552,8 +1567,9 @@ int most_stop_channel(struct most_interface *iface, int id) ...@@ -1552,8 +1567,9 @@ int most_stop_channel(struct most_interface *iface, int id)
if (unlikely(!c)) if (unlikely(!c))
return -EINVAL; return -EINVAL;
if (!c->is_started) mutex_lock(&c->start_mutex);
return 0; if (c->first_aim_refs + c->second_aim_refs >= 2)
goto out;
mutex_lock(&c->stop_task_mutex); mutex_lock(&c->stop_task_mutex);
if (c->hdm_enqueue_task) if (c->hdm_enqueue_task)
...@@ -1564,6 +1580,7 @@ int most_stop_channel(struct most_interface *iface, int id) ...@@ -1564,6 +1580,7 @@ int most_stop_channel(struct most_interface *iface, int id)
mutex_lock(&deregister_mutex); mutex_lock(&deregister_mutex);
if (atomic_read(&c->inst->tainted)) { if (atomic_read(&c->inst->tainted)) {
mutex_unlock(&deregister_mutex); mutex_unlock(&deregister_mutex);
mutex_unlock(&c->start_mutex);
return -ENODEV; return -ENODEV;
} }
mutex_unlock(&deregister_mutex); mutex_unlock(&deregister_mutex);
...@@ -1577,6 +1594,7 @@ int most_stop_channel(struct most_interface *iface, int id) ...@@ -1577,6 +1594,7 @@ int most_stop_channel(struct most_interface *iface, int id)
if (c->iface->poison_channel(c->iface, c->channel_id)) { if (c->iface->poison_channel(c->iface, c->channel_id)) {
pr_err("Cannot stop channel %d of mdev %s\n", c->channel_id, pr_err("Cannot stop channel %d of mdev %s\n", c->channel_id,
c->iface->description); c->iface->description);
mutex_unlock(&c->start_mutex);
return -EAGAIN; return -EAGAIN;
} }
flush_trash_fifo(c); flush_trash_fifo(c);
...@@ -1585,13 +1603,20 @@ int most_stop_channel(struct most_interface *iface, int id) ...@@ -1585,13 +1603,20 @@ int most_stop_channel(struct most_interface *iface, int id)
#ifdef CMPL_INTERRUPTIBLE #ifdef CMPL_INTERRUPTIBLE
if (wait_for_completion_interruptible(&c->cleanup)) { if (wait_for_completion_interruptible(&c->cleanup)) {
pr_info("Interrupted while clean up ch %d\n", c->channel_id); pr_info("Interrupted while clean up ch %d\n", c->channel_id);
mutex_unlock(&c->start_mutex);
return -EINTR; return -EINTR;
} }
#else #else
wait_for_completion(&c->cleanup); wait_for_completion(&c->cleanup);
#endif #endif
c->is_poisoned = false; c->is_poisoned = false;
c->is_started = false;
out:
if (aim == c->first_aim)
c->first_aim_refs--;
if (aim == c->second_aim)
c->second_aim_refs--;
mutex_unlock(&c->start_mutex);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(most_stop_channel); EXPORT_SYMBOL_GPL(most_stop_channel);
...@@ -1725,7 +1750,6 @@ struct kobject *most_register_interface(struct most_interface *iface) ...@@ -1725,7 +1750,6 @@ struct kobject *most_register_interface(struct most_interface *iface)
c->keep_mbo = false; c->keep_mbo = false;
c->enqueue_halt = false; c->enqueue_halt = false;
c->is_poisoned = false; c->is_poisoned = false;
c->is_started = false;
c->cfg.direction = 0; c->cfg.direction = 0;
c->cfg.data_type = 0; c->cfg.data_type = 0;
c->cfg.num_buffers = 0; c->cfg.num_buffers = 0;
...@@ -1738,6 +1762,7 @@ struct kobject *most_register_interface(struct most_interface *iface) ...@@ -1738,6 +1762,7 @@ struct kobject *most_register_interface(struct most_interface *iface)
INIT_LIST_HEAD(&c->halt_fifo); INIT_LIST_HEAD(&c->halt_fifo);
init_completion(&c->cleanup); init_completion(&c->cleanup);
atomic_set(&c->mbo_ref, 0); atomic_set(&c->mbo_ref, 0);
mutex_init(&c->start_mutex);
mutex_init(&c->stop_task_mutex); mutex_init(&c->stop_task_mutex);
list_add_tail(&c->list, &inst->channel_list); list_add_tail(&c->list, &inst->channel_list);
} }
...@@ -1784,7 +1809,7 @@ void most_deregister_interface(struct most_interface *iface) ...@@ -1784,7 +1809,7 @@ void most_deregister_interface(struct most_interface *iface)
} }
list_for_each_entry(c, &i->channel_list, list) { list_for_each_entry(c, &i->channel_list, list) {
if (!c->is_started) if (c->first_aim_refs + c->second_aim_refs <= 0)
continue; continue;
mutex_lock(&c->stop_task_mutex); mutex_lock(&c->stop_task_mutex);
......
...@@ -309,8 +309,10 @@ int most_register_aim(struct most_aim *aim); ...@@ -309,8 +309,10 @@ int most_register_aim(struct most_aim *aim);
int most_deregister_aim(struct most_aim *aim); int most_deregister_aim(struct most_aim *aim);
struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx); struct mbo *most_get_mbo(struct most_interface *iface, int channel_idx);
void most_put_mbo(struct mbo *mbo); void most_put_mbo(struct mbo *mbo);
int most_start_channel(struct most_interface *iface, int channel_idx); int most_start_channel(struct most_interface *iface, int channel_idx,
int most_stop_channel(struct most_interface *iface, int channel_idx); struct most_aim *);
int most_stop_channel(struct most_interface *iface, int channel_idx,
struct most_aim *);
#endif /* MOST_CORE_H_ */ #endif /* MOST_CORE_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment