// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2003-2022, Intel Corporation. All rights reserved. * Intel Management Engine Interface (Intel MEI) Linux driver */ #include <linux/sched/signal.h> #include <linux/wait.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/pm_runtime.h> #include <linux/dma-mapping.h> #include <linux/mei.h> #include "mei_dev.h" #include "hbm.h" #include "client.h" /** * mei_me_cl_init - initialize me client * * @me_cl: me client */ void mei_me_cl_init(struct mei_me_client *me_cl) { … } /** * mei_me_cl_get - increases me client refcount * * @me_cl: me client * * Locking: called under "dev->device_lock" lock * * Return: me client or NULL */ struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl) { … } /** * mei_me_cl_release - free me client * * @ref: me_client refcount * * Locking: called under "dev->device_lock" lock */ static void mei_me_cl_release(struct kref *ref) { … } /** * mei_me_cl_put - decrease me client refcount and free client if necessary * * @me_cl: me client * * Locking: called under "dev->device_lock" lock */ void mei_me_cl_put(struct mei_me_client *me_cl) { … } /** * __mei_me_cl_del - delete me client from the list and decrease * reference counter * * @dev: mei device * @me_cl: me client * * Locking: dev->me_clients_rwsem */ static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) { … } /** * mei_me_cl_del - delete me client from the list and decrease * reference counter * * @dev: mei device * @me_cl: me client */ void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl) { … } /** * mei_me_cl_add - add me client to the list * * @dev: mei device * @me_cl: me client */ void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl) { … } /** * __mei_me_cl_by_uuid - locate me client by uuid * increases ref count * * @dev: mei device * @uuid: me client uuid * * Return: me client or NULL if not found * * Locking: dev->me_clients_rwsem */ static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev, const uuid_le *uuid) { … } /** * mei_me_cl_by_uuid - locate me client by uuid * increases ref count * * @dev: mei device * @uuid: me client uuid * * Return: me client or NULL if not found * * Locking: dev->me_clients_rwsem */ struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev, const uuid_le *uuid) { … } /** * mei_me_cl_by_id - locate me client by client id * increases ref count * * @dev: the device structure * @client_id: me client id * * Return: me client or NULL if not found * * Locking: dev->me_clients_rwsem */ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id) { … } /** * __mei_me_cl_by_uuid_id - locate me client by client id and uuid * increases ref count * * @dev: the device structure * @uuid: me client uuid * @client_id: me client id * * Return: me client or null if not found * * Locking: dev->me_clients_rwsem */ static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 client_id) { … } /** * mei_me_cl_by_uuid_id - locate me client by client id and uuid * increases ref count * * @dev: the device structure * @uuid: me client uuid * @client_id: me client id * * Return: me client or null if not found */ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 client_id) { … } /** * mei_me_cl_rm_by_uuid - remove all me clients matching uuid * * @dev: the device structure * @uuid: me client uuid * * Locking: called under "dev->device_lock" lock */ void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid) { … } /** * mei_me_cl_rm_by_uuid_id - remove all me clients matching client id * * @dev: the device structure * @uuid: me client uuid * @id: me client id * * Locking: called under "dev->device_lock" lock */ void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id) { … } /** * mei_me_cl_rm_all - remove all me clients * * @dev: the device structure * * Locking: called under "dev->device_lock" lock */ void mei_me_cl_rm_all(struct mei_device *dev) { … } /** * mei_io_cb_free - free mei_cb_private related memory * * @cb: mei callback struct */ void mei_io_cb_free(struct mei_cl_cb *cb) { … } /** * mei_tx_cb_enqueue - queue tx callback * * @cb: mei callback struct * @head: an instance of list to queue on * * Locking: called under "dev->device_lock" lock */ static inline void mei_tx_cb_enqueue(struct mei_cl_cb *cb, struct list_head *head) { … } /** * mei_tx_cb_dequeue - dequeue tx callback * * @cb: mei callback struct to dequeue and free * * Locking: called under "dev->device_lock" lock */ static inline void mei_tx_cb_dequeue(struct mei_cl_cb *cb) { … } /** * mei_cl_set_read_by_fp - set pending_read flag to vtag struct for given fp * * @cl: mei client * @fp: pointer to file structure * * Locking: called under "dev->device_lock" lock */ static void mei_cl_set_read_by_fp(const struct mei_cl *cl, const struct file *fp) { … } /** * mei_io_cb_init - allocate and initialize io callback * * @cl: mei client * @type: operation type * @fp: pointer to file structure * * Return: mei_cl_cb pointer or NULL; */ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type, const struct file *fp) { … } /** * mei_io_list_flush_cl - removes cbs belonging to the cl. * * @head: an instance of our list structure * @cl: host client */ static void mei_io_list_flush_cl(struct list_head *head, const struct mei_cl *cl) { … } /** * mei_io_tx_list_free_cl - removes cb belonging to the cl and free them * * @head: An instance of our list structure * @cl: host client * @fp: file pointer (matching cb file object), may be NULL */ static void mei_io_tx_list_free_cl(struct list_head *head, const struct mei_cl *cl, const struct file *fp) { … } /** * mei_io_list_free_fp - free cb from a list that matches file pointer * * @head: io list * @fp: file pointer (matching cb file object), may be NULL */ static void mei_io_list_free_fp(struct list_head *head, const struct file *fp) { … } /** * mei_cl_free_pending - free pending cb * * @cl: host client */ static void mei_cl_free_pending(struct mei_cl *cl) { … } /** * mei_cl_alloc_cb - a convenient wrapper for allocating read cb * * @cl: host client * @length: size of the buffer * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure */ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops fop_type, const struct file *fp) { … } /** * mei_cl_enqueue_ctrl_wr_cb - a convenient wrapper for allocating * and enqueuing of the control commands cb * * @cl: host client * @length: size of the buffer * @fop_type: operation type * @fp: associated file pointer (might be NULL) * * Return: cb on success and NULL on failure * Locking: called under "dev->device_lock" lock */ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length, enum mei_cb_file_ops fop_type, const struct file *fp) { … } /** * mei_cl_read_cb - find this cl's callback in the read list * for a specific file * * @cl: host client * @fp: file pointer (matching cb file object), may be NULL * * Return: cb on success, NULL if cb is not found */ struct mei_cl_cb *mei_cl_read_cb(struct mei_cl *cl, const struct file *fp) { … } /** * mei_cl_flush_queues - flushes queue lists belonging to cl. * * @cl: host client * @fp: file pointer (matching cb file object), may be NULL * * Return: 0 on success, -EINVAL if cl or cl->dev is NULL. */ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp) { … } /** * mei_cl_init - initializes cl. * * @cl: host client to be initialized * @dev: mei device */ static void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) { … } /** * mei_cl_allocate - allocates cl structure and sets it up. * * @dev: mei device * Return: The allocated file or NULL on failure */ struct mei_cl *mei_cl_allocate(struct mei_device *dev) { … } /** * mei_cl_link - allocate host id in the host map * * @cl: host client * * Return: 0 on success * -EINVAL on incorrect values * -EMFILE if open count exceeded. */ int mei_cl_link(struct mei_cl *cl) { … } /** * mei_cl_unlink - remove host client from the list * * @cl: host client * * Return: always 0 */ int mei_cl_unlink(struct mei_cl *cl) { … } void mei_host_client_init(struct mei_device *dev) { … } /** * mei_hbuf_acquire - try to acquire host buffer * * @dev: the device structure * Return: true if host buffer was acquired */ bool mei_hbuf_acquire(struct mei_device *dev) { … } /** * mei_cl_wake_all - wake up readers, writers and event waiters so * they can be interrupted * * @cl: host client */ static void mei_cl_wake_all(struct mei_cl *cl) { … } /** * mei_cl_set_disconnected - set disconnected state and clear * associated states and resources * * @cl: host client */ static void mei_cl_set_disconnected(struct mei_cl *cl) { … } static int mei_cl_set_connecting(struct mei_cl *cl, struct mei_me_client *me_cl) { … } /* * mei_cl_send_disconnect - send disconnect request * * @cl: host client * @cb: callback block * * Return: 0, OK; otherwise, error. */ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb) { … } /** * mei_cl_irq_disconnect - processes close related operation from * interrupt thread context - send disconnect request * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * * Return: 0, OK; otherwise, error. */ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } /** * __mei_cl_disconnect - disconnect host client from the me one * internal function runtime pm has to be already acquired * * @cl: host client * * Return: 0 on success, <0 on failure. */ static int __mei_cl_disconnect(struct mei_cl *cl) { … } /** * mei_cl_disconnect - disconnect host client from the me one * * @cl: host client * * Locking: called under "dev->device_lock" lock * * Return: 0 on success, <0 on failure. */ int mei_cl_disconnect(struct mei_cl *cl) { … } /** * mei_cl_is_other_connecting - checks if other * client with the same me client id is connecting * * @cl: private data of the file object * * Return: true if other client is connected, false - otherwise. */ static bool mei_cl_is_other_connecting(struct mei_cl *cl) { … } /** * mei_cl_send_connect - send connect request * * @cl: host client * @cb: callback block * * Return: 0, OK; otherwise, error. */ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb) { … } /** * mei_cl_irq_connect - send connect request in irq_thread context * * @cl: host client * @cb: callback block * @cmpl_list: complete list * * Return: 0, OK; otherwise, error. */ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } /** * mei_cl_connect - connect host client to the me one * * @cl: host client * @me_cl: me client * @fp: pointer to file structure * * Locking: called under "dev->device_lock" lock * * Return: 0 on success, <0 on failure. */ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, const struct file *fp) { … } /** * mei_cl_alloc_linked - allocate and link host client * * @dev: the device structure * * Return: cl on success ERR_PTR on failure */ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev) { … } /** * mei_cl_tx_flow_ctrl_creds - checks flow_control credits for cl. * * @cl: host client * * Return: 1 if tx_flow_ctrl_creds >0, 0 - otherwise. */ static int mei_cl_tx_flow_ctrl_creds(struct mei_cl *cl) { … } /** * mei_cl_tx_flow_ctrl_creds_reduce - reduces transmit flow control credits * for a client * * @cl: host client * * Return: * 0 on success * -EINVAL when ctrl credits are <= 0 */ static int mei_cl_tx_flow_ctrl_creds_reduce(struct mei_cl *cl) { … } /** * mei_cl_vtag_alloc - allocate and fill the vtag structure * * @fp: pointer to file structure * @vtag: vm tag * * Return: * * Pointer to allocated struct - on success * * ERR_PTR(-ENOMEM) on memory allocation failure */ struct mei_cl_vtag *mei_cl_vtag_alloc(struct file *fp, u8 vtag) { … } /** * mei_cl_fp_by_vtag - obtain the file pointer by vtag * * @cl: host client * @vtag: virtual tag * * Return: * * A file pointer - on success * * ERR_PTR(-ENOENT) if vtag is not found in the client vtag list */ const struct file *mei_cl_fp_by_vtag(const struct mei_cl *cl, u8 vtag) { … } /** * mei_cl_reset_read_by_vtag - reset pending_read flag by given vtag * * @cl: host client * @vtag: vm tag */ static void mei_cl_reset_read_by_vtag(const struct mei_cl *cl, u8 vtag) { … } /** * mei_cl_read_vtag_add_fc - add flow control for next pending reader * in the vtag list * * @cl: host client */ static void mei_cl_read_vtag_add_fc(struct mei_cl *cl) { … } /** * mei_cl_vt_support_check - check if client support vtags * * @cl: host client * * Return: * * 0 - supported, or not connected at all * * -EOPNOTSUPP - vtags are not supported by client */ int mei_cl_vt_support_check(const struct mei_cl *cl) { … } /** * mei_cl_add_rd_completed - add read completed callback to list with lock * and vtag check * * @cl: host client * @cb: callback block * */ void mei_cl_add_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) { … } /** * mei_cl_del_rd_completed - free read completed callback with lock * * @cl: host client * @cb: callback block * */ void mei_cl_del_rd_completed(struct mei_cl *cl, struct mei_cl_cb *cb) { … } /** * mei_cl_notify_fop2req - convert fop to proper request * * @fop: client notification start response command * * Return: MEI_HBM_NOTIFICATION_START/STOP */ u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) { … } /** * mei_cl_notify_req2fop - convert notification request top file operation type * * @req: hbm notification request type * * Return: MEI_FOP_NOTIFY_START/STOP */ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) { … } /** * mei_cl_irq_notify - send notification request in irq_thread context * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * * Return: 0 on such and error otherwise. */ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } /** * mei_cl_notify_request - send notification stop/start request * * @cl: host client * @fp: associate request with file * @request: 1 for start or 0 for stop * * Locking: called under "dev->device_lock" lock * * Return: 0 on such and error otherwise. */ int mei_cl_notify_request(struct mei_cl *cl, const struct file *fp, u8 request) { … } /** * mei_cl_notify - raise notification * * @cl: host client * * Locking: called under "dev->device_lock" lock */ void mei_cl_notify(struct mei_cl *cl) { … } /** * mei_cl_notify_get - get or wait for notification event * * @cl: host client * @block: this request is blocking * @notify_ev: true if notification event was received * * Locking: called under "dev->device_lock" lock * * Return: 0 on such and error otherwise. */ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) { … } /** * mei_cl_read_start - the start read client message function. * * @cl: host client * @length: number of bytes to read * @fp: pointer to file structure * * Return: 0 on success, <0 on failure. */ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp) { … } static inline u8 mei_ext_hdr_set_vtag(void *ext, u8 vtag) { … } static inline bool mei_ext_hdr_is_gsc(struct mei_ext_hdr *ext) { … } static inline u8 mei_ext_hdr_set_gsc(struct mei_ext_hdr *ext, struct mei_ext_hdr *gsc_hdr) { … } /** * mei_msg_hdr_init - allocate and initialize mei message header * * @cb: message callback structure * * Return: a pointer to initialized header or ERR_PTR on failure */ static struct mei_msg_hdr *mei_msg_hdr_init(const struct mei_cl_cb *cb) { … } /** * mei_cl_irq_write - write a message to device * from the interrupt thread context * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * * Return: 0, OK; otherwise error. */ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } /** * mei_cl_write - submit a write cb to mei device * assumes device_lock is locked * * @cl: host client * @cb: write callback with filled data * @timeout: send timeout in milliseconds. * effective only for blocking writes: the cb->blocking is set. * set timeout to the MAX_SCHEDULE_TIMEOUT to maixum allowed wait. * * Return: number of bytes sent on success, <0 on failure. */ ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, unsigned long timeout) { … } /** * mei_cl_complete - processes completed operation for a client * * @cl: private data of the file object. * @cb: callback block. */ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) { … } /** * mei_cl_all_disconnect - disconnect forcefully all connected clients * * @dev: mei device */ void mei_cl_all_disconnect(struct mei_device *dev) { … } EXPORT_SYMBOL_GPL(…); static struct mei_cl *mei_cl_dma_map_find(struct mei_device *dev, u8 buffer_id) { … } /** * mei_cl_irq_dma_map - send client dma map request in irq_thread context * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * * Return: 0 on such and error otherwise. */ int mei_cl_irq_dma_map(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } /** * mei_cl_irq_dma_unmap - send client dma unmap request in irq_thread context * * @cl: client * @cb: callback block. * @cmpl_list: complete list. * * Return: 0 on such and error otherwise. */ int mei_cl_irq_dma_unmap(struct mei_cl *cl, struct mei_cl_cb *cb, struct list_head *cmpl_list) { … } static int mei_cl_dma_alloc(struct mei_cl *cl, u8 buf_id, size_t size) { … } static void mei_cl_dma_free(struct mei_cl *cl) { … } /** * mei_cl_dma_alloc_and_map - send client dma map request * * @cl: host client * @fp: pointer to file structure * @buffer_id: id of the mapped buffer * @size: size of the buffer * * Locking: called under "dev->device_lock" lock * * Return: * * -ENODEV * * -EINVAL * * -EOPNOTSUPP * * -EPROTO * * -ENOMEM; */ int mei_cl_dma_alloc_and_map(struct mei_cl *cl, const struct file *fp, u8 buffer_id, size_t size) { … } /** * mei_cl_dma_unmap - send client dma unmap request * * @cl: host client * @fp: pointer to file structure * * Locking: called under "dev->device_lock" lock * * Return: 0 on such and error otherwise. */ int mei_cl_dma_unmap(struct mei_cl *cl, const struct file *fp) { … }