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