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: releng/10.3/sys/dev/iicbus/if_ic.c 255471 2013-09-11 09:19:44Z glebius $");
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 <net/route.h>
5338774Snsouch#include <netinet/in.h>
5438774Snsouch#include <netinet/in_systm.h>
5538774Snsouch#include <netinet/in_var.h>
5638774Snsouch#include <netinet/ip.h>
5738774Snsouch#include <netinet/if_ether.h>
5838774Snsouch
5938774Snsouch#include <net/bpf.h>
6038774Snsouch
6138774Snsouch#include <dev/iicbus/iiconf.h>
6238774Snsouch#include <dev/iicbus/iicbus.h>
6338774Snsouch
6438774Snsouch#include "iicbus_if.h"
6538774Snsouch
6693023Snsouch#define PCF_MASTER_ADDRESS 0xaa
6793023Snsouch
68123922Ssam#define ICHDRLEN	sizeof(u_int32_t)
6938774Snsouch#define ICMTU		1500		/* default mtu */
7038774Snsouch
7138774Snsouchstruct ic_softc {
72147256Sbrooks	struct ifnet *ic_ifp;
73181305Sjhb	device_t ic_dev;
7438774Snsouch
7538774Snsouch	u_char ic_addr;			/* peer I2C address */
7638774Snsouch
77181305Sjhb	int ic_flags;
7838774Snsouch
7938774Snsouch	char *ic_obuf;
8038774Snsouch	char *ic_ifbuf;
8138774Snsouch	char *ic_cp;
8238774Snsouch
8338774Snsouch	int ic_xfercnt;
8438774Snsouch
8538774Snsouch	int ic_iferrs;
86181305Sjhb
87181305Sjhb	struct mtx ic_lock;
8838774Snsouch};
8938774Snsouch
90181305Sjhb#define	IC_SENDING		0x0001
91181305Sjhb#define	IC_OBUF_BUSY		0x0002
92181305Sjhb#define	IC_IFBUF_BUSY		0x0004
93181305Sjhb#define	IC_BUFFERS_BUSY		(IC_OBUF_BUSY | IC_IFBUF_BUSY)
94181305Sjhb#define	IC_BUFFER_WAITER	0x0004
95181305Sjhb
9638774Snsouchstatic devclass_t ic_devclass;
9738774Snsouch
9838774Snsouchstatic int icprobe(device_t);
9938774Snsouchstatic int icattach(device_t);
10038774Snsouch
10138774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t);
102249925Sglebiusstatic int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
103191148Skmacy               struct route *);
10438774Snsouch
105188461Simpstatic int icintr(device_t, int, char *);
10638774Snsouch
10738774Snsouchstatic device_method_t ic_methods[] = {
10838774Snsouch	/* device interface */
10938774Snsouch	DEVMETHOD(device_probe,		icprobe),
11038774Snsouch	DEVMETHOD(device_attach,	icattach),
11138774Snsouch
11238774Snsouch	/* iicbus interface */
11338774Snsouch	DEVMETHOD(iicbus_intr,		icintr),
11438774Snsouch
11538774Snsouch	{ 0, 0 }
11638774Snsouch};
11738774Snsouch
11838774Snsouchstatic driver_t ic_driver = {
11938774Snsouch	"ic",
12038774Snsouch	ic_methods,
12138774Snsouch	sizeof(struct ic_softc),
12238774Snsouch};
12338774Snsouch
124181305Sjhbstatic void
125181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu)
126181305Sjhb{
127181305Sjhb	char *obuf, *ifbuf;
128181305Sjhb
129181305Sjhb	obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
130181305Sjhb	ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
131181305Sjhb
132181305Sjhb	mtx_lock(&sc->ic_lock);
133181305Sjhb	while (sc->ic_flags & IC_BUFFERS_BUSY) {
134181305Sjhb		sc->ic_flags |= IC_BUFFER_WAITER;
135181305Sjhb		mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
136181305Sjhb		sc->ic_flags &= ~IC_BUFFER_WAITER;
137181305Sjhb	}
138181305Sjhb
139181305Sjhb	free(sc->ic_obuf, M_DEVBUF);
140181305Sjhb	free(sc->ic_ifbuf, M_DEVBUF);
141181305Sjhb	sc->ic_obuf = obuf;
142181305Sjhb	sc->ic_ifbuf = ifbuf;
143181305Sjhb	sc->ic_ifp->if_mtu = mtu;
144181305Sjhb	mtx_unlock(&sc->ic_lock);
145181305Sjhb}
146181305Sjhb
14738774Snsouch/*
14838774Snsouch * icprobe()
14938774Snsouch */
15038774Snsouchstatic int
15138774Snsouchicprobe(device_t dev)
15238774Snsouch{
153186833Snwhitehorn	return (BUS_PROBE_NOWILDCARD);
15438774Snsouch}
155181305Sjhb
15638774Snsouch/*
15738774Snsouch * icattach()
15838774Snsouch */
15938774Snsouchstatic int
16038774Snsouchicattach(device_t dev)
16138774Snsouch{
16238774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
163147256Sbrooks	struct ifnet *ifp;
16438774Snsouch
165147256Sbrooks	ifp = sc->ic_ifp = if_alloc(IFT_PARA);
166157497Simp	if (ifp == NULL)
167147256Sbrooks		return (ENOSPC);
168147256Sbrooks
169181305Sjhb	mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
170181305Sjhb	    MTX_DEF);
17193023Snsouch	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
172181305Sjhb	sc->ic_dev = dev;
17338774Snsouch
17438774Snsouch	ifp->if_softc = sc;
175121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
176181305Sjhb	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
17738774Snsouch	ifp->if_ioctl = icioctl;
17838774Snsouch	ifp->if_output = icoutput;
17938774Snsouch	ifp->if_hdrlen = 0;
18038774Snsouch	ifp->if_addrlen = 0;
181207554Ssobomax	ifp->if_snd.ifq_maxlen = ifqmaxlen;
18238774Snsouch
183181305Sjhb	ic_alloc_buffers(sc, ICMTU);
184181305Sjhb
18538774Snsouch	if_attach(ifp);
18638774Snsouch
18738774Snsouch	bpfattach(ifp, DLT_NULL, ICHDRLEN);
18838774Snsouch
18938774Snsouch	return (0);
19038774Snsouch}
19138774Snsouch
19238774Snsouch/*
19338774Snsouch * iciotcl()
19438774Snsouch */
19538774Snsouchstatic int
19638774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
19738774Snsouch{
198181305Sjhb	struct ic_softc *sc = ifp->if_softc;
199181305Sjhb	device_t icdev = sc->ic_dev;
200157497Simp	device_t parent = device_get_parent(icdev);
201157497Simp	struct ifaddr *ifa = (struct ifaddr *)data;
202157497Simp	struct ifreq *ifr = (struct ifreq *)data;
203157497Simp	int error;
20438774Snsouch
205157497Simp	switch (cmd) {
20638774Snsouch
207157497Simp	case SIOCAIFADDR:
208157497Simp	case SIOCSIFADDR:
209157497Simp		if (ifa->ifa_addr->sa_family != AF_INET)
210157497Simp			return (EAFNOSUPPORT);
211181305Sjhb		mtx_lock(&sc->ic_lock);
212157497Simp		ifp->if_flags |= IFF_UP;
213181305Sjhb		goto locked;
214157497Simp	case SIOCSIFFLAGS:
215181305Sjhb		mtx_lock(&sc->ic_lock);
216181305Sjhb	locked:
217157497Simp		if ((!(ifp->if_flags & IFF_UP)) &&
218157497Simp		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
21938774Snsouch
220157497Simp			/* XXX disable PCF */
221157497Simp			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
222181305Sjhb			mtx_unlock(&sc->ic_lock);
22338774Snsouch
224157497Simp			/* IFF_UP is not set, try to release the bus anyway */
225157497Simp			iicbus_release_bus(parent, icdev);
226157497Simp			break;
227157497Simp		}
228157497Simp		if (((ifp->if_flags & IFF_UP)) &&
229157497Simp		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
230181305Sjhb			mtx_unlock(&sc->ic_lock);
231157497Simp			if ((error = iicbus_request_bus(parent, icdev,
232157497Simp			    IIC_WAIT | IIC_INTR)))
233157497Simp				return (error);
234181305Sjhb			mtx_lock(&sc->ic_lock);
235157497Simp			iicbus_reset(parent, IIC_FASTEST, 0, NULL);
236157497Simp			ifp->if_drv_flags |= IFF_DRV_RUNNING;
237157497Simp		}
238181305Sjhb		mtx_unlock(&sc->ic_lock);
239157497Simp		break;
24038774Snsouch
241157497Simp	case SIOCSIFMTU:
242181305Sjhb		ic_alloc_buffers(sc, ifr->ifr_mtu);
243157497Simp		break;
24438774Snsouch
245157497Simp	case SIOCGIFMTU:
246181305Sjhb		mtx_lock(&sc->ic_lock);
247157497Simp		ifr->ifr_mtu = sc->ic_ifp->if_mtu;
248181305Sjhb		mtx_unlock(&sc->ic_lock);
249157497Simp		break;
25038774Snsouch
251157497Simp	case SIOCADDMULTI:
252157497Simp	case SIOCDELMULTI:
253157497Simp		if (ifr == 0)
254157497Simp			return (EAFNOSUPPORT);		/* XXX */
255157497Simp		switch (ifr->ifr_addr.sa_family) {
256157497Simp		case AF_INET:
257157497Simp			break;
258157497Simp		default:
259157497Simp			return (EAFNOSUPPORT);
260157497Simp		}
261157497Simp		break;
26238774Snsouch	default:
263157497Simp		return (EINVAL);
26438774Snsouch	}
265157497Simp	return (0);
26638774Snsouch}
26738774Snsouch
26838774Snsouch/*
26938774Snsouch * icintr()
27038774Snsouch */
271188461Simpstatic int
272161516Simpicintr(device_t dev, int event, char *ptr)
27338774Snsouch{
27438774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
27538774Snsouch	struct mbuf *top;
276181305Sjhb	int len;
27738774Snsouch
278181305Sjhb	mtx_lock(&sc->ic_lock);
279181305Sjhb
28038774Snsouch	switch (event) {
28138774Snsouch
28238774Snsouch	case INTR_GENERAL:
28338774Snsouch	case INTR_START:
28438774Snsouch		sc->ic_cp = sc->ic_ifbuf;
28538774Snsouch		sc->ic_xfercnt = 0;
286181305Sjhb		sc->ic_flags |= IC_IFBUF_BUSY;
28738774Snsouch		break;
28838774Snsouch
28938774Snsouch	case INTR_STOP:
29038774Snsouch
291157497Simp		/* if any error occured during transfert,
292157497Simp		 * drop the packet */
293181305Sjhb		sc->ic_flags &= ~IC_IFBUF_BUSY;
294181305Sjhb		if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
295181305Sjhb		    IC_BUFFER_WAITER)
296181305Sjhb			wakeup(&sc);
297157497Simp		if (sc->ic_iferrs)
298157497Simp			goto err;
299157497Simp		if ((len = sc->ic_xfercnt) == 0)
300157497Simp			break;					/* ignore */
301157497Simp		if (len <= ICHDRLEN)
302157497Simp			goto err;
303157497Simp		len -= ICHDRLEN;
304157497Simp		sc->ic_ifp->if_ipackets++;
305157497Simp		sc->ic_ifp->if_ibytes += len;
306157497Simp		BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
307157497Simp		top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
308181305Sjhb		if (top) {
309181305Sjhb			mtx_unlock(&sc->ic_lock);
310223741Sbz			M_SETFIB(top, sc->ic_ifp->if_fib);
311157497Simp			netisr_dispatch(NETISR_IP, top);
312181305Sjhb			mtx_lock(&sc->ic_lock);
313181305Sjhb		}
314157497Simp		break;
31538774Snsouch	err:
316181305Sjhb		if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
317157497Simp		sc->ic_iferrs = 0;			/* reset error count */
318157497Simp		sc->ic_ifp->if_ierrors++;
319157497Simp		break;
32038774Snsouch
32138774Snsouch	case INTR_RECEIVE:
322181305Sjhb		if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
323157497Simp			sc->ic_iferrs++;
32438774Snsouch		} else {
32538774Snsouch			*sc->ic_cp++ = *ptr;
326157497Simp			sc->ic_xfercnt++;
32738774Snsouch		}
32838774Snsouch		break;
32938774Snsouch
33038774Snsouch	case INTR_NOACK:			/* xfer terminated by master */
33138774Snsouch		break;
33238774Snsouch
33338774Snsouch	case INTR_TRANSMIT:
33438774Snsouch		*ptr = 0xff;					/* XXX */
33538774Snsouch	  	break;
33638774Snsouch
33738774Snsouch	case INTR_ERROR:
338157497Simp		sc->ic_iferrs++;
33938774Snsouch		break;
34038774Snsouch
34138774Snsouch	default:
34287599Sobrien		panic("%s: unknown event (%d)!", __func__, event);
34338774Snsouch	}
34438774Snsouch
345181305Sjhb	mtx_unlock(&sc->ic_lock);
346188461Simp	return (0);
34738774Snsouch}
34838774Snsouch
34938774Snsouch/*
35038774Snsouch * icoutput()
35138774Snsouch */
35238774Snsouchstatic int
353249925Sglebiusicoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
354191148Skmacy    struct route *ro)
35538774Snsouch{
356181305Sjhb	struct ic_softc *sc = ifp->if_softc;
357181305Sjhb	device_t icdev = sc->ic_dev;
35838774Snsouch	device_t parent = device_get_parent(icdev);
359181305Sjhb	int len, sent;
36038774Snsouch	struct mbuf *mm;
36138774Snsouch	u_char *cp;
362147611Sdwmalone	u_int32_t hdr;
36338774Snsouch
364147611Sdwmalone	/* BPF writes need to be handled specially. */
365147611Sdwmalone	if (dst->sa_family == AF_UNSPEC)
366147611Sdwmalone		bcopy(dst->sa_data, &hdr, sizeof(hdr));
367147611Sdwmalone	else
368147611Sdwmalone		hdr = dst->sa_family;
369147611Sdwmalone
370181305Sjhb	mtx_lock(&sc->ic_lock);
371148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
37238774Snsouch
37338774Snsouch	/* already sending? */
374181305Sjhb	if (sc->ic_flags & IC_SENDING) {
375157497Simp		ifp->if_oerrors++;
37638774Snsouch		goto error;
37738774Snsouch	}
37838774Snsouch
37938774Snsouch	/* insert header */
38038774Snsouch	bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
38138774Snsouch
38238774Snsouch	cp = sc->ic_obuf + ICHDRLEN;
38338774Snsouch	len = 0;
38438774Snsouch	mm = m;
38538774Snsouch	do {
386147256Sbrooks		if (len + mm->m_len > sc->ic_ifp->if_mtu) {
387181305Sjhb			/* packet too large */
388157497Simp			ifp->if_oerrors++;
38938774Snsouch			goto error;
39038774Snsouch		}
39138774Snsouch
39238774Snsouch		bcopy(mtod(mm,char *), cp, mm->m_len);
39338774Snsouch		cp += mm->m_len;
39438774Snsouch		len += mm->m_len;
39538774Snsouch
39638774Snsouch	} while ((mm = mm->m_next));
39738774Snsouch
398123922Ssam	BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
39938774Snsouch
400181305Sjhb	sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
40138774Snsouch
40238774Snsouch	m_freem(m);
403181305Sjhb	mtx_unlock(&sc->ic_lock);
40438774Snsouch
40538774Snsouch	/* send the packet */
40638774Snsouch	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
40738774Snsouch				len + ICHDRLEN, &sent))
40838774Snsouch
409157497Simp		ifp->if_oerrors++;
41038774Snsouch	else {
411157497Simp		ifp->if_opackets++;
41238774Snsouch		ifp->if_obytes += len;
413181305Sjhb	}
41438774Snsouch
415181305Sjhb	mtx_lock(&sc->ic_lock);
416181305Sjhb	sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
417181305Sjhb	if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
418181305Sjhb	    IC_BUFFER_WAITER)
419181305Sjhb		wakeup(&sc);
420181305Sjhb	mtx_unlock(&sc->ic_lock);
42138774Snsouch
42238774Snsouch	return (0);
42338774Snsouch
42438774Snsoucherror:
42538774Snsouch	m_freem(m);
426181305Sjhb	mtx_unlock(&sc->ic_lock);
42738774Snsouch
42838774Snsouch	return(0);
42938774Snsouch}
43038774Snsouch
43138774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
43293023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
43393023SnsouchMODULE_VERSION(ic, 1);
434