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/mmc/mmcsd.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 Bernd Walter.  All rights reserved.
    3  * Copyright (c) 2006 M. Warner Losh.  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 ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  *
   25  * Portions of this software may have been developed with reference to
   26  * the SD Simplified Specification.  The following disclaimer may apply:
   27  *
   28  * The following conditions apply to the release of the simplified
   29  * specification ("Simplified Specification") by the SD Card Association and
   30  * the SD Group. The Simplified Specification is a subset of the complete SD
   31  * Specification which is owned by the SD Card Association and the SD
   32  * Group. This Simplified Specification is provided on a non-confidential
   33  * basis subject to the disclaimers below. Any implementation of the
   34  * Simplified Specification may require a license from the SD Card
   35  * Association, SD Group, SD-3C LLC or other third parties.
   36  *
   37  * Disclaimers:
   38  *
   39  * The information contained in the Simplified Specification is presented only
   40  * as a standard specification for SD Cards and SD Host/Ancillary products and
   41  * is provided "AS-IS" without any representations or warranties of any
   42  * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
   43  * Card Association for any damages, any infringements of patents or other
   44  * right of the SD Group, SD-3C LLC, the SD Card Association or any third
   45  * parties, which may result from its use. No license is granted by
   46  * implication, estoppel or otherwise under any patent or other rights of the
   47  * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
   48  * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
   49  * or the SD Card Association to disclose or distribute any technical
   50  * information, know-how or other confidential information to any third party.
   51  */
   52 
   53 #include <sys/cdefs.h>
   54 __FBSDID("$FreeBSD$");
   55 
   56 #include <sys/param.h>
   57 #include <sys/systm.h>
   58 #include <sys/bio.h>
   59 #include <sys/bus.h>
   60 #include <sys/conf.h>
   61 #include <sys/kernel.h>
   62 #include <sys/kthread.h>
   63 #include <sys/lock.h>
   64 #include <sys/malloc.h>
   65 #include <sys/module.h>
   66 #include <sys/mutex.h>
   67 #include <geom/geom_disk.h>
   68 
   69 #include <dev/mmc/mmcbrvar.h>
   70 #include <dev/mmc/mmcreg.h>
   71 #include <dev/mmc/mmcvar.h>
   72 
   73 #include "mmcbus_if.h"
   74 
   75 #if __FreeBSD_version < 800002
   76 #define kproc_create    kthread_create
   77 #define kproc_exit      kthread_exit
   78 #endif
   79 
   80 struct mmcsd_softc {
   81         device_t dev;
   82         struct mtx sc_mtx;
   83         struct disk *disk;
   84         struct proc *p;
   85         struct bio_queue_head bio_queue;
   86         daddr_t eblock, eend;   /* Range remaining after the last erase. */
   87         int running;
   88         int suspend;
   89 };
   90 
   91 /* bus entry points */
   92 static int mmcsd_attach(device_t dev);
   93 static int mmcsd_detach(device_t dev);
   94 static int mmcsd_probe(device_t dev);
   95 
   96 /* disk routines */
   97 static int mmcsd_close(struct disk *dp);
   98 static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
   99         off_t offset, size_t length);
  100 static int mmcsd_open(struct disk *dp);
  101 static void mmcsd_strategy(struct bio *bp);
  102 static void mmcsd_task(void *arg);
  103 
  104 static int mmcsd_bus_bit_width(device_t dev);
  105 static daddr_t mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp);
  106 static daddr_t mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp);
  107 
  108 #define MMCSD_LOCK(_sc)         mtx_lock(&(_sc)->sc_mtx)
  109 #define MMCSD_UNLOCK(_sc)       mtx_unlock(&(_sc)->sc_mtx)
  110 #define MMCSD_LOCK_INIT(_sc) \
  111         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
  112             "mmcsd", MTX_DEF)
  113 #define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
  114 #define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
  115 #define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  116 
  117 static int
  118 mmcsd_probe(device_t dev)
  119 {
  120 
  121         device_quiet(dev);
  122         device_set_desc(dev, "MMC/SD Memory Card");
  123         return (0);
  124 }
  125 
  126 static int
  127 mmcsd_attach(device_t dev)
  128 {
  129         struct mmcsd_softc *sc;
  130         struct disk *d;
  131         intmax_t mb;
  132         uint32_t speed;
  133         uint32_t maxblocks;
  134         char unit;
  135 
  136         sc = device_get_softc(dev);
  137         sc->dev = dev;
  138         MMCSD_LOCK_INIT(sc);
  139 
  140         d = sc->disk = disk_alloc();
  141         d->d_open = mmcsd_open;
  142         d->d_close = mmcsd_close;
  143         d->d_strategy = mmcsd_strategy;
  144         d->d_dump = mmcsd_dump;
  145         d->d_name = "mmcsd";
  146         d->d_drv1 = sc;
  147         d->d_maxsize = 4*1024*1024;     /* Maximum defined SD card AU size. */
  148         d->d_sectorsize = mmc_get_sector_size(dev);
  149         d->d_mediasize = (off_t)mmc_get_media_size(dev) * d->d_sectorsize;
  150         d->d_stripeoffset = 0;
  151         d->d_stripesize = mmc_get_erase_sector(dev) * d->d_sectorsize;
  152         d->d_unit = device_get_unit(dev);
  153         d->d_flags = DISKFLAG_CANDELETE;
  154         /*
  155          * Display in most natural units.  There's no cards < 1MB.
  156          * The SD standard goes to 2GiB, but the data format supports
  157          * up to 4GiB and some card makers push it up to this limit.
  158          * The SDHC standard only goes to 32GiB (the data format in
  159          * SDHC is good to 2TiB however, which isn't too ugly at
  160          * 2048GiBm, so we note it in passing here and don't add the
  161          * code to print TiB).
  162          */
  163         mb = d->d_mediasize >> 20;      /* 1MiB == 1 << 20 */
  164         unit = 'M';
  165         if (mb >= 10240) {              /* 1GiB = 1024 MiB */
  166                 unit = 'G';
  167                 mb /= 1024;
  168         }
  169         /*
  170          * Report the clock speed of the underlying hardware, which might be
  171          * different than what the card reports due to hardware limitations.
  172          * Report how many blocks the hardware transfers at once, but clip the
  173          * number to MAXPHYS since the system won't initiate larger transfers.
  174          */
  175         speed = mmcbr_get_clock(device_get_parent(dev));
  176         maxblocks = mmc_get_max_data(dev);
  177         if (maxblocks > MAXPHYS)
  178                 maxblocks = MAXPHYS;
  179         device_printf(dev, "%ju%cB <%s>%s at %s %d.%01dMHz/%dbit/%d-block\n",
  180             mb, unit, mmc_get_card_id_string(dev),
  181             mmc_get_read_only(dev) ? " (read-only)" : "",
  182             device_get_nameunit(device_get_parent(dev)),
  183             speed / 1000000, (speed / 100000) % 10,
  184             mmcsd_bus_bit_width(dev), maxblocks);
  185         disk_create(d, DISK_VERSION);
  186         bioq_init(&sc->bio_queue);
  187 
  188         sc->running = 1;
  189         sc->suspend = 0;
  190         sc->eblock = sc->eend = 0;
  191         kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
  192 
  193         return (0);
  194 }
  195 
  196 static int
  197 mmcsd_detach(device_t dev)
  198 {
  199         struct mmcsd_softc *sc = device_get_softc(dev);
  200 
  201         MMCSD_LOCK(sc);
  202         sc->suspend = 0;
  203         if (sc->running > 0) {
  204                 /* kill thread */
  205                 sc->running = 0;
  206                 wakeup(sc);
  207                 /* wait for thread to finish. */
  208                 while (sc->running != -1)
  209                         msleep(sc, &sc->sc_mtx, 0, "detach", 0);
  210         }
  211         MMCSD_UNLOCK(sc);
  212 
  213         /* Flush the request queue. */
  214         bioq_flush(&sc->bio_queue, NULL, ENXIO);
  215         /* kill disk */
  216         disk_destroy(sc->disk);
  217 
  218         MMCSD_LOCK_DESTROY(sc);
  219 
  220         return (0);
  221 }
  222 
  223 static int
  224 mmcsd_suspend(device_t dev)
  225 {
  226         struct mmcsd_softc *sc = device_get_softc(dev);
  227 
  228         MMCSD_LOCK(sc);
  229         sc->suspend = 1;
  230         if (sc->running > 0) {
  231                 /* kill thread */
  232                 sc->running = 0;
  233                 wakeup(sc);
  234                 /* wait for thread to finish. */
  235                 while (sc->running != -1)
  236                         msleep(sc, &sc->sc_mtx, 0, "detach", 0);
  237         }
  238         MMCSD_UNLOCK(sc);
  239         return (0);
  240 }
  241 
  242 static int
  243 mmcsd_resume(device_t dev)
  244 {
  245         struct mmcsd_softc *sc = device_get_softc(dev);
  246 
  247         MMCSD_LOCK(sc);
  248         sc->suspend = 0;
  249         if (sc->running <= 0) {
  250                 sc->running = 1;
  251                 MMCSD_UNLOCK(sc);
  252                 kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
  253         } else
  254                 MMCSD_UNLOCK(sc);
  255         return (0);
  256 }
  257 
  258 static int
  259 mmcsd_open(struct disk *dp)
  260 {
  261 
  262         return (0);
  263 }
  264 
  265 static int
  266 mmcsd_close(struct disk *dp)
  267 {
  268 
  269         return (0);
  270 }
  271 
  272 static void
  273 mmcsd_strategy(struct bio *bp)
  274 {
  275         struct mmcsd_softc *sc;
  276 
  277         sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1;
  278         MMCSD_LOCK(sc);
  279         if (sc->running > 0 || sc->suspend > 0) {
  280                 bioq_disksort(&sc->bio_queue, bp);
  281                 MMCSD_UNLOCK(sc);
  282                 wakeup(sc);
  283         } else {
  284                 MMCSD_UNLOCK(sc);
  285                 biofinish(bp, NULL, ENXIO);
  286         }
  287 }
  288 
  289 static daddr_t
  290 mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp)
  291 {
  292         daddr_t block, end;
  293         struct mmc_command cmd;
  294         struct mmc_command stop;
  295         struct mmc_request req;
  296         struct mmc_data data;
  297         device_t dev = sc->dev;
  298         int sz = sc->disk->d_sectorsize;
  299 
  300         block = bp->bio_pblkno;
  301         end = bp->bio_pblkno + (bp->bio_bcount / sz);
  302         while (block < end) {
  303                 char *vaddr = bp->bio_data +
  304                     (block - bp->bio_pblkno) * sz;
  305                 int numblocks = min(end - block, mmc_get_max_data(dev));
  306                 memset(&req, 0, sizeof(req));
  307                 memset(&cmd, 0, sizeof(cmd));
  308                 memset(&stop, 0, sizeof(stop));
  309                 req.cmd = &cmd;
  310                 cmd.data = &data;
  311                 if (bp->bio_cmd == BIO_READ) {
  312                         if (numblocks > 1)
  313                                 cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
  314                         else
  315                                 cmd.opcode = MMC_READ_SINGLE_BLOCK;
  316                 } else {
  317                         if (numblocks > 1)
  318                                 cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
  319                         else
  320                                 cmd.opcode = MMC_WRITE_BLOCK;
  321                 }
  322                 cmd.arg = block;
  323                 if (!mmc_get_high_cap(dev))
  324                         cmd.arg <<= 9;
  325                 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
  326                 data.data = vaddr;
  327                 data.mrq = &req;
  328                 if (bp->bio_cmd == BIO_READ)
  329                         data.flags = MMC_DATA_READ;
  330                 else
  331                         data.flags = MMC_DATA_WRITE;
  332                 data.len = numblocks * sz;
  333                 if (numblocks > 1) {
  334                         data.flags |= MMC_DATA_MULTI;
  335                         stop.opcode = MMC_STOP_TRANSMISSION;
  336                         stop.arg = 0;
  337                         stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
  338                         req.stop = &stop;
  339                 }
  340 //              printf("Len %d  %lld-%lld flags %#x sz %d\n",
  341 //                  (int)data.len, (long long)block, (long long)end, data.flags, sz);
  342                 MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  343                     &req);
  344                 if (req.cmd->error != MMC_ERR_NONE)
  345                         break;
  346                 block += numblocks;
  347         }
  348         return (block);
  349 }
  350 
  351 static daddr_t
  352 mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp)
  353 {
  354         daddr_t block, end, start, stop;
  355         struct mmc_command cmd;
  356         struct mmc_request req;
  357         device_t dev = sc->dev;
  358         int sz = sc->disk->d_sectorsize;
  359         int erase_sector;
  360 
  361         block = bp->bio_pblkno;
  362         end = bp->bio_pblkno + (bp->bio_bcount / sz);
  363         /* Coalesce with part remaining from previous request. */
  364         if (block > sc->eblock && block <= sc->eend)
  365                 block = sc->eblock;
  366         if (end >= sc->eblock && end < sc->eend)
  367                 end = sc->eend;
  368         /* Safe round to the erase sector boundaries. */
  369         erase_sector = mmc_get_erase_sector(dev);
  370         start = block + erase_sector - 1;        /* Round up. */
  371         start -= start % erase_sector;
  372         stop = end;                             /* Round down. */
  373         stop -= end % erase_sector;      
  374         /* We can't erase area smaller then sector, store it for later. */
  375         if (start >= stop) {
  376                 sc->eblock = block;
  377                 sc->eend = end;
  378                 return (end);
  379         }
  380 
  381         /* Set erase start position. */
  382         memset(&req, 0, sizeof(req));
  383         memset(&cmd, 0, sizeof(cmd));
  384         req.cmd = &cmd;
  385         if (mmc_get_card_type(dev) == mode_sd)
  386                 cmd.opcode = SD_ERASE_WR_BLK_START;
  387         else
  388                 cmd.opcode = MMC_ERASE_GROUP_START;
  389         cmd.arg = start;
  390         if (!mmc_get_high_cap(dev))
  391                 cmd.arg <<= 9;
  392         cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
  393         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  394             &req);
  395         if (req.cmd->error != MMC_ERR_NONE) {
  396             printf("erase err1: %d\n", req.cmd->error);
  397             return (block);
  398         }
  399         /* Set erase stop position. */
  400         memset(&req, 0, sizeof(req));
  401         memset(&cmd, 0, sizeof(cmd));
  402         req.cmd = &cmd;
  403         if (mmc_get_card_type(dev) == mode_sd)
  404                 cmd.opcode = SD_ERASE_WR_BLK_END;
  405         else
  406                 cmd.opcode = MMC_ERASE_GROUP_END;
  407         cmd.arg = stop;
  408         if (!mmc_get_high_cap(dev))
  409                 cmd.arg <<= 9;
  410         cmd.arg--;
  411         cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
  412         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  413             &req);
  414         if (req.cmd->error != MMC_ERR_NONE) {
  415             printf("erase err2: %d\n", req.cmd->error);
  416             return (block);
  417         }
  418         /* Erase range. */
  419         memset(&req, 0, sizeof(req));
  420         memset(&cmd, 0, sizeof(cmd));
  421         req.cmd = &cmd;
  422         cmd.opcode = MMC_ERASE;
  423         cmd.arg = 0;
  424         cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
  425         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  426             &req);
  427         if (req.cmd->error != MMC_ERR_NONE) {
  428             printf("erase err3 %d\n", req.cmd->error);
  429             return (block);
  430         }
  431         /* Store one of remaining parts for the next call. */
  432         if (bp->bio_pblkno >= sc->eblock || block == start) {
  433                 sc->eblock = stop;      /* Predict next forward. */
  434                 sc->eend = end;
  435         } else {
  436                 sc->eblock = block;     /* Predict next backward. */
  437                 sc->eend = start;
  438         }
  439         return (end);
  440 }
  441 
  442 static int
  443 mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
  444         off_t offset, size_t length)
  445 {
  446         struct disk *disk = arg;
  447         struct mmcsd_softc *sc = (struct mmcsd_softc *)disk->d_drv1;
  448         device_t dev = sc->dev;
  449         struct bio bp;
  450         daddr_t block, end;
  451 
  452         /* length zero is special and really means flush buffers to media */
  453         if (!length)
  454                 return (0);
  455 
  456         bzero(&bp, sizeof(struct bio));
  457         bp.bio_disk = disk;
  458         bp.bio_pblkno = offset / disk->d_sectorsize;
  459         bp.bio_bcount = length;
  460         bp.bio_data = virtual;
  461         bp.bio_cmd = BIO_WRITE;
  462         end = bp.bio_pblkno + bp.bio_bcount / sc->disk->d_sectorsize;
  463         MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
  464         block = mmcsd_rw(sc, &bp);
  465         MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
  466         return ((end < block) ? EIO : 0);
  467 }
  468 
  469 static void
  470 mmcsd_task(void *arg)
  471 {
  472         struct mmcsd_softc *sc = (struct mmcsd_softc*)arg;
  473         struct bio *bp;
  474         int sz;
  475         daddr_t block, end;
  476         device_t dev;
  477 
  478         dev = sc->dev;
  479         while (1) {
  480                 MMCSD_LOCK(sc);
  481                 do {
  482                         if (sc->running == 0)
  483                                 goto out;
  484                         bp = bioq_takefirst(&sc->bio_queue);
  485                         if (bp == NULL)
  486                                 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
  487                 } while (bp == NULL);
  488                 MMCSD_UNLOCK(sc);
  489                 if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) {
  490                         bp->bio_error = EROFS;
  491                         bp->bio_resid = bp->bio_bcount;
  492                         bp->bio_flags |= BIO_ERROR;
  493                         biodone(bp);
  494                         continue;
  495                 }
  496                 MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
  497                 sz = sc->disk->d_sectorsize;
  498                 block = bp->bio_pblkno;
  499                 end = bp->bio_pblkno + (bp->bio_bcount / sz);
  500                 if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
  501                         /* Access to the remaining erase block obsoletes it. */
  502                         if (block < sc->eend && end > sc->eblock)
  503                                 sc->eblock = sc->eend = 0;
  504                         block = mmcsd_rw(sc, bp);
  505                 } else if (bp->bio_cmd == BIO_DELETE) {
  506                         block = mmcsd_delete(sc, bp);
  507                 }
  508                 MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
  509                 if (block < end) {
  510                         bp->bio_error = EIO;
  511                         bp->bio_resid = (end - block) * sz;
  512                         bp->bio_flags |= BIO_ERROR;
  513                 }
  514                 biodone(bp);
  515         }
  516 out:
  517         /* tell parent we're done */
  518         sc->running = -1;
  519         MMCSD_UNLOCK(sc);
  520         wakeup(sc);
  521 
  522         kproc_exit(0);
  523 }
  524 
  525 static int
  526 mmcsd_bus_bit_width(device_t dev)
  527 {
  528 
  529         if (mmc_get_bus_width(dev) == bus_width_1)
  530                 return (1);
  531         if (mmc_get_bus_width(dev) == bus_width_4)
  532                 return (4);
  533         return (8);
  534 }
  535 
  536 static device_method_t mmcsd_methods[] = {
  537         DEVMETHOD(device_probe, mmcsd_probe),
  538         DEVMETHOD(device_attach, mmcsd_attach),
  539         DEVMETHOD(device_detach, mmcsd_detach),
  540         DEVMETHOD(device_suspend, mmcsd_suspend),
  541         DEVMETHOD(device_resume, mmcsd_resume),
  542         DEVMETHOD_END
  543 };
  544 
  545 static driver_t mmcsd_driver = {
  546         "mmcsd",
  547         mmcsd_methods,
  548         sizeof(struct mmcsd_softc),
  549 };
  550 static devclass_t mmcsd_devclass;
  551 
  552 DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, NULL, NULL);

Cache object: 508f14c25231db493ba829c7088bc78b


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