greybus: module: add gb_module_find()
[cascardo/linux.git] / drivers / staging / greybus / module.c
1 /*
2  * Greybus modules
3  *
4  * Copyright 2014 Google Inc.
5  *
6  * Released under the GPLv2 only.
7  */
8
9 #include "greybus.h"
10
11 /* XXX This could be per-host device */
12 static DEFINE_SPINLOCK(gb_modules_lock);
13
14 static int gb_module_match_one_id(struct gb_module *gmod,
15                                 const struct greybus_module_id *id)
16 {
17         if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
18             (id->vendor != gmod->vendor))
19                 return 0;
20
21         if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
22             (id->product != gmod->product))
23                 return 0;
24
25         if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
26             (id->unique_id != gmod->unique_id))
27                 return 0;
28
29         return 1;
30 }
31
32 const struct greybus_module_id *gb_module_match_id(struct gb_module *gmod,
33                                 const struct greybus_module_id *id)
34 {
35         if (id == NULL)
36                 return NULL;
37
38         for (; id->vendor || id->product || id->unique_id ||
39                         id->driver_info; id++) {
40                 if (gb_module_match_one_id(gmod, id))
41                         return id;
42         }
43
44         return NULL;
45 }
46
47 static void gb_module_interfaces_exit(struct gb_module *gmod)
48 {
49         struct gb_interface *interface;
50         struct gb_interface *next;
51
52         list_for_each_entry_safe(interface, next, &gmod->interfaces, links)
53                 gb_interface_destroy(interface);
54 }
55
56 /*
57  * A Greybus module represents a user-replacable component on an Ara
58  * phone.
59  *
60  * Create a gb_module structure to represent a discovered module.
61  * The position within the Endo is encoded in the "module_id" argument.
62  * Returns a pointer to the new module or a null pointer if a
63  * failure occurs due to memory exhaustion.
64  */
65 struct gb_module *gb_module_create(struct greybus_host_device *hd, u8 module_id)
66 {
67         struct gb_module *gmod;
68
69         gmod = kzalloc(sizeof(*gmod), GFP_KERNEL);
70         if (!gmod)
71                 return NULL;
72
73         gmod->hd = hd;          /* XXX refcount? */
74         gmod->module_id = module_id;    /* XXX check for dups */
75         INIT_LIST_HEAD(&gmod->interfaces);
76
77         spin_lock_irq(&gb_modules_lock);
78         list_add_tail(&gmod->links, &hd->modules);
79         spin_unlock_irq(&gb_modules_lock);
80
81         return gmod;
82 }
83
84 /*
85  * Tear down a previously set up module.
86  */
87 void gb_module_destroy(struct gb_module *gmod)
88 {
89         if (WARN_ON(!gmod))
90                 return;
91
92         spin_lock_irq(&gb_modules_lock);
93         list_del(&gmod->links);
94         spin_unlock_irq(&gb_modules_lock);
95
96         gb_module_interfaces_exit(gmod);
97         /* XXX Do something with gmod->gb_tty */
98
99         put_device(&gmod->dev);
100         /* kfree(gmod->dev->name); */
101
102         kfree(gmod->product_string);
103         kfree(gmod->vendor_string);
104         /* kref_put(module->hd); */
105
106
107         kfree(gmod);
108 }
109
110 struct gb_module *gb_module_find(struct greybus_host_device *hd, u8 module_id)
111 {
112         struct gb_module *module;
113
114         list_for_each_entry(module, &hd->modules, links)
115                 if (module->module_id == module_id)
116                         return module;
117
118         return NULL;
119 }
120
121 void gb_module_interfaces_init(struct gb_module *gmod)
122 {
123         struct gb_interface *interface;
124         int ret = 0;
125
126         list_for_each_entry(interface, &gmod->interfaces, links) {
127                 ret = gb_interface_connections_init(interface);
128                 if (ret)
129                         dev_err(gmod->hd->parent,
130                                 "module interface init error %d\n", ret);
131         }
132 }