Commit 1906bc68 authored by Christoph Hellwig's avatar Christoph Hellwig

[PATCH] convert acenic to pci_driver API

parent e578ca5a
......@@ -420,372 +420,323 @@ static int max_rx_desc[ACE_MAX_MOD_PARMS];
static int tx_ratio[ACE_MAX_MOD_PARMS];
static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1};
MODULE_AUTHOR("Jes Sorensen <jes@trained-monkey.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i");
MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state");
MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level");
MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives");
MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait");
MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives");
MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait");
MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)");
static char version[] __initdata =
"acenic.c: v0.92 08/05/2002 Jes Sorensen, linux-acenic@SunSITE.dk\n"
" http://home.cern.ch/~jes/gige/acenic.html\n";
static struct net_device *root_dev;
static int probed __initdata = 0;
static int __init acenic_probe(void)
static int __devinit acenic_probe_one(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct net_device *dev;
struct ace_private *ap;
struct pci_dev *pdev = NULL;
int boards_found = 0;
int version_disp;
if (probed)
return -ENODEV;
probed++;
version_disp = 0;
while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) {
if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) &&
((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) ||
(pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&&
!((pdev->vendor == PCI_VENDOR_ID_3COM) &&
(pdev->device == PCI_DEVICE_ID_3COM_3C985)) &&
!((pdev->vendor == PCI_VENDOR_ID_NETGEAR) &&
((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) ||
(pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) &&
/*
* Farallon used the DEC vendor ID on their cards by
* mistake for a while
*/
!((pdev->vendor == PCI_VENDOR_ID_DEC) &&
(pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) &&
!((pdev->vendor == PCI_VENDOR_ID_ALTEON) &&
(pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T)) &&
!((pdev->vendor == PCI_VENDOR_ID_SGI) &&
(pdev->device == PCI_DEVICE_ID_SGI_ACENIC)))
continue;
dev = alloc_etherdev(sizeof(struct ace_private));
if (dev == NULL) {
printk(KERN_ERR "acenic: Unable to allocate "
"net_device structure!\n");
break;
}
static int boards_found;
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
dev = alloc_etherdev(sizeof(struct ace_private));
if (dev == NULL) {
printk(KERN_ERR "acenic: Unable to allocate "
"net_device structure!\n");
return -ENOMEM;
}
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
ap = dev->priv;
ap->pdev = pdev;
ap = dev->priv;
ap->pdev = pdev;
dev->open = &ace_open;
dev->hard_start_xmit = &ace_start_xmit;
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
#if ACENIC_DO_VLAN
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->vlan_rx_register = ace_vlan_rx_register;
dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid;
dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
dev->vlan_rx_register = ace_vlan_rx_register;
dev->vlan_rx_kill_vid = ace_vlan_rx_kill_vid;
#endif
if (1) {
static void ace_watchdog(struct net_device *dev);
dev->tx_timeout = &ace_watchdog;
dev->watchdog_timeo = 5*HZ;
}
dev->stop = &ace_close;
dev->get_stats = &ace_get_stats;
dev->set_multicast_list = &ace_set_multicast_list;
dev->do_ioctl = &ace_ioctl;
dev->set_mac_address = &ace_set_mac_addr;
dev->change_mtu = &ace_change_mtu;
/* display version info if adapter is found */
if (!version_disp)
{
/* set display flag to TRUE so that */
/* we only display this string ONCE */
version_disp = 1;
printk(version);
}
if (1) {
static void ace_watchdog(struct net_device *dev);
dev->tx_timeout = &ace_watchdog;
dev->watchdog_timeo = 5*HZ;
}
if (pci_enable_device(pdev)) {
free_netdev(dev);
continue;
}
dev->open = &ace_open;
dev->stop = &ace_close;
dev->hard_start_xmit = &ace_start_xmit;
dev->get_stats = &ace_get_stats;
dev->set_multicast_list = &ace_set_multicast_list;
dev->do_ioctl = &ace_ioctl;
dev->set_mac_address = &ace_set_mac_addr;
dev->change_mtu = &ace_change_mtu;
/*
* Enable master mode before we start playing with the
* pci_command word since pci_set_master() will modify
* it.
*/
pci_set_master(pdev);
/* we only display this string ONCE */
if (!boards_found)
printk(version);
pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command);
if (pci_enable_device(pdev))
goto fail_free_netdev;
/* OpenFirmware on Mac's does not set this - DOH.. */
if (!(ap->pci_command & PCI_COMMAND_MEMORY)) {
printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
"access - was not enabled by BIOS/Firmware\n",
dev->name);
ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY;
pci_write_config_word(ap->pdev, PCI_COMMAND,
ap->pci_command);
wmb();
}
/*
* Enable master mode before we start playing with the
* pci_command word since pci_set_master() will modify
* it.
*/
pci_set_master(pdev);
pci_read_config_byte(pdev, PCI_LATENCY_TIMER,
&ap->pci_latency);
if (ap->pci_latency <= 0x40) {
ap->pci_latency = 0x40;
pci_write_config_byte(pdev, PCI_LATENCY_TIMER,
ap->pci_latency);
}
pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command);
/*
* Remap the regs into kernel space - this is abuse of
* dev->base_addr since it was means for I/O port
* addresses but who gives a damn.
*/
dev->base_addr = pci_resource_start(pdev, 0);
ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000);
if (!ap->regs) {
printk(KERN_ERR "%s: Unable to map I/O register, "
"AceNIC %i will be disabled.\n",
dev->name, boards_found);
break;
}
/* OpenFirmware on Mac's does not set this - DOH.. */
if (!(ap->pci_command & PCI_COMMAND_MEMORY)) {
printk(KERN_INFO "%s: Enabling PCI Memory Mapped "
"access - was not enabled by BIOS/Firmware\n",
dev->name);
ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY;
pci_write_config_word(ap->pdev, PCI_COMMAND,
ap->pci_command);
wmb();
}
switch(pdev->vendor) {
case PCI_VENDOR_ID_ALTEON:
if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) {
strncpy(ap->name, "Farallon PN9100-T "
"Gigabit Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Farallon PN9100-T ",
dev->name);
} else {
strncpy(ap->name, "AceNIC Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: Alteon AceNIC ",
dev->name);
}
break;
case PCI_VENDOR_ID_3COM:
strncpy(ap->name, "3Com 3C985 Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: 3Com 3C985 ", dev->name);
break;
case PCI_VENDOR_ID_NETGEAR:
strncpy(ap->name, "NetGear GA620 Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: NetGear GA620 ", dev->name);
break;
case PCI_VENDOR_ID_DEC:
if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) {
strncpy(ap->name, "Farallon PN9000-SX "
"Gigabit Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Farallon PN9000-SX ",
dev->name);
break;
}
case PCI_VENDOR_ID_SGI:
strncpy(ap->name, "SGI AceNIC Gigabit Ethernet",
pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &ap->pci_latency);
if (ap->pci_latency <= 0x40) {
ap->pci_latency = 0x40;
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ap->pci_latency);
}
/*
* Remap the regs into kernel space - this is abuse of
* dev->base_addr since it was means for I/O port
* addresses but who gives a damn.
*/
dev->base_addr = pci_resource_start(pdev, 0);
ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000);
if (!ap->regs) {
printk(KERN_ERR "%s: Unable to map I/O register, "
"AceNIC %i will be disabled.\n",
dev->name, boards_found);
goto fail_free_netdev;
}
switch(pdev->vendor) {
case PCI_VENDOR_ID_ALTEON:
if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) {
strncpy(ap->name, "Farallon PN9100-T "
"Gigabit Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Farallon PN9100-T ",
dev->name);
} else {
strncpy(ap->name, "AceNIC Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: SGI AceNIC ", dev->name);
break;
default:
strncpy(ap->name, "Unknown AceNIC based Gigabit "
"Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Unknown AceNIC ", dev->name);
printk(KERN_INFO "%s: Alteon AceNIC ",
dev->name);
}
break;
case PCI_VENDOR_ID_3COM:
strncpy(ap->name, "3Com 3C985 Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: 3Com 3C985 ", dev->name);
break;
case PCI_VENDOR_ID_NETGEAR:
strncpy(ap->name, "NetGear GA620 Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: NetGear GA620 ", dev->name);
break;
case PCI_VENDOR_ID_DEC:
if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) {
strncpy(ap->name, "Farallon PN9000-SX "
"Gigabit Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Farallon PN9000-SX ",
dev->name);
break;
}
ap->name [sizeof (ap->name) - 1] = '\0';
printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr);
case PCI_VENDOR_ID_SGI:
strncpy(ap->name, "SGI AceNIC Gigabit Ethernet",
sizeof (ap->name));
printk(KERN_INFO "%s: SGI AceNIC ", dev->name);
break;
default:
strncpy(ap->name, "Unknown AceNIC based Gigabit "
"Ethernet", sizeof (ap->name));
printk(KERN_INFO "%s: Unknown AceNIC ", dev->name);
break;
}
ap->name [sizeof (ap->name) - 1] = '\0';
printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr);
#ifdef __sparc__
printk("irq %s\n", __irq_itoa(pdev->irq));
printk("irq %s\n", __irq_itoa(pdev->irq));
#else
printk("irq %i\n", pdev->irq);
printk("irq %i\n", pdev->irq);
#endif
#ifdef CONFIG_ACENIC_OMIT_TIGON_I
if ((readl(&ap->regs->HostCtrl) >> 28) == 4) {
printk(KERN_ERR "%s: Driver compiled without Tigon I"
" support - NIC disabled\n", dev->name);
ace_init_cleanup(dev);
free_netdev(dev);
continue;
}
if ((readl(&ap->regs->HostCtrl) >> 28) == 4) {
printk(KERN_ERR "%s: Driver compiled without Tigon I"
" support - NIC disabled\n", dev->name);
goto fail_uninit;
}
#endif
if (ace_allocate_descriptors(dev)) {
/*
* ace_allocate_descriptors() calls
* ace_init_cleanup() on error.
*/
free_netdev(dev);
continue;
}
if (ace_allocate_descriptors(dev))
goto fail_free_netdev;
#ifdef MODULE
if (boards_found >= ACE_MAX_MOD_PARMS)
ap->board_idx = BOARD_IDX_OVERFLOW;
else
ap->board_idx = boards_found;
if (boards_found >= ACE_MAX_MOD_PARMS)
ap->board_idx = BOARD_IDX_OVERFLOW;
else
ap->board_idx = boards_found;
#else
ap->board_idx = BOARD_IDX_STATIC;
ap->board_idx = BOARD_IDX_STATIC;
#endif
if (ace_init(dev)) {
/*
* ace_init() calls ace_init_cleanup() on error.
*/
free_netdev(dev);
continue;
}
if (ace_init(dev))
goto fail_free_netdev;
if (register_netdev(dev)) {
printk(KERN_ERR "acenic: device registration failed\n");
ace_init_cleanup(dev);
free_netdev(dev);
continue;
}
if (register_netdev(dev)) {
printk(KERN_ERR "acenic: device registration failed\n");
goto fail_uninit;
}
if (ap->pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
if (ap->pci_using_dac)
dev->features |= NETIF_F_HIGHDMA;
boards_found++;
}
pci_set_drvdata(pdev, dev);
/*
* If we're at this point we're going through ace_probe() for
* the first time. Return success (0) if we've initialized 1
* or more boards. Otherwise, return failure (-ENODEV).
*/
boards_found++;
return 0;
if (boards_found > 0)
return 0;
else
return -ENODEV;
fail_uninit:
ace_init_cleanup(dev);
fail_free_netdev:
free_netdev(dev);
return -ENODEV;
}
static void __devexit acenic_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct ace_private *ap = dev->priv;
struct ace_regs *regs = ap->regs;
short i;
MODULE_AUTHOR("Jes Sorensen <jes@trained-monkey.org>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("AceNIC/3C985/GA620 Gigabit Ethernet driver");
MODULE_PARM(link, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(trace, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i");
MODULE_PARM(tx_ratio, "1-" __MODULE_STRING(8) "i");
MODULE_PARM_DESC(link, "AceNIC/3C985/NetGear link state");
MODULE_PARM_DESC(trace, "AceNIC/3C985/NetGear firmware trace level");
MODULE_PARM_DESC(tx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first tx descriptor arrives");
MODULE_PARM_DESC(max_tx_desc, "AceNIC/3C985/GA620 max number of transmit descriptors to wait");
MODULE_PARM_DESC(rx_coal_tick, "AceNIC/3C985/GA620 max clock ticks to wait from first rx descriptor arrives");
MODULE_PARM_DESC(max_rx_desc, "AceNIC/3C985/GA620 max number of receive descriptors to wait");
MODULE_PARM_DESC(tx_ratio, "AceNIC/3C985/GA620 ratio of NIC memory used for TX/RX descriptors (range 0-63)");
unregister_netdev(dev);
writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);
if (ap->version >= 2)
writel(readl(&regs->CpuBCtrl) | CPU_HALT, &regs->CpuBCtrl);
/*
* This clears any pending interrupts
*/
writel(1, &regs->Mb0Lo);
readl(&regs->CpuCtrl); /* flush */
static void __exit ace_module_cleanup(void)
{
struct ace_private *ap;
struct ace_regs *regs;
struct net_device *next;
short i;
/*
* Make sure no other CPUs are processing interrupts
* on the card before the buffers are being released.
* Otherwise one might experience some `interesting'
* effects.
*
* Then release the RX buffers - jumbo buffers were
* already released in ace_close().
*/
ace_sync_irq(dev->irq);
while (root_dev) {
ap = root_dev->priv;
next = ap->next;
unregister_netdev(root_dev);
for (i = 0; i < RX_STD_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
regs = ap->regs;
if (skb) {
struct ring_info *ringp;
dma_addr_t mapping;
writel(readl(&regs->CpuCtrl) | CPU_HALT, &regs->CpuCtrl);
if (ap->version >= 2)
writel(readl(&regs->CpuBCtrl) | CPU_HALT,
&regs->CpuBCtrl);
/*
* This clears any pending interrupts
*/
writel(1, &regs->Mb0Lo);
readl(&regs->CpuCtrl); /* flush */
ringp = &ap->skb->rx_std_skbuff[i];
mapping = pci_unmap_addr(ringp, mapping);
pci_unmap_page(ap->pdev, mapping,
ACE_STD_BUFSIZE - (2 + 16),
PCI_DMA_FROMDEVICE);
/*
* Make sure no other CPUs are processing interrupts
* on the card before the buffers are being released.
* Otherwise one might experience some `interesting'
* effects.
*
* Then release the RX buffers - jumbo buffers were
* already released in ace_close().
*/
ace_sync_irq(root_dev->irq);
ap->rx_std_ring[i].size = 0;
ap->skb->rx_std_skbuff[i].skb = NULL;
dev_kfree_skb(skb);
}
}
for (i = 0; i < RX_STD_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
if (ap->version >= 2) {
for (i = 0; i < RX_MINI_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
if (skb) {
struct ring_info *ringp;
dma_addr_t mapping;
ringp = &ap->skb->rx_std_skbuff[i];
mapping = pci_unmap_addr(ringp, mapping);
ringp = &ap->skb->rx_mini_skbuff[i];
mapping = pci_unmap_addr(ringp,mapping);
pci_unmap_page(ap->pdev, mapping,
ACE_STD_BUFSIZE - (2 + 16),
ACE_MINI_BUFSIZE - (2 + 16),
PCI_DMA_FROMDEVICE);
ap->rx_std_ring[i].size = 0;
ap->skb->rx_std_skbuff[i].skb = NULL;
ap->rx_mini_ring[i].size = 0;
ap->skb->rx_mini_skbuff[i].skb = NULL;
dev_kfree_skb(skb);
}
}
if (ap->version >= 2) {
for (i = 0; i < RX_MINI_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
if (skb) {
struct ring_info *ringp;
dma_addr_t mapping;
ringp = &ap->skb->rx_mini_skbuff[i];
mapping = pci_unmap_addr(ringp,mapping);
pci_unmap_page(ap->pdev, mapping,
ACE_MINI_BUFSIZE - (2 + 16),
PCI_DMA_FROMDEVICE);
ap->rx_mini_ring[i].size = 0;
ap->skb->rx_mini_skbuff[i].skb = NULL;
dev_kfree_skb(skb);
}
}
}
for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
if (skb) {
struct ring_info *ringp;
dma_addr_t mapping;
}
ringp = &ap->skb->rx_jumbo_skbuff[i];
mapping = pci_unmap_addr(ringp, mapping);
pci_unmap_page(ap->pdev, mapping,
ACE_JUMBO_BUFSIZE - (2 + 16),
PCI_DMA_FROMDEVICE);
for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
if (skb) {
struct ring_info *ringp;
dma_addr_t mapping;
ap->rx_jumbo_ring[i].size = 0;
ap->skb->rx_jumbo_skbuff[i].skb = NULL;
dev_kfree_skb(skb);
}
}
ringp = &ap->skb->rx_jumbo_skbuff[i];
mapping = pci_unmap_addr(ringp, mapping);
pci_unmap_page(ap->pdev, mapping,
ACE_JUMBO_BUFSIZE - (2 + 16),
PCI_DMA_FROMDEVICE);
ace_init_cleanup(root_dev);
free_netdev(root_dev);
root_dev = next;
ap->rx_jumbo_ring[i].size = 0;
ap->skb->rx_jumbo_skbuff[i].skb = NULL;
dev_kfree_skb(skb);
}
}
ace_init_cleanup(dev);
free_netdev(dev);
}
module_init(acenic_probe);
module_exit(ace_module_cleanup);
static struct pci_driver acenic_pci_driver = {
.name = "acenic",
.id_table = acenic_pci_tbl,
.probe = acenic_probe_one,
.remove = __devexit_p(acenic_remove_one),
};
static int __init acenic_init(void)
{
return pci_module_init(&acenic_pci_driver);
}
static void __exit acenic_exit(void)
{
pci_unregister_driver(&acenic_pci_driver);
}
module_init(acenic_init);
module_exit(acenic_exit);
static void ace_free_descriptors(struct net_device *dev)
{
......@@ -1252,13 +1203,6 @@ static int __init ace_init(struct net_device *dev)
} else
dev->irq = pdev->irq;
/*
* Register the device here to be able to catch allocated
* interrupt handlers in case the firmware doesn't come up.
*/
ap->next = root_dev;
root_dev = dev;
#ifdef INDEX_DEBUG
spin_lock_init(&ap->debug_lock);
ap->last_tx = ACE_TX_RING_ENTRIES(ap) - 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment