1155517Sambrisko/*- 2155517Sambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3155517Sambrisko * All rights reserved. 4155517Sambrisko * 5155517Sambrisko * Redistribution and use in source and binary forms, with or without 6155517Sambrisko * modification, are permitted provided that the following conditions 7155517Sambrisko * are met: 8155517Sambrisko * 1. Redistributions of source code must retain the above copyright 9155517Sambrisko * notice, this list of conditions and the following disclaimer. 10155517Sambrisko * 2. Redistributions in binary form must reproduce the above copyright 11155517Sambrisko * notice, this list of conditions and the following disclaimer in the 12155517Sambrisko * documentation and/or other materials provided with the distribution. 13155517Sambrisko * 14155517Sambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15155517Sambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16155517Sambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17155517Sambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18155517Sambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19155517Sambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20155517Sambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21155517Sambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22155517Sambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23155517Sambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24155517Sambrisko * SUCH DAMAGE. 25155517Sambrisko */ 26155517Sambrisko 27155517Sambrisko#include <sys/cdefs.h> 28155517Sambrisko__FBSDID("$FreeBSD: releng/11.0/sys/dev/ipmi/ipmi.c 297179 2016-03-22 06:24:52Z mav $"); 29155517Sambrisko 30155517Sambrisko#include <sys/param.h> 31155517Sambrisko#include <sys/systm.h> 32162562Sjhb#include <sys/bus.h> 33162562Sjhb#include <sys/condvar.h> 34162562Sjhb#include <sys/conf.h> 35155517Sambrisko#include <sys/kernel.h> 36155517Sambrisko#include <sys/malloc.h> 37162562Sjhb#include <sys/module.h> 38155517Sambrisko#include <sys/poll.h> 39162562Sjhb#include <sys/rman.h> 40155517Sambrisko#include <sys/selinfo.h> 41162562Sjhb#include <sys/sysctl.h> 42155517Sambrisko#include <sys/watchdog.h> 43155517Sambrisko 44155517Sambrisko#ifdef LOCAL_MODULE 45155517Sambrisko#include <ipmi.h> 46155517Sambrisko#include <ipmivars.h> 47155517Sambrisko#else 48155517Sambrisko#include <sys/ipmi.h> 49155517Sambrisko#include <dev/ipmi/ipmivars.h> 50155517Sambrisko#endif 51155517Sambrisko 52278321Sjhb/* 53278321Sjhb * Driver request structures are allocated on the stack via alloca() to 54278321Sjhb * avoid calling malloc(), especially for the watchdog handler. 55278321Sjhb * To avoid too much stack growth, a previously allocated structure can 56278321Sjhb * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 57278321Sjhb * that there is adequate reply/request space in the original allocation. 58278321Sjhb */ 59278321Sjhb#define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 60278321Sjhb bzero((req), sizeof(struct ipmi_request)); \ 61278321Sjhb ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 62278321Sjhb 63278321Sjhb#define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 64278321Sjhb (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 65278321Sjhb (reqlen) + (replylen)); \ 66278321Sjhb IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 67278321Sjhb (replylen)) 68278321Sjhb 69155517Sambrisko#ifdef IPMB 70155517Sambriskostatic int ipmi_ipmb_checksum(u_char, int); 71155517Sambriskostatic int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 72155517Sambrisko u_char, u_char, int) 73155517Sambrisko#endif 74155517Sambrisko 75155517Sambriskostatic d_ioctl_t ipmi_ioctl; 76155517Sambriskostatic d_poll_t ipmi_poll; 77155517Sambriskostatic d_open_t ipmi_open; 78182322Sjhbstatic void ipmi_dtor(void *arg); 79155517Sambrisko 80155517Sambriskoint ipmi_attached = 0; 81155517Sambrisko 82155517Sambriskostatic int on = 1; 83227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, 84227309Sed "IPMI driver parameters"); 85155517SambriskoSYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, 86162562Sjhb &on, 0, ""); 87155517Sambrisko 88155517Sambriskostatic struct cdevsw ipmi_cdevsw = { 89155517Sambrisko .d_version = D_VERSION, 90155517Sambrisko .d_open = ipmi_open, 91155517Sambrisko .d_ioctl = ipmi_ioctl, 92155517Sambrisko .d_poll = ipmi_poll, 93155517Sambrisko .d_name = "ipmi", 94155517Sambrisko}; 95155517Sambrisko 96227293Sedstatic MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 97155517Sambrisko 98162562Sjhbstatic int 99162562Sjhbipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 100155517Sambrisko{ 101162562Sjhb struct ipmi_device *dev; 102155517Sambrisko struct ipmi_softc *sc; 103182322Sjhb int error; 104155517Sambrisko 105155517Sambrisko if (!on) 106162562Sjhb return (ENOENT); 107155517Sambrisko 108182322Sjhb /* Initialize the per file descriptor data. */ 109182322Sjhb dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 110182322Sjhb error = devfs_set_cdevpriv(dev, ipmi_dtor); 111182322Sjhb if (error) { 112182322Sjhb free(dev, M_IPMI); 113182322Sjhb return (error); 114182322Sjhb } 115182322Sjhb 116182322Sjhb sc = cdev->si_drv1; 117182322Sjhb TAILQ_INIT(&dev->ipmi_completed_requests); 118182322Sjhb dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 119182322Sjhb dev->ipmi_lun = IPMI_BMC_SMS_LUN; 120182322Sjhb dev->ipmi_softc = sc; 121162562Sjhb IPMI_LOCK(sc); 122182322Sjhb sc->ipmi_opened++; 123162562Sjhb IPMI_UNLOCK(sc); 124155517Sambrisko 125162562Sjhb return (0); 126155517Sambrisko} 127155517Sambrisko 128162562Sjhbstatic int 129162562Sjhbipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 130155517Sambrisko{ 131162562Sjhb struct ipmi_device *dev; 132155517Sambrisko struct ipmi_softc *sc; 133155517Sambrisko int revents = 0; 134155517Sambrisko 135182322Sjhb if (devfs_get_cdevpriv((void **)&dev)) 136182322Sjhb return (0); 137155517Sambrisko 138182322Sjhb sc = cdev->si_drv1; 139162562Sjhb IPMI_LOCK(sc); 140155517Sambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 141162562Sjhb if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 142155517Sambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 143162562Sjhb if (dev->ipmi_requests == 0) 144155517Sambrisko revents |= POLLERR; 145155517Sambrisko } 146155517Sambrisko 147155517Sambrisko if (revents == 0) { 148155517Sambrisko if (poll_events & (POLLIN | POLLRDNORM)) 149162562Sjhb selrecord(td, &dev->ipmi_select); 150155517Sambrisko } 151162562Sjhb IPMI_UNLOCK(sc); 152155517Sambrisko 153162562Sjhb return (revents); 154155517Sambrisko} 155155517Sambrisko 156162562Sjhbstatic void 157162562Sjhbipmi_purge_completed_requests(struct ipmi_device *dev) 158155517Sambrisko{ 159162562Sjhb struct ipmi_request *req; 160162562Sjhb 161162562Sjhb while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 162162562Sjhb req = TAILQ_FIRST(&dev->ipmi_completed_requests); 163162562Sjhb TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 164162562Sjhb dev->ipmi_requests--; 165162562Sjhb ipmi_free_request(req); 166162562Sjhb } 167162562Sjhb} 168162562Sjhb 169182322Sjhbstatic void 170182322Sjhbipmi_dtor(void *arg) 171162562Sjhb{ 172162562Sjhb struct ipmi_request *req, *nreq; 173162562Sjhb struct ipmi_device *dev; 174155517Sambrisko struct ipmi_softc *sc; 175155517Sambrisko 176182322Sjhb dev = arg; 177162562Sjhb sc = dev->ipmi_softc; 178155517Sambrisko 179162562Sjhb IPMI_LOCK(sc); 180162562Sjhb if (dev->ipmi_requests) { 181162562Sjhb /* Throw away any pending requests for this device. */ 182162562Sjhb TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 183162562Sjhb nreq) { 184162562Sjhb if (req->ir_owner == dev) { 185162562Sjhb TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 186162562Sjhb ir_link); 187162562Sjhb dev->ipmi_requests--; 188162562Sjhb ipmi_free_request(req); 189162562Sjhb } 190162562Sjhb } 191155517Sambrisko 192162562Sjhb /* Throw away any pending completed requests for this device. */ 193162562Sjhb ipmi_purge_completed_requests(dev); 194162562Sjhb 195162562Sjhb /* 196162562Sjhb * If we still have outstanding requests, they must be stuck 197162562Sjhb * in an interface driver, so wait for those to drain. 198162562Sjhb */ 199162562Sjhb dev->ipmi_closing = 1; 200162562Sjhb while (dev->ipmi_requests > 0) { 201278321Sjhb msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 202278321Sjhb PWAIT, "ipmidrain", 0); 203162562Sjhb ipmi_purge_completed_requests(dev); 204162562Sjhb } 205162562Sjhb } 206182322Sjhb sc->ipmi_opened--; 207162562Sjhb IPMI_UNLOCK(sc); 208162562Sjhb 209162562Sjhb /* Cleanup. */ 210162562Sjhb free(dev, M_IPMI); 211155517Sambrisko} 212155517Sambrisko 213155517Sambrisko#ifdef IPMB 214155517Sambriskostatic int 215155517Sambriskoipmi_ipmb_checksum(u_char *data, int len) 216155517Sambrisko{ 217155517Sambrisko u_char sum = 0; 218155517Sambrisko 219155517Sambrisko for (; len; len--) { 220155517Sambrisko sum += *data++; 221155517Sambrisko } 222162562Sjhb return (-sum); 223155517Sambrisko} 224155517Sambrisko 225162562Sjhb/* XXX: Needs work */ 226155517Sambriskostatic int 227155517Sambriskoipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 228155517Sambrisko u_char command, u_char seq, u_char *data, int data_len) 229155517Sambrisko{ 230155517Sambrisko struct ipmi_softc *sc = device_get_softc(dev); 231162562Sjhb struct ipmi_request *req; 232162562Sjhb u_char slave_addr = 0x52; 233155517Sambrisko int error; 234155517Sambrisko 235278321Sjhb IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 236162562Sjhb IPMI_SEND_MSG, data_len + 8, 0); 237162562Sjhb req->ir_request[0] = channel; 238162562Sjhb req->ir_request[1] = slave_addr; 239162562Sjhb req->ir_request[2] = IPMI_ADDR(netfn, 0); 240162562Sjhb req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 241162562Sjhb req->ir_request[4] = sc->ipmi_address; 242162562Sjhb req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 243162562Sjhb req->ir_request[6] = command; 244155517Sambrisko 245162562Sjhb bcopy(data, &req->ir_request[7], data_len); 246162562Sjhb temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 247162562Sjhb data_len + 3); 248155517Sambrisko 249162562Sjhb ipmi_submit_driver_request(sc, req); 250162562Sjhb error = req->ir_error; 251155517Sambrisko 252162562Sjhb return (error); 253155517Sambrisko} 254155517Sambrisko 255155517Sambriskostatic int 256162562Sjhbipmi_handle_attn(struct ipmi_softc *sc) 257155517Sambrisko{ 258162562Sjhb struct ipmi_request *req; 259155517Sambrisko int error; 260155517Sambrisko 261155517Sambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 262278321Sjhb IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 263162562Sjhb IPMI_GET_MSG_FLAGS, 0, 1); 264155517Sambrisko 265162562Sjhb ipmi_submit_driver_request(sc, req); 266162562Sjhb 267162562Sjhb if (req->ir_error == 0 && req->ir_compcode == 0) { 268162562Sjhb if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 269155517Sambrisko device_printf(sc->ipmi_dev, "message buffer full"); 270155517Sambrisko } 271162562Sjhb if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 272155517Sambrisko device_printf(sc->ipmi_dev, 273155517Sambrisko "watchdog about to go off"); 274155517Sambrisko } 275162562Sjhb if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 276278321Sjhb IPMI_ALLOC_DRIVER_REQUEST(req, 277162562Sjhb IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 278162562Sjhb 16); 279162562Sjhb 280155517Sambrisko device_printf(sc->ipmi_dev, "throw out message "); 281155517Sambrisko dump_buf(temp, 16); 282155517Sambrisko } 283162562Sjhb } 284162562Sjhb error = req->ir_error; 285162562Sjhb 286162562Sjhb return (error); 287155517Sambrisko} 288155517Sambrisko#endif 289155517Sambrisko 290162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 291162562Sjhb#define PTRIN(p) ((void *)(uintptr_t)(p)) 292162562Sjhb#define PTROUT(p) ((uintptr_t)(p)) 293155517Sambrisko#endif 294155517Sambrisko 295155517Sambriskostatic int 296162562Sjhbipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 297155517Sambrisko int flags, struct thread *td) 298155517Sambrisko{ 299155517Sambrisko struct ipmi_softc *sc; 300162562Sjhb struct ipmi_device *dev; 301162562Sjhb struct ipmi_request *kreq; 302155517Sambrisko struct ipmi_req *req = (struct ipmi_req *)data; 303155517Sambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 304155517Sambrisko struct ipmi_addr addr; 305162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 306162562Sjhb struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 307162562Sjhb struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 308162562Sjhb union { 309162562Sjhb struct ipmi_req req; 310162562Sjhb struct ipmi_recv recv; 311162562Sjhb } thunk32; 312162562Sjhb#endif 313155517Sambrisko int error, len; 314155517Sambrisko 315182322Sjhb error = devfs_get_cdevpriv((void **)&dev); 316182322Sjhb if (error) 317182322Sjhb return (error); 318155517Sambrisko 319182322Sjhb sc = cdev->si_drv1; 320182322Sjhb 321162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 322162562Sjhb /* Convert 32-bit structures to native. */ 323155517Sambrisko switch (cmd) { 324162562Sjhb case IPMICTL_SEND_COMMAND_32: 325162562Sjhb req = &thunk32.req; 326162562Sjhb req->addr = PTRIN(req32->addr); 327162562Sjhb req->addr_len = req32->addr_len; 328162562Sjhb req->msgid = req32->msgid; 329162562Sjhb req->msg.netfn = req32->msg.netfn; 330162562Sjhb req->msg.cmd = req32->msg.cmd; 331162562Sjhb req->msg.data_len = req32->msg.data_len; 332162562Sjhb req->msg.data = PTRIN(req32->msg.data); 333162562Sjhb break; 334162562Sjhb case IPMICTL_RECEIVE_MSG_TRUNC_32: 335162562Sjhb case IPMICTL_RECEIVE_MSG_32: 336162562Sjhb recv = &thunk32.recv; 337162562Sjhb recv->addr = PTRIN(recv32->addr); 338162562Sjhb recv->addr_len = recv32->addr_len; 339162562Sjhb recv->msg.data_len = recv32->msg.data_len; 340162562Sjhb recv->msg.data = PTRIN(recv32->msg.data); 341162562Sjhb break; 342162562Sjhb } 343162562Sjhb#endif 344162562Sjhb 345162562Sjhb switch (cmd) { 346162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 347162562Sjhb case IPMICTL_SEND_COMMAND_32: 348162562Sjhb#endif 349155517Sambrisko case IPMICTL_SEND_COMMAND: 350162562Sjhb /* 351162562Sjhb * XXX: Need to add proper handling of this. 352162562Sjhb */ 353162562Sjhb error = copyin(req->addr, &addr, sizeof(addr)); 354162562Sjhb if (error) 355162562Sjhb return (error); 356162562Sjhb 357162562Sjhb IPMI_LOCK(sc); 358155517Sambrisko /* clear out old stuff in queue of stuff done */ 359162562Sjhb /* XXX: This seems odd. */ 360162562Sjhb while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 361162562Sjhb TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 362162562Sjhb ir_link); 363162562Sjhb dev->ipmi_requests--; 364162562Sjhb ipmi_free_request(kreq); 365155517Sambrisko } 366162562Sjhb IPMI_UNLOCK(sc); 367155517Sambrisko 368162562Sjhb kreq = ipmi_alloc_request(dev, req->msgid, 369162562Sjhb IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 370162562Sjhb req->msg.data_len, IPMI_MAX_RX); 371162562Sjhb error = copyin(req->msg.data, kreq->ir_request, 372155517Sambrisko req->msg.data_len); 373162562Sjhb if (error) { 374162562Sjhb ipmi_free_request(kreq); 375162562Sjhb return (error); 376155517Sambrisko } 377162562Sjhb IPMI_LOCK(sc); 378162562Sjhb dev->ipmi_requests++; 379162562Sjhb error = sc->ipmi_enqueue_request(sc, kreq); 380162562Sjhb IPMI_UNLOCK(sc); 381162562Sjhb if (error) 382162562Sjhb return (error); 383162562Sjhb break; 384162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 385162562Sjhb case IPMICTL_RECEIVE_MSG_TRUNC_32: 386162562Sjhb case IPMICTL_RECEIVE_MSG_32: 387162562Sjhb#endif 388155517Sambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 389155517Sambrisko case IPMICTL_RECEIVE_MSG: 390162562Sjhb error = copyin(recv->addr, &addr, sizeof(addr)); 391162562Sjhb if (error) 392162562Sjhb return (error); 393162562Sjhb 394162562Sjhb IPMI_LOCK(sc); 395162562Sjhb kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 396162562Sjhb if (kreq == NULL) { 397162562Sjhb IPMI_UNLOCK(sc); 398162562Sjhb return (EAGAIN); 399155517Sambrisko } 400155517Sambrisko addr.channel = IPMI_BMC_CHANNEL; 401162562Sjhb /* XXX */ 402155517Sambrisko recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 403162562Sjhb recv->msgid = kreq->ir_msgid; 404162562Sjhb recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 405162562Sjhb recv->msg.cmd = kreq->ir_command; 406162562Sjhb error = kreq->ir_error; 407162562Sjhb if (error) { 408162562Sjhb TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 409162562Sjhb ir_link); 410162562Sjhb dev->ipmi_requests--; 411162562Sjhb IPMI_UNLOCK(sc); 412162562Sjhb ipmi_free_request(kreq); 413162562Sjhb return (error); 414155517Sambrisko } 415162562Sjhb len = kreq->ir_replylen + 1; 416162562Sjhb if (recv->msg.data_len < len && 417162562Sjhb (cmd == IPMICTL_RECEIVE_MSG 418162562Sjhb#ifdef IPMICTL_RECEIVE_MSG_32 419184949Sobrien || cmd == IPMICTL_RECEIVE_MSG_32 420162562Sjhb#endif 421162562Sjhb )) { 422162562Sjhb IPMI_UNLOCK(sc); 423162562Sjhb return (EMSGSIZE); 424162562Sjhb } 425162562Sjhb TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 426162562Sjhb dev->ipmi_requests--; 427162562Sjhb IPMI_UNLOCK(sc); 428155517Sambrisko len = min(recv->msg.data_len, len); 429155517Sambrisko recv->msg.data_len = len; 430155517Sambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 431155517Sambrisko if (error == 0) 432162562Sjhb error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 433162562Sjhb if (error == 0) 434162562Sjhb error = copyout(kreq->ir_reply, recv->msg.data + 1, 435162562Sjhb len - 1); 436162562Sjhb ipmi_free_request(kreq); 437162562Sjhb if (error) 438162562Sjhb return (error); 439162562Sjhb break; 440155517Sambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 441162562Sjhb IPMI_LOCK(sc); 442162562Sjhb dev->ipmi_address = *(int*)data; 443162562Sjhb IPMI_UNLOCK(sc); 444162562Sjhb break; 445155517Sambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 446162562Sjhb IPMI_LOCK(sc); 447162562Sjhb *(int*)data = dev->ipmi_address; 448162562Sjhb IPMI_UNLOCK(sc); 449162562Sjhb break; 450155517Sambrisko case IPMICTL_SET_MY_LUN_CMD: 451162562Sjhb IPMI_LOCK(sc); 452162562Sjhb dev->ipmi_lun = *(int*)data & 0x3; 453162562Sjhb IPMI_UNLOCK(sc); 454162562Sjhb break; 455155517Sambrisko case IPMICTL_GET_MY_LUN_CMD: 456162562Sjhb IPMI_LOCK(sc); 457162562Sjhb *(int*)data = dev->ipmi_lun; 458162562Sjhb IPMI_UNLOCK(sc); 459162562Sjhb break; 460155517Sambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 461155517Sambrisko /* 462155517Sambrisko device_printf(sc->ipmi_dev, 463155517Sambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 464155517Sambrisko */ 465162562Sjhb break; 466155517Sambrisko case IPMICTL_REGISTER_FOR_CMD: 467155517Sambrisko case IPMICTL_UNREGISTER_FOR_CMD: 468162562Sjhb return (EOPNOTSUPP); 469162562Sjhb default: 470162562Sjhb device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 471162562Sjhb return (ENOIOCTL); 472155517Sambrisko } 473155517Sambrisko 474162562Sjhb#ifdef IPMICTL_SEND_COMMAND_32 475162562Sjhb /* Update changed fields in 32-bit structures. */ 476162562Sjhb switch (cmd) { 477162562Sjhb case IPMICTL_RECEIVE_MSG_TRUNC_32: 478162562Sjhb case IPMICTL_RECEIVE_MSG_32: 479162562Sjhb recv32->recv_type = recv->recv_type; 480162562Sjhb recv32->msgid = recv->msgid; 481162562Sjhb recv32->msg.netfn = recv->msg.netfn; 482162562Sjhb recv32->msg.cmd = recv->msg.cmd; 483162562Sjhb recv32->msg.data_len = recv->msg.data_len; 484162562Sjhb break; 485162562Sjhb } 486162562Sjhb#endif 487162562Sjhb return (0); 488155517Sambrisko} 489155517Sambrisko 490162562Sjhb/* 491162562Sjhb * Request management. 492162562Sjhb */ 493155517Sambrisko 494278321Sjhbstatic __inline void 495278321Sjhbipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 496278321Sjhb uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 497162562Sjhb{ 498155517Sambrisko 499162562Sjhb req->ir_owner = dev; 500162562Sjhb req->ir_msgid = msgid; 501162562Sjhb req->ir_addr = addr; 502162562Sjhb req->ir_command = command; 503162562Sjhb if (requestlen) { 504162562Sjhb req->ir_request = (char *)&req[1]; 505162562Sjhb req->ir_requestlen = requestlen; 506155517Sambrisko } 507162562Sjhb if (replylen) { 508162562Sjhb req->ir_reply = (char *)&req[1] + requestlen; 509162562Sjhb req->ir_replybuflen = replylen; 510162562Sjhb } 511278321Sjhb} 512278321Sjhb 513278321Sjhb/* Allocate a new request with request and reply buffers. */ 514278321Sjhbstruct ipmi_request * 515278321Sjhbipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 516278321Sjhb uint8_t command, size_t requestlen, size_t replylen) 517278321Sjhb{ 518278321Sjhb struct ipmi_request *req; 519278321Sjhb 520278321Sjhb req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 521278321Sjhb M_IPMI, M_WAITOK | M_ZERO); 522278321Sjhb ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 523162562Sjhb return (req); 524155517Sambrisko} 525155517Sambrisko 526162562Sjhb/* Free a request no longer in use. */ 527162562Sjhbvoid 528162562Sjhbipmi_free_request(struct ipmi_request *req) 529162562Sjhb{ 530155517Sambrisko 531162562Sjhb free(req, M_IPMI); 532155517Sambrisko} 533155517Sambrisko 534162562Sjhb/* Store a processed request on the appropriate completion queue. */ 535162562Sjhbvoid 536162562Sjhbipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 537162562Sjhb{ 538162562Sjhb struct ipmi_device *dev; 539155517Sambrisko 540162562Sjhb IPMI_LOCK_ASSERT(sc); 541155517Sambrisko 542162562Sjhb /* 543162562Sjhb * Anonymous requests (from inside the driver) always have a 544162562Sjhb * waiter that we awaken. 545162562Sjhb */ 546162562Sjhb if (req->ir_owner == NULL) 547162562Sjhb wakeup(req); 548162562Sjhb else { 549162562Sjhb dev = req->ir_owner; 550162562Sjhb TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 551162562Sjhb selwakeup(&dev->ipmi_select); 552162562Sjhb if (dev->ipmi_closing) 553162562Sjhb wakeup(&dev->ipmi_requests); 554155517Sambrisko } 555155517Sambrisko} 556155517Sambrisko 557278321Sjhb/* Perform an internal driver request. */ 558162562Sjhbint 559162562Sjhbipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 560162562Sjhb int timo) 561162562Sjhb{ 562155517Sambrisko 563278321Sjhb return (sc->ipmi_driver_request(sc, req, timo)); 564155517Sambrisko} 565155517Sambrisko 566162562Sjhb/* 567162562Sjhb * Helper routine for polled system interfaces that use 568162562Sjhb * ipmi_polled_enqueue_request() to queue requests. This request 569162562Sjhb * waits until there is a pending request and then returns the first 570162562Sjhb * request. If the driver is shutting down, it returns NULL. 571162562Sjhb */ 572162562Sjhbstruct ipmi_request * 573162562Sjhbipmi_dequeue_request(struct ipmi_softc *sc) 574162562Sjhb{ 575162562Sjhb struct ipmi_request *req; 576155517Sambrisko 577162562Sjhb IPMI_LOCK_ASSERT(sc); 578155517Sambrisko 579162562Sjhb while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 580278321Sjhb cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 581162562Sjhb if (sc->ipmi_detaching) 582162562Sjhb return (NULL); 583155517Sambrisko 584162562Sjhb req = TAILQ_FIRST(&sc->ipmi_pending_requests); 585162562Sjhb TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 586162562Sjhb return (req); 587155517Sambrisko} 588155517Sambrisko 589162562Sjhb/* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 590155517Sambriskoint 591162562Sjhbipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 592162562Sjhb{ 593155517Sambrisko 594163278Sjhb IPMI_LOCK_ASSERT(sc); 595163278Sjhb 596162562Sjhb TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 597162562Sjhb cv_signal(&sc->ipmi_request_added); 598162562Sjhb return (0); 599155517Sambrisko} 600155517Sambrisko 601155517Sambrisko/* 602155517Sambrisko * Watchdog event handler. 603155517Sambrisko */ 604155517Sambrisko 605200666Srustatic int 606297179Smavipmi_reset_watchdog(struct ipmi_softc *sc) 607297179Smav{ 608297179Smav struct ipmi_request *req; 609297179Smav int error; 610297179Smav 611297179Smav IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 612297179Smav IPMI_RESET_WDOG, 0, 0); 613297179Smav error = ipmi_submit_driver_request(sc, req, 0); 614297179Smav if (error) 615297179Smav device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 616297179Smav return (error); 617297179Smav} 618297179Smav 619297179Smavstatic int 620200666Sruipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 621162562Sjhb{ 622162562Sjhb struct ipmi_request *req; 623162562Sjhb int error; 624155517Sambrisko 625200666Sru if (sec > 0xffff / 10) 626200666Sru return (EINVAL); 627200666Sru 628278321Sjhb IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 629162562Sjhb IPMI_SET_WDOG, 6, 0); 630155517Sambrisko if (sec) { 631162562Sjhb req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 632155517Sambrisko | IPMI_SET_WD_TIMER_SMS_OS; 633162562Sjhb req->ir_request[1] = IPMI_SET_WD_ACTION_RESET; 634162562Sjhb req->ir_request[2] = 0; 635162562Sjhb req->ir_request[3] = 0; /* Timer use */ 636162562Sjhb req->ir_request[4] = (sec * 10) & 0xff; 637200666Sru req->ir_request[5] = (sec * 10) >> 8; 638155517Sambrisko } else { 639162562Sjhb req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 640162562Sjhb req->ir_request[1] = 0; 641162562Sjhb req->ir_request[2] = 0; 642162562Sjhb req->ir_request[3] = 0; /* Timer use */ 643162562Sjhb req->ir_request[4] = 0; 644162562Sjhb req->ir_request[5] = 0; 645155517Sambrisko } 646162562Sjhb error = ipmi_submit_driver_request(sc, req, 0); 647162562Sjhb if (error) 648162562Sjhb device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 649200666Sru return (error); 650155517Sambrisko} 651155517Sambrisko 652155517Sambriskostatic void 653155517Sambriskoipmi_wd_event(void *arg, unsigned int cmd, int *error) 654155517Sambrisko{ 655155517Sambrisko struct ipmi_softc *sc = arg; 656155517Sambrisko unsigned int timeout; 657200666Sru int e; 658155517Sambrisko 659257421Sglebius if (dumping) 660257421Sglebius return; 661257421Sglebius 662165260Sn_hibma cmd &= WD_INTERVAL; 663165260Sn_hibma if (cmd > 0 && cmd <= 63) { 664200666Sru timeout = ((uint64_t)1 << cmd) / 1000000000; 665200666Sru if (timeout == 0) 666200666Sru timeout = 1; 667297179Smav if (timeout != sc->ipmi_watchdog_active) { 668297179Smav e = ipmi_set_watchdog(sc, timeout); 669297179Smav if (e == 0) { 670297179Smav sc->ipmi_watchdog_active = timeout; 671297179Smav } else { 672297179Smav (void)ipmi_set_watchdog(sc, 0); 673297179Smav sc->ipmi_watchdog_active = 0; 674297179Smav } 675297179Smav } 676297179Smav if (sc->ipmi_watchdog_active != 0) { 677297179Smav e = ipmi_reset_watchdog(sc); 678297179Smav if (e == 0) { 679297179Smav *error = 0; 680297179Smav } else { 681297179Smav (void)ipmi_set_watchdog(sc, 0); 682297179Smav sc->ipmi_watchdog_active = 0; 683297179Smav } 684297179Smav } 685239128Sjhb } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 686200666Sru e = ipmi_set_watchdog(sc, 0); 687200666Sru if (e != 0 && cmd == 0) 688200666Sru *error = EOPNOTSUPP; 689155517Sambrisko } 690155517Sambrisko} 691155517Sambrisko 692162562Sjhbstatic void 693162562Sjhbipmi_startup(void *arg) 694162562Sjhb{ 695162562Sjhb struct ipmi_softc *sc = arg; 696162562Sjhb struct ipmi_request *req; 697162562Sjhb device_t dev; 698162562Sjhb int error, i; 699162562Sjhb 700162562Sjhb config_intrhook_disestablish(&sc->ipmi_ich); 701162562Sjhb dev = sc->ipmi_dev; 702162562Sjhb 703162562Sjhb /* Initialize interface-independent state. */ 704278321Sjhb mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 705278321Sjhb mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 706162562Sjhb cv_init(&sc->ipmi_request_added, "ipmireq"); 707162562Sjhb TAILQ_INIT(&sc->ipmi_pending_requests); 708162562Sjhb 709162562Sjhb /* Initialize interface-dependent state. */ 710162562Sjhb error = sc->ipmi_startup(sc); 711162562Sjhb if (error) { 712162562Sjhb device_printf(dev, "Failed to initialize interface: %d\n", 713162562Sjhb error); 714162562Sjhb return; 715162562Sjhb } 716162562Sjhb 717162562Sjhb /* Send a GET_DEVICE_ID request. */ 718278321Sjhb IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 719162562Sjhb IPMI_GET_DEVICE_ID, 0, 15); 720162562Sjhb 721162562Sjhb error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 722162562Sjhb if (error == EWOULDBLOCK) { 723162562Sjhb device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 724162562Sjhb return; 725162562Sjhb } else if (error) { 726162562Sjhb device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 727162562Sjhb return; 728162562Sjhb } else if (req->ir_compcode != 0) { 729162562Sjhb device_printf(dev, 730162562Sjhb "Bad completion code for GET_DEVICE_ID: %d\n", 731162562Sjhb req->ir_compcode); 732162562Sjhb return; 733162562Sjhb } else if (req->ir_replylen < 5) { 734162562Sjhb device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 735162562Sjhb req->ir_replylen); 736162562Sjhb return; 737162562Sjhb } 738162562Sjhb 739220614Sru device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 740155517Sambrisko "version %d.%d\n", 741162562Sjhb req->ir_reply[1] & 0x0f, 742220614Sru req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 743162562Sjhb req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 744155517Sambrisko 745278321Sjhb IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 746162562Sjhb IPMI_CLEAR_FLAGS, 1, 0); 747162562Sjhb 748162562Sjhb ipmi_submit_driver_request(sc, req, 0); 749162562Sjhb 750162562Sjhb /* XXX: Magic numbers */ 751162562Sjhb if (req->ir_compcode == 0xc0) { 752155517Sambrisko device_printf(dev, "Clear flags is busy\n"); 753155517Sambrisko } 754162562Sjhb if (req->ir_compcode == 0xc1) { 755155517Sambrisko device_printf(dev, "Clear flags illegal\n"); 756155517Sambrisko } 757155517Sambrisko 758162562Sjhb for (i = 0; i < 8; i++) { 759278321Sjhb IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 760162562Sjhb IPMI_GET_CHANNEL_INFO, 1, 0); 761162562Sjhb req->ir_request[0] = i; 762162562Sjhb 763162562Sjhb ipmi_submit_driver_request(sc, req, 0); 764162562Sjhb 765278321Sjhb if (req->ir_compcode != 0) 766155517Sambrisko break; 767155517Sambrisko } 768155517Sambrisko device_printf(dev, "Number of channels %d\n", i); 769155517Sambrisko 770281941Sjhb /* 771281941Sjhb * Probe for watchdog, but only for backends which support 772281941Sjhb * polled driver requests. 773281941Sjhb */ 774281941Sjhb if (sc->ipmi_driver_requests_polled) { 775281941Sjhb IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 776281941Sjhb IPMI_GET_WDOG, 0, 0); 777162562Sjhb 778281941Sjhb ipmi_submit_driver_request(sc, req, 0); 779162562Sjhb 780281941Sjhb if (req->ir_compcode == 0x00) { 781281941Sjhb device_printf(dev, "Attached watchdog\n"); 782281941Sjhb /* register the watchdog event handler */ 783281941Sjhb sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 784281941Sjhb watchdog_list, ipmi_wd_event, sc, 0); 785281941Sjhb } 786155517Sambrisko } 787155517Sambrisko 788182322Sjhb sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 789162562Sjhb UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 790182322Sjhb if (sc->ipmi_cdev == NULL) { 791162562Sjhb device_printf(dev, "Failed to create cdev\n"); 792162562Sjhb return; 793162562Sjhb } 794182322Sjhb sc->ipmi_cdev->si_drv1 = sc; 795162562Sjhb} 796162562Sjhb 797162562Sjhbint 798162562Sjhbipmi_attach(device_t dev) 799162562Sjhb{ 800162562Sjhb struct ipmi_softc *sc = device_get_softc(dev); 801162562Sjhb int error; 802162562Sjhb 803162562Sjhb if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 804162562Sjhb error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 805166901Spiso NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 806162562Sjhb if (error) { 807162562Sjhb device_printf(dev, "can't set up interrupt\n"); 808162562Sjhb return (error); 809162562Sjhb } 810162562Sjhb } 811162562Sjhb 812162562Sjhb bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 813162562Sjhb sc->ipmi_ich.ich_func = ipmi_startup; 814162562Sjhb sc->ipmi_ich.ich_arg = sc; 815162562Sjhb if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 816162562Sjhb device_printf(dev, "can't establish configuration hook\n"); 817162562Sjhb return (ENOMEM); 818162562Sjhb } 819162562Sjhb 820155517Sambrisko ipmi_attached = 1; 821162562Sjhb return (0); 822155517Sambrisko} 823155517Sambrisko 824155517Sambriskoint 825155517Sambriskoipmi_detach(device_t dev) 826155517Sambrisko{ 827155517Sambrisko struct ipmi_softc *sc; 828155517Sambrisko 829155517Sambrisko sc = device_get_softc(dev); 830162562Sjhb 831162562Sjhb /* Fail if there are any open handles. */ 832162562Sjhb IPMI_LOCK(sc); 833182322Sjhb if (sc->ipmi_opened) { 834162562Sjhb IPMI_UNLOCK(sc); 835162562Sjhb return (EBUSY); 836162562Sjhb } 837162562Sjhb IPMI_UNLOCK(sc); 838182322Sjhb if (sc->ipmi_cdev) 839182322Sjhb destroy_dev(sc->ipmi_cdev); 840162562Sjhb 841162562Sjhb /* Detach from watchdog handling and turn off watchdog. */ 842162562Sjhb if (sc->ipmi_watchdog_tag) { 843162562Sjhb EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 844162562Sjhb ipmi_set_watchdog(sc, 0); 845162562Sjhb } 846162562Sjhb 847162562Sjhb /* XXX: should use shutdown callout I think. */ 848162562Sjhb /* If the backend uses a kthread, shut it down. */ 849162562Sjhb IPMI_LOCK(sc); 850162562Sjhb sc->ipmi_detaching = 1; 851162562Sjhb if (sc->ipmi_kthread) { 852162562Sjhb cv_broadcast(&sc->ipmi_request_added); 853278321Sjhb msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 854278321Sjhb "ipmi_wait", 0); 855162562Sjhb } 856162562Sjhb IPMI_UNLOCK(sc); 857162562Sjhb if (sc->ipmi_irq) 858162562Sjhb bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 859162562Sjhb 860162562Sjhb ipmi_release_resources(dev); 861278321Sjhb mtx_destroy(&sc->ipmi_io_lock); 862278321Sjhb mtx_destroy(&sc->ipmi_requests_lock); 863162562Sjhb return (0); 864155517Sambrisko} 865155517Sambrisko 866162562Sjhbvoid 867162562Sjhbipmi_release_resources(device_t dev) 868162562Sjhb{ 869162562Sjhb struct ipmi_softc *sc; 870162562Sjhb int i; 871162562Sjhb 872162562Sjhb sc = device_get_softc(dev); 873162562Sjhb if (sc->ipmi_irq) 874162562Sjhb bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 875162562Sjhb if (sc->ipmi_irq_res) 876162562Sjhb bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 877162562Sjhb sc->ipmi_irq_res); 878162562Sjhb for (i = 0; i < MAX_RES; i++) 879162562Sjhb if (sc->ipmi_io_res[i]) 880162562Sjhb bus_release_resource(dev, sc->ipmi_io_type, 881162562Sjhb sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 882162562Sjhb} 883162562Sjhb 884162562Sjhbdevclass_t ipmi_devclass; 885162562Sjhb 886162562Sjhb/* XXX: Why? */ 887162562Sjhbstatic void 888162562Sjhbipmi_unload(void *arg) 889162562Sjhb{ 890162562Sjhb device_t * devs; 891162562Sjhb int count; 892162562Sjhb int i; 893162562Sjhb 894162664Sjhb if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 895162664Sjhb return; 896162562Sjhb for (i = 0; i < count; i++) 897162562Sjhb device_delete_child(device_get_parent(devs[i]), devs[i]); 898162664Sjhb free(devs, M_TEMP); 899162562Sjhb} 900162562SjhbSYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 901162562Sjhb 902155605Sambrisko#ifdef IMPI_DEBUG 903155517Sambriskostatic void 904162562Sjhbdump_buf(u_char *data, int len) 905162562Sjhb{ 906155517Sambrisko char buf[20]; 907155517Sambrisko char line[1024]; 908155517Sambrisko char temp[30]; 909155517Sambrisko int count = 0; 910155517Sambrisko int i=0; 911155517Sambrisko 912155517Sambrisko printf("Address %p len %d\n", data, len); 913155517Sambrisko if (len > 256) 914155517Sambrisko len = 256; 915155517Sambrisko line[0] = '\000'; 916155517Sambrisko for (; len > 0; len--, data++) { 917155517Sambrisko sprintf(temp, "%02x ", *data); 918155517Sambrisko strcat(line, temp); 919155517Sambrisko if (*data >= ' ' && *data <= '~') 920155517Sambrisko buf[count] = *data; 921155517Sambrisko else if (*data >= 'A' && *data <= 'Z') 922155517Sambrisko buf[count] = *data; 923155517Sambrisko else 924155517Sambrisko buf[count] = '.'; 925155517Sambrisko if (++count == 16) { 926155517Sambrisko buf[count] = '\000'; 927155517Sambrisko count = 0; 928155517Sambrisko printf(" %3x %s %s\n", i, line, buf); 929155517Sambrisko i+=16; 930155517Sambrisko line[0] = '\000'; 931155517Sambrisko } 932155517Sambrisko } 933155517Sambrisko buf[count] = '\000'; 934155517Sambrisko 935155517Sambrisko for (; count != 16; count++) { 936155517Sambrisko strcat(line, " "); 937155517Sambrisko } 938155517Sambrisko printf(" %3x %s %s\n", i, line, buf); 939155517Sambrisko} 940155517Sambrisko#endif 941