usb: gadget: gsi: Add a debugfs entry to simulate remote wakeup
Add a debugfs entry to simulate IPA callback for remote wakeup
on USB side. On executing the below command a timer will be
started and remote wakeup will be done for every specified
time interval.
- To enable timer execute the below command:
echo 1 > /sys/kernel/debug/usb_gsi/remote_wakeup_enable
- To disable timer execute the below command:
echo 0 > /sys/kernel/debug/usb_gsi/remote_wakeup_enable
- To update timer value, execute the below command (default 500 ms):
echo 5000 > /sys/kernel/debug/usb_gsi/remote_wakeup_interval
Change-Id: I0799123710e064311321593b893e06940a54aa14
Signed-off-by: Azhar Shaikh <azhars@codeaurora.org>
Signed-off-by: Mayank Rana <mrana@codeaurora.org>
Signed-off-by: Chandana Kishori Chiluveru <cchiluve@codeaurora.org>
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index 94dd64a..7c2a557 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -14,6 +14,12 @@
#include "f_gsi.h"
#include "rndis.h"
+struct usb_gsi_debugfs {
+ struct dentry *debugfs_root;
+};
+
+static struct usb_gsi_debugfs debugfs;
+
static bool qti_packet_debug;
module_param(qti_packet_debug, bool, 0644);
MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
@@ -205,6 +211,223 @@
return ret;
}
+static void debugfs_rw_timer_func(unsigned long arg)
+{
+ struct f_gsi *gsi;
+
+ gsi = (struct f_gsi *)arg;
+
+ if (!atomic_read(&gsi->connected)) {
+ log_event_dbg("%s: gsi not connected..del timer\n", __func__);
+ gsi->debugfs_rw_enable = 0;
+ del_timer(&gsi->debugfs_rw_timer);
+ return;
+ }
+
+ log_event_dbg("%s: calling gsi_wakeup_host\n", __func__);
+ gsi_wakeup_host(gsi);
+
+ if (gsi->debugfs_rw_enable) {
+ log_event_dbg("%s: re-arm the timer\n", __func__);
+ mod_timer(&gsi->debugfs_rw_timer,
+ jiffies + msecs_to_jiffies(gsi->debugfs_rw_interval));
+ }
+}
+
+static struct f_gsi *get_connected_gsi(void)
+{
+ struct f_gsi *connected_gsi;
+ bool gsi_connected = false;
+ unsigned int i;
+
+ for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++) {
+ connected_gsi = __gsi[i];
+ if (connected_gsi && atomic_read(&connected_gsi->connected)) {
+ gsi_connected = true;
+ break;
+ }
+ }
+
+ if (!gsi_connected)
+ connected_gsi = NULL;
+
+ return connected_gsi;
+}
+
+#define DEFAULT_RW_TIMER_INTERVAL 500 /* in ms */
+static ssize_t usb_gsi_rw_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct f_gsi *gsi;
+ u8 input;
+ int ret;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ goto err;
+ }
+
+ if (ubuf == NULL) {
+ log_event_dbg("%s: buffer is Null.\n", __func__);
+ goto err;
+ }
+
+ ret = kstrtou8_from_user(ubuf, count, 0, &input);
+ if (ret) {
+ log_event_err("%s: Invalid value. err:%d\n", __func__, ret);
+ goto err;
+ }
+
+ if (gsi->debugfs_rw_enable == !!input) {
+ if (!!input)
+ log_event_dbg("%s: RW already enabled\n", __func__);
+ else
+ log_event_dbg("%s: RW already disabled\n", __func__);
+ goto err;
+ }
+
+ gsi->debugfs_rw_enable = !!input;
+ if (gsi->debugfs_rw_enable) {
+ init_timer(&gsi->debugfs_rw_timer);
+ gsi->debugfs_rw_timer.data = (unsigned long) gsi;
+ gsi->debugfs_rw_timer.function = debugfs_rw_timer_func;
+
+ /* Use default remote wakeup timer interval if it is not set */
+ if (!gsi->debugfs_rw_interval)
+ gsi->debugfs_rw_interval = DEFAULT_RW_TIMER_INTERVAL;
+ gsi->debugfs_rw_timer.expires = jiffies +
+ msecs_to_jiffies(gsi->debugfs_rw_interval);
+ add_timer(&gsi->debugfs_rw_timer);
+ log_event_dbg("%s: timer initialized\n", __func__);
+ } else {
+ del_timer_sync(&gsi->debugfs_rw_timer);
+ log_event_dbg("%s: timer deleted\n", __func__);
+ }
+
+err:
+ return count;
+}
+
+static int usb_gsi_rw_show(struct seq_file *s, void *unused)
+{
+
+ struct f_gsi *gsi;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ return 0;
+ }
+
+ seq_printf(s, "%d\n", gsi->debugfs_rw_enable);
+
+ return 0;
+}
+
+static int usb_gsi_rw_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, usb_gsi_rw_show, inode->i_private);
+}
+
+static const struct file_operations fops_usb_gsi_rw = {
+ .open = usb_gsi_rw_open,
+ .read = seq_read,
+ .write = usb_gsi_rw_write,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t usb_gsi_rw_timer_write(struct file *file,
+ const char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct f_gsi *gsi;
+ u16 timer_val;
+ int ret;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ goto err;
+ }
+
+ if (ubuf == NULL) {
+ log_event_dbg("%s: buffer is NULL.\n", __func__);
+ goto err;
+ }
+
+ ret = kstrtou16_from_user(ubuf, count, 0, &timer_val);
+ if (ret) {
+ log_event_err("%s: Invalid value. err:%d\n", __func__, ret);
+ goto err;
+ }
+
+ if (timer_val <= 0 || timer_val > 10000) {
+ log_event_err("%s: value must be > 0 and < 10000.\n", __func__);
+ goto err;
+ }
+
+ gsi->debugfs_rw_interval = timer_val;
+err:
+ return count;
+}
+
+static int usb_gsi_rw_timer_show(struct seq_file *s, void *unused)
+{
+ struct f_gsi *gsi;
+ unsigned int timer_interval;
+
+ gsi = get_connected_gsi();
+ if (!gsi) {
+ log_event_dbg("%s: gsi not connected\n", __func__);
+ return 0;
+ }
+
+ timer_interval = DEFAULT_RW_TIMER_INTERVAL;
+ if (gsi->debugfs_rw_interval)
+ timer_interval = gsi->debugfs_rw_interval;
+
+ seq_printf(s, "%ums\n", timer_interval);
+
+ return 0;
+}
+
+static int usb_gsi_rw_timer_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, usb_gsi_rw_timer_show, inode->i_private);
+}
+
+static const struct file_operations fops_usb_gsi_rw_timer = {
+ .open = usb_gsi_rw_timer_open,
+ .read = seq_read,
+ .write = usb_gsi_rw_timer_write,
+ .owner = THIS_MODULE,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int usb_gsi_debugfs_init(void)
+{
+ debugfs.debugfs_root = debugfs_create_dir("usb_gsi", NULL);
+ if (!debugfs.debugfs_root)
+ return -ENOMEM;
+
+ debugfs_create_file("remote_wakeup_enable", 0600,
+ debugfs.debugfs_root,
+ __gsi, &fops_usb_gsi_rw);
+ debugfs_create_file("remote_wakeup_interval", 0600,
+ debugfs.debugfs_root,
+ __gsi,
+ &fops_usb_gsi_rw_timer);
+ return 0;
+}
+
+static void usb_gsi_debugfs_exit(void)
+{
+ debugfs_remove_recursive(debugfs.debugfs_root);
+}
+
/*
* Callback for when when network interface is up
* and userspace is ready to answer DHCP requests, or remote wakeup
@@ -3366,6 +3589,7 @@
if (!ipc_log_ctxt)
pr_err("%s: Err allocating ipc_log_ctxt\n", __func__);
+ usb_gsi_debugfs_init();
return usb_function_register(&gsiusb_func);
}
module_init(fgsi_init);
@@ -3382,6 +3606,7 @@
for (i = 0; i < USB_PROT_MAX; i++)
kfree(__gsi[i]);
+ usb_gsi_debugfs_exit();
usb_function_unregister(&gsiusb_func);
}
module_exit(fgsi_exit);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 7e998c4..1d4b4e3 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -26,6 +26,7 @@
#include <linux/debugfs.h>
#include <linux/ipa_usb.h>
#include <linux/ipc_logging.h>
+#include <linux/timer.h>
#include "u_ether.h"
@@ -275,6 +276,10 @@
struct gsi_data_port d_port;
struct gsi_ctrl_port c_port;
bool rmnet_dtr_status;
+ /* To test remote wakeup using debugfs */
+ struct timer_list debugfs_rw_timer;
+ u8 debugfs_rw_enable;
+ u16 debugfs_rw_interval;
};
static inline struct f_gsi *func_to_gsi(struct usb_function *f)