Implemented MA-USB management messages processing and communication with user-space driver via mapped memory. MA USB PAL events are being enqueued into ring buffer from which data is being send/received to/from user-space. Signed-off-by: Vladimir Stankovic <vladimir.stankovic@xxxxxxxxxxxxxxx> --- drivers/usb/mausb_host/Makefile | 1 + drivers/usb/mausb_host/hcd.c | 252 +++++++- drivers/usb/mausb_host/hpal.c | 449 +++++++++++++- drivers/usb/mausb_host/hpal.h | 44 ++ drivers/usb/mausb_host/hpal_events.c | 611 +++++++++++++++++++ drivers/usb/mausb_host/hpal_events.h | 85 +++ drivers/usb/mausb_host/mausb_driver_status.h | 17 + drivers/usb/mausb_host/utils.c | 275 +++++++++ drivers/usb/mausb_host/utils.h | 5 + 9 files changed, 1729 insertions(+), 10 deletions(-) create mode 100644 drivers/usb/mausb_host/hpal_events.c create mode 100644 drivers/usb/mausb_host/hpal_events.h create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile index 829314b15cbb..fd2a36a04ad6 100644 --- a/drivers/usb/mausb_host/Makefile +++ b/drivers/usb/mausb_host/Makefile @@ -11,5 +11,6 @@ mausb_host-y += utils.o mausb_host-y += ip_link.o mausb_host-y += hcd.o mausb_host-y += hpal.o +mausb_host-y += hpal_events.o ccflags-y += -I$(srctree)/$(src) diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c index 70cb633c39ba..8cf885612684 100644 --- a/drivers/usb/mausb_host/hcd.c +++ b/drivers/usb/mausb_host/hcd.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include "hpal.h" +#include "hpal_events.h" #include "utils.h" static int mausb_open(struct inode *inode, struct file *file); @@ -1075,6 +1076,18 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev) } if (ep_ctx) { + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d", + ep_ctx->ep_handle, dev_handle, status); + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + if (status < 0) + mausb_pr_warn("ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x", + ep_ctx->ep_handle, dev_handle); dev->ep0.hcpriv = NULL; kfree(ep_ctx); @@ -1082,6 +1095,14 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev) mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle); } + if (dev_handle != DEV_HANDLE_NOT_ASSIGNED) { + status = mausb_usbdevdisconnect_event_to_user(ma_dev, + dev_handle); + if (status < 0) + mausb_pr_warn("usb_dev_disconnect req failed for dev_handle=%#x", + dev_handle); + } + free_dev: if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) { mausb_pr_info("All usb devices destroyed - proceed with disconnecting"); @@ -1096,6 +1117,21 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev) mausb_clear_hcd_madev(port_number); } +static int mausb_device_assign_address(struct mausb_device *dev, + struct mausb_usb_device_ctx *usb_dev_ctx) +{ + int status = + mausb_setusbdevaddress_event_to_user(dev, + usb_dev_ctx->dev_handle, + RESPONSE_TIMEOUT); + + mausb_pr_info("dev_handle=%#x, status=%d", usb_dev_ctx->dev_handle, + status); + usb_dev_ctx->addressed = (status == 0); + + return status; +} + static struct mausb_usb_device_ctx * mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev, struct mausb_device *ma_dev, u16 port_number, @@ -1182,6 +1218,12 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev) return status; } + if (!usb_device_ctx->addressed) { + status = mausb_device_assign_address(ma_dev, usb_device_ctx); + if (status < 0) + return status; + } + endpoint_ctx = dev->ep0.hcpriv; if (!endpoint_ctx) { mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x", @@ -1189,7 +1231,23 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev) return -EINVAL; } - return 0; + /* + * Fix to support usb 2.0 logitech camera. If endoint handle of usb 2.0 + * device is already modified, do not modify it again. + */ + if (dev->speed < USB_SPEED_SUPER && endpoint_ctx->ep_handle != 0) + return 0; + + status = mausb_modifyep0_event_to_user(ma_dev, + usb_device_ctx->dev_handle, + &endpoint_ctx->ep_handle, + dev->ep0.desc.wMaxPacketSize); + + mausb_pr_info("modify ep0 response, ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + return status; } static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev, @@ -1242,10 +1300,32 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev, mausb_init_superspeed_ep_descriptor(&descriptor_ss, &endpoint->desc, &endpoint->ss_ep_comp); + status = mausb_ephandle_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + sizeof(descriptor_ss), + &descriptor_ss, + &endpoint_ctx->ep_handle); + } else { mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc); + status = mausb_ephandle_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + sizeof(descriptor), + &descriptor, + &endpoint_ctx->ep_handle); } + if (status < 0) { + mausb_pr_err("ep_handle_request failed dev_handle=%#x", + usb_dev_ctx->dev_handle); + endpoint->hcpriv = NULL; + kfree(endpoint_ctx); + return status; + } + + mausb_pr_info("Endpoint added ep_handle=%#x, dev_handle=%#x", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle); + return 0; } @@ -1254,6 +1334,7 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev, { u8 port_number; int status; + int retries = 0; struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; struct mausb_device *ma_dev; struct mausb_usb_device_ctx *usb_dev_ctx; @@ -1288,9 +1369,49 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev, return -ENODEV; } + mausb_pr_info("Start dropping ep_handle=%#x, dev_handle=%#x", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle); + + if (atomic_read(&ma_dev->unresponsive_client)) { + mausb_pr_err("Client is not responsive anymore - drop endpoint immediately"); + endpoint->hcpriv = NULL; + kfree(endpoint_ctx); + return -ESHUTDOWN; + } + + status = mausb_epinactivate_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + while (true) { + status = mausb_epdelete_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("ep_handle_delete_request, ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + if (status == -EBUSY) { + if (++retries < MAUSB_BUSY_RETRIES_COUNT) + usleep_range(10000, 10001); + else + return -EBUSY; + } else { + break; + } + } + + mausb_pr_info("Endpoint dropped ep_handle=%#x, dev_handle=%#x", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle); + endpoint->hcpriv = NULL; kfree(endpoint_ctx); - return 0; + return status; } static int mausb_device_assign_dev_handle(struct usb_hcd *hcd, @@ -1343,6 +1464,20 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd, return -EINVAL; } + status = mausb_usbdevhandle_event_to_user(ma_dev, (u8)dev_speed, + dev->route, hub_dev_handle, + parent_hs_hub_dev_handle, + parent_hs_hub_port, 0, + ma_dev->lse, + &usb_device_ctx->dev_handle); + + mausb_pr_info("mausb_usbdevhandle_event status=%d", status); + + if (status < 0) + return status; + + mausb_pr_info("Get ref dev_handle=%#x", usb_device_ctx->dev_handle); + endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC); if (!endpoint_ctx) return -ENOMEM; @@ -1354,6 +1489,22 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd, mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc); + status = mausb_ephandle_event_to_user(ma_dev, + usb_device_ctx->dev_handle, + sizeof(descriptor), + &descriptor, + &endpoint_ctx->ep_handle); + + mausb_pr_info("mausb_ephandle_event ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + if (status < 0) { + dev->ep0.hcpriv = NULL; + kfree(endpoint_ctx); + return status; + } + return 0; } @@ -1396,9 +1547,21 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev) return status; } + mausb_pr_info("Device assigned and addressed usb_device_ctx=%p", + usb_device_ctx); + if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED) return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev, usb_device_ctx); + + /* + * Fix for usb 2.0 logitech camera + */ + if (!usb_device_ctx->addressed) + return mausb_device_assign_address(ma_dev, usb_device_ctx); + + mausb_pr_info("Device assigned and addressed dev_handle=%#x", + usb_device_ctx->dev_handle); return 0; } @@ -1444,7 +1607,15 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev) return -ENODEV; } - return 0; + status = mausb_updatedev_event_to_user(ma_dev, + usb_device_ctx->dev_handle, + 0, 0, 0, 0, 0, 0, + &dev->descriptor); + + mausb_pr_info("Finished dev_handle=%#x, status=%d", + usb_device_ctx->dev_handle, status); + + return status; } static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev, @@ -1454,8 +1625,10 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev, u8 port_number; unsigned long flags; u16 max_exit_latency = 0; + u8 number_of_ports = (u8)dev->maxchild; u8 mtt = 0; u8 ttt = 0; + u8 integrated_hub_latency = 0; struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; struct mausb_device *ma_dev; struct mausb_usb_device_ctx *usb_device_ctx; @@ -1495,7 +1668,17 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev, else if (dev->usb3_lpm_u2_enabled) max_exit_latency = (u16)dev->u2_params.mel; - return 0; + status = mausb_updatedev_event_to_user(ma_dev, + usb_device_ctx->dev_handle, + max_exit_latency, 1, + number_of_ports, mtt, ttt, + integrated_hub_latency, + &dev->descriptor); + + mausb_pr_info("Finished dev_handle=%#x, status=%d", + usb_device_ctx->dev_handle, status); + + return status; } static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev) @@ -1531,6 +1714,8 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd, struct mausb_usb_device_ctx *usb_device_ctx; struct usb_device *dev; struct mausb_endpoint_ctx *ep_ctx; + struct ma_usb_ephandlereq_desc_ss descriptor_ss; + struct ma_usb_ephandlereq_desc_std descriptor; ep_ctx = endpoint->hcpriv; if (!ep_ctx) { @@ -1565,6 +1750,15 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd, is_out = usb_endpoint_dir_out(&endpoint->desc); tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]); + status = mausb_epreset_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle, tsp); + + mausb_pr_info("ep_reset status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + if (status != EUCLEAN) { if (!tsp) { usb_settoggle(dev, epnum, is_out, 0U); @@ -1572,12 +1766,52 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd, usb_settoggle(dev, epnum, !is_out, 0U); } + status = mausb_epactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_err("ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + return; } if (tsp) return; + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + if (dev->speed >= USB_SPEED_SUPER) { + mausb_init_superspeed_ep_descriptor(&descriptor_ss, + &endpoint->desc, + &endpoint->ss_ep_comp); + status = mausb_ephandle_event_to_user(ma_dev, dev_handle, + sizeof(descriptor_ss), + &descriptor_ss, + &ep_ctx->ep_handle); + } else { + mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc); + status = mausb_ephandle_event_to_user(ma_dev, dev_handle, + sizeof(descriptor), + &descriptor, + &ep_ctx->ep_handle); + } + mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x", status, ep_ctx->ep_handle, dev_handle); } @@ -1624,7 +1858,15 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev) dev_handle = usb_device_ctx->dev_handle; - return 0; + status = mausb_usbdevreset_event_to_user(ma_dev, dev_handle); + + mausb_pr_info("usb_dev_reset dev_handle=%#x, status=%d", + dev_handle, status); + + if (status == 0) + usb_device_ctx->addressed = false; + + return status; } void mausb_clear_hcd_madev(u16 port_number) diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c index b8e00e6ef69c..1e7bbe3b230a 100644 --- a/drivers/usb/mausb_host/hpal.c +++ b/drivers/usb/mausb_host/hpal.c @@ -11,6 +11,7 @@ #include <linux/uio.h> #include "hcd.h" +#include "hpal_events.h" #include "utils.h" #define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000 @@ -278,6 +279,31 @@ void mausb_release_event_resources(struct mausb_event *event) kfree(receive_buffer); } +static void mausb_iterator_reset(struct mausb_device *dev, + struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + struct mausb_urb_ctx *urb_ctx; + + urb_ctx = mausb_find_urb_in_tree(urb); + + if (urb_ctx) + mausb_reset_data_iterator(&urb_ctx->iterator); +} + +static void mausb_iterator_seek(struct mausb_device *dev, + struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + struct mausb_urb_ctx *urb_ctx; + + urb_ctx = mausb_find_urb_in_tree(urb); + + if (urb_ctx) + mausb_data_iterator_seek(&urb_ctx->iterator, + event->data.iterator_seek_delta); +} + void mausb_complete_urb(struct mausb_event *event) { struct urb *urb = (struct urb *)event->data.urb; @@ -291,6 +317,46 @@ void mausb_complete_urb(struct mausb_event *event) event->status); } +static void mausb_delete_ma_dev(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_signal_event(dev, event, event->mgmt.delete_ma_dev.event_id); +} + +static void mausb_process_user_finished(struct mausb_device *dev, + struct mausb_event *event) +{ + complete(&dev->user_finished_event); +} + +static int mausb_send_mgmt_msg(struct mausb_device *dev, + struct mausb_event *event) +{ + struct mausb_kvec_data_wrapper wrapper; + struct kvec kvec; + struct ma_usb_hdr_common *hdr; + int status; + + hdr = (struct ma_usb_hdr_common *)event->mgmt.mgmt_hdr.hdr; + + mausb_pr_info("event=%d, type=%d", event->type, hdr->type); + + kvec.iov_base = hdr; + kvec.iov_len = hdr->length; + wrapper.kvec = &kvec; + wrapper.kvec_num = 1; + wrapper.length = hdr->length; + + status = mausb_ip_send(dev->mgmt_channel, &wrapper); + if (status < 0) { + mausb_pr_err("Send failed. Disconnecting... status=%d", status); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + return status; +} + static int mausb_get_first_free_port_number(u16 *port_number) { (*port_number) = 0; @@ -338,11 +404,142 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev, mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev); } +static void mausb_complete_timeout_event(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug("Event type=%d, event_id=%llu", event->type, + event->mgmt.mgmt_req_timedout.event_id); + mausb_signal_event(dev, event, event->mgmt.mgmt_req_timedout.event_id); +} + +static void mausb_process_event(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug("Event type=%d", event->type); + + switch (event->type) { + case MAUSB_EVENT_TYPE_USB_DEV_HANDLE: + mausb_usbdevhandle_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE: + mausb_ephandle_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE: + mausb_epactivate_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE: + mausb_epinactivate_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_RESET: + mausb_epreset_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_DELETE: + mausb_epdelete_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_MODIFY_EP0: + mausb_modifyep0_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS: + mausb_setusbdevaddress_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_UPDATE_DEV: + mausb_updatedev_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_USB_DEV_RESET: + mausb_usbdevreset_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_CANCEL_TRANSFER: + mausb_canceltransfer_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_PORT_CHANGED: + mausb_port_has_changed_event(dev, event); + break; + case MAUSB_EVENT_TYPE_PING: + mausb_send_mgmt_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_SEND_MGMT_MSG: + mausb_send_mgmt_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_SEND_DATA_MSG: + mausb_send_data_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG: + mausb_receive_data_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_URB_COMPLETE: + mausb_complete_urb(event); + break; + case MAUSB_EVENT_TYPE_SEND_ACK: + mausb_send_transfer_ack(dev, event); + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_ITERATOR_RESET: + mausb_iterator_reset(dev, event); + break; + case MAUSB_EVENT_TYPE_ITERATOR_SEEK: + mausb_iterator_seek(dev, event); + break; + case MAUSB_EVENT_TYPE_DELETE_MA_DEV: + mausb_delete_ma_dev(dev, event); + break; + case MAUSB_EVENT_TYPE_USER_FINISHED: + mausb_process_user_finished(dev, event); + break; + case MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES: + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_NONE: + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT: + mausb_complete_timeout_event(dev, event); + break; + default: + break; + } + + mausb_notify_completed_user_events(dev->ring_buffer); +} + +static void mausb_hpal_kernel_work(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + work); + struct mausb_event *event; + int status; + u16 i; + u16 events; + u16 completed_events; + unsigned long flags; + struct mausb_ring_buffer *dev_mausb_ring = dev->ring_buffer; + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + events = dev->num_of_user_events; + completed_events = dev->num_of_completed_events; + dev->num_of_user_events = 0; + dev->num_of_completed_events = 0; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + + status = mausb_ring_buffer_move_tail(dev_mausb_ring, completed_events); + if (status < 0) { + mausb_pr_err("Dequeue failed, status=%d", status); + kref_put(&dev->refcount, mausb_release_ma_dev_async); + return; + } + + for (i = 0; i < events; ++i) { + event = mausb_ring_current_from_user(dev_mausb_ring); + mausb_ring_next_from_user(dev_mausb_ring); + mausb_process_event(dev, event); + } +} + static void mausb_socket_disconnect_event(struct work_struct *work) { struct mausb_device *dev = container_of(work, struct mausb_device, socket_disconnect_work); struct mausb_event event; + int status; mausb_pr_info("madev_addr=%d", dev->madev_addr); @@ -363,6 +560,11 @@ static void mausb_socket_disconnect_event(struct work_struct *work) event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED; event.data.device_id = dev->id; + status = mausb_enqueue_event_to_user(dev, &event); + + mausb_pr_info("Sending notification to user that network is disconnected status=%d", + status); + mausb_pr_info("Releasing MAUSB device ref"); kref_put(&dev->refcount, mausb_release_ma_dev_async); } @@ -427,6 +629,13 @@ static void mausb_delete_madev(struct work_struct *work) mausb_insert_event(dev, &mausb_completion); + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) { + mausb_remove_event(dev, &mausb_completion); + mausb_pr_err("Ring buffer full, enqueue failed"); + return; + } + mausb_pr_debug("Deleting MAUSB device..."); status = wait_for_completion_interruptible_timeout(&completion, @@ -449,10 +658,14 @@ static void mausb_delete_madev(struct work_struct *work) mausb_clear_hcd_madev(dev->port_number); + mausb_ring_buffer_cleanup(dev->ring_buffer); + mausb_ring_buffer_destroy(dev->ring_buffer); + mausb_remove_madev_from_list(dev->madev_addr); put_net(dev->net_ns); + kfree(dev->ring_buffer); kfree(dev); mausb_signal_empty_mss(); @@ -463,6 +676,7 @@ static void mausb_ping_work(struct work_struct *work) { struct mausb_device *dev = container_of(work, struct mausb_device, ping_work); + int status = 0; if (mausb_start_connection_timer(dev) < 0) { mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d", @@ -471,6 +685,13 @@ static void mausb_ping_work(struct work_struct *work) queue_work(dev->workq, &dev->hcd_disconnect_work); return; } + + status = mausb_ping_event_to_user(dev); + + if (status < 0) { + mausb_pr_err("Ring buffer full"); + return; + } } static void mausb_heartbeat_work(struct work_struct *work) @@ -576,6 +797,7 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address, dev->workq = workq; + INIT_WORK(&dev->work, mausb_hpal_kernel_work); INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event); INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event); INIT_WORK(&dev->madev_delete_work, mausb_delete_madev); @@ -601,6 +823,15 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address, dev->madev_addr = madev_address; dev->net_ns = get_net(current->nsproxy->net_ns); + if (!list_empty(&mss.available_ring_buffers)) { + dev->ring_buffer = container_of(mss.available_ring_buffers.next, + struct mausb_ring_buffer, + list_entry); + list_del(mss.available_ring_buffers.next); + } else { + mausb_pr_alert("Ring buffer for mausb device is not availbale!"); + } + list_add_tail(&dev->list_entry, &mss.madev_list); reinit_completion(&mss.empty); @@ -659,6 +890,14 @@ int mausb_initiate_dev_connection(struct mausb_device_address dev_addr, return 0; } +void mausb_on_madev_connected(struct mausb_device *dev) +{ + struct mausb_event mausb_event; + + mausb_dev_reset_req_event(&mausb_event); + mausb_enqueue_event_to_user(dev, &mausb_event); +} + int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events, u16 num_of_completed) { @@ -683,9 +922,29 @@ int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events, return 0; } +int mausb_enqueue_event_to_user(struct mausb_device *dev, + struct mausb_event *event) +{ + int status; + + event->madev_addr = dev->madev_addr; + status = mausb_ring_buffer_put(dev->ring_buffer, event); + if (status < 0) { + mausb_pr_err("Ring buffer operation failed"); + mausb_cleanup_ring_buffer_event(event); + return status; + } + + mausb_notify_ring_events(dev->ring_buffer); + mausb_pr_debug("User-space notification sent."); + + return 0; +} + int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle, struct urb *request) { + int status; struct mausb_event mausb_event; mausb_event.type = MAUSB_EVENT_TYPE_SEND_DATA_MSG; @@ -728,7 +987,12 @@ int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle, &request->dev->route, sizeof(request->dev->route)); } - return 0; + status = mausb_enqueue_event_to_user(dev, &mausb_event); + if (status < 0) + mausb_pr_err("Failed to enqueue event to user-space ep_handle=%#x, status=%d", + mausb_event.data.ep_handle, status); + + return status; } void mausb_complete_request(struct urb *urb, u32 actual_length, int status) @@ -841,6 +1105,17 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work) mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x", urb, ep_ctx->ep_handle, ep_ctx->dev_handle); + if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) { + status = mausb_canceltransfer_event_to_user(ep_ctx->ma_dev, + ep_ctx->dev_handle, + ep_ctx->ep_handle, + (u64)urb); + if (status < 0) { + mausb_pr_err("Failed to enqueue cancel transfer to user"); + goto complete_urb; + } + } + memset(&mausb_event, 0, sizeof(mausb_event)); mausb_event.type = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER; @@ -855,6 +1130,13 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work) MAUSB_DATA_MSG_DIRECTION_IN : MAUSB_DATA_MSG_DIRECTION_OUT); + status = mausb_enqueue_event_to_user(ep_ctx->ma_dev, &mausb_event); + if (status < 0) { + mausb_pr_alert("Failed to enqueue event to user-space ep_handle=%#x, status=%d", + mausb_event.data.ep_handle, status); + goto complete_urb; + } + if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) { mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d", urb, mausb_event.data.ep_handle, status); @@ -916,6 +1198,7 @@ void mausb_deinitialize_mss(void) wait_for_completion(&mss.empty); mausb_pr_debug("Waiting for completion on disconnect_event ended"); + mausb_stop_ring_events(); timeout = wait_for_completion_timeout(&mss.client_stopped, timeout); mausb_pr_info("Remaining time after waiting for stopping client %ld", @@ -1104,7 +1387,6 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event) int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event) { struct mausb_urb_ctx *urb_ctx; - int status = 0; if (event->status != 0) { mausb_pr_err("Event %d failed with status %d", @@ -1119,10 +1401,9 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event) /* Transfer will be deleted from dequeue task */ mausb_pr_warn("Urb is already cancelled for event=%d", event->type); - return status; } - return status; + return 0; } int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event) @@ -1142,8 +1423,10 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event) urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb); - if (!urb_ctx) + if (!urb_ctx) { + /* Transfer will be deleted from dequeue task */ mausb_pr_warn("Urb is already cancelled"); + } cleanup: mausb_release_event_resources(event); @@ -1279,6 +1562,7 @@ static void mausb_connect_callback(struct mausb_device *dev, if (channel == MAUSB_ISOCH_CHANNEL) { dev->channel_map[MAUSB_INTR_CHANNEL] = dev->channel_map[MAUSB_CTRL_CHANNEL]; + mausb_on_madev_connected(dev); } } @@ -1305,6 +1589,16 @@ static void mausb_handle_receive_event(struct mausb_device *dev, } mausb_reset_connection_timer(dev); + + status = mausb_msg_received_event(&event, + (struct ma_usb_hdr_common *)data, + channel); + + if (status == 0) + status = mausb_enqueue_event_to_user(dev, &event); + + if (status < 0) + mausb_pr_err("Failed to enqueue, status=%d", status); } void mausb_ip_callback(void *ctx, enum mausb_channel channel, @@ -1614,3 +1908,148 @@ u32 mausb_data_iterator_length(struct mausb_data_iter *iterator) { return iterator->length; } + +static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 1) { + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + memcpy(event, ring->to_user_buffer + ring->tail, sizeof(*event)); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->tail = (ring->tail + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +int mausb_ring_buffer_init(struct mausb_ring_buffer *ring) +{ + unsigned int page_order = + mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + ring->to_user_buffer = + (struct mausb_event *)__get_free_pages(GFP_KERNEL, page_order); + if (!ring->to_user_buffer) + return -ENOMEM; + ring->from_user_buffer = ring->to_user_buffer + MAUSB_RING_BUFFER_SIZE; + ring->head = 0; + ring->tail = 0; + ring->current_from_user = 0; + ring->buffer_full = false; + spin_lock_init(&ring->lock); + + return 0; +} + +int mausb_ring_buffer_put(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + + if (ring->buffer_full) { + mausb_pr_err("Ring buffer is full"); + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + + if (CIRC_SPACE(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 2) { + mausb_pr_err("Ring buffer capacity exceeded, disconnecting device"); + ring->buffer_full = true; + mausb_disconect_event_unsafe(ring, event->madev_addr); + ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + memcpy(ring->to_user_buffer + ring->head, event, sizeof(*event)); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < count) { + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + mausb_pr_debug("old HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->tail = (ring->tail + (int)count) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("new HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring) +{ + struct mausb_event event; + + while (mausb_ring_buffer_get(ring, &event) == 0) + mausb_cleanup_ring_buffer_event(&event); +} + +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring) +{ + unsigned int page_order = + mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + if (ring && ring->to_user_buffer) + free_pages((unsigned long)ring->to_user_buffer, page_order); +} + +void mausb_cleanup_ring_buffer_event(struct mausb_event *event) +{ + mausb_pr_debug("event=%d", event->type); + switch (event->type) { + case MAUSB_EVENT_TYPE_SEND_DATA_MSG: + mausb_cleanup_send_data_msg_event(event); + break; + case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG: + mausb_cleanup_received_data_msg_event(event); + break; + case MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER: + mausb_cleanup_delete_data_transfer_event(event); + break; + case MAUSB_EVENT_TYPE_NONE: + break; + default: + mausb_pr_warn("Unknown event type"); + break; + } +} + +void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring, + uint8_t madev_addr) +{ + struct mausb_event disconnect_event; + struct mausb_device *dev = mausb_get_dev_from_addr_unsafe(madev_addr); + + if (!dev) { + mausb_pr_err("Device not found, madev_addr=%#x", madev_addr); + return; + } + + disconnect_event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT; + disconnect_event.status = -EINVAL; + disconnect_event.madev_addr = madev_addr; + + memcpy(ring->to_user_buffer + ring->head, &disconnect_event, + sizeof(disconnect_event)); + + mausb_pr_info("Disconnect event added to ring buffer for madev_addr=%#x", + madev_addr); + mausb_pr_info("Adding hcd_disconnect_work to dev workq, madev_addr=%#x", + madev_addr); + queue_work(dev->workq, &dev->hcd_disconnect_work); +} diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h index a04ed120ba5e..24846d4bc4a3 100644 --- a/drivers/usb/mausb_host/hpal.h +++ b/drivers/usb/mausb_host/hpal.h @@ -67,6 +67,7 @@ struct mss { struct mausb_device { struct mausb_device_address dev_addr; struct net *net_ns; + struct mausb_ring_buffer *ring_buffer; struct list_head list_entry; struct mausb_ip_ctx *mgmt_channel; @@ -133,6 +134,10 @@ static inline u64 mausb_event_id(struct mausb_device *dev) int mausb_initiate_dev_connection(struct mausb_device_address device_address, u8 madev_address); +int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events, + u16 num_of_completed); +int mausb_enqueue_event_to_user(struct mausb_device *dev, + struct mausb_event *event); int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle, struct urb *request); int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event, @@ -282,6 +287,33 @@ void mausb_reset_data_iterator(struct mausb_data_iter *iterator); void mausb_uninit_data_iterator(struct mausb_data_iter *iterator); void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta); +struct mausb_ring_buffer { + atomic_t mausb_ring_events; + atomic_t mausb_completed_user_events; + + struct mausb_event *to_user_buffer; + int head; + int tail; + spinlock_t lock; /* Protect ring buffer */ + u64 id; + + struct mausb_event *from_user_buffer; + int current_from_user; + + struct list_head list_entry; + bool buffer_full; +}; + +int mausb_ring_buffer_init(struct mausb_ring_buffer *ring); +int mausb_ring_buffer_put(struct mausb_ring_buffer *ring, + struct mausb_event *event); +int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count); +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring); +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring); +void mausb_cleanup_ring_buffer_event(struct mausb_event *event); +void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring, + uint8_t madev_addr); + static inline unsigned int mausb_get_page_order(unsigned int num_of_elems, unsigned int elem_size) { @@ -292,4 +324,16 @@ static inline unsigned int mausb_get_page_order(unsigned int num_of_elems, return order; } +static inline +struct mausb_event *mausb_ring_current_from_user(struct mausb_ring_buffer *ring) +{ + return ring->from_user_buffer + ring->current_from_user; +} + +static inline void mausb_ring_next_from_user(struct mausb_ring_buffer *ring) +{ + ring->current_from_user = (ring->current_from_user + 1) & + (MAUSB_RING_BUFFER_SIZE - 1); +} + #endif /* __MAUSB_HPAL_H__ */ diff --git a/drivers/usb/mausb_host/hpal_events.c b/drivers/usb/mausb_host/hpal_events.c new file mode 100644 index 000000000000..6bec951213ea --- /dev/null +++ b/drivers/usb/mausb_host/hpal_events.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + */ +#include "hpal_events.h" + +#include <linux/slab.h> + +#include "hcd.h" +#include "utils.h" + +void mausb_dev_reset_req_event(struct mausb_event *event) +{ + event->type = MAUSB_EVENT_TYPE_DEV_RESET; +} + +static int mausb_mgmt_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + int status = 0; + + mausb_pr_info("channel=%d, type=%d", channel, hdr->type); + if (hdr->length <= MAUSB_MAX_MGMT_SIZE) { + event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG; + memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length); + } else { + mausb_pr_err("MGMT message to long, failed to copy"); + status = -EINVAL; + } + + kfree(hdr); + return status; +} + +static int mausb_data_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + event->type = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG; + event->data.transfer_type = mausb_transfer_type_from_hdr(hdr); + event->data.device_id = (u16)((hdr->ssid << 8) | hdr->dev_addr); + event->data.ep_handle = hdr->handle.epv; + event->data.recv_buf = (u64)hdr; + + memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE); + + if (mausb_ctrl_transfer(hdr) && + hdr->length <= 2 * MAUSB_TRANSFER_HDR_SIZE) { + memcpy(event->data.hdr_ack, + shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE), + (size_t)(hdr->length - MAUSB_TRANSFER_HDR_SIZE)); + } + + return 0; +} + +static int mausb_isoch_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + event->type = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG; + event->data.transfer_type = mausb_transfer_type_from_hdr(hdr); + event->data.device_id = (u16)((hdr->ssid << 8) | hdr->dev_addr); + event->data.ep_handle = hdr->handle.epv; + event->data.recv_buf = (u64)hdr; + + memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE); + + return 0; +} + +int mausb_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + mausb_pr_debug("channel=%d, type=%d", channel, hdr->type); + if (mausb_is_management_hdr_type(hdr->type)) + return mausb_mgmt_msg_received_event(event, hdr, channel); + else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(TRANSFER)) + return mausb_data_msg_received_event(event, hdr, channel); + else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(ISOCHTRANSFER)) + return mausb_isoch_msg_received_event(event, hdr, channel); + + kfree(hdr); + mausb_pr_warn("Unknown event type event=%d", hdr->type); + return -EBADR; +} + +static void mausb_prepare_completion(struct mausb_completion *mausb_completion, + struct completion *completion, + struct mausb_event *event, u64 event_id) +{ + init_completion(completion); + + mausb_completion->completion_event = completion; + mausb_completion->event_id = event_id; + mausb_completion->mausb_event = event; +} + +static int mausb_wait_for_completion(struct mausb_event *event, u64 event_id, + struct mausb_device *dev) +{ + struct completion completion; + struct mausb_completion mausb_completion; + long status; + unsigned long timeout; + + mausb_prepare_completion(&mausb_completion, &completion, event, + event_id); + mausb_insert_event(dev, &mausb_completion); + + status = mausb_enqueue_event_to_user(dev, event); + if (status < 0) { + mausb_remove_event(dev, &mausb_completion); + mausb_pr_err("Ring buffer full, event_id=%lld", event_id); + return (int)status; + } + + timeout = msecs_to_jiffies(MANAGEMENT_EVENT_TIMEOUT); + status = wait_for_completion_interruptible_timeout(&completion, + timeout); + + mausb_remove_event(dev, &mausb_completion); + + if (status == 0) { + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return -ETIMEDOUT; + } + + return 0; +} + +int mausb_usbdevhandle_event_to_user(struct mausb_device *dev, + u8 device_speed, + u32 route_string, + u16 hub_dev_handle, + u16 parent_hs_hub_dev_handle, + u16 parent_hs_hub_port, u16 mtt, + u8 lse, s32 *usb_dev_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_USB_DEV_HANDLE; + event.mgmt.dev_handle.device_speed = device_speed; + event.mgmt.dev_handle.route_string = route_string; + event.mgmt.dev_handle.hub_dev_handle = hub_dev_handle; + event.mgmt.dev_handle.parent_hs_hub_port = parent_hs_hub_port; + event.mgmt.dev_handle.mtt = mtt; + event.mgmt.dev_handle.lse = lse; + event.mgmt.dev_handle.event_id = event_id; + event.madev_addr = dev->madev_addr; + event.mgmt.dev_handle.parent_hs_hub_dev_handle = + parent_hs_hub_dev_handle; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Usbdevhandle failed, event_id=%lld", event_id); + return status; + } + + if (event.status < 0) + return event.status; + + *usb_dev_handle = event.mgmt.dev_handle.dev_handle; + + return 0; +} + +int mausb_usbdevhandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.dev_handle.event_id); +} + +int mausb_ephandle_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 descriptor_size, void *descriptor, + u16 *ep_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_EP_HANDLE; + event.mgmt.ep_handle.device_handle = device_handle; + event.mgmt.ep_handle.descriptor_size = descriptor_size; + event.mgmt.ep_handle.event_id = event_id; + event.madev_addr = dev->madev_addr; + + memcpy(event.mgmt.ep_handle.descriptor, descriptor, descriptor_size); + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Ephandle failed, event_id=%lld", event_id); + return status; + } + + if (event.status < 0) + return event.status; + + *ep_handle = event.mgmt.ep_handle.ep_handle; + + return 0; +} + +int mausb_ephandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.ep_handle.event_id); +} + +int mausb_epactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE; + event.mgmt.ep_activate.device_handle = device_handle; + event.mgmt.ep_activate.ep_handle = ep_handle; + event.mgmt.ep_activate.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Epactivate failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_epactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, + event->mgmt.ep_activate.event_id); +} + +int mausb_epinactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE; + event.mgmt.ep_inactivate.device_handle = device_handle; + event.mgmt.ep_inactivate.ep_handle = ep_handle; + event.mgmt.ep_inactivate.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Epinactivate failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_epinactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, + event->mgmt.ep_inactivate.event_id); +} + +int mausb_epreset_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle, + u8 tsp_flag) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_EP_HANDLE_RESET; + event.mgmt.ep_reset.device_handle = device_handle; + event.mgmt.ep_reset.ep_handle = ep_handle; + event.mgmt.ep_reset.tsp = tsp_flag; + event.mgmt.ep_reset.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Epreset failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_epreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.ep_reset.event_id); +} + +int mausb_epdelete_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_EP_HANDLE_DELETE; + event.mgmt.ep_delete.device_handle = device_handle; + event.mgmt.ep_delete.ep_handle = ep_handle; + event.mgmt.ep_delete.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Epdelete failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_epdelete_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.ep_delete.event_id); +} + +int mausb_modifyep0_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 *ep_handle, + __le16 max_packet_size) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_MODIFY_EP0; + event.mgmt.modify_ep0.device_handle = device_handle; + event.mgmt.modify_ep0.ep_handle = *ep_handle; + event.mgmt.modify_ep0.max_packet_size = max_packet_size; + event.mgmt.modify_ep0.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Modifyep0 failed, event_id=%lld", event_id); + return status; + } + + if (event.status < 0) + return event.status; + + *ep_handle = event.mgmt.modify_ep0.ep_handle; + + return 0; +} + +int mausb_modifyep0_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.modify_ep0.event_id); +} + +int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 response_timeout) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS; + event.mgmt.set_usb_dev_address.device_handle = device_handle; + event.mgmt.set_usb_dev_address.response_timeout = response_timeout; + event.mgmt.set_usb_dev_address.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Setusbdevaddress failed, event_id=%lld", + event_id); + return status; + } + + return event.status; +} + +int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, + event->mgmt.set_usb_dev_address.event_id); +} + +static void +mausb_init_device_descriptor(struct ma_usb_updatedevreq_desc *update_descriptor, + struct usb_device_descriptor *device_descriptor) +{ + update_descriptor->usb20.bLength = device_descriptor->bLength; + update_descriptor->usb20.bDescriptorType = + device_descriptor->bDescriptorType; + update_descriptor->usb20.bcdUSB = device_descriptor->bcdUSB; + update_descriptor->usb20.bDeviceClass = + device_descriptor->bDeviceClass; + update_descriptor->usb20.bDeviceSubClass = + device_descriptor->bDeviceSubClass; + update_descriptor->usb20.bDeviceProtocol = + device_descriptor->bDeviceProtocol; + update_descriptor->usb20.bMaxPacketSize0 = + device_descriptor->bMaxPacketSize0; + update_descriptor->usb20.idVendor = device_descriptor->idVendor; + update_descriptor->usb20.idProduct = device_descriptor->idProduct; + update_descriptor->usb20.bcdDevice = device_descriptor->bcdDevice; + update_descriptor->usb20.iManufacturer = + device_descriptor->iManufacturer; + update_descriptor->usb20.iProduct = device_descriptor->iProduct; + update_descriptor->usb20.iSerialNumber = + device_descriptor->iSerialNumber; + update_descriptor->usb20.bNumConfigurations = + device_descriptor->bNumConfigurations; +} + +int mausb_updatedev_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 max_exit_latency, u8 hub, + u8 number_of_ports, u8 mtt, + u8 ttt, u8 integrated_hub_latency, + struct usb_device_descriptor *dev_descriptor) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_UPDATE_DEV; + event.mgmt.update_dev.device_handle = device_handle; + event.mgmt.update_dev.max_exit_latency = max_exit_latency; + event.mgmt.update_dev.hub = hub; + event.mgmt.update_dev.number_of_ports = number_of_ports; + event.mgmt.update_dev.mtt = mtt; + event.mgmt.update_dev.ttt = ttt; + event.mgmt.update_dev.integrated_hub_latency = integrated_hub_latency; + event.mgmt.update_dev.event_id = event_id; + event.madev_addr = dev->madev_addr; + + mausb_init_device_descriptor(&event.mgmt.update_dev.update_descriptor, + dev_descriptor); + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Updatedev failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_updatedev_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, event->mgmt.update_dev.event_id); +} + +int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev, + u16 dev_handle) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT; + event.mgmt.usb_dev_disconnect.device_handle = dev_handle; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) + mausb_pr_err("Ring buffer full, usbdevdisconnect failed"); + + return status; +} + +int mausb_ping_event_to_user(struct mausb_device *dev) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_PING; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) + mausb_pr_err("Ring buffer full, devdisconnect failed"); + + return status; +} + +__attribute__((unused)) +static int mausb_devdisconnect_event_to_user(struct mausb_device *dev) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) + mausb_pr_err("Ring buffer full, devdisconnect failed"); + + return status; +} + +int mausb_usbdevreset_event_to_user(struct mausb_device *dev, + u16 device_handle) +{ + struct mausb_event event; + int status; + u64 event_id = mausb_event_id(dev); + + event.type = MAUSB_EVENT_TYPE_USB_DEV_RESET; + event.mgmt.usb_dev_reset.device_handle = device_handle; + event.mgmt.usb_dev_reset.event_id = event_id; + event.madev_addr = dev->madev_addr; + + status = mausb_wait_for_completion(&event, event_id, dev); + + if (status < 0) { + mausb_pr_err("Usbdevreset failed, event_id=%lld", event_id); + return status; + } + + return event.status; +} + +int mausb_usbdevreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + return mausb_signal_event(dev, event, + event->mgmt.usb_dev_reset.event_id); +} + +int mausb_canceltransfer_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 ep_handle, u64 urb) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER; + event.mgmt.cancel_transfer.device_handle = device_handle; + event.mgmt.cancel_transfer.ep_handle = ep_handle; + event.mgmt.cancel_transfer.urb = urb; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) { + mausb_pr_err("Ring buffer full, canceltransfer failed"); + return status; + } + + return status; +} + +int mausb_canceltransfer_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug(""); + return 0; +} + +void mausb_cleanup_send_data_msg_event(struct mausb_event *event) +{ + mausb_complete_urb(event); +} + +void mausb_cleanup_received_data_msg_event(struct mausb_event *event) +{ + mausb_release_event_resources(event); +} + +void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + struct mausb_urb_ctx *urb_ctx; + int status = 0; + + urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status); + if (!urb_ctx) { + mausb_pr_warn("Urb=%p is not in tree", urb); + return; + } + + /* Deallocate urb_ctx */ + mausb_uninit_data_iterator(&urb_ctx->iterator); + kfree(urb_ctx); + + urb->status = -EPROTO; + urb->actual_length = 0; + atomic_dec(&urb->use_count); + usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status); +} diff --git a/drivers/usb/mausb_host/hpal_events.h b/drivers/usb/mausb_host/hpal_events.h new file mode 100644 index 000000000000..fb424d526b12 --- /dev/null +++ b/drivers/usb/mausb_host/hpal_events.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + */ +#ifndef __MAUSB_HPAL_EVENTS_H__ +#define __MAUSB_HPAL_EVENTS_H__ + +#include "hpal.h" +#include "ip_link.h" +#include "mausb_event.h" + +#define MANAGEMENT_EVENT_TIMEOUT 3000 + +int mausb_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel); +int mausb_usbdevhandle_event_to_user(struct mausb_device *dev, + u8 device_speed, + u32 route_string, + u16 hub_dev_handle, + u16 parent_hs_hub_dev_handle, + u16 parent_hs_hub_port, u16 mtt, + u8 lse, s32 *usb_dev_handle); +int mausb_usbdevhandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_ephandle_event_to_user(struct mausb_device *dev, u16 device_handle, + u16 descriptor_size, void *descriptor, + u16 *ep_handle); +int mausb_ephandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle); +int mausb_epactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epinactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 ep_handle); +int mausb_epinactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epreset_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle, + u8 tsp_flag); +int mausb_epreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epdelete_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle); +int mausb_epdelete_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_modifyep0_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 *ep_handle, + __le16 max_packet_size); +int mausb_modifyep0_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 response_timeout); +int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_updatedev_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 max_exit_latency, u8 hub, + u8 number_of_ports, u8 mtt, + u8 ttt, u8 integrated_hub_latency, + struct usb_device_descriptor *dev_descriptor); +int mausb_updatedev_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev, + u16 dev_handle); +int mausb_ping_event_to_user(struct mausb_device *dev); +int mausb_usbdevreset_event_to_user(struct mausb_device *dev, + u16 device_handle); +int mausb_usbdevreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_canceltransfer_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 ep_handle, u64 urb); +int mausb_canceltransfer_event_from_user(struct mausb_device *dev, + struct mausb_event *event); + +void mausb_dev_reset_req_event(struct mausb_event *event); +void mausb_cleanup_send_data_msg_event(struct mausb_event *event); +void mausb_cleanup_received_data_msg_event(struct mausb_event *event); +void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event); + +#endif /* __MAUSB_HPAL_EVENTS_H__ */ diff --git a/drivers/usb/mausb_host/mausb_driver_status.h b/drivers/usb/mausb_host/mausb_driver_status.h new file mode 100644 index 000000000000..9b55befe9cae --- /dev/null +++ b/drivers/usb/mausb_host/mausb_driver_status.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + */ +#ifndef __MAUSB_MAUSB_DRIVER_STATUS_H__ +#define __MAUSB_MAUSB_DRIVER_STATUS_H__ + +#define MAUSB_DRIVER_BAD_READ_BUFFER_SIZE -1 +#define MAUSB_DRIVER_RING_EVENTS_STOPPED -2 +#define MAUSB_DRIVER_SYSTEM_SUSPENDED -3 +#define MAUSB_DRIVER_COPY_TO_BUFFER_FAILED -4 + +#define MAUSB_DRIVER_READ_TIMEOUT 0 +#define MAUSB_DRIVER_READ_ERROR -1 +#define MAUSB_DRIVER_WRITE_ERROR -2 + +#endif /* __MAUSB_MAUSB_DRIVER_STATUS_H__ */ diff --git a/drivers/usb/mausb_host/utils.c b/drivers/usb/mausb_host/utils.c index c055b578e402..643671821709 100644 --- a/drivers/usb/mausb_host/utils.c +++ b/drivers/usb/mausb_host/utils.c @@ -13,6 +13,8 @@ #include <linux/slab.h> #include <linux/uaccess.h> +#include "mausb_driver_status.h" + #define MAUSB_KERNEL_DEV_NAME "mausb_host" #define MAUSB_READ_DEVICE_TIMEOUT_MS 500 @@ -20,6 +22,47 @@ static dev_t mausb_major_kernel; static struct cdev mausb_kernel_dev; static struct class *mausb_kernel_class; +static void mausb_vm_open(struct vm_area_struct *vma) +{ + mausb_pr_debug(""); +} + +static void mausb_vm_close(struct vm_area_struct *vma) +{ + struct mausb_ring_buffer *buffer = NULL, *next = NULL; + unsigned long flags = 0; + u64 ring_buffer_id = *(u64 *)(vma->vm_private_data); + + mausb_pr_info("Releasing ring buffer with id: %llu", ring_buffer_id); + spin_lock_irqsave(&mss.lock, flags); + list_for_each_entry_safe(buffer, next, &mss.available_ring_buffers, + list_entry) { + if (buffer->id == ring_buffer_id) { + list_del(&buffer->list_entry); + mausb_ring_buffer_destroy(buffer); + kfree(buffer); + break; + } + } + spin_unlock_irqrestore(&mss.lock, flags); +} + +/* + * mausb_vm_fault is called the first time a memory area is accessed which is + * not in memory + */ +static vm_fault_t mausb_vm_fault(struct vm_fault *vmf) +{ + mausb_pr_debug(""); + return 0; +} + +static const struct vm_operations_struct mausb_vm_ops = { + .open = mausb_vm_open, + .close = mausb_vm_close, + .fault = mausb_vm_fault, +}; + static int mausb_file_open(struct inode *inode, struct file *filp) { filp->private_data = NULL; @@ -35,9 +78,215 @@ static int mausb_file_close(struct inode *inode, struct file *filp) return 0; } +static ssize_t mausb_file_read(struct file *filp, char __user *user_buffer, + size_t size, loff_t *offset) +{ + ssize_t num_of_bytes_to_read = MAUSB_MAX_NUM_OF_MA_DEVS * + sizeof(struct mausb_events_notification); + unsigned long num_of_bytes_not_copied; + int completed_events; + int ring_events; + struct mausb_ring_buffer *ring_buffer; + struct mausb_device *dev; + struct completion *ring_has_events; + u8 current_device = 0; + s8 fail_ret_val; + unsigned long flags; + unsigned long timeout; + long status; + + /* Reset heartbeat timer events */ + mausb_reset_heartbeat_cnt(); + + if ((ssize_t)size != num_of_bytes_to_read) { + mausb_pr_alert("Different expected bytes to read (%ld) from actual size (%ld)", + num_of_bytes_to_read, size); + fail_ret_val = MAUSB_DRIVER_BAD_READ_BUFFER_SIZE; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + /* If suspend/hibernate happened delete all devices */ + if (atomic_xchg(&mss.num_of_transitions_to_sleep, 0)) { + mausb_pr_alert("Suspend system event detected"); + fail_ret_val = MAUSB_DRIVER_SYSTEM_SUSPENDED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + ring_has_events = &mss.rings_events.mausb_ring_has_events; + timeout = msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS); + status = wait_for_completion_interruptible_timeout(ring_has_events, + timeout); + reinit_completion(ring_has_events); + + if (atomic_read(&mss.rings_events.mausb_stop_reading_ring_events)) { + mausb_pr_alert("Ring events stopped"); + fail_ret_val = MAUSB_DRIVER_RING_EVENTS_STOPPED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + /* There are no new events - waiting for events hit timeout */ + if (status == 0) + return MAUSB_DRIVER_READ_TIMEOUT; + + spin_lock_irqsave(&mss.lock, flags); + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mss.events[current_device].madev_addr = dev->madev_addr; + ring_buffer = dev->ring_buffer; + ring_events = atomic_xchg(&ring_buffer->mausb_ring_events, 0); + completed_events = + atomic_xchg(&ring_buffer->mausb_completed_user_events, + 0); + mss.events[current_device].num_of_events = (u16)ring_events; + mss.events[current_device].num_of_completed_events = + (u16)completed_events; + if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS) + break; + } + + spin_unlock_irqrestore(&mss.lock, flags); + + num_of_bytes_to_read = + (ssize_t)(current_device * + sizeof(struct mausb_events_notification)); + num_of_bytes_not_copied = + copy_to_user(user_buffer, &mss.events, + (unsigned long)num_of_bytes_to_read); + + mausb_pr_debug("num_of_bytes_not_copied %ld, num_of_bytes_to_read %ld", + num_of_bytes_not_copied, num_of_bytes_to_read); + + if (num_of_bytes_not_copied) { + fail_ret_val = MAUSB_DRIVER_COPY_TO_BUFFER_FAILED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + return num_of_bytes_to_read; +} + +static ssize_t mausb_file_write(struct file *filp, const char __user *buffer, + size_t size, loff_t *offset) +{ + ssize_t num_of_bytes_to_write = + sizeof(struct mausb_events_notification); + struct mausb_events_notification notification; + unsigned long flags; + struct mausb_device *dev; + + if (size != (size_t)num_of_bytes_to_write) { + mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)", + num_of_bytes_to_write, size); + return MAUSB_DRIVER_WRITE_ERROR; + } + + if (copy_from_user(¬ification, buffer, size)) + return MAUSB_DRIVER_WRITE_ERROR; + + spin_lock_irqsave(&mss.lock, flags); + dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr); + + if (!dev) { + spin_unlock_irqrestore(&mss.lock, flags); + return 0; + } + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + dev->num_of_user_events += notification.num_of_events; + dev->num_of_completed_events += notification.num_of_completed_events; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + + queue_work(dev->workq, &dev->work); + spin_unlock_irqrestore(&mss.lock, flags); + + return num_of_bytes_to_write; +} + +static inline unsigned long mausb_ring_buffer_length(void) +{ + int page_order = mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + return PAGE_SIZE << page_order; +} + +static int mausb_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start; + int ret; + struct page *page = NULL; + unsigned long flags = 0; + struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer), + GFP_KERNEL); + if (!ring_buffer) + return -ENOMEM; + + ret = mausb_ring_buffer_init(ring_buffer); + if (ret < 0) { + mausb_pr_err("Ring buffer init failed"); + goto release_ring_buffer; + } + + vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL); + if (!vma->vm_private_data) { + ret = -ENOMEM; + goto release_ring_buffer; + } + + filp->private_data = vma->vm_private_data; + + if (size > mausb_ring_buffer_length()) { + mausb_pr_err("Invalid memory size to map"); + ret = -EINVAL; + goto release_ring_buffer; + } + + vma->vm_ops = &mausb_vm_ops; + mausb_vm_open(vma); + + page = virt_to_page(ring_buffer->to_user_buffer); + ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, + vma->vm_page_prot); + if (ret < 0) { + mausb_pr_err("Could not map the address area"); + goto release_ring_buffer; + } + + spin_lock_irqsave(&mss.lock, flags); + ring_buffer->id = mss.ring_buffer_id++; + *(u64 *)(vma->vm_private_data) = ring_buffer->id; + list_add_tail(&ring_buffer->list_entry, &mss.available_ring_buffers); + mausb_pr_info("Allocated ring buffer with id: %llu", ring_buffer->id); + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; + +release_ring_buffer: + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return ret; +} + static const struct file_operations mausb_file_ops = { .open = mausb_file_open, .release = mausb_file_close, + .read = mausb_file_read, + .write = mausb_file_write, + .mmap = mausb_mmap, }; int mausb_create_dev(void) @@ -83,3 +332,29 @@ void mausb_cleanup_dev(int device_created) unregister_chrdev_region(mausb_major_kernel, 1); } + +void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer) +{ + int completed; + + completed = + atomic_inc_return(&ring_buffer->mausb_completed_user_events); + mausb_pr_debug("mausb_completed_user_events INCREMENTED %d", completed); + if (completed > MAUSB_RING_BUFFER_SIZE / 16) + complete(&mss.rings_events.mausb_ring_has_events); +} + +void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer) +{ + int events; + + events = atomic_inc_return(&ring_buffer->mausb_ring_events); + if (events == 1) + complete(&mss.rings_events.mausb_ring_has_events); +} + +void mausb_stop_ring_events(void) +{ + atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1); + complete(&mss.rings_events.mausb_ring_has_events); +} diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h index 699f94fcb75b..e3ddb12afadd 100644 --- a/drivers/usb/mausb_host/utils.h +++ b/drivers/usb/mausb_host/utils.h @@ -5,6 +5,8 @@ #ifndef __MAUSB_UTILS_H__ #define __MAUSB_UTILS_H__ +#include "hpal.h" + #if defined(MAUSB_NO_LOGS) #define mausb_pr_logs(...) #else @@ -36,5 +38,8 @@ int mausb_create_dev(void); void mausb_cleanup_dev(int device_created); +void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer); +void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer); +void mausb_stop_ring_events(void); #endif /* __MAUSB_UTILS_H__ */ -- 2.17.1