From: Seamus Kelly <seamus.ke...@intel.com> 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 <a...@arndb.de> Cc: Greg Kroah-Hartman <gre...@linuxfoundation.org> Reviewed-by: Mark Gross <mgr...@linux.intel.com> Signed-off-by: Mark Gross <mgr...@linux.intel.com> Signed-off-by: Seamus Kelly <seamus.ke...@intel.com> --- drivers/misc/xlink-core/xlink-core.c | 497 ++++++++++++++++---- drivers/misc/xlink-core/xlink-core.h | 11 +- drivers/misc/xlink-core/xlink-defs.h | 6 +- drivers/misc/xlink-core/xlink-dispatcher.c | 53 +-- drivers/misc/xlink-core/xlink-ioctl.c | 146 +++++- drivers/misc/xlink-core/xlink-ioctl.h | 6 + drivers/misc/xlink-core/xlink-multiplexer.c | 176 +++++-- drivers/misc/xlink-core/xlink-platform.c | 27 ++ include/linux/xlink.h | 15 +- 9 files changed, 757 insertions(+), 180 deletions(-) diff --git a/drivers/misc/xlink-core/xlink-core.c b/drivers/misc/xlink-core/xlink-core.c index d0a3f98d16af..23c0025f6f0d 100644 --- a/drivers/misc/xlink-core/xlink-core.c +++ b/drivers/misc/xlink-core/xlink-core.c @@ -55,6 +55,8 @@ static struct cdev xlink_cdev; static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static struct mutex dev_event_lock; + static const struct file_operations fops = { .owner = THIS_MODULE, .unlocked_ioctl = xlink_ioctl, @@ -66,14 +68,75 @@ 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 +// sysfs attribute functions + +static ssize_t eventx_show(struct device *dev, struct device_attribute *attr, + int index, char *buf) +{ + struct keembay_xlink_dev *xlink_dev = dev_get_drvdata(dev); + struct xlink_attr *a = &xlink_dev->eventx[index]; + + return sysfs_emit(buf, "0x%x 0x%lx\n", a->sw_dev_id, a->value); +} + +static ssize_t event0_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return eventx_show(dev, attr, 0, buf); +} + +static ssize_t event1_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return eventx_show(dev, attr, 1, buf); +} + +static ssize_t event2_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return eventx_show(dev, attr, 2, buf); +} + +static ssize_t event3_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return eventx_show(dev, attr, 3, buf); +} + +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. @@ -207,7 +270,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: @@ -231,7 +301,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"); @@ -251,7 +320,6 @@ static int kmb_xlink_remove(struct platform_device *pdev) * IOCTL function for User Space access to xlink kernel functions * */ - static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int rc; @@ -263,6 +331,12 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case XL_OPEN_CHANNEL: rc = ioctl_open_channel(arg); break; + case XL_DATA_READY_CALLBACK: + rc = ioctl_data_ready_callback(arg); + break; + case XL_DATA_CONSUMED_CALLBACK: + rc = ioctl_data_consumed_callback(arg); + break; case XL_READ_DATA: rc = ioctl_read_data(arg); break; @@ -275,6 +349,9 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case XL_WRITE_VOLATILE: rc = ioctl_write_volatile_data(arg); break; + case XL_WRITE_CONTROL_DATA: + rc = ioctl_write_control_data(arg); + break; case XL_RELEASE_DATA: rc = ioctl_release_data(arg); break; @@ -285,10 +362,10 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg) rc = ioctl_start_vpu(arg); break; case XL_STOP_VPU: - rc = xlink_stop_vpu(); + rc = ioctl_stop_vpu(); break; case XL_RESET_VPU: - rc = xlink_stop_vpu(); + rc = ioctl_stop_vpu(); break; case XL_DISCONNECT: rc = ioctl_disconnect(arg); @@ -314,6 +391,12 @@ static long xlink_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case XL_SET_DEVICE_MODE: rc = ioctl_set_device_mode(arg); break; + case XL_REGISTER_DEV_EVENT: + rc = ioctl_register_device_event(arg); + break; + case XL_UNREGISTER_DEV_EVENT: + rc = ioctl_unregister_device_event(arg); + break; } if (rc) return -EIO; @@ -387,14 +470,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"); @@ -405,7 +486,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); @@ -413,7 +493,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: @@ -423,64 +502,109 @@ enum xlink_error xlink_connect(struct xlink_handle *handle) } EXPORT_SYMBOL_GPL(xlink_connect); -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) { struct xlink_event *event; struct xlink_link *link; - int event_queued = 0; 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_OPEN_CHANNEL_REQ, - &link->handle, chan, data_size, timeout); + event = xlink_create_event(link->id, XLINK_DATA_READY_CALLBACK_REQ, + &link->handle, chan, 0, 0); if (!event) return X_LINK_ERROR; - - event->data = (void *)mode; + 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_GPL(xlink_open_channel); - -enum xlink_error xlink_close_channel(struct xlink_handle *handle, - u16 chan) +EXPORT_SYMBOL_GPL(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_CLOSE_CHANNEL_REQ, + 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_GPL(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) +{ + struct xlink_event *event; + struct xlink_link *link; + int event_queued = 0; + enum xlink_error rc; + + if (!xlink || !handle) + return X_LINK_ERROR; + + link = get_link_by_sw_device_id(handle->sw_device_id); + if (!link) + return X_LINK_ERROR; + + event = xlink_create_event(link->id, XLINK_OPEN_CHANNEL_REQ, + &link->handle, chan, data_size, timeout); + if (!event) + return X_LINK_ERROR; + event->data = (void *)mode; rc = xlink_multiplexer_tx(event, &event_queued); if (!event_queued) xlink_destroy_event(event); return rc; } -EXPORT_SYMBOL_GPL(xlink_close_channel); +EXPORT_SYMBOL_GPL(xlink_open_channel); -enum xlink_error xlink_write_data(struct xlink_handle *handle, - u16 chan, u8 const *pmessage, u32 size) +enum xlink_error xlink_close_channel(struct xlink_handle *handle, + u16 chan) { struct xlink_event *event; struct xlink_link *link; @@ -490,38 +614,26 @@ enum xlink_error xlink_write_data(struct xlink_handle *handle, if (!xlink || !handle) return X_LINK_ERROR; - if (size > XLINK_MAX_DATA_SIZE) - return X_LINK_ERROR; - link = get_link_by_sw_device_id(handle->sw_device_id); if (!link) return X_LINK_ERROR; - event = xlink_create_event(link->id, XLINK_WRITE_REQ, &link->handle, - chan, size, 0); + event = xlink_create_event(link->id, XLINK_CLOSE_CHANNEL_REQ, + &link->handle, chan, 0, 0); if (!event) return X_LINK_ERROR; - if (chan < XLINK_IPC_MAX_CHANNELS && - event->interface == IPC_INTERFACE) { - /* only passing message address across IPC interface */ - event->data = &pmessage; - rc = xlink_multiplexer_tx(event, &event_queued); + rc = xlink_multiplexer_tx(event, &event_queued); + if (!event_queued) xlink_destroy_event(event); - } else { - event->data = (u8 *)pmessage; - event->paddr = 0; - rc = xlink_multiplexer_tx(event, &event_queued); - if (!event_queued) - xlink_destroy_event(event); - } + return rc; } -EXPORT_SYMBOL_GPL(xlink_write_data); +EXPORT_SYMBOL_GPL(xlink_close_channel); -enum xlink_error xlink_write_data_user(struct xlink_handle *handle, - u16 chan, u8 const *pmessage, - u32 size) +static enum xlink_error do_xlink_write_data(struct xlink_handle *handle, + u16 chan, u8 const *pmessage, + u32 size, u32 user_flag) { struct xlink_event *event; struct xlink_link *link; @@ -544,48 +656,78 @@ enum xlink_error xlink_write_data_user(struct xlink_handle *handle, chan, size, 0); if (!event) return X_LINK_ERROR; - event->user_data = 1; + event->user_data = user_flag; if (chan < XLINK_IPC_MAX_CHANNELS && event->interface == IPC_INTERFACE) { /* only passing message address across IPC interface */ - if (get_user(addr, (u32 __user *)pmessage)) { - xlink_destroy_event(event); - return X_LINK_ERROR; + if (user_flag) { + if (get_user(addr, (u32 __user *)pmessage)) { + xlink_destroy_event(event); + return X_LINK_ERROR; + } + event->data = &addr; + } else { + event->data = &pmessage; } - event->data = &addr; rc = xlink_multiplexer_tx(event, &event_queued); xlink_destroy_event(event); } else { - event->data = xlink_platform_allocate(&xlink->pdev->dev, &paddr, - size, - XLINK_PACKET_ALIGNMENT, - XLINK_NORMAL_MEMORY); - if (!event->data) { - xlink_destroy_event(event); - return X_LINK_ERROR; - } - if (copy_from_user(event->data, (void __user *)pmessage, size)) { - xlink_platform_deallocate(&xlink->pdev->dev, - event->data, paddr, size, - XLINK_PACKET_ALIGNMENT, - XLINK_NORMAL_MEMORY); - xlink_destroy_event(event); - return X_LINK_ERROR; + if (user_flag) { + event->data = xlink_platform_allocate(&xlink->pdev->dev, &paddr, + size, + XLINK_PACKET_ALIGNMENT, + XLINK_NORMAL_MEMORY); + if (!event->data) { + xlink_destroy_event(event); + return X_LINK_ERROR; + } + if (copy_from_user(event->data, (void __user *)pmessage, size)) { + xlink_platform_deallocate(&xlink->pdev->dev, + event->data, paddr, size, + XLINK_PACKET_ALIGNMENT, + XLINK_NORMAL_MEMORY); + xlink_destroy_event(event); + return X_LINK_ERROR; + } + event->paddr = paddr; + } else { + event->data = (u8 *)pmessage; + event->paddr = 0; } - event->paddr = paddr; rc = xlink_multiplexer_tx(event, &event_queued); if (!event_queued) { - xlink_platform_deallocate(&xlink->pdev->dev, - event->data, paddr, size, - XLINK_PACKET_ALIGNMENT, - XLINK_NORMAL_MEMORY); + if (user_flag) { + xlink_platform_deallocate(&xlink->pdev->dev, + event->data, paddr, size, + XLINK_PACKET_ALIGNMENT, + XLINK_NORMAL_MEMORY); + } xlink_destroy_event(event); } } return rc; } +enum xlink_error xlink_write_data(struct xlink_handle *handle, + u16 chan, u8 const *pmessage, u32 size) +{ + enum xlink_error rc = 0; + + rc = do_xlink_write_data(handle, chan, pmessage, size, 0); + return rc; +} +EXPORT_SYMBOL_GPL(xlink_write_data); + +enum xlink_error xlink_write_data_user(struct xlink_handle *handle, + u16 chan, u8 const *pmessage, u32 size) +{ + enum xlink_error rc = 0; + + rc = do_xlink_write_data(handle, chan, pmessage, size, 1); + return rc; +} + enum xlink_error xlink_write_control_data(struct xlink_handle *handle, u16 chan, u8 const *pmessage, u32 size) @@ -614,16 +756,7 @@ enum xlink_error xlink_write_control_data(struct xlink_handle *handle, } EXPORT_SYMBOL_GPL(xlink_write_control_data); -enum xlink_error xlink_write_volatile(struct xlink_handle *handle, - u16 chan, u8 const *message, u32 size) -{ - enum xlink_error rc = 0; - - rc = do_xlink_write_volatile(handle, chan, message, size, 0); - return rc; -} - -enum xlink_error do_xlink_write_volatile(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) { @@ -668,6 +801,26 @@ enum xlink_error do_xlink_write_volatile(struct xlink_handle *handle, return rc; } +enum xlink_error xlink_write_volatile_user(struct xlink_handle *handle, + u16 chan, u8 const *message, + u32 size) +{ + enum xlink_error rc = 0; + + rc = do_xlink_write_volatile(handle, chan, message, size, 1); + return rc; +} + +enum xlink_error xlink_write_volatile(struct xlink_handle *handle, + u16 chan, u8 const *message, u32 size) +{ + enum xlink_error rc = 0; + + rc = do_xlink_write_volatile(handle, chan, message, size, 0); + return rc; +} +EXPORT_SYMBOL_GPL(xlink_write_volatile); + enum xlink_error xlink_read_data(struct xlink_handle *handle, u16 chan, u8 **pmessage, u32 *size) { @@ -757,8 +910,8 @@ EXPORT_SYMBOL_GPL(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; @@ -767,7 +920,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 @@ -946,6 +1098,179 @@ enum xlink_error xlink_get_device_mode(struct xlink_handle *handle, return rc; } EXPORT_SYMBOL_GPL(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 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_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_GPL(xlink_register_device_event); + +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_GPL(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-core.h b/drivers/misc/xlink-core/xlink-core.h index 5ba7ac653bf7..ee10058a15ac 100644 --- a/drivers/misc/xlink-core/xlink-core.h +++ b/drivers/misc/xlink-core/xlink-core.h @@ -12,11 +12,14 @@ #define NUM_REG_EVENTS 4 -enum xlink_error do_xlink_write_volatile(struct xlink_handle *handle, - u16 chan, u8 const *message, - u32 size, u32 user_flag); - enum xlink_error xlink_write_data_user(struct xlink_handle *handle, u16 chan, u8 const *pmessage, u32 size); +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 xlink_write_volatile_user(struct xlink_handle *handle, + u16 chan, u8 const *message, + u32 size); #endif /* XLINK_CORE_H_ */ diff --git a/drivers/misc/xlink-core/xlink-defs.h b/drivers/misc/xlink-core/xlink-defs.h index 8985f6631175..81aa3bfffcd3 100644 --- a/drivers/misc/xlink-core/xlink-defs.h +++ b/drivers/misc/xlink-core/xlink-defs.h @@ -35,7 +35,7 @@ #define CONTROL_CHANNEL_TIMEOUT_MS 0U // wait indefinitely #define SIGXLNK 44 // signal XLink uses for callback signalling -#define UNUSED(x) (void)(x) +#define UNUSED(x) ((void)(x)) // the end of the IPC channel range (starting at zero) #define XLINK_IPC_MAX_CHANNELS 1024 @@ -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-ioctl.c b/drivers/misc/xlink-core/xlink-ioctl.c index 90947bbccfed..7822a7b35bb6 100644 --- a/drivers/misc/xlink-core/xlink-ioctl.c +++ b/drivers/misc/xlink-core/xlink-ioctl.c @@ -28,15 +28,6 @@ static int copy_result_to_user(u32 *where, int rc) return rc; } -static enum xlink_error xlink_write_volatile_user(struct xlink_handle *handle, - u16 chan, u8 const *message, u32 size) -{ - enum xlink_error rc = 0; - - rc = do_xlink_write_volatile(handle, chan, message, size, 1); - return rc; -} - int ioctl_connect(unsigned long arg) { struct xlink_handle devh = {}; @@ -158,6 +149,28 @@ int ioctl_write_data(unsigned long arg) return copy_result_to_user(wr.return_code, rc); } +int ioctl_write_control_data(unsigned long arg) +{ + struct xlink_handle devh = {}; + struct xlinkwritedata wr = {}; + u8 volbuf[XLINK_MAX_BUF_SIZE]; + int rc = 0; + + if (copy_from_user(&wr, (void __user *)arg, + sizeof(struct xlinkwritedata))) + return -EFAULT; + if (copy_from_user(&devh, (void __user *)wr.handle, + sizeof(struct xlink_handle))) + return -EFAULT; + if (wr.size > XLINK_MAX_CONTROL_DATA_SIZE) + return -EFAULT; + if (copy_from_user(volbuf, (void __user *)wr.pmessage, wr.size)) + return -EFAULT; + rc = xlink_write_control_data(&devh, wr.chan, volbuf, wr.size); + + return copy_result_to_user(wr.return_code, rc); +} + int ioctl_write_volatile_data(unsigned long arg) { struct xlink_handle devh = {}; @@ -242,6 +255,14 @@ int ioctl_start_vpu(unsigned long arg) return copy_result_to_user(startvpu.return_code, rc); } +int ioctl_stop_vpu(void) +{ + int rc = 0; + + rc = xlink_stop_vpu(); + return rc; +} + int ioctl_disconnect(unsigned long arg) { struct xlink_handle devh = {}; @@ -424,3 +445,110 @@ int ioctl_set_device_mode(unsigned long arg) return copy_result_to_user(devm.return_code, rc); } + +int ioctl_register_device_event(unsigned long arg) +{ + struct xlink_handle devh = {}; + struct xlinkregdevevent regdevevent = {}; + u32 num_events = 0; + u32 *ev_list; + int rc = 0; + + 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; + } + + return copy_result_to_user(regdevevent.return_code, rc); +} + +int ioctl_unregister_device_event(unsigned long arg) +{ + struct xlink_handle devh = {}; + struct xlinkregdevevent regdevevent = {}; + u32 num_events = 0; + u32 *ev_list; + int rc = 0; + + 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; + } + + return copy_result_to_user(regdevevent.return_code, rc); +} + +int ioctl_data_ready_callback(unsigned long arg) +{ + struct xlink_handle devh = {}; + struct xlinkcallback cb = {}; + int rc = 0; + + 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); + + return copy_result_to_user(cb.return_code, rc); +} + +int ioctl_data_consumed_callback(unsigned long arg) +{ + struct xlink_handle devh = {}; + struct xlinkcallback cb = {}; + int rc = 0; + + 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); + + return copy_result_to_user(cb.return_code, rc); +} diff --git a/drivers/misc/xlink-core/xlink-ioctl.h b/drivers/misc/xlink-core/xlink-ioctl.h index d016d8418f30..7818b676d488 100644 --- a/drivers/misc/xlink-core/xlink-ioctl.h +++ b/drivers/misc/xlink-core/xlink-ioctl.h @@ -14,10 +14,12 @@ int ioctl_open_channel(unsigned long arg); int ioctl_read_data(unsigned long arg); int ioctl_read_to_buffer(unsigned long arg); int ioctl_write_data(unsigned long arg); +int ioctl_write_control_data(unsigned long arg); int ioctl_write_volatile_data(unsigned long arg); int ioctl_release_data(unsigned long arg); int ioctl_close_channel(unsigned long arg); int ioctl_start_vpu(unsigned long arg); +int ioctl_stop_vpu(void); int ioctl_disconnect(unsigned long arg); int ioctl_get_device_name(unsigned long arg); int ioctl_get_device_list(unsigned long arg); @@ -26,5 +28,9 @@ int ioctl_boot_device(unsigned long arg); int ioctl_reset_device(unsigned long arg); int ioctl_get_device_mode(unsigned long arg); int ioctl_set_device_mode(unsigned long arg); +int ioctl_register_device_event(unsigned long arg); +int ioctl_unregister_device_event(unsigned long arg); +int ioctl_data_ready_callback(unsigned long arg); +int ioctl_data_consumed_callback(unsigned long arg); #endif /* XLINK_IOCTL_H_ */ 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