if_ic.c revision 207554
159243Sobrien/*- 2231990Smp * Copyright (c) 1998, 2001 Nicolas Souchu 359243Sobrien * All rights reserved. 459243Sobrien * 5131962Smp * Redistribution and use in source and binary forms, with or without 6131962Smp * modification, are permitted provided that the following conditions 7131962Smp * are met: 8145479Smp * 1. Redistributions of source code must retain the above copyright 9131962Smp * notice, this list of conditions and the following disclaimer. 10131962Smp * 2. Redistributions in binary form must reproduce the above copyright 11131962Smp * notice, this list of conditions and the following disclaimer in the 12131962Smp * documentation and/or other materials provided with the distribution. 13131962Smp * 14131962Smp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15131962Smp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16131962Smp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17131962Smp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18131962Smp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19131962Smp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20131962Smp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2459243Sobrien * SUCH DAMAGE. 2559243Sobrien */ 2659243Sobrien 2759243Sobrien#include <sys/cdefs.h> 2859243Sobrien__FBSDID("$FreeBSD: head/sys/dev/iicbus/if_ic.c 207554 2010-05-03 07:32:50Z sobomax $"); 2959243Sobrien 3059243Sobrien/* 3159243Sobrien * I2C bus IP driver 3259243Sobrien */ 3383098Smp 3459243Sobrien#include <sys/param.h> 3559243Sobrien#include <sys/systm.h> 3659243Sobrien#include <sys/mbuf.h> 3759243Sobrien#include <sys/socket.h> 3883098Smp#include <sys/filio.h> 3959243Sobrien#include <sys/sockio.h> 40100616Smp#include <sys/kernel.h> 41195609Smp#include <sys/lock.h> 42195609Smp#include <sys/module.h> 43195609Smp#include <sys/mutex.h> 4459243Sobrien#include <sys/bus.h> 4559243Sobrien#include <sys/time.h> 46195609Smp#include <sys/malloc.h> 47195609Smp 4859243Sobrien#include <net/if.h> 4959243Sobrien#include <net/if_types.h> 50195609Smp#include <net/netisr.h> 51195609Smp 52195609Smp#include <sys/mbuf.h> 53100616Smp#include <sys/socket.h> 54100616Smp#include <net/netisr.h> 5559243Sobrien#include <net/route.h> 5659243Sobrien#include <netinet/in.h> 5759243Sobrien#include <netinet/in_systm.h> 5859243Sobrien#include <netinet/in_var.h> 5959243Sobrien#include <netinet/ip.h> 6059243Sobrien#include <netinet/if_ether.h> 6159243Sobrien 6259243Sobrien#include <net/bpf.h> 6359243Sobrien 6459243Sobrien#include <dev/iicbus/iiconf.h> 6559243Sobrien#include <dev/iicbus/iicbus.h> 6659243Sobrien 6759243Sobrien#include "iicbus_if.h" 6859243Sobrien 6959243Sobrien#define PCF_MASTER_ADDRESS 0xaa 7059243Sobrien 7159243Sobrien#define ICHDRLEN sizeof(u_int32_t) 7259243Sobrien#define ICMTU 1500 /* default mtu */ 7359243Sobrien 7459243Sobrienstruct ic_softc { 7559243Sobrien struct ifnet *ic_ifp; 7659243Sobrien device_t ic_dev; 7759243Sobrien 7859243Sobrien u_char ic_addr; /* peer I2C address */ 7959243Sobrien 8059243Sobrien int ic_flags; 8159243Sobrien 8259243Sobrien char *ic_obuf; 8359243Sobrien char *ic_ifbuf; 8459243Sobrien char *ic_cp; 8559243Sobrien 8659243Sobrien int ic_xfercnt; 8759243Sobrien 8859243Sobrien int ic_iferrs; 8959243Sobrien 9059243Sobrien struct mtx ic_lock; 9159243Sobrien}; 9259243Sobrien 9359243Sobrien#define IC_SENDING 0x0001 9459243Sobrien#define IC_OBUF_BUSY 0x0002 9559243Sobrien#define IC_IFBUF_BUSY 0x0004 9659243Sobrien#define IC_BUFFERS_BUSY (IC_OBUF_BUSY | IC_IFBUF_BUSY) 9759243Sobrien#define IC_BUFFER_WAITER 0x0004 98145479Smp 99145479Smpstatic devclass_t ic_devclass; 100145479Smp 101145479Smpstatic int icprobe(device_t); 102145479Smpstatic int icattach(device_t); 10359243Sobrien 10459243Sobrienstatic int icioctl(struct ifnet *, u_long, caddr_t); 10559243Sobrienstatic int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 10659243Sobrien struct route *); 10759243Sobrien 10859243Sobrienstatic int icintr(device_t, int, char *); 10959243Sobrien 11059243Sobrienstatic device_method_t ic_methods[] = { 11159243Sobrien /* device interface */ 11259243Sobrien DEVMETHOD(device_probe, icprobe), 11359243Sobrien DEVMETHOD(device_attach, icattach), 11459243Sobrien 11559243Sobrien /* iicbus interface */ 11659243Sobrien DEVMETHOD(iicbus_intr, icintr), 11759243Sobrien 11859243Sobrien { 0, 0 } 11959243Sobrien}; 12059243Sobrien 12159243Sobrienstatic driver_t ic_driver = { 12259243Sobrien "ic", 12359243Sobrien ic_methods, 12459243Sobrien sizeof(struct ic_softc), 12559243Sobrien}; 12659243Sobrien 12759243Sobrienstatic void 12859243Sobrienic_alloc_buffers(struct ic_softc *sc, int mtu) 12959243Sobrien{ 13059243Sobrien char *obuf, *ifbuf; 13159243Sobrien 13259243Sobrien obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 13359243Sobrien ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK); 13459243Sobrien 13559243Sobrien mtx_lock(&sc->ic_lock); 13659243Sobrien while (sc->ic_flags & IC_BUFFERS_BUSY) { 13759243Sobrien sc->ic_flags |= IC_BUFFER_WAITER; 13859243Sobrien mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0); 13959243Sobrien sc->ic_flags &= ~IC_BUFFER_WAITER; 14059243Sobrien } 14159243Sobrien 14259243Sobrien free(sc->ic_obuf, M_DEVBUF); 14359243Sobrien free(sc->ic_ifbuf, M_DEVBUF); 14459243Sobrien sc->ic_obuf = obuf; 14559243Sobrien sc->ic_ifbuf = ifbuf; 14659243Sobrien sc->ic_ifp->if_mtu = mtu; 14759243Sobrien mtx_unlock(&sc->ic_lock); 14859243Sobrien} 14959243Sobrien 15059243Sobrien/* 15169408Sache * icprobe() 15259243Sobrien */ 15359243Sobrienstatic int 15459243Sobrienicprobe(device_t dev) 15559243Sobrien{ 15659243Sobrien return (BUS_PROBE_NOWILDCARD); 15759243Sobrien} 15859243Sobrien 15959243Sobrien/* 16059243Sobrien * icattach() 16159243Sobrien */ 16259243Sobrienstatic int 16359243Sobrienicattach(device_t dev) 16459243Sobrien{ 16559243Sobrien struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 16659243Sobrien struct ifnet *ifp; 16759243Sobrien 16859243Sobrien ifp = sc->ic_ifp = if_alloc(IFT_PARA); 16959243Sobrien if (ifp == NULL) 17059243Sobrien return (ENOSPC); 17159243Sobrien 17259243Sobrien mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 17359243Sobrien MTX_DEF); 17459243Sobrien sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ 17559243Sobrien sc->ic_dev = dev; 17659243Sobrien 17759243Sobrien ifp->if_softc = sc; 17859243Sobrien if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 17959243Sobrien ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 18059243Sobrien ifp->if_ioctl = icioctl; 18159243Sobrien ifp->if_output = icoutput; 182145479Smp ifp->if_hdrlen = 0; 183145479Smp ifp->if_addrlen = 0; 184145479Smp ifp->if_snd.ifq_maxlen = ifqmaxlen; 185145479Smp 186145479Smp ic_alloc_buffers(sc, ICMTU); 187145479Smp 188145479Smp if_attach(ifp); 189145479Smp 190145479Smp bpfattach(ifp, DLT_NULL, ICHDRLEN); 191145479Smp 192131962Smp return (0); 193145479Smp} 194145479Smp 195131962Smp/* 19659243Sobrien * iciotcl() 19759243Sobrien */ 19859243Sobrienstatic int 19959243Sobrienicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 20059243Sobrien{ 20159243Sobrien struct ic_softc *sc = ifp->if_softc; 20259243Sobrien device_t icdev = sc->ic_dev; 20359243Sobrien device_t parent = device_get_parent(icdev); 20459243Sobrien struct ifaddr *ifa = (struct ifaddr *)data; 20559243Sobrien struct ifreq *ifr = (struct ifreq *)data; 20659243Sobrien int error; 20759243Sobrien 20859243Sobrien switch (cmd) { 20959243Sobrien 21059243Sobrien case SIOCSIFDSTADDR: 21159243Sobrien case SIOCAIFADDR: 21259243Sobrien case SIOCSIFADDR: 21359243Sobrien if (ifa->ifa_addr->sa_family != AF_INET) 21459243Sobrien return (EAFNOSUPPORT); 21559243Sobrien mtx_lock(&sc->ic_lock); 21659243Sobrien ifp->if_flags |= IFF_UP; 21759243Sobrien goto locked; 21859243Sobrien case SIOCSIFFLAGS: 21959243Sobrien mtx_lock(&sc->ic_lock); 22059243Sobrien locked: 22159243Sobrien if ((!(ifp->if_flags & IFF_UP)) && 22259243Sobrien (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 22359243Sobrien 22459243Sobrien /* XXX disable PCF */ 22559243Sobrien ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 226131962Smp mtx_unlock(&sc->ic_lock); 227195609Smp 22859243Sobrien /* IFF_UP is not set, try to release the bus anyway */ 22959243Sobrien iicbus_release_bus(parent, icdev); 23059243Sobrien break; 23159243Sobrien } 23259243Sobrien if (((ifp->if_flags & IFF_UP)) && 23359243Sobrien (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) { 23459243Sobrien mtx_unlock(&sc->ic_lock); 23559243Sobrien if ((error = iicbus_request_bus(parent, icdev, 23659243Sobrien IIC_WAIT | IIC_INTR))) 23759243Sobrien return (error); 23859243Sobrien mtx_lock(&sc->ic_lock); 23959243Sobrien iicbus_reset(parent, IIC_FASTEST, 0, NULL); 24059243Sobrien ifp->if_drv_flags |= IFF_DRV_RUNNING; 24159243Sobrien } 24259243Sobrien mtx_unlock(&sc->ic_lock); 24359243Sobrien break; 24459243Sobrien 24559243Sobrien case SIOCSIFMTU: 24659243Sobrien ic_alloc_buffers(sc, ifr->ifr_mtu); 24759243Sobrien break; 24859243Sobrien 24959243Sobrien case SIOCGIFMTU: 250145479Smp mtx_lock(&sc->ic_lock); 251145479Smp ifr->ifr_mtu = sc->ic_ifp->if_mtu; 252145479Smp mtx_unlock(&sc->ic_lock); 25359243Sobrien break; 25459243Sobrien 25559243Sobrien case SIOCADDMULTI: 25659243Sobrien case SIOCDELMULTI: 25759243Sobrien if (ifr == 0) 25859243Sobrien return (EAFNOSUPPORT); /* XXX */ 25959243Sobrien switch (ifr->ifr_addr.sa_family) { 26059243Sobrien case AF_INET: 26159243Sobrien break; 262145479Smp default: 263131962Smp return (EAFNOSUPPORT); 264131962Smp } 265131962Smp break; 266131962Smp default: 267131962Smp return (EINVAL); 268131962Smp } 269131962Smp return (0); 270131962Smp} 271131962Smp 272131962Smp/* 273131962Smp * icintr() 274145479Smp */ 27559243Sobrienstatic int 27659243Sobrienicintr(device_t dev, int event, char *ptr) 27759243Sobrien{ 27859243Sobrien struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 27959243Sobrien struct mbuf *top; 28059243Sobrien int len; 28159243Sobrien 28259243Sobrien mtx_lock(&sc->ic_lock); 28359243Sobrien 28459243Sobrien switch (event) { 28559243Sobrien 28659243Sobrien case INTR_GENERAL: 28759243Sobrien case INTR_START: 28859243Sobrien sc->ic_cp = sc->ic_ifbuf; 289195609Smp sc->ic_xfercnt = 0; 29059243Sobrien sc->ic_flags |= IC_IFBUF_BUSY; 29159243Sobrien break; 29259243Sobrien 29359243Sobrien case INTR_STOP: 29459243Sobrien 29559243Sobrien /* if any error occured during transfert, 29659243Sobrien * drop the packet */ 29759243Sobrien sc->ic_flags &= ~IC_IFBUF_BUSY; 29859243Sobrien if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 29959243Sobrien IC_BUFFER_WAITER) 30059243Sobrien wakeup(&sc); 30159243Sobrien if (sc->ic_iferrs) 30259243Sobrien goto err; 30359243Sobrien if ((len = sc->ic_xfercnt) == 0) 30459243Sobrien break; /* ignore */ 30559243Sobrien if (len <= ICHDRLEN) 30659243Sobrien goto err; 30759243Sobrien len -= ICHDRLEN; 30859243Sobrien sc->ic_ifp->if_ipackets++; 30959243Sobrien sc->ic_ifp->if_ibytes += len; 31059243Sobrien BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN); 31159243Sobrien top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0); 31259243Sobrien if (top) { 31359243Sobrien mtx_unlock(&sc->ic_lock); 31459243Sobrien netisr_dispatch(NETISR_IP, top); 31559243Sobrien mtx_lock(&sc->ic_lock); 31659243Sobrien } 31759243Sobrien break; 31859243Sobrien err: 31959243Sobrien if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs); 32059243Sobrien sc->ic_iferrs = 0; /* reset error count */ 32159243Sobrien sc->ic_ifp->if_ierrors++; 32259243Sobrien break; 32359243Sobrien 32459243Sobrien case INTR_RECEIVE: 32559243Sobrien if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) { 32659243Sobrien sc->ic_iferrs++; 32759243Sobrien } else { 32859243Sobrien *sc->ic_cp++ = *ptr; 32959243Sobrien sc->ic_xfercnt++; 33059243Sobrien } 33159243Sobrien break; 33259243Sobrien 33359243Sobrien case INTR_NOACK: /* xfer terminated by master */ 33459243Sobrien break; 33559243Sobrien 33659243Sobrien case INTR_TRANSMIT: 33759243Sobrien *ptr = 0xff; /* XXX */ 33859243Sobrien break; 33959243Sobrien 34059243Sobrien case INTR_ERROR: 34159243Sobrien sc->ic_iferrs++; 34259243Sobrien break; 34359243Sobrien 34459243Sobrien default: 34559243Sobrien panic("%s: unknown event (%d)!", __func__, event); 34659243Sobrien } 34759243Sobrien 34859243Sobrien mtx_unlock(&sc->ic_lock); 34959243Sobrien return (0); 35059243Sobrien} 35159243Sobrien 35259243Sobrien/* 35359243Sobrien * icoutput() 35459243Sobrien */ 35559243Sobrienstatic int 35659243Sobrienicoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 35759243Sobrien struct route *ro) 35859243Sobrien{ 35959243Sobrien struct ic_softc *sc = ifp->if_softc; 36059243Sobrien device_t icdev = sc->ic_dev; 36159243Sobrien device_t parent = device_get_parent(icdev); 36259243Sobrien int len, sent; 36359243Sobrien struct mbuf *mm; 36459243Sobrien u_char *cp; 36559243Sobrien u_int32_t hdr; 36659243Sobrien 36759243Sobrien /* BPF writes need to be handled specially. */ 36859243Sobrien if (dst->sa_family == AF_UNSPEC) 36959243Sobrien bcopy(dst->sa_data, &hdr, sizeof(hdr)); 37059243Sobrien else 37159243Sobrien hdr = dst->sa_family; 37259243Sobrien 37359243Sobrien mtx_lock(&sc->ic_lock); 37459243Sobrien ifp->if_drv_flags |= IFF_DRV_RUNNING; 37559243Sobrien 37659243Sobrien /* already sending? */ 37759243Sobrien if (sc->ic_flags & IC_SENDING) { 37859243Sobrien ifp->if_oerrors++; 37959243Sobrien goto error; 38059243Sobrien } 38159243Sobrien 38259243Sobrien /* insert header */ 38359243Sobrien bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38459243Sobrien 38559243Sobrien cp = sc->ic_obuf + ICHDRLEN; 38659243Sobrien len = 0; 38759243Sobrien mm = m; 38859243Sobrien do { 38959243Sobrien if (len + mm->m_len > sc->ic_ifp->if_mtu) { 39059243Sobrien /* packet too large */ 39159243Sobrien ifp->if_oerrors++; 39259243Sobrien goto error; 39359243Sobrien } 39459243Sobrien 39559243Sobrien bcopy(mtod(mm,char *), cp, mm->m_len); 39659243Sobrien cp += mm->m_len; 39759243Sobrien len += mm->m_len; 39859243Sobrien 39959243Sobrien } while ((mm = mm->m_next)); 40059243Sobrien 40159243Sobrien BPF_MTAP2(ifp, &hdr, sizeof(hdr), m); 40259243Sobrien 40359243Sobrien sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY); 40459243Sobrien 40559243Sobrien m_freem(m); 40659243Sobrien mtx_unlock(&sc->ic_lock); 40759243Sobrien 40859243Sobrien /* send the packet */ 40959243Sobrien if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 41059243Sobrien len + ICHDRLEN, &sent)) 41159243Sobrien 41259243Sobrien ifp->if_oerrors++; 41359243Sobrien else { 41459243Sobrien ifp->if_opackets++; 41559243Sobrien ifp->if_obytes += len; 41659243Sobrien } 41759243Sobrien 41859243Sobrien mtx_lock(&sc->ic_lock); 41959243Sobrien sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY); 42059243Sobrien if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) == 42159243Sobrien IC_BUFFER_WAITER) 42259243Sobrien wakeup(&sc); 42359243Sobrien mtx_unlock(&sc->ic_lock); 42459243Sobrien 42559243Sobrien return (0); 42659243Sobrien 42759243Sobrienerror: 42859243Sobrien m_freem(m); 42959243Sobrien mtx_unlock(&sc->ic_lock); 43059243Sobrien 43159243Sobrien return(0); 43259243Sobrien} 43359243Sobrien 43459243SobrienDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 43559243SobrienMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 43659243SobrienMODULE_VERSION(ic, 1); 43759243Sobrien