Implemeneted connection between PAL and Link layers and set up
environment for exchanging PAL-to-PAL messages.

Within this patch, driver's sysfs parameters have been created
with intention to configure remote connection parameters.

Signed-off-by: Vladimir Stankovic <[email protected]>
---
 drivers/usb/host/mausb/hpal.c       | 529 ++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hpal.h       |   6 +
 drivers/usb/host/mausb/mausb_core.c | 140 ++++++++
 3 files changed, 675 insertions(+)

diff --git a/drivers/usb/host/mausb/hpal.c b/drivers/usb/host/mausb/hpal.c
index 5368c500a320..be600e8c30ec 100644
--- a/drivers/usb/host/mausb/hpal.c
+++ b/drivers/usb/host/mausb/hpal.c
@@ -9,10 +9,15 @@
 #include "hcd.h"
 #include "utils.h"
 
+#define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
+
 struct mss mss;
 
+static int mausb_start_connection_timer(struct mausb_device *dev);
 static int mausb_power_state_cb(struct notifier_block *nb, unsigned long 
action,
                                void *data);
+static void mausb_signal_empty_mss(void);
+static void mausb_remove_madev_from_list(u8 madev_addr);
 static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
 static int mausb_start_heartbeat_timer(void);
 
@@ -173,6 +178,56 @@ static bool mausb_return_urb_ctx_to_tree(struct 
mausb_urb_ctx *urb_ctx,
        return true;
 }
 
+static void mausb_complete_urbs_from_tree(void)
+{
+       struct mausb_urb_ctx *urb_ctx = NULL;
+       struct urb           *current_urb = NULL;
+       struct rb_node       *current_node = NULL;
+       unsigned long flags;
+       int status = 0;
+       int ret;
+
+       dev_dbg(mausb_host_dev.this_device, "Completing all urbs from tree");
+
+       spin_lock_irqsave(&mhcd->lock, flags);
+
+       while ((current_node = rb_first(&mhcd->mausb_urbs))) {
+               urb_ctx = rb_entry(current_node, struct mausb_urb_ctx, rb_node);
+
+               current_urb = urb_ctx->urb;
+               mausb_delete_urb_ctx_from_tree(urb_ctx);
+               mausb_uninit_data_iterator(&urb_ctx->iterator);
+               kfree(urb_ctx);
+
+               ret = usb_hcd_check_unlink_urb(current_urb->hcpriv,
+                                              current_urb, status);
+               if (ret == -EIDRM)
+                       dev_warn(mausb_host_dev.this_device, "Urb=%p is already 
unlinked",
+                                current_urb);
+               else
+                       usb_hcd_unlink_urb_from_ep(current_urb->hcpriv,
+                                                  current_urb);
+
+               spin_unlock_irqrestore(&mhcd->lock, flags);
+
+               /* Prepare urb for completion */
+               dev_dbg(mausb_host_dev.this_device, "Completing urb=%p",
+                       current_urb);
+
+               current_urb->status        = -EPROTO;
+               current_urb->actual_length = 0;
+               atomic_dec(&current_urb->use_count);
+               usb_hcd_giveback_urb(current_urb->hcpriv, current_urb,
+                                    current_urb->status);
+
+               spin_lock_irqsave(&mhcd->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&mhcd->lock, flags);
+
+       dev_dbg(mausb_host_dev.this_device, "Completed all urbs from tree");
+}
+
 /*After this function call only valid thing to do with urb is to give it back*/
 struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
                                                            int status)
@@ -280,6 +335,164 @@ static inline void mausb_port_has_changed_event(struct 
mausb_device *dev,
                mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+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;
+
+       dev_info(mausb_host_dev.this_device, "Disconnect madev_addr=%d",
+                dev->madev_addr);
+
+       mausb_ip_disconnect(dev->ctrl_channel);
+       mausb_destroy_ip_ctx(dev->ctrl_channel);
+       dev->ctrl_channel = NULL;
+
+       mausb_ip_disconnect(dev->bulk_channel);
+       mausb_destroy_ip_ctx(dev->bulk_channel);
+       dev->bulk_channel = NULL;
+
+       mausb_ip_disconnect(dev->isoch_channel);
+       mausb_destroy_ip_ctx(dev->isoch_channel);
+       dev->isoch_channel = NULL;
+
+       if (dev->mgmt_channel) {
+               memset(&event, 0, sizeof(event));
+               event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
+               event.data.device_id = dev->id;
+
+               dev_info(mausb_host_dev.this_device, "Releasing MAUSB device 
ref");
+               kref_put(&dev->refcount, mausb_release_ma_dev_async);
+       }
+
+       mausb_ip_disconnect(dev->mgmt_channel);
+       mausb_destroy_ip_ctx(dev->mgmt_channel);
+       dev->mgmt_channel = NULL;
+
+       memset(dev->channel_map, 0, sizeof(dev->channel_map));
+}
+
+static void mausb_disconnect_ma_dev(struct mausb_device *dev)
+{
+       dev_info(mausb_host_dev.this_device, "Disconnecting MAUSB device 
madev_addr=%d",
+                dev->madev_addr);
+
+       if (!dev->dev_connected) {
+               dev_warn(mausb_host_dev.this_device, "MAUSB device is not 
connected");
+               kref_put(&dev->refcount, mausb_release_ma_dev_async);
+               return;
+       }
+       mausb_hcd_disconnect(dev->port_number, dev->dev_type, dev->dev_speed);
+
+       if (dev->dev_type == USB30HUB)
+               mausb_hcd_disconnect(dev->port_number, USB20HUB, HIGH_SPEED);
+}
+
+static void mausb_hcd_disconnect_event(struct work_struct *work)
+{
+       struct mausb_device *ma_dev = container_of(work, struct mausb_device,
+                                                  hcd_disconnect_work);
+
+       mausb_disconnect_ma_dev(ma_dev);
+}
+
+static void mausb_delete_madev(struct work_struct *work)
+{
+       struct mausb_device *dev = container_of(work, struct mausb_device,
+                                               madev_delete_work);
+       struct mausb_event      event;
+       struct completion       completion;
+       struct completion       *user_event;
+       struct mausb_completion mausb_completion;
+       long status;
+       unsigned long timeout = msecs_to_jiffies(MAUSB_DELETE_MADEV_TIMEOUT_MS);
+
+       dev_info(mausb_host_dev.this_device, "Deleting MAUSB device 
madev_addr=%d",
+                dev->madev_addr);
+
+       del_timer_sync(&dev->connection_timer);
+
+       /* Client IS responsive */
+       if (!atomic_read(&dev->unresponsive_client)) {
+               memset(&event, 0, sizeof(event));
+               event.type = MAUSB_EVENT_TYPE_DELETE_MA_DEV;
+               event.mgmt.delete_ma_dev.device_id = dev->id;
+               event.mgmt.delete_ma_dev.event_id  = mausb_event_id(dev);
+
+               init_completion(&completion);
+               mausb_completion.completion_event = &completion;
+               mausb_completion.event_id = event.mgmt.delete_ma_dev.event_id;
+               mausb_completion.mausb_event = &event;
+
+               mausb_insert_event(dev, &mausb_completion);
+
+               dev_dbg(mausb_host_dev.this_device, "Deleting MAUSB device...");
+
+               status = wait_for_completion_interruptible_timeout(&completion,
+                                                                  timeout);
+
+               dev_dbg(mausb_host_dev.this_device, "Deleting MAUSB device 
event finished with %ld",
+                       status);
+
+               mausb_remove_event(dev, &mausb_completion);
+
+               user_event = &dev->user_finished_event;
+
+               status = wait_for_completion_interruptible_timeout(user_event,
+                                                                  timeout);
+               dev_info(mausb_host_dev.this_device, "User event finished with 
%ld",
+                        status);
+       }
+
+       flush_workqueue(dev->workq);
+       destroy_workqueue(dev->workq);
+
+       mausb_clear_hcd_madev(dev->port_number);
+
+       mausb_remove_madev_from_list(dev->madev_addr);
+
+       put_net(dev->net_ns);
+
+       kfree(dev);
+       mausb_signal_empty_mss();
+
+       dev_dbg(mausb_host_dev.this_device, "MAUSB device deleted.");
+}
+
+static void mausb_ping_work(struct work_struct *work)
+{
+       struct mausb_device *dev = container_of(work, struct mausb_device,
+                                               ping_work);
+
+       if (mausb_start_connection_timer(dev) < 0) {
+               dev_err(mausb_host_dev.this_device, "Session timeout - 
disconnnecting device madev_addr=%d",
+                       dev->madev_addr);
+               queue_work(dev->workq, &dev->socket_disconnect_work);
+               queue_work(dev->workq, &dev->hcd_disconnect_work);
+               return;
+       }
+}
+
+static void mausb_heartbeat_work(struct work_struct *work)
+{
+       struct mausb_device *dev = container_of(work, struct mausb_device,
+                                               heartbeat_work);
+
+       dev_err(mausb_host_dev.this_device, "App is unresponsive - disconnect 
device");
+       atomic_set(&dev->unresponsive_client, 1);
+       mausb_complete_urbs_from_tree();
+       queue_work(dev->workq, &dev->socket_disconnect_work);
+       queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
+
+static void mausb_connection_timer_func(struct timer_list *timer)
+{
+       struct mausb_device *dev = container_of(timer, struct mausb_device,
+                                               connection_timer);
+
+       queue_work(dev->workq, &dev->ping_work);
+}
+
 static void mausb_heartbeat_timer_func(struct timer_list *timer)
 {
        unsigned long flags = 0;
@@ -304,6 +517,99 @@ static void mausb_heartbeat_timer_func(struct timer_list 
*timer)
        }
 }
 
+static struct mausb_device *
+mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
+                  int *status)
+{
+       struct mausb_device *dev;
+       unsigned long flags = 0;
+       char workq_name[16];
+       struct workqueue_struct *workq;
+
+       memset(workq_name, 0, sizeof(workq_name));
+       sprintf(workq_name, "%x", madev_address);
+       strcat(workq_name, "_madev_workq");
+
+       dev_vdbg(mausb_host_dev.this_device, "madev_workq_name = %s",
+                workq_name);
+
+       workq = alloc_ordered_workqueue(workq_name, WQ_MEM_RECLAIM);
+       if (!workq) {
+               *status = -ENOMEM;
+               return NULL;
+       }
+
+       spin_lock_irqsave(&mss.lock, flags);
+
+       if (mss.deinit_in_progress) {
+               spin_unlock_irqrestore(&mss.lock, flags);
+               dev_alert(mausb_host_dev.this_device, "Device creating failed - 
mss deinit in progress");
+               flush_workqueue(workq);
+               destroy_workqueue(workq);
+               *status = -ESHUTDOWN;
+               return NULL;
+       }
+
+       dev = mausb_get_dev_from_addr_unsafe(madev_address);
+       if (dev) {
+               spin_unlock_irqrestore(&mss.lock, flags);
+               dev_warn(mausb_host_dev.this_device, "MAUSB device already 
connected, madev_address=%x",
+                        madev_address);
+               flush_workqueue(workq);
+               destroy_workqueue(workq);
+               *status = -EEXIST;
+               return NULL;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+
+       if (!dev) {
+               spin_unlock_irqrestore(&mss.lock, flags);
+               dev_alert(mausb_host_dev.this_device, "Could not allocate MAUSB 
device!");
+               flush_workqueue(workq);
+               destroy_workqueue(workq);
+               *status = -ENOMEM;
+               return NULL;
+       }
+
+       dev_dbg(mausb_host_dev.this_device, "Create MAUSB device.");
+
+       dev->workq = workq;
+
+       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);
+       INIT_WORK(&dev->ping_work, mausb_ping_work);
+       INIT_WORK(&dev->heartbeat_work, mausb_heartbeat_work);
+
+       kref_init(&dev->refcount);
+
+       dev->event_id = 0;
+       spin_lock_init(&dev->event_id_lock);
+
+       INIT_LIST_HEAD(&dev->completion_events);
+       spin_lock_init(&dev->completion_events_lock);
+       spin_lock_init(&dev->num_of_user_events_lock);
+       spin_lock_init(&dev->connection_timer_lock);
+
+       init_completion(&dev->user_finished_event);
+       atomic_set(&dev->unresponsive_client, 0);
+
+       timer_setup(&dev->connection_timer, mausb_connection_timer_func, 0);
+
+       dev->dev_addr = dev_addr;
+       dev->madev_addr = madev_address;
+       dev->net_ns = get_net(current->nsproxy->net_ns);
+
+       list_add_tail(&dev->list_entry, &mss.madev_list);
+
+       reinit_completion(&mss.empty);
+
+       spin_unlock_irqrestore(&mss.lock, flags);
+
+       return dev;
+}
+
 void mausb_release_ma_dev_async(struct kref *kref)
 {
        struct mausb_device *dev = container_of(kref, struct mausb_device,
@@ -314,6 +620,46 @@ void mausb_release_ma_dev_async(struct kref *kref)
        schedule_work(&dev->madev_delete_work);
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
+                                 u8 madev_address)
+{
+       int error = 0;
+       struct mausb_device *dev;
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&mss.lock, flags);
+       dev = mausb_get_dev_from_addr_unsafe(madev_address);
+       spin_unlock_irqrestore(&mss.lock, flags);
+
+       if (dev) {
+               dev_warn(mausb_host_dev.this_device, "MAUSB device already 
connected, madev_address=%x",
+                        madev_address);
+               return -EEXIST;
+       }
+
+       dev = mausb_create_madev(dev_addr, madev_address, &error);
+       if (!dev)
+               return error;
+
+       dev_info(mausb_host_dev.this_device, "New MAUSB device created 
madev_addr=%d",
+                madev_address);
+
+       error = mausb_init_ip_ctx(&dev->mgmt_channel, dev->net_ns,
+                                 dev->dev_addr.ip.address,
+                                 dev->dev_addr.ip.port.management, dev,
+                                 mausb_ip_callback, MAUSB_MGMT_CHANNEL);
+       if (error) {
+               dev_err(mausb_host_dev.this_device, "Mgmt ip context init 
failed: error=%d",
+                       error);
+               kref_put(&dev->refcount, mausb_release_ma_dev_async);
+               return error;
+       }
+
+       mausb_ip_connect_async(dev->mgmt_channel);
+
+       return 0;
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
                                  u16 num_of_completed)
 {
@@ -414,6 +760,26 @@ int mausb_signal_event(struct mausb_device *dev,
        return -ETIMEDOUT;
 }
 
+static int mausb_start_connection_timer(struct mausb_device *dev)
+{
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&dev->connection_timer_lock, flags);
+
+       if (++dev->receive_failures_num > MAUSB_MAX_RECEIVE_FAILURES) {
+               dev_err(mausb_host_dev.this_device, "Missed more than %d ping 
responses",
+                       MAUSB_MAX_RECEIVE_FAILURES);
+               spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+               return -ETIMEDOUT;
+       }
+
+       mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+       spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+
+       return 0;
+}
+
 void mausb_reset_connection_timer(struct mausb_device *dev)
 {
        unsigned long flags = 0;
@@ -658,6 +1024,36 @@ struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 
madev_addr)
        return NULL;
 }
 
+static void mausb_remove_madev_from_list(u8 madev_addr)
+{
+       unsigned long flags = 0;
+       struct mausb_device *ma_dev, *tmp = NULL;
+
+       spin_lock_irqsave(&mss.lock, flags);
+
+       list_for_each_entry_safe(ma_dev, tmp, &mss.madev_list, list_entry) {
+               if (ma_dev->madev_addr == madev_addr) {
+                       list_del(&ma_dev->list_entry);
+                       break;
+               }
+       }
+
+       if (list_empty(&mss.madev_list))
+               reinit_completion(&mss.rings_events.mausb_ring_has_events);
+
+       spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_signal_empty_mss(void)
+{
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&mss.lock, flags);
+       if (list_empty(&mss.madev_list))
+               complete(&mss.empty);
+       spin_unlock_irqrestore(&mss.lock, flags);
+}
+
 static inline
 struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
                                            enum mausb_channel channel)
@@ -812,6 +1208,139 @@ void mausb_cleanup_chunks_list(struct list_head 
*chunks_list)
        }
 }
 
+static void mausb_init_ip_ctx_helper(struct mausb_device *dev,
+                                    struct mausb_ip_ctx **ip_ctx,
+                                    u16 port,
+                                    enum mausb_channel channel)
+{
+       int status = mausb_init_ip_ctx(ip_ctx, dev->net_ns,
+                                      dev->dev_addr.ip.address, port, dev,
+                                      mausb_ip_callback, channel);
+       if (status < 0) {
+               dev_err(mausb_host_dev.this_device, "Init ip context failed 
with error=%d",
+                       status);
+               queue_work(dev->workq, &dev->socket_disconnect_work);
+               return;
+       }
+
+       dev->channel_map[channel] = *ip_ctx;
+       mausb_ip_connect_async(*ip_ctx);
+}
+
+static void mausb_connect_callback(struct mausb_device *dev,
+                                  enum mausb_channel channel, int status)
+{
+       struct mausb_device_address *dev_addr = &dev->dev_addr;
+
+       dev_info(mausb_host_dev.this_device, "Connect callback for channel=%d 
with status=%d",
+                     channel, status);
+
+       if (status < 0) {
+               queue_work(dev->workq, &dev->socket_disconnect_work);
+               return;
+       }
+
+       if (channel == MAUSB_MGMT_CHANNEL) {
+               if (dev_addr->ip.port.control == 0) {
+                       dev->channel_map[MAUSB_CTRL_CHANNEL] =
+                               dev->mgmt_channel;
+                       channel = MAUSB_CTRL_CHANNEL;
+               } else {
+                       mausb_init_ip_ctx_helper(dev, &dev->ctrl_channel,
+                                                dev_addr->ip.port.control,
+                                                MAUSB_CTRL_CHANNEL);
+                       return;
+               }
+       }
+
+       if (channel == MAUSB_CTRL_CHANNEL) {
+               if (dev_addr->ip.port.bulk == 0) {
+                       dev->channel_map[MAUSB_BULK_CHANNEL] =
+                               dev->channel_map[MAUSB_CTRL_CHANNEL];
+                       channel = MAUSB_BULK_CHANNEL;
+               } else {
+                       mausb_init_ip_ctx_helper(dev, &dev->bulk_channel,
+                                                dev_addr->ip.port.bulk,
+                                                MAUSB_BULK_CHANNEL);
+                       return;
+               }
+       }
+
+       if (channel == MAUSB_BULK_CHANNEL) {
+               if (dev_addr->ip.port.isochronous == 0) {
+                       /* if there is no isoch port use tcp for it */
+                       dev->channel_map[MAUSB_ISOCH_CHANNEL] =
+                               dev->channel_map[MAUSB_BULK_CHANNEL];
+                       channel = MAUSB_ISOCH_CHANNEL;
+               } else {
+                       mausb_init_ip_ctx_helper(dev, &dev->isoch_channel,
+                                                dev_addr->ip.port.isochronous,
+                                                MAUSB_ISOCH_CHANNEL);
+                       return;
+               }
+       }
+
+       if (channel == MAUSB_ISOCH_CHANNEL) {
+               dev->channel_map[MAUSB_INTR_CHANNEL] =
+                               dev->channel_map[MAUSB_CTRL_CHANNEL];
+       }
+}
+
+static void mausb_handle_connect_event(struct mausb_device *dev,
+                                      enum mausb_channel channel, int status,
+                                      void *data)
+{
+       mausb_connect_callback(dev, channel, status);
+}
+
+static void mausb_handle_receive_event(struct mausb_device *dev,
+                                      enum mausb_channel channel, int status,
+                                      void *data)
+{
+       struct mausb_event event;
+
+       event.madev_addr = dev->madev_addr;
+
+       if (status <= 0) {
+               dev_err(mausb_host_dev.this_device, "Receive event error 
status=%d",
+                       status);
+               queue_work(dev->workq, &dev->socket_disconnect_work);
+               queue_work(dev->workq, &dev->hcd_disconnect_work);
+               return;
+       }
+
+       mausb_reset_connection_timer(dev);
+}
+
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+                      enum mausb_link_action action, int status, void *data)
+{
+       struct mausb_device *dev = (struct mausb_device *)ctx;
+
+       switch (action) {
+       case MAUSB_LINK_CONNECT:
+               mausb_handle_connect_event(dev, channel, status, data);
+               break;
+       case MAUSB_LINK_SEND:
+               /*
+                * Currently there is nothing to do, as send operation is
+                * synchronous
+                */
+               break;
+       case MAUSB_LINK_RECV:
+               mausb_handle_receive_event(dev, channel, status, data);
+               break;
+       case MAUSB_LINK_DISCONNECT:
+               /*
+                * Currently there is nothing to do, as disconnect operation is
+                * synchronous
+                */
+               break;
+       default:
+               dev_warn(mausb_host_dev.this_device, "Unknown network action");
+       }
+}
+
 static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
                                     u32 byte_num,
                                     struct list_head *data_chunks_list,
diff --git a/drivers/usb/host/mausb/hpal.h b/drivers/usb/host/mausb/hpal.h
index f184bbc07783..a04ed120ba5e 100644
--- a/drivers/usb/host/mausb/hpal.h
+++ b/drivers/usb/host/mausb/hpal.h
@@ -131,6 +131,8 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
        return val;
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address device_address,
+                                 u8 madev_address);
 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,
@@ -158,6 +160,7 @@ static inline void mausb_remove_event(struct mausb_device 
*dev,
 }
 
 void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_on_madev_connected(struct mausb_device *dev);
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
 void mausb_complete_urb(struct mausb_event *event);
 void mausb_reset_connection_timer(struct mausb_device *dev);
@@ -240,6 +243,9 @@ enum mausb_channel mausb_transfer_type_to_channel(u8 
transfer_type)
        return transfer_type >> 3;
 }
 
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+                      enum mausb_link_action action, int status, void *data);
+
 struct mausb_data_iter {
        u32 length;
 
diff --git a/drivers/usb/host/mausb/mausb_core.c 
b/drivers/usb/host/mausb/mausb_core.c
index e5ccf4e9173b..f394365700da 100644
--- a/drivers/usb/host/mausb/mausb_core.c
+++ b/drivers/usb/host/mausb/mausb_core.c
@@ -11,6 +11,146 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("DisplayLink (UK) Ltd.");
 
+static struct mausb_device_address     device_address;
+static int                             mausb_device_disconnect_param;
+static u16                             madev_addr;
+static u8                              mausb_client_connect_param;
+static u8                              mausb_client_disconnect_param;
+
+static int mausb_client_connect(const char *value,
+                               const struct kernel_param *kp)
+{
+       unsigned long flags = 0;
+
+       spin_lock_irqsave(&mss.lock, flags);
+       if (mss.client_connected) {
+               dev_err(mausb_host_dev.this_device, "MA-USB client is already 
connected");
+               spin_unlock_irqrestore(&mss.lock, flags);
+               return -EEXIST;
+       }
+       /* Save heartbeat client information */
+       mss.client_connected = true;
+       mss.missed_heartbeats = 0;
+       reinit_completion(&mss.client_stopped);
+       spin_unlock_irqrestore(&mss.lock, flags);
+       /* Start hearbeat timer */
+       mod_timer(&mss.heartbeat_timer,
+                 jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
+       return 0;
+}
+
+static int mausb_client_disconnect(const char *value,
+                                  const struct kernel_param *kp)
+{
+       unsigned long flags = 0;
+       struct mausb_device *dev = NULL;
+
+       spin_lock_irqsave(&mss.lock, flags);
+       if (!mss.client_connected) {
+               dev_err(mausb_host_dev.this_device, "MA-USB client is not 
connected");
+               spin_unlock_irqrestore(&mss.lock, flags);
+               return -ENODEV;
+       }
+
+       spin_unlock_irqrestore(&mss.lock, flags);
+
+       /* Stop heartbeat timer */
+       del_timer_sync(&mss.heartbeat_timer);
+
+       /* Clear heartbeat client information */
+       spin_lock_irqsave(&mss.lock, flags);
+       mss.client_connected = false;
+       mss.missed_heartbeats = 0;
+       list_for_each_entry(dev, &mss.madev_list, list_entry) {
+               dev_vdbg(mausb_host_dev.this_device, "Enqueue heartbeat_work 
madev_addr=%x",
+                        dev->madev_addr);
+               queue_work(dev->workq, &dev->heartbeat_work);
+       }
+       complete(&mss.client_stopped);
+       spin_unlock_irqrestore(&mss.lock, flags);
+
+       return 0;
+}
+
+static int mausb_device_connect(const char *value,
+                               const struct kernel_param *kp)
+{
+       int status = 0;
+
+       if (strlen(value) <= INET6_ADDRSTRLEN) {
+               strcpy(device_address.ip.address, value);
+               dev_info(mausb_host_dev.this_device, "Processing '%s' address",
+                        device_address.ip.address);
+       } else {
+               dev_err(mausb_host_dev.this_device, "Invalid IP format");
+               return 0;
+       }
+       status = mausb_initiate_dev_connection(device_address, madev_addr);
+       memset(&device_address, 0, sizeof(device_address));
+
+       return status;
+}
+
+static int mausb_device_disconnect(const char *value,
+                                  const struct kernel_param *kp)
+{
+       u8 dev_address = 0;
+       int status = 0;
+       unsigned long flags = 0;
+       struct mausb_device *dev = NULL;
+
+       status = kstrtou8(value, 0, &dev_address);
+       if (status < 0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&mss.lock, flags);
+
+       dev = mausb_get_dev_from_addr_unsafe(dev_address);
+       if (dev)
+               queue_work(dev->workq, &dev->hcd_disconnect_work);
+
+       spin_unlock_irqrestore(&mss.lock, flags);
+
+       return 0;
+}
+
+static const struct kernel_param_ops mausb_device_connect_ops = {
+       .set = mausb_device_connect
+};
+
+static const struct kernel_param_ops mausb_device_disconnect_ops = {
+       .set = mausb_device_disconnect
+};
+
+static const struct kernel_param_ops mausb_client_connect_ops = {
+       .set = mausb_client_connect
+};
+
+static const struct kernel_param_ops mausb_client_disconnect_ops = {
+       .set = mausb_client_disconnect
+};
+
+module_param_named(mgmt, device_address.ip.port.management, ushort, 0664);
+MODULE_PARM_DESC(mgmt, "MA-USB management port");
+module_param_named(ctrl, device_address.ip.port.control, ushort, 0664);
+MODULE_PARM_DESC(ctrl, "MA-USB control port");
+module_param_named(bulk, device_address.ip.port.bulk, ushort, 0664);
+MODULE_PARM_DESC(bulk, "MA-USB bulk port");
+module_param_named(isoch, device_address.ip.port.isochronous, ushort, 0664);
+MODULE_PARM_DESC(isoch, "MA-USB isochronous port");
+module_param_named(madev_addr, madev_addr, ushort, 0664);
+MODULE_PARM_DESC(madev_addr, "MA-USB device address");
+
+module_param_cb(client_connect, &mausb_client_connect_ops,
+               &mausb_client_connect_param, 0664);
+module_param_cb(client_disconnect, &mausb_client_disconnect_ops,
+               &mausb_client_disconnect_param, 0664);
+module_param_cb(ip, &mausb_device_connect_ops,
+               device_address.ip.address, 0664);
+module_param_cb(disconnect, &mausb_device_disconnect_ops,
+               &mausb_device_disconnect_param, 0664);
+
 static int mausb_host_init(void)
 {
        int status = mausb_host_dev_register();
-- 
2.17.1

Reply via email to