18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ */
38c2ecf20Sopenharmony_ci/* PLIP: A parallel port "network" driver for Linux. */
48c2ecf20Sopenharmony_ci/* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
58c2ecf20Sopenharmony_ci/*
68c2ecf20Sopenharmony_ci * Authors:	Donald Becker <becker@scyld.com>
78c2ecf20Sopenharmony_ci *		Tommy Thorn <thorn@daimi.aau.dk>
88c2ecf20Sopenharmony_ci *		Tanabe Hiroyasu <hiro@sanpo.t.u-tokyo.ac.jp>
98c2ecf20Sopenharmony_ci *		Alan Cox <gw4pts@gw4pts.ampr.org>
108c2ecf20Sopenharmony_ci *		Peter Bauer <100136.3530@compuserve.com>
118c2ecf20Sopenharmony_ci *		Niibe Yutaka <gniibe@mri.co.jp>
128c2ecf20Sopenharmony_ci *		Nimrod Zimerman <zimerman@mailandnews.com>
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Enhancements:
158c2ecf20Sopenharmony_ci *		Modularization and ifreq/ifmap support by Alan Cox.
168c2ecf20Sopenharmony_ci *		Rewritten by Niibe Yutaka.
178c2ecf20Sopenharmony_ci *		parport-sharing awareness code by Philip Blundell.
188c2ecf20Sopenharmony_ci *		SMP locking by Niibe Yutaka.
198c2ecf20Sopenharmony_ci *		Support for parallel ports with no IRQ (poll mode),
208c2ecf20Sopenharmony_ci *		Modifications to use the parallel port API
218c2ecf20Sopenharmony_ci *		by Nimrod Zimerman.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Fixes:
248c2ecf20Sopenharmony_ci *		Niibe Yutaka
258c2ecf20Sopenharmony_ci *		  - Module initialization.
268c2ecf20Sopenharmony_ci *		  - MTU fix.
278c2ecf20Sopenharmony_ci *		  - Make sure other end is OK, before sending a packet.
288c2ecf20Sopenharmony_ci *		  - Fix immediate timer problem.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *		Al Viro
318c2ecf20Sopenharmony_ci *		  - Changed {enable,disable}_irq handling to make it work
328c2ecf20Sopenharmony_ci *		    with new ("stack") semantics.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Original version and the name 'PLIP' from Donald Becker <becker@scyld.com>
378c2ecf20Sopenharmony_ci * inspired by Russ Nelson's parallel port packet driver.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * NOTE:
408c2ecf20Sopenharmony_ci *     Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0.
418c2ecf20Sopenharmony_ci *     Because of the necessity to communicate to DOS machines with the
428c2ecf20Sopenharmony_ci *     Crynwr packet driver, Peter Bauer changed the protocol again
438c2ecf20Sopenharmony_ci *     back to original protocol.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci *     This version follows original PLIP protocol.
468c2ecf20Sopenharmony_ci *     So, this PLIP can't communicate the PLIP of Linux v1.0.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci *     To use with DOS box, please do (Turn on ARP switch):
518c2ecf20Sopenharmony_ci *	# ifconfig plip[0-2] arp
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic const char version[] = "NET3 PLIP version 2.4-parport gniibe@mri.co.jp\n";
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci  Sources:
578c2ecf20Sopenharmony_ci	Ideas and protocols came from Russ Nelson's <nelson@crynwr.com>
588c2ecf20Sopenharmony_ci	"parallel.asm" parallel port packet driver.
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci  The "Crynwr" parallel port standard specifies the following protocol:
618c2ecf20Sopenharmony_ci    Trigger by sending nibble '0x8' (this causes interrupt on other end)
628c2ecf20Sopenharmony_ci    count-low octet
638c2ecf20Sopenharmony_ci    count-high octet
648c2ecf20Sopenharmony_ci    ... data octets
658c2ecf20Sopenharmony_ci    checksum octet
668c2ecf20Sopenharmony_ci  Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)>
678c2ecf20Sopenharmony_ci			<wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)>
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci  The packet is encapsulated as if it were ethernet.
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci  The cable used is a de facto standard parallel null cable -- sold as
728c2ecf20Sopenharmony_ci  a "LapLink" cable by various places.  You'll need a 12-conductor cable to
738c2ecf20Sopenharmony_ci  make one yourself.  The wiring is:
748c2ecf20Sopenharmony_ci    SLCTIN	17 - 17
758c2ecf20Sopenharmony_ci    GROUND	25 - 25
768c2ecf20Sopenharmony_ci    D0->ERROR	2 - 15		15 - 2
778c2ecf20Sopenharmony_ci    D1->SLCT	3 - 13		13 - 3
788c2ecf20Sopenharmony_ci    D2->PAPOUT	4 - 12		12 - 4
798c2ecf20Sopenharmony_ci    D3->ACK	5 - 10		10 - 5
808c2ecf20Sopenharmony_ci    D4->BUSY	6 - 11		11 - 6
818c2ecf20Sopenharmony_ci  Do not connect the other pins.  They are
828c2ecf20Sopenharmony_ci    D5,D6,D7 are 7,8,9
838c2ecf20Sopenharmony_ci    STROBE is 1, FEED is 14, INIT is 16
848c2ecf20Sopenharmony_ci    extra grounds are 18,19,20,21,22,23,24
858c2ecf20Sopenharmony_ci*/
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#include <linux/module.h>
888c2ecf20Sopenharmony_ci#include <linux/kernel.h>
898c2ecf20Sopenharmony_ci#include <linux/types.h>
908c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
918c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
928c2ecf20Sopenharmony_ci#include <linux/string.h>
938c2ecf20Sopenharmony_ci#include <linux/slab.h>
948c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
958c2ecf20Sopenharmony_ci#include <linux/in.h>
968c2ecf20Sopenharmony_ci#include <linux/errno.h>
978c2ecf20Sopenharmony_ci#include <linux/delay.h>
988c2ecf20Sopenharmony_ci#include <linux/init.h>
998c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
1008c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
1018c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
1028c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
1038c2ecf20Sopenharmony_ci#include <linux/if_plip.h>
1048c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
1058c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
1068c2ecf20Sopenharmony_ci#include <linux/completion.h>
1078c2ecf20Sopenharmony_ci#include <linux/parport.h>
1088c2ecf20Sopenharmony_ci#include <linux/bitops.h>
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#include <net/neighbour.h>
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#include <asm/irq.h>
1138c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* Maximum number of devices to support. */
1168c2ecf20Sopenharmony_ci#define PLIP_MAX  8
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/* Use 0 for production, 1 for verification, >2 for debug */
1198c2ecf20Sopenharmony_ci#ifndef NET_DEBUG
1208c2ecf20Sopenharmony_ci#define NET_DEBUG 1
1218c2ecf20Sopenharmony_ci#endif
1228c2ecf20Sopenharmony_cistatic const unsigned int net_debug = NET_DEBUG;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define ENABLE(irq)  if (irq != -1) enable_irq(irq)
1258c2ecf20Sopenharmony_ci#define DISABLE(irq) if (irq != -1) disable_irq(irq)
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci/* In micro second */
1288c2ecf20Sopenharmony_ci#define PLIP_DELAY_UNIT		   1
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* Connection time out = PLIP_TRIGGER_WAIT * PLIP_DELAY_UNIT usec */
1318c2ecf20Sopenharmony_ci#define PLIP_TRIGGER_WAIT	 500
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
1348c2ecf20Sopenharmony_ci#define PLIP_NIBBLE_WAIT        3000
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* Bottom halves */
1378c2ecf20Sopenharmony_cistatic void plip_kick_bh(struct work_struct *work);
1388c2ecf20Sopenharmony_cistatic void plip_bh(struct work_struct *work);
1398c2ecf20Sopenharmony_cistatic void plip_timer_bh(struct work_struct *work);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* Interrupt handler */
1428c2ecf20Sopenharmony_cistatic void plip_interrupt(void *dev_id);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Functions for DEV methods */
1458c2ecf20Sopenharmony_cistatic netdev_tx_t plip_tx_packet(struct sk_buff *skb, struct net_device *dev);
1468c2ecf20Sopenharmony_cistatic int plip_hard_header(struct sk_buff *skb, struct net_device *dev,
1478c2ecf20Sopenharmony_ci                            unsigned short type, const void *daddr,
1488c2ecf20Sopenharmony_ci			    const void *saddr, unsigned len);
1498c2ecf20Sopenharmony_cistatic int plip_hard_header_cache(const struct neighbour *neigh,
1508c2ecf20Sopenharmony_ci                                  struct hh_cache *hh, __be16 type);
1518c2ecf20Sopenharmony_cistatic int plip_open(struct net_device *dev);
1528c2ecf20Sopenharmony_cistatic int plip_close(struct net_device *dev);
1538c2ecf20Sopenharmony_cistatic int plip_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
1548c2ecf20Sopenharmony_cistatic int plip_preempt(void *handle);
1558c2ecf20Sopenharmony_cistatic void plip_wakeup(void *handle);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cienum plip_connection_state {
1588c2ecf20Sopenharmony_ci	PLIP_CN_NONE=0,
1598c2ecf20Sopenharmony_ci	PLIP_CN_RECEIVE,
1608c2ecf20Sopenharmony_ci	PLIP_CN_SEND,
1618c2ecf20Sopenharmony_ci	PLIP_CN_CLOSING,
1628c2ecf20Sopenharmony_ci	PLIP_CN_ERROR
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cienum plip_packet_state {
1668c2ecf20Sopenharmony_ci	PLIP_PK_DONE=0,
1678c2ecf20Sopenharmony_ci	PLIP_PK_TRIGGER,
1688c2ecf20Sopenharmony_ci	PLIP_PK_LENGTH_LSB,
1698c2ecf20Sopenharmony_ci	PLIP_PK_LENGTH_MSB,
1708c2ecf20Sopenharmony_ci	PLIP_PK_DATA,
1718c2ecf20Sopenharmony_ci	PLIP_PK_CHECKSUM
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cienum plip_nibble_state {
1758c2ecf20Sopenharmony_ci	PLIP_NB_BEGIN,
1768c2ecf20Sopenharmony_ci	PLIP_NB_1,
1778c2ecf20Sopenharmony_ci	PLIP_NB_2,
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistruct plip_local {
1818c2ecf20Sopenharmony_ci	enum plip_packet_state state;
1828c2ecf20Sopenharmony_ci	enum plip_nibble_state nibble;
1838c2ecf20Sopenharmony_ci	union {
1848c2ecf20Sopenharmony_ci		struct {
1858c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN)
1868c2ecf20Sopenharmony_ci			unsigned char lsb;
1878c2ecf20Sopenharmony_ci			unsigned char msb;
1888c2ecf20Sopenharmony_ci#elif defined(__BIG_ENDIAN)
1898c2ecf20Sopenharmony_ci			unsigned char msb;
1908c2ecf20Sopenharmony_ci			unsigned char lsb;
1918c2ecf20Sopenharmony_ci#else
1928c2ecf20Sopenharmony_ci#error	"Please fix the endianness defines in <asm/byteorder.h>"
1938c2ecf20Sopenharmony_ci#endif
1948c2ecf20Sopenharmony_ci		} b;
1958c2ecf20Sopenharmony_ci		unsigned short h;
1968c2ecf20Sopenharmony_ci	} length;
1978c2ecf20Sopenharmony_ci	unsigned short byte;
1988c2ecf20Sopenharmony_ci	unsigned char  checksum;
1998c2ecf20Sopenharmony_ci	unsigned char  data;
2008c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistruct net_local {
2048c2ecf20Sopenharmony_ci	struct net_device *dev;
2058c2ecf20Sopenharmony_ci	struct work_struct immediate;
2068c2ecf20Sopenharmony_ci	struct delayed_work deferred;
2078c2ecf20Sopenharmony_ci	struct delayed_work timer;
2088c2ecf20Sopenharmony_ci	struct plip_local snd_data;
2098c2ecf20Sopenharmony_ci	struct plip_local rcv_data;
2108c2ecf20Sopenharmony_ci	struct pardevice *pardev;
2118c2ecf20Sopenharmony_ci	unsigned long  trigger;
2128c2ecf20Sopenharmony_ci	unsigned long  nibble;
2138c2ecf20Sopenharmony_ci	enum plip_connection_state connection;
2148c2ecf20Sopenharmony_ci	unsigned short timeout_count;
2158c2ecf20Sopenharmony_ci	int is_deferred;
2168c2ecf20Sopenharmony_ci	int port_owner;
2178c2ecf20Sopenharmony_ci	int should_relinquish;
2188c2ecf20Sopenharmony_ci	spinlock_t lock;
2198c2ecf20Sopenharmony_ci	atomic_t kill_timer;
2208c2ecf20Sopenharmony_ci	struct completion killed_timer_cmp;
2218c2ecf20Sopenharmony_ci};
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic inline void enable_parport_interrupts (struct net_device *dev)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	if (dev->irq != -1)
2268c2ecf20Sopenharmony_ci	{
2278c2ecf20Sopenharmony_ci		struct parport *port =
2288c2ecf20Sopenharmony_ci		   ((struct net_local *)netdev_priv(dev))->pardev->port;
2298c2ecf20Sopenharmony_ci		port->ops->enable_irq (port);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic inline void disable_parport_interrupts (struct net_device *dev)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	if (dev->irq != -1)
2368c2ecf20Sopenharmony_ci	{
2378c2ecf20Sopenharmony_ci		struct parport *port =
2388c2ecf20Sopenharmony_ci		   ((struct net_local *)netdev_priv(dev))->pardev->port;
2398c2ecf20Sopenharmony_ci		port->ops->disable_irq (port);
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic inline void write_data (struct net_device *dev, unsigned char data)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct parport *port =
2468c2ecf20Sopenharmony_ci	   ((struct net_local *)netdev_priv(dev))->pardev->port;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	port->ops->write_data (port, data);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic inline unsigned char read_status (struct net_device *dev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct parport *port =
2548c2ecf20Sopenharmony_ci	   ((struct net_local *)netdev_priv(dev))->pardev->port;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return port->ops->read_status (port);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic const struct header_ops plip_header_ops = {
2608c2ecf20Sopenharmony_ci	.create	= plip_hard_header,
2618c2ecf20Sopenharmony_ci	.cache  = plip_hard_header_cache,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic const struct net_device_ops plip_netdev_ops = {
2658c2ecf20Sopenharmony_ci	.ndo_open		 = plip_open,
2668c2ecf20Sopenharmony_ci	.ndo_stop		 = plip_close,
2678c2ecf20Sopenharmony_ci	.ndo_start_xmit		 = plip_tx_packet,
2688c2ecf20Sopenharmony_ci	.ndo_do_ioctl		 = plip_ioctl,
2698c2ecf20Sopenharmony_ci	.ndo_set_mac_address	 = eth_mac_addr,
2708c2ecf20Sopenharmony_ci	.ndo_validate_addr	 = eth_validate_addr,
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/* Entry point of PLIP driver.
2748c2ecf20Sopenharmony_ci   Probe the hardware, and register/initialize the driver.
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci   PLIP is rather weird, because of the way it interacts with the parport
2778c2ecf20Sopenharmony_ci   system.  It is _not_ initialised from Space.c.  Instead, plip_init()
2788c2ecf20Sopenharmony_ci   is called, and that function makes up a "struct net_device" for each port, and
2798c2ecf20Sopenharmony_ci   then calls us here.
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci   */
2828c2ecf20Sopenharmony_cistatic void
2838c2ecf20Sopenharmony_ciplip_init_netdev(struct net_device *dev)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* Then, override parts of it */
2888c2ecf20Sopenharmony_ci	dev->tx_queue_len 	 = 10;
2898c2ecf20Sopenharmony_ci	dev->flags	         = IFF_POINTOPOINT|IFF_NOARP;
2908c2ecf20Sopenharmony_ci	memset(dev->dev_addr, 0xfc, ETH_ALEN);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	dev->netdev_ops		 = &plip_netdev_ops;
2938c2ecf20Sopenharmony_ci	dev->header_ops          = &plip_header_ops;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	nl->port_owner = 0;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Initialize constants */
2998c2ecf20Sopenharmony_ci	nl->trigger	= PLIP_TRIGGER_WAIT;
3008c2ecf20Sopenharmony_ci	nl->nibble	= PLIP_NIBBLE_WAIT;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Initialize task queue structures */
3038c2ecf20Sopenharmony_ci	INIT_WORK(&nl->immediate, plip_bh);
3048c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&nl->deferred, plip_kick_bh);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (dev->irq == -1)
3078c2ecf20Sopenharmony_ci		INIT_DELAYED_WORK(&nl->timer, plip_timer_bh);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	spin_lock_init(&nl->lock);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci/* Bottom half handler for the delayed request.
3138c2ecf20Sopenharmony_ci   This routine is kicked by do_timer().
3148c2ecf20Sopenharmony_ci   Request `plip_bh' to be invoked. */
3158c2ecf20Sopenharmony_cistatic void
3168c2ecf20Sopenharmony_ciplip_kick_bh(struct work_struct *work)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct net_local *nl =
3198c2ecf20Sopenharmony_ci		container_of(work, struct net_local, deferred.work);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (nl->is_deferred)
3228c2ecf20Sopenharmony_ci		schedule_work(&nl->immediate);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci/* Forward declarations of internal routines */
3268c2ecf20Sopenharmony_cistatic int plip_none(struct net_device *, struct net_local *,
3278c2ecf20Sopenharmony_ci		     struct plip_local *, struct plip_local *);
3288c2ecf20Sopenharmony_cistatic int plip_receive_packet(struct net_device *, struct net_local *,
3298c2ecf20Sopenharmony_ci			       struct plip_local *, struct plip_local *);
3308c2ecf20Sopenharmony_cistatic int plip_send_packet(struct net_device *, struct net_local *,
3318c2ecf20Sopenharmony_ci			    struct plip_local *, struct plip_local *);
3328c2ecf20Sopenharmony_cistatic int plip_connection_close(struct net_device *, struct net_local *,
3338c2ecf20Sopenharmony_ci				 struct plip_local *, struct plip_local *);
3348c2ecf20Sopenharmony_cistatic int plip_error(struct net_device *, struct net_local *,
3358c2ecf20Sopenharmony_ci		      struct plip_local *, struct plip_local *);
3368c2ecf20Sopenharmony_cistatic int plip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
3378c2ecf20Sopenharmony_ci				 struct plip_local *snd,
3388c2ecf20Sopenharmony_ci				 struct plip_local *rcv,
3398c2ecf20Sopenharmony_ci				 int error);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci#define OK        0
3428c2ecf20Sopenharmony_ci#define TIMEOUT   1
3438c2ecf20Sopenharmony_ci#define ERROR     2
3448c2ecf20Sopenharmony_ci#define HS_TIMEOUT	3
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_citypedef int (*plip_func)(struct net_device *dev, struct net_local *nl,
3478c2ecf20Sopenharmony_ci			 struct plip_local *snd, struct plip_local *rcv);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic const plip_func connection_state_table[] =
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	plip_none,
3528c2ecf20Sopenharmony_ci	plip_receive_packet,
3538c2ecf20Sopenharmony_ci	plip_send_packet,
3548c2ecf20Sopenharmony_ci	plip_connection_close,
3558c2ecf20Sopenharmony_ci	plip_error
3568c2ecf20Sopenharmony_ci};
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/* Bottom half handler of PLIP. */
3598c2ecf20Sopenharmony_cistatic void
3608c2ecf20Sopenharmony_ciplip_bh(struct work_struct *work)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct net_local *nl = container_of(work, struct net_local, immediate);
3638c2ecf20Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
3648c2ecf20Sopenharmony_ci	struct plip_local *rcv = &nl->rcv_data;
3658c2ecf20Sopenharmony_ci	plip_func f;
3668c2ecf20Sopenharmony_ci	int r;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	nl->is_deferred = 0;
3698c2ecf20Sopenharmony_ci	f = connection_state_table[nl->connection];
3708c2ecf20Sopenharmony_ci	if ((r = (*f)(nl->dev, nl, snd, rcv)) != OK &&
3718c2ecf20Sopenharmony_ci	    (r = plip_bh_timeout_error(nl->dev, nl, snd, rcv, r)) != OK) {
3728c2ecf20Sopenharmony_ci		nl->is_deferred = 1;
3738c2ecf20Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void
3788c2ecf20Sopenharmony_ciplip_timer_bh(struct work_struct *work)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct net_local *nl =
3818c2ecf20Sopenharmony_ci		container_of(work, struct net_local, timer.work);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (!(atomic_read (&nl->kill_timer))) {
3848c2ecf20Sopenharmony_ci		plip_interrupt (nl->dev);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		schedule_delayed_work(&nl->timer, 1);
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	else {
3898c2ecf20Sopenharmony_ci		complete(&nl->killed_timer_cmp);
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic int
3948c2ecf20Sopenharmony_ciplip_bh_timeout_error(struct net_device *dev, struct net_local *nl,
3958c2ecf20Sopenharmony_ci		      struct plip_local *snd, struct plip_local *rcv,
3968c2ecf20Sopenharmony_ci		      int error)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	unsigned char c0;
3998c2ecf20Sopenharmony_ci	/*
4008c2ecf20Sopenharmony_ci	 * This is tricky. If we got here from the beginning of send (either
4018c2ecf20Sopenharmony_ci	 * with ERROR or HS_TIMEOUT) we have IRQ enabled. Otherwise it's
4028c2ecf20Sopenharmony_ci	 * already disabled. With the old variant of {enable,disable}_irq()
4038c2ecf20Sopenharmony_ci	 * extra disable_irq() was a no-op. Now it became mortal - it's
4048c2ecf20Sopenharmony_ci	 * unbalanced and thus we'll never re-enable IRQ (until rmmod plip,
4058c2ecf20Sopenharmony_ci	 * that is). So we have to treat HS_TIMEOUT and ERROR from send
4068c2ecf20Sopenharmony_ci	 * in a special way.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	spin_lock_irq(&nl->lock);
4108c2ecf20Sopenharmony_ci	if (nl->connection == PLIP_CN_SEND) {
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		if (error != ERROR) { /* Timeout */
4138c2ecf20Sopenharmony_ci			nl->timeout_count++;
4148c2ecf20Sopenharmony_ci			if ((error == HS_TIMEOUT && nl->timeout_count <= 10) ||
4158c2ecf20Sopenharmony_ci			    nl->timeout_count <= 3) {
4168c2ecf20Sopenharmony_ci				spin_unlock_irq(&nl->lock);
4178c2ecf20Sopenharmony_ci				/* Try again later */
4188c2ecf20Sopenharmony_ci				return TIMEOUT;
4198c2ecf20Sopenharmony_ci			}
4208c2ecf20Sopenharmony_ci			c0 = read_status(dev);
4218c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n",
4228c2ecf20Sopenharmony_ci			       dev->name, snd->state, c0);
4238c2ecf20Sopenharmony_ci		} else
4248c2ecf20Sopenharmony_ci			error = HS_TIMEOUT;
4258c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
4268c2ecf20Sopenharmony_ci		dev->stats.tx_aborted_errors++;
4278c2ecf20Sopenharmony_ci	} else if (nl->connection == PLIP_CN_RECEIVE) {
4288c2ecf20Sopenharmony_ci		if (rcv->state == PLIP_PK_TRIGGER) {
4298c2ecf20Sopenharmony_ci			/* Transmission was interrupted. */
4308c2ecf20Sopenharmony_ci			spin_unlock_irq(&nl->lock);
4318c2ecf20Sopenharmony_ci			return OK;
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci		if (error != ERROR) { /* Timeout */
4348c2ecf20Sopenharmony_ci			if (++nl->timeout_count <= 3) {
4358c2ecf20Sopenharmony_ci				spin_unlock_irq(&nl->lock);
4368c2ecf20Sopenharmony_ci				/* Try again later */
4378c2ecf20Sopenharmony_ci				return TIMEOUT;
4388c2ecf20Sopenharmony_ci			}
4398c2ecf20Sopenharmony_ci			c0 = read_status(dev);
4408c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n",
4418c2ecf20Sopenharmony_ci			       dev->name, rcv->state, c0);
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		dev->stats.rx_dropped++;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci	rcv->state = PLIP_PK_DONE;
4468c2ecf20Sopenharmony_ci	if (rcv->skb) {
4478c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(rcv->skb);
4488c2ecf20Sopenharmony_ci		rcv->skb = NULL;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	snd->state = PLIP_PK_DONE;
4518c2ecf20Sopenharmony_ci	if (snd->skb) {
4528c2ecf20Sopenharmony_ci		dev_consume_skb_irq(snd->skb);
4538c2ecf20Sopenharmony_ci		snd->skb = NULL;
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	spin_unlock_irq(&nl->lock);
4568c2ecf20Sopenharmony_ci	if (error == HS_TIMEOUT) {
4578c2ecf20Sopenharmony_ci		DISABLE(dev->irq);
4588c2ecf20Sopenharmony_ci		synchronize_irq(dev->irq);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	disable_parport_interrupts (dev);
4618c2ecf20Sopenharmony_ci	netif_stop_queue (dev);
4628c2ecf20Sopenharmony_ci	nl->connection = PLIP_CN_ERROR;
4638c2ecf20Sopenharmony_ci	write_data (dev, 0x00);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return TIMEOUT;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int
4698c2ecf20Sopenharmony_ciplip_none(struct net_device *dev, struct net_local *nl,
4708c2ecf20Sopenharmony_ci	  struct plip_local *snd, struct plip_local *rcv)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	return OK;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* PLIP_RECEIVE --- receive a byte(two nibbles)
4768c2ecf20Sopenharmony_ci   Returns OK on success, TIMEOUT on timeout */
4778c2ecf20Sopenharmony_cistatic inline int
4788c2ecf20Sopenharmony_ciplip_receive(unsigned short nibble_timeout, struct net_device *dev,
4798c2ecf20Sopenharmony_ci	     enum plip_nibble_state *ns_p, unsigned char *data_p)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	unsigned char c0, c1;
4828c2ecf20Sopenharmony_ci	unsigned int cx;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	switch (*ns_p) {
4858c2ecf20Sopenharmony_ci	case PLIP_NB_BEGIN:
4868c2ecf20Sopenharmony_ci		cx = nibble_timeout;
4878c2ecf20Sopenharmony_ci		while (1) {
4888c2ecf20Sopenharmony_ci			c0 = read_status(dev);
4898c2ecf20Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
4908c2ecf20Sopenharmony_ci			if ((c0 & 0x80) == 0) {
4918c2ecf20Sopenharmony_ci				c1 = read_status(dev);
4928c2ecf20Sopenharmony_ci				if (c0 == c1)
4938c2ecf20Sopenharmony_ci					break;
4948c2ecf20Sopenharmony_ci			}
4958c2ecf20Sopenharmony_ci			if (--cx == 0)
4968c2ecf20Sopenharmony_ci				return TIMEOUT;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci		*data_p = (c0 >> 3) & 0x0f;
4998c2ecf20Sopenharmony_ci		write_data (dev, 0x10); /* send ACK */
5008c2ecf20Sopenharmony_ci		*ns_p = PLIP_NB_1;
5018c2ecf20Sopenharmony_ci		fallthrough;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	case PLIP_NB_1:
5048c2ecf20Sopenharmony_ci		cx = nibble_timeout;
5058c2ecf20Sopenharmony_ci		while (1) {
5068c2ecf20Sopenharmony_ci			c0 = read_status(dev);
5078c2ecf20Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
5088c2ecf20Sopenharmony_ci			if (c0 & 0x80) {
5098c2ecf20Sopenharmony_ci				c1 = read_status(dev);
5108c2ecf20Sopenharmony_ci				if (c0 == c1)
5118c2ecf20Sopenharmony_ci					break;
5128c2ecf20Sopenharmony_ci			}
5138c2ecf20Sopenharmony_ci			if (--cx == 0)
5148c2ecf20Sopenharmony_ci				return TIMEOUT;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci		*data_p |= (c0 << 1) & 0xf0;
5178c2ecf20Sopenharmony_ci		write_data (dev, 0x00); /* send ACK */
5188c2ecf20Sopenharmony_ci		*ns_p = PLIP_NB_BEGIN;
5198c2ecf20Sopenharmony_ci	case PLIP_NB_2:
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci	return OK;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci/*
5268c2ecf20Sopenharmony_ci *	Determine the packet's protocol ID. The rule here is that we
5278c2ecf20Sopenharmony_ci *	assume 802.3 if the type field is short enough to be a length.
5288c2ecf20Sopenharmony_ci *	This is normal practice and works for any 'now in use' protocol.
5298c2ecf20Sopenharmony_ci *
5308c2ecf20Sopenharmony_ci *	PLIP is ethernet ish but the daddr might not be valid if unicast.
5318c2ecf20Sopenharmony_ci *	PLIP fortunately has no bus architecture (its Point-to-point).
5328c2ecf20Sopenharmony_ci *
5338c2ecf20Sopenharmony_ci *	We can't fix the daddr thing as that quirk (more bug) is embedded
5348c2ecf20Sopenharmony_ci *	in far too many old systems not all even running Linux.
5358c2ecf20Sopenharmony_ci */
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_cistatic __be16 plip_type_trans(struct sk_buff *skb, struct net_device *dev)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	struct ethhdr *eth;
5408c2ecf20Sopenharmony_ci	unsigned char *rawp;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	skb_reset_mac_header(skb);
5438c2ecf20Sopenharmony_ci	skb_pull(skb,dev->hard_header_len);
5448c2ecf20Sopenharmony_ci	eth = eth_hdr(skb);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if(is_multicast_ether_addr(eth->h_dest))
5478c2ecf20Sopenharmony_ci	{
5488c2ecf20Sopenharmony_ci		if(ether_addr_equal_64bits(eth->h_dest, dev->broadcast))
5498c2ecf20Sopenharmony_ci			skb->pkt_type=PACKET_BROADCAST;
5508c2ecf20Sopenharmony_ci		else
5518c2ecf20Sopenharmony_ci			skb->pkt_type=PACKET_MULTICAST;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/*
5558c2ecf20Sopenharmony_ci	 *	This ALLMULTI check should be redundant by 1.4
5568c2ecf20Sopenharmony_ci	 *	so don't forget to remove it.
5578c2ecf20Sopenharmony_ci	 */
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
5608c2ecf20Sopenharmony_ci		return eth->h_proto;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	rawp = skb->data;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/*
5658c2ecf20Sopenharmony_ci	 *	This is a magic hack to spot IPX packets. Older Novell breaks
5668c2ecf20Sopenharmony_ci	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
5678c2ecf20Sopenharmony_ci	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
5688c2ecf20Sopenharmony_ci	 *	won't work for fault tolerant netware but does for the rest.
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	if (*(unsigned short *)rawp == 0xFFFF)
5718c2ecf20Sopenharmony_ci		return htons(ETH_P_802_3);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	/*
5748c2ecf20Sopenharmony_ci	 *	Real 802.2 LLC
5758c2ecf20Sopenharmony_ci	 */
5768c2ecf20Sopenharmony_ci	return htons(ETH_P_802_2);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci/* PLIP_RECEIVE_PACKET --- receive a packet */
5808c2ecf20Sopenharmony_cistatic int
5818c2ecf20Sopenharmony_ciplip_receive_packet(struct net_device *dev, struct net_local *nl,
5828c2ecf20Sopenharmony_ci		    struct plip_local *snd, struct plip_local *rcv)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	unsigned short nibble_timeout = nl->nibble;
5858c2ecf20Sopenharmony_ci	unsigned char *lbuf;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	switch (rcv->state) {
5888c2ecf20Sopenharmony_ci	case PLIP_PK_TRIGGER:
5898c2ecf20Sopenharmony_ci		DISABLE(dev->irq);
5908c2ecf20Sopenharmony_ci		/* Don't need to synchronize irq, as we can safely ignore it */
5918c2ecf20Sopenharmony_ci		disable_parport_interrupts (dev);
5928c2ecf20Sopenharmony_ci		write_data (dev, 0x01); /* send ACK */
5938c2ecf20Sopenharmony_ci		if (net_debug > 2)
5948c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: receive start\n", dev->name);
5958c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_LENGTH_LSB;
5968c2ecf20Sopenharmony_ci		rcv->nibble = PLIP_NB_BEGIN;
5978c2ecf20Sopenharmony_ci		fallthrough;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	case PLIP_PK_LENGTH_LSB:
6008c2ecf20Sopenharmony_ci		if (snd->state != PLIP_PK_DONE) {
6018c2ecf20Sopenharmony_ci			if (plip_receive(nl->trigger, dev,
6028c2ecf20Sopenharmony_ci					 &rcv->nibble, &rcv->length.b.lsb)) {
6038c2ecf20Sopenharmony_ci				/* collision, here dev->tbusy == 1 */
6048c2ecf20Sopenharmony_ci				rcv->state = PLIP_PK_DONE;
6058c2ecf20Sopenharmony_ci				nl->is_deferred = 1;
6068c2ecf20Sopenharmony_ci				nl->connection = PLIP_CN_SEND;
6078c2ecf20Sopenharmony_ci				schedule_delayed_work(&nl->deferred, 1);
6088c2ecf20Sopenharmony_ci				enable_parport_interrupts (dev);
6098c2ecf20Sopenharmony_ci				ENABLE(dev->irq);
6108c2ecf20Sopenharmony_ci				return OK;
6118c2ecf20Sopenharmony_ci			}
6128c2ecf20Sopenharmony_ci		} else {
6138c2ecf20Sopenharmony_ci			if (plip_receive(nibble_timeout, dev,
6148c2ecf20Sopenharmony_ci					 &rcv->nibble, &rcv->length.b.lsb))
6158c2ecf20Sopenharmony_ci				return TIMEOUT;
6168c2ecf20Sopenharmony_ci		}
6178c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_LENGTH_MSB;
6188c2ecf20Sopenharmony_ci		fallthrough;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	case PLIP_PK_LENGTH_MSB:
6218c2ecf20Sopenharmony_ci		if (plip_receive(nibble_timeout, dev,
6228c2ecf20Sopenharmony_ci				 &rcv->nibble, &rcv->length.b.msb))
6238c2ecf20Sopenharmony_ci			return TIMEOUT;
6248c2ecf20Sopenharmony_ci		if (rcv->length.h > dev->mtu + dev->hard_header_len ||
6258c2ecf20Sopenharmony_ci		    rcv->length.h < 8) {
6268c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h);
6278c2ecf20Sopenharmony_ci			return ERROR;
6288c2ecf20Sopenharmony_ci		}
6298c2ecf20Sopenharmony_ci		/* Malloc up new buffer. */
6308c2ecf20Sopenharmony_ci		rcv->skb = dev_alloc_skb(rcv->length.h + 2);
6318c2ecf20Sopenharmony_ci		if (rcv->skb == NULL) {
6328c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: Memory squeeze.\n", dev->name);
6338c2ecf20Sopenharmony_ci			return ERROR;
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci		skb_reserve(rcv->skb, 2);	/* Align IP on 16 byte boundaries */
6368c2ecf20Sopenharmony_ci		skb_put(rcv->skb,rcv->length.h);
6378c2ecf20Sopenharmony_ci		rcv->skb->dev = dev;
6388c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_DATA;
6398c2ecf20Sopenharmony_ci		rcv->byte = 0;
6408c2ecf20Sopenharmony_ci		rcv->checksum = 0;
6418c2ecf20Sopenharmony_ci		fallthrough;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	case PLIP_PK_DATA:
6448c2ecf20Sopenharmony_ci		lbuf = rcv->skb->data;
6458c2ecf20Sopenharmony_ci		do {
6468c2ecf20Sopenharmony_ci			if (plip_receive(nibble_timeout, dev,
6478c2ecf20Sopenharmony_ci					 &rcv->nibble, &lbuf[rcv->byte]))
6488c2ecf20Sopenharmony_ci				return TIMEOUT;
6498c2ecf20Sopenharmony_ci		} while (++rcv->byte < rcv->length.h);
6508c2ecf20Sopenharmony_ci		do {
6518c2ecf20Sopenharmony_ci			rcv->checksum += lbuf[--rcv->byte];
6528c2ecf20Sopenharmony_ci		} while (rcv->byte);
6538c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_CHECKSUM;
6548c2ecf20Sopenharmony_ci		fallthrough;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	case PLIP_PK_CHECKSUM:
6578c2ecf20Sopenharmony_ci		if (plip_receive(nibble_timeout, dev,
6588c2ecf20Sopenharmony_ci				 &rcv->nibble, &rcv->data))
6598c2ecf20Sopenharmony_ci			return TIMEOUT;
6608c2ecf20Sopenharmony_ci		if (rcv->data != rcv->checksum) {
6618c2ecf20Sopenharmony_ci			dev->stats.rx_crc_errors++;
6628c2ecf20Sopenharmony_ci			if (net_debug)
6638c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "%s: checksum error\n", dev->name);
6648c2ecf20Sopenharmony_ci			return ERROR;
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_DONE;
6678c2ecf20Sopenharmony_ci		fallthrough;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	case PLIP_PK_DONE:
6708c2ecf20Sopenharmony_ci		/* Inform the upper layer for the arrival of a packet. */
6718c2ecf20Sopenharmony_ci		rcv->skb->protocol=plip_type_trans(rcv->skb, dev);
6728c2ecf20Sopenharmony_ci		netif_rx_ni(rcv->skb);
6738c2ecf20Sopenharmony_ci		dev->stats.rx_bytes += rcv->length.h;
6748c2ecf20Sopenharmony_ci		dev->stats.rx_packets++;
6758c2ecf20Sopenharmony_ci		rcv->skb = NULL;
6768c2ecf20Sopenharmony_ci		if (net_debug > 2)
6778c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: receive end\n", dev->name);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		/* Close the connection. */
6808c2ecf20Sopenharmony_ci		write_data (dev, 0x00);
6818c2ecf20Sopenharmony_ci		spin_lock_irq(&nl->lock);
6828c2ecf20Sopenharmony_ci		if (snd->state != PLIP_PK_DONE) {
6838c2ecf20Sopenharmony_ci			nl->connection = PLIP_CN_SEND;
6848c2ecf20Sopenharmony_ci			spin_unlock_irq(&nl->lock);
6858c2ecf20Sopenharmony_ci			schedule_work(&nl->immediate);
6868c2ecf20Sopenharmony_ci			enable_parport_interrupts (dev);
6878c2ecf20Sopenharmony_ci			ENABLE(dev->irq);
6888c2ecf20Sopenharmony_ci			return OK;
6898c2ecf20Sopenharmony_ci		} else {
6908c2ecf20Sopenharmony_ci			nl->connection = PLIP_CN_NONE;
6918c2ecf20Sopenharmony_ci			spin_unlock_irq(&nl->lock);
6928c2ecf20Sopenharmony_ci			enable_parport_interrupts (dev);
6938c2ecf20Sopenharmony_ci			ENABLE(dev->irq);
6948c2ecf20Sopenharmony_ci			return OK;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci	return OK;
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci/* PLIP_SEND --- send a byte (two nibbles)
7018c2ecf20Sopenharmony_ci   Returns OK on success, TIMEOUT when timeout    */
7028c2ecf20Sopenharmony_cistatic inline int
7038c2ecf20Sopenharmony_ciplip_send(unsigned short nibble_timeout, struct net_device *dev,
7048c2ecf20Sopenharmony_ci	  enum plip_nibble_state *ns_p, unsigned char data)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	unsigned char c0;
7078c2ecf20Sopenharmony_ci	unsigned int cx;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	switch (*ns_p) {
7108c2ecf20Sopenharmony_ci	case PLIP_NB_BEGIN:
7118c2ecf20Sopenharmony_ci		write_data (dev, data & 0x0f);
7128c2ecf20Sopenharmony_ci		*ns_p = PLIP_NB_1;
7138c2ecf20Sopenharmony_ci		fallthrough;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	case PLIP_NB_1:
7168c2ecf20Sopenharmony_ci		write_data (dev, 0x10 | (data & 0x0f));
7178c2ecf20Sopenharmony_ci		cx = nibble_timeout;
7188c2ecf20Sopenharmony_ci		while (1) {
7198c2ecf20Sopenharmony_ci			c0 = read_status(dev);
7208c2ecf20Sopenharmony_ci			if ((c0 & 0x80) == 0)
7218c2ecf20Sopenharmony_ci				break;
7228c2ecf20Sopenharmony_ci			if (--cx == 0)
7238c2ecf20Sopenharmony_ci				return TIMEOUT;
7248c2ecf20Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
7258c2ecf20Sopenharmony_ci		}
7268c2ecf20Sopenharmony_ci		write_data (dev, 0x10 | (data >> 4));
7278c2ecf20Sopenharmony_ci		*ns_p = PLIP_NB_2;
7288c2ecf20Sopenharmony_ci		fallthrough;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	case PLIP_NB_2:
7318c2ecf20Sopenharmony_ci		write_data (dev, (data >> 4));
7328c2ecf20Sopenharmony_ci		cx = nibble_timeout;
7338c2ecf20Sopenharmony_ci		while (1) {
7348c2ecf20Sopenharmony_ci			c0 = read_status(dev);
7358c2ecf20Sopenharmony_ci			if (c0 & 0x80)
7368c2ecf20Sopenharmony_ci				break;
7378c2ecf20Sopenharmony_ci			if (--cx == 0)
7388c2ecf20Sopenharmony_ci				return TIMEOUT;
7398c2ecf20Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
7408c2ecf20Sopenharmony_ci		}
7418c2ecf20Sopenharmony_ci		*ns_p = PLIP_NB_BEGIN;
7428c2ecf20Sopenharmony_ci		return OK;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci	return OK;
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci/* PLIP_SEND_PACKET --- send a packet */
7488c2ecf20Sopenharmony_cistatic int
7498c2ecf20Sopenharmony_ciplip_send_packet(struct net_device *dev, struct net_local *nl,
7508c2ecf20Sopenharmony_ci		 struct plip_local *snd, struct plip_local *rcv)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	unsigned short nibble_timeout = nl->nibble;
7538c2ecf20Sopenharmony_ci	unsigned char *lbuf;
7548c2ecf20Sopenharmony_ci	unsigned char c0;
7558c2ecf20Sopenharmony_ci	unsigned int cx;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) {
7588c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: send skb lost\n", dev->name);
7598c2ecf20Sopenharmony_ci		snd->state = PLIP_PK_DONE;
7608c2ecf20Sopenharmony_ci		snd->skb = NULL;
7618c2ecf20Sopenharmony_ci		return ERROR;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	switch (snd->state) {
7658c2ecf20Sopenharmony_ci	case PLIP_PK_TRIGGER:
7668c2ecf20Sopenharmony_ci		if ((read_status(dev) & 0xf8) != 0x80)
7678c2ecf20Sopenharmony_ci			return HS_TIMEOUT;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		/* Trigger remote rx interrupt. */
7708c2ecf20Sopenharmony_ci		write_data (dev, 0x08);
7718c2ecf20Sopenharmony_ci		cx = nl->trigger;
7728c2ecf20Sopenharmony_ci		while (1) {
7738c2ecf20Sopenharmony_ci			udelay(PLIP_DELAY_UNIT);
7748c2ecf20Sopenharmony_ci			spin_lock_irq(&nl->lock);
7758c2ecf20Sopenharmony_ci			if (nl->connection == PLIP_CN_RECEIVE) {
7768c2ecf20Sopenharmony_ci				spin_unlock_irq(&nl->lock);
7778c2ecf20Sopenharmony_ci				/* Interrupted. */
7788c2ecf20Sopenharmony_ci				dev->stats.collisions++;
7798c2ecf20Sopenharmony_ci				return OK;
7808c2ecf20Sopenharmony_ci			}
7818c2ecf20Sopenharmony_ci			c0 = read_status(dev);
7828c2ecf20Sopenharmony_ci			if (c0 & 0x08) {
7838c2ecf20Sopenharmony_ci				spin_unlock_irq(&nl->lock);
7848c2ecf20Sopenharmony_ci				DISABLE(dev->irq);
7858c2ecf20Sopenharmony_ci				synchronize_irq(dev->irq);
7868c2ecf20Sopenharmony_ci				if (nl->connection == PLIP_CN_RECEIVE) {
7878c2ecf20Sopenharmony_ci					/* Interrupted.
7888c2ecf20Sopenharmony_ci					   We don't need to enable irq,
7898c2ecf20Sopenharmony_ci					   as it is soon disabled.    */
7908c2ecf20Sopenharmony_ci					/* Yes, we do. New variant of
7918c2ecf20Sopenharmony_ci					   {enable,disable}_irq *counts*
7928c2ecf20Sopenharmony_ci					   them.  -- AV  */
7938c2ecf20Sopenharmony_ci					ENABLE(dev->irq);
7948c2ecf20Sopenharmony_ci					dev->stats.collisions++;
7958c2ecf20Sopenharmony_ci					return OK;
7968c2ecf20Sopenharmony_ci				}
7978c2ecf20Sopenharmony_ci				disable_parport_interrupts (dev);
7988c2ecf20Sopenharmony_ci				if (net_debug > 2)
7998c2ecf20Sopenharmony_ci					printk(KERN_DEBUG "%s: send start\n", dev->name);
8008c2ecf20Sopenharmony_ci				snd->state = PLIP_PK_LENGTH_LSB;
8018c2ecf20Sopenharmony_ci				snd->nibble = PLIP_NB_BEGIN;
8028c2ecf20Sopenharmony_ci				nl->timeout_count = 0;
8038c2ecf20Sopenharmony_ci				break;
8048c2ecf20Sopenharmony_ci			}
8058c2ecf20Sopenharmony_ci			spin_unlock_irq(&nl->lock);
8068c2ecf20Sopenharmony_ci			if (--cx == 0) {
8078c2ecf20Sopenharmony_ci				write_data (dev, 0x00);
8088c2ecf20Sopenharmony_ci				return HS_TIMEOUT;
8098c2ecf20Sopenharmony_ci			}
8108c2ecf20Sopenharmony_ci		}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	case PLIP_PK_LENGTH_LSB:
8138c2ecf20Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
8148c2ecf20Sopenharmony_ci			      &snd->nibble, snd->length.b.lsb))
8158c2ecf20Sopenharmony_ci			return TIMEOUT;
8168c2ecf20Sopenharmony_ci		snd->state = PLIP_PK_LENGTH_MSB;
8178c2ecf20Sopenharmony_ci		fallthrough;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	case PLIP_PK_LENGTH_MSB:
8208c2ecf20Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
8218c2ecf20Sopenharmony_ci			      &snd->nibble, snd->length.b.msb))
8228c2ecf20Sopenharmony_ci			return TIMEOUT;
8238c2ecf20Sopenharmony_ci		snd->state = PLIP_PK_DATA;
8248c2ecf20Sopenharmony_ci		snd->byte = 0;
8258c2ecf20Sopenharmony_ci		snd->checksum = 0;
8268c2ecf20Sopenharmony_ci		fallthrough;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	case PLIP_PK_DATA:
8298c2ecf20Sopenharmony_ci		do {
8308c2ecf20Sopenharmony_ci			if (plip_send(nibble_timeout, dev,
8318c2ecf20Sopenharmony_ci				      &snd->nibble, lbuf[snd->byte]))
8328c2ecf20Sopenharmony_ci				return TIMEOUT;
8338c2ecf20Sopenharmony_ci		} while (++snd->byte < snd->length.h);
8348c2ecf20Sopenharmony_ci		do {
8358c2ecf20Sopenharmony_ci			snd->checksum += lbuf[--snd->byte];
8368c2ecf20Sopenharmony_ci		} while (snd->byte);
8378c2ecf20Sopenharmony_ci		snd->state = PLIP_PK_CHECKSUM;
8388c2ecf20Sopenharmony_ci		fallthrough;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	case PLIP_PK_CHECKSUM:
8418c2ecf20Sopenharmony_ci		if (plip_send(nibble_timeout, dev,
8428c2ecf20Sopenharmony_ci			      &snd->nibble, snd->checksum))
8438c2ecf20Sopenharmony_ci			return TIMEOUT;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci		dev->stats.tx_bytes += snd->skb->len;
8468c2ecf20Sopenharmony_ci		dev_kfree_skb(snd->skb);
8478c2ecf20Sopenharmony_ci		dev->stats.tx_packets++;
8488c2ecf20Sopenharmony_ci		snd->state = PLIP_PK_DONE;
8498c2ecf20Sopenharmony_ci		fallthrough;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	case PLIP_PK_DONE:
8528c2ecf20Sopenharmony_ci		/* Close the connection */
8538c2ecf20Sopenharmony_ci		write_data (dev, 0x00);
8548c2ecf20Sopenharmony_ci		snd->skb = NULL;
8558c2ecf20Sopenharmony_ci		if (net_debug > 2)
8568c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: send end\n", dev->name);
8578c2ecf20Sopenharmony_ci		nl->connection = PLIP_CN_CLOSING;
8588c2ecf20Sopenharmony_ci		nl->is_deferred = 1;
8598c2ecf20Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
8608c2ecf20Sopenharmony_ci		enable_parport_interrupts (dev);
8618c2ecf20Sopenharmony_ci		ENABLE(dev->irq);
8628c2ecf20Sopenharmony_ci		return OK;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci	return OK;
8658c2ecf20Sopenharmony_ci}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_cistatic int
8688c2ecf20Sopenharmony_ciplip_connection_close(struct net_device *dev, struct net_local *nl,
8698c2ecf20Sopenharmony_ci		      struct plip_local *snd, struct plip_local *rcv)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	spin_lock_irq(&nl->lock);
8728c2ecf20Sopenharmony_ci	if (nl->connection == PLIP_CN_CLOSING) {
8738c2ecf20Sopenharmony_ci		nl->connection = PLIP_CN_NONE;
8748c2ecf20Sopenharmony_ci		netif_wake_queue (dev);
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci	spin_unlock_irq(&nl->lock);
8778c2ecf20Sopenharmony_ci	if (nl->should_relinquish) {
8788c2ecf20Sopenharmony_ci		nl->should_relinquish = nl->port_owner = 0;
8798c2ecf20Sopenharmony_ci		parport_release(nl->pardev);
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci	return OK;
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci/* PLIP_ERROR --- wait till other end settled */
8858c2ecf20Sopenharmony_cistatic int
8868c2ecf20Sopenharmony_ciplip_error(struct net_device *dev, struct net_local *nl,
8878c2ecf20Sopenharmony_ci	   struct plip_local *snd, struct plip_local *rcv)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	unsigned char status;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	status = read_status(dev);
8928c2ecf20Sopenharmony_ci	if ((status & 0xf8) == 0x80) {
8938c2ecf20Sopenharmony_ci		if (net_debug > 2)
8948c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: reset interface.\n", dev->name);
8958c2ecf20Sopenharmony_ci		nl->connection = PLIP_CN_NONE;
8968c2ecf20Sopenharmony_ci		nl->should_relinquish = 0;
8978c2ecf20Sopenharmony_ci		netif_start_queue (dev);
8988c2ecf20Sopenharmony_ci		enable_parport_interrupts (dev);
8998c2ecf20Sopenharmony_ci		ENABLE(dev->irq);
9008c2ecf20Sopenharmony_ci		netif_wake_queue (dev);
9018c2ecf20Sopenharmony_ci	} else {
9028c2ecf20Sopenharmony_ci		nl->is_deferred = 1;
9038c2ecf20Sopenharmony_ci		schedule_delayed_work(&nl->deferred, 1);
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	return OK;
9078c2ecf20Sopenharmony_ci}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci/* Handle the parallel port interrupts. */
9108c2ecf20Sopenharmony_cistatic void
9118c2ecf20Sopenharmony_ciplip_interrupt(void *dev_id)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
9148c2ecf20Sopenharmony_ci	struct net_local *nl;
9158c2ecf20Sopenharmony_ci	struct plip_local *rcv;
9168c2ecf20Sopenharmony_ci	unsigned char c0;
9178c2ecf20Sopenharmony_ci	unsigned long flags;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	nl = netdev_priv(dev);
9208c2ecf20Sopenharmony_ci	rcv = &nl->rcv_data;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	spin_lock_irqsave (&nl->lock, flags);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	c0 = read_status(dev);
9258c2ecf20Sopenharmony_ci	if ((c0 & 0xf8) != 0xc0) {
9268c2ecf20Sopenharmony_ci		if ((dev->irq != -1) && (net_debug > 1))
9278c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: spurious interrupt\n", dev->name);
9288c2ecf20Sopenharmony_ci		spin_unlock_irqrestore (&nl->lock, flags);
9298c2ecf20Sopenharmony_ci		return;
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	if (net_debug > 3)
9338c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: interrupt.\n", dev->name);
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	switch (nl->connection) {
9368c2ecf20Sopenharmony_ci	case PLIP_CN_CLOSING:
9378c2ecf20Sopenharmony_ci		netif_wake_queue (dev);
9388c2ecf20Sopenharmony_ci		fallthrough;
9398c2ecf20Sopenharmony_ci	case PLIP_CN_NONE:
9408c2ecf20Sopenharmony_ci	case PLIP_CN_SEND:
9418c2ecf20Sopenharmony_ci		rcv->state = PLIP_PK_TRIGGER;
9428c2ecf20Sopenharmony_ci		nl->connection = PLIP_CN_RECEIVE;
9438c2ecf20Sopenharmony_ci		nl->timeout_count = 0;
9448c2ecf20Sopenharmony_ci		schedule_work(&nl->immediate);
9458c2ecf20Sopenharmony_ci		break;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	case PLIP_CN_RECEIVE:
9488c2ecf20Sopenharmony_ci		/* May occur because there is race condition
9498c2ecf20Sopenharmony_ci		   around test and set of dev->interrupt.
9508c2ecf20Sopenharmony_ci		   Ignore this interrupt. */
9518c2ecf20Sopenharmony_ci		break;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	case PLIP_CN_ERROR:
9548c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: receive interrupt in error state\n", dev->name);
9558c2ecf20Sopenharmony_ci		break;
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nl->lock, flags);
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic netdev_tx_t
9628c2ecf20Sopenharmony_ciplip_tx_packet(struct sk_buff *skb, struct net_device *dev)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
9658c2ecf20Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev))
9688c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	/* We may need to grab the bus */
9718c2ecf20Sopenharmony_ci	if (!nl->port_owner) {
9728c2ecf20Sopenharmony_ci		if (parport_claim(nl->pardev))
9738c2ecf20Sopenharmony_ci			return NETDEV_TX_BUSY;
9748c2ecf20Sopenharmony_ci		nl->port_owner = 1;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	netif_stop_queue (dev);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (skb->len > dev->mtu + dev->hard_header_len) {
9808c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len);
9818c2ecf20Sopenharmony_ci		netif_start_queue (dev);
9828c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	if (net_debug > 2)
9868c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: send request\n", dev->name);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	spin_lock_irq(&nl->lock);
9898c2ecf20Sopenharmony_ci	snd->skb = skb;
9908c2ecf20Sopenharmony_ci	snd->length.h = skb->len;
9918c2ecf20Sopenharmony_ci	snd->state = PLIP_PK_TRIGGER;
9928c2ecf20Sopenharmony_ci	if (nl->connection == PLIP_CN_NONE) {
9938c2ecf20Sopenharmony_ci		nl->connection = PLIP_CN_SEND;
9948c2ecf20Sopenharmony_ci		nl->timeout_count = 0;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	schedule_work(&nl->immediate);
9978c2ecf20Sopenharmony_ci	spin_unlock_irq(&nl->lock);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic void
10038c2ecf20Sopenharmony_ciplip_rewrite_address(const struct net_device *dev, struct ethhdr *eth)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	const struct in_device *in_dev;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	rcu_read_lock();
10088c2ecf20Sopenharmony_ci	in_dev = __in_dev_get_rcu(dev);
10098c2ecf20Sopenharmony_ci	if (in_dev) {
10108c2ecf20Sopenharmony_ci		/* Any address will do - we take the first */
10118c2ecf20Sopenharmony_ci		const struct in_ifaddr *ifa = rcu_dereference(in_dev->ifa_list);
10128c2ecf20Sopenharmony_ci		if (ifa) {
10138c2ecf20Sopenharmony_ci			memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
10148c2ecf20Sopenharmony_ci			memset(eth->h_dest, 0xfc, 2);
10158c2ecf20Sopenharmony_ci			memcpy(eth->h_dest+2, &ifa->ifa_address, 4);
10168c2ecf20Sopenharmony_ci		}
10178c2ecf20Sopenharmony_ci	}
10188c2ecf20Sopenharmony_ci	rcu_read_unlock();
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic int
10228c2ecf20Sopenharmony_ciplip_hard_header(struct sk_buff *skb, struct net_device *dev,
10238c2ecf20Sopenharmony_ci		 unsigned short type, const void *daddr,
10248c2ecf20Sopenharmony_ci		 const void *saddr, unsigned len)
10258c2ecf20Sopenharmony_ci{
10268c2ecf20Sopenharmony_ci	int ret;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	ret = eth_header(skb, dev, type, daddr, saddr, len);
10298c2ecf20Sopenharmony_ci	if (ret >= 0)
10308c2ecf20Sopenharmony_ci		plip_rewrite_address (dev, (struct ethhdr *)skb->data);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	return ret;
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic int plip_hard_header_cache(const struct neighbour *neigh,
10368c2ecf20Sopenharmony_ci				  struct hh_cache *hh, __be16 type)
10378c2ecf20Sopenharmony_ci{
10388c2ecf20Sopenharmony_ci	int ret;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	ret = eth_header_cache(neigh, hh, type);
10418c2ecf20Sopenharmony_ci	if (ret == 0) {
10428c2ecf20Sopenharmony_ci		struct ethhdr *eth;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci		eth = (struct ethhdr*)(((u8*)hh->hh_data) +
10458c2ecf20Sopenharmony_ci				       HH_DATA_OFF(sizeof(*eth)));
10468c2ecf20Sopenharmony_ci		plip_rewrite_address (neigh->dev, eth);
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	return ret;
10508c2ecf20Sopenharmony_ci}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci/* Open/initialize the board.  This is called (in the current kernel)
10538c2ecf20Sopenharmony_ci   sometime after booting when the 'ifconfig' program is run.
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci   This routine gets exclusive access to the parallel port by allocating
10568c2ecf20Sopenharmony_ci   its IRQ line.
10578c2ecf20Sopenharmony_ci */
10588c2ecf20Sopenharmony_cistatic int
10598c2ecf20Sopenharmony_ciplip_open(struct net_device *dev)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
10628c2ecf20Sopenharmony_ci	struct in_device *in_dev;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	/* Grab the port */
10658c2ecf20Sopenharmony_ci	if (!nl->port_owner) {
10668c2ecf20Sopenharmony_ci		if (parport_claim(nl->pardev)) return -EAGAIN;
10678c2ecf20Sopenharmony_ci		nl->port_owner = 1;
10688c2ecf20Sopenharmony_ci	}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	nl->should_relinquish = 0;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/* Clear the data port. */
10738c2ecf20Sopenharmony_ci	write_data (dev, 0x00);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	/* Enable rx interrupt. */
10768c2ecf20Sopenharmony_ci	enable_parport_interrupts (dev);
10778c2ecf20Sopenharmony_ci	if (dev->irq == -1)
10788c2ecf20Sopenharmony_ci	{
10798c2ecf20Sopenharmony_ci		atomic_set (&nl->kill_timer, 0);
10808c2ecf20Sopenharmony_ci		schedule_delayed_work(&nl->timer, 1);
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/* Initialize the state machine. */
10848c2ecf20Sopenharmony_ci	nl->rcv_data.state = nl->snd_data.state = PLIP_PK_DONE;
10858c2ecf20Sopenharmony_ci	nl->rcv_data.skb = nl->snd_data.skb = NULL;
10868c2ecf20Sopenharmony_ci	nl->connection = PLIP_CN_NONE;
10878c2ecf20Sopenharmony_ci	nl->is_deferred = 0;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* Fill in the MAC-level header.
10908c2ecf20Sopenharmony_ci	   We used to abuse dev->broadcast to store the point-to-point
10918c2ecf20Sopenharmony_ci	   MAC address, but we no longer do it. Instead, we fetch the
10928c2ecf20Sopenharmony_ci	   interface address whenever it is needed, which is cheap enough
10938c2ecf20Sopenharmony_ci	   because we use the hh_cache. Actually, abusing dev->broadcast
10948c2ecf20Sopenharmony_ci	   didn't work, because when using plip_open the point-to-point
10958c2ecf20Sopenharmony_ci	   address isn't yet known.
10968c2ecf20Sopenharmony_ci	   PLIP doesn't have a real MAC address, but we need it to be
10978c2ecf20Sopenharmony_ci	   DOS compatible, and to properly support taps (otherwise,
10988c2ecf20Sopenharmony_ci	   when the device address isn't identical to the address of a
10998c2ecf20Sopenharmony_ci	   received frame, the kernel incorrectly drops it).             */
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	in_dev=__in_dev_get_rtnl(dev);
11028c2ecf20Sopenharmony_ci	if (in_dev) {
11038c2ecf20Sopenharmony_ci		/* Any address will do - we take the first. We already
11048c2ecf20Sopenharmony_ci		   have the first two bytes filled with 0xfc, from
11058c2ecf20Sopenharmony_ci		   plip_init_dev(). */
11068c2ecf20Sopenharmony_ci		const struct in_ifaddr *ifa = rtnl_dereference(in_dev->ifa_list);
11078c2ecf20Sopenharmony_ci		if (ifa != NULL) {
11088c2ecf20Sopenharmony_ci			memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
11098c2ecf20Sopenharmony_ci		}
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	netif_start_queue (dev);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	return 0;
11158c2ecf20Sopenharmony_ci}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci/* The inverse routine to plip_open (). */
11188c2ecf20Sopenharmony_cistatic int
11198c2ecf20Sopenharmony_ciplip_close(struct net_device *dev)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
11228c2ecf20Sopenharmony_ci	struct plip_local *snd = &nl->snd_data;
11238c2ecf20Sopenharmony_ci	struct plip_local *rcv = &nl->rcv_data;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	netif_stop_queue (dev);
11268c2ecf20Sopenharmony_ci	DISABLE(dev->irq);
11278c2ecf20Sopenharmony_ci	synchronize_irq(dev->irq);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if (dev->irq == -1)
11308c2ecf20Sopenharmony_ci	{
11318c2ecf20Sopenharmony_ci		init_completion(&nl->killed_timer_cmp);
11328c2ecf20Sopenharmony_ci		atomic_set (&nl->kill_timer, 1);
11338c2ecf20Sopenharmony_ci		wait_for_completion(&nl->killed_timer_cmp);
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci#ifdef NOTDEF
11378c2ecf20Sopenharmony_ci	outb(0x00, PAR_DATA(dev));
11388c2ecf20Sopenharmony_ci#endif
11398c2ecf20Sopenharmony_ci	nl->is_deferred = 0;
11408c2ecf20Sopenharmony_ci	nl->connection = PLIP_CN_NONE;
11418c2ecf20Sopenharmony_ci	if (nl->port_owner) {
11428c2ecf20Sopenharmony_ci		parport_release(nl->pardev);
11438c2ecf20Sopenharmony_ci		nl->port_owner = 0;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	snd->state = PLIP_PK_DONE;
11478c2ecf20Sopenharmony_ci	if (snd->skb) {
11488c2ecf20Sopenharmony_ci		dev_kfree_skb(snd->skb);
11498c2ecf20Sopenharmony_ci		snd->skb = NULL;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci	rcv->state = PLIP_PK_DONE;
11528c2ecf20Sopenharmony_ci	if (rcv->skb) {
11538c2ecf20Sopenharmony_ci		kfree_skb(rcv->skb);
11548c2ecf20Sopenharmony_ci		rcv->skb = NULL;
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci#ifdef NOTDEF
11588c2ecf20Sopenharmony_ci	/* Reset. */
11598c2ecf20Sopenharmony_ci	outb(0x00, PAR_CONTROL(dev));
11608c2ecf20Sopenharmony_ci#endif
11618c2ecf20Sopenharmony_ci	return 0;
11628c2ecf20Sopenharmony_ci}
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cistatic int
11658c2ecf20Sopenharmony_ciplip_preempt(void *handle)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct net_device *dev = (struct net_device *)handle;
11688c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	/* Stand our ground if a datagram is on the wire */
11718c2ecf20Sopenharmony_ci	if (nl->connection != PLIP_CN_NONE) {
11728c2ecf20Sopenharmony_ci		nl->should_relinquish = 1;
11738c2ecf20Sopenharmony_ci		return 1;
11748c2ecf20Sopenharmony_ci	}
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	nl->port_owner = 0;	/* Remember that we released the bus */
11778c2ecf20Sopenharmony_ci	return 0;
11788c2ecf20Sopenharmony_ci}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_cistatic void
11818c2ecf20Sopenharmony_ciplip_wakeup(void *handle)
11828c2ecf20Sopenharmony_ci{
11838c2ecf20Sopenharmony_ci	struct net_device *dev = (struct net_device *)handle;
11848c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	if (nl->port_owner) {
11878c2ecf20Sopenharmony_ci		/* Why are we being woken up? */
11888c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: why am I being woken up?\n", dev->name);
11898c2ecf20Sopenharmony_ci		if (!parport_claim(nl->pardev))
11908c2ecf20Sopenharmony_ci			/* bus_owner is already set (but why?) */
11918c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: I'm broken.\n", dev->name);
11928c2ecf20Sopenharmony_ci		else
11938c2ecf20Sopenharmony_ci			return;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	if (!(dev->flags & IFF_UP))
11978c2ecf20Sopenharmony_ci		/* Don't need the port when the interface is down */
11988c2ecf20Sopenharmony_ci		return;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	if (!parport_claim(nl->pardev)) {
12018c2ecf20Sopenharmony_ci		nl->port_owner = 1;
12028c2ecf20Sopenharmony_ci		/* Clear the data port. */
12038c2ecf20Sopenharmony_ci		write_data (dev, 0x00);
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_cistatic int
12088c2ecf20Sopenharmony_ciplip_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	struct net_local *nl = netdev_priv(dev);
12118c2ecf20Sopenharmony_ci	struct plipconf *pc = (struct plipconf *) &rq->ifr_ifru;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	if (cmd != SIOCDEVPLIP)
12148c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	switch(pc->pcmd) {
12178c2ecf20Sopenharmony_ci	case PLIP_GET_TIMEOUT:
12188c2ecf20Sopenharmony_ci		pc->trigger = nl->trigger;
12198c2ecf20Sopenharmony_ci		pc->nibble  = nl->nibble;
12208c2ecf20Sopenharmony_ci		break;
12218c2ecf20Sopenharmony_ci	case PLIP_SET_TIMEOUT:
12228c2ecf20Sopenharmony_ci		if(!capable(CAP_NET_ADMIN))
12238c2ecf20Sopenharmony_ci			return -EPERM;
12248c2ecf20Sopenharmony_ci		nl->trigger = pc->trigger;
12258c2ecf20Sopenharmony_ci		nl->nibble  = pc->nibble;
12268c2ecf20Sopenharmony_ci		break;
12278c2ecf20Sopenharmony_ci	default:
12288c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci	return 0;
12318c2ecf20Sopenharmony_ci}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic int parport[PLIP_MAX] = { [0 ... PLIP_MAX-1] = -1 };
12348c2ecf20Sopenharmony_cistatic int timid;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cimodule_param_array(parport, int, NULL, 0);
12378c2ecf20Sopenharmony_cimodule_param(timid, int, 0);
12388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(parport, "List of parport device numbers to use by plip");
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic struct net_device *dev_plip[PLIP_MAX] = { NULL, };
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_cistatic inline int
12438c2ecf20Sopenharmony_ciplip_searchfor(int list[], int a)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	int i;
12468c2ecf20Sopenharmony_ci	for (i = 0; i < PLIP_MAX && list[i] != -1; i++) {
12478c2ecf20Sopenharmony_ci		if (list[i] == a) return 1;
12488c2ecf20Sopenharmony_ci	}
12498c2ecf20Sopenharmony_ci	return 0;
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci/* plip_attach() is called (by the parport code) when a port is
12538c2ecf20Sopenharmony_ci * available to use. */
12548c2ecf20Sopenharmony_cistatic void plip_attach (struct parport *port)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	static int unit;
12578c2ecf20Sopenharmony_ci	struct net_device *dev;
12588c2ecf20Sopenharmony_ci	struct net_local *nl;
12598c2ecf20Sopenharmony_ci	char name[IFNAMSIZ];
12608c2ecf20Sopenharmony_ci	struct pardev_cb plip_cb;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if ((parport[0] == -1 && (!timid || !port->devices)) ||
12638c2ecf20Sopenharmony_ci	    plip_searchfor(parport, port->number)) {
12648c2ecf20Sopenharmony_ci		if (unit == PLIP_MAX) {
12658c2ecf20Sopenharmony_ci			printk(KERN_ERR "plip: too many devices\n");
12668c2ecf20Sopenharmony_ci			return;
12678c2ecf20Sopenharmony_ci		}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci		sprintf(name, "plip%d", unit);
12708c2ecf20Sopenharmony_ci		dev = alloc_etherdev(sizeof(struct net_local));
12718c2ecf20Sopenharmony_ci		if (!dev)
12728c2ecf20Sopenharmony_ci			return;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci		strcpy(dev->name, name);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci		dev->irq = port->irq;
12778c2ecf20Sopenharmony_ci		dev->base_addr = port->base;
12788c2ecf20Sopenharmony_ci		if (port->irq == -1) {
12798c2ecf20Sopenharmony_ci			printk(KERN_INFO "plip: %s has no IRQ. Using IRQ-less mode,"
12808c2ecf20Sopenharmony_ci		                 "which is fairly inefficient!\n", port->name);
12818c2ecf20Sopenharmony_ci		}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		nl = netdev_priv(dev);
12848c2ecf20Sopenharmony_ci		nl->dev = dev;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		memset(&plip_cb, 0, sizeof(plip_cb));
12878c2ecf20Sopenharmony_ci		plip_cb.private = dev;
12888c2ecf20Sopenharmony_ci		plip_cb.preempt = plip_preempt;
12898c2ecf20Sopenharmony_ci		plip_cb.wakeup = plip_wakeup;
12908c2ecf20Sopenharmony_ci		plip_cb.irq_func = plip_interrupt;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci		nl->pardev = parport_register_dev_model(port, dev->name,
12938c2ecf20Sopenharmony_ci							&plip_cb, unit);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci		if (!nl->pardev) {
12968c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: parport_register failed\n", name);
12978c2ecf20Sopenharmony_ci			goto err_free_dev;
12988c2ecf20Sopenharmony_ci		}
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		plip_init_netdev(dev);
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci		if (register_netdev(dev)) {
13038c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: network register failed\n", name);
13048c2ecf20Sopenharmony_ci			goto err_parport_unregister;
13058c2ecf20Sopenharmony_ci		}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s", version);
13088c2ecf20Sopenharmony_ci		if (dev->irq != -1)
13098c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: Parallel port at %#3lx, "
13108c2ecf20Sopenharmony_ci					 "using IRQ %d.\n",
13118c2ecf20Sopenharmony_ci				         dev->name, dev->base_addr, dev->irq);
13128c2ecf20Sopenharmony_ci		else
13138c2ecf20Sopenharmony_ci			printk(KERN_INFO "%s: Parallel port at %#3lx, "
13148c2ecf20Sopenharmony_ci					 "not using IRQ.\n",
13158c2ecf20Sopenharmony_ci					 dev->name, dev->base_addr);
13168c2ecf20Sopenharmony_ci		dev_plip[unit++] = dev;
13178c2ecf20Sopenharmony_ci	}
13188c2ecf20Sopenharmony_ci	return;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_cierr_parport_unregister:
13218c2ecf20Sopenharmony_ci	parport_unregister_device(nl->pardev);
13228c2ecf20Sopenharmony_cierr_free_dev:
13238c2ecf20Sopenharmony_ci	free_netdev(dev);
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci/* plip_detach() is called (by the parport code) when a port is
13278c2ecf20Sopenharmony_ci * no longer available to use. */
13288c2ecf20Sopenharmony_cistatic void plip_detach (struct parport *port)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	/* Nothing to do */
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_cistatic int plip_probe(struct pardevice *par_dev)
13348c2ecf20Sopenharmony_ci{
13358c2ecf20Sopenharmony_ci	struct device_driver *drv = par_dev->dev.driver;
13368c2ecf20Sopenharmony_ci	int len = strlen(drv->name);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	if (strncmp(par_dev->name, drv->name, len))
13398c2ecf20Sopenharmony_ci		return -ENODEV;
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	return 0;
13428c2ecf20Sopenharmony_ci}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cistatic struct parport_driver plip_driver = {
13458c2ecf20Sopenharmony_ci	.name		= "plip",
13468c2ecf20Sopenharmony_ci	.probe		= plip_probe,
13478c2ecf20Sopenharmony_ci	.match_port	= plip_attach,
13488c2ecf20Sopenharmony_ci	.detach		= plip_detach,
13498c2ecf20Sopenharmony_ci	.devmodel	= true,
13508c2ecf20Sopenharmony_ci};
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_cistatic void __exit plip_cleanup_module (void)
13538c2ecf20Sopenharmony_ci{
13548c2ecf20Sopenharmony_ci	struct net_device *dev;
13558c2ecf20Sopenharmony_ci	int i;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	for (i=0; i < PLIP_MAX; i++) {
13588c2ecf20Sopenharmony_ci		if ((dev = dev_plip[i])) {
13598c2ecf20Sopenharmony_ci			struct net_local *nl = netdev_priv(dev);
13608c2ecf20Sopenharmony_ci			unregister_netdev(dev);
13618c2ecf20Sopenharmony_ci			if (nl->port_owner)
13628c2ecf20Sopenharmony_ci				parport_release(nl->pardev);
13638c2ecf20Sopenharmony_ci			parport_unregister_device(nl->pardev);
13648c2ecf20Sopenharmony_ci			free_netdev(dev);
13658c2ecf20Sopenharmony_ci			dev_plip[i] = NULL;
13668c2ecf20Sopenharmony_ci		}
13678c2ecf20Sopenharmony_ci	}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	parport_unregister_driver(&plip_driver);
13708c2ecf20Sopenharmony_ci}
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci#ifndef MODULE
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic int parport_ptr;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_cistatic int __init plip_setup(char *str)
13778c2ecf20Sopenharmony_ci{
13788c2ecf20Sopenharmony_ci	int ints[4];
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	str = get_options(str, ARRAY_SIZE(ints), ints);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	/* Ugh. */
13838c2ecf20Sopenharmony_ci	if (!strncmp(str, "parport", 7)) {
13848c2ecf20Sopenharmony_ci		int n = simple_strtoul(str+7, NULL, 10);
13858c2ecf20Sopenharmony_ci		if (parport_ptr < PLIP_MAX)
13868c2ecf20Sopenharmony_ci			parport[parport_ptr++] = n;
13878c2ecf20Sopenharmony_ci		else
13888c2ecf20Sopenharmony_ci			printk(KERN_INFO "plip: too many ports, %s ignored.\n",
13898c2ecf20Sopenharmony_ci			       str);
13908c2ecf20Sopenharmony_ci	} else if (!strcmp(str, "timid")) {
13918c2ecf20Sopenharmony_ci		timid = 1;
13928c2ecf20Sopenharmony_ci	} else {
13938c2ecf20Sopenharmony_ci		if (ints[0] == 0 || ints[1] == 0) {
13948c2ecf20Sopenharmony_ci			/* disable driver on "plip=" or "plip=0" */
13958c2ecf20Sopenharmony_ci			parport[0] = -2;
13968c2ecf20Sopenharmony_ci		} else {
13978c2ecf20Sopenharmony_ci			printk(KERN_WARNING "warning: 'plip=0x%x' ignored\n",
13988c2ecf20Sopenharmony_ci			       ints[1]);
13998c2ecf20Sopenharmony_ci		}
14008c2ecf20Sopenharmony_ci	}
14018c2ecf20Sopenharmony_ci	return 1;
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci__setup("plip=", plip_setup);
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci#endif /* !MODULE */
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_cistatic int __init plip_init (void)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	if (parport[0] == -2)
14118c2ecf20Sopenharmony_ci		return 0;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	if (parport[0] != -1 && timid) {
14148c2ecf20Sopenharmony_ci		printk(KERN_WARNING "plip: warning, ignoring `timid' since specific ports given.\n");
14158c2ecf20Sopenharmony_ci		timid = 0;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	if (parport_register_driver (&plip_driver)) {
14198c2ecf20Sopenharmony_ci		printk (KERN_WARNING "plip: couldn't register driver\n");
14208c2ecf20Sopenharmony_ci		return 1;
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	return 0;
14248c2ecf20Sopenharmony_ci}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_cimodule_init(plip_init);
14278c2ecf20Sopenharmony_cimodule_exit(plip_cleanup_module);
14288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1429