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/if_ic.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 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 /*
   33  * I2C bus IP driver
   34  */
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/socket.h>
   40 #include <sys/filio.h>
   41 #include <sys/sockio.h>
   42 #include <sys/kernel.h>
   43 #include <sys/lock.h>
   44 #include <sys/module.h>
   45 #include <sys/mutex.h>
   46 #include <sys/bus.h>
   47 #include <sys/time.h>
   48 #include <sys/malloc.h>
   49 
   50 #include <net/if.h>
   51 #include <net/if_var.h>
   52 #include <net/if_types.h>
   53 #include <net/netisr.h>
   54 
   55 #include <net/route.h>
   56 #include <netinet/in.h>
   57 #include <netinet/in_systm.h>
   58 #include <netinet/in_var.h>
   59 #include <netinet/ip.h>
   60 #include <netinet/if_ether.h>
   61 
   62 #include <net/bpf.h>
   63 
   64 #include <dev/iicbus/iiconf.h>
   65 #include <dev/iicbus/iicbus.h>
   66 
   67 #include "iicbus_if.h"
   68 
   69 #define PCF_MASTER_ADDRESS 0xaa
   70 
   71 #define ICHDRLEN        sizeof(u_int32_t)
   72 #define ICMTU           1500            /* default mtu */
   73 
   74 struct ic_softc {
   75         struct ifnet *ic_ifp;
   76         device_t ic_dev;
   77 
   78         u_char ic_addr;                 /* peer I2C address */
   79 
   80         int ic_flags;
   81 
   82         char *ic_obuf;
   83         char *ic_ifbuf;
   84         char *ic_cp;
   85 
   86         int ic_xfercnt;
   87 
   88         int ic_iferrs;
   89 
   90         struct mtx ic_lock;
   91 };
   92 
   93 #define IC_SENDING              0x0001
   94 #define IC_OBUF_BUSY            0x0002
   95 #define IC_IFBUF_BUSY           0x0004
   96 #define IC_BUFFERS_BUSY         (IC_OBUF_BUSY | IC_IFBUF_BUSY)
   97 #define IC_BUFFER_WAITER        0x0004
   98 
   99 static int icprobe(device_t);
  100 static int icattach(device_t);
  101 
  102 static int icioctl(struct ifnet *, u_long, caddr_t);
  103 static int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
  104                struct route *);
  105 
  106 static int icintr(device_t, int, char *);
  107 
  108 static device_method_t ic_methods[] = {
  109         /* device interface */
  110         DEVMETHOD(device_probe,         icprobe),
  111         DEVMETHOD(device_attach,        icattach),
  112 
  113         /* iicbus interface */
  114         DEVMETHOD(iicbus_intr,          icintr),
  115 
  116         { 0, 0 }
  117 };
  118 
  119 static driver_t ic_driver = {
  120         "ic",
  121         ic_methods,
  122         sizeof(struct ic_softc),
  123 };
  124 
  125 static void
  126 ic_alloc_buffers(struct ic_softc *sc, int mtu)
  127 {
  128         char *obuf, *ifbuf;
  129 
  130         obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
  131         ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
  132 
  133         mtx_lock(&sc->ic_lock);
  134         while (sc->ic_flags & IC_BUFFERS_BUSY) {
  135                 sc->ic_flags |= IC_BUFFER_WAITER;
  136                 mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
  137                 sc->ic_flags &= ~IC_BUFFER_WAITER;
  138         }
  139 
  140         free(sc->ic_obuf, M_DEVBUF);
  141         free(sc->ic_ifbuf, M_DEVBUF);
  142         sc->ic_obuf = obuf;
  143         sc->ic_ifbuf = ifbuf;
  144         sc->ic_ifp->if_mtu = mtu;
  145         mtx_unlock(&sc->ic_lock);
  146 }
  147 
  148 /*
  149  * icprobe()
  150  */
  151 static int
  152 icprobe(device_t dev)
  153 {
  154         return (BUS_PROBE_NOWILDCARD);
  155 }
  156 
  157 /*
  158  * icattach()
  159  */
  160 static int
  161 icattach(device_t dev)
  162 {
  163         struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
  164         struct ifnet *ifp;
  165 
  166         ifp = sc->ic_ifp = if_alloc(IFT_PARA);
  167         if (ifp == NULL)
  168                 return (ENOSPC);
  169 
  170         mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
  171             MTX_DEF);
  172         sc->ic_addr = PCF_MASTER_ADDRESS;       /* XXX only PCF masters */
  173         sc->ic_dev = dev;
  174 
  175         ifp->if_softc = sc;
  176         if_initname(ifp, device_get_name(dev), device_get_unit(dev));
  177         ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
  178         ifp->if_ioctl = icioctl;
  179         ifp->if_output = icoutput;
  180         ifp->if_hdrlen = 0;
  181         ifp->if_addrlen = 0;
  182         ifp->if_snd.ifq_maxlen = ifqmaxlen;
  183 
  184         ic_alloc_buffers(sc, ICMTU);
  185 
  186         if_attach(ifp);
  187 
  188         bpfattach(ifp, DLT_NULL, ICHDRLEN);
  189 
  190         return (0);
  191 }
  192 
  193 /*
  194  * iciotcl()
  195  */
  196 static int
  197 icioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  198 {
  199         struct ic_softc *sc = ifp->if_softc;
  200         device_t icdev = sc->ic_dev;
  201         device_t parent = device_get_parent(icdev);
  202         struct ifaddr *ifa = (struct ifaddr *)data;
  203         struct ifreq *ifr = (struct ifreq *)data;
  204         int error;
  205 
  206         switch (cmd) {
  207 
  208         case SIOCAIFADDR:
  209         case SIOCSIFADDR:
  210                 if (ifa->ifa_addr->sa_family != AF_INET)
  211                         return (EAFNOSUPPORT);
  212                 mtx_lock(&sc->ic_lock);
  213                 ifp->if_flags |= IFF_UP;
  214                 goto locked;
  215         case SIOCSIFFLAGS:
  216                 mtx_lock(&sc->ic_lock);
  217         locked:
  218                 if ((!(ifp->if_flags & IFF_UP)) &&
  219                     (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
  220 
  221                         /* XXX disable PCF */
  222                         ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
  223                         mtx_unlock(&sc->ic_lock);
  224 
  225                         /* IFF_UP is not set, try to release the bus anyway */
  226                         iicbus_release_bus(parent, icdev);
  227                         break;
  228                 }
  229                 if (((ifp->if_flags & IFF_UP)) &&
  230                     (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
  231                         mtx_unlock(&sc->ic_lock);
  232                         if ((error = iicbus_request_bus(parent, icdev,
  233                             IIC_WAIT | IIC_INTR)))
  234                                 return (error);
  235                         mtx_lock(&sc->ic_lock);
  236                         iicbus_reset(parent, IIC_FASTEST, 0, NULL);
  237                         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  238                 }
  239                 mtx_unlock(&sc->ic_lock);
  240                 break;
  241 
  242         case SIOCSIFMTU:
  243                 ic_alloc_buffers(sc, ifr->ifr_mtu);
  244                 break;
  245 
  246         case SIOCGIFMTU:
  247                 mtx_lock(&sc->ic_lock);
  248                 ifr->ifr_mtu = sc->ic_ifp->if_mtu;
  249                 mtx_unlock(&sc->ic_lock);
  250                 break;
  251 
  252         case SIOCADDMULTI:
  253         case SIOCDELMULTI:
  254                 if (ifr == NULL)
  255                         return (EAFNOSUPPORT);          /* XXX */
  256                 switch (ifr->ifr_addr.sa_family) {
  257                 case AF_INET:
  258                         break;
  259                 default:
  260                         return (EAFNOSUPPORT);
  261                 }
  262                 break;
  263         default:
  264                 return (EINVAL);
  265         }
  266         return (0);
  267 }
  268 
  269 /*
  270  * icintr()
  271  */
  272 static int
  273 icintr(device_t dev, int event, char *ptr)
  274 {
  275         struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
  276         struct mbuf *top;
  277         int len;
  278 
  279         mtx_lock(&sc->ic_lock);
  280 
  281         switch (event) {
  282 
  283         case INTR_GENERAL:
  284         case INTR_START:
  285                 sc->ic_cp = sc->ic_ifbuf;
  286                 sc->ic_xfercnt = 0;
  287                 sc->ic_flags |= IC_IFBUF_BUSY;
  288                 break;
  289 
  290         case INTR_STOP:
  291 
  292                 /* if any error occurred during transfert,
  293                  * drop the packet */
  294                 sc->ic_flags &= ~IC_IFBUF_BUSY;
  295                 if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
  296                     IC_BUFFER_WAITER)
  297                         wakeup(&sc);
  298                 if (sc->ic_iferrs)
  299                         goto err;
  300                 if ((len = sc->ic_xfercnt) == 0)
  301                         break;                                  /* ignore */
  302                 if (len <= ICHDRLEN)
  303                         goto err;
  304                 len -= ICHDRLEN;
  305                 if_inc_counter(sc->ic_ifp, IFCOUNTER_IPACKETS, 1);
  306                 if_inc_counter(sc->ic_ifp, IFCOUNTER_IBYTES, len);
  307                 BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
  308                 top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
  309                 if (top) {
  310                         struct epoch_tracker et;
  311 
  312                         mtx_unlock(&sc->ic_lock);
  313                         M_SETFIB(top, sc->ic_ifp->if_fib);
  314                         NET_EPOCH_ENTER(et);
  315                         netisr_dispatch(NETISR_IP, top);
  316                         NET_EPOCH_EXIT(et);
  317                         mtx_lock(&sc->ic_lock);
  318                 }
  319                 break;
  320         err:
  321                 if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
  322                 sc->ic_iferrs = 0;                      /* reset error count */
  323                 if_inc_counter(sc->ic_ifp, IFCOUNTER_IERRORS, 1);
  324                 break;
  325 
  326         case INTR_RECEIVE:
  327                 if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
  328                         sc->ic_iferrs++;
  329                 } else {
  330                         *sc->ic_cp++ = *ptr;
  331                         sc->ic_xfercnt++;
  332                 }
  333                 break;
  334 
  335         case INTR_NOACK:                        /* xfer terminated by master */
  336                 break;
  337 
  338         case INTR_TRANSMIT:
  339                 *ptr = 0xff;                                    /* XXX */
  340                 break;
  341 
  342         case INTR_ERROR:
  343                 sc->ic_iferrs++;
  344                 break;
  345 
  346         default:
  347                 panic("%s: unknown event (%d)!", __func__, event);
  348         }
  349 
  350         mtx_unlock(&sc->ic_lock);
  351         return (0);
  352 }
  353 
  354 /*
  355  * icoutput()
  356  */
  357 static int
  358 icoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
  359     struct route *ro)
  360 {
  361         struct ic_softc *sc = ifp->if_softc;
  362         device_t icdev = sc->ic_dev;
  363         device_t parent = device_get_parent(icdev);
  364         int len, sent;
  365         struct mbuf *mm;
  366         u_char *cp;
  367         u_int32_t hdr;
  368 
  369         /* BPF writes need to be handled specially. */ 
  370         if (dst->sa_family == AF_UNSPEC)
  371                 bcopy(dst->sa_data, &hdr, sizeof(hdr));
  372         else 
  373                 hdr = RO_GET_FAMILY(ro, dst);
  374 
  375         mtx_lock(&sc->ic_lock);
  376         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  377 
  378         /* already sending? */
  379         if (sc->ic_flags & IC_SENDING) {
  380                 if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  381                 goto error;
  382         }
  383                 
  384         /* insert header */
  385         bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
  386 
  387         cp = sc->ic_obuf + ICHDRLEN;
  388         len = 0;
  389         mm = m;
  390         do {
  391                 if (len + mm->m_len > sc->ic_ifp->if_mtu) {
  392                         /* packet too large */
  393                         if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  394                         goto error;
  395                 }
  396                         
  397                 bcopy(mtod(mm,char *), cp, mm->m_len);
  398                 cp += mm->m_len;
  399                 len += mm->m_len;
  400 
  401         } while ((mm = mm->m_next));
  402 
  403         BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
  404 
  405         sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
  406 
  407         m_freem(m);
  408         mtx_unlock(&sc->ic_lock);
  409 
  410         /* send the packet */
  411         if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
  412                                 len + ICHDRLEN, &sent))
  413 
  414                 if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
  415         else {
  416                 if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
  417                 if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
  418         }       
  419 
  420         mtx_lock(&sc->ic_lock);
  421         sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
  422         if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
  423             IC_BUFFER_WAITER)
  424                 wakeup(&sc);
  425         mtx_unlock(&sc->ic_lock);
  426 
  427         return (0);
  428 
  429 error:
  430         m_freem(m);
  431         mtx_unlock(&sc->ic_lock);
  432 
  433         return(0);
  434 }
  435 
  436 DRIVER_MODULE(ic, iicbus, ic_driver, 0, 0);
  437 MODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  438 MODULE_VERSION(ic, 1);

Cache object: d19ed27aa06a1cbcc902388236f4c434


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