// SPDX-License-Identifier: GPL-2.0-or-later /* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */ /* PLIP: A parallel port "network" driver for Linux. */ /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */ /* * Authors: Donald Becker <becker@scyld.com> * Tommy Thorn <thorn@daimi.aau.dk> * Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp> * Alan Cox <gw4pts@gw4pts.ampr.org> * Peter Bauer <100136.3530@compuserve.com> * Niibe Yutaka <gniibe@mri.co.jp> * Nimrod Zimerman <zimerman@mailandnews.com> * * Enhancements: * Modularization and ifreq/ifmap support by Alan Cox. * Rewritten by Niibe Yutaka. * parport-sharing awareness code by Philip Blundell. * SMP locking by Niibe Yutaka. * Support for parallel ports with no IRQ (poll mode), * Modifications to use the parallel port API * by Nimrod Zimerman. * * Fixes: * Niibe Yutaka * - Module initialization. * - MTU fix. * - Make sure other end is OK, before sending a packet. * - Fix immediate timer problem. * * Al Viro * - Changed {enable,disable}_irq handling to make it work * with new ("stack") semantics.
*/
/* * Original version and the name 'PLIP' from Donald Becker <becker@scyld.com> * inspired by Russ Nelson's parallel port packet driver. * * NOTE: * Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0. * Because of the necessity to communicate to DOS machines with the * Crynwr packet driver, Peter Bauer changed the protocol again * back to original protocol. * * This version follows original PLIP protocol. * So, this PLIP can't communicate the PLIP of Linux v1.0.
*/
/* * To use with DOS box, please do (Turn on ARP switch): * # ifconfig plip[0-2] arp
*/ staticconstchar version[] = "NET3 PLIP version 2.4-parport gniibe@mri.co.jp\n";
/* Sources: Ideas and protocols came from Russ Nelson's <nelson@crynwr.com> "parallel.asm" parallel port packet driver.
The "Crynwr" parallel port standard specifies the following protocol: Trigger by sending nibble '0x8' (this causes interrupt on other end) count-low octet count-high octet ... data octets checksum octet Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)> <wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
The packet is encapsulated as if it were ethernet.
The cable used is a de facto standard parallel null cable -- sold as a "LapLink" cable by various places. You'll need a 12-conductor cable to make one yourself. The wiring is: SLCTIN 17 - 17 GROUND 25 - 25 D0->ERROR 2 - 15 15 - 2 D1->SLCT 3 - 13 13 - 3 D2->PAPOUT 4 - 12 12 - 4 D3->ACK 5 - 10 10 - 5 D4->BUSY 6 - 11 11 - 6 Do not connect the other pins. They are D5,D6,D7 are 7,8,9 STROBE is 1, FEED is 14, INIT is 16 extra grounds are 18,19,20,21,22,23,24
*/
/* Entry point of PLIP driver. Probe the hardware, and register/initialize the driver.
PLIP is rather weird, because of the way it interacts with the parport system. It is _not_ initialised from Space.c. Instead, plip_init() is called, and that function makes up a "struct net_device" for each port, and then calls us here.
if (dev->irq == -1)
INIT_DELAYED_WORK(&nl->timer, plip_timer_bh);
spin_lock_init(&nl->lock);
}
/* Bottom half handler for the delayed request. This routine is kicked by do_timer().
Request `plip_bh' to be invoked. */ staticvoid
plip_kick_bh(struct work_struct *work)
{ struct net_local *nl =
container_of(work, struct net_local, deferred.work);
if (nl->is_deferred)
schedule_work(&nl->immediate);
}
staticint
plip_bh_timeout_error(struct net_device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv, int error)
{ unsignedchar c0; /* * This is tricky. If we got here from the beginning of send (either * with ERROR or HS_TIMEOUT) we have IRQ enabled. Otherwise it's * already disabled. With the old variant of {enable,disable}_irq() * extra disable_irq() was a no-op. Now it became mortal - it's * unbalanced and thus we'll never re-enable IRQ (until rmmod plip, * that is). So we have to treat HS_TIMEOUT and ERROR from send * in a special way.
*/
spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_SEND) {
case PLIP_NB_1:
cx = nibble_timeout; while (1) {
c0 = read_status(dev);
udelay(PLIP_DELAY_UNIT); if (c0 & 0x80) {
c1 = read_status(dev); if (c0 == c1) break;
} if (--cx == 0) return TIMEOUT;
}
*data_p |= (c0 << 1) & 0xf0;
write_data (dev, 0x00); /* send ACK */
*ns_p = PLIP_NB_BEGIN; break; case PLIP_NB_2: break;
} return OK;
}
/* * Determine the packet's protocol ID. The rule here is that we * assume 802.3 if the type field is short enough to be a length. * This is normal practice and works for any 'now in use' protocol. * * PLIP is ethernet ish but the daddr might not be valid if unicast. * PLIP fortunately has no bus architecture (its Point-to-point). * * We can't fix the daddr thing as that quirk (more bug) is embedded * in far too many old systems not all even running Linux.
*/
/* * This ALLMULTI check should be redundant by 1.4 * so don't forget to remove it.
*/
if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) return eth->h_proto;
rawp = skb->data;
/* * This is a magic hack to spot IPX packets. Older Novell breaks * the protocol design and runs IPX over 802.3 without an 802.2 LLC * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This * won't work for fault tolerant netware but does for the rest.
*/ if (*(unsignedshort *)rawp == 0xFFFF) return htons(ETH_P_802_3);
/* * Real 802.2 LLC
*/ return htons(ETH_P_802_2);
}
case PLIP_PK_LENGTH_MSB: if (plip_receive(nibble_timeout, dev,
&rcv->nibble, &rcv->length.b.msb)) return TIMEOUT; if (rcv->length.h > dev->mtu + dev->hard_header_len ||
rcv->length.h < 8) {
printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h); return ERROR;
} /* Malloc up new buffer. */
rcv->skb = dev_alloc_skb(rcv->length.h + 2); if (rcv->skb == NULL) {
printk(KERN_ERR "%s: Memory squeeze.\n", dev->name); return ERROR;
}
skb_reserve(rcv->skb, 2); /* Align IP on 16 byte boundaries */
skb_put(rcv->skb,rcv->length.h);
rcv->skb->dev = dev;
rcv->state = PLIP_PK_DATA;
rcv->byte = 0;
rcv->checksum = 0;
fallthrough;
case PLIP_PK_DATA:
lbuf = rcv->skb->data; do { if (plip_receive(nibble_timeout, dev,
&rcv->nibble, &lbuf[rcv->byte])) return TIMEOUT;
} while (++rcv->byte < rcv->length.h); do {
rcv->checksum += lbuf[--rcv->byte];
} while (rcv->byte);
rcv->state = PLIP_PK_CHECKSUM;
fallthrough;
case PLIP_PK_CHECKSUM: if (plip_receive(nibble_timeout, dev,
&rcv->nibble, &rcv->data)) return TIMEOUT; if (rcv->data != rcv->checksum) {
dev->stats.rx_crc_errors++; if (net_debug)
printk(KERN_DEBUG "%s: checksum error\n", dev->name); return ERROR;
}
rcv->state = PLIP_PK_DONE;
fallthrough;
case PLIP_PK_DONE: /* Inform the upper layer for the arrival of a packet. */
rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
netif_rx(rcv->skb);
dev->stats.rx_bytes += rcv->length.h;
dev->stats.rx_packets++;
rcv->skb = NULL; if (net_debug > 2)
printk(KERN_DEBUG "%s: receive end\n", dev->name);
switch (snd->state) { case PLIP_PK_TRIGGER: if ((read_status(dev) & 0xf8) != 0x80) return HS_TIMEOUT;
/* Trigger remote rx interrupt. */
write_data (dev, 0x08);
cx = nl->trigger; while (1) {
udelay(PLIP_DELAY_UNIT);
spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_RECEIVE) {
spin_unlock_irq(&nl->lock); /* Interrupted. */
dev->stats.collisions++; return OK;
}
c0 = read_status(dev); if (c0 & 0x08) {
spin_unlock_irq(&nl->lock);
DISABLE(dev->irq);
synchronize_irq(dev->irq); if (nl->connection == PLIP_CN_RECEIVE) { /* Interrupted. We don't need to enable irq,
as it is soon disabled. */ /* Yes, we do. New variant of {enable,disable}_irq *counts*
them. -- AV */
ENABLE(dev->irq);
dev->stats.collisions++; return OK;
}
disable_parport_interrupts (dev); if (net_debug > 2)
printk(KERN_DEBUG "%s: send start\n", dev->name);
snd->state = PLIP_PK_LENGTH_LSB;
snd->nibble = PLIP_NB_BEGIN;
nl->timeout_count = 0; break;
}
spin_unlock_irq(&nl->lock); if (--cx == 0) {
write_data (dev, 0x00); return HS_TIMEOUT;
}
}
fallthrough;
case PLIP_PK_LENGTH_LSB: if (plip_send(nibble_timeout, dev,
&snd->nibble, snd->length.b.lsb)) return TIMEOUT;
snd->state = PLIP_PK_LENGTH_MSB;
fallthrough;
case PLIP_PK_LENGTH_MSB: if (plip_send(nibble_timeout, dev,
&snd->nibble, snd->length.b.msb)) return TIMEOUT;
snd->state = PLIP_PK_DATA;
snd->byte = 0;
snd->checksum = 0;
fallthrough;
case PLIP_PK_DATA: do { if (plip_send(nibble_timeout, dev,
&snd->nibble, lbuf[snd->byte])) return TIMEOUT;
} while (++snd->byte < snd->length.h); do {
snd->checksum += lbuf[--snd->byte];
} while (snd->byte);
snd->state = PLIP_PK_CHECKSUM;
fallthrough;
case PLIP_PK_CHECKSUM: if (plip_send(nibble_timeout, dev,
&snd->nibble, snd->checksum)) return TIMEOUT;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); if (in_dev) { /* Any address will do - we take the first */ conststruct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list); if (ifa) {
memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
memset(eth->h_dest, 0xfc, 2);
memcpy(eth->h_dest+2, &ifa->ifa_address, 4);
}
}
rcu_read_unlock();
}
ret = eth_header_cache(neigh, hh, type); if (ret == 0) { struct ethhdr *eth;
eth = (struct ethhdr*)(((u8*)hh->hh_data) +
HH_DATA_OFF(sizeof(*eth)));
plip_rewrite_address (neigh->dev, eth);
}
return ret;
}
/* Open/initialize the board. This is called (in the current kernel) sometime after booting when the 'ifconfig' program is run.
This routine gets exclusive access to the parallel port by allocating its IRQ line.
*/ staticint
plip_open(struct net_device *dev)
{ struct net_local *nl = netdev_priv(dev); struct in_device *in_dev;
/* Grab the port */ if (!nl->port_owner) { if (parport_claim(nl->pardev)) return -EAGAIN;
nl->port_owner = 1;
}
nl->should_relinquish = 0;
/* Clear the data port. */
write_data (dev, 0x00);
/* Fill in the MAC-level header. We used to abuse dev->broadcast to store the point-to-point MAC address, but we no longer do it. Instead, we fetch the interface address whenever it is needed, which is cheap enough because we use the hh_cache. Actually, abusing dev->broadcast didn't work, because when using plip_open the point-to-point address isn't yet known. PLIP doesn't have a real MAC address, but we need it to be DOS compatible, and to properly support taps (otherwise, when the device address isn't identical to the address of a
received frame, the kernel incorrectly drops it). */
in_dev=__in_dev_get_rtnl(dev); if (in_dev) { /* Any address will do - we take the first. We already have the first two bytes filled with 0xfc, from
plip_init_dev(). */ conststruct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list); if (ifa != NULL) {
dev_addr_mod(dev, 2, &ifa->ifa_local, 4);
}
}
if (nl->port_owner) { /* Why are we being woken up? */
printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name); if (!parport_claim(nl->pardev)) /* bus_owner is already set (but why?) */
printk(KERN_DEBUG "%s: I'm broken.\n", dev->name); else return;
}
if (!(dev->flags & IFF_UP)) /* Don't need the port when the interface is down */ return;
if (!parport_claim(nl->pardev)) {
nl->port_owner = 1; /* Clear the data port. */
write_data (dev, 0x00);
}
}
staticinlineint
plip_searchfor(int list[], int a)
{ int i; for (i = 0; i < PLIP_MAX && list[i] != -1; i++) { if (list[i] == a) return 1;
} return 0;
}
/* plip_attach() is called (by the parport code) when a port is
* available to use. */ staticvoid plip_attach (struct parport *port)
{ staticint unit; struct net_device *dev; struct net_local *nl; char name[IFNAMSIZ]; struct pardev_cb plip_cb;
if ((parport[0] == -1 && (!timid || !port->devices)) ||
plip_searchfor(parport, port->number)) { if (unit == PLIP_MAX) {
printk(KERN_ERR "plip: too many devices\n"); return;
}
sprintf(name, "plip%d", unit);
dev = alloc_etherdev(sizeof(struct net_local)); if (!dev) return;
strcpy(dev->name, name);
dev->irq = port->irq;
dev->base_addr = port->base; if (port->irq == -1) {
printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode," "which is fairly inefficient!\n", port->name);
}
/* plip_detach() is called (by the parport code) when a port is
* no longer available to use. */ staticvoid plip_detach (struct parport *port)
{ /* Nothing to do */
}
staticint plip_probe(struct pardevice *par_dev)
{ struct device_driver *drv = par_dev->dev.driver; int len = strlen(drv->name);
if (strncmp(par_dev->name, drv->name, len)) return -ENODEV;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.