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

Cache object: 68053a2144991f0204806f46852c5e88


[ 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.