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