if_ic.c revision 186833
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 186833 2009-01-06 17:23:37Z nwhitehorn $");
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>
41181305Sjhb#include <sys/lock.h>
4238774Snsouch#include <sys/module.h>
43181305Sjhb#include <sys/mutex.h>
4438774Snsouch#include <sys/bus.h>
4538774Snsouch#include <sys/time.h>
4638774Snsouch#include <sys/malloc.h>
4738774Snsouch
4838774Snsouch#include <net/if.h>
4938774Snsouch#include <net/if_types.h>
5038774Snsouch#include <net/netisr.h>
5138774Snsouch
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
6993023Snsouch#define PCF_MASTER_ADDRESS 0xaa
7093023Snsouch
71123922Ssam#define ICHDRLEN	sizeof(u_int32_t)
7238774Snsouch#define ICMTU		1500		/* default mtu */
7338774Snsouch
7438774Snsouchstruct ic_softc {
75147256Sbrooks	struct ifnet *ic_ifp;
76181305Sjhb	device_t ic_dev;
7738774Snsouch
7838774Snsouch	u_char ic_addr;			/* peer I2C address */
7938774Snsouch
80181305Sjhb	int ic_flags;
8138774Snsouch
8238774Snsouch	char *ic_obuf;
8338774Snsouch	char *ic_ifbuf;
8438774Snsouch	char *ic_cp;
8538774Snsouch
8638774Snsouch	int ic_xfercnt;
8738774Snsouch
8838774Snsouch	int ic_iferrs;
89181305Sjhb
90181305Sjhb	struct mtx ic_lock;
9138774Snsouch};
9238774Snsouch
93181305Sjhb#define	IC_SENDING		0x0001
94181305Sjhb#define	IC_OBUF_BUSY		0x0002
95181305Sjhb#define	IC_IFBUF_BUSY		0x0004
96181305Sjhb#define	IC_BUFFERS_BUSY		(IC_OBUF_BUSY | IC_IFBUF_BUSY)
97181305Sjhb#define	IC_BUFFER_WAITER	0x0004
98181305Sjhb
9938774Snsouchstatic devclass_t ic_devclass;
10038774Snsouch
10138774Snsouchstatic int icprobe(device_t);
10238774Snsouchstatic int icattach(device_t);
10338774Snsouch
10438774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t);
10538774Snsouchstatic int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
10638774Snsouch		struct rtentry *);
10738774Snsouch
10838774Snsouchstatic void icintr(device_t, int, char *);
10938774Snsouch
11038774Snsouchstatic device_method_t ic_methods[] = {
11138774Snsouch	/* device interface */
11238774Snsouch	DEVMETHOD(device_probe,		icprobe),
11338774Snsouch	DEVMETHOD(device_attach,	icattach),
11438774Snsouch
11538774Snsouch	/* iicbus interface */
11638774Snsouch	DEVMETHOD(iicbus_intr,		icintr),
11738774Snsouch
11838774Snsouch	{ 0, 0 }
11938774Snsouch};
12038774Snsouch
12138774Snsouchstatic driver_t ic_driver = {
12238774Snsouch	"ic",
12338774Snsouch	ic_methods,
12438774Snsouch	sizeof(struct ic_softc),
12538774Snsouch};
12638774Snsouch
127181305Sjhbstatic void
128181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu)
129181305Sjhb{
130181305Sjhb	char *obuf, *ifbuf;
131181305Sjhb
132181305Sjhb	obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
133181305Sjhb	ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
134181305Sjhb
135181305Sjhb	mtx_lock(&sc->ic_lock);
136181305Sjhb	while (sc->ic_flags & IC_BUFFERS_BUSY) {
137181305Sjhb		sc->ic_flags |= IC_BUFFER_WAITER;
138181305Sjhb		mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
139181305Sjhb		sc->ic_flags &= ~IC_BUFFER_WAITER;
140181305Sjhb	}
141181305Sjhb
142181305Sjhb	free(sc->ic_obuf, M_DEVBUF);
143181305Sjhb	free(sc->ic_ifbuf, M_DEVBUF);
144181305Sjhb	sc->ic_obuf = obuf;
145181305Sjhb	sc->ic_ifbuf = ifbuf;
146181305Sjhb	sc->ic_ifp->if_mtu = mtu;
147181305Sjhb	mtx_unlock(&sc->ic_lock);
148181305Sjhb}
149181305Sjhb
15038774Snsouch/*
15138774Snsouch * icprobe()
15238774Snsouch */
15338774Snsouchstatic int
15438774Snsouchicprobe(device_t dev)
15538774Snsouch{
156186833Snwhitehorn	return (BUS_PROBE_NOWILDCARD);
15738774Snsouch}
158181305Sjhb
15938774Snsouch/*
16038774Snsouch * icattach()
16138774Snsouch */
16238774Snsouchstatic int
16338774Snsouchicattach(device_t dev)
16438774Snsouch{
16538774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
166147256Sbrooks	struct ifnet *ifp;
16738774Snsouch
168147256Sbrooks	ifp = sc->ic_ifp = if_alloc(IFT_PARA);
169157497Simp	if (ifp == NULL)
170147256Sbrooks		return (ENOSPC);
171147256Sbrooks
172181305Sjhb	mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
173181305Sjhb	    MTX_DEF);
17493023Snsouch	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
175181305Sjhb	sc->ic_dev = dev;
17638774Snsouch
17738774Snsouch	ifp->if_softc = sc;
178121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
179181305Sjhb	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
18038774Snsouch	ifp->if_ioctl = icioctl;
18138774Snsouch	ifp->if_output = icoutput;
18238774Snsouch	ifp->if_hdrlen = 0;
18338774Snsouch	ifp->if_addrlen = 0;
18438774Snsouch	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
18538774Snsouch
186181305Sjhb	ic_alloc_buffers(sc, ICMTU);
187181305Sjhb
18838774Snsouch	if_attach(ifp);
18938774Snsouch
19038774Snsouch	bpfattach(ifp, DLT_NULL, ICHDRLEN);
19138774Snsouch
19238774Snsouch	return (0);
19338774Snsouch}
19438774Snsouch
19538774Snsouch/*
19638774Snsouch * iciotcl()
19738774Snsouch */
19838774Snsouchstatic int
19938774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
20038774Snsouch{
201181305Sjhb	struct ic_softc *sc = ifp->if_softc;
202181305Sjhb	device_t icdev = sc->ic_dev;
203157497Simp	device_t parent = device_get_parent(icdev);
204157497Simp	struct ifaddr *ifa = (struct ifaddr *)data;
205157497Simp	struct ifreq *ifr = (struct ifreq *)data;
206157497Simp	int error;
20738774Snsouch
208157497Simp	switch (cmd) {
20938774Snsouch
210157497Simp	case SIOCSIFDSTADDR:
211157497Simp	case SIOCAIFADDR:
212157497Simp	case SIOCSIFADDR:
213157497Simp		if (ifa->ifa_addr->sa_family != AF_INET)
214157497Simp			return (EAFNOSUPPORT);
215181305Sjhb		mtx_lock(&sc->ic_lock);
216157497Simp		ifp->if_flags |= IFF_UP;
217181305Sjhb		goto locked;
218157497Simp	case SIOCSIFFLAGS:
219181305Sjhb		mtx_lock(&sc->ic_lock);
220181305Sjhb	locked:
221157497Simp		if ((!(ifp->if_flags & IFF_UP)) &&
222157497Simp		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
22338774Snsouch
224157497Simp			/* XXX disable PCF */
225157497Simp			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
226181305Sjhb			mtx_unlock(&sc->ic_lock);
22738774Snsouch
228157497Simp			/* IFF_UP is not set, try to release the bus anyway */
229157497Simp			iicbus_release_bus(parent, icdev);
230157497Simp			break;
231157497Simp		}
232157497Simp		if (((ifp->if_flags & IFF_UP)) &&
233157497Simp		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
234181305Sjhb			mtx_unlock(&sc->ic_lock);
235157497Simp			if ((error = iicbus_request_bus(parent, icdev,
236157497Simp			    IIC_WAIT | IIC_INTR)))
237157497Simp				return (error);
238181305Sjhb			mtx_lock(&sc->ic_lock);
239157497Simp			iicbus_reset(parent, IIC_FASTEST, 0, NULL);
240157497Simp			ifp->if_drv_flags |= IFF_DRV_RUNNING;
241157497Simp		}
242181305Sjhb		mtx_unlock(&sc->ic_lock);
243157497Simp		break;
24438774Snsouch
245157497Simp	case SIOCSIFMTU:
246181305Sjhb		ic_alloc_buffers(sc, ifr->ifr_mtu);
247157497Simp		break;
24838774Snsouch
249157497Simp	case SIOCGIFMTU:
250181305Sjhb		mtx_lock(&sc->ic_lock);
251157497Simp		ifr->ifr_mtu = sc->ic_ifp->if_mtu;
252181305Sjhb		mtx_unlock(&sc->ic_lock);
253157497Simp		break;
25438774Snsouch
255157497Simp	case SIOCADDMULTI:
256157497Simp	case SIOCDELMULTI:
257157497Simp		if (ifr == 0)
258157497Simp			return (EAFNOSUPPORT);		/* XXX */
259157497Simp		switch (ifr->ifr_addr.sa_family) {
260157497Simp		case AF_INET:
261157497Simp			break;
262157497Simp		default:
263157497Simp			return (EAFNOSUPPORT);
264157497Simp		}
265157497Simp		break;
26638774Snsouch	default:
267157497Simp		return (EINVAL);
26838774Snsouch	}
269157497Simp	return (0);
27038774Snsouch}
27138774Snsouch
27238774Snsouch/*
27338774Snsouch * icintr()
27438774Snsouch */
27538774Snsouchstatic void
276161516Simpicintr(device_t dev, int event, char *ptr)
27738774Snsouch{
27838774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
27938774Snsouch	struct mbuf *top;
280181305Sjhb	int len;
28138774Snsouch
282181305Sjhb	mtx_lock(&sc->ic_lock);
283181305Sjhb
28438774Snsouch	switch (event) {
28538774Snsouch
28638774Snsouch	case INTR_GENERAL:
28738774Snsouch	case INTR_START:
28838774Snsouch		sc->ic_cp = sc->ic_ifbuf;
28938774Snsouch		sc->ic_xfercnt = 0;
290181305Sjhb		sc->ic_flags |= IC_IFBUF_BUSY;
29138774Snsouch		break;
29238774Snsouch
29338774Snsouch	case INTR_STOP:
29438774Snsouch
295157497Simp		/* if any error occured during transfert,
296157497Simp		 * drop the packet */
297181305Sjhb		sc->ic_flags &= ~IC_IFBUF_BUSY;
298181305Sjhb		if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
299181305Sjhb		    IC_BUFFER_WAITER)
300181305Sjhb			wakeup(&sc);
301157497Simp		if (sc->ic_iferrs)
302157497Simp			goto err;
303157497Simp		if ((len = sc->ic_xfercnt) == 0)
304157497Simp			break;					/* ignore */
305157497Simp		if (len <= ICHDRLEN)
306157497Simp			goto err;
307157497Simp		len -= ICHDRLEN;
308157497Simp		sc->ic_ifp->if_ipackets++;
309157497Simp		sc->ic_ifp->if_ibytes += len;
310157497Simp		BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
311157497Simp		top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
312181305Sjhb		if (top) {
313181305Sjhb			mtx_unlock(&sc->ic_lock);
314157497Simp			netisr_dispatch(NETISR_IP, top);
315181305Sjhb			mtx_lock(&sc->ic_lock);
316181305Sjhb		}
317157497Simp		break;
31838774Snsouch	err:
319181305Sjhb		if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
320157497Simp		sc->ic_iferrs = 0;			/* reset error count */
321157497Simp		sc->ic_ifp->if_ierrors++;
322157497Simp		break;
32338774Snsouch
32438774Snsouch	case INTR_RECEIVE:
325181305Sjhb		if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
326157497Simp			sc->ic_iferrs++;
32738774Snsouch		} else {
32838774Snsouch			*sc->ic_cp++ = *ptr;
329157497Simp			sc->ic_xfercnt++;
33038774Snsouch		}
33138774Snsouch		break;
33238774Snsouch
33338774Snsouch	case INTR_NOACK:			/* xfer terminated by master */
33438774Snsouch		break;
33538774Snsouch
33638774Snsouch	case INTR_TRANSMIT:
33738774Snsouch		*ptr = 0xff;					/* XXX */
33838774Snsouch	  	break;
33938774Snsouch
34038774Snsouch	case INTR_ERROR:
341157497Simp		sc->ic_iferrs++;
34238774Snsouch		break;
34338774Snsouch
34438774Snsouch	default:
34587599Sobrien		panic("%s: unknown event (%d)!", __func__, event);
34638774Snsouch	}
34738774Snsouch
348181305Sjhb	mtx_unlock(&sc->ic_lock);
34938774Snsouch	return;
35038774Snsouch}
35138774Snsouch
35238774Snsouch/*
35338774Snsouch * icoutput()
35438774Snsouch */
35538774Snsouchstatic int
356161516Simpicoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
357161516Simp    struct rtentry *rt)
35838774Snsouch{
359181305Sjhb	struct ic_softc *sc = ifp->if_softc;
360181305Sjhb	device_t icdev = sc->ic_dev;
36138774Snsouch	device_t parent = device_get_parent(icdev);
362181305Sjhb	int len, sent;
36338774Snsouch	struct mbuf *mm;
36438774Snsouch	u_char *cp;
365147611Sdwmalone	u_int32_t hdr;
36638774Snsouch
367147611Sdwmalone	/* BPF writes need to be handled specially. */
368147611Sdwmalone	if (dst->sa_family == AF_UNSPEC)
369147611Sdwmalone		bcopy(dst->sa_data, &hdr, sizeof(hdr));
370147611Sdwmalone	else
371147611Sdwmalone		hdr = dst->sa_family;
372147611Sdwmalone
373181305Sjhb	mtx_lock(&sc->ic_lock);
374148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
37538774Snsouch
37638774Snsouch	/* already sending? */
377181305Sjhb	if (sc->ic_flags & IC_SENDING) {
378157497Simp		ifp->if_oerrors++;
37938774Snsouch		goto error;
38038774Snsouch	}
38138774Snsouch
38238774Snsouch	/* insert header */
38338774Snsouch	bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
38438774Snsouch
38538774Snsouch	cp = sc->ic_obuf + ICHDRLEN;
38638774Snsouch	len = 0;
38738774Snsouch	mm = m;
38838774Snsouch	do {
389147256Sbrooks		if (len + mm->m_len > sc->ic_ifp->if_mtu) {
390181305Sjhb			/* packet too large */
391157497Simp			ifp->if_oerrors++;
39238774Snsouch			goto error;
39338774Snsouch		}
39438774Snsouch
39538774Snsouch		bcopy(mtod(mm,char *), cp, mm->m_len);
39638774Snsouch		cp += mm->m_len;
39738774Snsouch		len += mm->m_len;
39838774Snsouch
39938774Snsouch	} while ((mm = mm->m_next));
40038774Snsouch
401123922Ssam	BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
40238774Snsouch
403181305Sjhb	sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
40438774Snsouch
40538774Snsouch	m_freem(m);
406181305Sjhb	mtx_unlock(&sc->ic_lock);
40738774Snsouch
40838774Snsouch	/* send the packet */
40938774Snsouch	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
41038774Snsouch				len + ICHDRLEN, &sent))
41138774Snsouch
412157497Simp		ifp->if_oerrors++;
41338774Snsouch	else {
414157497Simp		ifp->if_opackets++;
41538774Snsouch		ifp->if_obytes += len;
416181305Sjhb	}
41738774Snsouch
418181305Sjhb	mtx_lock(&sc->ic_lock);
419181305Sjhb	sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
420181305Sjhb	if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
421181305Sjhb	    IC_BUFFER_WAITER)
422181305Sjhb		wakeup(&sc);
423181305Sjhb	mtx_unlock(&sc->ic_lock);
42438774Snsouch
42538774Snsouch	return (0);
42638774Snsouch
42738774Snsoucherror:
42838774Snsouch	m_freem(m);
429181305Sjhb	mtx_unlock(&sc->ic_lock);
43038774Snsouch
43138774Snsouch	return(0);
43238774Snsouch}
43338774Snsouch
43438774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
43593023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
43693023SnsouchMODULE_VERSION(ic, 1);
437