.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | | - * |
---|
| 3 | + * Copyright (c) 2003-2020, Intel Corporation. All rights reserved. |
---|
3 | 4 | * Intel Management Engine Interface (Intel MEI) Linux driver |
---|
4 | | - * Copyright (c) 2003-2018, Intel Corporation. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms and conditions of the GNU General Public License, |
---|
8 | | - * version 2, as published by the Free Software Foundation. |
---|
9 | | - * |
---|
10 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
---|
11 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
12 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
13 | | - * more details. |
---|
14 | | - * |
---|
15 | 5 | */ |
---|
| 6 | + |
---|
16 | 7 | #include <linux/module.h> |
---|
17 | 8 | #include <linux/moduleparam.h> |
---|
18 | 9 | #include <linux/kernel.h> |
---|
.. | .. |
---|
36 | 27 | |
---|
37 | 28 | #include "mei_dev.h" |
---|
38 | 29 | #include "client.h" |
---|
| 30 | + |
---|
| 31 | +static struct class *mei_class; |
---|
| 32 | +static dev_t mei_devt; |
---|
| 33 | +#define MEI_MAX_DEVS MINORMASK |
---|
| 34 | +static DEFINE_MUTEX(mei_minor_lock); |
---|
| 35 | +static DEFINE_IDR(mei_idr); |
---|
39 | 36 | |
---|
40 | 37 | /** |
---|
41 | 38 | * mei_open - the open function |
---|
.. | .. |
---|
84 | 81 | } |
---|
85 | 82 | |
---|
86 | 83 | /** |
---|
| 84 | + * mei_cl_vtag_remove_by_fp - remove vtag that corresponds to fp from list |
---|
| 85 | + * |
---|
| 86 | + * @cl: host client |
---|
| 87 | + * @fp: pointer to file structure |
---|
| 88 | + * |
---|
| 89 | + */ |
---|
| 90 | +static void mei_cl_vtag_remove_by_fp(const struct mei_cl *cl, |
---|
| 91 | + const struct file *fp) |
---|
| 92 | +{ |
---|
| 93 | + struct mei_cl_vtag *vtag_l, *next; |
---|
| 94 | + |
---|
| 95 | + list_for_each_entry_safe(vtag_l, next, &cl->vtag_map, list) { |
---|
| 96 | + if (vtag_l->fp == fp) { |
---|
| 97 | + list_del(&vtag_l->list); |
---|
| 98 | + kfree(vtag_l); |
---|
| 99 | + return; |
---|
| 100 | + } |
---|
| 101 | + } |
---|
| 102 | +} |
---|
| 103 | + |
---|
| 104 | +/** |
---|
87 | 105 | * mei_release - the release function |
---|
88 | 106 | * |
---|
89 | 107 | * @inode: pointer to inode structure |
---|
.. | .. |
---|
104 | 122 | |
---|
105 | 123 | mutex_lock(&dev->device_lock); |
---|
106 | 124 | |
---|
107 | | - rets = mei_cl_disconnect(cl); |
---|
| 125 | + mei_cl_vtag_remove_by_fp(cl, file); |
---|
108 | 126 | |
---|
109 | | - mei_cl_flush_queues(cl, file); |
---|
| 127 | + if (!list_empty(&cl->vtag_map)) { |
---|
| 128 | + cl_dbg(dev, cl, "not the last vtag\n"); |
---|
| 129 | + mei_cl_flush_queues(cl, file); |
---|
| 130 | + rets = 0; |
---|
| 131 | + goto out; |
---|
| 132 | + } |
---|
| 133 | + |
---|
| 134 | + rets = mei_cl_disconnect(cl); |
---|
| 135 | + /* |
---|
| 136 | + * Check again: This is necessary since disconnect releases the lock |
---|
| 137 | + * and another client can connect in the meantime. |
---|
| 138 | + */ |
---|
| 139 | + if (!list_empty(&cl->vtag_map)) { |
---|
| 140 | + cl_dbg(dev, cl, "not the last vtag after disconnect\n"); |
---|
| 141 | + mei_cl_flush_queues(cl, file); |
---|
| 142 | + goto out; |
---|
| 143 | + } |
---|
| 144 | + |
---|
| 145 | + mei_cl_flush_queues(cl, NULL); |
---|
110 | 146 | cl_dbg(dev, cl, "removing\n"); |
---|
111 | 147 | |
---|
112 | 148 | mei_cl_unlink(cl); |
---|
113 | | - |
---|
114 | | - file->private_data = NULL; |
---|
115 | | - |
---|
116 | 149 | kfree(cl); |
---|
| 150 | + |
---|
| 151 | +out: |
---|
| 152 | + file->private_data = NULL; |
---|
117 | 153 | |
---|
118 | 154 | mutex_unlock(&dev->device_lock); |
---|
119 | 155 | return rets; |
---|
.. | .. |
---|
181 | 217 | |
---|
182 | 218 | mutex_unlock(&dev->device_lock); |
---|
183 | 219 | if (wait_event_interruptible(cl->rx_wait, |
---|
184 | | - !list_empty(&cl->rd_completed) || |
---|
| 220 | + mei_cl_read_cb(cl, file) || |
---|
185 | 221 | !mei_cl_is_connected(cl))) { |
---|
186 | 222 | if (signal_pending(current)) |
---|
187 | 223 | return -EINTR; |
---|
.. | .. |
---|
232 | 268 | goto out; |
---|
233 | 269 | |
---|
234 | 270 | free: |
---|
235 | | - mei_io_cb_free(cb); |
---|
| 271 | + mei_cl_del_rd_completed(cl, cb); |
---|
236 | 272 | *offset = 0; |
---|
237 | 273 | |
---|
238 | 274 | out: |
---|
.. | .. |
---|
240 | 276 | mutex_unlock(&dev->device_lock); |
---|
241 | 277 | return rets; |
---|
242 | 278 | } |
---|
| 279 | + |
---|
| 280 | +/** |
---|
| 281 | + * mei_cl_vtag_by_fp - obtain the vtag by file pointer |
---|
| 282 | + * |
---|
| 283 | + * @cl: host client |
---|
| 284 | + * @fp: pointer to file structure |
---|
| 285 | + * |
---|
| 286 | + * Return: vtag value on success, otherwise 0 |
---|
| 287 | + */ |
---|
| 288 | +static u8 mei_cl_vtag_by_fp(const struct mei_cl *cl, const struct file *fp) |
---|
| 289 | +{ |
---|
| 290 | + struct mei_cl_vtag *cl_vtag; |
---|
| 291 | + |
---|
| 292 | + if (!fp) |
---|
| 293 | + return 0; |
---|
| 294 | + |
---|
| 295 | + list_for_each_entry(cl_vtag, &cl->vtag_map, list) |
---|
| 296 | + if (cl_vtag->fp == fp) |
---|
| 297 | + return cl_vtag->vtag; |
---|
| 298 | + return 0; |
---|
| 299 | +} |
---|
| 300 | + |
---|
243 | 301 | /** |
---|
244 | 302 | * mei_write - the write function. |
---|
245 | 303 | * |
---|
.. | .. |
---|
317 | 375 | rets = -ENOMEM; |
---|
318 | 376 | goto out; |
---|
319 | 377 | } |
---|
| 378 | + cb->vtag = mei_cl_vtag_by_fp(cl, file); |
---|
320 | 379 | |
---|
321 | 380 | rets = copy_from_user(cb->buf.data, ubuf, length); |
---|
322 | 381 | if (rets) { |
---|
.. | .. |
---|
336 | 395 | * mei_ioctl_connect_client - the connect to fw client IOCTL function |
---|
337 | 396 | * |
---|
338 | 397 | * @file: private data of the file object |
---|
339 | | - * @data: IOCTL connect data, input and output parameters |
---|
| 398 | + * @in_client_uuid: requested UUID for connection |
---|
| 399 | + * @client: IOCTL connect data, output parameters |
---|
340 | 400 | * |
---|
341 | 401 | * Locking: called under "dev->device_lock" lock |
---|
342 | 402 | * |
---|
343 | 403 | * Return: 0 on success, <0 on failure. |
---|
344 | 404 | */ |
---|
345 | 405 | static int mei_ioctl_connect_client(struct file *file, |
---|
346 | | - struct mei_connect_client_data *data) |
---|
| 406 | + const uuid_le *in_client_uuid, |
---|
| 407 | + struct mei_client *client) |
---|
347 | 408 | { |
---|
348 | 409 | struct mei_device *dev; |
---|
349 | | - struct mei_client *client; |
---|
350 | 410 | struct mei_me_client *me_cl; |
---|
351 | 411 | struct mei_cl *cl; |
---|
352 | 412 | int rets; |
---|
.. | .. |
---|
354 | 414 | cl = file->private_data; |
---|
355 | 415 | dev = cl->dev; |
---|
356 | 416 | |
---|
357 | | - if (dev->dev_state != MEI_DEV_ENABLED) |
---|
358 | | - return -ENODEV; |
---|
359 | | - |
---|
360 | 417 | if (cl->state != MEI_FILE_INITIALIZING && |
---|
361 | 418 | cl->state != MEI_FILE_DISCONNECTED) |
---|
362 | 419 | return -EBUSY; |
---|
363 | 420 | |
---|
364 | 421 | /* find ME client we're trying to connect to */ |
---|
365 | | - me_cl = mei_me_cl_by_uuid(dev, &data->in_client_uuid); |
---|
| 422 | + me_cl = mei_me_cl_by_uuid(dev, in_client_uuid); |
---|
366 | 423 | if (!me_cl) { |
---|
367 | 424 | dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", |
---|
368 | | - &data->in_client_uuid); |
---|
| 425 | + in_client_uuid); |
---|
369 | 426 | rets = -ENOTTY; |
---|
370 | 427 | goto end; |
---|
371 | 428 | } |
---|
.. | .. |
---|
375 | 432 | !dev->allow_fixed_address : !dev->hbm_f_fa_supported; |
---|
376 | 433 | if (forbidden) { |
---|
377 | 434 | dev_dbg(dev->dev, "Connection forbidden to FW Client UUID = %pUl\n", |
---|
378 | | - &data->in_client_uuid); |
---|
| 435 | + in_client_uuid); |
---|
379 | 436 | rets = -ENOTTY; |
---|
380 | 437 | goto end; |
---|
381 | 438 | } |
---|
.. | .. |
---|
389 | 446 | me_cl->props.max_msg_length); |
---|
390 | 447 | |
---|
391 | 448 | /* prepare the output buffer */ |
---|
392 | | - client = &data->out_client_properties; |
---|
393 | 449 | client->max_msg_length = me_cl->props.max_msg_length; |
---|
394 | 450 | client->protocol_version = me_cl->props.protocol_version; |
---|
395 | 451 | dev_dbg(dev->dev, "Can connect?\n"); |
---|
.. | .. |
---|
399 | 455 | end: |
---|
400 | 456 | mei_me_cl_put(me_cl); |
---|
401 | 457 | return rets; |
---|
| 458 | +} |
---|
| 459 | + |
---|
| 460 | +/** |
---|
| 461 | + * mei_vt_support_check - check if client support vtags |
---|
| 462 | + * |
---|
| 463 | + * Locking: called under "dev->device_lock" lock |
---|
| 464 | + * |
---|
| 465 | + * @dev: mei_device |
---|
| 466 | + * @uuid: client UUID |
---|
| 467 | + * |
---|
| 468 | + * Return: |
---|
| 469 | + * 0 - supported |
---|
| 470 | + * -ENOTTY - no such client |
---|
| 471 | + * -EOPNOTSUPP - vtags are not supported by client |
---|
| 472 | + */ |
---|
| 473 | +static int mei_vt_support_check(struct mei_device *dev, const uuid_le *uuid) |
---|
| 474 | +{ |
---|
| 475 | + struct mei_me_client *me_cl; |
---|
| 476 | + int ret; |
---|
| 477 | + |
---|
| 478 | + if (!dev->hbm_f_vt_supported) |
---|
| 479 | + return -EOPNOTSUPP; |
---|
| 480 | + |
---|
| 481 | + me_cl = mei_me_cl_by_uuid(dev, uuid); |
---|
| 482 | + if (!me_cl) { |
---|
| 483 | + dev_dbg(dev->dev, "Cannot connect to FW Client UUID = %pUl\n", |
---|
| 484 | + uuid); |
---|
| 485 | + return -ENOTTY; |
---|
| 486 | + } |
---|
| 487 | + ret = me_cl->props.vt_supported ? 0 : -EOPNOTSUPP; |
---|
| 488 | + mei_me_cl_put(me_cl); |
---|
| 489 | + |
---|
| 490 | + return ret; |
---|
| 491 | +} |
---|
| 492 | + |
---|
| 493 | +/** |
---|
| 494 | + * mei_ioctl_connect_vtag - connect to fw client with vtag IOCTL function |
---|
| 495 | + * |
---|
| 496 | + * @file: private data of the file object |
---|
| 497 | + * @in_client_uuid: requested UUID for connection |
---|
| 498 | + * @client: IOCTL connect data, output parameters |
---|
| 499 | + * @vtag: vm tag |
---|
| 500 | + * |
---|
| 501 | + * Locking: called under "dev->device_lock" lock |
---|
| 502 | + * |
---|
| 503 | + * Return: 0 on success, <0 on failure. |
---|
| 504 | + */ |
---|
| 505 | +static int mei_ioctl_connect_vtag(struct file *file, |
---|
| 506 | + const uuid_le *in_client_uuid, |
---|
| 507 | + struct mei_client *client, |
---|
| 508 | + u8 vtag) |
---|
| 509 | +{ |
---|
| 510 | + struct mei_device *dev; |
---|
| 511 | + struct mei_cl *cl; |
---|
| 512 | + struct mei_cl *pos; |
---|
| 513 | + struct mei_cl_vtag *cl_vtag; |
---|
| 514 | + |
---|
| 515 | + cl = file->private_data; |
---|
| 516 | + dev = cl->dev; |
---|
| 517 | + |
---|
| 518 | + dev_dbg(dev->dev, "FW Client %pUl vtag %d\n", in_client_uuid, vtag); |
---|
| 519 | + |
---|
| 520 | + switch (cl->state) { |
---|
| 521 | + case MEI_FILE_DISCONNECTED: |
---|
| 522 | + if (mei_cl_vtag_by_fp(cl, file) != vtag) { |
---|
| 523 | + dev_err(dev->dev, "reconnect with different vtag\n"); |
---|
| 524 | + return -EINVAL; |
---|
| 525 | + } |
---|
| 526 | + break; |
---|
| 527 | + case MEI_FILE_INITIALIZING: |
---|
| 528 | + /* malicious connect from another thread may push vtag */ |
---|
| 529 | + if (!IS_ERR(mei_cl_fp_by_vtag(cl, vtag))) { |
---|
| 530 | + dev_err(dev->dev, "vtag already filled\n"); |
---|
| 531 | + return -EINVAL; |
---|
| 532 | + } |
---|
| 533 | + |
---|
| 534 | + list_for_each_entry(pos, &dev->file_list, link) { |
---|
| 535 | + if (pos == cl) |
---|
| 536 | + continue; |
---|
| 537 | + if (!pos->me_cl) |
---|
| 538 | + continue; |
---|
| 539 | + |
---|
| 540 | + /* only search for same UUID */ |
---|
| 541 | + if (uuid_le_cmp(*mei_cl_uuid(pos), *in_client_uuid)) |
---|
| 542 | + continue; |
---|
| 543 | + |
---|
| 544 | + /* if tag already exist try another fp */ |
---|
| 545 | + if (!IS_ERR(mei_cl_fp_by_vtag(pos, vtag))) |
---|
| 546 | + continue; |
---|
| 547 | + |
---|
| 548 | + /* replace cl with acquired one */ |
---|
| 549 | + dev_dbg(dev->dev, "replacing with existing cl\n"); |
---|
| 550 | + mei_cl_unlink(cl); |
---|
| 551 | + kfree(cl); |
---|
| 552 | + file->private_data = pos; |
---|
| 553 | + cl = pos; |
---|
| 554 | + break; |
---|
| 555 | + } |
---|
| 556 | + |
---|
| 557 | + cl_vtag = mei_cl_vtag_alloc(file, vtag); |
---|
| 558 | + if (IS_ERR(cl_vtag)) |
---|
| 559 | + return -ENOMEM; |
---|
| 560 | + |
---|
| 561 | + list_add_tail(&cl_vtag->list, &cl->vtag_map); |
---|
| 562 | + break; |
---|
| 563 | + default: |
---|
| 564 | + return -EBUSY; |
---|
| 565 | + } |
---|
| 566 | + |
---|
| 567 | + while (cl->state != MEI_FILE_INITIALIZING && |
---|
| 568 | + cl->state != MEI_FILE_DISCONNECTED && |
---|
| 569 | + cl->state != MEI_FILE_CONNECTED) { |
---|
| 570 | + mutex_unlock(&dev->device_lock); |
---|
| 571 | + wait_event_timeout(cl->wait, |
---|
| 572 | + (cl->state == MEI_FILE_CONNECTED || |
---|
| 573 | + cl->state == MEI_FILE_DISCONNECTED || |
---|
| 574 | + cl->state == MEI_FILE_DISCONNECT_REQUIRED || |
---|
| 575 | + cl->state == MEI_FILE_DISCONNECT_REPLY), |
---|
| 576 | + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); |
---|
| 577 | + mutex_lock(&dev->device_lock); |
---|
| 578 | + } |
---|
| 579 | + |
---|
| 580 | + if (!mei_cl_is_connected(cl)) |
---|
| 581 | + return mei_ioctl_connect_client(file, in_client_uuid, client); |
---|
| 582 | + |
---|
| 583 | + client->max_msg_length = cl->me_cl->props.max_msg_length; |
---|
| 584 | + client->protocol_version = cl->me_cl->props.protocol_version; |
---|
| 585 | + |
---|
| 586 | + return 0; |
---|
402 | 587 | } |
---|
403 | 588 | |
---|
404 | 589 | /** |
---|
.. | .. |
---|
457 | 642 | { |
---|
458 | 643 | struct mei_device *dev; |
---|
459 | 644 | struct mei_cl *cl = file->private_data; |
---|
460 | | - struct mei_connect_client_data connect_data; |
---|
| 645 | + struct mei_connect_client_data conn; |
---|
| 646 | + struct mei_connect_client_data_vtag conn_vtag; |
---|
| 647 | + const uuid_le *cl_uuid; |
---|
| 648 | + struct mei_client *props; |
---|
| 649 | + u8 vtag; |
---|
461 | 650 | u32 notify_get, notify_req; |
---|
462 | 651 | int rets; |
---|
463 | 652 | |
---|
.. | .. |
---|
478 | 667 | switch (cmd) { |
---|
479 | 668 | case IOCTL_MEI_CONNECT_CLIENT: |
---|
480 | 669 | dev_dbg(dev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n"); |
---|
481 | | - if (copy_from_user(&connect_data, (char __user *)data, |
---|
482 | | - sizeof(struct mei_connect_client_data))) { |
---|
| 670 | + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { |
---|
| 671 | + dev_dbg(dev->dev, "failed to copy data from userland\n"); |
---|
| 672 | + rets = -EFAULT; |
---|
| 673 | + goto out; |
---|
| 674 | + } |
---|
| 675 | + cl_uuid = &conn.in_client_uuid; |
---|
| 676 | + props = &conn.out_client_properties; |
---|
| 677 | + vtag = 0; |
---|
| 678 | + |
---|
| 679 | + rets = mei_vt_support_check(dev, cl_uuid); |
---|
| 680 | + if (rets == -ENOTTY) |
---|
| 681 | + goto out; |
---|
| 682 | + if (!rets) |
---|
| 683 | + rets = mei_ioctl_connect_vtag(file, cl_uuid, props, |
---|
| 684 | + vtag); |
---|
| 685 | + else |
---|
| 686 | + rets = mei_ioctl_connect_client(file, cl_uuid, props); |
---|
| 687 | + if (rets) |
---|
| 688 | + goto out; |
---|
| 689 | + |
---|
| 690 | + /* if all is ok, copying the data back to user. */ |
---|
| 691 | + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { |
---|
| 692 | + dev_dbg(dev->dev, "failed to copy data to userland\n"); |
---|
| 693 | + rets = -EFAULT; |
---|
| 694 | + goto out; |
---|
| 695 | + } |
---|
| 696 | + |
---|
| 697 | + break; |
---|
| 698 | + |
---|
| 699 | + case IOCTL_MEI_CONNECT_CLIENT_VTAG: |
---|
| 700 | + dev_dbg(dev->dev, "IOCTL_MEI_CONNECT_CLIENT_VTAG\n"); |
---|
| 701 | + if (copy_from_user(&conn_vtag, (char __user *)data, |
---|
| 702 | + sizeof(conn_vtag))) { |
---|
483 | 703 | dev_dbg(dev->dev, "failed to copy data from userland\n"); |
---|
484 | 704 | rets = -EFAULT; |
---|
485 | 705 | goto out; |
---|
486 | 706 | } |
---|
487 | 707 | |
---|
488 | | - rets = mei_ioctl_connect_client(file, &connect_data); |
---|
| 708 | + cl_uuid = &conn_vtag.connect.in_client_uuid; |
---|
| 709 | + props = &conn_vtag.out_client_properties; |
---|
| 710 | + vtag = conn_vtag.connect.vtag; |
---|
| 711 | + |
---|
| 712 | + rets = mei_vt_support_check(dev, cl_uuid); |
---|
| 713 | + if (rets == -EOPNOTSUPP) |
---|
| 714 | + dev_dbg(dev->dev, "FW Client %pUl does not support vtags\n", |
---|
| 715 | + cl_uuid); |
---|
| 716 | + if (rets) |
---|
| 717 | + goto out; |
---|
| 718 | + |
---|
| 719 | + if (!vtag) { |
---|
| 720 | + dev_dbg(dev->dev, "vtag can't be zero\n"); |
---|
| 721 | + rets = -EINVAL; |
---|
| 722 | + goto out; |
---|
| 723 | + } |
---|
| 724 | + |
---|
| 725 | + rets = mei_ioctl_connect_vtag(file, cl_uuid, props, vtag); |
---|
489 | 726 | if (rets) |
---|
490 | 727 | goto out; |
---|
491 | 728 | |
---|
492 | 729 | /* if all is ok, copying the data back to user. */ |
---|
493 | | - if (copy_to_user((char __user *)data, &connect_data, |
---|
494 | | - sizeof(struct mei_connect_client_data))) { |
---|
| 730 | + if (copy_to_user((char __user *)data, &conn_vtag, |
---|
| 731 | + sizeof(conn_vtag))) { |
---|
495 | 732 | dev_dbg(dev->dev, "failed to copy data to userland\n"); |
---|
496 | 733 | rets = -EFAULT; |
---|
497 | 734 | goto out; |
---|
.. | .. |
---|
536 | 773 | } |
---|
537 | 774 | |
---|
538 | 775 | /** |
---|
539 | | - * mei_compat_ioctl - the compat IOCTL function |
---|
540 | | - * |
---|
541 | | - * @file: pointer to file structure |
---|
542 | | - * @cmd: ioctl command |
---|
543 | | - * @data: pointer to mei message structure |
---|
544 | | - * |
---|
545 | | - * Return: 0 on success , <0 on error |
---|
546 | | - */ |
---|
547 | | -#ifdef CONFIG_COMPAT |
---|
548 | | -static long mei_compat_ioctl(struct file *file, |
---|
549 | | - unsigned int cmd, unsigned long data) |
---|
550 | | -{ |
---|
551 | | - return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data)); |
---|
552 | | -} |
---|
553 | | -#endif |
---|
554 | | - |
---|
555 | | - |
---|
556 | | -/** |
---|
557 | 776 | * mei_poll - the poll function |
---|
558 | 777 | * |
---|
559 | 778 | * @file: pointer to file structure |
---|
.. | .. |
---|
593 | 812 | if (req_events & (EPOLLIN | EPOLLRDNORM)) { |
---|
594 | 813 | poll_wait(file, &cl->rx_wait, wait); |
---|
595 | 814 | |
---|
596 | | - if (!list_empty(&cl->rd_completed)) |
---|
| 815 | + if (mei_cl_read_cb(cl, file)) |
---|
597 | 816 | mask |= EPOLLIN | EPOLLRDNORM; |
---|
598 | 817 | else |
---|
599 | 818 | mei_cl_read_start(cl, mei_cl_mtu(cl), file); |
---|
.. | .. |
---|
702 | 921 | |
---|
703 | 922 | return fasync_helper(fd, file, band, &cl->ev_async); |
---|
704 | 923 | } |
---|
| 924 | + |
---|
| 925 | +/** |
---|
| 926 | + * trc_show - mei device trc attribute show method |
---|
| 927 | + * |
---|
| 928 | + * @device: device pointer |
---|
| 929 | + * @attr: attribute pointer |
---|
| 930 | + * @buf: char out buffer |
---|
| 931 | + * |
---|
| 932 | + * Return: number of the bytes printed into buf or error |
---|
| 933 | + */ |
---|
| 934 | +static ssize_t trc_show(struct device *device, |
---|
| 935 | + struct device_attribute *attr, char *buf) |
---|
| 936 | +{ |
---|
| 937 | + struct mei_device *dev = dev_get_drvdata(device); |
---|
| 938 | + u32 trc; |
---|
| 939 | + int ret; |
---|
| 940 | + |
---|
| 941 | + ret = mei_trc_status(dev, &trc); |
---|
| 942 | + if (ret) |
---|
| 943 | + return ret; |
---|
| 944 | + return sprintf(buf, "%08X\n", trc); |
---|
| 945 | +} |
---|
| 946 | +static DEVICE_ATTR_RO(trc); |
---|
705 | 947 | |
---|
706 | 948 | /** |
---|
707 | 949 | * fw_status_show - mei device fw_status attribute show method |
---|
.. | .. |
---|
838 | 1080 | } |
---|
839 | 1081 | static DEVICE_ATTR_RO(fw_ver); |
---|
840 | 1082 | |
---|
| 1083 | +/** |
---|
| 1084 | + * dev_state_show - display device state |
---|
| 1085 | + * |
---|
| 1086 | + * @device: device pointer |
---|
| 1087 | + * @attr: attribute pointer |
---|
| 1088 | + * @buf: char out buffer |
---|
| 1089 | + * |
---|
| 1090 | + * Return: number of the bytes printed into buf or error |
---|
| 1091 | + */ |
---|
| 1092 | +static ssize_t dev_state_show(struct device *device, |
---|
| 1093 | + struct device_attribute *attr, char *buf) |
---|
| 1094 | +{ |
---|
| 1095 | + struct mei_device *dev = dev_get_drvdata(device); |
---|
| 1096 | + enum mei_dev_state dev_state; |
---|
| 1097 | + |
---|
| 1098 | + mutex_lock(&dev->device_lock); |
---|
| 1099 | + dev_state = dev->dev_state; |
---|
| 1100 | + mutex_unlock(&dev->device_lock); |
---|
| 1101 | + |
---|
| 1102 | + return sprintf(buf, "%s", mei_dev_state_str(dev_state)); |
---|
| 1103 | +} |
---|
| 1104 | +static DEVICE_ATTR_RO(dev_state); |
---|
| 1105 | + |
---|
| 1106 | +/** |
---|
| 1107 | + * dev_set_devstate: set to new device state and notify sysfs file. |
---|
| 1108 | + * |
---|
| 1109 | + * @dev: mei_device |
---|
| 1110 | + * @state: new device state |
---|
| 1111 | + */ |
---|
| 1112 | +void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) |
---|
| 1113 | +{ |
---|
| 1114 | + struct device *clsdev; |
---|
| 1115 | + |
---|
| 1116 | + if (dev->dev_state == state) |
---|
| 1117 | + return; |
---|
| 1118 | + |
---|
| 1119 | + dev->dev_state = state; |
---|
| 1120 | + |
---|
| 1121 | + clsdev = class_find_device_by_devt(mei_class, dev->cdev.dev); |
---|
| 1122 | + if (clsdev) { |
---|
| 1123 | + sysfs_notify(&clsdev->kobj, NULL, "dev_state"); |
---|
| 1124 | + put_device(clsdev); |
---|
| 1125 | + } |
---|
| 1126 | +} |
---|
| 1127 | + |
---|
| 1128 | +/** |
---|
| 1129 | + * kind_show - display device kind |
---|
| 1130 | + * |
---|
| 1131 | + * @device: device pointer |
---|
| 1132 | + * @attr: attribute pointer |
---|
| 1133 | + * @buf: char out buffer |
---|
| 1134 | + * |
---|
| 1135 | + * Return: number of the bytes printed into buf or error |
---|
| 1136 | + */ |
---|
| 1137 | +static ssize_t kind_show(struct device *device, |
---|
| 1138 | + struct device_attribute *attr, char *buf) |
---|
| 1139 | +{ |
---|
| 1140 | + struct mei_device *dev = dev_get_drvdata(device); |
---|
| 1141 | + ssize_t ret; |
---|
| 1142 | + |
---|
| 1143 | + if (dev->kind) |
---|
| 1144 | + ret = sprintf(buf, "%s\n", dev->kind); |
---|
| 1145 | + else |
---|
| 1146 | + ret = sprintf(buf, "%s\n", "mei"); |
---|
| 1147 | + |
---|
| 1148 | + return ret; |
---|
| 1149 | +} |
---|
| 1150 | +static DEVICE_ATTR_RO(kind); |
---|
| 1151 | + |
---|
841 | 1152 | static struct attribute *mei_attrs[] = { |
---|
842 | 1153 | &dev_attr_fw_status.attr, |
---|
843 | 1154 | &dev_attr_hbm_ver.attr, |
---|
844 | 1155 | &dev_attr_hbm_ver_drv.attr, |
---|
845 | 1156 | &dev_attr_tx_queue_limit.attr, |
---|
846 | 1157 | &dev_attr_fw_ver.attr, |
---|
| 1158 | + &dev_attr_dev_state.attr, |
---|
| 1159 | + &dev_attr_trc.attr, |
---|
| 1160 | + &dev_attr_kind.attr, |
---|
847 | 1161 | NULL |
---|
848 | 1162 | }; |
---|
849 | 1163 | ATTRIBUTE_GROUPS(mei); |
---|
.. | .. |
---|
855 | 1169 | .owner = THIS_MODULE, |
---|
856 | 1170 | .read = mei_read, |
---|
857 | 1171 | .unlocked_ioctl = mei_ioctl, |
---|
858 | | -#ifdef CONFIG_COMPAT |
---|
859 | | - .compat_ioctl = mei_compat_ioctl, |
---|
860 | | -#endif |
---|
| 1172 | + .compat_ioctl = compat_ptr_ioctl, |
---|
861 | 1173 | .open = mei_open, |
---|
862 | 1174 | .release = mei_release, |
---|
863 | 1175 | .write = mei_write, |
---|
.. | .. |
---|
866 | 1178 | .fasync = mei_fasync, |
---|
867 | 1179 | .llseek = no_llseek |
---|
868 | 1180 | }; |
---|
869 | | - |
---|
870 | | -static struct class *mei_class; |
---|
871 | | -static dev_t mei_devt; |
---|
872 | | -#define MEI_MAX_DEVS MINORMASK |
---|
873 | | -static DEFINE_MUTEX(mei_minor_lock); |
---|
874 | | -static DEFINE_IDR(mei_idr); |
---|
875 | 1181 | |
---|
876 | 1182 | /** |
---|
877 | 1183 | * mei_minor_get - obtain next free device minor number |
---|
.. | .. |
---|
940 | 1246 | goto err_dev_create; |
---|
941 | 1247 | } |
---|
942 | 1248 | |
---|
943 | | - ret = mei_dbgfs_register(dev, dev_name(clsdev)); |
---|
944 | | - if (ret) { |
---|
945 | | - dev_err(clsdev, "cannot register debugfs ret = %d\n", ret); |
---|
946 | | - goto err_dev_dbgfs; |
---|
947 | | - } |
---|
| 1249 | + mei_dbgfs_register(dev, dev_name(clsdev)); |
---|
948 | 1250 | |
---|
949 | 1251 | return 0; |
---|
950 | 1252 | |
---|
951 | | -err_dev_dbgfs: |
---|
952 | | - device_destroy(mei_class, devno); |
---|
953 | 1253 | err_dev_create: |
---|
954 | 1254 | cdev_del(&dev->cdev); |
---|
955 | 1255 | err_dev_add: |
---|