📄 plip.c
字号:
if (rcv->skb) { kfree_skb(rcv->skb); rcv->skb = NULL; } snd->state = PLIP_PK_DONE; if (snd->skb) { dev_kfree_skb(snd->skb); snd->skb = NULL; } spin_unlock_irq(&nl->lock); if (error == HS_TIMEOUT) { DISABLE(dev->irq); synchronize_irq(); } outb(PAR_INTR_OFF, PAR_CONTROL(dev)); dev->tbusy = 1; nl->connection = PLIP_CN_ERROR; outb(0x00, PAR_DATA(dev)); return TIMEOUT;}static intplip_none(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv){ return OK;}/* PLIP_RECEIVE --- receive a byte(two nibbles) Returns OK on success, TIMEOUT on timeout */inline static intplip_receive(unsigned short nibble_timeout, unsigned short status_addr, enum plip_nibble_state *ns_p, unsigned char *data_p){ unsigned char c0, c1; unsigned int cx; switch (*ns_p) { case PLIP_NB_BEGIN: cx = nibble_timeout; while (1) { c0 = inb(status_addr); udelay(PLIP_DELAY_UNIT); if ((c0 & 0x80) == 0) { c1 = inb(status_addr); if (c0 == c1) break; } if (--cx == 0) return TIMEOUT; } *data_p = (c0 >> 3) & 0x0f; outb(0x10, --status_addr); /* send ACK */ status_addr++; *ns_p = PLIP_NB_1; case PLIP_NB_1: cx = nibble_timeout; while (1) { c0 = inb(status_addr); udelay(PLIP_DELAY_UNIT); if (c0 & 0x80) { c1 = inb(status_addr); if (c0 == c1) break; } if (--cx == 0) return TIMEOUT; } *data_p |= (c0 << 1) & 0xf0; outb(0x00, --status_addr); /* send ACK */ status_addr++; *ns_p = PLIP_NB_BEGIN; case PLIP_NB_2: break; } return OK;}/* PLIP_RECEIVE_PACKET --- receive a packet */static intplip_receive_packet(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv){ unsigned short status_addr = PAR_STATUS(dev); unsigned short nibble_timeout = nl->nibble; unsigned char *lbuf; switch (rcv->state) { case PLIP_PK_TRIGGER: DISABLE(dev->irq); /* Don't need to synchronize irq, as we can safely ignore it */ outb(PAR_INTR_OFF, PAR_CONTROL(dev)); dev->interrupt = 0; outb(0x01, PAR_DATA(dev)); /* send ACK */ if (net_debug > 2) printk(KERN_DEBUG "%s: receive start\n", dev->name); rcv->state = PLIP_PK_LENGTH_LSB; rcv->nibble = PLIP_NB_BEGIN; case PLIP_PK_LENGTH_LSB: if (snd->state != PLIP_PK_DONE) { if (plip_receive(nl->trigger, status_addr, &rcv->nibble, &rcv->length.b.lsb)) { /* collision, here dev->tbusy == 1 */ rcv->state = PLIP_PK_DONE; nl->is_deferred = 1; nl->connection = PLIP_CN_SEND; queue_task(&nl->deferred, &tq_timer); outb(PAR_INTR_ON, PAR_CONTROL(dev)); ENABLE(dev->irq); return OK; } } else { if (plip_receive(nibble_timeout, status_addr, &rcv->nibble, &rcv->length.b.lsb)) return TIMEOUT; } rcv->state = PLIP_PK_LENGTH_MSB; case PLIP_PK_LENGTH_MSB: if (plip_receive(nibble_timeout, status_addr, &rcv->nibble, &rcv->length.b.msb)) return TIMEOUT; if (rcv->length.h > dev->mtu + dev->hard_header_len || rcv->length.h < 8) { printk(KERN_WARNING "%s: bogus packet size %d.\n", dev->name, rcv->length.h); return ERROR; } /* Malloc up new buffer. */ rcv->skb = dev_alloc_skb(rcv->length.h); if (rcv->skb == NULL) { printk(KERN_ERR "%s: Memory squeeze.\n", dev->name); return ERROR; } skb_put(rcv->skb,rcv->length.h); rcv->skb->dev = dev; rcv->state = PLIP_PK_DATA; rcv->byte = 0; rcv->checksum = 0; case PLIP_PK_DATA: lbuf = rcv->skb->data; do if (plip_receive(nibble_timeout, status_addr, &rcv->nibble, &lbuf[rcv->byte])) return TIMEOUT; while (++rcv->byte < rcv->length.h); do rcv->checksum += lbuf[--rcv->byte]; while (rcv->byte); rcv->state = PLIP_PK_CHECKSUM; case PLIP_PK_CHECKSUM: if (plip_receive(nibble_timeout, status_addr, &rcv->nibble, &rcv->data)) return TIMEOUT; if (rcv->data != rcv->checksum) { nl->enet_stats.rx_crc_errors++; if (net_debug) printk(KERN_DEBUG "%s: checksum error\n", dev->name); return ERROR; } rcv->state = PLIP_PK_DONE; case PLIP_PK_DONE: /* Inform the upper layer for the arrival of a packet. */ rcv->skb->protocol=eth_type_trans(rcv->skb, dev); netif_rx(rcv->skb); nl->enet_stats.rx_bytes += rcv->length.h; nl->enet_stats.rx_packets++; rcv->skb = NULL; if (net_debug > 2) printk(KERN_DEBUG "%s: receive end\n", dev->name); /* Close the connection. */ outb (0x00, PAR_DATA(dev)); spin_lock_irq(&nl->lock); if (snd->state != PLIP_PK_DONE) { nl->connection = PLIP_CN_SEND; spin_unlock_irq(&nl->lock); queue_task(&nl->immediate, &tq_immediate); mark_bh(IMMEDIATE_BH); outb(PAR_INTR_ON, PAR_CONTROL(dev)); ENABLE(dev->irq); return OK; } else { nl->connection = PLIP_CN_NONE; spin_unlock_irq(&nl->lock); outb(PAR_INTR_ON, PAR_CONTROL(dev)); ENABLE(dev->irq); return OK; } } return OK;}/* PLIP_SEND --- send a byte (two nibbles) Returns OK on success, TIMEOUT when timeout */inline static intplip_send(unsigned short nibble_timeout, unsigned short data_addr, enum plip_nibble_state *ns_p, unsigned char data){ unsigned char c0; unsigned int cx; switch (*ns_p) { case PLIP_NB_BEGIN: outb((data & 0x0f), data_addr); *ns_p = PLIP_NB_1; case PLIP_NB_1: outb(0x10 | (data & 0x0f), data_addr); cx = nibble_timeout; data_addr++; while (1) { c0 = inb(data_addr); if ((c0 & 0x80) == 0) break; if (--cx == 0) return TIMEOUT; udelay(PLIP_DELAY_UNIT); } outb(0x10 | (data >> 4), --data_addr); *ns_p = PLIP_NB_2; case PLIP_NB_2: outb((data >> 4), data_addr); data_addr++; cx = nibble_timeout; while (1) { c0 = inb(data_addr); if (c0 & 0x80) break; if (--cx == 0) return TIMEOUT; udelay(PLIP_DELAY_UNIT); } data_addr--; *ns_p = PLIP_NB_BEGIN; return OK; } return OK;}/* PLIP_SEND_PACKET --- send a packet */static intplip_send_packet(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv){ unsigned short data_addr = PAR_DATA(dev); unsigned short nibble_timeout = nl->nibble; unsigned char *lbuf; unsigned char c0; unsigned int cx; if (snd->skb == NULL || (lbuf = snd->skb->data) == NULL) { printk(KERN_DEBUG "%s: send skb lost\n", dev->name); snd->state = PLIP_PK_DONE; snd->skb = NULL; return ERROR; } switch (snd->state) { case PLIP_PK_TRIGGER: if ((inb(PAR_STATUS(dev)) & 0xf8) != 0x80) return HS_TIMEOUT; /* Trigger remote rx interrupt. */ outb(0x08, data_addr); cx = nl->trigger; while (1) { udelay(PLIP_DELAY_UNIT); spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_RECEIVE) { spin_unlock_irq(&nl->lock); /* Interrupted. */ nl->enet_stats.collisions++; return OK; } c0 = inb(PAR_STATUS(dev)); if (c0 & 0x08) { spin_unlock_irq(&nl->lock); DISABLE(dev->irq); synchronize_irq(); if (nl->connection == PLIP_CN_RECEIVE) { /* Interrupted. We don't need to enable irq, as it is soon disabled. */ /* Yes, we do. New variant of {enable,disable}_irq *counts* them. -- AV */ ENABLE(dev->irq); nl->enet_stats.collisions++; return OK; } outb(PAR_INTR_OFF, PAR_CONTROL(dev)); if (net_debug > 2) printk(KERN_DEBUG "%s: send start\n", dev->name); snd->state = PLIP_PK_LENGTH_LSB; snd->nibble = PLIP_NB_BEGIN; nl->timeout_count = 0; break; } spin_unlock_irq(&nl->lock); if (--cx == 0) { outb(0x00, data_addr); return HS_TIMEOUT; } } case PLIP_PK_LENGTH_LSB: if (plip_send(nibble_timeout, data_addr, &snd->nibble, snd->length.b.lsb)) return TIMEOUT; snd->state = PLIP_PK_LENGTH_MSB; case PLIP_PK_LENGTH_MSB: if (plip_send(nibble_timeout, data_addr, &snd->nibble, snd->length.b.msb)) return TIMEOUT; snd->state = PLIP_PK_DATA; snd->byte = 0; snd->checksum = 0; case PLIP_PK_DATA: do if (plip_send(nibble_timeout, data_addr, &snd->nibble, lbuf[snd->byte])) return TIMEOUT; while (++snd->byte < snd->length.h); do snd->checksum += lbuf[--snd->byte]; while (snd->byte); snd->state = PLIP_PK_CHECKSUM; case PLIP_PK_CHECKSUM: if (plip_send(nibble_timeout, data_addr, &snd->nibble, snd->checksum)) return TIMEOUT; nl->enet_stats.tx_bytes += snd->skb->len; dev_kfree_skb(snd->skb); nl->enet_stats.tx_packets++; snd->state = PLIP_PK_DONE; case PLIP_PK_DONE: /* Close the connection */ outb (0x00, data_addr); snd->skb = NULL; if (net_debug > 2) printk(KERN_DEBUG "%s: send end\n", dev->name); nl->connection = PLIP_CN_CLOSING; nl->is_deferred = 1; queue_task(&nl->deferred, &tq_timer); outb(PAR_INTR_ON, PAR_CONTROL(dev)); ENABLE(dev->irq); return OK; } return OK;}static intplip_connection_close(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv){ spin_lock_irq(&nl->lock); if (nl->connection == PLIP_CN_CLOSING) { nl->connection = PLIP_CN_NONE; dev->tbusy = 0; mark_bh(NET_BH); } spin_unlock_irq(&nl->lock); if (nl->should_relinquish) { nl->should_relinquish = nl->port_owner = 0; parport_release(nl->pardev); } return OK;}/* PLIP_ERROR --- wait till other end settled */static intplip_error(struct device *dev, struct net_local *nl, struct plip_local *snd, struct plip_local *rcv){ unsigned char status; status = inb(PAR_STATUS(dev)); if ((status & 0xf8) == 0x80) { if (net_debug > 2) printk(KERN_DEBUG "%s: reset interface.\n", dev->name); nl->connection = PLIP_CN_NONE; nl->should_relinquish = 0; dev->tbusy = 0; dev->interrupt = 0; outb(PAR_INTR_ON, PAR_CONTROL(dev)); ENABLE(dev->irq); mark_bh(NET_BH); } else { nl->is_deferred = 1; queue_task(&nl->deferred, &tq_timer); } return OK;}/* Handle the parallel port interrupts. */static voidplip_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct device *dev = dev_id; struct net_local *nl; struct plip_local *rcv; unsigned char c0; if (dev == NULL) { printk(KERN_DEBUG "plip_interrupt: irq %d for unknown device.\n", irq); return; } nl = (struct net_local *)dev->priv; rcv = &nl->rcv_data; if (dev->interrupt) return; c0 = inb(PAR_STATUS(dev)); if ((c0 & 0xf8) != 0xc0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -