* Copyright (c) 1982 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)if_pcl.c 6.7 (Berkeley) %G%
* DEC CSS PCL-11B Parallel Communications Interface
* Written by Mike Muuss and Jeff Schwab.
#include "../machine/pte.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
/* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */
#define PCLMTU (1006) /* Max transmission unit (bytes) */
#define PCLMAXTDM 7 /* Max unit number on TDM bus */
int pclprobe(), pclattach(), pclrint(), pclxint();
int pclinit(), pclioctl(), pcloutput(), pclreset();
struct uba_device
*pclinfo
[NPCL
];
u_short pclstd
[] = { 0 };
#define PCLUNIT(x) minor(x)
struct uba_driver pcldriver
=
{ pclprobe
, 0, pclattach
, 0, pclstd
, "pcl", pclinfo
};
* PCL software status per interface.
* Each interface is referenced by a network interface structure,
* sc_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
* We also have, for each interface, a UBA interface structure, which
* contains information about the UNIBUS resources held by the interface:
* map registers, buffered data paths, etc. Information is cached in this
* structure for use by the if_uba.c routines in running the interface
struct ifnet sc_if
; /* network-visible interface */
struct ifuba sc_ifuba
; /* UNIBUS resources */
short sc_oactive
; /* is output active? */
short sc_olen
; /* length of last output */
short sc_lastdest
; /* previous destination */
short sc_odest
; /* current xmit destination */
short sc_bdest
; /* buffer's stated destination */
short sc_pattern
; /* identification pattern */
* Structure of "local header", which only goes between
* pcloutput and pclstart.
short pcl_dest
; /* Destination PCL station */
* Do non-DMA output of 1 word to determine presence of interface,
* and to find the interupt vector. 1 word messages are a special
* case in the receiver routine, and will be discarded.
register int br
, cvec
; /* r11, r10 value-result */
register struct pcldevice
*addr
= (struct pcldevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
addr
->pcl_rcr
= PCL_RCINIT
;
addr
->pcl_tcr
= PCL_TXINIT
;
addr
->pcl_tsbc
= -4; /* really short */
((1 & 0xF) << 8) | PCL_TXNPR
| PCL_SNDWD
| PCL_STTXM
| PCL_IE
| 0x0030;
addr
->pcl_tcr
= PCL_TXINIT
;
return (sizeof (struct pcldevice
));
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct pcl_softc
*sc
= &pcl_softc
[ui
->ui_unit
];
sc
->sc_if
.if_unit
= ui
->ui_unit
;
sc
->sc_if
.if_name
= "pcl";
sc
->sc_if
.if_mtu
= PCLMTU
;
sc
->sc_if
.if_init
= pclinit
;
sc
->sc_if
.if_output
= pcloutput
;
sc
->sc_if
.if_ioctl
= pclioctl
;
sc
->sc_if
.if_reset
= pclreset
;
sc
->sc_if
.if_flags
= IFF_BROADCAST
;
sc
->sc_ifuba
.ifu_flags
= UBA_NEEDBDP
;
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
register struct uba_device
*ui
;
if (unit
>= NPCL
|| (ui
= pclinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
register struct pcl_softc
*sc
= &pcl_softc
[unit
];
register struct uba_device
*ui
= pclinfo
[unit
];
register struct pcldevice
*addr
;
if (sc
->sc_if
.if_addrlist
== (struct ifaddr
*)0)
if (if_ubainit(&sc
->sc_ifuba
, ui
->ui_ubanum
, 0,
(int)btoc(PCLMTU
)) == 0) {
printf("pcl%d: can't init\n", unit
);
sc
->sc_if
.if_flags
&= ~(IFF_UP
| IFF_RUNNING
);
sc
->sc_if
.if_flags
|= IFF_RUNNING
;
addr
= (struct pcldevice
*)ui
->ui_addr
;
addr
->pcl_rcr
= PCL_RCINIT
;
addr
->pcl_tcr
= PCL_TXINIT
;
* Hang a receive and start any
* pending writes by faking a transmit complete.
addr
->pcl_rdba
= (short) sc
->sc_ifuba
.ifu_r
.ifrw_info
;
addr
->pcl_rdbc
= -PCLMTU
;
addr
->pcl_rcr
= (((int)(sc
->sc_ifuba
.ifu_r
.ifrw_info
>>12))&0x0030) |
PCL_RCNPR
| PCL_RCVWD
| PCL_RCVDAT
| PCL_IE
;
switch (dst
->sa_family
) {
if (in_broadcast(((struct sockaddr_in
*)dst
)->sin_addr
))
dest
= in_lnaof(((struct sockaddr_in
*)dst
)->sin_addr
);
printf("pcl%d: can't handle af%d\n", ifp
->if_unit
,
* Add pseudo local net header.
* Actually, it does not get transmitted, but merely stripped
* off and used by the START routine to route the packet.
* If no space in first mbuf, allocate another.
if (m
->m_off
> MMAXOFF
||
MMINOFF
+ sizeof (struct pcl_header
) > m
->m_off
) {
m2
= m_get(M_DONTWAIT
, MT_HEADER
);
m2
->m_len
= sizeof (struct pcl_header
);
m
->m_off
-= sizeof (struct pcl_header
);
m
->m_len
+= sizeof (struct pcl_header
);
pclp
= mtod(m
, struct pcl_header
*);
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
if (pcl_softc
[ifp
->if_unit
].sc_oactive
== 0)
* Start or restart output on interface.
* If interface is already active, then this is a retransmit.
* If interface is not already active, get another datagram
* to send off of the interface queue, and map it to the interface
* before starting the output.
struct uba_device
*ui
= pclinfo
[unit
];
register struct pcl_softc
*sc
= &pcl_softc
[unit
];
register struct pcldevice
*addr
;
* Not already active: dequeue another request
* and map it to the UNIBUS. If no more requests,
IF_DEQUEUE(&sc
->sc_if
.if_snd
, m
);
* Pull destination node out of pseudo-local net header.
* remove it from outbound data.
* Note that if_wubaput calls m_bcopy, which is prepared for
* m_len to be 0 in the first mbuf in the chain.
sc
->sc_bdest
= mtod(m
, struct pcl_header
*)->pcl_dest
;
sc
->sc_odest
= sc
->sc_bdest
? sc
->sc_bdest
: 1;
m
->m_off
+= sizeof (struct pcl_header
);
m
->m_len
-= sizeof (struct pcl_header
);
/* Map out to the DMA area */
sc
->sc_olen
= if_wubaput(&sc
->sc_ifuba
, m
);
* Have request mapped to UNIBUS for transmission.
* Purge any stale data from this BDP, and start the output.
if (sc
->sc_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(sc
->sc_ifuba
.ifu_uba
, sc
->sc_ifuba
.ifu_w
.ifrw_bdp
);
addr
= (struct pcldevice
*)ui
->ui_addr
;
addr
->pcl_tcr
= PCL_TXINIT
;
addr
->pcl_tsba
= (int)sc
->sc_ifuba
.ifu_w
.ifrw_info
;
addr
->pcl_tsbc
= -sc
->sc_olen
;
* RIB (retry if busy) is used on the second and subsequent packets
* to a single host, because TCP often wants to transmit multiple
* and if they are all going to the same place, the second and
* subsequent ones may be lost due to receiver not ready again yet.
* This can cause serious problems, because the TCP will resend the
* whole window, which just repeats the problem. The result is that
* a perfectly good link appears not to work unless we take steps here.
addr
->pcl_tcr
= (((int)(sc
->sc_ifuba
.ifu_w
.ifrw_info
>>12))&0x0030) |
((sc
->sc_odest
& 0xF)<<8) |
PCL_TXNPR
| PCL_SNDWD
| PCL_STTXM
| PCL_IE
|
(sc
->sc_odest
== sc
->sc_lastdest
? PCL_RIB
: 0);
sc
->sc_lastdest
= sc
->sc_odest
;
* PCL transmitter interrupt.
* Start another output if more data to send.
register struct uba_device
*ui
= pclinfo
[unit
];
register struct pcl_softc
*sc
= &pcl_softc
[unit
];
register struct pcldevice
*addr
= (struct pcldevice
*)ui
->ui_addr
;
if (sc
->sc_oactive
== 0) {
printf ("pcl%d: stray interrupt\n", unit
);
if (addr
->pcl_tsr
& PCL_ERR
) {
sc
->sc_lastdest
= 0; /* don't bother with RIB */
if (addr
->pcl_tsr
& PCL_MSTDWN
) {
addr
->pcl_tmmr
= PCL_MASTER
|PCL_AUTOADDR
;
pclstart(unit
); /* Retry */
printf("pcl%d: master\n", unit
);
if ((addr
->pcl_tsr
& (PCL_ERR
|PCL_RESPB
)) == (PCL_ERR
|0)) {
; /* Receiver Offline -- not exactly an error */
printf("pcl%d: send error, tcr=%b tsr=%b\n",
unit
, addr
->pcl_tcr
, PCL_TCSRBITS
,
addr
->pcl_tsr
, PCL_TERRBITS
);
if (sc
->sc_bdest
== 0 && sc
->sc_odest
< PCLMAXTDM
) {
sc
->sc_odest
++; /* do next host (broadcast) */
if (sc
->sc_ifuba
.ifu_xtofree
) {
m_freem(sc
->sc_ifuba
.ifu_xtofree
);
sc
->sc_ifuba
.ifu_xtofree
= 0;
* PCL interface receiver interrupt.
* If input error just drop packet.
register struct pcl_softc
*sc
= &pcl_softc
[unit
];
struct pcldevice
*addr
= (struct pcldevice
*)pclinfo
[unit
]->ui_addr
;
register struct ifqueue
*inq
;
* Purge BDP; drop if input error indicated.
if (sc
->sc_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(sc
->sc_ifuba
.ifu_uba
, sc
->sc_ifuba
.ifu_r
.ifrw_bdp
);
if (addr
->pcl_rsr
& PCL_ERR
) {
printf("pcl%d: rcv error, rcr=%b rsr=%b\n",
unit
, addr
->pcl_rcr
, PCL_RCSRBITS
,
addr
->pcl_rsr
, PCL_RERRBITS
);
len
= PCLMTU
+ addr
->pcl_rdbc
;
if (len
<= 0 || len
> PCLMTU
) {
printf("pcl%d: bad len=%d.\n", unit
, len
);
/* Really short packets will be part of the startup sequence */
/* Later, do comming-up processing here */
goto setup
; /* drop packet */
* Pull packet off interface.
m
= if_rubaget(&sc
->sc_ifuba
, len
, 0, &sc
->sc_if
);
addr
->pcl_rcr
= PCL_RCINIT
;
addr
->pcl_rdba
= (int)sc
->sc_ifuba
.ifu_r
.ifrw_info
;
addr
->pcl_rdbc
= -PCLMTU
;
addr
->pcl_rcr
= (((int)(sc
->sc_ifuba
.ifu_w
.ifrw_info
>>12))&0x0030) |
PCL_RCNPR
| PCL_RCVWD
| PCL_RCVDAT
| PCL_IE
;
* Process an ioctl request.
register struct ifnet
*ifp
;
int s
= splimp(), error
= 0;
if ((ifp
->if_flags
& IFF_RUNNING
) == 0)