1/*-
2 * Copyright (c) 1998, 2001 Nicolas Souchu
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: releng/10.3/sys/dev/iicbus/if_ic.c 255471 2013-09-11 09:19:44Z glebius $");
29
30/*
31 * I2C bus IP driver
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/mbuf.h>
37#include <sys/socket.h>
38#include <sys/filio.h>
39#include <sys/sockio.h>
40#include <sys/kernel.h>
41#include <sys/lock.h>
42#include <sys/module.h>
43#include <sys/mutex.h>
44#include <sys/bus.h>
45#include <sys/time.h>
46#include <sys/malloc.h>
47
48#include <net/if.h>
49#include <net/if_types.h>
50#include <net/netisr.h>
51
52#include <net/route.h>
53#include <netinet/in.h>
54#include <netinet/in_systm.h>
55#include <netinet/in_var.h>
56#include <netinet/ip.h>
57#include <netinet/if_ether.h>
58
59#include <net/bpf.h>
60
61#include <dev/iicbus/iiconf.h>
62#include <dev/iicbus/iicbus.h>
63
64#include "iicbus_if.h"
65
66#define PCF_MASTER_ADDRESS 0xaa
67
68#define ICHDRLEN	sizeof(u_int32_t)
69#define ICMTU		1500		/* default mtu */
70
71struct ic_softc {
72	struct ifnet *ic_ifp;
73	device_t ic_dev;
74
75	u_char ic_addr;			/* peer I2C address */
76
77	int ic_flags;
78
79	char *ic_obuf;
80	char *ic_ifbuf;
81	char *ic_cp;
82
83	int ic_xfercnt;
84
85	int ic_iferrs;
86
87	struct mtx ic_lock;
88};
89
90#define	IC_SENDING		0x0001
91#define	IC_OBUF_BUSY		0x0002
92#define	IC_IFBUF_BUSY		0x0004
93#define	IC_BUFFERS_BUSY		(IC_OBUF_BUSY | IC_IFBUF_BUSY)
94#define	IC_BUFFER_WAITER	0x0004
95
96static devclass_t ic_devclass;
97
98static int icprobe(device_t);
99static int icattach(device_t);
100
101static int icioctl(struct ifnet *, u_long, caddr_t);
102static int icoutput(struct ifnet *, struct mbuf *, const struct sockaddr *,
103               struct route *);
104
105static int icintr(device_t, int, char *);
106
107static device_method_t ic_methods[] = {
108	/* device interface */
109	DEVMETHOD(device_probe,		icprobe),
110	DEVMETHOD(device_attach,	icattach),
111
112	/* iicbus interface */
113	DEVMETHOD(iicbus_intr,		icintr),
114
115	{ 0, 0 }
116};
117
118static driver_t ic_driver = {
119	"ic",
120	ic_methods,
121	sizeof(struct ic_softc),
122};
123
124static void
125ic_alloc_buffers(struct ic_softc *sc, int mtu)
126{
127	char *obuf, *ifbuf;
128
129	obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
130	ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
131
132	mtx_lock(&sc->ic_lock);
133	while (sc->ic_flags & IC_BUFFERS_BUSY) {
134		sc->ic_flags |= IC_BUFFER_WAITER;
135		mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
136		sc->ic_flags &= ~IC_BUFFER_WAITER;
137	}
138
139	free(sc->ic_obuf, M_DEVBUF);
140	free(sc->ic_ifbuf, M_DEVBUF);
141	sc->ic_obuf = obuf;
142	sc->ic_ifbuf = ifbuf;
143	sc->ic_ifp->if_mtu = mtu;
144	mtx_unlock(&sc->ic_lock);
145}
146
147/*
148 * icprobe()
149 */
150static int
151icprobe(device_t dev)
152{
153	return (BUS_PROBE_NOWILDCARD);
154}
155
156/*
157 * icattach()
158 */
159static int
160icattach(device_t dev)
161{
162	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
163	struct ifnet *ifp;
164
165	ifp = sc->ic_ifp = if_alloc(IFT_PARA);
166	if (ifp == NULL)
167		return (ENOSPC);
168
169	mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
170	    MTX_DEF);
171	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
172	sc->ic_dev = dev;
173
174	ifp->if_softc = sc;
175	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
176	ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
177	ifp->if_ioctl = icioctl;
178	ifp->if_output = icoutput;
179	ifp->if_hdrlen = 0;
180	ifp->if_addrlen = 0;
181	ifp->if_snd.ifq_maxlen = ifqmaxlen;
182
183	ic_alloc_buffers(sc, ICMTU);
184
185	if_attach(ifp);
186
187	bpfattach(ifp, DLT_NULL, ICHDRLEN);
188
189	return (0);
190}
191
192/*
193 * iciotcl()
194 */
195static int
196icioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
197{
198	struct ic_softc *sc = ifp->if_softc;
199	device_t icdev = sc->ic_dev;
200	device_t parent = device_get_parent(icdev);
201	struct ifaddr *ifa = (struct ifaddr *)data;
202	struct ifreq *ifr = (struct ifreq *)data;
203	int error;
204
205	switch (cmd) {
206
207	case SIOCAIFADDR:
208	case SIOCSIFADDR:
209		if (ifa->ifa_addr->sa_family != AF_INET)
210			return (EAFNOSUPPORT);
211		mtx_lock(&sc->ic_lock);
212		ifp->if_flags |= IFF_UP;
213		goto locked;
214	case SIOCSIFFLAGS:
215		mtx_lock(&sc->ic_lock);
216	locked:
217		if ((!(ifp->if_flags & IFF_UP)) &&
218		    (ifp->if_drv_flags & IFF_DRV_RUNNING)) {
219
220			/* XXX disable PCF */
221			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
222			mtx_unlock(&sc->ic_lock);
223
224			/* IFF_UP is not set, try to release the bus anyway */
225			iicbus_release_bus(parent, icdev);
226			break;
227		}
228		if (((ifp->if_flags & IFF_UP)) &&
229		    (!(ifp->if_drv_flags & IFF_DRV_RUNNING))) {
230			mtx_unlock(&sc->ic_lock);
231			if ((error = iicbus_request_bus(parent, icdev,
232			    IIC_WAIT | IIC_INTR)))
233				return (error);
234			mtx_lock(&sc->ic_lock);
235			iicbus_reset(parent, IIC_FASTEST, 0, NULL);
236			ifp->if_drv_flags |= IFF_DRV_RUNNING;
237		}
238		mtx_unlock(&sc->ic_lock);
239		break;
240
241	case SIOCSIFMTU:
242		ic_alloc_buffers(sc, ifr->ifr_mtu);
243		break;
244
245	case SIOCGIFMTU:
246		mtx_lock(&sc->ic_lock);
247		ifr->ifr_mtu = sc->ic_ifp->if_mtu;
248		mtx_unlock(&sc->ic_lock);
249		break;
250
251	case SIOCADDMULTI:
252	case SIOCDELMULTI:
253		if (ifr == 0)
254			return (EAFNOSUPPORT);		/* XXX */
255		switch (ifr->ifr_addr.sa_family) {
256		case AF_INET:
257			break;
258		default:
259			return (EAFNOSUPPORT);
260		}
261		break;
262	default:
263		return (EINVAL);
264	}
265	return (0);
266}
267
268/*
269 * icintr()
270 */
271static int
272icintr(device_t dev, int event, char *ptr)
273{
274	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
275	struct mbuf *top;
276	int len;
277
278	mtx_lock(&sc->ic_lock);
279
280	switch (event) {
281
282	case INTR_GENERAL:
283	case INTR_START:
284		sc->ic_cp = sc->ic_ifbuf;
285		sc->ic_xfercnt = 0;
286		sc->ic_flags |= IC_IFBUF_BUSY;
287		break;
288
289	case INTR_STOP:
290
291		/* if any error occured during transfert,
292		 * drop the packet */
293		sc->ic_flags &= ~IC_IFBUF_BUSY;
294		if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
295		    IC_BUFFER_WAITER)
296			wakeup(&sc);
297		if (sc->ic_iferrs)
298			goto err;
299		if ((len = sc->ic_xfercnt) == 0)
300			break;					/* ignore */
301		if (len <= ICHDRLEN)
302			goto err;
303		len -= ICHDRLEN;
304		sc->ic_ifp->if_ipackets++;
305		sc->ic_ifp->if_ibytes += len;
306		BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
307		top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
308		if (top) {
309			mtx_unlock(&sc->ic_lock);
310			M_SETFIB(top, sc->ic_ifp->if_fib);
311			netisr_dispatch(NETISR_IP, top);
312			mtx_lock(&sc->ic_lock);
313		}
314		break;
315	err:
316		if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
317		sc->ic_iferrs = 0;			/* reset error count */
318		sc->ic_ifp->if_ierrors++;
319		break;
320
321	case INTR_RECEIVE:
322		if (sc->ic_xfercnt >= sc->ic_ifp->if_mtu + ICHDRLEN) {
323			sc->ic_iferrs++;
324		} else {
325			*sc->ic_cp++ = *ptr;
326			sc->ic_xfercnt++;
327		}
328		break;
329
330	case INTR_NOACK:			/* xfer terminated by master */
331		break;
332
333	case INTR_TRANSMIT:
334		*ptr = 0xff;					/* XXX */
335	  	break;
336
337	case INTR_ERROR:
338		sc->ic_iferrs++;
339		break;
340
341	default:
342		panic("%s: unknown event (%d)!", __func__, event);
343	}
344
345	mtx_unlock(&sc->ic_lock);
346	return (0);
347}
348
349/*
350 * icoutput()
351 */
352static int
353icoutput(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
354    struct route *ro)
355{
356	struct ic_softc *sc = ifp->if_softc;
357	device_t icdev = sc->ic_dev;
358	device_t parent = device_get_parent(icdev);
359	int len, sent;
360	struct mbuf *mm;
361	u_char *cp;
362	u_int32_t hdr;
363
364	/* BPF writes need to be handled specially. */
365	if (dst->sa_family == AF_UNSPEC)
366		bcopy(dst->sa_data, &hdr, sizeof(hdr));
367	else
368		hdr = dst->sa_family;
369
370	mtx_lock(&sc->ic_lock);
371	ifp->if_drv_flags |= IFF_DRV_RUNNING;
372
373	/* already sending? */
374	if (sc->ic_flags & IC_SENDING) {
375		ifp->if_oerrors++;
376		goto error;
377	}
378
379	/* insert header */
380	bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
381
382	cp = sc->ic_obuf + ICHDRLEN;
383	len = 0;
384	mm = m;
385	do {
386		if (len + mm->m_len > sc->ic_ifp->if_mtu) {
387			/* packet too large */
388			ifp->if_oerrors++;
389			goto error;
390		}
391
392		bcopy(mtod(mm,char *), cp, mm->m_len);
393		cp += mm->m_len;
394		len += mm->m_len;
395
396	} while ((mm = mm->m_next));
397
398	BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
399
400	sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
401
402	m_freem(m);
403	mtx_unlock(&sc->ic_lock);
404
405	/* send the packet */
406	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
407				len + ICHDRLEN, &sent))
408
409		ifp->if_oerrors++;
410	else {
411		ifp->if_opackets++;
412		ifp->if_obytes += len;
413	}
414
415	mtx_lock(&sc->ic_lock);
416	sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
417	if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
418	    IC_BUFFER_WAITER)
419		wakeup(&sc);
420	mtx_unlock(&sc->ic_lock);
421
422	return (0);
423
424error:
425	m_freem(m);
426	mtx_unlock(&sc->ic_lock);
427
428	return(0);
429}
430
431DRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
432MODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
433MODULE_VERSION(ic, 1);
434