aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorGravatar Greg Kroah-Hartman <greg@kroah.com> 2014-08-31 16:17:04 -0700
committerGravatar Greg Kroah-Hartman <greg@kroah.com> 2014-08-31 16:17:04 -0700
commitde536e3094760880569e23166854c99b7c66135c (patch)
tree2b88e7a8fa96eba22e081bf53c57088bf3ee35b7 /drivers
parentgreybus: register the bus with the driver core and add framework for debugfs ... (diff)
downloadlinux-de536e3094760880569e23166854c99b7c66135c.tar.gz
linux-de536e3094760880569e23166854c99b7c66135c.tar.bz2
linux-de536e3094760880569e23166854c99b7c66135c.zip
greybus: ap message loop added.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/greybus/Makefile9
-rw-r--r--drivers/staging/greybus/ap.c125
-rw-r--r--drivers/staging/greybus/core.c14
-rw-r--r--drivers/staging/greybus/debugfs.c4
-rw-r--r--drivers/staging/greybus/es1-ap-usb.c64
-rw-r--r--drivers/staging/greybus/greybus.h12
6 files changed, 219 insertions, 9 deletions
diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile
index 3badfefd237d..9815e8abac56 100644
--- a/drivers/staging/greybus/Makefile
+++ b/drivers/staging/greybus/Makefile
@@ -1,4 +1,11 @@
-greybus-y := core.o gbuf.o debugfs.o i2c-gb.o gpio-gb.o sdio-gb.o uart-gb.o
+greybus-y := core.o \
+ gbuf.o \
+ debugfs.o \
+ ap.o \
+ i2c-gb.o \
+ gpio-gb.o \
+ sdio-gb.o \
+ uart-gb.o
obj-m += greybus.o
obj-m += es1-ap-usb.o
diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c
new file mode 100644
index 000000000000..5189e8e05ad2
--- /dev/null
+++ b/drivers/staging/greybus/ap.c
@@ -0,0 +1,125 @@
+/*
+ * Greybus "AP" message loop handling
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include "greybus.h"
+
+struct ap_msg {
+ u8 *data;
+ int size;
+ struct list_head list;
+};
+
+static LIST_HEAD(ap_msg_list);
+static spinlock_t ap_msg_list_lock;
+static struct task_struct *ap_thread;
+static wait_queue_head_t ap_wait;
+
+static struct ap_msg *get_ap_msg(void)
+{
+ struct ap_msg *ap_msg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ap_msg_list_lock, flags);
+
+ ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list);
+ if (ap_msg != NULL)
+ list_del(&ap_msg->list);
+ spin_unlock_irqrestore(&ap_msg_list_lock, flags);
+
+ return ap_msg;
+}
+
+static int ap_process_loop(void *data)
+{
+ struct ap_msg *ap_msg;
+
+ while (!kthread_should_stop()) {
+ wait_event_interruptible(ap_wait, kthread_should_stop());
+
+ if (kthread_should_stop())
+ break;
+
+ /* Get some data off of the ap list and process it */
+ ap_msg = get_ap_msg();
+ if (!ap_msg)
+ continue;
+
+ // FIXME - process the message
+
+ /* clean the message up */
+ kfree(ap_msg->data);
+ kfree(ap_msg);
+ }
+ return 0;
+}
+
+int gb_new_ap_msg(u8 *data, int size)
+{
+ struct ap_msg *ap_msg;
+ unsigned long flags;
+
+ /*
+ * Totally naive copy the message into a new structure that we slowly
+ * create and add it to the list. Let's get this working, the odds of
+ * this being any "slow path" for AP messages is really low at this
+ * point in time, but you never know, so this comment is here to point
+ * out that maybe we should use a slab allocator, or even just not copy
+ * the data, but use it directly and force the urbs to be "new" each
+ * time.
+ */
+
+ /* Note - this can, and will, be called in interrupt context. */
+ ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
+ if (!ap_msg)
+ return -ENOMEM;
+ ap_msg->data = kmalloc(size, GFP_ATOMIC);
+ if (!ap_msg->data) {
+ kfree(ap_msg);
+ return -ENOMEM;
+ }
+ memcpy(ap_msg->data, data, size);
+ ap_msg->size = size;
+
+ spin_lock_irqsave(&ap_msg_list_lock, flags);
+ list_add(&ap_msg->list, &ap_msg_list);
+ spin_unlock_irqrestore(&ap_msg_list_lock, flags);
+
+ /* kick our thread to handle the message */
+ wake_up_interruptible(&ap_wait);
+
+ return 0;
+}
+
+int gb_thread_init(void)
+{
+ init_waitqueue_head(&ap_wait);
+ spin_lock_init(&ap_msg_list_lock);
+
+ ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap");
+ if (IS_ERR(ap_thread))
+ return PTR_ERR(ap_thread);
+
+ return 0;
+}
+
+void gb_thread_destroy(void)
+{
+ kthread_stop(ap_thread);
+}
+
+
diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c
index 143882a523e3..887aa601fd22 100644
--- a/drivers/staging/greybus/core.c
+++ b/drivers/staging/greybus/core.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
+#include <linux/kthread.h>
#include <linux/device.h>
#include "greybus.h"
@@ -199,7 +200,7 @@ static int __init gb_init(void)
{
int retval;
- retval = greybus_debugfs_init();
+ retval = gb_debugfs_init();
if (retval)
return retval;
@@ -207,6 +208,10 @@ static int __init gb_init(void)
if (retval)
goto error_bus;
+ retval = gb_thread_init();
+ if (retval)
+ goto error_thread;
+
// FIXME - more gb core init goes here
retval = gb_tty_init();
@@ -216,10 +221,13 @@ static int __init gb_init(void)
return 0;
error_tty:
+ gb_thread_destroy();
+
+error_thread:
bus_unregister(&greybus_bus_type);
error_bus:
- greybus_debugfs_cleanup();
+ gb_debugfs_cleanup();
return retval;
}
@@ -228,7 +236,7 @@ static void __exit gb_exit(void)
{
gb_tty_exit();
bus_unregister(&greybus_bus_type);
- greybus_debugfs_cleanup();
+ gb_debugfs_cleanup();
}
module_init(gb_init);
diff --git a/drivers/staging/greybus/debugfs.c b/drivers/staging/greybus/debugfs.c
index 097b32d2bb56..4e313f1a5143 100644
--- a/drivers/staging/greybus/debugfs.c
+++ b/drivers/staging/greybus/debugfs.c
@@ -19,7 +19,7 @@
static struct dentry *gb_debug_root;
-int greybus_debugfs_init(void)
+int gb_debugfs_init(void)
{
gb_debug_root = debugfs_create_dir("greybus", NULL);
if (!gb_debug_root)
@@ -28,7 +28,7 @@ int greybus_debugfs_init(void)
return 0;
}
-void greybus_debugfs_cleanup(void)
+void gb_debugfs_cleanup(void)
{
debugfs_remove_recursive(gb_debug_root);
}
diff --git a/drivers/staging/greybus/es1-ap-usb.c b/drivers/staging/greybus/es1-ap-usb.c
index bebef6dbae7b..abf9dfe66450 100644
--- a/drivers/staging/greybus/es1-ap-usb.c
+++ b/drivers/staging/greybus/es1-ap-usb.c
@@ -10,7 +10,7 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/usb.h>
-
+#include "greybus.h"
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0000, 0x0000) }, // FIXME
@@ -34,6 +34,68 @@ struct es1_ap_dev {
*/
static struct es1_ap_dev *es1_ap_dev;
+static void ap_in_callback(struct urb *urb)
+{
+ struct device *dev = &urb->dev->dev;
+ int status = urb->status;
+ int retval;
+
+ switch (status) {
+ case 0:
+ break;
+ case -EOVERFLOW:
+ dev_err(dev, "%s: overflow actual length is %d\n",
+ __func__, urb->actual_length);
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EILSEQ:
+ /* device is gone, stop sending */
+ return;
+ default:
+ dev_err(dev, "%s: unknown status %d\n", __func__, status);
+ goto exit;
+ }
+
+ /* We have a message, create a new message structure, add it to the
+ * list, and wake up our thread that will process the messages.
+ */
+ gb_new_ap_msg(urb->transfer_buffer, urb->actual_length);
+
+exit:
+ /* resubmit the urb to get more messages */
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
+}
+
+static void ap_out_callback(struct urb *urb)
+{
+ struct device *dev = &urb->dev->dev;
+ int status = urb->status;
+
+ switch (status) {
+ case 0:
+ break;
+ case -EOVERFLOW:
+ dev_err(dev, "%s: overflow actual length is %d\n",
+ __func__, urb->actual_length);
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EILSEQ:
+ /* device is gone, stop sending */
+ return;
+ default:
+ dev_err(dev, "%s: unknown status %d\n", __func__, status);
+ goto exit;
+ }
+
+ // FIXME - queue up next AP message to send???
+exit:
+ return;
+}
+
static int ap_probe(struct usb_interface *interface,
const struct usb_device_id *id)
diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h
index 7ce8c6ef0287..9b0b41bb2a65 100644
--- a/drivers/staging/greybus/greybus.h
+++ b/drivers/staging/greybus/greybus.h
@@ -176,8 +176,16 @@ void greybus_deregister(struct greybus_driver *driver);
int greybus_disabled(void);
-int greybus_debugfs_init(void);
-void greybus_debugfs_cleanup(void);
+
+/* Internal functions to gb module, move to internal .h file eventually. */
+
+int gb_new_ap_msg(u8 *data, int length);
+int gb_thread_init(void);
+void gb_thread_destroy(void);
+int gb_debugfs_init(void);
+void gb_debugfs_cleanup(void);
+
+
#endif /* __KERNEL__ */
#endif /* __LINUX_GREYBUS_H */