18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ipmi_msghandler.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Incoming and outgoing message routing for an IPMI interface.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc.
88c2ecf20Sopenharmony_ci *         Corey Minyard <minyard@mvista.com>
98c2ecf20Sopenharmony_ci *         source@mvista.com
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Copyright 2002 MontaVista Software Inc.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "IPMI message handler: " fmt
158c2ecf20Sopenharmony_ci#define dev_fmt(fmt) pr_fmt(fmt)
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/poll.h>
208c2ecf20Sopenharmony_ci#include <linux/sched.h>
218c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
228c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
238c2ecf20Sopenharmony_ci#include <linux/mutex.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci#include <linux/ipmi.h>
268c2ecf20Sopenharmony_ci#include <linux/ipmi_smi.h>
278c2ecf20Sopenharmony_ci#include <linux/notifier.h>
288c2ecf20Sopenharmony_ci#include <linux/init.h>
298c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
308c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
318c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
328c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
338c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
348c2ecf20Sopenharmony_ci#include <linux/uuid.h>
358c2ecf20Sopenharmony_ci#include <linux/nospec.h>
368c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
378c2ecf20Sopenharmony_ci#include <linux/delay.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define IPMI_DRIVER_VERSION "39.2"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
428c2ecf20Sopenharmony_cistatic int ipmi_init_msghandler(void);
438c2ecf20Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t);
448c2ecf20Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf);
458c2ecf20Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf);
468c2ecf20Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf,
478c2ecf20Sopenharmony_ci			       struct ipmi_smi_msg *msg);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic bool initialized;
508c2ecf20Sopenharmony_cistatic bool drvregistered;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cienum ipmi_panic_event_op {
538c2ecf20Sopenharmony_ci	IPMI_SEND_PANIC_EVENT_NONE,
548c2ecf20Sopenharmony_ci	IPMI_SEND_PANIC_EVENT,
558c2ecf20Sopenharmony_ci	IPMI_SEND_PANIC_EVENT_STRING
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci#ifdef CONFIG_IPMI_PANIC_STRING
588c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING
598c2ecf20Sopenharmony_ci#elif defined(CONFIG_IPMI_PANIC_EVENT)
608c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT
618c2ecf20Sopenharmony_ci#else
628c2ecf20Sopenharmony_ci#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int panic_op_write_handler(const char *val,
688c2ecf20Sopenharmony_ci				  const struct kernel_param *kp)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	char valcp[16];
718c2ecf20Sopenharmony_ci	char *s;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	strncpy(valcp, val, 15);
748c2ecf20Sopenharmony_ci	valcp[15] = '\0';
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	s = strstrip(valcp);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (strcmp(s, "none") == 0)
798c2ecf20Sopenharmony_ci		ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_NONE;
808c2ecf20Sopenharmony_ci	else if (strcmp(s, "event") == 0)
818c2ecf20Sopenharmony_ci		ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT;
828c2ecf20Sopenharmony_ci	else if (strcmp(s, "string") == 0)
838c2ecf20Sopenharmony_ci		ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_STRING;
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		return -EINVAL;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int panic_op_read_handler(char *buffer, const struct kernel_param *kp)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	switch (ipmi_send_panic_event) {
938c2ecf20Sopenharmony_ci	case IPMI_SEND_PANIC_EVENT_NONE:
948c2ecf20Sopenharmony_ci		strcpy(buffer, "none\n");
958c2ecf20Sopenharmony_ci		break;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	case IPMI_SEND_PANIC_EVENT:
988c2ecf20Sopenharmony_ci		strcpy(buffer, "event\n");
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	case IPMI_SEND_PANIC_EVENT_STRING:
1028c2ecf20Sopenharmony_ci		strcpy(buffer, "string\n");
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	default:
1068c2ecf20Sopenharmony_ci		strcpy(buffer, "???\n");
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return strlen(buffer);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct kernel_param_ops panic_op_ops = {
1148c2ecf20Sopenharmony_ci	.set = panic_op_write_handler,
1158c2ecf20Sopenharmony_ci	.get = panic_op_read_handler
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_cimodule_param_cb(panic_op, &panic_op_ops, NULL, 0600);
1188c2ecf20Sopenharmony_ciMODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic.  Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events.");
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define MAX_EVENTS_IN_QUEUE	25
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* Remain in auto-maintenance mode for this amount of time (in ms). */
1248c2ecf20Sopenharmony_cistatic unsigned long maintenance_mode_timeout_ms = 30000;
1258c2ecf20Sopenharmony_cimodule_param(maintenance_mode_timeout_ms, ulong, 0644);
1268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maintenance_mode_timeout_ms,
1278c2ecf20Sopenharmony_ci		 "The time (milliseconds) after the last maintenance message that the connection stays in maintenance mode.");
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * Don't let a message sit in a queue forever, always time it with at lest
1318c2ecf20Sopenharmony_ci * the max message timer.  This is in milliseconds.
1328c2ecf20Sopenharmony_ci */
1338c2ecf20Sopenharmony_ci#define MAX_MSG_TIMEOUT		60000
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/*
1368c2ecf20Sopenharmony_ci * Timeout times below are in milliseconds, and are done off a 1
1378c2ecf20Sopenharmony_ci * second timer.  So setting the value to 1000 would mean anything
1388c2ecf20Sopenharmony_ci * between 0 and 1000ms.  So really the only reasonable minimum
1398c2ecf20Sopenharmony_ci * setting it 2000ms, which is between 1 and 2 seconds.
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/* The default timeout for message retries. */
1438c2ecf20Sopenharmony_cistatic unsigned long default_retry_ms = 2000;
1448c2ecf20Sopenharmony_cimodule_param(default_retry_ms, ulong, 0644);
1458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_retry_ms,
1468c2ecf20Sopenharmony_ci		 "The time (milliseconds) between retry sends");
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* The default timeout for maintenance mode message retries. */
1498c2ecf20Sopenharmony_cistatic unsigned long default_maintenance_retry_ms = 3000;
1508c2ecf20Sopenharmony_cimodule_param(default_maintenance_retry_ms, ulong, 0644);
1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_maintenance_retry_ms,
1528c2ecf20Sopenharmony_ci		 "The time (milliseconds) between retry sends in maintenance mode");
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* The default maximum number of retries */
1558c2ecf20Sopenharmony_cistatic unsigned int default_max_retries = 4;
1568c2ecf20Sopenharmony_cimodule_param(default_max_retries, uint, 0644);
1578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(default_max_retries,
1588c2ecf20Sopenharmony_ci		 "The time (milliseconds) between retry sends in maintenance mode");
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/* Call every ~1000 ms. */
1618c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT_TIME	1000
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* How many jiffies does it take to get to the timeout time. */
1648c2ecf20Sopenharmony_ci#define IPMI_TIMEOUT_JIFFIES	((IPMI_TIMEOUT_TIME * HZ) / 1000)
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * Request events from the queue every second (this is the number of
1688c2ecf20Sopenharmony_ci * IPMI_TIMEOUT_TIMES between event requests).  Hopefully, in the
1698c2ecf20Sopenharmony_ci * future, IPMI will add a way to know immediately if an event is in
1708c2ecf20Sopenharmony_ci * the queue and this silliness can go away.
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_ci#define IPMI_REQUEST_EV_TIME	(1000 / (IPMI_TIMEOUT_TIME))
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* How long should we cache dynamic device IDs? */
1758c2ecf20Sopenharmony_ci#define IPMI_DYN_DEV_ID_EXPIRY	(10 * HZ)
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/*
1788c2ecf20Sopenharmony_ci * The main "user" data structure.
1798c2ecf20Sopenharmony_ci */
1808c2ecf20Sopenharmony_cistruct ipmi_user {
1818c2ecf20Sopenharmony_ci	struct list_head link;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/*
1848c2ecf20Sopenharmony_ci	 * Set to NULL when the user is destroyed, a pointer to myself
1858c2ecf20Sopenharmony_ci	 * so srcu_dereference can be used on it.
1868c2ecf20Sopenharmony_ci	 */
1878c2ecf20Sopenharmony_ci	struct ipmi_user *self;
1888c2ecf20Sopenharmony_ci	struct srcu_struct release_barrier;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	struct kref refcount;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* The upper layer that handles receive messages. */
1938c2ecf20Sopenharmony_ci	const struct ipmi_user_hndl *handler;
1948c2ecf20Sopenharmony_ci	void             *handler_data;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* The interface this user is bound to. */
1978c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* Does this interface receive IPMI events? */
2008c2ecf20Sopenharmony_ci	bool gets_events;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* Free must run in process context for RCU cleanup. */
2038c2ecf20Sopenharmony_ci	struct work_struct remove_work;
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic struct workqueue_struct *remove_work_wq;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index)
2098c2ecf20Sopenharmony_ci	__acquires(user->release_barrier)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct ipmi_user *ruser;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	*index = srcu_read_lock(&user->release_barrier);
2148c2ecf20Sopenharmony_ci	ruser = srcu_dereference(user->self, &user->release_barrier);
2158c2ecf20Sopenharmony_ci	if (!ruser)
2168c2ecf20Sopenharmony_ci		srcu_read_unlock(&user->release_barrier, *index);
2178c2ecf20Sopenharmony_ci	return ruser;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void release_ipmi_user(struct ipmi_user *user, int index)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	srcu_read_unlock(&user->release_barrier, index);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistruct cmd_rcvr {
2268c2ecf20Sopenharmony_ci	struct list_head link;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	struct ipmi_user *user;
2298c2ecf20Sopenharmony_ci	unsigned char netfn;
2308c2ecf20Sopenharmony_ci	unsigned char cmd;
2318c2ecf20Sopenharmony_ci	unsigned int  chans;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/*
2348c2ecf20Sopenharmony_ci	 * This is used to form a linked lised during mass deletion.
2358c2ecf20Sopenharmony_ci	 * Since this is in an RCU list, we cannot use the link above
2368c2ecf20Sopenharmony_ci	 * or change any data until the RCU period completes.  So we
2378c2ecf20Sopenharmony_ci	 * use this next variable during mass deletion so we can have
2388c2ecf20Sopenharmony_ci	 * a list and don't have to wait and restart the search on
2398c2ecf20Sopenharmony_ci	 * every individual deletion of a command.
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci	struct cmd_rcvr *next;
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistruct seq_table {
2458c2ecf20Sopenharmony_ci	unsigned int         inuse : 1;
2468c2ecf20Sopenharmony_ci	unsigned int         broadcast : 1;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	unsigned long        timeout;
2498c2ecf20Sopenharmony_ci	unsigned long        orig_timeout;
2508c2ecf20Sopenharmony_ci	unsigned int         retries_left;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/*
2538c2ecf20Sopenharmony_ci	 * To verify on an incoming send message response that this is
2548c2ecf20Sopenharmony_ci	 * the message that the response is for, we keep a sequence id
2558c2ecf20Sopenharmony_ci	 * and increment it every time we send a message.
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci	long                 seqid;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/*
2608c2ecf20Sopenharmony_ci	 * This is held so we can properly respond to the message on a
2618c2ecf20Sopenharmony_ci	 * timeout, and it is used to hold the temporary data for
2628c2ecf20Sopenharmony_ci	 * retransmission, too.
2638c2ecf20Sopenharmony_ci	 */
2648c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *recv_msg;
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci * Store the information in a msgid (long) to allow us to find a
2698c2ecf20Sopenharmony_ci * sequence table entry from the msgid.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ci#define STORE_SEQ_IN_MSGID(seq, seqid) \
2728c2ecf20Sopenharmony_ci	((((seq) & 0x3f) << 26) | ((seqid) & 0x3ffffff))
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \
2758c2ecf20Sopenharmony_ci	do {								\
2768c2ecf20Sopenharmony_ci		seq = (((msgid) >> 26) & 0x3f);				\
2778c2ecf20Sopenharmony_ci		seqid = ((msgid) & 0x3ffffff);				\
2788c2ecf20Sopenharmony_ci	} while (0)
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci#define IPMI_MAX_CHANNELS       16
2838c2ecf20Sopenharmony_cistruct ipmi_channel {
2848c2ecf20Sopenharmony_ci	unsigned char medium;
2858c2ecf20Sopenharmony_ci	unsigned char protocol;
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistruct ipmi_channel_set {
2898c2ecf20Sopenharmony_ci	struct ipmi_channel c[IPMI_MAX_CHANNELS];
2908c2ecf20Sopenharmony_ci};
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistruct ipmi_my_addrinfo {
2938c2ecf20Sopenharmony_ci	/*
2948c2ecf20Sopenharmony_ci	 * My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,
2958c2ecf20Sopenharmony_ci	 * but may be changed by the user.
2968c2ecf20Sopenharmony_ci	 */
2978c2ecf20Sopenharmony_ci	unsigned char address;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/*
3008c2ecf20Sopenharmony_ci	 * My LUN.  This should generally stay the SMS LUN, but just in
3018c2ecf20Sopenharmony_ci	 * case...
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	unsigned char lun;
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/*
3078c2ecf20Sopenharmony_ci * Note that the product id, manufacturer id, guid, and device id are
3088c2ecf20Sopenharmony_ci * immutable in this structure, so dyn_mutex is not required for
3098c2ecf20Sopenharmony_ci * accessing those.  If those change on a BMC, a new BMC is allocated.
3108c2ecf20Sopenharmony_ci */
3118c2ecf20Sopenharmony_cistruct bmc_device {
3128c2ecf20Sopenharmony_ci	struct platform_device pdev;
3138c2ecf20Sopenharmony_ci	struct list_head       intfs; /* Interfaces on this BMC. */
3148c2ecf20Sopenharmony_ci	struct ipmi_device_id  id;
3158c2ecf20Sopenharmony_ci	struct ipmi_device_id  fetch_id;
3168c2ecf20Sopenharmony_ci	int                    dyn_id_set;
3178c2ecf20Sopenharmony_ci	unsigned long          dyn_id_expiry;
3188c2ecf20Sopenharmony_ci	struct mutex           dyn_mutex; /* Protects id, intfs, & dyn* */
3198c2ecf20Sopenharmony_ci	guid_t                 guid;
3208c2ecf20Sopenharmony_ci	guid_t                 fetch_guid;
3218c2ecf20Sopenharmony_ci	int                    dyn_guid_set;
3228c2ecf20Sopenharmony_ci	struct kref	       usecount;
3238c2ecf20Sopenharmony_ci	struct work_struct     remove_work;
3248c2ecf20Sopenharmony_ci	unsigned char	       cc; /* completion code */
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ci#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
3298c2ecf20Sopenharmony_ci			     struct ipmi_device_id *id,
3308c2ecf20Sopenharmony_ci			     bool *guid_set, guid_t *guid);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/*
3338c2ecf20Sopenharmony_ci * Various statistics for IPMI, these index stats[] in the ipmi_smi
3348c2ecf20Sopenharmony_ci * structure.
3358c2ecf20Sopenharmony_ci */
3368c2ecf20Sopenharmony_cienum ipmi_stat_indexes {
3378c2ecf20Sopenharmony_ci	/* Commands we got from the user that were invalid. */
3388c2ecf20Sopenharmony_ci	IPMI_STAT_sent_invalid_commands = 0,
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Commands we sent to the MC. */
3418c2ecf20Sopenharmony_ci	IPMI_STAT_sent_local_commands,
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Responses from the MC that were delivered to a user. */
3448c2ecf20Sopenharmony_ci	IPMI_STAT_handled_local_responses,
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* Responses from the MC that were not delivered to a user. */
3478c2ecf20Sopenharmony_ci	IPMI_STAT_unhandled_local_responses,
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* Commands we sent out to the IPMB bus. */
3508c2ecf20Sopenharmony_ci	IPMI_STAT_sent_ipmb_commands,
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* Commands sent on the IPMB that had errors on the SEND CMD */
3538c2ecf20Sopenharmony_ci	IPMI_STAT_sent_ipmb_command_errs,
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* Each retransmit increments this count. */
3568c2ecf20Sopenharmony_ci	IPMI_STAT_retransmitted_ipmb_commands,
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/*
3598c2ecf20Sopenharmony_ci	 * When a message times out (runs out of retransmits) this is
3608c2ecf20Sopenharmony_ci	 * incremented.
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	IPMI_STAT_timed_out_ipmb_commands,
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/*
3658c2ecf20Sopenharmony_ci	 * This is like above, but for broadcasts.  Broadcasts are
3668c2ecf20Sopenharmony_ci	 * *not* included in the above count (they are expected to
3678c2ecf20Sopenharmony_ci	 * time out).
3688c2ecf20Sopenharmony_ci	 */
3698c2ecf20Sopenharmony_ci	IPMI_STAT_timed_out_ipmb_broadcasts,
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* Responses I have sent to the IPMB bus. */
3728c2ecf20Sopenharmony_ci	IPMI_STAT_sent_ipmb_responses,
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* The response was delivered to the user. */
3758c2ecf20Sopenharmony_ci	IPMI_STAT_handled_ipmb_responses,
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	/* The response had invalid data in it. */
3788c2ecf20Sopenharmony_ci	IPMI_STAT_invalid_ipmb_responses,
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* The response didn't have anyone waiting for it. */
3818c2ecf20Sopenharmony_ci	IPMI_STAT_unhandled_ipmb_responses,
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/* Commands we sent out to the IPMB bus. */
3848c2ecf20Sopenharmony_ci	IPMI_STAT_sent_lan_commands,
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Commands sent on the IPMB that had errors on the SEND CMD */
3878c2ecf20Sopenharmony_ci	IPMI_STAT_sent_lan_command_errs,
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Each retransmit increments this count. */
3908c2ecf20Sopenharmony_ci	IPMI_STAT_retransmitted_lan_commands,
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/*
3938c2ecf20Sopenharmony_ci	 * When a message times out (runs out of retransmits) this is
3948c2ecf20Sopenharmony_ci	 * incremented.
3958c2ecf20Sopenharmony_ci	 */
3968c2ecf20Sopenharmony_ci	IPMI_STAT_timed_out_lan_commands,
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* Responses I have sent to the IPMB bus. */
3998c2ecf20Sopenharmony_ci	IPMI_STAT_sent_lan_responses,
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* The response was delivered to the user. */
4028c2ecf20Sopenharmony_ci	IPMI_STAT_handled_lan_responses,
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* The response had invalid data in it. */
4058c2ecf20Sopenharmony_ci	IPMI_STAT_invalid_lan_responses,
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	/* The response didn't have anyone waiting for it. */
4088c2ecf20Sopenharmony_ci	IPMI_STAT_unhandled_lan_responses,
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* The command was delivered to the user. */
4118c2ecf20Sopenharmony_ci	IPMI_STAT_handled_commands,
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/* The command had invalid data in it. */
4148c2ecf20Sopenharmony_ci	IPMI_STAT_invalid_commands,
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* The command didn't have anyone waiting for it. */
4178c2ecf20Sopenharmony_ci	IPMI_STAT_unhandled_commands,
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* Invalid data in an event. */
4208c2ecf20Sopenharmony_ci	IPMI_STAT_invalid_events,
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* Events that were received with the proper format. */
4238c2ecf20Sopenharmony_ci	IPMI_STAT_events,
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Retransmissions on IPMB that failed. */
4268c2ecf20Sopenharmony_ci	IPMI_STAT_dropped_rexmit_ipmb_commands,
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* Retransmissions on LAN that failed. */
4298c2ecf20Sopenharmony_ci	IPMI_STAT_dropped_rexmit_lan_commands,
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* This *must* remain last, add new values above this. */
4328c2ecf20Sopenharmony_ci	IPMI_NUM_STATS
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci#define IPMI_IPMB_NUM_SEQ	64
4378c2ecf20Sopenharmony_cistruct ipmi_smi {
4388c2ecf20Sopenharmony_ci	struct module *owner;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* What interface number are we? */
4418c2ecf20Sopenharmony_ci	int intf_num;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	struct kref refcount;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* Set when the interface is being unregistered. */
4468c2ecf20Sopenharmony_ci	bool in_shutdown;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* Used for a list of interfaces. */
4498c2ecf20Sopenharmony_ci	struct list_head link;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/*
4528c2ecf20Sopenharmony_ci	 * The list of upper layers that are using me.  seq_lock write
4538c2ecf20Sopenharmony_ci	 * protects this.  Read protection is with srcu.
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci	struct list_head users;
4568c2ecf20Sopenharmony_ci	struct srcu_struct users_srcu;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* Used for wake ups at startup. */
4598c2ecf20Sopenharmony_ci	wait_queue_head_t waitq;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/*
4628c2ecf20Sopenharmony_ci	 * Prevents the interface from being unregistered when the
4638c2ecf20Sopenharmony_ci	 * interface is used by being looked up through the BMC
4648c2ecf20Sopenharmony_ci	 * structure.
4658c2ecf20Sopenharmony_ci	 */
4668c2ecf20Sopenharmony_ci	struct mutex bmc_reg_mutex;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	struct bmc_device tmp_bmc;
4698c2ecf20Sopenharmony_ci	struct bmc_device *bmc;
4708c2ecf20Sopenharmony_ci	bool bmc_registered;
4718c2ecf20Sopenharmony_ci	struct list_head bmc_link;
4728c2ecf20Sopenharmony_ci	char *my_dev_name;
4738c2ecf20Sopenharmony_ci	bool in_bmc_register;  /* Handle recursive situations.  Yuck. */
4748c2ecf20Sopenharmony_ci	struct work_struct bmc_reg_work;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	const struct ipmi_smi_handlers *handlers;
4778c2ecf20Sopenharmony_ci	void                     *send_info;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Driver-model device for the system interface. */
4808c2ecf20Sopenharmony_ci	struct device          *si_dev;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * A table of sequence numbers for this interface.  We use the
4848c2ecf20Sopenharmony_ci	 * sequence numbers for IPMB messages that go out of the
4858c2ecf20Sopenharmony_ci	 * interface to match them up with their responses.  A routine
4868c2ecf20Sopenharmony_ci	 * is called periodically to time the items in this list.
4878c2ecf20Sopenharmony_ci	 */
4888c2ecf20Sopenharmony_ci	spinlock_t       seq_lock;
4898c2ecf20Sopenharmony_ci	struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
4908c2ecf20Sopenharmony_ci	int curr_seq;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	/*
4938c2ecf20Sopenharmony_ci	 * Messages queued for delivery.  If delivery fails (out of memory
4948c2ecf20Sopenharmony_ci	 * for instance), They will stay in here to be processed later in a
4958c2ecf20Sopenharmony_ci	 * periodic timer interrupt.  The tasklet is for handling received
4968c2ecf20Sopenharmony_ci	 * messages directly from the handler.
4978c2ecf20Sopenharmony_ci	 */
4988c2ecf20Sopenharmony_ci	spinlock_t       waiting_rcv_msgs_lock;
4998c2ecf20Sopenharmony_ci	struct list_head waiting_rcv_msgs;
5008c2ecf20Sopenharmony_ci	atomic_t	 watchdog_pretimeouts_to_deliver;
5018c2ecf20Sopenharmony_ci	struct tasklet_struct recv_tasklet;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	spinlock_t             xmit_msgs_lock;
5048c2ecf20Sopenharmony_ci	struct list_head       xmit_msgs;
5058c2ecf20Sopenharmony_ci	struct ipmi_smi_msg    *curr_msg;
5068c2ecf20Sopenharmony_ci	struct list_head       hp_xmit_msgs;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	/*
5098c2ecf20Sopenharmony_ci	 * The list of command receivers that are registered for commands
5108c2ecf20Sopenharmony_ci	 * on this interface.
5118c2ecf20Sopenharmony_ci	 */
5128c2ecf20Sopenharmony_ci	struct mutex     cmd_rcvrs_mutex;
5138c2ecf20Sopenharmony_ci	struct list_head cmd_rcvrs;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/*
5168c2ecf20Sopenharmony_ci	 * Events that were queues because no one was there to receive
5178c2ecf20Sopenharmony_ci	 * them.
5188c2ecf20Sopenharmony_ci	 */
5198c2ecf20Sopenharmony_ci	spinlock_t       events_lock; /* For dealing with event stuff. */
5208c2ecf20Sopenharmony_ci	struct list_head waiting_events;
5218c2ecf20Sopenharmony_ci	unsigned int     waiting_events_count; /* How many events in queue? */
5228c2ecf20Sopenharmony_ci	char             delivering_events;
5238c2ecf20Sopenharmony_ci	char             event_msg_printed;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	/* How many users are waiting for events? */
5268c2ecf20Sopenharmony_ci	atomic_t         event_waiters;
5278c2ecf20Sopenharmony_ci	unsigned int     ticks_to_req_ev;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	spinlock_t       watch_lock; /* For dealing with watch stuff below. */
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* How many users are waiting for commands? */
5328c2ecf20Sopenharmony_ci	unsigned int     command_waiters;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* How many users are waiting for watchdogs? */
5358c2ecf20Sopenharmony_ci	unsigned int     watchdog_waiters;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* How many users are waiting for message responses? */
5388c2ecf20Sopenharmony_ci	unsigned int     response_waiters;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/*
5418c2ecf20Sopenharmony_ci	 * Tells what the lower layer has last been asked to watch for,
5428c2ecf20Sopenharmony_ci	 * messages and/or watchdogs.  Protected by watch_lock.
5438c2ecf20Sopenharmony_ci	 */
5448c2ecf20Sopenharmony_ci	unsigned int     last_watch_mask;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/*
5478c2ecf20Sopenharmony_ci	 * The event receiver for my BMC, only really used at panic
5488c2ecf20Sopenharmony_ci	 * shutdown as a place to store this.
5498c2ecf20Sopenharmony_ci	 */
5508c2ecf20Sopenharmony_ci	unsigned char event_receiver;
5518c2ecf20Sopenharmony_ci	unsigned char event_receiver_lun;
5528c2ecf20Sopenharmony_ci	unsigned char local_sel_device;
5538c2ecf20Sopenharmony_ci	unsigned char local_event_generator;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* For handling of maintenance mode. */
5568c2ecf20Sopenharmony_ci	int maintenance_mode;
5578c2ecf20Sopenharmony_ci	bool maintenance_mode_enable;
5588c2ecf20Sopenharmony_ci	int auto_maintenance_timeout;
5598c2ecf20Sopenharmony_ci	spinlock_t maintenance_mode_lock; /* Used in a timer... */
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/*
5628c2ecf20Sopenharmony_ci	 * If we are doing maintenance on something on IPMB, extend
5638c2ecf20Sopenharmony_ci	 * the timeout time to avoid timeouts writing firmware and
5648c2ecf20Sopenharmony_ci	 * such.
5658c2ecf20Sopenharmony_ci	 */
5668c2ecf20Sopenharmony_ci	int ipmb_maintenance_mode_timeout;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/*
5698c2ecf20Sopenharmony_ci	 * A cheap hack, if this is non-null and a message to an
5708c2ecf20Sopenharmony_ci	 * interface comes in with a NULL user, call this routine with
5718c2ecf20Sopenharmony_ci	 * it.  Note that the message will still be freed by the
5728c2ecf20Sopenharmony_ci	 * caller.  This only works on the system interface.
5738c2ecf20Sopenharmony_ci	 *
5748c2ecf20Sopenharmony_ci	 * Protected by bmc_reg_mutex.
5758c2ecf20Sopenharmony_ci	 */
5768c2ecf20Sopenharmony_ci	void (*null_user_handler)(struct ipmi_smi *intf,
5778c2ecf20Sopenharmony_ci				  struct ipmi_recv_msg *msg);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	/*
5808c2ecf20Sopenharmony_ci	 * When we are scanning the channels for an SMI, this will
5818c2ecf20Sopenharmony_ci	 * tell which channel we are scanning.
5828c2ecf20Sopenharmony_ci	 */
5838c2ecf20Sopenharmony_ci	int curr_channel;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Channel information */
5868c2ecf20Sopenharmony_ci	struct ipmi_channel_set *channel_list;
5878c2ecf20Sopenharmony_ci	unsigned int curr_working_cset; /* First index into the following. */
5888c2ecf20Sopenharmony_ci	struct ipmi_channel_set wchannels[2];
5898c2ecf20Sopenharmony_ci	struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
5908c2ecf20Sopenharmony_ci	bool channels_ready;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	atomic_t stats[IPMI_NUM_STATS];
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	/*
5958c2ecf20Sopenharmony_ci	 * run_to_completion duplicate of smb_info, smi_info
5968c2ecf20Sopenharmony_ci	 * and ipmi_serial_info structures. Used to decrease numbers of
5978c2ecf20Sopenharmony_ci	 * parameters passed by "low" level IPMI code.
5988c2ecf20Sopenharmony_ci	 */
5998c2ecf20Sopenharmony_ci	int run_to_completion;
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf);
6048c2ecf20Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf);
6058c2ecf20Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf,
6068c2ecf20Sopenharmony_ci			       struct ipmi_device_id *id,
6078c2ecf20Sopenharmony_ci			       bool guid_set, guid_t *guid, int intf_num);
6088c2ecf20Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/**
6128c2ecf20Sopenharmony_ci * The driver model view of the IPMI messaging driver.
6138c2ecf20Sopenharmony_ci */
6148c2ecf20Sopenharmony_cistatic struct platform_driver ipmidriver = {
6158c2ecf20Sopenharmony_ci	.driver = {
6168c2ecf20Sopenharmony_ci		.name = "ipmi",
6178c2ecf20Sopenharmony_ci		.bus = &platform_bus_type
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci};
6208c2ecf20Sopenharmony_ci/*
6218c2ecf20Sopenharmony_ci * This mutex keeps us from adding the same BMC twice.
6228c2ecf20Sopenharmony_ci */
6238c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipmidriver_mutex);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic LIST_HEAD(ipmi_interfaces);
6268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ipmi_interfaces_mutex);
6278c2ecf20Sopenharmony_ci#define ipmi_interfaces_mutex_held() \
6288c2ecf20Sopenharmony_ci	lockdep_is_held(&ipmi_interfaces_mutex)
6298c2ecf20Sopenharmony_cistatic struct srcu_struct ipmi_interfaces_srcu;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/*
6328c2ecf20Sopenharmony_ci * List of watchers that want to know when smi's are added and deleted.
6338c2ecf20Sopenharmony_ci */
6348c2ecf20Sopenharmony_cistatic LIST_HEAD(smi_watchers);
6358c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smi_watchers_mutex);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci#define ipmi_inc_stat(intf, stat) \
6388c2ecf20Sopenharmony_ci	atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat])
6398c2ecf20Sopenharmony_ci#define ipmi_get_stat(intf, stat) \
6408c2ecf20Sopenharmony_ci	((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat]))
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic const char * const addr_src_to_str[] = {
6438c2ecf20Sopenharmony_ci	"invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI",
6448c2ecf20Sopenharmony_ci	"device-tree", "platform"
6458c2ecf20Sopenharmony_ci};
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ciconst char *ipmi_addr_src_to_str(enum ipmi_addr_src src)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	if (src >= SI_LAST)
6508c2ecf20Sopenharmony_ci		src = 0; /* Invalid */
6518c2ecf20Sopenharmony_ci	return addr_src_to_str[src];
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_src_to_str);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic int is_lan_addr(struct ipmi_addr *addr)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	return addr->addr_type == IPMI_LAN_ADDR_TYPE;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int is_ipmb_addr(struct ipmi_addr *addr)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	return addr->addr_type == IPMI_IPMB_ADDR_TYPE;
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_cistatic int is_ipmb_bcast_addr(struct ipmi_addr *addr)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic void free_recv_msg_list(struct list_head *q)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *msg, *msg2;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	list_for_each_entry_safe(msg, msg2, q, link) {
6758c2ecf20Sopenharmony_ci		list_del(&msg->link);
6768c2ecf20Sopenharmony_ci		ipmi_free_recv_msg(msg);
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic void free_smi_msg_list(struct list_head *q)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg, *msg2;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(msg, msg2, q, link) {
6858c2ecf20Sopenharmony_ci		list_del(&msg->link);
6868c2ecf20Sopenharmony_ci		ipmi_free_smi_msg(msg);
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_cistatic void clean_up_interface_data(struct ipmi_smi *intf)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	int              i;
6938c2ecf20Sopenharmony_ci	struct cmd_rcvr  *rcvr, *rcvr2;
6948c2ecf20Sopenharmony_ci	struct list_head list;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	tasklet_kill(&intf->recv_tasklet);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	free_smi_msg_list(&intf->waiting_rcv_msgs);
6998c2ecf20Sopenharmony_ci	free_recv_msg_list(&intf->waiting_events);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	/*
7028c2ecf20Sopenharmony_ci	 * Wholesale remove all the entries from the list in the
7038c2ecf20Sopenharmony_ci	 * interface and wait for RCU to know that none are in use.
7048c2ecf20Sopenharmony_ci	 */
7058c2ecf20Sopenharmony_ci	mutex_lock(&intf->cmd_rcvrs_mutex);
7068c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&list);
7078c2ecf20Sopenharmony_ci	list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
7088c2ecf20Sopenharmony_ci	mutex_unlock(&intf->cmd_rcvrs_mutex);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	list_for_each_entry_safe(rcvr, rcvr2, &list, link)
7118c2ecf20Sopenharmony_ci		kfree(rcvr);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
7148c2ecf20Sopenharmony_ci		if ((intf->seq_table[i].inuse)
7158c2ecf20Sopenharmony_ci					&& (intf->seq_table[i].recv_msg))
7168c2ecf20Sopenharmony_ci			ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_cistatic void intf_free(struct kref *ref)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	clean_up_interface_data(intf);
7258c2ecf20Sopenharmony_ci	kfree(intf);
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_cistruct watcher_entry {
7298c2ecf20Sopenharmony_ci	int              intf_num;
7308c2ecf20Sopenharmony_ci	struct ipmi_smi  *intf;
7318c2ecf20Sopenharmony_ci	struct list_head link;
7328c2ecf20Sopenharmony_ci};
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ciint ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
7378c2ecf20Sopenharmony_ci	int index, rv;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/*
7408c2ecf20Sopenharmony_ci	 * Make sure the driver is actually initialized, this handles
7418c2ecf20Sopenharmony_ci	 * problems with initialization order.
7428c2ecf20Sopenharmony_ci	 */
7438c2ecf20Sopenharmony_ci	rv = ipmi_init_msghandler();
7448c2ecf20Sopenharmony_ci	if (rv)
7458c2ecf20Sopenharmony_ci		return rv;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	mutex_lock(&smi_watchers_mutex);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	list_add(&watcher->link, &smi_watchers);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	index = srcu_read_lock(&ipmi_interfaces_srcu);
7528c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(intf, &ipmi_interfaces, link,
7538c2ecf20Sopenharmony_ci			lockdep_is_held(&smi_watchers_mutex)) {
7548c2ecf20Sopenharmony_ci		int intf_num = READ_ONCE(intf->intf_num);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		if (intf_num == -1)
7578c2ecf20Sopenharmony_ci			continue;
7588c2ecf20Sopenharmony_ci		watcher->new_smi(intf_num, intf->si_dev);
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	mutex_unlock(&smi_watchers_mutex);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	return 0;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_register);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ciint ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	mutex_lock(&smi_watchers_mutex);
7718c2ecf20Sopenharmony_ci	list_del(&watcher->link);
7728c2ecf20Sopenharmony_ci	mutex_unlock(&smi_watchers_mutex);
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watcher_unregister);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci/*
7788c2ecf20Sopenharmony_ci * Must be called with smi_watchers_mutex held.
7798c2ecf20Sopenharmony_ci */
7808c2ecf20Sopenharmony_cistatic void
7818c2ecf20Sopenharmony_cicall_smi_watchers(int i, struct device *dev)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct ipmi_smi_watcher *w;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	mutex_lock(&smi_watchers_mutex);
7868c2ecf20Sopenharmony_ci	list_for_each_entry(w, &smi_watchers, link) {
7878c2ecf20Sopenharmony_ci		if (try_module_get(w->owner)) {
7888c2ecf20Sopenharmony_ci			w->new_smi(i, dev);
7898c2ecf20Sopenharmony_ci			module_put(w->owner);
7908c2ecf20Sopenharmony_ci		}
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci	mutex_unlock(&smi_watchers_mutex);
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_cistatic int
7968c2ecf20Sopenharmony_ciipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
7978c2ecf20Sopenharmony_ci{
7988c2ecf20Sopenharmony_ci	if (addr1->addr_type != addr2->addr_type)
7998c2ecf20Sopenharmony_ci		return 0;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	if (addr1->channel != addr2->channel)
8028c2ecf20Sopenharmony_ci		return 0;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
8058c2ecf20Sopenharmony_ci		struct ipmi_system_interface_addr *smi_addr1
8068c2ecf20Sopenharmony_ci		    = (struct ipmi_system_interface_addr *) addr1;
8078c2ecf20Sopenharmony_ci		struct ipmi_system_interface_addr *smi_addr2
8088c2ecf20Sopenharmony_ci		    = (struct ipmi_system_interface_addr *) addr2;
8098c2ecf20Sopenharmony_ci		return (smi_addr1->lun == smi_addr2->lun);
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) {
8138c2ecf20Sopenharmony_ci		struct ipmi_ipmb_addr *ipmb_addr1
8148c2ecf20Sopenharmony_ci		    = (struct ipmi_ipmb_addr *) addr1;
8158c2ecf20Sopenharmony_ci		struct ipmi_ipmb_addr *ipmb_addr2
8168c2ecf20Sopenharmony_ci		    = (struct ipmi_ipmb_addr *) addr2;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr)
8198c2ecf20Sopenharmony_ci			&& (ipmb_addr1->lun == ipmb_addr2->lun));
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (is_lan_addr(addr1)) {
8238c2ecf20Sopenharmony_ci		struct ipmi_lan_addr *lan_addr1
8248c2ecf20Sopenharmony_ci			= (struct ipmi_lan_addr *) addr1;
8258c2ecf20Sopenharmony_ci		struct ipmi_lan_addr *lan_addr2
8268c2ecf20Sopenharmony_ci		    = (struct ipmi_lan_addr *) addr2;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci		return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID)
8298c2ecf20Sopenharmony_ci			&& (lan_addr1->local_SWID == lan_addr2->local_SWID)
8308c2ecf20Sopenharmony_ci			&& (lan_addr1->session_handle
8318c2ecf20Sopenharmony_ci			    == lan_addr2->session_handle)
8328c2ecf20Sopenharmony_ci			&& (lan_addr1->lun == lan_addr2->lun));
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	return 1;
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ciint ipmi_validate_addr(struct ipmi_addr *addr, int len)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	if (len < sizeof(struct ipmi_system_interface_addr))
8418c2ecf20Sopenharmony_ci		return -EINVAL;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
8448c2ecf20Sopenharmony_ci		if (addr->channel != IPMI_BMC_CHANNEL)
8458c2ecf20Sopenharmony_ci			return -EINVAL;
8468c2ecf20Sopenharmony_ci		return 0;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if ((addr->channel == IPMI_BMC_CHANNEL)
8508c2ecf20Sopenharmony_ci	    || (addr->channel >= IPMI_MAX_CHANNELS)
8518c2ecf20Sopenharmony_ci	    || (addr->channel < 0))
8528c2ecf20Sopenharmony_ci		return -EINVAL;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) {
8558c2ecf20Sopenharmony_ci		if (len < sizeof(struct ipmi_ipmb_addr))
8568c2ecf20Sopenharmony_ci			return -EINVAL;
8578c2ecf20Sopenharmony_ci		return 0;
8588c2ecf20Sopenharmony_ci	}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (is_lan_addr(addr)) {
8618c2ecf20Sopenharmony_ci		if (len < sizeof(struct ipmi_lan_addr))
8628c2ecf20Sopenharmony_ci			return -EINVAL;
8638c2ecf20Sopenharmony_ci		return 0;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return -EINVAL;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_validate_addr);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ciunsigned int ipmi_addr_length(int addr_type)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
8738c2ecf20Sopenharmony_ci		return sizeof(struct ipmi_system_interface_addr);
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	if ((addr_type == IPMI_IPMB_ADDR_TYPE)
8768c2ecf20Sopenharmony_ci			|| (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
8778c2ecf20Sopenharmony_ci		return sizeof(struct ipmi_ipmb_addr);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (addr_type == IPMI_LAN_ADDR_TYPE)
8808c2ecf20Sopenharmony_ci		return sizeof(struct ipmi_lan_addr);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	return 0;
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_addr_length);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	int rv = 0;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (!msg->user) {
8918c2ecf20Sopenharmony_ci		/* Special handling for NULL users. */
8928c2ecf20Sopenharmony_ci		if (intf->null_user_handler) {
8938c2ecf20Sopenharmony_ci			intf->null_user_handler(intf, msg);
8948c2ecf20Sopenharmony_ci		} else {
8958c2ecf20Sopenharmony_ci			/* No handler, so give up. */
8968c2ecf20Sopenharmony_ci			rv = -EINVAL;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci		ipmi_free_recv_msg(msg);
8998c2ecf20Sopenharmony_ci	} else if (oops_in_progress) {
9008c2ecf20Sopenharmony_ci		/*
9018c2ecf20Sopenharmony_ci		 * If we are running in the panic context, calling the
9028c2ecf20Sopenharmony_ci		 * receive handler doesn't much meaning and has a deadlock
9038c2ecf20Sopenharmony_ci		 * risk.  At this moment, simply skip it in that case.
9048c2ecf20Sopenharmony_ci		 */
9058c2ecf20Sopenharmony_ci		ipmi_free_recv_msg(msg);
9068c2ecf20Sopenharmony_ci	} else {
9078c2ecf20Sopenharmony_ci		int index;
9088c2ecf20Sopenharmony_ci		struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		if (user) {
9118c2ecf20Sopenharmony_ci			user->handler->ipmi_recv_hndl(msg, user->handler_data);
9128c2ecf20Sopenharmony_ci			release_ipmi_user(user, index);
9138c2ecf20Sopenharmony_ci		} else {
9148c2ecf20Sopenharmony_ci			/* User went away, give up. */
9158c2ecf20Sopenharmony_ci			ipmi_free_recv_msg(msg);
9168c2ecf20Sopenharmony_ci			rv = -EINVAL;
9178c2ecf20Sopenharmony_ci		}
9188c2ecf20Sopenharmony_ci	}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	return rv;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic void deliver_local_response(struct ipmi_smi *intf,
9248c2ecf20Sopenharmony_ci				   struct ipmi_recv_msg *msg)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	if (deliver_response(intf, msg))
9278c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_local_responses);
9288c2ecf20Sopenharmony_ci	else
9298c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, handled_local_responses);
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic void deliver_err_response(struct ipmi_smi *intf,
9338c2ecf20Sopenharmony_ci				 struct ipmi_recv_msg *msg, int err)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
9368c2ecf20Sopenharmony_ci	msg->msg_data[0] = err;
9378c2ecf20Sopenharmony_ci	msg->msg.netfn |= 1; /* Convert to a response. */
9388c2ecf20Sopenharmony_ci	msg->msg.data_len = 1;
9398c2ecf20Sopenharmony_ci	msg->msg.data = msg->msg_data;
9408c2ecf20Sopenharmony_ci	deliver_local_response(intf, msg);
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic void smi_add_watch(struct ipmi_smi *intf, unsigned int flags)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	unsigned long iflags;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	if (!intf->handlers->set_need_watch)
9488c2ecf20Sopenharmony_ci		return;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->watch_lock, iflags);
9518c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
9528c2ecf20Sopenharmony_ci		intf->response_waiters++;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
9558c2ecf20Sopenharmony_ci		intf->watchdog_waiters++;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
9588c2ecf20Sopenharmony_ci		intf->command_waiters++;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	if ((intf->last_watch_mask & flags) != flags) {
9618c2ecf20Sopenharmony_ci		intf->last_watch_mask |= flags;
9628c2ecf20Sopenharmony_ci		intf->handlers->set_need_watch(intf->send_info,
9638c2ecf20Sopenharmony_ci					       intf->last_watch_mask);
9648c2ecf20Sopenharmony_ci	}
9658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->watch_lock, iflags);
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	unsigned long iflags;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	if (!intf->handlers->set_need_watch)
9738c2ecf20Sopenharmony_ci		return;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->watch_lock, iflags);
9768c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
9778c2ecf20Sopenharmony_ci		intf->response_waiters--;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
9808c2ecf20Sopenharmony_ci		intf->watchdog_waiters--;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
9838c2ecf20Sopenharmony_ci		intf->command_waiters--;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	flags = 0;
9868c2ecf20Sopenharmony_ci	if (intf->response_waiters)
9878c2ecf20Sopenharmony_ci		flags |= IPMI_WATCH_MASK_CHECK_MESSAGES;
9888c2ecf20Sopenharmony_ci	if (intf->watchdog_waiters)
9898c2ecf20Sopenharmony_ci		flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG;
9908c2ecf20Sopenharmony_ci	if (intf->command_waiters)
9918c2ecf20Sopenharmony_ci		flags |= IPMI_WATCH_MASK_CHECK_COMMANDS;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (intf->last_watch_mask != flags) {
9948c2ecf20Sopenharmony_ci		intf->last_watch_mask = flags;
9958c2ecf20Sopenharmony_ci		intf->handlers->set_need_watch(intf->send_info,
9968c2ecf20Sopenharmony_ci					       intf->last_watch_mask);
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->watch_lock, iflags);
9998c2ecf20Sopenharmony_ci}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci/*
10028c2ecf20Sopenharmony_ci * Find the next sequence number not being used and add the given
10038c2ecf20Sopenharmony_ci * message with the given timeout to the sequence table.  This must be
10048c2ecf20Sopenharmony_ci * called with the interface's seq_lock held.
10058c2ecf20Sopenharmony_ci */
10068c2ecf20Sopenharmony_cistatic int intf_next_seq(struct ipmi_smi      *intf,
10078c2ecf20Sopenharmony_ci			 struct ipmi_recv_msg *recv_msg,
10088c2ecf20Sopenharmony_ci			 unsigned long        timeout,
10098c2ecf20Sopenharmony_ci			 int                  retries,
10108c2ecf20Sopenharmony_ci			 int                  broadcast,
10118c2ecf20Sopenharmony_ci			 unsigned char        *seq,
10128c2ecf20Sopenharmony_ci			 long                 *seqid)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	int          rv = 0;
10158c2ecf20Sopenharmony_ci	unsigned int i;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (timeout == 0)
10188c2ecf20Sopenharmony_ci		timeout = default_retry_ms;
10198c2ecf20Sopenharmony_ci	if (retries < 0)
10208c2ecf20Sopenharmony_ci		retries = default_max_retries;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
10238c2ecf20Sopenharmony_ci					i = (i+1)%IPMI_IPMB_NUM_SEQ) {
10248c2ecf20Sopenharmony_ci		if (!intf->seq_table[i].inuse)
10258c2ecf20Sopenharmony_ci			break;
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (!intf->seq_table[i].inuse) {
10298c2ecf20Sopenharmony_ci		intf->seq_table[i].recv_msg = recv_msg;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci		/*
10328c2ecf20Sopenharmony_ci		 * Start with the maximum timeout, when the send response
10338c2ecf20Sopenharmony_ci		 * comes in we will start the real timer.
10348c2ecf20Sopenharmony_ci		 */
10358c2ecf20Sopenharmony_ci		intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;
10368c2ecf20Sopenharmony_ci		intf->seq_table[i].orig_timeout = timeout;
10378c2ecf20Sopenharmony_ci		intf->seq_table[i].retries_left = retries;
10388c2ecf20Sopenharmony_ci		intf->seq_table[i].broadcast = broadcast;
10398c2ecf20Sopenharmony_ci		intf->seq_table[i].inuse = 1;
10408c2ecf20Sopenharmony_ci		intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);
10418c2ecf20Sopenharmony_ci		*seq = i;
10428c2ecf20Sopenharmony_ci		*seqid = intf->seq_table[i].seqid;
10438c2ecf20Sopenharmony_ci		intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
10448c2ecf20Sopenharmony_ci		smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
10458c2ecf20Sopenharmony_ci		need_waiter(intf);
10468c2ecf20Sopenharmony_ci	} else {
10478c2ecf20Sopenharmony_ci		rv = -EAGAIN;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return rv;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci/*
10548c2ecf20Sopenharmony_ci * Return the receive message for the given sequence number and
10558c2ecf20Sopenharmony_ci * release the sequence number so it can be reused.  Some other data
10568c2ecf20Sopenharmony_ci * is passed in to be sure the message matches up correctly (to help
10578c2ecf20Sopenharmony_ci * guard against message coming in after their timeout and the
10588c2ecf20Sopenharmony_ci * sequence number being reused).
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_cistatic int intf_find_seq(struct ipmi_smi      *intf,
10618c2ecf20Sopenharmony_ci			 unsigned char        seq,
10628c2ecf20Sopenharmony_ci			 short                channel,
10638c2ecf20Sopenharmony_ci			 unsigned char        cmd,
10648c2ecf20Sopenharmony_ci			 unsigned char        netfn,
10658c2ecf20Sopenharmony_ci			 struct ipmi_addr     *addr,
10668c2ecf20Sopenharmony_ci			 struct ipmi_recv_msg **recv_msg)
10678c2ecf20Sopenharmony_ci{
10688c2ecf20Sopenharmony_ci	int           rv = -ENODEV;
10698c2ecf20Sopenharmony_ci	unsigned long flags;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	if (seq >= IPMI_IPMB_NUM_SEQ)
10728c2ecf20Sopenharmony_ci		return -EINVAL;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
10758c2ecf20Sopenharmony_ci	if (intf->seq_table[seq].inuse) {
10768c2ecf20Sopenharmony_ci		struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci		if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd)
10798c2ecf20Sopenharmony_ci				&& (msg->msg.netfn == netfn)
10808c2ecf20Sopenharmony_ci				&& (ipmi_addr_equal(addr, &msg->addr))) {
10818c2ecf20Sopenharmony_ci			*recv_msg = msg;
10828c2ecf20Sopenharmony_ci			intf->seq_table[seq].inuse = 0;
10838c2ecf20Sopenharmony_ci			smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
10848c2ecf20Sopenharmony_ci			rv = 0;
10858c2ecf20Sopenharmony_ci		}
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	return rv;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci/* Start the timer for a specific sequence table entry. */
10948c2ecf20Sopenharmony_cistatic int intf_start_seq_timer(struct ipmi_smi *intf,
10958c2ecf20Sopenharmony_ci				long       msgid)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	int           rv = -ENODEV;
10988c2ecf20Sopenharmony_ci	unsigned long flags;
10998c2ecf20Sopenharmony_ci	unsigned char seq;
11008c2ecf20Sopenharmony_ci	unsigned long seqid;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	GET_SEQ_FROM_MSGID(msgid, seq, seqid);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
11068c2ecf20Sopenharmony_ci	/*
11078c2ecf20Sopenharmony_ci	 * We do this verification because the user can be deleted
11088c2ecf20Sopenharmony_ci	 * while a message is outstanding.
11098c2ecf20Sopenharmony_ci	 */
11108c2ecf20Sopenharmony_ci	if ((intf->seq_table[seq].inuse)
11118c2ecf20Sopenharmony_ci				&& (intf->seq_table[seq].seqid == seqid)) {
11128c2ecf20Sopenharmony_ci		struct seq_table *ent = &intf->seq_table[seq];
11138c2ecf20Sopenharmony_ci		ent->timeout = ent->orig_timeout;
11148c2ecf20Sopenharmony_ci		rv = 0;
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	return rv;
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci/* Got an error for the send message for a specific sequence number. */
11228c2ecf20Sopenharmony_cistatic int intf_err_seq(struct ipmi_smi *intf,
11238c2ecf20Sopenharmony_ci			long         msgid,
11248c2ecf20Sopenharmony_ci			unsigned int err)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	int                  rv = -ENODEV;
11278c2ecf20Sopenharmony_ci	unsigned long        flags;
11288c2ecf20Sopenharmony_ci	unsigned char        seq;
11298c2ecf20Sopenharmony_ci	unsigned long        seqid;
11308c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *msg = NULL;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	GET_SEQ_FROM_MSGID(msgid, seq, seqid);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
11368c2ecf20Sopenharmony_ci	/*
11378c2ecf20Sopenharmony_ci	 * We do this verification because the user can be deleted
11388c2ecf20Sopenharmony_ci	 * while a message is outstanding.
11398c2ecf20Sopenharmony_ci	 */
11408c2ecf20Sopenharmony_ci	if ((intf->seq_table[seq].inuse)
11418c2ecf20Sopenharmony_ci				&& (intf->seq_table[seq].seqid == seqid)) {
11428c2ecf20Sopenharmony_ci		struct seq_table *ent = &intf->seq_table[seq];
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci		ent->inuse = 0;
11458c2ecf20Sopenharmony_ci		smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
11468c2ecf20Sopenharmony_ci		msg = ent->recv_msg;
11478c2ecf20Sopenharmony_ci		rv = 0;
11488c2ecf20Sopenharmony_ci	}
11498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	if (msg)
11528c2ecf20Sopenharmony_ci		deliver_err_response(intf, msg, err);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	return rv;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic void free_user_work(struct work_struct *work)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	struct ipmi_user *user = container_of(work, struct ipmi_user,
11608c2ecf20Sopenharmony_ci					      remove_work);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	cleanup_srcu_struct(&user->release_barrier);
11638c2ecf20Sopenharmony_ci	vfree(user);
11648c2ecf20Sopenharmony_ci}
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ciint ipmi_create_user(unsigned int          if_num,
11678c2ecf20Sopenharmony_ci		     const struct ipmi_user_hndl *handler,
11688c2ecf20Sopenharmony_ci		     void                  *handler_data,
11698c2ecf20Sopenharmony_ci		     struct ipmi_user      **user)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	unsigned long flags;
11728c2ecf20Sopenharmony_ci	struct ipmi_user *new_user;
11738c2ecf20Sopenharmony_ci	int           rv, index;
11748c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	/*
11778c2ecf20Sopenharmony_ci	 * There is no module usecount here, because it's not
11788c2ecf20Sopenharmony_ci	 * required.  Since this can only be used by and called from
11798c2ecf20Sopenharmony_ci	 * other modules, they will implicitly use this module, and
11808c2ecf20Sopenharmony_ci	 * thus this can't be removed unless the other modules are
11818c2ecf20Sopenharmony_ci	 * removed.
11828c2ecf20Sopenharmony_ci	 */
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	if (handler == NULL)
11858c2ecf20Sopenharmony_ci		return -EINVAL;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	/*
11888c2ecf20Sopenharmony_ci	 * Make sure the driver is actually initialized, this handles
11898c2ecf20Sopenharmony_ci	 * problems with initialization order.
11908c2ecf20Sopenharmony_ci	 */
11918c2ecf20Sopenharmony_ci	rv = ipmi_init_msghandler();
11928c2ecf20Sopenharmony_ci	if (rv)
11938c2ecf20Sopenharmony_ci		return rv;
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	new_user = vzalloc(sizeof(*new_user));
11968c2ecf20Sopenharmony_ci	if (!new_user)
11978c2ecf20Sopenharmony_ci		return -ENOMEM;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	index = srcu_read_lock(&ipmi_interfaces_srcu);
12008c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
12018c2ecf20Sopenharmony_ci		if (intf->intf_num == if_num)
12028c2ecf20Sopenharmony_ci			goto found;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci	/* Not found, return an error */
12058c2ecf20Sopenharmony_ci	rv = -EINVAL;
12068c2ecf20Sopenharmony_ci	goto out_kfree;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci found:
12098c2ecf20Sopenharmony_ci	INIT_WORK(&new_user->remove_work, free_user_work);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	rv = init_srcu_struct(&new_user->release_barrier);
12128c2ecf20Sopenharmony_ci	if (rv)
12138c2ecf20Sopenharmony_ci		goto out_kfree;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	if (!try_module_get(intf->owner)) {
12168c2ecf20Sopenharmony_ci		rv = -ENODEV;
12178c2ecf20Sopenharmony_ci		goto out_kfree;
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	/* Note that each existing user holds a refcount to the interface. */
12218c2ecf20Sopenharmony_ci	kref_get(&intf->refcount);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	kref_init(&new_user->refcount);
12248c2ecf20Sopenharmony_ci	new_user->handler = handler;
12258c2ecf20Sopenharmony_ci	new_user->handler_data = handler_data;
12268c2ecf20Sopenharmony_ci	new_user->intf = intf;
12278c2ecf20Sopenharmony_ci	new_user->gets_events = false;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	rcu_assign_pointer(new_user->self, new_user);
12308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
12318c2ecf20Sopenharmony_ci	list_add_rcu(&new_user->link, &intf->users);
12328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
12338c2ecf20Sopenharmony_ci	if (handler->ipmi_watchdog_pretimeout)
12348c2ecf20Sopenharmony_ci		/* User wants pretimeouts, so make sure to watch for them. */
12358c2ecf20Sopenharmony_ci		smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
12368c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
12378c2ecf20Sopenharmony_ci	*user = new_user;
12388c2ecf20Sopenharmony_ci	return 0;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ciout_kfree:
12418c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
12428c2ecf20Sopenharmony_ci	vfree(new_user);
12438c2ecf20Sopenharmony_ci	return rv;
12448c2ecf20Sopenharmony_ci}
12458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_create_user);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ciint ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	int rv, index;
12508c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	index = srcu_read_lock(&ipmi_interfaces_srcu);
12538c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
12548c2ecf20Sopenharmony_ci		if (intf->intf_num == if_num)
12558c2ecf20Sopenharmony_ci			goto found;
12568c2ecf20Sopenharmony_ci	}
12578c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/* Not found, return an error */
12608c2ecf20Sopenharmony_ci	return -EINVAL;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_cifound:
12638c2ecf20Sopenharmony_ci	if (!intf->handlers->get_smi_info)
12648c2ecf20Sopenharmony_ci		rv = -ENOTTY;
12658c2ecf20Sopenharmony_ci	else
12668c2ecf20Sopenharmony_ci		rv = intf->handlers->get_smi_info(intf->send_info, data);
12678c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	return rv;
12708c2ecf20Sopenharmony_ci}
12718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_smi_info);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic void free_user(struct kref *ref)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	/* SRCU cleanup must happen in task context. */
12788c2ecf20Sopenharmony_ci	queue_work(remove_work_wq, &user->remove_work);
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cistatic void _ipmi_destroy_user(struct ipmi_user *user)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	struct ipmi_smi  *intf = user->intf;
12848c2ecf20Sopenharmony_ci	int              i;
12858c2ecf20Sopenharmony_ci	unsigned long    flags;
12868c2ecf20Sopenharmony_ci	struct cmd_rcvr  *rcvr;
12878c2ecf20Sopenharmony_ci	struct cmd_rcvr  *rcvrs = NULL;
12888c2ecf20Sopenharmony_ci	struct module    *owner;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (!acquire_ipmi_user(user, &i)) {
12918c2ecf20Sopenharmony_ci		/*
12928c2ecf20Sopenharmony_ci		 * The user has already been cleaned up, just make sure
12938c2ecf20Sopenharmony_ci		 * nothing is using it and return.
12948c2ecf20Sopenharmony_ci		 */
12958c2ecf20Sopenharmony_ci		synchronize_srcu(&user->release_barrier);
12968c2ecf20Sopenharmony_ci		return;
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	rcu_assign_pointer(user->self, NULL);
13008c2ecf20Sopenharmony_ci	release_ipmi_user(user, i);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	synchronize_srcu(&user->release_barrier);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	if (user->handler->shutdown)
13058c2ecf20Sopenharmony_ci		user->handler->shutdown(user->handler_data);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (user->handler->ipmi_watchdog_pretimeout)
13088c2ecf20Sopenharmony_ci		smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	if (user->gets_events)
13118c2ecf20Sopenharmony_ci		atomic_dec(&intf->event_waiters);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	/* Remove the user from the interface's sequence table. */
13148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
13158c2ecf20Sopenharmony_ci	list_del_rcu(&user->link);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
13188c2ecf20Sopenharmony_ci		if (intf->seq_table[i].inuse
13198c2ecf20Sopenharmony_ci		    && (intf->seq_table[i].recv_msg->user == user)) {
13208c2ecf20Sopenharmony_ci			intf->seq_table[i].inuse = 0;
13218c2ecf20Sopenharmony_ci			smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
13228c2ecf20Sopenharmony_ci			ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci	}
13258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	/*
13288c2ecf20Sopenharmony_ci	 * Remove the user from the command receiver's table.  First
13298c2ecf20Sopenharmony_ci	 * we build a list of everything (not using the standard link,
13308c2ecf20Sopenharmony_ci	 * since other things may be using it till we do
13318c2ecf20Sopenharmony_ci	 * synchronize_srcu()) then free everything in that list.
13328c2ecf20Sopenharmony_ci	 */
13338c2ecf20Sopenharmony_ci	mutex_lock(&intf->cmd_rcvrs_mutex);
13348c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
13358c2ecf20Sopenharmony_ci				lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
13368c2ecf20Sopenharmony_ci		if (rcvr->user == user) {
13378c2ecf20Sopenharmony_ci			list_del_rcu(&rcvr->link);
13388c2ecf20Sopenharmony_ci			rcvr->next = rcvrs;
13398c2ecf20Sopenharmony_ci			rcvrs = rcvr;
13408c2ecf20Sopenharmony_ci		}
13418c2ecf20Sopenharmony_ci	}
13428c2ecf20Sopenharmony_ci	mutex_unlock(&intf->cmd_rcvrs_mutex);
13438c2ecf20Sopenharmony_ci	synchronize_rcu();
13448c2ecf20Sopenharmony_ci	while (rcvrs) {
13458c2ecf20Sopenharmony_ci		rcvr = rcvrs;
13468c2ecf20Sopenharmony_ci		rcvrs = rcvr->next;
13478c2ecf20Sopenharmony_ci		kfree(rcvr);
13488c2ecf20Sopenharmony_ci	}
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	owner = intf->owner;
13518c2ecf20Sopenharmony_ci	kref_put(&intf->refcount, intf_free);
13528c2ecf20Sopenharmony_ci	module_put(owner);
13538c2ecf20Sopenharmony_ci}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ciint ipmi_destroy_user(struct ipmi_user *user)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	_ipmi_destroy_user(user);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	kref_put(&user->refcount, free_user);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	return 0;
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_destroy_user);
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ciint ipmi_get_version(struct ipmi_user *user,
13668c2ecf20Sopenharmony_ci		     unsigned char *major,
13678c2ecf20Sopenharmony_ci		     unsigned char *minor)
13688c2ecf20Sopenharmony_ci{
13698c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
13708c2ecf20Sopenharmony_ci	int rv, index;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
13738c2ecf20Sopenharmony_ci	if (!user)
13748c2ecf20Sopenharmony_ci		return -ENODEV;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
13778c2ecf20Sopenharmony_ci	if (!rv) {
13788c2ecf20Sopenharmony_ci		*major = ipmi_version_major(&id);
13798c2ecf20Sopenharmony_ci		*minor = ipmi_version_minor(&id);
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	return rv;
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_version);
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ciint ipmi_set_my_address(struct ipmi_user *user,
13888c2ecf20Sopenharmony_ci			unsigned int  channel,
13898c2ecf20Sopenharmony_ci			unsigned char address)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	int index, rv = 0;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
13948c2ecf20Sopenharmony_ci	if (!user)
13958c2ecf20Sopenharmony_ci		return -ENODEV;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	if (channel >= IPMI_MAX_CHANNELS) {
13988c2ecf20Sopenharmony_ci		rv = -EINVAL;
13998c2ecf20Sopenharmony_ci	} else {
14008c2ecf20Sopenharmony_ci		channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
14018c2ecf20Sopenharmony_ci		user->intf->addrinfo[channel].address = address;
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	return rv;
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_address);
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ciint ipmi_get_my_address(struct ipmi_user *user,
14108c2ecf20Sopenharmony_ci			unsigned int  channel,
14118c2ecf20Sopenharmony_ci			unsigned char *address)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	int index, rv = 0;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
14168c2ecf20Sopenharmony_ci	if (!user)
14178c2ecf20Sopenharmony_ci		return -ENODEV;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	if (channel >= IPMI_MAX_CHANNELS) {
14208c2ecf20Sopenharmony_ci		rv = -EINVAL;
14218c2ecf20Sopenharmony_ci	} else {
14228c2ecf20Sopenharmony_ci		channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
14238c2ecf20Sopenharmony_ci		*address = user->intf->addrinfo[channel].address;
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	return rv;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_address);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ciint ipmi_set_my_LUN(struct ipmi_user *user,
14328c2ecf20Sopenharmony_ci		    unsigned int  channel,
14338c2ecf20Sopenharmony_ci		    unsigned char LUN)
14348c2ecf20Sopenharmony_ci{
14358c2ecf20Sopenharmony_ci	int index, rv = 0;
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
14388c2ecf20Sopenharmony_ci	if (!user)
14398c2ecf20Sopenharmony_ci		return -ENODEV;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	if (channel >= IPMI_MAX_CHANNELS) {
14428c2ecf20Sopenharmony_ci		rv = -EINVAL;
14438c2ecf20Sopenharmony_ci	} else {
14448c2ecf20Sopenharmony_ci		channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
14458c2ecf20Sopenharmony_ci		user->intf->addrinfo[channel].lun = LUN & 0x3;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	return rv;
14508c2ecf20Sopenharmony_ci}
14518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_my_LUN);
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ciint ipmi_get_my_LUN(struct ipmi_user *user,
14548c2ecf20Sopenharmony_ci		    unsigned int  channel,
14558c2ecf20Sopenharmony_ci		    unsigned char *address)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	int index, rv = 0;
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
14608c2ecf20Sopenharmony_ci	if (!user)
14618c2ecf20Sopenharmony_ci		return -ENODEV;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (channel >= IPMI_MAX_CHANNELS) {
14648c2ecf20Sopenharmony_ci		rv = -EINVAL;
14658c2ecf20Sopenharmony_ci	} else {
14668c2ecf20Sopenharmony_ci		channel = array_index_nospec(channel, IPMI_MAX_CHANNELS);
14678c2ecf20Sopenharmony_ci		*address = user->intf->addrinfo[channel].lun;
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	return rv;
14728c2ecf20Sopenharmony_ci}
14738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_my_LUN);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ciint ipmi_get_maintenance_mode(struct ipmi_user *user)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	int mode, index;
14788c2ecf20Sopenharmony_ci	unsigned long flags;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
14818c2ecf20Sopenharmony_ci	if (!user)
14828c2ecf20Sopenharmony_ci		return -ENODEV;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags);
14858c2ecf20Sopenharmony_ci	mode = user->intf->maintenance_mode;
14868c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags);
14878c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	return mode;
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_get_maintenance_mode);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_cistatic void maintenance_mode_update(struct ipmi_smi *intf)
14948c2ecf20Sopenharmony_ci{
14958c2ecf20Sopenharmony_ci	if (intf->handlers->set_maintenance_mode)
14968c2ecf20Sopenharmony_ci		intf->handlers->set_maintenance_mode(
14978c2ecf20Sopenharmony_ci			intf->send_info, intf->maintenance_mode_enable);
14988c2ecf20Sopenharmony_ci}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ciint ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	int rv = 0, index;
15038c2ecf20Sopenharmony_ci	unsigned long flags;
15048c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = user->intf;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
15078c2ecf20Sopenharmony_ci	if (!user)
15088c2ecf20Sopenharmony_ci		return -ENODEV;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
15118c2ecf20Sopenharmony_ci	if (intf->maintenance_mode != mode) {
15128c2ecf20Sopenharmony_ci		switch (mode) {
15138c2ecf20Sopenharmony_ci		case IPMI_MAINTENANCE_MODE_AUTO:
15148c2ecf20Sopenharmony_ci			intf->maintenance_mode_enable
15158c2ecf20Sopenharmony_ci				= (intf->auto_maintenance_timeout > 0);
15168c2ecf20Sopenharmony_ci			break;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci		case IPMI_MAINTENANCE_MODE_OFF:
15198c2ecf20Sopenharmony_ci			intf->maintenance_mode_enable = false;
15208c2ecf20Sopenharmony_ci			break;
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci		case IPMI_MAINTENANCE_MODE_ON:
15238c2ecf20Sopenharmony_ci			intf->maintenance_mode_enable = true;
15248c2ecf20Sopenharmony_ci			break;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		default:
15278c2ecf20Sopenharmony_ci			rv = -EINVAL;
15288c2ecf20Sopenharmony_ci			goto out_unlock;
15298c2ecf20Sopenharmony_ci		}
15308c2ecf20Sopenharmony_ci		intf->maintenance_mode = mode;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci		maintenance_mode_update(intf);
15338c2ecf20Sopenharmony_ci	}
15348c2ecf20Sopenharmony_ci out_unlock:
15358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);
15368c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	return rv;
15398c2ecf20Sopenharmony_ci}
15408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_maintenance_mode);
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ciint ipmi_set_gets_events(struct ipmi_user *user, bool val)
15438c2ecf20Sopenharmony_ci{
15448c2ecf20Sopenharmony_ci	unsigned long        flags;
15458c2ecf20Sopenharmony_ci	struct ipmi_smi      *intf = user->intf;
15468c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *msg, *msg2;
15478c2ecf20Sopenharmony_ci	struct list_head     msgs;
15488c2ecf20Sopenharmony_ci	int index;
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
15518c2ecf20Sopenharmony_ci	if (!user)
15528c2ecf20Sopenharmony_ci		return -ENODEV;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&msgs);
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->events_lock, flags);
15578c2ecf20Sopenharmony_ci	if (user->gets_events == val)
15588c2ecf20Sopenharmony_ci		goto out;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	user->gets_events = val;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (val) {
15638c2ecf20Sopenharmony_ci		if (atomic_inc_return(&intf->event_waiters) == 1)
15648c2ecf20Sopenharmony_ci			need_waiter(intf);
15658c2ecf20Sopenharmony_ci	} else {
15668c2ecf20Sopenharmony_ci		atomic_dec(&intf->event_waiters);
15678c2ecf20Sopenharmony_ci	}
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	if (intf->delivering_events)
15708c2ecf20Sopenharmony_ci		/*
15718c2ecf20Sopenharmony_ci		 * Another thread is delivering events for this, so
15728c2ecf20Sopenharmony_ci		 * let it handle any new events.
15738c2ecf20Sopenharmony_ci		 */
15748c2ecf20Sopenharmony_ci		goto out;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	/* Deliver any queued events. */
15778c2ecf20Sopenharmony_ci	while (user->gets_events && !list_empty(&intf->waiting_events)) {
15788c2ecf20Sopenharmony_ci		list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link)
15798c2ecf20Sopenharmony_ci			list_move_tail(&msg->link, &msgs);
15808c2ecf20Sopenharmony_ci		intf->waiting_events_count = 0;
15818c2ecf20Sopenharmony_ci		if (intf->event_msg_printed) {
15828c2ecf20Sopenharmony_ci			dev_warn(intf->si_dev, "Event queue no longer full\n");
15838c2ecf20Sopenharmony_ci			intf->event_msg_printed = 0;
15848c2ecf20Sopenharmony_ci		}
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci		intf->delivering_events = 1;
15878c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->events_lock, flags);
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci		list_for_each_entry_safe(msg, msg2, &msgs, link) {
15908c2ecf20Sopenharmony_ci			msg->user = user;
15918c2ecf20Sopenharmony_ci			kref_get(&user->refcount);
15928c2ecf20Sopenharmony_ci			deliver_local_response(intf, msg);
15938c2ecf20Sopenharmony_ci		}
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->events_lock, flags);
15968c2ecf20Sopenharmony_ci		intf->delivering_events = 0;
15978c2ecf20Sopenharmony_ci	}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci out:
16008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->events_lock, flags);
16018c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci	return 0;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_set_gets_events);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cistatic struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf,
16088c2ecf20Sopenharmony_ci				      unsigned char netfn,
16098c2ecf20Sopenharmony_ci				      unsigned char cmd,
16108c2ecf20Sopenharmony_ci				      unsigned char chan)
16118c2ecf20Sopenharmony_ci{
16128c2ecf20Sopenharmony_ci	struct cmd_rcvr *rcvr;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
16158c2ecf20Sopenharmony_ci				lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
16168c2ecf20Sopenharmony_ci		if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
16178c2ecf20Sopenharmony_ci					&& (rcvr->chans & (1 << chan)))
16188c2ecf20Sopenharmony_ci			return rcvr;
16198c2ecf20Sopenharmony_ci	}
16208c2ecf20Sopenharmony_ci	return NULL;
16218c2ecf20Sopenharmony_ci}
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_cistatic int is_cmd_rcvr_exclusive(struct ipmi_smi *intf,
16248c2ecf20Sopenharmony_ci				 unsigned char netfn,
16258c2ecf20Sopenharmony_ci				 unsigned char cmd,
16268c2ecf20Sopenharmony_ci				 unsigned int  chans)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	struct cmd_rcvr *rcvr;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link,
16318c2ecf20Sopenharmony_ci				lockdep_is_held(&intf->cmd_rcvrs_mutex)) {
16328c2ecf20Sopenharmony_ci		if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
16338c2ecf20Sopenharmony_ci					&& (rcvr->chans & chans))
16348c2ecf20Sopenharmony_ci			return 0;
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci	return 1;
16378c2ecf20Sopenharmony_ci}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ciint ipmi_register_for_cmd(struct ipmi_user *user,
16408c2ecf20Sopenharmony_ci			  unsigned char netfn,
16418c2ecf20Sopenharmony_ci			  unsigned char cmd,
16428c2ecf20Sopenharmony_ci			  unsigned int  chans)
16438c2ecf20Sopenharmony_ci{
16448c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = user->intf;
16458c2ecf20Sopenharmony_ci	struct cmd_rcvr *rcvr;
16468c2ecf20Sopenharmony_ci	int rv = 0, index;
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
16498c2ecf20Sopenharmony_ci	if (!user)
16508c2ecf20Sopenharmony_ci		return -ENODEV;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL);
16538c2ecf20Sopenharmony_ci	if (!rcvr) {
16548c2ecf20Sopenharmony_ci		rv = -ENOMEM;
16558c2ecf20Sopenharmony_ci		goto out_release;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci	rcvr->cmd = cmd;
16588c2ecf20Sopenharmony_ci	rcvr->netfn = netfn;
16598c2ecf20Sopenharmony_ci	rcvr->chans = chans;
16608c2ecf20Sopenharmony_ci	rcvr->user = user;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	mutex_lock(&intf->cmd_rcvrs_mutex);
16638c2ecf20Sopenharmony_ci	/* Make sure the command/netfn is not already registered. */
16648c2ecf20Sopenharmony_ci	if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) {
16658c2ecf20Sopenharmony_ci		rv = -EBUSY;
16668c2ecf20Sopenharmony_ci		goto out_unlock;
16678c2ecf20Sopenharmony_ci	}
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ciout_unlock:
16748c2ecf20Sopenharmony_ci	mutex_unlock(&intf->cmd_rcvrs_mutex);
16758c2ecf20Sopenharmony_ci	if (rv)
16768c2ecf20Sopenharmony_ci		kfree(rcvr);
16778c2ecf20Sopenharmony_ciout_release:
16788c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	return rv;
16818c2ecf20Sopenharmony_ci}
16828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_register_for_cmd);
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ciint ipmi_unregister_for_cmd(struct ipmi_user *user,
16858c2ecf20Sopenharmony_ci			    unsigned char netfn,
16868c2ecf20Sopenharmony_ci			    unsigned char cmd,
16878c2ecf20Sopenharmony_ci			    unsigned int  chans)
16888c2ecf20Sopenharmony_ci{
16898c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = user->intf;
16908c2ecf20Sopenharmony_ci	struct cmd_rcvr *rcvr;
16918c2ecf20Sopenharmony_ci	struct cmd_rcvr *rcvrs = NULL;
16928c2ecf20Sopenharmony_ci	int i, rv = -ENOENT, index;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
16958c2ecf20Sopenharmony_ci	if (!user)
16968c2ecf20Sopenharmony_ci		return -ENODEV;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	mutex_lock(&intf->cmd_rcvrs_mutex);
16998c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
17008c2ecf20Sopenharmony_ci		if (((1 << i) & chans) == 0)
17018c2ecf20Sopenharmony_ci			continue;
17028c2ecf20Sopenharmony_ci		rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
17038c2ecf20Sopenharmony_ci		if (rcvr == NULL)
17048c2ecf20Sopenharmony_ci			continue;
17058c2ecf20Sopenharmony_ci		if (rcvr->user == user) {
17068c2ecf20Sopenharmony_ci			rv = 0;
17078c2ecf20Sopenharmony_ci			rcvr->chans &= ~chans;
17088c2ecf20Sopenharmony_ci			if (rcvr->chans == 0) {
17098c2ecf20Sopenharmony_ci				list_del_rcu(&rcvr->link);
17108c2ecf20Sopenharmony_ci				rcvr->next = rcvrs;
17118c2ecf20Sopenharmony_ci				rcvrs = rcvr;
17128c2ecf20Sopenharmony_ci			}
17138c2ecf20Sopenharmony_ci		}
17148c2ecf20Sopenharmony_ci	}
17158c2ecf20Sopenharmony_ci	mutex_unlock(&intf->cmd_rcvrs_mutex);
17168c2ecf20Sopenharmony_ci	synchronize_rcu();
17178c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
17188c2ecf20Sopenharmony_ci	while (rcvrs) {
17198c2ecf20Sopenharmony_ci		smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
17208c2ecf20Sopenharmony_ci		rcvr = rcvrs;
17218c2ecf20Sopenharmony_ci		rcvrs = rcvr->next;
17228c2ecf20Sopenharmony_ci		kfree(rcvr);
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	return rv;
17268c2ecf20Sopenharmony_ci}
17278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_for_cmd);
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_cistatic unsigned char
17308c2ecf20Sopenharmony_ciipmb_checksum(unsigned char *data, int size)
17318c2ecf20Sopenharmony_ci{
17328c2ecf20Sopenharmony_ci	unsigned char csum = 0;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	for (; size > 0; size--, data++)
17358c2ecf20Sopenharmony_ci		csum += *data;
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	return -csum;
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_cistatic inline void format_ipmb_msg(struct ipmi_smi_msg   *smi_msg,
17418c2ecf20Sopenharmony_ci				   struct kernel_ipmi_msg *msg,
17428c2ecf20Sopenharmony_ci				   struct ipmi_ipmb_addr *ipmb_addr,
17438c2ecf20Sopenharmony_ci				   long                  msgid,
17448c2ecf20Sopenharmony_ci				   unsigned char         ipmb_seq,
17458c2ecf20Sopenharmony_ci				   int                   broadcast,
17468c2ecf20Sopenharmony_ci				   unsigned char         source_address,
17478c2ecf20Sopenharmony_ci				   unsigned char         source_lun)
17488c2ecf20Sopenharmony_ci{
17498c2ecf20Sopenharmony_ci	int i = broadcast;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	/* Format the IPMB header data. */
17528c2ecf20Sopenharmony_ci	smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
17538c2ecf20Sopenharmony_ci	smi_msg->data[1] = IPMI_SEND_MSG_CMD;
17548c2ecf20Sopenharmony_ci	smi_msg->data[2] = ipmb_addr->channel;
17558c2ecf20Sopenharmony_ci	if (broadcast)
17568c2ecf20Sopenharmony_ci		smi_msg->data[3] = 0;
17578c2ecf20Sopenharmony_ci	smi_msg->data[i+3] = ipmb_addr->slave_addr;
17588c2ecf20Sopenharmony_ci	smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
17598c2ecf20Sopenharmony_ci	smi_msg->data[i+5] = ipmb_checksum(&smi_msg->data[i + 3], 2);
17608c2ecf20Sopenharmony_ci	smi_msg->data[i+6] = source_address;
17618c2ecf20Sopenharmony_ci	smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
17628c2ecf20Sopenharmony_ci	smi_msg->data[i+8] = msg->cmd;
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	/* Now tack on the data to the message. */
17658c2ecf20Sopenharmony_ci	if (msg->data_len > 0)
17668c2ecf20Sopenharmony_ci		memcpy(&smi_msg->data[i + 9], msg->data, msg->data_len);
17678c2ecf20Sopenharmony_ci	smi_msg->data_size = msg->data_len + 9;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	/* Now calculate the checksum and tack it on. */
17708c2ecf20Sopenharmony_ci	smi_msg->data[i+smi_msg->data_size]
17718c2ecf20Sopenharmony_ci		= ipmb_checksum(&smi_msg->data[i + 6], smi_msg->data_size - 6);
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	/*
17748c2ecf20Sopenharmony_ci	 * Add on the checksum size and the offset from the
17758c2ecf20Sopenharmony_ci	 * broadcast.
17768c2ecf20Sopenharmony_ci	 */
17778c2ecf20Sopenharmony_ci	smi_msg->data_size += 1 + i;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	smi_msg->msgid = msgid;
17808c2ecf20Sopenharmony_ci}
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_cistatic inline void format_lan_msg(struct ipmi_smi_msg   *smi_msg,
17838c2ecf20Sopenharmony_ci				  struct kernel_ipmi_msg *msg,
17848c2ecf20Sopenharmony_ci				  struct ipmi_lan_addr  *lan_addr,
17858c2ecf20Sopenharmony_ci				  long                  msgid,
17868c2ecf20Sopenharmony_ci				  unsigned char         ipmb_seq,
17878c2ecf20Sopenharmony_ci				  unsigned char         source_lun)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	/* Format the IPMB header data. */
17908c2ecf20Sopenharmony_ci	smi_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
17918c2ecf20Sopenharmony_ci	smi_msg->data[1] = IPMI_SEND_MSG_CMD;
17928c2ecf20Sopenharmony_ci	smi_msg->data[2] = lan_addr->channel;
17938c2ecf20Sopenharmony_ci	smi_msg->data[3] = lan_addr->session_handle;
17948c2ecf20Sopenharmony_ci	smi_msg->data[4] = lan_addr->remote_SWID;
17958c2ecf20Sopenharmony_ci	smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3);
17968c2ecf20Sopenharmony_ci	smi_msg->data[6] = ipmb_checksum(&smi_msg->data[4], 2);
17978c2ecf20Sopenharmony_ci	smi_msg->data[7] = lan_addr->local_SWID;
17988c2ecf20Sopenharmony_ci	smi_msg->data[8] = (ipmb_seq << 2) | source_lun;
17998c2ecf20Sopenharmony_ci	smi_msg->data[9] = msg->cmd;
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	/* Now tack on the data to the message. */
18028c2ecf20Sopenharmony_ci	if (msg->data_len > 0)
18038c2ecf20Sopenharmony_ci		memcpy(&smi_msg->data[10], msg->data, msg->data_len);
18048c2ecf20Sopenharmony_ci	smi_msg->data_size = msg->data_len + 10;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	/* Now calculate the checksum and tack it on. */
18078c2ecf20Sopenharmony_ci	smi_msg->data[smi_msg->data_size]
18088c2ecf20Sopenharmony_ci		= ipmb_checksum(&smi_msg->data[7], smi_msg->data_size - 7);
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	/*
18118c2ecf20Sopenharmony_ci	 * Add on the checksum size and the offset from the
18128c2ecf20Sopenharmony_ci	 * broadcast.
18138c2ecf20Sopenharmony_ci	 */
18148c2ecf20Sopenharmony_ci	smi_msg->data_size += 1;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	smi_msg->msgid = msgid;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_cistatic struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
18208c2ecf20Sopenharmony_ci					     struct ipmi_smi_msg *smi_msg,
18218c2ecf20Sopenharmony_ci					     int priority)
18228c2ecf20Sopenharmony_ci{
18238c2ecf20Sopenharmony_ci	if (intf->curr_msg) {
18248c2ecf20Sopenharmony_ci		if (priority > 0)
18258c2ecf20Sopenharmony_ci			list_add_tail(&smi_msg->link, &intf->hp_xmit_msgs);
18268c2ecf20Sopenharmony_ci		else
18278c2ecf20Sopenharmony_ci			list_add_tail(&smi_msg->link, &intf->xmit_msgs);
18288c2ecf20Sopenharmony_ci		smi_msg = NULL;
18298c2ecf20Sopenharmony_ci	} else {
18308c2ecf20Sopenharmony_ci		intf->curr_msg = smi_msg;
18318c2ecf20Sopenharmony_ci	}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return smi_msg;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic void smi_send(struct ipmi_smi *intf,
18378c2ecf20Sopenharmony_ci		     const struct ipmi_smi_handlers *handlers,
18388c2ecf20Sopenharmony_ci		     struct ipmi_smi_msg *smi_msg, int priority)
18398c2ecf20Sopenharmony_ci{
18408c2ecf20Sopenharmony_ci	int run_to_completion = intf->run_to_completion;
18418c2ecf20Sopenharmony_ci	unsigned long flags = 0;
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci	if (!run_to_completion)
18448c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
18458c2ecf20Sopenharmony_ci	smi_msg = smi_add_send_msg(intf, smi_msg, priority);
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	if (!run_to_completion)
18488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	if (smi_msg)
18518c2ecf20Sopenharmony_ci		handlers->sender(intf->send_info, smi_msg);
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_cistatic bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	return (((msg->netfn == IPMI_NETFN_APP_REQUEST)
18578c2ecf20Sopenharmony_ci		 && ((msg->cmd == IPMI_COLD_RESET_CMD)
18588c2ecf20Sopenharmony_ci		     || (msg->cmd == IPMI_WARM_RESET_CMD)))
18598c2ecf20Sopenharmony_ci		|| (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST));
18608c2ecf20Sopenharmony_ci}
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_cistatic int i_ipmi_req_sysintf(struct ipmi_smi        *intf,
18638c2ecf20Sopenharmony_ci			      struct ipmi_addr       *addr,
18648c2ecf20Sopenharmony_ci			      long                   msgid,
18658c2ecf20Sopenharmony_ci			      struct kernel_ipmi_msg *msg,
18668c2ecf20Sopenharmony_ci			      struct ipmi_smi_msg    *smi_msg,
18678c2ecf20Sopenharmony_ci			      struct ipmi_recv_msg   *recv_msg,
18688c2ecf20Sopenharmony_ci			      int                    retries,
18698c2ecf20Sopenharmony_ci			      unsigned int           retry_time_ms)
18708c2ecf20Sopenharmony_ci{
18718c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr *smi_addr;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	if (msg->netfn & 1)
18748c2ecf20Sopenharmony_ci		/* Responses are not allowed to the SMI. */
18758c2ecf20Sopenharmony_ci		return -EINVAL;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	smi_addr = (struct ipmi_system_interface_addr *) addr;
18788c2ecf20Sopenharmony_ci	if (smi_addr->lun > 3) {
18798c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
18808c2ecf20Sopenharmony_ci		return -EINVAL;
18818c2ecf20Sopenharmony_ci	}
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	memcpy(&recv_msg->addr, smi_addr, sizeof(*smi_addr));
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci	if ((msg->netfn == IPMI_NETFN_APP_REQUEST)
18868c2ecf20Sopenharmony_ci	    && ((msg->cmd == IPMI_SEND_MSG_CMD)
18878c2ecf20Sopenharmony_ci		|| (msg->cmd == IPMI_GET_MSG_CMD)
18888c2ecf20Sopenharmony_ci		|| (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) {
18898c2ecf20Sopenharmony_ci		/*
18908c2ecf20Sopenharmony_ci		 * We don't let the user do these, since we manage
18918c2ecf20Sopenharmony_ci		 * the sequence numbers.
18928c2ecf20Sopenharmony_ci		 */
18938c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
18948c2ecf20Sopenharmony_ci		return -EINVAL;
18958c2ecf20Sopenharmony_ci	}
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	if (is_maintenance_mode_cmd(msg)) {
18988c2ecf20Sopenharmony_ci		unsigned long flags;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
19018c2ecf20Sopenharmony_ci		intf->auto_maintenance_timeout
19028c2ecf20Sopenharmony_ci			= maintenance_mode_timeout_ms;
19038c2ecf20Sopenharmony_ci		if (!intf->maintenance_mode
19048c2ecf20Sopenharmony_ci		    && !intf->maintenance_mode_enable) {
19058c2ecf20Sopenharmony_ci			intf->maintenance_mode_enable = true;
19068c2ecf20Sopenharmony_ci			maintenance_mode_update(intf);
19078c2ecf20Sopenharmony_ci		}
19088c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->maintenance_mode_lock,
19098c2ecf20Sopenharmony_ci				       flags);
19108c2ecf20Sopenharmony_ci	}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	if (msg->data_len + 2 > IPMI_MAX_MSG_LENGTH) {
19138c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
19148c2ecf20Sopenharmony_ci		return -EMSGSIZE;
19158c2ecf20Sopenharmony_ci	}
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci	smi_msg->data[0] = (msg->netfn << 2) | (smi_addr->lun & 0x3);
19188c2ecf20Sopenharmony_ci	smi_msg->data[1] = msg->cmd;
19198c2ecf20Sopenharmony_ci	smi_msg->msgid = msgid;
19208c2ecf20Sopenharmony_ci	smi_msg->user_data = recv_msg;
19218c2ecf20Sopenharmony_ci	if (msg->data_len > 0)
19228c2ecf20Sopenharmony_ci		memcpy(&smi_msg->data[2], msg->data, msg->data_len);
19238c2ecf20Sopenharmony_ci	smi_msg->data_size = msg->data_len + 2;
19248c2ecf20Sopenharmony_ci	ipmi_inc_stat(intf, sent_local_commands);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	return 0;
19278c2ecf20Sopenharmony_ci}
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_cistatic int i_ipmi_req_ipmb(struct ipmi_smi        *intf,
19308c2ecf20Sopenharmony_ci			   struct ipmi_addr       *addr,
19318c2ecf20Sopenharmony_ci			   long                   msgid,
19328c2ecf20Sopenharmony_ci			   struct kernel_ipmi_msg *msg,
19338c2ecf20Sopenharmony_ci			   struct ipmi_smi_msg    *smi_msg,
19348c2ecf20Sopenharmony_ci			   struct ipmi_recv_msg   *recv_msg,
19358c2ecf20Sopenharmony_ci			   unsigned char          source_address,
19368c2ecf20Sopenharmony_ci			   unsigned char          source_lun,
19378c2ecf20Sopenharmony_ci			   int                    retries,
19388c2ecf20Sopenharmony_ci			   unsigned int           retry_time_ms)
19398c2ecf20Sopenharmony_ci{
19408c2ecf20Sopenharmony_ci	struct ipmi_ipmb_addr *ipmb_addr;
19418c2ecf20Sopenharmony_ci	unsigned char ipmb_seq;
19428c2ecf20Sopenharmony_ci	long seqid;
19438c2ecf20Sopenharmony_ci	int broadcast = 0;
19448c2ecf20Sopenharmony_ci	struct ipmi_channel *chans;
19458c2ecf20Sopenharmony_ci	int rv = 0;
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	if (addr->channel >= IPMI_MAX_CHANNELS) {
19488c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
19498c2ecf20Sopenharmony_ci		return -EINVAL;
19508c2ecf20Sopenharmony_ci	}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	chans = READ_ONCE(intf->channel_list)->c;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
19558c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
19568c2ecf20Sopenharmony_ci		return -EINVAL;
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) {
19608c2ecf20Sopenharmony_ci		/*
19618c2ecf20Sopenharmony_ci		 * Broadcasts add a zero at the beginning of the
19628c2ecf20Sopenharmony_ci		 * message, but otherwise is the same as an IPMB
19638c2ecf20Sopenharmony_ci		 * address.
19648c2ecf20Sopenharmony_ci		 */
19658c2ecf20Sopenharmony_ci		addr->addr_type = IPMI_IPMB_ADDR_TYPE;
19668c2ecf20Sopenharmony_ci		broadcast = 1;
19678c2ecf20Sopenharmony_ci		retries = 0; /* Don't retry broadcasts. */
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	/*
19718c2ecf20Sopenharmony_ci	 * 9 for the header and 1 for the checksum, plus
19728c2ecf20Sopenharmony_ci	 * possibly one for the broadcast.
19738c2ecf20Sopenharmony_ci	 */
19748c2ecf20Sopenharmony_ci	if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) {
19758c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
19768c2ecf20Sopenharmony_ci		return -EMSGSIZE;
19778c2ecf20Sopenharmony_ci	}
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	ipmb_addr = (struct ipmi_ipmb_addr *) addr;
19808c2ecf20Sopenharmony_ci	if (ipmb_addr->lun > 3) {
19818c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
19828c2ecf20Sopenharmony_ci		return -EINVAL;
19838c2ecf20Sopenharmony_ci	}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci	memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr));
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	if (recv_msg->msg.netfn & 0x1) {
19888c2ecf20Sopenharmony_ci		/*
19898c2ecf20Sopenharmony_ci		 * It's a response, so use the user's sequence
19908c2ecf20Sopenharmony_ci		 * from msgid.
19918c2ecf20Sopenharmony_ci		 */
19928c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_ipmb_responses);
19938c2ecf20Sopenharmony_ci		format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid,
19948c2ecf20Sopenharmony_ci				msgid, broadcast,
19958c2ecf20Sopenharmony_ci				source_address, source_lun);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci		/*
19988c2ecf20Sopenharmony_ci		 * Save the receive message so we can use it
19998c2ecf20Sopenharmony_ci		 * to deliver the response.
20008c2ecf20Sopenharmony_ci		 */
20018c2ecf20Sopenharmony_ci		smi_msg->user_data = recv_msg;
20028c2ecf20Sopenharmony_ci	} else {
20038c2ecf20Sopenharmony_ci		/* It's a command, so get a sequence for it. */
20048c2ecf20Sopenharmony_ci		unsigned long flags;
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->seq_lock, flags);
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci		if (is_maintenance_mode_cmd(msg))
20098c2ecf20Sopenharmony_ci			intf->ipmb_maintenance_mode_timeout =
20108c2ecf20Sopenharmony_ci				maintenance_mode_timeout_ms;
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci		if (intf->ipmb_maintenance_mode_timeout && retry_time_ms == 0)
20138c2ecf20Sopenharmony_ci			/* Different default in maintenance mode */
20148c2ecf20Sopenharmony_ci			retry_time_ms = default_maintenance_retry_ms;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci		/*
20178c2ecf20Sopenharmony_ci		 * Create a sequence number with a 1 second
20188c2ecf20Sopenharmony_ci		 * timeout and 4 retries.
20198c2ecf20Sopenharmony_ci		 */
20208c2ecf20Sopenharmony_ci		rv = intf_next_seq(intf,
20218c2ecf20Sopenharmony_ci				   recv_msg,
20228c2ecf20Sopenharmony_ci				   retry_time_ms,
20238c2ecf20Sopenharmony_ci				   retries,
20248c2ecf20Sopenharmony_ci				   broadcast,
20258c2ecf20Sopenharmony_ci				   &ipmb_seq,
20268c2ecf20Sopenharmony_ci				   &seqid);
20278c2ecf20Sopenharmony_ci		if (rv)
20288c2ecf20Sopenharmony_ci			/*
20298c2ecf20Sopenharmony_ci			 * We have used up all the sequence numbers,
20308c2ecf20Sopenharmony_ci			 * probably, so abort.
20318c2ecf20Sopenharmony_ci			 */
20328c2ecf20Sopenharmony_ci			goto out_err;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_ipmb_commands);
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci		/*
20378c2ecf20Sopenharmony_ci		 * Store the sequence number in the message,
20388c2ecf20Sopenharmony_ci		 * so that when the send message response
20398c2ecf20Sopenharmony_ci		 * comes back we can start the timer.
20408c2ecf20Sopenharmony_ci		 */
20418c2ecf20Sopenharmony_ci		format_ipmb_msg(smi_msg, msg, ipmb_addr,
20428c2ecf20Sopenharmony_ci				STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
20438c2ecf20Sopenharmony_ci				ipmb_seq, broadcast,
20448c2ecf20Sopenharmony_ci				source_address, source_lun);
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci		/*
20478c2ecf20Sopenharmony_ci		 * Copy the message into the recv message data, so we
20488c2ecf20Sopenharmony_ci		 * can retransmit it later if necessary.
20498c2ecf20Sopenharmony_ci		 */
20508c2ecf20Sopenharmony_ci		memcpy(recv_msg->msg_data, smi_msg->data,
20518c2ecf20Sopenharmony_ci		       smi_msg->data_size);
20528c2ecf20Sopenharmony_ci		recv_msg->msg.data = recv_msg->msg_data;
20538c2ecf20Sopenharmony_ci		recv_msg->msg.data_len = smi_msg->data_size;
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci		/*
20568c2ecf20Sopenharmony_ci		 * We don't unlock until here, because we need
20578c2ecf20Sopenharmony_ci		 * to copy the completed message into the
20588c2ecf20Sopenharmony_ci		 * recv_msg before we release the lock.
20598c2ecf20Sopenharmony_ci		 * Otherwise, race conditions may bite us.  I
20608c2ecf20Sopenharmony_ci		 * know that's pretty paranoid, but I prefer
20618c2ecf20Sopenharmony_ci		 * to be correct.
20628c2ecf20Sopenharmony_ci		 */
20638c2ecf20Sopenharmony_ciout_err:
20648c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->seq_lock, flags);
20658c2ecf20Sopenharmony_ci	}
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	return rv;
20688c2ecf20Sopenharmony_ci}
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_cistatic int i_ipmi_req_lan(struct ipmi_smi        *intf,
20718c2ecf20Sopenharmony_ci			  struct ipmi_addr       *addr,
20728c2ecf20Sopenharmony_ci			  long                   msgid,
20738c2ecf20Sopenharmony_ci			  struct kernel_ipmi_msg *msg,
20748c2ecf20Sopenharmony_ci			  struct ipmi_smi_msg    *smi_msg,
20758c2ecf20Sopenharmony_ci			  struct ipmi_recv_msg   *recv_msg,
20768c2ecf20Sopenharmony_ci			  unsigned char          source_lun,
20778c2ecf20Sopenharmony_ci			  int                    retries,
20788c2ecf20Sopenharmony_ci			  unsigned int           retry_time_ms)
20798c2ecf20Sopenharmony_ci{
20808c2ecf20Sopenharmony_ci	struct ipmi_lan_addr  *lan_addr;
20818c2ecf20Sopenharmony_ci	unsigned char ipmb_seq;
20828c2ecf20Sopenharmony_ci	long seqid;
20838c2ecf20Sopenharmony_ci	struct ipmi_channel *chans;
20848c2ecf20Sopenharmony_ci	int rv = 0;
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	if (addr->channel >= IPMI_MAX_CHANNELS) {
20878c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
20888c2ecf20Sopenharmony_ci		return -EINVAL;
20898c2ecf20Sopenharmony_ci	}
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	chans = READ_ONCE(intf->channel_list)->c;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	if ((chans[addr->channel].medium
20948c2ecf20Sopenharmony_ci				!= IPMI_CHANNEL_MEDIUM_8023LAN)
20958c2ecf20Sopenharmony_ci			&& (chans[addr->channel].medium
20968c2ecf20Sopenharmony_ci			    != IPMI_CHANNEL_MEDIUM_ASYNC)) {
20978c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
20988c2ecf20Sopenharmony_ci		return -EINVAL;
20998c2ecf20Sopenharmony_ci	}
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	/* 11 for the header and 1 for the checksum. */
21028c2ecf20Sopenharmony_ci	if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) {
21038c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
21048c2ecf20Sopenharmony_ci		return -EMSGSIZE;
21058c2ecf20Sopenharmony_ci	}
21068c2ecf20Sopenharmony_ci
21078c2ecf20Sopenharmony_ci	lan_addr = (struct ipmi_lan_addr *) addr;
21088c2ecf20Sopenharmony_ci	if (lan_addr->lun > 3) {
21098c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
21108c2ecf20Sopenharmony_ci		return -EINVAL;
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr));
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	if (recv_msg->msg.netfn & 0x1) {
21168c2ecf20Sopenharmony_ci		/*
21178c2ecf20Sopenharmony_ci		 * It's a response, so use the user's sequence
21188c2ecf20Sopenharmony_ci		 * from msgid.
21198c2ecf20Sopenharmony_ci		 */
21208c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_lan_responses);
21218c2ecf20Sopenharmony_ci		format_lan_msg(smi_msg, msg, lan_addr, msgid,
21228c2ecf20Sopenharmony_ci			       msgid, source_lun);
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci		/*
21258c2ecf20Sopenharmony_ci		 * Save the receive message so we can use it
21268c2ecf20Sopenharmony_ci		 * to deliver the response.
21278c2ecf20Sopenharmony_ci		 */
21288c2ecf20Sopenharmony_ci		smi_msg->user_data = recv_msg;
21298c2ecf20Sopenharmony_ci	} else {
21308c2ecf20Sopenharmony_ci		/* It's a command, so get a sequence for it. */
21318c2ecf20Sopenharmony_ci		unsigned long flags;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->seq_lock, flags);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci		/*
21368c2ecf20Sopenharmony_ci		 * Create a sequence number with a 1 second
21378c2ecf20Sopenharmony_ci		 * timeout and 4 retries.
21388c2ecf20Sopenharmony_ci		 */
21398c2ecf20Sopenharmony_ci		rv = intf_next_seq(intf,
21408c2ecf20Sopenharmony_ci				   recv_msg,
21418c2ecf20Sopenharmony_ci				   retry_time_ms,
21428c2ecf20Sopenharmony_ci				   retries,
21438c2ecf20Sopenharmony_ci				   0,
21448c2ecf20Sopenharmony_ci				   &ipmb_seq,
21458c2ecf20Sopenharmony_ci				   &seqid);
21468c2ecf20Sopenharmony_ci		if (rv)
21478c2ecf20Sopenharmony_ci			/*
21488c2ecf20Sopenharmony_ci			 * We have used up all the sequence numbers,
21498c2ecf20Sopenharmony_ci			 * probably, so abort.
21508c2ecf20Sopenharmony_ci			 */
21518c2ecf20Sopenharmony_ci			goto out_err;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_lan_commands);
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci		/*
21568c2ecf20Sopenharmony_ci		 * Store the sequence number in the message,
21578c2ecf20Sopenharmony_ci		 * so that when the send message response
21588c2ecf20Sopenharmony_ci		 * comes back we can start the timer.
21598c2ecf20Sopenharmony_ci		 */
21608c2ecf20Sopenharmony_ci		format_lan_msg(smi_msg, msg, lan_addr,
21618c2ecf20Sopenharmony_ci			       STORE_SEQ_IN_MSGID(ipmb_seq, seqid),
21628c2ecf20Sopenharmony_ci			       ipmb_seq, source_lun);
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci		/*
21658c2ecf20Sopenharmony_ci		 * Copy the message into the recv message data, so we
21668c2ecf20Sopenharmony_ci		 * can retransmit it later if necessary.
21678c2ecf20Sopenharmony_ci		 */
21688c2ecf20Sopenharmony_ci		memcpy(recv_msg->msg_data, smi_msg->data,
21698c2ecf20Sopenharmony_ci		       smi_msg->data_size);
21708c2ecf20Sopenharmony_ci		recv_msg->msg.data = recv_msg->msg_data;
21718c2ecf20Sopenharmony_ci		recv_msg->msg.data_len = smi_msg->data_size;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci		/*
21748c2ecf20Sopenharmony_ci		 * We don't unlock until here, because we need
21758c2ecf20Sopenharmony_ci		 * to copy the completed message into the
21768c2ecf20Sopenharmony_ci		 * recv_msg before we release the lock.
21778c2ecf20Sopenharmony_ci		 * Otherwise, race conditions may bite us.  I
21788c2ecf20Sopenharmony_ci		 * know that's pretty paranoid, but I prefer
21798c2ecf20Sopenharmony_ci		 * to be correct.
21808c2ecf20Sopenharmony_ci		 */
21818c2ecf20Sopenharmony_ciout_err:
21828c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->seq_lock, flags);
21838c2ecf20Sopenharmony_ci	}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	return rv;
21868c2ecf20Sopenharmony_ci}
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci/*
21898c2ecf20Sopenharmony_ci * Separate from ipmi_request so that the user does not have to be
21908c2ecf20Sopenharmony_ci * supplied in certain circumstances (mainly at panic time).  If
21918c2ecf20Sopenharmony_ci * messages are supplied, they will be freed, even if an error
21928c2ecf20Sopenharmony_ci * occurs.
21938c2ecf20Sopenharmony_ci */
21948c2ecf20Sopenharmony_cistatic int i_ipmi_request(struct ipmi_user     *user,
21958c2ecf20Sopenharmony_ci			  struct ipmi_smi      *intf,
21968c2ecf20Sopenharmony_ci			  struct ipmi_addr     *addr,
21978c2ecf20Sopenharmony_ci			  long                 msgid,
21988c2ecf20Sopenharmony_ci			  struct kernel_ipmi_msg *msg,
21998c2ecf20Sopenharmony_ci			  void                 *user_msg_data,
22008c2ecf20Sopenharmony_ci			  void                 *supplied_smi,
22018c2ecf20Sopenharmony_ci			  struct ipmi_recv_msg *supplied_recv,
22028c2ecf20Sopenharmony_ci			  int                  priority,
22038c2ecf20Sopenharmony_ci			  unsigned char        source_address,
22048c2ecf20Sopenharmony_ci			  unsigned char        source_lun,
22058c2ecf20Sopenharmony_ci			  int                  retries,
22068c2ecf20Sopenharmony_ci			  unsigned int         retry_time_ms)
22078c2ecf20Sopenharmony_ci{
22088c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *smi_msg;
22098c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *recv_msg;
22108c2ecf20Sopenharmony_ci	int rv = 0;
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	if (supplied_recv)
22138c2ecf20Sopenharmony_ci		recv_msg = supplied_recv;
22148c2ecf20Sopenharmony_ci	else {
22158c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
22168c2ecf20Sopenharmony_ci		if (recv_msg == NULL) {
22178c2ecf20Sopenharmony_ci			rv = -ENOMEM;
22188c2ecf20Sopenharmony_ci			goto out;
22198c2ecf20Sopenharmony_ci		}
22208c2ecf20Sopenharmony_ci	}
22218c2ecf20Sopenharmony_ci	recv_msg->user_msg_data = user_msg_data;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	if (supplied_smi)
22248c2ecf20Sopenharmony_ci		smi_msg = (struct ipmi_smi_msg *) supplied_smi;
22258c2ecf20Sopenharmony_ci	else {
22268c2ecf20Sopenharmony_ci		smi_msg = ipmi_alloc_smi_msg();
22278c2ecf20Sopenharmony_ci		if (smi_msg == NULL) {
22288c2ecf20Sopenharmony_ci			if (!supplied_recv)
22298c2ecf20Sopenharmony_ci				ipmi_free_recv_msg(recv_msg);
22308c2ecf20Sopenharmony_ci			rv = -ENOMEM;
22318c2ecf20Sopenharmony_ci			goto out;
22328c2ecf20Sopenharmony_ci		}
22338c2ecf20Sopenharmony_ci	}
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	rcu_read_lock();
22368c2ecf20Sopenharmony_ci	if (intf->in_shutdown) {
22378c2ecf20Sopenharmony_ci		rv = -ENODEV;
22388c2ecf20Sopenharmony_ci		goto out_err;
22398c2ecf20Sopenharmony_ci	}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci	recv_msg->user = user;
22428c2ecf20Sopenharmony_ci	if (user)
22438c2ecf20Sopenharmony_ci		/* The put happens when the message is freed. */
22448c2ecf20Sopenharmony_ci		kref_get(&user->refcount);
22458c2ecf20Sopenharmony_ci	recv_msg->msgid = msgid;
22468c2ecf20Sopenharmony_ci	/*
22478c2ecf20Sopenharmony_ci	 * Store the message to send in the receive message so timeout
22488c2ecf20Sopenharmony_ci	 * responses can get the proper response data.
22498c2ecf20Sopenharmony_ci	 */
22508c2ecf20Sopenharmony_ci	recv_msg->msg = *msg;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
22538c2ecf20Sopenharmony_ci		rv = i_ipmi_req_sysintf(intf, addr, msgid, msg, smi_msg,
22548c2ecf20Sopenharmony_ci					recv_msg, retries, retry_time_ms);
22558c2ecf20Sopenharmony_ci	} else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) {
22568c2ecf20Sopenharmony_ci		rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
22578c2ecf20Sopenharmony_ci				     source_address, source_lun,
22588c2ecf20Sopenharmony_ci				     retries, retry_time_ms);
22598c2ecf20Sopenharmony_ci	} else if (is_lan_addr(addr)) {
22608c2ecf20Sopenharmony_ci		rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
22618c2ecf20Sopenharmony_ci				    source_lun, retries, retry_time_ms);
22628c2ecf20Sopenharmony_ci	} else {
22638c2ecf20Sopenharmony_ci	    /* Unknown address type. */
22648c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, sent_invalid_commands);
22658c2ecf20Sopenharmony_ci		rv = -EINVAL;
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	if (rv) {
22698c2ecf20Sopenharmony_ciout_err:
22708c2ecf20Sopenharmony_ci		ipmi_free_smi_msg(smi_msg);
22718c2ecf20Sopenharmony_ci		ipmi_free_recv_msg(recv_msg);
22728c2ecf20Sopenharmony_ci	} else {
22738c2ecf20Sopenharmony_ci		pr_debug("Send: %*ph\n", smi_msg->data_size, smi_msg->data);
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci		smi_send(intf, intf->handlers, smi_msg, priority);
22768c2ecf20Sopenharmony_ci	}
22778c2ecf20Sopenharmony_ci	rcu_read_unlock();
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ciout:
22808c2ecf20Sopenharmony_ci	return rv;
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_cistatic int check_addr(struct ipmi_smi  *intf,
22848c2ecf20Sopenharmony_ci		      struct ipmi_addr *addr,
22858c2ecf20Sopenharmony_ci		      unsigned char    *saddr,
22868c2ecf20Sopenharmony_ci		      unsigned char    *lun)
22878c2ecf20Sopenharmony_ci{
22888c2ecf20Sopenharmony_ci	if (addr->channel >= IPMI_MAX_CHANNELS)
22898c2ecf20Sopenharmony_ci		return -EINVAL;
22908c2ecf20Sopenharmony_ci	addr->channel = array_index_nospec(addr->channel, IPMI_MAX_CHANNELS);
22918c2ecf20Sopenharmony_ci	*lun = intf->addrinfo[addr->channel].lun;
22928c2ecf20Sopenharmony_ci	*saddr = intf->addrinfo[addr->channel].address;
22938c2ecf20Sopenharmony_ci	return 0;
22948c2ecf20Sopenharmony_ci}
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ciint ipmi_request_settime(struct ipmi_user *user,
22978c2ecf20Sopenharmony_ci			 struct ipmi_addr *addr,
22988c2ecf20Sopenharmony_ci			 long             msgid,
22998c2ecf20Sopenharmony_ci			 struct kernel_ipmi_msg  *msg,
23008c2ecf20Sopenharmony_ci			 void             *user_msg_data,
23018c2ecf20Sopenharmony_ci			 int              priority,
23028c2ecf20Sopenharmony_ci			 int              retries,
23038c2ecf20Sopenharmony_ci			 unsigned int     retry_time_ms)
23048c2ecf20Sopenharmony_ci{
23058c2ecf20Sopenharmony_ci	unsigned char saddr = 0, lun = 0;
23068c2ecf20Sopenharmony_ci	int rv, index;
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci	if (!user)
23098c2ecf20Sopenharmony_ci		return -EINVAL;
23108c2ecf20Sopenharmony_ci
23118c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
23128c2ecf20Sopenharmony_ci	if (!user)
23138c2ecf20Sopenharmony_ci		return -ENODEV;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	rv = check_addr(user->intf, addr, &saddr, &lun);
23168c2ecf20Sopenharmony_ci	if (!rv)
23178c2ecf20Sopenharmony_ci		rv = i_ipmi_request(user,
23188c2ecf20Sopenharmony_ci				    user->intf,
23198c2ecf20Sopenharmony_ci				    addr,
23208c2ecf20Sopenharmony_ci				    msgid,
23218c2ecf20Sopenharmony_ci				    msg,
23228c2ecf20Sopenharmony_ci				    user_msg_data,
23238c2ecf20Sopenharmony_ci				    NULL, NULL,
23248c2ecf20Sopenharmony_ci				    priority,
23258c2ecf20Sopenharmony_ci				    saddr,
23268c2ecf20Sopenharmony_ci				    lun,
23278c2ecf20Sopenharmony_ci				    retries,
23288c2ecf20Sopenharmony_ci				    retry_time_ms);
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
23318c2ecf20Sopenharmony_ci	return rv;
23328c2ecf20Sopenharmony_ci}
23338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_settime);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ciint ipmi_request_supply_msgs(struct ipmi_user     *user,
23368c2ecf20Sopenharmony_ci			     struct ipmi_addr     *addr,
23378c2ecf20Sopenharmony_ci			     long                 msgid,
23388c2ecf20Sopenharmony_ci			     struct kernel_ipmi_msg *msg,
23398c2ecf20Sopenharmony_ci			     void                 *user_msg_data,
23408c2ecf20Sopenharmony_ci			     void                 *supplied_smi,
23418c2ecf20Sopenharmony_ci			     struct ipmi_recv_msg *supplied_recv,
23428c2ecf20Sopenharmony_ci			     int                  priority)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	unsigned char saddr = 0, lun = 0;
23458c2ecf20Sopenharmony_ci	int rv, index;
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	if (!user)
23488c2ecf20Sopenharmony_ci		return -EINVAL;
23498c2ecf20Sopenharmony_ci
23508c2ecf20Sopenharmony_ci	user = acquire_ipmi_user(user, &index);
23518c2ecf20Sopenharmony_ci	if (!user)
23528c2ecf20Sopenharmony_ci		return -ENODEV;
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	rv = check_addr(user->intf, addr, &saddr, &lun);
23558c2ecf20Sopenharmony_ci	if (!rv)
23568c2ecf20Sopenharmony_ci		rv = i_ipmi_request(user,
23578c2ecf20Sopenharmony_ci				    user->intf,
23588c2ecf20Sopenharmony_ci				    addr,
23598c2ecf20Sopenharmony_ci				    msgid,
23608c2ecf20Sopenharmony_ci				    msg,
23618c2ecf20Sopenharmony_ci				    user_msg_data,
23628c2ecf20Sopenharmony_ci				    supplied_smi,
23638c2ecf20Sopenharmony_ci				    supplied_recv,
23648c2ecf20Sopenharmony_ci				    priority,
23658c2ecf20Sopenharmony_ci				    saddr,
23668c2ecf20Sopenharmony_ci				    lun,
23678c2ecf20Sopenharmony_ci				    -1, 0);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	release_ipmi_user(user, index);
23708c2ecf20Sopenharmony_ci	return rv;
23718c2ecf20Sopenharmony_ci}
23728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_request_supply_msgs);
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_cistatic void bmc_device_id_handler(struct ipmi_smi *intf,
23758c2ecf20Sopenharmony_ci				  struct ipmi_recv_msg *msg)
23768c2ecf20Sopenharmony_ci{
23778c2ecf20Sopenharmony_ci	int rv;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_ci	if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
23808c2ecf20Sopenharmony_ci			|| (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
23818c2ecf20Sopenharmony_ci			|| (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) {
23828c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
23838c2ecf20Sopenharmony_ci			 "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n",
23848c2ecf20Sopenharmony_ci			 msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd);
23858c2ecf20Sopenharmony_ci		return;
23868c2ecf20Sopenharmony_ci	}
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
23898c2ecf20Sopenharmony_ci			msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id);
23908c2ecf20Sopenharmony_ci	if (rv) {
23918c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev, "device id demangle failed: %d\n", rv);
23928c2ecf20Sopenharmony_ci		/* record completion code when error */
23938c2ecf20Sopenharmony_ci		intf->bmc->cc = msg->msg.data[0];
23948c2ecf20Sopenharmony_ci		intf->bmc->dyn_id_set = 0;
23958c2ecf20Sopenharmony_ci	} else {
23968c2ecf20Sopenharmony_ci		/*
23978c2ecf20Sopenharmony_ci		 * Make sure the id data is available before setting
23988c2ecf20Sopenharmony_ci		 * dyn_id_set.
23998c2ecf20Sopenharmony_ci		 */
24008c2ecf20Sopenharmony_ci		smp_wmb();
24018c2ecf20Sopenharmony_ci		intf->bmc->dyn_id_set = 1;
24028c2ecf20Sopenharmony_ci	}
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	wake_up(&intf->waitq);
24058c2ecf20Sopenharmony_ci}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_cistatic int
24088c2ecf20Sopenharmony_cisend_get_device_id_cmd(struct ipmi_smi *intf)
24098c2ecf20Sopenharmony_ci{
24108c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr si;
24118c2ecf20Sopenharmony_ci	struct kernel_ipmi_msg msg;
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
24148c2ecf20Sopenharmony_ci	si.channel = IPMI_BMC_CHANNEL;
24158c2ecf20Sopenharmony_ci	si.lun = 0;
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	msg.netfn = IPMI_NETFN_APP_REQUEST;
24188c2ecf20Sopenharmony_ci	msg.cmd = IPMI_GET_DEVICE_ID_CMD;
24198c2ecf20Sopenharmony_ci	msg.data = NULL;
24208c2ecf20Sopenharmony_ci	msg.data_len = 0;
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	return i_ipmi_request(NULL,
24238c2ecf20Sopenharmony_ci			      intf,
24248c2ecf20Sopenharmony_ci			      (struct ipmi_addr *) &si,
24258c2ecf20Sopenharmony_ci			      0,
24268c2ecf20Sopenharmony_ci			      &msg,
24278c2ecf20Sopenharmony_ci			      intf,
24288c2ecf20Sopenharmony_ci			      NULL,
24298c2ecf20Sopenharmony_ci			      NULL,
24308c2ecf20Sopenharmony_ci			      0,
24318c2ecf20Sopenharmony_ci			      intf->addrinfo[0].address,
24328c2ecf20Sopenharmony_ci			      intf->addrinfo[0].lun,
24338c2ecf20Sopenharmony_ci			      -1, 0);
24348c2ecf20Sopenharmony_ci}
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_cistatic int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc)
24378c2ecf20Sopenharmony_ci{
24388c2ecf20Sopenharmony_ci	int rv;
24398c2ecf20Sopenharmony_ci	unsigned int retry_count = 0;
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	intf->null_user_handler = bmc_device_id_handler;
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ciretry:
24448c2ecf20Sopenharmony_ci	bmc->cc = 0;
24458c2ecf20Sopenharmony_ci	bmc->dyn_id_set = 2;
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	rv = send_get_device_id_cmd(intf);
24488c2ecf20Sopenharmony_ci	if (rv)
24498c2ecf20Sopenharmony_ci		goto out_reset_handler;
24508c2ecf20Sopenharmony_ci
24518c2ecf20Sopenharmony_ci	wait_event(intf->waitq, bmc->dyn_id_set != 2);
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	if (!bmc->dyn_id_set) {
24548c2ecf20Sopenharmony_ci		if ((bmc->cc == IPMI_DEVICE_IN_FW_UPDATE_ERR
24558c2ecf20Sopenharmony_ci		     || bmc->cc ==  IPMI_DEVICE_IN_INIT_ERR
24568c2ecf20Sopenharmony_ci		     || bmc->cc ==  IPMI_NOT_IN_MY_STATE_ERR)
24578c2ecf20Sopenharmony_ci		     && ++retry_count <= GET_DEVICE_ID_MAX_RETRY) {
24588c2ecf20Sopenharmony_ci			msleep(500);
24598c2ecf20Sopenharmony_ci			dev_warn(intf->si_dev,
24608c2ecf20Sopenharmony_ci			    "BMC returned 0x%2.2x, retry get bmc device id\n",
24618c2ecf20Sopenharmony_ci			    bmc->cc);
24628c2ecf20Sopenharmony_ci			goto retry;
24638c2ecf20Sopenharmony_ci		}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_ci		rv = -EIO; /* Something went wrong in the fetch. */
24668c2ecf20Sopenharmony_ci	}
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	/* dyn_id_set makes the id data available. */
24698c2ecf20Sopenharmony_ci	smp_rmb();
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ciout_reset_handler:
24728c2ecf20Sopenharmony_ci	intf->null_user_handler = NULL;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci	return rv;
24758c2ecf20Sopenharmony_ci}
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_ci/*
24788c2ecf20Sopenharmony_ci * Fetch the device id for the bmc/interface.  You must pass in either
24798c2ecf20Sopenharmony_ci * bmc or intf, this code will get the other one.  If the data has
24808c2ecf20Sopenharmony_ci * been recently fetched, this will just use the cached data.  Otherwise
24818c2ecf20Sopenharmony_ci * it will run a new fetch.
24828c2ecf20Sopenharmony_ci *
24838c2ecf20Sopenharmony_ci * Except for the first time this is called (in ipmi_add_smi()),
24848c2ecf20Sopenharmony_ci * this will always return good data;
24858c2ecf20Sopenharmony_ci */
24868c2ecf20Sopenharmony_cistatic int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
24878c2ecf20Sopenharmony_ci			       struct ipmi_device_id *id,
24888c2ecf20Sopenharmony_ci			       bool *guid_set, guid_t *guid, int intf_num)
24898c2ecf20Sopenharmony_ci{
24908c2ecf20Sopenharmony_ci	int rv = 0;
24918c2ecf20Sopenharmony_ci	int prev_dyn_id_set, prev_guid_set;
24928c2ecf20Sopenharmony_ci	bool intf_set = intf != NULL;
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci	if (!intf) {
24958c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
24968c2ecf20Sopenharmony_ciretry_bmc_lock:
24978c2ecf20Sopenharmony_ci		if (list_empty(&bmc->intfs)) {
24988c2ecf20Sopenharmony_ci			mutex_unlock(&bmc->dyn_mutex);
24998c2ecf20Sopenharmony_ci			return -ENOENT;
25008c2ecf20Sopenharmony_ci		}
25018c2ecf20Sopenharmony_ci		intf = list_first_entry(&bmc->intfs, struct ipmi_smi,
25028c2ecf20Sopenharmony_ci					bmc_link);
25038c2ecf20Sopenharmony_ci		kref_get(&intf->refcount);
25048c2ecf20Sopenharmony_ci		mutex_unlock(&bmc->dyn_mutex);
25058c2ecf20Sopenharmony_ci		mutex_lock(&intf->bmc_reg_mutex);
25068c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
25078c2ecf20Sopenharmony_ci		if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi,
25088c2ecf20Sopenharmony_ci					     bmc_link)) {
25098c2ecf20Sopenharmony_ci			mutex_unlock(&intf->bmc_reg_mutex);
25108c2ecf20Sopenharmony_ci			kref_put(&intf->refcount, intf_free);
25118c2ecf20Sopenharmony_ci			goto retry_bmc_lock;
25128c2ecf20Sopenharmony_ci		}
25138c2ecf20Sopenharmony_ci	} else {
25148c2ecf20Sopenharmony_ci		mutex_lock(&intf->bmc_reg_mutex);
25158c2ecf20Sopenharmony_ci		bmc = intf->bmc;
25168c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
25178c2ecf20Sopenharmony_ci		kref_get(&intf->refcount);
25188c2ecf20Sopenharmony_ci	}
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	/* If we have a valid and current ID, just return that. */
25218c2ecf20Sopenharmony_ci	if (intf->in_bmc_register ||
25228c2ecf20Sopenharmony_ci	    (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)))
25238c2ecf20Sopenharmony_ci		goto out_noprocessing;
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	prev_guid_set = bmc->dyn_guid_set;
25268c2ecf20Sopenharmony_ci	__get_guid(intf);
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_ci	prev_dyn_id_set = bmc->dyn_id_set;
25298c2ecf20Sopenharmony_ci	rv = __get_device_id(intf, bmc);
25308c2ecf20Sopenharmony_ci	if (rv)
25318c2ecf20Sopenharmony_ci		goto out;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	/*
25348c2ecf20Sopenharmony_ci	 * The guid, device id, manufacturer id, and product id should
25358c2ecf20Sopenharmony_ci	 * not change on a BMC.  If it does we have to do some dancing.
25368c2ecf20Sopenharmony_ci	 */
25378c2ecf20Sopenharmony_ci	if (!intf->bmc_registered
25388c2ecf20Sopenharmony_ci	    || (!prev_guid_set && bmc->dyn_guid_set)
25398c2ecf20Sopenharmony_ci	    || (!prev_dyn_id_set && bmc->dyn_id_set)
25408c2ecf20Sopenharmony_ci	    || (prev_guid_set && bmc->dyn_guid_set
25418c2ecf20Sopenharmony_ci		&& !guid_equal(&bmc->guid, &bmc->fetch_guid))
25428c2ecf20Sopenharmony_ci	    || bmc->id.device_id != bmc->fetch_id.device_id
25438c2ecf20Sopenharmony_ci	    || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id
25448c2ecf20Sopenharmony_ci	    || bmc->id.product_id != bmc->fetch_id.product_id) {
25458c2ecf20Sopenharmony_ci		struct ipmi_device_id id = bmc->fetch_id;
25468c2ecf20Sopenharmony_ci		int guid_set = bmc->dyn_guid_set;
25478c2ecf20Sopenharmony_ci		guid_t guid;
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci		guid = bmc->fetch_guid;
25508c2ecf20Sopenharmony_ci		mutex_unlock(&bmc->dyn_mutex);
25518c2ecf20Sopenharmony_ci
25528c2ecf20Sopenharmony_ci		__ipmi_bmc_unregister(intf);
25538c2ecf20Sopenharmony_ci		/* Fill in the temporary BMC for good measure. */
25548c2ecf20Sopenharmony_ci		intf->bmc->id = id;
25558c2ecf20Sopenharmony_ci		intf->bmc->dyn_guid_set = guid_set;
25568c2ecf20Sopenharmony_ci		intf->bmc->guid = guid;
25578c2ecf20Sopenharmony_ci		if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num))
25588c2ecf20Sopenharmony_ci			need_waiter(intf); /* Retry later on an error. */
25598c2ecf20Sopenharmony_ci		else
25608c2ecf20Sopenharmony_ci			__scan_channels(intf, &id);
25618c2ecf20Sopenharmony_ci
25628c2ecf20Sopenharmony_ci
25638c2ecf20Sopenharmony_ci		if (!intf_set) {
25648c2ecf20Sopenharmony_ci			/*
25658c2ecf20Sopenharmony_ci			 * We weren't given the interface on the
25668c2ecf20Sopenharmony_ci			 * command line, so restart the operation on
25678c2ecf20Sopenharmony_ci			 * the next interface for the BMC.
25688c2ecf20Sopenharmony_ci			 */
25698c2ecf20Sopenharmony_ci			mutex_unlock(&intf->bmc_reg_mutex);
25708c2ecf20Sopenharmony_ci			mutex_lock(&bmc->dyn_mutex);
25718c2ecf20Sopenharmony_ci			goto retry_bmc_lock;
25728c2ecf20Sopenharmony_ci		}
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci		/* We have a new BMC, set it up. */
25758c2ecf20Sopenharmony_ci		bmc = intf->bmc;
25768c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
25778c2ecf20Sopenharmony_ci		goto out_noprocessing;
25788c2ecf20Sopenharmony_ci	} else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
25798c2ecf20Sopenharmony_ci		/* Version info changes, scan the channels again. */
25808c2ecf20Sopenharmony_ci		__scan_channels(intf, &bmc->fetch_id);
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_ci	bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ciout:
25858c2ecf20Sopenharmony_ci	if (rv && prev_dyn_id_set) {
25868c2ecf20Sopenharmony_ci		rv = 0; /* Ignore failures if we have previous data. */
25878c2ecf20Sopenharmony_ci		bmc->dyn_id_set = prev_dyn_id_set;
25888c2ecf20Sopenharmony_ci	}
25898c2ecf20Sopenharmony_ci	if (!rv) {
25908c2ecf20Sopenharmony_ci		bmc->id = bmc->fetch_id;
25918c2ecf20Sopenharmony_ci		if (bmc->dyn_guid_set)
25928c2ecf20Sopenharmony_ci			bmc->guid = bmc->fetch_guid;
25938c2ecf20Sopenharmony_ci		else if (prev_guid_set)
25948c2ecf20Sopenharmony_ci			/*
25958c2ecf20Sopenharmony_ci			 * The guid used to be valid and it failed to fetch,
25968c2ecf20Sopenharmony_ci			 * just use the cached value.
25978c2ecf20Sopenharmony_ci			 */
25988c2ecf20Sopenharmony_ci			bmc->dyn_guid_set = prev_guid_set;
25998c2ecf20Sopenharmony_ci	}
26008c2ecf20Sopenharmony_ciout_noprocessing:
26018c2ecf20Sopenharmony_ci	if (!rv) {
26028c2ecf20Sopenharmony_ci		if (id)
26038c2ecf20Sopenharmony_ci			*id = bmc->id;
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci		if (guid_set)
26068c2ecf20Sopenharmony_ci			*guid_set = bmc->dyn_guid_set;
26078c2ecf20Sopenharmony_ci
26088c2ecf20Sopenharmony_ci		if (guid && bmc->dyn_guid_set)
26098c2ecf20Sopenharmony_ci			*guid =  bmc->guid;
26108c2ecf20Sopenharmony_ci	}
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci	mutex_unlock(&bmc->dyn_mutex);
26138c2ecf20Sopenharmony_ci	mutex_unlock(&intf->bmc_reg_mutex);
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_ci	kref_put(&intf->refcount, intf_free);
26168c2ecf20Sopenharmony_ci	return rv;
26178c2ecf20Sopenharmony_ci}
26188c2ecf20Sopenharmony_ci
26198c2ecf20Sopenharmony_cistatic int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
26208c2ecf20Sopenharmony_ci			     struct ipmi_device_id *id,
26218c2ecf20Sopenharmony_ci			     bool *guid_set, guid_t *guid)
26228c2ecf20Sopenharmony_ci{
26238c2ecf20Sopenharmony_ci	return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1);
26248c2ecf20Sopenharmony_ci}
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_cistatic ssize_t device_id_show(struct device *dev,
26278c2ecf20Sopenharmony_ci			      struct device_attribute *attr,
26288c2ecf20Sopenharmony_ci			      char *buf)
26298c2ecf20Sopenharmony_ci{
26308c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
26318c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
26328c2ecf20Sopenharmony_ci	int rv;
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
26358c2ecf20Sopenharmony_ci	if (rv)
26368c2ecf20Sopenharmony_ci		return rv;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%u\n", id.device_id);
26398c2ecf20Sopenharmony_ci}
26408c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device_id);
26418c2ecf20Sopenharmony_ci
26428c2ecf20Sopenharmony_cistatic ssize_t provides_device_sdrs_show(struct device *dev,
26438c2ecf20Sopenharmony_ci					 struct device_attribute *attr,
26448c2ecf20Sopenharmony_ci					 char *buf)
26458c2ecf20Sopenharmony_ci{
26468c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
26478c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
26488c2ecf20Sopenharmony_ci	int rv;
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
26518c2ecf20Sopenharmony_ci	if (rv)
26528c2ecf20Sopenharmony_ci		return rv;
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7);
26558c2ecf20Sopenharmony_ci}
26568c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(provides_device_sdrs);
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_cistatic ssize_t revision_show(struct device *dev, struct device_attribute *attr,
26598c2ecf20Sopenharmony_ci			     char *buf)
26608c2ecf20Sopenharmony_ci{
26618c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
26628c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
26638c2ecf20Sopenharmony_ci	int rv;
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
26668c2ecf20Sopenharmony_ci	if (rv)
26678c2ecf20Sopenharmony_ci		return rv;
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci	return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F);
26708c2ecf20Sopenharmony_ci}
26718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(revision);
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_cistatic ssize_t firmware_revision_show(struct device *dev,
26748c2ecf20Sopenharmony_ci				      struct device_attribute *attr,
26758c2ecf20Sopenharmony_ci				      char *buf)
26768c2ecf20Sopenharmony_ci{
26778c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
26788c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
26798c2ecf20Sopenharmony_ci	int rv;
26808c2ecf20Sopenharmony_ci
26818c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
26828c2ecf20Sopenharmony_ci	if (rv)
26838c2ecf20Sopenharmony_ci		return rv;
26848c2ecf20Sopenharmony_ci
26858c2ecf20Sopenharmony_ci	return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1,
26868c2ecf20Sopenharmony_ci			id.firmware_revision_2);
26878c2ecf20Sopenharmony_ci}
26888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(firmware_revision);
26898c2ecf20Sopenharmony_ci
26908c2ecf20Sopenharmony_cistatic ssize_t ipmi_version_show(struct device *dev,
26918c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
26928c2ecf20Sopenharmony_ci				 char *buf)
26938c2ecf20Sopenharmony_ci{
26948c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
26958c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
26968c2ecf20Sopenharmony_ci	int rv;
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
26998c2ecf20Sopenharmony_ci	if (rv)
27008c2ecf20Sopenharmony_ci		return rv;
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_ci	return snprintf(buf, 20, "%u.%u\n",
27038c2ecf20Sopenharmony_ci			ipmi_version_major(&id),
27048c2ecf20Sopenharmony_ci			ipmi_version_minor(&id));
27058c2ecf20Sopenharmony_ci}
27068c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ipmi_version);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_cistatic ssize_t add_dev_support_show(struct device *dev,
27098c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
27108c2ecf20Sopenharmony_ci				    char *buf)
27118c2ecf20Sopenharmony_ci{
27128c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
27138c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
27148c2ecf20Sopenharmony_ci	int rv;
27158c2ecf20Sopenharmony_ci
27168c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
27178c2ecf20Sopenharmony_ci	if (rv)
27188c2ecf20Sopenharmony_ci		return rv;
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "0x%02x\n", id.additional_device_support);
27218c2ecf20Sopenharmony_ci}
27228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show,
27238c2ecf20Sopenharmony_ci		   NULL);
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_cistatic ssize_t manufacturer_id_show(struct device *dev,
27268c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
27278c2ecf20Sopenharmony_ci				    char *buf)
27288c2ecf20Sopenharmony_ci{
27298c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
27308c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
27318c2ecf20Sopenharmony_ci	int rv;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
27348c2ecf20Sopenharmony_ci	if (rv)
27358c2ecf20Sopenharmony_ci		return rv;
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id);
27388c2ecf20Sopenharmony_ci}
27398c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(manufacturer_id);
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_cistatic ssize_t product_id_show(struct device *dev,
27428c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
27438c2ecf20Sopenharmony_ci			       char *buf)
27448c2ecf20Sopenharmony_ci{
27458c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
27468c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
27478c2ecf20Sopenharmony_ci	int rv;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
27508c2ecf20Sopenharmony_ci	if (rv)
27518c2ecf20Sopenharmony_ci		return rv;
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "0x%4.4x\n", id.product_id);
27548c2ecf20Sopenharmony_ci}
27558c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(product_id);
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_cistatic ssize_t aux_firmware_rev_show(struct device *dev,
27588c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
27598c2ecf20Sopenharmony_ci				     char *buf)
27608c2ecf20Sopenharmony_ci{
27618c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
27628c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
27638c2ecf20Sopenharmony_ci	int rv;
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
27668c2ecf20Sopenharmony_ci	if (rv)
27678c2ecf20Sopenharmony_ci		return rv;
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci	return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
27708c2ecf20Sopenharmony_ci			id.aux_firmware_revision[3],
27718c2ecf20Sopenharmony_ci			id.aux_firmware_revision[2],
27728c2ecf20Sopenharmony_ci			id.aux_firmware_revision[1],
27738c2ecf20Sopenharmony_ci			id.aux_firmware_revision[0]);
27748c2ecf20Sopenharmony_ci}
27758c2ecf20Sopenharmony_cistatic DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL);
27768c2ecf20Sopenharmony_ci
27778c2ecf20Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr,
27788c2ecf20Sopenharmony_ci			 char *buf)
27798c2ecf20Sopenharmony_ci{
27808c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
27818c2ecf20Sopenharmony_ci	bool guid_set;
27828c2ecf20Sopenharmony_ci	guid_t guid;
27838c2ecf20Sopenharmony_ci	int rv;
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci	rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, &guid);
27868c2ecf20Sopenharmony_ci	if (rv)
27878c2ecf20Sopenharmony_ci		return rv;
27888c2ecf20Sopenharmony_ci	if (!guid_set)
27898c2ecf20Sopenharmony_ci		return -ENOENT;
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
27928c2ecf20Sopenharmony_ci}
27938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(guid);
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_cistatic struct attribute *bmc_dev_attrs[] = {
27968c2ecf20Sopenharmony_ci	&dev_attr_device_id.attr,
27978c2ecf20Sopenharmony_ci	&dev_attr_provides_device_sdrs.attr,
27988c2ecf20Sopenharmony_ci	&dev_attr_revision.attr,
27998c2ecf20Sopenharmony_ci	&dev_attr_firmware_revision.attr,
28008c2ecf20Sopenharmony_ci	&dev_attr_ipmi_version.attr,
28018c2ecf20Sopenharmony_ci	&dev_attr_additional_device_support.attr,
28028c2ecf20Sopenharmony_ci	&dev_attr_manufacturer_id.attr,
28038c2ecf20Sopenharmony_ci	&dev_attr_product_id.attr,
28048c2ecf20Sopenharmony_ci	&dev_attr_aux_firmware_revision.attr,
28058c2ecf20Sopenharmony_ci	&dev_attr_guid.attr,
28068c2ecf20Sopenharmony_ci	NULL
28078c2ecf20Sopenharmony_ci};
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_cistatic umode_t bmc_dev_attr_is_visible(struct kobject *kobj,
28108c2ecf20Sopenharmony_ci				       struct attribute *attr, int idx)
28118c2ecf20Sopenharmony_ci{
28128c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
28138c2ecf20Sopenharmony_ci	struct bmc_device *bmc = to_bmc_device(dev);
28148c2ecf20Sopenharmony_ci	umode_t mode = attr->mode;
28158c2ecf20Sopenharmony_ci	int rv;
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci	if (attr == &dev_attr_aux_firmware_revision.attr) {
28188c2ecf20Sopenharmony_ci		struct ipmi_device_id id;
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci		rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
28218c2ecf20Sopenharmony_ci		return (!rv && id.aux_firmware_revision_set) ? mode : 0;
28228c2ecf20Sopenharmony_ci	}
28238c2ecf20Sopenharmony_ci	if (attr == &dev_attr_guid.attr) {
28248c2ecf20Sopenharmony_ci		bool guid_set;
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci		rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, NULL);
28278c2ecf20Sopenharmony_ci		return (!rv && guid_set) ? mode : 0;
28288c2ecf20Sopenharmony_ci	}
28298c2ecf20Sopenharmony_ci	return mode;
28308c2ecf20Sopenharmony_ci}
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_cistatic const struct attribute_group bmc_dev_attr_group = {
28338c2ecf20Sopenharmony_ci	.attrs		= bmc_dev_attrs,
28348c2ecf20Sopenharmony_ci	.is_visible	= bmc_dev_attr_is_visible,
28358c2ecf20Sopenharmony_ci};
28368c2ecf20Sopenharmony_ci
28378c2ecf20Sopenharmony_cistatic const struct attribute_group *bmc_dev_attr_groups[] = {
28388c2ecf20Sopenharmony_ci	&bmc_dev_attr_group,
28398c2ecf20Sopenharmony_ci	NULL
28408c2ecf20Sopenharmony_ci};
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_cistatic const struct device_type bmc_device_type = {
28438c2ecf20Sopenharmony_ci	.groups		= bmc_dev_attr_groups,
28448c2ecf20Sopenharmony_ci};
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_cistatic int __find_bmc_guid(struct device *dev, const void *data)
28478c2ecf20Sopenharmony_ci{
28488c2ecf20Sopenharmony_ci	const guid_t *guid = data;
28498c2ecf20Sopenharmony_ci	struct bmc_device *bmc;
28508c2ecf20Sopenharmony_ci	int rv;
28518c2ecf20Sopenharmony_ci
28528c2ecf20Sopenharmony_ci	if (dev->type != &bmc_device_type)
28538c2ecf20Sopenharmony_ci		return 0;
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_ci	bmc = to_bmc_device(dev);
28568c2ecf20Sopenharmony_ci	rv = bmc->dyn_guid_set && guid_equal(&bmc->guid, guid);
28578c2ecf20Sopenharmony_ci	if (rv)
28588c2ecf20Sopenharmony_ci		rv = kref_get_unless_zero(&bmc->usecount);
28598c2ecf20Sopenharmony_ci	return rv;
28608c2ecf20Sopenharmony_ci}
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci/*
28638c2ecf20Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL.
28648c2ecf20Sopenharmony_ci */
28658c2ecf20Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
28668c2ecf20Sopenharmony_ci					     guid_t *guid)
28678c2ecf20Sopenharmony_ci{
28688c2ecf20Sopenharmony_ci	struct device *dev;
28698c2ecf20Sopenharmony_ci	struct bmc_device *bmc = NULL;
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
28728c2ecf20Sopenharmony_ci	if (dev) {
28738c2ecf20Sopenharmony_ci		bmc = to_bmc_device(dev);
28748c2ecf20Sopenharmony_ci		put_device(dev);
28758c2ecf20Sopenharmony_ci	}
28768c2ecf20Sopenharmony_ci	return bmc;
28778c2ecf20Sopenharmony_ci}
28788c2ecf20Sopenharmony_ci
28798c2ecf20Sopenharmony_cistruct prod_dev_id {
28808c2ecf20Sopenharmony_ci	unsigned int  product_id;
28818c2ecf20Sopenharmony_ci	unsigned char device_id;
28828c2ecf20Sopenharmony_ci};
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_cistatic int __find_bmc_prod_dev_id(struct device *dev, const void *data)
28858c2ecf20Sopenharmony_ci{
28868c2ecf20Sopenharmony_ci	const struct prod_dev_id *cid = data;
28878c2ecf20Sopenharmony_ci	struct bmc_device *bmc;
28888c2ecf20Sopenharmony_ci	int rv;
28898c2ecf20Sopenharmony_ci
28908c2ecf20Sopenharmony_ci	if (dev->type != &bmc_device_type)
28918c2ecf20Sopenharmony_ci		return 0;
28928c2ecf20Sopenharmony_ci
28938c2ecf20Sopenharmony_ci	bmc = to_bmc_device(dev);
28948c2ecf20Sopenharmony_ci	rv = (bmc->id.product_id == cid->product_id
28958c2ecf20Sopenharmony_ci	      && bmc->id.device_id == cid->device_id);
28968c2ecf20Sopenharmony_ci	if (rv)
28978c2ecf20Sopenharmony_ci		rv = kref_get_unless_zero(&bmc->usecount);
28988c2ecf20Sopenharmony_ci	return rv;
28998c2ecf20Sopenharmony_ci}
29008c2ecf20Sopenharmony_ci
29018c2ecf20Sopenharmony_ci/*
29028c2ecf20Sopenharmony_ci * Returns with the bmc's usecount incremented, if it is non-NULL.
29038c2ecf20Sopenharmony_ci */
29048c2ecf20Sopenharmony_cistatic struct bmc_device *ipmi_find_bmc_prod_dev_id(
29058c2ecf20Sopenharmony_ci	struct device_driver *drv,
29068c2ecf20Sopenharmony_ci	unsigned int product_id, unsigned char device_id)
29078c2ecf20Sopenharmony_ci{
29088c2ecf20Sopenharmony_ci	struct prod_dev_id id = {
29098c2ecf20Sopenharmony_ci		.product_id = product_id,
29108c2ecf20Sopenharmony_ci		.device_id = device_id,
29118c2ecf20Sopenharmony_ci	};
29128c2ecf20Sopenharmony_ci	struct device *dev;
29138c2ecf20Sopenharmony_ci	struct bmc_device *bmc = NULL;
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
29168c2ecf20Sopenharmony_ci	if (dev) {
29178c2ecf20Sopenharmony_ci		bmc = to_bmc_device(dev);
29188c2ecf20Sopenharmony_ci		put_device(dev);
29198c2ecf20Sopenharmony_ci	}
29208c2ecf20Sopenharmony_ci	return bmc;
29218c2ecf20Sopenharmony_ci}
29228c2ecf20Sopenharmony_ci
29238c2ecf20Sopenharmony_cistatic DEFINE_IDA(ipmi_bmc_ida);
29248c2ecf20Sopenharmony_ci
29258c2ecf20Sopenharmony_cistatic void
29268c2ecf20Sopenharmony_cirelease_bmc_device(struct device *dev)
29278c2ecf20Sopenharmony_ci{
29288c2ecf20Sopenharmony_ci	kfree(to_bmc_device(dev));
29298c2ecf20Sopenharmony_ci}
29308c2ecf20Sopenharmony_ci
29318c2ecf20Sopenharmony_cistatic void cleanup_bmc_work(struct work_struct *work)
29328c2ecf20Sopenharmony_ci{
29338c2ecf20Sopenharmony_ci	struct bmc_device *bmc = container_of(work, struct bmc_device,
29348c2ecf20Sopenharmony_ci					      remove_work);
29358c2ecf20Sopenharmony_ci	int id = bmc->pdev.id; /* Unregister overwrites id */
29368c2ecf20Sopenharmony_ci
29378c2ecf20Sopenharmony_ci	platform_device_unregister(&bmc->pdev);
29388c2ecf20Sopenharmony_ci	ida_simple_remove(&ipmi_bmc_ida, id);
29398c2ecf20Sopenharmony_ci}
29408c2ecf20Sopenharmony_ci
29418c2ecf20Sopenharmony_cistatic void
29428c2ecf20Sopenharmony_cicleanup_bmc_device(struct kref *ref)
29438c2ecf20Sopenharmony_ci{
29448c2ecf20Sopenharmony_ci	struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount);
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci	/*
29478c2ecf20Sopenharmony_ci	 * Remove the platform device in a work queue to avoid issues
29488c2ecf20Sopenharmony_ci	 * with removing the device attributes while reading a device
29498c2ecf20Sopenharmony_ci	 * attribute.
29508c2ecf20Sopenharmony_ci	 */
29518c2ecf20Sopenharmony_ci	queue_work(remove_work_wq, &bmc->remove_work);
29528c2ecf20Sopenharmony_ci}
29538c2ecf20Sopenharmony_ci
29548c2ecf20Sopenharmony_ci/*
29558c2ecf20Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held.
29568c2ecf20Sopenharmony_ci */
29578c2ecf20Sopenharmony_cistatic void __ipmi_bmc_unregister(struct ipmi_smi *intf)
29588c2ecf20Sopenharmony_ci{
29598c2ecf20Sopenharmony_ci	struct bmc_device *bmc = intf->bmc;
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	if (!intf->bmc_registered)
29628c2ecf20Sopenharmony_ci		return;
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_ci	sysfs_remove_link(&intf->si_dev->kobj, "bmc");
29658c2ecf20Sopenharmony_ci	sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
29668c2ecf20Sopenharmony_ci	kfree(intf->my_dev_name);
29678c2ecf20Sopenharmony_ci	intf->my_dev_name = NULL;
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci	mutex_lock(&bmc->dyn_mutex);
29708c2ecf20Sopenharmony_ci	list_del(&intf->bmc_link);
29718c2ecf20Sopenharmony_ci	mutex_unlock(&bmc->dyn_mutex);
29728c2ecf20Sopenharmony_ci	intf->bmc = &intf->tmp_bmc;
29738c2ecf20Sopenharmony_ci	kref_put(&bmc->usecount, cleanup_bmc_device);
29748c2ecf20Sopenharmony_ci	intf->bmc_registered = false;
29758c2ecf20Sopenharmony_ci}
29768c2ecf20Sopenharmony_ci
29778c2ecf20Sopenharmony_cistatic void ipmi_bmc_unregister(struct ipmi_smi *intf)
29788c2ecf20Sopenharmony_ci{
29798c2ecf20Sopenharmony_ci	mutex_lock(&intf->bmc_reg_mutex);
29808c2ecf20Sopenharmony_ci	__ipmi_bmc_unregister(intf);
29818c2ecf20Sopenharmony_ci	mutex_unlock(&intf->bmc_reg_mutex);
29828c2ecf20Sopenharmony_ci}
29838c2ecf20Sopenharmony_ci
29848c2ecf20Sopenharmony_ci/*
29858c2ecf20Sopenharmony_ci * Must be called with intf->bmc_reg_mutex held.
29868c2ecf20Sopenharmony_ci */
29878c2ecf20Sopenharmony_cistatic int __ipmi_bmc_register(struct ipmi_smi *intf,
29888c2ecf20Sopenharmony_ci			       struct ipmi_device_id *id,
29898c2ecf20Sopenharmony_ci			       bool guid_set, guid_t *guid, int intf_num)
29908c2ecf20Sopenharmony_ci{
29918c2ecf20Sopenharmony_ci	int               rv;
29928c2ecf20Sopenharmony_ci	struct bmc_device *bmc;
29938c2ecf20Sopenharmony_ci	struct bmc_device *old_bmc;
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	/*
29968c2ecf20Sopenharmony_ci	 * platform_device_register() can cause bmc_reg_mutex to
29978c2ecf20Sopenharmony_ci	 * be claimed because of the is_visible functions of
29988c2ecf20Sopenharmony_ci	 * the attributes.  Eliminate possible recursion and
29998c2ecf20Sopenharmony_ci	 * release the lock.
30008c2ecf20Sopenharmony_ci	 */
30018c2ecf20Sopenharmony_ci	intf->in_bmc_register = true;
30028c2ecf20Sopenharmony_ci	mutex_unlock(&intf->bmc_reg_mutex);
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	/*
30058c2ecf20Sopenharmony_ci	 * Try to find if there is an bmc_device struct
30068c2ecf20Sopenharmony_ci	 * representing the interfaced BMC already
30078c2ecf20Sopenharmony_ci	 */
30088c2ecf20Sopenharmony_ci	mutex_lock(&ipmidriver_mutex);
30098c2ecf20Sopenharmony_ci	if (guid_set)
30108c2ecf20Sopenharmony_ci		old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid);
30118c2ecf20Sopenharmony_ci	else
30128c2ecf20Sopenharmony_ci		old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
30138c2ecf20Sopenharmony_ci						    id->product_id,
30148c2ecf20Sopenharmony_ci						    id->device_id);
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci	/*
30178c2ecf20Sopenharmony_ci	 * If there is already an bmc_device, free the new one,
30188c2ecf20Sopenharmony_ci	 * otherwise register the new BMC device
30198c2ecf20Sopenharmony_ci	 */
30208c2ecf20Sopenharmony_ci	if (old_bmc) {
30218c2ecf20Sopenharmony_ci		bmc = old_bmc;
30228c2ecf20Sopenharmony_ci		/*
30238c2ecf20Sopenharmony_ci		 * Note: old_bmc already has usecount incremented by
30248c2ecf20Sopenharmony_ci		 * the BMC find functions.
30258c2ecf20Sopenharmony_ci		 */
30268c2ecf20Sopenharmony_ci		intf->bmc = old_bmc;
30278c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
30288c2ecf20Sopenharmony_ci		list_add_tail(&intf->bmc_link, &bmc->intfs);
30298c2ecf20Sopenharmony_ci		mutex_unlock(&bmc->dyn_mutex);
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci		dev_info(intf->si_dev,
30328c2ecf20Sopenharmony_ci			 "interfacing existing BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
30338c2ecf20Sopenharmony_ci			 bmc->id.manufacturer_id,
30348c2ecf20Sopenharmony_ci			 bmc->id.product_id,
30358c2ecf20Sopenharmony_ci			 bmc->id.device_id);
30368c2ecf20Sopenharmony_ci	} else {
30378c2ecf20Sopenharmony_ci		bmc = kzalloc(sizeof(*bmc), GFP_KERNEL);
30388c2ecf20Sopenharmony_ci		if (!bmc) {
30398c2ecf20Sopenharmony_ci			rv = -ENOMEM;
30408c2ecf20Sopenharmony_ci			goto out;
30418c2ecf20Sopenharmony_ci		}
30428c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&bmc->intfs);
30438c2ecf20Sopenharmony_ci		mutex_init(&bmc->dyn_mutex);
30448c2ecf20Sopenharmony_ci		INIT_WORK(&bmc->remove_work, cleanup_bmc_work);
30458c2ecf20Sopenharmony_ci
30468c2ecf20Sopenharmony_ci		bmc->id = *id;
30478c2ecf20Sopenharmony_ci		bmc->dyn_id_set = 1;
30488c2ecf20Sopenharmony_ci		bmc->dyn_guid_set = guid_set;
30498c2ecf20Sopenharmony_ci		bmc->guid = *guid;
30508c2ecf20Sopenharmony_ci		bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_ci		bmc->pdev.name = "ipmi_bmc";
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci		rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL);
30558c2ecf20Sopenharmony_ci		if (rv < 0) {
30568c2ecf20Sopenharmony_ci			kfree(bmc);
30578c2ecf20Sopenharmony_ci			goto out;
30588c2ecf20Sopenharmony_ci		}
30598c2ecf20Sopenharmony_ci
30608c2ecf20Sopenharmony_ci		bmc->pdev.dev.driver = &ipmidriver.driver;
30618c2ecf20Sopenharmony_ci		bmc->pdev.id = rv;
30628c2ecf20Sopenharmony_ci		bmc->pdev.dev.release = release_bmc_device;
30638c2ecf20Sopenharmony_ci		bmc->pdev.dev.type = &bmc_device_type;
30648c2ecf20Sopenharmony_ci		kref_init(&bmc->usecount);
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci		intf->bmc = bmc;
30678c2ecf20Sopenharmony_ci		mutex_lock(&bmc->dyn_mutex);
30688c2ecf20Sopenharmony_ci		list_add_tail(&intf->bmc_link, &bmc->intfs);
30698c2ecf20Sopenharmony_ci		mutex_unlock(&bmc->dyn_mutex);
30708c2ecf20Sopenharmony_ci
30718c2ecf20Sopenharmony_ci		rv = platform_device_register(&bmc->pdev);
30728c2ecf20Sopenharmony_ci		if (rv) {
30738c2ecf20Sopenharmony_ci			dev_err(intf->si_dev,
30748c2ecf20Sopenharmony_ci				"Unable to register bmc device: %d\n",
30758c2ecf20Sopenharmony_ci				rv);
30768c2ecf20Sopenharmony_ci			goto out_list_del;
30778c2ecf20Sopenharmony_ci		}
30788c2ecf20Sopenharmony_ci
30798c2ecf20Sopenharmony_ci		dev_info(intf->si_dev,
30808c2ecf20Sopenharmony_ci			 "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
30818c2ecf20Sopenharmony_ci			 bmc->id.manufacturer_id,
30828c2ecf20Sopenharmony_ci			 bmc->id.product_id,
30838c2ecf20Sopenharmony_ci			 bmc->id.device_id);
30848c2ecf20Sopenharmony_ci	}
30858c2ecf20Sopenharmony_ci
30868c2ecf20Sopenharmony_ci	/*
30878c2ecf20Sopenharmony_ci	 * create symlink from system interface device to bmc device
30888c2ecf20Sopenharmony_ci	 * and back.
30898c2ecf20Sopenharmony_ci	 */
30908c2ecf20Sopenharmony_ci	rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc");
30918c2ecf20Sopenharmony_ci	if (rv) {
30928c2ecf20Sopenharmony_ci		dev_err(intf->si_dev, "Unable to create bmc symlink: %d\n", rv);
30938c2ecf20Sopenharmony_ci		goto out_put_bmc;
30948c2ecf20Sopenharmony_ci	}
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	if (intf_num == -1)
30978c2ecf20Sopenharmony_ci		intf_num = intf->intf_num;
30988c2ecf20Sopenharmony_ci	intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num);
30998c2ecf20Sopenharmony_ci	if (!intf->my_dev_name) {
31008c2ecf20Sopenharmony_ci		rv = -ENOMEM;
31018c2ecf20Sopenharmony_ci		dev_err(intf->si_dev, "Unable to allocate link from BMC: %d\n",
31028c2ecf20Sopenharmony_ci			rv);
31038c2ecf20Sopenharmony_ci		goto out_unlink1;
31048c2ecf20Sopenharmony_ci	}
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci	rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj,
31078c2ecf20Sopenharmony_ci			       intf->my_dev_name);
31088c2ecf20Sopenharmony_ci	if (rv) {
31098c2ecf20Sopenharmony_ci		dev_err(intf->si_dev, "Unable to create symlink to bmc: %d\n",
31108c2ecf20Sopenharmony_ci			rv);
31118c2ecf20Sopenharmony_ci		goto out_free_my_dev_name;
31128c2ecf20Sopenharmony_ci	}
31138c2ecf20Sopenharmony_ci
31148c2ecf20Sopenharmony_ci	intf->bmc_registered = true;
31158c2ecf20Sopenharmony_ci
31168c2ecf20Sopenharmony_ciout:
31178c2ecf20Sopenharmony_ci	mutex_unlock(&ipmidriver_mutex);
31188c2ecf20Sopenharmony_ci	mutex_lock(&intf->bmc_reg_mutex);
31198c2ecf20Sopenharmony_ci	intf->in_bmc_register = false;
31208c2ecf20Sopenharmony_ci	return rv;
31218c2ecf20Sopenharmony_ci
31228c2ecf20Sopenharmony_ci
31238c2ecf20Sopenharmony_ciout_free_my_dev_name:
31248c2ecf20Sopenharmony_ci	kfree(intf->my_dev_name);
31258c2ecf20Sopenharmony_ci	intf->my_dev_name = NULL;
31268c2ecf20Sopenharmony_ci
31278c2ecf20Sopenharmony_ciout_unlink1:
31288c2ecf20Sopenharmony_ci	sysfs_remove_link(&intf->si_dev->kobj, "bmc");
31298c2ecf20Sopenharmony_ci
31308c2ecf20Sopenharmony_ciout_put_bmc:
31318c2ecf20Sopenharmony_ci	mutex_lock(&bmc->dyn_mutex);
31328c2ecf20Sopenharmony_ci	list_del(&intf->bmc_link);
31338c2ecf20Sopenharmony_ci	mutex_unlock(&bmc->dyn_mutex);
31348c2ecf20Sopenharmony_ci	intf->bmc = &intf->tmp_bmc;
31358c2ecf20Sopenharmony_ci	kref_put(&bmc->usecount, cleanup_bmc_device);
31368c2ecf20Sopenharmony_ci	goto out;
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ciout_list_del:
31398c2ecf20Sopenharmony_ci	mutex_lock(&bmc->dyn_mutex);
31408c2ecf20Sopenharmony_ci	list_del(&intf->bmc_link);
31418c2ecf20Sopenharmony_ci	mutex_unlock(&bmc->dyn_mutex);
31428c2ecf20Sopenharmony_ci	intf->bmc = &intf->tmp_bmc;
31438c2ecf20Sopenharmony_ci	put_device(&bmc->pdev.dev);
31448c2ecf20Sopenharmony_ci	goto out;
31458c2ecf20Sopenharmony_ci}
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_cistatic int
31488c2ecf20Sopenharmony_cisend_guid_cmd(struct ipmi_smi *intf, int chan)
31498c2ecf20Sopenharmony_ci{
31508c2ecf20Sopenharmony_ci	struct kernel_ipmi_msg            msg;
31518c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr si;
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci	si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
31548c2ecf20Sopenharmony_ci	si.channel = IPMI_BMC_CHANNEL;
31558c2ecf20Sopenharmony_ci	si.lun = 0;
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_ci	msg.netfn = IPMI_NETFN_APP_REQUEST;
31588c2ecf20Sopenharmony_ci	msg.cmd = IPMI_GET_DEVICE_GUID_CMD;
31598c2ecf20Sopenharmony_ci	msg.data = NULL;
31608c2ecf20Sopenharmony_ci	msg.data_len = 0;
31618c2ecf20Sopenharmony_ci	return i_ipmi_request(NULL,
31628c2ecf20Sopenharmony_ci			      intf,
31638c2ecf20Sopenharmony_ci			      (struct ipmi_addr *) &si,
31648c2ecf20Sopenharmony_ci			      0,
31658c2ecf20Sopenharmony_ci			      &msg,
31668c2ecf20Sopenharmony_ci			      intf,
31678c2ecf20Sopenharmony_ci			      NULL,
31688c2ecf20Sopenharmony_ci			      NULL,
31698c2ecf20Sopenharmony_ci			      0,
31708c2ecf20Sopenharmony_ci			      intf->addrinfo[0].address,
31718c2ecf20Sopenharmony_ci			      intf->addrinfo[0].lun,
31728c2ecf20Sopenharmony_ci			      -1, 0);
31738c2ecf20Sopenharmony_ci}
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_cistatic void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
31768c2ecf20Sopenharmony_ci{
31778c2ecf20Sopenharmony_ci	struct bmc_device *bmc = intf->bmc;
31788c2ecf20Sopenharmony_ci
31798c2ecf20Sopenharmony_ci	if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
31808c2ecf20Sopenharmony_ci	    || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
31818c2ecf20Sopenharmony_ci	    || (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD))
31828c2ecf20Sopenharmony_ci		/* Not for me */
31838c2ecf20Sopenharmony_ci		return;
31848c2ecf20Sopenharmony_ci
31858c2ecf20Sopenharmony_ci	if (msg->msg.data[0] != 0) {
31868c2ecf20Sopenharmony_ci		/* Error from getting the GUID, the BMC doesn't have one. */
31878c2ecf20Sopenharmony_ci		bmc->dyn_guid_set = 0;
31888c2ecf20Sopenharmony_ci		goto out;
31898c2ecf20Sopenharmony_ci	}
31908c2ecf20Sopenharmony_ci
31918c2ecf20Sopenharmony_ci	if (msg->msg.data_len < UUID_SIZE + 1) {
31928c2ecf20Sopenharmony_ci		bmc->dyn_guid_set = 0;
31938c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
31948c2ecf20Sopenharmony_ci			 "The GUID response from the BMC was too short, it was %d but should have been %d.  Assuming GUID is not available.\n",
31958c2ecf20Sopenharmony_ci			 msg->msg.data_len, UUID_SIZE + 1);
31968c2ecf20Sopenharmony_ci		goto out;
31978c2ecf20Sopenharmony_ci	}
31988c2ecf20Sopenharmony_ci
31998c2ecf20Sopenharmony_ci	import_guid(&bmc->fetch_guid, msg->msg.data + 1);
32008c2ecf20Sopenharmony_ci	/*
32018c2ecf20Sopenharmony_ci	 * Make sure the guid data is available before setting
32028c2ecf20Sopenharmony_ci	 * dyn_guid_set.
32038c2ecf20Sopenharmony_ci	 */
32048c2ecf20Sopenharmony_ci	smp_wmb();
32058c2ecf20Sopenharmony_ci	bmc->dyn_guid_set = 1;
32068c2ecf20Sopenharmony_ci out:
32078c2ecf20Sopenharmony_ci	wake_up(&intf->waitq);
32088c2ecf20Sopenharmony_ci}
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_cistatic void __get_guid(struct ipmi_smi *intf)
32118c2ecf20Sopenharmony_ci{
32128c2ecf20Sopenharmony_ci	int rv;
32138c2ecf20Sopenharmony_ci	struct bmc_device *bmc = intf->bmc;
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci	bmc->dyn_guid_set = 2;
32168c2ecf20Sopenharmony_ci	intf->null_user_handler = guid_handler;
32178c2ecf20Sopenharmony_ci	rv = send_guid_cmd(intf, 0);
32188c2ecf20Sopenharmony_ci	if (rv)
32198c2ecf20Sopenharmony_ci		/* Send failed, no GUID available. */
32208c2ecf20Sopenharmony_ci		bmc->dyn_guid_set = 0;
32218c2ecf20Sopenharmony_ci	else
32228c2ecf20Sopenharmony_ci		wait_event(intf->waitq, bmc->dyn_guid_set != 2);
32238c2ecf20Sopenharmony_ci
32248c2ecf20Sopenharmony_ci	/* dyn_guid_set makes the guid data available. */
32258c2ecf20Sopenharmony_ci	smp_rmb();
32268c2ecf20Sopenharmony_ci
32278c2ecf20Sopenharmony_ci	intf->null_user_handler = NULL;
32288c2ecf20Sopenharmony_ci}
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_cistatic int
32318c2ecf20Sopenharmony_cisend_channel_info_cmd(struct ipmi_smi *intf, int chan)
32328c2ecf20Sopenharmony_ci{
32338c2ecf20Sopenharmony_ci	struct kernel_ipmi_msg            msg;
32348c2ecf20Sopenharmony_ci	unsigned char                     data[1];
32358c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr si;
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_ci	si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
32388c2ecf20Sopenharmony_ci	si.channel = IPMI_BMC_CHANNEL;
32398c2ecf20Sopenharmony_ci	si.lun = 0;
32408c2ecf20Sopenharmony_ci
32418c2ecf20Sopenharmony_ci	msg.netfn = IPMI_NETFN_APP_REQUEST;
32428c2ecf20Sopenharmony_ci	msg.cmd = IPMI_GET_CHANNEL_INFO_CMD;
32438c2ecf20Sopenharmony_ci	msg.data = data;
32448c2ecf20Sopenharmony_ci	msg.data_len = 1;
32458c2ecf20Sopenharmony_ci	data[0] = chan;
32468c2ecf20Sopenharmony_ci	return i_ipmi_request(NULL,
32478c2ecf20Sopenharmony_ci			      intf,
32488c2ecf20Sopenharmony_ci			      (struct ipmi_addr *) &si,
32498c2ecf20Sopenharmony_ci			      0,
32508c2ecf20Sopenharmony_ci			      &msg,
32518c2ecf20Sopenharmony_ci			      intf,
32528c2ecf20Sopenharmony_ci			      NULL,
32538c2ecf20Sopenharmony_ci			      NULL,
32548c2ecf20Sopenharmony_ci			      0,
32558c2ecf20Sopenharmony_ci			      intf->addrinfo[0].address,
32568c2ecf20Sopenharmony_ci			      intf->addrinfo[0].lun,
32578c2ecf20Sopenharmony_ci			      -1, 0);
32588c2ecf20Sopenharmony_ci}
32598c2ecf20Sopenharmony_ci
32608c2ecf20Sopenharmony_cistatic void
32618c2ecf20Sopenharmony_cichannel_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
32628c2ecf20Sopenharmony_ci{
32638c2ecf20Sopenharmony_ci	int rv = 0;
32648c2ecf20Sopenharmony_ci	int ch;
32658c2ecf20Sopenharmony_ci	unsigned int set = intf->curr_working_cset;
32668c2ecf20Sopenharmony_ci	struct ipmi_channel *chans;
32678c2ecf20Sopenharmony_ci
32688c2ecf20Sopenharmony_ci	if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
32698c2ecf20Sopenharmony_ci	    && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
32708c2ecf20Sopenharmony_ci	    && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) {
32718c2ecf20Sopenharmony_ci		/* It's the one we want */
32728c2ecf20Sopenharmony_ci		if (msg->msg.data[0] != 0) {
32738c2ecf20Sopenharmony_ci			/* Got an error from the channel, just go on. */
32748c2ecf20Sopenharmony_ci			if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) {
32758c2ecf20Sopenharmony_ci				/*
32768c2ecf20Sopenharmony_ci				 * If the MC does not support this
32778c2ecf20Sopenharmony_ci				 * command, that is legal.  We just
32788c2ecf20Sopenharmony_ci				 * assume it has one IPMB at channel
32798c2ecf20Sopenharmony_ci				 * zero.
32808c2ecf20Sopenharmony_ci				 */
32818c2ecf20Sopenharmony_ci				intf->wchannels[set].c[0].medium
32828c2ecf20Sopenharmony_ci					= IPMI_CHANNEL_MEDIUM_IPMB;
32838c2ecf20Sopenharmony_ci				intf->wchannels[set].c[0].protocol
32848c2ecf20Sopenharmony_ci					= IPMI_CHANNEL_PROTOCOL_IPMB;
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci				intf->channel_list = intf->wchannels + set;
32878c2ecf20Sopenharmony_ci				intf->channels_ready = true;
32888c2ecf20Sopenharmony_ci				wake_up(&intf->waitq);
32898c2ecf20Sopenharmony_ci				goto out;
32908c2ecf20Sopenharmony_ci			}
32918c2ecf20Sopenharmony_ci			goto next_channel;
32928c2ecf20Sopenharmony_ci		}
32938c2ecf20Sopenharmony_ci		if (msg->msg.data_len < 4) {
32948c2ecf20Sopenharmony_ci			/* Message not big enough, just go on. */
32958c2ecf20Sopenharmony_ci			goto next_channel;
32968c2ecf20Sopenharmony_ci		}
32978c2ecf20Sopenharmony_ci		ch = intf->curr_channel;
32988c2ecf20Sopenharmony_ci		chans = intf->wchannels[set].c;
32998c2ecf20Sopenharmony_ci		chans[ch].medium = msg->msg.data[2] & 0x7f;
33008c2ecf20Sopenharmony_ci		chans[ch].protocol = msg->msg.data[3] & 0x1f;
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci next_channel:
33038c2ecf20Sopenharmony_ci		intf->curr_channel++;
33048c2ecf20Sopenharmony_ci		if (intf->curr_channel >= IPMI_MAX_CHANNELS) {
33058c2ecf20Sopenharmony_ci			intf->channel_list = intf->wchannels + set;
33068c2ecf20Sopenharmony_ci			intf->channels_ready = true;
33078c2ecf20Sopenharmony_ci			wake_up(&intf->waitq);
33088c2ecf20Sopenharmony_ci		} else {
33098c2ecf20Sopenharmony_ci			intf->channel_list = intf->wchannels + set;
33108c2ecf20Sopenharmony_ci			intf->channels_ready = true;
33118c2ecf20Sopenharmony_ci			rv = send_channel_info_cmd(intf, intf->curr_channel);
33128c2ecf20Sopenharmony_ci		}
33138c2ecf20Sopenharmony_ci
33148c2ecf20Sopenharmony_ci		if (rv) {
33158c2ecf20Sopenharmony_ci			/* Got an error somehow, just give up. */
33168c2ecf20Sopenharmony_ci			dev_warn(intf->si_dev,
33178c2ecf20Sopenharmony_ci				 "Error sending channel information for channel %d: %d\n",
33188c2ecf20Sopenharmony_ci				 intf->curr_channel, rv);
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci			intf->channel_list = intf->wchannels + set;
33218c2ecf20Sopenharmony_ci			intf->channels_ready = true;
33228c2ecf20Sopenharmony_ci			wake_up(&intf->waitq);
33238c2ecf20Sopenharmony_ci		}
33248c2ecf20Sopenharmony_ci	}
33258c2ecf20Sopenharmony_ci out:
33268c2ecf20Sopenharmony_ci	return;
33278c2ecf20Sopenharmony_ci}
33288c2ecf20Sopenharmony_ci
33298c2ecf20Sopenharmony_ci/*
33308c2ecf20Sopenharmony_ci * Must be holding intf->bmc_reg_mutex to call this.
33318c2ecf20Sopenharmony_ci */
33328c2ecf20Sopenharmony_cistatic int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id)
33338c2ecf20Sopenharmony_ci{
33348c2ecf20Sopenharmony_ci	int rv;
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci	if (ipmi_version_major(id) > 1
33378c2ecf20Sopenharmony_ci			|| (ipmi_version_major(id) == 1
33388c2ecf20Sopenharmony_ci			    && ipmi_version_minor(id) >= 5)) {
33398c2ecf20Sopenharmony_ci		unsigned int set;
33408c2ecf20Sopenharmony_ci
33418c2ecf20Sopenharmony_ci		/*
33428c2ecf20Sopenharmony_ci		 * Start scanning the channels to see what is
33438c2ecf20Sopenharmony_ci		 * available.
33448c2ecf20Sopenharmony_ci		 */
33458c2ecf20Sopenharmony_ci		set = !intf->curr_working_cset;
33468c2ecf20Sopenharmony_ci		intf->curr_working_cset = set;
33478c2ecf20Sopenharmony_ci		memset(&intf->wchannels[set], 0,
33488c2ecf20Sopenharmony_ci		       sizeof(struct ipmi_channel_set));
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_ci		intf->null_user_handler = channel_handler;
33518c2ecf20Sopenharmony_ci		intf->curr_channel = 0;
33528c2ecf20Sopenharmony_ci		rv = send_channel_info_cmd(intf, 0);
33538c2ecf20Sopenharmony_ci		if (rv) {
33548c2ecf20Sopenharmony_ci			dev_warn(intf->si_dev,
33558c2ecf20Sopenharmony_ci				 "Error sending channel information for channel 0, %d\n",
33568c2ecf20Sopenharmony_ci				 rv);
33578c2ecf20Sopenharmony_ci			intf->null_user_handler = NULL;
33588c2ecf20Sopenharmony_ci			return -EIO;
33598c2ecf20Sopenharmony_ci		}
33608c2ecf20Sopenharmony_ci
33618c2ecf20Sopenharmony_ci		/* Wait for the channel info to be read. */
33628c2ecf20Sopenharmony_ci		wait_event(intf->waitq, intf->channels_ready);
33638c2ecf20Sopenharmony_ci		intf->null_user_handler = NULL;
33648c2ecf20Sopenharmony_ci	} else {
33658c2ecf20Sopenharmony_ci		unsigned int set = intf->curr_working_cset;
33668c2ecf20Sopenharmony_ci
33678c2ecf20Sopenharmony_ci		/* Assume a single IPMB channel at zero. */
33688c2ecf20Sopenharmony_ci		intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
33698c2ecf20Sopenharmony_ci		intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
33708c2ecf20Sopenharmony_ci		intf->channel_list = intf->wchannels + set;
33718c2ecf20Sopenharmony_ci		intf->channels_ready = true;
33728c2ecf20Sopenharmony_ci	}
33738c2ecf20Sopenharmony_ci
33748c2ecf20Sopenharmony_ci	return 0;
33758c2ecf20Sopenharmony_ci}
33768c2ecf20Sopenharmony_ci
33778c2ecf20Sopenharmony_cistatic void ipmi_poll(struct ipmi_smi *intf)
33788c2ecf20Sopenharmony_ci{
33798c2ecf20Sopenharmony_ci	if (intf->handlers->poll)
33808c2ecf20Sopenharmony_ci		intf->handlers->poll(intf->send_info);
33818c2ecf20Sopenharmony_ci	/* In case something came in */
33828c2ecf20Sopenharmony_ci	handle_new_recv_msgs(intf);
33838c2ecf20Sopenharmony_ci}
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_civoid ipmi_poll_interface(struct ipmi_user *user)
33868c2ecf20Sopenharmony_ci{
33878c2ecf20Sopenharmony_ci	ipmi_poll(user->intf);
33888c2ecf20Sopenharmony_ci}
33898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_poll_interface);
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_cistatic void redo_bmc_reg(struct work_struct *work)
33928c2ecf20Sopenharmony_ci{
33938c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = container_of(work, struct ipmi_smi,
33948c2ecf20Sopenharmony_ci					     bmc_reg_work);
33958c2ecf20Sopenharmony_ci
33968c2ecf20Sopenharmony_ci	if (!intf->in_shutdown)
33978c2ecf20Sopenharmony_ci		bmc_get_device_id(intf, NULL, NULL, NULL, NULL);
33988c2ecf20Sopenharmony_ci
33998c2ecf20Sopenharmony_ci	kref_put(&intf->refcount, intf_free);
34008c2ecf20Sopenharmony_ci}
34018c2ecf20Sopenharmony_ci
34028c2ecf20Sopenharmony_ciint ipmi_add_smi(struct module         *owner,
34038c2ecf20Sopenharmony_ci		 const struct ipmi_smi_handlers *handlers,
34048c2ecf20Sopenharmony_ci		 void		       *send_info,
34058c2ecf20Sopenharmony_ci		 struct device         *si_dev,
34068c2ecf20Sopenharmony_ci		 unsigned char         slave_addr)
34078c2ecf20Sopenharmony_ci{
34088c2ecf20Sopenharmony_ci	int              i, j;
34098c2ecf20Sopenharmony_ci	int              rv;
34108c2ecf20Sopenharmony_ci	struct ipmi_smi *intf, *tintf;
34118c2ecf20Sopenharmony_ci	struct list_head *link;
34128c2ecf20Sopenharmony_ci	struct ipmi_device_id id;
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	/*
34158c2ecf20Sopenharmony_ci	 * Make sure the driver is actually initialized, this handles
34168c2ecf20Sopenharmony_ci	 * problems with initialization order.
34178c2ecf20Sopenharmony_ci	 */
34188c2ecf20Sopenharmony_ci	rv = ipmi_init_msghandler();
34198c2ecf20Sopenharmony_ci	if (rv)
34208c2ecf20Sopenharmony_ci		return rv;
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ci	intf = kzalloc(sizeof(*intf), GFP_KERNEL);
34238c2ecf20Sopenharmony_ci	if (!intf)
34248c2ecf20Sopenharmony_ci		return -ENOMEM;
34258c2ecf20Sopenharmony_ci
34268c2ecf20Sopenharmony_ci	rv = init_srcu_struct(&intf->users_srcu);
34278c2ecf20Sopenharmony_ci	if (rv) {
34288c2ecf20Sopenharmony_ci		kfree(intf);
34298c2ecf20Sopenharmony_ci		return rv;
34308c2ecf20Sopenharmony_ci	}
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_ci	intf->owner = owner;
34338c2ecf20Sopenharmony_ci	intf->bmc = &intf->tmp_bmc;
34348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->bmc->intfs);
34358c2ecf20Sopenharmony_ci	mutex_init(&intf->bmc->dyn_mutex);
34368c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->bmc_link);
34378c2ecf20Sopenharmony_ci	mutex_init(&intf->bmc_reg_mutex);
34388c2ecf20Sopenharmony_ci	intf->intf_num = -1; /* Mark it invalid for now. */
34398c2ecf20Sopenharmony_ci	kref_init(&intf->refcount);
34408c2ecf20Sopenharmony_ci	INIT_WORK(&intf->bmc_reg_work, redo_bmc_reg);
34418c2ecf20Sopenharmony_ci	intf->si_dev = si_dev;
34428c2ecf20Sopenharmony_ci	for (j = 0; j < IPMI_MAX_CHANNELS; j++) {
34438c2ecf20Sopenharmony_ci		intf->addrinfo[j].address = IPMI_BMC_SLAVE_ADDR;
34448c2ecf20Sopenharmony_ci		intf->addrinfo[j].lun = 2;
34458c2ecf20Sopenharmony_ci	}
34468c2ecf20Sopenharmony_ci	if (slave_addr != 0)
34478c2ecf20Sopenharmony_ci		intf->addrinfo[0].address = slave_addr;
34488c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->users);
34498c2ecf20Sopenharmony_ci	intf->handlers = handlers;
34508c2ecf20Sopenharmony_ci	intf->send_info = send_info;
34518c2ecf20Sopenharmony_ci	spin_lock_init(&intf->seq_lock);
34528c2ecf20Sopenharmony_ci	for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) {
34538c2ecf20Sopenharmony_ci		intf->seq_table[j].inuse = 0;
34548c2ecf20Sopenharmony_ci		intf->seq_table[j].seqid = 0;
34558c2ecf20Sopenharmony_ci	}
34568c2ecf20Sopenharmony_ci	intf->curr_seq = 0;
34578c2ecf20Sopenharmony_ci	spin_lock_init(&intf->waiting_rcv_msgs_lock);
34588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->waiting_rcv_msgs);
34598c2ecf20Sopenharmony_ci	tasklet_setup(&intf->recv_tasklet,
34608c2ecf20Sopenharmony_ci		     smi_recv_tasklet);
34618c2ecf20Sopenharmony_ci	atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0);
34628c2ecf20Sopenharmony_ci	spin_lock_init(&intf->xmit_msgs_lock);
34638c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->xmit_msgs);
34648c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->hp_xmit_msgs);
34658c2ecf20Sopenharmony_ci	spin_lock_init(&intf->events_lock);
34668c2ecf20Sopenharmony_ci	spin_lock_init(&intf->watch_lock);
34678c2ecf20Sopenharmony_ci	atomic_set(&intf->event_waiters, 0);
34688c2ecf20Sopenharmony_ci	intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
34698c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->waiting_events);
34708c2ecf20Sopenharmony_ci	intf->waiting_events_count = 0;
34718c2ecf20Sopenharmony_ci	mutex_init(&intf->cmd_rcvrs_mutex);
34728c2ecf20Sopenharmony_ci	spin_lock_init(&intf->maintenance_mode_lock);
34738c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&intf->cmd_rcvrs);
34748c2ecf20Sopenharmony_ci	init_waitqueue_head(&intf->waitq);
34758c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_NUM_STATS; i++)
34768c2ecf20Sopenharmony_ci		atomic_set(&intf->stats[i], 0);
34778c2ecf20Sopenharmony_ci
34788c2ecf20Sopenharmony_ci	mutex_lock(&ipmi_interfaces_mutex);
34798c2ecf20Sopenharmony_ci	/* Look for a hole in the numbers. */
34808c2ecf20Sopenharmony_ci	i = 0;
34818c2ecf20Sopenharmony_ci	link = &ipmi_interfaces;
34828c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(tintf, &ipmi_interfaces, link,
34838c2ecf20Sopenharmony_ci				ipmi_interfaces_mutex_held()) {
34848c2ecf20Sopenharmony_ci		if (tintf->intf_num != i) {
34858c2ecf20Sopenharmony_ci			link = &tintf->link;
34868c2ecf20Sopenharmony_ci			break;
34878c2ecf20Sopenharmony_ci		}
34888c2ecf20Sopenharmony_ci		i++;
34898c2ecf20Sopenharmony_ci	}
34908c2ecf20Sopenharmony_ci	/* Add the new interface in numeric order. */
34918c2ecf20Sopenharmony_ci	if (i == 0)
34928c2ecf20Sopenharmony_ci		list_add_rcu(&intf->link, &ipmi_interfaces);
34938c2ecf20Sopenharmony_ci	else
34948c2ecf20Sopenharmony_ci		list_add_tail_rcu(&intf->link, link);
34958c2ecf20Sopenharmony_ci
34968c2ecf20Sopenharmony_ci	rv = handlers->start_processing(send_info, intf);
34978c2ecf20Sopenharmony_ci	if (rv)
34988c2ecf20Sopenharmony_ci		goto out_err;
34998c2ecf20Sopenharmony_ci
35008c2ecf20Sopenharmony_ci	rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i);
35018c2ecf20Sopenharmony_ci	if (rv) {
35028c2ecf20Sopenharmony_ci		dev_err(si_dev, "Unable to get the device id: %d\n", rv);
35038c2ecf20Sopenharmony_ci		goto out_err_started;
35048c2ecf20Sopenharmony_ci	}
35058c2ecf20Sopenharmony_ci
35068c2ecf20Sopenharmony_ci	mutex_lock(&intf->bmc_reg_mutex);
35078c2ecf20Sopenharmony_ci	rv = __scan_channels(intf, &id);
35088c2ecf20Sopenharmony_ci	mutex_unlock(&intf->bmc_reg_mutex);
35098c2ecf20Sopenharmony_ci	if (rv)
35108c2ecf20Sopenharmony_ci		goto out_err_bmc_reg;
35118c2ecf20Sopenharmony_ci
35128c2ecf20Sopenharmony_ci	/*
35138c2ecf20Sopenharmony_ci	 * Keep memory order straight for RCU readers.  Make
35148c2ecf20Sopenharmony_ci	 * sure everything else is committed to memory before
35158c2ecf20Sopenharmony_ci	 * setting intf_num to mark the interface valid.
35168c2ecf20Sopenharmony_ci	 */
35178c2ecf20Sopenharmony_ci	smp_wmb();
35188c2ecf20Sopenharmony_ci	intf->intf_num = i;
35198c2ecf20Sopenharmony_ci	mutex_unlock(&ipmi_interfaces_mutex);
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	/* After this point the interface is legal to use. */
35228c2ecf20Sopenharmony_ci	call_smi_watchers(i, intf->si_dev);
35238c2ecf20Sopenharmony_ci
35248c2ecf20Sopenharmony_ci	return 0;
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_ci out_err_bmc_reg:
35278c2ecf20Sopenharmony_ci	ipmi_bmc_unregister(intf);
35288c2ecf20Sopenharmony_ci out_err_started:
35298c2ecf20Sopenharmony_ci	if (intf->handlers->shutdown)
35308c2ecf20Sopenharmony_ci		intf->handlers->shutdown(intf->send_info);
35318c2ecf20Sopenharmony_ci out_err:
35328c2ecf20Sopenharmony_ci	list_del_rcu(&intf->link);
35338c2ecf20Sopenharmony_ci	mutex_unlock(&ipmi_interfaces_mutex);
35348c2ecf20Sopenharmony_ci	synchronize_srcu(&ipmi_interfaces_srcu);
35358c2ecf20Sopenharmony_ci	cleanup_srcu_struct(&intf->users_srcu);
35368c2ecf20Sopenharmony_ci	kref_put(&intf->refcount, intf_free);
35378c2ecf20Sopenharmony_ci
35388c2ecf20Sopenharmony_ci	return rv;
35398c2ecf20Sopenharmony_ci}
35408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_add_smi);
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_cistatic void deliver_smi_err_response(struct ipmi_smi *intf,
35438c2ecf20Sopenharmony_ci				     struct ipmi_smi_msg *msg,
35448c2ecf20Sopenharmony_ci				     unsigned char err)
35458c2ecf20Sopenharmony_ci{
35468c2ecf20Sopenharmony_ci	int rv;
35478c2ecf20Sopenharmony_ci	msg->rsp[0] = msg->data[0] | 4;
35488c2ecf20Sopenharmony_ci	msg->rsp[1] = msg->data[1];
35498c2ecf20Sopenharmony_ci	msg->rsp[2] = err;
35508c2ecf20Sopenharmony_ci	msg->rsp_size = 3;
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_ci	/* This will never requeue, but it may ask us to free the message. */
35538c2ecf20Sopenharmony_ci	rv = handle_one_recv_msg(intf, msg);
35548c2ecf20Sopenharmony_ci	if (rv == 0)
35558c2ecf20Sopenharmony_ci		ipmi_free_smi_msg(msg);
35568c2ecf20Sopenharmony_ci}
35578c2ecf20Sopenharmony_ci
35588c2ecf20Sopenharmony_cistatic void cleanup_smi_msgs(struct ipmi_smi *intf)
35598c2ecf20Sopenharmony_ci{
35608c2ecf20Sopenharmony_ci	int              i;
35618c2ecf20Sopenharmony_ci	struct seq_table *ent;
35628c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg;
35638c2ecf20Sopenharmony_ci	struct list_head *entry;
35648c2ecf20Sopenharmony_ci	struct list_head tmplist;
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_ci	/* Clear out our transmit queues and hold the messages. */
35678c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tmplist);
35688c2ecf20Sopenharmony_ci	list_splice_tail(&intf->hp_xmit_msgs, &tmplist);
35698c2ecf20Sopenharmony_ci	list_splice_tail(&intf->xmit_msgs, &tmplist);
35708c2ecf20Sopenharmony_ci
35718c2ecf20Sopenharmony_ci	/* Current message first, to preserve order */
35728c2ecf20Sopenharmony_ci	while (intf->curr_msg && !list_empty(&intf->waiting_rcv_msgs)) {
35738c2ecf20Sopenharmony_ci		/* Wait for the message to clear out. */
35748c2ecf20Sopenharmony_ci		schedule_timeout(1);
35758c2ecf20Sopenharmony_ci	}
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	/* No need for locks, the interface is down. */
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci	/*
35808c2ecf20Sopenharmony_ci	 * Return errors for all pending messages in queue and in the
35818c2ecf20Sopenharmony_ci	 * tables waiting for remote responses.
35828c2ecf20Sopenharmony_ci	 */
35838c2ecf20Sopenharmony_ci	while (!list_empty(&tmplist)) {
35848c2ecf20Sopenharmony_ci		entry = tmplist.next;
35858c2ecf20Sopenharmony_ci		list_del(entry);
35868c2ecf20Sopenharmony_ci		msg = list_entry(entry, struct ipmi_smi_msg, link);
35878c2ecf20Sopenharmony_ci		deliver_smi_err_response(intf, msg, IPMI_ERR_UNSPECIFIED);
35888c2ecf20Sopenharmony_ci	}
35898c2ecf20Sopenharmony_ci
35908c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
35918c2ecf20Sopenharmony_ci		ent = &intf->seq_table[i];
35928c2ecf20Sopenharmony_ci		if (!ent->inuse)
35938c2ecf20Sopenharmony_ci			continue;
35948c2ecf20Sopenharmony_ci		deliver_err_response(intf, ent->recv_msg, IPMI_ERR_UNSPECIFIED);
35958c2ecf20Sopenharmony_ci	}
35968c2ecf20Sopenharmony_ci}
35978c2ecf20Sopenharmony_ci
35988c2ecf20Sopenharmony_civoid ipmi_unregister_smi(struct ipmi_smi *intf)
35998c2ecf20Sopenharmony_ci{
36008c2ecf20Sopenharmony_ci	struct ipmi_smi_watcher *w;
36018c2ecf20Sopenharmony_ci	int intf_num = intf->intf_num, index;
36028c2ecf20Sopenharmony_ci
36038c2ecf20Sopenharmony_ci	mutex_lock(&ipmi_interfaces_mutex);
36048c2ecf20Sopenharmony_ci	intf->intf_num = -1;
36058c2ecf20Sopenharmony_ci	intf->in_shutdown = true;
36068c2ecf20Sopenharmony_ci	list_del_rcu(&intf->link);
36078c2ecf20Sopenharmony_ci	mutex_unlock(&ipmi_interfaces_mutex);
36088c2ecf20Sopenharmony_ci	synchronize_srcu(&ipmi_interfaces_srcu);
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ci	/* At this point no users can be added to the interface. */
36118c2ecf20Sopenharmony_ci
36128c2ecf20Sopenharmony_ci	/*
36138c2ecf20Sopenharmony_ci	 * Call all the watcher interfaces to tell them that
36148c2ecf20Sopenharmony_ci	 * an interface is going away.
36158c2ecf20Sopenharmony_ci	 */
36168c2ecf20Sopenharmony_ci	mutex_lock(&smi_watchers_mutex);
36178c2ecf20Sopenharmony_ci	list_for_each_entry(w, &smi_watchers, link)
36188c2ecf20Sopenharmony_ci		w->smi_gone(intf_num);
36198c2ecf20Sopenharmony_ci	mutex_unlock(&smi_watchers_mutex);
36208c2ecf20Sopenharmony_ci
36218c2ecf20Sopenharmony_ci	index = srcu_read_lock(&intf->users_srcu);
36228c2ecf20Sopenharmony_ci	while (!list_empty(&intf->users)) {
36238c2ecf20Sopenharmony_ci		struct ipmi_user *user =
36248c2ecf20Sopenharmony_ci			container_of(list_next_rcu(&intf->users),
36258c2ecf20Sopenharmony_ci				     struct ipmi_user, link);
36268c2ecf20Sopenharmony_ci
36278c2ecf20Sopenharmony_ci		_ipmi_destroy_user(user);
36288c2ecf20Sopenharmony_ci	}
36298c2ecf20Sopenharmony_ci	srcu_read_unlock(&intf->users_srcu, index);
36308c2ecf20Sopenharmony_ci
36318c2ecf20Sopenharmony_ci	if (intf->handlers->shutdown)
36328c2ecf20Sopenharmony_ci		intf->handlers->shutdown(intf->send_info);
36338c2ecf20Sopenharmony_ci
36348c2ecf20Sopenharmony_ci	cleanup_smi_msgs(intf);
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci	ipmi_bmc_unregister(intf);
36378c2ecf20Sopenharmony_ci
36388c2ecf20Sopenharmony_ci	cleanup_srcu_struct(&intf->users_srcu);
36398c2ecf20Sopenharmony_ci	kref_put(&intf->refcount, intf_free);
36408c2ecf20Sopenharmony_ci}
36418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_unregister_smi);
36428c2ecf20Sopenharmony_ci
36438c2ecf20Sopenharmony_cistatic int handle_ipmb_get_msg_rsp(struct ipmi_smi *intf,
36448c2ecf20Sopenharmony_ci				   struct ipmi_smi_msg *msg)
36458c2ecf20Sopenharmony_ci{
36468c2ecf20Sopenharmony_ci	struct ipmi_ipmb_addr ipmb_addr;
36478c2ecf20Sopenharmony_ci	struct ipmi_recv_msg  *recv_msg;
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_ci	/*
36508c2ecf20Sopenharmony_ci	 * This is 11, not 10, because the response must contain a
36518c2ecf20Sopenharmony_ci	 * completion code.
36528c2ecf20Sopenharmony_ci	 */
36538c2ecf20Sopenharmony_ci	if (msg->rsp_size < 11) {
36548c2ecf20Sopenharmony_ci		/* Message not big enough, just ignore it. */
36558c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_ipmb_responses);
36568c2ecf20Sopenharmony_ci		return 0;
36578c2ecf20Sopenharmony_ci	}
36588c2ecf20Sopenharmony_ci
36598c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
36608c2ecf20Sopenharmony_ci		/* An error getting the response, just ignore it. */
36618c2ecf20Sopenharmony_ci		return 0;
36628c2ecf20Sopenharmony_ci	}
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci	ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE;
36658c2ecf20Sopenharmony_ci	ipmb_addr.slave_addr = msg->rsp[6];
36668c2ecf20Sopenharmony_ci	ipmb_addr.channel = msg->rsp[3] & 0x0f;
36678c2ecf20Sopenharmony_ci	ipmb_addr.lun = msg->rsp[7] & 3;
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci	/*
36708c2ecf20Sopenharmony_ci	 * It's a response from a remote entity.  Look up the sequence
36718c2ecf20Sopenharmony_ci	 * number and handle the response.
36728c2ecf20Sopenharmony_ci	 */
36738c2ecf20Sopenharmony_ci	if (intf_find_seq(intf,
36748c2ecf20Sopenharmony_ci			  msg->rsp[7] >> 2,
36758c2ecf20Sopenharmony_ci			  msg->rsp[3] & 0x0f,
36768c2ecf20Sopenharmony_ci			  msg->rsp[8],
36778c2ecf20Sopenharmony_ci			  (msg->rsp[4] >> 2) & (~1),
36788c2ecf20Sopenharmony_ci			  (struct ipmi_addr *) &ipmb_addr,
36798c2ecf20Sopenharmony_ci			  &recv_msg)) {
36808c2ecf20Sopenharmony_ci		/*
36818c2ecf20Sopenharmony_ci		 * We were unable to find the sequence number,
36828c2ecf20Sopenharmony_ci		 * so just nuke the message.
36838c2ecf20Sopenharmony_ci		 */
36848c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_ipmb_responses);
36858c2ecf20Sopenharmony_ci		return 0;
36868c2ecf20Sopenharmony_ci	}
36878c2ecf20Sopenharmony_ci
36888c2ecf20Sopenharmony_ci	memcpy(recv_msg->msg_data, &msg->rsp[9], msg->rsp_size - 9);
36898c2ecf20Sopenharmony_ci	/*
36908c2ecf20Sopenharmony_ci	 * The other fields matched, so no need to set them, except
36918c2ecf20Sopenharmony_ci	 * for netfn, which needs to be the response that was
36928c2ecf20Sopenharmony_ci	 * returned, not the request value.
36938c2ecf20Sopenharmony_ci	 */
36948c2ecf20Sopenharmony_ci	recv_msg->msg.netfn = msg->rsp[4] >> 2;
36958c2ecf20Sopenharmony_ci	recv_msg->msg.data = recv_msg->msg_data;
36968c2ecf20Sopenharmony_ci	recv_msg->msg.data_len = msg->rsp_size - 10;
36978c2ecf20Sopenharmony_ci	recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
36988c2ecf20Sopenharmony_ci	if (deliver_response(intf, recv_msg))
36998c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_ipmb_responses);
37008c2ecf20Sopenharmony_ci	else
37018c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, handled_ipmb_responses);
37028c2ecf20Sopenharmony_ci
37038c2ecf20Sopenharmony_ci	return 0;
37048c2ecf20Sopenharmony_ci}
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_cistatic int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
37078c2ecf20Sopenharmony_ci				   struct ipmi_smi_msg *msg)
37088c2ecf20Sopenharmony_ci{
37098c2ecf20Sopenharmony_ci	struct cmd_rcvr          *rcvr;
37108c2ecf20Sopenharmony_ci	int                      rv = 0;
37118c2ecf20Sopenharmony_ci	unsigned char            netfn;
37128c2ecf20Sopenharmony_ci	unsigned char            cmd;
37138c2ecf20Sopenharmony_ci	unsigned char            chan;
37148c2ecf20Sopenharmony_ci	struct ipmi_user         *user = NULL;
37158c2ecf20Sopenharmony_ci	struct ipmi_ipmb_addr    *ipmb_addr;
37168c2ecf20Sopenharmony_ci	struct ipmi_recv_msg     *recv_msg;
37178c2ecf20Sopenharmony_ci
37188c2ecf20Sopenharmony_ci	if (msg->rsp_size < 10) {
37198c2ecf20Sopenharmony_ci		/* Message not big enough, just ignore it. */
37208c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_commands);
37218c2ecf20Sopenharmony_ci		return 0;
37228c2ecf20Sopenharmony_ci	}
37238c2ecf20Sopenharmony_ci
37248c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
37258c2ecf20Sopenharmony_ci		/* An error getting the response, just ignore it. */
37268c2ecf20Sopenharmony_ci		return 0;
37278c2ecf20Sopenharmony_ci	}
37288c2ecf20Sopenharmony_ci
37298c2ecf20Sopenharmony_ci	netfn = msg->rsp[4] >> 2;
37308c2ecf20Sopenharmony_ci	cmd = msg->rsp[8];
37318c2ecf20Sopenharmony_ci	chan = msg->rsp[3] & 0xf;
37328c2ecf20Sopenharmony_ci
37338c2ecf20Sopenharmony_ci	rcu_read_lock();
37348c2ecf20Sopenharmony_ci	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
37358c2ecf20Sopenharmony_ci	if (rcvr) {
37368c2ecf20Sopenharmony_ci		user = rcvr->user;
37378c2ecf20Sopenharmony_ci		kref_get(&user->refcount);
37388c2ecf20Sopenharmony_ci	} else
37398c2ecf20Sopenharmony_ci		user = NULL;
37408c2ecf20Sopenharmony_ci	rcu_read_unlock();
37418c2ecf20Sopenharmony_ci
37428c2ecf20Sopenharmony_ci	if (user == NULL) {
37438c2ecf20Sopenharmony_ci		/* We didn't find a user, deliver an error response. */
37448c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_commands);
37458c2ecf20Sopenharmony_ci
37468c2ecf20Sopenharmony_ci		msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
37478c2ecf20Sopenharmony_ci		msg->data[1] = IPMI_SEND_MSG_CMD;
37488c2ecf20Sopenharmony_ci		msg->data[2] = msg->rsp[3];
37498c2ecf20Sopenharmony_ci		msg->data[3] = msg->rsp[6];
37508c2ecf20Sopenharmony_ci		msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
37518c2ecf20Sopenharmony_ci		msg->data[5] = ipmb_checksum(&msg->data[3], 2);
37528c2ecf20Sopenharmony_ci		msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address;
37538c2ecf20Sopenharmony_ci		/* rqseq/lun */
37548c2ecf20Sopenharmony_ci		msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
37558c2ecf20Sopenharmony_ci		msg->data[8] = msg->rsp[8]; /* cmd */
37568c2ecf20Sopenharmony_ci		msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE;
37578c2ecf20Sopenharmony_ci		msg->data[10] = ipmb_checksum(&msg->data[6], 4);
37588c2ecf20Sopenharmony_ci		msg->data_size = 11;
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci		pr_debug("Invalid command: %*ph\n", msg->data_size, msg->data);
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci		rcu_read_lock();
37638c2ecf20Sopenharmony_ci		if (!intf->in_shutdown) {
37648c2ecf20Sopenharmony_ci			smi_send(intf, intf->handlers, msg, 0);
37658c2ecf20Sopenharmony_ci			/*
37668c2ecf20Sopenharmony_ci			 * We used the message, so return the value
37678c2ecf20Sopenharmony_ci			 * that causes it to not be freed or
37688c2ecf20Sopenharmony_ci			 * queued.
37698c2ecf20Sopenharmony_ci			 */
37708c2ecf20Sopenharmony_ci			rv = -1;
37718c2ecf20Sopenharmony_ci		}
37728c2ecf20Sopenharmony_ci		rcu_read_unlock();
37738c2ecf20Sopenharmony_ci	} else {
37748c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
37758c2ecf20Sopenharmony_ci		if (!recv_msg) {
37768c2ecf20Sopenharmony_ci			/*
37778c2ecf20Sopenharmony_ci			 * We couldn't allocate memory for the
37788c2ecf20Sopenharmony_ci			 * message, so requeue it for handling
37798c2ecf20Sopenharmony_ci			 * later.
37808c2ecf20Sopenharmony_ci			 */
37818c2ecf20Sopenharmony_ci			rv = 1;
37828c2ecf20Sopenharmony_ci			kref_put(&user->refcount, free_user);
37838c2ecf20Sopenharmony_ci		} else {
37848c2ecf20Sopenharmony_ci			/* Extract the source address from the data. */
37858c2ecf20Sopenharmony_ci			ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
37868c2ecf20Sopenharmony_ci			ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE;
37878c2ecf20Sopenharmony_ci			ipmb_addr->slave_addr = msg->rsp[6];
37888c2ecf20Sopenharmony_ci			ipmb_addr->lun = msg->rsp[7] & 3;
37898c2ecf20Sopenharmony_ci			ipmb_addr->channel = msg->rsp[3] & 0xf;
37908c2ecf20Sopenharmony_ci
37918c2ecf20Sopenharmony_ci			/*
37928c2ecf20Sopenharmony_ci			 * Extract the rest of the message information
37938c2ecf20Sopenharmony_ci			 * from the IPMB header.
37948c2ecf20Sopenharmony_ci			 */
37958c2ecf20Sopenharmony_ci			recv_msg->user = user;
37968c2ecf20Sopenharmony_ci			recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
37978c2ecf20Sopenharmony_ci			recv_msg->msgid = msg->rsp[7] >> 2;
37988c2ecf20Sopenharmony_ci			recv_msg->msg.netfn = msg->rsp[4] >> 2;
37998c2ecf20Sopenharmony_ci			recv_msg->msg.cmd = msg->rsp[8];
38008c2ecf20Sopenharmony_ci			recv_msg->msg.data = recv_msg->msg_data;
38018c2ecf20Sopenharmony_ci
38028c2ecf20Sopenharmony_ci			/*
38038c2ecf20Sopenharmony_ci			 * We chop off 10, not 9 bytes because the checksum
38048c2ecf20Sopenharmony_ci			 * at the end also needs to be removed.
38058c2ecf20Sopenharmony_ci			 */
38068c2ecf20Sopenharmony_ci			recv_msg->msg.data_len = msg->rsp_size - 10;
38078c2ecf20Sopenharmony_ci			memcpy(recv_msg->msg_data, &msg->rsp[9],
38088c2ecf20Sopenharmony_ci			       msg->rsp_size - 10);
38098c2ecf20Sopenharmony_ci			if (deliver_response(intf, recv_msg))
38108c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, unhandled_commands);
38118c2ecf20Sopenharmony_ci			else
38128c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, handled_commands);
38138c2ecf20Sopenharmony_ci		}
38148c2ecf20Sopenharmony_ci	}
38158c2ecf20Sopenharmony_ci
38168c2ecf20Sopenharmony_ci	return rv;
38178c2ecf20Sopenharmony_ci}
38188c2ecf20Sopenharmony_ci
38198c2ecf20Sopenharmony_cistatic int handle_lan_get_msg_rsp(struct ipmi_smi *intf,
38208c2ecf20Sopenharmony_ci				  struct ipmi_smi_msg *msg)
38218c2ecf20Sopenharmony_ci{
38228c2ecf20Sopenharmony_ci	struct ipmi_lan_addr  lan_addr;
38238c2ecf20Sopenharmony_ci	struct ipmi_recv_msg  *recv_msg;
38248c2ecf20Sopenharmony_ci
38258c2ecf20Sopenharmony_ci
38268c2ecf20Sopenharmony_ci	/*
38278c2ecf20Sopenharmony_ci	 * This is 13, not 12, because the response must contain a
38288c2ecf20Sopenharmony_ci	 * completion code.
38298c2ecf20Sopenharmony_ci	 */
38308c2ecf20Sopenharmony_ci	if (msg->rsp_size < 13) {
38318c2ecf20Sopenharmony_ci		/* Message not big enough, just ignore it. */
38328c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_lan_responses);
38338c2ecf20Sopenharmony_ci		return 0;
38348c2ecf20Sopenharmony_ci	}
38358c2ecf20Sopenharmony_ci
38368c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
38378c2ecf20Sopenharmony_ci		/* An error getting the response, just ignore it. */
38388c2ecf20Sopenharmony_ci		return 0;
38398c2ecf20Sopenharmony_ci	}
38408c2ecf20Sopenharmony_ci
38418c2ecf20Sopenharmony_ci	lan_addr.addr_type = IPMI_LAN_ADDR_TYPE;
38428c2ecf20Sopenharmony_ci	lan_addr.session_handle = msg->rsp[4];
38438c2ecf20Sopenharmony_ci	lan_addr.remote_SWID = msg->rsp[8];
38448c2ecf20Sopenharmony_ci	lan_addr.local_SWID = msg->rsp[5];
38458c2ecf20Sopenharmony_ci	lan_addr.channel = msg->rsp[3] & 0x0f;
38468c2ecf20Sopenharmony_ci	lan_addr.privilege = msg->rsp[3] >> 4;
38478c2ecf20Sopenharmony_ci	lan_addr.lun = msg->rsp[9] & 3;
38488c2ecf20Sopenharmony_ci
38498c2ecf20Sopenharmony_ci	/*
38508c2ecf20Sopenharmony_ci	 * It's a response from a remote entity.  Look up the sequence
38518c2ecf20Sopenharmony_ci	 * number and handle the response.
38528c2ecf20Sopenharmony_ci	 */
38538c2ecf20Sopenharmony_ci	if (intf_find_seq(intf,
38548c2ecf20Sopenharmony_ci			  msg->rsp[9] >> 2,
38558c2ecf20Sopenharmony_ci			  msg->rsp[3] & 0x0f,
38568c2ecf20Sopenharmony_ci			  msg->rsp[10],
38578c2ecf20Sopenharmony_ci			  (msg->rsp[6] >> 2) & (~1),
38588c2ecf20Sopenharmony_ci			  (struct ipmi_addr *) &lan_addr,
38598c2ecf20Sopenharmony_ci			  &recv_msg)) {
38608c2ecf20Sopenharmony_ci		/*
38618c2ecf20Sopenharmony_ci		 * We were unable to find the sequence number,
38628c2ecf20Sopenharmony_ci		 * so just nuke the message.
38638c2ecf20Sopenharmony_ci		 */
38648c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_lan_responses);
38658c2ecf20Sopenharmony_ci		return 0;
38668c2ecf20Sopenharmony_ci	}
38678c2ecf20Sopenharmony_ci
38688c2ecf20Sopenharmony_ci	memcpy(recv_msg->msg_data, &msg->rsp[11], msg->rsp_size - 11);
38698c2ecf20Sopenharmony_ci	/*
38708c2ecf20Sopenharmony_ci	 * The other fields matched, so no need to set them, except
38718c2ecf20Sopenharmony_ci	 * for netfn, which needs to be the response that was
38728c2ecf20Sopenharmony_ci	 * returned, not the request value.
38738c2ecf20Sopenharmony_ci	 */
38748c2ecf20Sopenharmony_ci	recv_msg->msg.netfn = msg->rsp[6] >> 2;
38758c2ecf20Sopenharmony_ci	recv_msg->msg.data = recv_msg->msg_data;
38768c2ecf20Sopenharmony_ci	recv_msg->msg.data_len = msg->rsp_size - 12;
38778c2ecf20Sopenharmony_ci	recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
38788c2ecf20Sopenharmony_ci	if (deliver_response(intf, recv_msg))
38798c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_lan_responses);
38808c2ecf20Sopenharmony_ci	else
38818c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, handled_lan_responses);
38828c2ecf20Sopenharmony_ci
38838c2ecf20Sopenharmony_ci	return 0;
38848c2ecf20Sopenharmony_ci}
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_cistatic int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
38878c2ecf20Sopenharmony_ci				  struct ipmi_smi_msg *msg)
38888c2ecf20Sopenharmony_ci{
38898c2ecf20Sopenharmony_ci	struct cmd_rcvr          *rcvr;
38908c2ecf20Sopenharmony_ci	int                      rv = 0;
38918c2ecf20Sopenharmony_ci	unsigned char            netfn;
38928c2ecf20Sopenharmony_ci	unsigned char            cmd;
38938c2ecf20Sopenharmony_ci	unsigned char            chan;
38948c2ecf20Sopenharmony_ci	struct ipmi_user         *user = NULL;
38958c2ecf20Sopenharmony_ci	struct ipmi_lan_addr     *lan_addr;
38968c2ecf20Sopenharmony_ci	struct ipmi_recv_msg     *recv_msg;
38978c2ecf20Sopenharmony_ci
38988c2ecf20Sopenharmony_ci	if (msg->rsp_size < 12) {
38998c2ecf20Sopenharmony_ci		/* Message not big enough, just ignore it. */
39008c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_commands);
39018c2ecf20Sopenharmony_ci		return 0;
39028c2ecf20Sopenharmony_ci	}
39038c2ecf20Sopenharmony_ci
39048c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
39058c2ecf20Sopenharmony_ci		/* An error getting the response, just ignore it. */
39068c2ecf20Sopenharmony_ci		return 0;
39078c2ecf20Sopenharmony_ci	}
39088c2ecf20Sopenharmony_ci
39098c2ecf20Sopenharmony_ci	netfn = msg->rsp[6] >> 2;
39108c2ecf20Sopenharmony_ci	cmd = msg->rsp[10];
39118c2ecf20Sopenharmony_ci	chan = msg->rsp[3] & 0xf;
39128c2ecf20Sopenharmony_ci
39138c2ecf20Sopenharmony_ci	rcu_read_lock();
39148c2ecf20Sopenharmony_ci	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
39158c2ecf20Sopenharmony_ci	if (rcvr) {
39168c2ecf20Sopenharmony_ci		user = rcvr->user;
39178c2ecf20Sopenharmony_ci		kref_get(&user->refcount);
39188c2ecf20Sopenharmony_ci	} else
39198c2ecf20Sopenharmony_ci		user = NULL;
39208c2ecf20Sopenharmony_ci	rcu_read_unlock();
39218c2ecf20Sopenharmony_ci
39228c2ecf20Sopenharmony_ci	if (user == NULL) {
39238c2ecf20Sopenharmony_ci		/* We didn't find a user, just give up. */
39248c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_commands);
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci		/*
39278c2ecf20Sopenharmony_ci		 * Don't do anything with these messages, just allow
39288c2ecf20Sopenharmony_ci		 * them to be freed.
39298c2ecf20Sopenharmony_ci		 */
39308c2ecf20Sopenharmony_ci		rv = 0;
39318c2ecf20Sopenharmony_ci	} else {
39328c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
39338c2ecf20Sopenharmony_ci		if (!recv_msg) {
39348c2ecf20Sopenharmony_ci			/*
39358c2ecf20Sopenharmony_ci			 * We couldn't allocate memory for the
39368c2ecf20Sopenharmony_ci			 * message, so requeue it for handling later.
39378c2ecf20Sopenharmony_ci			 */
39388c2ecf20Sopenharmony_ci			rv = 1;
39398c2ecf20Sopenharmony_ci			kref_put(&user->refcount, free_user);
39408c2ecf20Sopenharmony_ci		} else {
39418c2ecf20Sopenharmony_ci			/* Extract the source address from the data. */
39428c2ecf20Sopenharmony_ci			lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
39438c2ecf20Sopenharmony_ci			lan_addr->addr_type = IPMI_LAN_ADDR_TYPE;
39448c2ecf20Sopenharmony_ci			lan_addr->session_handle = msg->rsp[4];
39458c2ecf20Sopenharmony_ci			lan_addr->remote_SWID = msg->rsp[8];
39468c2ecf20Sopenharmony_ci			lan_addr->local_SWID = msg->rsp[5];
39478c2ecf20Sopenharmony_ci			lan_addr->lun = msg->rsp[9] & 3;
39488c2ecf20Sopenharmony_ci			lan_addr->channel = msg->rsp[3] & 0xf;
39498c2ecf20Sopenharmony_ci			lan_addr->privilege = msg->rsp[3] >> 4;
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_ci			/*
39528c2ecf20Sopenharmony_ci			 * Extract the rest of the message information
39538c2ecf20Sopenharmony_ci			 * from the IPMB header.
39548c2ecf20Sopenharmony_ci			 */
39558c2ecf20Sopenharmony_ci			recv_msg->user = user;
39568c2ecf20Sopenharmony_ci			recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
39578c2ecf20Sopenharmony_ci			recv_msg->msgid = msg->rsp[9] >> 2;
39588c2ecf20Sopenharmony_ci			recv_msg->msg.netfn = msg->rsp[6] >> 2;
39598c2ecf20Sopenharmony_ci			recv_msg->msg.cmd = msg->rsp[10];
39608c2ecf20Sopenharmony_ci			recv_msg->msg.data = recv_msg->msg_data;
39618c2ecf20Sopenharmony_ci
39628c2ecf20Sopenharmony_ci			/*
39638c2ecf20Sopenharmony_ci			 * We chop off 12, not 11 bytes because the checksum
39648c2ecf20Sopenharmony_ci			 * at the end also needs to be removed.
39658c2ecf20Sopenharmony_ci			 */
39668c2ecf20Sopenharmony_ci			recv_msg->msg.data_len = msg->rsp_size - 12;
39678c2ecf20Sopenharmony_ci			memcpy(recv_msg->msg_data, &msg->rsp[11],
39688c2ecf20Sopenharmony_ci			       msg->rsp_size - 12);
39698c2ecf20Sopenharmony_ci			if (deliver_response(intf, recv_msg))
39708c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, unhandled_commands);
39718c2ecf20Sopenharmony_ci			else
39728c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, handled_commands);
39738c2ecf20Sopenharmony_ci		}
39748c2ecf20Sopenharmony_ci	}
39758c2ecf20Sopenharmony_ci
39768c2ecf20Sopenharmony_ci	return rv;
39778c2ecf20Sopenharmony_ci}
39788c2ecf20Sopenharmony_ci
39798c2ecf20Sopenharmony_ci/*
39808c2ecf20Sopenharmony_ci * This routine will handle "Get Message" command responses with
39818c2ecf20Sopenharmony_ci * channels that use an OEM Medium. The message format belongs to
39828c2ecf20Sopenharmony_ci * the OEM.  See IPMI 2.0 specification, Chapter 6 and
39838c2ecf20Sopenharmony_ci * Chapter 22, sections 22.6 and 22.24 for more details.
39848c2ecf20Sopenharmony_ci */
39858c2ecf20Sopenharmony_cistatic int handle_oem_get_msg_cmd(struct ipmi_smi *intf,
39868c2ecf20Sopenharmony_ci				  struct ipmi_smi_msg *msg)
39878c2ecf20Sopenharmony_ci{
39888c2ecf20Sopenharmony_ci	struct cmd_rcvr       *rcvr;
39898c2ecf20Sopenharmony_ci	int                   rv = 0;
39908c2ecf20Sopenharmony_ci	unsigned char         netfn;
39918c2ecf20Sopenharmony_ci	unsigned char         cmd;
39928c2ecf20Sopenharmony_ci	unsigned char         chan;
39938c2ecf20Sopenharmony_ci	struct ipmi_user *user = NULL;
39948c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr *smi_addr;
39958c2ecf20Sopenharmony_ci	struct ipmi_recv_msg  *recv_msg;
39968c2ecf20Sopenharmony_ci
39978c2ecf20Sopenharmony_ci	/*
39988c2ecf20Sopenharmony_ci	 * We expect the OEM SW to perform error checking
39998c2ecf20Sopenharmony_ci	 * so we just do some basic sanity checks
40008c2ecf20Sopenharmony_ci	 */
40018c2ecf20Sopenharmony_ci	if (msg->rsp_size < 4) {
40028c2ecf20Sopenharmony_ci		/* Message not big enough, just ignore it. */
40038c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_commands);
40048c2ecf20Sopenharmony_ci		return 0;
40058c2ecf20Sopenharmony_ci	}
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
40088c2ecf20Sopenharmony_ci		/* An error getting the response, just ignore it. */
40098c2ecf20Sopenharmony_ci		return 0;
40108c2ecf20Sopenharmony_ci	}
40118c2ecf20Sopenharmony_ci
40128c2ecf20Sopenharmony_ci	/*
40138c2ecf20Sopenharmony_ci	 * This is an OEM Message so the OEM needs to know how
40148c2ecf20Sopenharmony_ci	 * handle the message. We do no interpretation.
40158c2ecf20Sopenharmony_ci	 */
40168c2ecf20Sopenharmony_ci	netfn = msg->rsp[0] >> 2;
40178c2ecf20Sopenharmony_ci	cmd = msg->rsp[1];
40188c2ecf20Sopenharmony_ci	chan = msg->rsp[3] & 0xf;
40198c2ecf20Sopenharmony_ci
40208c2ecf20Sopenharmony_ci	rcu_read_lock();
40218c2ecf20Sopenharmony_ci	rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
40228c2ecf20Sopenharmony_ci	if (rcvr) {
40238c2ecf20Sopenharmony_ci		user = rcvr->user;
40248c2ecf20Sopenharmony_ci		kref_get(&user->refcount);
40258c2ecf20Sopenharmony_ci	} else
40268c2ecf20Sopenharmony_ci		user = NULL;
40278c2ecf20Sopenharmony_ci	rcu_read_unlock();
40288c2ecf20Sopenharmony_ci
40298c2ecf20Sopenharmony_ci	if (user == NULL) {
40308c2ecf20Sopenharmony_ci		/* We didn't find a user, just give up. */
40318c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, unhandled_commands);
40328c2ecf20Sopenharmony_ci
40338c2ecf20Sopenharmony_ci		/*
40348c2ecf20Sopenharmony_ci		 * Don't do anything with these messages, just allow
40358c2ecf20Sopenharmony_ci		 * them to be freed.
40368c2ecf20Sopenharmony_ci		 */
40378c2ecf20Sopenharmony_ci
40388c2ecf20Sopenharmony_ci		rv = 0;
40398c2ecf20Sopenharmony_ci	} else {
40408c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
40418c2ecf20Sopenharmony_ci		if (!recv_msg) {
40428c2ecf20Sopenharmony_ci			/*
40438c2ecf20Sopenharmony_ci			 * We couldn't allocate memory for the
40448c2ecf20Sopenharmony_ci			 * message, so requeue it for handling
40458c2ecf20Sopenharmony_ci			 * later.
40468c2ecf20Sopenharmony_ci			 */
40478c2ecf20Sopenharmony_ci			rv = 1;
40488c2ecf20Sopenharmony_ci			kref_put(&user->refcount, free_user);
40498c2ecf20Sopenharmony_ci		} else {
40508c2ecf20Sopenharmony_ci			/*
40518c2ecf20Sopenharmony_ci			 * OEM Messages are expected to be delivered via
40528c2ecf20Sopenharmony_ci			 * the system interface to SMS software.  We might
40538c2ecf20Sopenharmony_ci			 * need to visit this again depending on OEM
40548c2ecf20Sopenharmony_ci			 * requirements
40558c2ecf20Sopenharmony_ci			 */
40568c2ecf20Sopenharmony_ci			smi_addr = ((struct ipmi_system_interface_addr *)
40578c2ecf20Sopenharmony_ci				    &recv_msg->addr);
40588c2ecf20Sopenharmony_ci			smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
40598c2ecf20Sopenharmony_ci			smi_addr->channel = IPMI_BMC_CHANNEL;
40608c2ecf20Sopenharmony_ci			smi_addr->lun = msg->rsp[0] & 3;
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci			recv_msg->user = user;
40638c2ecf20Sopenharmony_ci			recv_msg->user_msg_data = NULL;
40648c2ecf20Sopenharmony_ci			recv_msg->recv_type = IPMI_OEM_RECV_TYPE;
40658c2ecf20Sopenharmony_ci			recv_msg->msg.netfn = msg->rsp[0] >> 2;
40668c2ecf20Sopenharmony_ci			recv_msg->msg.cmd = msg->rsp[1];
40678c2ecf20Sopenharmony_ci			recv_msg->msg.data = recv_msg->msg_data;
40688c2ecf20Sopenharmony_ci
40698c2ecf20Sopenharmony_ci			/*
40708c2ecf20Sopenharmony_ci			 * The message starts at byte 4 which follows the
40718c2ecf20Sopenharmony_ci			 * the Channel Byte in the "GET MESSAGE" command
40728c2ecf20Sopenharmony_ci			 */
40738c2ecf20Sopenharmony_ci			recv_msg->msg.data_len = msg->rsp_size - 4;
40748c2ecf20Sopenharmony_ci			memcpy(recv_msg->msg_data, &msg->rsp[4],
40758c2ecf20Sopenharmony_ci			       msg->rsp_size - 4);
40768c2ecf20Sopenharmony_ci			if (deliver_response(intf, recv_msg))
40778c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, unhandled_commands);
40788c2ecf20Sopenharmony_ci			else
40798c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, handled_commands);
40808c2ecf20Sopenharmony_ci		}
40818c2ecf20Sopenharmony_ci	}
40828c2ecf20Sopenharmony_ci
40838c2ecf20Sopenharmony_ci	return rv;
40848c2ecf20Sopenharmony_ci}
40858c2ecf20Sopenharmony_ci
40868c2ecf20Sopenharmony_cistatic void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg,
40878c2ecf20Sopenharmony_ci				     struct ipmi_smi_msg  *msg)
40888c2ecf20Sopenharmony_ci{
40898c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr *smi_addr;
40908c2ecf20Sopenharmony_ci
40918c2ecf20Sopenharmony_ci	recv_msg->msgid = 0;
40928c2ecf20Sopenharmony_ci	smi_addr = (struct ipmi_system_interface_addr *) &recv_msg->addr;
40938c2ecf20Sopenharmony_ci	smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
40948c2ecf20Sopenharmony_ci	smi_addr->channel = IPMI_BMC_CHANNEL;
40958c2ecf20Sopenharmony_ci	smi_addr->lun = msg->rsp[0] & 3;
40968c2ecf20Sopenharmony_ci	recv_msg->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE;
40978c2ecf20Sopenharmony_ci	recv_msg->msg.netfn = msg->rsp[0] >> 2;
40988c2ecf20Sopenharmony_ci	recv_msg->msg.cmd = msg->rsp[1];
40998c2ecf20Sopenharmony_ci	memcpy(recv_msg->msg_data, &msg->rsp[3], msg->rsp_size - 3);
41008c2ecf20Sopenharmony_ci	recv_msg->msg.data = recv_msg->msg_data;
41018c2ecf20Sopenharmony_ci	recv_msg->msg.data_len = msg->rsp_size - 3;
41028c2ecf20Sopenharmony_ci}
41038c2ecf20Sopenharmony_ci
41048c2ecf20Sopenharmony_cistatic int handle_read_event_rsp(struct ipmi_smi *intf,
41058c2ecf20Sopenharmony_ci				 struct ipmi_smi_msg *msg)
41068c2ecf20Sopenharmony_ci{
41078c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *recv_msg, *recv_msg2;
41088c2ecf20Sopenharmony_ci	struct list_head     msgs;
41098c2ecf20Sopenharmony_ci	struct ipmi_user     *user;
41108c2ecf20Sopenharmony_ci	int rv = 0, deliver_count = 0, index;
41118c2ecf20Sopenharmony_ci	unsigned long        flags;
41128c2ecf20Sopenharmony_ci
41138c2ecf20Sopenharmony_ci	if (msg->rsp_size < 19) {
41148c2ecf20Sopenharmony_ci		/* Message is too small to be an IPMB event. */
41158c2ecf20Sopenharmony_ci		ipmi_inc_stat(intf, invalid_events);
41168c2ecf20Sopenharmony_ci		return 0;
41178c2ecf20Sopenharmony_ci	}
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_ci	if (msg->rsp[2] != 0) {
41208c2ecf20Sopenharmony_ci		/* An error getting the event, just ignore it. */
41218c2ecf20Sopenharmony_ci		return 0;
41228c2ecf20Sopenharmony_ci	}
41238c2ecf20Sopenharmony_ci
41248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&msgs);
41258c2ecf20Sopenharmony_ci
41268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->events_lock, flags);
41278c2ecf20Sopenharmony_ci
41288c2ecf20Sopenharmony_ci	ipmi_inc_stat(intf, events);
41298c2ecf20Sopenharmony_ci
41308c2ecf20Sopenharmony_ci	/*
41318c2ecf20Sopenharmony_ci	 * Allocate and fill in one message for every user that is
41328c2ecf20Sopenharmony_ci	 * getting events.
41338c2ecf20Sopenharmony_ci	 */
41348c2ecf20Sopenharmony_ci	index = srcu_read_lock(&intf->users_srcu);
41358c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(user, &intf->users, link) {
41368c2ecf20Sopenharmony_ci		if (!user->gets_events)
41378c2ecf20Sopenharmony_ci			continue;
41388c2ecf20Sopenharmony_ci
41398c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
41408c2ecf20Sopenharmony_ci		if (!recv_msg) {
41418c2ecf20Sopenharmony_ci			rcu_read_unlock();
41428c2ecf20Sopenharmony_ci			list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
41438c2ecf20Sopenharmony_ci						 link) {
41448c2ecf20Sopenharmony_ci				list_del(&recv_msg->link);
41458c2ecf20Sopenharmony_ci				ipmi_free_recv_msg(recv_msg);
41468c2ecf20Sopenharmony_ci			}
41478c2ecf20Sopenharmony_ci			/*
41488c2ecf20Sopenharmony_ci			 * We couldn't allocate memory for the
41498c2ecf20Sopenharmony_ci			 * message, so requeue it for handling
41508c2ecf20Sopenharmony_ci			 * later.
41518c2ecf20Sopenharmony_ci			 */
41528c2ecf20Sopenharmony_ci			rv = 1;
41538c2ecf20Sopenharmony_ci			goto out;
41548c2ecf20Sopenharmony_ci		}
41558c2ecf20Sopenharmony_ci
41568c2ecf20Sopenharmony_ci		deliver_count++;
41578c2ecf20Sopenharmony_ci
41588c2ecf20Sopenharmony_ci		copy_event_into_recv_msg(recv_msg, msg);
41598c2ecf20Sopenharmony_ci		recv_msg->user = user;
41608c2ecf20Sopenharmony_ci		kref_get(&user->refcount);
41618c2ecf20Sopenharmony_ci		list_add_tail(&recv_msg->link, &msgs);
41628c2ecf20Sopenharmony_ci	}
41638c2ecf20Sopenharmony_ci	srcu_read_unlock(&intf->users_srcu, index);
41648c2ecf20Sopenharmony_ci
41658c2ecf20Sopenharmony_ci	if (deliver_count) {
41668c2ecf20Sopenharmony_ci		/* Now deliver all the messages. */
41678c2ecf20Sopenharmony_ci		list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) {
41688c2ecf20Sopenharmony_ci			list_del(&recv_msg->link);
41698c2ecf20Sopenharmony_ci			deliver_local_response(intf, recv_msg);
41708c2ecf20Sopenharmony_ci		}
41718c2ecf20Sopenharmony_ci	} else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) {
41728c2ecf20Sopenharmony_ci		/*
41738c2ecf20Sopenharmony_ci		 * No one to receive the message, put it in queue if there's
41748c2ecf20Sopenharmony_ci		 * not already too many things in the queue.
41758c2ecf20Sopenharmony_ci		 */
41768c2ecf20Sopenharmony_ci		recv_msg = ipmi_alloc_recv_msg();
41778c2ecf20Sopenharmony_ci		if (!recv_msg) {
41788c2ecf20Sopenharmony_ci			/*
41798c2ecf20Sopenharmony_ci			 * We couldn't allocate memory for the
41808c2ecf20Sopenharmony_ci			 * message, so requeue it for handling
41818c2ecf20Sopenharmony_ci			 * later.
41828c2ecf20Sopenharmony_ci			 */
41838c2ecf20Sopenharmony_ci			rv = 1;
41848c2ecf20Sopenharmony_ci			goto out;
41858c2ecf20Sopenharmony_ci		}
41868c2ecf20Sopenharmony_ci
41878c2ecf20Sopenharmony_ci		copy_event_into_recv_msg(recv_msg, msg);
41888c2ecf20Sopenharmony_ci		list_add_tail(&recv_msg->link, &intf->waiting_events);
41898c2ecf20Sopenharmony_ci		intf->waiting_events_count++;
41908c2ecf20Sopenharmony_ci	} else if (!intf->event_msg_printed) {
41918c2ecf20Sopenharmony_ci		/*
41928c2ecf20Sopenharmony_ci		 * There's too many things in the queue, discard this
41938c2ecf20Sopenharmony_ci		 * message.
41948c2ecf20Sopenharmony_ci		 */
41958c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
41968c2ecf20Sopenharmony_ci			 "Event queue full, discarding incoming events\n");
41978c2ecf20Sopenharmony_ci		intf->event_msg_printed = 1;
41988c2ecf20Sopenharmony_ci	}
41998c2ecf20Sopenharmony_ci
42008c2ecf20Sopenharmony_ci out:
42018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->events_lock, flags);
42028c2ecf20Sopenharmony_ci
42038c2ecf20Sopenharmony_ci	return rv;
42048c2ecf20Sopenharmony_ci}
42058c2ecf20Sopenharmony_ci
42068c2ecf20Sopenharmony_cistatic int handle_bmc_rsp(struct ipmi_smi *intf,
42078c2ecf20Sopenharmony_ci			  struct ipmi_smi_msg *msg)
42088c2ecf20Sopenharmony_ci{
42098c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *recv_msg;
42108c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr *smi_addr;
42118c2ecf20Sopenharmony_ci
42128c2ecf20Sopenharmony_ci	recv_msg = (struct ipmi_recv_msg *) msg->user_data;
42138c2ecf20Sopenharmony_ci	if (recv_msg == NULL) {
42148c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
42158c2ecf20Sopenharmony_ci			 "IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error.  Contact your hardware vendor for assistance.\n");
42168c2ecf20Sopenharmony_ci		return 0;
42178c2ecf20Sopenharmony_ci	}
42188c2ecf20Sopenharmony_ci
42198c2ecf20Sopenharmony_ci	recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
42208c2ecf20Sopenharmony_ci	recv_msg->msgid = msg->msgid;
42218c2ecf20Sopenharmony_ci	smi_addr = ((struct ipmi_system_interface_addr *)
42228c2ecf20Sopenharmony_ci		    &recv_msg->addr);
42238c2ecf20Sopenharmony_ci	smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
42248c2ecf20Sopenharmony_ci	smi_addr->channel = IPMI_BMC_CHANNEL;
42258c2ecf20Sopenharmony_ci	smi_addr->lun = msg->rsp[0] & 3;
42268c2ecf20Sopenharmony_ci	recv_msg->msg.netfn = msg->rsp[0] >> 2;
42278c2ecf20Sopenharmony_ci	recv_msg->msg.cmd = msg->rsp[1];
42288c2ecf20Sopenharmony_ci	memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2);
42298c2ecf20Sopenharmony_ci	recv_msg->msg.data = recv_msg->msg_data;
42308c2ecf20Sopenharmony_ci	recv_msg->msg.data_len = msg->rsp_size - 2;
42318c2ecf20Sopenharmony_ci	deliver_local_response(intf, recv_msg);
42328c2ecf20Sopenharmony_ci
42338c2ecf20Sopenharmony_ci	return 0;
42348c2ecf20Sopenharmony_ci}
42358c2ecf20Sopenharmony_ci
42368c2ecf20Sopenharmony_ci/*
42378c2ecf20Sopenharmony_ci * Handle a received message.  Return 1 if the message should be requeued,
42388c2ecf20Sopenharmony_ci * 0 if the message should be freed, or -1 if the message should not
42398c2ecf20Sopenharmony_ci * be freed or requeued.
42408c2ecf20Sopenharmony_ci */
42418c2ecf20Sopenharmony_cistatic int handle_one_recv_msg(struct ipmi_smi *intf,
42428c2ecf20Sopenharmony_ci			       struct ipmi_smi_msg *msg)
42438c2ecf20Sopenharmony_ci{
42448c2ecf20Sopenharmony_ci	int requeue;
42458c2ecf20Sopenharmony_ci	int chan;
42468c2ecf20Sopenharmony_ci
42478c2ecf20Sopenharmony_ci	pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);
42488c2ecf20Sopenharmony_ci
42498c2ecf20Sopenharmony_ci	if ((msg->data_size >= 2)
42508c2ecf20Sopenharmony_ci	    && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
42518c2ecf20Sopenharmony_ci	    && (msg->data[1] == IPMI_SEND_MSG_CMD)
42528c2ecf20Sopenharmony_ci	    && (msg->user_data == NULL)) {
42538c2ecf20Sopenharmony_ci
42548c2ecf20Sopenharmony_ci		if (intf->in_shutdown)
42558c2ecf20Sopenharmony_ci			goto free_msg;
42568c2ecf20Sopenharmony_ci
42578c2ecf20Sopenharmony_ci		/*
42588c2ecf20Sopenharmony_ci		 * This is the local response to a command send, start
42598c2ecf20Sopenharmony_ci		 * the timer for these.  The user_data will not be
42608c2ecf20Sopenharmony_ci		 * NULL if this is a response send, and we will let
42618c2ecf20Sopenharmony_ci		 * response sends just go through.
42628c2ecf20Sopenharmony_ci		 */
42638c2ecf20Sopenharmony_ci
42648c2ecf20Sopenharmony_ci		/*
42658c2ecf20Sopenharmony_ci		 * Check for errors, if we get certain errors (ones
42668c2ecf20Sopenharmony_ci		 * that mean basically we can try again later), we
42678c2ecf20Sopenharmony_ci		 * ignore them and start the timer.  Otherwise we
42688c2ecf20Sopenharmony_ci		 * report the error immediately.
42698c2ecf20Sopenharmony_ci		 */
42708c2ecf20Sopenharmony_ci		if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
42718c2ecf20Sopenharmony_ci		    && (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
42728c2ecf20Sopenharmony_ci		    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
42738c2ecf20Sopenharmony_ci		    && (msg->rsp[2] != IPMI_BUS_ERR)
42748c2ecf20Sopenharmony_ci		    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
42758c2ecf20Sopenharmony_ci			int ch = msg->rsp[3] & 0xf;
42768c2ecf20Sopenharmony_ci			struct ipmi_channel *chans;
42778c2ecf20Sopenharmony_ci
42788c2ecf20Sopenharmony_ci			/* Got an error sending the message, handle it. */
42798c2ecf20Sopenharmony_ci
42808c2ecf20Sopenharmony_ci			chans = READ_ONCE(intf->channel_list)->c;
42818c2ecf20Sopenharmony_ci			if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
42828c2ecf20Sopenharmony_ci			    || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
42838c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, sent_lan_command_errs);
42848c2ecf20Sopenharmony_ci			else
42858c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf, sent_ipmb_command_errs);
42868c2ecf20Sopenharmony_ci			intf_err_seq(intf, msg->msgid, msg->rsp[2]);
42878c2ecf20Sopenharmony_ci		} else
42888c2ecf20Sopenharmony_ci			/* The message was sent, start the timer. */
42898c2ecf20Sopenharmony_ci			intf_start_seq_timer(intf, msg->msgid);
42908c2ecf20Sopenharmony_cifree_msg:
42918c2ecf20Sopenharmony_ci		requeue = 0;
42928c2ecf20Sopenharmony_ci		goto out;
42938c2ecf20Sopenharmony_ci
42948c2ecf20Sopenharmony_ci	} else if (msg->rsp_size < 2) {
42958c2ecf20Sopenharmony_ci		/* Message is too small to be correct. */
42968c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
42978c2ecf20Sopenharmony_ci			 "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
42988c2ecf20Sopenharmony_ci			 (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
42998c2ecf20Sopenharmony_ci
43008c2ecf20Sopenharmony_ci		/* Generate an error response for the message. */
43018c2ecf20Sopenharmony_ci		msg->rsp[0] = msg->data[0] | (1 << 2);
43028c2ecf20Sopenharmony_ci		msg->rsp[1] = msg->data[1];
43038c2ecf20Sopenharmony_ci		msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
43048c2ecf20Sopenharmony_ci		msg->rsp_size = 3;
43058c2ecf20Sopenharmony_ci	} else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))
43068c2ecf20Sopenharmony_ci		   || (msg->rsp[1] != msg->data[1])) {
43078c2ecf20Sopenharmony_ci		/*
43088c2ecf20Sopenharmony_ci		 * The NetFN and Command in the response is not even
43098c2ecf20Sopenharmony_ci		 * marginally correct.
43108c2ecf20Sopenharmony_ci		 */
43118c2ecf20Sopenharmony_ci		dev_warn(intf->si_dev,
43128c2ecf20Sopenharmony_ci			 "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n",
43138c2ecf20Sopenharmony_ci			 (msg->data[0] >> 2) | 1, msg->data[1],
43148c2ecf20Sopenharmony_ci			 msg->rsp[0] >> 2, msg->rsp[1]);
43158c2ecf20Sopenharmony_ci
43168c2ecf20Sopenharmony_ci		/* Generate an error response for the message. */
43178c2ecf20Sopenharmony_ci		msg->rsp[0] = msg->data[0] | (1 << 2);
43188c2ecf20Sopenharmony_ci		msg->rsp[1] = msg->data[1];
43198c2ecf20Sopenharmony_ci		msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
43208c2ecf20Sopenharmony_ci		msg->rsp_size = 3;
43218c2ecf20Sopenharmony_ci	}
43228c2ecf20Sopenharmony_ci
43238c2ecf20Sopenharmony_ci	if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
43248c2ecf20Sopenharmony_ci	    && (msg->rsp[1] == IPMI_SEND_MSG_CMD)
43258c2ecf20Sopenharmony_ci	    && (msg->user_data != NULL)) {
43268c2ecf20Sopenharmony_ci		/*
43278c2ecf20Sopenharmony_ci		 * It's a response to a response we sent.  For this we
43288c2ecf20Sopenharmony_ci		 * deliver a send message response to the user.
43298c2ecf20Sopenharmony_ci		 */
43308c2ecf20Sopenharmony_ci		struct ipmi_recv_msg *recv_msg = msg->user_data;
43318c2ecf20Sopenharmony_ci
43328c2ecf20Sopenharmony_ci		requeue = 0;
43338c2ecf20Sopenharmony_ci		if (msg->rsp_size < 2)
43348c2ecf20Sopenharmony_ci			/* Message is too small to be correct. */
43358c2ecf20Sopenharmony_ci			goto out;
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_ci		chan = msg->data[2] & 0x0f;
43388c2ecf20Sopenharmony_ci		if (chan >= IPMI_MAX_CHANNELS)
43398c2ecf20Sopenharmony_ci			/* Invalid channel number */
43408c2ecf20Sopenharmony_ci			goto out;
43418c2ecf20Sopenharmony_ci
43428c2ecf20Sopenharmony_ci		if (!recv_msg)
43438c2ecf20Sopenharmony_ci			goto out;
43448c2ecf20Sopenharmony_ci
43458c2ecf20Sopenharmony_ci		recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
43468c2ecf20Sopenharmony_ci		recv_msg->msg.data = recv_msg->msg_data;
43478c2ecf20Sopenharmony_ci		recv_msg->msg.data_len = 1;
43488c2ecf20Sopenharmony_ci		recv_msg->msg_data[0] = msg->rsp[2];
43498c2ecf20Sopenharmony_ci		deliver_local_response(intf, recv_msg);
43508c2ecf20Sopenharmony_ci	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
43518c2ecf20Sopenharmony_ci		   && (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
43528c2ecf20Sopenharmony_ci		struct ipmi_channel   *chans;
43538c2ecf20Sopenharmony_ci
43548c2ecf20Sopenharmony_ci		/* It's from the receive queue. */
43558c2ecf20Sopenharmony_ci		chan = msg->rsp[3] & 0xf;
43568c2ecf20Sopenharmony_ci		if (chan >= IPMI_MAX_CHANNELS) {
43578c2ecf20Sopenharmony_ci			/* Invalid channel number */
43588c2ecf20Sopenharmony_ci			requeue = 0;
43598c2ecf20Sopenharmony_ci			goto out;
43608c2ecf20Sopenharmony_ci		}
43618c2ecf20Sopenharmony_ci
43628c2ecf20Sopenharmony_ci		/*
43638c2ecf20Sopenharmony_ci		 * We need to make sure the channels have been initialized.
43648c2ecf20Sopenharmony_ci		 * The channel_handler routine will set the "curr_channel"
43658c2ecf20Sopenharmony_ci		 * equal to or greater than IPMI_MAX_CHANNELS when all the
43668c2ecf20Sopenharmony_ci		 * channels for this interface have been initialized.
43678c2ecf20Sopenharmony_ci		 */
43688c2ecf20Sopenharmony_ci		if (!intf->channels_ready) {
43698c2ecf20Sopenharmony_ci			requeue = 0; /* Throw the message away */
43708c2ecf20Sopenharmony_ci			goto out;
43718c2ecf20Sopenharmony_ci		}
43728c2ecf20Sopenharmony_ci
43738c2ecf20Sopenharmony_ci		chans = READ_ONCE(intf->channel_list)->c;
43748c2ecf20Sopenharmony_ci
43758c2ecf20Sopenharmony_ci		switch (chans[chan].medium) {
43768c2ecf20Sopenharmony_ci		case IPMI_CHANNEL_MEDIUM_IPMB:
43778c2ecf20Sopenharmony_ci			if (msg->rsp[4] & 0x04) {
43788c2ecf20Sopenharmony_ci				/*
43798c2ecf20Sopenharmony_ci				 * It's a response, so find the
43808c2ecf20Sopenharmony_ci				 * requesting message and send it up.
43818c2ecf20Sopenharmony_ci				 */
43828c2ecf20Sopenharmony_ci				requeue = handle_ipmb_get_msg_rsp(intf, msg);
43838c2ecf20Sopenharmony_ci			} else {
43848c2ecf20Sopenharmony_ci				/*
43858c2ecf20Sopenharmony_ci				 * It's a command to the SMS from some other
43868c2ecf20Sopenharmony_ci				 * entity.  Handle that.
43878c2ecf20Sopenharmony_ci				 */
43888c2ecf20Sopenharmony_ci				requeue = handle_ipmb_get_msg_cmd(intf, msg);
43898c2ecf20Sopenharmony_ci			}
43908c2ecf20Sopenharmony_ci			break;
43918c2ecf20Sopenharmony_ci
43928c2ecf20Sopenharmony_ci		case IPMI_CHANNEL_MEDIUM_8023LAN:
43938c2ecf20Sopenharmony_ci		case IPMI_CHANNEL_MEDIUM_ASYNC:
43948c2ecf20Sopenharmony_ci			if (msg->rsp[6] & 0x04) {
43958c2ecf20Sopenharmony_ci				/*
43968c2ecf20Sopenharmony_ci				 * It's a response, so find the
43978c2ecf20Sopenharmony_ci				 * requesting message and send it up.
43988c2ecf20Sopenharmony_ci				 */
43998c2ecf20Sopenharmony_ci				requeue = handle_lan_get_msg_rsp(intf, msg);
44008c2ecf20Sopenharmony_ci			} else {
44018c2ecf20Sopenharmony_ci				/*
44028c2ecf20Sopenharmony_ci				 * It's a command to the SMS from some other
44038c2ecf20Sopenharmony_ci				 * entity.  Handle that.
44048c2ecf20Sopenharmony_ci				 */
44058c2ecf20Sopenharmony_ci				requeue = handle_lan_get_msg_cmd(intf, msg);
44068c2ecf20Sopenharmony_ci			}
44078c2ecf20Sopenharmony_ci			break;
44088c2ecf20Sopenharmony_ci
44098c2ecf20Sopenharmony_ci		default:
44108c2ecf20Sopenharmony_ci			/* Check for OEM Channels.  Clients had better
44118c2ecf20Sopenharmony_ci			   register for these commands. */
44128c2ecf20Sopenharmony_ci			if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
44138c2ecf20Sopenharmony_ci			    && (chans[chan].medium
44148c2ecf20Sopenharmony_ci				<= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
44158c2ecf20Sopenharmony_ci				requeue = handle_oem_get_msg_cmd(intf, msg);
44168c2ecf20Sopenharmony_ci			} else {
44178c2ecf20Sopenharmony_ci				/*
44188c2ecf20Sopenharmony_ci				 * We don't handle the channel type, so just
44198c2ecf20Sopenharmony_ci				 * free the message.
44208c2ecf20Sopenharmony_ci				 */
44218c2ecf20Sopenharmony_ci				requeue = 0;
44228c2ecf20Sopenharmony_ci			}
44238c2ecf20Sopenharmony_ci		}
44248c2ecf20Sopenharmony_ci
44258c2ecf20Sopenharmony_ci	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
44268c2ecf20Sopenharmony_ci		   && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) {
44278c2ecf20Sopenharmony_ci		/* It's an asynchronous event. */
44288c2ecf20Sopenharmony_ci		requeue = handle_read_event_rsp(intf, msg);
44298c2ecf20Sopenharmony_ci	} else {
44308c2ecf20Sopenharmony_ci		/* It's a response from the local BMC. */
44318c2ecf20Sopenharmony_ci		requeue = handle_bmc_rsp(intf, msg);
44328c2ecf20Sopenharmony_ci	}
44338c2ecf20Sopenharmony_ci
44348c2ecf20Sopenharmony_ci out:
44358c2ecf20Sopenharmony_ci	return requeue;
44368c2ecf20Sopenharmony_ci}
44378c2ecf20Sopenharmony_ci
44388c2ecf20Sopenharmony_ci/*
44398c2ecf20Sopenharmony_ci * If there are messages in the queue or pretimeouts, handle them.
44408c2ecf20Sopenharmony_ci */
44418c2ecf20Sopenharmony_cistatic void handle_new_recv_msgs(struct ipmi_smi *intf)
44428c2ecf20Sopenharmony_ci{
44438c2ecf20Sopenharmony_ci	struct ipmi_smi_msg  *smi_msg;
44448c2ecf20Sopenharmony_ci	unsigned long        flags = 0;
44458c2ecf20Sopenharmony_ci	int                  rv;
44468c2ecf20Sopenharmony_ci	int                  run_to_completion = intf->run_to_completion;
44478c2ecf20Sopenharmony_ci
44488c2ecf20Sopenharmony_ci	/* See if any waiting messages need to be processed. */
44498c2ecf20Sopenharmony_ci	if (!run_to_completion)
44508c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
44518c2ecf20Sopenharmony_ci	while (!list_empty(&intf->waiting_rcv_msgs)) {
44528c2ecf20Sopenharmony_ci		smi_msg = list_entry(intf->waiting_rcv_msgs.next,
44538c2ecf20Sopenharmony_ci				     struct ipmi_smi_msg, link);
44548c2ecf20Sopenharmony_ci		list_del(&smi_msg->link);
44558c2ecf20Sopenharmony_ci		if (!run_to_completion)
44568c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
44578c2ecf20Sopenharmony_ci					       flags);
44588c2ecf20Sopenharmony_ci		rv = handle_one_recv_msg(intf, smi_msg);
44598c2ecf20Sopenharmony_ci		if (!run_to_completion)
44608c2ecf20Sopenharmony_ci			spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
44618c2ecf20Sopenharmony_ci		if (rv > 0) {
44628c2ecf20Sopenharmony_ci			/*
44638c2ecf20Sopenharmony_ci			 * To preserve message order, quit if we
44648c2ecf20Sopenharmony_ci			 * can't handle a message.  Add the message
44658c2ecf20Sopenharmony_ci			 * back at the head, this is safe because this
44668c2ecf20Sopenharmony_ci			 * tasklet is the only thing that pulls the
44678c2ecf20Sopenharmony_ci			 * messages.
44688c2ecf20Sopenharmony_ci			 */
44698c2ecf20Sopenharmony_ci			list_add(&smi_msg->link, &intf->waiting_rcv_msgs);
44708c2ecf20Sopenharmony_ci			break;
44718c2ecf20Sopenharmony_ci		} else {
44728c2ecf20Sopenharmony_ci			if (rv == 0)
44738c2ecf20Sopenharmony_ci				/* Message handled */
44748c2ecf20Sopenharmony_ci				ipmi_free_smi_msg(smi_msg);
44758c2ecf20Sopenharmony_ci			/* If rv < 0, fatal error, del but don't free. */
44768c2ecf20Sopenharmony_ci		}
44778c2ecf20Sopenharmony_ci	}
44788c2ecf20Sopenharmony_ci	if (!run_to_completion)
44798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags);
44808c2ecf20Sopenharmony_ci
44818c2ecf20Sopenharmony_ci	/*
44828c2ecf20Sopenharmony_ci	 * If the pretimout count is non-zero, decrement one from it and
44838c2ecf20Sopenharmony_ci	 * deliver pretimeouts to all the users.
44848c2ecf20Sopenharmony_ci	 */
44858c2ecf20Sopenharmony_ci	if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) {
44868c2ecf20Sopenharmony_ci		struct ipmi_user *user;
44878c2ecf20Sopenharmony_ci		int index;
44888c2ecf20Sopenharmony_ci
44898c2ecf20Sopenharmony_ci		index = srcu_read_lock(&intf->users_srcu);
44908c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(user, &intf->users, link) {
44918c2ecf20Sopenharmony_ci			if (user->handler->ipmi_watchdog_pretimeout)
44928c2ecf20Sopenharmony_ci				user->handler->ipmi_watchdog_pretimeout(
44938c2ecf20Sopenharmony_ci					user->handler_data);
44948c2ecf20Sopenharmony_ci		}
44958c2ecf20Sopenharmony_ci		srcu_read_unlock(&intf->users_srcu, index);
44968c2ecf20Sopenharmony_ci	}
44978c2ecf20Sopenharmony_ci}
44988c2ecf20Sopenharmony_ci
44998c2ecf20Sopenharmony_cistatic void smi_recv_tasklet(struct tasklet_struct *t)
45008c2ecf20Sopenharmony_ci{
45018c2ecf20Sopenharmony_ci	unsigned long flags = 0; /* keep us warning-free. */
45028c2ecf20Sopenharmony_ci	struct ipmi_smi *intf = from_tasklet(intf, t, recv_tasklet);
45038c2ecf20Sopenharmony_ci	int run_to_completion = intf->run_to_completion;
45048c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *newmsg = NULL;
45058c2ecf20Sopenharmony_ci
45068c2ecf20Sopenharmony_ci	/*
45078c2ecf20Sopenharmony_ci	 * Start the next message if available.
45088c2ecf20Sopenharmony_ci	 *
45098c2ecf20Sopenharmony_ci	 * Do this here, not in the actual receiver, because we may deadlock
45108c2ecf20Sopenharmony_ci	 * because the lower layer is allowed to hold locks while calling
45118c2ecf20Sopenharmony_ci	 * message delivery.
45128c2ecf20Sopenharmony_ci	 */
45138c2ecf20Sopenharmony_ci
45148c2ecf20Sopenharmony_ci	rcu_read_lock();
45158c2ecf20Sopenharmony_ci
45168c2ecf20Sopenharmony_ci	if (!run_to_completion)
45178c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
45188c2ecf20Sopenharmony_ci	if (intf->curr_msg == NULL && !intf->in_shutdown) {
45198c2ecf20Sopenharmony_ci		struct list_head *entry = NULL;
45208c2ecf20Sopenharmony_ci
45218c2ecf20Sopenharmony_ci		/* Pick the high priority queue first. */
45228c2ecf20Sopenharmony_ci		if (!list_empty(&intf->hp_xmit_msgs))
45238c2ecf20Sopenharmony_ci			entry = intf->hp_xmit_msgs.next;
45248c2ecf20Sopenharmony_ci		else if (!list_empty(&intf->xmit_msgs))
45258c2ecf20Sopenharmony_ci			entry = intf->xmit_msgs.next;
45268c2ecf20Sopenharmony_ci
45278c2ecf20Sopenharmony_ci		if (entry) {
45288c2ecf20Sopenharmony_ci			list_del(entry);
45298c2ecf20Sopenharmony_ci			newmsg = list_entry(entry, struct ipmi_smi_msg, link);
45308c2ecf20Sopenharmony_ci			intf->curr_msg = newmsg;
45318c2ecf20Sopenharmony_ci		}
45328c2ecf20Sopenharmony_ci	}
45338c2ecf20Sopenharmony_ci
45348c2ecf20Sopenharmony_ci	if (!run_to_completion)
45358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
45368c2ecf20Sopenharmony_ci	if (newmsg)
45378c2ecf20Sopenharmony_ci		intf->handlers->sender(intf->send_info, newmsg);
45388c2ecf20Sopenharmony_ci
45398c2ecf20Sopenharmony_ci	rcu_read_unlock();
45408c2ecf20Sopenharmony_ci
45418c2ecf20Sopenharmony_ci	handle_new_recv_msgs(intf);
45428c2ecf20Sopenharmony_ci}
45438c2ecf20Sopenharmony_ci
45448c2ecf20Sopenharmony_ci/* Handle a new message from the lower layer. */
45458c2ecf20Sopenharmony_civoid ipmi_smi_msg_received(struct ipmi_smi *intf,
45468c2ecf20Sopenharmony_ci			   struct ipmi_smi_msg *msg)
45478c2ecf20Sopenharmony_ci{
45488c2ecf20Sopenharmony_ci	unsigned long flags = 0; /* keep us warning-free. */
45498c2ecf20Sopenharmony_ci	int run_to_completion = intf->run_to_completion;
45508c2ecf20Sopenharmony_ci
45518c2ecf20Sopenharmony_ci	/*
45528c2ecf20Sopenharmony_ci	 * To preserve message order, we keep a queue and deliver from
45538c2ecf20Sopenharmony_ci	 * a tasklet.
45548c2ecf20Sopenharmony_ci	 */
45558c2ecf20Sopenharmony_ci	if (!run_to_completion)
45568c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
45578c2ecf20Sopenharmony_ci	list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
45588c2ecf20Sopenharmony_ci	if (!run_to_completion)
45598c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
45608c2ecf20Sopenharmony_ci				       flags);
45618c2ecf20Sopenharmony_ci
45628c2ecf20Sopenharmony_ci	if (!run_to_completion)
45638c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
45648c2ecf20Sopenharmony_ci	/*
45658c2ecf20Sopenharmony_ci	 * We can get an asynchronous event or receive message in addition
45668c2ecf20Sopenharmony_ci	 * to commands we send.
45678c2ecf20Sopenharmony_ci	 */
45688c2ecf20Sopenharmony_ci	if (msg == intf->curr_msg)
45698c2ecf20Sopenharmony_ci		intf->curr_msg = NULL;
45708c2ecf20Sopenharmony_ci	if (!run_to_completion)
45718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
45728c2ecf20Sopenharmony_ci
45738c2ecf20Sopenharmony_ci	if (run_to_completion)
45748c2ecf20Sopenharmony_ci		smi_recv_tasklet(&intf->recv_tasklet);
45758c2ecf20Sopenharmony_ci	else
45768c2ecf20Sopenharmony_ci		tasklet_schedule(&intf->recv_tasklet);
45778c2ecf20Sopenharmony_ci}
45788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_msg_received);
45798c2ecf20Sopenharmony_ci
45808c2ecf20Sopenharmony_civoid ipmi_smi_watchdog_pretimeout(struct ipmi_smi *intf)
45818c2ecf20Sopenharmony_ci{
45828c2ecf20Sopenharmony_ci	if (intf->in_shutdown)
45838c2ecf20Sopenharmony_ci		return;
45848c2ecf20Sopenharmony_ci
45858c2ecf20Sopenharmony_ci	atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
45868c2ecf20Sopenharmony_ci	tasklet_schedule(&intf->recv_tasklet);
45878c2ecf20Sopenharmony_ci}
45888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);
45898c2ecf20Sopenharmony_ci
45908c2ecf20Sopenharmony_cistatic struct ipmi_smi_msg *
45918c2ecf20Sopenharmony_cismi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg,
45928c2ecf20Sopenharmony_ci		  unsigned char seq, long seqid)
45938c2ecf20Sopenharmony_ci{
45948c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg();
45958c2ecf20Sopenharmony_ci	if (!smi_msg)
45968c2ecf20Sopenharmony_ci		/*
45978c2ecf20Sopenharmony_ci		 * If we can't allocate the message, then just return, we
45988c2ecf20Sopenharmony_ci		 * get 4 retries, so this should be ok.
45998c2ecf20Sopenharmony_ci		 */
46008c2ecf20Sopenharmony_ci		return NULL;
46018c2ecf20Sopenharmony_ci
46028c2ecf20Sopenharmony_ci	memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len);
46038c2ecf20Sopenharmony_ci	smi_msg->data_size = recv_msg->msg.data_len;
46048c2ecf20Sopenharmony_ci	smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid);
46058c2ecf20Sopenharmony_ci
46068c2ecf20Sopenharmony_ci	pr_debug("Resend: %*ph\n", smi_msg->data_size, smi_msg->data);
46078c2ecf20Sopenharmony_ci
46088c2ecf20Sopenharmony_ci	return smi_msg;
46098c2ecf20Sopenharmony_ci}
46108c2ecf20Sopenharmony_ci
46118c2ecf20Sopenharmony_cistatic void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
46128c2ecf20Sopenharmony_ci			      struct list_head *timeouts,
46138c2ecf20Sopenharmony_ci			      unsigned long timeout_period,
46148c2ecf20Sopenharmony_ci			      int slot, unsigned long *flags,
46158c2ecf20Sopenharmony_ci			      bool *need_timer)
46168c2ecf20Sopenharmony_ci{
46178c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *msg;
46188c2ecf20Sopenharmony_ci
46198c2ecf20Sopenharmony_ci	if (intf->in_shutdown)
46208c2ecf20Sopenharmony_ci		return;
46218c2ecf20Sopenharmony_ci
46228c2ecf20Sopenharmony_ci	if (!ent->inuse)
46238c2ecf20Sopenharmony_ci		return;
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci	if (timeout_period < ent->timeout) {
46268c2ecf20Sopenharmony_ci		ent->timeout -= timeout_period;
46278c2ecf20Sopenharmony_ci		*need_timer = true;
46288c2ecf20Sopenharmony_ci		return;
46298c2ecf20Sopenharmony_ci	}
46308c2ecf20Sopenharmony_ci
46318c2ecf20Sopenharmony_ci	if (ent->retries_left == 0) {
46328c2ecf20Sopenharmony_ci		/* The message has used all its retries. */
46338c2ecf20Sopenharmony_ci		ent->inuse = 0;
46348c2ecf20Sopenharmony_ci		smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
46358c2ecf20Sopenharmony_ci		msg = ent->recv_msg;
46368c2ecf20Sopenharmony_ci		list_add_tail(&msg->link, timeouts);
46378c2ecf20Sopenharmony_ci		if (ent->broadcast)
46388c2ecf20Sopenharmony_ci			ipmi_inc_stat(intf, timed_out_ipmb_broadcasts);
46398c2ecf20Sopenharmony_ci		else if (is_lan_addr(&ent->recv_msg->addr))
46408c2ecf20Sopenharmony_ci			ipmi_inc_stat(intf, timed_out_lan_commands);
46418c2ecf20Sopenharmony_ci		else
46428c2ecf20Sopenharmony_ci			ipmi_inc_stat(intf, timed_out_ipmb_commands);
46438c2ecf20Sopenharmony_ci	} else {
46448c2ecf20Sopenharmony_ci		struct ipmi_smi_msg *smi_msg;
46458c2ecf20Sopenharmony_ci		/* More retries, send again. */
46468c2ecf20Sopenharmony_ci
46478c2ecf20Sopenharmony_ci		*need_timer = true;
46488c2ecf20Sopenharmony_ci
46498c2ecf20Sopenharmony_ci		/*
46508c2ecf20Sopenharmony_ci		 * Start with the max timer, set to normal timer after
46518c2ecf20Sopenharmony_ci		 * the message is sent.
46528c2ecf20Sopenharmony_ci		 */
46538c2ecf20Sopenharmony_ci		ent->timeout = MAX_MSG_TIMEOUT;
46548c2ecf20Sopenharmony_ci		ent->retries_left--;
46558c2ecf20Sopenharmony_ci		smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot,
46568c2ecf20Sopenharmony_ci					    ent->seqid);
46578c2ecf20Sopenharmony_ci		if (!smi_msg) {
46588c2ecf20Sopenharmony_ci			if (is_lan_addr(&ent->recv_msg->addr))
46598c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf,
46608c2ecf20Sopenharmony_ci					      dropped_rexmit_lan_commands);
46618c2ecf20Sopenharmony_ci			else
46628c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf,
46638c2ecf20Sopenharmony_ci					      dropped_rexmit_ipmb_commands);
46648c2ecf20Sopenharmony_ci			return;
46658c2ecf20Sopenharmony_ci		}
46668c2ecf20Sopenharmony_ci
46678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->seq_lock, *flags);
46688c2ecf20Sopenharmony_ci
46698c2ecf20Sopenharmony_ci		/*
46708c2ecf20Sopenharmony_ci		 * Send the new message.  We send with a zero
46718c2ecf20Sopenharmony_ci		 * priority.  It timed out, I doubt time is that
46728c2ecf20Sopenharmony_ci		 * critical now, and high priority messages are really
46738c2ecf20Sopenharmony_ci		 * only for messages to the local MC, which don't get
46748c2ecf20Sopenharmony_ci		 * resent.
46758c2ecf20Sopenharmony_ci		 */
46768c2ecf20Sopenharmony_ci		if (intf->handlers) {
46778c2ecf20Sopenharmony_ci			if (is_lan_addr(&ent->recv_msg->addr))
46788c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf,
46798c2ecf20Sopenharmony_ci					      retransmitted_lan_commands);
46808c2ecf20Sopenharmony_ci			else
46818c2ecf20Sopenharmony_ci				ipmi_inc_stat(intf,
46828c2ecf20Sopenharmony_ci					      retransmitted_ipmb_commands);
46838c2ecf20Sopenharmony_ci
46848c2ecf20Sopenharmony_ci			smi_send(intf, intf->handlers, smi_msg, 0);
46858c2ecf20Sopenharmony_ci		} else
46868c2ecf20Sopenharmony_ci			ipmi_free_smi_msg(smi_msg);
46878c2ecf20Sopenharmony_ci
46888c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->seq_lock, *flags);
46898c2ecf20Sopenharmony_ci	}
46908c2ecf20Sopenharmony_ci}
46918c2ecf20Sopenharmony_ci
46928c2ecf20Sopenharmony_cistatic bool ipmi_timeout_handler(struct ipmi_smi *intf,
46938c2ecf20Sopenharmony_ci				 unsigned long timeout_period)
46948c2ecf20Sopenharmony_ci{
46958c2ecf20Sopenharmony_ci	struct list_head     timeouts;
46968c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *msg, *msg2;
46978c2ecf20Sopenharmony_ci	unsigned long        flags;
46988c2ecf20Sopenharmony_ci	int                  i;
46998c2ecf20Sopenharmony_ci	bool                 need_timer = false;
47008c2ecf20Sopenharmony_ci
47018c2ecf20Sopenharmony_ci	if (!intf->bmc_registered) {
47028c2ecf20Sopenharmony_ci		kref_get(&intf->refcount);
47038c2ecf20Sopenharmony_ci		if (!schedule_work(&intf->bmc_reg_work)) {
47048c2ecf20Sopenharmony_ci			kref_put(&intf->refcount, intf_free);
47058c2ecf20Sopenharmony_ci			need_timer = true;
47068c2ecf20Sopenharmony_ci		}
47078c2ecf20Sopenharmony_ci	}
47088c2ecf20Sopenharmony_ci
47098c2ecf20Sopenharmony_ci	/*
47108c2ecf20Sopenharmony_ci	 * Go through the seq table and find any messages that
47118c2ecf20Sopenharmony_ci	 * have timed out, putting them in the timeouts
47128c2ecf20Sopenharmony_ci	 * list.
47138c2ecf20Sopenharmony_ci	 */
47148c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&timeouts);
47158c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intf->seq_lock, flags);
47168c2ecf20Sopenharmony_ci	if (intf->ipmb_maintenance_mode_timeout) {
47178c2ecf20Sopenharmony_ci		if (intf->ipmb_maintenance_mode_timeout <= timeout_period)
47188c2ecf20Sopenharmony_ci			intf->ipmb_maintenance_mode_timeout = 0;
47198c2ecf20Sopenharmony_ci		else
47208c2ecf20Sopenharmony_ci			intf->ipmb_maintenance_mode_timeout -= timeout_period;
47218c2ecf20Sopenharmony_ci	}
47228c2ecf20Sopenharmony_ci	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
47238c2ecf20Sopenharmony_ci		check_msg_timeout(intf, &intf->seq_table[i],
47248c2ecf20Sopenharmony_ci				  &timeouts, timeout_period, i,
47258c2ecf20Sopenharmony_ci				  &flags, &need_timer);
47268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intf->seq_lock, flags);
47278c2ecf20Sopenharmony_ci
47288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(msg, msg2, &timeouts, link)
47298c2ecf20Sopenharmony_ci		deliver_err_response(intf, msg, IPMI_TIMEOUT_COMPLETION_CODE);
47308c2ecf20Sopenharmony_ci
47318c2ecf20Sopenharmony_ci	/*
47328c2ecf20Sopenharmony_ci	 * Maintenance mode handling.  Check the timeout
47338c2ecf20Sopenharmony_ci	 * optimistically before we claim the lock.  It may
47348c2ecf20Sopenharmony_ci	 * mean a timeout gets missed occasionally, but that
47358c2ecf20Sopenharmony_ci	 * only means the timeout gets extended by one period
47368c2ecf20Sopenharmony_ci	 * in that case.  No big deal, and it avoids the lock
47378c2ecf20Sopenharmony_ci	 * most of the time.
47388c2ecf20Sopenharmony_ci	 */
47398c2ecf20Sopenharmony_ci	if (intf->auto_maintenance_timeout > 0) {
47408c2ecf20Sopenharmony_ci		spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
47418c2ecf20Sopenharmony_ci		if (intf->auto_maintenance_timeout > 0) {
47428c2ecf20Sopenharmony_ci			intf->auto_maintenance_timeout
47438c2ecf20Sopenharmony_ci				-= timeout_period;
47448c2ecf20Sopenharmony_ci			if (!intf->maintenance_mode
47458c2ecf20Sopenharmony_ci			    && (intf->auto_maintenance_timeout <= 0)) {
47468c2ecf20Sopenharmony_ci				intf->maintenance_mode_enable = false;
47478c2ecf20Sopenharmony_ci				maintenance_mode_update(intf);
47488c2ecf20Sopenharmony_ci			}
47498c2ecf20Sopenharmony_ci		}
47508c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intf->maintenance_mode_lock,
47518c2ecf20Sopenharmony_ci				       flags);
47528c2ecf20Sopenharmony_ci	}
47538c2ecf20Sopenharmony_ci
47548c2ecf20Sopenharmony_ci	tasklet_schedule(&intf->recv_tasklet);
47558c2ecf20Sopenharmony_ci
47568c2ecf20Sopenharmony_ci	return need_timer;
47578c2ecf20Sopenharmony_ci}
47588c2ecf20Sopenharmony_ci
47598c2ecf20Sopenharmony_cistatic void ipmi_request_event(struct ipmi_smi *intf)
47608c2ecf20Sopenharmony_ci{
47618c2ecf20Sopenharmony_ci	/* No event requests when in maintenance mode. */
47628c2ecf20Sopenharmony_ci	if (intf->maintenance_mode_enable)
47638c2ecf20Sopenharmony_ci		return;
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci	if (!intf->in_shutdown)
47668c2ecf20Sopenharmony_ci		intf->handlers->request_events(intf->send_info);
47678c2ecf20Sopenharmony_ci}
47688c2ecf20Sopenharmony_ci
47698c2ecf20Sopenharmony_cistatic struct timer_list ipmi_timer;
47708c2ecf20Sopenharmony_ci
47718c2ecf20Sopenharmony_cistatic atomic_t stop_operation;
47728c2ecf20Sopenharmony_ci
47738c2ecf20Sopenharmony_cistatic void ipmi_timeout(struct timer_list *unused)
47748c2ecf20Sopenharmony_ci{
47758c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
47768c2ecf20Sopenharmony_ci	bool need_timer = false;
47778c2ecf20Sopenharmony_ci	int index;
47788c2ecf20Sopenharmony_ci
47798c2ecf20Sopenharmony_ci	if (atomic_read(&stop_operation))
47808c2ecf20Sopenharmony_ci		return;
47818c2ecf20Sopenharmony_ci
47828c2ecf20Sopenharmony_ci	index = srcu_read_lock(&ipmi_interfaces_srcu);
47838c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
47848c2ecf20Sopenharmony_ci		if (atomic_read(&intf->event_waiters)) {
47858c2ecf20Sopenharmony_ci			intf->ticks_to_req_ev--;
47868c2ecf20Sopenharmony_ci			if (intf->ticks_to_req_ev == 0) {
47878c2ecf20Sopenharmony_ci				ipmi_request_event(intf);
47888c2ecf20Sopenharmony_ci				intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
47898c2ecf20Sopenharmony_ci			}
47908c2ecf20Sopenharmony_ci			need_timer = true;
47918c2ecf20Sopenharmony_ci		}
47928c2ecf20Sopenharmony_ci
47938c2ecf20Sopenharmony_ci		need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
47948c2ecf20Sopenharmony_ci	}
47958c2ecf20Sopenharmony_ci	srcu_read_unlock(&ipmi_interfaces_srcu, index);
47968c2ecf20Sopenharmony_ci
47978c2ecf20Sopenharmony_ci	if (need_timer)
47988c2ecf20Sopenharmony_ci		mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
47998c2ecf20Sopenharmony_ci}
48008c2ecf20Sopenharmony_ci
48018c2ecf20Sopenharmony_cistatic void need_waiter(struct ipmi_smi *intf)
48028c2ecf20Sopenharmony_ci{
48038c2ecf20Sopenharmony_ci	/* Racy, but worst case we start the timer twice. */
48048c2ecf20Sopenharmony_ci	if (!timer_pending(&ipmi_timer))
48058c2ecf20Sopenharmony_ci		mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
48068c2ecf20Sopenharmony_ci}
48078c2ecf20Sopenharmony_ci
48088c2ecf20Sopenharmony_cistatic atomic_t smi_msg_inuse_count = ATOMIC_INIT(0);
48098c2ecf20Sopenharmony_cistatic atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);
48108c2ecf20Sopenharmony_ci
48118c2ecf20Sopenharmony_cistatic void free_smi_msg(struct ipmi_smi_msg *msg)
48128c2ecf20Sopenharmony_ci{
48138c2ecf20Sopenharmony_ci	atomic_dec(&smi_msg_inuse_count);
48148c2ecf20Sopenharmony_ci	/* Try to keep as much stuff out of the panic path as possible. */
48158c2ecf20Sopenharmony_ci	if (!oops_in_progress)
48168c2ecf20Sopenharmony_ci		kfree(msg);
48178c2ecf20Sopenharmony_ci}
48188c2ecf20Sopenharmony_ci
48198c2ecf20Sopenharmony_cistruct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
48208c2ecf20Sopenharmony_ci{
48218c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *rv;
48228c2ecf20Sopenharmony_ci	rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC);
48238c2ecf20Sopenharmony_ci	if (rv) {
48248c2ecf20Sopenharmony_ci		rv->done = free_smi_msg;
48258c2ecf20Sopenharmony_ci		rv->user_data = NULL;
48268c2ecf20Sopenharmony_ci		atomic_inc(&smi_msg_inuse_count);
48278c2ecf20Sopenharmony_ci	}
48288c2ecf20Sopenharmony_ci	return rv;
48298c2ecf20Sopenharmony_ci}
48308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_alloc_smi_msg);
48318c2ecf20Sopenharmony_ci
48328c2ecf20Sopenharmony_cistatic void free_recv_msg(struct ipmi_recv_msg *msg)
48338c2ecf20Sopenharmony_ci{
48348c2ecf20Sopenharmony_ci	atomic_dec(&recv_msg_inuse_count);
48358c2ecf20Sopenharmony_ci	/* Try to keep as much stuff out of the panic path as possible. */
48368c2ecf20Sopenharmony_ci	if (!oops_in_progress)
48378c2ecf20Sopenharmony_ci		kfree(msg);
48388c2ecf20Sopenharmony_ci}
48398c2ecf20Sopenharmony_ci
48408c2ecf20Sopenharmony_cistatic struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
48418c2ecf20Sopenharmony_ci{
48428c2ecf20Sopenharmony_ci	struct ipmi_recv_msg *rv;
48438c2ecf20Sopenharmony_ci
48448c2ecf20Sopenharmony_ci	rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC);
48458c2ecf20Sopenharmony_ci	if (rv) {
48468c2ecf20Sopenharmony_ci		rv->user = NULL;
48478c2ecf20Sopenharmony_ci		rv->done = free_recv_msg;
48488c2ecf20Sopenharmony_ci		atomic_inc(&recv_msg_inuse_count);
48498c2ecf20Sopenharmony_ci	}
48508c2ecf20Sopenharmony_ci	return rv;
48518c2ecf20Sopenharmony_ci}
48528c2ecf20Sopenharmony_ci
48538c2ecf20Sopenharmony_civoid ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
48548c2ecf20Sopenharmony_ci{
48558c2ecf20Sopenharmony_ci	if (msg->user && !oops_in_progress)
48568c2ecf20Sopenharmony_ci		kref_put(&msg->user->refcount, free_user);
48578c2ecf20Sopenharmony_ci	msg->done(msg);
48588c2ecf20Sopenharmony_ci}
48598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ipmi_free_recv_msg);
48608c2ecf20Sopenharmony_ci
48618c2ecf20Sopenharmony_cistatic atomic_t panic_done_count = ATOMIC_INIT(0);
48628c2ecf20Sopenharmony_ci
48638c2ecf20Sopenharmony_cistatic void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
48648c2ecf20Sopenharmony_ci{
48658c2ecf20Sopenharmony_ci	atomic_dec(&panic_done_count);
48668c2ecf20Sopenharmony_ci}
48678c2ecf20Sopenharmony_ci
48688c2ecf20Sopenharmony_cistatic void dummy_recv_done_handler(struct ipmi_recv_msg *msg)
48698c2ecf20Sopenharmony_ci{
48708c2ecf20Sopenharmony_ci	atomic_dec(&panic_done_count);
48718c2ecf20Sopenharmony_ci}
48728c2ecf20Sopenharmony_ci
48738c2ecf20Sopenharmony_ci/*
48748c2ecf20Sopenharmony_ci * Inside a panic, send a message and wait for a response.
48758c2ecf20Sopenharmony_ci */
48768c2ecf20Sopenharmony_cistatic void ipmi_panic_request_and_wait(struct ipmi_smi *intf,
48778c2ecf20Sopenharmony_ci					struct ipmi_addr *addr,
48788c2ecf20Sopenharmony_ci					struct kernel_ipmi_msg *msg)
48798c2ecf20Sopenharmony_ci{
48808c2ecf20Sopenharmony_ci	struct ipmi_smi_msg  smi_msg;
48818c2ecf20Sopenharmony_ci	struct ipmi_recv_msg recv_msg;
48828c2ecf20Sopenharmony_ci	int rv;
48838c2ecf20Sopenharmony_ci
48848c2ecf20Sopenharmony_ci	smi_msg.done = dummy_smi_done_handler;
48858c2ecf20Sopenharmony_ci	recv_msg.done = dummy_recv_done_handler;
48868c2ecf20Sopenharmony_ci	atomic_add(2, &panic_done_count);
48878c2ecf20Sopenharmony_ci	rv = i_ipmi_request(NULL,
48888c2ecf20Sopenharmony_ci			    intf,
48898c2ecf20Sopenharmony_ci			    addr,
48908c2ecf20Sopenharmony_ci			    0,
48918c2ecf20Sopenharmony_ci			    msg,
48928c2ecf20Sopenharmony_ci			    intf,
48938c2ecf20Sopenharmony_ci			    &smi_msg,
48948c2ecf20Sopenharmony_ci			    &recv_msg,
48958c2ecf20Sopenharmony_ci			    0,
48968c2ecf20Sopenharmony_ci			    intf->addrinfo[0].address,
48978c2ecf20Sopenharmony_ci			    intf->addrinfo[0].lun,
48988c2ecf20Sopenharmony_ci			    0, 1); /* Don't retry, and don't wait. */
48998c2ecf20Sopenharmony_ci	if (rv)
49008c2ecf20Sopenharmony_ci		atomic_sub(2, &panic_done_count);
49018c2ecf20Sopenharmony_ci	else if (intf->handlers->flush_messages)
49028c2ecf20Sopenharmony_ci		intf->handlers->flush_messages(intf->send_info);
49038c2ecf20Sopenharmony_ci
49048c2ecf20Sopenharmony_ci	while (atomic_read(&panic_done_count) != 0)
49058c2ecf20Sopenharmony_ci		ipmi_poll(intf);
49068c2ecf20Sopenharmony_ci}
49078c2ecf20Sopenharmony_ci
49088c2ecf20Sopenharmony_cistatic void event_receiver_fetcher(struct ipmi_smi *intf,
49098c2ecf20Sopenharmony_ci				   struct ipmi_recv_msg *msg)
49108c2ecf20Sopenharmony_ci{
49118c2ecf20Sopenharmony_ci	if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
49128c2ecf20Sopenharmony_ci	    && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE)
49138c2ecf20Sopenharmony_ci	    && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD)
49148c2ecf20Sopenharmony_ci	    && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) {
49158c2ecf20Sopenharmony_ci		/* A get event receiver command, save it. */
49168c2ecf20Sopenharmony_ci		intf->event_receiver = msg->msg.data[1];
49178c2ecf20Sopenharmony_ci		intf->event_receiver_lun = msg->msg.data[2] & 0x3;
49188c2ecf20Sopenharmony_ci	}
49198c2ecf20Sopenharmony_ci}
49208c2ecf20Sopenharmony_ci
49218c2ecf20Sopenharmony_cistatic void device_id_fetcher(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
49228c2ecf20Sopenharmony_ci{
49238c2ecf20Sopenharmony_ci	if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
49248c2ecf20Sopenharmony_ci	    && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
49258c2ecf20Sopenharmony_ci	    && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD)
49268c2ecf20Sopenharmony_ci	    && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) {
49278c2ecf20Sopenharmony_ci		/*
49288c2ecf20Sopenharmony_ci		 * A get device id command, save if we are an event
49298c2ecf20Sopenharmony_ci		 * receiver or generator.
49308c2ecf20Sopenharmony_ci		 */
49318c2ecf20Sopenharmony_ci		intf->local_sel_device = (msg->msg.data[6] >> 2) & 1;
49328c2ecf20Sopenharmony_ci		intf->local_event_generator = (msg->msg.data[6] >> 5) & 1;
49338c2ecf20Sopenharmony_ci	}
49348c2ecf20Sopenharmony_ci}
49358c2ecf20Sopenharmony_ci
49368c2ecf20Sopenharmony_cistatic void send_panic_events(struct ipmi_smi *intf, char *str)
49378c2ecf20Sopenharmony_ci{
49388c2ecf20Sopenharmony_ci	struct kernel_ipmi_msg msg;
49398c2ecf20Sopenharmony_ci	unsigned char data[16];
49408c2ecf20Sopenharmony_ci	struct ipmi_system_interface_addr *si;
49418c2ecf20Sopenharmony_ci	struct ipmi_addr addr;
49428c2ecf20Sopenharmony_ci	char *p = str;
49438c2ecf20Sopenharmony_ci	struct ipmi_ipmb_addr *ipmb;
49448c2ecf20Sopenharmony_ci	int j;
49458c2ecf20Sopenharmony_ci
49468c2ecf20Sopenharmony_ci	if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE)
49478c2ecf20Sopenharmony_ci		return;
49488c2ecf20Sopenharmony_ci
49498c2ecf20Sopenharmony_ci	si = (struct ipmi_system_interface_addr *) &addr;
49508c2ecf20Sopenharmony_ci	si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
49518c2ecf20Sopenharmony_ci	si->channel = IPMI_BMC_CHANNEL;
49528c2ecf20Sopenharmony_ci	si->lun = 0;
49538c2ecf20Sopenharmony_ci
49548c2ecf20Sopenharmony_ci	/* Fill in an event telling that we have failed. */
49558c2ecf20Sopenharmony_ci	msg.netfn = 0x04; /* Sensor or Event. */
49568c2ecf20Sopenharmony_ci	msg.cmd = 2; /* Platform event command. */
49578c2ecf20Sopenharmony_ci	msg.data = data;
49588c2ecf20Sopenharmony_ci	msg.data_len = 8;
49598c2ecf20Sopenharmony_ci	data[0] = 0x41; /* Kernel generator ID, IPMI table 5-4 */
49608c2ecf20Sopenharmony_ci	data[1] = 0x03; /* This is for IPMI 1.0. */
49618c2ecf20Sopenharmony_ci	data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */
49628c2ecf20Sopenharmony_ci	data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */
49638c2ecf20Sopenharmony_ci	data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */
49648c2ecf20Sopenharmony_ci
49658c2ecf20Sopenharmony_ci	/*
49668c2ecf20Sopenharmony_ci	 * Put a few breadcrumbs in.  Hopefully later we can add more things
49678c2ecf20Sopenharmony_ci	 * to make the panic events more useful.
49688c2ecf20Sopenharmony_ci	 */
49698c2ecf20Sopenharmony_ci	if (str) {
49708c2ecf20Sopenharmony_ci		data[3] = str[0];
49718c2ecf20Sopenharmony_ci		data[6] = str[1];
49728c2ecf20Sopenharmony_ci		data[7] = str[2];
49738c2ecf20Sopenharmony_ci	}
49748c2ecf20Sopenharmony_ci
49758c2ecf20Sopenharmony_ci	/* Send the event announcing the panic. */
49768c2ecf20Sopenharmony_ci	ipmi_panic_request_and_wait(intf, &addr, &msg);
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci	/*
49798c2ecf20Sopenharmony_ci	 * On every interface, dump a bunch of OEM event holding the
49808c2ecf20Sopenharmony_ci	 * string.
49818c2ecf20Sopenharmony_ci	 */
49828c2ecf20Sopenharmony_ci	if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str)
49838c2ecf20Sopenharmony_ci		return;
49848c2ecf20Sopenharmony_ci
49858c2ecf20Sopenharmony_ci	/*
49868c2ecf20Sopenharmony_ci	 * intf_num is used as an marker to tell if the
49878c2ecf20Sopenharmony_ci	 * interface is valid.  Thus we need a read barrier to
49888c2ecf20Sopenharmony_ci	 * make sure data fetched before checking intf_num
49898c2ecf20Sopenharmony_ci	 * won't be used.
49908c2ecf20Sopenharmony_ci	 */
49918c2ecf20Sopenharmony_ci	smp_rmb();
49928c2ecf20Sopenharmony_ci
49938c2ecf20Sopenharmony_ci	/*
49948c2ecf20Sopenharmony_ci	 * First job here is to figure out where to send the
49958c2ecf20Sopenharmony_ci	 * OEM events.  There's no way in IPMI to send OEM
49968c2ecf20Sopenharmony_ci	 * events using an event send command, so we have to
49978c2ecf20Sopenharmony_ci	 * find the SEL to put them in and stick them in
49988c2ecf20Sopenharmony_ci	 * there.
49998c2ecf20Sopenharmony_ci	 */
50008c2ecf20Sopenharmony_ci
50018c2ecf20Sopenharmony_ci	/* Get capabilities from the get device id. */
50028c2ecf20Sopenharmony_ci	intf->local_sel_device = 0;
50038c2ecf20Sopenharmony_ci	intf->local_event_generator = 0;
50048c2ecf20Sopenharmony_ci	intf->event_receiver = 0;
50058c2ecf20Sopenharmony_ci
50068c2ecf20Sopenharmony_ci	/* Request the device info from the local MC. */
50078c2ecf20Sopenharmony_ci	msg.netfn = IPMI_NETFN_APP_REQUEST;
50088c2ecf20Sopenharmony_ci	msg.cmd = IPMI_GET_DEVICE_ID_CMD;
50098c2ecf20Sopenharmony_ci	msg.data = NULL;
50108c2ecf20Sopenharmony_ci	msg.data_len = 0;
50118c2ecf20Sopenharmony_ci	intf->null_user_handler = device_id_fetcher;
50128c2ecf20Sopenharmony_ci	ipmi_panic_request_and_wait(intf, &addr, &msg);
50138c2ecf20Sopenharmony_ci
50148c2ecf20Sopenharmony_ci	if (intf->local_event_generator) {
50158c2ecf20Sopenharmony_ci		/* Request the event receiver from the local MC. */
50168c2ecf20Sopenharmony_ci		msg.netfn = IPMI_NETFN_SENSOR_EVENT_REQUEST;
50178c2ecf20Sopenharmony_ci		msg.cmd = IPMI_GET_EVENT_RECEIVER_CMD;
50188c2ecf20Sopenharmony_ci		msg.data = NULL;
50198c2ecf20Sopenharmony_ci		msg.data_len = 0;
50208c2ecf20Sopenharmony_ci		intf->null_user_handler = event_receiver_fetcher;
50218c2ecf20Sopenharmony_ci		ipmi_panic_request_and_wait(intf, &addr, &msg);
50228c2ecf20Sopenharmony_ci	}
50238c2ecf20Sopenharmony_ci	intf->null_user_handler = NULL;
50248c2ecf20Sopenharmony_ci
50258c2ecf20Sopenharmony_ci	/*
50268c2ecf20Sopenharmony_ci	 * Validate the event receiver.  The low bit must not
50278c2ecf20Sopenharmony_ci	 * be 1 (it must be a valid IPMB address), it cannot
50288c2ecf20Sopenharmony_ci	 * be zero, and it must not be my address.
50298c2ecf20Sopenharmony_ci	 */
50308c2ecf20Sopenharmony_ci	if (((intf->event_receiver & 1) == 0)
50318c2ecf20Sopenharmony_ci	    && (intf->event_receiver != 0)
50328c2ecf20Sopenharmony_ci	    && (intf->event_receiver != intf->addrinfo[0].address)) {
50338c2ecf20Sopenharmony_ci		/*
50348c2ecf20Sopenharmony_ci		 * The event receiver is valid, send an IPMB
50358c2ecf20Sopenharmony_ci		 * message.
50368c2ecf20Sopenharmony_ci		 */
50378c2ecf20Sopenharmony_ci		ipmb = (struct ipmi_ipmb_addr *) &addr;
50388c2ecf20Sopenharmony_ci		ipmb->addr_type = IPMI_IPMB_ADDR_TYPE;
50398c2ecf20Sopenharmony_ci		ipmb->channel = 0; /* FIXME - is this right? */
50408c2ecf20Sopenharmony_ci		ipmb->lun = intf->event_receiver_lun;
50418c2ecf20Sopenharmony_ci		ipmb->slave_addr = intf->event_receiver;
50428c2ecf20Sopenharmony_ci	} else if (intf->local_sel_device) {
50438c2ecf20Sopenharmony_ci		/*
50448c2ecf20Sopenharmony_ci		 * The event receiver was not valid (or was
50458c2ecf20Sopenharmony_ci		 * me), but I am an SEL device, just dump it
50468c2ecf20Sopenharmony_ci		 * in my SEL.
50478c2ecf20Sopenharmony_ci		 */
50488c2ecf20Sopenharmony_ci		si = (struct ipmi_system_interface_addr *) &addr;
50498c2ecf20Sopenharmony_ci		si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
50508c2ecf20Sopenharmony_ci		si->channel = IPMI_BMC_CHANNEL;
50518c2ecf20Sopenharmony_ci		si->lun = 0;
50528c2ecf20Sopenharmony_ci	} else
50538c2ecf20Sopenharmony_ci		return; /* No where to send the event. */
50548c2ecf20Sopenharmony_ci
50558c2ecf20Sopenharmony_ci	msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */
50568c2ecf20Sopenharmony_ci	msg.cmd = IPMI_ADD_SEL_ENTRY_CMD;
50578c2ecf20Sopenharmony_ci	msg.data = data;
50588c2ecf20Sopenharmony_ci	msg.data_len = 16;
50598c2ecf20Sopenharmony_ci
50608c2ecf20Sopenharmony_ci	j = 0;
50618c2ecf20Sopenharmony_ci	while (*p) {
50628c2ecf20Sopenharmony_ci		int size = strlen(p);
50638c2ecf20Sopenharmony_ci
50648c2ecf20Sopenharmony_ci		if (size > 11)
50658c2ecf20Sopenharmony_ci			size = 11;
50668c2ecf20Sopenharmony_ci		data[0] = 0;
50678c2ecf20Sopenharmony_ci		data[1] = 0;
50688c2ecf20Sopenharmony_ci		data[2] = 0xf0; /* OEM event without timestamp. */
50698c2ecf20Sopenharmony_ci		data[3] = intf->addrinfo[0].address;
50708c2ecf20Sopenharmony_ci		data[4] = j++; /* sequence # */
50718c2ecf20Sopenharmony_ci		/*
50728c2ecf20Sopenharmony_ci		 * Always give 11 bytes, so strncpy will fill
50738c2ecf20Sopenharmony_ci		 * it with zeroes for me.
50748c2ecf20Sopenharmony_ci		 */
50758c2ecf20Sopenharmony_ci		strncpy(data+5, p, 11);
50768c2ecf20Sopenharmony_ci		p += size;
50778c2ecf20Sopenharmony_ci
50788c2ecf20Sopenharmony_ci		ipmi_panic_request_and_wait(intf, &addr, &msg);
50798c2ecf20Sopenharmony_ci	}
50808c2ecf20Sopenharmony_ci}
50818c2ecf20Sopenharmony_ci
50828c2ecf20Sopenharmony_cistatic int has_panicked;
50838c2ecf20Sopenharmony_ci
50848c2ecf20Sopenharmony_cistatic int panic_event(struct notifier_block *this,
50858c2ecf20Sopenharmony_ci		       unsigned long         event,
50868c2ecf20Sopenharmony_ci		       void                  *ptr)
50878c2ecf20Sopenharmony_ci{
50888c2ecf20Sopenharmony_ci	struct ipmi_smi *intf;
50898c2ecf20Sopenharmony_ci	struct ipmi_user *user;
50908c2ecf20Sopenharmony_ci
50918c2ecf20Sopenharmony_ci	if (has_panicked)
50928c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
50938c2ecf20Sopenharmony_ci	has_panicked = 1;
50948c2ecf20Sopenharmony_ci
50958c2ecf20Sopenharmony_ci	/* For every registered interface, set it to run to completion. */
50968c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
50978c2ecf20Sopenharmony_ci		if (!intf->handlers || intf->intf_num == -1)
50988c2ecf20Sopenharmony_ci			/* Interface is not ready. */
50998c2ecf20Sopenharmony_ci			continue;
51008c2ecf20Sopenharmony_ci
51018c2ecf20Sopenharmony_ci		if (!intf->handlers->poll)
51028c2ecf20Sopenharmony_ci			continue;
51038c2ecf20Sopenharmony_ci
51048c2ecf20Sopenharmony_ci		/*
51058c2ecf20Sopenharmony_ci		 * If we were interrupted while locking xmit_msgs_lock or
51068c2ecf20Sopenharmony_ci		 * waiting_rcv_msgs_lock, the corresponding list may be
51078c2ecf20Sopenharmony_ci		 * corrupted.  In this case, drop items on the list for
51088c2ecf20Sopenharmony_ci		 * the safety.
51098c2ecf20Sopenharmony_ci		 */
51108c2ecf20Sopenharmony_ci		if (!spin_trylock(&intf->xmit_msgs_lock)) {
51118c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&intf->xmit_msgs);
51128c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&intf->hp_xmit_msgs);
51138c2ecf20Sopenharmony_ci		} else
51148c2ecf20Sopenharmony_ci			spin_unlock(&intf->xmit_msgs_lock);
51158c2ecf20Sopenharmony_ci
51168c2ecf20Sopenharmony_ci		if (!spin_trylock(&intf->waiting_rcv_msgs_lock))
51178c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(&intf->waiting_rcv_msgs);
51188c2ecf20Sopenharmony_ci		else
51198c2ecf20Sopenharmony_ci			spin_unlock(&intf->waiting_rcv_msgs_lock);
51208c2ecf20Sopenharmony_ci
51218c2ecf20Sopenharmony_ci		intf->run_to_completion = 1;
51228c2ecf20Sopenharmony_ci		if (intf->handlers->set_run_to_completion)
51238c2ecf20Sopenharmony_ci			intf->handlers->set_run_to_completion(intf->send_info,
51248c2ecf20Sopenharmony_ci							      1);
51258c2ecf20Sopenharmony_ci
51268c2ecf20Sopenharmony_ci		list_for_each_entry_rcu(user, &intf->users, link) {
51278c2ecf20Sopenharmony_ci			if (user->handler->ipmi_panic_handler)
51288c2ecf20Sopenharmony_ci				user->handler->ipmi_panic_handler(
51298c2ecf20Sopenharmony_ci					user->handler_data);
51308c2ecf20Sopenharmony_ci		}
51318c2ecf20Sopenharmony_ci
51328c2ecf20Sopenharmony_ci		send_panic_events(intf, ptr);
51338c2ecf20Sopenharmony_ci	}
51348c2ecf20Sopenharmony_ci
51358c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
51368c2ecf20Sopenharmony_ci}
51378c2ecf20Sopenharmony_ci
51388c2ecf20Sopenharmony_ci/* Must be called with ipmi_interfaces_mutex held. */
51398c2ecf20Sopenharmony_cistatic int ipmi_register_driver(void)
51408c2ecf20Sopenharmony_ci{
51418c2ecf20Sopenharmony_ci	int rv;
51428c2ecf20Sopenharmony_ci
51438c2ecf20Sopenharmony_ci	if (drvregistered)
51448c2ecf20Sopenharmony_ci		return 0;
51458c2ecf20Sopenharmony_ci
51468c2ecf20Sopenharmony_ci	rv = driver_register(&ipmidriver.driver);
51478c2ecf20Sopenharmony_ci	if (rv)
51488c2ecf20Sopenharmony_ci		pr_err("Could not register IPMI driver\n");
51498c2ecf20Sopenharmony_ci	else
51508c2ecf20Sopenharmony_ci		drvregistered = true;
51518c2ecf20Sopenharmony_ci	return rv;
51528c2ecf20Sopenharmony_ci}
51538c2ecf20Sopenharmony_ci
51548c2ecf20Sopenharmony_cistatic struct notifier_block panic_block = {
51558c2ecf20Sopenharmony_ci	.notifier_call	= panic_event,
51568c2ecf20Sopenharmony_ci	.next		= NULL,
51578c2ecf20Sopenharmony_ci	.priority	= 200	/* priority: INT_MAX >= x >= 0 */
51588c2ecf20Sopenharmony_ci};
51598c2ecf20Sopenharmony_ci
51608c2ecf20Sopenharmony_cistatic int ipmi_init_msghandler(void)
51618c2ecf20Sopenharmony_ci{
51628c2ecf20Sopenharmony_ci	int rv;
51638c2ecf20Sopenharmony_ci
51648c2ecf20Sopenharmony_ci	mutex_lock(&ipmi_interfaces_mutex);
51658c2ecf20Sopenharmony_ci	rv = ipmi_register_driver();
51668c2ecf20Sopenharmony_ci	if (rv)
51678c2ecf20Sopenharmony_ci		goto out;
51688c2ecf20Sopenharmony_ci	if (initialized)
51698c2ecf20Sopenharmony_ci		goto out;
51708c2ecf20Sopenharmony_ci
51718c2ecf20Sopenharmony_ci	rv = init_srcu_struct(&ipmi_interfaces_srcu);
51728c2ecf20Sopenharmony_ci	if (rv)
51738c2ecf20Sopenharmony_ci		goto out;
51748c2ecf20Sopenharmony_ci
51758c2ecf20Sopenharmony_ci	remove_work_wq = create_singlethread_workqueue("ipmi-msghandler-remove-wq");
51768c2ecf20Sopenharmony_ci	if (!remove_work_wq) {
51778c2ecf20Sopenharmony_ci		pr_err("unable to create ipmi-msghandler-remove-wq workqueue");
51788c2ecf20Sopenharmony_ci		rv = -ENOMEM;
51798c2ecf20Sopenharmony_ci		goto out_wq;
51808c2ecf20Sopenharmony_ci	}
51818c2ecf20Sopenharmony_ci
51828c2ecf20Sopenharmony_ci	timer_setup(&ipmi_timer, ipmi_timeout, 0);
51838c2ecf20Sopenharmony_ci	mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
51848c2ecf20Sopenharmony_ci
51858c2ecf20Sopenharmony_ci	atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
51868c2ecf20Sopenharmony_ci
51878c2ecf20Sopenharmony_ci	initialized = true;
51888c2ecf20Sopenharmony_ci
51898c2ecf20Sopenharmony_ciout_wq:
51908c2ecf20Sopenharmony_ci	if (rv)
51918c2ecf20Sopenharmony_ci		cleanup_srcu_struct(&ipmi_interfaces_srcu);
51928c2ecf20Sopenharmony_ciout:
51938c2ecf20Sopenharmony_ci	mutex_unlock(&ipmi_interfaces_mutex);
51948c2ecf20Sopenharmony_ci	return rv;
51958c2ecf20Sopenharmony_ci}
51968c2ecf20Sopenharmony_ci
51978c2ecf20Sopenharmony_cistatic int __init ipmi_init_msghandler_mod(void)
51988c2ecf20Sopenharmony_ci{
51998c2ecf20Sopenharmony_ci	int rv;
52008c2ecf20Sopenharmony_ci
52018c2ecf20Sopenharmony_ci	pr_info("version " IPMI_DRIVER_VERSION "\n");
52028c2ecf20Sopenharmony_ci
52038c2ecf20Sopenharmony_ci	mutex_lock(&ipmi_interfaces_mutex);
52048c2ecf20Sopenharmony_ci	rv = ipmi_register_driver();
52058c2ecf20Sopenharmony_ci	mutex_unlock(&ipmi_interfaces_mutex);
52068c2ecf20Sopenharmony_ci
52078c2ecf20Sopenharmony_ci	return rv;
52088c2ecf20Sopenharmony_ci}
52098c2ecf20Sopenharmony_ci
52108c2ecf20Sopenharmony_cistatic void __exit cleanup_ipmi(void)
52118c2ecf20Sopenharmony_ci{
52128c2ecf20Sopenharmony_ci	int count;
52138c2ecf20Sopenharmony_ci
52148c2ecf20Sopenharmony_ci	if (initialized) {
52158c2ecf20Sopenharmony_ci		destroy_workqueue(remove_work_wq);
52168c2ecf20Sopenharmony_ci
52178c2ecf20Sopenharmony_ci		atomic_notifier_chain_unregister(&panic_notifier_list,
52188c2ecf20Sopenharmony_ci						 &panic_block);
52198c2ecf20Sopenharmony_ci
52208c2ecf20Sopenharmony_ci		/*
52218c2ecf20Sopenharmony_ci		 * This can't be called if any interfaces exist, so no worry
52228c2ecf20Sopenharmony_ci		 * about shutting down the interfaces.
52238c2ecf20Sopenharmony_ci		 */
52248c2ecf20Sopenharmony_ci
52258c2ecf20Sopenharmony_ci		/*
52268c2ecf20Sopenharmony_ci		 * Tell the timer to stop, then wait for it to stop.  This
52278c2ecf20Sopenharmony_ci		 * avoids problems with race conditions removing the timer
52288c2ecf20Sopenharmony_ci		 * here.
52298c2ecf20Sopenharmony_ci		 */
52308c2ecf20Sopenharmony_ci		atomic_set(&stop_operation, 1);
52318c2ecf20Sopenharmony_ci		del_timer_sync(&ipmi_timer);
52328c2ecf20Sopenharmony_ci
52338c2ecf20Sopenharmony_ci		initialized = false;
52348c2ecf20Sopenharmony_ci
52358c2ecf20Sopenharmony_ci		/* Check for buffer leaks. */
52368c2ecf20Sopenharmony_ci		count = atomic_read(&smi_msg_inuse_count);
52378c2ecf20Sopenharmony_ci		if (count != 0)
52388c2ecf20Sopenharmony_ci			pr_warn("SMI message count %d at exit\n", count);
52398c2ecf20Sopenharmony_ci		count = atomic_read(&recv_msg_inuse_count);
52408c2ecf20Sopenharmony_ci		if (count != 0)
52418c2ecf20Sopenharmony_ci			pr_warn("recv message count %d at exit\n", count);
52428c2ecf20Sopenharmony_ci
52438c2ecf20Sopenharmony_ci		cleanup_srcu_struct(&ipmi_interfaces_srcu);
52448c2ecf20Sopenharmony_ci	}
52458c2ecf20Sopenharmony_ci	if (drvregistered)
52468c2ecf20Sopenharmony_ci		driver_unregister(&ipmidriver.driver);
52478c2ecf20Sopenharmony_ci}
52488c2ecf20Sopenharmony_cimodule_exit(cleanup_ipmi);
52498c2ecf20Sopenharmony_ci
52508c2ecf20Sopenharmony_cimodule_init(ipmi_init_msghandler_mod);
52518c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
52528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
52538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI"
52548c2ecf20Sopenharmony_ci		   " interface.");
52558c2ecf20Sopenharmony_ciMODULE_VERSION(IPMI_DRIVER_VERSION);
52568c2ecf20Sopenharmony_ciMODULE_SOFTDEP("post: ipmi_devintf");
5257