blob: 449add58392ef7aae1a2aa2f4912fcfe3851bf5d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 - 2021 DisplayLink (UK) Ltd.
*/
#include <linux/module.h>
#include "hcd.h"
#include "mausb_address.h"
#include "utils.h"
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;
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;
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 (!value)
return -EINVAL;
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;
struct mausb_device *dev = NULL;
if (!value)
return -EINVAL;
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();
if (status < 0)
return status;
status = mausb_host_driver_init();
if (status < 0)
goto cleanup_dev;
status = mausb_register_power_state_listener();
if (status < 0)
goto cleanup;
mausb_initialize_mss();
return 0;
cleanup:
mausb_host_driver_deinit();
cleanup_dev:
mausb_host_dev_deregister();
return status;
}
static void mausb_host_exit(void)
{
mausb_unregister_power_state_listener();
mausb_deinitialize_mss();
mausb_host_driver_deinit();
mausb_host_dev_deregister();
}
module_init(mausb_host_init);
module_exit(mausb_host_exit);