blob: edac2383e492a2471fd34fdc19549d4603b53c51 [file] [log] [blame]
Alex Eldere1e9dbd2014-10-01 21:54:11 -05001/*
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -08002 * Greybus interface code
Alex Eldere1e9dbd2014-10-01 21:54:11 -05003 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Alex Eldere1e9dbd2014-10-01 21:54:11 -05006 *
7 * Released under the GPLv2 only.
8 */
9
10#include "greybus.h"
11
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080012/* interface sysfs attributes */
13#define gb_interface_attr(field, type) \
14static ssize_t field##_show(struct device *dev, \
15 struct device_attribute *attr, \
16 char *buf) \
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050017{ \
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080018 struct gb_interface *intf = to_gb_interface(dev); \
Viresh Kumar2c7df742015-12-18 15:04:27 +053019 return scnprintf(buf, PAGE_SIZE, type"\n", intf->field); \
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050020} \
21static DEVICE_ATTR_RO(field)
22
Viresh Kumar0e9403a02015-12-22 22:04:34 +053023gb_interface_attr(ddbl1_manufacturer_id, "0x%08x");
24gb_interface_attr(ddbl1_product_id, "0x%08x");
Viresh Kumar2c7df742015-12-18 15:04:27 +053025gb_interface_attr(interface_id, "%u");
26gb_interface_attr(vendor_id, "0x%08x");
27gb_interface_attr(product_id, "0x%08x");
28gb_interface_attr(vendor_string, "%s");
29gb_interface_attr(product_string, "%s");
Viresh Kumar57c6bcc2015-12-28 11:59:00 +053030gb_interface_attr(serial_number, "0x%016llx");
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050031
Viresh Kumard39bf702015-12-28 11:59:01 +053032static ssize_t version_show(struct device *dev, struct device_attribute *attr,
33 char *buf)
34{
35 struct gb_interface *intf = to_gb_interface(dev);
36
37 return scnprintf(buf, PAGE_SIZE, "%u.%u\n", intf->version_major,
38 intf->version_minor);
39}
40static DEVICE_ATTR_RO(version);
41
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080042static struct attribute *interface_attrs[] = {
Viresh Kumar0e9403a02015-12-22 22:04:34 +053043 &dev_attr_ddbl1_manufacturer_id.attr,
44 &dev_attr_ddbl1_product_id.attr,
Johan Hovold320421a2015-11-25 15:58:58 +010045 &dev_attr_interface_id.attr,
Johan Hovold9f592632015-11-25 15:58:56 +010046 &dev_attr_vendor_id.attr,
47 &dev_attr_product_id.attr,
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050048 &dev_attr_vendor_string.attr,
49 &dev_attr_product_string.attr,
Viresh Kumar57c6bcc2015-12-28 11:59:00 +053050 &dev_attr_serial_number.attr,
Viresh Kumard39bf702015-12-28 11:59:01 +053051 &dev_attr_version.attr,
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050052 NULL,
53};
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080054ATTRIBUTE_GROUPS(interface);
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050055
56
Alex Eldere1e9dbd2014-10-01 21:54:11 -050057/* XXX This could be per-host device */
Greg Kroah-Hartman49011752014-12-19 14:56:37 -080058static DEFINE_SPINLOCK(gb_interfaces_lock);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050059
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080060// FIXME, odds are you don't want to call this function, rework the caller to
61// not need it please.
Johan Hovold25376362015-11-03 18:03:23 +010062struct gb_interface *gb_interface_find(struct gb_host_device *hd,
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +053063 u8 interface_id)
Viresh Kumar9ca4d622014-11-14 17:25:06 +053064{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080065 struct gb_interface *intf;
Viresh Kumar9ca4d622014-11-14 17:25:06 +053066
Greg Kroah-Hartman1cd56a82014-12-19 14:56:36 -080067 list_for_each_entry(intf, &hd->interfaces, links)
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +053068 if (intf->interface_id == interface_id)
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080069 return intf;
Viresh Kumar9ca4d622014-11-14 17:25:06 +053070
71 return NULL;
72}
73
Viresh Kumar51b5d8d2015-05-20 17:33:51 +053074static void gb_interface_release(struct device *dev)
Alex Elder697e55d2014-10-20 23:01:04 -050075{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080076 struct gb_interface *intf = to_gb_interface(dev);
Alex Elder697e55d2014-10-20 23:01:04 -050077
Johan Hovoldeeb6a6f2015-11-11 10:07:05 +010078 kfree(intf->product_string);
79 kfree(intf->vendor_string);
80
Johan Hovoldc6346502015-12-15 15:28:56 +010081 if (intf->control)
82 gb_control_destroy(intf->control);
83
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080084 kfree(intf);
Alex Elder697e55d2014-10-20 23:01:04 -050085}
86
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080087struct device_type greybus_interface_type = {
88 .name = "greybus_interface",
Viresh Kumar51b5d8d2015-05-20 17:33:51 +053089 .release = gb_interface_release,
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080090};
91
Alex Eldere1e9dbd2014-10-01 21:54:11 -050092/*
Bryan O'Donoghue464dc8c2015-08-17 00:57:16 +010093 * A Greybus module represents a user-replaceable component on an Ara
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080094 * phone. An interface is the physical connection on that module. A
95 * module may have more than one interface.
Alex Eldere1e9dbd2014-10-01 21:54:11 -050096 *
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +053097 * Create a gb_interface structure to represent a discovered interface.
98 * The position of interface within the Endo is encoded in "interface_id"
99 * argument.
100 *
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -0800101 * Returns a pointer to the new interfce or a null pointer if a
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500102 * failure occurs due to memory exhaustion.
103 */
Johan Hovold25376362015-11-03 18:03:23 +0100104struct gb_interface *gb_interface_create(struct gb_host_device *hd,
Viresh Kumar6c68da22015-06-22 16:42:27 +0530105 u8 interface_id)
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500106{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800107 struct gb_interface *intf;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500108
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800109 intf = kzalloc(sizeof(*intf), GFP_KERNEL);
110 if (!intf)
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100111 return NULL;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500112
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800113 intf->hd = hd; /* XXX refcount? */
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +0530114 intf->interface_id = interface_id;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800115 INIT_LIST_HEAD(&intf->bundles);
Greg Kroah-Hartman86cad662014-12-23 15:16:50 -0800116 INIT_LIST_HEAD(&intf->manifest_descs);
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500117
Viresh Kumarc3add782015-07-01 12:13:58 +0530118 /* Invalid device id to start with */
119 intf->device_id = GB_DEVICE_ID_BAD;
120
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100121 intf->dev.parent = &hd->dev;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800122 intf->dev.bus = &greybus_bus_type;
123 intf->dev.type = &greybus_interface_type;
124 intf->dev.groups = interface_groups;
Johan Hovold2adaefb2015-11-25 15:59:02 +0100125 intf->dev.dma_mask = hd->dev.dma_mask;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800126 device_initialize(&intf->dev);
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100127 dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800128
Johan Hovoldc6346502015-12-15 15:28:56 +0100129 intf->control = gb_control_create(intf);
130 if (!intf->control) {
131 put_device(&intf->dev);
132 return NULL;
133 }
134
Greg Kroah-Hartman49011752014-12-19 14:56:37 -0800135 spin_lock_irq(&gb_interfaces_lock);
Viresh Kumar928f2ab2015-06-04 18:16:45 +0530136 list_add(&intf->links, &hd->interfaces);
Greg Kroah-Hartman49011752014-12-19 14:56:37 -0800137 spin_unlock_irq(&gb_interfaces_lock);
Viresh Kumar0a68a162014-11-13 18:14:37 +0530138
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800139 return intf;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500140}
141
142/*
Johan Hovoldfebe2522015-11-11 10:07:06 +0100143 * Tear down a previously set up interface.
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500144 */
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700145void gb_interface_remove(struct gb_interface *intf)
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500146{
Alex Elderfe53b452015-06-12 10:21:11 -0500147 struct gb_bundle *bundle;
148 struct gb_bundle *next;
Viresh Kumar2352a732015-04-02 17:53:47 +0530149
Johan Hovold141af4f2015-12-15 15:28:57 +0100150 if (intf->disconnected)
151 gb_control_disable(intf->control);
152
Alex Elderfe53b452015-06-12 10:21:11 -0500153 list_for_each_entry_safe(bundle, next, &intf->bundles, links)
154 gb_bundle_destroy(bundle);
Alex Elder697e55d2014-10-20 23:01:04 -0500155
Johan Hovoldab66dd72015-12-07 15:05:45 +0100156 if (device_is_registered(&intf->dev))
157 device_del(&intf->dev);
158
Johan Hovoldc6346502015-12-15 15:28:56 +0100159 gb_control_disable(intf->control);
Johan Hovold47091af2015-11-25 15:59:26 +0100160
Johan Hovoldab66dd72015-12-07 15:05:45 +0100161 spin_lock_irq(&gb_interfaces_lock);
162 list_del(&intf->links);
163 spin_unlock_irq(&gb_interfaces_lock);
164
165 put_device(&intf->dev);
Alex Elder574341c2014-10-16 06:35:35 -0500166}
167
Johan Hovold25376362015-11-03 18:03:23 +0100168void gb_interfaces_remove(struct gb_host_device *hd)
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700169{
170 struct gb_interface *intf, *temp;
171
172 list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
173 gb_interface_remove(intf);
174}
175
Viresh Kumar676daaf2014-11-14 17:25:07 +0530176/**
Viresh Kumarb950dc22015-07-03 17:00:26 +0530177 * gb_interface_init
Viresh Kumar676daaf2014-11-14 17:25:07 +0530178 *
Viresh Kumar6c68da22015-06-22 16:42:27 +0530179 * Create connection for control CPort and then request/parse manifest.
180 * Finally initialize all the bundles to set routes via SVC and initialize all
181 * connections.
Viresh Kumar676daaf2014-11-14 17:25:07 +0530182 */
Viresh Kumar6c68da22015-06-22 16:42:27 +0530183int gb_interface_init(struct gb_interface *intf, u8 device_id)
Viresh Kumar676daaf2014-11-14 17:25:07 +0530184{
Johan Hovold708d07a2015-12-07 15:05:44 +0100185 struct gb_bundle *bundle, *tmp;
Johan Hovold47091af2015-11-25 15:59:26 +0100186 struct gb_connection *connection;
Viresh Kumar6c68da22015-06-22 16:42:27 +0530187 int ret, size;
188 void *manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530189
Viresh Kumarc3add782015-07-01 12:13:58 +0530190 intf->device_id = device_id;
191
Johan Hovoldc6346502015-12-15 15:28:56 +0100192 /* Establish control connection */
193 ret = gb_control_enable(intf->control);
194 if (ret)
Johan Hovold0bf1f242015-12-07 15:05:34 +0100195 return ret;
Johan Hovold0bf1f242015-12-07 15:05:34 +0100196
Viresh Kumar6c68da22015-06-22 16:42:27 +0530197 /* Get manifest size using control protocol on CPort */
198 size = gb_control_get_manifest_size_operation(intf);
199 if (size <= 0) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100200 dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
Viresh Kumar6c68da22015-06-22 16:42:27 +0530201 if (size)
202 return size;
203 else
204 return -EINVAL;
205 }
206
207 manifest = kmalloc(size, GFP_KERNEL);
208 if (!manifest)
209 return -ENOMEM;
210
211 /* Get manifest using control protocol on CPort */
212 ret = gb_control_get_manifest_operation(intf, manifest, size);
213 if (ret) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100214 dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
Viresh Kumar6c68da22015-06-22 16:42:27 +0530215 goto free_manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530216 }
217
218 /*
Viresh Kumar6c68da22015-06-22 16:42:27 +0530219 * Parse the manifest and build up our data structures representing
220 * what's in it.
Viresh Kumar676daaf2014-11-14 17:25:07 +0530221 */
Viresh Kumar6c68da22015-06-22 16:42:27 +0530222 if (!gb_manifest_parse(intf, manifest, size)) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100223 dev_err(&intf->dev, "failed to parse manifest\n");
Viresh Kumar6c68da22015-06-22 16:42:27 +0530224 ret = -EINVAL;
225 goto free_manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530226 }
227
Viresh Kumard39bf702015-12-28 11:59:01 +0530228 ret = gb_control_get_interface_version_operation(intf);
229 if (ret)
230 goto free_manifest;
231
Johan Hovoldab66dd72015-12-07 15:05:45 +0100232 /* Register the interface and its bundles. */
233 ret = device_add(&intf->dev);
234 if (ret) {
235 dev_err(&intf->dev, "failed to register interface: %d\n", ret);
236 goto free_manifest;
237 }
238
Johan Hovold708d07a2015-12-07 15:05:44 +0100239 list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
240 ret = gb_bundle_add(bundle);
241 if (ret) {
242 gb_bundle_destroy(bundle);
243 continue;
244 }
245
246 list_for_each_entry(connection, &bundle->connections,
247 bundle_links) {
248 ret = gb_connection_init(connection);
249 if (ret)
250 break;
251 }
252 if (ret)
253 gb_bundle_destroy(bundle);
254 }
255
256 ret = 0;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530257
Viresh Kumar6c68da22015-06-22 16:42:27 +0530258free_manifest:
259 kfree(manifest);
260 return ret;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530261}