Commit | Line | Data |
---|---|---|
e1e9dbdd | 1 | /* |
4ab9b3c2 | 2 | * Greybus interface code |
e1e9dbdd AE |
3 | * |
4 | * Copyright 2014 Google Inc. | |
a46e9671 | 5 | * Copyright 2014 Linaro Ltd. |
e1e9dbdd AE |
6 | * |
7 | * Released under the GPLv2 only. | |
8 | */ | |
9 | ||
10 | #include "greybus.h" | |
11 | ||
4ab9b3c2 GKH |
12 | /* interface sysfs attributes */ |
13 | #define gb_interface_attr(field, type) \ | |
14 | static ssize_t field##_show(struct device *dev, \ | |
15 | struct device_attribute *attr, \ | |
16 | char *buf) \ | |
ab88eb58 | 17 | { \ |
4ab9b3c2 | 18 | struct gb_interface *intf = to_gb_interface(dev); \ |
505c9d27 | 19 | return scnprintf(buf, PAGE_SIZE, "%"#type"\n", intf->field); \ |
ab88eb58 GKH |
20 | } \ |
21 | static DEVICE_ATTR_RO(field) | |
22 | ||
320421a8 | 23 | gb_interface_attr(interface_id, u); |
9f59263a JH |
24 | gb_interface_attr(vendor_id, x); |
25 | gb_interface_attr(product_id, x); | |
4ab9b3c2 GKH |
26 | gb_interface_attr(vendor_string, s); |
27 | gb_interface_attr(product_string, s); | |
ab88eb58 | 28 | |
4ab9b3c2 | 29 | static struct attribute *interface_attrs[] = { |
320421a8 | 30 | &dev_attr_interface_id.attr, |
9f59263a JH |
31 | &dev_attr_vendor_id.attr, |
32 | &dev_attr_product_id.attr, | |
ab88eb58 GKH |
33 | &dev_attr_vendor_string.attr, |
34 | &dev_attr_product_string.attr, | |
35 | NULL, | |
36 | }; | |
4ab9b3c2 | 37 | ATTRIBUTE_GROUPS(interface); |
ab88eb58 GKH |
38 | |
39 | ||
e1e9dbdd | 40 | /* XXX This could be per-host device */ |
4901175f | 41 | static DEFINE_SPINLOCK(gb_interfaces_lock); |
e1e9dbdd | 42 | |
df671553 GKH |
43 | // FIXME, odds are you don't want to call this function, rework the caller to |
44 | // not need it please. | |
2537636a | 45 | struct gb_interface *gb_interface_find(struct gb_host_device *hd, |
c9d9d0d4 | 46 | u8 interface_id) |
9ca4d62f | 47 | { |
4ab9b3c2 | 48 | struct gb_interface *intf; |
9ca4d62f | 49 | |
1cd56a80 | 50 | list_for_each_entry(intf, &hd->interfaces, links) |
c9d9d0d4 | 51 | if (intf->interface_id == interface_id) |
4ab9b3c2 | 52 | return intf; |
9ca4d62f VK |
53 | |
54 | return NULL; | |
55 | } | |
56 | ||
51b5d8d7 | 57 | static void gb_interface_release(struct device *dev) |
697e55d3 | 58 | { |
4ab9b3c2 | 59 | struct gb_interface *intf = to_gb_interface(dev); |
697e55d3 | 60 | |
eeb6a6ff JH |
61 | kfree(intf->product_string); |
62 | kfree(intf->vendor_string); | |
63 | ||
4ab9b3c2 | 64 | kfree(intf); |
697e55d3 AE |
65 | } |
66 | ||
4ab9b3c2 GKH |
67 | struct device_type greybus_interface_type = { |
68 | .name = "greybus_interface", | |
51b5d8d7 | 69 | .release = gb_interface_release, |
f0f61b90 GKH |
70 | }; |
71 | ||
6c68da26 VK |
72 | /* |
73 | * Create kernel structures corresponding to a bundle and connection for | |
7a24a3f6 | 74 | * managing control/svc CPort. |
6c68da26 | 75 | */ |
7a24a3f6 | 76 | int gb_create_bundle_connection(struct gb_interface *intf, u8 class) |
6c68da26 VK |
77 | { |
78 | struct gb_bundle *bundle; | |
7a24a3f6 VK |
79 | u32 ida_start, ida_end; |
80 | u8 bundle_id, protocol_id; | |
81 | u16 cport_id; | |
82 | ||
83 | if (class == GREYBUS_CLASS_CONTROL) { | |
84 | protocol_id = GREYBUS_PROTOCOL_CONTROL; | |
85 | bundle_id = GB_CONTROL_BUNDLE_ID; | |
86 | cport_id = GB_CONTROL_CPORT_ID; | |
87 | ida_start = 0; | |
144670c2 | 88 | ida_end = intf->hd->num_cports - 1; |
7a24a3f6 VK |
89 | } else if (class == GREYBUS_CLASS_SVC) { |
90 | protocol_id = GREYBUS_PROTOCOL_SVC; | |
91 | bundle_id = GB_SVC_BUNDLE_ID; | |
92 | cport_id = GB_SVC_CPORT_ID; | |
93 | ida_start = GB_SVC_CPORT_ID; | |
94 | ida_end = GB_SVC_CPORT_ID + 1; | |
95 | } else { | |
96 | WARN_ON(1); | |
97 | return -EINVAL; | |
98 | } | |
6c68da26 | 99 | |
7a24a3f6 | 100 | bundle = gb_bundle_create(intf, bundle_id, class); |
6c68da26 VK |
101 | if (!bundle) |
102 | return -EINVAL; | |
103 | ||
582b3a13 | 104 | if (!gb_connection_create_range(bundle->intf->hd, bundle, |
7a24a3f6 VK |
105 | cport_id, protocol_id, ida_start, |
106 | ida_end)) | |
6c68da26 VK |
107 | return -EINVAL; |
108 | ||
6c68da26 VK |
109 | return 0; |
110 | } | |
111 | ||
e1e9dbdd | 112 | /* |
464dc8cb | 113 | * A Greybus module represents a user-replaceable component on an Ara |
4ab9b3c2 GKH |
114 | * phone. An interface is the physical connection on that module. A |
115 | * module may have more than one interface. | |
e1e9dbdd | 116 | * |
c9d9d0d4 VK |
117 | * Create a gb_interface structure to represent a discovered interface. |
118 | * The position of interface within the Endo is encoded in "interface_id" | |
119 | * argument. | |
120 | * | |
df671553 | 121 | * Returns a pointer to the new interfce or a null pointer if a |
e1e9dbdd AE |
122 | * failure occurs due to memory exhaustion. |
123 | */ | |
2537636a | 124 | struct gb_interface *gb_interface_create(struct gb_host_device *hd, |
6c68da26 | 125 | u8 interface_id) |
e1e9dbdd | 126 | { |
df671553 | 127 | struct gb_module *module; |
4ab9b3c2 | 128 | struct gb_interface *intf; |
f0f61b90 | 129 | int retval; |
e1e9dbdd | 130 | |
51e93aea | 131 | module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id)); |
df671553 GKH |
132 | if (!module) |
133 | return NULL; | |
134 | ||
4ab9b3c2 GKH |
135 | intf = kzalloc(sizeof(*intf), GFP_KERNEL); |
136 | if (!intf) | |
71e49380 | 137 | goto put_module; |
e1e9dbdd | 138 | |
4ab9b3c2 | 139 | intf->hd = hd; /* XXX refcount? */ |
df671553 | 140 | intf->module = module; |
c9d9d0d4 | 141 | intf->interface_id = interface_id; |
4ab9b3c2 | 142 | INIT_LIST_HEAD(&intf->bundles); |
86cad666 | 143 | INIT_LIST_HEAD(&intf->manifest_descs); |
e1e9dbdd | 144 | |
c3add788 VK |
145 | /* Invalid device id to start with */ |
146 | intf->device_id = GB_DEVICE_ID_BAD; | |
147 | ||
df671553 | 148 | intf->dev.parent = &module->dev; |
4ab9b3c2 GKH |
149 | intf->dev.bus = &greybus_bus_type; |
150 | intf->dev.type = &greybus_interface_type; | |
151 | intf->dev.groups = interface_groups; | |
2adaefb1 | 152 | intf->dev.dma_mask = hd->dev.dma_mask; |
4ab9b3c2 | 153 | device_initialize(&intf->dev); |
df671553 | 154 | dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id); |
f0f61b90 | 155 | |
4ab9b3c2 | 156 | retval = device_add(&intf->dev); |
f0f61b90 | 157 | if (retval) { |
35a84ba7 | 158 | pr_err("failed to add interface %u\n", interface_id); |
71e49380 | 159 | goto free_intf; |
f0f61b90 GKH |
160 | } |
161 | ||
4901175f | 162 | spin_lock_irq(&gb_interfaces_lock); |
928f2abd | 163 | list_add(&intf->links, &hd->interfaces); |
4901175f | 164 | spin_unlock_irq(&gb_interfaces_lock); |
0a68a16b | 165 | |
4ab9b3c2 | 166 | return intf; |
71e49380 VK |
167 | |
168 | free_intf: | |
169 | put_device(&intf->dev); | |
71e49380 VK |
170 | put_module: |
171 | put_device(&module->dev); | |
172 | return NULL; | |
e1e9dbdd AE |
173 | } |
174 | ||
175 | /* | |
febe2521 | 176 | * Tear down a previously set up interface. |
e1e9dbdd | 177 | */ |
80d1ede8 | 178 | void gb_interface_remove(struct gb_interface *intf) |
e1e9dbdd | 179 | { |
2352a732 | 180 | struct gb_module *module; |
fe53b45c AE |
181 | struct gb_bundle *bundle; |
182 | struct gb_bundle *next; | |
2352a732 | 183 | |
4ab9b3c2 | 184 | if (WARN_ON(!intf)) |
e1e9dbdd AE |
185 | return; |
186 | ||
4901175f | 187 | spin_lock_irq(&gb_interfaces_lock); |
4ab9b3c2 | 188 | list_del(&intf->links); |
4901175f | 189 | spin_unlock_irq(&gb_interfaces_lock); |
e1e9dbdd | 190 | |
fe53b45c AE |
191 | list_for_each_entry_safe(bundle, next, &intf->bundles, links) |
192 | gb_bundle_destroy(bundle); | |
697e55d3 | 193 | |
2352a732 VK |
194 | module = intf->module; |
195 | device_unregister(&intf->dev); | |
a4d9150c | 196 | put_device(&module->dev); |
574341c6 AE |
197 | } |
198 | ||
2537636a | 199 | void gb_interfaces_remove(struct gb_host_device *hd) |
80d1ede8 VK |
200 | { |
201 | struct gb_interface *intf, *temp; | |
202 | ||
203 | list_for_each_entry_safe(intf, temp, &hd->interfaces, links) | |
204 | gb_interface_remove(intf); | |
205 | } | |
206 | ||
676daaf4 | 207 | /** |
b950dc28 | 208 | * gb_interface_init |
676daaf4 | 209 | * |
6c68da26 VK |
210 | * Create connection for control CPort and then request/parse manifest. |
211 | * Finally initialize all the bundles to set routes via SVC and initialize all | |
212 | * connections. | |
676daaf4 | 213 | */ |
6c68da26 | 214 | int gb_interface_init(struct gb_interface *intf, u8 device_id) |
676daaf4 | 215 | { |
6c68da26 VK |
216 | int ret, size; |
217 | void *manifest; | |
218 | ||
c3add788 VK |
219 | intf->device_id = device_id; |
220 | ||
6c68da26 | 221 | /* Establish control CPort connection */ |
7a24a3f6 | 222 | ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_CONTROL); |
6c68da26 VK |
223 | if (ret) { |
224 | dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret); | |
225 | return ret; | |
226 | } | |
676daaf4 | 227 | |
6c68da26 VK |
228 | /* Get manifest size using control protocol on CPort */ |
229 | size = gb_control_get_manifest_size_operation(intf); | |
230 | if (size <= 0) { | |
231 | dev_err(&intf->dev, "%s: Failed to get manifest size (%d)\n", | |
232 | __func__, size); | |
233 | if (size) | |
234 | return size; | |
235 | else | |
236 | return -EINVAL; | |
237 | } | |
238 | ||
239 | manifest = kmalloc(size, GFP_KERNEL); | |
240 | if (!manifest) | |
241 | return -ENOMEM; | |
242 | ||
243 | /* Get manifest using control protocol on CPort */ | |
244 | ret = gb_control_get_manifest_operation(intf, manifest, size); | |
245 | if (ret) { | |
246 | dev_err(&intf->dev, "%s: Failed to get manifest\n", __func__); | |
247 | goto free_manifest; | |
676daaf4 VK |
248 | } |
249 | ||
250 | /* | |
6c68da26 VK |
251 | * Parse the manifest and build up our data structures representing |
252 | * what's in it. | |
676daaf4 | 253 | */ |
6c68da26 VK |
254 | if (!gb_manifest_parse(intf, manifest, size)) { |
255 | dev_err(&intf->dev, "%s: Failed to parse manifest\n", __func__); | |
256 | ret = -EINVAL; | |
257 | goto free_manifest; | |
676daaf4 VK |
258 | } |
259 | ||
260 | /* | |
261 | * XXX | |
262 | * We've successfully parsed the manifest. Now we need to | |
263 | * allocate CPort Id's for connecting to the CPorts found on | |
264 | * other modules. For each of these, establish a connection | |
265 | * between the local and remote CPorts (including | |
266 | * configuring the switch to allow them to communicate). | |
267 | */ | |
268 | ||
6c68da26 VK |
269 | free_manifest: |
270 | kfree(manifest); | |
271 | return ret; | |
676daaf4 | 272 | } |