diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 15fac532688e28..4106d3b4cfe890 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -123,8 +123,9 @@ enum sof_ipc4_global_msg { /* Notification (FW to SW driver) */ SOF_IPC4_GLB_NOTIFICATION, - /* 28 .. 31: RESERVED - do not use */ + /* 28 .. 30: RESERVED - do not use */ + SOF_IPC4_GLB_ENTER_GDB = 31, SOF_IPC4_GLB_TYPE_LAST, }; @@ -540,16 +541,45 @@ struct sof_ipc4_notify_resource_data { uint32_t data[6]; } __packed __aligned(4); -#define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ - /* - * The debug memory window is divided into 16 slots, and the - * first slot is used as a recorder for the other 15 slots. + * The debug memory window contains up to 16 4kB large pages, where pages one to + * fifteen are used for user slots and page zero contains slot descriptors: + * + * ------------------------ + * | Page0 - descriptors | + * ------------------------ + * | Page1 - slot0 | + * ------------------------ + * | Page2 - slot1 | + * ------------------------ + * | ... | + * ------------------------ + * | Page14 - slot13 | + * ------------------------ + * | Page15 - slot14 | + * ------------------------ + * + * The slot descriptor is: + * u32 res_id; + * u32 type; + * u32 vma; */ + +#define SOF_IPC4_DEBUG_PAGE_SIZE 0x1000 +#define SOF_IPC4_DEBUG_SLOT_SIZE SOF_IPC4_DEBUG_PAGE_SIZE +#define SOF_IPC4_DEBUG_PAGE0_SLOT_OFFSET 1024 + +/* Usable slots 0..14 (page 1..15) */ #define SOF_IPC4_MAX_DEBUG_SLOTS 15 -#define SOF_IPC4_DEBUG_SLOT_SIZE 0x1000 -/* debug log slot types */ +/* + * Descriptor 0-14: slot 0-14 (page 1-15) + * Descriptor 15: partial slot at page 0 + 1024 + */ +#define SOF_IPC4_MAX_DEBUG_DESCS (SOF_IPC4_MAX_DEBUG_SLOTS + 1) +#define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ + +/* Debug log slot types */ #define SOF_IPC4_DEBUG_SLOT_UNUSED 0x00000000 #define SOF_IPC4_DEBUG_SLOT_CRITICAL_LOG 0x54524300 /* byte 0: core ID */ #define SOF_IPC4_DEBUG_SLOT_DEBUG_LOG 0x474f4c00 /* byte 0: core ID */ diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 32ffd345e07fc4..893df198543d5a 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -258,6 +258,18 @@ config SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR Say Y if you want to enable the IPC kernel injector. If unsure, select "N". +config SND_SOC_SOF_DEBUG_FW_GDB + tristate "SOF enable Firmware GDB debugging" + select SND_SOC_SOF_CLIENT + help + This enables GDB debugging of the SOF firmware. If selected, this + will make the kernel create a debugfs file, that can be used to + communicate with the firmware GDB stub. When the file is opened, the + kernel sends a command to the firmware to switch to the GDB mode and + to wait for GDB commands. socat can be used to connect that file to a + socket, to which GDB can then connect. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index fc29fff7b568be..18dea3b4ade4fc 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -35,6 +35,7 @@ endif ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) snd-sof-probes-y += sof-client-probes-ipc4.o endif +snd-sof-fw-gdb-objs := sof-client-fw-gdb.o snd-sof-nocodec-y := nocodec.o @@ -53,6 +54,7 @@ obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) += snd-sof-ipc-kernel-injector.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_FW_GDB) += snd-sof-fw-gdb.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/ diff --git a/sound/soc/sof/ipc4-mtrace.c b/sound/soc/sof/ipc4-mtrace.c index aa5b78604db69b..8268e4ad793585 100644 --- a/sound/soc/sof/ipc4-mtrace.c +++ b/sound/soc/sof/ipc4-mtrace.c @@ -10,31 +10,7 @@ #include "ipc4-priv.h" /* - * debug info window is organized in 16 (equal sized) pages: - * - * ------------------------ - * | Page0 - descriptors | - * ------------------------ - * | Page1 - slot0 | - * ------------------------ - * | Page2 - slot1 | - * ------------------------ - * | ... | - * ------------------------ - * | Page14 - slot13 | - * ------------------------ - * | Page15 - slot14 | - * ------------------------ - * - * The slot size == page size - * - * The first page contains descriptors for the remaining 15 cores - * The slot descriptor is: - * u32 res_id; - * u32 type; - * u32 vma; - * - * Log buffer slots have the following layout: + * Log buffer debug window slots have the following layout: * u32 host_read_ptr; * u32 dsp_write_ptr; * u8 buffer[]; diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index a4a090e6724a63..fed74fa2dd807f 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -164,6 +164,7 @@ static const char * const ipc4_dbg_glb_msg_type[] = { DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY_PREPARE), DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE), DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION), + DBG_IPC4_MSG_TYPE_ENTRY(GLB_ENTER_GDB), }; #define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type) [SOF_IPC4_NOTIFY_##type] = #type @@ -585,11 +586,16 @@ size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev, /* The type is the second u32 in the slot descriptor */ slot_desc_type_offset = sdev->debug_box.offset + sizeof(u32); - for (i = 0; i < SOF_IPC4_MAX_DEBUG_SLOTS; i++) { + for (i = 0; i < SOF_IPC4_MAX_DEBUG_DESCS; i++) { sof_mailbox_read(sdev, slot_desc_type_offset, &type, sizeof(type)); - if (type == slot_type) + if (type == slot_type) { + if (i == SOF_IPC4_MAX_DEBUG_DESCS - 1) + /* Desc 15: see comment on SOF_IPC4_MAX_DEBUG_DESCS in header.h */ + return sdev->debug_box.offset + SOF_IPC4_DEBUG_PAGE0_SLOT_OFFSET; + return sdev->debug_box.offset + (i + 1) * SOF_IPC4_DEBUG_SLOT_SIZE; + } slot_desc_type_offset += SOF_IPC4_DEBUG_DESCRIPTOR_SIZE; } diff --git a/sound/soc/sof/sof-client-fw-gdb.c b/sound/soc/sof/sof-client-fw-gdb.c new file mode 100644 index 00000000000000..f30e47e87f68cb --- /dev/null +++ b/sound/soc/sof/sof-client-fw-gdb.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2022 Intel Corporation. +// +// Authors: Noah Klayman +// Guennadi Liakhovetski +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sof-client.h" +#include "sof-priv.h" +#include "ipc4-priv.h" +#include "ops.h" + +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 +#define SOF_FS_GDB_POLL_DELAY_MS 5 + +/* + * We should use a free slot in the debug window, but on Tiger Lake all slots + * are occupied. So we use the reserved space in the first page of the window. + * That page only contains an array of descriptors in the beginning, which + * currently occupies 180 bytes. We leave the first kilobyte reserved for future + * compatibility but we should use a free slot on other platforms with larger + * debug windows. + */ + +union sof_ipc_gdb_message { + struct { + struct sof_ipc_cmd_hdr hdr; + char cmd[]; + } ipc3; + struct { + struct sof_ipc4_msg msg; + char cmd[]; + } ipc4; +} __packed; + +struct sof_fw_gdb_priv { + struct dentry *dfs_file; + enum sof_ipc_type ipc_type; + union sof_ipc_gdb_message *tx_buffer; + struct delayed_work poll_work; + wait_queue_head_t rxq; + wait_queue_head_t txq; + struct mutex mutex; /* Protect the ring buffer */ + struct sof_client_dev *cdev; +}; + +#define DSP_CACHE_LINE_SIZE 64 +/* + * Must match the respective structure in the firmware. Ring buffer "head" and + * "tail" pointers are placed in separate DSP cache lines to guarantee coherency. + */ +#define RING_SIZE (8 * DSP_CACHE_LINE_SIZE) +struct ring { + u32 head; + u8 fill1[DSP_CACHE_LINE_SIZE - sizeof(u32)]; + u32 tail; + u8 fill2[DSP_CACHE_LINE_SIZE - sizeof(u32)]; + u8 data[RING_SIZE]; +} __packed; + +#define RING_TX_OFFSET 0 +#define RING_RX_OFFSET sizeof(struct ring) + +static int sof_fw_gdb_send_message(struct sof_client_dev *cdev) +{ + struct sof_fw_gdb_priv *priv = cdev->data; + struct device *dev = &cdev->auxdev.dev; + int ret; + + switch (priv->ipc_type) { + case SOF_IPC_TYPE_3: + { + struct sof_ipc_cmd_hdr *hdr = &priv->tx_buffer->ipc3.hdr; + + hdr->cmd = SOF_IPC_GLB_GDB_DEBUG; + hdr->size = sizeof(*hdr); + break; + } + case SOF_IPC_TYPE_4: + { + struct sof_ipc4_msg *msg = &priv->tx_buffer->ipc4.msg; + + msg->primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_ENTER_GDB) | + SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST) | + SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + break; + } + default: + return -EINVAL; + } + + /* send the message */ + ret = sof_client_ipc_tx_message_no_reply(cdev, priv->tx_buffer); + if (ret < 0) + dev_err(dev, "IPC message send failed: %d\n", ret); + + return ret; +} + +static bool gdb_is_open; + +static int sof_fw_gdb_dfs_open(struct inode *inode, struct file *file) +{ + struct sof_client_dev *cdev = inode->i_private; + struct device *dev = &cdev->auxdev.dev; + struct sof_fw_gdb_priv *priv = cdev->data; + int ret; + + if (sof_client_get_fw_state(cdev) == SOF_FW_CRASHED) + return -ENODEV; + + if (gdb_is_open) + return -EBUSY; + + ret = debugfs_file_get(file->f_path.dentry); + if (ret < 0) + return ret; + + ret = simple_open(inode, file); + if (ret < 0) { + dev_err(dev, "failed to open %d\n", ret); + goto e_dbgfs; + } + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "debugfs open failed to resume %d\n", ret); + goto e_dbgfs; + } + + ret = sof_client_boot_dsp(cdev); + if (ret < 0) { + dev_err(dev, "debugfs open failed to power on DSP %d\n", ret); + goto e_pm; + } + + /* When file is opened, send GDB init command to FW */ + if (!gdb_is_open) { + ret = sof_fw_gdb_send_message(cdev); + if (ret < 0) { + dev_err(dev, "failed to send IPC %d\n", ret); + goto e_pm; + } + + gdb_is_open = true; + } + + priv->cdev = cdev; + + schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(SOF_FS_GDB_POLL_DELAY_MS)); + + return ret; + +e_pm: + pm_runtime_put_autosuspend(dev); +e_dbgfs: + debugfs_file_put(file->f_path.dentry); + return ret; +} + +static ssize_t sof_fw_gdb_get_ptrs(struct sof_client_dev *cdev, + u32 *head, u32 *tail, size_t offset) +{ + ssize_t slot_offset = sof_client_ipc4_find_debug_slot_offset_by_type(cdev, + SOF_IPC4_DEBUG_SLOT_GDB_STUB); + + if (!slot_offset) + return -ENODEV; + + slot_offset += offset; + + sof_client_mailbox_read(cdev, slot_offset, head, sizeof(*head)); + sof_client_mailbox_read(cdev, slot_offset + offsetof(struct ring, tail), + tail, sizeof(*tail)); + + return slot_offset; +} + +static ssize_t sof_fw_gdb_get_rx_ptrs(struct sof_client_dev *cdev, + u32 *head, u32 *tail) +{ + return sof_fw_gdb_get_ptrs(cdev, head, tail, RING_RX_OFFSET); +} + +static ssize_t sof_debug_read(struct sof_client_dev *cdev, char *buff, size_t bytes) +{ + size_t count = 0; + u32 head, tail; + ssize_t rx_ring_offset = sof_fw_gdb_get_rx_ptrs(cdev, &head, &tail); + unsigned int i; + + if (rx_ring_offset < 0) + return rx_ring_offset; + + if (tail >= RING_SIZE) { + dev_err(&cdev->auxdev.dev, "%s: %#zx head %x beyond buffer limit!\n", + __func__, rx_ring_offset, head); + return -EIO; + } + + for (i = 0; i < bytes; i++) { + if (tail == head) { + /* No Data */ + break; + } + sof_client_mailbox_read(cdev, rx_ring_offset + offsetof(struct ring, data) + tail, + buff + i, sizeof(buff[i])); + tail = (tail + 1) % RING_SIZE; + count++; + } + + /* Read out up to "bytes" bytes from "tail." Update "tail" to the new location */ + sof_client_mailbox_write(cdev, rx_ring_offset + offsetof(struct ring, tail), &tail, + sizeof(tail)); + + return count; +} + +static ssize_t sof_fw_gdb_dfs_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_fw_gdb_priv *priv = cdev->data; + char *kbuff = kzalloc(count, GFP_KERNEL); + + if (!kbuff) + return -ENOMEM; + + mutex_lock(&priv->mutex); + + ssize_t count_read = sof_debug_read(cdev, kbuff, count); + + mutex_unlock(&priv->mutex); + + unsigned long remain = copy_to_user(buffer, kbuff, count_read); + + kfree(kbuff); + + if (remain) + return -EFAULT; + + /* + * Since the DSP runs asynchronously to the kernel, we have to wait a + * while for a response. If we return no data, gdb will exit. This keeps + * it listening for future data. + */ + if (count_read == 0) { + if (copy_to_user(buffer, "\0", 1)) + return -EFAULT; + + return 1; + } + + return count_read; +} + +static ssize_t sof_fw_gdb_get_tx_ptrs(struct sof_client_dev *cdev, + u32 *head, u32 *tail) +{ + return sof_fw_gdb_get_ptrs(cdev, head, tail, RING_TX_OFFSET); +} + +static ssize_t sof_debug_write(struct sof_client_dev *cdev, char *message, size_t bytes) +{ + size_t count = 0; + u32 head, tail; + ssize_t tx_ring_offset = sof_fw_gdb_get_tx_ptrs(cdev, &head, &tail); + unsigned int i; + + if (tx_ring_offset < 0) + return tx_ring_offset; + + if (head >= RING_SIZE) { + dev_err(&cdev->auxdev.dev, "%s: %#zx head %x beyond buffer limit!\n", + __func__, tx_ring_offset, head); + return -EIO; + } + + for (i = 0; i < bytes; i++) { + if ((head + 1) % RING_SIZE == tail) + break; + + sof_client_mailbox_write(cdev, tx_ring_offset + offsetof(struct ring, data) + head, + message + i, sizeof(message[i])); + head = (head + 1) % RING_SIZE; + count++; + } + + sof_client_mailbox_write(cdev, tx_ring_offset, &head, sizeof(head)); + + return count; +} + +static ssize_t sof_fw_gdb_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_fw_gdb_priv *priv = cdev->data; + char *kbuff = kzalloc(count + 1, GFP_KERNEL); + + if (!kbuff) + return -ENOMEM; + + unsigned long num_failed = copy_from_user(kbuff, buffer, count); + + if (num_failed == count) { + kfree(kbuff); + return -EFAULT; + } + + mutex_lock(&priv->mutex); + + ssize_t num_written = sof_debug_write(cdev, kbuff, count - num_failed); + + mutex_unlock(&priv->mutex); + + kfree(kbuff); + + return num_written; +}; + +static int sof_fw_gdb_dfs_release(struct inode *inode, struct file *file) +{ + struct sof_client_dev *cdev = inode->i_private; + struct device *dev = &cdev->auxdev.dev; + struct sof_fw_gdb_priv *priv = cdev->data; + + cancel_delayed_work_sync(&priv->poll_work); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + debugfs_file_put(file->f_path.dentry); + gdb_is_open = false; + + return 0; +} + +static __poll_t sof_fw_gdb_dfs_poll(struct file *file, struct poll_table_struct *wait) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_fw_gdb_priv *priv = cdev->data; + __poll_t mask = 0; + + mutex_lock(&priv->mutex); + u32 head, tail; + + poll_wait(file, &priv->rxq, wait); + poll_wait(file, &priv->txq, wait); + + if (sof_fw_gdb_get_rx_ptrs(cdev, &head, &tail) >= 0 && tail != head) + mask |= POLLIN | POLLRDNORM; + /* readable */ + + if (sof_fw_gdb_get_tx_ptrs(cdev, &head, &tail) >= 0 && (head + 1) % RING_SIZE != tail) + mask |= POLLOUT | POLLWRNORM; + /* writable */ + + mutex_unlock(&priv->mutex); + + return mask; +} + +static const struct file_operations sof_fw_gdb_fops = { + .open = sof_fw_gdb_dfs_open, + .read = sof_fw_gdb_dfs_read, + .write = sof_fw_gdb_dfs_write, + .poll = sof_fw_gdb_dfs_poll, + .llseek = default_llseek, + .release = sof_fw_gdb_dfs_release, + + .owner = THIS_MODULE, +}; + +static void sof_fw_gdb_poll_work(struct work_struct *work) +{ + struct sof_fw_gdb_priv *priv = container_of(work, struct sof_fw_gdb_priv, + poll_work.work); + u32 head, tail; + + mutex_lock(&priv->mutex); + + if (sof_fw_gdb_get_rx_ptrs(priv->cdev, &head, &tail) >= 0 && head != tail) + wake_up_interruptible(&priv->rxq); + + if (sof_fw_gdb_get_tx_ptrs(priv->cdev, &head, &tail) >= 0 && (head + 1) % RING_SIZE != tail) + wake_up_interruptible(&priv->txq); + + mutex_unlock(&priv->mutex); + + schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(SOF_FS_GDB_POLL_DELAY_MS)); +} + +static int sof_fw_gdb_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + static const struct file_operations *fops; + struct device *dev = &auxdev->dev; + struct sof_fw_gdb_priv *priv; + size_t alloc_size; + + /* allocate memory for client data */ + priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ipc_type = sof_client_get_ipc_type(cdev); + alloc_size = sizeof(*priv->tx_buffer); + INIT_DELAYED_WORK(&priv->poll_work, sof_fw_gdb_poll_work); + + priv->tx_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL); + if (!priv->tx_buffer) + return -ENOMEM; + + fops = &sof_fw_gdb_fops; + + cdev->data = priv; + mutex_init(&priv->mutex); + init_waitqueue_head(&priv->rxq); + init_waitqueue_head(&priv->txq); + + priv->dfs_file = debugfs_create_file("fw_gdb", 0644, debugfs_root, + cdev, fops); + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_fw_gdb_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_fw_gdb_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + debugfs_remove(priv->dfs_file); +} + +static const struct auxiliary_device_id sof_fw_gdb_client_id_table[] = { + { .name = "snd_sof.fw_gdb" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_fw_gdb_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_gdb_fw_client_drv = { + .probe = sof_fw_gdb_probe, + .remove = sof_fw_gdb_remove, + + .id_table = sof_fw_gdb_client_id_table, +}; + +module_auxiliary_driver(sof_gdb_fw_client_drv); + +MODULE_DESCRIPTION("SOF IPC FW GDB Client Driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index b0802484a2d339..03af9efa6eb3bb 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -167,6 +167,25 @@ static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_FW_GDB) +static int sof_register_fw_gdb(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "fw_gdb", 0, NULL, 0); +} + +static void sof_unregister_fw_gdb(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "fw_gdb", 0); +} +#else +static inline int sof_register_fw_gdb(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_fw_gdb(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_FW_GDB */ + int sof_register_clients(struct snd_sof_dev *sdev) { int ret; @@ -193,6 +212,12 @@ int sof_register_clients(struct snd_sof_dev *sdev) goto err_kernel_injector; } + ret = sof_register_fw_gdb(sdev); + if (ret) { + dev_err(sdev->dev, "Firmware GDB client registration failed\n"); + goto err_fw_gdb; + } + /* Platform dependent client device registration */ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) @@ -203,6 +228,9 @@ int sof_register_clients(struct snd_sof_dev *sdev) sof_unregister_ipc_kernel_injector(sdev); +err_fw_gdb: + sof_unregister_fw_gdb(sdev); + err_kernel_injector: sof_unregister_ipc_msg_injector(sdev); @@ -220,6 +248,7 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) sof_unregister_ipc_kernel_injector(sdev); sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); + sof_unregister_fw_gdb(sdev); } int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, @@ -393,6 +422,13 @@ struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev return NULL; } EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT"); + +ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cdev, + u32 type) +{ + return sof_ipc4_find_debug_slot_offset_by_type(sof_client_dev_to_sof_dev(cdev), type); +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_debug_slot_offset_by_type, "SND_SOC_SOF_CLIENT"); #endif int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) @@ -673,3 +709,17 @@ struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev) return centry->sdev; } EXPORT_SYMBOL_NS_GPL(sof_client_dev_to_sof_dev, "SND_SOC_SOF_CLIENT"); + +void sof_client_mailbox_read(struct sof_client_dev *cdev, u32 offset, + void *message, size_t bytes) +{ + sof_mailbox_read(sof_client_dev_to_sof_dev(cdev), offset, message, bytes); +} +EXPORT_SYMBOL_NS_GPL(sof_client_mailbox_read, "SND_SOC_SOF_CLIENT"); + +void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, + void *message, size_t bytes) +{ + sof_mailbox_write(sof_client_dev_to_sof_dev(cdev), offset, message, bytes); +} +EXPORT_SYMBOL_NS_GPL(sof_client_mailbox_write, "SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 3b02506c03f1ca..e796801570c823 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -76,4 +76,12 @@ void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev); enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev); int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf); +void sof_client_mailbox_read(struct sof_client_dev *cdev, u32 offset, + void *message, size_t bytes); +void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, + void *message, size_t bytes); + +ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cdev, + u32 type); + #endif /* __SOC_SOF_CLIENT_H */