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  * Copyright (c) 1998, 2001 Nicolas Souchu
    3  * 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 AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 /*
   31  * I2C bus IP driver
   32  */
   33 
   34 #include <sys/param.h>
   35 #include <sys/systm.h>
   36 #include <sys/mbuf.h>
   37 #include <sys/socket.h>
   38 #include <sys/filio.h>
   39 #include <sys/sockio.h>
   40 #include <sys/kernel.h>
   41 #include <sys/lock.h>
   42 #include <sys/module.h>
   43 #include <sys/mutex.h>
   44 #include <sys/bus.h>
   45 #include <sys/time.h>
   46 #include <sys/malloc.h>
   47 
   48 #include <net/if.h>
   49 #include <net/if_types.h>
   50 #include <net/netisr.h>
   51 
   52 #include <net/route.h>
   53 #include <netinet/in.h>
   54 #include <netinet/in_systm.h>
   55 #include <netinet/in_var.h>
   56 #include <netinet/ip.h>
   57 #include <netinet/if_ether.h>
   58 
   59 #include <net/bpf.h>
   60 
   61 #include <dev/iicbus/iiconf.h>
   62 #include <dev/iicbus/iicbus.h>
   63 
   64 #include "iicbus_if.h"
   65 
   66 #define PCF_MASTER_ADDRESS 0xaa
   67 
   68 #define ICHDRLEN        sizeof(u_int32_t)
   69 #define ICMTU           1500            /* default mtu */
   70 
   71 struct ic_softc {
   72         struct ifnet *ic_ifp;
   73         device_t ic_dev;
   74 
   75         u_char ic_addr;                 /* peer I2C address */
   76 
   77         int ic_flags;
   78 
   79         char *ic_obuf;
   80         char *ic_ifbuf;
   81         char *ic_cp;
   82 
   83         int ic_xfercnt;
   84 
   85         int ic_iferrs;
   86 
   87         struct mtx ic_lock;
   88 };
   89 
   90 #define IC_SENDING              0x0001
   91 #define IC_OBUF_BUSY            0x0002
   92 #define IC_IFBUF_BUSY           0x0004
   93 #define IC_BUFFERS_BUSY         (IC_OBUF_BUSY | IC_IFBUF_BUSY)
   94 #define IC_BUFFER_WAITER        0x0004
   95 
   96 static devclass_t ic_devclass;
   97 
   98 static int icprobe(device_t);
   99 static int icattach(device_t);
  100 
  101 static int icioctl(struct ifnet *, u_long, caddr_t);
  102 static int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
  103                struct route *);
  104 
  105 static int icintr(device_t, int, char *);
  106 
  107 static device_method_t ic_methods[] = {
  108         /* device interface */
  109         DEVMETHOD(device_probe,         icprobe),
  110         DEVMETHOD(device_attach,        icattach),
  111 
  112         /* iicbus interface */
  113         DEVMETHOD(iicbus_intr,          icintr),
  114 
  115         { 0, 0 }
  116 };
  117 
  118 static driver_t ic_driver = {
  119         "ic",
  120         ic_methods,
  121         sizeof(struct ic_softc),
  122 };
  123 
  124 static void
  125 ic_alloc_buffers(struct ic_softc *sc, int mtu)
  126 {
  127         char *obuf, *ifbuf;
  128 
  129         obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
  130         ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
  131 
  132         mtx_lock(&sc->ic_lock);
  133         while (sc->ic_flags & IC_BUFFERS_BUSY) {
  134                 sc->ic_flags |= IC_BUFFER_WAITER;
  135                 mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
  136                 sc->ic_flags &= ~IC_BUFFER_WAITER;
  137         }
  138 
  139         free(sc->ic_obuf, M_DEVBUF);
  140         free(sc->ic_ifbuf, M_DEVBUF);
  141         sc->ic_obuf = obuf;
  142         sc->ic_ifbuf = ifbuf;
  143         sc->ic_ifp->if_mtu = mtu;
  144         mtx_unlock(&sc->ic_lock);
  145 }
  146 
  147 /*
  148  * icprobe()
  149  */
  150 static int
  151 icprobe(device_t dev)
  152 {
  153         return (BUS_PROBE_NOWILDCARD);
  154 }
  155 
  156 /*
  157  * icattach()
  158  */
  159 static int
  160 icattach(device_t dev)
  161 {
  162         struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
  163         struct ifnet *ifp;
  164 
  165         ifp = sc->ic_ifp = if_alloc(IFT_PARA);
  166         if (ifp == NULL)
  167                 return (ENOSPC);
  168 
  169         mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
  170             MTX_DEF);
  171         sc->ic_addr = PCF_MASTER_ADDRESS;       /* XXX only PCF masters */
  172         sc->ic_dev = dev;
  173 
  174         ifp->if_softc = sc;
  175         if_initname(ifp, device_get_name(dev), device_get_unit(dev));
  176         ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
  177         ifp->if_ioctl = icioctl;
  178         ifp->if_output = icoutput;
  179         ifp->if_hdrlen = 0;
  180         ifp->if_addrlen = 0;
  181         ifp->if_snd.ifq_maxlen = ifqmaxlen;
  182 
  183         ic_alloc_buffers(sc, ICMTU);
  184 
  185         if_attach(ifp);
  186 
  187         bpfattach(ifp, DLT_NULL, ICHDRLEN);
  188 
  189         return (0);
  190 }
  191 
  192 /*
  193  * iciotcl()
  194  */
  195 static int
  196 icioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
  197 {
  198         struct ic_softc *sc = ifp->if_softc;
  199         device_t icdev = sc->ic_dev;
  200         device_t parent = device_get_parent(icdev);
  201         struct ifaddr *ifa = (struct ifaddr *)data;
  202         struct ifreq *ifr = (struct ifreq *)data;
  203         int error;
  204 
  205         switch (cmd) {
  206 
  207         case SIOCSIFDSTADDR:
  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 == 0)
  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 occured 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                 sc->ic_ifp->if_ipackets++;
  306                 sc->ic_ifp->if_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                         mtx_unlock(&sc->ic_lock);
  311                         M_SETFIB(top, sc->ic_ifp->if_fib);
  312                         netisr_dispatch(NETISR_IP, top);
  313                         mtx_lock(&sc->ic_lock);
  314                 }
  315                 break;
  316         err:
  317                 if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
  318                 sc->ic_iferrs = 0;                      /* reset error count */
  319                 sc->ic_ifp->if_ierrors++;
  320                 break;
  321 
  322         case INTR_RECEIVE:
  323                 if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
  324                         sc->ic_iferrs++;
  325                 } else {
  326                         *sc->ic_cp++ = *ptr;
  327                         sc->ic_xfercnt++;
  328                 }
  329                 break;
  330 
  331         case INTR_NOACK:                        /* xfer terminated by master */
  332                 break;
  333 
  334         case INTR_TRANSMIT:
  335                 *ptr = 0xff;                                    /* XXX */
  336                 break;
  337 
  338         case INTR_ERROR:
  339                 sc->ic_iferrs++;
  340                 break;
  341 
  342         default:
  343                 panic("%s: unknown event (%d)!", __func__, event);
  344         }
  345 
  346         mtx_unlock(&sc->ic_lock);
  347         return (0);
  348 }
  349 
  350 /*
  351  * icoutput()
  352  */
  353 static int
  354 icoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
  355     struct route *ro)
  356 {
  357         struct ic_softc *sc = ifp->if_softc;
  358         device_t icdev = sc->ic_dev;
  359         device_t parent = device_get_parent(icdev);
  360         int len, sent;
  361         struct mbuf *mm;
  362         u_char *cp;
  363         u_int32_t hdr;
  364 
  365         /* BPF writes need to be handled specially. */ 
  366         if (dst->sa_family == AF_UNSPEC)
  367                 bcopy(dst->sa_data, &hdr, sizeof(hdr));
  368         else 
  369                 hdr = dst->sa_family;
  370 
  371         mtx_lock(&sc->ic_lock);
  372         ifp->if_drv_flags |= IFF_DRV_RUNNING;
  373 
  374         /* already sending? */
  375         if (sc->ic_flags & IC_SENDING) {
  376                 ifp->if_oerrors++;
  377                 goto error;
  378         }
  379                 
  380         /* insert header */
  381         bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
  382 
  383         cp = sc->ic_obuf + ICHDRLEN;
  384         len = 0;
  385         mm = m;
  386         do {
  387                 if (len + mm->m_len > sc->ic_ifp->if_mtu) {
  388                         /* packet too large */
  389                         ifp->if_oerrors++;
  390                         goto error;
  391                 }
  392                         
  393                 bcopy(mtod(mm,char *), cp, mm->m_len);
  394                 cp += mm->m_len;
  395                 len += mm->m_len;
  396 
  397         } while ((mm = mm->m_next));
  398 
  399         BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
  400 
  401         sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
  402 
  403         m_freem(m);
  404         mtx_unlock(&sc->ic_lock);
  405 
  406         /* send the packet */
  407         if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
  408                                 len + ICHDRLEN, &sent))
  409 
  410                 ifp->if_oerrors++;
  411         else {
  412                 ifp->if_opackets++;
  413                 ifp->if_obytes += len;
  414         }       
  415 
  416         mtx_lock(&sc->ic_lock);
  417         sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
  418         if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
  419             IC_BUFFER_WAITER)
  420                 wakeup(&sc);
  421         mtx_unlock(&sc->ic_lock);
  422 
  423         return (0);
  424 
  425 error:
  426         m_freem(m);
  427         mtx_unlock(&sc->ic_lock);
  428 
  429         return(0);
  430 }
  431 
  432 DRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
  433 MODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  434 MODULE_VERSION(ic, 1);

Cache object: 28a11a71ed62ae2a504db886e8f81162


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