xref: /freebsd/sys/dev/iicbus/if_ic.c (revision aa3860851b9f6a6002d135b1cac7736e0995eedc)
1c3e2dc6bSNicolas Souchu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5c3e2dc6bSNicolas Souchu  * All rights reserved.
6c3e2dc6bSNicolas Souchu  *
7c3e2dc6bSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8c3e2dc6bSNicolas Souchu  * modification, are permitted provided that the following conditions
9c3e2dc6bSNicolas Souchu  * are met:
10c3e2dc6bSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12c3e2dc6bSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14c3e2dc6bSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15c3e2dc6bSNicolas Souchu  *
16c3e2dc6bSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c3e2dc6bSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c3e2dc6bSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c3e2dc6bSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c3e2dc6bSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c3e2dc6bSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c3e2dc6bSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c3e2dc6bSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c3e2dc6bSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c3e2dc6bSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c3e2dc6bSNicolas Souchu  * SUCH DAMAGE.
27c3e2dc6bSNicolas Souchu  */
28c3e2dc6bSNicolas Souchu 
29aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
30c3e2dc6bSNicolas Souchu /*
31c3e2dc6bSNicolas Souchu  * I2C bus IP driver
32c3e2dc6bSNicolas Souchu  */
33c3e2dc6bSNicolas Souchu 
34c3e2dc6bSNicolas Souchu #include <sys/param.h>
35c3e2dc6bSNicolas Souchu #include <sys/systm.h>
36c3e2dc6bSNicolas Souchu #include <sys/mbuf.h>
37c3e2dc6bSNicolas Souchu #include <sys/socket.h>
38c3e2dc6bSNicolas Souchu #include <sys/filio.h>
39c3e2dc6bSNicolas Souchu #include <sys/sockio.h>
40c3e2dc6bSNicolas Souchu #include <sys/kernel.h>
41225f9723SJohn Baldwin #include <sys/lock.h>
42c3e2dc6bSNicolas Souchu #include <sys/module.h>
43225f9723SJohn Baldwin #include <sys/mutex.h>
44c3e2dc6bSNicolas Souchu #include <sys/bus.h>
45c3e2dc6bSNicolas Souchu #include <sys/time.h>
46c3e2dc6bSNicolas Souchu #include <sys/malloc.h>
47c3e2dc6bSNicolas Souchu 
48c3e2dc6bSNicolas Souchu #include <net/if.h>
4976039bc8SGleb Smirnoff #include <net/if_var.h>
50c3e2dc6bSNicolas Souchu #include <net/if_types.h>
51c3e2dc6bSNicolas Souchu #include <net/netisr.h>
52c3e2dc6bSNicolas Souchu 
53c3e2dc6bSNicolas Souchu #include <net/route.h>
54c3e2dc6bSNicolas Souchu #include <netinet/in.h>
55c3e2dc6bSNicolas Souchu #include <netinet/in_systm.h>
56c3e2dc6bSNicolas Souchu #include <netinet/in_var.h>
57c3e2dc6bSNicolas Souchu #include <netinet/ip.h>
58c3e2dc6bSNicolas Souchu #include <netinet/if_ether.h>
59c3e2dc6bSNicolas Souchu 
60c3e2dc6bSNicolas Souchu #include <net/bpf.h>
61c3e2dc6bSNicolas Souchu 
62c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h>
63c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h>
64c3e2dc6bSNicolas Souchu 
65c3e2dc6bSNicolas Souchu #include "iicbus_if.h"
66c3e2dc6bSNicolas Souchu 
67c17d4340SNicolas Souchu #define PCF_MASTER_ADDRESS 0xaa
68c17d4340SNicolas Souchu 
69437ffe18SSam Leffler #define ICHDRLEN	sizeof(u_int32_t)
70c3e2dc6bSNicolas Souchu #define ICMTU		1500		/* default mtu */
71c3e2dc6bSNicolas Souchu 
72c3e2dc6bSNicolas Souchu struct ic_softc {
73da247e0dSJustin Hibbits 	if_t ic_ifp;
74225f9723SJohn Baldwin 	device_t ic_dev;
75c3e2dc6bSNicolas Souchu 
76c3e2dc6bSNicolas Souchu 	u_char ic_addr;			/* peer I2C address */
77c3e2dc6bSNicolas Souchu 
78225f9723SJohn Baldwin 	int ic_flags;
79c3e2dc6bSNicolas Souchu 
80c3e2dc6bSNicolas Souchu 	char *ic_obuf;
81c3e2dc6bSNicolas Souchu 	char *ic_ifbuf;
82c3e2dc6bSNicolas Souchu 	char *ic_cp;
83c3e2dc6bSNicolas Souchu 
84c3e2dc6bSNicolas Souchu 	int ic_xfercnt;
85c3e2dc6bSNicolas Souchu 
86c3e2dc6bSNicolas Souchu 	int ic_iferrs;
87225f9723SJohn Baldwin 
88225f9723SJohn Baldwin 	struct mtx ic_lock;
89c3e2dc6bSNicolas Souchu };
90c3e2dc6bSNicolas Souchu 
91225f9723SJohn Baldwin #define	IC_SENDING		0x0001
92225f9723SJohn Baldwin #define	IC_OBUF_BUSY		0x0002
93225f9723SJohn Baldwin #define	IC_IFBUF_BUSY		0x0004
94225f9723SJohn Baldwin #define	IC_BUFFERS_BUSY		(IC_OBUF_BUSY | IC_IFBUF_BUSY)
95225f9723SJohn Baldwin #define	IC_BUFFER_WAITER	0x0004
96225f9723SJohn Baldwin 
97c3e2dc6bSNicolas Souchu static int icprobe(device_t);
98c3e2dc6bSNicolas Souchu static int icattach(device_t);
99c3e2dc6bSNicolas Souchu 
100da247e0dSJustin Hibbits static int icioctl(if_t, u_long, caddr_t);
101da247e0dSJustin Hibbits static int icoutput(if_t, struct mbuf *, const struct sockaddr *,
102279aa3d4SKip Macy                struct route *);
103c3e2dc6bSNicolas Souchu 
104b23193a5SWarner Losh static int icintr(device_t, int, char *);
105c3e2dc6bSNicolas Souchu 
106c3e2dc6bSNicolas Souchu static device_method_t ic_methods[] = {
107c3e2dc6bSNicolas Souchu 	/* device interface */
108c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_probe,		icprobe),
109c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_attach,	icattach),
110c3e2dc6bSNicolas Souchu 
111c3e2dc6bSNicolas Souchu 	/* iicbus interface */
112c3e2dc6bSNicolas Souchu 	DEVMETHOD(iicbus_intr,		icintr),
113c3e2dc6bSNicolas Souchu 
114c3e2dc6bSNicolas Souchu 	{ 0, 0 }
115c3e2dc6bSNicolas Souchu };
116c3e2dc6bSNicolas Souchu 
117c3e2dc6bSNicolas Souchu static driver_t ic_driver = {
118c3e2dc6bSNicolas Souchu 	"ic",
119c3e2dc6bSNicolas Souchu 	ic_methods,
120c3e2dc6bSNicolas Souchu 	sizeof(struct ic_softc),
121c3e2dc6bSNicolas Souchu };
122c3e2dc6bSNicolas Souchu 
123225f9723SJohn Baldwin static void
ic_alloc_buffers(struct ic_softc * sc,int mtu)124225f9723SJohn Baldwin ic_alloc_buffers(struct ic_softc *sc, int mtu)
125225f9723SJohn Baldwin {
126225f9723SJohn Baldwin 	char *obuf, *ifbuf;
127225f9723SJohn Baldwin 
128225f9723SJohn Baldwin 	obuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
129225f9723SJohn Baldwin 	ifbuf = malloc(mtu + ICHDRLEN, M_DEVBUF, M_WAITOK);
130225f9723SJohn Baldwin 
131225f9723SJohn Baldwin 	mtx_lock(&sc->ic_lock);
132225f9723SJohn Baldwin 	while (sc->ic_flags & IC_BUFFERS_BUSY) {
133225f9723SJohn Baldwin 		sc->ic_flags |= IC_BUFFER_WAITER;
134225f9723SJohn Baldwin 		mtx_sleep(sc, &sc->ic_lock, 0, "icalloc", 0);
135225f9723SJohn Baldwin 		sc->ic_flags &= ~IC_BUFFER_WAITER;
136225f9723SJohn Baldwin 	}
137225f9723SJohn Baldwin 
138225f9723SJohn Baldwin 	free(sc->ic_obuf, M_DEVBUF);
139225f9723SJohn Baldwin 	free(sc->ic_ifbuf, M_DEVBUF);
140225f9723SJohn Baldwin 	sc->ic_obuf = obuf;
141225f9723SJohn Baldwin 	sc->ic_ifbuf = ifbuf;
142da247e0dSJustin Hibbits 	if_setmtu(sc->ic_ifp, mtu);
143225f9723SJohn Baldwin 	mtx_unlock(&sc->ic_lock);
144225f9723SJohn Baldwin }
145225f9723SJohn Baldwin 
146c3e2dc6bSNicolas Souchu /*
147c3e2dc6bSNicolas Souchu  * icprobe()
148c3e2dc6bSNicolas Souchu  */
149c3e2dc6bSNicolas Souchu static int
icprobe(device_t dev)150c3e2dc6bSNicolas Souchu icprobe(device_t dev)
151c3e2dc6bSNicolas Souchu {
152789c4b9dSNathan Whitehorn 	return (BUS_PROBE_NOWILDCARD);
153c3e2dc6bSNicolas Souchu }
154c3e2dc6bSNicolas Souchu 
155c3e2dc6bSNicolas Souchu /*
156c3e2dc6bSNicolas Souchu  * icattach()
157c3e2dc6bSNicolas Souchu  */
158c3e2dc6bSNicolas Souchu static int
icattach(device_t dev)159c3e2dc6bSNicolas Souchu icattach(device_t dev)
160c3e2dc6bSNicolas Souchu {
161c3e2dc6bSNicolas Souchu 	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
162da247e0dSJustin Hibbits 	if_t ifp;
163fc74a9f9SBrooks Davis 
164fc74a9f9SBrooks Davis 	ifp = sc->ic_ifp = if_alloc(IFT_PARA);
165c3e2dc6bSNicolas Souchu 
166225f9723SJohn Baldwin 	mtx_init(&sc->ic_lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
167225f9723SJohn Baldwin 	    MTX_DEF);
168c17d4340SNicolas Souchu 	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
169225f9723SJohn Baldwin 	sc->ic_dev = dev;
170c3e2dc6bSNicolas Souchu 
171da247e0dSJustin Hibbits 	if_setsoftc(ifp, sc);
1729bf40edeSBrooks Davis 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
173da247e0dSJustin Hibbits 	if_setflags(ifp, IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST);
174da247e0dSJustin Hibbits 	if_setioctlfn(ifp, icioctl);
175da247e0dSJustin Hibbits 	if_setoutputfn(ifp, icoutput);
176da247e0dSJustin Hibbits 	if_setifheaderlen(ifp, 0);
177da247e0dSJustin Hibbits 	if_setsendqlen(ifp, ifqmaxlen);
178c3e2dc6bSNicolas Souchu 
179225f9723SJohn Baldwin 	ic_alloc_buffers(sc, ICMTU);
180225f9723SJohn Baldwin 
181c3e2dc6bSNicolas Souchu 	if_attach(ifp);
182c3e2dc6bSNicolas Souchu 
183c3e2dc6bSNicolas Souchu 	bpfattach(ifp, DLT_NULL, ICHDRLEN);
184c3e2dc6bSNicolas Souchu 
185c3e2dc6bSNicolas Souchu 	return (0);
186c3e2dc6bSNicolas Souchu }
187c3e2dc6bSNicolas Souchu 
188c3e2dc6bSNicolas Souchu /*
189c3e2dc6bSNicolas Souchu  * iciotcl()
190c3e2dc6bSNicolas Souchu  */
191c3e2dc6bSNicolas Souchu static int
icioctl(if_t ifp,u_long cmd,caddr_t data)192da247e0dSJustin Hibbits icioctl(if_t ifp, u_long cmd, caddr_t data)
193c3e2dc6bSNicolas Souchu {
194da247e0dSJustin Hibbits 	struct ic_softc *sc = if_getsoftc(ifp);
195225f9723SJohn Baldwin 	device_t icdev = sc->ic_dev;
196c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(icdev);
197c3e2dc6bSNicolas Souchu 	struct ifaddr *ifa = (struct ifaddr *)data;
198c3e2dc6bSNicolas Souchu 	struct ifreq *ifr = (struct ifreq *)data;
199c3e2dc6bSNicolas Souchu 	int error;
200c3e2dc6bSNicolas Souchu 
201c3e2dc6bSNicolas Souchu 	switch (cmd) {
202c3e2dc6bSNicolas Souchu 
203c3e2dc6bSNicolas Souchu 	case SIOCAIFADDR:
204c3e2dc6bSNicolas Souchu 	case SIOCSIFADDR:
205c3e2dc6bSNicolas Souchu 		if (ifa->ifa_addr->sa_family != AF_INET)
206522bc71eSWarner Losh 			return (EAFNOSUPPORT);
207225f9723SJohn Baldwin 		mtx_lock(&sc->ic_lock);
208da247e0dSJustin Hibbits 		if_setflagbits(ifp, IFF_UP, 0);
209225f9723SJohn Baldwin 		goto locked;
210c3e2dc6bSNicolas Souchu 	case SIOCSIFFLAGS:
211225f9723SJohn Baldwin 		mtx_lock(&sc->ic_lock);
212225f9723SJohn Baldwin 	locked:
213da247e0dSJustin Hibbits 		if ((!(if_getflags(ifp) & IFF_UP)) &&
214da247e0dSJustin Hibbits 		    (if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
215c3e2dc6bSNicolas Souchu 
216c3e2dc6bSNicolas Souchu 			/* XXX disable PCF */
217da247e0dSJustin Hibbits 			if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
218225f9723SJohn Baldwin 			mtx_unlock(&sc->ic_lock);
219c3e2dc6bSNicolas Souchu 
220c3e2dc6bSNicolas Souchu 			/* IFF_UP is not set, try to release the bus anyway */
221c3e2dc6bSNicolas Souchu 			iicbus_release_bus(parent, icdev);
222c3e2dc6bSNicolas Souchu 			break;
223c3e2dc6bSNicolas Souchu 		}
224da247e0dSJustin Hibbits 		if (((if_getflags(ifp) & IFF_UP)) &&
225da247e0dSJustin Hibbits 		    (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))) {
226225f9723SJohn Baldwin 			mtx_unlock(&sc->ic_lock);
227522bc71eSWarner Losh 			if ((error = iicbus_request_bus(parent, icdev,
228522bc71eSWarner Losh 			    IIC_WAIT | IIC_INTR)))
229c3e2dc6bSNicolas Souchu 				return (error);
230225f9723SJohn Baldwin 			mtx_lock(&sc->ic_lock);
23104f89a63SNicolas Souchu 			iicbus_reset(parent, IIC_FASTEST, 0, NULL);
232da247e0dSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
233c3e2dc6bSNicolas Souchu 		}
234225f9723SJohn Baldwin 		mtx_unlock(&sc->ic_lock);
235c3e2dc6bSNicolas Souchu 		break;
236c3e2dc6bSNicolas Souchu 
237c3e2dc6bSNicolas Souchu 	case SIOCSIFMTU:
238225f9723SJohn Baldwin 		ic_alloc_buffers(sc, ifr->ifr_mtu);
239c3e2dc6bSNicolas Souchu 		break;
240c3e2dc6bSNicolas Souchu 
241c3e2dc6bSNicolas Souchu 	case SIOCGIFMTU:
242225f9723SJohn Baldwin 		mtx_lock(&sc->ic_lock);
243da247e0dSJustin Hibbits 		ifr->ifr_mtu = if_getmtu(sc->ic_ifp);
244225f9723SJohn Baldwin 		mtx_unlock(&sc->ic_lock);
245c3e2dc6bSNicolas Souchu 		break;
246c3e2dc6bSNicolas Souchu 
247c3e2dc6bSNicolas Souchu 	case SIOCADDMULTI:
248c3e2dc6bSNicolas Souchu 	case SIOCDELMULTI:
2494d24901aSPedro F. Giffuni 		if (ifr == NULL)
250522bc71eSWarner Losh 			return (EAFNOSUPPORT);		/* XXX */
251c3e2dc6bSNicolas Souchu 		switch (ifr->ifr_addr.sa_family) {
252c3e2dc6bSNicolas Souchu 		case AF_INET:
253c3e2dc6bSNicolas Souchu 			break;
254c3e2dc6bSNicolas Souchu 		default:
255522bc71eSWarner Losh 			return (EAFNOSUPPORT);
256c3e2dc6bSNicolas Souchu 		}
257c3e2dc6bSNicolas Souchu 		break;
258c3e2dc6bSNicolas Souchu 	default:
259522bc71eSWarner Losh 		return (EINVAL);
260c3e2dc6bSNicolas Souchu 	}
261522bc71eSWarner Losh 	return (0);
262c3e2dc6bSNicolas Souchu }
263c3e2dc6bSNicolas Souchu 
264c3e2dc6bSNicolas Souchu /*
265c3e2dc6bSNicolas Souchu  * icintr()
266c3e2dc6bSNicolas Souchu  */
267b23193a5SWarner Losh static int
icintr(device_t dev,int event,char * ptr)268c3e2dc6bSNicolas Souchu icintr(device_t dev, int event, char *ptr)
269c3e2dc6bSNicolas Souchu {
270c3e2dc6bSNicolas Souchu 	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
271c3e2dc6bSNicolas Souchu 	struct mbuf *top;
272225f9723SJohn Baldwin 	int len;
273c3e2dc6bSNicolas Souchu 
274225f9723SJohn Baldwin 	mtx_lock(&sc->ic_lock);
275c3e2dc6bSNicolas Souchu 
276c3e2dc6bSNicolas Souchu 	switch (event) {
277c3e2dc6bSNicolas Souchu 
278c3e2dc6bSNicolas Souchu 	case INTR_GENERAL:
279c3e2dc6bSNicolas Souchu 	case INTR_START:
280c3e2dc6bSNicolas Souchu 		sc->ic_cp = sc->ic_ifbuf;
281c3e2dc6bSNicolas Souchu 		sc->ic_xfercnt = 0;
282225f9723SJohn Baldwin 		sc->ic_flags |= IC_IFBUF_BUSY;
283c3e2dc6bSNicolas Souchu 		break;
284c3e2dc6bSNicolas Souchu 
285c3e2dc6bSNicolas Souchu 	case INTR_STOP:
286c3e2dc6bSNicolas Souchu 
287453130d9SPedro F. Giffuni 		/* if any error occurred during transfert,
288c3e2dc6bSNicolas Souchu 		 * drop the packet */
289225f9723SJohn Baldwin 		sc->ic_flags &= ~IC_IFBUF_BUSY;
290225f9723SJohn Baldwin 		if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
291225f9723SJohn Baldwin 		    IC_BUFFER_WAITER)
292225f9723SJohn Baldwin 			wakeup(&sc);
293c3e2dc6bSNicolas Souchu 		if (sc->ic_iferrs)
294c3e2dc6bSNicolas Souchu 			goto err;
295c3e2dc6bSNicolas Souchu 		if ((len = sc->ic_xfercnt) == 0)
296c3e2dc6bSNicolas Souchu 			break;					/* ignore */
297c3e2dc6bSNicolas Souchu 		if (len <= ICHDRLEN)
298c3e2dc6bSNicolas Souchu 			goto err;
299c3e2dc6bSNicolas Souchu 		len -= ICHDRLEN;
300c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ic_ifp, IFCOUNTER_IPACKETS, 1);
301c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ic_ifp, IFCOUNTER_IBYTES, len);
302fc74a9f9SBrooks Davis 		BPF_TAP(sc->ic_ifp, sc->ic_ifbuf, len + ICHDRLEN);
303fc74a9f9SBrooks Davis 		top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, sc->ic_ifp, 0);
304225f9723SJohn Baldwin 		if (top) {
305b8a6e03fSGleb Smirnoff 			struct epoch_tracker et;
306b8a6e03fSGleb Smirnoff 
307225f9723SJohn Baldwin 			mtx_unlock(&sc->ic_lock);
308da247e0dSJustin Hibbits 			M_SETFIB(top, if_getfib(sc->ic_ifp));
309b8a6e03fSGleb Smirnoff 			NET_EPOCH_ENTER(et);
3101cafed39SJonathan Lemon 			netisr_dispatch(NETISR_IP, top);
311b8a6e03fSGleb Smirnoff 			NET_EPOCH_EXIT(et);
312225f9723SJohn Baldwin 			mtx_lock(&sc->ic_lock);
313225f9723SJohn Baldwin 		}
314c3e2dc6bSNicolas Souchu 		break;
315c3e2dc6bSNicolas Souchu 	err:
316225f9723SJohn Baldwin 		if_printf(sc->ic_ifp, "errors (%d)!\n", sc->ic_iferrs);
317c3e2dc6bSNicolas Souchu 		sc->ic_iferrs = 0;			/* reset error count */
318c8dfaf38SGleb Smirnoff 		if_inc_counter(sc->ic_ifp, IFCOUNTER_IERRORS, 1);
319c3e2dc6bSNicolas Souchu 		break;
320c3e2dc6bSNicolas Souchu 
321c3e2dc6bSNicolas Souchu 	case INTR_RECEIVE:
322da247e0dSJustin Hibbits 		if (sc->ic_xfercnt >= if_getmtu(sc->ic_ifp) + ICHDRLEN) {
323c3e2dc6bSNicolas Souchu 			sc->ic_iferrs++;
324c3e2dc6bSNicolas Souchu 		} else {
325c3e2dc6bSNicolas Souchu 			*sc->ic_cp++ = *ptr;
326c3e2dc6bSNicolas Souchu 			sc->ic_xfercnt++;
327c3e2dc6bSNicolas Souchu 		}
328c3e2dc6bSNicolas Souchu 		break;
329c3e2dc6bSNicolas Souchu 
330c3e2dc6bSNicolas Souchu 	case INTR_NOACK:			/* xfer terminated by master */
331c3e2dc6bSNicolas Souchu 		break;
332c3e2dc6bSNicolas Souchu 
333c3e2dc6bSNicolas Souchu 	case INTR_TRANSMIT:
334c3e2dc6bSNicolas Souchu 		*ptr = 0xff;					/* XXX */
335c3e2dc6bSNicolas Souchu 	  	break;
336c3e2dc6bSNicolas Souchu 
337c3e2dc6bSNicolas Souchu 	case INTR_ERROR:
338c3e2dc6bSNicolas Souchu 		sc->ic_iferrs++;
339c3e2dc6bSNicolas Souchu 		break;
340c3e2dc6bSNicolas Souchu 
341c3e2dc6bSNicolas Souchu 	default:
3426e551fb6SDavid E. O'Brien 		panic("%s: unknown event (%d)!", __func__, event);
343c3e2dc6bSNicolas Souchu 	}
344c3e2dc6bSNicolas Souchu 
345225f9723SJohn Baldwin 	mtx_unlock(&sc->ic_lock);
346b23193a5SWarner Losh 	return (0);
347c3e2dc6bSNicolas Souchu }
348c3e2dc6bSNicolas Souchu 
349c3e2dc6bSNicolas Souchu /*
350c3e2dc6bSNicolas Souchu  * icoutput()
351c3e2dc6bSNicolas Souchu  */
352c3e2dc6bSNicolas Souchu static int
icoutput(if_t ifp,struct mbuf * m,const struct sockaddr * dst,struct route * ro)353da247e0dSJustin Hibbits icoutput(if_t ifp, struct mbuf *m, const struct sockaddr *dst,
354279aa3d4SKip Macy     struct route *ro)
355c3e2dc6bSNicolas Souchu {
356da247e0dSJustin Hibbits 	struct ic_softc *sc = if_getsoftc(ifp);
357225f9723SJohn Baldwin 	device_t icdev = sc->ic_dev;
358c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(icdev);
359225f9723SJohn Baldwin 	int len, sent;
360c3e2dc6bSNicolas Souchu 	struct mbuf *mm;
361c3e2dc6bSNicolas Souchu 	u_char *cp;
36201399f34SDavid Malone 	u_int32_t hdr;
36301399f34SDavid Malone 
36401399f34SDavid Malone 	/* BPF writes need to be handled specially. */
365*2cb0fce2SSeth Hoffert 	if (dst->sa_family == AF_UNSPEC || dst->sa_family == pseudo_AF_HDRCMPLT)
36601399f34SDavid Malone 		bcopy(dst->sa_data, &hdr, sizeof(hdr));
36701399f34SDavid Malone 	else
36862e1a437SZhenlei Huang 		hdr = RO_GET_FAMILY(ro, dst);
369c3e2dc6bSNicolas Souchu 
370225f9723SJohn Baldwin 	mtx_lock(&sc->ic_lock);
371da247e0dSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
372c3e2dc6bSNicolas Souchu 
373c3e2dc6bSNicolas Souchu 	/* already sending? */
374225f9723SJohn Baldwin 	if (sc->ic_flags & IC_SENDING) {
375c8dfaf38SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
376c3e2dc6bSNicolas Souchu 		goto error;
377c3e2dc6bSNicolas Souchu 	}
378c3e2dc6bSNicolas Souchu 
379c3e2dc6bSNicolas Souchu 	/* insert header */
380c3e2dc6bSNicolas Souchu 	bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
381c3e2dc6bSNicolas Souchu 
382c3e2dc6bSNicolas Souchu 	cp = sc->ic_obuf + ICHDRLEN;
383c3e2dc6bSNicolas Souchu 	len = 0;
384c3e2dc6bSNicolas Souchu 	mm = m;
385c3e2dc6bSNicolas Souchu 	do {
386da247e0dSJustin Hibbits 		if (len + mm->m_len > if_getmtu(sc->ic_ifp)) {
387225f9723SJohn Baldwin 			/* packet too large */
388c8dfaf38SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
389c3e2dc6bSNicolas Souchu 			goto error;
390c3e2dc6bSNicolas Souchu 		}
391c3e2dc6bSNicolas Souchu 
392c3e2dc6bSNicolas Souchu 		bcopy(mtod(mm,char *), cp, mm->m_len);
393c3e2dc6bSNicolas Souchu 		cp += mm->m_len;
394c3e2dc6bSNicolas Souchu 		len += mm->m_len;
395c3e2dc6bSNicolas Souchu 
396c3e2dc6bSNicolas Souchu 	} while ((mm = mm->m_next));
397c3e2dc6bSNicolas Souchu 
398437ffe18SSam Leffler 	BPF_MTAP2(ifp, &hdr, sizeof(hdr), m);
399c3e2dc6bSNicolas Souchu 
400225f9723SJohn Baldwin 	sc->ic_flags |= (IC_SENDING | IC_OBUF_BUSY);
401c3e2dc6bSNicolas Souchu 
402c3e2dc6bSNicolas Souchu 	m_freem(m);
403225f9723SJohn Baldwin 	mtx_unlock(&sc->ic_lock);
404c3e2dc6bSNicolas Souchu 
405c3e2dc6bSNicolas Souchu 	/* send the packet */
406c3e2dc6bSNicolas Souchu 	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
407c3e2dc6bSNicolas Souchu 				len + ICHDRLEN, &sent))
408c3e2dc6bSNicolas Souchu 
409c8dfaf38SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
410c3e2dc6bSNicolas Souchu 	else {
411c8dfaf38SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
412c8dfaf38SGleb Smirnoff 		if_inc_counter(ifp, IFCOUNTER_OBYTES, len);
413c3e2dc6bSNicolas Souchu 	}
414c3e2dc6bSNicolas Souchu 
415225f9723SJohn Baldwin 	mtx_lock(&sc->ic_lock);
416225f9723SJohn Baldwin 	sc->ic_flags &= ~(IC_SENDING | IC_OBUF_BUSY);
417225f9723SJohn Baldwin 	if ((sc->ic_flags & (IC_BUFFERS_BUSY | IC_BUFFER_WAITER)) ==
418225f9723SJohn Baldwin 	    IC_BUFFER_WAITER)
419225f9723SJohn Baldwin 		wakeup(&sc);
420225f9723SJohn Baldwin 	mtx_unlock(&sc->ic_lock);
421c3e2dc6bSNicolas Souchu 
422c3e2dc6bSNicolas Souchu 	return (0);
423c3e2dc6bSNicolas Souchu 
424c3e2dc6bSNicolas Souchu error:
425c3e2dc6bSNicolas Souchu 	m_freem(m);
426225f9723SJohn Baldwin 	mtx_unlock(&sc->ic_lock);
427c3e2dc6bSNicolas Souchu 
428c3e2dc6bSNicolas Souchu 	return(0);
429c3e2dc6bSNicolas Souchu }
430c3e2dc6bSNicolas Souchu 
4313a866152SJohn Baldwin DRIVER_MODULE(ic, iicbus, ic_driver, 0, 0);
432c17d4340SNicolas Souchu MODULE_DEPEND(ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
433c17d4340SNicolas Souchu MODULE_VERSION(ic, 1);
434