drivers/cdx/controller/mcdi.c

Source file repositories/reference/linux-study-clean/drivers/cdx/controller/mcdi.c

File Facts

System
Linux kernel
Corpus path
drivers/cdx/controller/mcdi.c
Extension
.c
Size
23274 bytes
Lines
871
Domain
Driver Families
Bucket
drivers/cdx
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct cdx_mcdi_blocking_data {
	struct kref ref;
	bool done;
	wait_queue_head_t wq;
	int rc;
	struct cdx_dword *outbuf;
	size_t outlen;
	size_t outlen_actual;
};

static void cdx_mcdi_blocking_data_release(struct kref *ref)
{
	kfree(container_of(ref, struct cdx_mcdi_blocking_data, ref));
}

static void cdx_mcdi_rpc_completer(struct cdx_mcdi *cdx, unsigned long cookie,
				   int rc, struct cdx_dword *outbuf,
				   size_t outlen_actual)
{
	struct cdx_mcdi_blocking_data *wait_data =
		(struct cdx_mcdi_blocking_data *)cookie;

	wait_data->rc = rc;
	memcpy(wait_data->outbuf, outbuf,
	       min(outlen_actual, wait_data->outlen));
	wait_data->outlen_actual = outlen_actual;
	/* memory barrier */
	smp_wmb();
	wait_data->done = true;
	wake_up(&wait_data->wq);
	kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
}

static int cdx_mcdi_rpc_sync(struct cdx_mcdi *cdx, unsigned int cmd,
			     const struct cdx_dword *inbuf, size_t inlen,
			     struct cdx_dword *outbuf, size_t outlen,
			     size_t *outlen_actual, bool quiet)
{
	struct cdx_mcdi_blocking_data *wait_data;
	struct cdx_mcdi_cmd *cmd_item;
	unsigned int handle;
	int rc;

	if (outlen_actual)
		*outlen_actual = 0;

	wait_data = kmalloc_obj(*wait_data);
	if (!wait_data)
		return -ENOMEM;

	cmd_item = kmalloc_obj(*cmd_item);
	if (!cmd_item) {
		kfree(wait_data);
		return -ENOMEM;
	}

	kref_init(&wait_data->ref);
	wait_data->done = false;
	init_waitqueue_head(&wait_data->wq);
	wait_data->outbuf = outbuf;
	wait_data->outlen = outlen;

	kref_init(&cmd_item->ref);
	cmd_item->quiet = quiet;
	cmd_item->cookie = (unsigned long)wait_data;
	cmd_item->completer = &cdx_mcdi_rpc_completer;
	cmd_item->cmd = cmd;
	cmd_item->inlen = inlen;
	cmd_item->inbuf = inbuf;

	/* Claim an extra reference for the completer to put. */
	kref_get(&wait_data->ref);
	rc = cdx_mcdi_rpc_async_internal(cdx, cmd_item, &handle);
	if (rc) {
		kref_put(&wait_data->ref, cdx_mcdi_blocking_data_release);
		goto out;
	}

	if (!wait_event_timeout(wait_data->wq, wait_data->done,
				cdx_mcdi_rpc_timeout(cdx, cmd)) &&
	    !wait_data->done) {
		pr_err("MC command 0x%x inlen %zu timed out (sync)\n",
		       cmd, inlen);

		cdx_mcdi_cancel_cmd(cdx, cmd_item);

		wait_data->rc = -ETIMEDOUT;
		wait_data->outlen_actual = 0;
	}

Annotation

Implementation Notes