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/iicbus/iic.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) 1998, 2001 Nicolas Souchu
    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  * $FreeBSD$
   29  *
   30  */
   31 #include <sys/param.h>
   32 #include <sys/bus.h>
   33 #include <sys/conf.h>
   34 #include <sys/fcntl.h>
   35 #include <sys/lock.h>
   36 #include <sys/kernel.h>
   37 #include <sys/malloc.h>
   38 #include <sys/module.h>
   39 #include <sys/sx.h>
   40 #include <sys/systm.h>
   41 #include <sys/uio.h>
   42 #include <sys/errno.h>
   43 
   44 #include <dev/iicbus/iiconf.h>
   45 #include <dev/iicbus/iicbus.h>
   46 #include <dev/iicbus/iic.h>
   47 
   48 #include "iicbus_if.h"
   49 
   50 struct iic_softc {
   51         device_t sc_dev;
   52         struct cdev *sc_devnode;
   53 };
   54 
   55 struct iic_cdevpriv {
   56         struct sx lock;
   57         struct iic_softc *sc;
   58         bool started;
   59         uint8_t addr;
   60 };
   61 
   62 
   63 #define IIC_LOCK(cdp)                   sx_xlock(&(cdp)->lock)
   64 #define IIC_UNLOCK(cdp)                 sx_xunlock(&(cdp)->lock)
   65 
   66 static MALLOC_DEFINE(M_IIC, "iic", "I2C device data");
   67 
   68 static int iic_probe(device_t);
   69 static int iic_attach(device_t);
   70 static int iic_detach(device_t);
   71 static void iic_identify(driver_t *driver, device_t parent);
   72 static void iicdtor(void *data);
   73 static int iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last);
   74 static int iicuio(struct cdev *dev, struct uio *uio, int ioflag);
   75 static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags);
   76 
   77 static device_method_t iic_methods[] = {
   78         /* device interface */
   79         DEVMETHOD(device_identify,      iic_identify),
   80         DEVMETHOD(device_probe,         iic_probe),
   81         DEVMETHOD(device_attach,        iic_attach),
   82         DEVMETHOD(device_detach,        iic_detach),
   83 
   84         /* iicbus interface */
   85         DEVMETHOD(iicbus_intr,          iicbus_generic_intr),
   86 
   87         { 0, 0 }
   88 };
   89 
   90 static driver_t iic_driver = {
   91         "iic",
   92         iic_methods,
   93         sizeof(struct iic_softc),
   94 };
   95 
   96 static  d_open_t        iicopen;
   97 static  d_ioctl_t       iicioctl;
   98 
   99 static struct cdevsw iic_cdevsw = {
  100         .d_version =    D_VERSION,
  101         .d_open =       iicopen,
  102         .d_read =       iicuio,
  103         .d_write =      iicuio,
  104         .d_ioctl =      iicioctl,
  105         .d_name =       "iic",
  106 };
  107 
  108 static void
  109 iic_identify(driver_t *driver, device_t parent)
  110 {
  111 
  112         if (device_find_child(parent, "iic", -1) == NULL)
  113                 BUS_ADD_CHILD(parent, 0, "iic", -1);
  114 }
  115 
  116 static int
  117 iic_probe(device_t dev)
  118 {
  119         if (iicbus_get_addr(dev) > 0)
  120                 return (ENXIO);
  121 
  122         device_set_desc(dev, "I2C generic I/O");
  123 
  124         return (0);
  125 }
  126         
  127 static int
  128 iic_attach(device_t dev)
  129 {
  130         struct iic_softc *sc;
  131 
  132         sc = device_get_softc(dev);
  133         sc->sc_dev = dev;
  134         sc->sc_devnode = make_dev(&iic_cdevsw, device_get_unit(dev),
  135                         UID_ROOT, GID_WHEEL,
  136                         0600, "iic%d", device_get_unit(dev));
  137         if (sc->sc_devnode == NULL) {
  138                 device_printf(dev, "failed to create character device\n");
  139                 return (ENXIO);
  140         }
  141         sc->sc_devnode->si_drv1 = sc;
  142 
  143         return (0);
  144 }
  145 
  146 static int
  147 iic_detach(device_t dev)
  148 {
  149         struct iic_softc *sc;
  150 
  151         sc = device_get_softc(dev);
  152 
  153         if (sc->sc_devnode)
  154                 destroy_dev(sc->sc_devnode);
  155 
  156         return (0);
  157 }
  158 
  159 static int
  160 iicopen(struct cdev *dev, int flags, int fmt, struct thread *td)
  161 {
  162         struct iic_cdevpriv *priv;
  163         int error;
  164 
  165         priv = malloc(sizeof(*priv), M_IIC, M_WAITOK | M_ZERO);
  166 
  167         sx_init(&priv->lock, "iic");
  168         priv->sc = dev->si_drv1;
  169 
  170         error = devfs_set_cdevpriv(priv, iicdtor); 
  171         if (error != 0)
  172                 free(priv, M_IIC);
  173 
  174         return (error);
  175 }
  176 
  177 static void
  178 iicdtor(void *data)
  179 {
  180         device_t iicdev, parent;
  181         struct iic_cdevpriv *priv;
  182 
  183         priv = data;
  184         KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!"));
  185 
  186         iicdev = priv->sc->sc_dev;
  187         parent = device_get_parent(iicdev);
  188 
  189         if (priv->started) {
  190                 iicbus_stop(parent);
  191                 iicbus_reset(parent, IIC_UNKNOWN, 0, NULL);
  192                 iicbus_release_bus(parent, iicdev);
  193         }
  194 
  195         sx_destroy(&priv->lock);
  196         free(priv, M_IIC);
  197 }
  198 
  199 static int
  200 iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last)
  201 {
  202         device_t parent;
  203         int error, num_bytes, transferred_bytes, written_bytes;
  204         char buffer[128];
  205 
  206         parent = device_get_parent(priv->sc->sc_dev);
  207         error = 0;
  208 
  209         /*
  210          * We can only transfer up to sizeof(buffer) bytes in 1 shot, so loop until
  211          * everything has been transferred.
  212         */
  213         while ((error == 0) && (uio->uio_resid > 0)) {
  214 
  215                 num_bytes = MIN(uio->uio_resid, sizeof(buffer));
  216                 transferred_bytes = 0;
  217 
  218                 if (uio->uio_rw == UIO_WRITE) {
  219                         error = uiomove(buffer, num_bytes, uio);
  220 
  221                         while ((error == 0) && (transferred_bytes < num_bytes)) {
  222                                 written_bytes = 0;
  223                                 error = iicbus_write(parent, &buffer[transferred_bytes],
  224                                     num_bytes - transferred_bytes, &written_bytes, 0);
  225                                 transferred_bytes += written_bytes;
  226                         }
  227                                 
  228                 } else if (uio->uio_rw == UIO_READ) {
  229                         error = iicbus_read(parent, buffer,
  230                             num_bytes, &transferred_bytes,
  231                             ((uio->uio_resid <= sizeof(buffer)) ? last : 0), 0);
  232                         if (error == 0)
  233                                 error = uiomove(buffer, transferred_bytes, uio);
  234                 }
  235         }
  236 
  237         return (error);
  238 }
  239 
  240 static int
  241 iicuio(struct cdev *dev, struct uio *uio, int ioflag)
  242 {
  243         device_t parent;
  244         struct iic_cdevpriv *priv;
  245         int error;
  246         uint8_t addr;
  247 
  248         priv = NULL;
  249         error = devfs_get_cdevpriv((void**)&priv);
  250 
  251         if (error != 0)
  252                 return (error);
  253         KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!"));
  254 
  255         IIC_LOCK(priv);
  256         if (priv->started || (priv->addr == 0)) {
  257                 IIC_UNLOCK(priv);
  258                 return (ENXIO);
  259         }
  260         parent = device_get_parent(priv->sc->sc_dev);
  261 
  262         error = iicbus_request_bus(parent, priv->sc->sc_dev,
  263             (ioflag & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR));
  264         if (error != 0) {
  265                 IIC_UNLOCK(priv);
  266                 return (error);
  267         }
  268 
  269         if (uio->uio_rw == UIO_READ)
  270                 addr = priv->addr | LSB;
  271         else
  272                 addr = priv->addr & ~LSB;
  273 
  274         error = iicbus_start(parent, addr, 0);
  275         if (error != 0)
  276         {
  277                 iicbus_release_bus(parent, priv->sc->sc_dev);
  278                 IIC_UNLOCK(priv);
  279                 return (error);
  280         }
  281 
  282         error = iicuio_move(priv, uio, IIC_LAST_READ);
  283 
  284         iicbus_stop(parent);
  285         iicbus_release_bus(parent, priv->sc->sc_dev);
  286         IIC_UNLOCK(priv);
  287         return (error);
  288 }
  289 
  290 static int
  291 iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags)
  292 {
  293         struct iic_msg *buf, *m;
  294         void **usrbufs;
  295         device_t iicdev, parent;
  296         int error;
  297         uint32_t i;
  298 
  299         iicdev = priv->sc->sc_dev;
  300         parent = device_get_parent(iicdev);
  301         error = 0;
  302 
  303         if (d->nmsgs > IIC_RDRW_MAX_MSGS)
  304                 return (EINVAL);
  305 
  306         buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_IIC, M_WAITOK);
  307 
  308         error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs);
  309         if (error != 0) {
  310                 free(buf, M_IIC);
  311                 return (error);
  312         }
  313 
  314         /* Alloc kernel buffers for userland data, copyin write data */
  315         usrbufs = malloc(sizeof(void *) * d->nmsgs, M_IIC, M_WAITOK | M_ZERO);
  316 
  317         for (i = 0; i < d->nmsgs; i++) {
  318                 m = &(buf[i]);
  319                 usrbufs[i] = m->buf;
  320 
  321                 /*
  322                  * At least init the buffer to NULL so we can safely free() it later.
  323                  * If the copyin() to buf failed, don't try to malloc bogus m->len.
  324                  */
  325                 m->buf = NULL;
  326                 if (error != 0)
  327                         continue;
  328 
  329                 /* m->len is uint16_t, so allocation size is capped at 64K. */
  330                 m->buf = malloc(m->len, M_IIC, M_WAITOK);
  331                 if (!(m->flags & IIC_M_RD))
  332                         error = copyin(usrbufs[i], m->buf, m->len);
  333         }
  334 
  335         if (error == 0)
  336                 error = iicbus_request_bus(parent, iicdev,
  337                     (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR));
  338 
  339         if (error == 0) {
  340                 error = iicbus_transfer(iicdev, buf, d->nmsgs);
  341                 iicbus_release_bus(parent, iicdev);
  342         }
  343 
  344         /* Copyout all read segments, free up kernel buffers */
  345         for (i = 0; i < d->nmsgs; i++) {
  346                 m = &(buf[i]);
  347                 if ((error == 0) && (m->flags & IIC_M_RD))
  348                         error = copyout(m->buf, usrbufs[i], m->len);
  349                 free(m->buf, M_IIC);
  350         }
  351 
  352         free(usrbufs, M_IIC);
  353         free(buf, M_IIC);
  354         return (error);
  355 }
  356 
  357 static int
  358 iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
  359 {
  360         device_t parent, iicdev;
  361         struct iiccmd *s;
  362         struct uio ubuf;
  363         struct iovec uvec;
  364         struct iic_cdevpriv *priv;
  365         int error;
  366 
  367         s = (struct iiccmd *)data;
  368         error = devfs_get_cdevpriv((void**)&priv);
  369         if (error != 0)
  370                 return (error);
  371 
  372         KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!"));
  373 
  374         iicdev = priv->sc->sc_dev;
  375         parent = device_get_parent(iicdev);
  376         IIC_LOCK(priv);
  377 
  378 
  379         switch (cmd) {
  380         case I2CSTART:
  381                 if (priv->started) {
  382                         error = EINVAL;
  383                         break;
  384                 }
  385                 error = iicbus_request_bus(parent, iicdev,
  386                     (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR));
  387 
  388                 if (error == 0)
  389                         error = iicbus_start(parent, s->slave, 0);
  390 
  391                 if (error == 0) {
  392                         priv->addr = s->slave;
  393                         priv->started = true;
  394                 } else
  395                         iicbus_release_bus(parent, iicdev);
  396 
  397                 break;
  398 
  399         case I2CSTOP:
  400                 if (priv->started) {
  401                         error = iicbus_stop(parent);
  402                         iicbus_release_bus(parent, iicdev);
  403                         priv->started = false;
  404                 }
  405 
  406                 break;
  407 
  408         case I2CRSTCARD:
  409                 /*
  410                  * Bus should be owned before we reset it.
  411                  * We allow the bus to be already owned as the result of an in-progress
  412                  * sequence; however, bus reset will always be followed by release
  413                  * (a new start is presumably needed for I/O anyway). */ 
  414                 if (!priv->started)     
  415                         error = iicbus_request_bus(parent, iicdev,
  416                             (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR));
  417 
  418                 if (error == 0) {
  419                         error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL);
  420                         /*
  421                          * Ignore IIC_ENOADDR as it only means we have a master-only
  422                          * controller.
  423                          */
  424                         if (error == IIC_ENOADDR)
  425                                 error = 0;
  426 
  427                         iicbus_release_bus(parent, iicdev);
  428                         priv->started = false;
  429                 }
  430                 break;
  431 
  432         case I2CWRITE:
  433                 if (!priv->started) {
  434                         error = EINVAL;
  435                         break;
  436                 }
  437                 uvec.iov_base = s->buf;
  438                 uvec.iov_len = s->count;
  439                 ubuf.uio_iov = &uvec;
  440                 ubuf.uio_iovcnt = 1;
  441                 ubuf.uio_segflg = UIO_USERSPACE;
  442                 ubuf.uio_td = td;
  443                 ubuf.uio_resid = s->count;
  444                 ubuf.uio_offset = 0;
  445                 ubuf.uio_rw = UIO_WRITE;
  446                 error = iicuio_move(priv, &ubuf, 0);
  447                 break;
  448 
  449         case I2CREAD:
  450                 if (!priv->started) {
  451                         error = EINVAL;
  452                         break;
  453                 }
  454                 uvec.iov_base = s->buf;
  455                 uvec.iov_len = s->count;
  456                 ubuf.uio_iov = &uvec;
  457                 ubuf.uio_iovcnt = 1;
  458                 ubuf.uio_segflg = UIO_USERSPACE;
  459                 ubuf.uio_td = td;
  460                 ubuf.uio_resid = s->count;
  461                 ubuf.uio_offset = 0;
  462                 ubuf.uio_rw = UIO_READ;
  463                 error = iicuio_move(priv, &ubuf, s->last);
  464                 break;
  465 
  466         case I2CRDWR:
  467                 /*
  468                  * The rdwr list should be a self-contained set of
  469                  * transactions.  Fail if another transaction is in progress.
  470                  */
  471                 if (priv->started) {
  472                         error = EINVAL;
  473                         break;
  474                 }
  475 
  476                 error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags);
  477 
  478                 break;
  479 
  480         case I2CRPTSTART:
  481                 if (!priv->started) {
  482                         error = EINVAL;
  483                         break;
  484                 }
  485                 error = iicbus_repeated_start(parent, s->slave, 0);
  486                 break;
  487 
  488         case I2CSADDR:
  489                 priv->addr = *((uint8_t*)data);
  490                 break;
  491 
  492         default:
  493                 error = ENOTTY;
  494         }
  495 
  496         IIC_UNLOCK(priv);
  497         return (error);
  498 }
  499 
  500 DRIVER_MODULE(iic, iicbus, iic_driver, 0, 0);
  501 MODULE_DEPEND(iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  502 MODULE_VERSION(iic, 1);

Cache object: 53d61f8f7d8d65b0aaf092a2363dd185


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