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