ipmi_msghandler.c 117 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * ipmi_msghandler.c
 *
 * Incoming and outgoing message routing for an IPMI interface.
 *
 * Author: MontaVista Software, Inc.
 *         Corey Minyard <minyard@mvista.com>
 *         source@mvista.com
 *
 * Copyright 2002 MontaVista Software Inc.
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at your
 *  option) any later version.
 *
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/poll.h>
37
#include <linux/sched.h>
38
#include <linux/seq_file.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
#include <linux/spinlock.h>
40
#include <linux/mutex.h>
Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
44
45
46
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
47
#include <linux/rcupdate.h>
48
#include <linux/interrupt.h>
Linus Torvalds's avatar
Linus Torvalds committed
49
50

#define PFX "IPMI message handler: "
51

Corey Minyard's avatar
Corey Minyard committed
52
#define IPMI_DRIVER_VERSION "39.2"
Linus Torvalds's avatar
Linus Torvalds committed
53
54
55

static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
56
57
static void smi_recv_tasklet(unsigned long);
static void handle_new_recv_msgs(ipmi_smi_t intf);
58
static void need_waiter(ipmi_smi_t intf);
Linus Torvalds's avatar
Linus Torvalds committed
59

Randy Dunlap's avatar
Randy Dunlap committed
60
static int initialized;
Linus Torvalds's avatar
Linus Torvalds committed
61

62
#ifdef CONFIG_PROC_FS
Randy Dunlap's avatar
Randy Dunlap committed
63
static struct proc_dir_entry *proc_ipmi_root;
64
#endif /* CONFIG_PROC_FS */
Linus Torvalds's avatar
Linus Torvalds committed
65

66
67
68
/* Remain in auto-maintenance mode for this amount of time (in ms). */
#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000

Linus Torvalds's avatar
Linus Torvalds committed
69
70
#define MAX_EVENTS_IN_QUEUE	25

71
72
73
74
/*
 * Don't let a message sit in a queue forever, always time it with at lest
 * the max message timer.  This is in milliseconds.
 */
Linus Torvalds's avatar
Linus Torvalds committed
75
76
#define MAX_MSG_TIMEOUT		60000

77
78
79
80
81
82
83
84
85
86
87
88
89
90
/* Call every ~1000 ms. */
#define IPMI_TIMEOUT_TIME	1000

/* How many jiffies does it take to get to the timeout time. */
#define IPMI_TIMEOUT_JIFFIES	((IPMI_TIMEOUT_TIME * HZ) / 1000)

/*
 * Request events from the queue every second (this is the number of
 * IPMI_TIMEOUT_TIMES between event requests).  Hopefully, in the
 * future, IPMI will add a way to know immediately if an event is in
 * the queue and this silliness can go away.
 */
#define IPMI_REQUEST_EV_TIME	(1000 / (IPMI_TIMEOUT_TIME))

91
92
93
/*
 * The main "user" data structure.
 */
94
struct ipmi_user {
Linus Torvalds's avatar
Linus Torvalds committed
95
96
	struct list_head link;

Corey Minyard's avatar
Corey Minyard committed
97
98
	/* Set to false when the user is destroyed. */
	bool valid;
99
100
101

	struct kref refcount;

Linus Torvalds's avatar
Linus Torvalds committed
102
103
104
105
106
107
108
109
	/* The upper layer that handles receive messages. */
	struct ipmi_user_hndl *handler;
	void             *handler_data;

	/* The interface this user is bound to. */
	ipmi_smi_t intf;

	/* Does this interface receive IPMI events? */
110
	bool gets_events;
Linus Torvalds's avatar
Linus Torvalds committed
111
112
};

113
struct cmd_rcvr {
Linus Torvalds's avatar
Linus Torvalds committed
114
115
116
117
118
	struct list_head link;

	ipmi_user_t   user;
	unsigned char netfn;
	unsigned char cmd;
119
	unsigned int  chans;
120
121
122
123
124
125
126

	/*
	 * This is used to form a linked lised during mass deletion.
	 * Since this is in an RCU list, we cannot use the link above
	 * or change any data until the RCU period completes.  So we
	 * use this next variable during mass deletion so we can have
	 * a list and don't have to wait and restart the search on
127
128
	 * every individual deletion of a command.
	 */
129
	struct cmd_rcvr *next;
Linus Torvalds's avatar
Linus Torvalds committed
130
131
};

132
struct seq_table {
Linus Torvalds's avatar
Linus Torvalds committed
133
134
135
136
137
138
139
	unsigned int         inuse : 1;
	unsigned int         broadcast : 1;

	unsigned long        timeout;
	unsigned long        orig_timeout;
	unsigned int         retries_left;

140
141
142
143
144
	/*
	 * To verify on an incoming send message response that this is
	 * the message that the response is for, we keep a sequence id
	 * and increment it every time we send a message.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
145
146
	long                 seqid;

147
148
149
150
151
	/*
	 * This is held so we can properly respond to the message on a
	 * timeout, and it is used to hold the temporary data for
	 * retransmission, too.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
152
153
154
	struct ipmi_recv_msg *recv_msg;
};

155
156
157
158
/*
 * Store the information in a msgid (long) to allow us to find a
 * sequence table entry from the msgid.
 */
Linus Torvalds's avatar
Linus Torvalds committed
159
160
161
162
163
164
#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))

#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \
	do {								\
		seq = ((msgid >> 26) & 0x3f);				\
		seqid = (msgid & 0x3fffff);				\
165
	} while (0)
Linus Torvalds's avatar
Linus Torvalds committed
166
167
168

#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)

169
struct ipmi_channel {
Linus Torvalds's avatar
Linus Torvalds committed
170
171
	unsigned char medium;
	unsigned char protocol;
172

173
174
175
176
	/*
	 * My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,
	 * but may be changed by the user.
	 */
177
178
	unsigned char address;

179
180
181
182
	/*
	 * My LUN.  This should generally stay the SMS LUN, but just in
	 * case...
	 */
183
	unsigned char lun;
Linus Torvalds's avatar
Linus Torvalds committed
184
185
};

186
#ifdef CONFIG_PROC_FS
187
struct ipmi_proc_entry {
Linus Torvalds's avatar
Linus Torvalds committed
188
189
190
	char                   *name;
	struct ipmi_proc_entry *next;
};
191
#endif
Linus Torvalds's avatar
Linus Torvalds committed
192

193
struct bmc_device {
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
	struct platform_device *dev;
	struct ipmi_device_id  id;
	unsigned char          guid[16];
	int                    guid_set;

	struct kref	       refcount;

	/* bmc device attributes */
	struct device_attribute device_id_attr;
	struct device_attribute provides_dev_sdrs_attr;
	struct device_attribute revision_attr;
	struct device_attribute firmware_rev_attr;
	struct device_attribute version_attr;
	struct device_attribute add_dev_support_attr;
	struct device_attribute manufacturer_id_attr;
	struct device_attribute product_id_attr;
	struct device_attribute guid_attr;
	struct device_attribute aux_firmware_rev_attr;
};

214
215
216
217
/*
 * Various statistics for IPMI, these index stats[] in the ipmi_smi
 * structure.
 */
218
219
220
enum ipmi_stat_indexes {
	/* Commands we got from the user that were invalid. */
	IPMI_STAT_sent_invalid_commands = 0,
221

222
223
	/* Commands we sent to the MC. */
	IPMI_STAT_sent_local_commands,
224

225
226
	/* Responses from the MC that were delivered to a user. */
	IPMI_STAT_handled_local_responses,
227

228
229
	/* Responses from the MC that were not delivered to a user. */
	IPMI_STAT_unhandled_local_responses,
230

231
232
	/* Commands we sent out to the IPMB bus. */
	IPMI_STAT_sent_ipmb_commands,
233

234
235
	/* Commands sent on the IPMB that had errors on the SEND CMD */
	IPMI_STAT_sent_ipmb_command_errs,
236

237
238
	/* Each retransmit increments this count. */
	IPMI_STAT_retransmitted_ipmb_commands,
239

240
241
242
243
244
	/*
	 * When a message times out (runs out of retransmits) this is
	 * incremented.
	 */
	IPMI_STAT_timed_out_ipmb_commands,
245

246
247
248
249
250
251
	/*
	 * This is like above, but for broadcasts.  Broadcasts are
	 * *not* included in the above count (they are expected to
	 * time out).
	 */
	IPMI_STAT_timed_out_ipmb_broadcasts,
252

253
254
	/* Responses I have sent to the IPMB bus. */
	IPMI_STAT_sent_ipmb_responses,
255

256
257
	/* The response was delivered to the user. */
	IPMI_STAT_handled_ipmb_responses,
258

259
260
	/* The response had invalid data in it. */
	IPMI_STAT_invalid_ipmb_responses,
261

262
263
	/* The response didn't have anyone waiting for it. */
	IPMI_STAT_unhandled_ipmb_responses,
264

265
266
	/* Commands we sent out to the IPMB bus. */
	IPMI_STAT_sent_lan_commands,
267

268
269
	/* Commands sent on the IPMB that had errors on the SEND CMD */
	IPMI_STAT_sent_lan_command_errs,
270

271
272
	/* Each retransmit increments this count. */
	IPMI_STAT_retransmitted_lan_commands,
273

274
275
276
277
278
279
280
281
	/*
	 * When a message times out (runs out of retransmits) this is
	 * incremented.
	 */
	IPMI_STAT_timed_out_lan_commands,

	/* Responses I have sent to the IPMB bus. */
	IPMI_STAT_sent_lan_responses,
282

283
284
	/* The response was delivered to the user. */
	IPMI_STAT_handled_lan_responses,
285

286
287
	/* The response had invalid data in it. */
	IPMI_STAT_invalid_lan_responses,
288

289
290
	/* The response didn't have anyone waiting for it. */
	IPMI_STAT_unhandled_lan_responses,
291

292
293
	/* The command was delivered to the user. */
	IPMI_STAT_handled_commands,
294

295
296
	/* The command had invalid data in it. */
	IPMI_STAT_invalid_commands,
297

298
299
	/* The command didn't have anyone waiting for it. */
	IPMI_STAT_unhandled_commands,
300

301
302
	/* Invalid data in an event. */
	IPMI_STAT_invalid_events,
303

304
305
	/* Events that were received with the proper format. */
	IPMI_STAT_events,
306

307
308
309
310
311
	/* Retransmissions on IPMB that failed. */
	IPMI_STAT_dropped_rexmit_ipmb_commands,

	/* Retransmissions on LAN that failed. */
	IPMI_STAT_dropped_rexmit_lan_commands,
312

313
314
315
	/* This *must* remain last, add new values above this. */
	IPMI_NUM_STATS
};
316
317


Linus Torvalds's avatar
Linus Torvalds committed
318
#define IPMI_IPMB_NUM_SEQ	64
319
#define IPMI_MAX_CHANNELS       16
320
struct ipmi_smi {
Linus Torvalds's avatar
Linus Torvalds committed
321
322
323
	/* What interface number are we? */
	int intf_num;

324
325
	struct kref refcount;

326
327
328
	/* Used for a list of interfaces. */
	struct list_head link;

329
330
331
332
	/*
	 * The list of upper layers that are using me.  seq_lock
	 * protects this.
	 */
333
	struct list_head users;
Linus Torvalds's avatar
Linus Torvalds committed
334

335
336
337
338
	/* Information to supply to users. */
	unsigned char ipmi_version_major;
	unsigned char ipmi_version_minor;

Linus Torvalds's avatar
Linus Torvalds committed
339
340
341
	/* Used for wake ups at startup. */
	wait_queue_head_t waitq;

342
343
	struct bmc_device *bmc;
	char *my_dev_name;
344
	char *sysfs_name;
Linus Torvalds's avatar
Linus Torvalds committed
345

346
347
	/*
	 * This is the lower-layer's sender routine.  Note that you
348
349
	 * must either be holding the ipmi_interfaces_mutex or be in
	 * an umpreemptible region to use this.  You must fetch the
350
351
	 * value into a local variable and make sure it is not NULL.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
352
353
354
	struct ipmi_smi_handlers *handlers;
	void                     *send_info;

355
#ifdef CONFIG_PROC_FS
Corey Minyard's avatar
Corey Minyard committed
356
357
	/* A list of proc entries for this interface. */
	struct mutex           proc_entry_lock;
Linus Torvalds's avatar
Linus Torvalds committed
358
	struct ipmi_proc_entry *proc_entries;
359
#endif
Linus Torvalds's avatar
Linus Torvalds committed
360

361
362
363
	/* Driver-model device for the system interface. */
	struct device          *si_dev;

364
365
366
367
368
369
	/*
	 * A table of sequence numbers for this interface.  We use the
	 * sequence numbers for IPMB messages that go out of the
	 * interface to match them up with their responses.  A routine
	 * is called periodically to time the items in this list.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
370
371
372
373
	spinlock_t       seq_lock;
	struct seq_table seq_table[IPMI_IPMB_NUM_SEQ];
	int curr_seq;

374
	/*
375
376
377
378
	 * Messages queued for delivery.  If delivery fails (out of memory
	 * for instance), They will stay in here to be processed later in a
	 * periodic timer interrupt.  The tasklet is for handling received
	 * messages directly from the handler.
379
	 */
Linus Torvalds's avatar
Linus Torvalds committed
380
381
	spinlock_t       waiting_msgs_lock;
	struct list_head waiting_msgs;
382
383
	atomic_t	 watchdog_pretimeouts_to_deliver;
	struct tasklet_struct recv_tasklet;
Linus Torvalds's avatar
Linus Torvalds committed
384

385
386
387
388
	/*
	 * The list of command receivers that are registered for commands
	 * on this interface.
	 */
389
	struct mutex     cmd_rcvrs_mutex;
Linus Torvalds's avatar
Linus Torvalds committed
390
391
	struct list_head cmd_rcvrs;

392
393
394
395
	/*
	 * Events that were queues because no one was there to receive
	 * them.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
396
397
398
	spinlock_t       events_lock; /* For dealing with event stuff. */
	struct list_head waiting_events;
	unsigned int     waiting_events_count; /* How many events in queue? */
399
400
	char             delivering_events;
	char             event_msg_printed;
401
402
403
	atomic_t         event_waiters;
	unsigned int     ticks_to_req_ev;
	int              last_needs_timer;
Linus Torvalds's avatar
Linus Torvalds committed
404

405
406
407
408
	/*
	 * The event receiver for my BMC, only really used at panic
	 * shutdown as a place to store this.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
409
410
411
412
413
	unsigned char event_receiver;
	unsigned char event_receiver_lun;
	unsigned char local_sel_device;
	unsigned char local_event_generator;

414
415
	/* For handling of maintenance mode. */
	int maintenance_mode;
Corey Minyard's avatar
Corey Minyard committed
416
	bool maintenance_mode_enable;
417
418
419
	int auto_maintenance_timeout;
	spinlock_t maintenance_mode_lock; /* Used in a timer... */

420
421
422
423
424
425
	/*
	 * A cheap hack, if this is non-null and a message to an
	 * interface comes in with a NULL user, call this routine with
	 * it.  Note that the message will still be freed by the
	 * caller.  This only works on the system interface.
	 */
426
	void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
Linus Torvalds's avatar
Linus Torvalds committed
427

428
429
430
431
	/*
	 * When we are scanning the channels for an SMI, this will
	 * tell which channel we are scanning.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
435
436
437
438
439
440
	int curr_channel;

	/* Channel information */
	struct ipmi_channel channels[IPMI_MAX_CHANNELS];

	/* Proc FS stuff. */
	struct proc_dir_entry *proc_dir;
	char                  proc_dir_name[10];

441
	atomic_t stats[IPMI_NUM_STATS];
442
443
444
445
446
447
448

	/*
	 * run_to_completion duplicate of smb_info, smi_info
	 * and ipmi_serial_info structures. Used to decrease numbers of
	 * parameters passed by "low" level IPMI code.
	 */
	int run_to_completion;
Linus Torvalds's avatar
Linus Torvalds committed
449
};
450
#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
Linus Torvalds's avatar
Linus Torvalds committed
451

452
453
454
/**
 * The driver model view of the IPMI messaging driver.
 */
455
456
457
458
459
static struct platform_driver ipmidriver = {
	.driver = {
		.name = "ipmi",
		.bus = &platform_bus_type
	}
460
461
462
};
static DEFINE_MUTEX(ipmidriver_mutex);

463
static LIST_HEAD(ipmi_interfaces);
464
static DEFINE_MUTEX(ipmi_interfaces_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
465

466
467
468
/*
 * List of watchers that want to know when smi's are added and deleted.
 */
469
static LIST_HEAD(smi_watchers);
470
static DEFINE_MUTEX(smi_watchers_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
471

472
473
474
475
476
#define ipmi_inc_stat(intf, stat) \
	atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat])
#define ipmi_get_stat(intf, stat) \
	((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat]))

477
478
479
480
481
482
483
484
485
486
487
488
489
490
static int is_lan_addr(struct ipmi_addr *addr)
{
	return addr->addr_type == IPMI_LAN_ADDR_TYPE;
}

static int is_ipmb_addr(struct ipmi_addr *addr)
{
	return addr->addr_type == IPMI_IPMB_ADDR_TYPE;
}

static int is_ipmb_bcast_addr(struct ipmi_addr *addr)
{
	return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE;
}
491

492
493
494
495
496
497
498
499
500
501
static void free_recv_msg_list(struct list_head *q)
{
	struct ipmi_recv_msg *msg, *msg2;

	list_for_each_entry_safe(msg, msg2, q, link) {
		list_del(&msg->link);
		ipmi_free_recv_msg(msg);
	}
}

502
503
504
505
506
507
508
509
510
511
static void free_smi_msg_list(struct list_head *q)
{
	struct ipmi_smi_msg *msg, *msg2;

	list_for_each_entry_safe(msg, msg2, q, link) {
		list_del(&msg->link);
		ipmi_free_smi_msg(msg);
	}
}

512
513
514
515
516
517
static void clean_up_interface_data(ipmi_smi_t intf)
{
	int              i;
	struct cmd_rcvr  *rcvr, *rcvr2;
	struct list_head list;

518
519
	tasklet_kill(&intf->recv_tasklet);

520
	free_smi_msg_list(&intf->waiting_msgs);
521
522
	free_recv_msg_list(&intf->waiting_events);

523
524
525
526
	/*
	 * Wholesale remove all the entries from the list in the
	 * interface and wait for RCU to know that none are in use.
	 */
527
	mutex_lock(&intf->cmd_rcvrs_mutex);
528
529
	INIT_LIST_HEAD(&list);
	list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu);
530
	mutex_unlock(&intf->cmd_rcvrs_mutex);
531
532
533
534
535
536

	list_for_each_entry_safe(rcvr, rcvr2, &list, link)
		kfree(rcvr);

	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
		if ((intf->seq_table[i].inuse)
537
					&& (intf->seq_table[i].recv_msg))
538
539
540
541
542
543
544
545
546
547
548
549
			ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
	}
}

static void intf_free(struct kref *ref)
{
	ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount);

	clean_up_interface_data(intf);
	kfree(intf);
}

550
struct watcher_entry {
551
552
	int              intf_num;
	ipmi_smi_t       intf;
553
554
555
	struct list_head link;
};

Linus Torvalds's avatar
Linus Torvalds committed
556
557
int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
{
558
	ipmi_smi_t intf;
559
	LIST_HEAD(to_deliver);
560
561
	struct watcher_entry *e, *e2;

562
563
	mutex_lock(&smi_watchers_mutex);

564
565
	mutex_lock(&ipmi_interfaces_mutex);

566
	/* Build a list of things to deliver. */
567
	list_for_each_entry(intf, &ipmi_interfaces, link) {
568
569
570
571
572
		if (intf->intf_num == -1)
			continue;
		e = kmalloc(sizeof(*e), GFP_KERNEL);
		if (!e)
			goto out_err;
573
574
		kref_get(&intf->refcount);
		e->intf = intf;
575
576
577
		e->intf_num = intf->intf_num;
		list_add_tail(&e->link, &to_deliver);
	}
Linus Torvalds's avatar
Linus Torvalds committed
578

579
580
	/* We will succeed, so add it to the list. */
	list_add(&watcher->link, &smi_watchers);
581
582
583
584
585

	mutex_unlock(&ipmi_interfaces_mutex);

	list_for_each_entry_safe(e, e2, &to_deliver, link) {
		list_del(&e->link);
586
587
		watcher->new_smi(e->intf_num, e->intf->si_dev);
		kref_put(&e->intf->refcount, intf_free);
588
		kfree(e);
Linus Torvalds's avatar
Linus Torvalds committed
589
	}
590

591
	mutex_unlock(&smi_watchers_mutex);
592

Linus Torvalds's avatar
Linus Torvalds committed
593
	return 0;
594
595

 out_err:
596
597
	mutex_unlock(&ipmi_interfaces_mutex);
	mutex_unlock(&smi_watchers_mutex);
598
599
	list_for_each_entry_safe(e, e2, &to_deliver, link) {
		list_del(&e->link);
600
		kref_put(&e->intf->refcount, intf_free);
601
602
603
		kfree(e);
	}
	return -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
604
}
605
EXPORT_SYMBOL(ipmi_smi_watcher_register);
Linus Torvalds's avatar
Linus Torvalds committed
606
607
608

int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
{
609
	mutex_lock(&smi_watchers_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
610
	list_del(&(watcher->link));
611
	mutex_unlock(&smi_watchers_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
612
613
	return 0;
}
614
EXPORT_SYMBOL(ipmi_smi_watcher_unregister);
Linus Torvalds's avatar
Linus Torvalds committed
615

616
617
618
/*
 * Must be called with smi_watchers_mutex held.
 */
Linus Torvalds's avatar
Linus Torvalds committed
619
static void
620
call_smi_watchers(int i, struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
621
622
623
624
625
{
	struct ipmi_smi_watcher *w;

	list_for_each_entry(w, &smi_watchers, link) {
		if (try_module_get(w->owner)) {
626
			w->new_smi(i, dev);
Linus Torvalds's avatar
Linus Torvalds committed
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
			module_put(w->owner);
		}
	}
}

static int
ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
{
	if (addr1->addr_type != addr2->addr_type)
		return 0;

	if (addr1->channel != addr2->channel)
		return 0;

	if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
		struct ipmi_system_interface_addr *smi_addr1
		    = (struct ipmi_system_interface_addr *) addr1;
		struct ipmi_system_interface_addr *smi_addr2
		    = (struct ipmi_system_interface_addr *) addr2;
		return (smi_addr1->lun == smi_addr2->lun);
	}

649
	if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) {
Linus Torvalds's avatar
Linus Torvalds committed
650
651
652
653
654
655
656
657
658
		struct ipmi_ipmb_addr *ipmb_addr1
		    = (struct ipmi_ipmb_addr *) addr1;
		struct ipmi_ipmb_addr *ipmb_addr2
		    = (struct ipmi_ipmb_addr *) addr2;

		return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr)
			&& (ipmb_addr1->lun == ipmb_addr2->lun));
	}

659
	if (is_lan_addr(addr1)) {
Linus Torvalds's avatar
Linus Torvalds committed
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
		struct ipmi_lan_addr *lan_addr1
			= (struct ipmi_lan_addr *) addr1;
		struct ipmi_lan_addr *lan_addr2
		    = (struct ipmi_lan_addr *) addr2;

		return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID)
			&& (lan_addr1->local_SWID == lan_addr2->local_SWID)
			&& (lan_addr1->session_handle
			    == lan_addr2->session_handle)
			&& (lan_addr1->lun == lan_addr2->lun));
	}

	return 1;
}

int ipmi_validate_addr(struct ipmi_addr *addr, int len)
{
677
	if (len < sizeof(struct ipmi_system_interface_addr))
Linus Torvalds's avatar
Linus Torvalds committed
678
679
680
681
682
683
684
685
686
		return -EINVAL;

	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {
		if (addr->channel != IPMI_BMC_CHANNEL)
			return -EINVAL;
		return 0;
	}

	if ((addr->channel == IPMI_BMC_CHANNEL)
687
	    || (addr->channel >= IPMI_MAX_CHANNELS)
Linus Torvalds's avatar
Linus Torvalds committed
688
689
690
	    || (addr->channel < 0))
		return -EINVAL;

691
	if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) {
692
		if (len < sizeof(struct ipmi_ipmb_addr))
Linus Torvalds's avatar
Linus Torvalds committed
693
694
695
696
			return -EINVAL;
		return 0;
	}

697
	if (is_lan_addr(addr)) {
698
		if (len < sizeof(struct ipmi_lan_addr))
Linus Torvalds's avatar
Linus Torvalds committed
699
700
701
702
703
704
			return -EINVAL;
		return 0;
	}

	return -EINVAL;
}
705
EXPORT_SYMBOL(ipmi_validate_addr);
Linus Torvalds's avatar
Linus Torvalds committed
706
707
708
709
710
711
712

unsigned int ipmi_addr_length(int addr_type)
{
	if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
		return sizeof(struct ipmi_system_interface_addr);

	if ((addr_type == IPMI_IPMB_ADDR_TYPE)
713
			|| (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
Linus Torvalds's avatar
Linus Torvalds committed
714
715
716
717
718
719
720
		return sizeof(struct ipmi_ipmb_addr);

	if (addr_type == IPMI_LAN_ADDR_TYPE)
		return sizeof(struct ipmi_lan_addr);

	return 0;
}
721
EXPORT_SYMBOL(ipmi_addr_length);
Linus Torvalds's avatar
Linus Torvalds committed
722
723
724

static void deliver_response(struct ipmi_recv_msg *msg)
{
725
	if (!msg->user) {
726
727
728
729
730
		ipmi_smi_t    intf = msg->user_msg_data;

		/* Special handling for NULL users. */
		if (intf->null_user_handler) {
			intf->null_user_handler(intf, msg);
731
			ipmi_inc_stat(intf, handled_local_responses);
732
733
		} else {
			/* No handler, so give up. */
734
			ipmi_inc_stat(intf, unhandled_local_responses);
735
736
737
		}
		ipmi_free_recv_msg(msg);
	} else {
738
739
		ipmi_user_t user = msg->user;
		user->handler->ipmi_recv_hndl(msg, user->handler_data);
740
	}
Linus Torvalds's avatar
Linus Torvalds committed
741
742
}

743
744
745
746
747
748
749
750
751
752
753
static void
deliver_err_response(struct ipmi_recv_msg *msg, int err)
{
	msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
	msg->msg_data[0] = err;
	msg->msg.netfn |= 1; /* Convert to a response. */
	msg->msg.data_len = 1;
	msg->msg.data = msg->msg_data;
	deliver_response(msg);
}

754
755
756
757
758
/*
 * Find the next sequence number not being used and add the given
 * message with the given timeout to the sequence table.  This must be
 * called with the interface's seq_lock held.
 */
Linus Torvalds's avatar
Linus Torvalds committed
759
760
761
762
763
764
765
766
767
768
769
static int intf_next_seq(ipmi_smi_t           intf,
			 struct ipmi_recv_msg *recv_msg,
			 unsigned long        timeout,
			 int                  retries,
			 int                  broadcast,
			 unsigned char        *seq,
			 long                 *seqid)
{
	int          rv = 0;
	unsigned int i;

770
771
	for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
					i = (i+1)%IPMI_IPMB_NUM_SEQ) {
772
		if (!intf->seq_table[i].inuse)
Linus Torvalds's avatar
Linus Torvalds committed
773
774
775
			break;
	}

776
	if (!intf->seq_table[i].inuse) {
Linus Torvalds's avatar
Linus Torvalds committed
777
778
		intf->seq_table[i].recv_msg = recv_msg;

779
780
781
782
		/*
		 * Start with the maximum timeout, when the send response
		 * comes in we will start the real timer.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
783
784
785
786
787
788
789
790
791
		intf->seq_table[i].timeout = MAX_MSG_TIMEOUT;
		intf->seq_table[i].orig_timeout = timeout;
		intf->seq_table[i].retries_left = retries;
		intf->seq_table[i].broadcast = broadcast;
		intf->seq_table[i].inuse = 1;
		intf->seq_table[i].seqid = NEXT_SEQID(intf->seq_table[i].seqid);
		*seq = i;
		*seqid = intf->seq_table[i].seqid;
		intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
792
		need_waiter(intf);
Linus Torvalds's avatar
Linus Torvalds committed
793
794
795
	} else {
		rv = -EAGAIN;
	}
796

Linus Torvalds's avatar
Linus Torvalds committed
797
798
799
	return rv;
}

800
801
802
803
804
805
806
/*
 * Return the receive message for the given sequence number and
 * release the sequence number so it can be reused.  Some other data
 * is passed in to be sure the message matches up correctly (to help
 * guard against message coming in after their timeout and the
 * sequence number being reused).
 */
Linus Torvalds's avatar
Linus Torvalds committed
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
static int intf_find_seq(ipmi_smi_t           intf,
			 unsigned char        seq,
			 short                channel,
			 unsigned char        cmd,
			 unsigned char        netfn,
			 struct ipmi_addr     *addr,
			 struct ipmi_recv_msg **recv_msg)
{
	int           rv = -ENODEV;
	unsigned long flags;

	if (seq >= IPMI_IPMB_NUM_SEQ)
		return -EINVAL;

	spin_lock_irqsave(&(intf->seq_lock), flags);
	if (intf->seq_table[seq].inuse) {
		struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;

825
826
827
		if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd)
				&& (msg->msg.netfn == netfn)
				&& (ipmi_addr_equal(addr, &(msg->addr)))) {
Linus Torvalds's avatar
Linus Torvalds committed
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
			*recv_msg = msg;
			intf->seq_table[seq].inuse = 0;
			rv = 0;
		}
	}
	spin_unlock_irqrestore(&(intf->seq_lock), flags);

	return rv;
}


/* Start the timer for a specific sequence table entry. */
static int intf_start_seq_timer(ipmi_smi_t intf,
				long       msgid)
{
	int           rv = -ENODEV;
	unsigned long flags;
	unsigned char seq;
	unsigned long seqid;


	GET_SEQ_FROM_MSGID(msgid, seq, seqid);

	spin_lock_irqsave(&(intf->seq_lock), flags);
852
853
854
855
	/*
	 * We do this verification because the user can be deleted
	 * while a message is outstanding.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
856
	if ((intf->seq_table[seq].inuse)
857
				&& (intf->seq_table[seq].seqid == seqid)) {
Linus Torvalds's avatar
Linus Torvalds committed
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
		struct seq_table *ent = &(intf->seq_table[seq]);
		ent->timeout = ent->orig_timeout;
		rv = 0;
	}
	spin_unlock_irqrestore(&(intf->seq_lock), flags);

	return rv;
}

/* Got an error for the send message for a specific sequence number. */
static int intf_err_seq(ipmi_smi_t   intf,
			long         msgid,
			unsigned int err)
{
	int                  rv = -ENODEV;
	unsigned long        flags;
	unsigned char        seq;
	unsigned long        seqid;
	struct ipmi_recv_msg *msg = NULL;


	GET_SEQ_FROM_MSGID(msgid, seq, seqid);

	spin_lock_irqsave(&(intf->seq_lock), flags);
882
883
884
885
	/*
	 * We do this verification because the user can be deleted
	 * while a message is outstanding.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
886
	if ((intf->seq_table[seq].inuse)
887
				&& (intf->seq_table[seq].seqid == seqid)) {
Linus Torvalds's avatar
Linus Torvalds committed
888
889
890
891
892
893
894
895
		struct seq_table *ent = &(intf->seq_table[seq]);

		ent->inuse = 0;
		msg = ent->recv_msg;
		rv = 0;
	}
	spin_unlock_irqrestore(&(intf->seq_lock), flags);

896
897
	if (msg)
		deliver_err_response(msg, err);
Linus Torvalds's avatar
Linus Torvalds committed
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912

	return rv;
}


int ipmi_create_user(unsigned int          if_num,
		     struct ipmi_user_hndl *handler,
		     void                  *handler_data,
		     ipmi_user_t           *user)
{
	unsigned long flags;
	ipmi_user_t   new_user;
	int           rv = 0;
	ipmi_smi_t    intf;

913
914
915
916
917
918
919
	/*
	 * There is no module usecount here, because it's not
	 * required.  Since this can only be used by and called from
	 * other modules, they will implicitly use this module, and
	 * thus this can't be removed unless the other modules are
	 * removed.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
920
921
922
923

	if (handler == NULL)
		return -EINVAL;

924
925
926
927
	/*
	 * Make sure the driver is actually initialized, this handles
	 * problems with initialization order.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
928
929
930
931
932
	if (!initialized) {
		rv = ipmi_init_msghandler();
		if (rv)
			return rv;

933
934
935
936
		/*
		 * The init code doesn't return an error if it was turned
		 * off, but it won't initialize.  Check that.
		 */
Linus Torvalds's avatar
Linus Torvalds committed
937
938
939
940
941
		if (!initialized)
			return -ENODEV;
	}

	new_user = kmalloc(sizeof(*new_user), GFP_KERNEL);
942
	if (!new_user)
Linus Torvalds's avatar
Linus Torvalds committed
943
944
		return -ENOMEM;

945
	mutex_lock(&ipmi_interfaces_mutex);
946
947
948
	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
		if (intf->intf_num == if_num)
			goto found;
Linus Torvalds's avatar
Linus Torvalds committed
949
	}
950
	/* Not found, return an error */
951
952
	rv = -EINVAL;
	goto out_kfree;
Linus Torvalds's avatar
Linus Torvalds committed
953

954
 found:
955
956
	/* Note that each existing user holds a refcount to the interface. */
	kref_get(&intf->refcount);
Linus Torvalds's avatar
Linus Torvalds committed
957

958
	kref_init(&new_user->refcount);
Linus Torvalds's avatar
Linus Torvalds committed
959
960
961
	new_user->handler = handler;
	new_user->handler_data = handler_data;
	new_user->intf = intf;
962
	new_user->gets_events = false;
Linus Torvalds's avatar
Linus Torvalds committed
963
964
965

	if (!try_module_get(intf->handlers->owner)) {
		rv = -ENODEV;
966
		goto out_kref;
Linus Torvalds's avatar
Linus Torvalds committed
967
968
969
970
971
972
	}

	if (intf->handlers->inc_usecount) {
		rv = intf->handlers->inc_usecount(intf->send_info);
		if (rv) {
			module_put(intf->handlers->owner);
973
			goto out_kref;
Linus Torvalds's avatar
Linus Torvalds committed
974
975
976
		}
	}

977
978
979
980
	/*
	 * Hold the lock so intf->handlers is guaranteed to be good
	 * until now
	 */
981
982
	mutex_unlock(&ipmi_interfaces_mutex);

Corey Minyard's avatar
Corey Minyard committed
983
	new_user->valid = true;
984
985
986
	spin_lock_irqsave(&intf->seq_lock, flags);
	list_add_rcu(&new_user->link, &intf->users);
	spin_unlock_irqrestore(&intf->seq_lock, flags);
987
988
989
990
991
	if (handler->ipmi_watchdog_pretimeout) {
		/* User wants pretimeouts, so make sure to watch for them. */
		if (atomic_inc_return(&intf->event_waiters) == 1)
			need_waiter(intf);
	}
992
993
	*user = new_user;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
994

995
out_kref:
996
	kref_put(&intf->refcount, intf_free);
997
out_kfree:
998
	mutex_unlock(&ipmi_interfaces_mutex);
999
	kfree(new_user);
Linus Torvalds's avatar
Linus Torvalds committed
1000
1001
	return rv;
}
1002
EXPORT_SYMBOL(ipmi_create_user);
Linus Torvalds's avatar
Linus Torvalds committed
1003

1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
{
	int           rv = 0;
	ipmi_smi_t    intf;
	struct ipmi_smi_handlers *handlers;

	mutex_lock(&ipmi_interfaces_mutex);
	list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
		if (intf->intf_num == if_num)
			goto found;
	}
	/* Not found, return an error */
	rv = -EINVAL;
	mutex_unlock(&ipmi_interfaces_mutex);
	return rv;

found:
	handlers = intf->handlers;
	rv = -ENOSYS;
	if (handlers->get_smi_info)
		rv = handlers->get_smi_info(intf->send_info, data);
	mutex_unlock(&ipmi_interfaces_mutex);

	return rv;
}
EXPORT_SYMBOL(ipmi_get_smi_info);

1031
1032
1033
1034
1035
1036
1037
static void free_user(struct kref *ref)
{
	ipmi_user_t user = container_of(ref, struct ipmi_user, refcount);
	kfree(user);
}

int ipmi_destroy_user(ipmi_user_t user)
Linus Torvalds's avatar
Linus Torvalds committed
1038
{
1039
	ipmi_smi_t       intf = user->intf;
Linus Torvalds's avatar
Linus Torvalds committed
1040
1041
	int              i;
	unsigned long    flags;
1042
1043
	struct cmd_rcvr  *rcvr;
	struct cmd_rcvr  *rcvrs = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
1044

Corey Minyard's avatar
Corey Minyard committed
1045
	user->valid = false;
Linus Torvalds's avatar
Linus Torvalds committed
1046

1047
1048
1049
1050
1051
1052
	if (user->handler->ipmi_watchdog_pretimeout)
		atomic_dec(&intf->event_waiters);

	if (user->gets_events)
		atomic_dec(&intf->event_waiters);

1053
1054
1055
	/* Remove the user from the interface's sequence table. */
	spin_lock_irqsave(&intf->seq_lock, flags);
	list_del_rcu(&user->link);
Linus Torvalds's avatar
Linus Torvalds committed
1056

Corey Minyard's avatar
Corey Minyard committed
1057
	for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
1058
		if (intf->seq_table[i].inuse
1059
		    && (intf->seq_table[i].recv_msg->user == user)) {
1060
			intf->seq_table[i].inuse = 0;
1061
			ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
Linus Torvalds's avatar
Linus Torvalds committed
1062
1063
		}
	}
1064
1065
1066
1067
1068
1069
1070
1071
	spin_unlock_irqrestore(&intf->seq_lock, flags);

	/*
	 * Remove the user from the command receiver's table.  First
	 * we build a list of everything (not using the standard link,
	 * since other things may be using it till we do
	 * synchronize_rcu()) then free everything in that list.
	 */
1072
	mutex_lock(&intf->cmd_rcvrs_mutex);
1073
	list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
Linus Torvalds's avatar
Linus Torvalds committed
1074
		if (rcvr->user == user) {
1075
1076
1077
			list_del_rcu(&rcvr->link);
			rcvr->next = rcvrs;
			rcvrs = rcvr;
Linus Torvalds's avatar
Linus Torvalds committed
1078
1079
		}
	}
1080
	mutex_unlock(&intf->cmd_rcvrs_mutex);
1081
1082
1083
1084
1085
1086
	synchronize_rcu();
	while (rcvrs) {
		rcvr = rcvrs;
		rcvrs = rcvr->next;
		kfree(rcvr);
	}
Linus Torvalds's avatar
Linus Torvalds committed
1087

1088
1089
1090
1091
1092
1093
1094
	mutex_lock(&ipmi_interfaces_mutex);
	if (intf->handlers) {
		module_put(intf->handlers->owner);
		if (intf->handlers->dec_usecount)
			intf->handlers->dec_usecount(intf->send_info);
	}
	mutex_unlock(&ipmi_interfaces_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
1095

1096
	kref_put(&intf->refcount, intf_free);
Linus Torvalds's avatar
Linus Torvalds committed
1097

1098
	kref_put(&user->refcount, free_user);
Linus Torvalds's avatar
Linus Torvalds committed
1099

1100
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1101
}
1102
EXPORT_SYMBOL(ipmi_destroy_user);
Linus Torvalds's avatar
Linus Torvalds committed
1103
1104
1105
1106
1107

void ipmi_get_version(ipmi_user_t   user,
		      unsigned char *major,
		      unsigned char *minor)
{
1108
1109
	*major = user->intf->ipmi_version_major;
	*minor = user->intf->ipmi_version_minor;
Linus Torvalds's avatar
Linus Torvalds committed
1110
}
1111
EXPORT_SYMBOL(ipmi_get_version);
Linus Torvalds's avatar
Linus Torvalds committed
1112

1113
1114
1115
int ipmi_set_my_address(ipmi_user_t   user,
			unsigned int  channel,
			unsigned char address)
Linus Torvalds's avatar
Linus Torvalds committed
1116
{
1117
1118
1119
1120
	if (channel >= IPMI_MAX_CHANNELS)
		return -EINVAL;
	user->intf->channels[channel].address = address;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1121
}
1122
EXPORT_SYMBOL(ipmi_set_my_address);
Linus Torvalds's avatar
Linus Torvalds committed
1123

1124
1125
1126
int ipmi_get_my_address(ipmi_user_t   user,
			unsigned int  channel,
			unsigned char *address)
Linus Torvalds's avatar
Linus Torvalds committed
1127
{
1128
1129
1130
1131
	if (channel >= IPMI_MAX_CHANNELS)
		return -EINVAL;
	*address = user->intf->channels[channel].address;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1132
}
1133
EXPORT_SYMBOL(ipmi_get_my_address);
Linus Torvalds's avatar
Linus Torvalds committed
1134

1135
1136
1137
int ipmi_set_my_LUN(ipmi_user_t   user,
		    unsigned int  channel,
		    unsigned char LUN)
Linus Torvalds's avatar
Linus Torvalds committed
1138
{
1139
1140
1141
1142
	if (channel >= IPMI_MAX_CHANNELS)
		return -EINVAL;
	user->intf->channels[channel].lun = LUN & 0x3;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1143
}
1144
EXPORT_SYMBOL(ipmi_set_my_LUN);
Linus Torvalds's avatar
Linus Torvalds committed
1145

1146
1147
1148
int ipmi_get_my_LUN(ipmi_user_t   user,
		    unsigned int  channel,
		    unsigned char *address)
Linus Torvalds's avatar
Linus Torvalds committed
1149
{
1150
1151
1152
1153
	if (channel >= IPMI_MAX_CHANNELS)
		return -EINVAL;
	*address = user->intf->channels[channel].lun;
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1154
}
1155
EXPORT_SYMBOL(ipmi_get_my_LUN);
Linus Torvalds's avatar
Linus Torvalds committed
1156

1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
int ipmi_get_maintenance_mode(ipmi_user_t user)
{
	int           mode;
	unsigned long flags;

	spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags);
	mode = user->intf->maintenance_mode;
	spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags);

	return mode;
}
EXPORT_SYMBOL(ipmi_get_maintenance_mode);

static void maintenance_mode_update(ipmi_smi_t intf)
{
	if (intf->handlers->set_maintenance_mode)
		intf->handlers->set_maintenance_mode(
			intf->send_info, intf->maintenance_mode_enable);
}

int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
{
	int           rv = 0;
	unsigned long flags;
	ipmi_smi_t    intf = user->intf;

	spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
	if (intf->maintenance_mode != mode) {
		switch (mode) {
		case IPMI_MAINTENANCE_MODE_AUTO:
			intf->maintenance_mode_enable
				= (intf->auto_maintenance_timeout > 0);
			break;

		case IPMI_MAINTENANCE_MODE_OFF:
Corey Minyard's avatar
Corey Minyard committed
1192
			intf->maintenance_mode_enable = false;
1193
1194
1195
			break;

		case IPMI_MAINTENANCE_MODE_ON:
Corey Minyard's avatar
Corey Minyard committed
1196
			intf->maintenance_mode_enable = true;
1197
1198
1199
1200
1201
1202
			break;

		default:
			rv = -EINVAL;
			goto out_unlock;
		}
Corey Minyard's avatar
Corey Minyard committed
1203
		intf->maintenance_mode = mode;
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213

		maintenance_mode_update(intf);
	}
 out_unlock:
	spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);

	return rv;
}
EXPORT_SYMBOL(ipmi_set_maintenance_mode);

1214
int ipmi_set_gets_events(ipmi_user_t user, bool val)
Linus Torvalds's avatar
Linus Torvalds committed
1215
{
1216
1217
1218
1219
	unsigned long        flags;
	ipmi_smi_t           intf = user->intf;
	struct ipmi_recv_msg *msg, *msg2;
	struct list_head     msgs;
Linus Torvalds's avatar
Linus Torvalds committed
1220

1221
1222
1223
	INIT_LIST_HEAD(&msgs);

	spin_lock_irqsave(&intf->events_lock, flags);
1224
1225
1226
	if (user->gets_events == val)
		goto out;

Linus Torvalds's avatar
Linus Torvalds committed
1227
1228
	user->gets_events = val;

1229
1230
1231