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