xref: /kernel/linux/linux-6.6/drivers/net/plip/plip.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */
362306a36Sopenharmony_ci/* PLIP: A parallel port "network" driver for Linux. */
462306a36Sopenharmony_ci/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
562306a36Sopenharmony_ci/*
662306a36Sopenharmony_ci * Authors:	Donald Becker <becker@scyld.com>
762306a36Sopenharmony_ci *		Tommy Thorn <thorn@daimi.aau.dk>
862306a36Sopenharmony_ci *		Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp>
962306a36Sopenharmony_ci *		Alan Cox <gw4pts@gw4pts.ampr.org>
1062306a36Sopenharmony_ci *		Peter Bauer <100136.3530@compuserve.com>
1162306a36Sopenharmony_ci *		Niibe Yutaka <gniibe@mri.co.jp>
1262306a36Sopenharmony_ci *		Nimrod Zimerman <zimerman@mailandnews.com>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Enhancements:
1562306a36Sopenharmony_ci *		Modularization and ifreq/ifmap support by Alan Cox.
1662306a36Sopenharmony_ci *		Rewritten by Niibe Yutaka.
1762306a36Sopenharmony_ci *		parport-sharing awareness code by Philip Blundell.
1862306a36Sopenharmony_ci *		SMP locking by Niibe Yutaka.
1962306a36Sopenharmony_ci *		Support for parallel ports with no IRQ (poll mode),
2062306a36Sopenharmony_ci *		Modifications to use the parallel port API
2162306a36Sopenharmony_ci *		by Nimrod Zimerman.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Fixes:
2462306a36Sopenharmony_ci *		Niibe Yutaka
2562306a36Sopenharmony_ci *		  - Module initialization.
2662306a36Sopenharmony_ci *		  - MTU fix.
2762306a36Sopenharmony_ci *		  - Make sure other end is OK, before sending a packet.
2862306a36Sopenharmony_ci *		  - Fix immediate timer problem.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci *		Al Viro
3162306a36Sopenharmony_ci *		  - Changed {enable,disable}_irq handling to make it work
3262306a36Sopenharmony_ci *		    with new ("stack") semantics.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Original version and the name 'PLIP' from Donald Becker <becker@scyld.com>
3762306a36Sopenharmony_ci * inspired by Russ Nelson's parallel port packet driver.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * NOTE:
4062306a36Sopenharmony_ci *     Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
4162306a36Sopenharmony_ci *     Because of the necessity to communicate to DOS machines with the
4262306a36Sopenharmony_ci *     Crynwr packet driver, Peter Bauer changed the protocol again
4362306a36Sopenharmony_ci *     back to original protocol.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *     This version follows original PLIP protocol.
4662306a36Sopenharmony_ci *     So, this PLIP can't communicate the PLIP of Linux v1.0.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci *     To use with DOS box, please do (Turn on ARP switch):
5162306a36Sopenharmony_ci *	# ifconfig plip[0-2] arp
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_cistatic const char version[] = "NET3 PLIP version 2.4-parport gniibe@mri.co.jp\n";
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci  Sources:
5762306a36Sopenharmony_ci	Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
5862306a36Sopenharmony_ci	"parallel.asm" parallel port packet driver.
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci  The "Crynwr" parallel port standard specifies the following protocol:
6162306a36Sopenharmony_ci    Trigger by sending nibble '0x8' (this causes interrupt on other end)
6262306a36Sopenharmony_ci    count-low octet
6362306a36Sopenharmony_ci    count-high octet
6462306a36Sopenharmony_ci    ... data octets
6562306a36Sopenharmony_ci    checksum octet
6662306a36Sopenharmony_ci  Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
6762306a36Sopenharmony_ci			<wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci  The packet is encapsulated as if it were ethernet.
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci  The cable used is a de facto standard parallel null cable -- sold as
7262306a36Sopenharmony_ci  a "LapLink" cable by various places.  You'll need a 12-conductor cable to
7362306a36Sopenharmony_ci  make one yourself.  The wiring is:
7462306a36Sopenharmony_ci    SLCTIN	17 - 17
7562306a36Sopenharmony_ci    GROUND	25 - 25
7662306a36Sopenharmony_ci    D0->ERROR	2 - 15		15 - 2
7762306a36Sopenharmony_ci    D1->SLCT	3 - 13		13 - 3
7862306a36Sopenharmony_ci    D2->PAPOUT	4 - 12		12 - 4
7962306a36Sopenharmony_ci    D3->ACK	5 - 10		10 - 5
8062306a36Sopenharmony_ci    D4->BUSY	6 - 11		11 - 6
8162306a36Sopenharmony_ci  Do not connect the other pins.  They are
8262306a36Sopenharmony_ci    D5,D6,D7 are 7,8,9
8362306a36Sopenharmony_ci    STROBE is 1, FEED is 14, INIT is 16
8462306a36Sopenharmony_ci    extra grounds are 18,19,20,21,22,23,24
8562306a36Sopenharmony_ci*/
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#include <linux/compat.h>
8862306a36Sopenharmony_ci#include <linux/module.h>
8962306a36Sopenharmony_ci#include <linux/kernel.h>
9062306a36Sopenharmony_ci#include <linux/types.h>
9162306a36Sopenharmony_ci#include <linux/fcntl.h>
9262306a36Sopenharmony_ci#include <linux/interrupt.h>
9362306a36Sopenharmony_ci#include <linux/string.h>
9462306a36Sopenharmony_ci#include <linux/slab.h>
9562306a36Sopenharmony_ci#include <linux/if_ether.h>
9662306a36Sopenharmony_ci#include <linux/in.h>
9762306a36Sopenharmony_ci#include <linux/errno.h>
9862306a36Sopenharmony_ci#include <linux/delay.h>
9962306a36Sopenharmony_ci#include <linux/init.h>
10062306a36Sopenharmony_ci#include <linux/netdevice.h>
10162306a36Sopenharmony_ci#include <linux/etherdevice.h>
10262306a36Sopenharmony_ci#include <linux/inetdevice.h>
10362306a36Sopenharmony_ci#include <linux/skbuff.h>
10462306a36Sopenharmony_ci#include <linux/if_plip.h>
10562306a36Sopenharmony_ci#include <linux/workqueue.h>
10662306a36Sopenharmony_ci#include <linux/spinlock.h>
10762306a36Sopenharmony_ci#include <linux/completion.h>
10862306a36Sopenharmony_ci#include <linux/parport.h>
10962306a36Sopenharmony_ci#include <linux/bitops.h>
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#include <net/neighbour.h>
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#include <asm/irq.h>
11462306a36Sopenharmony_ci#include <asm/byteorder.h>
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* Maximum number of devices to support. */
11762306a36Sopenharmony_ci#define PLIP_MAX  8
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Use 0 for production, 1 for verification, >2 for debug */
12062306a36Sopenharmony_ci#ifndef NET_DEBUG
12162306a36Sopenharmony_ci#define NET_DEBUG 1
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_cistatic const unsigned int net_debug = NET_DEBUG;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#define ENABLE(irq)  if (irq != -1) enable_irq(irq)
12662306a36Sopenharmony_ci#define DISABLE(irq) if (irq != -1) disable_irq(irq)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* In micro second */
12962306a36Sopenharmony_ci#define PLIP_DELAY_UNIT		   1
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/* Connection time out = PLIP_TRIGGER_WAIT * PLIP_DELAY_UNIT usec */
13262306a36Sopenharmony_ci#define PLIP_TRIGGER_WAIT	 500
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
13562306a36Sopenharmony_ci#define PLIP_NIBBLE_WAIT        3000
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/* Bottom halves */
13862306a36Sopenharmony_cistatic void plip_kick_bh(struct work_struct *work);
13962306a36Sopenharmony_cistatic void plip_bh(struct work_struct *work);
14062306a36Sopenharmony_cistatic void plip_timer_bh(struct work_struct *work);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* Interrupt handler */
14362306a36Sopenharmony_cistatic void plip_interrupt(void *dev_id);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* Functions for DEV methods */
14662306a36Sopenharmony_cistatic netdev_tx_t plip_tx_packet(struct sk_buff *skb, struct net_device *dev);
14762306a36Sopenharmony_cistatic int plip_hard_header(struct sk_buff *skb, struct net_device *dev,
14862306a36Sopenharmony_ci                            unsigned short type, const void *daddr,
14962306a36Sopenharmony_ci			    const void *saddr, unsigned len);
15062306a36Sopenharmony_cistatic int plip_hard_header_cache(const struct neighbour *neigh,
15162306a36Sopenharmony_ci                                  struct hh_cache *hh, __be16 type);
15262306a36Sopenharmony_cistatic int plip_open(struct net_device *dev);
15362306a36Sopenharmony_cistatic int plip_close(struct net_device *dev);
15462306a36Sopenharmony_cistatic int plip_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
15562306a36Sopenharmony_ci			       void __user *data, int cmd);
15662306a36Sopenharmony_cistatic int plip_preempt(void *handle);
15762306a36Sopenharmony_cistatic void plip_wakeup(void *handle);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cienum plip_connection_state {
16062306a36Sopenharmony_ci	PLIP_CN_NONE=0,
16162306a36Sopenharmony_ci	PLIP_CN_RECEIVE,
16262306a36Sopenharmony_ci	PLIP_CN_SEND,
16362306a36Sopenharmony_ci	PLIP_CN_CLOSING,
16462306a36Sopenharmony_ci	PLIP_CN_ERROR
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cienum plip_packet_state {
16862306a36Sopenharmony_ci	PLIP_PK_DONE=0,
16962306a36Sopenharmony_ci	PLIP_PK_TRIGGER,
17062306a36Sopenharmony_ci	PLIP_PK_LENGTH_LSB,
17162306a36Sopenharmony_ci	PLIP_PK_LENGTH_MSB,
17262306a36Sopenharmony_ci	PLIP_PK_DATA,
17362306a36Sopenharmony_ci	PLIP_PK_CHECKSUM
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cienum plip_nibble_state {
17762306a36Sopenharmony_ci	PLIP_NB_BEGIN,
17862306a36Sopenharmony_ci	PLIP_NB_1,
17962306a36Sopenharmony_ci	PLIP_NB_2,
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistruct plip_local {
18362306a36Sopenharmony_ci	enum plip_packet_state state;
18462306a36Sopenharmony_ci	enum plip_nibble_state nibble;
18562306a36Sopenharmony_ci	union {
18662306a36Sopenharmony_ci		struct {
18762306a36Sopenharmony_ci#if defined(__LITTLE_ENDIAN)
18862306a36Sopenharmony_ci			unsigned char lsb;
18962306a36Sopenharmony_ci			unsigned char msb;
19062306a36Sopenharmony_ci#elif defined(__BIG_ENDIAN)
19162306a36Sopenharmony_ci			unsigned char msb;
19262306a36Sopenharmony_ci			unsigned char lsb;
19362306a36Sopenharmony_ci#else
19462306a36Sopenharmony_ci#error	"Please fix the endianness defines in <asm/byteorder.h>"
19562306a36Sopenharmony_ci#endif
19662306a36Sopenharmony_ci		} b;
19762306a36Sopenharmony_ci		unsigned short h;
19862306a36Sopenharmony_ci	} length;
19962306a36Sopenharmony_ci	unsigned short byte;
20062306a36Sopenharmony_ci	unsigned char  checksum;
20162306a36Sopenharmony_ci	unsigned char  data;
20262306a36Sopenharmony_ci	struct sk_buff *skb;
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistruct net_local {
20662306a36Sopenharmony_ci	struct net_device *dev;
20762306a36Sopenharmony_ci	struct work_struct immediate;
20862306a36Sopenharmony_ci	struct delayed_work deferred;
20962306a36Sopenharmony_ci	struct delayed_work timer;
21062306a36Sopenharmony_ci	struct plip_local snd_data;
21162306a36Sopenharmony_ci	struct plip_local rcv_data;
21262306a36Sopenharmony_ci	struct pardevice *pardev;
21362306a36Sopenharmony_ci	unsigned long  trigger;
21462306a36Sopenharmony_ci	unsigned long  nibble;
21562306a36Sopenharmony_ci	enum plip_connection_state connection;
21662306a36Sopenharmony_ci	unsigned short timeout_count;
21762306a36Sopenharmony_ci	int is_deferred;
21862306a36Sopenharmony_ci	int port_owner;
21962306a36Sopenharmony_ci	int should_relinquish;
22062306a36Sopenharmony_ci	spinlock_t lock;
22162306a36Sopenharmony_ci	atomic_t kill_timer;
22262306a36Sopenharmony_ci	struct completion killed_timer_cmp;
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic inline void enable_parport_interrupts (struct net_device *dev)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	if (dev->irq != -1)
22862306a36Sopenharmony_ci	{
22962306a36Sopenharmony_ci		struct parport *port =
23062306a36Sopenharmony_ci		   ((struct net_local *)netdev_priv(dev))->pardev->port;
23162306a36Sopenharmony_ci		port->ops->enable_irq (port);
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic inline void disable_parport_interrupts (struct net_device *dev)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	if (dev->irq != -1)
23862306a36Sopenharmony_ci	{
23962306a36Sopenharmony_ci		struct parport *port =
24062306a36Sopenharmony_ci		   ((struct net_local *)netdev_priv(dev))->pardev->port;
24162306a36Sopenharmony_ci		port->ops->disable_irq (port);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic inline void write_data (struct net_device *dev, unsigned char data)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	struct parport *port =
24862306a36Sopenharmony_ci	   ((struct net_local *)netdev_priv(dev))->pardev->port;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	port->ops->write_data (port, data);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic inline unsigned char read_status (struct net_device *dev)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	struct parport *port =
25662306a36Sopenharmony_ci	   ((struct net_local *)netdev_priv(dev))->pardev->port;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return port->ops->read_status (port);
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cistatic const struct header_ops plip_header_ops = {
26262306a36Sopenharmony_ci	.create	= plip_hard_header,
26362306a36Sopenharmony_ci	.cache  = plip_hard_header_cache,
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic const struct net_device_ops plip_netdev_ops = {
26762306a36Sopenharmony_ci	.ndo_open		 = plip_open,
26862306a36Sopenharmony_ci	.ndo_stop		 = plip_close,
26962306a36Sopenharmony_ci	.ndo_start_xmit		 = plip_tx_packet,
27062306a36Sopenharmony_ci	.ndo_siocdevprivate	 = plip_siocdevprivate,
27162306a36Sopenharmony_ci	.ndo_set_mac_address	 = eth_mac_addr,
27262306a36Sopenharmony_ci	.ndo_validate_addr	 = eth_validate_addr,
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Entry point of PLIP driver.
27662306a36Sopenharmony_ci   Probe the hardware, and register/initialize the driver.
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci   PLIP is rather weird, because of the way it interacts with the parport
27962306a36Sopenharmony_ci   system.  It is _not_ initialised from Space.c.  Instead, plip_init()
28062306a36Sopenharmony_ci   is called, and that function makes up a "struct net_device" for each port, and
28162306a36Sopenharmony_ci   then calls us here.
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci   */
28462306a36Sopenharmony_cistatic void
28562306a36Sopenharmony_ciplip_init_netdev(struct net_device *dev)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	static const u8 addr_init[ETH_ALEN] = {
28862306a36Sopenharmony_ci		0xfc, 0xfc, 0xfc,
28962306a36Sopenharmony_ci		0xfc, 0xfc, 0xfc,
29062306a36Sopenharmony_ci	};
29162306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Then, override parts of it */
29462306a36Sopenharmony_ci	dev->tx_queue_len 	 = 10;
29562306a36Sopenharmony_ci	dev->flags	         = IFF_POINTOPOINT|IFF_NOARP;
29662306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr_init);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	dev->netdev_ops		 = &plip_netdev_ops;
29962306a36Sopenharmony_ci	dev->header_ops          = &plip_header_ops;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	nl->port_owner = 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Initialize constants */
30562306a36Sopenharmony_ci	nl->trigger	= PLIP_TRIGGER_WAIT;
30662306a36Sopenharmony_ci	nl->nibble	= PLIP_NIBBLE_WAIT;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Initialize task queue structures */
30962306a36Sopenharmony_ci	INIT_WORK(&nl->immediate, plip_bh);
31062306a36Sopenharmony_ci	INIT_DELAYED_WORK(&nl->deferred, plip_kick_bh);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (dev->irq == -1)
31362306a36Sopenharmony_ci		INIT_DELAYED_WORK(&nl->timer, plip_timer_bh);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	spin_lock_init(&nl->lock);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/* Bottom half handler for the delayed request.
31962306a36Sopenharmony_ci   This routine is kicked by do_timer().
32062306a36Sopenharmony_ci   Request `plip_bh' to be invoked. */
32162306a36Sopenharmony_cistatic void
32262306a36Sopenharmony_ciplip_kick_bh(struct work_struct *work)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct net_local *nl =
32562306a36Sopenharmony_ci		container_of(work, struct net_local, deferred.work);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (nl->is_deferred)
32862306a36Sopenharmony_ci		schedule_work(&nl->immediate);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/* Forward declarations of internal routines */
33262306a36Sopenharmony_cistatic int plip_none(struct net_device *, struct net_local *,
33362306a36Sopenharmony_ci		     struct plip_local *, struct plip_local *);
33462306a36Sopenharmony_cistatic int plip_receive_packet(struct net_device *, struct net_local *,
33562306a36Sopenharmony_ci			       struct plip_local *, struct plip_local *);
33662306a36Sopenharmony_cistatic int plip_send_packet(struct net_device *, struct net_local *,
33762306a36Sopenharmony_ci			    struct plip_local *, struct plip_local *);
33862306a36Sopenharmony_cistatic int plip_connection_close(struct net_device *, struct net_local *,
33962306a36Sopenharmony_ci				 struct plip_local *, struct plip_local *);
34062306a36Sopenharmony_cistatic int plip_error(struct net_device *, struct net_local *,
34162306a36Sopenharmony_ci		      struct plip_local *, struct plip_local *);
34262306a36Sopenharmony_cistatic int plip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
34362306a36Sopenharmony_ci				 struct plip_local *snd,
34462306a36Sopenharmony_ci				 struct plip_local *rcv,
34562306a36Sopenharmony_ci				 int error);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci#define OK        0
34862306a36Sopenharmony_ci#define TIMEOUT   1
34962306a36Sopenharmony_ci#define ERROR     2
35062306a36Sopenharmony_ci#define HS_TIMEOUT	3
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_citypedef int (*plip_func)(struct net_device *dev, struct net_local *nl,
35362306a36Sopenharmony_ci			 struct plip_local *snd, struct plip_local *rcv);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic const plip_func connection_state_table[] =
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	plip_none,
35862306a36Sopenharmony_ci	plip_receive_packet,
35962306a36Sopenharmony_ci	plip_send_packet,
36062306a36Sopenharmony_ci	plip_connection_close,
36162306a36Sopenharmony_ci	plip_error
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/* Bottom half handler of PLIP. */
36562306a36Sopenharmony_cistatic void
36662306a36Sopenharmony_ciplip_bh(struct work_struct *work)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct net_local *nl = container_of(work, struct net_local, immediate);
36962306a36Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
37062306a36Sopenharmony_ci	struct plip_local *rcv = &nl->rcv_data;
37162306a36Sopenharmony_ci	plip_func f;
37262306a36Sopenharmony_ci	int r;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	nl->is_deferred = 0;
37562306a36Sopenharmony_ci	f = connection_state_table[nl->connection];
37662306a36Sopenharmony_ci	if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK &&
37762306a36Sopenharmony_ci	    (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) {
37862306a36Sopenharmony_ci		nl->is_deferred = 1;
37962306a36Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void
38462306a36Sopenharmony_ciplip_timer_bh(struct work_struct *work)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct net_local *nl =
38762306a36Sopenharmony_ci		container_of(work, struct net_local, timer.work);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (!(atomic_read (&nl->kill_timer))) {
39062306a36Sopenharmony_ci		plip_interrupt (nl->dev);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		schedule_delayed_work(&nl->timer, 1);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	else {
39562306a36Sopenharmony_ci		complete(&nl->killed_timer_cmp);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int
40062306a36Sopenharmony_ciplip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
40162306a36Sopenharmony_ci		      struct plip_local *snd, struct plip_local *rcv,
40262306a36Sopenharmony_ci		      int error)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	unsigned char c0;
40562306a36Sopenharmony_ci	/*
40662306a36Sopenharmony_ci	 * This is tricky. If we got here from the beginning of send (either
40762306a36Sopenharmony_ci	 * with ERROR or HS_TIMEOUT) we have IRQ enabled. Otherwise it's
40862306a36Sopenharmony_ci	 * already disabled. With the old variant of {enable,disable}_irq()
40962306a36Sopenharmony_ci	 * extra disable_irq() was a no-op. Now it became mortal - it's
41062306a36Sopenharmony_ci	 * unbalanced and thus we'll never re-enable IRQ (until rmmod plip,
41162306a36Sopenharmony_ci	 * that is). So we have to treat HS_TIMEOUT and ERROR from send
41262306a36Sopenharmony_ci	 * in a special way.
41362306a36Sopenharmony_ci	 */
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	spin_lock_irq(&nl->lock);
41662306a36Sopenharmony_ci	if (nl->connection == PLIP_CN_SEND) {
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		if (error != ERROR) { /* Timeout */
41962306a36Sopenharmony_ci			nl->timeout_count++;
42062306a36Sopenharmony_ci			if ((error == HS_TIMEOUT && nl->timeout_count <= 10) ||
42162306a36Sopenharmony_ci			    nl->timeout_count <= 3) {
42262306a36Sopenharmony_ci				spin_unlock_irq(&nl->lock);
42362306a36Sopenharmony_ci				/* Try again later */
42462306a36Sopenharmony_ci				return TIMEOUT;
42562306a36Sopenharmony_ci			}
42662306a36Sopenharmony_ci			c0 = read_status(dev);
42762306a36Sopenharmony_ci			printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n",
42862306a36Sopenharmony_ci			       dev->name, snd->state, c0);
42962306a36Sopenharmony_ci		} else
43062306a36Sopenharmony_ci			error = HS_TIMEOUT;
43162306a36Sopenharmony_ci		dev->stats.tx_errors++;
43262306a36Sopenharmony_ci		dev->stats.tx_aborted_errors++;
43362306a36Sopenharmony_ci	} else if (nl->connection == PLIP_CN_RECEIVE) {
43462306a36Sopenharmony_ci		if (rcv->state == PLIP_PK_TRIGGER) {
43562306a36Sopenharmony_ci			/* Transmission was interrupted. */
43662306a36Sopenharmony_ci			spin_unlock_irq(&nl->lock);
43762306a36Sopenharmony_ci			return OK;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci		if (error != ERROR) { /* Timeout */
44062306a36Sopenharmony_ci			if (++nl->timeout_count <= 3) {
44162306a36Sopenharmony_ci				spin_unlock_irq(&nl->lock);
44262306a36Sopenharmony_ci				/* Try again later */
44362306a36Sopenharmony_ci				return TIMEOUT;
44462306a36Sopenharmony_ci			}
44562306a36Sopenharmony_ci			c0 = read_status(dev);
44662306a36Sopenharmony_ci			printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n",
44762306a36Sopenharmony_ci			       dev->name, rcv->state, c0);
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci		dev->stats.rx_dropped++;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci	rcv->state = PLIP_PK_DONE;
45262306a36Sopenharmony_ci	if (rcv->skb) {
45362306a36Sopenharmony_ci		dev_kfree_skb_irq(rcv->skb);
45462306a36Sopenharmony_ci		rcv->skb = NULL;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	snd->state = PLIP_PK_DONE;
45762306a36Sopenharmony_ci	if (snd->skb) {
45862306a36Sopenharmony_ci		dev_consume_skb_irq(snd->skb);
45962306a36Sopenharmony_ci		snd->skb = NULL;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	spin_unlock_irq(&nl->lock);
46262306a36Sopenharmony_ci	if (error == HS_TIMEOUT) {
46362306a36Sopenharmony_ci		DISABLE(dev->irq);
46462306a36Sopenharmony_ci		synchronize_irq(dev->irq);
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci	disable_parport_interrupts (dev);
46762306a36Sopenharmony_ci	netif_stop_queue (dev);
46862306a36Sopenharmony_ci	nl->connection = PLIP_CN_ERROR;
46962306a36Sopenharmony_ci	write_data (dev, 0x00);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return TIMEOUT;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int
47562306a36Sopenharmony_ciplip_none(struct net_device *dev, struct net_local *nl,
47662306a36Sopenharmony_ci	  struct plip_local *snd, struct plip_local *rcv)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	return OK;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/* PLIP_RECEIVE --- receive a byte(two nibbles)
48262306a36Sopenharmony_ci   Returns OK on success, TIMEOUT on timeout */
48362306a36Sopenharmony_cistatic inline int
48462306a36Sopenharmony_ciplip_receive(unsigned short nibble_timeout, struct net_device *dev,
48562306a36Sopenharmony_ci	     enum plip_nibble_state *ns_p, unsigned char *data_p)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	unsigned char c0, c1;
48862306a36Sopenharmony_ci	unsigned int cx;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	switch (*ns_p) {
49162306a36Sopenharmony_ci	case PLIP_NB_BEGIN:
49262306a36Sopenharmony_ci		cx = nibble_timeout;
49362306a36Sopenharmony_ci		while (1) {
49462306a36Sopenharmony_ci			c0 = read_status(dev);
49562306a36Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
49662306a36Sopenharmony_ci			if ((c0 & 0x80) == 0) {
49762306a36Sopenharmony_ci				c1 = read_status(dev);
49862306a36Sopenharmony_ci				if (c0 == c1)
49962306a36Sopenharmony_ci					break;
50062306a36Sopenharmony_ci			}
50162306a36Sopenharmony_ci			if (--cx == 0)
50262306a36Sopenharmony_ci				return TIMEOUT;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci		*data_p = (c0 >> 3) & 0x0f;
50562306a36Sopenharmony_ci		write_data (dev, 0x10); /* send ACK */
50662306a36Sopenharmony_ci		*ns_p = PLIP_NB_1;
50762306a36Sopenharmony_ci		fallthrough;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	case PLIP_NB_1:
51062306a36Sopenharmony_ci		cx = nibble_timeout;
51162306a36Sopenharmony_ci		while (1) {
51262306a36Sopenharmony_ci			c0 = read_status(dev);
51362306a36Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
51462306a36Sopenharmony_ci			if (c0 & 0x80) {
51562306a36Sopenharmony_ci				c1 = read_status(dev);
51662306a36Sopenharmony_ci				if (c0 == c1)
51762306a36Sopenharmony_ci					break;
51862306a36Sopenharmony_ci			}
51962306a36Sopenharmony_ci			if (--cx == 0)
52062306a36Sopenharmony_ci				return TIMEOUT;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci		*data_p |= (c0 << 1) & 0xf0;
52362306a36Sopenharmony_ci		write_data (dev, 0x00); /* send ACK */
52462306a36Sopenharmony_ci		*ns_p = PLIP_NB_BEGIN;
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	case PLIP_NB_2:
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci	return OK;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/*
53362306a36Sopenharmony_ci *	Determine the packet's protocol ID. The rule here is that we
53462306a36Sopenharmony_ci *	assume 802.3 if the type field is short enough to be a length.
53562306a36Sopenharmony_ci *	This is normal practice and works for any 'now in use' protocol.
53662306a36Sopenharmony_ci *
53762306a36Sopenharmony_ci *	PLIP is ethernet ish but the daddr might not be valid if unicast.
53862306a36Sopenharmony_ci *	PLIP fortunately has no bus architecture (its Point-to-point).
53962306a36Sopenharmony_ci *
54062306a36Sopenharmony_ci *	We can't fix the daddr thing as that quirk (more bug) is embedded
54162306a36Sopenharmony_ci *	in far too many old systems not all even running Linux.
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic __be16 plip_type_trans(struct sk_buff *skb, struct net_device *dev)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct ethhdr *eth;
54762306a36Sopenharmony_ci	unsigned char *rawp;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	skb_reset_mac_header(skb);
55062306a36Sopenharmony_ci	skb_pull(skb,dev->hard_header_len);
55162306a36Sopenharmony_ci	eth = eth_hdr(skb);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if(is_multicast_ether_addr(eth->h_dest))
55462306a36Sopenharmony_ci	{
55562306a36Sopenharmony_ci		if(ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
55662306a36Sopenharmony_ci			skb->pkt_type=PACKET_BROADCAST;
55762306a36Sopenharmony_ci		else
55862306a36Sopenharmony_ci			skb->pkt_type=PACKET_MULTICAST;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/*
56262306a36Sopenharmony_ci	 *	This ALLMULTI check should be redundant by 1.4
56362306a36Sopenharmony_ci	 *	so don't forget to remove it.
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
56762306a36Sopenharmony_ci		return eth->h_proto;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	rawp = skb->data;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/*
57262306a36Sopenharmony_ci	 *	This is a magic hack to spot IPX packets. Older Novell breaks
57362306a36Sopenharmony_ci	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
57462306a36Sopenharmony_ci	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
57562306a36Sopenharmony_ci	 *	won't work for fault tolerant netware but does for the rest.
57662306a36Sopenharmony_ci	 */
57762306a36Sopenharmony_ci	if (*(unsigned short *)rawp == 0xFFFF)
57862306a36Sopenharmony_ci		return htons(ETH_P_802_3);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 *	Real 802.2 LLC
58262306a36Sopenharmony_ci	 */
58362306a36Sopenharmony_ci	return htons(ETH_P_802_2);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci/* PLIP_RECEIVE_PACKET --- receive a packet */
58762306a36Sopenharmony_cistatic int
58862306a36Sopenharmony_ciplip_receive_packet(struct net_device *dev, struct net_local *nl,
58962306a36Sopenharmony_ci		    struct plip_local *snd, struct plip_local *rcv)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	unsigned short nibble_timeout = nl->nibble;
59262306a36Sopenharmony_ci	unsigned char *lbuf;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	switch (rcv->state) {
59562306a36Sopenharmony_ci	case PLIP_PK_TRIGGER:
59662306a36Sopenharmony_ci		DISABLE(dev->irq);
59762306a36Sopenharmony_ci		/* Don't need to synchronize irq, as we can safely ignore it */
59862306a36Sopenharmony_ci		disable_parport_interrupts (dev);
59962306a36Sopenharmony_ci		write_data (dev, 0x01); /* send ACK */
60062306a36Sopenharmony_ci		if (net_debug > 2)
60162306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: receive start\n", dev->name);
60262306a36Sopenharmony_ci		rcv->state = PLIP_PK_LENGTH_LSB;
60362306a36Sopenharmony_ci		rcv->nibble = PLIP_NB_BEGIN;
60462306a36Sopenharmony_ci		fallthrough;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	case PLIP_PK_LENGTH_LSB:
60762306a36Sopenharmony_ci		if (snd->state != PLIP_PK_DONE) {
60862306a36Sopenharmony_ci			if (plip_receive(nl->trigger, dev,
60962306a36Sopenharmony_ci					 &rcv->nibble, &rcv->length.b.lsb)) {
61062306a36Sopenharmony_ci				/* collision, here dev->tbusy == 1 */
61162306a36Sopenharmony_ci				rcv->state = PLIP_PK_DONE;
61262306a36Sopenharmony_ci				nl->is_deferred = 1;
61362306a36Sopenharmony_ci				nl->connection = PLIP_CN_SEND;
61462306a36Sopenharmony_ci				schedule_delayed_work(&nl->deferred, 1);
61562306a36Sopenharmony_ci				enable_parport_interrupts (dev);
61662306a36Sopenharmony_ci				ENABLE(dev->irq);
61762306a36Sopenharmony_ci				return OK;
61862306a36Sopenharmony_ci			}
61962306a36Sopenharmony_ci		} else {
62062306a36Sopenharmony_ci			if (plip_receive(nibble_timeout, dev,
62162306a36Sopenharmony_ci					 &rcv->nibble, &rcv->length.b.lsb))
62262306a36Sopenharmony_ci				return TIMEOUT;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci		rcv->state = PLIP_PK_LENGTH_MSB;
62562306a36Sopenharmony_ci		fallthrough;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	case PLIP_PK_LENGTH_MSB:
62862306a36Sopenharmony_ci		if (plip_receive(nibble_timeout, dev,
62962306a36Sopenharmony_ci				 &rcv->nibble, &rcv->length.b.msb))
63062306a36Sopenharmony_ci			return TIMEOUT;
63162306a36Sopenharmony_ci		if (rcv->length.h > dev->mtu + dev->hard_header_len ||
63262306a36Sopenharmony_ci		    rcv->length.h < 8) {
63362306a36Sopenharmony_ci			printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h);
63462306a36Sopenharmony_ci			return ERROR;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		/* Malloc up new buffer. */
63762306a36Sopenharmony_ci		rcv->skb = dev_alloc_skb(rcv->length.h + 2);
63862306a36Sopenharmony_ci		if (rcv->skb == NULL) {
63962306a36Sopenharmony_ci			printk(KERN_ERR "%s: Memory squeeze.\n", dev->name);
64062306a36Sopenharmony_ci			return ERROR;
64162306a36Sopenharmony_ci		}
64262306a36Sopenharmony_ci		skb_reserve(rcv->skb, 2);	/* Align IP on 16 byte boundaries */
64362306a36Sopenharmony_ci		skb_put(rcv->skb,rcv->length.h);
64462306a36Sopenharmony_ci		rcv->skb->dev = dev;
64562306a36Sopenharmony_ci		rcv->state = PLIP_PK_DATA;
64662306a36Sopenharmony_ci		rcv->byte = 0;
64762306a36Sopenharmony_ci		rcv->checksum = 0;
64862306a36Sopenharmony_ci		fallthrough;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	case PLIP_PK_DATA:
65162306a36Sopenharmony_ci		lbuf = rcv->skb->data;
65262306a36Sopenharmony_ci		do {
65362306a36Sopenharmony_ci			if (plip_receive(nibble_timeout, dev,
65462306a36Sopenharmony_ci					 &rcv->nibble, &lbuf[rcv->byte]))
65562306a36Sopenharmony_ci				return TIMEOUT;
65662306a36Sopenharmony_ci		} while (++rcv->byte < rcv->length.h);
65762306a36Sopenharmony_ci		do {
65862306a36Sopenharmony_ci			rcv->checksum += lbuf[--rcv->byte];
65962306a36Sopenharmony_ci		} while (rcv->byte);
66062306a36Sopenharmony_ci		rcv->state = PLIP_PK_CHECKSUM;
66162306a36Sopenharmony_ci		fallthrough;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	case PLIP_PK_CHECKSUM:
66462306a36Sopenharmony_ci		if (plip_receive(nibble_timeout, dev,
66562306a36Sopenharmony_ci				 &rcv->nibble, &rcv->data))
66662306a36Sopenharmony_ci			return TIMEOUT;
66762306a36Sopenharmony_ci		if (rcv->data != rcv->checksum) {
66862306a36Sopenharmony_ci			dev->stats.rx_crc_errors++;
66962306a36Sopenharmony_ci			if (net_debug)
67062306a36Sopenharmony_ci				printk(KERN_DEBUG "%s: checksum error\n", dev->name);
67162306a36Sopenharmony_ci			return ERROR;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci		rcv->state = PLIP_PK_DONE;
67462306a36Sopenharmony_ci		fallthrough;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	case PLIP_PK_DONE:
67762306a36Sopenharmony_ci		/* Inform the upper layer for the arrival of a packet. */
67862306a36Sopenharmony_ci		rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
67962306a36Sopenharmony_ci		netif_rx(rcv->skb);
68062306a36Sopenharmony_ci		dev->stats.rx_bytes += rcv->length.h;
68162306a36Sopenharmony_ci		dev->stats.rx_packets++;
68262306a36Sopenharmony_ci		rcv->skb = NULL;
68362306a36Sopenharmony_ci		if (net_debug > 2)
68462306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: receive end\n", dev->name);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		/* Close the connection. */
68762306a36Sopenharmony_ci		write_data (dev, 0x00);
68862306a36Sopenharmony_ci		spin_lock_irq(&nl->lock);
68962306a36Sopenharmony_ci		if (snd->state != PLIP_PK_DONE) {
69062306a36Sopenharmony_ci			nl->connection = PLIP_CN_SEND;
69162306a36Sopenharmony_ci			spin_unlock_irq(&nl->lock);
69262306a36Sopenharmony_ci			schedule_work(&nl->immediate);
69362306a36Sopenharmony_ci			enable_parport_interrupts (dev);
69462306a36Sopenharmony_ci			ENABLE(dev->irq);
69562306a36Sopenharmony_ci			return OK;
69662306a36Sopenharmony_ci		} else {
69762306a36Sopenharmony_ci			nl->connection = PLIP_CN_NONE;
69862306a36Sopenharmony_ci			spin_unlock_irq(&nl->lock);
69962306a36Sopenharmony_ci			enable_parport_interrupts (dev);
70062306a36Sopenharmony_ci			ENABLE(dev->irq);
70162306a36Sopenharmony_ci			return OK;
70262306a36Sopenharmony_ci		}
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	return OK;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/* PLIP_SEND --- send a byte (two nibbles)
70862306a36Sopenharmony_ci   Returns OK on success, TIMEOUT when timeout    */
70962306a36Sopenharmony_cistatic inline int
71062306a36Sopenharmony_ciplip_send(unsigned short nibble_timeout, struct net_device *dev,
71162306a36Sopenharmony_ci	  enum plip_nibble_state *ns_p, unsigned char data)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	unsigned char c0;
71462306a36Sopenharmony_ci	unsigned int cx;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	switch (*ns_p) {
71762306a36Sopenharmony_ci	case PLIP_NB_BEGIN:
71862306a36Sopenharmony_ci		write_data (dev, data & 0x0f);
71962306a36Sopenharmony_ci		*ns_p = PLIP_NB_1;
72062306a36Sopenharmony_ci		fallthrough;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	case PLIP_NB_1:
72362306a36Sopenharmony_ci		write_data (dev, 0x10 | (data & 0x0f));
72462306a36Sopenharmony_ci		cx = nibble_timeout;
72562306a36Sopenharmony_ci		while (1) {
72662306a36Sopenharmony_ci			c0 = read_status(dev);
72762306a36Sopenharmony_ci			if ((c0 & 0x80) == 0)
72862306a36Sopenharmony_ci				break;
72962306a36Sopenharmony_ci			if (--cx == 0)
73062306a36Sopenharmony_ci				return TIMEOUT;
73162306a36Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci		write_data (dev, 0x10 | (data >> 4));
73462306a36Sopenharmony_ci		*ns_p = PLIP_NB_2;
73562306a36Sopenharmony_ci		fallthrough;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	case PLIP_NB_2:
73862306a36Sopenharmony_ci		write_data (dev, (data >> 4));
73962306a36Sopenharmony_ci		cx = nibble_timeout;
74062306a36Sopenharmony_ci		while (1) {
74162306a36Sopenharmony_ci			c0 = read_status(dev);
74262306a36Sopenharmony_ci			if (c0 & 0x80)
74362306a36Sopenharmony_ci				break;
74462306a36Sopenharmony_ci			if (--cx == 0)
74562306a36Sopenharmony_ci				return TIMEOUT;
74662306a36Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		*ns_p = PLIP_NB_BEGIN;
74962306a36Sopenharmony_ci		return OK;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci	return OK;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci/* PLIP_SEND_PACKET --- send a packet */
75562306a36Sopenharmony_cistatic int
75662306a36Sopenharmony_ciplip_send_packet(struct net_device *dev, struct net_local *nl,
75762306a36Sopenharmony_ci		 struct plip_local *snd, struct plip_local *rcv)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	unsigned short nibble_timeout = nl->nibble;
76062306a36Sopenharmony_ci	unsigned char *lbuf;
76162306a36Sopenharmony_ci	unsigned char c0;
76262306a36Sopenharmony_ci	unsigned int cx;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
76562306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: send skb lost\n", dev->name);
76662306a36Sopenharmony_ci		snd->state = PLIP_PK_DONE;
76762306a36Sopenharmony_ci		snd->skb = NULL;
76862306a36Sopenharmony_ci		return ERROR;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	switch (snd->state) {
77262306a36Sopenharmony_ci	case PLIP_PK_TRIGGER:
77362306a36Sopenharmony_ci		if ((read_status(dev) & 0xf8) != 0x80)
77462306a36Sopenharmony_ci			return HS_TIMEOUT;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		/* Trigger remote rx interrupt. */
77762306a36Sopenharmony_ci		write_data (dev, 0x08);
77862306a36Sopenharmony_ci		cx = nl->trigger;
77962306a36Sopenharmony_ci		while (1) {
78062306a36Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
78162306a36Sopenharmony_ci			spin_lock_irq(&nl->lock);
78262306a36Sopenharmony_ci			if (nl->connection == PLIP_CN_RECEIVE) {
78362306a36Sopenharmony_ci				spin_unlock_irq(&nl->lock);
78462306a36Sopenharmony_ci				/* Interrupted. */
78562306a36Sopenharmony_ci				dev->stats.collisions++;
78662306a36Sopenharmony_ci				return OK;
78762306a36Sopenharmony_ci			}
78862306a36Sopenharmony_ci			c0 = read_status(dev);
78962306a36Sopenharmony_ci			if (c0 & 0x08) {
79062306a36Sopenharmony_ci				spin_unlock_irq(&nl->lock);
79162306a36Sopenharmony_ci				DISABLE(dev->irq);
79262306a36Sopenharmony_ci				synchronize_irq(dev->irq);
79362306a36Sopenharmony_ci				if (nl->connection == PLIP_CN_RECEIVE) {
79462306a36Sopenharmony_ci					/* Interrupted.
79562306a36Sopenharmony_ci					   We don't need to enable irq,
79662306a36Sopenharmony_ci					   as it is soon disabled.    */
79762306a36Sopenharmony_ci					/* Yes, we do. New variant of
79862306a36Sopenharmony_ci					   {enable,disable}_irq *counts*
79962306a36Sopenharmony_ci					   them.  -- AV  */
80062306a36Sopenharmony_ci					ENABLE(dev->irq);
80162306a36Sopenharmony_ci					dev->stats.collisions++;
80262306a36Sopenharmony_ci					return OK;
80362306a36Sopenharmony_ci				}
80462306a36Sopenharmony_ci				disable_parport_interrupts (dev);
80562306a36Sopenharmony_ci				if (net_debug > 2)
80662306a36Sopenharmony_ci					printk(KERN_DEBUG "%s: send start\n", dev->name);
80762306a36Sopenharmony_ci				snd->state = PLIP_PK_LENGTH_LSB;
80862306a36Sopenharmony_ci				snd->nibble = PLIP_NB_BEGIN;
80962306a36Sopenharmony_ci				nl->timeout_count = 0;
81062306a36Sopenharmony_ci				break;
81162306a36Sopenharmony_ci			}
81262306a36Sopenharmony_ci			spin_unlock_irq(&nl->lock);
81362306a36Sopenharmony_ci			if (--cx == 0) {
81462306a36Sopenharmony_ci				write_data (dev, 0x00);
81562306a36Sopenharmony_ci				return HS_TIMEOUT;
81662306a36Sopenharmony_ci			}
81762306a36Sopenharmony_ci		}
81862306a36Sopenharmony_ci		break;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	case PLIP_PK_LENGTH_LSB:
82162306a36Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
82262306a36Sopenharmony_ci			      &snd->nibble, snd->length.b.lsb))
82362306a36Sopenharmony_ci			return TIMEOUT;
82462306a36Sopenharmony_ci		snd->state = PLIP_PK_LENGTH_MSB;
82562306a36Sopenharmony_ci		fallthrough;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	case PLIP_PK_LENGTH_MSB:
82862306a36Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
82962306a36Sopenharmony_ci			      &snd->nibble, snd->length.b.msb))
83062306a36Sopenharmony_ci			return TIMEOUT;
83162306a36Sopenharmony_ci		snd->state = PLIP_PK_DATA;
83262306a36Sopenharmony_ci		snd->byte = 0;
83362306a36Sopenharmony_ci		snd->checksum = 0;
83462306a36Sopenharmony_ci		fallthrough;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	case PLIP_PK_DATA:
83762306a36Sopenharmony_ci		do {
83862306a36Sopenharmony_ci			if (plip_send(nibble_timeout, dev,
83962306a36Sopenharmony_ci				      &snd->nibble, lbuf[snd->byte]))
84062306a36Sopenharmony_ci				return TIMEOUT;
84162306a36Sopenharmony_ci		} while (++snd->byte < snd->length.h);
84262306a36Sopenharmony_ci		do {
84362306a36Sopenharmony_ci			snd->checksum += lbuf[--snd->byte];
84462306a36Sopenharmony_ci		} while (snd->byte);
84562306a36Sopenharmony_ci		snd->state = PLIP_PK_CHECKSUM;
84662306a36Sopenharmony_ci		fallthrough;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	case PLIP_PK_CHECKSUM:
84962306a36Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
85062306a36Sopenharmony_ci			      &snd->nibble, snd->checksum))
85162306a36Sopenharmony_ci			return TIMEOUT;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		dev->stats.tx_bytes += snd->skb->len;
85462306a36Sopenharmony_ci		dev_kfree_skb(snd->skb);
85562306a36Sopenharmony_ci		dev->stats.tx_packets++;
85662306a36Sopenharmony_ci		snd->state = PLIP_PK_DONE;
85762306a36Sopenharmony_ci		fallthrough;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	case PLIP_PK_DONE:
86062306a36Sopenharmony_ci		/* Close the connection */
86162306a36Sopenharmony_ci		write_data (dev, 0x00);
86262306a36Sopenharmony_ci		snd->skb = NULL;
86362306a36Sopenharmony_ci		if (net_debug > 2)
86462306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: send end\n", dev->name);
86562306a36Sopenharmony_ci		nl->connection = PLIP_CN_CLOSING;
86662306a36Sopenharmony_ci		nl->is_deferred = 1;
86762306a36Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
86862306a36Sopenharmony_ci		enable_parport_interrupts (dev);
86962306a36Sopenharmony_ci		ENABLE(dev->irq);
87062306a36Sopenharmony_ci		return OK;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci	return OK;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic int
87662306a36Sopenharmony_ciplip_connection_close(struct net_device *dev, struct net_local *nl,
87762306a36Sopenharmony_ci		      struct plip_local *snd, struct plip_local *rcv)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	spin_lock_irq(&nl->lock);
88062306a36Sopenharmony_ci	if (nl->connection == PLIP_CN_CLOSING) {
88162306a36Sopenharmony_ci		nl->connection = PLIP_CN_NONE;
88262306a36Sopenharmony_ci		netif_wake_queue (dev);
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci	spin_unlock_irq(&nl->lock);
88562306a36Sopenharmony_ci	if (nl->should_relinquish) {
88662306a36Sopenharmony_ci		nl->should_relinquish = nl->port_owner = 0;
88762306a36Sopenharmony_ci		parport_release(nl->pardev);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	return OK;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci/* PLIP_ERROR --- wait till other end settled */
89362306a36Sopenharmony_cistatic int
89462306a36Sopenharmony_ciplip_error(struct net_device *dev, struct net_local *nl,
89562306a36Sopenharmony_ci	   struct plip_local *snd, struct plip_local *rcv)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	unsigned char status;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	status = read_status(dev);
90062306a36Sopenharmony_ci	if ((status & 0xf8) == 0x80) {
90162306a36Sopenharmony_ci		if (net_debug > 2)
90262306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: reset interface.\n", dev->name);
90362306a36Sopenharmony_ci		nl->connection = PLIP_CN_NONE;
90462306a36Sopenharmony_ci		nl->should_relinquish = 0;
90562306a36Sopenharmony_ci		netif_start_queue (dev);
90662306a36Sopenharmony_ci		enable_parport_interrupts (dev);
90762306a36Sopenharmony_ci		ENABLE(dev->irq);
90862306a36Sopenharmony_ci		netif_wake_queue (dev);
90962306a36Sopenharmony_ci	} else {
91062306a36Sopenharmony_ci		nl->is_deferred = 1;
91162306a36Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	return OK;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci/* Handle the parallel port interrupts. */
91862306a36Sopenharmony_cistatic void
91962306a36Sopenharmony_ciplip_interrupt(void *dev_id)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct net_device *dev = dev_id;
92262306a36Sopenharmony_ci	struct net_local *nl;
92362306a36Sopenharmony_ci	struct plip_local *rcv;
92462306a36Sopenharmony_ci	unsigned char c0;
92562306a36Sopenharmony_ci	unsigned long flags;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	nl = netdev_priv(dev);
92862306a36Sopenharmony_ci	rcv = &nl->rcv_data;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	spin_lock_irqsave (&nl->lock, flags);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	c0 = read_status(dev);
93362306a36Sopenharmony_ci	if ((c0 & 0xf8) != 0xc0) {
93462306a36Sopenharmony_ci		if ((dev->irq != -1) && (net_debug > 1))
93562306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: spurious interrupt\n", dev->name);
93662306a36Sopenharmony_ci		spin_unlock_irqrestore (&nl->lock, flags);
93762306a36Sopenharmony_ci		return;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (net_debug > 3)
94162306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: interrupt.\n", dev->name);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	switch (nl->connection) {
94462306a36Sopenharmony_ci	case PLIP_CN_CLOSING:
94562306a36Sopenharmony_ci		netif_wake_queue (dev);
94662306a36Sopenharmony_ci		fallthrough;
94762306a36Sopenharmony_ci	case PLIP_CN_NONE:
94862306a36Sopenharmony_ci	case PLIP_CN_SEND:
94962306a36Sopenharmony_ci		rcv->state = PLIP_PK_TRIGGER;
95062306a36Sopenharmony_ci		nl->connection = PLIP_CN_RECEIVE;
95162306a36Sopenharmony_ci		nl->timeout_count = 0;
95262306a36Sopenharmony_ci		schedule_work(&nl->immediate);
95362306a36Sopenharmony_ci		break;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	case PLIP_CN_RECEIVE:
95662306a36Sopenharmony_ci		/* May occur because there is race condition
95762306a36Sopenharmony_ci		   around test and set of dev->interrupt.
95862306a36Sopenharmony_ci		   Ignore this interrupt. */
95962306a36Sopenharmony_ci		break;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	case PLIP_CN_ERROR:
96262306a36Sopenharmony_ci		printk(KERN_ERR "%s: receive interrupt in error state\n", dev->name);
96362306a36Sopenharmony_ci		break;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	spin_unlock_irqrestore(&nl->lock, flags);
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic netdev_tx_t
97062306a36Sopenharmony_ciplip_tx_packet(struct sk_buff *skb, struct net_device *dev)
97162306a36Sopenharmony_ci{
97262306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
97362306a36Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (netif_queue_stopped(dev))
97662306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* We may need to grab the bus */
97962306a36Sopenharmony_ci	if (!nl->port_owner) {
98062306a36Sopenharmony_ci		if (parport_claim(nl->pardev))
98162306a36Sopenharmony_ci			return NETDEV_TX_BUSY;
98262306a36Sopenharmony_ci		nl->port_owner = 1;
98362306a36Sopenharmony_ci	}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	netif_stop_queue (dev);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
98862306a36Sopenharmony_ci		printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len);
98962306a36Sopenharmony_ci		netif_start_queue (dev);
99062306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	if (net_debug > 2)
99462306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: send request\n", dev->name);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	spin_lock_irq(&nl->lock);
99762306a36Sopenharmony_ci	snd->skb = skb;
99862306a36Sopenharmony_ci	snd->length.h = skb->len;
99962306a36Sopenharmony_ci	snd->state = PLIP_PK_TRIGGER;
100062306a36Sopenharmony_ci	if (nl->connection == PLIP_CN_NONE) {
100162306a36Sopenharmony_ci		nl->connection = PLIP_CN_SEND;
100262306a36Sopenharmony_ci		nl->timeout_count = 0;
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci	schedule_work(&nl->immediate);
100562306a36Sopenharmony_ci	spin_unlock_irq(&nl->lock);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	return NETDEV_TX_OK;
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic void
101162306a36Sopenharmony_ciplip_rewrite_address(const struct net_device *dev, struct ethhdr *eth)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	const struct in_device *in_dev;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	rcu_read_lock();
101662306a36Sopenharmony_ci	in_dev = __in_dev_get_rcu(dev);
101762306a36Sopenharmony_ci	if (in_dev) {
101862306a36Sopenharmony_ci		/* Any address will do - we take the first */
101962306a36Sopenharmony_ci		const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list);
102062306a36Sopenharmony_ci		if (ifa) {
102162306a36Sopenharmony_ci			memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
102262306a36Sopenharmony_ci			memset(eth->h_dest, 0xfc, 2);
102362306a36Sopenharmony_ci			memcpy(eth->h_dest+2, &ifa->ifa_address, 4);
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci	rcu_read_unlock();
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_cistatic int
103062306a36Sopenharmony_ciplip_hard_header(struct sk_buff *skb, struct net_device *dev,
103162306a36Sopenharmony_ci		 unsigned short type, const void *daddr,
103262306a36Sopenharmony_ci		 const void *saddr, unsigned len)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	int ret;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	ret = eth_header(skb, dev, type, daddr, saddr, len);
103762306a36Sopenharmony_ci	if (ret >= 0)
103862306a36Sopenharmony_ci		plip_rewrite_address (dev, (struct ethhdr *)skb->data);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	return ret;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic int plip_hard_header_cache(const struct neighbour *neigh,
104462306a36Sopenharmony_ci				  struct hh_cache *hh, __be16 type)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	int ret;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	ret = eth_header_cache(neigh, hh, type);
104962306a36Sopenharmony_ci	if (ret == 0) {
105062306a36Sopenharmony_ci		struct ethhdr *eth;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		eth = (struct ethhdr*)(((u8*)hh->hh_data) +
105362306a36Sopenharmony_ci				       HH_DATA_OFF(sizeof(*eth)));
105462306a36Sopenharmony_ci		plip_rewrite_address (neigh->dev, eth);
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	return ret;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci/* Open/initialize the board.  This is called (in the current kernel)
106162306a36Sopenharmony_ci   sometime after booting when the 'ifconfig' program is run.
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci   This routine gets exclusive access to the parallel port by allocating
106462306a36Sopenharmony_ci   its IRQ line.
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_cistatic int
106762306a36Sopenharmony_ciplip_open(struct net_device *dev)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
107062306a36Sopenharmony_ci	struct in_device *in_dev;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* Grab the port */
107362306a36Sopenharmony_ci	if (!nl->port_owner) {
107462306a36Sopenharmony_ci		if (parport_claim(nl->pardev)) return -EAGAIN;
107562306a36Sopenharmony_ci		nl->port_owner = 1;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	nl->should_relinquish = 0;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* Clear the data port. */
108162306a36Sopenharmony_ci	write_data (dev, 0x00);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* Enable rx interrupt. */
108462306a36Sopenharmony_ci	enable_parport_interrupts (dev);
108562306a36Sopenharmony_ci	if (dev->irq == -1)
108662306a36Sopenharmony_ci	{
108762306a36Sopenharmony_ci		atomic_set (&nl->kill_timer, 0);
108862306a36Sopenharmony_ci		schedule_delayed_work(&nl->timer, 1);
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* Initialize the state machine. */
109262306a36Sopenharmony_ci	nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
109362306a36Sopenharmony_ci	nl->rcv_data.skb = nl->snd_data.skb = NULL;
109462306a36Sopenharmony_ci	nl->connection = PLIP_CN_NONE;
109562306a36Sopenharmony_ci	nl->is_deferred = 0;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* Fill in the MAC-level header.
109862306a36Sopenharmony_ci	   We used to abuse dev->broadcast to store the point-to-point
109962306a36Sopenharmony_ci	   MAC address, but we no longer do it. Instead, we fetch the
110062306a36Sopenharmony_ci	   interface address whenever it is needed, which is cheap enough
110162306a36Sopenharmony_ci	   because we use the hh_cache. Actually, abusing dev->broadcast
110262306a36Sopenharmony_ci	   didn't work, because when using plip_open the point-to-point
110362306a36Sopenharmony_ci	   address isn't yet known.
110462306a36Sopenharmony_ci	   PLIP doesn't have a real MAC address, but we need it to be
110562306a36Sopenharmony_ci	   DOS compatible, and to properly support taps (otherwise,
110662306a36Sopenharmony_ci	   when the device address isn't identical to the address of a
110762306a36Sopenharmony_ci	   received frame, the kernel incorrectly drops it).             */
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	in_dev=__in_dev_get_rtnl(dev);
111062306a36Sopenharmony_ci	if (in_dev) {
111162306a36Sopenharmony_ci		/* Any address will do - we take the first. We already
111262306a36Sopenharmony_ci		   have the first two bytes filled with 0xfc, from
111362306a36Sopenharmony_ci		   plip_init_dev(). */
111462306a36Sopenharmony_ci		const struct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list);
111562306a36Sopenharmony_ci		if (ifa != NULL) {
111662306a36Sopenharmony_ci			dev_addr_mod(dev, 2, &ifa->ifa_local, 4);
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	netif_start_queue (dev);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	return 0;
112362306a36Sopenharmony_ci}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci/* The inverse routine to plip_open (). */
112662306a36Sopenharmony_cistatic int
112762306a36Sopenharmony_ciplip_close(struct net_device *dev)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
113062306a36Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
113162306a36Sopenharmony_ci	struct plip_local *rcv = &nl->rcv_data;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	netif_stop_queue (dev);
113462306a36Sopenharmony_ci	DISABLE(dev->irq);
113562306a36Sopenharmony_ci	synchronize_irq(dev->irq);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (dev->irq == -1)
113862306a36Sopenharmony_ci	{
113962306a36Sopenharmony_ci		init_completion(&nl->killed_timer_cmp);
114062306a36Sopenharmony_ci		atomic_set (&nl->kill_timer, 1);
114162306a36Sopenharmony_ci		wait_for_completion(&nl->killed_timer_cmp);
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci#ifdef NOTDEF
114562306a36Sopenharmony_ci	outb(0x00, PAR_DATA(dev));
114662306a36Sopenharmony_ci#endif
114762306a36Sopenharmony_ci	nl->is_deferred = 0;
114862306a36Sopenharmony_ci	nl->connection = PLIP_CN_NONE;
114962306a36Sopenharmony_ci	if (nl->port_owner) {
115062306a36Sopenharmony_ci		parport_release(nl->pardev);
115162306a36Sopenharmony_ci		nl->port_owner = 0;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	snd->state = PLIP_PK_DONE;
115562306a36Sopenharmony_ci	if (snd->skb) {
115662306a36Sopenharmony_ci		dev_kfree_skb(snd->skb);
115762306a36Sopenharmony_ci		snd->skb = NULL;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci	rcv->state = PLIP_PK_DONE;
116062306a36Sopenharmony_ci	if (rcv->skb) {
116162306a36Sopenharmony_ci		kfree_skb(rcv->skb);
116262306a36Sopenharmony_ci		rcv->skb = NULL;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci#ifdef NOTDEF
116662306a36Sopenharmony_ci	/* Reset. */
116762306a36Sopenharmony_ci	outb(0x00, PAR_CONTROL(dev));
116862306a36Sopenharmony_ci#endif
116962306a36Sopenharmony_ci	return 0;
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int
117362306a36Sopenharmony_ciplip_preempt(void *handle)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *)handle;
117662306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	/* Stand our ground if a datagram is on the wire */
117962306a36Sopenharmony_ci	if (nl->connection != PLIP_CN_NONE) {
118062306a36Sopenharmony_ci		nl->should_relinquish = 1;
118162306a36Sopenharmony_ci		return 1;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	nl->port_owner = 0;	/* Remember that we released the bus */
118562306a36Sopenharmony_ci	return 0;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic void
118962306a36Sopenharmony_ciplip_wakeup(void *handle)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *)handle;
119262306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (nl->port_owner) {
119562306a36Sopenharmony_ci		/* Why are we being woken up? */
119662306a36Sopenharmony_ci		printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name);
119762306a36Sopenharmony_ci		if (!parport_claim(nl->pardev))
119862306a36Sopenharmony_ci			/* bus_owner is already set (but why?) */
119962306a36Sopenharmony_ci			printk(KERN_DEBUG "%s: I'm broken.\n", dev->name);
120062306a36Sopenharmony_ci		else
120162306a36Sopenharmony_ci			return;
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (!(dev->flags & IFF_UP))
120562306a36Sopenharmony_ci		/* Don't need the port when the interface is down */
120662306a36Sopenharmony_ci		return;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (!parport_claim(nl->pardev)) {
120962306a36Sopenharmony_ci		nl->port_owner = 1;
121062306a36Sopenharmony_ci		/* Clear the data port. */
121162306a36Sopenharmony_ci		write_data (dev, 0x00);
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic int
121662306a36Sopenharmony_ciplip_siocdevprivate(struct net_device *dev, struct ifreq *rq,
121762306a36Sopenharmony_ci		    void __user *data, int cmd)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
122062306a36Sopenharmony_ci	struct plipconf *pc = (struct plipconf *) &rq->ifr_ifru;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (cmd != SIOCDEVPLIP)
122362306a36Sopenharmony_ci		return -EOPNOTSUPP;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (in_compat_syscall())
122662306a36Sopenharmony_ci		return -EOPNOTSUPP;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	switch(pc->pcmd) {
122962306a36Sopenharmony_ci	case PLIP_GET_TIMEOUT:
123062306a36Sopenharmony_ci		pc->trigger = nl->trigger;
123162306a36Sopenharmony_ci		pc->nibble  = nl->nibble;
123262306a36Sopenharmony_ci		break;
123362306a36Sopenharmony_ci	case PLIP_SET_TIMEOUT:
123462306a36Sopenharmony_ci		if(!capable(CAP_NET_ADMIN))
123562306a36Sopenharmony_ci			return -EPERM;
123662306a36Sopenharmony_ci		nl->trigger = pc->trigger;
123762306a36Sopenharmony_ci		nl->nibble  = pc->nibble;
123862306a36Sopenharmony_ci		break;
123962306a36Sopenharmony_ci	default:
124062306a36Sopenharmony_ci		return -EOPNOTSUPP;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic int parport[PLIP_MAX] = { [0 ... PLIP_MAX-1] = -1 };
124662306a36Sopenharmony_cistatic int timid;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cimodule_param_array(parport, int, NULL, 0);
124962306a36Sopenharmony_cimodule_param(timid, int, 0);
125062306a36Sopenharmony_ciMODULE_PARM_DESC(parport, "List of parport device numbers to use by plip");
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic struct net_device *dev_plip[PLIP_MAX] = { NULL, };
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_cistatic inline int
125562306a36Sopenharmony_ciplip_searchfor(int list[], int a)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	int i;
125862306a36Sopenharmony_ci	for (i = 0; i < PLIP_MAX && list[i] != -1; i++) {
125962306a36Sopenharmony_ci		if (list[i] == a) return 1;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci	return 0;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci/* plip_attach() is called (by the parport code) when a port is
126562306a36Sopenharmony_ci * available to use. */
126662306a36Sopenharmony_cistatic void plip_attach (struct parport *port)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	static int unit;
126962306a36Sopenharmony_ci	struct net_device *dev;
127062306a36Sopenharmony_ci	struct net_local *nl;
127162306a36Sopenharmony_ci	char name[IFNAMSIZ];
127262306a36Sopenharmony_ci	struct pardev_cb plip_cb;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if ((parport[0] == -1 && (!timid || !port->devices)) ||
127562306a36Sopenharmony_ci	    plip_searchfor(parport, port->number)) {
127662306a36Sopenharmony_ci		if (unit == PLIP_MAX) {
127762306a36Sopenharmony_ci			printk(KERN_ERR "plip: too many devices\n");
127862306a36Sopenharmony_ci			return;
127962306a36Sopenharmony_ci		}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		sprintf(name, "plip%d", unit);
128262306a36Sopenharmony_ci		dev = alloc_etherdev(sizeof(struct net_local));
128362306a36Sopenharmony_ci		if (!dev)
128462306a36Sopenharmony_ci			return;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci		strcpy(dev->name, name);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		dev->irq = port->irq;
128962306a36Sopenharmony_ci		dev->base_addr = port->base;
129062306a36Sopenharmony_ci		if (port->irq == -1) {
129162306a36Sopenharmony_ci			printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode,"
129262306a36Sopenharmony_ci		                 "which is fairly inefficient!\n", port->name);
129362306a36Sopenharmony_ci		}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci		nl = netdev_priv(dev);
129662306a36Sopenharmony_ci		nl->dev = dev;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci		memset(&plip_cb, 0, sizeof(plip_cb));
129962306a36Sopenharmony_ci		plip_cb.private = dev;
130062306a36Sopenharmony_ci		plip_cb.preempt = plip_preempt;
130162306a36Sopenharmony_ci		plip_cb.wakeup = plip_wakeup;
130262306a36Sopenharmony_ci		plip_cb.irq_func = plip_interrupt;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		nl->pardev = parport_register_dev_model(port, dev->name,
130562306a36Sopenharmony_ci							&plip_cb, unit);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci		if (!nl->pardev) {
130862306a36Sopenharmony_ci			printk(KERN_ERR "%s: parport_register failed\n", name);
130962306a36Sopenharmony_ci			goto err_free_dev;
131062306a36Sopenharmony_ci		}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		plip_init_netdev(dev);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		if (register_netdev(dev)) {
131562306a36Sopenharmony_ci			printk(KERN_ERR "%s: network register failed\n", name);
131662306a36Sopenharmony_ci			goto err_parport_unregister;
131762306a36Sopenharmony_ci		}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci		printk(KERN_INFO "%s", version);
132062306a36Sopenharmony_ci		if (dev->irq != -1)
132162306a36Sopenharmony_ci			printk(KERN_INFO "%s: Parallel port at %#3lx, "
132262306a36Sopenharmony_ci					 "using IRQ %d.\n",
132362306a36Sopenharmony_ci				         dev->name, dev->base_addr, dev->irq);
132462306a36Sopenharmony_ci		else
132562306a36Sopenharmony_ci			printk(KERN_INFO "%s: Parallel port at %#3lx, "
132662306a36Sopenharmony_ci					 "not using IRQ.\n",
132762306a36Sopenharmony_ci					 dev->name, dev->base_addr);
132862306a36Sopenharmony_ci		dev_plip[unit++] = dev;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci	return;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cierr_parport_unregister:
133362306a36Sopenharmony_ci	parport_unregister_device(nl->pardev);
133462306a36Sopenharmony_cierr_free_dev:
133562306a36Sopenharmony_ci	free_netdev(dev);
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci/* plip_detach() is called (by the parport code) when a port is
133962306a36Sopenharmony_ci * no longer available to use. */
134062306a36Sopenharmony_cistatic void plip_detach (struct parport *port)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	/* Nothing to do */
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic int plip_probe(struct pardevice *par_dev)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct device_driver *drv = par_dev->dev.driver;
134862306a36Sopenharmony_ci	int len = strlen(drv->name);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	if (strncmp(par_dev->name, drv->name, len))
135162306a36Sopenharmony_ci		return -ENODEV;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	return 0;
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic struct parport_driver plip_driver = {
135762306a36Sopenharmony_ci	.name		= "plip",
135862306a36Sopenharmony_ci	.probe		= plip_probe,
135962306a36Sopenharmony_ci	.match_port	= plip_attach,
136062306a36Sopenharmony_ci	.detach		= plip_detach,
136162306a36Sopenharmony_ci	.devmodel	= true,
136262306a36Sopenharmony_ci};
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_cistatic void __exit plip_cleanup_module (void)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	struct net_device *dev;
136762306a36Sopenharmony_ci	int i;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	for (i=0; i < PLIP_MAX; i++) {
137062306a36Sopenharmony_ci		if ((dev = dev_plip[i])) {
137162306a36Sopenharmony_ci			struct net_local *nl = netdev_priv(dev);
137262306a36Sopenharmony_ci			unregister_netdev(dev);
137362306a36Sopenharmony_ci			if (nl->port_owner)
137462306a36Sopenharmony_ci				parport_release(nl->pardev);
137562306a36Sopenharmony_ci			parport_unregister_device(nl->pardev);
137662306a36Sopenharmony_ci			free_netdev(dev);
137762306a36Sopenharmony_ci			dev_plip[i] = NULL;
137862306a36Sopenharmony_ci		}
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	parport_unregister_driver(&plip_driver);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci#ifndef MODULE
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int parport_ptr;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic int __init plip_setup(char *str)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	int ints[4];
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	str = get_options(str, ARRAY_SIZE(ints), ints);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* Ugh. */
139562306a36Sopenharmony_ci	if (!strncmp(str, "parport", 7)) {
139662306a36Sopenharmony_ci		int n = simple_strtoul(str+7, NULL, 10);
139762306a36Sopenharmony_ci		if (parport_ptr < PLIP_MAX)
139862306a36Sopenharmony_ci			parport[parport_ptr++] = n;
139962306a36Sopenharmony_ci		else
140062306a36Sopenharmony_ci			printk(KERN_INFO "plip: too many ports, %s ignored.\n",
140162306a36Sopenharmony_ci			       str);
140262306a36Sopenharmony_ci	} else if (!strcmp(str, "timid")) {
140362306a36Sopenharmony_ci		timid = 1;
140462306a36Sopenharmony_ci	} else {
140562306a36Sopenharmony_ci		if (ints[0] == 0 || ints[1] == 0) {
140662306a36Sopenharmony_ci			/* disable driver on "plip=" or "plip=0" */
140762306a36Sopenharmony_ci			parport[0] = -2;
140862306a36Sopenharmony_ci		} else {
140962306a36Sopenharmony_ci			printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n",
141062306a36Sopenharmony_ci			       ints[1]);
141162306a36Sopenharmony_ci		}
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci	return 1;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci__setup("plip=", plip_setup);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci#endif /* !MODULE */
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_cistatic int __init plip_init (void)
142162306a36Sopenharmony_ci{
142262306a36Sopenharmony_ci	if (parport[0] == -2)
142362306a36Sopenharmony_ci		return 0;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (parport[0] != -1 && timid) {
142662306a36Sopenharmony_ci		printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
142762306a36Sopenharmony_ci		timid = 0;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (parport_register_driver (&plip_driver)) {
143162306a36Sopenharmony_ci		printk (KERN_WARNING "plip: couldn't register driver\n");
143262306a36Sopenharmony_ci		return 1;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	return 0;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_cimodule_init(plip_init);
143962306a36Sopenharmony_cimodule_exit(plip_cleanup_module);
144062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1441