// 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/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/poll.h> #include <linux/init.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/sched/signal.h> #include <linux/compat.h> #include <linux/jiffies.h> #include <linux/interrupt.h> #include <linux/mei.h> #include "mei_dev.h" #include "client.h" static const struct class mei_class = …; static dev_t mei_devt; #define MEI_MAX_DEVS … static DEFINE_MUTEX(mei_minor_lock); static DEFINE_IDR(mei_idr); /** * mei_open - the open function * * @inode: pointer to inode structure * @file: pointer to file structure * * Return: 0 on success, <0 on error */ static int mei_open(struct inode *inode, struct file *file) { … } /** * mei_cl_vtag_remove_by_fp - remove vtag that corresponds to fp from list * * @cl: host client * @fp: pointer to file structure * */ static void mei_cl_vtag_remove_by_fp(const struct mei_cl *cl, const struct file *fp) { … } /** * mei_release - the release function * * @inode: pointer to inode structure * @file: pointer to file structure * * Return: 0 on success, <0 on error */ static int mei_release(struct inode *inode, struct file *file) { … } /** * mei_read - the read function. * * @file: pointer to file structure * @ubuf: pointer to user buffer * @length: buffer length * @offset: data offset in buffer * * Return: >=0 data length on success , <0 on error */ static ssize_t mei_read(struct file *file, char __user *ubuf, size_t length, loff_t *offset) { … } /** * mei_cl_vtag_by_fp - obtain the vtag by file pointer * * @cl: host client * @fp: pointer to file structure * * Return: vtag value on success, otherwise 0 */ static u8 mei_cl_vtag_by_fp(const struct mei_cl *cl, const struct file *fp) { … } /** * mei_write - the write function. * * @file: pointer to file structure * @ubuf: pointer to user buffer * @length: buffer length * @offset: data offset in buffer * * Return: >=0 data length on success , <0 on error */ static ssize_t mei_write(struct file *file, const char __user *ubuf, size_t length, loff_t *offset) { … } /** * mei_ioctl_connect_client - the connect to fw client IOCTL function * * @file: private data of the file object * @in_client_uuid: requested UUID for connection * @client: IOCTL connect data, output parameters * * Locking: called under "dev->device_lock" lock * * Return: 0 on success, <0 on failure. */ static int mei_ioctl_connect_client(struct file *file, const uuid_le *in_client_uuid, struct mei_client *client) { … } /** * mei_vt_support_check - check if client support vtags * * @dev: mei_device * @uuid: client UUID * * Locking: called under "dev->device_lock" lock * * Return: * 0 - supported * -ENOTTY - no such client * -EOPNOTSUPP - vtags are not supported by client */ static int mei_vt_support_check(struct mei_device *dev, const uuid_le *uuid) { … } /** * mei_ioctl_connect_vtag - connect to fw client with vtag IOCTL function * * @file: private data of the file object * @in_client_uuid: requested UUID for connection * @client: IOCTL connect data, output parameters * @vtag: vm tag * * Locking: called under "dev->device_lock" lock * * Return: 0 on success, <0 on failure. */ static int mei_ioctl_connect_vtag(struct file *file, const uuid_le *in_client_uuid, struct mei_client *client, u8 vtag) { … } /** * mei_ioctl_client_notify_request - propagate event notification * request to client * * @file: pointer to file structure * @request: 0 - disable, 1 - enable * * Return: 0 on success , <0 on error */ static int mei_ioctl_client_notify_request(const struct file *file, u32 request) { … } /** * mei_ioctl_client_notify_get - wait for notification request * * @file: pointer to file structure * @notify_get: 0 - disable, 1 - enable * * Return: 0 on success , <0 on error */ static int mei_ioctl_client_notify_get(const struct file *file, u32 *notify_get) { … } /** * mei_ioctl - the IOCTL function * * @file: pointer to file structure * @cmd: ioctl command * @data: pointer to mei message structure * * Return: 0 on success , <0 on error */ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) { … } /** * mei_poll - the poll function * * @file: pointer to file structure * @wait: pointer to poll_table structure * * Return: poll mask */ static __poll_t mei_poll(struct file *file, poll_table *wait) { … } /** * mei_cl_is_write_queued - check if the client has pending writes. * * @cl: writing host client * * Return: true if client is writing, false otherwise. */ static bool mei_cl_is_write_queued(struct mei_cl *cl) { … } /** * mei_fsync - the fsync handler * * @fp: pointer to file structure * @start: unused * @end: unused * @datasync: unused * * Return: 0 on success, -ENODEV if client is not connected */ static int mei_fsync(struct file *fp, loff_t start, loff_t end, int datasync) { … } /** * mei_fasync - asynchronous io support * * @fd: file descriptor * @file: pointer to file structure * @band: band bitmap * * Return: negative on error, * 0 if it did no changes, * and positive a process was added or deleted */ static int mei_fasync(int fd, struct file *file, int band) { … } /** * trc_show - mei device trc attribute show method * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t trc_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(trc); /** * fw_status_show - mei device fw_status attribute show method * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t fw_status_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(fw_status); /** * hbm_ver_show - display HBM protocol version negotiated with FW * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t hbm_ver_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(hbm_ver); /** * hbm_ver_drv_show - display HBM protocol version advertised by driver * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t hbm_ver_drv_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(hbm_ver_drv); static ssize_t tx_queue_limit_show(struct device *device, struct device_attribute *attr, char *buf) { … } static ssize_t tx_queue_limit_store(struct device *device, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(tx_queue_limit); /** * fw_ver_show - display ME FW version * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(fw_ver); /** * dev_state_show - display device state * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t dev_state_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(dev_state); /** * mei_set_devstate: set to new device state and notify sysfs file. * * @dev: mei_device * @state: new device state */ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) { … } /** * kind_show - display device kind * * @device: device pointer * @attr: attribute pointer * @buf: char out buffer * * Return: number of the bytes printed into buf or error */ static ssize_t kind_show(struct device *device, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(kind); static struct attribute *mei_attrs[] = …; ATTRIBUTE_GROUPS(…); /* * file operations structure will be used for mei char device. */ static const struct file_operations mei_fops = …; /** * mei_minor_get - obtain next free device minor number * * @dev: device pointer * * Return: allocated minor, or -ENOSPC if no free minor left */ static int mei_minor_get(struct mei_device *dev) { … } /** * mei_minor_free - mark device minor number as free * * @dev: device pointer */ static void mei_minor_free(struct mei_device *dev) { … } int mei_register(struct mei_device *dev, struct device *parent) { … } EXPORT_SYMBOL_GPL(…); void mei_deregister(struct mei_device *dev) { … } EXPORT_SYMBOL_GPL(…); static int __init mei_init(void) { … } static void __exit mei_exit(void) { … } module_init(…) …; module_exit(mei_exit); MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;