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