1be8d0f7608386c75a7d5079176dab372e93155d
[dragonfly.git] / sys / dev / netif / ic / if_ic.c
1 /*-
2  * Copyright (c) 1998 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  * $FreeBSD: src/sys/dev/iicbus/if_ic.c,v 1.8 1999/12/29 04:35:39 peter Exp $
27  * $DragonFly: src/sys/dev/netif/ic/if_ic.c,v 1.18 2008/01/06 16:55:50 swildner Exp $
28  */
29
30 /*
31  * I2C bus IP driver
32  */
33
34 #ifdef _KERNEL
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/proc.h>
38 #include <sys/mbuf.h>
39 #include <sys/socket.h>
40 #include <sys/filio.h>
41 #include <sys/sockio.h>
42 #include <sys/kernel.h>
43 #include <sys/module.h>
44 #include <sys/bus.h>
45 #include <sys/time.h>
46 #include <sys/malloc.h>
47 #include <sys/thread2.h>
48
49 #include <net/if.h>
50 #include <net/ifq_var.h>
51 #include <net/if_types.h>
52 #include <net/netisr.h>
53
54 #endif
55 #include <sys/mbuf.h>
56 #include <sys/socket.h>
57 #include <net/netisr.h>
58 #include <net/route.h>
59 #include <netinet/in.h>
60 #include <netinet/in_systm.h>
61 #include <netinet/in_var.h>
62 #include <netinet/ip.h>
63 #include <netinet/if_ether.h>
64
65 #include <net/bpf.h>
66
67 #include <bus/iicbus/iiconf.h>
68 #include <bus/iicbus/iicbus.h>
69
70 #include "iicbus_if.h"
71
72 #define PCF_MASTER_ADDRESS 0xaa
73
74 #define ICHDRLEN        sizeof(uint32_t)
75 #define ICMTU           1500            /* default mtu */
76
77 struct ic_softc {
78         struct ifnet ic_if;
79
80         u_char ic_addr;                 /* peer I2C address */
81
82         int ic_sending;
83
84         char *ic_obuf;
85         char *ic_ifbuf;
86         char *ic_cp;
87
88         int ic_xfercnt;
89
90         int ic_iferrs;
91 };
92
93 static devclass_t ic_devclass;
94
95 static int icprobe(device_t);
96 static int icattach(device_t);
97
98 static int icioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
99 static int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
100                 struct rtentry *);
101
102 static void icintr(device_t, int, char *);
103
104 static device_method_t ic_methods[] = {
105         /* device interface */
106         DEVMETHOD(device_probe,         icprobe),
107         DEVMETHOD(device_attach,        icattach),
108
109         /* iicbus interface */
110         DEVMETHOD(iicbus_intr,          icintr),
111
112         { 0, 0 }
113 };
114
115 static driver_t ic_driver = {
116         "ic",
117         ic_methods,
118         sizeof(struct ic_softc),
119 };
120
121 /*
122  * icprobe()
123  */
124 static int
125 icprobe(device_t dev)
126 {
127         return (0);
128 }
129         
130 /*
131  * icattach()
132  */
133 static int
134 icattach(device_t dev)
135 {
136         struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
137         struct ifnet *ifp = &sc->ic_if;
138
139         sc->ic_addr = PCF_MASTER_ADDRESS;       /* XXX only PCF masters */
140
141         ifp->if_softc = sc;
142         if_initname(ifp, "ic", device_get_unit(dev));
143         ifp->if_mtu = ICMTU;
144         ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
145         ifp->if_ioctl = icioctl;
146         ifp->if_output = icoutput;
147         ifp->if_type = IFT_PARA;
148         ifp->if_hdrlen = 0;
149         ifp->if_addrlen = 0;
150         ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
151
152         if_attach(ifp, NULL);
153
154         bpfattach(ifp, DLT_NULL, ICHDRLEN);
155
156         return (0);
157 }
158
159 /*
160  * iciotcl()
161  */
162 static int
163 icioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
164 {
165     device_t icdev = devclass_get_device(ic_devclass, ifp->if_dunit);
166     device_t parent = device_get_parent(icdev);
167     struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev);
168
169     struct ifaddr *ifa = (struct ifaddr *)data;
170     struct ifreq *ifr = (struct ifreq *)data;
171
172     u_char *iptr, *optr;
173     int error;
174
175     switch (cmd) {
176
177     case SIOCSIFDSTADDR:
178     case SIOCAIFADDR:
179     case SIOCSIFADDR:
180         if (ifa->ifa_addr->sa_family != AF_INET)
181             return EAFNOSUPPORT;
182         ifp->if_flags |= IFF_UP;
183         /* FALLTHROUGH */
184     case SIOCSIFFLAGS:
185         if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
186
187             /* XXX disable PCF */
188             ifp->if_flags &= ~IFF_RUNNING;
189
190             /* IFF_UP is not set, try to release the bus anyway */
191             iicbus_release_bus(parent, icdev);
192             break;
193         }
194         if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
195
196             if ((error = iicbus_request_bus(parent, icdev, IIC_WAIT|IIC_INTR)))
197                 return (error);
198
199             sc->ic_obuf = kmalloc(sc->ic_if.if_mtu + ICHDRLEN,
200                                   M_DEVBUF, M_WAITOK);
201
202             sc->ic_ifbuf = kmalloc(sc->ic_if.if_mtu + ICHDRLEN,
203                                   M_DEVBUF, M_WAITOK);
204
205             iicbus_reset(parent, IIC_FASTEST, 0, NULL);
206
207             ifp->if_flags |= IFF_RUNNING;
208         }
209         break;
210
211     case SIOCSIFMTU:
212         /* save previous buffers */
213         iptr = sc->ic_ifbuf;
214         optr = sc->ic_obuf;
215
216         /* allocate input buffer */
217         sc->ic_ifbuf = kmalloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_WAITOK);
218
219         /* allocate output buffer */
220         sc->ic_ifbuf = kmalloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_WAITOK);
221
222         if (iptr)
223             kfree(iptr,M_DEVBUF);
224
225         if (optr)
226             kfree(optr,M_DEVBUF);
227
228         sc->ic_if.if_mtu = ifr->ifr_mtu;
229         break;
230
231     case SIOCGIFMTU:
232         ifr->ifr_mtu = sc->ic_if.if_mtu;
233         break;
234
235     case SIOCADDMULTI:
236     case SIOCDELMULTI:
237         if (ifr == 0) {
238             return EAFNOSUPPORT;                /* XXX */
239         }
240         switch (ifr->ifr_addr.sa_family) {
241
242         case AF_INET:
243             break;
244
245         default:
246             return EAFNOSUPPORT;
247         }
248         break;
249
250     default:
251         return EINVAL;
252     }
253     return 0;
254 }
255
256 /*
257  * icintr()
258  */
259 static void
260 icintr (device_t dev, int event, char *ptr)
261 {
262         struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
263         int unit = device_get_unit(dev);
264         int len;
265         struct mbuf *top;
266         
267         crit_enter();
268
269         switch (event) {
270
271         case INTR_GENERAL:
272         case INTR_START:
273                 sc->ic_cp = sc->ic_ifbuf;
274                 sc->ic_xfercnt = 0;
275                 break;
276
277         case INTR_STOP:
278
279           /* if any error occured during transfert,
280            * drop the packet */
281           if (sc->ic_iferrs)
282             goto err;
283
284           if ((len = sc->ic_xfercnt) == 0)
285                 break;                                  /* ignore */
286
287           if (len <= ICHDRLEN)
288             goto err;
289
290           len -= ICHDRLEN;
291           sc->ic_if.if_ipackets ++;
292           sc->ic_if.if_ibytes += len;
293
294           BPF_TAP(&sc->ic_if, sc->ic_ifbuf, len + ICHDRLEN);
295
296           top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, &sc->ic_if, 0);
297
298           if (top)
299             netisr_dispatch(NETISR_IP, top);
300           break;
301
302         err:
303           kprintf("ic%d: errors (%d)!\n", unit, sc->ic_iferrs);
304
305           sc->ic_iferrs = 0;                    /* reset error count */
306           sc->ic_if.if_ierrors ++;
307
308           break;
309
310         case INTR_RECEIVE:
311                 if (sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN) {
312                         sc->ic_iferrs ++;
313
314                 } else {
315                         *sc->ic_cp++ = *ptr;
316                         sc->ic_xfercnt ++;
317                 }
318                 break;
319
320         case INTR_NOACK:                        /* xfer terminated by master */
321                 break;
322
323         case INTR_TRANSMIT:
324                 *ptr = 0xff;                                    /* XXX */
325                 break;
326
327         case INTR_ERROR:
328                 sc->ic_iferrs ++;
329                 break;
330
331         default:
332                 panic("%s: unknown event (%d)!", __func__, event);
333         }
334
335         crit_exit();
336 }
337
338 /*
339  * icoutput()
340  */
341 static int
342 icoutput(struct ifnet *ifp, struct mbuf *m,
343         struct sockaddr *dst, struct rtentry *rt)
344 {
345         device_t icdev = devclass_get_device(ic_devclass, ifp->if_dunit);
346         device_t parent = device_get_parent(icdev);
347         struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev);
348         int len, sent;
349         struct mbuf *mm;
350         u_char *cp;
351         uint32_t hdr = dst->sa_family;
352
353         ifp->if_flags |= IFF_RUNNING;
354
355         crit_enter();
356
357         /* already sending? */
358         if (sc->ic_sending) {
359                 ifp->if_oerrors ++;
360                 goto error;
361         }
362                 
363         /* insert header */
364         bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
365
366         cp = sc->ic_obuf + ICHDRLEN;
367         len = 0;
368         mm = m;
369         do {
370                 if (len + mm->m_len > sc->ic_if.if_mtu) {
371                         /* packet to large */
372                         ifp->if_oerrors ++;
373                         goto error;
374                 }
375                         
376                 bcopy(mtod(mm,char *), cp, mm->m_len);
377                 cp += mm->m_len;
378                 len += mm->m_len;
379
380         } while ((mm = mm->m_next));
381
382         if (ifp->if_bpf)
383                 bpf_ptap(ifp->if_bpf, m, &hdr, ICHDRLEN);
384
385         sc->ic_sending = 1;
386
387         m_freem(m);
388
389         crit_exit();
390
391         /* send the packet */
392         if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
393                                 len + ICHDRLEN, &sent))
394
395                 ifp->if_oerrors ++;
396         else {
397                 ifp->if_opackets ++;
398                 ifp->if_obytes += len;
399         }
400
401         sc->ic_sending = 0;
402
403         return (0);
404
405 error:
406         m_freem(m);
407         crit_exit();
408
409         return(0);
410 }
411
412 DRIVER_MODULE(if_ic, iicbus, ic_driver, ic_devclass, 0, 0);
413 MODULE_DEPEND(if_ic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
414 MODULE_VERSION(if_ic, 1);