Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Greybus protocol handling |
| 3 | * |
| 4 | * Copyright 2014 Google Inc. |
Alex Elder | a46e967 | 2014-12-12 12:08:42 -0600 | [diff] [blame] | 5 | * Copyright 2014 Linaro Ltd. |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 6 | * |
| 7 | * Released under the GPLv2 only. |
| 8 | */ |
| 9 | |
| 10 | #include "greybus.h" |
| 11 | |
| 12 | /* Global list of registered protocols */ |
| 13 | static DEFINE_SPINLOCK(gb_protocols_lock); |
| 14 | static LIST_HEAD(gb_protocols); |
| 15 | |
| 16 | /* Caller must hold gb_protocols_lock */ |
Alex Elder | 6ae7fa4 | 2014-11-05 16:12:50 -0600 | [diff] [blame] | 17 | 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] | 18 | { |
| 19 | struct gb_protocol *protocol; |
| 20 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 21 | list_for_each_entry(protocol, &gb_protocols, links) { |
| 22 | if (protocol->id < id) |
| 23 | continue; |
| 24 | if (protocol->id > id) |
| 25 | break; |
| 26 | |
| 27 | if (protocol->major > major) |
| 28 | continue; |
| 29 | if (protocol->major < major) |
| 30 | break; |
| 31 | |
| 32 | if (protocol->minor > minor) |
| 33 | continue; |
| 34 | if (protocol->minor < minor) |
| 35 | break; |
| 36 | |
| 37 | return protocol; |
| 38 | } |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 39 | return NULL; |
| 40 | } |
| 41 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 42 | int gb_protocol_register(struct gb_protocol *protocol) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 43 | { |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 44 | struct gb_protocol *existing; |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 45 | u8 id = protocol->id; |
| 46 | u8 major = protocol->major; |
| 47 | u8 minor = protocol->minor; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 48 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 49 | /* |
| 50 | * The protocols list is sorted first by protocol id (low to |
| 51 | * high), then by major version (high to low), and finally |
| 52 | * by minor version (high to low). Searching only by |
| 53 | * protocol id will produce the newest implemented version |
| 54 | * of the protocol. |
| 55 | */ |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 56 | spin_lock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 57 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 58 | list_for_each_entry(existing, &gb_protocols, links) { |
| 59 | if (existing->id < id) |
| 60 | continue; |
| 61 | if (existing->id > id) |
| 62 | break; |
| 63 | |
| 64 | if (existing->major > major) |
| 65 | continue; |
| 66 | if (existing->major < major) |
| 67 | break; |
| 68 | |
| 69 | if (existing->minor > minor) |
| 70 | continue; |
| 71 | if (existing->minor < minor) |
| 72 | break; |
| 73 | |
| 74 | /* A matching protocol has already been registered */ |
| 75 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 76 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 77 | return -EEXIST; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 78 | } |
| 79 | |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 80 | /* |
| 81 | * We need to insert the protocol here, before the existing one |
| 82 | * (or before the head if we searched the whole list) |
| 83 | */ |
| 84 | list_add_tail(&protocol->links, &existing->links); |
| 85 | spin_unlock_irq(&gb_protocols_lock); |
| 86 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 87 | return 0; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 88 | } |
Greg Kroah-Hartman | df469a9 | 2014-12-23 15:16:52 -0800 | [diff] [blame^] | 89 | EXPORT_SYMBOL_GPL(gb_protocol_register); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 90 | |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 91 | /* |
| 92 | * De-register a previously registered protocol. |
| 93 | * |
| 94 | * XXX Currently this fails (and reports an error to the caller) if |
| 95 | * XXX the protocol is currently in use. We may want to forcefully |
| 96 | * XXX kill off a protocol and all its active users at some point. |
Viresh Kumar | 696e0cc | 2014-11-21 11:26:30 +0530 | [diff] [blame] | 97 | * XXX But I think that's better handled by quiescing modules that |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 98 | * XXX have users and having those users drop their reference. |
| 99 | * |
| 100 | * Returns true if successful, false otherwise. |
| 101 | */ |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 102 | int gb_protocol_deregister(struct gb_protocol *protocol) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 103 | { |
Alex Elder | dbb8894 | 2014-11-05 16:12:52 -0600 | [diff] [blame] | 104 | u8 protocol_count = 0; |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 105 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 106 | if (!protocol) |
| 107 | return 0; |
| 108 | |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 109 | spin_lock_irq(&gb_protocols_lock); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 110 | protocol = _gb_protocol_find(protocol->id, protocol->major, |
| 111 | protocol->minor); |
| 112 | if (protocol) { |
| 113 | protocol_count = protocol->count; |
| 114 | if (!protocol_count) |
| 115 | list_del(&protocol->links); |
| 116 | } |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 117 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 118 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 119 | return protocol && !protocol_count; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 120 | } |
Greg Kroah-Hartman | df469a9 | 2014-12-23 15:16:52 -0800 | [diff] [blame^] | 121 | EXPORT_SYMBOL_GPL(gb_protocol_deregister); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 122 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 123 | /* Returns the requested protocol if available, or a null pointer */ |
| 124 | struct gb_protocol *gb_protocol_get(u8 id, u8 major, u8 minor) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 125 | { |
| 126 | struct gb_protocol *protocol; |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 127 | u8 protocol_count; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 128 | |
| 129 | spin_lock_irq(&gb_protocols_lock); |
Alex Elder | 6ae7fa4 | 2014-11-05 16:12:50 -0600 | [diff] [blame] | 130 | protocol = _gb_protocol_find(id, major, minor); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 131 | if (protocol) { |
| 132 | protocol_count = protocol->count; |
| 133 | if (protocol_count != U8_MAX) |
| 134 | protocol->count++; |
| 135 | } |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 136 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 137 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 138 | if (protocol) |
| 139 | WARN_ON(protocol_count == U8_MAX); |
| 140 | else |
| 141 | pr_err("protocol id %hhu version %hhu.%hhu not found\n", |
| 142 | id, major, minor); |
| 143 | |
| 144 | return protocol; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 145 | } |
| 146 | |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 147 | void gb_protocol_put(struct gb_protocol *protocol) |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 148 | { |
Alex Elder | 6ae7fa4 | 2014-11-05 16:12:50 -0600 | [diff] [blame] | 149 | u8 major = protocol->major; |
| 150 | u8 minor = protocol->minor; |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 151 | u8 protocol_count; |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 152 | |
| 153 | spin_lock_irq(&gb_protocols_lock); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 154 | protocol = _gb_protocol_find(protocol->id, protocol->major, |
| 155 | protocol->minor); |
| 156 | if (protocol) { |
| 157 | protocol_count = protocol->count; |
| 158 | if (protocol_count) |
| 159 | protocol->count--; |
| 160 | } |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 161 | spin_unlock_irq(&gb_protocols_lock); |
Alex Elder | 0e44765 | 2014-11-05 16:12:51 -0600 | [diff] [blame] | 162 | if (protocol) |
| 163 | WARN_ON(!protocol_count); |
| 164 | else |
| 165 | pr_err("protocol id %hhu version %hhu.%hhu not found\n", |
| 166 | protocol->id, major, minor); |
Alex Elder | 4ccb6b7 | 2014-10-28 19:36:00 -0500 | [diff] [blame] | 167 | } |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 168 | |
| 169 | bool gb_protocol_init(void) |
| 170 | { |
| 171 | bool ret = true; |
| 172 | |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 173 | if (gb_battery_protocol_init()) { |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 174 | pr_err("error initializing battery protocol\n"); |
| 175 | ret = false; |
| 176 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 177 | if (gb_gpio_protocol_init()) { |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 178 | pr_err("error initializing gpio protocol\n"); |
| 179 | ret = false; |
| 180 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 181 | if (gb_i2c_protocol_init()) { |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 182 | pr_err("error initializing i2c protocol\n"); |
| 183 | ret = false; |
| 184 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 185 | if (gb_pwm_protocol_init()) { |
Matt Porter | 34c6507 | 2014-11-13 09:14:13 -0500 | [diff] [blame] | 186 | pr_err("error initializing pwm protocol\n"); |
| 187 | ret = false; |
| 188 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 189 | if (gb_uart_protocol_init()) { |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 190 | pr_err("error initializing uart protocol\n"); |
| 191 | ret = false; |
| 192 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 193 | if (gb_sdio_protocol_init()) { |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 194 | pr_err("error initializing sdio protocol\n"); |
| 195 | ret = false; |
| 196 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 197 | if (gb_vibrator_protocol_init()) { |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 198 | pr_err("error initializing vibrator protocol\n"); |
| 199 | ret = false; |
| 200 | } |
Greg Kroah-Hartman | 7c7d5b9 | 2014-12-23 15:16:51 -0800 | [diff] [blame] | 201 | if (gb_usb_protocol_init()) { |
Greg Kroah-Hartman | 615772a | 2014-11-25 16:59:21 -0800 | [diff] [blame] | 202 | pr_err("error initializing usb protocol\n"); |
| 203 | ret = false; |
| 204 | } |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 205 | return ret; |
| 206 | } |
| 207 | |
| 208 | void gb_protocol_exit(void) |
| 209 | { |
Greg Kroah-Hartman | 615772a | 2014-11-25 16:59:21 -0800 | [diff] [blame] | 210 | gb_usb_protocol_exit(); |
Greg Kroah-Hartman | ac4029f | 2014-11-17 16:03:34 -0800 | [diff] [blame] | 211 | gb_vibrator_protocol_exit(); |
Alex Elder | 19d03de | 2014-11-05 16:12:53 -0600 | [diff] [blame] | 212 | gb_sdio_protocol_exit(); |
| 213 | gb_uart_protocol_exit(); |
| 214 | gb_i2c_protocol_exit(); |
| 215 | gb_gpio_protocol_exit(); |
| 216 | gb_battery_protocol_exit(); |
| 217 | } |