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