]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/core.c
greybus: core: make greybus_kill_gbuf not return a value
[karo-tx-linux.git] / drivers / staging / greybus / core.c
1 /*
2  * Greybus "Core"
3  *
4  * Copyright 2014 Google Inc.
5  *
6  * Released under the GPLv2 only.
7  */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/types.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/device.h>
17
18 #include "greybus.h"
19
20 /* Allow greybus to be disabled at boot if needed */
21 static bool nogreybus;
22 #ifdef MODULE
23 module_param(nogreybus, bool, 0444);
24 #else
25 core_param(nogreybus, nogreybus, bool, 0444);
26 #endif
27 int greybus_disabled(void)
28 {
29         return nogreybus;
30 }
31 EXPORT_SYMBOL_GPL(greybus_disabled);
32
33 static int greybus_module_match(struct device *dev, struct device_driver *drv)
34 {
35         struct greybus_driver *driver = to_greybus_driver(dev->driver);
36         struct gb_module *gmod = to_gb_module(dev);
37         const struct greybus_module_id *id;
38
39         id = gb_module_match_id(gmod, driver->id_table);
40         if (id)
41                 return 1;
42         /* FIXME - Dyanmic ids? */
43         return 0;
44 }
45
46 static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
47 {
48         /* struct gb_module *gmod = to_gb_module(dev); */
49
50         /* FIXME - add some uevents here... */
51
52         /* FIXME - be sure to check the type to know how to handle modules and
53          * interfaces differently */
54
55         return 0;
56 }
57
58 struct bus_type greybus_bus_type = {
59         .name =         "greybus",
60         .match =        greybus_module_match,
61         .uevent =       greybus_uevent,
62 };
63
64 static int greybus_probe(struct device *dev)
65 {
66         struct greybus_driver *driver = to_greybus_driver(dev->driver);
67         struct gb_module *gmod = to_gb_module(dev);
68         const struct greybus_module_id *id;
69         int retval;
70
71         /* match id */
72         id = gb_module_match_id(gmod, driver->id_table);
73         if (!id)
74                 return -ENODEV;
75
76         retval = driver->probe(gmod, id);
77         if (retval)
78                 return retval;
79
80         return 0;
81 }
82
83 static int greybus_remove(struct device *dev)
84 {
85         struct greybus_driver *driver = to_greybus_driver(dev->driver);
86         struct gb_module *gmod = to_gb_module(dev);
87
88         driver->disconnect(gmod);
89         return 0;
90 }
91
92 int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
93                 const char *mod_name)
94 {
95         int retval;
96
97         if (greybus_disabled())
98                 return -ENODEV;
99
100         driver->driver.name = driver->name;
101         driver->driver.probe = greybus_probe;
102         driver->driver.remove = greybus_remove;
103         driver->driver.owner = owner;
104         driver->driver.mod_name = mod_name;
105
106         retval = driver_register(&driver->driver);
107         if (retval)
108                 return retval;
109
110         pr_info("registered new driver %s\n", driver->name);
111         return 0;
112 }
113 EXPORT_SYMBOL_GPL(greybus_register_driver);
114
115 void greybus_deregister(struct greybus_driver *driver)
116 {
117         driver_unregister(&driver->driver);
118 }
119 EXPORT_SYMBOL_GPL(greybus_deregister);
120
121
122 static const struct greybus_module_id fake_greybus_module_id = {
123         GREYBUS_DEVICE(0x42, 0x42)
124 };
125
126
127 /**
128  * gb_add_module
129  *
130  * Pass in a buffer that _should_ contain a Greybus module manifest
131  * and register a greybus device structure with the kernel core.
132  */
133 void gb_add_module(struct greybus_host_device *hd, u8 module_id,
134                    u8 *data, int size)
135 {
136         struct gb_module *gmod;
137
138         gmod = gb_module_create(hd, module_id);
139         if (!gmod) {
140                 dev_err(hd->parent, "failed to create module\n");
141                 return;
142         }
143
144         /*
145          * Parse the manifest and build up our data structures
146          * representing what's in it.
147          */
148         if (!gb_manifest_parse(gmod, data, size)) {
149                 dev_err(hd->parent, "manifest error\n");
150                 goto err_module;
151         }
152
153         /*
154          * XXX
155          * We've successfully parsed the manifest.  Now we need to
156          * allocate CPort Id's for connecting to the CPorts found on
157          * other modules.  For each of these, establish a connection
158          * between the local and remote CPorts (including
159          * configuring the switch to allow them to communicate).
160          */
161
162         return;
163
164 err_module:
165         gb_module_destroy(gmod);
166 }
167
168 void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
169 {
170         struct gb_module *gmod;
171         bool found = false;
172
173         list_for_each_entry(gmod, &hd->modules, links)
174                 if (gmod->module_id == module_id) {
175                         found = true;
176                         break;
177                 }
178
179         if (found)
180                 gb_module_destroy(gmod);
181         else
182                 dev_err(hd->parent, "module id %d remove error\n", module_id);
183 }
184
185 static void gb_remove_modules(struct greybus_host_device *hd)
186 {
187         struct gb_module *gmod, *temp;
188
189         list_for_each_entry_safe(gmod, temp, &hd->modules, links) {
190                 gb_module_destroy(gmod);
191         }
192 }
193
194 static DEFINE_MUTEX(hd_mutex);
195
196 static void free_hd(struct kref *kref)
197 {
198         struct greybus_host_device *hd;
199
200         hd = container_of(kref, struct greybus_host_device, kref);
201
202         kfree(hd);
203         mutex_unlock(&hd_mutex);
204 }
205
206 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
207                                               struct device *parent)
208 {
209         struct greybus_host_device *hd;
210
211         /*
212          * Validate that the driver implements all of the callbacks
213          * so that we don't have to every time we make them.
214          */
215         if ((!driver->alloc_gbuf_data) ||
216             (!driver->free_gbuf_data) ||
217             (!driver->submit_svc) ||
218             (!driver->submit_gbuf) ||
219             (!driver->kill_gbuf)) {
220                 pr_err("Must implement all greybus_host_driver callbacks!\n");
221                 return NULL;
222         }
223
224         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
225         if (!hd)
226                 return NULL;
227
228         kref_init(&hd->kref);
229         hd->parent = parent;
230         hd->driver = driver;
231         INIT_LIST_HEAD(&hd->modules);
232         hd->connections = RB_ROOT;
233         ida_init(&hd->cport_id_map);
234         spin_lock_init(&hd->cport_id_map_lock);
235
236         return hd;
237 }
238 EXPORT_SYMBOL_GPL(greybus_create_hd);
239
240 void greybus_remove_hd(struct greybus_host_device *hd)
241 {
242         /* Tear down all modules that happen to be associated with this host
243          * controller */
244         gb_remove_modules(hd);
245         kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
246 }
247 EXPORT_SYMBOL_GPL(greybus_remove_hd);
248
249 static int __init gb_init(void)
250 {
251         int retval;
252
253         BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
254
255         retval = gb_debugfs_init();
256         if (retval) {
257                 pr_err("debugfs failed\n");
258                 return retval;
259         }
260
261         retval = bus_register(&greybus_bus_type);
262         if (retval) {
263                 pr_err("bus_register failed\n");
264                 goto error_bus;
265         }
266
267         retval = gb_ap_init();
268         if (retval) {
269                 pr_err("gb_ap_init failed\n");
270                 goto error_ap;
271         }
272
273         retval = gb_gbuf_init();
274         if (retval) {
275                 pr_err("gb_gbuf_init failed\n");
276                 goto error_gbuf;
277         }
278
279         retval = gb_operation_init();
280         if (retval) {
281                 pr_err("gb_operation_init failed\n");
282                 goto error_operation;
283         }
284
285         retval = gb_tty_init();
286         if (retval) {
287                 pr_err("gb_tty_init failed\n");
288                 goto error_tty;
289         }
290
291         return 0;
292
293 error_tty:
294         gb_operation_exit();
295
296 error_operation:
297         gb_gbuf_exit();
298
299 error_gbuf:
300         gb_ap_exit();
301
302 error_ap:
303         bus_unregister(&greybus_bus_type);
304
305 error_bus:
306         gb_debugfs_cleanup();
307
308         return retval;
309 }
310
311 static void __exit gb_exit(void)
312 {
313         gb_tty_exit();
314         gb_operation_exit();
315         gb_gbuf_exit();
316         gb_ap_exit();
317         bus_unregister(&greybus_bus_type);
318         gb_debugfs_cleanup();
319 }
320
321 module_init(gb_init);
322 module_exit(gb_exit);
323
324 MODULE_LICENSE("GPL");
325 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");