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/mmcnull/mmcnull.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) 2013 Ilya Bakulin.  All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23  *
   24  */
   25 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include <sys/param.h>
   30 #include <sys/module.h>
   31 #include <sys/kernel.h>
   32 #include <sys/malloc.h>
   33 #include <sys/systm.h>
   34 #include <sys/lock.h>
   35 #include <sys/mutex.h>
   36 #include <sys/bus.h>
   37 #include <sys/endian.h>
   38 #include <sys/sysctl.h>
   39 
   40 #include <cam/cam.h>
   41 #include <cam/cam_ccb.h>
   42 #include <cam/cam_debug.h>
   43 #include <cam/cam_sim.h>
   44 #include <cam/cam_xpt_sim.h>
   45 #include <cam/scsi/scsi_all.h>
   46 
   47 static int is_sdio_mode = 1;
   48 
   49 struct mmcnull_softc {
   50         device_t dev;
   51         struct mtx sc_mtx;
   52 
   53         struct cam_devq         *devq;
   54         struct cam_sim          *sim;
   55         struct cam_path         *path;
   56 
   57         struct callout           tick;
   58         union ccb               *cur_ccb;
   59 };
   60 
   61 static void mmcnull_identify(driver_t *, device_t);
   62 static int  mmcnull_probe(device_t);
   63 static int  mmcnull_attach(device_t);
   64 static int  mmcnull_detach(device_t);
   65 static void mmcnull_action_sd(struct cam_sim *, union ccb *);
   66 static void mmcnull_action_sdio(struct cam_sim *, union ccb *);
   67 static void mmcnull_intr_sd(void *xsc);
   68 static void mmcnull_intr_sdio(void *xsc);
   69 static void mmcnull_poll(struct cam_sim *);
   70 
   71 static void
   72 mmcnull_identify(driver_t *driver, device_t parent)
   73 {
   74         device_t child;
   75 
   76         if (resource_disabled("mmcnull", 0))
   77                 return;
   78 
   79         if (device_get_unit(parent) != 0)
   80                 return;
   81 
   82         /* Avoid duplicates. */
   83         if (device_find_child(parent, "mmcnull", -1))
   84                 return;
   85 
   86         child = BUS_ADD_CHILD(parent, 20, "mmcnull", 0);
   87         if (child == NULL) {
   88                 device_printf(parent, "add MMCNULL child failed\n");
   89                 return;
   90         }
   91 }
   92 
   93 
   94 static int
   95 mmcnull_probe(device_t dev)
   96 {
   97         device_set_desc(dev, "Emulated MMC controller");
   98         return (BUS_PROBE_DEFAULT);
   99 }
  100 
  101 static int
  102 mmcnull_attach(device_t dev)
  103 {
  104         struct mmcnull_softc *sc;
  105         sim_action_func action_func;
  106 
  107         sc = device_get_softc(dev);
  108         sc->dev = dev;
  109 
  110         mtx_init(&sc->sc_mtx, "mmcnullmtx", NULL, MTX_DEF);
  111 
  112         if ((sc->devq = cam_simq_alloc(1)) == NULL)
  113                 return (ENOMEM);
  114 
  115         if (is_sdio_mode)
  116                 action_func = mmcnull_action_sdio;
  117         else
  118                 action_func = mmcnull_action_sd;
  119         sc->sim = cam_sim_alloc(action_func, mmcnull_poll, "mmcnull", sc,
  120                                 device_get_unit(dev), &sc->sc_mtx, 1, 1,
  121                                 sc->devq);
  122 
  123         if (sc->sim == NULL) {
  124                 cam_simq_free(sc->devq);
  125                 device_printf(dev, "cannot allocate CAM SIM\n");
  126                 return (EINVAL);
  127         }
  128 
  129         mtx_lock(&sc->sc_mtx);
  130         if (xpt_bus_register(sc->sim, dev, 0) != 0) {
  131                 device_printf(dev,
  132                               "cannot register SCSI pass-through bus\n");
  133                 cam_sim_free(sc->sim, FALSE);
  134                 cam_simq_free(sc->devq);
  135                 mtx_unlock(&sc->sc_mtx);
  136                 return (EINVAL);
  137         }
  138         mtx_unlock(&sc->sc_mtx);
  139 
  140         callout_init_mtx(&sc->tick, &sc->sc_mtx, 0);    /* Callout to emulate interrupts */
  141 
  142         device_printf(dev, "attached OK\n");
  143 
  144         return (0);
  145 }
  146 
  147 static int
  148 mmcnull_detach(device_t dev)
  149 {
  150         struct mmcnull_softc *sc;
  151 
  152         sc = device_get_softc(dev);
  153 
  154         if (sc == NULL)
  155                 return (EINVAL);
  156 
  157         if (sc->sim != NULL) {
  158                 mtx_lock(&sc->sc_mtx);
  159                 xpt_bus_deregister(cam_sim_path(sc->sim));
  160                 cam_sim_free(sc->sim, FALSE);
  161                 mtx_unlock(&sc->sc_mtx);
  162         }
  163 
  164         if (sc->devq != NULL)
  165                 cam_simq_free(sc->devq);
  166 
  167         callout_drain(&sc->tick);
  168         mtx_destroy(&sc->sc_mtx);
  169 
  170         device_printf(dev, "detached OK\n");
  171         return (0);
  172 }
  173 
  174 /*
  175  * The interrupt handler
  176  * This implementation calls it via callout(9)
  177  * with the mutex already taken
  178  */
  179 static void
  180 mmcnull_intr_sd(void *xsc) {
  181         struct mmcnull_softc *sc;
  182         union ccb *ccb;
  183         struct ccb_mmcio *mmcio;
  184 
  185         sc = (struct mmcnull_softc *) xsc;
  186         mtx_assert(&sc->sc_mtx, MA_OWNED);
  187 
  188         ccb = sc->cur_ccb;
  189         mmcio = &ccb->mmcio;
  190         device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
  191                       mmcio->cmd.opcode);
  192 
  193         switch (mmcio->cmd.opcode) {
  194         case MMC_GO_IDLE_STATE:
  195                 device_printf(sc->dev, "Reset device\n");
  196                 break;
  197         case SD_SEND_IF_COND:
  198                 mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
  199                 break;
  200         case MMC_APP_CMD:
  201                 mmcio->cmd.resp[0] = R1_APP_CMD;
  202                 break;
  203         case SD_SEND_RELATIVE_ADDR:
  204         case MMC_SELECT_CARD:
  205                 mmcio->cmd.resp[0] = 0x1 << 16;
  206                 break;
  207         case ACMD_SD_SEND_OP_COND:
  208                 mmcio->cmd.resp[0] = 0xc0ff8000;
  209                 mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
  210                 break;
  211         case MMC_ALL_SEND_CID:
  212                 /* Note: this is a real CID from Wandboard int mmc */
  213                 mmcio->cmd.resp[0] = 0x1b534d30;
  214                 mmcio->cmd.resp[1] = 0x30303030;
  215                 mmcio->cmd.resp[2] = 0x10842806;
  216                 mmcio->cmd.resp[3] = 0x5700e900;
  217                 break;
  218         case MMC_SEND_CSD:
  219                 /* Note: this is a real CSD from Wandboard int mmc */
  220                 mmcio->cmd.resp[0] = 0x400e0032;
  221                 mmcio->cmd.resp[1] = 0x5b590000;
  222                 mmcio->cmd.resp[2] = 0x751f7f80;
  223                 mmcio->cmd.resp[3] = 0x0a404000;
  224                 break;
  225         case MMC_READ_SINGLE_BLOCK:
  226         case MMC_READ_MULTIPLE_BLOCK:
  227                 strcpy(mmcio->cmd.data->data, "WTF?!");
  228                 break;
  229         default:
  230                 device_printf(sc->dev, "mmcnull_intr_sd: unknown command\n");
  231                 mmcio->cmd.error = 1;
  232         }
  233         ccb->ccb_h.status = CAM_REQ_CMP;
  234 
  235         sc->cur_ccb = NULL;
  236         xpt_done(ccb);
  237 }
  238 
  239 static void
  240 mmcnull_intr_sdio_newintr(void *xsc) {
  241         struct mmcnull_softc *sc;
  242         struct cam_path *dpath;
  243 
  244         sc = (struct mmcnull_softc *) xsc;
  245         mtx_assert(&sc->sc_mtx, MA_OWNED);
  246         device_printf(sc->dev, "mmcnull_intr_sdio_newintr()\n");
  247 
  248         /* Our path */
  249         if (xpt_create_path(&dpath, NULL, cam_sim_path(sc->sim), 0, 0) != CAM_REQ_CMP) {
  250                 device_printf(sc->dev, "mmcnull_intr_sdio_newintr(): cannot create path\n");
  251                 return;
  252         }
  253         xpt_async(AC_UNIT_ATTENTION, dpath, NULL);
  254         xpt_free_path(dpath);
  255 }
  256 
  257 static void
  258 mmcnull_intr_sdio(void *xsc) {
  259         struct mmcnull_softc *sc;
  260         union ccb *ccb;
  261         struct ccb_mmcio *mmcio;
  262 
  263         sc = (struct mmcnull_softc *) xsc;
  264         mtx_assert(&sc->sc_mtx, MA_OWNED);
  265 
  266         ccb = sc->cur_ccb;
  267         mmcio = &ccb->mmcio;
  268         device_printf(sc->dev, "mmcnull_intr: MMC command = %d\n",
  269                       mmcio->cmd.opcode);
  270 
  271         switch (mmcio->cmd.opcode) {
  272         case MMC_GO_IDLE_STATE:
  273                 device_printf(sc->dev, "Reset device\n");
  274                 break;
  275         case SD_SEND_IF_COND:
  276                 mmcio->cmd.resp[0] = 0x1AA; // To match mmc_xpt expectations :-)
  277                 break;
  278         case MMC_APP_CMD:
  279                 mmcio->cmd.resp[0] = R1_APP_CMD;
  280                 break;
  281         case IO_SEND_OP_COND:
  282                 mmcio->cmd.resp[0] = 0x12345678;
  283                 mmcio->cmd.resp[0] |= ~ R4_IO_MEM_PRESENT;
  284                 break;
  285         case SD_SEND_RELATIVE_ADDR:
  286         case MMC_SELECT_CARD:
  287                 mmcio->cmd.resp[0] = 0x1 << 16;
  288                 break;
  289         case ACMD_SD_SEND_OP_COND:
  290                 /* TODO: steal valid OCR from somewhere :-) */
  291                 mmcio->cmd.resp[0] = 0x123;
  292                 mmcio->cmd.resp[0] |= MMC_OCR_CARD_BUSY;
  293                 break;
  294         case MMC_ALL_SEND_CID:
  295                 mmcio->cmd.resp[0] = 0x1234;
  296                 mmcio->cmd.resp[1] = 0x5678;
  297                 mmcio->cmd.resp[2] = 0x9ABC;
  298                 mmcio->cmd.resp[3] = 0xDEF0;
  299                 break;
  300         case MMC_READ_SINGLE_BLOCK:
  301         case MMC_READ_MULTIPLE_BLOCK:
  302                 strcpy(mmcio->cmd.data->data, "WTF?!");
  303                 break;
  304         case SD_IO_RW_DIRECT:
  305                 device_printf(sc->dev, "Scheduling interrupt generation...\n");
  306                 callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio_newintr, sc);
  307                 break;
  308         default:
  309                 device_printf(sc->dev, "mmcnull_intr_sdio: unknown command\n");
  310         }
  311         ccb->ccb_h.status = CAM_REQ_CMP;
  312 
  313         sc->cur_ccb = NULL;
  314         xpt_done(ccb);
  315 }
  316 
  317 /*
  318  * This is a MMC IO handler
  319  * It extracts MMC command from CCB and sends it
  320  * to the h/w
  321  */
  322 static void
  323 mmcnull_handle_mmcio(struct cam_sim *sim, union ccb *ccb)
  324 {
  325         struct mmcnull_softc *sc;
  326         struct ccb_mmcio *mmcio;
  327 
  328         sc = cam_sim_softc(sim);
  329         mmcio = &ccb->mmcio;
  330         ccb->ccb_h.status = CAM_REQ_INPROG;
  331         sc->cur_ccb = ccb;
  332 
  333         /* Real h/w will wait for the interrupt */
  334         if (is_sdio_mode)
  335                 callout_reset(&sc->tick, hz / 10, mmcnull_intr_sdio, sc);
  336         else
  337                 callout_reset(&sc->tick, hz / 10, mmcnull_intr_sd, sc);
  338 }
  339 
  340 static void
  341 mmcnull_action_sd(struct cam_sim *sim, union ccb *ccb)
  342 {
  343         struct mmcnull_softc *sc;
  344 
  345         sc = cam_sim_softc(sim);
  346         if (sc == NULL) {
  347                 ccb->ccb_h.status = CAM_SEL_TIMEOUT;
  348                 xpt_done(ccb);
  349                 return;
  350         }
  351 
  352         mtx_assert(&sc->sc_mtx, MA_OWNED);
  353 
  354         device_printf(sc->dev, "action: func_code %0x\n", ccb->ccb_h.func_code);
  355 
  356         switch (ccb->ccb_h.func_code) {
  357         case XPT_PATH_INQ:
  358         {
  359                 struct ccb_pathinq *cpi;
  360 
  361                 cpi = &ccb->cpi;
  362                 cpi->version_num = 1;
  363                 cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_16;
  364                 cpi->target_sprt = 0;
  365                 cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN;
  366                 cpi->hba_eng_cnt = 0;
  367                 cpi->max_target = 0;
  368                 cpi->max_lun = 0;
  369                 cpi->initiator_id = 1;
  370                 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
  371                 strncpy(cpi->hba_vid, "FreeBSD Foundation", HBA_IDLEN);
  372                 strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
  373                 cpi->unit_number = cam_sim_unit(sim);
  374                 cpi->bus_id = cam_sim_bus(sim);
  375                 cpi->base_transfer_speed = 100; /* XXX WTF? */
  376                 cpi->protocol = PROTO_MMCSD;
  377                 cpi->protocol_version = SCSI_REV_0;
  378                 cpi->transport = XPORT_MMCSD;
  379                 cpi->transport_version = 0;
  380 
  381                 cpi->ccb_h.status = CAM_REQ_CMP;
  382                 break;
  383         }
  384         case XPT_GET_TRAN_SETTINGS:
  385         {
  386                 struct ccb_trans_settings *cts = &ccb->cts;
  387                 struct ccb_trans_settings_mmc *mcts;
  388                 mcts = &ccb->cts.proto_specific.mmc;
  389 
  390                 device_printf(sc->dev, "Got XPT_GET_TRAN_SETTINGS\n");
  391 
  392                 cts->protocol = PROTO_MMCSD;
  393                 cts->protocol_version = 0;
  394                 cts->transport = XPORT_MMCSD;
  395                 cts->transport_version = 0;
  396                 cts->xport_specific.valid = 0;
  397                 mcts->host_f_max = 12000000;
  398                 mcts->host_f_min = 200000;
  399                 mcts->host_ocr = 1; /* Fix this */
  400                 ccb->ccb_h.status = CAM_REQ_CMP;
  401                 break;
  402         }
  403         case XPT_SET_TRAN_SETTINGS:
  404                 device_printf(sc->dev, "Got XPT_SET_TRAN_SETTINGS, should update IOS...\n");
  405                 ccb->ccb_h.status = CAM_REQ_CMP;
  406                 break;
  407         case XPT_RESET_BUS:
  408                 device_printf(sc->dev, "Got XPT_RESET_BUS, ACK it...\n");
  409                 ccb->ccb_h.status = CAM_REQ_CMP;
  410                 break;
  411         case XPT_MMC_IO:
  412                 /*
  413                  * Here is the HW-dependent part of
  414                  * sending the command to the underlying h/w
  415                  * At some point in the future an interrupt comes.
  416                  * Then the request will be marked as completed.
  417                  */
  418                 device_printf(sc->dev, "Got XPT_MMC_IO\n");
  419                 mmcnull_handle_mmcio(sim, ccb);
  420                 return;
  421                 break;
  422         case XPT_RESET_DEV:
  423                 /* This is sent by `camcontrol reset`*/
  424                 device_printf(sc->dev, "Got XPT_RESET_DEV\n");
  425                 ccb->ccb_h.status = CAM_REQ_CMP;
  426                 break;
  427         default:
  428                 device_printf(sc->dev, "Func code %d is unknown\n", ccb->ccb_h.func_code);
  429                 ccb->ccb_h.status = CAM_REQ_INVALID;
  430                 break;
  431         }
  432         xpt_done(ccb);
  433         return;
  434 }
  435 
  436 static void
  437 mmcnull_action_sdio(struct cam_sim *sim, union ccb *ccb) {
  438         mmcnull_action_sd(sim, ccb);
  439 }
  440 
  441 static void
  442 mmcnull_poll(struct cam_sim *sim)
  443 {
  444         return;
  445 }
  446 
  447 
  448 static device_method_t mmcnull_methods[] = {
  449         /* Device interface */
  450         DEVMETHOD(device_identify,      mmcnull_identify),
  451         DEVMETHOD(device_probe,         mmcnull_probe),
  452         DEVMETHOD(device_attach,        mmcnull_attach),
  453         DEVMETHOD(device_detach,        mmcnull_detach),
  454         DEVMETHOD_END
  455 };
  456 
  457 static driver_t mmcnull_driver = {
  458         "mmcnull", mmcnull_methods, sizeof(struct mmcnull_softc)
  459 };
  460 
  461 DRIVER_MODULE(mmcnull, isa, mmcnull_driver, 0, 0);

Cache object: 54e1e7a62ca6a41e06006781928cd72d


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