📄 plip.c
字号:
/* $Id: plip.c,v 1.3.6.2 1997/04/16 15:07:56 phil Exp $ *//* PLIP: A parallel port "network" driver for Linux. *//* This driver is for parallel port with 5-bit cable (LapLink (R) cable). *//* * Authors: Donald Becker, <becker@super.org> * Tommy Thorn, <thorn@daimi.aau.dk> * Tanabe Hiroyasu, <hiro@sanpo.t.u-tokyo.ac.jp> * Alan Cox, <gw4pts@gw4pts.ampr.org> * Peter Bauer, <100136.3530@compuserve.com> * Niibe Yutaka, <gniibe@mri.co.jp> * * Modularization and ifreq/ifmap support by Alan Cox. * Rewritten by Niibe Yutaka. * parport-sharing awareness code by Philip Blundell. * SMP locking by Niibe Yutaka. * * Fixes: * Niibe Yutaka * - Module initialization. * - MTU fix. * - Make sure other end is OK, before sending a packet. * - Fix immediate timer problem. * * Al Viro * - Changed {enable,disable}_irq handling to make it work * with new ("stack") semantics. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *//* * Original version and the name 'PLIP' from Donald Becker <becker@super.org> * inspired by Russ Nelson's parallel port packet driver. * * NOTE: * Tanabe Hiroyasu had changed the protocol, and it was in Linux v1.0. * Because of the necessity to communicate to DOS machines with the * Crynwr packet driver, Peter Bauer changed the protocol again * back to original protocol. * * This version follows original PLIP protocol. * So, this PLIP can't communicate the PLIP of Linux v1.0. *//* * To use with DOS box, please do (Turn on ARP switch): * # ifconfig plip[0-2] arp */static const char *version = "NET3 PLIP version 2.3-parport gniibe@mri.co.jp\n";/* Sources: Ideas and protocols came from Russ Nelson's <nelson@crynwr.com> "parallel.asm" parallel port packet driver. The "Crynwr" parallel port standard specifies the following protocol: Trigger by sending '0x08' (this cause interrupt on other end) count-low octet count-high octet ... data octets checksum octet Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)> <wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)> The packet is encapsulated as if it were ethernet. The cable used is a de facto standard parallel null cable -- sold as a "LapLink" cable by various places. You'll need a 12-conductor cable to make one yourself. The wiring is: SLCTIN 17 - 17 GROUND 25 - 25 D0->ERROR 2 - 15 15 - 2 D1->SLCT 3 - 13 13 - 3 D2->PAPOUT 4 - 12 12 - 4 D3->ACK 5 - 10 10 - 5 D4->BUSY 6 - 11 11 - 6 Do not connect the other pins. They are D5,D6,D7 are 7,8,9 STROBE is 1, FEED is 14, INIT is 16 extra grounds are 18,19,20,21,22,23,24*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/if_ether.h>#include <asm/system.h>#include <asm/io.h>#include <linux/in.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/lp.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/inetdevice.h>#include <linux/skbuff.h>#include <linux/if_plip.h>#include <linux/tqueue.h>#include <linux/ioport.h>#include <asm/bitops.h>#include <asm/irq.h>#include <asm/byteorder.h>#include <asm/spinlock.h>#include <linux/parport.h>/* Maximum number of devices to support. */#define PLIP_MAX 8/* Use 0 for production, 1 for verification, >2 for debug */#ifndef NET_DEBUG#define NET_DEBUG 1#endifstatic unsigned int net_debug = NET_DEBUG;#define ENABLE(irq) enable_irq(irq)#define DISABLE(irq) disable_irq(irq)/* In micro second */#define PLIP_DELAY_UNIT 1/* Connection time out = PLIP_TRIGGER_WAIT * PLIP_DELAY_UNIT usec */#define PLIP_TRIGGER_WAIT 500/* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */#define PLIP_NIBBLE_WAIT 3000#define PAR_INTR_ON (LP_PINITP|LP_PSELECP|LP_PINTEN)#define PAR_INTR_OFF (LP_PINITP|LP_PSELECP)#define PAR_DATA(dev) ((dev)->base_addr+0)#define PAR_STATUS(dev) ((dev)->base_addr+1)#define PAR_CONTROL(dev) ((dev)->base_addr+2)/* Bottom halfs */static void plip_kick_bh(struct device *dev);static void plip_bh(struct device *dev);/* Interrupt handler */static void plip_interrupt(int irq, void *dev_id, struct pt_regs *regs);/* Functions for DEV methods */static int plip_rebuild_header(struct sk_buff *skb);static int plip_tx_packet(struct sk_buff *skb, struct device *dev);static int plip_open(struct device *dev);static int plip_close(struct device *dev);static struct net_device_stats *plip_get_stats(struct device *dev);static int plip_config(struct device *dev, struct ifmap *map);static int plip_ioctl(struct device *dev, struct ifreq *ifr, int cmd);static int plip_preempt(void *handle);static void plip_wakeup(void *handle);enum plip_connection_state { PLIP_CN_NONE=0, PLIP_CN_RECEIVE, PLIP_CN_SEND, PLIP_CN_CLOSING, PLIP_CN_ERROR};enum plip_packet_state { PLIP_PK_DONE=0, PLIP_PK_TRIGGER, PLIP_PK_LENGTH_LSB, PLIP_PK_LENGTH_MSB, PLIP_PK_DATA, PLIP_PK_CHECKSUM};enum plip_nibble_state { PLIP_NB_BEGIN, PLIP_NB_1, PLIP_NB_2,};struct plip_local { enum plip_packet_state state; enum plip_nibble_state nibble; union { struct {#if defined(__LITTLE_ENDIAN) unsigned char lsb; unsigned char msb;#elif defined(__BIG_ENDIAN) unsigned char msb; unsigned char lsb;#else#error "Please fix the endianness defines in <asm/byteorder.h>"#endif } b; unsigned short h; } length; unsigned short byte; unsigned char checksum; unsigned char data; struct sk_buff *skb;};struct net_local { struct net_device_stats enet_stats; struct tq_struct immediate; struct tq_struct deferred; struct plip_local snd_data; struct plip_local rcv_data; struct pardevice *pardev; unsigned long trigger; unsigned long nibble; enum plip_connection_state connection; unsigned short timeout_count; int is_deferred; int port_owner; int should_relinquish; int (*orig_rebuild_header)(struct sk_buff *skb); spinlock_t lock;};/* Entry point of PLIP driver. Probe the hardware, and register/initialize the driver. PLIP is rather weird, because of the way it interacts with the parport system. It is _not_ initialised from Space.c. Instead, plip_init() is called, and that function makes up a "struct device" for each port, and then calls us here. */__initfunc(intplip_init_dev(struct device *dev, struct parport *pb)){ struct net_local *nl; struct pardevice *pardev; dev->irq = pb->irq; dev->base_addr = pb->base; if (pb->irq == -1) { printk(KERN_INFO "plip: %s has no IRQ.\n", pb->name); return -ENODEV; } pardev = parport_register_device(pb, dev->name, plip_preempt, plip_wakeup, plip_interrupt, 0, dev); if (!pardev) return -ENODEV; printk(KERN_INFO "%s", version); printk(KERN_INFO "%s: Parallel port at %#3lx, using IRQ %d\n", dev->name, dev->base_addr, dev->irq); /* Fill in the generic fields of the device structure. */ ether_setup(dev); /* Then, override parts of it */ dev->hard_start_xmit = plip_tx_packet; dev->open = plip_open; dev->stop = plip_close; dev->get_stats = plip_get_stats; dev->set_config = plip_config; dev->do_ioctl = plip_ioctl; dev->tx_queue_len = 10; dev->flags = IFF_POINTOPOINT|IFF_NOARP; memset(dev->dev_addr, 0xfc, ETH_ALEN); /* Set the private structure */ dev->priv = kmalloc(sizeof (struct net_local), GFP_KERNEL); if (dev->priv == NULL) { printk(KERN_ERR "%s: out of memory\n", dev->name); parport_unregister_device(pardev); return -ENOMEM; } memset(dev->priv, 0, sizeof(struct net_local)); nl = (struct net_local *) dev->priv; nl->orig_rebuild_header = dev->rebuild_header; dev->rebuild_header = plip_rebuild_header; nl->pardev = pardev; nl->port_owner = 0; /* Initialize constants */ nl->trigger = PLIP_TRIGGER_WAIT; nl->nibble = PLIP_NIBBLE_WAIT; /* Initialize task queue structures */ nl->immediate.next = NULL; nl->immediate.sync = 0; nl->immediate.routine = (void *)(void *)plip_bh; nl->immediate.data = dev; nl->deferred.next = NULL; nl->deferred.sync = 0; nl->deferred.routine = (void *)(void *)plip_kick_bh; nl->deferred.data = dev; spin_lock_init(&nl->lock); return 0;}/* Bottom half handler for the delayed request. This routine is kicked by do_timer(). Request `plip_bh' to be invoked. */static voidplip_kick_bh(struct device *dev){ struct net_local *nl = (struct net_local *)dev->priv; if (nl->is_deferred) { queue_task(&nl->immediate, &tq_immediate); mark_bh(IMMEDIATE_BH); }}/* Forward declarations of internal routines */static int plip_none(struct device *, struct net_local *, struct plip_local *, struct plip_local *);static int plip_receive_packet(struct device *, struct net_local *, struct plip_local *, struct plip_local *);static int plip_send_packet(struct device *, struct net_local *, struct plip_local *, struct plip_local *);static int plip_connection_close(struct device *, struct net_local *, struct plip_local *, struct plip_local *);static int plip_error(struct device *, struct net_local *, struct plip_local *, struct plip_local *);static int plip_bh_timeout_error(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv, int error);#define OK 0#define TIMEOUT 1#define ERROR 2#define HS_TIMEOUT 3typedef int (*plip_func)(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv);static plip_func connection_state_table[] ={ plip_none, plip_receive_packet, plip_send_packet, plip_connection_close, plip_error};/* Bottom half handler of PLIP. */static voidplip_bh(struct device *dev){ struct net_local *nl = (struct net_local *)dev->priv; struct plip_local *snd = &nl->snd_data; struct plip_local *rcv = &nl->rcv_data; plip_func f; int r; nl->is_deferred = 0; f = connection_state_table[nl->connection]; if ((r = (*f)(dev, nl, snd, rcv)) != OK && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) { nl->is_deferred = 1; queue_task(&nl->deferred, &tq_timer); }}static intplip_bh_timeout_error(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv, int error){ unsigned char c0; /* * This is tricky. If we got here from the beginning of send (either * with ERROR or HS_TIMEOUT) we have IRQ enabled. Otherwise it's * already disabled. With the old variant of {enable,disable}_irq() * extra disable_irq() was a no-op. Now it became mortal - it's * unbalanced and thus we'll never re-enable IRQ (until rmmod plip, * that is). So we have to treat HS_TIMEOUT and ERROR from send * in a special way. */ spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_SEND) { if (error != ERROR) { /* Timeout */ nl->timeout_count++; if ((error == HS_TIMEOUT && nl->timeout_count <= 10) || nl->timeout_count <= 3) { spin_unlock_irq(&nl->lock); /* Try again later */ return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); printk(KERN_WARNING "%s: transmit timeout(%d,%02x)\n", dev->name, snd->state, c0); } else error = HS_TIMEOUT; nl->enet_stats.tx_errors++; nl->enet_stats.tx_aborted_errors++; } else if (nl->connection == PLIP_CN_RECEIVE) { if (rcv->state == PLIP_PK_TRIGGER) { /* Transmission was interrupted. */ spin_unlock_irq(&nl->lock); return OK; } if (error != ERROR) { /* Timeout */ if (++nl->timeout_count <= 3) { spin_unlock_irq(&nl->lock); /* Try again later */ return TIMEOUT; } c0 = inb(PAR_STATUS(dev)); printk(KERN_WARNING "%s: receive timeout(%d,%02x)\n", dev->name, rcv->state, c0); } nl->enet_stats.rx_dropped++; } rcv->state = PLIP_PK_DONE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -