📄 dm9xs.c
字号:
unregister_netdev(dev);
kfree(dev);
err_out:
return -ENODEV;
}
static void __exit dmfe_remove_one (struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct dmfe_board_info *db = dev->priv;
DMFE_DBUG(0, "dmfe_remove_one()", 0);
if (dev) {
pci_free_consistent(db->net_dev, sizeof(struct tx_desc) *
DESC_ALL_CNT + 0x20, db->desc_pool_ptr,
db->desc_pool_dma_ptr);
pci_free_consistent(db->net_dev, TX_BUF_ALLOC * TX_DESC_CNT + 4,
db->buf_pool_ptr, db->buf_pool_dma_ptr);
unregister_netdev(dev);
release_region(dev->base_addr,
CHK_IO_SIZE(pdev, db->chip_revision));
kfree(dev); /* free board information */
pci_set_drvdata(pdev, NULL);
}
DMFE_DBUG(0, "dmfe_remove_one() exit", 0);
}
/*
* Open the interface.
* The interface is opened whenever "ifconfig" actives it.
*/
static int dmfe_open(struct DEVICE *dev)
{
int ret;
struct dmfe_board_info *db = dev->priv;
DMFE_DBUG(0, "dmfe_open", 0);
ret = request_irq(dev->irq, &dmfe_interrupt, SA_SHIRQ, dev->name, dev);
if (ret)
return ret;
/* system variable init */
db->cr6_data = CR6_DEFAULT | dmfe_cr6_user_set;
db->tx_packet_cnt = 0;
db->tx_queue_cnt = 0;
db->rx_avail_cnt = 0;
db->link_failed = 1;
db->wait_reset = 0;
db->first_in_callback = 0;
db->NIC_capability = 0xf; /* All capability*/
db->PHY_reg4 = 0x1e0;
/* CR6 operation mode decision */
if ( !chkmode || (db->chip_id == PCI_DM9132_ID) ||
(db->chip_revision >= 0x02000030) ) {
db->cr6_data |= DMFE_TXTH_256;
db->cr0_data = CR0_DEFAULT;
db->dm910x_chk_mode=4; /* Enter the normal mode */
} else {
db->cr6_data |= CR6_SFT; /* Store & Forward mode */
db->cr0_data = 0;
db->dm910x_chk_mode = 1; /* Enter the check mode */
}
/* Initilize DM910X board */
dmfe_init_dm910x(dev);
/* Active System Interface */
netif_wake_queue(dev);
/* set and active a timer process */
init_timer(&db->timer);
db->timer.expires = DMFE_TIMER_WUT + HZ * 2;
db->timer.data = (unsigned long)dev;
db->timer.function = &dmfe_timer;
add_timer(&db->timer);
return 0;
}
/* Initilize DM910X board
* Reset DM910X board
* Initilize TX/Rx descriptor chain structure
* Send the set-up frame
* Enable Tx/Rx machine
*/
static void dmfe_init_dm910x(struct DEVICE *dev)
{
struct dmfe_board_info *db = dev->priv;
u32 ioaddr = db->ioaddr;
DMFE_DBUG(0, "dmfe_init_dm910x()", 0);
/* Reset DM910x MAC controller */
outl(DM910X_RESET, ioaddr + DCR0); /* RESET MAC */
udelay(100);
outl(db->cr0_data, ioaddr + DCR0);
DELAY_5US;
/* Phy addr : DM910(A)2/DM9132/9801, phy address = 1 */
db->phy_addr = 1;
/* Parser SROM and media mode */
dmfe_parse_srom(db);
db->media_mode = dmfe_media_mode;
/* RESET Phyxcer Chip by GPR port bit 7 */
outl(0x180, ioaddr + DCR12); /* Let bit 7 output port */
if (db->chip_id == PCI_DM9009_ID) {
outl(0x80, ioaddr + DCR12); /* Issue RESET signal */
mdelay(300); /* Delay 300 ms */
}
outl(0x0, ioaddr + DCR12); /* Clear RESET signal */
/* Process Phyxcer Media Mode */
if ( !(db->media_mode & 0x10) ) /* Force 1M mode */
dmfe_set_phyxcer(db);
/* Media Mode Process */
if ( !(db->media_mode & DMFE_AUTO) )
db->op_mode = db->media_mode; /* Force Mode */
/* Initiliaze Transmit/Receive decriptor and CR3/4 */
dmfe_descriptor_init(db, ioaddr);
/* Init CR6 to program DM910x operation */
update_cr6(db->cr6_data, ioaddr);
/* Send setup frame */
if (db->chip_id == PCI_DM9132_ID)
dm9132_id_table(dev, dev->mc_count); /* DM9132 */
else
send_filter_frame(dev, dev->mc_count); /* DM9102/DM9102A */
/* Init CR7, interrupt active bit */
db->cr7_data = CR7_DEFAULT;
outl(db->cr7_data, ioaddr + DCR7);
/* Init CR15, Tx jabber and Rx watchdog timer */
outl(db->cr15_data, ioaddr + DCR15);
/* Enable DM910X Tx/Rx function */
db->cr6_data |= CR6_RXSC | CR6_TXSC | 0x40000;
update_cr6(db->cr6_data, ioaddr);
}
/*
* Hardware start transmission.
* Send a packet to media from the upper layer.
*/
static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev)
{
struct dmfe_board_info *db = dev->priv;
struct tx_desc *txptr;
DMFE_DBUG(0, "dmfe_start_xmit", 0);
/* Resource flag check */
netif_stop_queue(dev);
/* Too large packet check */
if (skb->len > MAX_PACKET_SIZE) {
printk(KERN_ERR "<DM9XS>: big packet = %d\n", (u16)skb->len);
dev_kfree_skb(skb);
return 0;
}
/* No Tx resource check, it never happen nromally */
if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) {
printk(KERN_ERR "<DM9XS>: No Tx resource %d\n", db->tx_queue_cnt);
return 1;
}
/* Disable NIC interrupt */
outl(0, dev->base_addr + DCR7);
spin_lock_irq(&db->lock);
/* transmit this packet */
txptr = db->tx_insert_ptr;
memcpy( (char *) txptr->tx_buf_ptr, (char *) skb->data, skb->len);
txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len);
/* Point to next transmit free descriptor */
db->tx_insert_ptr = (struct tx_desc *) txptr->next_tx_desc;
/* Transmit Packet Process */
if ( (!db->tx_queue_cnt) && (db->tx_packet_cnt < TX_MAX_SEND_CNT) ) {
txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */
db->tx_packet_cnt++; /* Ready to send */
outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */
dev->trans_start = jiffies; /* saved time stamp */
} else {
db->tx_queue_cnt++; /* queue TX packet */
outl(0x1, dev->base_addr + DCR1); /* Issue Tx polling */
}
/* Tx resource check */
if ( db->tx_queue_cnt < TX_FREE_DESC_CNT )
netif_wake_queue(dev);
/* free this SKB */
dev_kfree_skb(skb);
/* Restore CR7 to enable interrupt */
spin_unlock_irq(&db->lock);
outl(db->cr7_data, dev->base_addr + DCR7);
return 0;
}
/*
* Stop the interface.
* The interface is stopped when it is brought.
*/
static int dmfe_stop(struct DEVICE *dev)
{
struct dmfe_board_info *db = dev->priv;
u32 ioaddr = dev->base_addr;
DMFE_DBUG(0, "dmfe_stop", 0);
/* disable system */
netif_stop_queue(dev);
/* deleted timer */
del_timer_sync(&db->timer);
/* Reset & stop DM910X board */
outl(DM910X_RESET, ioaddr + DCR0);
DELAY_5US;
phy_write(db->ioaddr, db->phy_addr, 0, 0x8000, db->chip_id);
/* free interrupt */
free_irq(dev->irq, dev);
/* free allocated rx buffer */
dmfe_free_rxbuffer(db);
#if 0
/* show statistic counter */
printk("<DM9XS>: FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n",
db->tx_fifo_underrun, db->tx_excessive_collision,
db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier,
db->tx_jabber_timeout, db->reset_count, db->reset_cr8,
db->reset_fatal, db->reset_TXtimeout);
#endif
return 0;
}
/*
* DM9102 insterrupt handler
* receive the packet to upper layer, free the transmitted packet
*/
static void dmfe_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct DEVICE *dev = dev_id;
struct dmfe_board_info *db = (struct dmfe_board_info *) dev->priv;
u32 ioaddr = dev->base_addr;
DMFE_DBUG(0, "dmfe_interrupt()", 0);
if (!dev) {
DMFE_DBUG(1, "dmfe_interrupt() without DEVICE arg", 0);
return;
}
/* Got DM910X status */
db->cr5_data = inl(ioaddr + DCR5);
outl(db->cr5_data, ioaddr + DCR5);
if ( !(db->cr5_data & 0xc1) )
return;
/* Disable all interrupt in CR7 to solve the interrupt edge problem */
outl(0, ioaddr + DCR7);
/* Check system status */
if (db->cr5_data & 0x2000) {
/* system bus error happen */
DMFE_DBUG(1, "System bus error happen. CR5=", db->cr5_data);
db->reset_fatal++;
db->wait_reset = 1; /* Need to RESET */
outl(0, ioaddr + DCR7); /* disable all interrupt */
return;
}
/* Received the coming packet */
if ( (db->cr5_data & 0x40) && db->rx_avail_cnt )
dmfe_rx_packet(dev, db);
/* reallocated rx descriptor buffer */
if (db->rx_avail_cnt<RX_DESC_CNT)
allocated_rx_buffer(db);
/* Free the transmitted descriptor */
if ( db->cr5_data & 0x01) {
spin_lock(&db->lock);
dmfe_free_tx_pkt(dev, db);
spin_unlock(&db->lock);
}
/* Mode Check */
if (db->dm910x_chk_mode & 0x2) {
db->dm910x_chk_mode = 0x4;
db->cr6_data |= 0x100;
update_cr6(db->cr6_data, db->ioaddr);
}
/* Restore CR7 to enable interrupt mask */
outl(db->cr7_data, ioaddr + DCR7);
}
/*
* Free TX resource after TX complete
*/
static void dmfe_free_tx_pkt(struct DEVICE *dev, struct dmfe_board_info * db)
{
struct tx_desc *txptr;
u32 ioaddr = dev->base_addr;
txptr = db->tx_remove_ptr;
while(db->tx_packet_cnt) {
/* printk("<DM9XS>: tdes0=%x\n", txptr->tdes0); */
if (txptr->tdes0 & 0x80000000)
break;
/* A packet sent completed */
db->tx_packet_cnt--;
db->stats.tx_packets++;
/* Transmit statistic counter */
if ( txptr->tdes0 != 0x7fffffff ) {
/* printk("<DM9XS>: tdes0=%x\n", txptr->tdes0); */
db->stats.collisions += (txptr->tdes0 >> 3) & 0xf;
db->stats.tx_bytes += txptr->tdes1 & 0x7ff;
if (txptr->tdes0 & TDES0_ERR_MASK) {
db->stats.tx_errors++;
if (txptr->tdes0 & 0x0002) { /* UnderRun */
db->tx_fifo_underrun++;
if ( !(db->cr6_data & CR6_SFT) ) {
db->cr6_data = db->cr6_data | CR6_SFT;
update_cr6(db->cr6_data, db->ioaddr);
}
}
if (txptr->tdes0 & 0x0100)
db->tx_excessive_collision++;
if (txptr->tdes0 & 0x0200)
db->tx_late_collision++;
if (txptr->tdes0 & 0x0400)
db->tx_no_carrier++;
if (txptr->tdes0 & 0x0800)
db->tx_loss_carrier++;
if (txptr->tdes0 & 0x4000)
db->tx_jabber_timeout++;
}
}
txptr = (struct tx_desc *) txptr->next_tx_desc;
}/* End of while */
/* Update TX remove pointer to next */
db->tx_remove_ptr = (struct tx_desc *) txptr;
/* Send the Tx packet in queue */
if ( (db->tx_packet_cnt < TX_MAX_SEND_CNT) && db->tx_queue_cnt ) {
txptr->tdes0 = cpu_to_le32(0x80000000); /* Set owner bit */
db->tx_packet_cnt++; /* Ready to send */
db->tx_queue_cnt--;
outl(0x1, ioaddr + DCR1); /* Issue Tx polling */
dev->trans_start = jiffies; /* saved time stamp */
}
/* Resource available check */
if ( db->tx_queue_cnt < TX_WAKE_DESC_CNT )
netif_wake_queue(dev); /* Active upper layer, send again */
}
/*
* Receive the come packet and pass to upper layer
*/
static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db)
{
struct rx_desc *rxptr;
struct sk_buff *skb;
int rxlen;
rxptr = db->rx_ready_ptr;
while(db->rx_avail_cnt) {
if (rxptr->rdes0 & 0x80000000) /* packet owner check */
break;
db->rx_avail_cnt--;
db->interval_rx_cnt++;
if ( (rxptr->rdes0 & 0x300) != 0x300) {
/* A packet without First/Last flag */
/* reused this SKB */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
} else {
/* A packet with First/Last flag */
rxlen = ( (rxptr->rdes0 >> 16) & 0x3fff) - 4;
/* error summary bit check */
if (rxptr->rdes0 & 0x8000) {
/* This is a error packet */
//printk("<DM9XS>: rdes0: %lx\n", rxptr->rdes0);
db->stats.rx_errors++;
if (rxptr->rdes0 & 1)
db->stats.rx_fifo_errors++;
if (rxptr->rdes0 & 2)
db->stats.rx_crc_errors++;
if (rxptr->rdes0 & 0x80)
db->stats.rx_length_errors++;
}
if ( !(rxptr->rdes0 & 0x8000) ||
((db->cr6_data & CR6_PM) && (rxlen>6)) ) {
skb = (struct sk_buff *) rxptr->rx_skb_ptr;
/* Received Packet CRC check need or not */
if ( (db->dm910x_chk_mode & 1) &&
(cal_CRC(skb->tail, rxlen, 1) !=
(*(unsigned long *) (skb->tail+rxlen) )
) ) {
/* Found a error received packet */
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
db->dm910x_chk_mode = 3;
} else {
/* Good packet, send to upper layer */
/* Shorst packet used new SKB */
if ( (rxlen < RX_COPY_SIZE) &&
( (skb = dev_alloc_skb(rxlen + 2) )
!= NULL) ) {
/* size less than COPY_SIZE, allocated a rxlen SKB */
skb->dev = dev;
skb_reserve(skb, 2); /* 16byte align */
memcpy(skb_put(skb, rxlen), ((struct sk_buff *) rxptr->rx_skb_ptr)->tail, rxlen);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
} else {
skb->dev = dev;
skb_put(skb, rxlen);
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
dev->last_rx = jiffies;
db->stats.rx_packets++;
db->stats.rx_bytes += rxlen;
}
} else {
/* Reuse SKB buffer when the packet is error */
DMFE_DBUG(0, "Reused SK buffer, rdes0", rxptr->rdes0);
dmfe_reused_skb(db, (struct sk_buff *) rxptr->rx_skb_ptr);
}
}
rxptr = (struct rx_desc *) rxptr->next_rx_desc;
}
db->rx_ready_ptr = rxptr;
}
/*
* Get statistics from driver.
*/
static struct net_device_stats * dmfe_get_stats(struct DEVICE *dev)
{
struct dmfe_board_info *db = (struct dmfe_board_info *)dev->priv;
DMFE_DBUG(0, "dmfe_get_stats", 0);
return &db->stats;
}
/*
* Set DM910X multicast address
*/
static void dmfe_set_filter_mode(struct DEVICE * dev)
{
struct dmfe_board_info *db = dev->priv;
DMFE_DBUG(0, "dmfe_set_filter_mode()", 0);
if (dev->flags & IFF_PROMISC) {
DMFE_DBUG(0, "Enable PROM Mode", 0);
db->cr6_data |= CR6_PM | CR6_PBF;
update_cr6(db->cr6_data, db->ioaddr);
return;
}
if (dev->flags & IFF_ALLMULTI || dev->mc_count > DMFE_MAX_MULTICAST) {
DMFE_DBUG(0, "Pass all multicast address", dev->mc_count);
db->cr6_data &= ~(CR6_PM | CR6_PBF);
db->cr6_data |= CR6_PAM;
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -