blob: 5e49bc826fe7e84f311246b23ec9d65435e09445 [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");
Viresh Kumar2c7df742015-12-18 15:04:27 +053026gb_interface_attr(vendor_string, "%s");
27gb_interface_attr(product_string, "%s");
Viresh Kumar57c6bcc2015-12-28 11:59:00 +053028gb_interface_attr(serial_number, "0x%016llx");
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050029
Michael Scottd5a26562016-02-08 17:08:46 -080030static ssize_t vendor_id_show(struct device *dev,
31 struct device_attribute *attr,
32 char *buf)
33{
34 struct gb_interface *intf = to_gb_interface(dev);
35
36 /* clear the upper 16-bits to keep userspace "simple" */
37 return scnprintf(buf, PAGE_SIZE, "0x%04x\n",
38 (0x0000FFFF & intf->vendor_id));
39}
40static DEVICE_ATTR_RO(vendor_id);
41
42static ssize_t product_id_show(struct device *dev, struct device_attribute *attr,
43 char *buf)
44{
45 struct gb_interface *intf = to_gb_interface(dev);
46
47 /* clear the upper 16-bits to keep userspace "simple" */
48 return scnprintf(buf, PAGE_SIZE, "0x%04x\n",
49 (0x0000FFFF & intf->product_id));
50}
51static DEVICE_ATTR_RO(product_id);
52
Viresh Kumard39bf702015-12-28 11:59:01 +053053static ssize_t version_show(struct device *dev, struct device_attribute *attr,
54 char *buf)
55{
56 struct gb_interface *intf = to_gb_interface(dev);
57
58 return scnprintf(buf, PAGE_SIZE, "%u.%u\n", intf->version_major,
59 intf->version_minor);
60}
61static DEVICE_ATTR_RO(version);
62
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080063static struct attribute *interface_attrs[] = {
Viresh Kumar0e9403a02015-12-22 22:04:34 +053064 &dev_attr_ddbl1_manufacturer_id.attr,
65 &dev_attr_ddbl1_product_id.attr,
Johan Hovold320421a2015-11-25 15:58:58 +010066 &dev_attr_interface_id.attr,
Johan Hovold9f592632015-11-25 15:58:56 +010067 &dev_attr_vendor_id.attr,
68 &dev_attr_product_id.attr,
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050069 &dev_attr_vendor_string.attr,
70 &dev_attr_product_string.attr,
Viresh Kumar57c6bcc2015-12-28 11:59:00 +053071 &dev_attr_serial_number.attr,
Viresh Kumard39bf702015-12-28 11:59:01 +053072 &dev_attr_version.attr,
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050073 NULL,
74};
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080075ATTRIBUTE_GROUPS(interface);
Greg Kroah-Hartmanab88eb52014-12-11 17:10:59 -050076
77
Alex Eldere1e9dbd2014-10-01 21:54:11 -050078/* XXX This could be per-host device */
Greg Kroah-Hartman49011752014-12-19 14:56:37 -080079static DEFINE_SPINLOCK(gb_interfaces_lock);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050080
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080081// FIXME, odds are you don't want to call this function, rework the caller to
82// not need it please.
Johan Hovold25376362015-11-03 18:03:23 +010083struct gb_interface *gb_interface_find(struct gb_host_device *hd,
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +053084 u8 interface_id)
Viresh Kumar9ca4d622014-11-14 17:25:06 +053085{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080086 struct gb_interface *intf;
Viresh Kumar9ca4d622014-11-14 17:25:06 +053087
Greg Kroah-Hartman1cd56a82014-12-19 14:56:36 -080088 list_for_each_entry(intf, &hd->interfaces, links)
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +053089 if (intf->interface_id == interface_id)
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080090 return intf;
Viresh Kumar9ca4d622014-11-14 17:25:06 +053091
92 return NULL;
93}
94
Viresh Kumar51b5d8d2015-05-20 17:33:51 +053095static void gb_interface_release(struct device *dev)
Alex Elder697e55d2014-10-20 23:01:04 -050096{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080097 struct gb_interface *intf = to_gb_interface(dev);
Alex Elder697e55d2014-10-20 23:01:04 -050098
Johan Hovoldeeb6a6f2015-11-11 10:07:05 +010099 kfree(intf->product_string);
100 kfree(intf->vendor_string);
101
Johan Hovoldc6346502015-12-15 15:28:56 +0100102 if (intf->control)
103 gb_control_destroy(intf->control);
104
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800105 kfree(intf);
Alex Elder697e55d2014-10-20 23:01:04 -0500106}
107
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800108struct device_type greybus_interface_type = {
109 .name = "greybus_interface",
Viresh Kumar51b5d8d2015-05-20 17:33:51 +0530110 .release = gb_interface_release,
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800111};
112
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500113/*
Bryan O'Donoghue464dc8c2015-08-17 00:57:16 +0100114 * A Greybus module represents a user-replaceable component on an Ara
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800115 * phone. An interface is the physical connection on that module. A
116 * module may have more than one interface.
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500117 *
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +0530118 * Create a gb_interface structure to represent a discovered interface.
119 * The position of interface within the Endo is encoded in "interface_id"
120 * argument.
121 *
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -0800122 * Returns a pointer to the new interfce or a null pointer if a
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500123 * failure occurs due to memory exhaustion.
124 */
Johan Hovold25376362015-11-03 18:03:23 +0100125struct gb_interface *gb_interface_create(struct gb_host_device *hd,
Viresh Kumar6c68da22015-06-22 16:42:27 +0530126 u8 interface_id)
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500127{
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800128 struct gb_interface *intf;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500129
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800130 intf = kzalloc(sizeof(*intf), GFP_KERNEL);
131 if (!intf)
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100132 return NULL;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500133
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800134 intf->hd = hd; /* XXX refcount? */
Viresh Kumarc9d9d0d2015-04-01 20:31:58 +0530135 intf->interface_id = interface_id;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800136 INIT_LIST_HEAD(&intf->bundles);
Greg Kroah-Hartman86cad662014-12-23 15:16:50 -0800137 INIT_LIST_HEAD(&intf->manifest_descs);
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500138
Viresh Kumarc3add782015-07-01 12:13:58 +0530139 /* Invalid device id to start with */
140 intf->device_id = GB_DEVICE_ID_BAD;
141
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100142 intf->dev.parent = &hd->dev;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800143 intf->dev.bus = &greybus_bus_type;
144 intf->dev.type = &greybus_interface_type;
145 intf->dev.groups = interface_groups;
Johan Hovold2adaefb2015-11-25 15:59:02 +0100146 intf->dev.dma_mask = hd->dev.dma_mask;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800147 device_initialize(&intf->dev);
Johan Hovold8b0df4b2015-11-25 15:59:04 +0100148 dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800149
Johan Hovoldc6346502015-12-15 15:28:56 +0100150 intf->control = gb_control_create(intf);
151 if (!intf->control) {
152 put_device(&intf->dev);
153 return NULL;
154 }
155
Greg Kroah-Hartman49011752014-12-19 14:56:37 -0800156 spin_lock_irq(&gb_interfaces_lock);
Viresh Kumar928f2ab2015-06-04 18:16:45 +0530157 list_add(&intf->links, &hd->interfaces);
Greg Kroah-Hartman49011752014-12-19 14:56:37 -0800158 spin_unlock_irq(&gb_interfaces_lock);
Viresh Kumar0a68a162014-11-13 18:14:37 +0530159
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800160 return intf;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500161}
162
163/*
Johan Hovoldfebe2522015-11-11 10:07:06 +0100164 * Tear down a previously set up interface.
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500165 */
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700166void gb_interface_remove(struct gb_interface *intf)
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500167{
Alex Elderfe53b452015-06-12 10:21:11 -0500168 struct gb_bundle *bundle;
169 struct gb_bundle *next;
Viresh Kumar2352a732015-04-02 17:53:47 +0530170
Johan Hovold141af4f2015-12-15 15:28:57 +0100171 if (intf->disconnected)
172 gb_control_disable(intf->control);
173
Alex Elderfe53b452015-06-12 10:21:11 -0500174 list_for_each_entry_safe(bundle, next, &intf->bundles, links)
175 gb_bundle_destroy(bundle);
Alex Elder697e55d2014-10-20 23:01:04 -0500176
Viresh Kumar907d1e12016-02-19 15:57:46 +0530177 if (device_is_registered(&intf->dev)) {
Johan Hovoldab66dd72015-12-07 15:05:45 +0100178 device_del(&intf->dev);
Viresh Kumar907d1e12016-02-19 15:57:46 +0530179 dev_info(&intf->dev, "Interface removed\n");
180 }
Johan Hovoldab66dd72015-12-07 15:05:45 +0100181
Johan Hovoldc6346502015-12-15 15:28:56 +0100182 gb_control_disable(intf->control);
Johan Hovold47091af2015-11-25 15:59:26 +0100183
Johan Hovoldab66dd72015-12-07 15:05:45 +0100184 spin_lock_irq(&gb_interfaces_lock);
185 list_del(&intf->links);
186 spin_unlock_irq(&gb_interfaces_lock);
187
188 put_device(&intf->dev);
Alex Elder574341c2014-10-16 06:35:35 -0500189}
190
Johan Hovold25376362015-11-03 18:03:23 +0100191void gb_interfaces_remove(struct gb_host_device *hd)
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700192{
193 struct gb_interface *intf, *temp;
194
195 list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
196 gb_interface_remove(intf);
197}
198
Viresh Kumar676daaf2014-11-14 17:25:07 +0530199/**
Viresh Kumarb950dc22015-07-03 17:00:26 +0530200 * gb_interface_init
Viresh Kumar676daaf2014-11-14 17:25:07 +0530201 *
Viresh Kumar6c68da22015-06-22 16:42:27 +0530202 * Create connection for control CPort and then request/parse manifest.
203 * Finally initialize all the bundles to set routes via SVC and initialize all
204 * connections.
Viresh Kumar676daaf2014-11-14 17:25:07 +0530205 */
Viresh Kumar6c68da22015-06-22 16:42:27 +0530206int gb_interface_init(struct gb_interface *intf, u8 device_id)
Viresh Kumar676daaf2014-11-14 17:25:07 +0530207{
Johan Hovold708d07a2015-12-07 15:05:44 +0100208 struct gb_bundle *bundle, *tmp;
Viresh Kumar6c68da22015-06-22 16:42:27 +0530209 int ret, size;
210 void *manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530211
Viresh Kumarc3add782015-07-01 12:13:58 +0530212 intf->device_id = device_id;
213
Johan Hovoldc6346502015-12-15 15:28:56 +0100214 /* Establish control connection */
215 ret = gb_control_enable(intf->control);
216 if (ret)
Johan Hovold0bf1f242015-12-07 15:05:34 +0100217 return ret;
Johan Hovold0bf1f242015-12-07 15:05:34 +0100218
Viresh Kumar6c68da22015-06-22 16:42:27 +0530219 /* Get manifest size using control protocol on CPort */
220 size = gb_control_get_manifest_size_operation(intf);
221 if (size <= 0) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100222 dev_err(&intf->dev, "failed to get manifest size: %d\n", size);
Viresh Kumar6c68da22015-06-22 16:42:27 +0530223 if (size)
224 return size;
225 else
226 return -EINVAL;
227 }
228
229 manifest = kmalloc(size, GFP_KERNEL);
230 if (!manifest)
231 return -ENOMEM;
232
233 /* Get manifest using control protocol on CPort */
234 ret = gb_control_get_manifest_operation(intf, manifest, size);
235 if (ret) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100236 dev_err(&intf->dev, "failed to get manifest: %d\n", ret);
Viresh Kumar6c68da22015-06-22 16:42:27 +0530237 goto free_manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530238 }
239
240 /*
Viresh Kumar6c68da22015-06-22 16:42:27 +0530241 * Parse the manifest and build up our data structures representing
242 * what's in it.
Viresh Kumar676daaf2014-11-14 17:25:07 +0530243 */
Viresh Kumar6c68da22015-06-22 16:42:27 +0530244 if (!gb_manifest_parse(intf, manifest, size)) {
Johan Hovold5626e0b2015-12-07 15:05:46 +0100245 dev_err(&intf->dev, "failed to parse manifest\n");
Viresh Kumar6c68da22015-06-22 16:42:27 +0530246 ret = -EINVAL;
247 goto free_manifest;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530248 }
249
Viresh Kumard39bf702015-12-28 11:59:01 +0530250 ret = gb_control_get_interface_version_operation(intf);
251 if (ret)
252 goto free_manifest;
253
Johan Hovoldb807aa72016-01-19 12:51:21 +0100254 ret = gb_control_get_bundle_versions(intf->control);
255 if (ret)
256 goto free_manifest;
257
Johan Hovoldab66dd72015-12-07 15:05:45 +0100258 /* Register the interface and its bundles. */
259 ret = device_add(&intf->dev);
260 if (ret) {
261 dev_err(&intf->dev, "failed to register interface: %d\n", ret);
262 goto free_manifest;
263 }
264
Viresh Kumar907d1e12016-02-19 15:57:46 +0530265 dev_info(&intf->dev, "Interface added: VID=0x%08x, PID=0x%08x\n",
266 intf->vendor_id, intf->product_id);
267 dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n",
268 intf->ddbl1_manufacturer_id, intf->ddbl1_product_id);
269
Johan Hovold708d07a2015-12-07 15:05:44 +0100270 list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) {
271 ret = gb_bundle_add(bundle);
272 if (ret) {
273 gb_bundle_destroy(bundle);
274 continue;
275 }
Johan Hovold708d07a2015-12-07 15:05:44 +0100276 }
277
278 ret = 0;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530279
Viresh Kumar6c68da22015-06-22 16:42:27 +0530280free_manifest:
281 kfree(manifest);
282 return ret;
Viresh Kumar676daaf2014-11-14 17:25:07 +0530283}