Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Greybus protocol handling |
| 3 | * |
Alex Elder | d3d2bea | 2015-03-26 21:25:01 -0500 | [diff] [blame] | 4 | * Copyright 2014-2015 Google Inc. |
| 5 | * Copyright 2014-2015 Linaro Ltd. |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 6 | * |
| 7 | * Released under the GPLv2 only. |
| 8 | */ |
| 9 | |
Greg Kroah-Hartman | 7422a1e | 2014-12-24 13:01:45 -0800 | [diff] [blame] | 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 11 | |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 12 | #include "greybus.h" |
| 13 | |
| 14 | /* Global list of registered protocols */ |
| 15 | static DEFINE_SPINLOCK(gb_protocols_lock); |
| 16 | static LIST_HEAD(gb_protocols); |
| 17 | |
| 18 | /* Caller must hold gb_protocols_lock */ |
Viresh Kumar | 8fd4658 | 2015-05-20 16:47:59 +0530 | [diff] [blame] | 19 | static struct gb_protocol *gb_protocol_find(u8 id, u8 major, u8 minor) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 20 | { |
| 21 | struct gb_protocol *protocol; |
| 22 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 23 | 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 Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 41 | return NULL; |
| 42 | } |
| 43 | |
Greg Kroah-Hartman | 12a5dfc | 2014-12-24 13:01:40 -0800 | [diff] [blame] | 44 | int __gb_protocol_register(struct gb_protocol *protocol, struct module *module) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 45 | { |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 46 | struct gb_protocol *existing; |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 47 | u8 id = protocol->id; |
| 48 | u8 major = protocol->major; |
| 49 | u8 minor = protocol->minor; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 50 | |
Greg Kroah-Hartman | 12a5dfc | 2014-12-24 13:01:40 -0800 | [diff] [blame] | 51 | protocol->owner = module; |
| 52 | |
Greg Kroah-Hartman | 6869eb5 | 2015-01-23 10:06:24 +0800 | [diff] [blame] | 53 | /* |
| 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 Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 60 | spin_lock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 61 | |
Greg Kroah-Hartman | 6869eb5 | 2015-01-23 10:06:24 +0800 | [diff] [blame] | 62 | 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 Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 79 | spin_unlock_irq(&gb_protocols_lock); |
Greg Kroah-Hartman | 6869eb5 | 2015-01-23 10:06:24 +0800 | [diff] [blame] | 80 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 81 | return -EEXIST; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 82 | } |
| 83 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 84 | /* |
| 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-Hartman | c1a0a8f | 2015-01-23 10:05:58 +0800 | [diff] [blame] | 88 | list_add_tail(&protocol->links, &existing->links); |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 89 | spin_unlock_irq(&gb_protocols_lock); |
| 90 | |
Greg Kroah-Hartman | 7422a1e | 2014-12-24 13:01:45 -0800 | [diff] [blame] | 91 | pr_info("Registered %s protocol.\n", protocol->name); |
| 92 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 93 | return 0; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 94 | } |
Greg Kroah-Hartman | 12a5dfc | 2014-12-24 13:01:40 -0800 | [diff] [blame] | 95 | EXPORT_SYMBOL_GPL(__gb_protocol_register); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 96 | |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 97 | /* |
| 98 | * De-register a previously registered protocol. |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 99 | */ |
Johan Hovold | 7803384 | 2015-10-13 19:10:24 +0200 | [diff] [blame] | 100 | void gb_protocol_deregister(struct gb_protocol *protocol) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 101 | { |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 102 | if (!protocol) |
Johan Hovold | 7803384 | 2015-10-13 19:10:24 +0200 | [diff] [blame] | 103 | return; |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 104 | |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 105 | spin_lock_irq(&gb_protocols_lock); |
Viresh Kumar | 8fd4658 | 2015-05-20 16:47:59 +0530 | [diff] [blame] | 106 | protocol = gb_protocol_find(protocol->id, protocol->major, |
| 107 | protocol->minor); |
Johan Hovold | 7803384 | 2015-10-13 19:10:24 +0200 | [diff] [blame] | 108 | if (WARN_ON(!protocol || protocol->count)) { |
| 109 | spin_unlock_irq(&gb_protocols_lock); |
| 110 | return; |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 111 | } |
Johan Hovold | 7803384 | 2015-10-13 19:10:24 +0200 | [diff] [blame] | 112 | |
| 113 | list_del(&protocol->links); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 114 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 115 | |
Johan Hovold | 7803384 | 2015-10-13 19:10:24 +0200 | [diff] [blame] | 116 | pr_info("Deregistered %s protocol.\n", protocol->name); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 117 | } |
Greg Kroah-Hartman | df469a9 | 2014-12-23 15:16:52 -0800 | [diff] [blame] | 118 | EXPORT_SYMBOL_GPL(gb_protocol_deregister); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 119 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 120 | /* Returns the requested protocol if available, or a null pointer */ |
| 121 | struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 122 | { |
| 123 | struct gb_protocol *protocol; |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 124 | u8 protocol_count; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 125 | |
| 126 | spin_lock_irq(&gb_protocols_lock); |
Viresh Kumar | 8fd4658 | 2015-05-20 16:47:59 +0530 | [diff] [blame] | 127 | protocol = gb_protocol_find(id, major, minor); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 128 | if (protocol) { |
Greg Kroah-Hartman | 12a5dfc | 2014-12-24 13:01:40 -0800 | [diff] [blame] | 129 | 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 Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 136 | } |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 137 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 138 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 139 | if (protocol) |
| 140 | WARN_ON(protocol_count == U8_MAX); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 141 | |
| 142 | return protocol; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 143 | } |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 144 | EXPORT_SYMBOL_GPL(gb_protocol_get); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 145 | |
Viresh Kumar | 1cb5fa4 | 2015-08-12 11:04:06 +0530 | [diff] [blame] | 146 | int gb_protocol_get_version(struct gb_connection *connection) |
Viresh Kumar | 36e79de | 2015-01-21 18:12:36 +0530 | [diff] [blame] | 147 | { |
Johan Hovold | cfb1690 | 2015-09-15 10:48:01 +0200 | [diff] [blame] | 148 | struct gb_protocol_version_request request; |
| 149 | struct gb_protocol_version_response response; |
Johan Hovold | 4505c4d | 2015-10-13 19:10:26 +0200 | [diff] [blame] | 150 | struct gb_protocol *protocol = connection->protocol; |
Viresh Kumar | 36e79de | 2015-01-21 18:12:36 +0530 | [diff] [blame] | 151 | int retval; |
| 152 | |
Johan Hovold | 4505c4d | 2015-10-13 19:10:26 +0200 | [diff] [blame] | 153 | request.major = protocol->major; |
| 154 | request.minor = protocol->minor; |
Viresh Kumar | 738599c | 2015-08-12 09:19:32 +0530 | [diff] [blame] | 155 | |
Viresh Kumar | bf81454 | 2015-08-11 07:36:16 +0530 | [diff] [blame] | 156 | retval = gb_operation_sync(connection, GB_REQUEST_TYPE_PROTOCOL_VERSION, |
Johan Hovold | cfb1690 | 2015-09-15 10:48:01 +0200 | [diff] [blame] | 157 | &request, sizeof(request), &response, |
| 158 | sizeof(response)); |
Viresh Kumar | 36e79de | 2015-01-21 18:12:36 +0530 | [diff] [blame] | 159 | if (retval) |
| 160 | return retval; |
| 161 | |
Johan Hovold | cfb1690 | 2015-09-15 10:48:01 +0200 | [diff] [blame] | 162 | if (response.major > connection->protocol->major) { |
Johan Hovold | 87757e3 | 2015-11-25 15:59:17 +0100 | [diff] [blame] | 163 | dev_err(&connection->hd->dev, |
Viresh Kumar | 2f3db92 | 2015-12-04 21:30:09 +0530 | [diff] [blame] | 164 | "%s: unsupported major version (%u > %u)\n", |
Johan Hovold | 87757e3 | 2015-11-25 15:59:17 +0100 | [diff] [blame] | 165 | connection->name, response.major, |
Greg Kroah-Hartman | 7e4c8d7 | 2015-10-16 16:54:18 -0700 | [diff] [blame] | 166 | connection->protocol->major); |
Viresh Kumar | 36e79de | 2015-01-21 18:12:36 +0530 | [diff] [blame] | 167 | return -ENOTSUPP; |
| 168 | } |
| 169 | |
Johan Hovold | cfb1690 | 2015-09-15 10:48:01 +0200 | [diff] [blame] | 170 | connection->module_major = response.major; |
| 171 | connection->module_minor = response.minor; |
Viresh Kumar | d653f4b1 | 2015-08-11 07:35:58 +0530 | [diff] [blame] | 172 | |
Johan Hovold | 87757e3 | 2015-11-25 15:59:17 +0100 | [diff] [blame] | 173 | dev_dbg(&connection->hd->dev, |
Viresh Kumar | 2f3db92 | 2015-12-04 21:30:09 +0530 | [diff] [blame] | 174 | "%s: %s (0x%02x) v%u.%u\n", connection->name, |
Greg Kroah-Hartman | 7e4c8d7 | 2015-10-16 16:54:18 -0700 | [diff] [blame] | 175 | protocol->name, protocol->id, response.major, response.minor); |
Viresh Kumar | 36e79de | 2015-01-21 18:12:36 +0530 | [diff] [blame] | 176 | |
| 177 | return 0; |
| 178 | } |
| 179 | EXPORT_SYMBOL_GPL(gb_protocol_get_version); |
| 180 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 181 | void gb_protocol_put(struct gb_protocol *protocol) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 182 | { |
Alex Elder | d3d2bea | 2015-03-26 21:25:01 -0500 | [diff] [blame] | 183 | u8 id; |
Greg Kroah-Hartman | 23ad7bb | 2014-12-24 13:12:10 -0800 | [diff] [blame] | 184 | u8 major; |
| 185 | u8 minor; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 186 | |
Alex Elder | d3d2bea | 2015-03-26 21:25:01 -0500 | [diff] [blame] | 187 | id = protocol->id; |
Greg Kroah-Hartman | 23ad7bb | 2014-12-24 13:12:10 -0800 | [diff] [blame] | 188 | major = protocol->major; |
| 189 | minor = protocol->minor; |
| 190 | |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 191 | spin_lock_irq(&gb_protocols_lock); |
Viresh Kumar | 8fd4658 | 2015-05-20 16:47:59 +0530 | [diff] [blame] | 192 | protocol = gb_protocol_find(id, major, minor); |
Johan Hovold | 519bf3c | 2015-10-13 19:10:25 +0200 | [diff] [blame] | 193 | if (WARN_ON(!protocol || !protocol->count)) |
| 194 | goto out; |
| 195 | |
| 196 | protocol->count--; |
| 197 | module_put(protocol->owner); |
| 198 | out: |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 199 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 200 | } |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 201 | EXPORT_SYMBOL_GPL(gb_protocol_put); |