2 * Greybus "AP" message loop handling
4 * Copyright 2014 Google Inc.
6 * Released under the GPLv2 only.
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
20 #include "greybus_manifest.h"
26 struct greybus_host_device *hd;
27 struct work_struct event;
30 static struct workqueue_struct *ap_workqueue;
32 static struct svc_msg *svc_msg_alloc(enum svc_function_id id)
34 struct svc_msg *svc_msg;
36 svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
40 // FIXME - verify we are only sending function IDs we should be
41 svc_msg->header.function_id = id;
45 static void svc_msg_free(struct svc_msg *svc_msg)
50 static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd)
54 // FIXME - Do we need to do more than just pass it to the hd and then
56 retval = hd->driver->submit_svc(svc_msg, hd);
58 svc_msg_free(svc_msg);
63 static void svc_handshake(struct svc_function_handshake *handshake,
64 int payload_length, struct greybus_host_device *hd)
66 struct svc_msg *svc_msg;
68 if (payload_length != sizeof(struct svc_function_handshake)) {
70 "Illegal size of svc handshake message %d\n",
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);
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);
91 /* Send back a AP_HELLO message */
92 svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
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;
104 (void)svc_msg_send(svc_msg, hd);
107 int svc_set_route_send(struct gb_interface *interface,
108 struct greybus_host_device *hd)
110 struct svc_msg *svc_msg;
112 svc_msg = svc_msg_alloc(SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT);
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;
122 return svc_msg_send(svc_msg, hd);
125 static void svc_management(struct svc_function_unipro_management *management,
126 int payload_length, struct greybus_host_device *hd)
128 struct gb_module *module;
131 if (payload_length != sizeof(struct svc_function_unipro_management)) {
133 "Illegal size of svc management message %d\n",
138 switch (management->management_packet_type) {
139 case SVC_MANAGEMENT_LINK_UP:
140 module = gb_module_find(hd, management->link_up.module_id);
142 dev_err(hd->parent, "Module ID %d not found\n",
143 management->link_up.module_id);
146 ret = gb_module_interface_init(module,
147 management->link_up.interface_id,
148 management->link_up.device_id);
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);
155 case SVC_MANAGEMENT_AP_DEVICE_ID:
156 hd->device_id = management->ap_device_id.device_id;
159 dev_err(hd->parent, "Unhandled UniPro management message\n");
163 static void svc_hotplug(struct svc_function_hotplug *hotplug,
164 int payload_length, struct greybus_host_device *hd)
166 u8 module_id = hotplug->module_id;
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 */
174 "Illegal size of svc hotplug message %d\n",
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);
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 */
188 "Illegal size of svc hotunplug message %d\n",
192 dev_dbg(hd->parent, "module id %d removed\n", module_id);
193 gb_remove_module(hd, module_id);
198 "Received invalid hotplug message type %d\n",
199 hotplug->hotplug_event);
204 static void svc_power(struct svc_function_power *power,
205 int payload_length, struct greybus_host_device *hd)
207 u8 module_id = power->module_id;
210 * The AP is only allowed to get a Battery Status message, not a Battery
213 if (power->power_type != SVC_POWER_BATTERY_STATUS) {
214 dev_err(hd->parent, "Received invalid power type %d\n",
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.
224 if (payload_length != sizeof(struct svc_function_power)) {
226 "Illegal size of svc power message %d\n",
231 dev_dbg(hd->parent, "power status for module id %d is %d\n",
232 module_id, power->status.status);
234 // FIXME - do something with the power information, like update our
235 // battery information...
238 static void svc_epm(struct svc_function_epm *epm,
239 int payload_length, struct greybus_host_device *hd)
241 /* What? An AP should not get this message */
242 dev_err(hd->parent, "Got an EPM message???\n");
245 static void svc_suspend(struct svc_function_suspend *suspend,
246 int payload_length, struct greybus_host_device *hd)
248 /* What? An AP should not get this message */
249 dev_err(hd->parent, "Got an suspend message???\n");
252 static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg,
253 struct greybus_host_device *hd)
255 struct svc_msg *svc_msg;
256 struct svc_msg_header *header;
258 svc_msg = (struct svc_msg *)ap_msg->data;
259 header = &svc_msg->header;
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);
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.
277 static void ap_process_event(struct work_struct *work)
279 struct svc_msg *svc_msg;
280 struct greybus_host_device *hd;
281 struct ap_msg *ap_msg;
284 ap_msg = container_of(work, struct ap_msg, event);
287 /* Turn the "raw" data into a real message */
288 svc_msg = convert_ap_message(ap_msg, hd);
292 payload_length = le16_to_cpu(svc_msg->header.payload_length);
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);
299 case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
300 svc_management(&svc_msg->management, payload_length, hd);
302 case SVC_FUNCTION_HOTPLUG:
303 svc_hotplug(&svc_msg->hotplug, payload_length, hd);
305 case SVC_FUNCTION_POWER:
306 svc_power(&svc_msg->power, payload_length, hd);
308 case SVC_FUNCTION_EPM:
309 svc_epm(&svc_msg->epm, payload_length, hd);
311 case SVC_FUNCTION_SUSPEND:
312 svc_suspend(&svc_msg->suspend, payload_length, hd);
315 dev_err(hd->parent, "received invalid SVC function ID %d\n",
316 svc_msg->header.function_id);
319 /* clean the message up */
324 int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size)
326 struct ap_msg *ap_msg;
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
338 /* Note - this can, and will, be called in interrupt context. */
339 ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
342 ap_msg->data = kmalloc(size, GFP_ATOMIC);
347 memcpy(ap_msg->data, data, size);
351 INIT_WORK(&ap_msg->event, ap_process_event);
352 queue_work(ap_workqueue, &ap_msg->event);
356 EXPORT_SYMBOL_GPL(greybus_svc_in);
360 ap_workqueue = alloc_workqueue("greybus_ap", 0, 1);
367 void gb_ap_exit(void)
369 destroy_workqueue(ap_workqueue);