blob: 057ab6057ee6248e4c04f933defd8889bf35ede0 [file] [log] [blame]
Alex Elder4ccb6b72014-10-28 19:36:00 -05001/*
2 * Greybus protocol handling
3 *
Alex Elderd3d2bea2015-03-26 21:25:01 -05004 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
Alex Elder4ccb6b72014-10-28 19:36:00 -05006 *
7 * Released under the GPLv2 only.
8 */
9
Greg Kroah-Hartman7422a1e2014-12-24 13:01:45 -080010#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
Alex Elder4ccb6b72014-10-28 19:36:00 -050012#include "greybus.h"
13
14/* Global list of registered protocols */
15static DEFINE_SPINLOCK(gb_protocols_lock);
16static LIST_HEAD(gb_protocols);
17
18/* Caller must hold gb_protocols_lock */
Viresh Kumar8fd46582015-05-20 16:47:59 +053019static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor)
Alex Elder4ccb6b72014-10-28 19:36:00 -050020{
21 struct gb_protocol *protocol;
22
Alex Elderdbb88942014-11-05 16:12:52 -060023 list_for_each_entry(protocol, &gb_protocols, links) {
24 if (protocol->id < id)
25 continue;
26 if (protocol->id > id)
27 break;
28
29 if (protocol->major > major)
30 continue;
31 if (protocol->major < major)
32 break;
33
34 if (protocol->minor > minor)
35 continue;
36 if (protocol->minor < minor)
37 break;
38
39 return protocol;
40 }
Alex Elder4ccb6b72014-10-28 19:36:00 -050041 return NULL;
42}
43
Greg Kroah-Hartman12a5dfc2014-12-24 13:01:40 -080044int __gb_protocol_register(struct gb_protocol *protocol, struct module *module)
Alex Elder4ccb6b72014-10-28 19:36:00 -050045{
Alex Elder4ccb6b72014-10-28 19:36:00 -050046 struct gb_protocol *existing;
Alex Elder19d03de2014-11-05 16:12:53 -060047 u8 id = protocol->id;
48 u8 major = protocol->major;
49 u8 minor = protocol->minor;
Alex Elder4ccb6b72014-10-28 19:36:00 -050050
Greg Kroah-Hartman12a5dfc2014-12-24 13:01:40 -080051 protocol->owner = module;
52
Greg Kroah-Hartman6869eb52015-01-23 10:06:24 +080053 /*
54 * The protocols list is sorted first by protocol id (low to
55 * high), then by major version (high to low), and finally
56 * by minor version (high to low). Searching only by
57 * protocol id will produce the newest implemented version
58 * of the protocol.
59 */
Alex Elder4ccb6b72014-10-28 19:36:00 -050060 spin_lock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -050061
Greg Kroah-Hartman6869eb52015-01-23 10:06:24 +080062 list_for_each_entry(existing, &gb_protocols, links) {
63 if (existing->id < id)
64 continue;
65 if (existing->id > id)
66 break;
67
68 if (existing->major > major)
69 continue;
70 if (existing->major < major)
71 break;
72
73 if (existing->minor > minor)
74 continue;
75 if (existing->minor < minor)
76 break;
77
78 /* A matching protocol has already been registered */
Alex Elderdbb88942014-11-05 16:12:52 -060079 spin_unlock_irq(&gb_protocols_lock);
Greg Kroah-Hartman6869eb52015-01-23 10:06:24 +080080
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -080081 return -EEXIST;
Alex Elder4ccb6b72014-10-28 19:36:00 -050082 }
83
Alex Elderdbb88942014-11-05 16:12:52 -060084 /*
85 * We need to insert the protocol here, before the existing one
86 * (or before the head if we searched the whole list)
87 */
Greg Kroah-Hartmanc1a0a8f2015-01-23 10:05:58 +080088 list_add_tail(&protocol->links, &existing->links);
Alex Elderdbb88942014-11-05 16:12:52 -060089 spin_unlock_irq(&gb_protocols_lock);
90
Greg Kroah-Hartman7422a1e2014-12-24 13:01:45 -080091 pr_info("Registered %s protocol.\n", protocol->name);
92
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -080093 return 0;
Alex Elder4ccb6b72014-10-28 19:36:00 -050094}
Greg Kroah-Hartman12a5dfc2014-12-24 13:01:40 -080095EXPORT_SYMBOL_GPL(__gb_protocol_register);
Alex Elder4ccb6b72014-10-28 19:36:00 -050096
Alex Elder19d03de2014-11-05 16:12:53 -060097/*
98 * De-register a previously registered protocol.
Alex Elder19d03de2014-11-05 16:12:53 -060099 */
Johan Hovold78033842015-10-13 19:10:24 +0200100void gb_protocol_deregister(struct gb_protocol *protocol)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500101{
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800102 if (!protocol)
Johan Hovold78033842015-10-13 19:10:24 +0200103 return;
Greg Kroah-Hartman7c7d5b92014-12-23 15:16:51 -0800104
Alex Elder4ccb6b72014-10-28 19:36:00 -0500105 spin_lock_irq(&gb_protocols_lock);
Viresh Kumar8fd46582015-05-20 16:47:59 +0530106 protocol = gb_protocol_find(protocol->id, protocol->major,
107 protocol->minor);
Johan Hovold78033842015-10-13 19:10:24 +0200108 if (WARN_ON(!protocol || protocol->count)) {
109 spin_unlock_irq(&gb_protocols_lock);
110 return;
Alex Elder0e447652014-11-05 16:12:51 -0600111 }
Johan Hovold78033842015-10-13 19:10:24 +0200112
113 list_del(&protocol->links);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500114 spin_unlock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500115
Johan Hovold78033842015-10-13 19:10:24 +0200116 pr_info("Deregistered %s protocol.\n", protocol->name);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500117}
Greg Kroah-Hartmandf469a92014-12-23 15:16:52 -0800118EXPORT_SYMBOL_GPL(gb_protocol_deregister);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500119
Alex Elder0e447652014-11-05 16:12:51 -0600120/* Returns the requested protocol if available, or a null pointer */
121struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500122{
123 struct gb_protocol *protocol;
Alex Elder0e447652014-11-05 16:12:51 -0600124 u8 protocol_count;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500125
126 spin_lock_irq(&gb_protocols_lock);
Viresh Kumar8fd46582015-05-20 16:47:59 +0530127 protocol = gb_protocol_find(id, major, minor);
Alex Elder0e447652014-11-05 16:12:51 -0600128 if (protocol) {
Greg Kroah-Hartman12a5dfc2014-12-24 13:01:40 -0800129 if (!try_module_get(protocol->owner)) {
130 protocol = NULL;
131 } else {
132 protocol_count = protocol->count;
133 if (protocol_count != U8_MAX)
134 protocol->count++;
135 }
Alex Elder0e447652014-11-05 16:12:51 -0600136 }
Alex Elder4ccb6b72014-10-28 19:36:00 -0500137 spin_unlock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500138
Alex Elder0e447652014-11-05 16:12:51 -0600139 if (protocol)
140 WARN_ON(protocol_count == U8_MAX);
Alex Elder0e447652014-11-05 16:12:51 -0600141
142 return protocol;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500143}
Johan Hovold50dfb872016-01-19 12:51:16 +0100144EXPORT_SYMBOL_GPL(gb_protocol_get);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500145
Viresh Kumar1cb5fa42015-08-12 11:04:06 +0530146int gb_protocol_get_version(struct gb_connection *connection)
Viresh Kumar36e79de2015-01-21 18:12:36 +0530147{
Johan Hovoldcfb16902015-09-15 10:48:01 +0200148 struct gb_protocol_version_request request;
149 struct gb_protocol_version_response response;
Johan Hovold4505c4d2015-10-13 19:10:26 +0200150 struct gb_protocol *protocol = connection->protocol;
Viresh Kumar36e79de2015-01-21 18:12:36 +0530151 int retval;
152
Johan Hovold4505c4d2015-10-13 19:10:26 +0200153 request.major = protocol->major;
154 request.minor = protocol->minor;
Viresh Kumar738599c2015-08-12 09:19:32 +0530155
Viresh Kumarbf814542015-08-11 07:36:16 +0530156 retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION,
Johan Hovoldcfb16902015-09-15 10:48:01 +0200157 &request, sizeof(request), &response,
158 sizeof(response));
Viresh Kumar36e79de2015-01-21 18:12:36 +0530159 if (retval)
160 return retval;
161
Johan Hovoldcfb16902015-09-15 10:48:01 +0200162 if (response.major > connection->protocol->major) {
Johan Hovold87757e32015-11-25 15:59:17 +0100163 dev_err(&connection->hd->dev,
Viresh Kumar2f3db922015-12-04 21:30:09 +0530164 "%s: unsupported major version (%u > %u)\n",
Johan Hovold87757e32015-11-25 15:59:17 +0100165 connection->name, response.major,
Greg Kroah-Hartman7e4c8d72015-10-16 16:54:18 -0700166 connection->protocol->major);
Viresh Kumar36e79de2015-01-21 18:12:36 +0530167 return -ENOTSUPP;
168 }
169
Johan Hovoldcfb16902015-09-15 10:48:01 +0200170 connection->module_major = response.major;
171 connection->module_minor = response.minor;
Viresh Kumard653f4b12015-08-11 07:35:58 +0530172
Johan Hovold87757e32015-11-25 15:59:17 +0100173 dev_dbg(&connection->hd->dev,
Viresh Kumar2f3db922015-12-04 21:30:09 +0530174 "%s: %s (0x%02x) v%u.%u\n", connection->name,
Greg Kroah-Hartman7e4c8d72015-10-16 16:54:18 -0700175 protocol->name, protocol->id, response.major, response.minor);
Viresh Kumar36e79de2015-01-21 18:12:36 +0530176
177 return 0;
178}
179EXPORT_SYMBOL_GPL(gb_protocol_get_version);
180
Alex Elder0e447652014-11-05 16:12:51 -0600181void gb_protocol_put(struct gb_protocol *protocol)
Alex Elder4ccb6b72014-10-28 19:36:00 -0500182{
Alex Elderd3d2bea2015-03-26 21:25:01 -0500183 u8 id;
Greg Kroah-Hartman23ad7bb2014-12-24 13:12:10 -0800184 u8 major;
185 u8 minor;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500186
Alex Elderd3d2bea2015-03-26 21:25:01 -0500187 id = protocol->id;
Greg Kroah-Hartman23ad7bb2014-12-24 13:12:10 -0800188 major = protocol->major;
189 minor = protocol->minor;
190
Alex Elder4ccb6b72014-10-28 19:36:00 -0500191 spin_lock_irq(&gb_protocols_lock);
Viresh Kumar8fd46582015-05-20 16:47:59 +0530192 protocol = gb_protocol_find(id, major, minor);
Johan Hovold519bf3c2015-10-13 19:10:25 +0200193 if (WARN_ON(!protocol || !protocol->count))
194 goto out;
195
196 protocol->count--;
197 module_put(protocol->owner);
198out:
Alex Elder4ccb6b72014-10-28 19:36:00 -0500199 spin_unlock_irq(&gb_protocols_lock);
Alex Elder4ccb6b72014-10-28 19:36:00 -0500200}
Johan Hovold50dfb872016-01-19 12:51:16 +0100201EXPORT_SYMBOL_GPL(gb_protocol_put);