greybus: svc: remove the DDB function message support
[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/workqueue.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 work_struct event;
28 };
29
30 static struct workqueue_struct *ap_workqueue;
31
32 static struct svc_msg *svc_msg_alloc(enum svc_function_id id)
33 {
34         struct svc_msg *svc_msg;
35
36         svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
37         if (!svc_msg)
38                 return NULL;
39
40         // FIXME - verify we are only sending function IDs we should be
41         svc_msg->header.function_id = id;
42         return svc_msg;
43 }
44
45 static void svc_msg_free(struct svc_msg *svc_msg)
46 {
47         kfree(svc_msg);
48 }
49
50 static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd)
51 {
52         int retval;
53
54         // FIXME - Do we need to do more than just pass it to the hd and then
55         // free it?
56         retval = hd->driver->submit_svc(svc_msg, hd);
57
58         svc_msg_free(svc_msg);
59         return retval;
60 }
61
62
63 static void svc_handshake(struct svc_function_handshake *handshake,
64                           int payload_length, struct greybus_host_device *hd)
65 {
66         struct svc_msg *svc_msg;
67
68         if (payload_length != sizeof(struct svc_function_handshake)) {
69                 dev_err(hd->parent,
70                         "Illegal size of svc handshake message %d\n",
71                         payload_length);
72                 return;
73         }
74
75         /* A new SVC communication channel, let's verify a supported version */
76         if ((handshake->version_major != GREYBUS_VERSION_MAJOR) &&
77             (handshake->version_minor != GREYBUS_VERSION_MINOR)) {
78                 dev_dbg(hd->parent, "received invalid greybus version %d:%d\n",
79                         handshake->version_major, handshake->version_minor);
80                 return;
81         }
82
83         /* Validate that the handshake came from the SVC */
84         if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) {
85                 /* we don't know what to do with this, log it and return */
86                 dev_dbg(hd->parent, "received invalid handshake type %d\n",
87                         handshake->handshake_type);
88                 return;
89         }
90
91         /* Send back a AP_HELLO message */
92         svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
93         if (!svc_msg)
94                 return;
95
96         svc_msg->header.function_id = SVC_FUNCTION_HANDSHAKE;
97         svc_msg->header.message_type = SVC_MSG_DATA;
98         svc_msg->header.payload_length =
99                 cpu_to_le16(sizeof(struct svc_function_handshake));
100         svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR;
101         svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR;
102         svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO;
103
104         (void)svc_msg_send(svc_msg, hd);
105 }
106
107 int svc_set_route_send(struct gb_interface *interface,
108                                struct greybus_host_device *hd)
109 {
110         struct svc_msg *svc_msg;
111
112         svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT);
113         if (!svc_msg)
114                 return -ENOMEM;
115
116         svc_msg->header.function_id = SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT;
117         svc_msg->header.message_type = SVC_MSG_DATA;
118         svc_msg->header.payload_length =
119                 cpu_to_le16(sizeof(struct svc_function_unipro_set_route));
120         svc_msg->management.set_route.device_id = interface->device_id;
121
122         return svc_msg_send(svc_msg, hd);
123 }
124
125 static void svc_management(struct svc_function_unipro_management *management,
126                            int payload_length, struct greybus_host_device *hd)
127 {
128         struct gb_module *module;
129         int ret;
130
131         if (payload_length != sizeof(struct svc_function_unipro_management)) {
132                 dev_err(hd->parent,
133                         "Illegal size of svc management message %d\n",
134                         payload_length);
135                 return;
136         }
137
138         switch (management->management_packet_type) {
139         case SVC_MANAGEMENT_LINK_UP:
140                 module = gb_module_find(hd, management->link_up.module_id);
141                 if (!module) {
142                         dev_err(hd->parent, "Module ID %d not found\n",
143                                 management->link_up.module_id);
144                         return;
145                 }
146                 ret = gb_module_interface_init(module,
147                                 management->link_up.interface_id,
148                                 management->link_up.device_id);
149                 if (ret)
150                         dev_err(hd->parent, "error %d initializing"
151                                 "module %hhu interface %hhu\n",
152                                 ret, management->link_up.module_id,
153                                 management->link_up.interface_id);
154                 break;
155         case SVC_MANAGEMENT_AP_DEVICE_ID:
156                 hd->device_id = management->ap_device_id.device_id;
157                 break;
158         default:
159                 dev_err(hd->parent, "Unhandled UniPro management message\n");
160         }
161 }
162
163 static void svc_hotplug(struct svc_function_hotplug *hotplug,
164                         int payload_length, struct greybus_host_device *hd)
165 {
166         u8 module_id = hotplug->module_id;
167
168         switch (hotplug->hotplug_event) {
169         case SVC_HOTPLUG_EVENT:
170                 /* Add a new module to the system */
171                 if (payload_length < 0x03) {
172                         /* Hotplug message is at lest 3 bytes big */
173                         dev_err(hd->parent,
174                                 "Illegal size of svc hotplug message %d\n",
175                                 payload_length);
176                         return;
177                 }
178                 dev_dbg(hd->parent, "module id %d added\n", module_id);
179                 gb_add_module(hd, module_id, hotplug->data,
180                               payload_length - 0x02);
181                 break;
182
183         case SVC_HOTUNPLUG_EVENT:
184                 /* Remove a module from the system */
185                 if (payload_length != 0x02) {
186                         /* Hotunplug message is only 2 bytes big */
187                         dev_err(hd->parent,
188                                 "Illegal size of svc hotunplug message %d\n",
189                                 payload_length);
190                         return;
191                 }
192                 dev_dbg(hd->parent, "module id %d removed\n", module_id);
193                 gb_remove_module(hd, module_id);
194                 break;
195
196         default:
197                 dev_err(hd->parent,
198                         "Received invalid hotplug message type %d\n",
199                         hotplug->hotplug_event);
200                 break;
201         }
202 }
203
204 static void svc_power(struct svc_function_power *power,
205                       int payload_length, struct greybus_host_device *hd)
206 {
207         u8 module_id = power->module_id;
208
209         /*
210          * The AP is only allowed to get a Battery Status message, not a Battery
211          * Status Request
212          */
213         if (power->power_type != SVC_POWER_BATTERY_STATUS) {
214                 dev_err(hd->parent, "Received invalid power type %d\n",
215                         power->power_type);
216                 return;
217         }
218
219         /*
220          * As struct struct svc_function_power_battery_status_request is 0 bytes
221          * big, we can just check the union of the whole structure to validate
222          * the size of this message.
223          */
224         if (payload_length != sizeof(struct svc_function_power)) {
225                 dev_err(hd->parent,
226                         "Illegal size of svc power message %d\n",
227                         payload_length);
228                 return;
229         }
230
231         dev_dbg(hd->parent, "power status for module id %d is %d\n",
232                 module_id, power->status.status);
233
234         // FIXME - do something with the power information, like update our
235         // battery information...
236 }
237
238 static void svc_epm(struct svc_function_epm *epm,
239                     int payload_length, struct greybus_host_device *hd)
240 {
241         /* What?  An AP should not get this message */
242         dev_err(hd->parent, "Got an EPM message???\n");
243 }
244
245 static void svc_suspend(struct svc_function_suspend *suspend,
246                         int payload_length, struct greybus_host_device *hd)
247 {
248         /* What?  An AP should not get this message */
249         dev_err(hd->parent, "Got an suspend message???\n");
250 }
251
252 static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg,
253                                           struct greybus_host_device *hd)
254 {
255         struct svc_msg *svc_msg;
256         struct svc_msg_header *header;
257
258         svc_msg = (struct svc_msg *)ap_msg->data;
259         header = &svc_msg->header;
260
261         /* Validate the message type */
262         if (header->message_type != SVC_MSG_DATA) {
263                 dev_err(hd->parent, "message type %d received?\n",
264                         header->message_type);
265                 return NULL;
266         }
267
268         /*
269          * The validation of the size of the message buffer happens in each
270          * svc_* function, due to the different types of messages, keeping the
271          * logic for each message only in one place.
272          */
273
274         return svc_msg;
275 }
276
277 static void ap_process_event(struct work_struct *work)
278 {
279         struct svc_msg *svc_msg;
280         struct greybus_host_device *hd;
281         struct ap_msg *ap_msg;
282         int payload_length;
283
284         ap_msg = container_of(work, struct ap_msg, event);
285         hd = ap_msg->hd;
286
287         /* Turn the "raw" data into a real message */
288         svc_msg = convert_ap_message(ap_msg, hd);
289         if (!svc_msg)
290                 return;
291
292         payload_length = le16_to_cpu(svc_msg->header.payload_length);
293
294         /* Look at the message to figure out what to do with it */
295         switch (svc_msg->header.function_id) {
296         case SVC_FUNCTION_HANDSHAKE:
297                 svc_handshake(&svc_msg->handshake, payload_length, hd);
298                 break;
299         case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
300                 svc_management(&svc_msg->management, payload_length, hd);
301                 break;
302         case SVC_FUNCTION_HOTPLUG:
303                 svc_hotplug(&svc_msg->hotplug, payload_length, hd);
304                 break;
305         case SVC_FUNCTION_POWER:
306                 svc_power(&svc_msg->power, payload_length, hd);
307                 break;
308         case SVC_FUNCTION_EPM:
309                 svc_epm(&svc_msg->epm, payload_length, hd);
310                 break;
311         case SVC_FUNCTION_SUSPEND:
312                 svc_suspend(&svc_msg->suspend, payload_length, hd);
313                 break;
314         default:
315                 dev_err(hd->parent, "received invalid SVC function ID %d\n",
316                         svc_msg->header.function_id);
317         }
318
319         /* clean the message up */
320         kfree(ap_msg->data);
321         kfree(ap_msg);
322 }
323
324 int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size)
325 {
326         struct ap_msg *ap_msg;
327
328         /*
329          * Totally naive copy the message into a new structure that we slowly
330          * create and add it to the list.  Let's get this working, the odds of
331          * this being any "slow path" for AP messages is really low at this
332          * point in time, but you never know, so this comment is here to point
333          * out that maybe we should use a slab allocator, or even just not copy
334          * the data, but use it directly and force the urbs to be "new" each
335          * time.
336          */
337
338         /* Note - this can, and will, be called in interrupt context. */
339         ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
340         if (!ap_msg)
341                 return -ENOMEM;
342         ap_msg->data = kmalloc(size, GFP_ATOMIC);
343         if (!ap_msg->data) {
344                 kfree(ap_msg);
345                 return -ENOMEM;
346         }
347         memcpy(ap_msg->data, data, size);
348         ap_msg->size = size;
349         ap_msg->hd = hd;
350
351         INIT_WORK(&ap_msg->event, ap_process_event);
352         queue_work(ap_workqueue, &ap_msg->event);
353
354         return 0;
355 }
356 EXPORT_SYMBOL_GPL(greybus_svc_in);
357
358 int gb_ap_init(void)
359 {
360         ap_workqueue = alloc_workqueue("greybus_ap", 0, 1);
361         if (!ap_workqueue)
362                 return -ENOMEM;
363
364         return 0;
365 }
366
367 void gb_ap_exit(void)
368 {
369         destroy_workqueue(ap_workqueue);
370 }
371
372