// 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. Load more
// 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; }