]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/interface.c
greybus: interface: fix potential use-after-free at remove
[karo-tx-linux.git] / drivers / staging / greybus / interface.c
1 /*
2  * Greybus interface code
3  *
4  * Copyright 2014 Google Inc.
5  * Copyright 2014 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #include "greybus.h"
11
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)                                  \
17 {                                                                       \
18         struct gb_interface *intf = to_gb_interface(dev);               \
19         return scnprintf(buf, PAGE_SIZE, "%"#type"\n", intf->field);    \
20 }                                                                       \
21 static DEVICE_ATTR_RO(field)
22
23 gb_interface_attr(device_id, d);
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);
29
30 static struct attribute *interface_attrs[] = {
31         &dev_attr_device_id.attr,
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 };
39 ATTRIBUTE_GROUPS(interface);
40
41
42 /* XXX This could be per-host device */
43 static DEFINE_SPINLOCK(gb_interfaces_lock);
44
45 // FIXME, odds are you don't want to call this function, rework the caller to
46 // not need it please.
47 struct gb_interface *gb_interface_find(struct gb_host_device *hd,
48                                        u8 interface_id)
49 {
50         struct gb_interface *intf;
51
52         list_for_each_entry(intf, &hd->interfaces, links)
53                 if (intf->interface_id == interface_id)
54                         return intf;
55
56         return NULL;
57 }
58
59 static void gb_interface_release(struct device *dev)
60 {
61         struct gb_interface *intf = to_gb_interface(dev);
62
63         kfree(intf->product_string);
64         kfree(intf->vendor_string);
65
66         kfree(intf);
67 }
68
69 struct device_type greybus_interface_type = {
70         .name =         "greybus_interface",
71         .release =      gb_interface_release,
72 };
73
74 /*
75  * Create kernel structures corresponding to a bundle and connection for
76  * managing control/svc CPort.
77  */
78 int gb_create_bundle_connection(struct gb_interface *intf, u8 class)
79 {
80         struct gb_bundle *bundle;
81         u32 ida_start, ida_end;
82         u8 bundle_id, protocol_id;
83         u16 cport_id;
84
85         if (class == GREYBUS_CLASS_CONTROL) {
86                 protocol_id = GREYBUS_PROTOCOL_CONTROL;
87                 bundle_id = GB_CONTROL_BUNDLE_ID;
88                 cport_id = GB_CONTROL_CPORT_ID;
89                 ida_start = 0;
90                 ida_end = intf->hd->num_cports - 1;
91         } else if (class == GREYBUS_CLASS_SVC) {
92                 protocol_id = GREYBUS_PROTOCOL_SVC;
93                 bundle_id = GB_SVC_BUNDLE_ID;
94                 cport_id = GB_SVC_CPORT_ID;
95                 ida_start = GB_SVC_CPORT_ID;
96                 ida_end = GB_SVC_CPORT_ID + 1;
97         } else {
98                 WARN_ON(1);
99                 return -EINVAL;
100         }
101
102         bundle = gb_bundle_create(intf, bundle_id, class);
103         if (!bundle)
104                 return -EINVAL;
105
106         if (!gb_connection_create_range(bundle->intf->hd, bundle, &bundle->dev,
107                                         cport_id, protocol_id, ida_start,
108                                         ida_end))
109                 return -EINVAL;
110
111         return 0;
112 }
113
114 /*
115  * A Greybus module represents a user-replaceable component on an Ara
116  * phone.  An interface is the physical connection on that module.  A
117  * module may have more than one interface.
118  *
119  * Create a gb_interface structure to represent a discovered interface.
120  * The position of interface within the Endo is encoded in "interface_id"
121  * argument.
122  *
123  * Returns a pointer to the new interfce or a null pointer if a
124  * failure occurs due to memory exhaustion.
125  */
126 struct gb_interface *gb_interface_create(struct gb_host_device *hd,
127                                          u8 interface_id)
128 {
129         struct gb_module *module;
130         struct gb_interface *intf;
131         int retval;
132
133         module = gb_module_find(hd, endo_get_module_id(hd->endo, interface_id));
134         if (!module)
135                 return NULL;
136
137         intf = kzalloc(sizeof(*intf), GFP_KERNEL);
138         if (!intf)
139                 goto put_module;
140
141         intf->hd = hd;          /* XXX refcount? */
142         intf->module = module;
143         intf->interface_id = interface_id;
144         INIT_LIST_HEAD(&intf->bundles);
145         INIT_LIST_HEAD(&intf->manifest_descs);
146
147         /* Invalid device id to start with */
148         intf->device_id = GB_DEVICE_ID_BAD;
149
150         intf->dev.parent = &module->dev;
151         intf->dev.bus = &greybus_bus_type;
152         intf->dev.type = &greybus_interface_type;
153         intf->dev.groups = interface_groups;
154         intf->dev.dma_mask = hd->parent->dma_mask;
155         device_initialize(&intf->dev);
156         dev_set_name(&intf->dev, "%s:%d", dev_name(&module->dev), interface_id);
157
158         retval = device_add(&intf->dev);
159         if (retval) {
160                 pr_err("failed to add interface device for id 0x%02hhx\n",
161                        interface_id);
162                 goto free_intf;
163         }
164
165         spin_lock_irq(&gb_interfaces_lock);
166         list_add(&intf->links, &hd->interfaces);
167         spin_unlock_irq(&gb_interfaces_lock);
168
169         return intf;
170
171 free_intf:
172         put_device(&intf->dev);
173 put_module:
174         put_device(&module->dev);
175         return NULL;
176 }
177
178 /*
179  * Tear down a previously set up module.
180  */
181 void gb_interface_remove(struct gb_interface *intf)
182 {
183         struct gb_module *module;
184         struct gb_bundle *bundle;
185         struct gb_bundle *next;
186
187         if (WARN_ON(!intf))
188                 return;
189
190         spin_lock_irq(&gb_interfaces_lock);
191         list_del(&intf->links);
192         spin_unlock_irq(&gb_interfaces_lock);
193
194         list_for_each_entry_safe(bundle, next, &intf->bundles, links)
195                 gb_bundle_destroy(bundle);
196
197         module = intf->module;
198         device_unregister(&intf->dev);
199         put_device(&module->dev);
200 }
201
202 void gb_interfaces_remove(struct gb_host_device *hd)
203 {
204         struct gb_interface *intf, *temp;
205
206         list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
207                 gb_interface_remove(intf);
208 }
209
210 /**
211  * gb_interface_init
212  *
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.
216  */
217 int gb_interface_init(struct gb_interface *intf, u8 device_id)
218 {
219         int ret, size;
220         void *manifest;
221
222         intf->device_id = device_id;
223
224         /* Establish control CPort connection */
225         ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_CONTROL);
226         if (ret) {
227                 dev_err(&intf->dev, "Failed to create control CPort connection (%d)\n", ret);
228                 return ret;
229         }
230
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;
251         }
252
253         /*
254          * Parse the manifest and build up our data structures representing
255          * what's in it.
256          */
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;
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
272 free_manifest:
273         kfree(manifest);
274         return ret;
275 }