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