From ca3ec299c2d090f99fedcc5995dc1f25ab609408 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 27 Mar 2015 11:38:07 +0100 Subject: [PATCH] greybus: es2: sync up with recent es1.c changes This brings the debug log functionality of es1.c to es2.c, along with some other minor cleanups. Signed-off-by: Greg Kroah-Hartman Reviewed-by: Alex Elder --- drivers/staging/greybus/es2.c | 146 +++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index a73a268c8759..e4637500d05c 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -8,10 +8,14 @@ */ #include #include +#include #include #include #include #include +#include +#include +#include #include "greybus.h" #include "svc_msg.h" @@ -28,6 +32,12 @@ static const struct usb_device_id id_table[] = { }; MODULE_DEVICE_TABLE(usb, id_table); +#define APB1_LOG_SIZE SZ_16K +static struct dentry *apb1_log_dentry; +static struct dentry *apb1_log_enable_dentry; +static struct task_struct *apb1_log_task; +static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); + /* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in @@ -84,6 +94,8 @@ static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd) } static void cport_out_callback(struct urb *urb); +static void usb_log_enable(struct es1_ap_dev *es1); +static void usb_log_disable(struct es1_ap_dev *es1); /* * Buffer constraints for the host driver. @@ -111,7 +123,7 @@ static void cport_out_callback(struct urb *urb); * host driver. I.e., ((char *)buffer - headroom) must * point to valid memory, usable only by the host driver. * size_max: The maximum size of a buffer (not including the - * headroom) must not exceed this. + * headroom) must not exceed this. */ static void hd_buffer_constraints(struct greybus_host_device *hd) { @@ -319,6 +331,8 @@ static void ap_disconnect(struct usb_interface *interface) if (!es1) return; + usb_log_disable(es1); + /* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { struct urb *urb = es1->cport_out_urb[i]; @@ -467,6 +481,123 @@ static void cport_out_callback(struct urb *urb) */ } +static void apb1_log_get(struct es1_ap_dev *es1) +{ + char buf[65]; + int retval; + + /* SVC messages go down our control pipe */ + do { + memset(buf, 0, 65); + retval = usb_control_msg(es1->usb_dev, + usb_rcvctrlpipe(es1->usb_dev, + es1->control_endpoint), + 0x02, /* vendor request APB1 log */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + buf, + 64, + ES1_TIMEOUT); + if (retval > 0) + kfifo_in(&apb1_log_fifo, buf, retval); + } while (retval > 0); +} + +static int apb1_log_poll(void *data) +{ + while (!kthread_should_stop()) { + msleep(1000); + apb1_log_get((struct es1_ap_dev *)data); + } + return 0; +} + +static ssize_t apb1_log_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + size_t copied; + char *tmp_buf; + + if (count > APB1_LOG_SIZE) + count = APB1_LOG_SIZE; + + tmp_buf = kmalloc(count, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + copied = kfifo_out(&apb1_log_fifo, tmp_buf, count); + ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); + + kfree(tmp_buf); + + return ret; +} + +static const struct file_operations apb1_log_fops = { + .read = apb1_log_read, +}; + +static void usb_log_enable(struct es1_ap_dev *es1) +{ + if (apb1_log_task != NULL) + return; + + /* get log from APB1 */ + apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log"); + if (apb1_log_task == ERR_PTR(-ENOMEM)) + return; + apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO, + gb_debugfs_get(), NULL, + &apb1_log_fops); +} + +static void usb_log_disable(struct es1_ap_dev *es1) +{ + if (apb1_log_task == NULL) + return; + + debugfs_remove(apb1_log_dentry); + apb1_log_dentry = NULL; + + kthread_stop(apb1_log_task); + apb1_log_task = NULL; +} + +static ssize_t apb1_log_enable_read(struct file *f, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmp_buf[3]; + int enable = apb1_log_task != NULL; + + sprintf(tmp_buf, "%d\n", enable); + return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); +} + +static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf, + size_t count, loff_t *ppos) +{ + int enable; + ssize_t retval; + struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private; + + retval = kstrtoint_from_user(buf, count, 10, &enable); + if (retval) + return retval; + + if (enable) + usb_log_enable(es1); + else + usb_log_disable(es1); + + return count; +} + +static const struct file_operations apb1_log_enable_fops = { + .read = apb1_log_enable_read, + .write = apb1_log_enable_write, +}; + /* * The ES1 USB Bridge device contains 4 endpoints * 1 Control - usual USB stuff + AP -> SVC messages @@ -552,9 +683,6 @@ static int ap_probe(struct usb_interface *interface, usb_rcvintpipe(udev, es1->svc_endpoint), es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, hd, svc_interval); - retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); - if (retval) - goto error; /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { @@ -591,6 +719,16 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", + (S_IWUSR | S_IRUGO), + gb_debugfs_get(), es1, + &apb1_log_enable_fops); + return 0; error: ap_disconnect(interface); -- 2.39.2