linux/drivers/misc/mei/amthif.c
<<
>>
Prefs
   1/*
   2 *
   3 * Intel Management Engine Interface (Intel MEI) Linux driver
   4 * Copyright (c) 2003-2012, 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 */
  16
  17#include <linux/kernel.h>
  18#include <linux/fs.h>
  19#include <linux/errno.h>
  20#include <linux/types.h>
  21#include <linux/fcntl.h>
  22#include <linux/ioctl.h>
  23#include <linux/cdev.h>
  24#include <linux/list.h>
  25#include <linux/delay.h>
  26#include <linux/sched.h>
  27#include <linux/uuid.h>
  28#include <linux/jiffies.h>
  29#include <linux/uaccess.h>
  30#include <linux/slab.h>
  31
  32#include <linux/mei.h>
  33
  34#include "mei_dev.h"
  35#include "hbm.h"
  36#include "client.h"
  37
  38const uuid_le mei_amthif_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
  39                                         0xac, 0xa8, 0x46, 0xe0,
  40                                         0xff, 0x65, 0x81, 0x4c);
  41
  42/**
  43 * mei_amthif_reset_params - initializes mei device iamthif
  44 *
  45 * @dev: the device structure
  46 */
  47void mei_amthif_reset_params(struct mei_device *dev)
  48{
  49        /* reset iamthif parameters. */
  50        dev->iamthif_current_cb = NULL;
  51        dev->iamthif_canceled = false;
  52        dev->iamthif_state = MEI_IAMTHIF_IDLE;
  53        dev->iamthif_stall_timer = 0;
  54        dev->iamthif_open_count = 0;
  55}
  56
  57/**
  58 * mei_amthif_host_init - mei initialization amthif client.
  59 *
  60 * @dev: the device structure
  61 * @me_cl: me client
  62 *
  63 * Return: 0 on success, <0 on failure.
  64 */
  65int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
  66{
  67        struct mei_cl *cl = &dev->iamthif_cl;
  68        int ret;
  69
  70        if (mei_cl_is_connected(cl))
  71                return 0;
  72
  73        dev->iamthif_state = MEI_IAMTHIF_IDLE;
  74
  75        mei_cl_init(cl, dev);
  76
  77        ret = mei_cl_link(cl);
  78        if (ret < 0) {
  79                dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
  80                return ret;
  81        }
  82
  83        ret = mei_cl_connect(cl, me_cl, NULL);
  84
  85        return ret;
  86}
  87
  88/**
  89 * mei_amthif_read - read data from AMTHIF client
  90 *
  91 * @dev: the device structure
  92 * @file: pointer to file object
  93 * @ubuf: pointer to user data in user space
  94 * @length: data length to read
  95 * @offset: data read offset
  96 *
  97 * Locking: called under "dev->device_lock" lock
  98 *
  99 * Return:
 100 *  returned data length on success,
 101 *  zero if no data to read,
 102 *  negative on failure.
 103 */
 104int mei_amthif_read(struct mei_device *dev, struct file *file,
 105               char __user *ubuf, size_t length, loff_t *offset)
 106{
 107        struct mei_cl *cl = file->private_data;
 108        struct mei_cl_cb *cb;
 109        int rets;
 110        int wait_ret;
 111
 112        dev_dbg(dev->dev, "checking amthif data\n");
 113        cb = mei_cl_read_cb(cl, file);
 114
 115        /* Check for if we can block or not*/
 116        if (cb == NULL && file->f_flags & O_NONBLOCK)
 117                return -EAGAIN;
 118
 119
 120        dev_dbg(dev->dev, "waiting for amthif data\n");
 121        while (cb == NULL) {
 122                /* unlock the Mutex */
 123                mutex_unlock(&dev->device_lock);
 124
 125                wait_ret = wait_event_interruptible(cl->rx_wait,
 126                                        !list_empty(&cl->rd_completed) ||
 127                                        !mei_cl_is_connected(cl));
 128
 129                /* Locking again the Mutex */
 130                mutex_lock(&dev->device_lock);
 131
 132                if (wait_ret)
 133                        return -ERESTARTSYS;
 134
 135                if (!mei_cl_is_connected(cl)) {
 136                        rets = -EBUSY;
 137                        goto out;
 138                }
 139
 140                cb = mei_cl_read_cb(cl, file);
 141        }
 142
 143        if (cb->status) {
 144                rets = cb->status;
 145                dev_dbg(dev->dev, "read operation failed %d\n", rets);
 146                goto free;
 147        }
 148
 149        dev_dbg(dev->dev, "Got amthif data\n");
 150        /* if the whole message will fit remove it from the list */
 151        if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
 152                list_del_init(&cb->list);
 153        else if (cb->buf_idx <= *offset) {
 154                /* end of the message has been reached */
 155                list_del_init(&cb->list);
 156                rets = 0;
 157                goto free;
 158        }
 159                /* else means that not full buffer will be read and do not
 160                 * remove message from deletion list
 161                 */
 162
 163        dev_dbg(dev->dev, "amthif cb->buf.size - %zu cb->buf_idx - %zu\n",
 164                cb->buf.size, cb->buf_idx);
 165
 166        /* length is being truncated to PAGE_SIZE, however,
 167         * the buf_idx may point beyond */
 168        length = min_t(size_t, length, (cb->buf_idx - *offset));
 169
 170        if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
 171                dev_dbg(dev->dev, "failed to copy data to userland\n");
 172                rets = -EFAULT;
 173        } else {
 174                rets = length;
 175                if ((*offset + length) < cb->buf_idx) {
 176                        *offset += length;
 177                        goto out;
 178                }
 179        }
 180free:
 181        dev_dbg(dev->dev, "free amthif cb memory.\n");
 182        *offset = 0;
 183        mei_io_cb_free(cb);
 184out:
 185        return rets;
 186}
 187
 188/**
 189 * mei_amthif_read_start - queue message for sending read credential
 190 *
 191 * @cl: host client
 192 * @file: file pointer of message recipient
 193 *
 194 * Return: 0 on success, <0 on failure.
 195 */
 196static int mei_amthif_read_start(struct mei_cl *cl, const struct file *file)
 197{
 198        struct mei_device *dev = cl->dev;
 199        struct mei_cl_cb *cb;
 200        int rets;
 201
 202        cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
 203        if (!cb) {
 204                rets = -ENOMEM;
 205                goto err;
 206        }
 207
 208        rets = mei_io_cb_alloc_buf(cb, mei_cl_mtu(cl));
 209        if (rets)
 210                goto err;
 211
 212        list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
 213
 214        dev->iamthif_state = MEI_IAMTHIF_READING;
 215        dev->iamthif_fp = cb->fp;
 216        dev->iamthif_current_cb = cb;
 217
 218        return 0;
 219err:
 220        mei_io_cb_free(cb);
 221        return rets;
 222}
 223
 224/**
 225 * mei_amthif_send_cmd - send amthif command to the ME
 226 *
 227 * @cl: the host client
 228 * @cb: mei call back struct
 229 *
 230 * Return: 0 on success, <0 on failure.
 231 */
 232static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
 233{
 234        struct mei_device *dev;
 235        int ret;
 236
 237        if (!cl->dev || !cb)
 238                return -ENODEV;
 239
 240        dev = cl->dev;
 241
 242        dev->iamthif_state = MEI_IAMTHIF_WRITING;
 243        dev->iamthif_current_cb = cb;
 244        dev->iamthif_fp = cb->fp;
 245        dev->iamthif_canceled = false;
 246
 247        ret = mei_cl_write(cl, cb, false);
 248        if (ret < 0)
 249                return ret;
 250
 251        if (cb->completed)
 252                cb->status = mei_amthif_read_start(cl, cb->fp);
 253
 254        return 0;
 255}
 256
 257/**
 258 * mei_amthif_run_next_cmd - send next amt command from queue
 259 *
 260 * @dev: the device structure
 261 *
 262 * Return: 0 on success, <0 on failure.
 263 */
 264int mei_amthif_run_next_cmd(struct mei_device *dev)
 265{
 266        struct mei_cl *cl = &dev->iamthif_cl;
 267        struct mei_cl_cb *cb;
 268
 269        dev->iamthif_canceled = false;
 270        dev->iamthif_state = MEI_IAMTHIF_IDLE;
 271        dev->iamthif_fp = NULL;
 272
 273        dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
 274
 275        cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
 276                                        typeof(*cb), list);
 277        if (!cb)
 278                return 0;
 279
 280        list_del_init(&cb->list);
 281        return mei_amthif_send_cmd(cl, cb);
 282}
 283
 284/**
 285 * mei_amthif_write - write amthif data to amthif client
 286 *
 287 * @cl: host client
 288 * @cb: mei call back struct
 289 *
 290 * Return: 0 on success, <0 on failure.
 291 */
 292int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
 293{
 294
 295        struct mei_device *dev = cl->dev;
 296
 297        list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
 298
 299        /*
 300         * The previous request is still in processing, queue this one.
 301         */
 302        if (dev->iamthif_state > MEI_IAMTHIF_IDLE &&
 303            dev->iamthif_state < MEI_IAMTHIF_READ_COMPLETE)
 304                return 0;
 305
 306        return mei_amthif_run_next_cmd(dev);
 307}
 308
 309/**
 310 * mei_amthif_poll - the amthif poll function
 311 *
 312 * @dev: the device structure
 313 * @file: pointer to file structure
 314 * @wait: pointer to poll_table structure
 315 *
 316 * Return: poll mask
 317 *
 318 * Locking: called under "dev->device_lock" lock
 319 */
 320
 321unsigned int mei_amthif_poll(struct mei_device *dev,
 322                struct file *file, poll_table *wait)
 323{
 324        unsigned int mask = 0;
 325
 326        poll_wait(file, &dev->iamthif_cl.rx_wait, wait);
 327
 328        if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
 329            dev->iamthif_fp == file) {
 330
 331                mask |= POLLIN | POLLRDNORM;
 332                mei_amthif_run_next_cmd(dev);
 333        }
 334
 335        return mask;
 336}
 337
 338
 339
 340/**
 341 * mei_amthif_irq_write - write iamthif command in irq thread context.
 342 *
 343 * @cl: private data of the file object.
 344 * @cb: callback block.
 345 * @cmpl_list: complete list.
 346 *
 347 * Return: 0, OK; otherwise, error.
 348 */
 349int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
 350                         struct mei_cl_cb *cmpl_list)
 351{
 352        int ret;
 353
 354        ret = mei_cl_irq_write(cl, cb, cmpl_list);
 355        if (ret)
 356                return ret;
 357
 358        if (cb->completed)
 359                cb->status = mei_amthif_read_start(cl, cb->fp);
 360
 361        return 0;
 362}
 363
 364/**
 365 * mei_amthif_irq_read_msg - read routine after ISR to
 366 *                      handle the read amthif message
 367 *
 368 * @cl: mei client
 369 * @mei_hdr: header of amthif message
 370 * @cmpl_list: completed callbacks list
 371 *
 372 * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
 373 */
 374int mei_amthif_irq_read_msg(struct mei_cl *cl,
 375                            struct mei_msg_hdr *mei_hdr,
 376                            struct mei_cl_cb *cmpl_list)
 377{
 378        struct mei_device *dev;
 379        int ret;
 380
 381        dev = cl->dev;
 382
 383        if (dev->iamthif_state != MEI_IAMTHIF_READING) {
 384                mei_irq_discard_msg(dev, mei_hdr);
 385                return 0;
 386        }
 387
 388        ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
 389        if (ret)
 390                return ret;
 391
 392        if (!mei_hdr->msg_complete)
 393                return 0;
 394
 395        dev_dbg(dev->dev, "completed amthif read.\n ");
 396        dev->iamthif_current_cb = NULL;
 397        dev->iamthif_stall_timer = 0;
 398
 399        return 0;
 400}
 401
 402/**
 403 * mei_amthif_complete - complete amthif callback.
 404 *
 405 * @cl: host client
 406 * @cb: callback block.
 407 */
 408void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
 409{
 410        struct mei_device *dev = cl->dev;
 411
 412        if (cb->fop_type == MEI_FOP_WRITE) {
 413                if (!cb->status) {
 414                        dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
 415                        mei_io_cb_free(cb);
 416                        return;
 417                }
 418                /*
 419                 * in case of error enqueue the write cb to complete read list
 420                 * so it can be propagated to the reader
 421                 */
 422                list_add_tail(&cb->list, &cl->rd_completed);
 423                wake_up_interruptible(&cl->rx_wait);
 424                return;
 425        }
 426
 427        if (!dev->iamthif_canceled) {
 428                dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
 429                dev->iamthif_stall_timer = 0;
 430                list_add_tail(&cb->list, &cl->rd_completed);
 431                dev_dbg(dev->dev, "amthif read completed\n");
 432        } else {
 433                mei_amthif_run_next_cmd(dev);
 434        }
 435
 436        dev_dbg(dev->dev, "completing amthif call back.\n");
 437        wake_up_interruptible(&cl->rx_wait);
 438}
 439
 440/**
 441 * mei_clear_list - removes all callbacks associated with file
 442 *              from mei_cb_list
 443 *
 444 * @dev: device structure.
 445 * @file: file structure
 446 * @mei_cb_list: callbacks list
 447 *
 448 * mei_clear_list is called to clear resources associated with file
 449 * when application calls close function or Ctrl-C was pressed
 450 *
 451 * Return: true if callback removed from the list, false otherwise
 452 */
 453static bool mei_clear_list(struct mei_device *dev,
 454                const struct file *file, struct list_head *mei_cb_list)
 455{
 456        struct mei_cl *cl = &dev->iamthif_cl;
 457        struct mei_cl_cb *cb, *next;
 458        bool removed = false;
 459
 460        /* list all list member */
 461        list_for_each_entry_safe(cb, next, mei_cb_list, list) {
 462                /* check if list member associated with a file */
 463                if (file == cb->fp) {
 464                        /* check if cb equal to current iamthif cb */
 465                        if (dev->iamthif_current_cb == cb) {
 466                                dev->iamthif_current_cb = NULL;
 467                                /* send flow control to iamthif client */
 468                                mei_hbm_cl_flow_control_req(dev, cl);
 469                        }
 470                        /* free all allocated buffers */
 471                        mei_io_cb_free(cb);
 472                        removed = true;
 473                }
 474        }
 475        return removed;
 476}
 477
 478/**
 479 * mei_clear_lists - removes all callbacks associated with file
 480 *
 481 * @dev: device structure
 482 * @file: file structure
 483 *
 484 * mei_clear_lists is called to clear resources associated with file
 485 * when application calls close function or Ctrl-C was pressed
 486 *
 487 * Return: true if callback removed from the list, false otherwise
 488 */
 489static bool mei_clear_lists(struct mei_device *dev, const struct file *file)
 490{
 491        bool removed = false;
 492        struct mei_cl *cl = &dev->iamthif_cl;
 493
 494        /* remove callbacks associated with a file */
 495        mei_clear_list(dev, file, &dev->amthif_cmd_list.list);
 496        if (mei_clear_list(dev, file, &cl->rd_completed))
 497                removed = true;
 498
 499        mei_clear_list(dev, file, &dev->ctrl_rd_list.list);
 500
 501        if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list))
 502                removed = true;
 503
 504        if (mei_clear_list(dev, file, &dev->write_waiting_list.list))
 505                removed = true;
 506
 507        if (mei_clear_list(dev, file, &dev->write_list.list))
 508                removed = true;
 509
 510        /* check if iamthif_current_cb not NULL */
 511        if (dev->iamthif_current_cb && !removed) {
 512                /* check file and iamthif current cb association */
 513                if (dev->iamthif_current_cb->fp == file) {
 514                        /* remove cb */
 515                        mei_io_cb_free(dev->iamthif_current_cb);
 516                        dev->iamthif_current_cb = NULL;
 517                        removed = true;
 518                }
 519        }
 520        return removed;
 521}
 522
 523/**
 524* mei_amthif_release - the release function
 525*
 526*  @dev: device structure
 527*  @file: pointer to file structure
 528*
 529*  Return: 0 on success, <0 on error
 530*/
 531int mei_amthif_release(struct mei_device *dev, struct file *file)
 532{
 533        if (dev->iamthif_open_count > 0)
 534                dev->iamthif_open_count--;
 535
 536        if (dev->iamthif_fp == file &&
 537            dev->iamthif_state != MEI_IAMTHIF_IDLE) {
 538
 539                dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
 540                    dev->iamthif_state);
 541                dev->iamthif_canceled = true;
 542                if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
 543                        dev_dbg(dev->dev, "run next amthif iamthif cb\n");
 544                        mei_amthif_run_next_cmd(dev);
 545                }
 546        }
 547
 548        if (mei_clear_lists(dev, file))
 549                dev->iamthif_state = MEI_IAMTHIF_IDLE;
 550
 551        return 0;
 552}
 553