if_ic.c revision 121816
138774Snsouch/*- 293023Snsouch * Copyright (c) 1998, 2001 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 */ 2638774Snsouch 27119418Sobrien#include <sys/cdefs.h> 28119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/iicbus/if_ic.c 121816 2003-10-31 18:32:15Z brooks $"); 29119418Sobrien 3038774Snsouch/* 3138774Snsouch * I2C bus IP driver 3238774Snsouch */ 3338774Snsouch 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 5038774Snsouch#include <sys/mbuf.h> 5138774Snsouch#include <sys/socket.h> 5238774Snsouch#include <net/netisr.h> 5338774Snsouch#include <net/route.h> 5438774Snsouch#include <netinet/in.h> 5538774Snsouch#include <netinet/in_systm.h> 5638774Snsouch#include <netinet/in_var.h> 5738774Snsouch#include <netinet/ip.h> 5838774Snsouch#include <netinet/if_ether.h> 5938774Snsouch 6038774Snsouch#include <net/bpf.h> 6138774Snsouch 6238774Snsouch#include <dev/iicbus/iiconf.h> 6338774Snsouch#include <dev/iicbus/iicbus.h> 6438774Snsouch 6538774Snsouch#include "iicbus_if.h" 6638774Snsouch 6793023Snsouch#define PCF_MASTER_ADDRESS 0xaa 6893023Snsouch 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 13493023Snsouch sc->ic_addr = PCF_MASTER_ADDRESS; /* XXX only PCF masters */ 13538774Snsouch 13638774Snsouch ifp->if_softc = sc; 137121816Sbrooks if_initname(ifp, device_get_name(dev), 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{ 160121816Sbrooks device_t icdev = devclass_get_device(ic_devclass, ifp->if_dunit); 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, 195111119Simp 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, 202111119Simp 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 313106937Ssam BPF_TAP(&sc->ic_if, sc->ic_ifbuf, len + ICHDRLEN); 31438774Snsouch 31538774Snsouch top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, &sc->ic_if, 0); 31638774Snsouch 317111888Sjlemon if (top) 318111888Sjlemon netisr_dispatch(NETISR_IP, top); 31938774Snsouch break; 32038774Snsouch 32138774Snsouch err: 32238774Snsouch printf("ic%d: errors (%d)!\n", unit, sc->ic_iferrs); 32338774Snsouch 32438774Snsouch sc->ic_iferrs = 0; /* reset error count */ 32538774Snsouch sc->ic_if.if_ierrors ++; 32638774Snsouch 32738774Snsouch break; 32838774Snsouch 32938774Snsouch case INTR_RECEIVE: 33038774Snsouch if (sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN) { 33138774Snsouch sc->ic_iferrs ++; 33238774Snsouch 33338774Snsouch } else { 33438774Snsouch *sc->ic_cp++ = *ptr; 33538774Snsouch sc->ic_xfercnt ++; 33638774Snsouch } 33738774Snsouch break; 33838774Snsouch 33938774Snsouch case INTR_NOACK: /* xfer terminated by master */ 34038774Snsouch break; 34138774Snsouch 34238774Snsouch case INTR_TRANSMIT: 34338774Snsouch *ptr = 0xff; /* XXX */ 34438774Snsouch break; 34538774Snsouch 34638774Snsouch case INTR_ERROR: 34738774Snsouch sc->ic_iferrs ++; 34838774Snsouch break; 34938774Snsouch 35038774Snsouch default: 35187599Sobrien panic("%s: unknown event (%d)!", __func__, event); 35238774Snsouch } 35338774Snsouch 35438774Snsouch splx(s); 35538774Snsouch return; 35638774Snsouch} 35738774Snsouch 35838774Snsouch/* 35938774Snsouch * icoutput() 36038774Snsouch */ 36138774Snsouchstatic int 36238774Snsouchicoutput(struct ifnet *ifp, struct mbuf *m, 36338774Snsouch struct sockaddr *dst, struct rtentry *rt) 36438774Snsouch{ 365121816Sbrooks device_t icdev = devclass_get_device(ic_devclass, ifp->if_dunit); 36638774Snsouch device_t parent = device_get_parent(icdev); 36738774Snsouch struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev); 36838774Snsouch 36938774Snsouch int s, len, sent; 37038774Snsouch struct mbuf *mm; 37138774Snsouch u_char *cp; 37238774Snsouch u_int hdr = dst->sa_family; 37338774Snsouch 37438774Snsouch ifp->if_flags |= IFF_RUNNING; 37538774Snsouch 37638774Snsouch s = splhigh(); 37738774Snsouch 37838774Snsouch /* already sending? */ 37938774Snsouch if (sc->ic_sending) { 38038774Snsouch ifp->if_oerrors ++; 38138774Snsouch goto error; 38238774Snsouch } 38338774Snsouch 38438774Snsouch /* insert header */ 38538774Snsouch bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN); 38638774Snsouch 38738774Snsouch cp = sc->ic_obuf + ICHDRLEN; 38838774Snsouch len = 0; 38938774Snsouch mm = m; 39038774Snsouch do { 39138774Snsouch if (len + mm->m_len > sc->ic_if.if_mtu) { 39238774Snsouch /* packet to large */ 39338774Snsouch ifp->if_oerrors ++; 39438774Snsouch goto error; 39538774Snsouch } 39638774Snsouch 39738774Snsouch bcopy(mtod(mm,char *), cp, mm->m_len); 39838774Snsouch cp += mm->m_len; 39938774Snsouch len += mm->m_len; 40038774Snsouch 40138774Snsouch } while ((mm = mm->m_next)); 40238774Snsouch 40338774Snsouch if (ifp->if_bpf) { 40438774Snsouch struct mbuf m0, *n = m; 40538774Snsouch 40638774Snsouch /* 40738774Snsouch * We need to prepend the address family as 40838774Snsouch * a four byte field. Cons up a dummy header 40938774Snsouch * to pacify bpf. This is safe because bpf 41038774Snsouch * will only read from the mbuf (i.e., it won't 41138774Snsouch * try to free it or keep a pointer a to it). 41238774Snsouch */ 41338774Snsouch m0.m_next = m; 41438774Snsouch m0.m_len = sizeof(u_int); 41538774Snsouch m0.m_data = (char *)&hdr; 41638774Snsouch n = &m0; 41738774Snsouch 418106937Ssam BPF_MTAP(ifp, n); 41938774Snsouch } 42038774Snsouch 42138774Snsouch sc->ic_sending = 1; 42238774Snsouch 42338774Snsouch m_freem(m); 42438774Snsouch splx(s); 42538774Snsouch 42638774Snsouch /* send the packet */ 42738774Snsouch if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf, 42838774Snsouch len + ICHDRLEN, &sent)) 42938774Snsouch 43038774Snsouch ifp->if_oerrors ++; 43138774Snsouch else { 43238774Snsouch ifp->if_opackets ++; 43338774Snsouch ifp->if_obytes += len; 43438774Snsouch } 43538774Snsouch 43638774Snsouch sc->ic_sending = 0; 43738774Snsouch 43838774Snsouch return (0); 43938774Snsouch 44038774Snsoucherror: 44138774Snsouch m_freem(m); 44238774Snsouch splx(s); 44338774Snsouch 44438774Snsouch return(0); 44538774Snsouch} 44638774Snsouch 44738774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0); 44893023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 44993023SnsouchMODULE_VERSION(ic, 1); 450