xref: /freebsd/sys/dev/ipmi/ipmi.c (revision bec0c98eaee483f59785559e03707d607d289d65)
137b1ce13SDoug Ambrisko /*-
237b1ce13SDoug Ambrisko  * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
337b1ce13SDoug Ambrisko  * All rights reserved.
437b1ce13SDoug Ambrisko  *
537b1ce13SDoug Ambrisko  * Redistribution and use in source and binary forms, with or without
637b1ce13SDoug Ambrisko  * modification, are permitted provided that the following conditions
737b1ce13SDoug Ambrisko  * are met:
837b1ce13SDoug Ambrisko  * 1. Redistributions of source code must retain the above copyright
937b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer.
1037b1ce13SDoug Ambrisko  * 2. Redistributions in binary form must reproduce the above copyright
1137b1ce13SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer in the
1237b1ce13SDoug Ambrisko  *    documentation and/or other materials provided with the distribution.
1337b1ce13SDoug Ambrisko  *
1437b1ce13SDoug Ambrisko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1537b1ce13SDoug Ambrisko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1637b1ce13SDoug Ambrisko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1737b1ce13SDoug Ambrisko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1837b1ce13SDoug Ambrisko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1937b1ce13SDoug Ambrisko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2037b1ce13SDoug Ambrisko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2137b1ce13SDoug Ambrisko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2237b1ce13SDoug Ambrisko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2337b1ce13SDoug Ambrisko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2437b1ce13SDoug Ambrisko  * SUCH DAMAGE.
2537b1ce13SDoug Ambrisko  */
2637b1ce13SDoug Ambrisko 
2737b1ce13SDoug Ambrisko #include <sys/cdefs.h>
2837b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$");
2937b1ce13SDoug Ambrisko 
3037b1ce13SDoug Ambrisko #include <sys/param.h>
3137b1ce13SDoug Ambrisko #include <sys/systm.h>
32d72a0786SJohn Baldwin #include <sys/bus.h>
33d72a0786SJohn Baldwin #include <sys/condvar.h>
34d72a0786SJohn Baldwin #include <sys/conf.h>
3537b1ce13SDoug Ambrisko #include <sys/kernel.h>
3637b1ce13SDoug Ambrisko #include <sys/malloc.h>
3737b1ce13SDoug Ambrisko #include <sys/module.h>
38d72a0786SJohn Baldwin #include <sys/poll.h>
3937b1ce13SDoug Ambrisko #include <sys/rman.h>
40d72a0786SJohn Baldwin #include <sys/selinfo.h>
4137b1ce13SDoug Ambrisko #include <sys/sysctl.h>
42d72a0786SJohn Baldwin #include <sys/watchdog.h>
4337b1ce13SDoug Ambrisko 
4437b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE
4537b1ce13SDoug Ambrisko #include <ipmi.h>
4637b1ce13SDoug Ambrisko #include <ipmivars.h>
4737b1ce13SDoug Ambrisko #else
4837b1ce13SDoug Ambrisko #include <sys/ipmi.h>
4937b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h>
5037b1ce13SDoug Ambrisko #endif
5137b1ce13SDoug Ambrisko 
5237b1ce13SDoug Ambrisko #ifdef IPMB
5337b1ce13SDoug Ambrisko static int ipmi_ipmb_checksum(u_char, int);
5437b1ce13SDoug Ambrisko static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char,
5537b1ce13SDoug Ambrisko      u_char, u_char, int)
5637b1ce13SDoug Ambrisko #endif
5737b1ce13SDoug Ambrisko 
5837b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl;
5937b1ce13SDoug Ambrisko static d_poll_t ipmi_poll;
6037b1ce13SDoug Ambrisko static d_open_t ipmi_open;
6137b1ce13SDoug Ambrisko static d_close_t ipmi_close;
6237b1ce13SDoug Ambrisko 
6337b1ce13SDoug Ambrisko int ipmi_attached = 0;
6437b1ce13SDoug Ambrisko 
6537b1ce13SDoug Ambrisko #define IPMI_MINOR	0
6637b1ce13SDoug Ambrisko 
6737b1ce13SDoug Ambrisko static int on = 1;
6837b1ce13SDoug Ambrisko SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters");
6937b1ce13SDoug Ambrisko SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW,
7037b1ce13SDoug Ambrisko 	&on, 0, "");
7137b1ce13SDoug Ambrisko 
7237b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = {
7337b1ce13SDoug Ambrisko 	.d_version =    D_VERSION,
7437b1ce13SDoug Ambrisko 	.d_open =	ipmi_open,
7537b1ce13SDoug Ambrisko 	.d_close =	ipmi_close,
7637b1ce13SDoug Ambrisko 	.d_ioctl =	ipmi_ioctl,
7737b1ce13SDoug Ambrisko 	.d_poll =	ipmi_poll,
7837b1ce13SDoug Ambrisko 	.d_name =	"ipmi",
7937b1ce13SDoug Ambrisko };
8037b1ce13SDoug Ambrisko 
8137b1ce13SDoug Ambrisko MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi");
8237b1ce13SDoug Ambrisko 
8337b1ce13SDoug Ambrisko static int
84d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td)
8537b1ce13SDoug Ambrisko {
86d72a0786SJohn Baldwin 	struct ipmi_device *dev;
8737b1ce13SDoug Ambrisko 	struct ipmi_softc *sc;
8837b1ce13SDoug Ambrisko 
8937b1ce13SDoug Ambrisko 	if (!on)
90d72a0786SJohn Baldwin 		return (ENOENT);
9137b1ce13SDoug Ambrisko 
92d72a0786SJohn Baldwin 	dev = cdev->si_drv1;
93d72a0786SJohn Baldwin 	sc = dev->ipmi_softc;
94d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
95d72a0786SJohn Baldwin 	if (dev->ipmi_open) {
96d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
97d72a0786SJohn Baldwin 		return (EBUSY);
9837b1ce13SDoug Ambrisko 	}
99d72a0786SJohn Baldwin 	dev->ipmi_open = 1;
100d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
10137b1ce13SDoug Ambrisko 
102d72a0786SJohn Baldwin 	return (0);
10337b1ce13SDoug Ambrisko }
10437b1ce13SDoug Ambrisko 
10537b1ce13SDoug Ambrisko static int
106d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td)
10737b1ce13SDoug Ambrisko {
108d72a0786SJohn Baldwin 	struct ipmi_device *dev;
10937b1ce13SDoug Ambrisko 	struct ipmi_softc *sc;
11037b1ce13SDoug Ambrisko 	int revents = 0;
11137b1ce13SDoug Ambrisko 
112d72a0786SJohn Baldwin 	dev = cdev->si_drv1;
113d72a0786SJohn Baldwin 	sc = dev->ipmi_softc;
11437b1ce13SDoug Ambrisko 
115d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
11637b1ce13SDoug Ambrisko 	if (poll_events & (POLLIN | POLLRDNORM)) {
117d72a0786SJohn Baldwin 		if (!TAILQ_EMPTY(&dev->ipmi_completed_requests))
11837b1ce13SDoug Ambrisko 		    revents |= poll_events & (POLLIN | POLLRDNORM);
119d72a0786SJohn Baldwin 		if (dev->ipmi_requests == 0)
12037b1ce13SDoug Ambrisko 		    revents |= POLLERR;
12137b1ce13SDoug Ambrisko 	}
12237b1ce13SDoug Ambrisko 
12337b1ce13SDoug Ambrisko 	if (revents == 0) {
12437b1ce13SDoug Ambrisko 		if (poll_events & (POLLIN | POLLRDNORM))
125d72a0786SJohn Baldwin 			selrecord(td, &dev->ipmi_select);
126d72a0786SJohn Baldwin 	}
127d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
128d72a0786SJohn Baldwin 
129d72a0786SJohn Baldwin 	return (revents);
13037b1ce13SDoug Ambrisko }
13137b1ce13SDoug Ambrisko 
132d72a0786SJohn Baldwin static void
133d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev)
134d72a0786SJohn Baldwin {
135d72a0786SJohn Baldwin 	struct ipmi_request *req;
136d72a0786SJohn Baldwin 
137d72a0786SJohn Baldwin 	while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) {
138d72a0786SJohn Baldwin 		req = TAILQ_FIRST(&dev->ipmi_completed_requests);
139d72a0786SJohn Baldwin 		TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link);
140d72a0786SJohn Baldwin 		dev->ipmi_requests--;
141d72a0786SJohn Baldwin 		ipmi_free_request(req);
142d72a0786SJohn Baldwin 	}
14337b1ce13SDoug Ambrisko }
14437b1ce13SDoug Ambrisko 
14537b1ce13SDoug Ambrisko static int
146d72a0786SJohn Baldwin ipmi_close(struct cdev *cdev, int flags, int fmt, struct thread *td)
14737b1ce13SDoug Ambrisko {
148d72a0786SJohn Baldwin 	struct ipmi_request *req, *nreq;
149d72a0786SJohn Baldwin 	struct ipmi_device *dev;
15037b1ce13SDoug Ambrisko 	struct ipmi_softc *sc;
151d72a0786SJohn Baldwin #ifdef CLONING
152d72a0786SJohn Baldwin 	int bit;
153d72a0786SJohn Baldwin #endif
15437b1ce13SDoug Ambrisko 
155d72a0786SJohn Baldwin 	dev = cdev->si_drv1;
156d72a0786SJohn Baldwin 	sc = dev->ipmi_softc;
15737b1ce13SDoug Ambrisko 
158d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
159d72a0786SJohn Baldwin 	if (dev->ipmi_requests) {
160d72a0786SJohn Baldwin 		/* Throw away any pending requests for this device. */
161d72a0786SJohn Baldwin 		TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link,
162d72a0786SJohn Baldwin 		    nreq) {
163d72a0786SJohn Baldwin 			if (req->ir_owner == dev) {
164d72a0786SJohn Baldwin 				TAILQ_REMOVE(&sc->ipmi_pending_requests, req,
165d72a0786SJohn Baldwin 				    ir_link);
166d72a0786SJohn Baldwin 				dev->ipmi_requests--;
167d72a0786SJohn Baldwin 				ipmi_free_request(req);
168d72a0786SJohn Baldwin 			}
169d72a0786SJohn Baldwin 		}
17037b1ce13SDoug Ambrisko 
171d72a0786SJohn Baldwin 		/* Throw away any pending completed requests for this device. */
172d72a0786SJohn Baldwin 		ipmi_purge_completed_requests(dev);
173d72a0786SJohn Baldwin 
174d72a0786SJohn Baldwin 		/*
175d72a0786SJohn Baldwin 		 * If we still have outstanding requests, they must be stuck
176d72a0786SJohn Baldwin 		 * in an interface driver, so wait for those to drain.
177d72a0786SJohn Baldwin 		 */
178d72a0786SJohn Baldwin 		dev->ipmi_closing = 1;
179d72a0786SJohn Baldwin 		while (dev->ipmi_requests > 0) {
180d72a0786SJohn Baldwin 			msleep(&dev->ipmi_requests, &sc->ipmi_lock, PWAIT,
181d72a0786SJohn Baldwin 			    "ipmidrain", 0);
182d72a0786SJohn Baldwin 			ipmi_purge_completed_requests(dev);
183d72a0786SJohn Baldwin 		}
184d72a0786SJohn Baldwin 	}
185d72a0786SJohn Baldwin 
186d72a0786SJohn Baldwin #ifdef CLONING
187d72a0786SJohn Baldwin 	/* Detach this sub-device from the main driver. */
188d72a0786SJohn Baldwin 	bit = minor(cdev) % 32;
189d72a0786SJohn Baldwin 	sc->ipmi_cdev_mask &= ~(1 << bit);
190d72a0786SJohn Baldwin 	TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link);
191d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
192d72a0786SJohn Baldwin 
193d72a0786SJohn Baldwin 	/* Cleanup. */
194d72a0786SJohn Baldwin 	cdev->si_drv1 = NULL;
195d72a0786SJohn Baldwin 	free(dev, M_IPMI);
196d72a0786SJohn Baldwin 	destroy_dev(cdev);
197d72a0786SJohn Baldwin #else
198d72a0786SJohn Baldwin 	dev->ipmi_open = 0;
199d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
200d72a0786SJohn Baldwin #endif
201d72a0786SJohn Baldwin 
202d72a0786SJohn Baldwin 	return (0);
20337b1ce13SDoug Ambrisko }
20437b1ce13SDoug Ambrisko 
20537b1ce13SDoug Ambrisko #ifdef IPMB
20637b1ce13SDoug Ambrisko static int
20737b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len)
20837b1ce13SDoug Ambrisko {
20937b1ce13SDoug Ambrisko 	u_char sum = 0;
21037b1ce13SDoug Ambrisko 
21137b1ce13SDoug Ambrisko 	for (; len; len--) {
21237b1ce13SDoug Ambrisko 		sum += *data++;
21337b1ce13SDoug Ambrisko 	}
214d72a0786SJohn Baldwin 	return (-sum);
21537b1ce13SDoug Ambrisko }
21637b1ce13SDoug Ambrisko 
217d72a0786SJohn Baldwin /* XXX: Needs work */
21837b1ce13SDoug Ambrisko static int
21937b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn,
22037b1ce13SDoug Ambrisko     u_char command, u_char seq, u_char *data, int data_len)
22137b1ce13SDoug Ambrisko {
22237b1ce13SDoug Ambrisko 	struct ipmi_softc *sc = device_get_softc(dev);
223d72a0786SJohn Baldwin 	struct ipmi_request *req;
22437b1ce13SDoug Ambrisko 	u_char slave_addr = 0x52;
225d72a0786SJohn Baldwin 	int error;
22637b1ce13SDoug Ambrisko 
227d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
228d72a0786SJohn Baldwin 	    IPMI_SEND_MSG, data_len + 8, 0);
229d72a0786SJohn Baldwin 	req->ir_request[0] = channel;
230d72a0786SJohn Baldwin 	req->ir_request[1] = slave_addr;
231d72a0786SJohn Baldwin 	req->ir_request[2] = IPMI_ADDR(netfn, 0);
232d72a0786SJohn Baldwin 	req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2);
233d72a0786SJohn Baldwin 	req->ir_request[4] = sc->ipmi_address;
234d72a0786SJohn Baldwin 	req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun);
235d72a0786SJohn Baldwin 	req->ir_request[6] = command;
23637b1ce13SDoug Ambrisko 
237d72a0786SJohn Baldwin 	bcopy(data, &req->ir_request[7], data_len);
238d72a0786SJohn Baldwin 	temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4],
239d72a0786SJohn Baldwin 	    data_len + 3);
24037b1ce13SDoug Ambrisko 
241d72a0786SJohn Baldwin 	ipmi_submit_driver_request(sc, req);
242d72a0786SJohn Baldwin 	error = req->ir_error;
243d72a0786SJohn Baldwin 	ipmi_free_request(req);
24437b1ce13SDoug Ambrisko 
245d72a0786SJohn Baldwin 	return (error);
24637b1ce13SDoug Ambrisko }
24737b1ce13SDoug Ambrisko 
24837b1ce13SDoug Ambrisko static int
249d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc)
25037b1ce13SDoug Ambrisko {
251d72a0786SJohn Baldwin 	struct ipmi_request *req;
25237b1ce13SDoug Ambrisko 	int error;
25337b1ce13SDoug Ambrisko 
25437b1ce13SDoug Ambrisko 	device_printf(sc->ipmi_dev, "BMC has a message\n");
255d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
256d72a0786SJohn Baldwin 	    IPMI_GET_MSG_FLAGS, 0, 1);
25737b1ce13SDoug Ambrisko 
258d72a0786SJohn Baldwin 	ipmi_submit_driver_request(sc, req);
259d72a0786SJohn Baldwin 
260d72a0786SJohn Baldwin 	if (req->ir_error == 0 && req->ir_compcode == 0) {
261d72a0786SJohn Baldwin 		if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) {
26237b1ce13SDoug Ambrisko 			device_printf(sc->ipmi_dev, "message buffer full");
26337b1ce13SDoug Ambrisko 		}
264d72a0786SJohn Baldwin 		if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) {
26537b1ce13SDoug Ambrisko 			device_printf(sc->ipmi_dev,
26637b1ce13SDoug Ambrisko 			    "watchdog about to go off");
26737b1ce13SDoug Ambrisko 		}
268d72a0786SJohn Baldwin 		if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) {
269d72a0786SJohn Baldwin 			ipmi_free_request(req);
270d72a0786SJohn Baldwin 
271d72a0786SJohn Baldwin 			req = ipmi_alloc_driver_request(
272d72a0786SJohn Baldwin 			    IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0,
273d72a0786SJohn Baldwin 			    16);
27437b1ce13SDoug Ambrisko 
27537b1ce13SDoug Ambrisko 			device_printf(sc->ipmi_dev, "throw out message ");
27637b1ce13SDoug Ambrisko 			dump_buf(temp, 16);
27737b1ce13SDoug Ambrisko 		}
27837b1ce13SDoug Ambrisko 	}
279d72a0786SJohn Baldwin 	error = req->ir_error;
280d72a0786SJohn Baldwin 	ipmi_free_request(req);
281d72a0786SJohn Baldwin 
282d72a0786SJohn Baldwin 	return (error);
283d72a0786SJohn Baldwin }
284d72a0786SJohn Baldwin #endif
285d72a0786SJohn Baldwin 
286d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
287d72a0786SJohn Baldwin #define	PTRIN(p)	((void *)(uintptr_t)(p))
288d72a0786SJohn Baldwin #define	PTROUT(p)	((uintptr_t)(p))
28937b1ce13SDoug Ambrisko #endif
29037b1ce13SDoug Ambrisko 
29137b1ce13SDoug Ambrisko static int
292d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data,
29337b1ce13SDoug Ambrisko     int flags, struct thread *td)
29437b1ce13SDoug Ambrisko {
29537b1ce13SDoug Ambrisko 	struct ipmi_softc *sc;
296d72a0786SJohn Baldwin 	struct ipmi_device *dev;
297d72a0786SJohn Baldwin 	struct ipmi_request *kreq;
29837b1ce13SDoug Ambrisko 	struct ipmi_req *req = (struct ipmi_req *)data;
29937b1ce13SDoug Ambrisko 	struct ipmi_recv *recv = (struct ipmi_recv *)data;
30037b1ce13SDoug Ambrisko 	struct ipmi_addr addr;
301d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
302d72a0786SJohn Baldwin 	struct ipmi_req32 *req32 = (struct ipmi_req32 *)data;
303d72a0786SJohn Baldwin 	struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data;
304d72a0786SJohn Baldwin 	union {
305d72a0786SJohn Baldwin 		struct ipmi_req req;
306d72a0786SJohn Baldwin 		struct ipmi_recv recv;
307d72a0786SJohn Baldwin 	} thunk32;
308d72a0786SJohn Baldwin #endif
30937b1ce13SDoug Ambrisko 	int error, len;
31037b1ce13SDoug Ambrisko 
311d72a0786SJohn Baldwin 	dev = cdev->si_drv1;
312d72a0786SJohn Baldwin 	sc = dev->ipmi_softc;
313d72a0786SJohn Baldwin 
314d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
315d72a0786SJohn Baldwin 	/* Convert 32-bit structures to native. */
316d72a0786SJohn Baldwin 	switch (cmd) {
317d72a0786SJohn Baldwin 	case IPMICTL_SEND_COMMAND_32:
318d72a0786SJohn Baldwin 		req = &thunk32.req;
319d72a0786SJohn Baldwin 		req->addr = PTRIN(req32->addr);
320d72a0786SJohn Baldwin 		req->addr_len = req32->addr_len;
321d72a0786SJohn Baldwin 		req->msgid = req32->msgid;
322d72a0786SJohn Baldwin 		req->msg.netfn = req32->msg.netfn;
323d72a0786SJohn Baldwin 		req->msg.cmd = req32->msg.cmd;
324d72a0786SJohn Baldwin 		req->msg.data_len = req32->msg.data_len;
325d72a0786SJohn Baldwin 		req->msg.data = PTRIN(req32->msg.data);
326d72a0786SJohn Baldwin 		break;
327d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_TRUNC_32:
328d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_32:
329d72a0786SJohn Baldwin 		recv = &thunk32.recv;
330d72a0786SJohn Baldwin 		recv->addr = PTRIN(recv32->addr);
331d72a0786SJohn Baldwin 		recv->addr_len = recv32->addr_len;
332d72a0786SJohn Baldwin 		recv->msg.data_len = recv32->msg.data_len;
333d72a0786SJohn Baldwin 		recv->msg.data = PTRIN(recv32->msg.data);
334d72a0786SJohn Baldwin 		break;
335d72a0786SJohn Baldwin 	}
336d72a0786SJohn Baldwin #endif
33737b1ce13SDoug Ambrisko 
33837b1ce13SDoug Ambrisko 	switch (cmd) {
339d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
340d72a0786SJohn Baldwin 	case IPMICTL_SEND_COMMAND_32:
341d72a0786SJohn Baldwin #endif
34237b1ce13SDoug Ambrisko 	case IPMICTL_SEND_COMMAND:
343d72a0786SJohn Baldwin 		/*
344d72a0786SJohn Baldwin 		 * XXX: Need to add proper handling of this.
345d72a0786SJohn Baldwin 		 */
34637b1ce13SDoug Ambrisko 		error = copyin(req->addr, &addr, sizeof(addr));
347d72a0786SJohn Baldwin 		if (error)
348d72a0786SJohn Baldwin 			return (error);
349d72a0786SJohn Baldwin 
350d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
351d72a0786SJohn Baldwin 		/* clear out old stuff in queue of stuff done */
352d72a0786SJohn Baldwin 		/* XXX: This seems odd. */
353d72a0786SJohn Baldwin 		while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) {
354d72a0786SJohn Baldwin 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
355d72a0786SJohn Baldwin 			    ir_link);
356d72a0786SJohn Baldwin 			dev->ipmi_requests--;
357d72a0786SJohn Baldwin 			ipmi_free_request(kreq);
35837b1ce13SDoug Ambrisko 		}
359d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
360d72a0786SJohn Baldwin 
361d72a0786SJohn Baldwin 		kreq = ipmi_alloc_request(dev, req->msgid,
362d72a0786SJohn Baldwin 		    IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd,
363d72a0786SJohn Baldwin 		    req->msg.data_len, IPMI_MAX_RX);
364d72a0786SJohn Baldwin 		error = copyin(req->msg.data, kreq->ir_request,
36537b1ce13SDoug Ambrisko 		    req->msg.data_len);
366d72a0786SJohn Baldwin 		if (error) {
367d72a0786SJohn Baldwin 			ipmi_free_request(kreq);
368d72a0786SJohn Baldwin 			return (error);
36937b1ce13SDoug Ambrisko 		}
370d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
371d72a0786SJohn Baldwin 		dev->ipmi_requests++;
372d72a0786SJohn Baldwin 		error = sc->ipmi_enqueue_request(sc, kreq);
373d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
374d72a0786SJohn Baldwin 		if (error)
375d72a0786SJohn Baldwin 			return (error);
376d72a0786SJohn Baldwin 		break;
377d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
378d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_TRUNC_32:
379d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_32:
380d72a0786SJohn Baldwin #endif
38137b1ce13SDoug Ambrisko 	case IPMICTL_RECEIVE_MSG_TRUNC:
38237b1ce13SDoug Ambrisko 	case IPMICTL_RECEIVE_MSG:
38337b1ce13SDoug Ambrisko 		error = copyin(recv->addr, &addr, sizeof(addr));
384d72a0786SJohn Baldwin 		if (error)
385d72a0786SJohn Baldwin 			return (error);
386d72a0786SJohn Baldwin 
387d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
388d72a0786SJohn Baldwin 		kreq = TAILQ_FIRST(&dev->ipmi_completed_requests);
389d72a0786SJohn Baldwin 		if (kreq == NULL) {
390d72a0786SJohn Baldwin 			IPMI_UNLOCK(sc);
391d72a0786SJohn Baldwin 			return (EAGAIN);
39237b1ce13SDoug Ambrisko 		}
393d72a0786SJohn Baldwin 		addr.channel = IPMI_BMC_CHANNEL;
394d72a0786SJohn Baldwin 		/* XXX */
395d72a0786SJohn Baldwin 		recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
396d72a0786SJohn Baldwin 		recv->msgid = kreq->ir_msgid;
397d72a0786SJohn Baldwin 		recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2;
398d72a0786SJohn Baldwin 		recv->msg.cmd = kreq->ir_command;
399d72a0786SJohn Baldwin 		error = kreq->ir_error;
400d72a0786SJohn Baldwin 		if (error) {
401d72a0786SJohn Baldwin 			TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq,
402d72a0786SJohn Baldwin 			    ir_link);
403d72a0786SJohn Baldwin 			dev->ipmi_requests--;
404d72a0786SJohn Baldwin 			IPMI_UNLOCK(sc);
405d72a0786SJohn Baldwin 			ipmi_free_request(kreq);
406d72a0786SJohn Baldwin 			return (error);
407d72a0786SJohn Baldwin 		}
408d72a0786SJohn Baldwin 		len = kreq->ir_replylen + 1;
409d72a0786SJohn Baldwin 		if (recv->msg.data_len < len &&
410d72a0786SJohn Baldwin 		    (cmd == IPMICTL_RECEIVE_MSG
411d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32
412d72a0786SJohn Baldwin 		     || cmd == IPMICTL_RECEIVE_MSG
413d72a0786SJohn Baldwin #endif
414d72a0786SJohn Baldwin 		    )) {
415d72a0786SJohn Baldwin 			IPMI_UNLOCK(sc);
416d72a0786SJohn Baldwin 			return (EMSGSIZE);
417d72a0786SJohn Baldwin 		}
418d72a0786SJohn Baldwin 		TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link);
419d72a0786SJohn Baldwin 		dev->ipmi_requests--;
420d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
42137b1ce13SDoug Ambrisko 		len = min(recv->msg.data_len, len);
42237b1ce13SDoug Ambrisko 		recv->msg.data_len = len;
42337b1ce13SDoug Ambrisko 		error = copyout(&addr, recv->addr,sizeof(addr));
42437b1ce13SDoug Ambrisko 		if (error == 0)
425d72a0786SJohn Baldwin 			error = copyout(&kreq->ir_compcode, recv->msg.data, 1);
426d72a0786SJohn Baldwin 		if (error == 0)
427d72a0786SJohn Baldwin 			error = copyout(kreq->ir_reply, recv->msg.data + 1,
428d72a0786SJohn Baldwin 			    len - 1);
429d72a0786SJohn Baldwin 		ipmi_free_request(kreq);
430d72a0786SJohn Baldwin 		if (error)
431d72a0786SJohn Baldwin 			return (error);
432d72a0786SJohn Baldwin 		break;
43337b1ce13SDoug Ambrisko 	case IPMICTL_SET_MY_ADDRESS_CMD:
434d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
435d72a0786SJohn Baldwin 		dev->ipmi_address = *(int*)data;
436d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
437d72a0786SJohn Baldwin 		break;
43837b1ce13SDoug Ambrisko 	case IPMICTL_GET_MY_ADDRESS_CMD:
439d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
440d72a0786SJohn Baldwin 		*(int*)data = dev->ipmi_address;
441d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
442d72a0786SJohn Baldwin 		break;
44337b1ce13SDoug Ambrisko 	case IPMICTL_SET_MY_LUN_CMD:
444d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
445d72a0786SJohn Baldwin 		dev->ipmi_lun = *(int*)data & 0x3;
446d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
447d72a0786SJohn Baldwin 		break;
44837b1ce13SDoug Ambrisko 	case IPMICTL_GET_MY_LUN_CMD:
449d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
450d72a0786SJohn Baldwin 		*(int*)data = dev->ipmi_lun;
451d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
452d72a0786SJohn Baldwin 		break;
45337b1ce13SDoug Ambrisko 	case IPMICTL_SET_GETS_EVENTS_CMD:
45437b1ce13SDoug Ambrisko 		/*
45537b1ce13SDoug Ambrisko 		device_printf(sc->ipmi_dev,
45637b1ce13SDoug Ambrisko 		    "IPMICTL_SET_GETS_EVENTS_CMD NA\n");
45737b1ce13SDoug Ambrisko 		*/
458d72a0786SJohn Baldwin 		break;
45937b1ce13SDoug Ambrisko 	case IPMICTL_REGISTER_FOR_CMD:
46037b1ce13SDoug Ambrisko 	case IPMICTL_UNREGISTER_FOR_CMD:
461d72a0786SJohn Baldwin 		return (EOPNOTSUPP);
462d72a0786SJohn Baldwin 	default:
46337b1ce13SDoug Ambrisko 		device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd);
464d72a0786SJohn Baldwin 		return (ENOIOCTL);
46537b1ce13SDoug Ambrisko 	}
46637b1ce13SDoug Ambrisko 
467d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32
468d72a0786SJohn Baldwin 	/* Update changed fields in 32-bit structures. */
469d72a0786SJohn Baldwin 	switch (cmd) {
470d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_TRUNC_32:
471d72a0786SJohn Baldwin 	case IPMICTL_RECEIVE_MSG_32:
472d72a0786SJohn Baldwin 		recv32->recv_type = recv->recv_type;
473d72a0786SJohn Baldwin 		recv32->msgid = recv->msgid;
474d72a0786SJohn Baldwin 		recv32->msg.netfn = recv->msg.netfn;
475d72a0786SJohn Baldwin 		recv32->msg.cmd = recv->msg.cmd;
476d72a0786SJohn Baldwin 		recv32->msg.data_len = recv->msg.data_len;
47737b1ce13SDoug Ambrisko 		break;
47837b1ce13SDoug Ambrisko 	}
479d72a0786SJohn Baldwin #endif
480d72a0786SJohn Baldwin 	return (0);
48137b1ce13SDoug Ambrisko }
48237b1ce13SDoug Ambrisko 
483d72a0786SJohn Baldwin /*
484d72a0786SJohn Baldwin  * Request management.
485d72a0786SJohn Baldwin  */
48637b1ce13SDoug Ambrisko 
487d72a0786SJohn Baldwin /* Allocate a new request with request and reply buffers. */
488d72a0786SJohn Baldwin struct ipmi_request *
489d72a0786SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr,
490d72a0786SJohn Baldwin     uint8_t command, size_t requestlen, size_t replylen)
491d72a0786SJohn Baldwin {
492d72a0786SJohn Baldwin 	struct ipmi_request *req;
493d72a0786SJohn Baldwin 
494d72a0786SJohn Baldwin 	req = malloc(sizeof(struct ipmi_request) + requestlen + replylen,
495d72a0786SJohn Baldwin 	    M_IPMI, M_WAITOK | M_ZERO);
496d72a0786SJohn Baldwin 	req->ir_owner = dev;
497d72a0786SJohn Baldwin 	req->ir_msgid = msgid;
498d72a0786SJohn Baldwin 	req->ir_addr = addr;
499d72a0786SJohn Baldwin 	req->ir_command = command;
500d72a0786SJohn Baldwin 	if (requestlen) {
501d72a0786SJohn Baldwin 		req->ir_request = (char *)&req[1];
502d72a0786SJohn Baldwin 		req->ir_requestlen = requestlen;
503d72a0786SJohn Baldwin 	}
504d72a0786SJohn Baldwin 	if (replylen) {
505d72a0786SJohn Baldwin 		req->ir_reply = (char *)&req[1] + requestlen;
506d72a0786SJohn Baldwin 		req->ir_replybuflen = replylen;
507d72a0786SJohn Baldwin 	}
508d72a0786SJohn Baldwin 	return (req);
50937b1ce13SDoug Ambrisko }
51037b1ce13SDoug Ambrisko 
511d72a0786SJohn Baldwin /* Free a request no longer in use. */
512d72a0786SJohn Baldwin void
513d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req)
514d72a0786SJohn Baldwin {
51537b1ce13SDoug Ambrisko 
516d72a0786SJohn Baldwin 	free(req, M_IPMI);
51737b1ce13SDoug Ambrisko }
51837b1ce13SDoug Ambrisko 
519d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */
520d72a0786SJohn Baldwin void
521d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req)
522d72a0786SJohn Baldwin {
523d72a0786SJohn Baldwin 	struct ipmi_device *dev;
52437b1ce13SDoug Ambrisko 
525d72a0786SJohn Baldwin 	IPMI_LOCK_ASSERT(sc);
526d72a0786SJohn Baldwin 
527d72a0786SJohn Baldwin 	/*
528d72a0786SJohn Baldwin 	 * Anonymous requests (from inside the driver) always have a
529d72a0786SJohn Baldwin 	 * waiter that we awaken.
530d72a0786SJohn Baldwin 	 */
531d72a0786SJohn Baldwin 	if (req->ir_owner == NULL)
532d72a0786SJohn Baldwin 		wakeup(req);
533d72a0786SJohn Baldwin 	else {
534d72a0786SJohn Baldwin 		dev = req->ir_owner;
535d72a0786SJohn Baldwin 		TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link);
536d72a0786SJohn Baldwin 		selwakeup(&dev->ipmi_select);
537d72a0786SJohn Baldwin 		if (dev->ipmi_closing)
538d72a0786SJohn Baldwin 			wakeup(&dev->ipmi_requests);
539d72a0786SJohn Baldwin 	}
54037b1ce13SDoug Ambrisko }
54137b1ce13SDoug Ambrisko 
542d72a0786SJohn Baldwin /* Enqueue an internal driver request and wait until it is completed. */
54337b1ce13SDoug Ambrisko int
544d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req,
545d72a0786SJohn Baldwin     int timo)
546d72a0786SJohn Baldwin {
547d72a0786SJohn Baldwin 	int error;
54837b1ce13SDoug Ambrisko 
549d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
550d72a0786SJohn Baldwin 	error = sc->ipmi_enqueue_request(sc, req);
551d72a0786SJohn Baldwin 	if (error == 0)
552d72a0786SJohn Baldwin 		error = msleep(req, &sc->ipmi_lock, 0, "ipmireq", timo);
553d72a0786SJohn Baldwin 	if (error == 0)
554d72a0786SJohn Baldwin 		error = req->ir_error;
555d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
556d72a0786SJohn Baldwin 	return (error);
55737b1ce13SDoug Ambrisko }
55837b1ce13SDoug Ambrisko 
559d72a0786SJohn Baldwin /*
560d72a0786SJohn Baldwin  * Helper routine for polled system interfaces that use
561d72a0786SJohn Baldwin  * ipmi_polled_enqueue_request() to queue requests.  This request
562d72a0786SJohn Baldwin  * waits until there is a pending request and then returns the first
563d72a0786SJohn Baldwin  * request.  If the driver is shutting down, it returns NULL.
564d72a0786SJohn Baldwin  */
565d72a0786SJohn Baldwin struct ipmi_request *
566d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc)
567d72a0786SJohn Baldwin {
568d72a0786SJohn Baldwin 	struct ipmi_request *req;
56937b1ce13SDoug Ambrisko 
570d72a0786SJohn Baldwin 	IPMI_LOCK_ASSERT(sc);
57137b1ce13SDoug Ambrisko 
572d72a0786SJohn Baldwin 	while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests))
573d72a0786SJohn Baldwin 		cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock);
574d72a0786SJohn Baldwin 	if (sc->ipmi_detaching)
575d72a0786SJohn Baldwin 		return (NULL);
57637b1ce13SDoug Ambrisko 
577d72a0786SJohn Baldwin 	req = TAILQ_FIRST(&sc->ipmi_pending_requests);
578d72a0786SJohn Baldwin 	TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
579d72a0786SJohn Baldwin 	return (req);
58037b1ce13SDoug Ambrisko }
58137b1ce13SDoug Ambrisko 
582d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */
583d72a0786SJohn Baldwin int
584d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
585d72a0786SJohn Baldwin {
58637b1ce13SDoug Ambrisko 
587d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
588d72a0786SJohn Baldwin 	TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link);
589d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
590d72a0786SJohn Baldwin 	cv_signal(&sc->ipmi_request_added);
591d72a0786SJohn Baldwin 	return (0);
59237b1ce13SDoug Ambrisko }
59337b1ce13SDoug Ambrisko 
59437b1ce13SDoug Ambrisko /*
59537b1ce13SDoug Ambrisko  * Watchdog event handler.
59637b1ce13SDoug Ambrisko  */
59737b1ce13SDoug Ambrisko 
59837b1ce13SDoug Ambrisko static void
599d72a0786SJohn Baldwin ipmi_set_watchdog(struct ipmi_softc *sc, int sec)
600d72a0786SJohn Baldwin {
601d72a0786SJohn Baldwin 	struct ipmi_request *req;
602d72a0786SJohn Baldwin 	int error;
60337b1ce13SDoug Ambrisko 
604d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
605d72a0786SJohn Baldwin 	    IPMI_SET_WDOG, 6, 0);
60637b1ce13SDoug Ambrisko 
60737b1ce13SDoug Ambrisko 	if (sec) {
608d72a0786SJohn Baldwin 		req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP
60937b1ce13SDoug Ambrisko 		    | IPMI_SET_WD_TIMER_SMS_OS;
610d72a0786SJohn Baldwin 		req->ir_request[1] = IPMI_SET_WD_ACTION_RESET;
611d72a0786SJohn Baldwin 		req->ir_request[2] = 0;
612d72a0786SJohn Baldwin 		req->ir_request[3] = 0;	/* Timer use */
613d72a0786SJohn Baldwin 		req->ir_request[4] = (sec * 10) & 0xff;
614d72a0786SJohn Baldwin 		req->ir_request[5] = (sec * 10) / 2550;
61537b1ce13SDoug Ambrisko 	} else {
616d72a0786SJohn Baldwin 		req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS;
617d72a0786SJohn Baldwin 		req->ir_request[1] = 0;
618d72a0786SJohn Baldwin 		req->ir_request[2] = 0;
619d72a0786SJohn Baldwin 		req->ir_request[3] = 0;	/* Timer use */
620d72a0786SJohn Baldwin 		req->ir_request[4] = 0;
621d72a0786SJohn Baldwin 		req->ir_request[5] = 0;
62237b1ce13SDoug Ambrisko 	}
62337b1ce13SDoug Ambrisko 
624d72a0786SJohn Baldwin 	error = ipmi_submit_driver_request(sc, req, 0);
625d72a0786SJohn Baldwin 	if (error)
626d72a0786SJohn Baldwin 		device_printf(sc->ipmi_dev, "Failed to set watchdog\n");
62737b1ce13SDoug Ambrisko 
628d72a0786SJohn Baldwin 	if (error == 0 && sec) {
629d72a0786SJohn Baldwin 		ipmi_free_request(req);
63037b1ce13SDoug Ambrisko 
631d72a0786SJohn Baldwin 		req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
632d72a0786SJohn Baldwin 		    IPMI_RESET_WDOG, 0, 0);
63337b1ce13SDoug Ambrisko 
634d72a0786SJohn Baldwin 		error = ipmi_submit_driver_request(sc, req, 0);
635d72a0786SJohn Baldwin 		if (error)
636d72a0786SJohn Baldwin 			device_printf(sc->ipmi_dev,
637d72a0786SJohn Baldwin 			    "Failed to reset watchdog\n");
63837b1ce13SDoug Ambrisko 	}
63937b1ce13SDoug Ambrisko 
640d72a0786SJohn Baldwin 	ipmi_free_request(req);
64137b1ce13SDoug Ambrisko 	/*
642d72a0786SJohn Baldwin 	dump_watchdog(sc);
64337b1ce13SDoug Ambrisko 	*/
64437b1ce13SDoug Ambrisko }
64537b1ce13SDoug Ambrisko 
64637b1ce13SDoug Ambrisko static void
64737b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error)
64837b1ce13SDoug Ambrisko {
64937b1ce13SDoug Ambrisko 	struct ipmi_softc *sc = arg;
65037b1ce13SDoug Ambrisko 	unsigned int timeout;
65137b1ce13SDoug Ambrisko 
65237b1ce13SDoug Ambrisko 	/* disable / enable */
65337b1ce13SDoug Ambrisko 	if (!(cmd & WD_ACTIVE)) {
654d72a0786SJohn Baldwin 		ipmi_set_watchdog(sc, 0);
65537b1ce13SDoug Ambrisko 		*error = 0;
65637b1ce13SDoug Ambrisko 		return;
65737b1ce13SDoug Ambrisko 	}
65837b1ce13SDoug Ambrisko 
65937b1ce13SDoug Ambrisko 	cmd &= WD_INTERVAL;
66037b1ce13SDoug Ambrisko 	/* convert from power-of-to-ns to WDT ticks */
66137b1ce13SDoug Ambrisko 	if (cmd >= 64) {
66237b1ce13SDoug Ambrisko 		*error = EINVAL;
66337b1ce13SDoug Ambrisko 		return;
66437b1ce13SDoug Ambrisko 	}
66537b1ce13SDoug Ambrisko 	timeout = ((uint64_t)1 << cmd) / 1800000000;
66637b1ce13SDoug Ambrisko 
66737b1ce13SDoug Ambrisko 	/* reload */
668d72a0786SJohn Baldwin 	ipmi_set_watchdog(sc, timeout);
66937b1ce13SDoug Ambrisko 
67037b1ce13SDoug Ambrisko 	*error = 0;
67137b1ce13SDoug Ambrisko }
67237b1ce13SDoug Ambrisko 
673d72a0786SJohn Baldwin #ifdef CLONING
674d72a0786SJohn Baldwin static void
675d72a0786SJohn Baldwin ipmi_clone(void *arg, struct ucred *cred, char *name, int namelen,
676d72a0786SJohn Baldwin     struct cdev **cdev)
677d72a0786SJohn Baldwin {
678d72a0786SJohn Baldwin 	struct ipmi_softc *sc = arg;
679d72a0786SJohn Baldwin 	struct ipmi_device *dev;
680d72a0786SJohn Baldwin 	int minor, unit;
681d72a0786SJohn Baldwin 
682d72a0786SJohn Baldwin 	if (*cdev != NULL)
683d72a0786SJohn Baldwin 		return;
684d72a0786SJohn Baldwin 
685d72a0786SJohn Baldwin 	if (strcmp(name, device_get_nameunit(sc->ipmi_dev)) != 0)
686d72a0786SJohn Baldwin 		return;
687d72a0786SJohn Baldwin 
688d72a0786SJohn Baldwin 	dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO);
689d72a0786SJohn Baldwin 
690d72a0786SJohn Baldwin 	/* Reserve a sub-device. */
691d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
692d72a0786SJohn Baldwin 	minor = ffs(~(sc->ipmi_cdev_mask & 0xffff));
693d72a0786SJohn Baldwin 	if (minor == 0 || !sc->ipmi_cloning) {
694d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
695d72a0786SJohn Baldwin 		free(dev, M_IPMI);
696d72a0786SJohn Baldwin 		return;
697d72a0786SJohn Baldwin 	}
698d72a0786SJohn Baldwin 	minor--;
699d72a0786SJohn Baldwin 	sc->ipmi_cdev_mask |= (1 << minor);
700d72a0786SJohn Baldwin 	TAILQ_INSERT_TAIL(&sc->ipmi_cdevs, dev, ipmi_link);
701d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
702d72a0786SJohn Baldwin 
703d72a0786SJohn Baldwin 	/* Initialize the device. */
704d72a0786SJohn Baldwin 	TAILQ_INIT(&dev->ipmi_completed_requests);
705d72a0786SJohn Baldwin 	dev->ipmi_softc = sc;
706d72a0786SJohn Baldwin 	dev->ipmi_address = IPMI_BMC_SLAVE_ADDR;
707d72a0786SJohn Baldwin 	dev->ipmi_lun = IPMI_BMC_SMS_LUN;
708d72a0786SJohn Baldwin 	unit = device_get_unit(sc->ipmi_dev);
709d72a0786SJohn Baldwin 	dev->ipmi_cdev = make_dev_cred(&ipmi_cdevsw, unit * 32 + minor, cred,
710d72a0786SJohn Baldwin 	    UID_ROOT, GID_OPERATOR, 0660, "ipmi%d.%d", unit, minor);
711d72a0786SJohn Baldwin 	if (dev->ipmi_cdev == NULL) {
712d72a0786SJohn Baldwin 		IPMI_LOCK(sc);
713d72a0786SJohn Baldwin 		sc->ipmi_cdev_mask &= ~(1 << minor);
714d72a0786SJohn Baldwin 		TAILQ_REMOVE(&sc->ipmi_cdevs, dev, ipmi_link);
715d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
716d72a0786SJohn Baldwin 		free(dev, M_IPMI);
717d72a0786SJohn Baldwin 		return;
718d72a0786SJohn Baldwin 	}
719d72a0786SJohn Baldwin 	dev->ipmi_cdev->si_drv1 = dev;
720d72a0786SJohn Baldwin 	*cdev = dev->ipmi_cdev;
721d72a0786SJohn Baldwin 	dev_ref(*cdev);
722d72a0786SJohn Baldwin }
723d72a0786SJohn Baldwin #endif
724d72a0786SJohn Baldwin 
725d72a0786SJohn Baldwin static void
726d72a0786SJohn Baldwin ipmi_startup(void *arg)
727d72a0786SJohn Baldwin {
728d72a0786SJohn Baldwin 	struct ipmi_softc *sc = arg;
729d72a0786SJohn Baldwin 	struct ipmi_request *req;
730d72a0786SJohn Baldwin 	device_t dev;
731d72a0786SJohn Baldwin 	int error, i;
732d72a0786SJohn Baldwin 
733d72a0786SJohn Baldwin 	config_intrhook_disestablish(&sc->ipmi_ich);
734d72a0786SJohn Baldwin 	dev = sc->ipmi_dev;
735d72a0786SJohn Baldwin 
736d72a0786SJohn Baldwin 	/* Initialize interface-independent state. */
737d72a0786SJohn Baldwin 	mtx_init(&sc->ipmi_lock, device_get_nameunit(dev), "ipmi", MTX_DEF);
738d72a0786SJohn Baldwin 	cv_init(&sc->ipmi_request_added, "ipmireq");
739d72a0786SJohn Baldwin 	TAILQ_INIT(&sc->ipmi_pending_requests);
740d72a0786SJohn Baldwin #ifdef CLONING
741d72a0786SJohn Baldwin 	TAILQ_INIT(&sc->ipmi_cdevs);
742d72a0786SJohn Baldwin #endif
743d72a0786SJohn Baldwin 
744d72a0786SJohn Baldwin 	/* Initialize interface-dependent state. */
745d72a0786SJohn Baldwin 	error = sc->ipmi_startup(sc);
746d72a0786SJohn Baldwin 	if (error) {
747d72a0786SJohn Baldwin 		device_printf(dev, "Failed to initialize interface: %d\n",
748d72a0786SJohn Baldwin 		    error);
749d72a0786SJohn Baldwin 		return;
750d72a0786SJohn Baldwin 	}
751d72a0786SJohn Baldwin 
752d72a0786SJohn Baldwin 	/* Send a GET_DEVICE_ID request. */
753d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
754d72a0786SJohn Baldwin 	    IPMI_GET_DEVICE_ID, 0, 15);
755d72a0786SJohn Baldwin 
756d72a0786SJohn Baldwin 	error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT);
757d72a0786SJohn Baldwin 	if (error == EWOULDBLOCK) {
758d72a0786SJohn Baldwin 		device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n");
759d72a0786SJohn Baldwin 		return;
760d72a0786SJohn Baldwin 	} else if (error) {
761d72a0786SJohn Baldwin 		device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error);
762d72a0786SJohn Baldwin 		return;
763d72a0786SJohn Baldwin 	} else if (req->ir_compcode != 0) {
764d72a0786SJohn Baldwin 		device_printf(dev,
765d72a0786SJohn Baldwin 		    "Bad completion code for GET_DEVICE_ID: %d\n",
766d72a0786SJohn Baldwin 		    req->ir_compcode);
767d72a0786SJohn Baldwin 		return;
768d72a0786SJohn Baldwin 	} else if (req->ir_replylen < 5) {
769d72a0786SJohn Baldwin 		device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n",
770d72a0786SJohn Baldwin 		    req->ir_replylen);
771d72a0786SJohn Baldwin 		return;
772d72a0786SJohn Baldwin 	}
773d72a0786SJohn Baldwin 
774d72a0786SJohn Baldwin 	device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d, "
775d72a0786SJohn Baldwin 	    "version %d.%d\n",
776d72a0786SJohn Baldwin 	     req->ir_reply[1] & 0x0f,
777d72a0786SJohn Baldwin 	     req->ir_reply[2] & 0x0f, req->ir_reply[4],
778d72a0786SJohn Baldwin 	     req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4);
779d72a0786SJohn Baldwin 
780d72a0786SJohn Baldwin 	ipmi_free_request(req);
781d72a0786SJohn Baldwin 
782d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
783d72a0786SJohn Baldwin 	    IPMI_CLEAR_FLAGS, 1, 0);
784d72a0786SJohn Baldwin 
785d72a0786SJohn Baldwin 	ipmi_submit_driver_request(sc, req, 0);
786d72a0786SJohn Baldwin 
787d72a0786SJohn Baldwin 	/* XXX: Magic numbers */
788d72a0786SJohn Baldwin 	if (req->ir_compcode == 0xc0) {
789d72a0786SJohn Baldwin 		device_printf(dev, "Clear flags is busy\n");
790d72a0786SJohn Baldwin 	}
791d72a0786SJohn Baldwin 	if (req->ir_compcode == 0xc1) {
792d72a0786SJohn Baldwin 		device_printf(dev, "Clear flags illegal\n");
793d72a0786SJohn Baldwin 	}
794d72a0786SJohn Baldwin 	ipmi_free_request(req);
795d72a0786SJohn Baldwin 
796d72a0786SJohn Baldwin 	for (i = 0; i < 8; i++) {
797d72a0786SJohn Baldwin 		req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
798d72a0786SJohn Baldwin 		    IPMI_GET_CHANNEL_INFO, 1, 0);
799d72a0786SJohn Baldwin 		req->ir_request[0] = i;
800d72a0786SJohn Baldwin 
801d72a0786SJohn Baldwin 		ipmi_submit_driver_request(sc, req, 0);
802d72a0786SJohn Baldwin 
803d72a0786SJohn Baldwin 		if (req->ir_compcode != 0) {
804d72a0786SJohn Baldwin 			ipmi_free_request(req);
805d72a0786SJohn Baldwin 			break;
806d72a0786SJohn Baldwin 		}
807d72a0786SJohn Baldwin 		ipmi_free_request(req);
808d72a0786SJohn Baldwin 	}
809d72a0786SJohn Baldwin 	device_printf(dev, "Number of channels %d\n", i);
810d72a0786SJohn Baldwin 
811d72a0786SJohn Baldwin 	/* probe for watchdog */
812d72a0786SJohn Baldwin 	req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
813d72a0786SJohn Baldwin 	    IPMI_GET_WDOG, 0, 0);
814d72a0786SJohn Baldwin 
815d72a0786SJohn Baldwin 	ipmi_submit_driver_request(sc, req, 0);
816d72a0786SJohn Baldwin 
817d72a0786SJohn Baldwin 	if (req->ir_compcode == 0x00) {
818d72a0786SJohn Baldwin 		device_printf(dev, "Attached watchdog\n");
819d72a0786SJohn Baldwin 		/* register the watchdog event handler */
820d72a0786SJohn Baldwin 		sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER(watchdog_list,
821d72a0786SJohn Baldwin 		    ipmi_wd_event, sc, 0);
822d72a0786SJohn Baldwin 	}
823d72a0786SJohn Baldwin 	ipmi_free_request(req);
824d72a0786SJohn Baldwin 
825d72a0786SJohn Baldwin #ifdef CLONING
826d72a0786SJohn Baldwin 	sc->ipmi_cloning = 1;
827d72a0786SJohn Baldwin 	sc->ipmi_clone_tag = EVENTHANDLER_REGISTER(dev_clone,  ipmi_clone, sc,
828d72a0786SJohn Baldwin 	    1000);
829d72a0786SJohn Baldwin #else
830d72a0786SJohn Baldwin 	/* Initialize the device. */
831d72a0786SJohn Baldwin 	TAILQ_INIT(&sc->ipmi_idev.ipmi_completed_requests);
832d72a0786SJohn Baldwin 	sc->ipmi_idev.ipmi_softc = sc;
833d72a0786SJohn Baldwin 	sc->ipmi_idev.ipmi_address = IPMI_BMC_SLAVE_ADDR;
834d72a0786SJohn Baldwin 	sc->ipmi_idev.ipmi_lun = IPMI_BMC_SMS_LUN;
835d72a0786SJohn Baldwin 	sc->ipmi_idev.ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev),
836d72a0786SJohn Baldwin 	    UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev));
837d72a0786SJohn Baldwin 	if (sc->ipmi_idev.ipmi_cdev == NULL) {
838d72a0786SJohn Baldwin 		device_printf(dev, "Failed to create cdev\n");
839d72a0786SJohn Baldwin 		return;
840d72a0786SJohn Baldwin 	}
841d72a0786SJohn Baldwin 	sc->ipmi_idev.ipmi_cdev->si_drv1 = &sc->ipmi_idev;
842d72a0786SJohn Baldwin #endif
843d72a0786SJohn Baldwin }
844d72a0786SJohn Baldwin 
84537b1ce13SDoug Ambrisko int
84637b1ce13SDoug Ambrisko ipmi_attach(device_t dev)
84737b1ce13SDoug Ambrisko {
84837b1ce13SDoug Ambrisko 	struct ipmi_softc *sc = device_get_softc(dev);
849d72a0786SJohn Baldwin 	int error;
85037b1ce13SDoug Ambrisko 
851d72a0786SJohn Baldwin 	if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) {
852d72a0786SJohn Baldwin 		error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC,
853d72a0786SJohn Baldwin 		    sc->ipmi_intr, sc, &sc->ipmi_irq);
854d72a0786SJohn Baldwin 		if (error) {
855d72a0786SJohn Baldwin 			device_printf(dev, "can't set up interrupt\n");
856d72a0786SJohn Baldwin 			return (error);
85737b1ce13SDoug Ambrisko 		}
85837b1ce13SDoug Ambrisko 	}
85937b1ce13SDoug Ambrisko 
860d72a0786SJohn Baldwin 	bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook));
861d72a0786SJohn Baldwin 	sc->ipmi_ich.ich_func = ipmi_startup;
862d72a0786SJohn Baldwin 	sc->ipmi_ich.ich_arg = sc;
863d72a0786SJohn Baldwin 	if (config_intrhook_establish(&sc->ipmi_ich) != 0) {
864d72a0786SJohn Baldwin 		device_printf(dev, "can't establish configuration hook\n");
865d72a0786SJohn Baldwin 		return (ENOMEM);
86637b1ce13SDoug Ambrisko 	}
86737b1ce13SDoug Ambrisko 
86837b1ce13SDoug Ambrisko 	ipmi_attached = 1;
869d72a0786SJohn Baldwin 	return (0);
87037b1ce13SDoug Ambrisko }
87137b1ce13SDoug Ambrisko 
87237b1ce13SDoug Ambrisko int
87337b1ce13SDoug Ambrisko ipmi_detach(device_t dev)
87437b1ce13SDoug Ambrisko {
87537b1ce13SDoug Ambrisko 	struct ipmi_softc *sc;
87637b1ce13SDoug Ambrisko 
87737b1ce13SDoug Ambrisko 	sc = device_get_softc(dev);
878d72a0786SJohn Baldwin 
879d72a0786SJohn Baldwin 	/* Fail if there are any open handles. */
880d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
881d72a0786SJohn Baldwin #ifdef CLONING
882d72a0786SJohn Baldwin 	if (!TAILQ_EMPTY(&sc->ipmi_cdevs)) {
883d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
884d72a0786SJohn Baldwin 		return (EBUSY);
88537b1ce13SDoug Ambrisko 	}
88637b1ce13SDoug Ambrisko 
887d72a0786SJohn Baldwin 	/* Turn off cloning. */
888d72a0786SJohn Baldwin 	sc->ipmi_cloning = 0;
889d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
890d72a0786SJohn Baldwin 
891d72a0786SJohn Baldwin 	EVENTHANDLER_DEREGISTER(dev_clone, sc->ipmi_clone_tag);
892d72a0786SJohn Baldwin #else
893d72a0786SJohn Baldwin 	if (sc->ipmi_idev.ipmi_open) {
894d72a0786SJohn Baldwin 		IPMI_UNLOCK(sc);
895d72a0786SJohn Baldwin 		return (EBUSY);
896d72a0786SJohn Baldwin 	}
897d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
898d72a0786SJohn Baldwin 	destroy_dev(sc->ipmi_idev.ipmi_cdev);
899d72a0786SJohn Baldwin #endif
900d72a0786SJohn Baldwin 
901d72a0786SJohn Baldwin 	/* Detach from watchdog handling and turn off watchdog. */
902d72a0786SJohn Baldwin 	if (sc->ipmi_watchdog_tag) {
903d72a0786SJohn Baldwin 		EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag);
904d72a0786SJohn Baldwin 		ipmi_set_watchdog(sc, 0);
905d72a0786SJohn Baldwin 	}
906d72a0786SJohn Baldwin 
907d72a0786SJohn Baldwin 	/* XXX: should use shutdown callout I think. */
908d72a0786SJohn Baldwin 	/* If the backend uses a kthread, shut it down. */
909d72a0786SJohn Baldwin 	IPMI_LOCK(sc);
910d72a0786SJohn Baldwin 	sc->ipmi_detaching = 1;
911d72a0786SJohn Baldwin 	if (sc->ipmi_kthread) {
912d72a0786SJohn Baldwin 		cv_broadcast(&sc->ipmi_request_added);
913d72a0786SJohn Baldwin 		msleep(sc->ipmi_kthread, &sc->ipmi_lock, 0, "ipmi_wait", 0);
914d72a0786SJohn Baldwin 	}
915d72a0786SJohn Baldwin 	IPMI_UNLOCK(sc);
916d72a0786SJohn Baldwin 	if (sc->ipmi_irq)
917d72a0786SJohn Baldwin 		bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
918d72a0786SJohn Baldwin 
919d72a0786SJohn Baldwin 	ipmi_release_resources(dev);
920d72a0786SJohn Baldwin 	mtx_destroy(&sc->ipmi_lock);
921d72a0786SJohn Baldwin 	return (0);
922d72a0786SJohn Baldwin }
923d72a0786SJohn Baldwin 
924d72a0786SJohn Baldwin void
925d72a0786SJohn Baldwin ipmi_release_resources(device_t dev)
926d72a0786SJohn Baldwin {
927d72a0786SJohn Baldwin 	struct ipmi_softc *sc;
928d72a0786SJohn Baldwin 	int i;
929d72a0786SJohn Baldwin 
930d72a0786SJohn Baldwin 	sc = device_get_softc(dev);
931d72a0786SJohn Baldwin 	if (sc->ipmi_irq)
932d72a0786SJohn Baldwin 		bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq);
933d72a0786SJohn Baldwin 	if (sc->ipmi_irq_res)
934d72a0786SJohn Baldwin 		bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid,
935d72a0786SJohn Baldwin 		    sc->ipmi_irq_res);
936d72a0786SJohn Baldwin 	for (i = 0; i < MAX_RES; i++)
937d72a0786SJohn Baldwin 		if (sc->ipmi_io_res[i])
938d72a0786SJohn Baldwin 			bus_release_resource(dev, sc->ipmi_io_type,
939d72a0786SJohn Baldwin 			    sc->ipmi_io_rid + i, sc->ipmi_io_res[i]);
940d72a0786SJohn Baldwin }
941d72a0786SJohn Baldwin 
942d72a0786SJohn Baldwin devclass_t ipmi_devclass;
943d72a0786SJohn Baldwin 
944d72a0786SJohn Baldwin /* XXX: Why? */
945d72a0786SJohn Baldwin static void
946d72a0786SJohn Baldwin ipmi_unload(void *arg)
947d72a0786SJohn Baldwin {
948d72a0786SJohn Baldwin 	device_t *	devs;
949d72a0786SJohn Baldwin 	int		count;
950d72a0786SJohn Baldwin 	int		i;
951d72a0786SJohn Baldwin 
952bec0c98eSJohn Baldwin 	if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0)
953bec0c98eSJohn Baldwin 		return;
954d72a0786SJohn Baldwin 	for (i = 0; i < count; i++)
955d72a0786SJohn Baldwin 		device_delete_child(device_get_parent(devs[i]), devs[i]);
956bec0c98eSJohn Baldwin 	free(devs, M_TEMP);
957d72a0786SJohn Baldwin }
958d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL);
959d72a0786SJohn Baldwin 
96019d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG
96137b1ce13SDoug Ambrisko static void
962d72a0786SJohn Baldwin dump_buf(u_char *data, int len)
963d72a0786SJohn Baldwin {
96437b1ce13SDoug Ambrisko 	char buf[20];
96537b1ce13SDoug Ambrisko 	char line[1024];
96637b1ce13SDoug Ambrisko 	char temp[30];
96737b1ce13SDoug Ambrisko 	int count = 0;
96837b1ce13SDoug Ambrisko 	int i=0;
96937b1ce13SDoug Ambrisko 
97037b1ce13SDoug Ambrisko 	printf("Address %p len %d\n", data, len);
97137b1ce13SDoug Ambrisko 	if (len > 256)
97237b1ce13SDoug Ambrisko 		len = 256;
97337b1ce13SDoug Ambrisko 	line[0] = '\000';
97437b1ce13SDoug Ambrisko 	for (; len > 0; len--, data++) {
97537b1ce13SDoug Ambrisko 		sprintf(temp, "%02x ", *data);
97637b1ce13SDoug Ambrisko 		strcat(line, temp);
97737b1ce13SDoug Ambrisko 		if (*data >= ' ' && *data <= '~')
97837b1ce13SDoug Ambrisko 			buf[count] = *data;
97937b1ce13SDoug Ambrisko 		else if (*data >= 'A' && *data <= 'Z')
98037b1ce13SDoug Ambrisko 			buf[count] = *data;
98137b1ce13SDoug Ambrisko 		else
98237b1ce13SDoug Ambrisko 			buf[count] = '.';
98337b1ce13SDoug Ambrisko 		if (++count == 16) {
98437b1ce13SDoug Ambrisko 			buf[count] = '\000';
98537b1ce13SDoug Ambrisko 			count = 0;
98637b1ce13SDoug Ambrisko 			printf("  %3x  %s %s\n", i, line, buf);
98737b1ce13SDoug Ambrisko 			i+=16;
98837b1ce13SDoug Ambrisko 			line[0] = '\000';
98937b1ce13SDoug Ambrisko 		}
99037b1ce13SDoug Ambrisko 	}
99137b1ce13SDoug Ambrisko 	buf[count] = '\000';
99237b1ce13SDoug Ambrisko 
99337b1ce13SDoug Ambrisko 	for (; count != 16; count++) {
99437b1ce13SDoug Ambrisko 		strcat(line, "   ");
99537b1ce13SDoug Ambrisko 	}
99637b1ce13SDoug Ambrisko 	printf("  %3x  %s %s\n", i, line, buf);
99737b1ce13SDoug Ambrisko }
99837b1ce13SDoug Ambrisko #endif
999