[PATCH 21/22] xlink-core: add async channel and events
From: mgross
Date: Mon Nov 30 2020 - 18:11:29 EST
From: Seamus Kelly <seamus.kelly@xxxxxxxxx>
Enable asynchronous channel and event communication.
Add APIs:
data ready callback:
The xLink Data Ready Callback function is used to
register a callback function that is invoked when data
is ready to be read from a channel
data consumed callback:
The xLink Data Consumed Callback function is used to
register a callback function that is invoked when data
is consumed by the peer node on a channel
Add event notification handling including APIs:
register device event:
The xLink Register Device Event function is used to
register a callback for notification of certain system
events. Currently XLink supports 4 such events [0-3]
whose meaning is system dependent. Registering for an
event means that the callback will be called when the
event occurs with 2 parameters the sw_device_id of the
device that triggered the event and the event number [0-3]
unregister device event
The xLink Unregister Device Event function is used to
unregister events that have previously been registered
by register device event API
Cc: Arnd Bergmann <arnd@xxxxxxxx>
Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
Reviewed-by: Mark Gross <mgross@xxxxxxxxxxxxxxx>
Signed-off-by: Seamus Kelly <seamus.kelly@xxxxxxxxx>
---
drivers/misc/xlink-core/xlink-core.c | 422 +++++++++++++++++++-
drivers/misc/xlink-core/xlink-defs.h | 4 +
drivers/misc/xlink-core/xlink-dispatcher.c | 53 ++-
drivers/misc/xlink-core/xlink-multiplexer.c | 176 +++++---
drivers/misc/xlink-core/xlink-platform.c | 27 ++
include/linux/xlink.h | 15 +-
6 files changed, 609 insertions(+), 88 deletions(-)
diff --git a/drivers/misc/xlink-core/xlink-core.c b/drivers/misc/xlink-core/xlink-core.c
index a600068840d3..63f65def8aa9 100644
--- a/drivers/misc/xlink-core/xlink-core.c
+++ b/drivers/misc/xlink-core/xlink-core.c
@@ -62,6 +62,16 @@ static enum xlink_error xlink_write_volatile_user(struct xlink_handle *handle,
static enum xlink_error do_xlink_write_volatile(struct xlink_handle *handle,
u16 chan, u8 const *message,
u32 size, u32 user_flag);
+static enum xlink_error xlink_register_device_event_user(struct xlink_handle *handle,
+ u32 *event_list,
+ u32 num_events,
+ xlink_device_event_cb event_notif_fn);
+static enum xlink_error do_xlink_register_device_event(struct xlink_handle *handle,
+ u32 *event_list,
+ u32 num_events,
+ xlink_device_event_cb event_notif_fn,
+ u32 user_flag);
+static struct mutex dev_event_lock;
static const struct file_operations fops = {
.owner = THIS_MODULE,
@@ -74,14 +84,81 @@ struct xlink_link {
struct kref refcount;
};
+struct xlink_attr {
+ unsigned long value;
+ u32 sw_dev_id;
+};
+
struct keembay_xlink_dev {
struct platform_device *pdev;
struct xlink_link links[XLINK_MAX_CONNECTIONS];
u32 nmb_connected_links;
struct mutex lock; // protect access to xlink_dev
+ struct xlink_attr eventx[4];
+};
+
+struct event_info {
+ struct list_head list;
+ u32 sw_device_id;
+ u32 event_type;
+ u32 user_flag;
+ xlink_device_event_cb event_notif_fn;
};
static u8 volbuf[XLINK_MAX_BUF_SIZE]; // buffer for volatile transactions
+#define NUM_REG_EVENTS 4
+
+// sysfs attribute functions
+
+static ssize_t event0_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct keembay_xlink_dev *xlink_dev = dev_get_drvdata(dev);
+ struct xlink_attr *a = &xlink_dev->eventx[0];
+
+ return scnprintf(buf, PAGE_SIZE, "0x%x 0x%lx\n", a->sw_dev_id, a->value);
+}
+
+static ssize_t event1_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct keembay_xlink_dev *xlink_dev = dev_get_drvdata(dev);
+ struct xlink_attr *a = &xlink_dev->eventx[1];
+
+ return scnprintf(buf, PAGE_SIZE, "0x%x 0x%lx\n", a->sw_dev_id, a->value);
+}
+
+static ssize_t event2_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct keembay_xlink_dev *xlink_dev = dev_get_drvdata(dev);
+ struct xlink_attr *a = &xlink_dev->eventx[2];
+
+ return scnprintf(buf, PAGE_SIZE, "0x%x 0x%lx\n", a->sw_dev_id, a->value);
+}
+
+static ssize_t event3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct keembay_xlink_dev *xlink_dev = dev_get_drvdata(dev);
+ struct xlink_attr *a = &xlink_dev->eventx[3];
+
+ return scnprintf(buf, PAGE_SIZE, "0x%x 0x%lx\n", a->sw_dev_id, a->value);
+}
+
+static DEVICE_ATTR_RO(event0);
+static DEVICE_ATTR_RO(event1);
+static DEVICE_ATTR_RO(event2);
+static DEVICE_ATTR_RO(event3);
+static struct attribute *xlink_sysfs_entries[] = {
+ &dev_attr_event0.attr,
+ &dev_attr_event1.attr,
+ &dev_attr_event2.attr,
+ &dev_attr_event3.attr,
+ NULL,
+};
+
+static const struct attribute_group xlink_sysfs_group = {
+ .attrs = xlink_sysfs_entries,
+};
+
+static struct event_info ev_info;
/*
* global variable pointing to our xlink device.
@@ -215,7 +292,14 @@ static int kmb_xlink_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Cannot add the device to the system\n");
goto r_class;
}
+ INIT_LIST_HEAD(&ev_info.list);
+ rc = devm_device_add_group(&pdev->dev, &xlink_sysfs_group);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to create sysfs entries: %d\n", rc);
+ return rc;
+ }
+ mutex_init(&dev_event_lock);
return 0;
r_device:
@@ -239,7 +323,6 @@ static int kmb_xlink_remove(struct platform_device *pdev)
rc = xlink_multiplexer_destroy();
if (rc != X_LINK_SUCCESS)
pr_err("Multiplexer destroy failed\n");
- // stop dispatchers and destroy
rc = xlink_dispatcher_destroy();
if (rc != X_LINK_SUCCESS)
pr_err("Dispatcher destroy failed\n");
@@ -270,19 +353,23 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct xlinkconnect con = {0};
struct xlinkrelease rel = {0};
struct xlinkstartvpu startvpu = {0};
+ struct xlinkcallback cb = {0};
struct xlinkgetdevicename devn = {0};
struct xlinkgetdevicelist devl = {0};
struct xlinkgetdevicestatus devs = {0};
struct xlinkbootdevice boot = {0};
struct xlinkresetdevice res = {0};
struct xlinkdevmode devm = {0};
+ struct xlinkregdevevent regdevevent = {0};
u32 sw_device_id_list[XLINK_MAX_DEVICE_LIST_SIZE];
char name[XLINK_MAX_DEVICE_NAME_SIZE];
int interface = NULL_INTERFACE;
u32 device_status = 0;
u32 num_devices = 0;
u32 device_mode = 0;
+ u32 num_events = 0;
char filename[64];
+ u32 *ev_list;
u8 reladdr;
u8 *rdaddr;
u32 size;
@@ -318,6 +405,30 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_to_user((void __user *)op.return_code, (void *)&rc, sizeof(rc)))
return -EFAULT;
break;
+ case XL_DATA_READY_CALLBACK:
+ if (copy_from_user(&cb, (void __user *)arg,
+ sizeof(struct xlinkcallback)))
+ return -EFAULT;
+ if (copy_from_user(&devh, (void __user *)cb.handle,
+ sizeof(struct xlink_handle)))
+ return -EFAULT;
+ CHANNEL_SET_USER_BIT(cb.chan); // set MSbit for user space call
+ rc = xlink_data_available_event(&devh, cb.chan, cb.callback);
+ if (copy_to_user((void __user *)cb.return_code, (void *)&rc, sizeof(rc)))
+ return -EFAULT;
+ break;
+ case XL_DATA_CONSUMED_CALLBACK:
+ if (copy_from_user(&cb, (void __user *)arg,
+ sizeof(struct xlinkcallback)))
+ return -EFAULT;
+ if (copy_from_user(&devh, (void __user *)cb.handle,
+ sizeof(struct xlink_handle)))
+ return -EFAULT;
+ CHANNEL_SET_USER_BIT(cb.chan); // set MSbit for user space call
+ rc = xlink_data_consumed_event(&devh, cb.chan, cb.callback);
+ if (copy_to_user((void __user *)cb.return_code, (void *)&rc, sizeof(rc)))
+ return -EFAULT;
+ break;
case XL_READ_DATA:
if (copy_from_user(&rd, (void __user *)arg,
sizeof(struct xlinkreaddata)))
@@ -598,6 +709,61 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_to_user((void __user *)devm.return_code, (void *)&rc, sizeof(rc)))
return -EFAULT;
break;
+ case XL_REGISTER_DEV_EVENT:
+ if (copy_from_user(®devevent, (void __user *)arg,
+ sizeof(struct xlinkregdevevent)))
+ return -EFAULT;
+ if (copy_from_user(&devh, (void __user *)regdevevent.handle,
+ sizeof(struct xlink_handle)))
+ return -EFAULT;
+ num_events = regdevevent.num_events;
+ if (num_events > 0 && num_events <= NUM_REG_EVENTS) {
+ ev_list = kzalloc((num_events * sizeof(u32)), GFP_KERNEL);
+ if (ev_list) {
+ if (copy_from_user(ev_list,
+ (void __user *)regdevevent.event_list,
+ (num_events * sizeof(u32)))) {
+ kfree(ev_list);
+ return -EFAULT;
+ }
+ rc = xlink_register_device_event_user(&devh,
+ ev_list,
+ num_events,
+ NULL);
+ kfree(ev_list);
+ } else {
+ rc = X_LINK_ERROR;
+ }
+ } else {
+ rc = X_LINK_ERROR;
+ }
+ if (copy_to_user((void __user *)regdevevent.return_code, (void *)&rc, sizeof(rc)))
+ return -EFAULT;
+ break;
+ case XL_UNREGISTER_DEV_EVENT:
+ if (copy_from_user(®devevent, (void __user *)arg,
+ sizeof(struct xlinkregdevevent)))
+ return -EFAULT;
+ if (copy_from_user(&devh, (void __user *)regdevevent.handle,
+ sizeof(struct xlink_handle)))
+ return -EFAULT;
+ num_events = regdevevent.num_events;
+ if (num_events <= NUM_REG_EVENTS) {
+ ev_list = kzalloc((num_events * sizeof(u32)), GFP_KERNEL);
+ if (copy_from_user(ev_list,
+ (void __user *)regdevevent.event_list,
+ (num_events * sizeof(u32)))) {
+ kfree(ev_list);
+ return -EFAULT;
+ }
+ rc = xlink_unregister_device_event(&devh, ev_list, num_events);
+ kfree(ev_list);
+ } else {
+ rc = X_LINK_ERROR;
+ }
+ if (copy_to_user((void __user *)regdevevent.return_code, (void *)&rc, sizeof(rc)))
+ return -EFAULT;
+ break;
}
if (rc)
return -EIO;
@@ -671,14 +837,12 @@ enum xlink_error xlink_connect(struct xlink_handle *handle)
xlink->nmb_connected_links++;
kref_init(&link->refcount);
if (interface != IPC_INTERFACE) {
- // start dispatcher
rc = xlink_dispatcher_start(link->id, &link->handle);
if (rc) {
pr_err("dispatcher start failed\n");
goto r_cleanup;
}
}
- // initialize multiplexer connection
rc = xlink_multiplexer_connect(link->id);
if (rc) {
pr_err("multiplexer connect failed\n");
@@ -689,7 +853,6 @@ enum xlink_error xlink_connect(struct xlink_handle *handle)
link->handle.dev_type,
xlink->nmb_connected_links);
} else {
- // already connected
pr_info("dev 0x%x ALREADY connected - dev_type %d\n",
link->handle.sw_device_id,
link->handle.dev_type);
@@ -697,7 +860,6 @@ enum xlink_error xlink_connect(struct xlink_handle *handle)
*handle = link->handle;
}
mutex_unlock(&xlink->lock);
- // TODO: implement ping
return X_LINK_SUCCESS;
r_cleanup:
@@ -707,6 +869,78 @@ enum xlink_error xlink_connect(struct xlink_handle *handle)
}
EXPORT_SYMBOL(xlink_connect);
+enum xlink_error xlink_data_available_event(struct xlink_handle *handle,
+ u16 chan,
+ xlink_event data_available_event)
+{
+ struct xlink_event *event;
+ struct xlink_link *link;
+ enum xlink_error rc;
+ int event_queued = 0;
+ char origin = 'K';
+
+ if (!xlink || !handle)
+ return X_LINK_ERROR;
+
+ if (CHANNEL_USER_BIT_IS_SET(chan))
+ origin = 'U'; // function called from user space
+ CHANNEL_CLEAR_USER_BIT(chan); // restore proper channel value
+
+ link = get_link_by_sw_device_id(handle->sw_device_id);
+ if (!link)
+ return X_LINK_ERROR;
+ event = xlink_create_event(link->id, XLINK_DATA_READY_CALLBACK_REQ,
+ &link->handle, chan, 0, 0);
+ if (!event)
+ return X_LINK_ERROR;
+ event->data = data_available_event;
+ event->callback_origin = origin;
+ if (!data_available_event)
+ event->calling_pid = NULL; // disable callbacks on this channel
+ else
+ event->calling_pid = current;
+ rc = xlink_multiplexer_tx(event, &event_queued);
+ if (!event_queued)
+ xlink_destroy_event(event);
+ return rc;
+}
+EXPORT_SYMBOL(xlink_data_available_event);
+enum xlink_error xlink_data_consumed_event(struct xlink_handle *handle,
+ u16 chan,
+ xlink_event data_consumed_event)
+{
+ struct xlink_event *event;
+ struct xlink_link *link;
+ enum xlink_error rc;
+ int event_queued = 0;
+ char origin = 'K';
+
+ if (!xlink || !handle)
+ return X_LINK_ERROR;
+
+ if (CHANNEL_USER_BIT_IS_SET(chan))
+ origin = 'U'; // function called from user space
+ CHANNEL_CLEAR_USER_BIT(chan); // restore proper channel value
+
+ link = get_link_by_sw_device_id(handle->sw_device_id);
+ if (!link)
+ return X_LINK_ERROR;
+ event = xlink_create_event(link->id, XLINK_DATA_CONSUMED_CALLBACK_REQ,
+ &link->handle, chan, 0, 0);
+ if (!event)
+ return X_LINK_ERROR;
+ event->data = data_consumed_event;
+ event->callback_origin = origin;
+ if (!data_consumed_event)
+ event->calling_pid = NULL; // disable callbacks on this channel
+ else
+ event->calling_pid = current;
+ rc = xlink_multiplexer_tx(event, &event_queued);
+ if (!event_queued)
+ xlink_destroy_event(event);
+ return rc;
+}
+EXPORT_SYMBOL(xlink_data_consumed_event);
enum xlink_error xlink_open_channel(struct xlink_handle *handle,
u16 chan, enum xlink_opmode mode,
u32 data_size, u32 timeout)
@@ -1051,8 +1285,8 @@ EXPORT_SYMBOL(xlink_release_data);
enum xlink_error xlink_disconnect(struct xlink_handle *handle)
{
struct xlink_link *link;
- int interface = NULL_INTERFACE;
- enum xlink_error rc = X_LINK_ERROR;
+ int interface;
+ enum xlink_error rc = 0;
if (!xlink || !handle)
return X_LINK_ERROR;
@@ -1061,7 +1295,6 @@ enum xlink_error xlink_disconnect(struct xlink_handle *handle)
if (!link)
return X_LINK_ERROR;
- // decrement refcount, if count is 0 lock mutex and disconnect
if (kref_put_mutex(&link->refcount, release_after_kref_put,
&xlink->lock)) {
// stop dispatcher
@@ -1240,6 +1473,179 @@ enum xlink_error xlink_get_device_mode(struct xlink_handle *handle,
return rc;
}
EXPORT_SYMBOL(xlink_get_device_mode);
+
+static int xlink_device_event_handler(u32 sw_device_id, u32 event_type)
+{
+ struct event_info *events = NULL;
+ xlink_device_event_cb event_cb;
+ bool found = false;
+ char event_attr[7];
+
+ mutex_lock(&dev_event_lock);
+ // find sw_device_id, event_type in list
+ list_for_each_entry(events, &ev_info.list, list) {
+ if (events) {
+ if (events->sw_device_id == sw_device_id &&
+ events->event_type == event_type) {
+ event_cb = events->event_notif_fn;
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found) {
+ if (events->user_flag) {
+ xlink->eventx[events->event_type].value = events->event_type;
+ xlink->eventx[events->event_type].sw_dev_id = sw_device_id;
+ sprintf(event_attr, "event%d", events->event_type);
+ sysfs_notify(&xlink->pdev->dev.kobj, NULL, event_attr);
+ } else {
+ if (event_cb) {
+ event_cb(sw_device_id, event_type);
+ } else {
+ pr_info("No callback found for sw_device_id : 0x%x event type %d\n",
+ sw_device_id, event_type);
+ mutex_unlock(&dev_event_lock);
+ return X_LINK_ERROR;
+ }
+ }
+ pr_info("sysfs_notify event %d swdev_id %xs\n",
+ events->event_type, sw_device_id);
+ }
+ mutex_unlock(&dev_event_lock);
+return X_LINK_SUCCESS;
+}
+
+static bool event_registered(u32 sw_dev_id, u32 event)
+{
+ struct event_info *events = NULL;
+
+ list_for_each_entry(events, &ev_info.list, list) {
+ if (events) {
+ if (events->sw_device_id == sw_dev_id &&
+ events->event_type == event) {
+ return true;
+ }
+ }
+ }
+return false;
+}
+
+static enum xlink_error xlink_register_device_event_user(struct xlink_handle *handle,
+ u32 *event_list, u32 num_events,
+ xlink_device_event_cb event_notif_fn)
+{
+ enum xlink_error rc;
+
+ rc = do_xlink_register_device_event(handle, event_list, num_events,
+ event_notif_fn, 1);
+ return rc;
+}
+
+enum xlink_error xlink_register_device_event(struct xlink_handle *handle,
+ u32 *event_list, u32 num_events,
+ xlink_device_event_cb event_notif_fn)
+{
+ enum xlink_error rc;
+
+ rc = do_xlink_register_device_event(handle, event_list, num_events,
+ event_notif_fn, 0);
+ return rc;
+}
+EXPORT_SYMBOL(xlink_register_device_event);
+
+static enum xlink_error do_xlink_register_device_event(struct xlink_handle *handle,
+ u32 *event_list,
+ u32 num_events,
+ xlink_device_event_cb event_notif_fn,
+ u32 user_flag)
+{
+ struct event_info *events;
+ u32 interface;
+ u32 event;
+ int i;
+
+ if (num_events < 0 || num_events >= NUM_REG_EVENTS)
+ return X_LINK_ERROR;
+ for (i = 0; i < num_events; i++) {
+ events = kzalloc(sizeof(*events), GFP_KERNEL);
+ if (!events)
+ return X_LINK_ERROR;
+ event = *event_list;
+ events->sw_device_id = handle->sw_device_id;
+ events->event_notif_fn = event_notif_fn;
+ events->event_type = *event_list++;
+ events->user_flag = user_flag;
+ if (user_flag) {
+ /* only add to list once if userspace */
+ /* xlink userspace handles multi process callbacks */
+ if (event_registered(handle->sw_device_id, event)) {
+ pr_info("xlink-core: Event 0x%x - %d already registered\n",
+ handle->sw_device_id, event);
+ kfree(events);
+ continue;
+ }
+ }
+ pr_info("xlink-core:Events: sw_device_id 0x%x event %d fn %p user_flag %d\n",
+ events->sw_device_id, events->event_type,
+ events->event_notif_fn, events->user_flag);
+ list_add_tail(&events->list, &ev_info.list);
+ }
+ interface = get_interface_from_sw_device_id(handle->sw_device_id);
+ if (interface == NULL_INTERFACE)
+ return X_LINK_ERROR;
+ xlink_platform_register_for_events(interface, handle->sw_device_id,
+ xlink_device_event_handler);
+ return X_LINK_SUCCESS;
+}
+
+enum xlink_error xlink_unregister_device_event(struct xlink_handle *handle,
+ u32 *event_list,
+ u32 num_events)
+{
+ struct event_info *events = NULL;
+ u32 interface;
+ int found = 0;
+ int count = 0;
+ int i;
+
+ if (num_events < 0 || num_events >= NUM_REG_EVENTS)
+ return X_LINK_ERROR;
+ for (i = 0; i < num_events; i++) {
+ list_for_each_entry(events, &ev_info.list, list) {
+ if (events->sw_device_id == handle->sw_device_id &&
+ events->event_type == event_list[i]) {
+ found = 1;
+ break;
+ }
+ }
+ if (!events || !found)
+ return X_LINK_ERROR;
+ pr_info("removing event %d for sw_device_id 0x%x\n",
+ events->event_type, events->sw_device_id);
+ list_del(&events->list);
+ kfree(events);
+ }
+ // check if any events left for this sw_device_id
+ // are still registered ( in list )
+ list_for_each_entry(events, &ev_info.list, list) {
+ if (events) {
+ if (events->sw_device_id == handle->sw_device_id) {
+ count++;
+ break;
+ }
+ }
+ }
+ if (count == 0) {
+ interface = get_interface_from_sw_device_id(handle->sw_device_id);
+ if (interface == NULL_INTERFACE)
+ return X_LINK_ERROR;
+ xlink_platform_unregister_for_events(interface, handle->sw_device_id);
+ }
+ return X_LINK_SUCCESS;
+}
+EXPORT_SYMBOL(xlink_unregister_device_event);
+
/* Device tree driver match. */
static const struct of_device_id kmb_xlink_of_match[] = {
{
diff --git a/drivers/misc/xlink-core/xlink-defs.h b/drivers/misc/xlink-core/xlink-defs.h
index 8985f6631175..df686e5185c5 100644
--- a/drivers/misc/xlink-core/xlink-defs.h
+++ b/drivers/misc/xlink-core/xlink-defs.h
@@ -102,6 +102,8 @@ enum xlink_event_type {
XLINK_CLOSE_CHANNEL_REQ,
XLINK_PING_REQ,
XLINK_WRITE_CONTROL_REQ,
+ XLINK_DATA_READY_CALLBACK_REQ,
+ XLINK_DATA_CONSUMED_CALLBACK_REQ,
XLINK_REQ_LAST,
// response events
XLINK_WRITE_RESP = 0x10,
@@ -113,6 +115,8 @@ enum xlink_event_type {
XLINK_CLOSE_CHANNEL_RESP,
XLINK_PING_RESP,
XLINK_WRITE_CONTROL_RESP,
+ XLINK_DATA_READY_CALLBACK_RESP,
+ XLINK_DATA_CONSUMED_CALLBACK_RESP,
XLINK_RESP_LAST,
};
diff --git a/drivers/misc/xlink-core/xlink-dispatcher.c b/drivers/misc/xlink-core/xlink-dispatcher.c
index 11ef8e4110ca..bc2f184488ac 100644
--- a/drivers/misc/xlink-core/xlink-dispatcher.c
+++ b/drivers/misc/xlink-core/xlink-dispatcher.c
@@ -5,18 +5,18 @@
* Copyright (C) 2018-2019 Intel Corporation
*
*/
-#include <linux/init.h>
-#include <linux/module.h>
+#include <linux/completion.h>
#include <linux/device.h>
+#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/list.h>
-#include <linux/semaphore.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/completion.h>
-#include <linux/sched/signal.h>
#include <linux/platform_device.h>
+#include <linux/sched/signal.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
#include "xlink-dispatcher.h"
#include "xlink-multiplexer.h"
@@ -34,18 +34,18 @@ enum dispatcher_state {
/* queue for dispatcher tx thread event handling */
struct event_queue {
+ struct list_head head; /* head of event linked list */
u32 count; /* number of events in the queue */
u32 capacity; /* capacity of events in the queue */
- struct list_head head; /* head of event linked list */
struct mutex lock; /* locks queue while accessing */
};
/* dispatcher servicing a single link to a device */
struct dispatcher {
u32 link_id; /* id of link being serviced */
+ int interface; /* underlying interface of link */
enum dispatcher_state state; /* state of the dispatcher */
struct xlink_handle *handle; /* xlink device handle */
- int interface; /* underlying interface of link */
struct task_struct *rxthread; /* kthread servicing rx */
struct task_struct *txthread; /* kthread servicing tx */
struct event_queue queue; /* xlink event queue */
@@ -82,7 +82,7 @@ static struct dispatcher *get_dispatcher_by_id(u32 id)
static u32 event_generate_id(void)
{
- static u32 id = 0xa; // TODO: temporary solution
+ static u32 id = 0xa;
return id++;
}
@@ -142,9 +142,6 @@ static int dispatcher_event_send(struct xlink_event *event)
size_t event_header_size = sizeof(event->header);
int rc;
- // write event header
- // printk(KERN_DEBUG "Sending event: type = 0x%x, id = 0x%x\n",
- // event->header.type, event->header.id);
rc = xlink_platform_write(event->interface,
event->handle->sw_device_id, &event->header,
&event_header_size, event->header.timeout, NULL);
@@ -159,8 +156,10 @@ static int dispatcher_event_send(struct xlink_event *event)
event->handle->sw_device_id, event->data,
&event->header.size, event->header.timeout,
NULL);
- if (rc)
+ if (rc) {
pr_err("Write data failed %d\n", rc);
+ return rc;
+ }
if (event->user_data == 1) {
if (event->paddr != 0) {
xlink_platform_deallocate(xlinkd->dev,
@@ -187,7 +186,6 @@ static int xlink_dispatcher_rxthread(void *context)
size_t size;
int rc;
- // printk(KERN_DEBUG "dispatcher rxthread started\n");
event = xlink_create_event(disp->link_id, 0, disp->handle, 0, 0, 0);
if (!event)
return -1;
@@ -214,7 +212,6 @@ static int xlink_dispatcher_rxthread(void *context)
}
}
}
- // printk(KERN_INFO "dispatcher rxthread stopped\n");
complete(&disp->rx_done);
do_exit(0);
return 0;
@@ -225,7 +222,6 @@ static int xlink_dispatcher_txthread(void *context)
struct dispatcher *disp = (struct dispatcher *)context;
struct xlink_event *event;
- // printk(KERN_DEBUG "dispatcher txthread started\n");
allow_signal(SIGTERM); // allow thread termination while waiting on sem
complete(&disp->tx_done);
while (!kthread_should_stop()) {
@@ -236,7 +232,6 @@ static int xlink_dispatcher_txthread(void *context)
dispatcher_event_send(event);
xlink_destroy_event(event); // free handled event
}
- // printk(KERN_INFO "dispatcher txthread stopped\n");
complete(&disp->tx_done);
do_exit(0);
return 0;
@@ -250,6 +245,7 @@ static int xlink_dispatcher_txthread(void *context)
enum xlink_error xlink_dispatcher_init(void *dev)
{
struct platform_device *plat_dev = (struct platform_device *)dev;
+ struct dispatcher *dsp;
int i;
xlinkd = kzalloc(sizeof(*xlinkd), GFP_KERNEL);
@@ -258,16 +254,16 @@ enum xlink_error xlink_dispatcher_init(void *dev)
xlinkd->dev = &plat_dev->dev;
for (i = 0; i < XLINK_MAX_CONNECTIONS; i++) {
- xlinkd->dispatchers[i].link_id = i;
- sema_init(&xlinkd->dispatchers[i].event_sem, 0);
- init_completion(&xlinkd->dispatchers[i].rx_done);
- init_completion(&xlinkd->dispatchers[i].tx_done);
- INIT_LIST_HEAD(&xlinkd->dispatchers[i].queue.head);
- mutex_init(&xlinkd->dispatchers[i].queue.lock);
- xlinkd->dispatchers[i].queue.count = 0;
- xlinkd->dispatchers[i].queue.capacity =
- XLINK_EVENT_QUEUE_CAPACITY;
- xlinkd->dispatchers[i].state = XLINK_DISPATCHER_INIT;
+ dsp = &xlinkd->dispatchers[i];
+ dsp->link_id = i;
+ sema_init(&dsp->event_sem, 0);
+ init_completion(&dsp->rx_done);
+ init_completion(&dsp->tx_done);
+ INIT_LIST_HEAD(&dsp->queue.head);
+ mutex_init(&dsp->queue.lock);
+ dsp->queue.count = 0;
+ dsp->queue.capacity = XLINK_EVENT_QUEUE_CAPACITY;
+ dsp->state = XLINK_DISPATCHER_INIT;
}
mutex_init(&xlinkd->lock);
@@ -329,7 +325,7 @@ enum xlink_error xlink_dispatcher_event_add(enum xlink_event_origin origin,
struct dispatcher *disp;
int rc;
- // get dispatcher by handle
+ // get dispatcher by link id
disp = get_dispatcher_by_id(event->link_id);
if (!disp)
return X_LINK_ERROR;
@@ -433,7 +429,6 @@ enum xlink_error xlink_dispatcher_destroy(void)
}
xlink_destroy_event(event);
}
- // destroy dispatcher
mutex_destroy(&disp->queue.lock);
}
mutex_destroy(&xlinkd->lock);
diff --git a/drivers/misc/xlink-core/xlink-multiplexer.c b/drivers/misc/xlink-core/xlink-multiplexer.c
index 48451dc30712..e09458b62c45 100644
--- a/drivers/misc/xlink-core/xlink-multiplexer.c
+++ b/drivers/misc/xlink-core/xlink-multiplexer.c
@@ -115,6 +115,38 @@ static struct xlink_multiplexer *xmux;
*
*/
+static enum xlink_error run_callback(struct open_channel *opchan,
+ void *callback, struct task_struct *pid)
+{
+ enum xlink_error rc = X_LINK_SUCCESS;
+ struct kernel_siginfo info;
+ void (*func)(int chan);
+ int ret;
+
+ if (opchan->callback_origin == 'U') { // user-space origin
+ if (pid) {
+ memset(&info, 0, sizeof(struct kernel_siginfo));
+ info.si_signo = SIGXLNK;
+ info.si_code = SI_QUEUE;
+ info.si_errno = opchan->id;
+ info.si_ptr = (void __user *)callback;
+ ret = send_sig_info(SIGXLNK, &info, pid);
+ if (ret < 0) {
+ pr_err("Unable to send signal %d\n", ret);
+ rc = X_LINK_ERROR;
+ }
+ } else {
+ pr_err("CHAN 0x%x -- calling_pid == NULL\n",
+ opchan->id);
+ rc = X_LINK_ERROR;
+ }
+ } else { // kernel origin
+ func = callback;
+ func(opchan->id);
+ }
+ return rc;
+}
+
static inline int chan_is_non_blocking_read(struct open_channel *opchan)
{
if (opchan->chan->mode == RXN_TXN || opchan->chan->mode == RXN_TXB)
@@ -151,7 +183,7 @@ static int is_channel_for_device(u16 chan, u32 sw_device_id,
enum xlink_dev_type dev_type)
{
struct xlink_channel_type const *chan_type = get_channel_type(chan);
- int interface = NULL_INTERFACE;
+ int interface;
if (chan_type) {
interface = get_interface_from_sw_device_id(sw_device_id);
@@ -181,13 +213,9 @@ static int is_enough_space_in_channel(struct open_channel *opchan,
}
}
if (opchan->tx_up_limit == 1) {
- if ((opchan->tx_fill_level + size)
- < ((opchan->chan->size / 100) * THR_LWR)) {
- opchan->tx_up_limit = 0;
- return 1;
- } else {
+ if ((opchan->tx_fill_level + size) >=
+ ((opchan->chan->size / 100) * THR_LWR))
return 0;
- }
}
return 1;
}
@@ -231,6 +259,8 @@ static int add_packet_to_channel(struct open_channel *opchan,
list_add_tail(&pkt->list, &queue->head);
queue->count++;
opchan->rx_fill_level += pkt->length;
+ } else {
+ return X_LINK_ERROR;
}
return X_LINK_SUCCESS;
}
@@ -262,9 +292,11 @@ static int release_packet_from_channel(struct open_channel *opchan,
} else {
// find packet in channel rx queue
list_for_each_entry(pkt, &queue->head, list) {
- if (pkt->data == addr) {
- packet_found = 1;
- break;
+ if (pkt) {
+ if (pkt->data == addr) {
+ packet_found = 1;
+ break;
+ }
}
}
}
@@ -629,16 +661,46 @@ enum xlink_error xlink_multiplexer_tx(struct xlink_event *event,
}
} else if (xmux->channels[link_id][chan].status == CHAN_OPEN_PEER) {
/* channel already open */
- xmux->channels[link_id][chan].status = CHAN_OPEN; // opened locally
- xmux->channels[link_id][chan].size = event->header.size;
- xmux->channels[link_id][chan].timeout = event->header.timeout;
- xmux->channels[link_id][chan].mode = (uintptr_t)event->data;
rc = multiplexer_open_channel(link_id, chan);
+ if (rc == X_LINK_SUCCESS) {
+ struct channel *xchan = &xmux->channels[link_id][chan];
+
+ xchan->status = CHAN_OPEN; // opened locally
+ xchan->size = event->header.size;
+ xchan->timeout = event->header.timeout;
+ xchan->mode = (uintptr_t)event->data;
+ }
} else {
/* channel already open */
rc = X_LINK_ALREADY_OPEN;
}
break;
+ case XLINK_DATA_READY_CALLBACK_REQ:
+ opchan = get_channel(link_id, chan);
+ if (!opchan) {
+ rc = X_LINK_COMMUNICATION_FAIL;
+ } else {
+ opchan->ready_callback = event->data;
+ opchan->ready_calling_pid = event->calling_pid;
+ opchan->callback_origin = event->callback_origin;
+ pr_info("xlink ready callback process registered - %lx chan %d\n",
+ (uintptr_t)event->calling_pid, chan);
+ release_channel(opchan);
+ }
+ break;
+ case XLINK_DATA_CONSUMED_CALLBACK_REQ:
+ opchan = get_channel(link_id, chan);
+ if (!opchan) {
+ rc = X_LINK_COMMUNICATION_FAIL;
+ } else {
+ opchan->consumed_callback = event->data;
+ opchan->consumed_calling_pid = event->calling_pid;
+ opchan->callback_origin = event->callback_origin;
+ pr_info("xlink consumed callback process registered - %lx chan %d\n",
+ (uintptr_t)event->calling_pid, chan);
+ release_channel(opchan);
+ }
+ break;
case XLINK_CLOSE_CHANNEL_REQ:
if (xmux->channels[link_id][chan].status == CHAN_OPEN) {
opchan = get_channel(link_id, chan);
@@ -709,7 +771,8 @@ enum xlink_error xlink_multiplexer_rx(struct xlink_event *event)
XLINK_PACKET_ALIGNMENT,
XLINK_NORMAL_MEMORY);
} else {
- pr_err("Fatal error: can't allocate memory in line:%d func:%s\n", __LINE__, __func__);
+ pr_err("Fatal error: can't allocate memory in line:%d func:%s\n",
+ __LINE__, __func__);
}
rc = X_LINK_COMMUNICATION_FAIL;
} else {
@@ -754,6 +817,14 @@ enum xlink_error xlink_multiplexer_rx(struct xlink_event *event)
xlink_dispatcher_event_add(EVENT_RX, event);
//complete regardless of mode/timeout
complete(&opchan->pkt_available);
+ // run callback
+ if (xmux->channels[link_id][chan].status == CHAN_OPEN &&
+ chan_is_non_blocking_read(opchan) &&
+ opchan->ready_callback) {
+ rc = run_callback(opchan, opchan->ready_callback,
+ opchan->ready_calling_pid);
+ break;
+ }
} else {
// failed to allocate buffer
rc = X_LINK_ERROR;
@@ -813,6 +884,13 @@ enum xlink_error xlink_multiplexer_rx(struct xlink_event *event)
xlink_dispatcher_event_add(EVENT_RX, event);
//complete regardless of mode/timeout
complete(&opchan->pkt_consumed);
+ // run callback
+ if (xmux->channels[link_id][chan].status == CHAN_OPEN &&
+ chan_is_non_blocking_write(opchan) &&
+ opchan->consumed_callback) {
+ rc = run_callback(opchan, opchan->consumed_callback,
+ opchan->consumed_calling_pid);
+ }
release_channel(opchan);
break;
case XLINK_RELEASE_REQ:
@@ -838,47 +916,47 @@ enum xlink_error xlink_multiplexer_rx(struct xlink_event *event)
rc = multiplexer_open_channel(link_id, chan);
if (rc) {
rc = X_LINK_ERROR;
- } else {
- opchan = get_channel(link_id, chan);
- if (!opchan) {
- rc = X_LINK_COMMUNICATION_FAIL;
- } else {
- xmux->channels[link_id][chan].status = CHAN_OPEN_PEER;
- complete(&opchan->opened);
- passthru_event = xlink_create_event(link_id,
- XLINK_OPEN_CHANNEL_RESP,
- event->handle,
- chan,
- 0,
- opchan->chan->timeout);
- if (!passthru_event) {
- rc = X_LINK_ERROR;
- release_channel(opchan);
- break;
- }
- xlink_dispatcher_event_add(EVENT_RX,
- passthru_event);
- }
+ break;
+ }
+ opchan = get_channel(link_id, chan);
+ if (!opchan) {
+ rc = X_LINK_COMMUNICATION_FAIL;
+ break;
+ }
+ xmux->channels[link_id][chan].status = CHAN_OPEN_PEER;
+ complete(&opchan->opened);
+ passthru_event = xlink_create_event(link_id,
+ XLINK_OPEN_CHANNEL_RESP,
+ event->handle,
+ chan,
+ 0,
+ opchan->chan->timeout);
+ if (!passthru_event) {
+ rc = X_LINK_ERROR;
release_channel(opchan);
+ break;
}
+ xlink_dispatcher_event_add(EVENT_RX,
+ passthru_event);
+ release_channel(opchan);
} else {
/* channel already open */
opchan = get_channel(link_id, chan);
if (!opchan) {
rc = X_LINK_COMMUNICATION_FAIL;
- } else {
- passthru_event = xlink_create_event(link_id,
- XLINK_OPEN_CHANNEL_RESP,
- event->handle,
- chan, 0, 0);
- if (!passthru_event) {
- release_channel(opchan);
- rc = X_LINK_ERROR;
- break;
- }
- xlink_dispatcher_event_add(EVENT_RX,
- passthru_event);
+ break;
+ }
+ passthru_event = xlink_create_event(link_id,
+ XLINK_OPEN_CHANNEL_RESP,
+ event->handle,
+ chan, 0, 0);
+ if (!passthru_event) {
+ release_channel(opchan);
+ rc = X_LINK_ERROR;
+ break;
}
+ xlink_dispatcher_event_add(EVENT_RX,
+ passthru_event);
release_channel(opchan);
}
rc = xlink_passthrough(event);
@@ -930,7 +1008,7 @@ enum xlink_error xlink_passthrough(struct xlink_event *event)
#ifdef CONFIG_XLINK_LOCAL_HOST
struct xlink_ipc_context ipc = {0};
phys_addr_t physaddr = 0;
- dma_addr_t vpuaddr = 0;
+ static dma_addr_t vpuaddr;
u32 timeout = 0;
u32 link_id;
u16 chan;
diff --git a/drivers/misc/xlink-core/xlink-platform.c b/drivers/misc/xlink-core/xlink-platform.c
index 56eb8da28a5f..b0076cb3671d 100644
--- a/drivers/misc/xlink-core/xlink-platform.c
+++ b/drivers/misc/xlink-core/xlink-platform.c
@@ -56,6 +56,11 @@ static inline int xlink_ipc_close_channel(u32 sw_device_id,
u32 channel)
{ return -1; }
+static inline int xlink_ipc_register_for_events(u32 sw_device_id,
+ int (*callback)(u32 sw_device_id, u32 event))
+{ return -1; }
+static inline int xlink_ipc_unregister_for_events(u32 sw_device_id)
+{ return -1; }
#endif /* CONFIG_XLINK_LOCAL_HOST */
/*
@@ -95,6 +100,13 @@ static int (*open_chan_fcts[NMB_OF_INTERFACES])(u32, u32) = {
static int (*close_chan_fcts[NMB_OF_INTERFACES])(u32, u32) = {
xlink_ipc_close_channel, NULL, NULL, NULL};
+static int (*register_for_events_fcts[NMB_OF_INTERFACES])(u32,
+ int (*callback)(u32 sw_device_id, u32 event)) = {
+ xlink_ipc_register_for_events,
+ xlink_pcie_register_device_event,
+ NULL, NULL};
+static int (*unregister_for_events_fcts[NMB_OF_INTERFACES])(u32) = {
+ xlink_ipc_unregister_for_events, xlink_pcie_unregister_device_event, NULL, NULL};
/*
* xlink low-level driver interface
@@ -207,6 +219,21 @@ int xlink_platform_close_channel(u32 interface, u32 sw_device_id,
return close_chan_fcts[interface](sw_device_id, channel);
}
+int xlink_platform_register_for_events(u32 interface, u32 sw_device_id,
+ xlink_device_event_cb event_notif_fn)
+{
+ if (interface >= NMB_OF_INTERFACES || !register_for_events_fcts[interface])
+ return -1;
+ return register_for_events_fcts[interface](sw_device_id, event_notif_fn);
+}
+
+int xlink_platform_unregister_for_events(u32 interface, u32 sw_device_id)
+{
+ if (interface >= NMB_OF_INTERFACES || !unregister_for_events_fcts[interface])
+ return -1;
+ return unregister_for_events_fcts[interface](sw_device_id);
+}
+
void *xlink_platform_allocate(struct device *dev, dma_addr_t *handle,
u32 size, u32 alignment,
enum xlink_memory_region region)
diff --git a/include/linux/xlink.h b/include/linux/xlink.h
index b00dbc719530..ac196ff85469 100644
--- a/include/linux/xlink.h
+++ b/include/linux/xlink.h
@@ -70,6 +70,12 @@ enum xlink_error xlink_open_channel(struct xlink_handle *handle,
u16 chan, enum xlink_opmode mode,
u32 data_size, u32 timeout);
+enum xlink_error xlink_data_available_event(struct xlink_handle *handle,
+ u16 chan,
+ xlink_event data_available_event);
+enum xlink_error xlink_data_consumed_event(struct xlink_handle *handle,
+ u16 chan,
+ xlink_event data_consumed_event);
enum xlink_error xlink_close_channel(struct xlink_handle *handle, u16 chan);
enum xlink_error xlink_write_data(struct xlink_handle *handle,
@@ -113,9 +119,14 @@ enum xlink_error xlink_set_device_mode(struct xlink_handle *handle,
enum xlink_error xlink_get_device_mode(struct xlink_handle *handle,
enum xlink_device_power_mode *power_mode);
-enum xlink_error xlink_start_vpu(char *filename); /* depreciated */
+enum xlink_error xlink_register_device_event(struct xlink_handle *handle,
+ u32 *event_list, u32 num_events,
+ xlink_device_event_cb event_notif_fn);
+enum xlink_error xlink_unregister_device_event(struct xlink_handle *handle,
+ u32 *event_list, u32 num_events);
+enum xlink_error xlink_start_vpu(char *filename); /* deprecated */
-enum xlink_error xlink_stop_vpu(void); /* depreciated */
+enum xlink_error xlink_stop_vpu(void); /* deprecated */
/* API functions to be implemented
*
--
2.17.1