1497 lines · 35.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * core.c - Implementation of core module of MOST Linux driver stack * * Copyright (C) 2013-2020 Microchip Technology Germany II GmbH & Co. KG */ #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 <linux/most.h> #define MAX_CHANNELS	64#define STRING_SIZE	80 static struct ida mdev_id;static int dummy_num_buffers;static struct list_head comp_list; struct pipe {	struct most_component *comp;	int refs;	int num_buffers;}; struct most_channel {	struct device dev;	struct completion cleanup;	atomic_t mbo_ref;	atomic_t mbo_nq_level;	u16 channel_id;	char name[STRING_SIZE];	bool is_poisoned;	struct mutex start_mutex; /* channel activation synchronization */	struct mutex nq_mutex; /* nq thread synchronization */	int is_starving;	struct most_interface *iface;	struct most_channel_config cfg;	bool keep_mbo;	bool enqueue_halt;	struct list_head fifo;	spinlock_t fifo_lock; /* fifo access synchronization */	struct list_head halt_fifo;	struct list_head list;	struct pipe pipe0;	struct pipe pipe1;	struct list_head trash_fifo;	struct task_struct *hdm_enqueue_task;	wait_queue_head_t hdm_fifo_wq; }; #define to_channel(d) container_of(d, struct most_channel, dev) struct interface_private {	int dev_id;	char name[STRING_SIZE];	struct most_channel *channel[MAX_CHANNELS];	struct list_head channel_list;}; static const struct {	int most_ch_data_type;	const char *name;} ch_data_type[] = {	{ MOST_CH_CONTROL, "control" },	{ MOST_CH_ASYNC, "async" },	{ MOST_CH_SYNC, "sync" },	{ MOST_CH_ISOC, "isoc"},	{ MOST_CH_ISOC, "isoc_avp"},}; /** * 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;								\}) /** * most_free_mbo_coherent - free an MBO and its coherent buffer * @mbo: most buffer */static void most_free_mbo_coherent(struct mbo *mbo){	struct most_channel *c = mbo->context;	u16 const coherent_buf_size = c->cfg.buffer_size + c->cfg.extra_len; 	if (c->iface->dma_free)		c->iface->dma_free(mbo, coherent_buf_size);	else		kfree(mbo->virt_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_channel *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))))		dev_warn(&c->dev, "Channel or trash fifo not empty\n");} /** * flush_trash_fifo - clear the trash fifo * @c: pointer to channel object */static int flush_trash_fifo(struct most_channel *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;} static ssize_t available_directions_show(struct device *dev,					 struct device_attribute *attr,					 char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,					struct device_attribute *attr,					char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,					     struct device_attribute *attr,					     char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,					     struct device_attribute *attr,					     char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,					  struct device_attribute *attr,					  char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,					  struct device_attribute *attr,					  char *buf){	struct most_channel *c = to_channel(dev);	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 device *dev,				     struct device_attribute *attr,				     char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->is_starving);} static ssize_t set_number_of_buffers_show(struct device *dev,					  struct device_attribute *attr,					  char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.num_buffers);} static ssize_t set_buffer_size_show(struct device *dev,				    struct device_attribute *attr,				    char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.buffer_size);} static ssize_t set_direction_show(struct device *dev,				  struct device_attribute *attr,				  char *buf){	struct most_channel *c = to_channel(dev); 	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_datatype_show(struct device *dev,				 struct device_attribute *attr,				 char *buf){	int i;	struct most_channel *c = to_channel(dev); 	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, "%s",					ch_data_type[i].name);	}	return snprintf(buf, PAGE_SIZE, "unconfigured\n");} static ssize_t set_subbuffer_size_show(struct device *dev,				       struct device_attribute *attr,				       char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.subbuffer_size);} static ssize_t set_packets_per_xact_show(struct device *dev,					 struct device_attribute *attr,					 char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.packets_per_xact);} static ssize_t set_dbr_size_show(struct device *dev,				 struct device_attribute *attr, char *buf){	struct most_channel *c = to_channel(dev); 	return snprintf(buf, PAGE_SIZE, "%d\n", c->cfg.dbr_size);} #define to_dev_attr(a) container_of(a, struct device_attribute, attr)static umode_t channel_attr_is_visible(struct kobject *kobj,				       struct attribute *attr, int index){	struct device_attribute *dev_attr = to_dev_attr(attr);	struct device *dev = kobj_to_dev(kobj);	struct most_channel *c = to_channel(dev); 	if (!strcmp(dev_attr->attr.name, "set_dbr_size") &&	    (c->iface->interface != ITYPE_MEDIALB_DIM2))		return 0;	if (!strcmp(dev_attr->attr.name, "set_packets_per_xact") &&	    (c->iface->interface != ITYPE_USB))		return 0; 	return attr->mode;} #define DEV_ATTR(_name)  (&dev_attr_##_name.attr) static DEVICE_ATTR_RO(available_directions);static DEVICE_ATTR_RO(available_datatypes);static DEVICE_ATTR_RO(number_of_packet_buffers);static DEVICE_ATTR_RO(number_of_stream_buffers);static DEVICE_ATTR_RO(size_of_stream_buffer);static DEVICE_ATTR_RO(size_of_packet_buffer);static DEVICE_ATTR_RO(channel_starving);static DEVICE_ATTR_RO(set_buffer_size);static DEVICE_ATTR_RO(set_number_of_buffers);static DEVICE_ATTR_RO(set_direction);static DEVICE_ATTR_RO(set_datatype);static DEVICE_ATTR_RO(set_subbuffer_size);static DEVICE_ATTR_RO(set_packets_per_xact);static DEVICE_ATTR_RO(set_dbr_size); static struct attribute *channel_attrs[] = {	DEV_ATTR(available_directions),	DEV_ATTR(available_datatypes),	DEV_ATTR(number_of_packet_buffers),	DEV_ATTR(number_of_stream_buffers),	DEV_ATTR(size_of_stream_buffer),	DEV_ATTR(size_of_packet_buffer),	DEV_ATTR(channel_starving),	DEV_ATTR(set_buffer_size),	DEV_ATTR(set_number_of_buffers),	DEV_ATTR(set_direction),	DEV_ATTR(set_datatype),	DEV_ATTR(set_subbuffer_size),	DEV_ATTR(set_packets_per_xact),	DEV_ATTR(set_dbr_size),	NULL,}; static const struct attribute_group channel_attr_group = {	.attrs = channel_attrs,	.is_visible = channel_attr_is_visible,}; static const struct attribute_group *channel_attr_groups[] = {	&channel_attr_group,	NULL,}; static ssize_t description_show(struct device *dev,				struct device_attribute *attr,				char *buf){	struct most_interface *iface = dev_get_drvdata(dev); 	return snprintf(buf, PAGE_SIZE, "%s\n", iface->description);} static ssize_t interface_show(struct device *dev,			      struct device_attribute *attr,			      char *buf){	struct most_interface *iface = dev_get_drvdata(dev); 	switch (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 DEVICE_ATTR_RO(description);static DEVICE_ATTR_RO(interface); static struct attribute *interface_attrs[] = {	DEV_ATTR(description),	DEV_ATTR(interface),	NULL,}; static const struct attribute_group interface_attr_group = {	.attrs = interface_attrs,}; static const struct attribute_group *interface_attr_groups[] = {	&interface_attr_group,	NULL,}; static struct most_component *match_component(char *name){	struct most_component *comp; 	list_for_each_entry(comp, &comp_list, list) {		if (!strcmp(comp->name, name))			return comp;	}	return NULL;} struct show_links_data {	int offs;	char *buf;}; static int print_links(struct device *dev, void *data){	struct show_links_data *d = data;	int offs = d->offs;	char *buf = d->buf;	struct most_channel *c;	struct most_interface *iface = dev_get_drvdata(dev); 	list_for_each_entry(c, &iface->p->channel_list, list) {		if (c->pipe0.comp) {			offs += scnprintf(buf + offs,					 PAGE_SIZE - offs,					 "%s:%s:%s\n",					 c->pipe0.comp->name,					 dev_name(iface->dev),					 dev_name(&c->dev));		}		if (c->pipe1.comp) {			offs += scnprintf(buf + offs,					 PAGE_SIZE - offs,					 "%s:%s:%s\n",					 c->pipe1.comp->name,					 dev_name(iface->dev),					 dev_name(&c->dev));		}	}	d->offs = offs;	return 0;} static int most_match(struct device *dev, const struct device_driver *drv){	if (!strcmp(dev_name(dev), "most"))		return 0;	else		return 1;}

Lines 1–500 of 1497.