Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /*- |
2 | * Copyright (c) 1997 Poul-Henning Kamp | |
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 | * From Id: lpt.c,v 1.55.2.1 1996/11/12 09:08:38 phk Exp | |
27 | * $FreeBSD: src/sys/dev/ppbus/if_plip.c,v 1.19.2.1 2000/05/24 00:20:57 n_hibma Exp $ | |
28 | */ | |
29 | ||
30 | /* | |
31 | * Parallel port TCP/IP interfaces added. I looked at the driver from | |
32 | * MACH but this is a complete rewrite, and btw. incompatible, and it | |
33 | * should perform better too. I have never run the MACH driver though. | |
34 | * | |
35 | * This driver sends two bytes (0x08, 0x00) in front of each packet, | |
36 | * to allow us to distinguish another format later. | |
37 | * | |
38 | * Now added an Linux/Crynwr compatibility mode which is enabled using | |
39 | * IF_LINK0 - Tim Wilkinson. | |
40 | * | |
41 | * TODO: | |
42 | * Make HDLC/PPP mode, use IF_LLC1 to enable. | |
43 | * | |
44 | * Connect the two computers using a Laplink parallel cable to use this | |
45 | * feature: | |
46 | * | |
47 | * +----------------------------------------+ | |
48 | * |A-name A-End B-End Descr. Port/Bit | | |
49 | * +----------------------------------------+ | |
50 | * |DATA0 2 15 Data 0/0x01 | | |
51 | * |-ERROR 15 2 1/0x08 | | |
52 | * +----------------------------------------+ | |
53 | * |DATA1 3 13 Data 0/0x02 | | |
54 | * |+SLCT 13 3 1/0x10 | | |
55 | * +----------------------------------------+ | |
56 | * |DATA2 4 12 Data 0/0x04 | | |
57 | * |+PE 12 4 1/0x20 | | |
58 | * +----------------------------------------+ | |
59 | * |DATA3 5 10 Strobe 0/0x08 | | |
60 | * |-ACK 10 5 1/0x40 | | |
61 | * +----------------------------------------+ | |
62 | * |DATA4 6 11 Data 0/0x10 | | |
63 | * |BUSY 11 6 1/~0x80 | | |
64 | * +----------------------------------------+ | |
65 | * |GND 18-25 18-25 GND - | | |
66 | * +----------------------------------------+ | |
67 | * | |
68 | * Expect transfer-rates up to 75 kbyte/sec. | |
69 | * | |
70 | * If GCC could correctly grok | |
71 | * register int port asm("edx") | |
72 | * the code would be cleaner | |
73 | * | |
74 | * Poul-Henning Kamp <phk@freebsd.org> | |
75 | */ | |
76 | ||
77 | /* | |
78 | * Update for ppbus, PLIP support only - Nicolas Souchu | |
79 | */ | |
80 | ||
81 | #include "opt_plip.h" | |
82 | ||
83 | #include <sys/param.h> | |
84 | #include <sys/systm.h> | |
85 | #include <sys/module.h> | |
86 | #include <sys/bus.h> | |
87 | #include <sys/mbuf.h> | |
88 | #include <sys/socket.h> | |
89 | #include <sys/sockio.h> | |
90 | #include <sys/kernel.h> | |
91 | #include <sys/malloc.h> | |
1f7ab7c9 | 92 | #include <sys/rman.h> |
a6d5e0d8 | 93 | #include <sys/thread2.h> |
984263bc MD |
94 | |
95 | #include <machine/clock.h> | |
984263bc MD |
96 | |
97 | #include <net/if.h> | |
c2576c10 | 98 | #include <net/ifq_var.h> |
984263bc MD |
99 | #include <net/if_types.h> |
100 | #include <net/netisr.h> | |
101 | ||
102 | #include <netinet/in.h> | |
103 | #include <netinet/in_var.h> | |
104 | ||
105 | #include <net/bpf.h> | |
106 | ||
1f2de5d4 | 107 | #include <bus/ppbus/ppbconf.h> |
984263bc | 108 | #include "ppbus_if.h" |
1f2de5d4 | 109 | #include <bus/ppbus/ppbio.h> |
984263bc MD |
110 | |
111 | #ifndef LPMTU /* MTU for the lp# interfaces */ | |
112 | #define LPMTU 1500 | |
113 | #endif | |
114 | ||
115 | #ifndef LPMAXSPIN1 /* DELAY factor for the lp# interfaces */ | |
116 | #define LPMAXSPIN1 8000 /* Spinning for remote intr to happen */ | |
117 | #endif | |
118 | ||
119 | #ifndef LPMAXSPIN2 /* DELAY factor for the lp# interfaces */ | |
120 | #define LPMAXSPIN2 500 /* Spinning for remote handshake to happen */ | |
121 | #endif | |
122 | ||
123 | #ifndef LPMAXERRS /* Max errors before !RUNNING */ | |
124 | #define LPMAXERRS 100 | |
125 | #endif | |
126 | ||
127 | #define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ | |
128 | #define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ | |
129 | #define MLPIPHDRLEN CLPIPHDRLEN | |
130 | ||
131 | #define LPIPHDRLEN 2 /* We send 0x08, 0x00 in front of packet */ | |
132 | #define LPIP_SHAKE 0x40 /* This bit toggles between nibble reception */ | |
133 | #if !defined(MLPIPHDRLEN) || LPIPHDRLEN > MLPIPHDRLEN | |
134 | #define MLPIPHDRLEN LPIPHDRLEN | |
135 | #endif | |
136 | ||
137 | #define LPIPTBLSIZE 256 /* Size of octet translation table */ | |
138 | ||
e3869ec7 | 139 | #define lprintf if (lptflag) kprintf |
984263bc MD |
140 | |
141 | #ifdef PLIP_DEBUG | |
142 | static int volatile lptflag = 1; | |
143 | #else | |
144 | static int volatile lptflag = 0; | |
145 | #endif | |
146 | ||
147 | struct lp_data { | |
148 | unsigned short lp_unit; | |
149 | ||
150 | struct ifnet sc_if; | |
151 | u_char *sc_ifbuf; | |
152 | int sc_iferrs; | |
153 | ||
154 | struct resource *res_irq; | |
155 | }; | |
156 | ||
157 | /* Tables for the lp# interface */ | |
158 | static u_char *txmith; | |
159 | #define txmitl (txmith+(1*LPIPTBLSIZE)) | |
160 | #define trecvh (txmith+(2*LPIPTBLSIZE)) | |
161 | #define trecvl (txmith+(3*LPIPTBLSIZE)) | |
162 | ||
163 | static u_char *ctxmith; | |
164 | #define ctxmitl (ctxmith+(1*LPIPTBLSIZE)) | |
165 | #define ctrecvh (ctxmith+(2*LPIPTBLSIZE)) | |
166 | #define ctrecvl (ctxmith+(3*LPIPTBLSIZE)) | |
167 | ||
168 | /* Functions for the lp# interface */ | |
169 | static int lpinittables(void); | |
bd4539cc | 170 | static int lpioctl(struct ifnet *, u_long, caddr_t, struct ucred *); |
984263bc MD |
171 | static int lpoutput(struct ifnet *, struct mbuf *, struct sockaddr *, |
172 | struct rtentry *); | |
173 | static void lp_intr(void *); | |
174 | ||
175 | #define DEVTOSOFTC(dev) \ | |
176 | ((struct lp_data *)device_get_softc(dev)) | |
177 | #define UNITOSOFTC(unit) \ | |
178 | ((struct lp_data *)devclass_get_softc(lp_devclass, (unit))) | |
179 | #define UNITODEVICE(unit) \ | |
180 | (devclass_get_device(lp_devclass, (unit))) | |
181 | ||
182 | static devclass_t lp_devclass; | |
183 | ||
984263bc MD |
184 | /* |
185 | * lpprobe() | |
186 | */ | |
187 | static int | |
188 | lp_probe(device_t dev) | |
189 | { | |
190 | device_t ppbus = device_get_parent(dev); | |
191 | struct lp_data *lp; | |
192 | int zero = 0; | |
193 | uintptr_t irq; | |
194 | ||
195 | lp = DEVTOSOFTC(dev); | |
984263bc MD |
196 | |
197 | /* retrieve the ppbus irq */ | |
198 | BUS_READ_IVAR(ppbus, dev, PPBUS_IVAR_IRQ, &irq); | |
199 | ||
200 | /* if we haven't interrupts, the probe fails */ | |
201 | if (irq == -1) { | |
202 | device_printf(dev, "not an interrupt driven port, failed.\n"); | |
203 | return (ENXIO); | |
204 | } | |
205 | ||
206 | /* reserve the interrupt resource, expecting irq is available to continue */ | |
abf21138 SZ |
207 | lp->res_irq = bus_alloc_legacy_irq_resource(dev, &zero, irq, |
208 | RF_SHAREABLE); | |
4090d6ff | 209 | if (lp->res_irq == NULL) { |
984263bc MD |
210 | device_printf(dev, "cannot reserve interrupt, failed.\n"); |
211 | return (ENXIO); | |
212 | } | |
213 | ||
214 | /* | |
215 | * lp dependent initialisation. | |
216 | */ | |
217 | lp->lp_unit = device_get_unit(dev); | |
218 | ||
219 | device_set_desc(dev, "PLIP network interface"); | |
220 | ||
221 | return (0); | |
222 | } | |
223 | ||
224 | static int | |
225 | lp_attach (device_t dev) | |
226 | { | |
227 | struct lp_data *lp = DEVTOSOFTC(dev); | |
228 | struct ifnet *ifp = &lp->sc_if; | |
229 | ||
230 | ifp->if_softc = lp; | |
3e4a09e7 | 231 | if_initname(ifp, "lp", device_get_unit(dev)); |
984263bc MD |
232 | ifp->if_mtu = LPMTU; |
233 | ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST; | |
234 | ifp->if_ioctl = lpioctl; | |
235 | ifp->if_output = lpoutput; | |
236 | ifp->if_type = IFT_PARA; | |
237 | ifp->if_hdrlen = 0; | |
238 | ifp->if_addrlen = 0; | |
c2576c10 | 239 | ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); |
78195a76 | 240 | if_attach(ifp, NULL); |
984263bc MD |
241 | |
242 | bpfattach(ifp, DLT_NULL, sizeof(u_int32_t)); | |
243 | ||
244 | return (0); | |
245 | } | |
246 | /* | |
247 | * Build the translation tables for the LPIP (BSD unix) protocol. | |
248 | * We don't want to calculate these nasties in our tight loop, so we | |
249 | * precalculate them when we initialize. | |
250 | */ | |
251 | static int | |
252 | lpinittables (void) | |
253 | { | |
254 | int i; | |
255 | ||
256 | if (!txmith) | |
efda3bd0 | 257 | txmith = kmalloc(4*LPIPTBLSIZE, M_DEVBUF, M_WAITOK); |
984263bc MD |
258 | |
259 | if (!ctxmith) | |
efda3bd0 | 260 | ctxmith = kmalloc(4*LPIPTBLSIZE, M_DEVBUF, M_WAITOK); |
984263bc MD |
261 | |
262 | for (i=0; i < LPIPTBLSIZE; i++) { | |
263 | ctxmith[i] = (i & 0xF0) >> 4; | |
264 | ctxmitl[i] = 0x10 | (i & 0x0F); | |
265 | ctrecvh[i] = (i & 0x78) << 1; | |
266 | ctrecvl[i] = (i & 0x78) >> 3; | |
267 | } | |
268 | ||
269 | for (i=0; i < LPIPTBLSIZE; i++) { | |
270 | txmith[i] = ((i & 0x80) >> 3) | ((i & 0x70) >> 4) | 0x08; | |
271 | txmitl[i] = ((i & 0x08) << 1) | (i & 0x07); | |
272 | trecvh[i] = ((~i) & 0x80) | ((i & 0x38) << 1); | |
273 | trecvl[i] = (((~i) & 0x80) >> 4) | ((i & 0x38) >> 3); | |
274 | } | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | /* | |
280 | * Process an ioctl request. | |
281 | */ | |
282 | ||
283 | static int | |
bd4539cc | 284 | lpioctl (struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) |
984263bc | 285 | { |
3e4a09e7 | 286 | device_t dev = UNITODEVICE(ifp->if_dunit); |
984263bc MD |
287 | device_t ppbus = device_get_parent(dev); |
288 | struct lp_data *sc = DEVTOSOFTC(dev); | |
289 | struct ifaddr *ifa = (struct ifaddr *)data; | |
290 | struct ifreq *ifr = (struct ifreq *)data; | |
291 | u_char *ptr; | |
292 | void *ih; | |
293 | int error; | |
294 | ||
295 | switch (cmd) { | |
296 | ||
297 | case SIOCSIFDSTADDR: | |
298 | case SIOCAIFADDR: | |
299 | case SIOCSIFADDR: | |
300 | if (ifa->ifa_addr->sa_family != AF_INET) | |
301 | return EAFNOSUPPORT; | |
302 | ||
303 | ifp->if_flags |= IFF_UP; | |
304 | /* FALLTHROUGH */ | |
305 | case SIOCSIFFLAGS: | |
306 | if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) { | |
307 | ||
308 | ppb_wctr(ppbus, 0x00); | |
309 | ifp->if_flags &= ~IFF_RUNNING; | |
310 | ||
311 | /* IFF_UP is not set, try to release the bus anyway */ | |
312 | ppb_release_bus(ppbus, dev); | |
313 | break; | |
314 | } | |
315 | if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) { | |
316 | ||
317 | /* XXX | |
318 | * Should the request be interruptible? | |
319 | */ | |
320 | if ((error = ppb_request_bus(ppbus, dev, PPB_WAIT|PPB_INTR))) | |
321 | return (error); | |
322 | ||
323 | /* Now IFF_UP means that we own the bus */ | |
324 | ||
325 | ppb_set_mode(ppbus, PPB_COMPATIBLE); | |
326 | ||
327 | if (lpinittables()) { | |
328 | ppb_release_bus(ppbus, dev); | |
329 | return ENOBUFS; | |
330 | } | |
331 | ||
efda3bd0 | 332 | sc->sc_ifbuf = kmalloc(sc->sc_if.if_mtu + MLPIPHDRLEN, |
984263bc | 333 | M_DEVBUF, M_WAITOK); |
984263bc MD |
334 | |
335 | /* attach our interrupt handler, later detached when the bus is released */ | |
ee61f228 | 336 | error = BUS_SETUP_INTR(ppbus, dev, sc->res_irq, 0, |
0e6f0e28 | 337 | lp_intr, dev, &ih, NULL, NULL); |
e9cb6d99 | 338 | if (error) { |
984263bc MD |
339 | ppb_release_bus(ppbus, dev); |
340 | return (error); | |
341 | } | |
342 | ||
343 | ppb_wctr(ppbus, IRQENABLE); | |
344 | ifp->if_flags |= IFF_RUNNING; | |
345 | } | |
346 | break; | |
347 | ||
348 | case SIOCSIFMTU: | |
349 | ptr = sc->sc_ifbuf; | |
efda3bd0 | 350 | sc->sc_ifbuf = kmalloc(ifr->ifr_mtu+MLPIPHDRLEN, M_DEVBUF, M_WAITOK); |
984263bc | 351 | if (ptr) |
efda3bd0 | 352 | kfree(ptr,M_DEVBUF); |
984263bc MD |
353 | sc->sc_if.if_mtu = ifr->ifr_mtu; |
354 | break; | |
355 | ||
356 | case SIOCGIFMTU: | |
357 | ifr->ifr_mtu = sc->sc_if.if_mtu; | |
358 | break; | |
359 | ||
360 | case SIOCADDMULTI: | |
361 | case SIOCDELMULTI: | |
4090d6ff | 362 | if (ifr == NULL) { |
984263bc MD |
363 | return EAFNOSUPPORT; /* XXX */ |
364 | } | |
365 | switch (ifr->ifr_addr.sa_family) { | |
366 | ||
367 | case AF_INET: | |
368 | break; | |
369 | ||
370 | default: | |
371 | return EAFNOSUPPORT; | |
372 | } | |
373 | break; | |
374 | ||
375 | case SIOCGIFMEDIA: | |
376 | /* | |
377 | * No ifmedia support at this stage; maybe use it | |
378 | * in future for eg. protocol selection. | |
379 | */ | |
380 | return EINVAL; | |
381 | ||
382 | default: | |
383 | lprintf("LP:ioctl(0x%lx)\n", cmd); | |
384 | return EINVAL; | |
385 | } | |
386 | return 0; | |
387 | } | |
388 | ||
389 | static __inline int | |
390 | clpoutbyte (u_char byte, int spin, device_t ppbus) | |
391 | { | |
392 | ppb_wdtr(ppbus, ctxmitl[byte]); | |
393 | while (ppb_rstr(ppbus) & CLPIP_SHAKE) | |
394 | if (--spin == 0) { | |
395 | return 1; | |
396 | } | |
397 | ppb_wdtr(ppbus, ctxmith[byte]); | |
398 | while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) | |
399 | if (--spin == 0) { | |
400 | return 1; | |
401 | } | |
402 | return 0; | |
403 | } | |
404 | ||
405 | static __inline int | |
406 | clpinbyte (int spin, device_t ppbus) | |
407 | { | |
408 | u_char c, cl; | |
409 | ||
410 | while((ppb_rstr(ppbus) & CLPIP_SHAKE)) | |
411 | if(!--spin) { | |
412 | return -1; | |
413 | } | |
414 | cl = ppb_rstr(ppbus); | |
415 | ppb_wdtr(ppbus, 0x10); | |
416 | ||
417 | while(!(ppb_rstr(ppbus) & CLPIP_SHAKE)) | |
418 | if(!--spin) { | |
419 | return -1; | |
420 | } | |
421 | c = ppb_rstr(ppbus); | |
422 | ppb_wdtr(ppbus, 0x00); | |
423 | ||
424 | return (ctrecvl[cl] | ctrecvh[c]); | |
425 | } | |
426 | ||
427 | static void | |
428 | lptap(struct ifnet *ifp, struct mbuf *m) | |
429 | { | |
430 | /* | |
1f8e62c9 | 431 | * We need to prepend the address family as a four byte field. |
984263bc | 432 | */ |
1f8e62c9 JS |
433 | static const uint32_t af = AF_INET; |
434 | ||
fda7d388 SZ |
435 | if (ifp->if_bpf) { |
436 | bpf_gettoken(); | |
437 | if (ifp->if_bpf) | |
438 | bpf_ptap(ifp->if_bpf, m, &af, sizeof(af)); | |
439 | bpf_reltoken(); | |
440 | } | |
984263bc MD |
441 | } |
442 | ||
443 | static void | |
444 | lp_intr (void *arg) | |
445 | { | |
446 | device_t dev = (device_t)arg; | |
848a176c | 447 | device_t ppbus = device_get_parent(dev); |
984263bc | 448 | struct lp_data *sc = DEVTOSOFTC(dev); |
a6d5e0d8 | 449 | int len, j; |
984263bc MD |
450 | u_char *bp; |
451 | u_char c, cl; | |
452 | struct mbuf *top; | |
453 | ||
a6d5e0d8 | 454 | crit_enter(); |
984263bc MD |
455 | |
456 | if (sc->sc_if.if_flags & IFF_LINK0) { | |
457 | ||
458 | /* Ack. the request */ | |
459 | ppb_wdtr(ppbus, 0x01); | |
460 | ||
461 | /* Get the packet length */ | |
462 | j = clpinbyte(LPMAXSPIN2, ppbus); | |
463 | if (j == -1) | |
464 | goto err; | |
465 | len = j; | |
466 | j = clpinbyte(LPMAXSPIN2, ppbus); | |
467 | if (j == -1) | |
468 | goto err; | |
469 | len = len + (j << 8); | |
470 | if (len > sc->sc_if.if_mtu + MLPIPHDRLEN) | |
471 | goto err; | |
472 | ||
473 | bp = sc->sc_ifbuf; | |
474 | ||
475 | while (len--) { | |
476 | j = clpinbyte(LPMAXSPIN2, ppbus); | |
477 | if (j == -1) { | |
478 | goto err; | |
479 | } | |
480 | *bp++ = j; | |
481 | } | |
482 | /* Get and ignore checksum */ | |
483 | j = clpinbyte(LPMAXSPIN2, ppbus); | |
484 | if (j == -1) { | |
485 | goto err; | |
486 | } | |
487 | ||
488 | len = bp - sc->sc_ifbuf; | |
489 | if (len <= CLPIPHDRLEN) | |
490 | goto err; | |
491 | ||
492 | sc->sc_iferrs = 0; | |
493 | ||
984263bc | 494 | len -= CLPIPHDRLEN; |
d40991ef SZ |
495 | IFNET_STAT_INC(&sc->sc_if, ipackets, 1); |
496 | IFNET_STAT_INC(&sc->sc_if, ibytes, len); | |
afd2da4d | 497 | top = m_devget(sc->sc_ifbuf + CLPIPHDRLEN, len, 0, &sc->sc_if); |
984263bc MD |
498 | if (top) { |
499 | if (sc->sc_if.if_bpf) | |
500 | lptap(&sc->sc_if, top); | |
8bde602d | 501 | netisr_queue(NETISR_IP, top); |
984263bc MD |
502 | } |
503 | goto done; | |
504 | } | |
505 | while ((ppb_rstr(ppbus) & LPIP_SHAKE)) { | |
506 | len = sc->sc_if.if_mtu + LPIPHDRLEN; | |
507 | bp = sc->sc_ifbuf; | |
508 | while (len--) { | |
509 | ||
510 | cl = ppb_rstr(ppbus); | |
511 | ppb_wdtr(ppbus, 8); | |
512 | ||
513 | j = LPMAXSPIN2; | |
514 | while((ppb_rstr(ppbus) & LPIP_SHAKE)) | |
515 | if(!--j) goto err; | |
516 | ||
517 | c = ppb_rstr(ppbus); | |
518 | ppb_wdtr(ppbus, 0); | |
519 | ||
520 | *bp++= trecvh[cl] | trecvl[c]; | |
521 | ||
522 | j = LPMAXSPIN2; | |
523 | while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) { | |
524 | if (cl != c && | |
525 | (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == | |
526 | (c & 0xf8)) | |
527 | goto end; | |
528 | if (!--j) goto err; | |
529 | } | |
530 | } | |
531 | ||
532 | end: | |
533 | len = bp - sc->sc_ifbuf; | |
534 | if (len <= LPIPHDRLEN) | |
535 | goto err; | |
536 | ||
537 | sc->sc_iferrs = 0; | |
538 | ||
984263bc | 539 | len -= LPIPHDRLEN; |
d40991ef SZ |
540 | IFNET_STAT_INC(&sc->sc_if, ipackets, 1); |
541 | IFNET_STAT_INC(&sc->sc_if, ibytes, len); | |
afd2da4d | 542 | top = m_devget(sc->sc_ifbuf + LPIPHDRLEN, len, 0, &sc->sc_if); |
984263bc MD |
543 | if (top) { |
544 | if (sc->sc_if.if_bpf) | |
545 | lptap(&sc->sc_if, top); | |
8bde602d | 546 | netisr_queue(NETISR_IP, top); |
984263bc MD |
547 | } |
548 | } | |
549 | goto done; | |
550 | ||
551 | err: | |
552 | ppb_wdtr(ppbus, 0); | |
553 | lprintf("R"); | |
d40991ef | 554 | IFNET_STAT_INC(&sc->sc_if, ierrors, 1); |
984263bc MD |
555 | sc->sc_iferrs++; |
556 | ||
557 | /* | |
558 | * We are not able to send receive anything for now, | |
559 | * so stop wasting our time | |
560 | */ | |
561 | if (sc->sc_iferrs > LPMAXERRS) { | |
e3869ec7 | 562 | kprintf("lp%d: Too many errors, Going off-line.\n", device_get_unit(dev)); |
984263bc MD |
563 | ppb_wctr(ppbus, 0x00); |
564 | sc->sc_if.if_flags &= ~IFF_RUNNING; | |
565 | sc->sc_iferrs=0; | |
566 | } | |
567 | ||
568 | done: | |
a6d5e0d8 | 569 | crit_exit(); |
984263bc MD |
570 | return; |
571 | } | |
572 | ||
573 | static __inline int | |
574 | lpoutbyte (u_char byte, int spin, device_t ppbus) | |
575 | { | |
576 | ppb_wdtr(ppbus, txmith[byte]); | |
577 | while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) | |
578 | if (--spin == 0) | |
579 | return 1; | |
580 | ppb_wdtr(ppbus, txmitl[byte]); | |
581 | while (ppb_rstr(ppbus) & LPIP_SHAKE) | |
582 | if (--spin == 0) | |
583 | return 1; | |
584 | return 0; | |
585 | } | |
586 | ||
587 | static int | |
588 | lpoutput (struct ifnet *ifp, struct mbuf *m, | |
589 | struct sockaddr *dst, struct rtentry *rt) | |
590 | { | |
3e4a09e7 | 591 | device_t dev = UNITODEVICE(ifp->if_dunit); |
984263bc | 592 | device_t ppbus = device_get_parent(dev); |
a6d5e0d8 | 593 | int err; |
984263bc MD |
594 | struct mbuf *mm; |
595 | u_char *cp = "\0\0"; | |
596 | u_char chksum = 0; | |
597 | int count = 0; | |
598 | int i, len, spin; | |
599 | ||
600 | /* We need a sensible value if we abort */ | |
601 | cp++; | |
602 | ifp->if_flags |= IFF_RUNNING; | |
603 | ||
604 | err = 1; /* assume we're aborting because of an error */ | |
605 | ||
a6d5e0d8 | 606 | crit_enter(); |
984263bc MD |
607 | |
608 | /* Suspend (on laptops) or receive-errors might have taken us offline */ | |
609 | ppb_wctr(ppbus, IRQENABLE); | |
610 | ||
611 | if (ifp->if_flags & IFF_LINK0) { | |
612 | ||
613 | if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { | |
614 | lprintf("&"); | |
615 | lp_intr(dev); | |
616 | } | |
617 | ||
618 | /* Alert other end to pending packet */ | |
619 | spin = LPMAXSPIN1; | |
620 | ppb_wdtr(ppbus, 0x08); | |
621 | while ((ppb_rstr(ppbus) & 0x08) == 0) | |
622 | if (--spin == 0) { | |
623 | goto nend; | |
624 | } | |
625 | ||
626 | /* Calculate length of packet, then send that */ | |
627 | ||
628 | count += 14; /* Ethernet header len */ | |
629 | ||
630 | mm = m; | |
631 | for (mm = m; mm; mm = mm->m_next) { | |
632 | count += mm->m_len; | |
633 | } | |
634 | if (clpoutbyte(count & 0xFF, LPMAXSPIN1, ppbus)) | |
635 | goto nend; | |
636 | if (clpoutbyte((count >> 8) & 0xFF, LPMAXSPIN1, ppbus)) | |
637 | goto nend; | |
638 | ||
639 | /* Send dummy ethernet header */ | |
640 | for (i = 0; i < 12; i++) { | |
641 | if (clpoutbyte(i, LPMAXSPIN1, ppbus)) | |
642 | goto nend; | |
643 | chksum += i; | |
644 | } | |
645 | ||
646 | if (clpoutbyte(0x08, LPMAXSPIN1, ppbus)) | |
647 | goto nend; | |
648 | if (clpoutbyte(0x00, LPMAXSPIN1, ppbus)) | |
649 | goto nend; | |
650 | chksum += 0x08 + 0x00; /* Add into checksum */ | |
651 | ||
652 | mm = m; | |
653 | do { | |
654 | cp = mtod(mm, u_char *); | |
655 | len = mm->m_len; | |
656 | while (len--) { | |
657 | chksum += *cp; | |
658 | if (clpoutbyte(*cp++, LPMAXSPIN2, ppbus)) | |
659 | goto nend; | |
660 | } | |
661 | } while ((mm = mm->m_next)); | |
662 | ||
663 | /* Send checksum */ | |
664 | if (clpoutbyte(chksum, LPMAXSPIN2, ppbus)) | |
665 | goto nend; | |
666 | ||
667 | /* Go quiescent */ | |
668 | ppb_wdtr(ppbus, 0); | |
669 | ||
670 | err = 0; /* No errors */ | |
671 | ||
672 | nend: | |
673 | if (err) { /* if we didn't timeout... */ | |
d40991ef | 674 | IFNET_STAT_INC(ifp, oerrors, 1); |
984263bc MD |
675 | lprintf("X"); |
676 | } else { | |
d40991ef SZ |
677 | IFNET_STAT_INC(ifp, opackets, 1); |
678 | IFNET_STAT_INC(ifp, obytes, m->m_pkthdr.len); | |
984263bc MD |
679 | if (ifp->if_bpf) |
680 | lptap(ifp, m); | |
681 | } | |
682 | ||
683 | m_freem(m); | |
684 | ||
685 | if (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) { | |
686 | lprintf("^"); | |
687 | lp_intr(dev); | |
688 | } | |
a6d5e0d8 | 689 | crit_exit(); |
984263bc MD |
690 | return 0; |
691 | } | |
692 | ||
693 | if (ppb_rstr(ppbus) & LPIP_SHAKE) { | |
694 | lprintf("&"); | |
695 | lp_intr(dev); | |
696 | } | |
697 | ||
698 | if (lpoutbyte(0x08, LPMAXSPIN1, ppbus)) | |
699 | goto end; | |
700 | if (lpoutbyte(0x00, LPMAXSPIN2, ppbus)) | |
701 | goto end; | |
702 | ||
703 | mm = m; | |
704 | do { | |
705 | cp = mtod(mm,u_char *); | |
706 | len = mm->m_len; | |
707 | while (len--) | |
708 | if (lpoutbyte(*cp++, LPMAXSPIN2, ppbus)) | |
709 | goto end; | |
710 | } while ((mm = mm->m_next)); | |
711 | ||
712 | err = 0; /* no errors were encountered */ | |
713 | ||
714 | end: | |
715 | --cp; | |
716 | ppb_wdtr(ppbus, txmitl[*cp] ^ 0x17); | |
717 | ||
718 | if (err) { /* if we didn't timeout... */ | |
d40991ef | 719 | IFNET_STAT_INC(ifp, oerrors, 1); |
984263bc MD |
720 | lprintf("X"); |
721 | } else { | |
d40991ef SZ |
722 | IFNET_STAT_INC(ifp, opackets, 1); |
723 | IFNET_STAT_INC(ifp, obytes, m->m_pkthdr.len); | |
984263bc MD |
724 | if (ifp->if_bpf) |
725 | lptap(ifp, m); | |
726 | } | |
727 | ||
728 | m_freem(m); | |
729 | ||
730 | if (ppb_rstr(ppbus) & LPIP_SHAKE) { | |
731 | lprintf("^"); | |
732 | lp_intr(dev); | |
733 | } | |
734 | ||
a6d5e0d8 | 735 | crit_exit(); |
984263bc MD |
736 | return 0; |
737 | } | |
738 | ||
39b5d600 MD |
739 | /* |
740 | * Because plip is a static device that always exists under any attached | |
741 | * ppbus device, and not scanned by the ppbus device, we need an identify | |
742 | * function to install the device. | |
743 | */ | |
984263bc MD |
744 | static device_method_t lp_methods[] = { |
745 | /* device interface */ | |
39b5d600 | 746 | DEVMETHOD(device_identify, bus_generic_identify), |
984263bc MD |
747 | DEVMETHOD(device_probe, lp_probe), |
748 | DEVMETHOD(device_attach, lp_attach), | |
749 | ||
d3c9c58e | 750 | DEVMETHOD_END |
984263bc MD |
751 | }; |
752 | ||
753 | static driver_t lp_driver = { | |
754 | "plip", | |
755 | lp_methods, | |
756 | sizeof(struct lp_data), | |
757 | }; | |
758 | ||
32832096 | 759 | DECLARE_DUMMY_MODULE(if_plip); |
aa2b9d05 | 760 | DRIVER_MODULE(if_plip, ppbus, lp_driver, lp_devclass, NULL, NULL); |
32832096 | 761 |