mb86950.c revision 1.1 1 1.1 jdolecek /* $NetBSD: mb86950.c,v 1.1 2005/04/03 11:21:44 jdolecek Exp $ */
2 1.1 jdolecek
3 1.1 jdolecek /*
4 1.1 jdolecek * All Rights Reserved, Copyright (C) Fujitsu Limited 1995
5 1.1 jdolecek *
6 1.1 jdolecek * This software may be used, modified, copied, distributed, and sold, in
7 1.1 jdolecek * both source and binary form provided that the above copyright, these
8 1.1 jdolecek * terms and the following disclaimer are retained. The name of the author
9 1.1 jdolecek * and/or the contributor may not be used to endorse or promote products
10 1.1 jdolecek * derived from this software without specific prior written permission.
11 1.1 jdolecek *
12 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE CONTRIBUTOR ``AS IS'' AND
13 1.1 jdolecek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 1.1 jdolecek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 1.1 jdolecek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE CONTRIBUTOR BE LIABLE
16 1.1 jdolecek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 1.1 jdolecek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18 1.1 jdolecek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION.
19 1.1 jdolecek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20 1.1 jdolecek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21 1.1 jdolecek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22 1.1 jdolecek * SUCH DAMAGE.
23 1.1 jdolecek */
24 1.1 jdolecek
25 1.1 jdolecek /*
26 1.1 jdolecek * Portions copyright (C) 1993, David Greenman. This software may be used,
27 1.1 jdolecek * modified, copied, distributed, and sold, in both source and binary form
28 1.1 jdolecek * provided that the above copyright and these terms are retained. Under no
29 1.1 jdolecek * circumstances is the author responsible for the proper functioning of this
30 1.1 jdolecek * software, nor does the author assume any responsibility for damages
31 1.1 jdolecek * incurred with its use.
32 1.1 jdolecek */
33 1.1 jdolecek
34 1.1 jdolecek /*
35 1.1 jdolecek * Portions copyright (c) 1995 Mika Kortelainen
36 1.1 jdolecek * All rights reserved.
37 1.1 jdolecek *
38 1.1 jdolecek * Redistribution and use in source and binary forms, with or without
39 1.1 jdolecek * modification, are permitted provided that the following conditions
40 1.1 jdolecek * are met:
41 1.1 jdolecek * 1. Redistributions of source code must retain the above copyright
42 1.1 jdolecek * notice, this list of conditions and the following disclaimer.
43 1.1 jdolecek * 2. Redistributions in binary form must reproduce the above copyright
44 1.1 jdolecek * notice, this list of conditions and the following disclaimer in the
45 1.1 jdolecek * documentation and/or other materials provided with the distribution.
46 1.1 jdolecek * 3. All advertising materials mentioning features or use of this software
47 1.1 jdolecek * must display the following acknowledgement:
48 1.1 jdolecek * This product includes software developed by Mika Kortelainen
49 1.1 jdolecek * 4. The name of the author may not be used to endorse or promote products
50 1.1 jdolecek * derived from this software without specific prior written permission
51 1.1 jdolecek *
52 1.1 jdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
53 1.1 jdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
54 1.1 jdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
55 1.1 jdolecek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
56 1.1 jdolecek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
57 1.1 jdolecek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58 1.1 jdolecek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59 1.1 jdolecek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60 1.1 jdolecek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
61 1.1 jdolecek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 1.1 jdolecek */
63 1.1 jdolecek
64 1.1 jdolecek /*
65 1.1 jdolecek * Device driver for Fujitsu MB86960A/MB86965A based Ethernet cards.
66 1.1 jdolecek * Contributed by M.S. <seki (at) sysrap.cs.fujitsu.co.jp>
67 1.1 jdolecek */
68 1.1 jdolecek
69 1.1 jdolecek #include <sys/cdefs.h>
70 1.1 jdolecek __KERNEL_RCSID(0, "$NetBSD: mb86950.c,v 1.1 2005/04/03 11:21:44 jdolecek Exp $");
71 1.1 jdolecek
72 1.1 jdolecek /*
73 1.1 jdolecek * Device driver for Fujitsu mb86950 based Ethernet cards.
74 1.1 jdolecek * Adapted by Dave J. Barnes from various Internet sources including
75 1.1 jdolecek * mb86960.c (NetBSD), if_qn.c (NetBSD/Amiga), DOS Packet Driver (Brian Fisher,
76 1.1 jdolecek * Queens University), EtherBoot Driver (Ken Yap).
77 1.1 jdolecek */
78 1.1 jdolecek
79 1.1 jdolecek /* XXX There are still rough edges......
80 1.1 jdolecek
81 1.1 jdolecek * (1) There is no watchdog timer for the transmitter. It's doubtful that
82 1.1 jdolecek * transmit from the chip could be restarted without a hardware reset though.
83 1.1 jdolecek Fixed - not fully tested
84 1.1 jdolecek
85 1.1 jdolecek * (2) The media interface callback goo is broke. No big deal since to change
86 1.1 jdolecek * from aui to bnc on the old Tiara LANCard requires moving 8 board jumpers.
87 1.1 jdolecek * Other cards (SMC ?) using the EtherStar chip may support media change via software.
88 1.1 jdolecek Fixed - tested
89 1.1 jdolecek
90 1.1 jdolecek * (3) The maximum outstanding transmit packets is set to 4. What is a good limit
91 1.1 jdolecek * of outstanding transmit packets for the EtherStar? Is there a way to tell how
92 1.1 jdolecek * many bytes are remaining to be transmitted? [no]
93 1.1 jdolecek ---
94 1.1 jdolecek When the EtherStar was designed, CPU power was a fraction of what it is now. The
95 1.1 jdolecek single EtherStar transmit buffer was fine. It was unlikely that the CPU could
96 1.1 jdolecek outrun the EtherStar. However, things in 2004 are quite different. sc->txb_size
97 1.1 jdolecek is used to keep the CPU from overrunning the EtherStar. At most allow one packet
98 1.1 jdolecek transmitting and one going into the fifo.
99 1.1 jdolecek ---
100 1.1 jdolecek No, that isn't right either :(
101 1.1 jdolecek
102 1.1 jdolecek * (4) Multicast isn't supported. Feel free to add multicast code if you know how
103 1.1 jdolecek * to make the EtherStar do multicast. Otherwise you'd have to use promiscuous mode
104 1.1 jdolecek * and do multicast in software. OUCH!
105 1.1 jdolecek
106 1.1 jdolecek * (5) There are no bus_space_barrier calls used. Are they needed? Maybe not.
107 1.1 jdolecek
108 1.1 jdolecek * (6) Access to the fifo assumes word (16 bit) mode. Cards configured for byte
109 1.1 jdolecek * wide fifo access will require driver code changes.
110 1.1 jdolecek
111 1.1 jdolecek * Only the minimum code necessary to make the Tiara LANCard work has been tested.
112 1.1 jdolecek * Other cards may require more work, especially byte mode fifo and if DMA is used.
113 1.1 jdolecek
114 1.1 jdolecek * djb / 2004
115 1.1 jdolecek
116 1.1 jdolecek */
117 1.1 jdolecek
118 1.1 jdolecek #include "opt_inet.h"
119 1.1 jdolecek #include "opt_ns.h"
120 1.1 jdolecek #include "bpfilter.h"
121 1.1 jdolecek #include "rnd.h"
122 1.1 jdolecek
123 1.1 jdolecek #include <sys/param.h>
124 1.1 jdolecek #include <sys/systm.h>
125 1.1 jdolecek #include <sys/errno.h>
126 1.1 jdolecek #include <sys/ioctl.h>
127 1.1 jdolecek #include <sys/mbuf.h>
128 1.1 jdolecek #include <sys/socket.h>
129 1.1 jdolecek #include <sys/syslog.h>
130 1.1 jdolecek #include <sys/device.h>
131 1.1 jdolecek #if NRND > 0
132 1.1 jdolecek #include <sys/rnd.h>
133 1.1 jdolecek #endif
134 1.1 jdolecek
135 1.1 jdolecek #include <net/if.h>
136 1.1 jdolecek #include <net/if_dl.h>
137 1.1 jdolecek #include <net/if_types.h>
138 1.1 jdolecek #include <net/if_media.h>
139 1.1 jdolecek #include <net/if_ether.h>
140 1.1 jdolecek
141 1.1 jdolecek #ifdef INET
142 1.1 jdolecek #include <netinet/in.h>
143 1.1 jdolecek #include <netinet/in_systm.h>
144 1.1 jdolecek #include <netinet/in_var.h>
145 1.1 jdolecek #include <netinet/ip.h>
146 1.1 jdolecek #include <netinet/if_inarp.h>
147 1.1 jdolecek #endif
148 1.1 jdolecek
149 1.1 jdolecek #ifdef NS
150 1.1 jdolecek #include <netns/ns.h>
151 1.1 jdolecek #include <netns/ns_if.h>
152 1.1 jdolecek #endif
153 1.1 jdolecek
154 1.1 jdolecek #if NBPFILTER > 0
155 1.1 jdolecek #include <net/bpf.h>
156 1.1 jdolecek #include <net/bpfdesc.h>
157 1.1 jdolecek #endif
158 1.1 jdolecek
159 1.1 jdolecek #include <machine/bus.h>
160 1.1 jdolecek
161 1.1 jdolecek #include <dev/ic/mb86950reg.h>
162 1.1 jdolecek #include <dev/ic/mb86950var.h>
163 1.1 jdolecek
164 1.1 jdolecek #ifndef __BUS_SPACE_HAS_STREAM_METHODS
165 1.1 jdolecek #define bus_space_write_stream_2 bus_space_write_2
166 1.1 jdolecek #define bus_space_write_multi_stream_2 bus_space_write_multi_2
167 1.1 jdolecek #define bus_space_read_multi_stream_2 bus_space_read_multi_2
168 1.1 jdolecek #endif /* __BUS_SPACE_HAS_STREAM_METHODS */
169 1.1 jdolecek
170 1.1 jdolecek /* Standard driver entry points. These can be static. */
171 1.1 jdolecek int mb86950_ioctl __P((struct ifnet *, u_long, caddr_t));
172 1.1 jdolecek void mb86950_init __P((struct mb86950_softc *));
173 1.1 jdolecek void mb86950_start __P((struct ifnet *));
174 1.1 jdolecek void mb86950_watchdog __P((struct ifnet *));
175 1.1 jdolecek void mb86950_reset __P((struct mb86950_softc *));
176 1.1 jdolecek
177 1.1 jdolecek /* Local functions. */
178 1.1 jdolecek void mb86950_stop __P((struct mb86950_softc *));
179 1.1 jdolecek void mb86950_tint __P((struct mb86950_softc *, u_int8_t));
180 1.1 jdolecek void mb86950_rint __P((struct mb86950_softc *, u_int8_t));
181 1.1 jdolecek int mb86950_get_fifo __P((struct mb86950_softc *, u_int));
182 1.1 jdolecek ushort mb86950_put_fifo __P((struct mb86950_softc *, struct mbuf *));
183 1.1 jdolecek void mb86950_drain_fifo __P((struct mb86950_softc *));
184 1.1 jdolecek
185 1.1 jdolecek int mb86950_mediachange __P((struct ifnet *));
186 1.1 jdolecek void mb86950_mediastatus __P((struct ifnet *, struct ifmediareq *));
187 1.1 jdolecek
188 1.1 jdolecek
189 1.1 jdolecek #if ESTAR_DEBUG >= 1
190 1.1 jdolecek void mb86950_dump __P((int, struct mb86950_softc *));
191 1.1 jdolecek #endif
192 1.1 jdolecek
193 1.1 jdolecek /********************************************************************/
194 1.1 jdolecek
195 1.1 jdolecek void
196 1.1 jdolecek mb86950_attach(sc, myea)
197 1.1 jdolecek struct mb86950_softc *sc;
198 1.1 jdolecek u_int8_t *myea;
199 1.1 jdolecek {
200 1.1 jdolecek
201 1.1 jdolecek #ifdef DIAGNOSTIC
202 1.1 jdolecek if (myea == NULL) {
203 1.1 jdolecek printf("%s: ethernet address shouldn't be NULL\n",
204 1.1 jdolecek sc->sc_dev.dv_xname);
205 1.1 jdolecek panic("NULL ethernet address");
206 1.1 jdolecek }
207 1.1 jdolecek #endif
208 1.1 jdolecek
209 1.1 jdolecek /* Initialize 86950. */
210 1.1 jdolecek mb86950_stop(sc);
211 1.1 jdolecek
212 1.1 jdolecek memcpy(sc->sc_enaddr, myea, sizeof(sc->sc_enaddr));
213 1.1 jdolecek
214 1.1 jdolecek sc->sc_stat |= ESTAR_STAT_ENABLED;
215 1.1 jdolecek }
216 1.1 jdolecek
217 1.1 jdolecek /*
218 1.1 jdolecek * Stop everything on the interface.
219 1.1 jdolecek *
220 1.1 jdolecek * All buffered packets, both transmitting and receiving,
221 1.1 jdolecek * if any, will be lost by stopping the interface.
222 1.1 jdolecek */
223 1.1 jdolecek void
224 1.1 jdolecek mb86950_stop(sc)
225 1.1 jdolecek struct mb86950_softc *sc;
226 1.1 jdolecek {
227 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
228 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
229 1.1 jdolecek
230 1.1 jdolecek /* Stop interface hardware. */
231 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_CONFIG, DISABLE_DLC);
232 1.1 jdolecek delay(200);
233 1.1 jdolecek
234 1.1 jdolecek /* Disable interrupts. */
235 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_INT_EN, 0);
236 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_INT_EN, 0);
237 1.1 jdolecek
238 1.1 jdolecek /* Ack / Clear all interrupt status. */
239 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_STAT, 0xff);
240 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_STAT, 0xff);
241 1.1 jdolecek
242 1.1 jdolecek /* Clear DMA Bit */
243 1.1 jdolecek bus_space_write_2(bst, bsh, BMPR_DMA, 0);
244 1.1 jdolecek
245 1.1 jdolecek /* accept no packets */
246 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_MODE, 0);
247 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_MODE, 0);
248 1.1 jdolecek
249 1.1 jdolecek mb86950_drain_fifo(sc);
250 1.1 jdolecek
251 1.1 jdolecek }
252 1.1 jdolecek
253 1.1 jdolecek void
254 1.1 jdolecek mb86950_drain_fifo(sc)
255 1.1 jdolecek struct mb86950_softc *sc;
256 1.1 jdolecek {
257 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
258 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
259 1.1 jdolecek
260 1.1 jdolecek /* XXX There ought to be a better way, eats CPU and bothers the chip ....... */
261 1.1 jdolecek /* Read data until bus read error (i.e. buffer empty). */
262 1.1 jdolecek while (!(bus_space_read_1(bst, bsh, DLCR_RX_STAT) & RX_BUS_RD_ERR))
263 1.1 jdolecek bus_space_read_2(bst, bsh, BMPR_FIFO);
264 1.1 jdolecek /* XXX */
265 1.1 jdolecek
266 1.1 jdolecek /* Clear Bus Rd Error */
267 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_STAT, RX_BUS_RD_ERR);
268 1.1 jdolecek }
269 1.1 jdolecek
270 1.1 jdolecek /*
271 1.1 jdolecek * Install interface into kernel networking data structures
272 1.1 jdolecek */
273 1.1 jdolecek void
274 1.1 jdolecek mb86950_config(sc, media, nmedia, defmedia)
275 1.1 jdolecek struct mb86950_softc *sc;
276 1.1 jdolecek int *media, nmedia, defmedia;
277 1.1 jdolecek {
278 1.1 jdolecek /* XXX
279 1.1 jdolecek struct cfdata *cf = sc->sc_dev.dv_cfdata;
280 1.1 jdolecek */
281 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
282 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
283 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
284 1.1 jdolecek /*
285 1.1 jdolecek int buf_config;
286 1.1 jdolecek */
287 1.1 jdolecek
288 1.1 jdolecek /* Initialize ifnet structure. */
289 1.1 jdolecek strcpy(ifp->if_xname, sc->sc_dev.dv_xname);
290 1.1 jdolecek ifp->if_softc = sc;
291 1.1 jdolecek ifp->if_start = mb86950_start;
292 1.1 jdolecek ifp->if_ioctl = mb86950_ioctl;
293 1.1 jdolecek ifp->if_watchdog = mb86950_watchdog;
294 1.1 jdolecek ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
295 1.1 jdolecek
296 1.1 jdolecek IFQ_SET_READY(&ifp->if_snd);
297 1.1 jdolecek
298 1.1 jdolecek /* Initialize media goo. */
299 1.1 jdolecek /* XXX The Tiara LANCard uses board jumpers to change media.
300 1.1 jdolecek * This code may have to be changed for other cards.
301 1.1 jdolecek */
302 1.1 jdolecek ifmedia_init(&sc->sc_media, 0, mb86950_mediachange, mb86950_mediastatus);
303 1.1 jdolecek ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL);
304 1.1 jdolecek ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL);
305 1.1 jdolecek
306 1.1 jdolecek /* Attach the interface. */
307 1.1 jdolecek if_attach(ifp);
308 1.1 jdolecek
309 1.1 jdolecek /* Feed the chip the station address. */
310 1.1 jdolecek bus_space_write_region_1(bst, bsh, DLCR_NODE_ID, sc->sc_enaddr, ETHER_ADDR_LEN);
311 1.1 jdolecek
312 1.1 jdolecek ether_ifattach(ifp, sc->sc_enaddr);
313 1.1 jdolecek
314 1.1 jdolecek #if NRND > 0
315 1.1 jdolecek rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname,
316 1.1 jdolecek RND_TYPE_NET, 0);
317 1.1 jdolecek #endif
318 1.1 jdolecek
319 1.1 jdolecek /* XXX No! This doesn't work - DLCR6 of the mb86950 is different
320 1.1 jdolecek
321 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_CONFIG, 0x0f);
322 1.1 jdolecek buf_config = bus_space_read_1(bst, bsh, DLCR_CONFIG);
323 1.1 jdolecek
324 1.1 jdolecek sc->txb_count = ((buf_config & 0x0c) ? 2 : 1);
325 1.1 jdolecek sc->txb_size = 1024 * (2 << ((buf_config & 0x0c) ? (((buf_config & 0x0c) >> 2) - 1) : 0));
326 1.1 jdolecek sc->txb_free = (sc->txb_size * sc->txb_count) / 1500;
327 1.1 jdolecek
328 1.1 jdolecek sc->rxb_size = ((8 << (buf_config & 3)) * 1024) - (sc->txb_size * sc->txb_count);
329 1.1 jdolecek sc->rxb_max = sc->rxb_size / 64;
330 1.1 jdolecek
331 1.1 jdolecek printf("mb86950: Buffer Size %dKB with %d transmit buffer(s) %dKB each.\n",
332 1.1 jdolecek (8 << (buf_config & 3)), sc->txb_count, (sc->txb_size / 1024));
333 1.1 jdolecek printf(" Transmit Buffer Space for %d maximum sized packet(s).\n",sc->txb_free);
334 1.1 jdolecek printf(" System Bus Width %d bits, Buffer Memory %d bits.\n",
335 1.1 jdolecek ((buf_config & 0x20) ? 8 : 16),
336 1.1 jdolecek ((buf_config & 0x10) ? 8 : 16));
337 1.1 jdolecek
338 1.1 jdolecek */
339 1.1 jdolecek
340 1.1 jdolecek /* Set reasonable values for number of packet flow control if not set elsewhere */
341 1.1 jdolecek if (sc->txb_num_pkt == 0) sc->txb_num_pkt = 1;
342 1.1 jdolecek if (sc->rxb_num_pkt == 0) sc->rxb_num_pkt = 100;
343 1.1 jdolecek
344 1.1 jdolecek /* Print additional info when attached. */
345 1.1 jdolecek printf("%s: Ethernet address %s\n", sc->sc_dev.dv_xname, ether_sprintf(sc->sc_enaddr));
346 1.1 jdolecek
347 1.1 jdolecek /* The attach is successful. */
348 1.1 jdolecek sc->sc_stat |= ESTAR_STAT_ATTACHED;
349 1.1 jdolecek }
350 1.1 jdolecek
351 1.1 jdolecek /*
352 1.1 jdolecek * Media change callback.
353 1.1 jdolecek */
354 1.1 jdolecek int
355 1.1 jdolecek mb86950_mediachange(ifp)
356 1.1 jdolecek struct ifnet *ifp;
357 1.1 jdolecek {
358 1.1 jdolecek
359 1.1 jdolecek struct mb86950_softc *sc = ifp->if_softc;
360 1.1 jdolecek
361 1.1 jdolecek if (sc->sc_mediachange)
362 1.1 jdolecek return ((*sc->sc_mediachange)(sc));
363 1.1 jdolecek
364 1.1 jdolecek return (0);
365 1.1 jdolecek }
366 1.1 jdolecek
367 1.1 jdolecek /*
368 1.1 jdolecek * Media status callback.
369 1.1 jdolecek */
370 1.1 jdolecek void
371 1.1 jdolecek mb86950_mediastatus(ifp, ifmr)
372 1.1 jdolecek struct ifnet *ifp;
373 1.1 jdolecek struct ifmediareq *ifmr;
374 1.1 jdolecek {
375 1.1 jdolecek struct mb86950_softc *sc = ifp->if_softc;
376 1.1 jdolecek
377 1.1 jdolecek if ((sc->sc_stat & ESTAR_STAT_ENABLED) == 0) {
378 1.1 jdolecek ifmr->ifm_active = IFM_ETHER | IFM_NONE;
379 1.1 jdolecek ifmr->ifm_status = 0;
380 1.1 jdolecek return;
381 1.1 jdolecek }
382 1.1 jdolecek
383 1.1 jdolecek if (sc->sc_mediastatus)
384 1.1 jdolecek (*sc->sc_mediastatus)(sc, ifmr);
385 1.1 jdolecek
386 1.1 jdolecek }
387 1.1 jdolecek
388 1.1 jdolecek /*
389 1.1 jdolecek * Reset interface.
390 1.1 jdolecek */
391 1.1 jdolecek void
392 1.1 jdolecek mb86950_reset(sc)
393 1.1 jdolecek struct mb86950_softc *sc;
394 1.1 jdolecek {
395 1.1 jdolecek int s;
396 1.1 jdolecek
397 1.1 jdolecek s = splnet();
398 1.1 jdolecek log(LOG_ERR, "%s: device reset\n", sc->sc_dev.dv_xname);
399 1.1 jdolecek mb86950_stop(sc);
400 1.1 jdolecek mb86950_init(sc);
401 1.1 jdolecek splx(s);
402 1.1 jdolecek }
403 1.1 jdolecek
404 1.1 jdolecek /*
405 1.1 jdolecek * Device timeout/watchdog routine. Entered if the device neglects to
406 1.1 jdolecek * generate an interrupt after a transmit has been started on it.
407 1.1 jdolecek */
408 1.1 jdolecek void
409 1.1 jdolecek mb86950_watchdog(ifp)
410 1.1 jdolecek struct ifnet *ifp;
411 1.1 jdolecek {
412 1.1 jdolecek struct mb86950_softc *sc = ifp->if_softc;
413 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
414 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
415 1.1 jdolecek u_int8_t tstat;
416 1.1 jdolecek
417 1.1 jdolecek /* verbose watchdog messages for debugging timeouts */
418 1.1 jdolecek if ((tstat = bus_space_read_1(bst, bsh, DLCR_TX_STAT)) != 0) {
419 1.1 jdolecek if (tstat & TX_CR_LOST) {
420 1.1 jdolecek if ((tstat & (TX_COL | TX_16COL)) == 0) log(LOG_ERR, "%s: carrier lost\n", sc->sc_dev.dv_xname);
421 1.1 jdolecek else log(LOG_ERR, "%s: excessive collisions\n", sc->sc_dev.dv_xname);
422 1.1 jdolecek }
423 1.1 jdolecek else if ((tstat & (TX_UNDERFLO | TX_BUS_WR_ERR)) != 0) log(LOG_ERR, "%s: tx fifo underflow/overflow\n", sc->sc_dev.dv_xname);
424 1.1 jdolecek else log(LOG_ERR, "%s: transmit error\n", sc->sc_dev.dv_xname);
425 1.1 jdolecek }
426 1.1 jdolecek else
427 1.1 jdolecek log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
428 1.1 jdolecek
429 1.1 jdolecek /* Don't know how many packets are lost by this accident.
430 1.1 jdolecek * ... So just errors = errors + 1
431 1.1 jdolecek */
432 1.1 jdolecek ifp->if_oerrors++;
433 1.1 jdolecek
434 1.1 jdolecek mb86950_reset(sc);
435 1.1 jdolecek
436 1.1 jdolecek }
437 1.1 jdolecek
438 1.1 jdolecek /*
439 1.1 jdolecek ******************** IOCTL
440 1.1 jdolecek * Process an ioctl request.
441 1.1 jdolecek */
442 1.1 jdolecek int
443 1.1 jdolecek mb86950_ioctl(ifp, cmd, data)
444 1.1 jdolecek struct ifnet *ifp;
445 1.1 jdolecek u_long cmd;
446 1.1 jdolecek caddr_t data;
447 1.1 jdolecek {
448 1.1 jdolecek struct mb86950_softc *sc = ifp->if_softc;
449 1.1 jdolecek struct ifaddr *ifa = (struct ifaddr *)data;
450 1.1 jdolecek struct ifreq *ifr = (struct ifreq *)data;
451 1.1 jdolecek
452 1.1 jdolecek int s, error = 0;
453 1.1 jdolecek
454 1.1 jdolecek s = splnet();
455 1.1 jdolecek
456 1.1 jdolecek switch (cmd) {
457 1.1 jdolecek case SIOCSIFADDR:
458 1.1 jdolecek /* XXX depreciated ? What should I use instead? */
459 1.1 jdolecek if ((error = mb86950_enable(sc)) != 0)
460 1.1 jdolecek break;
461 1.1 jdolecek
462 1.1 jdolecek ifp->if_flags |= IFF_UP;
463 1.1 jdolecek
464 1.1 jdolecek switch (ifa->ifa_addr->sa_family) {
465 1.1 jdolecek
466 1.1 jdolecek #ifdef INET
467 1.1 jdolecek case AF_INET:
468 1.1 jdolecek mb86950_init(sc);
469 1.1 jdolecek arp_ifinit(ifp, ifa);
470 1.1 jdolecek break;
471 1.1 jdolecek #endif
472 1.1 jdolecek
473 1.1 jdolecek #ifdef NS
474 1.1 jdolecek case AF_NS:
475 1.1 jdolecek {
476 1.1 jdolecek struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
477 1.1 jdolecek
478 1.1 jdolecek if (ns_nullhost(*ina))
479 1.1 jdolecek ina->x_host = *(union ns_host *)LLADDR(ifp->if_sadl);
480 1.1 jdolecek else {
481 1.1 jdolecek memcpy(LLADDR(ifp->if_sadl), ina->x_host.c_host, ETHER_ADDR_LEN);
482 1.1 jdolecek }
483 1.1 jdolecek /* Set new address. */
484 1.1 jdolecek mb86950_init(sc);
485 1.1 jdolecek break;
486 1.1 jdolecek }
487 1.1 jdolecek #endif
488 1.1 jdolecek
489 1.1 jdolecek default:
490 1.1 jdolecek mb86950_init(sc);
491 1.1 jdolecek break;
492 1.1 jdolecek }
493 1.1 jdolecek break;
494 1.1 jdolecek
495 1.1 jdolecek case SIOCSIFFLAGS:
496 1.1 jdolecek if ((ifp->if_flags & IFF_UP) == 0 &&
497 1.1 jdolecek (ifp->if_flags & IFF_RUNNING) != 0) {
498 1.1 jdolecek /*
499 1.1 jdolecek * If interface is marked down and it is running, then
500 1.1 jdolecek * stop it.
501 1.1 jdolecek */
502 1.1 jdolecek mb86950_stop(sc);
503 1.1 jdolecek ifp->if_flags &= ~IFF_RUNNING;
504 1.1 jdolecek mb86950_disable(sc);
505 1.1 jdolecek
506 1.1 jdolecek } else if ((ifp->if_flags & IFF_UP) != 0 &&
507 1.1 jdolecek (ifp->if_flags & IFF_RUNNING) == 0) {
508 1.1 jdolecek /*
509 1.1 jdolecek * If interface is marked up and it is stopped, then
510 1.1 jdolecek * start it.
511 1.1 jdolecek */
512 1.1 jdolecek if ((error = mb86950_enable(sc)) != 0)
513 1.1 jdolecek break;
514 1.1 jdolecek mb86950_init(sc);
515 1.1 jdolecek
516 1.1 jdolecek } else if ((ifp->if_flags & IFF_UP) != 0) {
517 1.1 jdolecek /*
518 1.1 jdolecek * Reset the interface to pick up changes in any other
519 1.1 jdolecek * flags that affect hardware registers.
520 1.1 jdolecek */
521 1.1 jdolecek /* Setmode not supported
522 1.1 jdolecek mb86950_setmode(sc);
523 1.1 jdolecek */
524 1.1 jdolecek }
525 1.1 jdolecek
526 1.1 jdolecek #if ESTAR_DEBUG >= 1
527 1.1 jdolecek /* "ifconfig fe0 debug" to print register dump. */
528 1.1 jdolecek if (ifp->if_flags & IFF_DEBUG) {
529 1.1 jdolecek log(LOG_INFO, "%s: SIOCSIFFLAGS(DEBUG)\n",
530 1.1 jdolecek sc->sc_dev.dv_xname);
531 1.1 jdolecek mb86950_dump(LOG_DEBUG, sc);
532 1.1 jdolecek }
533 1.1 jdolecek #endif
534 1.1 jdolecek break;
535 1.1 jdolecek
536 1.1 jdolecek case SIOCGIFMEDIA:
537 1.1 jdolecek case SIOCSIFMEDIA:
538 1.1 jdolecek error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
539 1.1 jdolecek break;
540 1.1 jdolecek
541 1.1 jdolecek default:
542 1.1 jdolecek error = EINVAL;
543 1.1 jdolecek break;
544 1.1 jdolecek }
545 1.1 jdolecek
546 1.1 jdolecek splx(s);
547 1.1 jdolecek return (error);
548 1.1 jdolecek }
549 1.1 jdolecek
550 1.1 jdolecek /*
551 1.1 jdolecek * Initialize device.
552 1.1 jdolecek */
553 1.1 jdolecek void
554 1.1 jdolecek mb86950_init(sc)
555 1.1 jdolecek struct mb86950_softc *sc;
556 1.1 jdolecek {
557 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
558 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
559 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
560 1.1 jdolecek
561 1.1 jdolecek /* Reset transmitter flags. */
562 1.1 jdolecek ifp->if_flags &= ~IFF_OACTIVE;
563 1.1 jdolecek ifp->if_timer = 0;
564 1.1 jdolecek sc->txb_sched = 0;
565 1.1 jdolecek
566 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_MODE, LBC);
567 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_MODE, NORMAL_MODE);
568 1.1 jdolecek
569 1.1 jdolecek /* Enable interrupts. */
570 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_INT_EN, TX_MASK);
571 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_INT_EN, RX_MASK);
572 1.1 jdolecek
573 1.1 jdolecek /* Enable transmitter and receiver. */
574 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_CONFIG, ENABLE_DLC);
575 1.1 jdolecek delay(200);
576 1.1 jdolecek
577 1.1 jdolecek /* Set 'running' flag. */
578 1.1 jdolecek ifp->if_flags |= IFF_RUNNING;
579 1.1 jdolecek
580 1.1 jdolecek /* ...and attempt to start output. */
581 1.1 jdolecek mb86950_start(ifp);
582 1.1 jdolecek
583 1.1 jdolecek }
584 1.1 jdolecek
585 1.1 jdolecek void
586 1.1 jdolecek mb86950_start(ifp)
587 1.1 jdolecek struct ifnet *ifp;
588 1.1 jdolecek {
589 1.1 jdolecek struct mb86950_softc *sc = ifp->if_softc;
590 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
591 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
592 1.1 jdolecek struct mbuf *m;
593 1.1 jdolecek int len;
594 1.1 jdolecek
595 1.1 jdolecek if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
596 1.1 jdolecek return;
597 1.1 jdolecek
598 1.1 jdolecek IF_DEQUEUE(&ifp->if_snd, m);
599 1.1 jdolecek if (m == 0)
600 1.1 jdolecek return;
601 1.1 jdolecek
602 1.1 jdolecek #if NBPFILTER > 0
603 1.1 jdolecek /* Tap off here if there is a BPF listener. */
604 1.1 jdolecek if (ifp->if_bpf)
605 1.1 jdolecek bpf_mtap(ifp->if_bpf, m);
606 1.1 jdolecek #endif
607 1.1 jdolecek
608 1.1 jdolecek /* Send the packet to the mb86950 */
609 1.1 jdolecek len = mb86950_put_fifo(sc,m);
610 1.1 jdolecek m_freem(m);
611 1.1 jdolecek
612 1.1 jdolecek /* XXX bus_space_barrier here ? */
613 1.1 jdolecek if (bus_space_read_1(bst, bsh, DLCR_TX_STAT) & (TX_UNDERFLO | TX_BUS_WR_ERR)) {
614 1.1 jdolecek log(LOG_ERR, "%s: tx fifo underflow/overflow\n", sc->sc_dev.dv_xname);
615 1.1 jdolecek }
616 1.1 jdolecek
617 1.1 jdolecek bus_space_write_2(bst, bsh, BMPR_TX_LENGTH, len | TRANSMIT_START);
618 1.1 jdolecek
619 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_INT_EN, TX_MASK);
620 1.1 jdolecek /* XXX */
621 1.1 jdolecek sc->txb_sched++;
622 1.1 jdolecek
623 1.1 jdolecek /* We have space for 'n' transmit packets of size 'mtu. */
624 1.1 jdolecek if (sc->txb_sched > sc->txb_num_pkt) {
625 1.1 jdolecek ifp->if_flags |= IFF_OACTIVE;
626 1.1 jdolecek ifp->if_timer = 2;
627 1.1 jdolecek }
628 1.1 jdolecek }
629 1.1 jdolecek
630 1.1 jdolecek /*
631 1.1 jdolecek * ******************** SEND PACKET
632 1.1 jdolecek * Copy packet from mbuf to the fifo
633 1.1 jdolecek */
634 1.1 jdolecek u_short
635 1.1 jdolecek mb86950_put_fifo(sc, m)
636 1.1 jdolecek struct mb86950_softc *sc;
637 1.1 jdolecek struct mbuf *m;
638 1.1 jdolecek {
639 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
640 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
641 1.1 jdolecek u_short *data;
642 1.1 jdolecek u_char savebyte[2];
643 1.1 jdolecek int len, len1, wantbyte;
644 1.1 jdolecek u_short totlen;
645 1.1 jdolecek
646 1.1 jdolecek totlen = wantbyte = 0;
647 1.1 jdolecek
648 1.1 jdolecek for (; m != NULL; m = m->m_next) {
649 1.1 jdolecek data = mtod(m, u_short *);
650 1.1 jdolecek len = m->m_len;
651 1.1 jdolecek if (len > 0) {
652 1.1 jdolecek totlen += len;
653 1.1 jdolecek
654 1.1 jdolecek /* Finish the last word. */
655 1.1 jdolecek if (wantbyte) {
656 1.1 jdolecek savebyte[1] = *((u_char *)data);
657 1.1 jdolecek bus_space_write_2(bst, bsh, BMPR_FIFO, *savebyte);
658 1.1 jdolecek ((u_char *)data)++;
659 1.1 jdolecek len--;
660 1.1 jdolecek wantbyte = 0;
661 1.1 jdolecek }
662 1.1 jdolecek /* Output contiguous words. */
663 1.1 jdolecek if (len > 1) {
664 1.1 jdolecek len1 = len/2;
665 1.1 jdolecek bus_space_write_multi_stream_2(bst, bsh, BMPR_FIFO, data, len1);
666 1.1 jdolecek data += len1;
667 1.1 jdolecek len &= 1;
668 1.1 jdolecek }
669 1.1 jdolecek /* Save last byte, if necessary. */
670 1.1 jdolecek if (len == 1) {
671 1.1 jdolecek savebyte[0] = *((u_char *)data);
672 1.1 jdolecek wantbyte = 1;
673 1.1 jdolecek }
674 1.1 jdolecek }
675 1.1 jdolecek }
676 1.1 jdolecek
677 1.1 jdolecek if (wantbyte) {
678 1.1 jdolecek savebyte[1] = 0;
679 1.1 jdolecek bus_space_write_2(bst, bsh, BMPR_FIFO, *savebyte);
680 1.1 jdolecek }
681 1.1 jdolecek
682 1.1 jdolecek if (totlen < (ETHER_MIN_LEN - ETHER_CRC_LEN)) {
683 1.1 jdolecek
684 1.1 jdolecek /* Fill the rest of the packet with zeros. */
685 1.1 jdolecek /*
686 1.1 jdolecek len1 = (ETHER_MIN_LEN - ETHER_CRC_LEN - totlen + 1) / 2;
687 1.1 jdolecek */
688 1.1 jdolecek /* XXX Replace this mess with something else, eats CPU */
689 1.1 jdolecek /* The zero fill and last byte ought to be combined somehow */
690 1.1 jdolecek for(len = totlen + 1; len < (ETHER_MIN_LEN - ETHER_CRC_LEN); len += 2)
691 1.1 jdolecek bus_space_write_2(bst, bsh, BMPR_FIFO, 0);
692 1.1 jdolecek /* XXX */
693 1.1 jdolecek
694 1.1 jdolecek totlen = (ETHER_MIN_LEN - ETHER_CRC_LEN);
695 1.1 jdolecek }
696 1.1 jdolecek
697 1.1 jdolecek return (totlen);
698 1.1 jdolecek }
699 1.1 jdolecek
700 1.1 jdolecek /*
701 1.1 jdolecek **************************** INTERRUPTS
702 1.1 jdolecek * Ethernet interface interrupt processor
703 1.1 jdolecek */
704 1.1 jdolecek int
705 1.1 jdolecek mb86950_intr(arg)
706 1.1 jdolecek void *arg;
707 1.1 jdolecek {
708 1.1 jdolecek struct mb86950_softc *sc = arg;
709 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
710 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
711 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
712 1.1 jdolecek u_int8_t tstat, rstat;
713 1.1 jdolecek
714 1.1 jdolecek /* Get interrupt status. */
715 1.1 jdolecek tstat = bus_space_read_1(bst, bsh, DLCR_TX_STAT);
716 1.1 jdolecek rstat = bus_space_read_1(bst, bsh, DLCR_RX_STAT);
717 1.1 jdolecek
718 1.1 jdolecek if (tstat == 0 && rstat == 0) return (0);
719 1.1 jdolecek
720 1.1 jdolecek /* Disable etherstar interrupts so that we won't miss anything. */
721 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_INT_EN, 0);
722 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_INT_EN, 0);
723 1.1 jdolecek
724 1.1 jdolecek /*
725 1.1 jdolecek * Handle transmitter interrupts. Handle these first because
726 1.1 jdolecek * the receiver will reset the board under some conditions.
727 1.1 jdolecek */
728 1.1 jdolecek if (tstat != 0) {
729 1.1 jdolecek
730 1.1 jdolecek mb86950_tint(sc, tstat);
731 1.1 jdolecek
732 1.1 jdolecek /* acknowledge transmit interrupt status. */
733 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_TX_STAT, tstat);
734 1.1 jdolecek
735 1.1 jdolecek }
736 1.1 jdolecek
737 1.1 jdolecek /* Handle receiver interrupts. */
738 1.1 jdolecek if (rstat != 0) {
739 1.1 jdolecek
740 1.1 jdolecek mb86950_rint(sc, rstat);
741 1.1 jdolecek
742 1.1 jdolecek /* acknowledge receive interrupt status. */
743 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_STAT, rstat);
744 1.1 jdolecek
745 1.1 jdolecek }
746 1.1 jdolecek
747 1.1 jdolecek /* If tx still pending reset tx interrupt mask */
748 1.1 jdolecek if (sc->txb_sched > 0) bus_space_write_1(bst, bsh, DLCR_TX_INT_EN, TX_MASK);
749 1.1 jdolecek
750 1.1 jdolecek /*
751 1.1 jdolecek * If it looks like the transmitter can take more data,
752 1.1 jdolecek * attempt to start output on the interface. This is done
753 1.1 jdolecek * after handling the receiver interrupt to give the
754 1.1 jdolecek * receive operation priority.
755 1.1 jdolecek */
756 1.1 jdolecek
757 1.1 jdolecek if ((ifp->if_flags & IFF_OACTIVE) == 0)
758 1.1 jdolecek mb86950_start(ifp);
759 1.1 jdolecek
760 1.1 jdolecek /* Set receive interrupts back */
761 1.1 jdolecek bus_space_write_1(bst, bsh, DLCR_RX_INT_EN, RX_MASK);
762 1.1 jdolecek
763 1.1 jdolecek return(1);
764 1.1 jdolecek }
765 1.1 jdolecek
766 1.1 jdolecek /* Transmission interrupt handler */
767 1.1 jdolecek void
768 1.1 jdolecek mb86950_tint(sc, tstat)
769 1.1 jdolecek struct mb86950_softc *sc;
770 1.1 jdolecek u_int8_t tstat;
771 1.1 jdolecek {
772 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
773 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
774 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
775 1.1 jdolecek int col;
776 1.1 jdolecek
777 1.1 jdolecek if (tstat & (TX_UNDERFLO | TX_BUS_WR_ERR)) {
778 1.1 jdolecek /* XXX What do we need to do here? reset ? */
779 1.1 jdolecek ifp->if_oerrors++;
780 1.1 jdolecek }
781 1.1 jdolecek
782 1.1 jdolecek /* excessive collision */
783 1.1 jdolecek if (tstat & TX_16COL) {
784 1.1 jdolecek ifp->if_collisions += 16;
785 1.1 jdolecek /* 16 collisions means that the packet has been thrown away. */
786 1.1 jdolecek if (sc->txb_sched > 0) sc->txb_sched--;
787 1.1 jdolecek }
788 1.1 jdolecek
789 1.1 jdolecek /* transmission complete. */
790 1.1 jdolecek if (tstat & TX_DONE) {
791 1.1 jdolecek /* successfully transmitted packets ++. */
792 1.1 jdolecek ifp->if_opackets++;
793 1.1 jdolecek if (sc->txb_sched > 0) sc->txb_sched--;
794 1.1 jdolecek
795 1.1 jdolecek /* Collision count valid only when TX_DONE is set */
796 1.1 jdolecek if (tstat & TX_COL) {
797 1.1 jdolecek col = (bus_space_read_1(bst, bsh, DLCR_TX_MODE) & COL_MASK) >> 4;
798 1.1 jdolecek ifp->if_collisions = ifp->if_collisions + col;
799 1.1 jdolecek }
800 1.1 jdolecek }
801 1.1 jdolecek
802 1.1 jdolecek if (sc->txb_sched == 0) {
803 1.1 jdolecek /* Reset output active flag and stop timer. */
804 1.1 jdolecek ifp->if_flags &= ~IFF_OACTIVE;
805 1.1 jdolecek ifp->if_timer = 0;
806 1.1 jdolecek }
807 1.1 jdolecek }
808 1.1 jdolecek
809 1.1 jdolecek /* receiver interrupt. */
810 1.1 jdolecek void
811 1.1 jdolecek mb86950_rint(sc, rstat)
812 1.1 jdolecek struct mb86950_softc *sc;
813 1.1 jdolecek u_int8_t rstat;
814 1.1 jdolecek {
815 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
816 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
817 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
818 1.1 jdolecek u_int status, len;
819 1.1 jdolecek int i;
820 1.1 jdolecek
821 1.1 jdolecek /* Update statistics if this interrupt is caused by an error. */
822 1.1 jdolecek if (rstat & RX_ERR_MASK) {
823 1.1 jdolecek
824 1.1 jdolecek /* tried to read past end of fifo, should be harmless
825 1.1 jdolecek * count everything else
826 1.1 jdolecek */
827 1.1 jdolecek if ((rstat & RX_BUS_RD_ERR) == 0) {
828 1.1 jdolecek ifp->if_ierrors++;
829 1.1 jdolecek }
830 1.1 jdolecek }
831 1.1 jdolecek
832 1.1 jdolecek /*
833 1.1 jdolecek * mb86950 has a flag indicating "receive buffer empty."
834 1.1 jdolecek * We just loop checking the flag to pull out all received
835 1.1 jdolecek * packets.
836 1.1 jdolecek *
837 1.1 jdolecek * We limit the number of iterrations to avoid infinite loop.
838 1.1 jdolecek * It can be caused by a very slow CPU (some broken
839 1.1 jdolecek * peripheral may insert incredible number of wait cycles)
840 1.1 jdolecek * or, worse, by a broken mb86950 chip.
841 1.1 jdolecek */
842 1.1 jdolecek for (i = 0; i < sc->rxb_num_pkt; i++) {
843 1.1 jdolecek /* Stop the iterration if 86950 indicates no packets. */
844 1.1 jdolecek if (bus_space_read_1(bst, bsh, DLCR_RX_MODE) & RX_BUF_EMTY)
845 1.1 jdolecek break;
846 1.1 jdolecek
847 1.1 jdolecek /* receive packet status */
848 1.1 jdolecek status = bus_space_read_2(bst, bsh, BMPR_FIFO);
849 1.1 jdolecek
850 1.1 jdolecek /* bad packet? */
851 1.1 jdolecek if ((status & GOOD_PKT) == 0) {
852 1.1 jdolecek ifp->if_ierrors++;
853 1.1 jdolecek mb86950_drain_fifo(sc);
854 1.1 jdolecek continue;
855 1.1 jdolecek }
856 1.1 jdolecek
857 1.1 jdolecek /* Length valid ? */
858 1.1 jdolecek len = bus_space_read_2(bst, bsh, BMPR_FIFO);
859 1.1 jdolecek
860 1.1 jdolecek if (len > (ETHER_MAX_LEN - ETHER_CRC_LEN) || len < ETHER_HDR_LEN) {
861 1.1 jdolecek ifp->if_ierrors++;
862 1.1 jdolecek mb86950_drain_fifo(sc);
863 1.1 jdolecek continue;
864 1.1 jdolecek }
865 1.1 jdolecek
866 1.1 jdolecek if (mb86950_get_fifo(sc, len) != 0) {
867 1.1 jdolecek /* No mbufs? Drop packet. */
868 1.1 jdolecek ifp->if_ierrors++;
869 1.1 jdolecek mb86950_drain_fifo(sc);
870 1.1 jdolecek return;
871 1.1 jdolecek }
872 1.1 jdolecek
873 1.1 jdolecek /* Successfully received a packet. Update stat. */
874 1.1 jdolecek ifp->if_ipackets++;
875 1.1 jdolecek }
876 1.1 jdolecek }
877 1.1 jdolecek
878 1.1 jdolecek /*
879 1.1 jdolecek * ********************* RECEIVE PACKET
880 1.1 jdolecek * Retrieve packet from receive buffer and send to the next level up via
881 1.1 jdolecek * ether_input(). If there is a BPF listener, give a copy to BPF, too.
882 1.1 jdolecek * Returns 0 if success, -1 if error (i.e., mbuf allocation failure).
883 1.1 jdolecek */
884 1.1 jdolecek int
885 1.1 jdolecek mb86950_get_fifo(sc, len)
886 1.1 jdolecek struct mb86950_softc *sc;
887 1.1 jdolecek u_int len;
888 1.1 jdolecek {
889 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
890 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
891 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
892 1.1 jdolecek struct mbuf *m;
893 1.1 jdolecek
894 1.1 jdolecek /* Allocate a header mbuf. */
895 1.1 jdolecek MGETHDR(m, M_DONTWAIT, MT_DATA);
896 1.1 jdolecek if (m == 0)
897 1.1 jdolecek return (-1);
898 1.1 jdolecek
899 1.1 jdolecek /*
900 1.1 jdolecek * Round len to even value.
901 1.1 jdolecek */
902 1.1 jdolecek if (len & 1)
903 1.1 jdolecek len++;
904 1.1 jdolecek
905 1.1 jdolecek m->m_pkthdr.rcvif = ifp;
906 1.1 jdolecek m->m_pkthdr.len = len;
907 1.1 jdolecek
908 1.1 jdolecek /* The following silliness is to make NFS happy. */
909 1.1 jdolecek #define EROUND ((sizeof(struct ether_header) + 3) & ~3)
910 1.1 jdolecek #define EOFF (EROUND - sizeof(struct ether_header))
911 1.1 jdolecek
912 1.1 jdolecek /*
913 1.1 jdolecek * Our strategy has one more problem. There is a policy on
914 1.1 jdolecek * mbuf cluster allocation. It says that we must have at
915 1.1 jdolecek * least MINCLSIZE (208 bytes) to allocate a cluster. For a
916 1.1 jdolecek * packet of a size between (MHLEN - 2) to (MINCLSIZE - 2),
917 1.1 jdolecek * our code violates the rule...
918 1.1 jdolecek * On the other hand, the current code is short, simple,
919 1.1 jdolecek * and fast, however. It does no harmful thing, just wastes
920 1.1 jdolecek * some memory. Any comments? FIXME.
921 1.1 jdolecek */
922 1.1 jdolecek
923 1.1 jdolecek /* Attach a cluster if this packet doesn't fit in a normal mbuf. */
924 1.1 jdolecek if (len > MHLEN - EOFF) {
925 1.1 jdolecek MCLGET(m, M_DONTWAIT);
926 1.1 jdolecek if ((m->m_flags & M_EXT) == 0) {
927 1.1 jdolecek m_freem(m);
928 1.1 jdolecek return (-1);
929 1.1 jdolecek }
930 1.1 jdolecek }
931 1.1 jdolecek
932 1.1 jdolecek /*
933 1.1 jdolecek * The following assumes there is room for the ether header in the
934 1.1 jdolecek * header mbuf.
935 1.1 jdolecek */
936 1.1 jdolecek m->m_data += EOFF;
937 1.1 jdolecek
938 1.1 jdolecek /* Set the length of this packet. */
939 1.1 jdolecek m->m_len = len;
940 1.1 jdolecek
941 1.1 jdolecek /* Get a packet. */
942 1.1 jdolecek bus_space_read_multi_stream_2(bst, bsh, BMPR_FIFO, mtod(m, u_int16_t *), (len + 1) >> 1);
943 1.1 jdolecek
944 1.1 jdolecek #if NBPFILTER > 0
945 1.1 jdolecek /*
946 1.1 jdolecek * Check if there's a BPF listener on this interface. If so, hand off
947 1.1 jdolecek * the raw packet to bpf.
948 1.1 jdolecek */
949 1.1 jdolecek if (ifp->if_bpf)
950 1.1 jdolecek bpf_mtap(ifp->if_bpf, m);
951 1.1 jdolecek #endif
952 1.1 jdolecek
953 1.1 jdolecek (*ifp->if_input)(ifp, m);
954 1.1 jdolecek return (0);
955 1.1 jdolecek }
956 1.1 jdolecek
957 1.1 jdolecek /*
958 1.1 jdolecek * Enable power on the interface.
959 1.1 jdolecek */
960 1.1 jdolecek int
961 1.1 jdolecek mb86950_enable(sc)
962 1.1 jdolecek struct mb86950_softc *sc;
963 1.1 jdolecek {
964 1.1 jdolecek
965 1.1 jdolecek if ((sc->sc_stat & ESTAR_STAT_ENABLED) == 0 && sc->sc_enable != NULL) {
966 1.1 jdolecek if ((*sc->sc_enable)(sc) != 0) {
967 1.1 jdolecek printf("%s: device enable failed\n",
968 1.1 jdolecek sc->sc_dev.dv_xname);
969 1.1 jdolecek return (EIO);
970 1.1 jdolecek }
971 1.1 jdolecek }
972 1.1 jdolecek
973 1.1 jdolecek sc->sc_stat |= ESTAR_STAT_ENABLED;
974 1.1 jdolecek return (0);
975 1.1 jdolecek }
976 1.1 jdolecek
977 1.1 jdolecek /*
978 1.1 jdolecek * Disable power on the interface.
979 1.1 jdolecek */
980 1.1 jdolecek void
981 1.1 jdolecek mb86950_disable(sc)
982 1.1 jdolecek struct mb86950_softc *sc;
983 1.1 jdolecek {
984 1.1 jdolecek
985 1.1 jdolecek if ((sc->sc_stat & ESTAR_STAT_ENABLED) != 0 && sc->sc_disable != NULL) {
986 1.1 jdolecek (*sc->sc_disable)(sc);
987 1.1 jdolecek sc->sc_stat &= ~ESTAR_STAT_ENABLED;
988 1.1 jdolecek }
989 1.1 jdolecek }
990 1.1 jdolecek
991 1.1 jdolecek /*
992 1.1 jdolecek * mbe_activate:
993 1.1 jdolecek *
994 1.1 jdolecek * Handle device activation/deactivation requests.
995 1.1 jdolecek */
996 1.1 jdolecek int
997 1.1 jdolecek mb86950_activate(self, act)
998 1.1 jdolecek struct device *self;
999 1.1 jdolecek enum devact act;
1000 1.1 jdolecek {
1001 1.1 jdolecek struct mb86950_softc *sc = (struct mb86950_softc *)self;
1002 1.1 jdolecek int rv, s;
1003 1.1 jdolecek
1004 1.1 jdolecek rv = 0;
1005 1.1 jdolecek s = splnet();
1006 1.1 jdolecek switch (act) {
1007 1.1 jdolecek case DVACT_ACTIVATE:
1008 1.1 jdolecek rv = EOPNOTSUPP;
1009 1.1 jdolecek break;
1010 1.1 jdolecek
1011 1.1 jdolecek case DVACT_DEACTIVATE:
1012 1.1 jdolecek if_deactivate(&sc->sc_ec.ec_if);
1013 1.1 jdolecek break;
1014 1.1 jdolecek }
1015 1.1 jdolecek splx(s);
1016 1.1 jdolecek return (rv);
1017 1.1 jdolecek }
1018 1.1 jdolecek
1019 1.1 jdolecek /*
1020 1.1 jdolecek * mb86950_detach:
1021 1.1 jdolecek *
1022 1.1 jdolecek * Detach a mb86950 interface.
1023 1.1 jdolecek */
1024 1.1 jdolecek int
1025 1.1 jdolecek mb86950_detach(sc)
1026 1.1 jdolecek struct mb86950_softc *sc;
1027 1.1 jdolecek {
1028 1.1 jdolecek struct ifnet *ifp = &sc->sc_ec.ec_if;
1029 1.1 jdolecek
1030 1.1 jdolecek /* Succeed now if there's no work to do. */
1031 1.1 jdolecek if ((sc->sc_stat & ESTAR_STAT_ATTACHED) == 0)
1032 1.1 jdolecek return (0);
1033 1.1 jdolecek
1034 1.1 jdolecek /* Delete all media. */
1035 1.1 jdolecek ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY);
1036 1.1 jdolecek
1037 1.1 jdolecek #if NRND > 0
1038 1.1 jdolecek /* Unhook the entropy source. */
1039 1.1 jdolecek rnd_detach_source(&sc->rnd_source);
1040 1.1 jdolecek #endif
1041 1.1 jdolecek ether_ifdetach(ifp);
1042 1.1 jdolecek if_detach(ifp);
1043 1.1 jdolecek
1044 1.1 jdolecek return (0);
1045 1.1 jdolecek }
1046 1.1 jdolecek
1047 1.1 jdolecek #if ESTAR_DEBUG >= 1
1048 1.1 jdolecek void
1049 1.1 jdolecek mb86950_dump(level, sc)
1050 1.1 jdolecek int level;
1051 1.1 jdolecek struct mb86950_softc *sc;
1052 1.1 jdolecek {
1053 1.1 jdolecek bus_space_tag_t bst = sc->sc_bst;
1054 1.1 jdolecek bus_space_handle_t bsh = sc->sc_bsh;
1055 1.1 jdolecek
1056 1.1 jdolecek log(level, "\tDLCR = %02x %02x %02x %02x %02x %02x %02x\n",
1057 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_TX_STAT),
1058 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_TX_INT_EN),
1059 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_RX_STAT),
1060 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_RX_INT_EN),
1061 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_TX_MODE),
1062 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_RX_MODE),
1063 1.1 jdolecek bus_space_read_1(bst, bsh, DLCR_CONFIG));
1064 1.1 jdolecek
1065 1.1 jdolecek /* XXX BMPR2, 4 write only ?
1066 1.1 jdolecek log(level, "\tBMPR = xxxx %04x %04x\n",
1067 1.1 jdolecek bus_space_read_2(bst, bsh, BMPR_TX_LENGTH),
1068 1.1 jdolecek bus_space_read_2(bst, bsh, BMPR_DMA));
1069 1.1 jdolecek */
1070 1.1 jdolecek
1071 1.1 jdolecek }
1072 1.1 jdolecek #endif
1073