diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
| commit | 3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch) | |
| tree | b767d7f6e26bc64334d3f14f8422d187239dc45d /sound/soc/sof/sof-client.c | |
| download | linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip | |
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe:
- Fix a task_work add use-after-free with SQPOLL.
The sqpoll thread could pop and complete the last request while
io_req_normal_work_add() was still looking at them after the mpscq
push.
Use the same approach as DEFER_TASKRUN to protect from that, holding
an RCU read lock across the add, and have exit wait for an RCU grace
period for SQPOLL rings as well.
- CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the
big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED
rings
- Mark the source filter table as COW when cloning bpf filters, so
registering another filter on the source doesn't modify the shared
table in place
- Initialize the task context before running the BPF loop
- Requeue zcrx multishot receives stopped by a local resource
- End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit)
* tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
io_uring: fix task_work add use-after-free with SQPOLL
io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full
io_uring/zcrx: requeue multishot receives stopped by a local resource
io_uring: initialize task context before running the BPF loop
io_uring: zero big_cqe for aux CQEs on CQE32 rings
io_uring: fix free entry check for 32b CQEs on CQE32 rings
io_uring: only post the dummy skip CQE on CQE_MIXED rings
io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'sound/soc/sof/sof-client.c')
| -rw-r--r-- | sound/soc/sof/sof-client.c | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c new file mode 100644 index 000000000..64da8df15 --- /dev/null +++ b/sound/soc/sof/sof-client.c @@ -0,0 +1,657 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation +// +// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> +// Peter Ujfalusi <peter.ujfalusi@linux.intel.com> +// + +#include <linux/debugfs.h> +#include <linux/errno.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <sound/sof/ipc4/header.h> +#include "ops.h" +#include "sof-client.h" +#include "sof-priv.h" +#include "ipc3-priv.h" +#include "ipc4-priv.h" + +/** + * struct sof_ipc_event_entry - IPC client event description + * @ipc_msg_type: IPC msg type of the event the client is interested + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_ipc_event_entry { + u32 ipc_msg_type; + struct sof_client_dev *cdev; + sof_client_event_callback callback; + struct list_head list; +}; + +/** + * struct sof_state_event_entry - DSP panic event subscription entry + * @cdev: sof_client_dev of the requesting client + * @callback: Callback function of the client + * @list: item in SOF core client event list + */ +struct sof_state_event_entry { + struct sof_client_dev *cdev; + sof_client_fw_state_callback callback; + struct list_head list; +}; + +/** + * struct sof_client_dev_entry - client device entry for internal management use + * @sdev: pointer to SOF core device struct + * @list: item in SOF core client dev list + * @client_dev: SOF client device + */ +struct sof_client_dev_entry { + struct snd_sof_dev *sdev; + struct list_head list; + + struct sof_client_dev client_dev; +}; + +#define cdev_to_centry(cdev) \ + container_of(cdev, struct sof_client_dev_entry, client_dev) + +static void sof_client_auxdev_release(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_client_dev_entry *centry = cdev_to_centry(cdev); + + kfree(cdev->auxdev.dev.platform_data); + kfree(centry); +} + +static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data, + size_t size) +{ + void *d = NULL; + + if (data) { + d = kmemdup(data, size, GFP_KERNEL); + if (!d) + return -ENOMEM; + } + + cdev->auxdev.dev.platform_data = d; + return 0; +} + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) +static int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) +{ + int ret = 0; + int i; + + if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3) + return 0; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) { + ret = sof_client_dev_register(sdev, "ipc_flood", i, NULL, 0); + if (ret < 0) + break; + } + + if (ret) { + for (; i >= 0; --i) + sof_client_dev_unregister(sdev, "ipc_flood", i); + } + + return ret; +} + +static void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) +{ + int i; + + for (i = 0; i < CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST_NUM; i++) + sof_client_dev_unregister(sdev, "ipc_flood", i); +} +#else +static inline int sof_register_ipc_flood_test(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_flood_test(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST */ + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) +static int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + return sof_client_dev_register(sdev, "msg_injector", 0, NULL, 0); +} + +static void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "msg_injector", 0); +} +#else +static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) +static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + /* Only IPC3 supported right now */ + if (sdev->pdata->ipc_type != SOF_IPC_TYPE_3) + return 0; + + return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0); +} + +static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "kernel_injector", 0); +} +#else +static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ + +int sof_register_clients(struct snd_sof_dev *sdev) +{ + int ret; + + if (sdev->dspless_mode_selected) + return 0; + + /* Register platform independent client devices */ + ret = sof_register_ipc_flood_test(sdev); + if (ret) { + dev_err(sdev->dev, "IPC flood test client registration failed\n"); + return ret; + } + + ret = sof_register_ipc_msg_injector(sdev); + if (ret) { + dev_err(sdev->dev, "IPC message injector client registration failed\n"); + goto err_msg_injector; + } + + ret = sof_register_ipc_kernel_injector(sdev); + if (ret) { + dev_err(sdev->dev, "IPC kernel injector client registration failed\n"); + goto err_kernel_injector; + } + + /* Platform dependent client device registration */ + + if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) + ret = sof_ops(sdev)->register_ipc_clients(sdev); + + if (!ret) + return 0; + + sof_unregister_ipc_kernel_injector(sdev); + +err_kernel_injector: + sof_unregister_ipc_msg_injector(sdev); + +err_msg_injector: + sof_unregister_ipc_flood_test(sdev); + + return ret; +} + +void sof_unregister_clients(struct snd_sof_dev *sdev) +{ + if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) + sof_ops(sdev)->unregister_ipc_clients(sdev); + + sof_unregister_ipc_kernel_injector(sdev); + sof_unregister_ipc_msg_injector(sdev); + sof_unregister_ipc_flood_test(sdev); +} + +int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, + const void *data, size_t size) +{ + struct sof_client_dev_entry *centry; + struct auxiliary_device *auxdev; + struct sof_client_dev *cdev; + int ret; + + centry = kzalloc_obj(*centry); + if (!centry) + return -ENOMEM; + + cdev = ¢ry->client_dev; + + centry->sdev = sdev; + auxdev = &cdev->auxdev; + auxdev->name = name; + auxdev->dev.parent = sdev->dev; + auxdev->dev.release = sof_client_auxdev_release; + auxdev->id = id; + + ret = sof_client_dev_add_data(cdev, data, size); + if (ret < 0) + goto err_dev_add_data; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id); + goto err_dev_init; + } + + ret = auxiliary_device_add(&cdev->auxdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id); + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + auxiliary_device_uninit(&cdev->auxdev); + return ret; + } + + /* add to list of SOF client devices */ + scoped_guard(mutex, &sdev->ipc_client_mutex) + list_add(¢ry->list, &sdev->ipc_client_list); + + return 0; + +err_dev_init: + kfree(cdev->auxdev.dev.platform_data); + +err_dev_add_data: + kfree(centry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, "SND_SOC_SOF_CLIENT"); + +void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id) +{ + struct sof_client_dev_entry *centry; + + guard(mutex)(&sdev->ipc_client_mutex); + + /* + * sof_client_auxdev_release() will be invoked to free up memory + * allocations through put_device() + */ + list_for_each_entry(centry, &sdev->ipc_client_list, list) { + struct sof_client_dev *cdev = ¢ry->client_dev; + + if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) { + list_del(¢ry->list); + auxiliary_device_delete(&cdev->auxdev); + auxiliary_device_uninit(&cdev->auxdev); + break; + } + } +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, "SND_SOC_SOF_CLIENT"); + +int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, + void *reply_data, size_t reply_bytes) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + return sof_ipc_tx_message(sdev->ipc, ipc_msg, hdr->size, + reply_data, reply_bytes); + } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct sof_ipc4_msg *msg = ipc_msg; + + return sof_ipc_tx_message(sdev->ipc, ipc_msg, msg->data_size, + reply_data, reply_bytes); + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, "SND_SOC_SOF_CLIENT"); + +int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) && + sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + if (hdr->size < sizeof(hdr)) { + dev_err(sdev->dev, "The received message size is invalid\n"); + return -EINVAL; + } + + sof_ipc3_do_rx_work(sdev, ipc_msg, msg_buf); + return 0; + } + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, "SND_SOC_SOF_CLIENT"); + +int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, + bool set) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + return sof_ipc_set_get_data(sdev->ipc, ipc_msg, hdr->size, set); + } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct sof_ipc4_msg *msg = ipc_msg; + + return sof_ipc_set_get_data(sdev->ipc, ipc_msg, msg->data_size, + set); + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_set_get_data, "SND_SOC_SOF_CLIENT"); + +#ifdef CONFIG_SND_SOC_SOF_IPC4 +struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *uuid) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(c); + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) + return sof_ipc4_find_module_by_uuid(sdev, uuid); + dev_err(sdev->dev, "Only supported with IPC4\n"); + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT"); + +struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev, + u32 module_id, int instance_id) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) + return sof_ipc4_find_swidget_by_ids(sdev, module_id, instance_id); + dev_err(sdev->dev, "Only supported with IPC4\n"); + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT"); +#endif + +int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) +{ + const struct auxiliary_driver *adrv; + struct sof_client_dev_entry *centry; + + guard(mutex)(&sdev->ipc_client_mutex); + + list_for_each_entry(centry, &sdev->ipc_client_list, list) { + struct sof_client_dev *cdev = ¢ry->client_dev; + + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->suspend) + adrv->suspend(&cdev->auxdev, state); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_suspend_clients, "SND_SOC_SOF_CLIENT"); + +int sof_resume_clients(struct snd_sof_dev *sdev) +{ + const struct auxiliary_driver *adrv; + struct sof_client_dev_entry *centry; + + guard(mutex)(&sdev->ipc_client_mutex); + + list_for_each_entry(centry, &sdev->ipc_client_list, list) { + struct sof_client_dev *cdev = ¢ry->client_dev; + + /* Skip devices without loaded driver */ + if (!cdev->auxdev.dev.driver) + continue; + + adrv = to_auxiliary_drv(cdev->auxdev.dev.driver); + if (adrv->resume) + adrv->resume(&cdev->auxdev); + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_resume_clients, "SND_SOC_SOF_CLIENT"); + +struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->debugfs_root; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, "SND_SOC_SOF_CLIENT"); + +/* DMA buffer allocation in client drivers must use the core SOF device */ +struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->dev; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, "SND_SOC_SOF_CLIENT"); + +const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return &sdev->fw_ready.version; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, "SND_SOC_SOF_CLIENT"); + +size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->ipc->max_payload_size; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_max_payload_size, "SND_SOC_SOF_CLIENT"); + +enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->ipc_type; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT"); + +int sof_client_boot_dsp(struct sof_client_dev *cdev) +{ + return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); +} +EXPORT_SYMBOL_NS_GPL(sof_client_boot_dsp, "SND_SOC_SOF_CLIENT"); + +/* module refcount management of SOF core */ +int sof_client_core_module_get(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (!try_module_get(sdev->dev->driver->owner)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, "SND_SOC_SOF_CLIENT"); + +void sof_client_core_module_put(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + module_put(sdev->dev->driver->owner); +} +EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, "SND_SOC_SOF_CLIENT"); + +/* IPC event handling */ +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) +{ + struct sof_ipc_event_entry *event; + u32 msg_type; + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { + struct sof_ipc_cmd_hdr *hdr = msg_buf; + + msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; + } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + struct sof_ipc4_msg *msg = msg_buf; + + msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); + } else { + dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n", + sdev->pdata->ipc_type); + return; + } + + guard(mutex)(&sdev->client_event_handler_mutex); + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->ipc_msg_type == msg_type) + event->callback(event->cdev, msg_buf); + } +} + +int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type, + sof_client_event_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + if (!callback) + return -EINVAL; + + if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { + if (!(ipc_msg_type & SOF_GLB_TYPE_MASK)) + return -EINVAL; + } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { + if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK)) + return -EINVAL; + } else { + dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n", + __func__, sdev->pdata->ipc_type); + return -EINVAL; + } + + event = kmalloc_obj(*event); + if (!event) + return -ENOMEM; + + event->ipc_msg_type = ipc_msg_type; + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + guard(mutex)(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->ipc_rx_handler_list); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); + +void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, + u32 ipc_msg_type) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_ipc_event_entry *event; + + guard(mutex)(&sdev->ipc_client_mutex); + + list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { + if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { + list_del(&event->list); + kfree(event); + break; + } + } +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); + +/*DSP state notification and query */ +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ + struct sof_state_event_entry *event; + + guard(mutex)(&sdev->ipc_client_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) + event->callback(event->cdev, sdev->fw_state); +} + +int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, + sof_client_fw_state_callback callback) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + if (!callback) + return -EINVAL; + + event = kmalloc_obj(*event); + if (!event) + return -ENOMEM; + + event->cdev = cdev; + event->callback = callback; + + /* add to list of SOF client devices */ + guard(mutex)(&sdev->client_event_handler_mutex); + list_add(&event->list, &sdev->fw_state_handler_list); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, "SND_SOC_SOF_CLIENT"); + +void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_state_event_entry *event; + + guard(mutex)(&sdev->ipc_client_mutex); + + list_for_each_entry(event, &sdev->fw_state_handler_list, list) { + if (event->cdev == cdev) { + list_del(&event->list); + kfree(event); + break; + } + } +} +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT"); + +enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->fw_state; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, "SND_SOC_SOF_CLIENT"); + +struct snd_sof_dev *sof_client_dev_to_sof_dev(struct sof_client_dev *cdev) +{ + struct sof_client_dev_entry *centry = cdev_to_centry(cdev); + + return centry->sdev; +} +EXPORT_SYMBOL_NS_GPL(sof_client_dev_to_sof_dev, "SND_SOC_SOF_CLIENT"); |
