blob: a70404d7c1f1f92e5ffc1f030d47e89aa6d667fc [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;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070025 size_t size;
26 struct greybus_host_device *hd;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070027 struct list_head list;
28};
29
30static LIST_HEAD(ap_msg_list);
31static spinlock_t ap_msg_list_lock;
32static struct task_struct *ap_thread;
33static wait_queue_head_t ap_wait;
34
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070035
36static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg)
37{
38 struct svc_msg *svc_msg;
39
40 // FIXME - validate message, right now we are trusting the size and data
41 // from the AP, what could go wrong? :)
42 // for now, just cast the pointer and run away...
43
44 svc_msg = (struct svc_msg *)ap_msg->data;
Greg Kroah-Hartman43cc32a2014-09-07 13:51:12 -070045
46 // FIXME - put in correct version numbers
47 if ((svc_msg->header.version_major != 0x00) &&
48 (svc_msg->header.version_minor != 0x00))
49 return NULL;
50
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070051 return svc_msg;
52}
53
54
55
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070056static struct ap_msg *get_ap_msg(void)
57{
58 struct ap_msg *ap_msg;
59 unsigned long flags;
60
61 spin_lock_irqsave(&ap_msg_list_lock, flags);
62
63 ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list);
64 if (ap_msg != NULL)
65 list_del(&ap_msg->list);
66 spin_unlock_irqrestore(&ap_msg_list_lock, flags);
67
68 return ap_msg;
69}
70
71static int ap_process_loop(void *data)
72{
73 struct ap_msg *ap_msg;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070074 struct svc_msg *svc_msg;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070075
76 while (!kthread_should_stop()) {
77 wait_event_interruptible(ap_wait, kthread_should_stop());
78
79 if (kthread_should_stop())
80 break;
81
82 /* Get some data off of the ap list and process it */
83 ap_msg = get_ap_msg();
84 if (!ap_msg)
85 continue;
86
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -070087 /* Turn the "raw" data into a real message */
88 svc_msg = convert_ap_message(ap_msg);
89 if (svc_msg) {
90 /* Pass the message to the host controller */
91 ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd);
92 }
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070093
94 /* clean the message up */
95 kfree(ap_msg->data);
96 kfree(ap_msg);
97 }
98 return 0;
99}
100
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700101int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd)
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700102{
103 struct ap_msg *ap_msg;
104 unsigned long flags;
105
106 /*
107 * Totally naive copy the message into a new structure that we slowly
108 * create and add it to the list. Let's get this working, the odds of
109 * this being any "slow path" for AP messages is really low at this
110 * point in time, but you never know, so this comment is here to point
111 * out that maybe we should use a slab allocator, or even just not copy
112 * the data, but use it directly and force the urbs to be "new" each
113 * time.
114 */
115
116 /* Note - this can, and will, be called in interrupt context. */
117 ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
118 if (!ap_msg)
119 return -ENOMEM;
120 ap_msg->data = kmalloc(size, GFP_ATOMIC);
121 if (!ap_msg->data) {
122 kfree(ap_msg);
123 return -ENOMEM;
124 }
125 memcpy(ap_msg->data, data, size);
126 ap_msg->size = size;
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700127 ap_msg->hd = hd;
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700128
129 spin_lock_irqsave(&ap_msg_list_lock, flags);
130 list_add(&ap_msg->list, &ap_msg_list);
131 spin_unlock_irqrestore(&ap_msg_list_lock, flags);
132
133 /* kick our thread to handle the message */
134 wake_up_interruptible(&ap_wait);
135
136 return 0;
137}
Greg Kroah-Hartmand94a44a2014-09-01 14:39:34 -0700138EXPORT_SYMBOL_GPL(gb_new_ap_msg);
139
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700140
141int gb_thread_init(void)
142{
143 init_waitqueue_head(&ap_wait);
144 spin_lock_init(&ap_msg_list_lock);
145
146 ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap");
147 if (IS_ERR(ap_thread))
148 return PTR_ERR(ap_thread);
149
150 return 0;
151}
152
153void gb_thread_destroy(void)
154{
155 kthread_stop(ap_thread);
156}
157
158