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