if_ic.c revision 55205
138774Snsouch/*- 238774Snsouch * Copyright (c) 1998 Nicolas Souchu 338774Snsouch * All rights reserved. 438774Snsouch * 538774Snsouch * Redistribution and use in source and binary forms, with or without 638774Snsouch * modification, are permitted provided that the following conditions 738774Snsouch * are met: 838774Snsouch * 1. Redistributions of source code must retain the above copyright 938774Snsouch * notice, this list of conditions and the following disclaimer. 1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright 1138774Snsouch * notice, this list of conditions and the following disclaimer in the 1238774Snsouch * documentation and/or other materials provided with the distribution. 1338774Snsouch * 1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738774Snsouch * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438774Snsouch * SUCH DAMAGE. 2538774Snsouch * 2650477Speter * $FreeBSD: head/sys/dev/iicbus/if_ic.c 55205 1999-12-29 04:46:21Z peter $ 2738774Snsouch */ 2838774Snsouch 2938774Snsouch/* 3038774Snsouch * I2C bus IP driver 3138774Snsouch */ 3238774Snsouch 3355205Speter#ifdef _KERNEL 3438774Snsouch#include <sys/param.h> 3538774Snsouch#include <sys/systm.h> 3638774Snsouch#include <sys/proc.h> 3738774Snsouch#include <sys/mbuf.h> 3838774Snsouch#include <sys/socket.h> 3938774Snsouch#include <sys/filio.h> 4038774Snsouch#include <sys/sockio.h> 4138774Snsouch#include <sys/kernel.h> 4238774Snsouch#include <sys/module.h> 4338774Snsouch#include <sys/bus.h> 4438774Snsouch#include <sys/time.h> 4538774Snsouch#include <sys/malloc.h> 4638774Snsouch 4738774Snsouch#include <net/if.h> 4838774Snsouch#include <net/if_types.h> 4938774Snsouch#include <net/netisr.h> 5038774Snsouch 5155205Speter#endif 5238774Snsouch#include <sys/mbuf.h> 5338774Snsouch#include <sys/socket.h> 5438774Snsouch#include <net/netisr.h> 5538774Snsouch#include <net/route.h> 5638774Snsouch#include <netinet/in.h> 5738774Snsouch#include <netinet/in_systm.h> 5838774Snsouch#include <netinet/in_var.h> 5938774Snsouch#include <netinet/ip.h> 6038774Snsouch#include <netinet/if_ether.h> 6138774Snsouch 6238774Snsouch#include <net/bpf.h> 6338774Snsouch 6438774Snsouch#include <dev/iicbus/iiconf.h> 6538774Snsouch#include <dev/iicbus/iicbus.h> 6638774Snsouch 6738774Snsouch#include "iicbus_if.h" 6838774Snsouch 6938774Snsouch#define ICHDRLEN sizeof(u_int) 7038774Snsouch#define ICMTU 1500 /* default mtu */ 7138774Snsouch 7238774Snsouchstruct ic_softc { 7338774Snsouch struct ifnet ic_if; 7438774Snsouch 7538774Snsouch u_char ic_addr; /* peer I2C address */ 7638774Snsouch 7738774Snsouch int ic_sending; 7838774Snsouch 7938774Snsouch char *ic_obuf; 8038774Snsouch char *ic_ifbuf; 8138774Snsouch char *ic_cp; 8238774Snsouch 8338774Snsouch int ic_xfercnt; 8438774Snsouch 8538774Snsouch int ic_iferrs; 8638774Snsouch}; 8738774Snsouch 8838774Snsouchstatic devclass_t ic_devclass; 8938774Snsouch 9038774Snsouchstatic int icprobe(device_t); 9138774Snsouchstatic int icattach(device_t); 9238774Snsouch 9338774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t); 9438774Snsouchstatic int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 9538774Snsouch struct rtentry *); 9638774Snsouch 9738774Snsouchstatic void icintr(device_t, int, char *); 9838774Snsouch 9938774Snsouchstatic device_method_t ic_methods[] = { 10038774Snsouch /* device interface */ 10138774Snsouch DEVMETHOD(device_probe, icprobe), 10238774Snsouch DEVMETHOD(device_attach, icattach), 10338774Snsouch 10438774Snsouch /* iicbus interface */ 10538774Snsouch DEVMETHOD(iicbus_intr, icintr), 10638774Snsouch 10738774Snsouch { 0, 0 } 10838774Snsouch}; 10938774Snsouch 11038774Snsouchstatic driver_t ic_driver = { 11138774Snsouch "ic", 11238774Snsouch ic_methods, 11338774Snsouch sizeof(struct ic_softc), 11438774Snsouch}; 11538774Snsouch 11638774Snsouch/* 11738774Snsouch * icprobe() 11838774Snsouch */ 11938774Snsouchstatic int 12038774Snsouchicprobe(device_t dev) 12138774Snsouch{ 12238774Snsouch return (0); 12338774Snsouch} 12438774Snsouch 12538774Snsouch/* 12638774Snsouch * icattach() 12738774Snsouch */ 12838774Snsouchstatic int 12938774Snsouchicattach(device_t dev) 13038774Snsouch{ 13138774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 13238774Snsouch struct ifnet *ifp = &sc->ic_if; 13338774Snsouch 13438774Snsouch sc->ic_addr = iicbus_get_addr(dev); 13538774Snsouch 13638774Snsouch ifp->if_softc = sc; 13738774Snsouch ifp->if_name = "ic"; 13838774Snsouch ifp->if_unit = device_get_unit(dev); 13938774Snsouch ifp->if_mtu = ICMTU; 14038774Snsouch ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 14138774Snsouch ifp->if_ioctl = icioctl; 14238774Snsouch ifp->if_output = icoutput; 14338774Snsouch ifp->if_type = IFT_PARA; 14438774Snsouch ifp->if_hdrlen = 0; 14538774Snsouch ifp->if_addrlen = 0; 14638774Snsouch ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 14738774Snsouch 14838774Snsouch if_attach(ifp); 14938774Snsouch 15038774Snsouch bpfattach(ifp, DLT_NULL, ICHDRLEN); 15138774Snsouch 15238774Snsouch return (0); 15338774Snsouch} 15438774Snsouch 15538774Snsouch/* 15638774Snsouch * iciotcl() 15738774Snsouch */ 15838774Snsouchstatic int 15938774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 16038774Snsouch{ 16138774Snsouch device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit); 16238774Snsouch device_t parent = device_get_parent(icdev); 16338774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev); 16438774Snsouch 16538774Snsouch struct ifaddr *ifa = (struct ifaddr *)data; 16638774Snsouch struct ifreq *ifr = (struct ifreq *)data; 16738774Snsouch 16838774Snsouch u_char *iptr, *optr; 16938774Snsouch int error; 17038774Snsouch 17138774Snsouch switch (cmd) { 17238774Snsouch 17338774Snsouch case SIOCSIFDSTADDR: 17438774Snsouch case SIOCAIFADDR: 17538774Snsouch case SIOCSIFADDR: 17638774Snsouch if (ifa->ifa_addr->sa_family != AF_INET) 17738774Snsouch return EAFNOSUPPORT; 17838774Snsouch ifp->if_flags |= IFF_UP; 17938774Snsouch /* FALLTHROUGH */ 18038774Snsouch case SIOCSIFFLAGS: 18138774Snsouch if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) { 18238774Snsouch 18338774Snsouch /* XXX disable PCF */ 18438774Snsouch ifp->if_flags &= ~IFF_RUNNING; 18538774Snsouch 18638774Snsouch /* IFF_UP is not set, try to release the bus anyway */ 18738774Snsouch iicbus_release_bus(parent, icdev); 18838774Snsouch break; 18938774Snsouch } 19038774Snsouch if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) { 19138774Snsouch 19238774Snsouch if ((error = iicbus_request_bus(parent, icdev, IIC_WAIT|IIC_INTR))) 19338774Snsouch return (error); 19438774Snsouch 19538774Snsouch sc->ic_obuf = malloc(sc->ic_if.if_mtu + ICHDRLEN, 19638774Snsouch M_DEVBUF, M_WAITOK); 19738774Snsouch if (!sc->ic_obuf) { 19838774Snsouch iicbus_release_bus(parent, icdev); 19938774Snsouch return ENOBUFS; 20038774Snsouch } 20138774Snsouch 20238774Snsouch sc->ic_ifbuf = malloc(sc->ic_if.if_mtu + ICHDRLEN, 20338774Snsouch M_DEVBUF, M_WAITOK); 20438774Snsouch if (!sc->ic_ifbuf) { 20538774Snsouch iicbus_release_bus(parent, icdev); 20638774Snsouch return ENOBUFS; 20738774Snsouch } 20838774Snsouch 20940782Snsouch iicbus_reset(parent, IIC_FASTEST, 0, NULL); 21038774Snsouch 21138774Snsouch ifp->if_flags |= IFF_RUNNING; 21238774Snsouch } 21338774Snsouch break; 21438774Snsouch 21538774Snsouch case SIOCSIFMTU: 21638774Snsouch /* save previous buffers */ 21738774Snsouch iptr = sc->ic_ifbuf; 21838774Snsouch optr = sc->ic_obuf; 21938774Snsouch 22038774Snsouch /* allocate input buffer */ 22138774Snsouch sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT); 22238774Snsouch if (!sc->ic_ifbuf) { 22338774Snsouch 22438774Snsouch sc->ic_ifbuf = iptr; 22538774Snsouch sc->ic_obuf = optr; 22638774Snsouch 22738774Snsouch return ENOBUFS; 22838774Snsouch } 22938774Snsouch 23038774Snsouch /* allocate output buffer */ 23138774Snsouch sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT); 23238774Snsouch if (!sc->ic_obuf) { 23338774Snsouch 23438774Snsouch free(sc->ic_ifbuf,M_DEVBUF); 23538774Snsouch 23638774Snsouch sc->ic_ifbuf = iptr; 23738774Snsouch sc->ic_obuf = optr; 23838774Snsouch 23938774Snsouch return ENOBUFS; 24038774Snsouch } 24138774Snsouch 24238774Snsouch if (iptr) 24338774Snsouch free(iptr,M_DEVBUF); 24438774Snsouch 24538774Snsouch if (optr) 24638774Snsouch free(optr,M_DEVBUF); 24738774Snsouch 24838774Snsouch sc->ic_if.if_mtu = ifr->ifr_mtu; 24938774Snsouch break; 25038774Snsouch 25138774Snsouch case SIOCGIFMTU: 25238774Snsouch ifr->ifr_mtu = sc->ic_if.if_mtu; 25338774Snsouch break; 25438774Snsouch 25538774Snsouch case SIOCADDMULTI: 25638774Snsouch case SIOCDELMULTI: 25738774Snsouch if (ifr == 0) { 25838774Snsouch return EAFNOSUPPORT; /* XXX */ 25938774Snsouch } 26038774Snsouch switch (ifr->ifr_addr.sa_family) { 26138774Snsouch 26238774Snsouch case AF_INET: 26338774Snsouch break; 26438774Snsouch 26538774Snsouch default: 26638774Snsouch return EAFNOSUPPORT; 26738774Snsouch } 26838774Snsouch break; 26938774Snsouch 27038774Snsouch default: 27138774Snsouch return EINVAL; 27238774Snsouch } 27338774Snsouch return 0; 27438774Snsouch} 27538774Snsouch 27638774Snsouch/* 27738774Snsouch * icintr() 27838774Snsouch */ 27938774Snsouchstatic void 28038774Snsouchicintr (device_t dev, int event, char *ptr) 28138774Snsouch{ 28238774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 28338774Snsouch int unit = device_get_unit(dev); 28438774Snsouch int s, len; 28538774Snsouch struct mbuf *top; 28638774Snsouch 28738774Snsouch s = splhigh(); 28838774Snsouch 28938774Snsouch switch (event) { 29038774Snsouch 29138774Snsouch case INTR_GENERAL: 29238774Snsouch case INTR_START: 29338774Snsouch sc->ic_cp = sc->ic_ifbuf; 29438774Snsouch sc->ic_xfercnt = 0; 29538774Snsouch break; 29638774Snsouch 29738774Snsouch case INTR_STOP: 29838774Snsouch 29938774Snsouch /* if any error occured during transfert, 30038774Snsouch * drop the packet */ 30138774Snsouch if (sc->ic_iferrs) 30238774Snsouch goto err; 30338774Snsouch 30438774Snsouch if ((len = sc->ic_xfercnt) == 0) 30538774Snsouch break; /* ignore */ 30638774Snsouch 30738774Snsouch if (len <= ICHDRLEN) 30838774Snsouch goto err; 30938774Snsouch 31038774Snsouch if (IF_QFULL(&ipintrq)) { 31138774Snsouch IF_DROP(&ipintrq); 31238774Snsouch break; 31338774Snsouch } 31438774Snsouch 31538774Snsouch len -= ICHDRLEN; 31638774Snsouch sc->ic_if.if_ipackets ++; 31738774Snsouch sc->ic_if.if_ibytes += len; 31838774Snsouch 31938774Snsouch if (sc->ic_if.if_bpf) 32038774Snsouch bpf_tap(&sc->ic_if, sc->ic_ifbuf, len + ICHDRLEN); 32138774Snsouch 32238774Snsouch top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, &sc->ic_if, 0); 32338774Snsouch 32438774Snsouch if (top) { 32538774Snsouch IF_ENQUEUE(&ipintrq, top); 32638774Snsouch schednetisr(NETISR_IP); 32738774Snsouch } 32838774Snsouch break; 32938774Snsouch 33038774Snsouch err: 33138774Snsouch printf("ic%d: errors (%d)!\n", unit, sc->ic_iferrs); 33238774Snsouch 33338774Snsouch sc->ic_iferrs = 0; /* reset error count */ 33438774Snsouch sc->ic_if.if_ierrors ++; 33538774Snsouch 33638774Snsouch break; 33738774Snsouch 33838774Snsouch case INTR_RECEIVE: 33938774Snsouch if (sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN) { 34038774Snsouch sc->ic_iferrs ++; 34138774Snsouch 34238774Snsouch } else { 34338774Snsouch *sc->ic_cp++ = *ptr; 34438774Snsouch sc->ic_xfercnt ++; 34538774Snsouch } 34638774Snsouch break; 34738774Snsouch 34838774Snsouch case INTR_NOACK: /* xfer terminated by master */ 34938774Snsouch break; 35038774Snsouch 35138774Snsouch case INTR_TRANSMIT: 35238774Snsouch *ptr = 0xff; /* XXX */ 35338774Snsouch break; 35438774Snsouch 35538774Snsouch case INTR_ERROR: 35638774Snsouch sc->ic_iferrs ++; 35738774Snsouch break; 35838774Snsouch 35938774Snsouch default: 36038774Snsouch panic("%s: unknown event (%d)!", __FUNCTION__, event); 36138774Snsouch } 36238774Snsouch 36338774Snsouch splx(s); 36438774Snsouch return; 36538774Snsouch} 36638774Snsouch 36738774Snsouch/* 36838774Snsouch * icoutput() 36938774Snsouch */ 37038774Snsouchstatic int 37138774Snsouchicoutput(struct ifnet *ifp, struct mbuf *m, 37238774Snsouch struct sockaddr *dst, struct rtentry *rt) 37338774Snsouch{ 37438774Snsouch device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit); 37538774Snsouch device_t parent = device_get_parent(icdev); 37638774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev); 37738774Snsouch 37838774Snsouch int s, len, sent; 37938774Snsouch struct mbuf *mm; 38038774Snsouch u_char *cp; 38138774Snsouch u_int hdr = dst->sa_family; 38238774Snsouch 38338774Snsouch ifp->if_flags |= IFF_RUNNING; 38438774Snsouch 38538774Snsouch s = splhigh(); 38638774Snsouch 38738774Snsouch /* already sending? */ 38838774Snsouch if (sc->ic_sending) { 38938774Snsouch ifp->if_oerrors ++; 39038774Snsouch goto error; 39138774Snsouch } 39238774Snsouch 39338774Snsouch /* insert header */ 39438774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 39538774Snsouch 39638774Snsouch cp = sc->ic_obuf + ICHDRLEN; 39738774Snsouch len = 0; 39838774Snsouch mm = m; 39938774Snsouch do { 40038774Snsouch if (len + mm->m_len > sc->ic_if.if_mtu) { 40138774Snsouch /* packet to large */ 40238774Snsouch ifp->if_oerrors ++; 40338774Snsouch goto error; 40438774Snsouch } 40538774Snsouch 40638774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 40738774Snsouch cp += mm->m_len; 40838774Snsouch len += mm->m_len; 40938774Snsouch 41038774Snsouch } while ((mm = mm->m_next)); 41138774Snsouch 41238774Snsouch if (ifp->if_bpf) { 41338774Snsouch struct mbuf m0, *n = m; 41438774Snsouch 41538774Snsouch /* 41638774Snsouch * We need to prepend the address family as 41738774Snsouch * a four byte field. Cons up a dummy header 41838774Snsouch * to pacify bpf. This is safe because bpf 41938774Snsouch * will only read from the mbuf (i.e., it won't 42038774Snsouch * try to free it or keep a pointer a to it). 42138774Snsouch */ 42238774Snsouch m0.m_next = m; 42338774Snsouch m0.m_len = sizeof(u_int); 42438774Snsouch m0.m_data = (char *)&hdr; 42538774Snsouch n = &m0; 42638774Snsouch 42738774Snsouch bpf_mtap(ifp, n); 42838774Snsouch } 42938774Snsouch 43038774Snsouch sc->ic_sending = 1; 43138774Snsouch 43238774Snsouch m_freem(m); 43338774Snsouch splx(s); 43438774Snsouch 43538774Snsouch /* send the packet */ 43638774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 43738774Snsouch len + ICHDRLEN, &sent)) 43838774Snsouch 43938774Snsouch ifp->if_oerrors ++; 44038774Snsouch else { 44138774Snsouch ifp->if_opackets ++; 44238774Snsouch ifp->if_obytes += len; 44338774Snsouch } 44438774Snsouch 44538774Snsouch sc->ic_sending = 0; 44638774Snsouch 44738774Snsouch return (0); 44838774Snsouch 44938774Snsoucherror: 45038774Snsouch m_freem(m); 45138774Snsouch splx(s); 45238774Snsouch 45338774Snsouch return(0); 45438774Snsouch} 45538774Snsouch 45638774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 457