?? at1700.c
字號:
static int net_open(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
int i;
/* Powerup the chip, initialize config register 1, and select bank 0. */
outb(0xe0, ioaddr + CONFIG_1);
/* Set the station address in bank zero. */
for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + 8 + i);
/* Switch to bank 1 and set the multicast table to accept none. */
outb(0xe4, ioaddr + 7);
for (i = 0; i < 8; i++)
outb(0x00, ioaddr + 8 + i);
/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
bus access, and two 4K Tx queues. */
outb(0xda, ioaddr + CONFIG_0);
/* Same config 0, except enable the Rx and Tx. */
outb(0x5a, ioaddr + CONFIG_0);
/* Switch to register bank 2 for the run-time registers. */
outb(0xe8, ioaddr + CONFIG_1);
/* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
outb(0x00, ioaddr + TX_INTR);
outb(0x81, ioaddr + RX_INTR);
lp->open_time = jiffies;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
return 0;
}
static int
net_send_packet(struct sk_buff *skb, struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
if (dev->tbusy) {
/* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 10)
return 1;
printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
? "IRQ conflict" : "network cable problem");
printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
inw(ioaddr + 12), inw(ioaddr + 14));
lp->stats.tx_errors++;
/* ToDo: We should try to restart the adaptor... */
outw(0xffff, ioaddr + 24);
outw(0xffff, ioaddr + TX_STATUS);
outw(0xe85a, ioaddr + CONFIG_0);
outw(0x8100, ioaddr + TX_INTR);
dev->tbusy=0;
dev->trans_start = jiffies;
}
/* If some higher layer thinks we've missed an tx-done interrupt
we are passed NULL. Caution: dev_tint() handles the cli()/sti()
itself. */
if (skb == NULL) {
dev_tint(dev);
return 0;
}
/* For ethernet, fill in the header. This should really be done by a
higher level, rather than duplicated for each ethernet adaptor. */
if (!skb->arp && dev->rebuild_header(skb->data, dev)) {
skb->dev = dev;
arp_queue (skb);
return 0;
}
skb->arp=1;
/* Block a timer-based transmit from overlapping. This could better be
done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
if (set_bit(0, (void*)&dev->tbusy) != 0)
printk("%s: Transmitter access conflict.\n", dev->name);
else {
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
unsigned char *buf = skb->data;
/* Turn off the possible Tx interrupts. */
outb(0x00, ioaddr + TX_INTR);
outw(length, ioaddr + DATAPORT);
outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
lp->tx_queue++;
lp->tx_queue_len += length + 2;
if (lp->tx_started == 0) {
/* If the Tx is idle, always trigger a transmit. */
outb(0x80 | lp->tx_queue, ioaddr + TX_START);
lp->tx_queue = 0;
lp->tx_queue_len = 0;
dev->trans_start = jiffies;
lp->tx_started = 1;
} else if (lp->tx_queue_len < 4096 - 1502) /* Room for one more packet? */
dev->tbusy = 0;
/* Turn on Tx interrupts back on. */
outb(0x82, ioaddr + TX_INTR);
}
if (skb->free)
kfree_skb (skb, FREE_WRITE);
return 0;
}
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
net_interrupt(int reg_ptr)
{
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
struct device *dev = (struct device *)(irq2dev_map[irq]);
struct net_local *lp;
int ioaddr, status;
if (dev == NULL) {
printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
return;
}
dev->interrupt = 1;
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
status = inw(ioaddr + TX_STATUS);
outw(status, ioaddr + TX_STATUS);
if (net_debug > 4)
printk("%s: Interrupt with status %04x.\n", dev->name, status);
if (status & 0xff00
|| (inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Got a packet(s). */
net_rx(dev);
}
if (status & 0x00ff) {
if (status & 0x80) {
lp->stats.tx_packets++;
if (lp->tx_queue) {
outb(0x80 | lp->tx_queue, ioaddr + TX_START);
lp->tx_queue = 0;
lp->tx_queue_len = 0;
dev->trans_start = jiffies;
dev->tbusy = 0;
mark_bh(INET_BH); /* Inform upper layers. */
} else {
lp->tx_started = 0;
/* Turn on Tx interrupts off. */
outb(0x00, ioaddr + TX_INTR);
dev->tbusy = 0;
}
}
}
return;
}
/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
int boguscount = 5;
while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
ushort status = inw(ioaddr + DATAPORT);
if (net_debug > 4)
printk("%s: Rxing packet mode %02x status %04x.\n",
dev->name, inb(ioaddr + RX_MODE), status);
#ifndef final_version
if (status == 0) {
outb(0x05, ioaddr + 14);
break;
}
#endif
if ((status & 0xF0) != 0x20) { /* There was an error. */
lp->stats.rx_errors++;
if (status & 0x08) lp->stats.rx_length_errors++;
if (status & 0x04) lp->stats.rx_frame_errors++;
if (status & 0x02) lp->stats.rx_crc_errors++;
if (status & 0x01) lp->stats.rx_over_errors++;
} else {
ushort pkt_len = inw(ioaddr + DATAPORT);
/* Malloc up new buffer. */
int sksize = sizeof(struct sk_buff) + pkt_len;
struct sk_buff *skb;
if (pkt_len > 1550) {
printk("%s: The AT1700 claimed a very large packet, size %d.\n",
dev->name, pkt_len);
outb(0x05, ioaddr + 14);
lp->stats.rx_errors++;
break;
}
skb = alloc_skb(sksize, GFP_ATOMIC);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet (len %d).\n",
dev->name, pkt_len);
outb(0x05, ioaddr + 14);
lp->stats.rx_dropped++;
break;
}
skb->mem_len = sksize;
skb->mem_addr = skb;
skb->len = pkt_len;
skb->dev = dev;
insw(ioaddr + DATAPORT, skb->data, (pkt_len + 1) >> 1);
if (net_debug > 5) {
int i;
printk("%s: Rxed packet of length %d: ", dev->name, pkt_len);
for (i = 0; i < 14; i++)
printk(" %02x", skb->data[i]);
printk(".\n");
}
#ifdef HAVE_NETIF_RX
netif_rx(skb);
#else
skb->lock = 0;
if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
kfree_s(skb, sksize);
lp->stats.rx_dropped++;
break;
}
#endif
lp->stats.rx_packets++;
}
if (--boguscount <= 0)
break;
}
/* If any worth-while packets have been received, dev_rint()
has done a mark_bh(INET_BH) for us and will work on them
when we get to the bottom-half routine. */
{
int i;
for (i = 0; i < 20; i++) {
if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
break;
outb(0x05, ioaddr + 14);
}
if (net_debug > 5)
printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
dev->name, inb(ioaddr + RX_MODE), i);
}
return;
}
/* The inverse routine to net_open(). */
static int net_close(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
lp->open_time = 0;
dev->tbusy = 1;
dev->start = 0;
/* Set configuration register 0 to disable Tx and Rx. */
outb(0xda, ioaddr + CONFIG_0);
/* Update the statistics -- ToDo. */
/* Power-down the chip. Green, green, green! */
outb(0x00, ioaddr + CONFIG_1);
return 0;
}
/* Get the current statistics. This may be called with the card open or
closed. */
static struct enet_statistics *
net_get_stats(struct device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
cli();
/* ToDo: Update the statistics from the device registers. */
sti();
return &lp->stats;
}
#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
num_addrs == -1 Promiscuous mode, receive all packets
num_addrs == 0 Normal mode, clear multicast list
num_addrs > 0 Multicast mode, receive normal and MC packets, and do
best-effort filtering.
*/
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
short ioaddr = dev->base_addr;
if (num_addrs) {
outw(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
} else
outw(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */
}
#endif
/*
* Local variables:
* compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c at1700.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
* End:
*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -