Commit | Line | Data |
---|---|---|
e5354107 SO |
1 | /* |
2 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
3 | * Copyright (c) 2012-2013, Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/module.h> | |
17 | #include <linux/device.h> | |
18 | #include <linux/kernel.h> | |
3e833295 | 19 | #include <linux/sched.h> |
e5354107 SO |
20 | #include <linux/init.h> |
21 | #include <linux/errno.h> | |
22 | #include <linux/slab.h> | |
23 | #include <linux/mutex.h> | |
24 | #include <linux/interrupt.h> | |
25 | #include <linux/pci.h> | |
26 | #include <linux/mei_cl_bus.h> | |
27 | ||
28 | #include "mei_dev.h" | |
3e833295 SO |
29 | #include "hw-me.h" |
30 | #include "client.h" | |
e5354107 SO |
31 | |
32 | #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) | |
33 | #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) | |
34 | ||
35 | static int mei_cl_device_match(struct device *dev, struct device_driver *drv) | |
36 | { | |
37 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
38 | struct mei_cl_driver *driver = to_mei_cl_driver(drv); | |
39 | const struct mei_cl_device_id *id; | |
40 | ||
41 | if (!device) | |
42 | return 0; | |
43 | ||
44 | if (!driver || !driver->id_table) | |
45 | return 0; | |
46 | ||
47 | id = driver->id_table; | |
48 | ||
49 | while (id->name[0]) { | |
50 | if (!strcmp(dev_name(dev), id->name)) | |
51 | return 1; | |
52 | ||
53 | id++; | |
54 | } | |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
59 | static int mei_cl_device_probe(struct device *dev) | |
60 | { | |
61 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
62 | struct mei_cl_driver *driver; | |
63 | struct mei_cl_device_id id; | |
64 | ||
65 | if (!device) | |
66 | return 0; | |
67 | ||
68 | driver = to_mei_cl_driver(dev->driver); | |
69 | if (!driver || !driver->probe) | |
70 | return -ENODEV; | |
71 | ||
72 | dev_dbg(dev, "Device probe\n"); | |
73 | ||
74 | strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); | |
75 | ||
76 | return driver->probe(device, &id); | |
77 | } | |
78 | ||
79 | static int mei_cl_device_remove(struct device *dev) | |
80 | { | |
81 | struct mei_cl_device *device = to_mei_cl_device(dev); | |
82 | struct mei_cl_driver *driver; | |
83 | ||
84 | if (!device || !dev->driver) | |
85 | return 0; | |
86 | ||
3e833295 SO |
87 | if (device->event_cb) { |
88 | device->event_cb = NULL; | |
89 | cancel_work_sync(&device->event_work); | |
90 | } | |
91 | ||
e5354107 SO |
92 | driver = to_mei_cl_driver(dev->driver); |
93 | if (!driver->remove) { | |
94 | dev->driver = NULL; | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | return driver->remove(device); | |
100 | } | |
101 | ||
102 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | |
103 | char *buf) | |
104 | { | |
105 | int len; | |
106 | ||
107 | len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); | |
108 | ||
109 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | |
110 | } | |
111 | ||
112 | static struct device_attribute mei_cl_dev_attrs[] = { | |
113 | __ATTR_RO(modalias), | |
114 | __ATTR_NULL, | |
115 | }; | |
116 | ||
117 | static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) | |
118 | { | |
119 | if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) | |
120 | return -ENOMEM; | |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
125 | static struct bus_type mei_cl_bus_type = { | |
126 | .name = "mei", | |
127 | .dev_attrs = mei_cl_dev_attrs, | |
128 | .match = mei_cl_device_match, | |
129 | .probe = mei_cl_device_probe, | |
130 | .remove = mei_cl_device_remove, | |
131 | .uevent = mei_cl_uevent, | |
132 | }; | |
133 | ||
134 | static void mei_cl_dev_release(struct device *dev) | |
135 | { | |
136 | kfree(to_mei_cl_device(dev)); | |
137 | } | |
138 | ||
139 | static struct device_type mei_cl_device_type = { | |
140 | .release = mei_cl_dev_release, | |
141 | }; | |
142 | ||
a7b71bc0 SO |
143 | static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, |
144 | uuid_le uuid) | |
145 | { | |
146 | struct mei_cl *cl, *next; | |
147 | ||
148 | list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { | |
149 | if (!uuid_le_cmp(uuid, cl->device_uuid)) | |
150 | return cl; | |
151 | } | |
152 | ||
153 | return NULL; | |
154 | } | |
155 | struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, | |
e5354107 SO |
156 | uuid_le uuid, char *name) |
157 | { | |
158 | struct mei_cl_device *device; | |
a7b71bc0 | 159 | struct mei_cl *cl; |
e5354107 SO |
160 | int status; |
161 | ||
a7b71bc0 SO |
162 | cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); |
163 | if (cl == NULL) | |
164 | return NULL; | |
165 | ||
e5354107 SO |
166 | device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); |
167 | if (!device) | |
168 | return NULL; | |
169 | ||
a7b71bc0 SO |
170 | device->cl = cl; |
171 | ||
172 | device->dev.parent = &dev->pdev->dev; | |
e5354107 SO |
173 | device->dev.bus = &mei_cl_bus_type; |
174 | device->dev.type = &mei_cl_device_type; | |
175 | ||
176 | dev_set_name(&device->dev, "%s", name); | |
177 | ||
178 | status = device_register(&device->dev); | |
a7b71bc0 SO |
179 | if (status) { |
180 | dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); | |
181 | kfree(device); | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | cl->device = device; | |
e5354107 SO |
186 | |
187 | dev_dbg(&device->dev, "client %s registered\n", name); | |
188 | ||
189 | return device; | |
e5354107 SO |
190 | } |
191 | EXPORT_SYMBOL_GPL(mei_cl_add_device); | |
192 | ||
193 | void mei_cl_remove_device(struct mei_cl_device *device) | |
194 | { | |
195 | device_unregister(&device->dev); | |
196 | } | |
197 | EXPORT_SYMBOL_GPL(mei_cl_remove_device); | |
333e4ee0 SO |
198 | |
199 | int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) | |
200 | { | |
201 | int err; | |
202 | ||
203 | driver->driver.name = driver->name; | |
204 | driver->driver.owner = owner; | |
205 | driver->driver.bus = &mei_cl_bus_type; | |
206 | ||
207 | err = driver_register(&driver->driver); | |
208 | if (err) | |
209 | return err; | |
210 | ||
211 | pr_debug("mei: driver [%s] registered\n", driver->driver.name); | |
212 | ||
213 | return 0; | |
214 | } | |
215 | EXPORT_SYMBOL_GPL(__mei_cl_driver_register); | |
216 | ||
217 | void mei_cl_driver_unregister(struct mei_cl_driver *driver) | |
218 | { | |
219 | driver_unregister(&driver->driver); | |
220 | ||
221 | pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); | |
222 | } | |
223 | EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); | |
3e833295 SO |
224 | |
225 | int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) | |
226 | { | |
227 | struct mei_device *dev; | |
228 | struct mei_msg_hdr mei_hdr; | |
229 | struct mei_cl_cb *cb; | |
230 | int me_cl_id, err; | |
231 | ||
232 | if (WARN_ON(!cl || !cl->dev)) | |
233 | return -ENODEV; | |
234 | ||
235 | if (cl->state != MEI_FILE_CONNECTED) | |
236 | return -ENODEV; | |
237 | ||
238 | cb = mei_io_cb_init(cl, NULL); | |
239 | if (!cb) | |
240 | return -ENOMEM; | |
241 | ||
242 | err = mei_io_cb_alloc_req_buf(cb, length); | |
243 | if (err < 0) { | |
244 | mei_io_cb_free(cb); | |
245 | return err; | |
246 | } | |
247 | ||
248 | memcpy(cb->request_buffer.data, buf, length); | |
249 | cb->fop_type = MEI_FOP_WRITE; | |
250 | ||
251 | dev = cl->dev; | |
252 | ||
253 | mutex_lock(&dev->device_lock); | |
254 | ||
255 | /* Check if we have an ME client device */ | |
256 | me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id); | |
257 | if (me_cl_id == dev->me_clients_num) { | |
258 | err = -ENODEV; | |
259 | goto out_err; | |
260 | } | |
261 | ||
262 | if (length > dev->me_clients[me_cl_id].props.max_msg_length) { | |
263 | err = -EINVAL; | |
264 | goto out_err; | |
265 | } | |
266 | ||
267 | err = mei_cl_flow_ctrl_creds(cl); | |
268 | if (err < 0) | |
269 | goto out_err; | |
270 | ||
271 | /* Host buffer is not ready, we queue the request */ | |
272 | if (err == 0 || !dev->hbuf_is_ready) { | |
273 | cb->buf_idx = 0; | |
274 | mei_hdr.msg_complete = 0; | |
275 | cl->writing_state = MEI_WRITING; | |
276 | list_add_tail(&cb->list, &dev->write_list.list); | |
277 | ||
278 | mutex_unlock(&dev->device_lock); | |
279 | ||
280 | return length; | |
281 | } | |
282 | ||
283 | dev->hbuf_is_ready = false; | |
284 | ||
285 | /* Check for a maximum length */ | |
286 | if (length > mei_hbuf_max_len(dev)) { | |
287 | mei_hdr.length = mei_hbuf_max_len(dev); | |
288 | mei_hdr.msg_complete = 0; | |
289 | } else { | |
290 | mei_hdr.length = length; | |
291 | mei_hdr.msg_complete = 1; | |
292 | } | |
293 | ||
294 | mei_hdr.host_addr = cl->host_client_id; | |
295 | mei_hdr.me_addr = cl->me_client_id; | |
296 | mei_hdr.reserved = 0; | |
297 | ||
298 | if (mei_write_message(dev, &mei_hdr, buf)) { | |
299 | err = -EIO; | |
300 | goto out_err; | |
301 | } | |
302 | ||
303 | cl->writing_state = MEI_WRITING; | |
304 | cb->buf_idx = mei_hdr.length; | |
305 | ||
306 | if (!mei_hdr.msg_complete) { | |
307 | list_add_tail(&cb->list, &dev->write_list.list); | |
308 | } else { | |
309 | if (mei_cl_flow_ctrl_reduce(cl)) { | |
310 | err = -EIO; | |
311 | goto out_err; | |
312 | } | |
313 | ||
314 | list_add_tail(&cb->list, &dev->write_waiting_list.list); | |
315 | } | |
316 | ||
317 | mutex_unlock(&dev->device_lock); | |
318 | ||
319 | return mei_hdr.length; | |
320 | ||
321 | out_err: | |
322 | mutex_unlock(&dev->device_lock); | |
323 | mei_io_cb_free(cb); | |
324 | ||
325 | return err; | |
326 | } | |
327 | ||
328 | int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) | |
329 | { | |
330 | struct mei_device *dev; | |
331 | struct mei_cl_cb *cb; | |
332 | size_t r_length; | |
333 | int err; | |
334 | ||
335 | if (WARN_ON(!cl || !cl->dev)) | |
336 | return -ENODEV; | |
337 | ||
338 | dev = cl->dev; | |
339 | ||
340 | mutex_lock(&dev->device_lock); | |
341 | ||
342 | if (!cl->read_cb) { | |
343 | err = mei_cl_read_start(cl); | |
344 | if (err < 0) { | |
345 | mutex_unlock(&dev->device_lock); | |
346 | return err; | |
347 | } | |
348 | } | |
349 | ||
350 | if (cl->reading_state != MEI_READ_COMPLETE && | |
351 | !waitqueue_active(&cl->rx_wait)) { | |
352 | mutex_unlock(&dev->device_lock); | |
353 | ||
354 | if (wait_event_interruptible(cl->rx_wait, | |
355 | (MEI_READ_COMPLETE == cl->reading_state))) { | |
356 | if (signal_pending(current)) | |
357 | return -EINTR; | |
358 | return -ERESTARTSYS; | |
359 | } | |
360 | ||
361 | mutex_lock(&dev->device_lock); | |
362 | } | |
363 | ||
364 | cb = cl->read_cb; | |
365 | ||
366 | if (cl->reading_state != MEI_READ_COMPLETE) { | |
367 | r_length = 0; | |
368 | goto out; | |
369 | } | |
370 | ||
371 | r_length = min_t(size_t, length, cb->buf_idx); | |
372 | ||
373 | memcpy(buf, cb->response_buffer.data, r_length); | |
374 | ||
375 | mei_io_cb_free(cb); | |
376 | cl->reading_state = MEI_IDLE; | |
377 | cl->read_cb = NULL; | |
378 | ||
379 | out: | |
380 | mutex_unlock(&dev->device_lock); | |
381 | ||
382 | return r_length; | |
383 | } | |
384 | ||
385 | int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) | |
386 | { | |
a7b71bc0 | 387 | struct mei_cl *cl = device->cl; |
3e833295 | 388 | |
a7b71bc0 SO |
389 | if (cl == NULL) |
390 | return -ENODEV; | |
3e833295 SO |
391 | |
392 | if (device->ops && device->ops->send) | |
393 | return device->ops->send(device, buf, length); | |
394 | ||
395 | return __mei_cl_send(cl, buf, length); | |
396 | } | |
397 | EXPORT_SYMBOL_GPL(mei_cl_send); | |
398 | ||
399 | int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) | |
400 | { | |
a7b71bc0 | 401 | struct mei_cl *cl = device->cl; |
3e833295 | 402 | |
a7b71bc0 SO |
403 | if (cl == NULL) |
404 | return -ENODEV; | |
3e833295 SO |
405 | |
406 | if (device->ops && device->ops->recv) | |
407 | return device->ops->recv(device, buf, length); | |
408 | ||
409 | return __mei_cl_recv(cl, buf, length); | |
410 | } | |
411 | EXPORT_SYMBOL_GPL(mei_cl_recv); | |
412 | ||
413 | static void mei_bus_event_work(struct work_struct *work) | |
414 | { | |
415 | struct mei_cl_device *device; | |
416 | ||
417 | device = container_of(work, struct mei_cl_device, event_work); | |
418 | ||
419 | if (device->event_cb) | |
420 | device->event_cb(device, device->events, device->event_context); | |
421 | ||
422 | device->events = 0; | |
423 | ||
424 | /* Prepare for the next read */ | |
425 | mei_cl_read_start(device->cl); | |
426 | } | |
427 | ||
428 | int mei_cl_register_event_cb(struct mei_cl_device *device, | |
429 | mei_cl_event_cb_t event_cb, void *context) | |
430 | { | |
431 | if (device->event_cb) | |
432 | return -EALREADY; | |
433 | ||
434 | device->events = 0; | |
435 | device->event_cb = event_cb; | |
436 | device->event_context = context; | |
437 | INIT_WORK(&device->event_work, mei_bus_event_work); | |
438 | ||
439 | mei_cl_read_start(device->cl); | |
440 | ||
441 | return 0; | |
442 | } | |
443 | EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); |