greybus: legacy: fix a null pointer dereference
[cascardo/linux.git] / drivers / staging / greybus / legacy.c
1 /*
2  * Greybus legacy-protocol driver
3  *
4  * Copyright 2015 Google Inc.
5  * Copyright 2015 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #include "greybus.h"
11 #include "legacy.h"
12 #include "protocol.h"
13
14
15 struct legacy_connection {
16         struct gb_connection *connection;
17         bool initialized;
18         struct gb_protocol *protocol;
19 };
20
21 struct legacy_data {
22         size_t num_cports;
23         struct legacy_connection *connections;
24 };
25
26
27 static int legacy_connection_get_version(struct gb_connection *connection)
28 {
29         int ret;
30
31         ret = gb_protocol_get_version(connection);
32         if (ret) {
33                 dev_err(&connection->hd->dev,
34                         "%s: failed to get protocol version: %d\n",
35                         connection->name, ret);
36                 return ret;
37         }
38
39         return 0;
40 }
41
42 static int legacy_request_handler(struct gb_operation *operation)
43 {
44         struct gb_protocol *protocol = operation->connection->protocol;
45
46         return protocol->request_recv(operation->type, operation);
47 }
48
49 static int legacy_connection_init(struct legacy_connection *lc)
50 {
51         struct gb_connection *connection = lc->connection;
52         int ret;
53
54         dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__,
55                         connection->name);
56
57         ret = gb_connection_enable(connection);
58         if (ret)
59                 return ret;
60
61         ret = legacy_connection_get_version(connection);
62         if (ret)
63                 goto err_disable;
64
65         ret = connection->protocol->connection_init(connection);
66         if (ret)
67                 goto err_disable;
68
69         lc->initialized = true;
70
71         return 0;
72
73 err_disable:
74         gb_connection_disable(connection);
75
76         return ret;
77 }
78
79 static void legacy_connection_exit(struct legacy_connection *lc)
80 {
81         struct gb_connection *connection = lc->connection;
82
83         if (!lc->initialized)
84                 return;
85
86         gb_connection_disable(connection);
87
88         connection->protocol->connection_exit(connection);
89
90         lc->initialized = false;
91 }
92
93 static int legacy_connection_create(struct legacy_connection *lc,
94                                         struct gb_bundle *bundle,
95                                         struct greybus_descriptor_cport *desc)
96 {
97         struct gb_connection *connection;
98         struct gb_protocol *protocol;
99         gb_request_handler_t handler;
100         u8 major, minor;
101         int ret;
102
103         /*
104          * The legacy protocols have always been looked up using a hard-coded
105          * version of 0.1, despite (or perhaps rather, due to) the fact that
106          * module version negotiation could not take place until after the
107          * protocol was bound.
108          */
109         major = 0;
110         minor = 1;
111
112         protocol = gb_protocol_get(desc->protocol_id, major, minor);
113         if (!protocol) {
114                 dev_err(&bundle->dev,
115                                 "protocol 0x%02x version %u.%u not found\n",
116                                 desc->protocol_id, major, minor);
117                 return -EPROTONOSUPPORT;
118         }
119
120         if (protocol->request_recv)
121                 handler = legacy_request_handler;
122         else
123                 handler = NULL;
124
125         connection = gb_connection_create(bundle, le16_to_cpu(desc->id),
126                                                 handler);
127         if (IS_ERR(connection)) {
128                 ret = PTR_ERR(connection);
129                 goto err_protocol_put;
130         }
131
132         /*
133          * NOTE: We need to keep a pointer to the protocol in the actual
134          *       connection structure for now.
135          */
136         connection->protocol = protocol;
137
138         lc->connection = connection;
139         lc->protocol = protocol;
140
141         return 0;
142
143 err_protocol_put:
144         gb_protocol_put(protocol);
145
146         return ret;
147 }
148
149 static void legacy_connection_destroy(struct legacy_connection *lc)
150 {
151         if (!lc->connection)
152                 return;
153
154         lc->connection->protocol = NULL;
155
156         gb_connection_destroy(lc->connection);
157
158         gb_protocol_put(lc->protocol);
159 }
160
161 static int legacy_probe(struct gb_bundle *bundle,
162                         const struct greybus_bundle_id *id)
163 {
164         struct greybus_descriptor_cport *cport_desc;
165         struct legacy_data *data;
166         struct legacy_connection *lc;
167         int i;
168         int ret;
169
170         dev_dbg(&bundle->dev,
171                         "%s - bundle class = 0x%02x, num_cports = %zu\n",
172                         __func__, bundle->class, bundle->num_cports);
173
174         data = kzalloc(sizeof(*data), GFP_KERNEL);
175         if (!data)
176                 return -ENOMEM;
177
178         data->num_cports = bundle->num_cports;
179         data->connections = kcalloc(data->num_cports,
180                                                 sizeof(*data->connections),
181                                                 GFP_KERNEL);
182         if (!data->connections) {
183                 ret = -ENOMEM;
184                 goto err_free_data;
185         }
186
187         for (i = 0; i < data->num_cports; ++i) {
188                 cport_desc = &bundle->cport_desc[i];
189                 lc = &data->connections[i];
190
191                 ret = legacy_connection_create(lc, bundle, cport_desc);
192                 if (ret)
193                         goto err_connections_destroy;
194         }
195
196         greybus_set_drvdata(bundle, data);
197
198         for (i = 0; i < data->num_cports; ++i) {
199                 lc = &data->connections[i];
200
201                 ret = legacy_connection_init(lc);
202                 if (ret)
203                         goto err_connections_disable;
204         }
205
206         return 0;
207
208 err_connections_disable:
209         for (--i; i >= 0; --i)
210                 legacy_connection_exit(&data->connections[i]);
211 err_connections_destroy:
212         for (i = 0; i < data->num_cports; ++i)
213                 legacy_connection_destroy(&data->connections[i]);
214         kfree(data->connections);
215 err_free_data:
216         kfree(data);
217
218         return ret;
219 }
220
221 static void legacy_disconnect(struct gb_bundle *bundle)
222 {
223         struct legacy_data *data = greybus_get_drvdata(bundle);
224         int i;
225
226         dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__,
227                         bundle->class);
228
229         for (i = 0; i < data->num_cports; ++i) {
230                 legacy_connection_exit(&data->connections[i]);
231                 legacy_connection_destroy(&data->connections[i]);
232         }
233
234         kfree(data->connections);
235         kfree(data);
236 }
237
238 static const struct greybus_bundle_id legacy_id_table[] = {
239         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) },
240         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) },
241         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) },
242         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) },
243         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) },
244         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
245         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) },
246         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) },
247         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_DISPLAY) },
248         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
249         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) },
250         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) },
251         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) },
252         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) },
253         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_DATA) },
254         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SVC) },
255         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) },
256         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) },
257         { }
258 };
259 MODULE_DEVICE_TABLE(greybus, legacy_id_table);
260
261 static struct greybus_driver legacy_driver = {
262         .name           = "legacy",
263         .probe          = legacy_probe,
264         .disconnect     = legacy_disconnect,
265         .id_table       = legacy_id_table,
266 };
267
268 int gb_legacy_init(void)
269 {
270         return greybus_register(&legacy_driver);
271 }
272
273 void gb_legacy_exit(void)
274 {
275         greybus_deregister(&legacy_driver);
276 }