ipmi_ssif.c revision 167086
1/*- 2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/ipmi/ipmi_ssif.c 167086 2007-02-27 17:23:29Z jhb $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/bus.h> 33#include <sys/condvar.h> 34#include <sys/eventhandler.h> 35#include <sys/kernel.h> 36#include <sys/kthread.h> 37#include <sys/module.h> 38#include <sys/selinfo.h> 39 40#include <dev/smbus/smbconf.h> 41#include <dev/smbus/smb.h> 42 43#include "smbus_if.h" 44 45#ifdef LOCAL_MODULE 46#include <ipmivars.h> 47#else 48#include <dev/ipmi/ipmivars.h> 49#endif 50 51#define SMBUS_WRITE_SINGLE 0x02 52#define SMBUS_WRITE_START 0x06 53#define SMBUS_WRITE_CONT 0x07 54#define SMBUS_READ_START 0x03 55#define SMBUS_READ_CONT 0x09 56#define SMBUS_DATA_SIZE 32 57 58#ifdef SSIF_DEBUG 59static void 60dump_buffer(device_t dev, const char *msg, u_char *bytes, int len) 61{ 62 int i; 63 64 device_printf(dev, "%s:", msg); 65 for (i = 0; i < len; i++) 66 printf(" %02x", bytes[i]); 67 printf("\n"); 68} 69#endif 70 71static int 72ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 73{ 74 u_char ssif_buf[SMBUS_DATA_SIZE]; 75 device_t dev = sc->ipmi_dev; 76 device_t smbus = sc->ipmi_ssif_smbus; 77 u_char *cp, block, count, offset; 78 size_t len; 79 int error; 80 81 /* Acquire the bus while we send the request. */ 82 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 83 return (0); 84 85 /* 86 * First, send out the request. Begin by filling out the first 87 * packet which includes the NetFn/LUN and command. 88 */ 89 ssif_buf[0] = req->ir_addr; 90 ssif_buf[1] = req->ir_command; 91 if (req->ir_requestlen > 0) 92 bcopy(req->ir_request, &ssif_buf[2], 93 min(req->ir_requestlen, SMBUS_DATA_SIZE - 2)); 94 95 /* Small requests are sent with a single command. */ 96 if (req->ir_requestlen <= 30) { 97#ifdef SSIF_DEBUG 98 dump_buffer(dev, "WRITE_SINGLE", ssif_buf, 99 req->ir_requestlen + 2); 100#endif 101 error = smbus_error(smbus_bwrite(smbus, 102 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE, 103 req->ir_requestlen + 2, ssif_buf)); 104 if (error) { 105#ifdef SSIF_ERROR_DEBUG 106 device_printf(dev, "SSIF: WRITE_SINGLE error %d\n", 107 error); 108#endif 109 goto fail; 110 } 111 } else { 112 /* Longer requests are sent out in 32-byte messages. */ 113#ifdef SSIF_DEBUG 114 dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE); 115#endif 116 error = smbus_error(smbus_bwrite(smbus, 117 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START, 118 SMBUS_DATA_SIZE, ssif_buf)); 119 if (error) { 120#ifdef SSIF_ERROR_DEBUG 121 device_printf(dev, "SSIF: WRITE_START error %d\n", 122 error); 123#endif 124 goto fail; 125 } 126 127 len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2); 128 cp = req->ir_request + (SMBUS_DATA_SIZE - 2); 129 while (len > 0) { 130#ifdef SSIF_DEBUG 131 dump_buffer(dev, "WRITE_CONT", cp, 132 min(len, SMBUS_DATA_SIZE)); 133#endif 134 error = smbus_error(smbus_bwrite(smbus, 135 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 136 min(len, SMBUS_DATA_SIZE), cp)); 137 if (error) { 138#ifdef SSIF_ERROR_DEBUG 139 device_printf(dev, "SSIF: WRITE_CONT error %d\n", 140 error); 141#endif 142 goto fail; 143 } 144 cp += SMBUS_DATA_SIZE; 145 len -= SMBUS_DATA_SIZE; 146 } 147 148 /* 149 * The final WRITE_CONT transaction has to have a non-zero 150 * length that is also not SMBUS_DATA_SIZE. If our last 151 * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE 152 * bytes, then len will be 0, and we send an extra 0x00 byte 153 * to terminate the transaction. 154 */ 155 if (len == 0) { 156 char c = 0; 157 158#ifdef SSIF_DEBUG 159 dump_buffer(dev, "WRITE_CONT", &c, 1); 160#endif 161 error = smbus_error(smbus_bwrite(smbus, 162 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 163 1, &c)); 164 if (error) { 165#ifdef SSIF_ERROR_DEBUG 166 device_printf(dev, "SSIF: WRITE_CONT error %d\n", 167 error); 168#endif 169 goto fail; 170 } 171 } 172 } 173 174 /* Release the bus. */ 175 smbus_release_bus(smbus, dev); 176 177 /* Give the BMC 100ms to chew on the request. */ 178 pause("ssifwt", hz / 10); 179 180 /* Try to read the first packet. */ 181read_start: 182 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) 183 return (0); 184 count = SMBUS_DATA_SIZE; 185 error = smbus_error(smbus_bread(smbus, 186 sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf)); 187 if (error == ENXIO || error == EBUSY) { 188 smbus_release_bus(smbus, dev); 189#ifdef SSIF_DEBUG 190 device_printf(dev, "SSIF: READ_START retry\n"); 191#endif 192 /* Give the BMC another 10ms. */ 193 pause("ssifwt", hz / 100); 194 goto read_start; 195 } 196 if (error) { 197#ifdef SSIF_ERROR_DEBUG 198 device_printf(dev, "SSIF: READ_START failed: %d\n", error); 199#endif 200 goto fail; 201 } 202#ifdef SSIF_DEBUG 203 device_printf("SSIF: READ_START: ok\n"); 204#endif 205 206 /* 207 * If this is the first part of a multi-part read, then we need to 208 * skip the first two bytes. 209 */ 210 if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1) 211 offset = 2; 212 else 213 offset = 0; 214 215 /* We had better get the reply header. */ 216 if (count < 3) { 217 device_printf(dev, "SSIF: Short reply packet\n"); 218 goto fail; 219 } 220 221 /* Verify the NetFn/LUN. */ 222 if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) { 223 device_printf(dev, "SSIF: Reply address mismatch\n"); 224 goto fail; 225 } 226 227 /* Verify the command. */ 228 if (ssif_buf[offset + 1] != req->ir_command) { 229 device_printf(dev, "SMIC: Command mismatch\n"); 230 goto fail; 231 } 232 233 /* Read the completion code. */ 234 req->ir_compcode = ssif_buf[offset + 2]; 235 236 /* If this is a single read, just copy the data and return. */ 237 if (offset == 0) { 238#ifdef SSIF_DEBUG 239 dump_buffer(dev, "READ_SINGLE", ssif_buf, count); 240#endif 241 len = count - 3; 242 bcopy(&ssif_buf[3], req->ir_reply, 243 min(req->ir_replybuflen, len)); 244 goto done; 245 } 246 247 /* 248 * This is the first part of a multi-read transaction, so copy 249 * out the payload and start looping. 250 */ 251#ifdef SSIF_DEBUG 252 dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2); 253#endif 254 bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5)); 255 len = count - 5; 256 block = 1; 257 258 for (;;) { 259 /* Read another packet via READ_CONT. */ 260 count = SMBUS_DATA_SIZE; 261 error = smbus_error(smbus_bread(smbus, 262 sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count, 263 ssif_buf)); 264 if (error) { 265#ifdef SSIF_ERROR_DEBUG 266 printf("SSIF: READ_CONT failed: %d\n", error); 267#endif 268 goto fail; 269 } 270#ifdef SSIF_DEBUG 271 device_printf(dev, "SSIF: READ_CONT... ok\n"); 272#endif 273 274 /* Verify the block number. 0xff marks the last block. */ 275 if (ssif_buf[0] != 0xff && ssif_buf[0] != block) { 276 device_printf(dev, "SSIF: Read wrong block %d %d\n", 277 ssif_buf[0], block); 278 goto fail; 279 } 280 if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) { 281 device_printf(dev, 282 "SSIF: Read short middle block, length %d\n", 283 count); 284 goto fail; 285 } 286#ifdef SSIF_DEBUG 287 if (ssif_buf[0] == 0xff) 288 dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1); 289 else 290 dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1); 291#endif 292 if (len < req->ir_replybuflen) 293 bcopy(&ssif_buf[1], &req->ir_reply[len], 294 min(req->ir_replybuflen - len, count - 1)); 295 len += count - 1; 296 297 /* If this was the last block we are done. */ 298 if (ssif_buf[0] != 0xff) 299 break; 300 block++; 301 } 302 303done: 304 /* Save the total length and return success. */ 305 req->ir_replylen = len; 306 smbus_release_bus(smbus, dev); 307 return (1); 308 309fail: 310 smbus_release_bus(smbus, dev); 311 return (0); 312} 313 314static void 315ssif_loop(void *arg) 316{ 317 struct ipmi_softc *sc = arg; 318 struct ipmi_request *req; 319 int i, ok; 320 321 IPMI_LOCK(sc); 322 while ((req = ipmi_dequeue_request(sc)) != NULL) { 323 IPMI_UNLOCK(sc); 324 ok = 0; 325 for (i = 0; i < 5; i++) { 326 ok = ssif_polled_request(sc, req); 327 if (ok) 328 break; 329 330 /* Wait 60 ms between retries. */ 331 pause("retry", 60 * hz / 1000); 332#ifdef SSIF_RETRY_DEBUG 333 device_printf(sc->ipmi_dev, 334 "SSIF: Retrying request (%d)\n", i + 1); 335#endif 336 } 337 if (ok) 338 req->ir_error = 0; 339 else 340 req->ir_error = EIO; 341 IPMI_LOCK(sc); 342 ipmi_complete_request(sc, req); 343 IPMI_UNLOCK(sc); 344 345 /* Enforce 10ms between requests. */ 346 pause("delay", hz / 100); 347 348 IPMI_LOCK(sc); 349 } 350 IPMI_UNLOCK(sc); 351 kthread_exit(0); 352} 353 354static int 355ssif_startup(struct ipmi_softc *sc) 356{ 357 358 return (kthread_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0, 359 "%s: ssif", device_get_nameunit(sc->ipmi_dev))); 360} 361 362int 363ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address) 364{ 365 366 /* Setup smbus address. */ 367 sc->ipmi_ssif_smbus = smbus; 368 sc->ipmi_ssif_smbus_address = smbus_address; 369 370 /* Setup function pointers. */ 371 sc->ipmi_startup = ssif_startup; 372 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 373 374 return (0); 375} 376