2 * Greybus protocol handling
4 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
7 * Released under the GPLv2 only.
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 /* Global list of registered protocols */
15 static DEFINE_SPINLOCK(gb_protocols_lock);
16 static LIST_HEAD(gb_protocols);
18 /* Caller must hold gb_protocols_lock */
19 static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor)
21 struct gb_protocol *protocol;
23 list_for_each_entry(protocol, &gb_protocols, links) {
24 if (protocol->id < id)
26 if (protocol->id > id)
29 if (protocol->major > major)
31 if (protocol->major < major)
34 if (protocol->minor > minor)
36 if (protocol->minor < minor)
44 int __gb_protocol_register(struct gb_protocol *protocol, struct module *module)
46 struct gb_protocol *existing;
48 u8 major = protocol->major;
49 u8 minor = protocol->minor;
51 protocol->owner = module;
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
60 spin_lock_irq(&gb_protocols_lock);
62 list_for_each_entry(existing, &gb_protocols, links) {
63 if (existing->id < id)
65 if (existing->id > id)
68 if (existing->major > major)
70 if (existing->major < major)
73 if (existing->minor > minor)
75 if (existing->minor < minor)
78 /* A matching protocol has already been registered */
79 spin_unlock_irq(&gb_protocols_lock);
85 * We need to insert the protocol here, before the existing one
86 * (or before the head if we searched the whole list)
88 list_add_tail(&protocol->links, &existing->links);
89 spin_unlock_irq(&gb_protocols_lock);
91 pr_info("Registered %s protocol.\n", protocol->name);
94 * Go try to bind any unbound connections, as we have a
95 * new protocol in the system
97 gb_bundle_bind_protocols();
101 EXPORT_SYMBOL_GPL(__gb_protocol_register);
104 * De-register a previously registered protocol.
106 void gb_protocol_deregister(struct gb_protocol *protocol)
111 spin_lock_irq(&gb_protocols_lock);
112 protocol = gb_protocol_find(protocol->id, protocol->major,
114 if (WARN_ON(!protocol || protocol->count)) {
115 spin_unlock_irq(&gb_protocols_lock);
119 list_del(&protocol->links);
120 spin_unlock_irq(&gb_protocols_lock);
122 pr_info("Deregistered %s protocol.\n", protocol->name);
124 EXPORT_SYMBOL_GPL(gb_protocol_deregister);
126 /* Returns the requested protocol if available, or a null pointer */
127 struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor)
129 struct gb_protocol *protocol;
132 spin_lock_irq(&gb_protocols_lock);
133 protocol = gb_protocol_find(id, major, minor);
135 if (!try_module_get(protocol->owner)) {
138 protocol_count = protocol->count;
139 if (protocol_count != U8_MAX)
143 spin_unlock_irq(&gb_protocols_lock);
146 WARN_ON(protocol_count == U8_MAX);
151 int gb_protocol_get_version(struct gb_connection *connection)
153 struct gb_protocol_version_request request;
154 struct gb_protocol_version_response response;
155 struct gb_protocol *protocol = connection->protocol;
158 request.major = protocol->major;
159 request.minor = protocol->minor;
161 retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION,
162 &request, sizeof(request), &response,
167 if (response.major > connection->protocol->major) {
168 dev_err(&connection->dev,
169 "unsupported major version (%hhu > %hhu)\n",
170 response.major, connection->protocol->major);
174 connection->module_major = response.major;
175 connection->module_minor = response.minor;
178 dev_dbg(&connection->dev, "%s - %s (0x%02hhx) v%hhu.%hhu\n", __func__,
179 protocol->name, protocol->id,
180 response.major, response.minor);
184 EXPORT_SYMBOL_GPL(gb_protocol_get_version);
186 void gb_protocol_put(struct gb_protocol *protocol)
193 major = protocol->major;
194 minor = protocol->minor;
196 spin_lock_irq(&gb_protocols_lock);
197 protocol = gb_protocol_find(id, major, minor);
198 if (WARN_ON(!protocol || !protocol->count))
202 module_put(protocol->owner);
204 spin_unlock_irq(&gb_protocols_lock);