if_ic.c revision 87599
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 87599 2001-12-10 08:09:49Z obrien $ 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/mbuf.h> 3738774Snsouch#include <sys/socket.h> 3838774Snsouch#include <sys/filio.h> 3938774Snsouch#include <sys/sockio.h> 4038774Snsouch#include <sys/kernel.h> 4138774Snsouch#include <sys/module.h> 4238774Snsouch#include <sys/bus.h> 4338774Snsouch#include <sys/time.h> 4438774Snsouch#include <sys/malloc.h> 4538774Snsouch 4638774Snsouch#include <net/if.h> 4738774Snsouch#include <net/if_types.h> 4838774Snsouch#include <net/netisr.h> 4938774Snsouch 5055205Speter#endif 5138774Snsouch#include <sys/mbuf.h> 5238774Snsouch#include <sys/socket.h> 5338774Snsouch#include <net/netisr.h> 5438774Snsouch#include <net/route.h> 5538774Snsouch#include <netinet/in.h> 5638774Snsouch#include <netinet/in_systm.h> 5738774Snsouch#include <netinet/in_var.h> 5838774Snsouch#include <netinet/ip.h> 5938774Snsouch#include <netinet/if_ether.h> 6038774Snsouch 6138774Snsouch#include <net/bpf.h> 6238774Snsouch 6338774Snsouch#include <dev/iicbus/iiconf.h> 6438774Snsouch#include <dev/iicbus/iicbus.h> 6538774Snsouch 6638774Snsouch#include "iicbus_if.h" 6738774Snsouch 6838774Snsouch#define ICHDRLEN sizeof(u_int) 6938774Snsouch#define ICMTU 1500 /* default mtu */ 7038774Snsouch 7138774Snsouchstruct ic_softc { 7238774Snsouch struct ifnet ic_if; 7338774Snsouch 7438774Snsouch u_char ic_addr; /* peer I2C address */ 7538774Snsouch 7638774Snsouch int ic_sending; 7738774Snsouch 7838774Snsouch char *ic_obuf; 7938774Snsouch char *ic_ifbuf; 8038774Snsouch char *ic_cp; 8138774Snsouch 8238774Snsouch int ic_xfercnt; 8338774Snsouch 8438774Snsouch int ic_iferrs; 8538774Snsouch}; 8638774Snsouch 8738774Snsouchstatic devclass_t ic_devclass; 8838774Snsouch 8938774Snsouchstatic int icprobe(device_t); 9038774Snsouchstatic int icattach(device_t); 9138774Snsouch 9238774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t); 9338774Snsouchstatic int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *, 9438774Snsouch struct rtentry *); 9538774Snsouch 9638774Snsouchstatic void icintr(device_t, int, char *); 9738774Snsouch 9838774Snsouchstatic device_method_t ic_methods[] = { 9938774Snsouch /* device interface */ 10038774Snsouch DEVMETHOD(device_probe, icprobe), 10138774Snsouch DEVMETHOD(device_attach, icattach), 10238774Snsouch 10338774Snsouch /* iicbus interface */ 10438774Snsouch DEVMETHOD(iicbus_intr, icintr), 10538774Snsouch 10638774Snsouch { 0, 0 } 10738774Snsouch}; 10838774Snsouch 10938774Snsouchstatic driver_t ic_driver = { 11038774Snsouch "ic", 11138774Snsouch ic_methods, 11238774Snsouch sizeof(struct ic_softc), 11338774Snsouch}; 11438774Snsouch 11538774Snsouch/* 11638774Snsouch * icprobe() 11738774Snsouch */ 11838774Snsouchstatic int 11938774Snsouchicprobe(device_t dev) 12038774Snsouch{ 12138774Snsouch return (0); 12238774Snsouch} 12338774Snsouch 12438774Snsouch/* 12538774Snsouch * icattach() 12638774Snsouch */ 12738774Snsouchstatic int 12838774Snsouchicattach(device_t dev) 12938774Snsouch{ 13038774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 13138774Snsouch struct ifnet *ifp = &sc->ic_if; 13238774Snsouch 13338774Snsouch sc->ic_addr = iicbus_get_addr(dev); 13438774Snsouch 13538774Snsouch ifp->if_softc = sc; 13638774Snsouch ifp->if_name = "ic"; 13738774Snsouch ifp->if_unit = device_get_unit(dev); 13838774Snsouch ifp->if_mtu = ICMTU; 13938774Snsouch ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; 14038774Snsouch ifp->if_ioctl = icioctl; 14138774Snsouch ifp->if_output = icoutput; 14238774Snsouch ifp->if_type = IFT_PARA; 14338774Snsouch ifp->if_hdrlen = 0; 14438774Snsouch ifp->if_addrlen = 0; 14538774Snsouch ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 14638774Snsouch 14738774Snsouch if_attach(ifp); 14838774Snsouch 14938774Snsouch bpfattach(ifp, DLT_NULL, ICHDRLEN); 15038774Snsouch 15138774Snsouch return (0); 15238774Snsouch} 15338774Snsouch 15438774Snsouch/* 15538774Snsouch * iciotcl() 15638774Snsouch */ 15738774Snsouchstatic int 15838774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 15938774Snsouch{ 16038774Snsouch device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit); 16138774Snsouch device_t parent = device_get_parent(icdev); 16238774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev); 16338774Snsouch 16438774Snsouch struct ifaddr *ifa = (struct ifaddr *)data; 16538774Snsouch struct ifreq *ifr = (struct ifreq *)data; 16638774Snsouch 16738774Snsouch u_char *iptr, *optr; 16838774Snsouch int error; 16938774Snsouch 17038774Snsouch switch (cmd) { 17138774Snsouch 17238774Snsouch case SIOCSIFDSTADDR: 17338774Snsouch case SIOCAIFADDR: 17438774Snsouch case SIOCSIFADDR: 17538774Snsouch if (ifa->ifa_addr->sa_family != AF_INET) 17638774Snsouch return EAFNOSUPPORT; 17738774Snsouch ifp->if_flags |= IFF_UP; 17838774Snsouch /* FALLTHROUGH */ 17938774Snsouch case SIOCSIFFLAGS: 18038774Snsouch if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) { 18138774Snsouch 18238774Snsouch /* XXX disable PCF */ 18338774Snsouch ifp->if_flags &= ~IFF_RUNNING; 18438774Snsouch 18538774Snsouch /* IFF_UP is not set, try to release the bus anyway */ 18638774Snsouch iicbus_release_bus(parent, icdev); 18738774Snsouch break; 18838774Snsouch } 18938774Snsouch if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) { 19038774Snsouch 19138774Snsouch if ((error = iicbus_request_bus(parent, icdev, IIC_WAIT|IIC_INTR))) 19238774Snsouch return (error); 19338774Snsouch 19438774Snsouch sc->ic_obuf = malloc(sc->ic_if.if_mtu + ICHDRLEN, 19538774Snsouch M_DEVBUF, M_WAITOK); 19638774Snsouch if (!sc->ic_obuf) { 19738774Snsouch iicbus_release_bus(parent, icdev); 19838774Snsouch return ENOBUFS; 19938774Snsouch } 20038774Snsouch 20138774Snsouch sc->ic_ifbuf = malloc(sc->ic_if.if_mtu + ICHDRLEN, 20238774Snsouch M_DEVBUF, M_WAITOK); 20338774Snsouch if (!sc->ic_ifbuf) { 20438774Snsouch iicbus_release_bus(parent, icdev); 20538774Snsouch return ENOBUFS; 20638774Snsouch } 20738774Snsouch 20840782Snsouch iicbus_reset(parent, IIC_FASTEST, 0, NULL); 20938774Snsouch 21038774Snsouch ifp->if_flags |= IFF_RUNNING; 21138774Snsouch } 21238774Snsouch break; 21338774Snsouch 21438774Snsouch case SIOCSIFMTU: 21538774Snsouch /* save previous buffers */ 21638774Snsouch iptr = sc->ic_ifbuf; 21738774Snsouch optr = sc->ic_obuf; 21838774Snsouch 21938774Snsouch /* allocate input buffer */ 22038774Snsouch sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT); 22138774Snsouch if (!sc->ic_ifbuf) { 22238774Snsouch 22338774Snsouch sc->ic_ifbuf = iptr; 22438774Snsouch sc->ic_obuf = optr; 22538774Snsouch 22638774Snsouch return ENOBUFS; 22738774Snsouch } 22838774Snsouch 22938774Snsouch /* allocate output buffer */ 23038774Snsouch sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT); 23138774Snsouch if (!sc->ic_obuf) { 23238774Snsouch 23338774Snsouch free(sc->ic_ifbuf,M_DEVBUF); 23438774Snsouch 23538774Snsouch sc->ic_ifbuf = iptr; 23638774Snsouch sc->ic_obuf = optr; 23738774Snsouch 23838774Snsouch return ENOBUFS; 23938774Snsouch } 24038774Snsouch 24138774Snsouch if (iptr) 24238774Snsouch free(iptr,M_DEVBUF); 24338774Snsouch 24438774Snsouch if (optr) 24538774Snsouch free(optr,M_DEVBUF); 24638774Snsouch 24738774Snsouch sc->ic_if.if_mtu = ifr->ifr_mtu; 24838774Snsouch break; 24938774Snsouch 25038774Snsouch case SIOCGIFMTU: 25138774Snsouch ifr->ifr_mtu = sc->ic_if.if_mtu; 25238774Snsouch break; 25338774Snsouch 25438774Snsouch case SIOCADDMULTI: 25538774Snsouch case SIOCDELMULTI: 25638774Snsouch if (ifr == 0) { 25738774Snsouch return EAFNOSUPPORT; /* XXX */ 25838774Snsouch } 25938774Snsouch switch (ifr->ifr_addr.sa_family) { 26038774Snsouch 26138774Snsouch case AF_INET: 26238774Snsouch break; 26338774Snsouch 26438774Snsouch default: 26538774Snsouch return EAFNOSUPPORT; 26638774Snsouch } 26738774Snsouch break; 26838774Snsouch 26938774Snsouch default: 27038774Snsouch return EINVAL; 27138774Snsouch } 27238774Snsouch return 0; 27338774Snsouch} 27438774Snsouch 27538774Snsouch/* 27638774Snsouch * icintr() 27738774Snsouch */ 27838774Snsouchstatic void 27938774Snsouchicintr (device_t dev, int event, char *ptr) 28038774Snsouch{ 28138774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev); 28238774Snsouch int unit = device_get_unit(dev); 28338774Snsouch int s, len; 28438774Snsouch struct mbuf *top; 28538774Snsouch 28638774Snsouch s = splhigh(); 28738774Snsouch 28838774Snsouch switch (event) { 28938774Snsouch 29038774Snsouch case INTR_GENERAL: 29138774Snsouch case INTR_START: 29238774Snsouch sc->ic_cp = sc->ic_ifbuf; 29338774Snsouch sc->ic_xfercnt = 0; 29438774Snsouch break; 29538774Snsouch 29638774Snsouch case INTR_STOP: 29738774Snsouch 29838774Snsouch /* if any error occured during transfert, 29938774Snsouch * drop the packet */ 30038774Snsouch if (sc->ic_iferrs) 30138774Snsouch goto err; 30238774Snsouch 30338774Snsouch if ((len = sc->ic_xfercnt) == 0) 30438774Snsouch break; /* ignore */ 30538774Snsouch 30638774Snsouch if (len <= ICHDRLEN) 30738774Snsouch goto err; 30838774Snsouch 30938774Snsouch len -= ICHDRLEN; 31038774Snsouch sc->ic_if.if_ipackets ++; 31138774Snsouch sc->ic_if.if_ibytes += len; 31238774Snsouch 31338774Snsouch if (sc->ic_if.if_bpf) 31438774Snsouch bpf_tap(&sc->ic_if, sc->ic_ifbuf, len + ICHDRLEN); 31538774Snsouch 31638774Snsouch top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, &sc->ic_if, 0); 31738774Snsouch 31838774Snsouch if (top) { 31969152Sjlemon if (IF_HANDOFF(&ipintrq, top, NULL)) 32069152Sjlemon schednetisr(NETISR_IP); 32138774Snsouch } 32238774Snsouch break; 32338774Snsouch 32438774Snsouch err: 32538774Snsouch printf("ic%d: errors (%d)!\n", unit, sc->ic_iferrs); 32638774Snsouch 32738774Snsouch sc->ic_iferrs = 0; /* reset error count */ 32838774Snsouch sc->ic_if.if_ierrors ++; 32938774Snsouch 33038774Snsouch break; 33138774Snsouch 33238774Snsouch case INTR_RECEIVE: 33338774Snsouch if (sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN) { 33438774Snsouch sc->ic_iferrs ++; 33538774Snsouch 33638774Snsouch } else { 33738774Snsouch *sc->ic_cp++ = *ptr; 33838774Snsouch sc->ic_xfercnt ++; 33938774Snsouch } 34038774Snsouch break; 34138774Snsouch 34238774Snsouch case INTR_NOACK: /* xfer terminated by master */ 34338774Snsouch break; 34438774Snsouch 34538774Snsouch case INTR_TRANSMIT: 34638774Snsouch *ptr = 0xff; /* XXX */ 34738774Snsouch break; 34838774Snsouch 34938774Snsouch case INTR_ERROR: 35038774Snsouch sc->ic_iferrs ++; 35138774Snsouch break; 35238774Snsouch 35338774Snsouch default: 35487599Sobrien panic("%s: unknown event (%d)!", __func__, event); 35538774Snsouch } 35638774Snsouch 35738774Snsouch splx(s); 35838774Snsouch return; 35938774Snsouch} 36038774Snsouch 36138774Snsouch/* 36238774Snsouch * icoutput() 36338774Snsouch */ 36438774Snsouchstatic int 36538774Snsouchicoutput(struct ifnet *ifp, struct mbuf *m, 36638774Snsouch struct sockaddr *dst, struct rtentry *rt) 36738774Snsouch{ 36838774Snsouch device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit); 36938774Snsouch device_t parent = device_get_parent(icdev); 37038774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev); 37138774Snsouch 37238774Snsouch int s, len, sent; 37338774Snsouch struct mbuf *mm; 37438774Snsouch u_char *cp; 37538774Snsouch u_int hdr = dst->sa_family; 37638774Snsouch 37738774Snsouch ifp->if_flags |= IFF_RUNNING; 37838774Snsouch 37938774Snsouch s = splhigh(); 38038774Snsouch 38138774Snsouch /* already sending? */ 38238774Snsouch if (sc->ic_sending) { 38338774Snsouch ifp->if_oerrors ++; 38438774Snsouch goto error; 38538774Snsouch } 38638774Snsouch 38738774Snsouch /* insert header */ 38838774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38938774Snsouch 39038774Snsouch cp = sc->ic_obuf + ICHDRLEN; 39138774Snsouch len = 0; 39238774Snsouch mm = m; 39338774Snsouch do { 39438774Snsouch if (len + mm->m_len > sc->ic_if.if_mtu) { 39538774Snsouch /* packet to large */ 39638774Snsouch ifp->if_oerrors ++; 39738774Snsouch goto error; 39838774Snsouch } 39938774Snsouch 40038774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 40138774Snsouch cp += mm->m_len; 40238774Snsouch len += mm->m_len; 40338774Snsouch 40438774Snsouch } while ((mm = mm->m_next)); 40538774Snsouch 40638774Snsouch if (ifp->if_bpf) { 40738774Snsouch struct mbuf m0, *n = m; 40838774Snsouch 40938774Snsouch /* 41038774Snsouch * We need to prepend the address family as 41138774Snsouch * a four byte field. Cons up a dummy header 41238774Snsouch * to pacify bpf. This is safe because bpf 41338774Snsouch * will only read from the mbuf (i.e., it won't 41438774Snsouch * try to free it or keep a pointer a to it). 41538774Snsouch */ 41638774Snsouch m0.m_next = m; 41738774Snsouch m0.m_len = sizeof(u_int); 41838774Snsouch m0.m_data = (char *)&hdr; 41938774Snsouch n = &m0; 42038774Snsouch 42138774Snsouch bpf_mtap(ifp, n); 42238774Snsouch } 42338774Snsouch 42438774Snsouch sc->ic_sending = 1; 42538774Snsouch 42638774Snsouch m_freem(m); 42738774Snsouch splx(s); 42838774Snsouch 42938774Snsouch /* send the packet */ 43038774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 43138774Snsouch len + ICHDRLEN, &sent)) 43238774Snsouch 43338774Snsouch ifp->if_oerrors ++; 43438774Snsouch else { 43538774Snsouch ifp->if_opackets ++; 43638774Snsouch ifp->if_obytes += len; 43738774Snsouch } 43838774Snsouch 43938774Snsouch sc->ic_sending = 0; 44038774Snsouch 44138774Snsouch return (0); 44238774Snsouch 44338774Snsoucherror: 44438774Snsouch m_freem(m); 44538774Snsouch splx(s); 44638774Snsouch 44738774Snsouch return(0); 44838774Snsouch} 44938774Snsouch 45038774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 451