From de536e3094760880569e23166854c99b7c66135c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 31 Aug 2014 16:17:04 -0700 Subject: [PATCH] greybus: ap message loop added. --- drivers/staging/greybus/Makefile | 9 ++- drivers/staging/greybus/ap.c | 125 +++++++++++++++++++++++++++++++++++ drivers/staging/greybus/core.c | 14 +++- drivers/staging/greybus/debugfs.c | 4 +- drivers/staging/greybus/es1-ap-usb.c | 64 +++++++++++++++++- drivers/staging/greybus/greybus.h | 12 +++- 6 files changed, 219 insertions(+), 9 deletions(-) create mode 100644 drivers/staging/greybus/ap.c diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index 3badfef..9815e8a 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 0000000..5189e8e --- /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 +#include +#include +#include +#include +#include +#include +#include +#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 143882a..887aa60 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #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 097b32d..4e313f1 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 bebef6d..abf9dfe 100644 --- a/drivers/staging/greybus/es1-ap-usb.c +++ b/drivers/staging/greybus/es1-ap-usb.c @@ -10,7 +10,7 @@ #include #include #include - +#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 7ce8c6e..9b0b41b 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 */ -- 2.7.4