The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ipmi/ipmi_ssif.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/9.0/sys/dev/ipmi/ipmi_ssif.c 172836 2007-10-20 23:23:23Z julian $");
   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
   59 static void
   60 dump_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 
   71 static int
   72 ssif_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. */
  181 read_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 
  303 done:
  304         /* Save the total length and return success. */
  305         req->ir_replylen = len;
  306         smbus_release_bus(smbus, dev);
  307         return (1);
  308 
  309 fail:
  310         smbus_release_bus(smbus, dev);
  311         return (0);
  312 }
  313 
  314 static void
  315 ssif_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         kproc_exit(0);
  352 }
  353 
  354 static int
  355 ssif_startup(struct ipmi_softc *sc)
  356 {
  357 
  358         return (kproc_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0,
  359             "%s: ssif", device_get_nameunit(sc->ipmi_dev)));
  360 }
  361 
  362 int
  363 ipmi_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 }

Cache object: 52b0fbbef10a029a2c29f0c329963f3b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.