blob: e87521ec526d98b0d4230cade56f49e2c7375611 [file] [log] [blame]
Alex Elderc68adb22014-10-01 21:54:14 -05001/*
2 * Greybus connections
3 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Alex Elderc68adb22014-10-01 21:54:14 -05006 *
7 * Released under the GPLv2 only.
8 */
9
Alex Eldere88afa52014-10-01 21:54:15 -050010#include <linux/atomic.h>
11
12#include "kernel_ver.h"
Alex Elderc68adb22014-10-01 21:54:14 -050013#include "greybus.h"
14
Alex Elder748e1232014-10-03 14:14:22 -050015static DEFINE_SPINLOCK(gb_connections_lock);
16
Viresh Kumar12eba9f2015-05-20 16:48:00 +053017struct gb_connection *gb_connection_hd_find(struct greybus_host_device *hd,
Alex Elderee9ebe42014-10-06 06:53:08 -050018 u16 cport_id)
19{
20 struct gb_connection *connection = NULL;
Johan Hovold8f5eadb2015-03-02 09:55:26 +010021 unsigned long flags;
Alex Elderee9ebe42014-10-06 06:53:08 -050022
Johan Hovold8f5eadb2015-03-02 09:55:26 +010023 spin_lock_irqsave(&gb_connections_lock, flags);
Alex Elder2c43ce42014-11-17 08:08:44 -060024 list_for_each_entry(connection, &hd->connections, hd_links)
25 if (connection->hd_cport_id == cport_id)
Marti Bolivare86905b2014-10-06 13:26:02 -040026 goto found;
Marti Bolivare86905b2014-10-06 13:26:02 -040027 connection = NULL;
28 found:
Johan Hovold8f5eadb2015-03-02 09:55:26 +010029 spin_unlock_irqrestore(&gb_connections_lock, flags);
Alex Elderee9ebe42014-10-06 06:53:08 -050030
31 return connection;
32}
33
Alex Elderde3557d2014-11-20 16:09:18 -060034/*
35 * Callback from the host driver to let us know that data has been
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050036 * received on the bundle.
Alex Elderde3557d2014-11-20 16:09:18 -060037 */
38void greybus_data_rcvd(struct greybus_host_device *hd, u16 cport_id,
Alex Elder374e6a22014-11-17 18:08:37 -060039 u8 *data, size_t length)
40{
41 struct gb_connection *connection;
42
Viresh Kumar12eba9f2015-05-20 16:48:00 +053043 connection = gb_connection_hd_find(hd, cport_id);
Alex Elder374e6a22014-11-17 18:08:37 -060044 if (!connection) {
45 dev_err(hd->parent,
46 "nonexistent connection (%zu bytes dropped)\n", length);
47 return;
48 }
Alex Elder61089e82014-11-18 13:26:50 -060049 gb_connection_recv(connection, data, length);
Alex Elder374e6a22014-11-17 18:08:37 -060050}
Alex Elderde3557d2014-11-20 16:09:18 -060051EXPORT_SYMBOL_GPL(greybus_data_rcvd);
Alex Elder374e6a22014-11-17 18:08:37 -060052
Alex Elderc68adb22014-10-01 21:54:14 -050053/*
Alex Elder177404b2014-10-03 14:14:24 -050054 * Allocate an available CPort Id for use for the host side of the
55 * given connection. The lowest-available id is returned, so the
56 * first call is guaranteed to allocate CPort Id 0.
57 *
58 * Assigns the connection's hd_cport_id and returns true if successful.
59 * Returns false otherwise.
60 */
Alex Elderf6aec252014-10-06 06:53:06 -050061static bool gb_connection_hd_cport_id_alloc(struct gb_connection *connection)
Alex Elder177404b2014-10-03 14:14:24 -050062{
63 struct ida *ida = &connection->hd->cport_id_map;
64 int id;
65
Johan Hovold067f3b62015-02-12 11:22:47 +080066 id = ida_simple_get(ida, 0, HOST_DEV_CPORT_ID_MAX, GFP_ATOMIC);
Alex Elder177404b2014-10-03 14:14:24 -050067 if (id < 0)
68 return false;
69
70 connection->hd_cport_id = (u16)id;
71
72 return true;
73}
74
75/*
76 * Free a previously-allocated CPort Id on the given host device.
77 */
Alex Elderf6aec252014-10-06 06:53:06 -050078static void gb_connection_hd_cport_id_free(struct gb_connection *connection)
Alex Elder177404b2014-10-03 14:14:24 -050079{
80 struct ida *ida = &connection->hd->cport_id_map;
81
82 ida_simple_remove(ida, connection->hd_cport_id);
83 connection->hd_cport_id = CPORT_ID_BAD;
84}
85
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080086static ssize_t state_show(struct device *dev, struct device_attribute *attr,
87 char *buf)
88{
89 struct gb_connection *connection = to_gb_connection(dev);
90
Greg Kroah-Hartman88e70a62014-12-23 16:09:26 -080091 return sprintf(buf, "%d\n", connection->state);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080092}
93static DEVICE_ATTR_RO(state);
94
Alex Elder7fba0072014-10-28 19:35:59 -050095static ssize_t
96protocol_id_show(struct device *dev, struct device_attribute *attr, char *buf)
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080097{
98 struct gb_connection *connection = to_gb_connection(dev);
99
Greg Kroah-Hartman88e70a62014-12-23 16:09:26 -0800100 return sprintf(buf, "%d\n", connection->protocol->id);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800101}
Alex Elder7fba0072014-10-28 19:35:59 -0500102static DEVICE_ATTR_RO(protocol_id);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800103
104static struct attribute *connection_attrs[] = {
105 &dev_attr_state.attr,
Alex Elder7fba0072014-10-28 19:35:59 -0500106 &dev_attr_protocol_id.attr,
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800107 NULL,
108};
109
110ATTRIBUTE_GROUPS(connection);
111
112static void gb_connection_release(struct device *dev)
113{
114 struct gb_connection *connection = to_gb_connection(dev);
115
116 kfree(connection);
117}
118
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800119struct device_type greybus_connection_type = {
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800120 .name = "greybus_connection",
121 .release = gb_connection_release,
122};
123
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800124
125void gb_connection_bind_protocol(struct gb_connection *connection)
126{
127 struct gb_bundle *bundle;
128 struct gb_protocol *protocol;
129
130 /* If we already have a protocol bound here, just return */
131 if (connection->protocol)
132 return;
133
134 protocol = gb_protocol_get(connection->protocol_id,
135 connection->major,
136 connection->minor);
137 if (!protocol)
138 return;
139 connection->protocol = protocol;
140
141 /*
142 * If we have a valid device_id for the bundle, then we have an active
143 * device, so bring up the connection at the same time.
144 * */
145 bundle = connection->bundle;
Greg Kroah-Hartman1b6ea0d2014-12-24 13:01:39 -0800146 if (bundle->device_id != GB_DEVICE_ID_BAD)
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800147 gb_connection_init(connection);
148}
149
Alex Elder177404b2014-10-03 14:14:24 -0500150/*
Alex Elderc68adb22014-10-01 21:54:14 -0500151 * Set up a Greybus connection, representing the bidirectional link
152 * between a CPort on a (local) Greybus host device and a CPort on
153 * another Greybus module.
154 *
Alex Eldere88afa52014-10-01 21:54:15 -0500155 * A connection also maintains the state of operations sent over the
156 * connection.
157 *
Alex Elderc68adb22014-10-01 21:54:14 -0500158 * Returns a pointer to the new connection if successful, or a null
159 * pointer otherwise.
160 */
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500161struct gb_connection *gb_connection_create(struct gb_bundle *bundle,
Alex Elder7fba0072014-10-28 19:35:59 -0500162 u16 cport_id, u8 protocol_id)
Alex Elderc68adb22014-10-01 21:54:14 -0500163{
164 struct gb_connection *connection;
Alex Eldercd345072014-10-02 12:30:05 -0500165 struct greybus_host_device *hd;
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800166 int retval;
Alex Elder6ae7fa42014-11-05 16:12:50 -0600167 u8 major = 0;
168 u8 minor = 1;
Alex Elderc68adb22014-10-01 21:54:14 -0500169
170 connection = kzalloc(sizeof(*connection), GFP_KERNEL);
171 if (!connection)
172 return NULL;
173
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800174 connection->protocol_id = protocol_id;
175 connection->major = major;
176 connection->minor = minor;
Alex Elder4ccb6b72014-10-28 19:36:00 -0500177
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800178 hd = bundle->intf->hd;
Greg Kroah-Hartman4b640bb2014-10-29 09:57:08 +0800179 connection->hd = hd;
Alex Elderf6aec252014-10-06 06:53:06 -0500180 if (!gb_connection_hd_cport_id_alloc(connection)) {
Alex Elder0e447652014-11-05 16:12:51 -0600181 gb_protocol_put(connection->protocol);
Alex Elder9e8a6862014-10-02 12:30:04 -0500182 kfree(connection);
183 return NULL;
184 }
Greg Kroah-Hartman25b7b6d2014-10-06 20:29:40 -0700185
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500186 connection->bundle = bundle;
187 connection->bundle_cport_id = cport_id;
Alex Elder36561f22014-10-22 02:04:30 -0500188 connection->state = GB_CONNECTION_STATE_DISABLED;
Alex Elderad1c4492014-10-02 12:30:06 -0500189
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500190 connection->dev.parent = &bundle->dev;
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800191 connection->dev.bus = &greybus_bus_type;
192 connection->dev.type = &greybus_connection_type;
193 connection->dev.groups = connection_groups;
194 device_initialize(&connection->dev);
195 dev_set_name(&connection->dev, "%s:%d",
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500196 dev_name(&bundle->dev), cport_id);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800197
198 retval = device_add(&connection->dev);
199 if (retval) {
Alex Elder6b099382014-11-05 16:03:12 -0600200 pr_err("failed to add connection device for cport 0x%04hx\n",
201 cport_id);
Alex Elderb2969962014-10-28 19:35:58 -0500202 gb_connection_hd_cport_id_free(connection);
Alex Elder0e447652014-11-05 16:12:51 -0600203 gb_protocol_put(connection->protocol);
Greg Kroah-Hartman4b640bb2014-10-29 09:57:08 +0800204 put_device(&connection->dev);
Viresh Kumara68bd742014-11-13 18:14:39 +0530205 kfree(connection);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800206 return NULL;
207 }
208
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800209 /* XXX Will have to establish connections to get version */
210 gb_connection_bind_protocol(connection);
211 if (!connection->protocol)
212 dev_warn(&bundle->dev,
213 "protocol 0x%02hhx handler not found\n", protocol_id);
214
Alex Elder748e1232014-10-03 14:14:22 -0500215 spin_lock_irq(&gb_connections_lock);
Alex Elder2c43ce42014-11-17 08:08:44 -0600216 list_add_tail(&connection->hd_links, &hd->connections);
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500217 list_add_tail(&connection->bundle_links, &bundle->connections);
Alex Elder748e1232014-10-03 14:14:22 -0500218 spin_unlock_irq(&gb_connections_lock);
219
Alex Elder4afb7fd2014-12-03 08:35:08 -0600220 atomic_set(&connection->op_cycle, 0);
Alex Eldere88afa52014-10-01 21:54:15 -0500221 INIT_LIST_HEAD(&connection->operations);
Alex Elderc68adb22014-10-01 21:54:14 -0500222
223 return connection;
224}
225
226/*
227 * Tear down a previously set up connection.
228 */
229void gb_connection_destroy(struct gb_connection *connection)
230{
Alex Eldere1158df2014-10-22 02:04:29 -0500231 struct gb_operation *operation;
232 struct gb_operation *next;
233
Alex Elderc68adb22014-10-01 21:54:14 -0500234 if (WARN_ON(!connection))
235 return;
236
237 /* XXX Need to wait for any outstanding requests to complete */
Viresh Kumar38d61dd2014-11-14 17:25:02 +0530238 if (WARN_ON(!list_empty(&connection->operations))) {
239 list_for_each_entry_safe(operation, next,
240 &connection->operations, links)
Alex Elderf68c05c2014-11-21 19:29:17 -0600241 gb_operation_cancel(operation, -ESHUTDOWN);
Alex Eldere1158df2014-10-22 02:04:29 -0500242 }
Alex Elder748e1232014-10-03 14:14:22 -0500243 spin_lock_irq(&gb_connections_lock);
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500244 list_del(&connection->bundle_links);
Alex Elder2c43ce42014-11-17 08:08:44 -0600245 list_del(&connection->hd_links);
Alex Elder748e1232014-10-03 14:14:22 -0500246 spin_unlock_irq(&gb_connections_lock);
247
Alex Elderf6aec252014-10-06 06:53:06 -0500248 gb_connection_hd_cport_id_free(connection);
Alex Elder0e447652014-11-05 16:12:51 -0600249 gb_protocol_put(connection->protocol);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800250
Viresh Kumar2352a732015-04-02 17:53:47 +0530251 device_unregister(&connection->dev);
Alex Elderc68adb22014-10-01 21:54:14 -0500252}
Alex Eldere88afa52014-10-01 21:54:15 -0500253
Alex Elder574341c2014-10-16 06:35:35 -0500254int gb_connection_init(struct gb_connection *connection)
255{
Alex Elder36561f22014-10-22 02:04:30 -0500256 int ret;
257
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600258 if (!connection->protocol) {
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800259 dev_warn(&connection->dev, "init without protocol.\n");
260 return 0;
Alex Elder574341c2014-10-16 06:35:35 -0500261 }
Alex Elder36561f22014-10-22 02:04:30 -0500262
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600263 /* Need to enable the connection to initialize it */
264 connection->state = GB_CONNECTION_STATE_ENABLED;
265 ret = connection->protocol->connection_init(connection);
Alex Elder36561f22014-10-22 02:04:30 -0500266 if (ret)
267 connection->state = GB_CONNECTION_STATE_ERROR;
268
269 return ret;
Alex Elder574341c2014-10-16 06:35:35 -0500270}
Alex Elder697e55d2014-10-20 23:01:04 -0500271
272void gb_connection_exit(struct gb_connection *connection)
273{
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600274 if (!connection->protocol) {
Greg Kroah-Hartmanfb69cb52014-12-23 15:16:53 -0800275 dev_warn(&connection->dev, "exit without protocol.\n");
Alex Elder3689f972014-10-27 06:04:30 -0500276 return;
Alex Elder697e55d2014-10-20 23:01:04 -0500277 }
Johan Hovold44538392015-03-17 10:55:52 +0100278
279 if (connection->state != GB_CONNECTION_STATE_ENABLED)
280 return;
281
Alex Elder3689f972014-10-27 06:04:30 -0500282 connection->state = GB_CONNECTION_STATE_DESTROYING;
Alex Elder5d9fd7e2014-11-05 16:12:54 -0600283 connection->protocol->connection_exit(connection);
Alex Elder697e55d2014-10-20 23:01:04 -0500284}