greybus: AP: move a bunch of svc message handling logic into ap.c
[cascardo/linux.git] / drivers / staging / greybus / ap.c
1 /*
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>
19 #include "svc_msg.h"
20 #include "greybus_manifest.h"
21 #include "greybus.h"
22
23 struct ap_msg {
24         u8 *data;
25         size_t size;
26         struct greybus_host_device *hd;
27         struct list_head list;
28 };
29
30 static LIST_HEAD(ap_msg_list);
31 static spinlock_t ap_msg_list_lock;
32 static struct task_struct *ap_thread;
33 static wait_queue_head_t ap_wait;
34
35 static struct svc_msg *svc_msg_alloc(enum svc_function_type type)
36 {
37         struct svc_msg *svc_msg;
38
39         svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
40         if (!svc_msg)
41                 return NULL;
42
43         // FIXME - verify we are only sending message types we should be
44         svc_msg->header.type = type;
45         return svc_msg;
46 }
47
48 static void svc_msg_free(struct svc_msg *svc_msg)
49 {
50         kfree(svc_msg);
51 }
52
53 static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd)
54 {
55         int retval;
56
57         // FIXME - Do we need to do more than just pass it to the hd and then
58         // free it?
59         retval = hd->driver->send_svc_msg(svc_msg, hd);
60
61         svc_msg_free(svc_msg);
62         return retval;
63 }
64
65
66 static void svc_handshake(struct svc_function_handshake *handshake,
67                           struct greybus_host_device *hd)
68 {
69         struct svc_msg *svc_msg;
70
71         /* A new SVC communication channel, let's verify it was for us */
72         if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) {
73                 /* we don't know what to do with this, log it and return */
74                 dev_dbg(&hd->dev, "received invalid handshake type %d\n",
75                         handshake->handshake_type);
76                 return;
77         }
78
79         /* Send back a AP_HELLO message */
80         svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
81         if (!svc_msg)
82                 return;
83
84         svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO;
85         svc_msg_send(svc_msg, hd);
86 }
87
88 static void svc_management(struct svc_function_unipro_management *management,
89                            struct greybus_host_device *hd)
90 {
91         /* What?  An AP should not get this message */
92         dev_err(&hd->dev, "Got an svc management message???\n");
93 }
94
95 static void svc_hotplug(struct svc_function_hotplug *hotplug,
96                         struct greybus_host_device *hd)
97 {
98         u8 module_id = hotplug->module_id;
99
100         switch (hotplug->hotplug_event) {
101         case SVC_HOTPLUG_EVENT:
102                 dev_dbg(&hd->dev, "module id %d added\n", module_id);
103                 // FIXME - add the module to the system
104                 break;
105
106         case SVC_HOTUNPLUG_EVENT:
107                 dev_dbg(&hd->dev, "module id %d removed\n", module_id);
108                 // FIXME - remove the module from the system
109                 break;
110
111         default:
112                 dev_err(&hd->dev, "received invalid hotplug message type %d\n",
113                         hotplug->hotplug_event);
114                 break;
115         }
116 }
117
118 static void svc_ddb(struct svc_function_ddb *ddb,
119                     struct greybus_host_device *hd)
120 {
121         /* What?  An AP should not get this message */
122         dev_err(&hd->dev, "Got an svc DDB message???\n");
123 }
124
125 static void svc_power(struct svc_function_power *power,
126                       struct greybus_host_device *hd)
127 {
128         u8 module_id = power->module_id;
129
130         if (power->power_type != SVC_POWER_BATTERY_STATUS) {
131                 dev_err(&hd->dev, "received invalid power type %d\n",
132                         power->power_type);
133                 return;
134         }
135
136         dev_dbg(&hd->dev, "power status for module id %d is %d\n",
137                 module_id, power->status.status);
138
139         // FIXME - do something with the power information, like update our
140         // battery information...
141 }
142
143 static void svc_epm(struct svc_function_epm *epm,
144                     struct greybus_host_device *hd)
145 {
146         /* What?  An AP should not get this message */
147         dev_err(&hd->dev, "Got an EPM message???\n");
148 }
149
150 static void svc_suspend(struct svc_function_suspend *suspend,
151                         struct greybus_host_device *hd)
152 {
153         /* What?  An AP should not get this message */
154         dev_err(&hd->dev, "Got an suspend message???\n");
155 }
156
157 static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg)
158 {
159         struct svc_msg *svc_msg;
160
161         // FIXME - validate message, right now we are trusting the size and data
162         // from the AP, what could go wrong?  :)
163         // for now, just cast the pointer and run away...
164
165         svc_msg = (struct svc_msg *)ap_msg->data;
166
167         /* Verify the version is something we can handle with this code */
168         if ((svc_msg->header.version_major != GREYBUS_VERSION_MAJOR) &&
169             (svc_msg->header.version_minor != GREYBUS_VERSION_MINOR))
170                 return NULL;
171
172         return svc_msg;
173 }
174
175 static void process_ap_message(struct ap_msg *ap_msg)
176 {
177         struct svc_msg *svc_msg;
178         struct greybus_host_device *hd;
179
180         /* Turn the "raw" data into a real message */
181         svc_msg = convert_ap_message(ap_msg);
182         if (!svc_msg) {
183                 // FIXME log an error???
184                 return;
185         }
186
187         hd = ap_msg->hd;
188
189         /* Pass the message to the host controller */
190 //      ap_msg->hd->driver->ap_msg(svc_msg, ap_msg->hd);
191
192         /* Look at the message to figure out what to do with it */
193         switch (svc_msg->header.type) {
194         case SVC_FUNCTION_HANDSHAKE:
195                 svc_handshake(&svc_msg->handshake, hd);
196                 break;
197         case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
198                 svc_management(&svc_msg->management, hd);
199                 break;
200         case SVC_FUNCTION_HOTPLUG:
201                 svc_hotplug(&svc_msg->hotplug, hd);
202                 break;
203         case SVC_FUNCTION_DDB:
204                 svc_ddb(&svc_msg->ddb, hd);
205                 break;
206         case SVC_FUNCTION_POWER:
207                 svc_power(&svc_msg->power, hd);
208                 break;
209         case SVC_FUNCTION_EPM:
210                 svc_epm(&svc_msg->epm, hd);
211                 break;
212         case SVC_FUNCTION_SUSPEND:
213                 svc_suspend(&svc_msg->suspend, hd);
214                 break;
215         default:
216                 dev_err(&hd->dev, "received invalid SVC message type %d\n",
217                         svc_msg->header.type);
218         }
219
220
221 }
222
223 static struct ap_msg *get_ap_msg(void)
224 {
225         struct ap_msg *ap_msg;
226         unsigned long flags;
227
228         spin_lock_irqsave(&ap_msg_list_lock, flags);
229
230         ap_msg = list_first_entry_or_null(&ap_msg_list, struct ap_msg, list);
231         if (ap_msg != NULL)
232                 list_del(&ap_msg->list);
233         spin_unlock_irqrestore(&ap_msg_list_lock, flags);
234
235         return ap_msg;
236 }
237
238 static int ap_process_loop(void *data)
239 {
240         struct ap_msg *ap_msg;
241
242         while (!kthread_should_stop()) {
243                 wait_event_interruptible(ap_wait, kthread_should_stop());
244
245                 if (kthread_should_stop())
246                         break;
247
248                 /* Get some data off of the ap list and process it */
249                 ap_msg = get_ap_msg();
250                 if (!ap_msg)
251                         continue;
252
253                 process_ap_message(ap_msg);
254
255                 /* clean the message up */
256                 kfree(ap_msg->data);
257                 kfree(ap_msg);
258         }
259         return 0;
260 }
261
262 int gb_new_ap_msg(u8 *data, int size, struct greybus_host_device *hd)
263 {
264         struct ap_msg *ap_msg;
265         unsigned long flags;
266
267         /*
268          * Totally naive copy the message into a new structure that we slowly
269          * create and add it to the list.  Let's get this working, the odds of
270          * this being any "slow path" for AP messages is really low at this
271          * point in time, but you never know, so this comment is here to point
272          * out that maybe we should use a slab allocator, or even just not copy
273          * the data, but use it directly and force the urbs to be "new" each
274          * time.
275          */
276
277         /* Note - this can, and will, be called in interrupt context. */
278         ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
279         if (!ap_msg)
280                 return -ENOMEM;
281         ap_msg->data = kmalloc(size, GFP_ATOMIC);
282         if (!ap_msg->data) {
283                 kfree(ap_msg);
284                 return -ENOMEM;
285         }
286         memcpy(ap_msg->data, data, size);
287         ap_msg->size = size;
288         ap_msg->hd = hd;
289
290         spin_lock_irqsave(&ap_msg_list_lock, flags);
291         list_add(&ap_msg->list, &ap_msg_list);
292         spin_unlock_irqrestore(&ap_msg_list_lock, flags);
293
294         /* kick our thread to handle the message */
295         wake_up_interruptible(&ap_wait);
296
297         return 0;
298 }
299 EXPORT_SYMBOL_GPL(gb_new_ap_msg);
300
301
302 int gb_thread_init(void)
303 {
304         init_waitqueue_head(&ap_wait);
305         spin_lock_init(&ap_msg_list_lock);
306
307         ap_thread = kthread_run(ap_process_loop, NULL, "greybus_ap");
308         if (IS_ERR(ap_thread))
309                 return PTR_ERR(ap_thread);
310
311         return 0;
312 }
313
314 void gb_thread_destroy(void)
315 {
316         kthread_stop(ap_thread);
317 }
318
319