Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum raw_mode.c   Sprache: C

 

// SPDX-License-Identifier: GPL-2.0
/*
 * System Control and Management Interface (SCMI) Raw mode support
 *
 * Copyright (C) 2022 ARM Ltd.
 */

/**
 * DOC: Theory of operation
 *
 * When enabled the SCMI Raw mode support exposes a userspace API which allows
 * to send and receive SCMI commands, replies and notifications from a user
 * application through injection and snooping of bare SCMI messages in binary
 * little-endian format.
 *
 * Such injected SCMI transactions will then be routed through the SCMI core
 * stack towards the SCMI backend server using whatever SCMI transport is
 * currently configured on the system under test.
 *
 * It is meant to help in running any sort of SCMI backend server testing, no
 * matter where the server is placed, as long as it is normally reachable via
 * the transport configured on the system.
 *
 * It is activated by a Kernel configuration option since it is NOT meant to
 * be used in production but only during development and in CI deployments.
 *
 * In order to avoid possible interferences between the SCMI Raw transactions
 * originated from a test-suite and the normal operations of the SCMI drivers,
 * when Raw mode is enabled, by default, all the regular SCMI drivers are
 * inhibited, unless CONFIG_ARM_SCMI_RAW_MODE_SUPPORT_COEX is enabled: in this
 * latter case the regular SCMI stack drivers will be loaded as usual and it is
 * up to the user of this interface to take care of manually inhibiting the
 * regular SCMI drivers in order to avoid interferences during the test runs.
 *
 * The exposed API is as follows.
 *
 * All SCMI Raw entries are rooted under a common top /raw debugfs top directory
 * which in turn is rooted under the corresponding underlying  SCMI instance.
 *
 * /sys/kernel/debug/scmi/
 * `-- 0
 *     |-- atomic_threshold_us
 *     |-- instance_name
 *     |-- raw
 *     |   |-- channels
 *     |   |   |-- 0x10
 *     |   |   |   |-- message
 *     |   |   |   `-- message_async
 *     |   |   `-- 0x13
 *     |   |       |-- message
 *     |   |       `-- message_async
 *     |   |-- errors
 *     |   |-- message
 *     |   |-- message_async
 *     |   |-- notification
 *     |   `-- reset
 *     `-- transport
 *         |-- is_atomic
 *         |-- max_msg_size
 *         |-- max_rx_timeout_ms
 *         |-- rx_max_msg
 *         |-- tx_max_msg
 *         `-- type
 *
 * where:
 *
 *  - errors: used to read back timed-out and unexpected replies
 *  - message*: used to send sync/async commands and read back immediate and
 *  delayed reponses (if any)
 *  - notification: used to read any notification being emitted by the system
 *      (if previously enabled by the user app)
 *  - reset: used to flush the queues of messages (of any kind) still pending
 *      to be read; this is useful at test-suite start/stop to get
 *      rid of any unread messages from the previous run.
 *
 * with the per-channel entries rooted at /channels being present only on a
 * system where multiple transport channels have been configured.
 *
 * Such per-channel entries can be used to explicitly choose a specific channel
 * for SCMI bare message injection, in contrast with the general entries above
 * where, instead, the selection of the proper channel to use is automatically
 * performed based the protocol embedded in the injected message and on how the
 * transport is configured on the system.
 *
 * Note that other common general entries are available under transport/ to let
 * the user applications properly make up their expectations in terms of
 * timeouts and message characteristics.
 *
 * Each write to the message* entries causes one command request to be built
 * and sent while the replies or delayed response are read back from those same
 * entries one message at time (receiving an EOF at each message boundary).
 *
 * The user application running the test is in charge of handling timeouts
 * on replies and properly choosing SCMI sequence numbers for the outgoing
 * requests (using the same sequence number is supported but discouraged).
 *
 * Injection of multiple in-flight requests is supported as long as the user
 * application uses properly distinct sequence numbers for concurrent requests
 * and takes care to properly manage all the related issues about concurrency
 * and command/reply pairing. Keep in mind that, anyway, the real level of
 * parallelism attainable in such scenario is dependent on the characteristics
 * of the underlying transport being used.
 *
 * Since the SCMI core regular stack is partially used to deliver and collect
 * the messages, late replies arrived after timeouts and any other sort of
 * unexpected message can be identified by the SCMI core as usual and they will
 * be reported as messages under "errors" for later analysis.
 */


#include <linux/bitmap.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/xarray.h>

#include "common.h"

#include "raw_mode.h"

#include <trace/events/scmi.h>

#define SCMI_XFER_RAW_MAX_RETRIES 10

/**
 * struct scmi_raw_queue  - Generic Raw queue descriptor
 *
 * @free_bufs: A freelists listhead used to keep unused raw buffers
 * @free_bufs_lock: Spinlock used to protect access to @free_bufs
 * @msg_q: A listhead to a queue of snooped messages waiting to be read out
 * @msg_q_lock: Spinlock used to protect access to @msg_q
 * @wq: A waitqueue used to wait and poll on related @msg_q
 */

struct scmi_raw_queue {
 struct list_head free_bufs;
 /* Protect free_bufs[] lists */
 spinlock_t free_bufs_lock;
 struct list_head msg_q;
 /* Protect msg_q[] lists */
 spinlock_t msg_q_lock;
 wait_queue_head_t wq;
};

/**
 * struct scmi_raw_mode_info  - Structure holding SCMI Raw instance data
 *
 * @id: Sequential Raw instance ID.
 * @handle: Pointer to SCMI entity handle to use
 * @desc: Pointer to the transport descriptor to use
 * @tx_max_msg: Maximum number of concurrent TX in-flight messages
 * @q: An array of Raw queue descriptors
 * @chans_q: An XArray mapping optional additional per-channel queues
 * @free_waiters: Head of freelist for unused waiters
 * @free_mtx: A mutex to protect the waiters freelist
 * @active_waiters: Head of list for currently active and used waiters
 * @active_mtx: A mutex to protect the active waiters list
 * @waiters_work: A work descriptor to be used with the workqueue machinery
 * @wait_wq: A workqueue reference to the created workqueue
 * @dentry: Top debugfs root dentry for SCMI Raw
 * @gid: A group ID used for devres accounting
 *
 * Note that this descriptor is passed back to the core after SCMI Raw is
 * initialized as an opaque handle to use by subsequent SCMI Raw call hooks.
 *
 */

struct scmi_raw_mode_info {
 unsigned int id;
 const struct scmi_handle *handle;
 const struct scmi_desc *desc;
 int tx_max_msg;
 struct scmi_raw_queue *q[SCMI_RAW_MAX_QUEUE];
 struct xarray chans_q;
 struct list_head free_waiters;
 /* Protect free_waiters list */
 struct mutex free_mtx;
 struct list_head active_waiters;
 /* Protect active_waiters list */
 struct mutex active_mtx;
 struct work_struct waiters_work;
 struct workqueue_struct *wait_wq;
 struct dentry *dentry;
 void *gid;
};

/**
 * struct scmi_xfer_raw_waiter  - Structure to describe an xfer to be waited for
 *
 * @start_jiffies: The timestamp in jiffies of when this structure was queued.
 * @cinfo: A reference to the channel to use for this transaction
 * @xfer: A reference to the xfer to be waited for
 * @async_response: A completion to be, optionally, used for async waits: it
 *      will be setup by @scmi_do_xfer_raw_start, if needed, to be
 *      pointed at by xfer->async_done.
 * @node: A list node.
 */

struct scmi_xfer_raw_waiter {
 unsigned long start_jiffies;
 struct scmi_chan_info *cinfo;
 struct scmi_xfer *xfer;
 struct completion async_response;
 struct list_head node;
};

/**
 * struct scmi_raw_buffer  - Structure to hold a full SCMI message
 *
 * @max_len: The maximum allowed message size (header included) that can be
 *      stored into @msg
 * @msg: A message buffer used to collect a full message grabbed from an xfer.
 * @node: A list node.
 */

struct scmi_raw_buffer {
 size_t max_len;
 struct scmi_msg msg;
 struct list_head node;
};

/**
 * struct scmi_dbg_raw_data  - Structure holding data needed by the debugfs
 * layer
 *
 * @chan_id: The preferred channel to use: if zero the channel is automatically
 *      selected based on protocol.
 * @raw: A reference to the Raw instance.
 * @tx: A message buffer used to collect TX message on write.
 * @tx_size: The effective size of the TX message.
 * @tx_req_size: The final expected size of the complete TX message.
 * @rx: A message buffer to collect RX message on read.
 * @rx_size: The effective size of the RX message.
 */

struct scmi_dbg_raw_data {
 u8 chan_id;
 struct scmi_raw_mode_info *raw;
 struct scmi_msg tx;
 size_t tx_size;
 size_t tx_req_size;
 struct scmi_msg rx;
 size_t rx_size;
};

static struct scmi_raw_queue *
scmi_raw_queue_select(struct scmi_raw_mode_info *raw, unsigned int idx,
        unsigned int chan_id)
{
 if (!chan_id)
  return raw->q[idx];

 return xa_load(&raw->chans_q, chan_id);
}

static struct scmi_raw_buffer *scmi_raw_buffer_get(struct scmi_raw_queue *q)
{
 unsigned long flags;
 struct scmi_raw_buffer *rb = NULL;
 struct list_head *head = &q->free_bufs;

 spin_lock_irqsave(&q->free_bufs_lock, flags);
 if (!list_empty(head)) {
  rb = list_first_entry(head, struct scmi_raw_buffer, node);
  list_del_init(&rb->node);
 }
 spin_unlock_irqrestore(&q->free_bufs_lock, flags);

 return rb;
}

static void scmi_raw_buffer_put(struct scmi_raw_queue *q,
    struct scmi_raw_buffer *rb)
{
 unsigned long flags;

 /* Reset to full buffer length */
 rb->msg.len = rb->max_len;

 spin_lock_irqsave(&q->free_bufs_lock, flags);
 list_add_tail(&rb->node, &q->free_bufs);
 spin_unlock_irqrestore(&q->free_bufs_lock, flags);
}

static void scmi_raw_buffer_enqueue(struct scmi_raw_queue *q,
        struct scmi_raw_buffer *rb)
{
 unsigned long flags;

 spin_lock_irqsave(&q->msg_q_lock, flags);
 list_add_tail(&rb->node, &q->msg_q);
 spin_unlock_irqrestore(&q->msg_q_lock, flags);

 wake_up_interruptible(&q->wq);
}

static struct scmi_raw_buffer*
scmi_raw_buffer_dequeue_unlocked(struct scmi_raw_queue *q)
{
 struct scmi_raw_buffer *rb = NULL;

 if (!list_empty(&q->msg_q)) {
  rb = list_first_entry(&q->msg_q, struct scmi_raw_buffer, node);
  list_del_init(&rb->node);
 }

 return rb;
}

static struct scmi_raw_buffer *scmi_raw_buffer_dequeue(struct scmi_raw_queue *q)
{
 unsigned long flags;
 struct scmi_raw_buffer *rb;

 spin_lock_irqsave(&q->msg_q_lock, flags);
 rb = scmi_raw_buffer_dequeue_unlocked(q);
 spin_unlock_irqrestore(&q->msg_q_lock, flags);

 return rb;
}

static void scmi_raw_buffer_queue_flush(struct scmi_raw_queue *q)
{
 struct scmi_raw_buffer *rb;

 do {
  rb = scmi_raw_buffer_dequeue(q);
  if (rb)
   scmi_raw_buffer_put(q, rb);
 } while (rb);
}

static struct scmi_xfer_raw_waiter *
scmi_xfer_raw_waiter_get(struct scmi_raw_mode_info *raw, struct scmi_xfer *xfer,
    struct scmi_chan_info *cinfo, bool async)
{
 struct scmi_xfer_raw_waiter *rw = NULL;

 mutex_lock(&raw->free_mtx);
 if (!list_empty(&raw->free_waiters)) {
  rw = list_first_entry(&raw->free_waiters,
          struct scmi_xfer_raw_waiter, node);
  list_del_init(&rw->node);

  if (async) {
   reinit_completion(&rw->async_response);
   xfer->async_done = &rw->async_response;
  }

  rw->cinfo = cinfo;
  rw->xfer = xfer;
 }
 mutex_unlock(&raw->free_mtx);

 return rw;
}

static void scmi_xfer_raw_waiter_put(struct scmi_raw_mode_info *raw,
         struct scmi_xfer_raw_waiter *rw)
{
 if (rw->xfer) {
  rw->xfer->async_done = NULL;
  rw->xfer = NULL;
 }

 mutex_lock(&raw->free_mtx);
 list_add_tail(&rw->node, &raw->free_waiters);
 mutex_unlock(&raw->free_mtx);
}

static void scmi_xfer_raw_waiter_enqueue(struct scmi_raw_mode_info *raw,
      struct scmi_xfer_raw_waiter *rw)
{
 /* A timestamp for the deferred worker to know how much this has aged */
 rw->start_jiffies = jiffies;

 trace_scmi_xfer_response_wait(rw->xfer->transfer_id, rw->xfer->hdr.id,
          rw->xfer->hdr.protocol_id,
          rw->xfer->hdr.seq,
          raw->desc->max_rx_timeout_ms,
          rw->xfer->hdr.poll_completion);

 mutex_lock(&raw->active_mtx);
 list_add_tail(&rw->node, &raw->active_waiters);
 mutex_unlock(&raw->active_mtx);

 /* kick waiter work */
 queue_work(raw->wait_wq, &raw->waiters_work);
}

static struct scmi_xfer_raw_waiter *
scmi_xfer_raw_waiter_dequeue(struct scmi_raw_mode_info *raw)
{
 struct scmi_xfer_raw_waiter *rw = NULL;

 mutex_lock(&raw->active_mtx);
 if (!list_empty(&raw->active_waiters)) {
  rw = list_first_entry(&raw->active_waiters,
          struct scmi_xfer_raw_waiter, node);
  list_del_init(&rw->node);
 }
 mutex_unlock(&raw->active_mtx);

 return rw;
}

/**
 * scmi_xfer_raw_worker  - Work function to wait for Raw xfers completions
 *
 * @work: A reference to the work.
 *
 * In SCMI Raw mode, once a user-provided injected SCMI message is sent, we
 * cannot wait to receive its response (if any) in the context of the injection
 * routines so as not to leave the userspace write syscall, which delivered the
 * SCMI message to send, pending till eventually a reply is received.
 * Userspace should and will poll/wait instead on the read syscalls which will
 * be in charge of reading a received reply (if any).
 *
 * Even though reply messages are collected and reported into the SCMI Raw layer
 * on the RX path, nonetheless we have to properly wait for their completion as
 * usual (and async_completion too if needed) in order to properly release the
 * xfer structure at the end: to do this out of the context of the write/send
 * these waiting jobs are delegated to this deferred worker.
 *
 * Any sent xfer, to be waited for, is timestamped and queued for later
 * consumption by this worker: queue aging is accounted for while choosing a
 * timeout for the completion, BUT we do not really care here if we end up
 * accidentally waiting for a bit too long.
 */

static void scmi_xfer_raw_worker(struct work_struct *work)
{
 struct scmi_raw_mode_info *raw;
 struct device *dev;
 unsigned long max_tmo;

 raw = container_of(work, struct scmi_raw_mode_info, waiters_work);
 dev = raw->handle->dev;
 max_tmo = msecs_to_jiffies(raw->desc->max_rx_timeout_ms);

 do {
  int ret = 0;
  unsigned int timeout_ms;
  unsigned long aging;
  struct scmi_xfer *xfer;
  struct scmi_xfer_raw_waiter *rw;
  struct scmi_chan_info *cinfo;

  rw = scmi_xfer_raw_waiter_dequeue(raw);
  if (!rw)
   return;

  cinfo = rw->cinfo;
  xfer = rw->xfer;
  /*
   * Waiters are queued by wait-deadline at the end, so some of
   * them could have been already expired when processed, BUT we
   * have to check the completion status anyway just in case a
   * virtually expired (aged) transaction was indeed completed
   * fine and we'll have to wait for the asynchronous part (if
   * any): for this reason a 1 ms timeout is used for already
   * expired/aged xfers.
 */

  aging = jiffies - rw->start_jiffies;
  timeout_ms = max_tmo > aging ?
   jiffies_to_msecs(max_tmo - aging) : 1;

  ret = scmi_xfer_raw_wait_for_message_response(cinfo, xfer,
             timeout_ms);
  if (!ret && xfer->hdr.status)
   ret = scmi_to_linux_errno(xfer->hdr.status);

  if (raw->desc->ops->mark_txdone)
   raw->desc->ops->mark_txdone(rw->cinfo, ret, xfer);

  trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
        xfer->hdr.protocol_id, xfer->hdr.seq,
        ret, scmi_inflight_count(raw->handle));

  /* Wait also for an async delayed response if needed */
  if (!ret && xfer->async_done) {
   unsigned long tmo = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);

   if (!wait_for_completion_timeout(xfer->async_done, tmo))
    dev_err(dev,
     "timed out in RAW delayed resp - HDR:%08X\n",
     pack_scmi_header(&xfer->hdr));
  }

  /* Release waiter and xfer */
  scmi_xfer_raw_put(raw->handle, xfer);
  scmi_xfer_raw_waiter_put(raw, rw);
 } while (1);
}

static void scmi_xfer_raw_reset(struct scmi_raw_mode_info *raw)
{
 int i;

 dev_info(raw->handle->dev, "Resetting SCMI Raw stack.\n");

 for (i = 0; i < SCMI_RAW_MAX_QUEUE; i++)
  scmi_raw_buffer_queue_flush(raw->q[i]);
}

/**
 * scmi_xfer_raw_get_init  - An helper to build a valid xfer from the provided
 * bare SCMI message.
 *
 * @raw: A reference to the Raw instance.
 * @buf: A buffer containing the whole SCMI message to send (including the
 *  header) in little-endian binary formmat.
 * @len: Length of the message in @buf.
 * @p: A pointer to return the initialized Raw xfer.
 *
 * After an xfer is picked from the TX pool and filled in with the message
 * content, the xfer is registered as pending with the core in the usual way
 * using the original sequence number provided by the user with the message.
 *
 * Note that, in case the testing user application is NOT using distinct
 * sequence-numbers between successive SCMI messages such registration could
 * fail temporarily if the previous message, using the same sequence number,
 * had still not released; in such a case we just wait and retry.
 *
 * Return: 0 on Success
 */

static int scmi_xfer_raw_get_init(struct scmi_raw_mode_info *raw, void *buf,
      size_t len, struct scmi_xfer **p)
{
 u32 msg_hdr;
 size_t tx_size;
 struct scmi_xfer *xfer;
 int ret, retry = SCMI_XFER_RAW_MAX_RETRIES;
 struct device *dev = raw->handle->dev;

 if (!buf || len < sizeof(u32))
  return -EINVAL;

 tx_size = len - sizeof(u32);
 /* Ensure we have sane transfer sizes */
 if (tx_size > raw->desc->max_msg_size)
  return -ERANGE;

 xfer = scmi_xfer_raw_get(raw->handle);
 if (IS_ERR(xfer)) {
  dev_warn(dev, "RAW - Cannot get a free RAW xfer !\n");
  return PTR_ERR(xfer);
 }

 /* Build xfer from the provided SCMI bare LE message */
 msg_hdr = le32_to_cpu(*((__le32 *)buf));
 unpack_scmi_header(msg_hdr, &xfer->hdr);
 xfer->hdr.seq = (u16)MSG_XTRACT_TOKEN(msg_hdr);
 /* Polling not supported */
 xfer->hdr.poll_completion = false;
 xfer->hdr.status = SCMI_SUCCESS;
 xfer->tx.len = tx_size;
 xfer->rx.len = raw->desc->max_msg_size;
 /* Clear the whole TX buffer */
 memset(xfer->tx.buf, 0x00, raw->desc->max_msg_size);
 if (xfer->tx.len)
  memcpy(xfer->tx.buf, (u8 *)buf + sizeof(msg_hdr), xfer->tx.len);
 *p = xfer;

 /*
  * In flight registration can temporarily fail in case of Raw messages
  * if the user injects messages without using monotonically increasing
  * sequence numbers since, in Raw mode, the xfer (and the token) is
  * finally released later by a deferred worker. Just retry for a while.
 */

 do {
  ret = scmi_xfer_raw_inflight_register(raw->handle, xfer);
  if (ret) {
   dev_dbg(dev,
    "...retrying[%d] inflight registration\n",
    retry);
   msleep(raw->desc->max_rx_timeout_ms /
          SCMI_XFER_RAW_MAX_RETRIES);
  }
 } while (ret && --retry);

 if (ret) {
  dev_warn(dev,
    "RAW - Could NOT register xfer %d in-flight HDR:0x%08X\n",
    xfer->hdr.seq, msg_hdr);
  scmi_xfer_raw_put(raw->handle, xfer);
 }

 return ret;
}

/**
 * scmi_do_xfer_raw_start  - An helper to send a valid raw xfer
 *
 * @raw: A reference to the Raw instance.
 * @xfer: The xfer to send
 * @chan_id: The channel ID to use, if zero the channels is automatically
 *      selected based on the protocol used.
 * @async: A flag stating if an asynchronous command is required.
 *
 * This function send a previously built raw xfer using an appropriate channel
 * and queues the related waiting work.
 *
 * Note that we need to know explicitly if the required command is meant to be
 * asynchronous in kind since we have to properly setup the waiter.
 * (and deducing this from the payload is weak and do not scale given there is
 *  NOT a common header-flag stating if the command is asynchronous or not)
 *
 * Return: 0 on Success
 */

static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
      struct scmi_xfer *xfer, u8 chan_id,
      bool async)
{
 int ret;
 struct scmi_chan_info *cinfo;
 struct scmi_xfer_raw_waiter *rw;
 struct device *dev = raw->handle->dev;

 if (!chan_id)
  chan_id = xfer->hdr.protocol_id;
 else
  xfer->flags |= SCMI_XFER_FLAG_CHAN_SET;

 cinfo = scmi_xfer_raw_channel_get(raw->handle, chan_id);
 if (IS_ERR(cinfo))
  return PTR_ERR(cinfo);

 rw = scmi_xfer_raw_waiter_get(raw, xfer, cinfo, async);
 if (!rw) {
  dev_warn(dev, "RAW - Cannot get a free waiter !\n");
  return -ENOMEM;
 }

 /* True ONLY if also supported by transport. */
 if (is_polling_enabled(cinfo, raw->desc))
  xfer->hdr.poll_completion = true;

 reinit_completion(&xfer->done);
 /* Make sure xfer state update is visible before sending */
 smp_store_mb(xfer->state, SCMI_XFER_SENT_OK);

 trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
         xfer->hdr.protocol_id, xfer->hdr.seq,
         xfer->hdr.poll_completion,
         scmi_inflight_count(raw->handle));

 ret = raw->desc->ops->send_message(rw->cinfo, xfer);
 if (ret) {
  dev_err(dev, "Failed to send RAW message %d\n", ret);
  scmi_xfer_raw_waiter_put(raw, rw);
  return ret;
 }

 trace_scmi_msg_dump(raw->id, cinfo->id, xfer->hdr.protocol_id,
       xfer->hdr.id, "cmnd", xfer->hdr.seq,
       xfer->hdr.status,
       xfer->tx.buf, xfer->tx.len);

 scmi_xfer_raw_waiter_enqueue(raw, rw);

 return ret;
}

/**
 * scmi_raw_message_send  - An helper to build and send an SCMI command using
 * the provided SCMI bare message buffer
 *
 * @raw: A reference to the Raw instance.
 * @buf: A buffer containing the whole SCMI message to send (including the
 *  header) in little-endian binary format.
 * @len: Length of the message in @buf.
 * @chan_id: The channel ID to use.
 * @async: A flag stating if an asynchronous command is required.
 * @poll: A flag stating if a polling transmission is required.
 *
 * Return: 0 on Success
 */

static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
     void *buf, size_t len, u8 chan_id,
     bool async, bool poll)
{
 int ret;
 struct scmi_xfer *xfer;

 ret = scmi_xfer_raw_get_init(raw, buf, len, &xfer);
 if (ret)
  return ret;

 if (poll) {
  if (is_transport_polling_capable(raw->desc)) {
   xfer->hdr.poll_completion = true;
  } else {
   dev_err(raw->handle->dev,
    "Failed to send RAW message - Polling NOT supported\n");
   return -EINVAL;
  }
 }

 ret = scmi_do_xfer_raw_start(raw, xfer, chan_id, async);
 if (ret)
  scmi_xfer_raw_put(raw->handle, xfer);

 return ret;
}

static struct scmi_raw_buffer *
scmi_raw_message_dequeue(struct scmi_raw_queue *q, bool o_nonblock)
{
 unsigned long flags;
 struct scmi_raw_buffer *rb;

 spin_lock_irqsave(&q->msg_q_lock, flags);
 while (list_empty(&q->msg_q)) {
  spin_unlock_irqrestore(&q->msg_q_lock, flags);

  if (o_nonblock)
   return ERR_PTR(-EAGAIN);

  if (wait_event_interruptible(q->wq, !list_empty(&q->msg_q)))
   return ERR_PTR(-ERESTARTSYS);

  spin_lock_irqsave(&q->msg_q_lock, flags);
 }

 rb = scmi_raw_buffer_dequeue_unlocked(q);

 spin_unlock_irqrestore(&q->msg_q_lock, flags);

 return rb;
}

/**
 * scmi_raw_message_receive  - An helper to dequeue and report the next
 * available enqueued raw message payload that has been collected.
 *
 * @raw: A reference to the Raw instance.
 * @buf: A buffer to get hold of the whole SCMI message received and represented
 *  in little-endian binary format.
 * @len: Length of @buf.
 * @size: The effective size of the message copied into @buf
 * @idx: The index of the queue to pick the next queued message from.
 * @chan_id: The channel ID to use.
 * @o_nonblock: A flag to request a non-blocking message dequeue.
 *
 * Return: 0 on Success
 */

static int scmi_raw_message_receive(struct scmi_raw_mode_info *raw,
        void *buf, size_t len, size_t *size,
        unsigned int idx, unsigned int chan_id,
        bool o_nonblock)
{
 int ret = 0;
 struct scmi_raw_buffer *rb;
 struct scmi_raw_queue *q;

 q = scmi_raw_queue_select(raw, idx, chan_id);
 if (!q)
  return -ENODEV;

 rb = scmi_raw_message_dequeue(q, o_nonblock);
 if (IS_ERR(rb)) {
  dev_dbg(raw->handle->dev, "RAW - No message available!\n");
  return PTR_ERR(rb);
 }

 if (rb->msg.len <= len) {
  memcpy(buf, rb->msg.buf, rb->msg.len);
  *size = rb->msg.len;
 } else {
  ret = -ENOSPC;
 }

 scmi_raw_buffer_put(q, rb);

 return ret;
}

/* SCMI Raw debugfs helpers */

static ssize_t scmi_dbg_raw_mode_common_read(struct file *filp,
          char __user *buf,
          size_t count, loff_t *ppos,
          unsigned int idx)
{
 ssize_t cnt;
 struct scmi_dbg_raw_data *rd = filp->private_data;

 if (!rd->rx_size) {
  int ret;

  ret = scmi_raw_message_receive(rd->raw, rd->rx.buf, rd->rx.len,
            &rd->rx_size, idx, rd->chan_id,
            filp->f_flags & O_NONBLOCK);
  if (ret) {
   rd->rx_size = 0;
   return ret;
  }

  /* Reset any previous filepos change, including writes */
  *ppos = 0;
 } else if (*ppos == rd->rx_size) {
  /* Return EOF once all the message has been read-out */
  rd->rx_size = 0;
  return 0;
 }

 cnt = simple_read_from_buffer(buf, count, ppos,
          rd->rx.buf, rd->rx_size);

 return cnt;
}

static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
           const char __user *buf,
           size_t count, loff_t *ppos,
           bool async, bool poll)
{
 int ret;
 struct scmi_dbg_raw_data *rd = filp->private_data;

 if (count > rd->tx.len - rd->tx_size)
  return -ENOSPC;

 /* On first write attempt @count carries the total full message size. */
 if (!rd->tx_size)
  rd->tx_req_size = count;

 /*
  * Gather a full message, possibly across multiple interrupted wrrtes,
  * before sending it with a single RAW xfer.
 */

 if (rd->tx_size < rd->tx_req_size) {
  ssize_t cnt;

  cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos,
          buf, count);
  if (cnt < 0)
   return cnt;

  rd->tx_size += cnt;
  if (cnt < count)
   return cnt;
 }

 ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size,
        rd->chan_id, async, poll);

 /* Reset ppos for next message ... */
 rd->tx_size = 0;
 *ppos = 0;

 return ret ?: count;
}

static __poll_t scmi_test_dbg_raw_common_poll(struct file *filp,
           struct poll_table_struct *wait,
           unsigned int idx)
{
 unsigned long flags;
 struct scmi_dbg_raw_data *rd = filp->private_data;
 struct scmi_raw_queue *q;
 __poll_t mask = 0;

 q = scmi_raw_queue_select(rd->raw, idx, rd->chan_id);
 if (!q)
  return mask;

 poll_wait(filp, &q->wq, wait);

 spin_lock_irqsave(&q->msg_q_lock, flags);
 if (!list_empty(&q->msg_q))
  mask = EPOLLIN | EPOLLRDNORM;
 spin_unlock_irqrestore(&q->msg_q_lock, flags);

 return mask;
}

static ssize_t scmi_dbg_raw_mode_message_read(struct file *filp,
           char __user *buf,
           size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
          SCMI_RAW_REPLY_QUEUE);
}

static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp,
            const char __user *buf,
            size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
           false, false);
}

static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp,
            struct poll_table_struct *wait)
{
 return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_REPLY_QUEUE);
}

static int scmi_dbg_raw_mode_open(struct inode *inode, struct file *filp)
{
 struct scmi_raw_mode_info *raw;
 struct scmi_dbg_raw_data *rd;

 if (!inode->i_private)
  return -ENODEV;

 raw = inode->i_private;
 rd = kzalloc(sizeof(*rd), GFP_KERNEL);
 if (!rd)
  return -ENOMEM;

 rd->rx.len = raw->desc->max_msg_size + sizeof(u32);
 rd->rx.buf = kzalloc(rd->rx.len, GFP_KERNEL);
 if (!rd->rx.buf) {
  kfree(rd);
  return -ENOMEM;
 }

 rd->tx.len = raw->desc->max_msg_size + sizeof(u32);
 rd->tx.buf = kzalloc(rd->tx.len, GFP_KERNEL);
 if (!rd->tx.buf) {
  kfree(rd->rx.buf);
  kfree(rd);
  return -ENOMEM;
 }

 /* Grab channel ID from debugfs entry naming if any */
 /* not set - reassing 0 we already had after kzalloc() */
 rd->chan_id = debugfs_get_aux_num(filp);

 rd->raw = raw;
 filp->private_data = rd;

 return nonseekable_open(inode, filp);
}

static int scmi_dbg_raw_mode_release(struct inode *inode, struct file *filp)
{
 struct scmi_dbg_raw_data *rd = filp->private_data;

 kfree(rd->rx.buf);
 kfree(rd->tx.buf);
 kfree(rd);

 return 0;
}

static ssize_t scmi_dbg_raw_mode_reset_write(struct file *filp,
          const char __user *buf,
          size_t count, loff_t *ppos)
{
 struct scmi_dbg_raw_data *rd = filp->private_data;

 scmi_xfer_raw_reset(rd->raw);

 return count;
}

static const struct file_operations scmi_dbg_raw_mode_reset_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .write = scmi_dbg_raw_mode_reset_write,
 .owner = THIS_MODULE,
};

static const struct file_operations scmi_dbg_raw_mode_message_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_dbg_raw_mode_message_read,
 .write = scmi_dbg_raw_mode_message_write,
 .poll = scmi_dbg_raw_mode_message_poll,
 .owner = THIS_MODULE,
};

static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp,
           const char __user *buf,
           size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
           true, false);
}

static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_dbg_raw_mode_message_read,
 .write = scmi_dbg_raw_mode_message_async_write,
 .poll = scmi_dbg_raw_mode_message_poll,
 .owner = THIS_MODULE,
};

static ssize_t scmi_dbg_raw_mode_message_poll_write(struct file *filp,
          const char __user *buf,
          size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
           false, true);
}

static const struct file_operations scmi_dbg_raw_mode_message_poll_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_dbg_raw_mode_message_read,
 .write = scmi_dbg_raw_mode_message_poll_write,
 .poll = scmi_dbg_raw_mode_message_poll,
 .owner = THIS_MODULE,
};

static ssize_t scmi_dbg_raw_mode_message_poll_async_write(struct file *filp,
         const char __user *buf,
         size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos,
           true, true);
}

static const struct file_operations scmi_dbg_raw_mode_message_poll_async_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_dbg_raw_mode_message_read,
 .write = scmi_dbg_raw_mode_message_poll_async_write,
 .poll = scmi_dbg_raw_mode_message_poll,
 .owner = THIS_MODULE,
};

static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp,
       char __user *buf,
       size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
          SCMI_RAW_NOTIF_QUEUE);
}

static __poll_t
scmi_test_dbg_raw_mode_notif_poll(struct file *filp,
      struct poll_table_struct *wait)
{
 return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_NOTIF_QUEUE);
}

static const struct file_operations scmi_dbg_raw_mode_notification_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_test_dbg_raw_mode_notif_read,
 .poll = scmi_test_dbg_raw_mode_notif_poll,
 .owner = THIS_MODULE,
};

static ssize_t scmi_test_dbg_raw_mode_errors_read(struct file *filp,
        char __user *buf,
        size_t count, loff_t *ppos)
{
 return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
          SCMI_RAW_ERRS_QUEUE);
}

static __poll_t
scmi_test_dbg_raw_mode_errors_poll(struct file *filp,
       struct poll_table_struct *wait)
{
 return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_ERRS_QUEUE);
}

static const struct file_operations scmi_dbg_raw_mode_errors_fops = {
 .open = scmi_dbg_raw_mode_open,
 .release = scmi_dbg_raw_mode_release,
 .read = scmi_test_dbg_raw_mode_errors_read,
 .poll = scmi_test_dbg_raw_mode_errors_poll,
 .owner = THIS_MODULE,
};

static struct scmi_raw_queue *
scmi_raw_queue_init(struct scmi_raw_mode_info *raw)
{
 int i;
 struct scmi_raw_buffer *rb;
 struct device *dev = raw->handle->dev;
 struct scmi_raw_queue *q;

 q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
 if (!q)
  return ERR_PTR(-ENOMEM);

 rb = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rb), GFP_KERNEL);
 if (!rb)
  return ERR_PTR(-ENOMEM);

 spin_lock_init(&q->free_bufs_lock);
 INIT_LIST_HEAD(&q->free_bufs);
 for (i = 0; i < raw->tx_max_msg; i++, rb++) {
  rb->max_len = raw->desc->max_msg_size + sizeof(u32);
  rb->msg.buf = devm_kzalloc(dev, rb->max_len, GFP_KERNEL);
  if (!rb->msg.buf)
   return ERR_PTR(-ENOMEM);
  scmi_raw_buffer_put(q, rb);
 }

 spin_lock_init(&q->msg_q_lock);
 INIT_LIST_HEAD(&q->msg_q);
 init_waitqueue_head(&q->wq);

 return q;
}

static int scmi_xfer_raw_worker_init(struct scmi_raw_mode_info *raw)
{
 int i;
 struct scmi_xfer_raw_waiter *rw;
 struct device *dev = raw->handle->dev;

 rw = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rw), GFP_KERNEL);
 if (!rw)
  return -ENOMEM;

 raw->wait_wq = alloc_workqueue("scmi-raw-wait-wq-%d",
           WQ_UNBOUND | WQ_FREEZABLE |
           WQ_HIGHPRI | WQ_SYSFS, 0, raw->id);
 if (!raw->wait_wq)
  return -ENOMEM;

 mutex_init(&raw->free_mtx);
 INIT_LIST_HEAD(&raw->free_waiters);
 mutex_init(&raw->active_mtx);
 INIT_LIST_HEAD(&raw->active_waiters);

 for (i = 0; i < raw->tx_max_msg; i++, rw++) {
  init_completion(&rw->async_response);
  scmi_xfer_raw_waiter_put(raw, rw);
 }
 INIT_WORK(&raw->waiters_work, scmi_xfer_raw_worker);

 return 0;
}

static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw,
          u8 *channels, int num_chans)
{
 int ret, idx;
 void *gid;
 struct device *dev = raw->handle->dev;

 gid = devres_open_group(dev, NULL, GFP_KERNEL);
 if (!gid)
  return -ENOMEM;

 for (idx = 0; idx < SCMI_RAW_MAX_QUEUE; idx++) {
  raw->q[idx] = scmi_raw_queue_init(raw);
  if (IS_ERR(raw->q[idx])) {
   ret = PTR_ERR(raw->q[idx]);
   goto err;
  }
 }

 xa_init(&raw->chans_q);
 if (num_chans > 1) {
  int i;

  for (i = 0; i < num_chans; i++) {
   struct scmi_raw_queue *q;

   q = scmi_raw_queue_init(raw);
   if (IS_ERR(q)) {
    ret = PTR_ERR(q);
    goto err_xa;
   }

   ret = xa_insert(&raw->chans_q, channels[i], q,
     GFP_KERNEL);
   if (ret) {
    dev_err(dev,
     "Fail to allocate Raw queue 0x%02X\n",
     channels[i]);
    goto err_xa;
   }
  }
 }

 ret = scmi_xfer_raw_worker_init(raw);
 if (ret)
  goto err_xa;

 devres_close_group(dev, gid);
 raw->gid = gid;

 return 0;

err_xa:
 xa_destroy(&raw->chans_q);
err:
 devres_release_group(dev, gid);
 return ret;
}

/**
 * scmi_raw_mode_init  - Function to initialize the SCMI Raw stack
 *
 * @handle: Pointer to SCMI entity handle
 * @top_dentry: A reference to the top Raw debugfs dentry
 * @instance_id: The ID of the underlying SCMI platform instance represented by
 *   this Raw instance
 * @channels: The list of the existing channels
 * @num_chans: The number of entries in @channels
 * @desc: Reference to the transport operations
 * @tx_max_msg: Max number of in-flight messages allowed by the transport
 *
 * This function prepare the SCMI Raw stack and creates the debugfs API.
 *
 * Return: An opaque handle to the Raw instance on Success, an ERR_PTR otherwise
 */

void *scmi_raw_mode_init(const struct scmi_handle *handle,
    struct dentry *top_dentry, int instance_id,
    u8 *channels, int num_chans,
    const struct scmi_desc *desc, int tx_max_msg)
{
 int ret;
 struct scmi_raw_mode_info *raw;
 struct device *dev;

 if (!handle || !desc)
  return ERR_PTR(-EINVAL);

 dev = handle->dev;
 raw = devm_kzalloc(dev, sizeof(*raw), GFP_KERNEL);
 if (!raw)
  return ERR_PTR(-ENOMEM);

 raw->handle = handle;
 raw->desc = desc;
 raw->tx_max_msg = tx_max_msg;
 raw->id = instance_id;

 ret = scmi_raw_mode_setup(raw, channels, num_chans);
 if (ret) {
  devm_kfree(dev, raw);
  return ERR_PTR(ret);
 }

 raw->dentry = debugfs_create_dir("raw", top_dentry);

 debugfs_create_file("reset", 0200, raw->dentry, raw,
       &scmi_dbg_raw_mode_reset_fops);

 debugfs_create_file("message", 0600, raw->dentry, raw,
       &scmi_dbg_raw_mode_message_fops);

 debugfs_create_file("message_async", 0600, raw->dentry, raw,
       &scmi_dbg_raw_mode_message_async_fops);

 debugfs_create_file("message_poll", 0600, raw->dentry, raw,
       &scmi_dbg_raw_mode_message_poll_fops);

 debugfs_create_file("message_poll_async", 0600, raw->dentry, raw,
       &scmi_dbg_raw_mode_message_poll_async_fops);

 debugfs_create_file("notification", 0400, raw->dentry, raw,
       &scmi_dbg_raw_mode_notification_fops);

 debugfs_create_file("errors", 0400, raw->dentry, raw,
       &scmi_dbg_raw_mode_errors_fops);

 /*
  * Expose per-channel entries if multiple channels available.
  * Just ignore errors while setting up these interfaces since we
  * have anyway already a working core Raw support.
 */

 if (num_chans > 1) {
  int i;
  struct dentry *top_chans;

  top_chans = debugfs_create_dir("channels", raw->dentry);

  for (i = 0; i < num_chans; i++) {
   char cdir[8];
   struct dentry *chd;

   snprintf(cdir, 8, "0x%02X", channels[i]);
   chd = debugfs_create_dir(cdir, top_chans);

   debugfs_create_file_aux_num("message", 0600, chd,
         raw, channels[i],
         &scmi_dbg_raw_mode_message_fops);

   debugfs_create_file_aux_num("message_async", 0600, chd,
         raw, channels[i],
         &scmi_dbg_raw_mode_message_async_fops);

   debugfs_create_file_aux_num("message_poll", 0600, chd,
          raw, channels[i],
          &scmi_dbg_raw_mode_message_poll_fops);

   debugfs_create_file_aux_num("message_poll_async", 0600,
          chd, raw, channels[i],
          &scmi_dbg_raw_mode_message_poll_async_fops);
  }
 }

 dev_info(dev, "SCMI RAW Mode initialized for instance %d\n", raw->id);

 return raw;
}

/**
 * scmi_raw_mode_cleanup  - Function to cleanup the SCMI Raw stack
 *
 * @r: An opaque handle to an initialized SCMI Raw instance
 */

void scmi_raw_mode_cleanup(void *r)
{
 struct scmi_raw_mode_info *raw = r;

 if (!raw)
  return;

 debugfs_remove_recursive(raw->dentry);

 cancel_work_sync(&raw->waiters_work);
 destroy_workqueue(raw->wait_wq);
 xa_destroy(&raw->chans_q);
}

static int scmi_xfer_raw_collect(void *msg, size_t *msg_len,
     struct scmi_xfer *xfer)
{
 __le32 *m;
 size_t msg_size;

 if (!xfer || !msg || !msg_len)
  return -EINVAL;

 /* Account for hdr ...*/
 msg_size = xfer->rx.len + sizeof(u32);
 /* ... and status if needed */
 if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
  msg_size += sizeof(u32);

 if (msg_size > *msg_len)
  return -ENOSPC;

 m = msg;
 *m = cpu_to_le32(pack_scmi_header(&xfer->hdr));
 if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
  *++m = cpu_to_le32(xfer->hdr.status);

 memcpy(++m, xfer->rx.buf, xfer->rx.len);

 *msg_len = msg_size;

 return 0;
}

/**
 * scmi_raw_message_report  - Helper to report back valid reponses/notifications
 * to raw message requests.
 *
 * @r: An opaque reference to the raw instance configuration
 * @xfer: The xfer containing the message to be reported
 * @idx: The index of the queue.
 * @chan_id: The channel ID to use.
 *
 * If Raw mode is enabled, this is called from the SCMI core on the regular RX
 * path to save and enqueue the response/notification payload carried by this
 * xfer into a dedicated scmi_raw_buffer for later consumption by the user.
 *
 * This way the caller can free the related xfer immediately afterwards and the
 * user can read back the raw message payload at its own pace (if ever) without
 * holding an xfer for too long.
 */

void scmi_raw_message_report(void *r, struct scmi_xfer *xfer,
        unsigned int idx, unsigned int chan_id)
{
 int ret;
 unsigned long flags;
 struct scmi_raw_buffer *rb;
 struct device *dev;
 struct scmi_raw_queue *q;
 struct scmi_raw_mode_info *raw = r;

 if (!raw || (idx == SCMI_RAW_REPLY_QUEUE && !SCMI_XFER_IS_RAW(xfer)))
  return;

 dev = raw->handle->dev;
 q = scmi_raw_queue_select(raw, idx,
      SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0);
 if (!q) {
  dev_warn(dev,
    "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n",
    idx, chan_id);
  return;
 }

 /*
  * Grab the msg_q_lock upfront to avoid a possible race between
  * realizing the free list was empty and effectively picking the next
  * buffer to use from the oldest one enqueued and still unread on this
  * msg_q.
  *
  * Note that nowhere else these locks are taken together, so no risk of
  * deadlocks du eto inversion.
 */

 spin_lock_irqsave(&q->msg_q_lock, flags);
 rb = scmi_raw_buffer_get(q);
 if (!rb) {
  /*
   * Immediate and delayed replies to previously injected Raw
   * commands MUST be read back from userspace to free the buffers:
   * if this is not happening something is seriously broken and
   * must be fixed at the application level: complain loudly.
 */

  if (idx == SCMI_RAW_REPLY_QUEUE) {
   spin_unlock_irqrestore(&q->msg_q_lock, flags);
   dev_warn(dev,
     "RAW[%d] - Buffers exhausted. Dropping report.\n",
     idx);
   return;
  }

  /*
   * Notifications and errors queues are instead handled in a
   * circular manner: unread old buffers are just overwritten by
   * newer ones.
   *
   * The main reason for this is that notifications originated
   * by Raw requests cannot be distinguished from normal ones, so
   * your Raw buffers queues risk to be flooded and depleted by
   * notifications if you left it mistakenly enabled or when in
   * coexistence mode.
 */

  rb = scmi_raw_buffer_dequeue_unlocked(q);
  if (WARN_ON(!rb)) {
   spin_unlock_irqrestore(&q->msg_q_lock, flags);
   return;
  }

  /* Reset to full buffer length */
  rb->msg.len = rb->max_len;

  dev_warn_once(dev,
         "RAW[%d] - Buffers exhausted. Re-using oldest.\n",
         idx);
 }
 spin_unlock_irqrestore(&q->msg_q_lock, flags);

 ret = scmi_xfer_raw_collect(rb->msg.buf, &rb->msg.len, xfer);
 if (ret) {
  dev_warn(dev, "RAW - Cannot collect xfer into buffer !\n");
  scmi_raw_buffer_put(q, rb);
  return;
 }

 scmi_raw_buffer_enqueue(q, rb);
}

static void scmi_xfer_raw_fill(struct scmi_raw_mode_info *raw,
          struct scmi_chan_info *cinfo,
          struct scmi_xfer *xfer, u32 msg_hdr)
{
 /* Unpack received HDR as it is */
 unpack_scmi_header(msg_hdr, &xfer->hdr);
 xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);

 memset(xfer->rx.buf, 0x00, xfer->rx.len);

 raw->desc->ops->fetch_response(cinfo, xfer);
}

/**
 * scmi_raw_error_report  - Helper to report back timed-out or generally
 * unexpected replies.
 *
 * @r: An opaque reference to the raw instance configuration
 * @cinfo: A reference to the channel to use to retrieve the broken xfer
 * @msg_hdr: The SCMI message header of the message to fetch and report
 * @priv: Any private data related to the xfer.
 *
 * If Raw mode is enabled, this is called from the SCMI core on the RX path in
 * case of errors to save and enqueue the bad message payload carried by the
 * message that has just been received.
 *
 * Note that we have to manually fetch any available payload into a temporary
 * xfer to be able to save and enqueue the message, since the regular RX error
 * path which had called this would have not fetched the message payload having
 * classified it as an error.
 */

void scmi_raw_error_report(void *r, struct scmi_chan_info *cinfo,
      u32 msg_hdr, void *priv)
{
 struct scmi_xfer xfer;
 struct scmi_raw_mode_info *raw = r;

 if (!raw)
  return;

 xfer.rx.len = raw->desc->max_msg_size;
 xfer.rx.buf = kzalloc(xfer.rx.len, GFP_ATOMIC);
 if (!xfer.rx.buf) {
  dev_info(raw->handle->dev,
    "Cannot report Raw error for HDR:0x%X - ENOMEM\n",
    msg_hdr);
  return;
 }

 /* Any transport-provided priv must be passed back down to transport */
 if (priv)
  /* Ensure priv is visible */
  smp_store_mb(xfer.priv, priv);

 scmi_xfer_raw_fill(raw, cinfo, &xfer, msg_hdr);
 scmi_raw_message_report(raw, &xfer, SCMI_RAW_ERRS_QUEUE, 0);

 kfree(xfer.rx.buf);
}

Messung V0.5 in Prozent
C=94 H=94 G=93

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.30Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-11) ¤

*Eine klare Vorstellung vom Zielzustand






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1127926
#Domains=2039723