blob: 5b06b1919153ad6a323059d45cf78a92a49bedec [file] [log] [blame]
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -07001/*
2 * Greybus "AP" message loop handling
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/types.h>
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/uaccess.h>
17#include <linux/kthread.h>
18#include <linux/device.h>
Greg Kroah-Hartmanb9b2a462014-08-31 17:43:38 -070019#include "svc_msg.h"
Greg Kroah-Hartman80ebe8a2014-08-31 18:08:52 -070020#include "greybus_desc.h"
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070021#include "greybus.h"
22
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070023struct ap_msg {
24 u8 *data;
25 int size;
26 struct list_head list;
27};
28
29static LIST_HEAD(ap_msg_list);
30static spinlock_t ap_msg_list_lock;
31static struct task_struct *ap_thread;
32static wait_queue_head_t ap_wait;
33
34static struct ap_msg *get_ap_msg(void)
35{
36 struct ap_msg *ap_msg;
37 unsigned long flags;
38
39 spin_lock_irqsave(&ap_msg_list_lock, flags);
40
41 ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list);
42 if (ap_msg != NULL)
43 list_del(&ap_msg->list);
44 spin_unlock_irqrestore(&ap_msg_list_lock, flags);
45
46 return ap_msg;
47}
48
49static int ap_process_loop(void *data)
50{
51 struct ap_msg *ap_msg;
52
53 while (!kthread_should_stop()) {
54 wait_event_interruptible(ap_wait, kthread_should_stop());
55
56 if (kthread_should_stop())
57 break;
58
59 /* Get some data off of the ap list and process it */
60 ap_msg = get_ap_msg();
61 if (!ap_msg)
62 continue;
63
64 // FIXME - process the message
65
66 /* clean the message up */
67 kfree(ap_msg->data);
68 kfree(ap_msg);
69 }
70 return 0;
71}
72
73int gb_new_ap_msg(u8 *data, int size)
74{
75 struct ap_msg *ap_msg;
76 unsigned long flags;
77
78 /*
79 * Totally naive copy the message into a new structure that we slowly
80 * create and add it to the list. Let's get this working, the odds of
81 * this being any "slow path" for AP messages is really low at this
82 * point in time, but you never know, so this comment is here to point
83 * out that maybe we should use a slab allocator, or even just not copy
84 * the data, but use it directly and force the urbs to be "new" each
85 * time.
86 */
87
88 /* Note - this can, and will, be called in interrupt context. */
89 ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
90 if (!ap_msg)
91 return -ENOMEM;
92 ap_msg->data = kmalloc(size, GFP_ATOMIC);
93 if (!ap_msg->data) {
94 kfree(ap_msg);
95 return -ENOMEM;
96 }
97 memcpy(ap_msg->data, data, size);
98 ap_msg->size = size;
99
100 spin_lock_irqsave(&ap_msg_list_lock, flags);
101 list_add(&ap_msg->list, &ap_msg_list);
102 spin_unlock_irqrestore(&ap_msg_list_lock, flags);
103
104 /* kick our thread to handle the message */
105 wake_up_interruptible(&ap_wait);
106
107 return 0;
108}
Greg Kroah-Hartmand94a44a2014-09-01 14:39:34 -0700109EXPORT_SYMBOL_GPL(gb_new_ap_msg);
110
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700111
112int gb_thread_init(void)
113{
114 init_waitqueue_head(&ap_wait);
115 spin_lock_init(&ap_msg_list_lock);
116
117 ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap");
118 if (IS_ERR(ap_thread))
119 return PTR_ERR(ap_thread);
120
121 return 0;
122}
123
124void gb_thread_destroy(void)
125{
126 kthread_stop(ap_thread);
127}
128
129