2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 /* $FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.16 2011/11/07 15:43:11 ed Exp $ */
30 * Copyright 2012, Joyent, Inc. All rights reserved.
31 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
34 #include <sys/devops.h>
36 #include <sys/modctl.h>
37 #include <sys/types.h>
39 #include <sys/errno.h>
44 #include <sys/cmn_err.h>
46 #include <sys/sunddi.h>
47 #include <sys/smbios.h>
48 #include <sys/smbios_impl.h>
56 /* Allocate a new request with request and reply buffers. */
58 ipmi_alloc_request(struct ipmi_device
*dev
, long msgid
, uint8_t addr
,
59 uint8_t command
, size_t requestlen
, size_t replylen
)
61 struct ipmi_request
*req
;
63 req
= kmem_zalloc(sizeof (struct ipmi_request
) + requestlen
+ replylen
,
65 req
->ir_sz
= sizeof (struct ipmi_request
) + requestlen
+ replylen
;
67 req
->ir_msgid
= msgid
;
69 req
->ir_command
= command
;
71 req
->ir_request
= (uchar_t
*)&req
[1];
72 req
->ir_requestlen
= requestlen
;
75 req
->ir_reply
= (uchar_t
*)&req
[1] + requestlen
;
76 req
->ir_replybuflen
= replylen
;
79 cv_init(&req
->ir_cv
, NULL
, CV_DEFAULT
, NULL
);
80 req
->ir_status
= IRS_ALLOCATED
;
85 /* Free a request no longer in use. */
87 ipmi_free_request(struct ipmi_request
*req
)
92 cv_destroy(&req
->ir_cv
);
94 kmem_free(req
, req
->ir_sz
);
97 /* Store a processed request on the appropriate completion queue. */
100 ipmi_complete_request(struct ipmi_softc
*sc
, struct ipmi_request
*req
)
102 struct ipmi_device
*dev
;
104 IPMI_LOCK_ASSERT(sc
);
106 if (req
->ir_status
== IRS_CANCELED
) {
107 ASSERT(req
->ir_owner
== NULL
);
108 ipmi_free_request(req
);
112 req
->ir_status
= IRS_COMPLETED
;
115 * Anonymous requests (from inside the driver) always have a
116 * waiter that we awaken.
118 if (req
->ir_owner
== NULL
) {
119 cv_signal(&req
->ir_cv
);
122 TAILQ_INSERT_TAIL(&dev
->ipmi_completed_requests
, req
, ir_link
);
123 pollwakeup(dev
->ipmi_pollhead
, POLLIN
| POLLRDNORM
);
125 dev
->ipmi_status
&= ~IPMI_BUSY
;
126 if (dev
->ipmi_status
& IPMI_CLOSING
)
127 cv_signal(&dev
->ipmi_cv
);
132 * Enqueue an internal driver request and wait until it is completed.
135 ipmi_submit_driver_request(struct ipmi_softc
*sc
, struct ipmi_request
**preq
,
139 struct ipmi_request
*req
= *preq
;
141 ASSERT(req
->ir_owner
== NULL
);
144 error
= sc
->ipmi_enqueue_request(sc
, req
);
151 while (req
->ir_status
!= IRS_COMPLETED
&& error
>= 0)
153 cv_wait(&req
->ir_cv
, &sc
->ipmi_lock
);
155 error
= cv_timedwait(&req
->ir_cv
, &sc
->ipmi_lock
,
156 ddi_get_lbolt() + timo
);
158 switch (req
->ir_status
) {
160 TAILQ_REMOVE(&sc
->ipmi_pending_requests
, req
, ir_link
);
161 req
->ir_status
= IRS_CANCELED
;
165 req
->ir_status
= IRS_CANCELED
;
170 error
= req
->ir_error
;
173 panic("IPMI: Invalid request status");
182 * Helper routine for polled system interfaces that use
183 * ipmi_polled_enqueue_request() to queue requests. This request
184 * waits until there is a pending request and then returns the first
185 * request. If the driver is shutting down, it returns NULL.
187 struct ipmi_request
*
188 ipmi_dequeue_request(struct ipmi_softc
*sc
)
190 struct ipmi_request
*req
;
192 IPMI_LOCK_ASSERT(sc
);
194 while (!sc
->ipmi_detaching
&& TAILQ_EMPTY(&sc
->ipmi_pending_requests
))
195 cv_wait(&sc
->ipmi_request_added
, &sc
->ipmi_lock
);
196 if (sc
->ipmi_detaching
)
199 req
= TAILQ_FIRST(&sc
->ipmi_pending_requests
);
200 TAILQ_REMOVE(&sc
->ipmi_pending_requests
, req
, ir_link
);
201 req
->ir_status
= IRS_PROCESSED
;
203 if (req
->ir_owner
!= NULL
)
204 req
->ir_owner
->ipmi_status
|= IPMI_BUSY
;
210 ipmi_polled_enqueue_request(struct ipmi_softc
*sc
, struct ipmi_request
*req
)
213 IPMI_LOCK_ASSERT(sc
);
215 TAILQ_INSERT_TAIL(&sc
->ipmi_pending_requests
, req
, ir_link
);
216 req
->ir_status
= IRS_QUEUED
;
217 cv_signal(&sc
->ipmi_request_added
);
222 ipmi_shutdown(struct ipmi_softc
*sc
)
224 taskq_destroy(sc
->ipmi_kthread
);
226 cv_destroy(&sc
->ipmi_request_added
);
227 mutex_destroy(&sc
->ipmi_lock
);
231 ipmi_startup(struct ipmi_softc
*sc
)
233 struct ipmi_request
*req
;
236 /* Initialize interface-independent state. */
237 mutex_init(&sc
->ipmi_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
238 cv_init(&sc
->ipmi_request_added
, NULL
, CV_DEFAULT
, NULL
);
239 TAILQ_INIT(&sc
->ipmi_pending_requests
);
241 /* Initialize interface-dependent state. */
242 error
= sc
->ipmi_startup(sc
);
244 cmn_err(CE_WARN
, "Failed to initialize interface: %d", error
);
248 /* Send a GET_DEVICE_ID request. */
249 req
= ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST
, 0),
250 IPMI_GET_DEVICE_ID
, 0, 15);
252 error
= ipmi_submit_driver_request(sc
, &req
, MAX_TIMEOUT
);
253 if (error
== EWOULDBLOCK
) {
254 cmn_err(CE_WARN
, "Timed out waiting for GET_DEVICE_ID");
255 ipmi_free_request(req
);
258 cmn_err(CE_WARN
, "Failed GET_DEVICE_ID: %d", error
);
259 ipmi_free_request(req
);
261 } else if (req
->ir_compcode
!= 0) {
263 "Bad completion code for GET_DEVICE_ID: %d",
265 ipmi_free_request(req
);
267 } else if (req
->ir_replylen
< 5) {
268 cmn_err(CE_WARN
, "Short reply for GET_DEVICE_ID: %d",
270 ipmi_free_request(req
);
274 cmn_err(CE_CONT
, "!device rev. %d, firmware rev. %d.%d%d, "
276 req
->ir_reply
[1] & 0x0f, req
->ir_reply
[2] & 0x7f,
277 req
->ir_reply
[3] >> 4, req
->ir_reply
[3] & 0x0f,
278 req
->ir_reply
[4] & 0x0f, req
->ir_reply
[4] >> 4);
280 ipmi_free_request(req
);
282 req
= ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST
, 0),
283 IPMI_CLEAR_FLAGS
, 1, 0);
285 if ((error
= ipmi_submit_driver_request(sc
, &req
, 0)) != 0) {
286 cmn_err(CE_WARN
, "Failed to clear IPMI flags: %d\n", error
);
287 ipmi_free_request(req
);
292 if (req
->ir_compcode
== 0xc0) {
293 cmn_err(CE_NOTE
, "!Clear flags is busy");
295 if (req
->ir_compcode
== 0xc1) {
296 cmn_err(CE_NOTE
, "!Clear flags illegal");
298 ipmi_free_request(req
);
300 for (i
= 0; i
< 8; i
++) {
301 req
= ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST
, 0),
302 IPMI_GET_CHANNEL_INFO
, 1, 0);
303 req
->ir_request
[0] = (uchar_t
)i
;
305 if (ipmi_submit_driver_request(sc
, &req
, 0) != 0) {
306 ipmi_free_request(req
);
310 if (req
->ir_compcode
!= 0) {
311 ipmi_free_request(req
);
314 ipmi_free_request(req
);
316 cmn_err(CE_CONT
, "!number of channels %d", i
);
318 /* probe for watchdog */
319 req
= ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST
, 0),
320 IPMI_GET_WDOG
, 0, 0);
322 if ((error
= ipmi_submit_driver_request(sc
, &req
, 0)) != 0) {
323 cmn_err(CE_WARN
, "Failed to check IPMI watchdog: %d\n", error
);
324 ipmi_free_request(req
);
328 if (req
->ir_compcode
== 0x00) {
329 cmn_err(CE_CONT
, "!watchdog supported");
332 * Here is where we could register a watchdog event handler.
333 * See ipmi_wd_event() in the FreeBSD code.
336 ipmi_free_request(req
);