[PATCH v1 1/2] usb: typec: tcpm: Expose tcpm logs through a misc device

Badhri Jagan Sridharan
2021-05-17T23:36:15Z

Although TCPM logs were primarily focussed to aid developers
during bringup, TCPM logs have proved to be critical even
post-bringup as it provides a good starting point to triage
interoperability issues with accessories. TCPM logs
are exposed through debugfs filesystem. For systems that
don't mount debugfs by default, this change introduces a
module parameter log_misc_dev which when set exports the
tcpm logs through a misc device. This change also leaves
the option of exporting tcpm logs through debugfs for
backwards compatibility.

Signed-off-by: Badhri Jagan Sridharan <badhri@google.com>
---
drivers/usb/typec/tcpm/tcpm.c | 98 +++++++++++++++++++++++++----------
1 file changed, 72 insertions(+), 26 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index c4fdc00a3bc8..b79194919b27 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -12,6 +12,7 @@
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
+#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
@@ -33,6 +34,10 @@

#include <uapi/linux/sched/types.h>

+static bool modparam_log_misc_dev;
+module_param_named(log_misc_dev, modparam_log_misc_dev, bool, 0444);
+MODULE_PARM_DESC(log_misc_dev, "Expose tcpm logs through misc device");
+
#define FOREACH_STATE(S) \
S(INVALID_STATE), \
S(TOGGLING), \
@@ -465,13 +470,15 @@ struct tcpm_port {
* SNK_READY for non-pd link.
*/
bool slow_charger_loop;
-#ifdef CONFIG_DEBUG_FS
+
struct dentry *dentry;
struct mutex logbuffer_lock; /* log buffer access lock */
int logbuffer_head;
int logbuffer_tail;
u8 *logbuffer[LOG_BUFFER_ENTRIES];
-#endif
+
+ /* TCPM logs are exposed through misc device when modparam_log_misc_dev is set. */
+ struct miscdevice misc;
};

struct pd_rx_event {
@@ -565,8 +572,6 @@ static bool tcpm_port_is_disconnected(struct tcpm_port *port)
* Logging
*/

-#ifdef CONFIG_DEBUG_FS
-
static bool tcpm_log_full(struct tcpm_port *port)
{
return port->logbuffer_tail ==
@@ -626,6 +631,9 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...)
{
va_list args;

+ if (!modparam_log_misc_dev && !IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
/* Do not log while disconnected and unattached */
if (tcpm_port_is_disconnected(port) &&
(port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED ||
@@ -642,6 +650,9 @@ static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...)
{
va_list args;

+ if (!modparam_log_misc_dev && !IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
va_start(args, fmt);
_tcpm_log(port, fmt, args);
va_end(args);
@@ -651,6 +662,9 @@ static void tcpm_log_source_caps(struct tcpm_port *port)
{
int i;

+ if (!modparam_log_misc_dev && !IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
for (i = 0; i < port->nr_source_caps; i++) {
u32 pdo = port->source_caps[i];
enum pd_pdo_type type = pdo_type(pdo);
@@ -708,7 +722,7 @@ static void tcpm_log_source_caps(struct tcpm_port *port)
}
}

-static int tcpm_debug_show(struct seq_file *s, void *v)
+static int tcpm_log_show(struct seq_file *s, void *v)
{
struct tcpm_port *port = (struct tcpm_port *)s->private;
int tail;
@@ -725,23 +739,59 @@ static int tcpm_debug_show(struct seq_file *s, void *v)

return 0;
}
-DEFINE_SHOW_ATTRIBUTE(tcpm_debug);
+DEFINE_SHOW_ATTRIBUTE(tcpm_log);

-static void tcpm_debugfs_init(struct tcpm_port *port)
+static int tcpm_log_dev_open(struct inode *inode, struct file *file)
+{
+ struct tcpm_port *port = container_of(file->private_data, struct tcpm_port, misc);
+
+ inode->i_private = port;
+ file->private_data = NULL;
+ return single_open(file, tcpm_log_show, inode->i_private);
+}
+
+static const struct file_operations tcpm_log_dev_operations = {
+ .owner = THIS_MODULE,
+ .open = tcpm_log_dev_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static int tcpm_log_init(struct tcpm_port *port)
{
char name[NAME_MAX];
+ int ret;
+
+ if (!modparam_log_misc_dev && !IS_ENABLED(CONFIG_DEBUG_FS))
+ return 0;

mutex_init(&port->logbuffer_lock);
snprintf(name, NAME_MAX, "tcpm-%s", dev_name(port->dev));
+ if (modparam_log_misc_dev) {
+ port->misc.minor = MISC_DYNAMIC_MINOR;
+ port->misc.name = name;
+ port->misc.fops = &tcpm_log_dev_operations;
+
+ ret = misc_register(&port->misc);
+ if (ret < 0)
+ dev_err(port->dev, "error while doing misc_register ret=%d\n", ret);
+ return ret;
+ }
+
port->dentry = debugfs_create_dir(name, usb_debug_root);
debugfs_create_file("log", S_IFREG | 0444, port->dentry, port,
- &tcpm_debug_fops);
+ &tcpm_log_fops);
+
+ return 0;
}

-static void tcpm_debugfs_exit(struct tcpm_port *port)
+static void tcpm_log_exit(struct tcpm_port *port)
{
int i;

+ if (!modparam_log_misc_dev && !IS_ENABLED(CONFIG_DEBUG_FS))
+ return;
+
mutex_lock(&port->logbuffer_lock);
for (i = 0; i < LOG_BUFFER_ENTRIES; i++) {
kfree(port->logbuffer[i]);
@@ -749,21 +799,14 @@ static void tcpm_debugfs_exit(struct tcpm_port *port)
}
mutex_unlock(&port->logbuffer_lock);

+ if (modparam_log_misc_dev) {
+ misc_deregister(&port->misc);
+ return;
+ }
+
debugfs_remove(port->dentry);
}

-#else
-
-__printf(2, 3)
-static void tcpm_log(const struct tcpm_port *port, const char *fmt, ...) { }
-__printf(2, 3)
-static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) { }
-static void tcpm_log_source_caps(struct tcpm_port *port) { }
-static void tcpm_debugfs_init(const struct tcpm_port *port) { }
-static void tcpm_debugfs_exit(const struct tcpm_port *port) { }
-
-#endif
-
static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
{
tcpm_log(port, "cc:=%d", cc);
@@ -6135,11 +6178,13 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
init_completion(&port->tx_complete);
init_completion(&port->swap_complete);
init_completion(&port->pps_complete);
- tcpm_debugfs_init(port);
+ err = tcpm_log_init(port);
+ if (err < 0)
+ goto out_destroy_wq;

err = tcpm_fw_get_caps(port, tcpc->fwnode);
if (err < 0)
- goto out_destroy_wq;
+ goto out_unreg_log;

port->try_role = port->typec_caps.prefer_role;

@@ -6157,7 +6202,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->role_sw = usb_role_switch_get(port->dev);
if (IS_ERR(port->role_sw)) {
err = PTR_ERR(port->role_sw);
- goto out_destroy_wq;
+ goto out_unreg_log;
}

err = devm_tcpm_psy_register(port);
@@ -6184,8 +6229,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)

out_role_sw_put:
usb_role_switch_put(port->role_sw);
+out_unreg_log:
+ tcpm_log_exit(port);
out_destroy_wq:
- tcpm_debugfs_exit(port);
kthread_destroy_worker(port->wq);
return ERR_PTR(err);
}
@@ -6200,7 +6246,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
typec_unregister_altmode(port->port_altmode[i]);
typec_unregister_port(port->typec_port);
usb_role_switch_put(port->role_sw);
- tcpm_debugfs_exit(port);
+ tcpm_log_exit(port);
kthread_destroy_worker(port->wq);
}
EXPORT_SYMBOL_GPL(tcpm_unregister_port);
--
2.31.1.751.gd2f1c929bd-goog