📄 ve100.c
字号:
if(!(nic->rxs = kcalloc(count,sizeof(struct rx),GFP_ATOMIC)))
return -ENOMEM;
for(rx = nic->rxs,i=0; i < count; i++,rx++)
{
rx->next = (i+1 <count) ? rx+1 : nic->rxs;
rx->prev = (i == 0) ? nic->rxs + count -1 : rx - 1;
if(ve100_rx_alloc_skb(nic,rx))
{
ve100_rx_clean_list(nic);
return -ENOMEM;
}
}
nic->rx_to_use = nic->rx_to_clean = nic->rxs;
nic->ru_running = RU_SUSPENDED;
return 0;
}
/* interrupt */
static irqreturn_t ve100_intr(int irq,void* id)
{
struct net_device* ndev = id;
struct nic* nic = netdev_priv(ndev);
u8 stat_ack = ioread8(&nic->csr->scb.stat_ack);
if(stat_ack == stat_ack_not_ours || stat_ack == stat_ack_not_present)
return IRQ_NONE;
/* ack interrupt */
iowrite8(stat_ack,&nic->csr->scb.stat_ack);
/* hit receive no resource; restart ru after cleaning */
if(stat_ack & stat_ack_rnr)
nic->ru_running = RU_SUSPENDED;
if(likely(netif_rx_schedule_prep(ndev)))
{
ve100_disable_irq(nic);
__netif_rx_schedule(ndev);
}
return IRQ_HANDLED;
}
/* ve100 up */
static int ve100_start(struct nic* nic)
{
int ret;
if((ret = ve100_rx_alloc_list(nic)))
{
printk("aaa\n");
return ret;
}
if((ret = ve100_alloc_cbs(nic)))
{
printk("bbb\n");
goto err_rx_clean_list;
}
if((ret = ve100_hw_init(nic)))
{
printk("ccc\n");
goto err_clean_cbs;
}
ve100_set_multicast_list(nic->ndev);
ve100_start_receiver(nic,NULL);
mod_timer(&nic->watchdog,jiffies);
if((ret = request_irq(nic->pdev->irq,ve100_intr,IRQF_SHARED,nic->ndev->name,nic->ndev)))
goto err_no_irq;
/*Lionel 2008 to start the transmission queue */
netif_wake_queue(nic->ndev);
/* to enable the poll method */
netif_poll_enable(nic->ndev);
/* enable ints _after_ enabling poll, preventing a race between
* disable ints+schedule */
ve100_enable_irq(nic);
return 0;
err_no_irq:
del_timer_sync(&nic->watchdog);
err_clean_cbs:
ve100_clean_cbs(nic);
err_rx_clean_list:
ve100_rx_clean_list(nic);
return ret;
}
/* ve100 down */
static void ve100_end(struct nic* nic)
{
/* wait for poll complete */
netif_poll_disable(nic->ndev);
netif_stop_queue(nic->ndev);
ve100_hw_reset(nic);
free_irq(nic->pdev->irq,nic->ndev);
del_timer_sync(&nic->watchdog);
netif_carrier_off(nic->ndev);
ve100_clean_cbs(nic);
ve100_rx_clean_list(nic);
}
/* tranmist timeout task */
static void ve100_tx_timeout_task(struct work_struct* work)
{
struct nic* nic = container_of(work,struct nic,tx_timeout_task);
struct net_device* ndev = nic->ndev;
ve100_end(netdev_priv(ndev));
ve100_start(netdev_priv(ndev));
}
/* blink led */
static void ve100_blink_led(unsigned long data)
{
struct nic* nic = (struct nic*)data;
enum led_state
{
led_on = 0x1,
led_off = 0x4,
led_on_559 = 0x5,
led_on_557 = 0x7,
};
nic->leds = (nic->leds & led_on) ? led_off : (nic->mac < mac_82559_D101M) ? led_on_557 : led_on_559;
mdio_write(nic->ndev,nic->mii.phy_id,MII_LED_CONTROL,nic->leds);
mod_timer(&nic->blink_timer,jiffies + HZ /4);
}
/* e100_multi */
static void ve100_multi(struct nic* nic,struct cb* cb,struct sk_buff* skb)
{
struct net_device* ndev = nic->ndev;
struct dev_mc_list* list = ndev->mc_list;
u16 i,count = min(ndev->mc_count,Max_Multicast_Addrs);
cb->command = cpu_to_le16(cb_multi);
cb->u.multi.count = cpu_to_le16(count* ETH_ALEN);
for(i = 0; list&&i<count; i++,list = list->next)
memcpy(&cb->u.multi.addr[i * ETH_ALEN],&list->dmi_addr,ETH_ALEN);
}
/* set multicast list */
static void ve100_set_multicast_list(struct net_device* ndev)
{
struct nic* nic = netdev_priv(ndev);
if(ndev->flags & IFF_PROMISC)
nic->flags |= promiscuous;
else
nic->flags &= ~promiscuous;
if(ndev->flags & IFF_ALLMULTI || ndev->mc_count > Max_Multicast_Addrs)
nic->flags |= multicast_all;
else
nic->flags &= ~multicast_all;
ve100_exec_cb(nic,NULL,ve100_configure);
ve100_exec_cb(nic,NULL,ve100_multi);
}
/* Adjust inter-frame-spacing (IFS) between two transmits if
* we're getting collisions on a half-duplex connection. */
static void ve100_adjust_adaptive_ifs(struct nic* nic,int speed,int duplex)
{
if(duplex == DUPLEX_HALF)
{
u32 prev = nic->adaptive_ifs;
u32 min_frames = (speed == SPEED_100) ? 1000 : 100;
if((nic->tx_frames /32 < nic->tx_collisions) && (nic->tx_frames > min_frames))
{
if(nic->adaptive_ifs < 60)
nic->adaptive_ifs += 5;
}
else if(nic->tx_frames < min_frames)
{
if(nic->adaptive_ifs >= 5)
nic->adaptive_ifs -= 5;
}
if(nic->adaptive_ifs != prev)
ve100_exec_cb(nic,NULL,ve100_configure);
}
}
/* transmit prepare */
static void ve100_xmit_prepare(struct nic* nic,struct cb* cb,struct sk_buff* skb)
{
cb->command = nic->tx_command;
if((nic->cbs_avail & ~15) == nic->cbs_avail)
/*Lionel 2008 If the I bit is set to one, the device generates an interrupt after the execution of the CB is
finished. If I is not set to one, the CX interrupt will not be generated.*/
cb->command |= cpu_to_le16(cb_i);
cb->u.tcb.tbd_array = cb->dma_addr + offsetof(struct cb, u.tcb.tbd);
cb->u.tcb.tcb_byte_count = 0;
cb->u.tcb.threshold = nic->tx_threshold;
cb->u.tcb.tbd_count = 1;
cb->u.tcb.tbd.buf_addr = cpu_to_le32(pci_map_single(nic->pdev,
skb->data, skb->len, PCI_DMA_TODEVICE));
/* check for mapping failure? */
cb->u.tcb.tbd.size = cpu_to_le16(skb->len);
}
/* watchdog function */
static void ve100_watchdog(unsigned long data)
{
struct ethtool_cmd cmd;
struct nic* nic = (struct nic*)data;
mii_ethtool_gset(&nic->mii,&cmd);
//mii_link_ok(&nic->mii);
//netif_carrier_ok(nic->ndev);
if(mii_link_ok(&nic->mii) && !netif_carrier_ok(nic->ndev))
{
printk("link up,%sMbps, %s-duplex\n",cmd.speed == SPEED_100 ? "100":"10",cmd.duplex==DUPLEX_FULL ? "full" : "half");
}
else if(!mii_link_ok(&nic->mii) && netif_carrier_ok(nic->ndev))
{
printk("link down\n");
}
mii_check_link(&nic->mii);
spin_lock_irq(&nic->cmd_lock);
iowrite8(irq_sw_gen | ioread8(&nic->csr->scb.cmd_high),&nic->csr->scb.cmd_high);
ve100_write_flush(nic);
spin_unlock_irq(&nic->cmd_lock);
ve100_update_stats(nic);
ve100_adjust_adaptive_ifs(nic,cmd.speed,cmd.duplex);
if(nic->mac <= mac_82557_D100_C)
ve100_set_multicast_list(nic->ndev);
if(nic->flags & ich && cmd.speed == SPEED_10 && cmd.duplex==DUPLEX_HALF)
nic->flags |= ich_10h_workaround;
else
nic->flags &= ~ich_10h_workaround;
mod_timer(&nic->watchdog,jiffies + VE100_WATCHDOG_PERIOD);
}
/* ve100 asf*/
static int ve100_asf(struct nic* nic)
{
/* ASF can be enabled from eeprom */
return((nic->pdev->device >= 0x1050) && (nic->pdev->device <= 0x1057) &&
(nic->eeprom[eeprom_config_asf] & eeprom_asf) &&
!(nic->eeprom[eeprom_config_asf] & eeprom_gcl) &&
((nic->eeprom[eeprom_smbus_addr] & 0xFF) != 0xFE));
}
/* shutdown ve100 */
static void vd100_shutdown(struct pci_dev* pdev)
{
struct net_device* ndev = pci_get_drvdata(pdev);
struct nic* nic = netdev_priv(ndev);
if(netif_running(ndev))
netif_poll_disable(nic->ndev);
del_timer_sync(&nic->watchdog);
netif_carrier_off(nic->ndev);
if(ve100_asf(nic) | (nic->flags & wol_magic))
{
pci_enable_wake(pdev,PCI_D3hot,1);
pci_enable_wake(pdev,PCI_D3cold,1);
}
else
{
pci_enable_wake(pdev,PCI_D3hot,0);
pci_enable_wake(pdev,PCI_D3cold,0);
}
pci_disable_device(pdev);
pci_set_power_state(pdev,PCI_D3hot);
}
static int __devinit ve100_probe(struct pci_dev* pdev,const struct pci_device_id* pdi)
{
struct net_device *ndev;
struct nic* nic;
int ret;
if(!(ndev = alloc_etherdev(sizeof(struct nic))))
{
return -ENOMEM;
}
ndev->open = ve100_open;
ndev->stop = ve100_close;
ndev->hard_start_xmit = ve100_xmit_frame;
ndev->get_stats = ve100_get_stats;
ndev->set_multicast_list = ve100_set_multicast_list;
ndev->set_mac_address = ve100_set_mac_address;
ndev->change_mtu = ve100_change_mtu;
ndev->do_ioctl = ve100_do_ioctl;
ndev->tx_timeout = ve100_tx_timeout;
ndev->watchdog_timeo = VE100_WATCHDOG_PERIOD;
ndev->poll = ve100_poll;
ndev->weight = VE100_NAPI_WEIGHT;
strncpy(ndev->name,pci_name(pdev),sizeof(ndev->name) -1);
/*Lionel 2008 initialize the net device and bind to the netcard */
nic = netdev_priv(ndev);
nic->ndev = ndev;
nic->pdev = pdev;
nic->msg_enable = (1 << debug) - 1;
pci_set_drvdata(pdev,ndev);
/*Lionel 2008 activate the net device */
if((ret = pci_enable_device(pdev)))
{
printk("Can't enable PCI device, exit!\n");
goto err_out_free_dev;
}
/*Lionel 2008 get flags related to the resource */
if(!(pci_resource_flags(pdev,0) & IORESOURCE_MEM))
{
printk("Can't find proper PCI device, exit!\n");
ret = -ENODEV;
goto err_out_disable_pdev;
}
/* Lionel 2008 get pci resources for 6 registers request of pci */
if((ret = pci_request_regions(pdev,DRV_NAME)))
{
printk("Can't get PCI resources, exit!\n");
goto err_out_disable_pdev;
}
/*set 32bit dma mask */
if((ret = pci_set_dma_mask(pdev,DMA_32BIT_MASK)))
{
printk("DMA config error,exit !\n");
goto err_out_free_res;
}
SET_MODULE_OWNER(ndev);
SET_NETDEV_DEV(ndev,&pdev->dev);
if (use_io)
printk( "using i/o access mode\n");
nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr));
/* Lionel 2008 map the control status register to virtual address for kernel */
//nic->csr = ioremap(pci_resource_start(pdev,0),sizeof(struct csr));
if(!nic->csr)
{
printk("can't map register to dma, exit!\n");
ret = -ENOMEM;
goto err_out_free_res;
}
if(pdi->driver_data)
nic->flags |= ich;
else
nic->flags &= ~ich;
ve100_get_defaults(nic);
/* locks should be initialized */
spin_lock_init(&nic->cb_lock);
spin_lock_init(&nic->cmd_lock);
spin_lock_init(&nic->mdio_lock);
/* Reset the device before pci_set_master() in case device is in some
* funky state and has an interrupt pending - hint: we don't have the
* interrupt handler registered yet. */
ve100_hw_reset(nic);
pci_set_master(pdev);
/* Lionel 2008 Initialize the watchdog */
init_timer(&nic->watchdog);
nic->watchdog.function = ve100_watchdog;
nic->watchdog.data = (unsigned long)nic;
init_timer(&nic->blink_timer);
nic->blink_timer.function = ve100_blink_led;
nic->blink_timer.data = (unsigned long)nic;
INIT_WORK(&nic->tx_timeout_task,ve100_tx_timeout_task);
/*Lionel 2008 alloc pci resources */
if((ret = ve100_alloc(nic))) {
printk("Cannot alloc driver memory, aborting.\n");
goto err_out_iounmap;
}
if((ret = ve100_eeprom_load(nic)))
goto err_out_free;
ve100_phy_init(nic);
memcpy(ndev->dev_addr,nic->eeprom,ETH_ALEN);
memcpy(ndev->perm_addr,nic->eeprom,ETH_ALEN);
if(!is_valid_ether_addr(ndev->perm_addr))
{
if(!eeprom_bad_csum_allow){
printk("invalid address, exit!\n");
ret = -EAGAIN;
goto err_out_free;}
else
printk("Invalid Mac address from eeprom \n");
}
/* I don't know whether the code is useful or not so I keep it */
/* Wol magic packet can be enabled from eeprom */
if((nic->mac >= mac_82558_D101_A4) &&
(nic->eeprom[eeprom_id] & eeprom_id_wol))
nic->flags |= wol_magic;
ret = pci_enable_wake(pdev,0,0);
if(ret)
printk("failed to wake\n");
strcpy(ndev->name,"eth%d");
/*Lionel 2008 register net device */
if((ret = register_netdev(ndev)))
{
printk("can't register net device, exit!\n");
goto err_out_free;
}
return 0;
err_out_free:
ve100_free(nic);
err_out_iounmap:
pci_iounmap(pdev,nic->csr);
err_out_free_res:
pci_release_regions(pdev);
err_out_disable_pdev:
pci_disable_device(pdev);
err_out_free_dev:
pci_set_drvdata(pdev,NULL);
free_netdev(ndev);
return ret;
}
/* remove ve100 from system */
static void __devexit ve100_remove(struct pci_dev* pdev)
{
struct net_device* ndev = pci_get_drvdata(pdev);
if(ndev)
{
struct nic* nic = netdev_priv(ndev);
unregister_netdev(ndev); //unregister net device
ve100_free(nic); //free net device descriptor*/
iounmap(nic->csr); //unmap control status register*/
free_netdev(ndev); //free net dev
pci_release_regions(pdev); //release pci regions
pci_disable_device(pdev); //disable device
pci_set_drvdata(pdev,NULL); //set pdev as NULL
}
}
/* shutdown */
static void ve100_shutdown(struct pci_dev* pdev)
{
struct net_device* ndev = pci_get_drvdata(pdev);
struct nic* nic = netdev_priv(ndev);
if(netif_running(ndev))
netif_poll_disable(nic->ndev);
del_timer_sync(&nic->watchdog);
netif_carrier_off(nic->ndev);
if((nic->flags & wol_magic) | ve100_asf(nic))
{
pci_enable_wake(pdev,PCI_D3hot,1);
pci_enable_wake(pdev,PCI_D3cold,1);
}
else
{
pci_enable_wake(pdev,PCI_D3hot,0);
pci_enable_wake(pdev,PCI_D3cold,0);
}
pci_disable_device(pdev);
pci_set_power_state(pdev,PCI_D3hot);
}
static struct pci_driver ve100_driver = {
.name = DRV_NAME,
.id_table = ve100_id_tbl,
.probe = ve100_probe,
.remove = __devexit_p(ve100_remove),
.shutdown = ve100_shutdown,
};
static int __init ve100_init_module(void)
{
return pci_register_driver(&ve100_driver);
}
static void __exit ve100_cleanup_module(void)
{
pci_unregister_driver(&ve100_driver);
}
module_init(ve100_init_module);
module_exit(ve100_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -