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: stable/11/sys/dev/iicbus/if_ic.c 315221 2017-03-14 02:06:03Z pfg $");
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>
49257176Sglebius#include <net/if_var.h>
5038774Snsouch#include <net/if_types.h>
5138774Snsouch#include <net/netisr.h>
5238774Snsouch
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
69123922Ssam#define ICHDRLEN	sizeof(u_int32_t)
7038774Snsouch#define ICMTU		1500		/* default mtu */
7138774Snsouch
7238774Snsouchstruct ic_softc {
73147256Sbrooks	struct ifnet *ic_ifp;
74181305Sjhb	device_t ic_dev;
7538774Snsouch
7638774Snsouch	u_char ic_addr;			/* peer I2C address */
7738774Snsouch
78181305Sjhb	int ic_flags;
7938774Snsouch
8038774Snsouch	char *ic_obuf;
8138774Snsouch	char *ic_ifbuf;
8238774Snsouch	char *ic_cp;
8338774Snsouch
8438774Snsouch	int ic_xfercnt;
8538774Snsouch
8638774Snsouch	int ic_iferrs;
87181305Sjhb
88181305Sjhb	struct mtx ic_lock;
8938774Snsouch};
9038774Snsouch
91181305Sjhb#define	IC_SENDING		0x0001
92181305Sjhb#define	IC_OBUF_BUSY		0x0002
93181305Sjhb#define	IC_IFBUF_BUSY		0x0004
94181305Sjhb#define	IC_BUFFERS_BUSY		(IC_OBUF_BUSY | IC_IFBUF_BUSY)
95181305Sjhb#define	IC_BUFFER_WAITER	0x0004
96181305Sjhb
9738774Snsouchstatic devclass_t ic_devclass;
9838774Snsouch
9938774Snsouchstatic int icprobe(device_t);
10038774Snsouchstatic int icattach(device_t);
10138774Snsouch
10238774Snsouchstatic int icioctl(struct ifnet *, u_long, caddr_t);
103249925Sglebiusstatic int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
104191148Skmacy               struct route *);
10538774Snsouch
106188461Simpstatic int icintr(device_t, int, char *);
10738774Snsouch
10838774Snsouchstatic device_method_t ic_methods[] = {
10938774Snsouch	/* device interface */
11038774Snsouch	DEVMETHOD(device_probe,		icprobe),
11138774Snsouch	DEVMETHOD(device_attach,	icattach),
11238774Snsouch
11338774Snsouch	/* iicbus interface */
11438774Snsouch	DEVMETHOD(iicbus_intr,		icintr),
11538774Snsouch
11638774Snsouch	{ 0, 0 }
11738774Snsouch};
11838774Snsouch
11938774Snsouchstatic driver_t ic_driver = {
12038774Snsouch	"ic",
12138774Snsouch	ic_methods,
12238774Snsouch	sizeof(struct ic_softc),
12338774Snsouch};
12438774Snsouch
125181305Sjhbstatic void
126181305Sjhbic_alloc_buffers(struct ic_softc *sc, int mtu)
127181305Sjhb{
128181305Sjhb	char *obuf, *ifbuf;
129181305Sjhb
130181305Sjhb	obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
131181305Sjhb	ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
132181305Sjhb
133181305Sjhb	mtx_lock(&sc->ic_lock);
134181305Sjhb	while (sc->ic_flags & IC_BUFFERS_BUSY) {
135181305Sjhb		sc->ic_flags |= IC_BUFFER_WAITER;
136181305Sjhb		mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
137181305Sjhb		sc->ic_flags &= ~IC_BUFFER_WAITER;
138181305Sjhb	}
139181305Sjhb
140181305Sjhb	free(sc->ic_obuf, M_DEVBUF);
141181305Sjhb	free(sc->ic_ifbuf, M_DEVBUF);
142181305Sjhb	sc->ic_obuf = obuf;
143181305Sjhb	sc->ic_ifbuf = ifbuf;
144181305Sjhb	sc->ic_ifp->if_mtu = mtu;
145181305Sjhb	mtx_unlock(&sc->ic_lock);
146181305Sjhb}
147181305Sjhb
14838774Snsouch/*
14938774Snsouch * icprobe()
15038774Snsouch */
15138774Snsouchstatic int
15238774Snsouchicprobe(device_t dev)
15338774Snsouch{
154186833Snwhitehorn	return (BUS_PROBE_NOWILDCARD);
15538774Snsouch}
156181305Sjhb
15738774Snsouch/*
15838774Snsouch * icattach()
15938774Snsouch */
16038774Snsouchstatic int
16138774Snsouchicattach(device_t dev)
16238774Snsouch{
16338774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
164147256Sbrooks	struct ifnet *ifp;
16538774Snsouch
166147256Sbrooks	ifp = sc->ic_ifp = if_alloc(IFT_PARA);
167157497Simp	if (ifp == NULL)
168147256Sbrooks		return (ENOSPC);
169147256Sbrooks
170181305Sjhb	mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
171181305Sjhb	    MTX_DEF);
17293023Snsouch	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
173181305Sjhb	sc->ic_dev = dev;
17438774Snsouch
17538774Snsouch	ifp->if_softc = sc;
176121816Sbrooks	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
177181305Sjhb	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
17838774Snsouch	ifp->if_ioctl = icioctl;
17938774Snsouch	ifp->if_output = icoutput;
18038774Snsouch	ifp->if_hdrlen = 0;
18138774Snsouch	ifp->if_addrlen = 0;
182207554Ssobomax	ifp->if_snd.ifq_maxlen = ifqmaxlen;
18338774Snsouch
184181305Sjhb	ic_alloc_buffers(sc, ICMTU);
185181305Sjhb
18638774Snsouch	if_attach(ifp);
18738774Snsouch
18838774Snsouch	bpfattach(ifp, DLT_NULL, ICHDRLEN);
18938774Snsouch
19038774Snsouch	return (0);
19138774Snsouch}
19238774Snsouch
19338774Snsouch/*
19438774Snsouch * iciotcl()
19538774Snsouch */
19638774Snsouchstatic int
19738774Snsouchicioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
19838774Snsouch{
199181305Sjhb	struct ic_softc *sc = ifp->if_softc;
200181305Sjhb	device_t icdev = sc->ic_dev;
201157497Simp	device_t parent = device_get_parent(icdev);
202157497Simp	struct ifaddr *ifa = (struct ifaddr *)data;
203157497Simp	struct ifreq *ifr = (struct ifreq *)data;
204157497Simp	int error;
20538774Snsouch
206157497Simp	switch (cmd) {
20738774Snsouch
208157497Simp	case SIOCAIFADDR:
209157497Simp	case SIOCSIFADDR:
210157497Simp		if (ifa->ifa_addr->sa_family != AF_INET)
211157497Simp			return (EAFNOSUPPORT);
212181305Sjhb		mtx_lock(&sc->ic_lock);
213157497Simp		ifp->if_flags |= IFF_UP;
214181305Sjhb		goto locked;
215157497Simp	case SIOCSIFFLAGS:
216181305Sjhb		mtx_lock(&sc->ic_lock);
217181305Sjhb	locked:
218157497Simp		if ((!(ifp->if_flags & IFF_UP)) &&
219157497Simp		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
22038774Snsouch
221157497Simp			/* XXX disable PCF */
222157497Simp			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
223181305Sjhb			mtx_unlock(&sc->ic_lock);
22438774Snsouch
225157497Simp			/* IFF_UP is not set, try to release the bus anyway */
226157497Simp			iicbus_release_bus(parent, icdev);
227157497Simp			break;
228157497Simp		}
229157497Simp		if (((ifp->if_flags & IFF_UP)) &&
230157497Simp		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
231181305Sjhb			mtx_unlock(&sc->ic_lock);
232157497Simp			if ((error = iicbus_request_bus(parent, icdev,
233157497Simp			    IIC_WAIT | IIC_INTR)))
234157497Simp				return (error);
235181305Sjhb			mtx_lock(&sc->ic_lock);
236157497Simp			iicbus_reset(parent, IIC_FASTEST, 0, NULL);
237157497Simp			ifp->if_drv_flags |= IFF_DRV_RUNNING;
238157497Simp		}
239181305Sjhb		mtx_unlock(&sc->ic_lock);
240157497Simp		break;
24138774Snsouch
242157497Simp	case SIOCSIFMTU:
243181305Sjhb		ic_alloc_buffers(sc, ifr->ifr_mtu);
244157497Simp		break;
24538774Snsouch
246157497Simp	case SIOCGIFMTU:
247181305Sjhb		mtx_lock(&sc->ic_lock);
248157497Simp		ifr->ifr_mtu = sc->ic_ifp->if_mtu;
249181305Sjhb		mtx_unlock(&sc->ic_lock);
250157497Simp		break;
25138774Snsouch
252157497Simp	case SIOCADDMULTI:
253157497Simp	case SIOCDELMULTI:
254315221Spfg		if (ifr == NULL)
255157497Simp			return (EAFNOSUPPORT);		/* XXX */
256157497Simp		switch (ifr->ifr_addr.sa_family) {
257157497Simp		case AF_INET:
258157497Simp			break;
259157497Simp		default:
260157497Simp			return (EAFNOSUPPORT);
261157497Simp		}
262157497Simp		break;
26338774Snsouch	default:
264157497Simp		return (EINVAL);
26538774Snsouch	}
266157497Simp	return (0);
26738774Snsouch}
26838774Snsouch
26938774Snsouch/*
27038774Snsouch * icintr()
27138774Snsouch */
272188461Simpstatic int
273161516Simpicintr(device_t dev, int event, char *ptr)
27438774Snsouch{
27538774Snsouch	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
27638774Snsouch	struct mbuf *top;
277181305Sjhb	int len;
27838774Snsouch
279181305Sjhb	mtx_lock(&sc->ic_lock);
280181305Sjhb
28138774Snsouch	switch (event) {
28238774Snsouch
28338774Snsouch	case INTR_GENERAL:
28438774Snsouch	case INTR_START:
28538774Snsouch		sc->ic_cp = sc->ic_ifbuf;
28638774Snsouch		sc->ic_xfercnt = 0;
287181305Sjhb		sc->ic_flags |= IC_IFBUF_BUSY;
28838774Snsouch		break;
28938774Snsouch
29038774Snsouch	case INTR_STOP:
29138774Snsouch
292298955Spfg		/* if any error occurred during transfert,
293157497Simp		 * drop the packet */
294181305Sjhb		sc->ic_flags &= ~IC_IFBUF_BUSY;
295181305Sjhb		if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
296181305Sjhb		    IC_BUFFER_WAITER)
297181305Sjhb			wakeup(&sc);
298157497Simp		if (sc->ic_iferrs)
299157497Simp			goto err;
300157497Simp		if ((len = sc->ic_xfercnt) == 0)
301157497Simp			break;					/* ignore */
302157497Simp		if (len <= ICHDRLEN)
303157497Simp			goto err;
304157497Simp		len -= ICHDRLEN;
305271849Sglebius		if_inc_counter(sc->ic_ifp, IFCOUNTER_IPACKETS, 1);
306271849Sglebius		if_inc_counter(sc->ic_ifp, IFCOUNTER_IBYTES, len);
307157497Simp		BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
308157497Simp		top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
309181305Sjhb		if (top) {
310181305Sjhb			mtx_unlock(&sc->ic_lock);
311223741Sbz			M_SETFIB(top, sc->ic_ifp->if_fib);
312157497Simp			netisr_dispatch(NETISR_IP, top);
313181305Sjhb			mtx_lock(&sc->ic_lock);
314181305Sjhb		}
315157497Simp		break;
31638774Snsouch	err:
317181305Sjhb		if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
318157497Simp		sc->ic_iferrs = 0;			/* reset error count */
319271849Sglebius		if_inc_counter(sc->ic_ifp, IFCOUNTER_IERRORS, 1);
320157497Simp		break;
32138774Snsouch
32238774Snsouch	case INTR_RECEIVE:
323181305Sjhb		if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
324157497Simp			sc->ic_iferrs++;
32538774Snsouch		} else {
32638774Snsouch			*sc->ic_cp++ = *ptr;
327157497Simp			sc->ic_xfercnt++;
32838774Snsouch		}
32938774Snsouch		break;
33038774Snsouch
33138774Snsouch	case INTR_NOACK:			/* xfer terminated by master */
33238774Snsouch		break;
33338774Snsouch
33438774Snsouch	case INTR_TRANSMIT:
33538774Snsouch		*ptr = 0xff;					/* XXX */
33638774Snsouch	  	break;
33738774Snsouch
33838774Snsouch	case INTR_ERROR:
339157497Simp		sc->ic_iferrs++;
34038774Snsouch		break;
34138774Snsouch
34238774Snsouch	default:
34387599Sobrien		panic("%s: unknown event (%d)!", __func__, event);
34438774Snsouch	}
34538774Snsouch
346181305Sjhb	mtx_unlock(&sc->ic_lock);
347188461Simp	return (0);
34838774Snsouch}
34938774Snsouch
35038774Snsouch/*
35138774Snsouch * icoutput()
35238774Snsouch */
35338774Snsouchstatic int
354249925Sglebiusicoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
355191148Skmacy    struct route *ro)
35638774Snsouch{
357181305Sjhb	struct ic_softc *sc = ifp->if_softc;
358181305Sjhb	device_t icdev = sc->ic_dev;
35938774Snsouch	device_t parent = device_get_parent(icdev);
360181305Sjhb	int len, sent;
36138774Snsouch	struct mbuf *mm;
36238774Snsouch	u_char *cp;
363147611Sdwmalone	u_int32_t hdr;
36438774Snsouch
365147611Sdwmalone	/* BPF writes need to be handled specially. */
366147611Sdwmalone	if (dst->sa_family == AF_UNSPEC)
367147611Sdwmalone		bcopy(dst->sa_data, &hdr, sizeof(hdr));
368147611Sdwmalone	else
369147611Sdwmalone		hdr = dst->sa_family;
370147611Sdwmalone
371181305Sjhb	mtx_lock(&sc->ic_lock);
372148887Srwatson	ifp->if_drv_flags |= IFF_DRV_RUNNING;
37338774Snsouch
37438774Snsouch	/* already sending? */
375181305Sjhb	if (sc->ic_flags & IC_SENDING) {
376271849Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
37738774Snsouch		goto error;
37838774Snsouch	}
37938774Snsouch
38038774Snsouch	/* insert header */
38138774Snsouch	bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
38238774Snsouch
38338774Snsouch	cp = sc->ic_obuf + ICHDRLEN;
38438774Snsouch	len = 0;
38538774Snsouch	mm = m;
38638774Snsouch	do {
387147256Sbrooks		if (len + mm->m_len > sc->ic_ifp->if_mtu) {
388181305Sjhb			/* packet too large */
389271849Sglebius			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
39038774Snsouch			goto error;
39138774Snsouch		}
39238774Snsouch
39338774Snsouch		bcopy(mtod(mm,char *), cp, mm->m_len);
39438774Snsouch		cp += mm->m_len;
39538774Snsouch		len += mm->m_len;
39638774Snsouch
39738774Snsouch	} while ((mm = mm->m_next));
39838774Snsouch
399123922Ssam	BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
40038774Snsouch
401181305Sjhb	sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
40238774Snsouch
40338774Snsouch	m_freem(m);
404181305Sjhb	mtx_unlock(&sc->ic_lock);
40538774Snsouch
40638774Snsouch	/* send the packet */
40738774Snsouch	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
40838774Snsouch				len + ICHDRLEN, &sent))
40938774Snsouch
410271849Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
41138774Snsouch	else {
412271849Sglebius		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
413271849Sglebius		if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
414181305Sjhb	}
41538774Snsouch
416181305Sjhb	mtx_lock(&sc->ic_lock);
417181305Sjhb	sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
418181305Sjhb	if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
419181305Sjhb	    IC_BUFFER_WAITER)
420181305Sjhb		wakeup(&sc);
421181305Sjhb	mtx_unlock(&sc->ic_lock);
42238774Snsouch
42338774Snsouch	return (0);
42438774Snsouch
42538774Snsoucherror:
42638774Snsouch	m_freem(m);
427181305Sjhb	mtx_unlock(&sc->ic_lock);
42838774Snsouch
42938774Snsouch	return(0);
43038774Snsouch}
43138774Snsouch
43238774SnsouchDRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
43393023SnsouchMODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
43493023SnsouchMODULE_VERSION(ic, 1);
435