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