450 lines
11 KiB
Diff
Executable File
450 lines
11 KiB
Diff
Executable File
Index: linux-2.6.22-source/drivers/net/conet.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ linux-2.6.22-source/drivers/net/conet.c
|
|
@@ -0,0 +1,444 @@
|
|
+/*
|
|
+ * Copyright (C) 2003-2004 Dan Aloni <da-x@gmx.net>
|
|
+ * Copyright (C) 2004 Pat Erley
|
|
+ * Copyright (C) 2004 George Boutwell
|
|
+ * Copyright (C) 2007 Steve Shoecraft <sshoecraft@earthlink.net>
|
|
+ *
|
|
+ * Cooperative Linux Network Device implementation
|
|
+ */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/ethtool.h>
|
|
+#include <linux/mii.h>
|
|
+#include <linux/pci.h>
|
|
+
|
|
+#include <linux/cooperative_internal.h>
|
|
+#include <linux/cooperative_pci.h>
|
|
+#include <asm/irq.h>
|
|
+
|
|
+#define DRV_NAME "conet"
|
|
+#define DRV_VERSION "1.02"
|
|
+
|
|
+struct conet_priv {
|
|
+ struct net_device_stats stats;
|
|
+ int unit;
|
|
+ unsigned short flags;
|
|
+ struct pci_dev *pdev;
|
|
+ spinlock_t rx_lock;
|
|
+ spinlock_t ioctl_lock;
|
|
+ struct mii_if_info mii_if;
|
|
+};
|
|
+
|
|
+#define CONET_FLAG_ENABLED 0x01
|
|
+#define CONET_FLAG_HANDLING 0x02
|
|
+#define CONET_FLAG_DEBUG 0x80
|
|
+
|
|
+static struct net_device *conet_dev[CO_MODULE_MAX_CONET];
|
|
+
|
|
+static int conet_open(struct net_device *dev)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if (priv->flags & CONET_FLAG_ENABLED) return 0;
|
|
+
|
|
+ priv->flags |= CONET_FLAG_ENABLED;
|
|
+
|
|
+ netif_start_queue(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int conet_stop(struct net_device *dev)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ priv->flags &= ~CONET_FLAG_ENABLED;
|
|
+
|
|
+ netif_stop_queue(dev);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int conet_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
+{
|
|
+ int len;
|
|
+ char *data;
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
|
|
+ data = skb->data;
|
|
+
|
|
+ dev->trans_start = jiffies; /* save the timestamp */
|
|
+
|
|
+ co_send_message(CO_MODULE_LINUX,
|
|
+ CO_MODULE_CONET0 + priv->unit,
|
|
+ CO_PRIORITY_DISCARDABLE,
|
|
+ CO_MESSAGE_TYPE_OTHER,
|
|
+ len,
|
|
+ data);
|
|
+
|
|
+ priv->stats.tx_bytes+=skb->len;
|
|
+ priv->stats.tx_packets++;
|
|
+
|
|
+ dev_kfree_skb(skb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void conet_rx(struct net_device *dev, co_linux_message_t *message)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+ int len;
|
|
+ unsigned char *buf;
|
|
+
|
|
+ len = message->size;
|
|
+ if (len > 0x10000) {
|
|
+ printk("conet rx: buggy network reception\n");
|
|
+ priv->stats.rx_dropped++;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ buf = message->data;
|
|
+
|
|
+ /*
|
|
+ * The packet has been retrieved from the transmission
|
|
+ * medium. Build an skb around it, so upper layers can handle it
|
|
+ */
|
|
+ skb = dev_alloc_skb(len+2);
|
|
+ if (!skb) {
|
|
+ printk("conet rx: low on mem - packet dropped\n");
|
|
+ priv->stats.rx_dropped++;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ memcpy(skb_put(skb, len), buf, len);
|
|
+
|
|
+ /* Write metadata, and then pass to the receive level */
|
|
+ skb->dev = dev;
|
|
+ skb->protocol = eth_type_trans(skb, dev);
|
|
+ skb->ip_summed = CHECKSUM_NONE; /* make the kernel calculate and verify
|
|
+ the checksum */
|
|
+
|
|
+ priv->stats.rx_bytes += len;
|
|
+ priv->stats.rx_packets++;
|
|
+
|
|
+ netif_rx(skb);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static irqreturn_t conet_interrupt(int irq, void *dev_id)
|
|
+{
|
|
+ co_message_node_t *node_message;
|
|
+
|
|
+ while (co_get_message(&node_message, CO_DEVICE_NETWORK)) {
|
|
+ struct net_device *dev;
|
|
+ struct conet_priv *priv;
|
|
+ co_linux_message_t *message;
|
|
+
|
|
+ message = (co_linux_message_t *)&node_message->msg.data;
|
|
+ if (message->unit >= CO_MODULE_MAX_CONET) {
|
|
+ printk("conet interrupt: buggy network reception unit %d\n", message->unit);
|
|
+ return IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ dev = conet_dev[message->unit];
|
|
+ if (!dev) {
|
|
+ co_free_message(node_message);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!netif_running(dev)) {
|
|
+ co_free_message(node_message);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (node_message->msg.type == CO_MESSAGE_TYPE_STRING) {
|
|
+ int connected= *(int*)(message+1);
|
|
+ if (connected)
|
|
+ netif_carrier_on(dev);
|
|
+ else
|
|
+ netif_carrier_off(dev);
|
|
+ co_free_message(node_message);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ priv = netdev_priv(dev);
|
|
+ spin_lock(&priv->rx_lock);
|
|
+#if 0
|
|
+ if (priv->flags & CONET_FLAG_HANDLING) {
|
|
+ co_free_message(node_message);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ priv->flags |= CONET_FLAG_HANDLING;
|
|
+ conet_rx(dev, message);
|
|
+ co_free_message(node_message);
|
|
+ priv->flags &= ~CONET_FLAG_HANDLING;
|
|
+#endif
|
|
+ conet_rx(dev, message);
|
|
+ co_free_message(node_message);
|
|
+ spin_unlock(&priv->rx_lock);
|
|
+ }
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static struct net_device_stats* conet_get_stats(struct net_device *dev)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ return &priv->stats;
|
|
+}
|
|
+
|
|
+static int conet_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
+{
|
|
+ /* We support 100-baseT Full duplex TP */
|
|
+ cmd->port = PORT_TP;
|
|
+ cmd->duplex = DUPLEX_FULL;
|
|
+ cmd->supported = SUPPORTED_TP | SUPPORTED_100baseT_Full;
|
|
+ cmd->speed = SPEED_100;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int conet_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
+{
|
|
+ /* We support TP Full duplex 100 */
|
|
+ if (cmd->port != PORT_TP || cmd->duplex != DUPLEX_FULL || cmd->speed != SPEED_100)
|
|
+ return -EOPNOTSUPP;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void conet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ strcpy(info->driver, DRV_NAME);
|
|
+ strcpy(info->version, DRV_VERSION);
|
|
+ strcpy(info->bus_info, pci_name(priv->pdev));
|
|
+}
|
|
+
|
|
+static u32 conet_get_link(struct net_device *dev)
|
|
+{
|
|
+ /* Always connected */
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static u32 conet_get_msglevel(struct net_device *dev)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ return ((priv->flags & CONET_FLAG_DEBUG) != 0);
|
|
+}
|
|
+
|
|
+static void conet_set_msglevel(struct net_device *dev, u32 level)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if (level)
|
|
+ priv->flags |= CONET_FLAG_DEBUG;
|
|
+ else
|
|
+ priv->flags &= ~CONET_FLAG_DEBUG;
|
|
+}
|
|
+
|
|
+static int conet_mdio_read(struct net_device *dev, int id, int reg)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+ int val;
|
|
+
|
|
+ if (priv->flags & CONET_FLAG_DEBUG)
|
|
+ printk(KERN_INFO "conet%d: mdio_read: id: %d, reg: %d\n", priv->unit, id, reg);
|
|
+ switch(reg) {
|
|
+ case MII_BMCR: /* Basic mode control register */
|
|
+ val = BMCR_FULLDPLX | BMCR_SPEED100;
|
|
+ break;
|
|
+ case MII_BMSR: /* Basic mode status register */
|
|
+ val = BMSR_LSTATUS | BMSR_100FULL;
|
|
+ break;
|
|
+ default:
|
|
+ val = 0;
|
|
+ break;
|
|
+ }
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static void conet_mdio_write(struct net_device *dev, int id, int reg, int val)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ if (priv->flags & CONET_FLAG_DEBUG)
|
|
+ printk(KERN_INFO "conet%d: mdio_write: id: %d, reg: %d, val: %d\n", priv->unit, id, reg, val);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int conet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
|
+{
|
|
+ struct conet_priv *priv = netdev_priv(dev);
|
|
+ int rc;
|
|
+
|
|
+ spin_lock(&priv->ioctl_lock);
|
|
+ rc = generic_mii_ioctl(&priv->mii_if, if_mii(rq), cmd, NULL);
|
|
+ spin_unlock(&priv->ioctl_lock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static const struct ethtool_ops conet_ethtool_ops = {
|
|
+ .get_settings = conet_get_settings,
|
|
+ .set_settings = conet_set_settings,
|
|
+ .get_drvinfo = conet_get_drvinfo,
|
|
+ .get_link = conet_get_link,
|
|
+ .get_msglevel = conet_get_msglevel,
|
|
+ .set_msglevel = conet_set_msglevel,
|
|
+#if 0
|
|
+ .nway_reset = conet_nway_reset,
|
|
+ .get_ringparam = conet_get_ringparam,
|
|
+ .set_ringparam = conet_set_ringparam,
|
|
+ .get_tx_csum = ethtool_op_get_tx_csum,
|
|
+ .get_sg = ethtool_op_get_sg,
|
|
+ .get_tso = ethtool_op_get_tso,
|
|
+ .get_strings = conet_get_strings,
|
|
+ .self_test_count = conet_self_test_count,
|
|
+ .self_test = conet_ethtool_test,
|
|
+ .phys_id = conet_phys_id,
|
|
+ .get_regs_len = conet_get_regs_len,
|
|
+ .get_regs = conet_get_regs,
|
|
+ .get_perm_addr = ethtool_op_get_perm_addr,
|
|
+#endif
|
|
+};
|
|
+
|
|
+static struct pci_device_id conet_pci_ids[] = {
|
|
+ { PCI_DEVICE(PCI_VENDOR_ID_CO, PCI_DEVICE_ID_CONET) },
|
|
+ { 0 }
|
|
+};
|
|
+
|
|
+MODULE_DEVICE_TABLE(pci, conet_pci_ids);
|
|
+
|
|
+static int __devinit conet_pci_probe( struct pci_dev *pdev,
|
|
+ const struct pci_device_id *ent)
|
|
+{
|
|
+ u8 unit, addr[6];
|
|
+ struct net_device *dev;
|
|
+ struct conet_priv *priv;
|
|
+ int rc;
|
|
+
|
|
+// printk(KERN_INFO "CONET: probing!\n");
|
|
+
|
|
+ unit = -1;
|
|
+ pci_read_config_byte(pdev, PCI_CO_UNIT, &unit);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC1, &addr[0]);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC2, &addr[1]);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC3, &addr[2]);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC4, &addr[3]);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC5, &addr[4]);
|
|
+ pci_read_config_byte(pdev, PCI_CO_MAC6, &addr[5]);
|
|
+
|
|
+ dev = alloc_etherdev(sizeof(*priv));
|
|
+ if (dev == NULL) {
|
|
+ printk(KERN_ERR "conet%d: could not allocate memory for device.\n", unit);
|
|
+ rc = -ENOMEM;
|
|
+ goto error_out_pdev;
|
|
+ }
|
|
+ SET_MODULE_OWNER(dev);
|
|
+ SET_NETDEV_DEV(dev, &pdev->dev);
|
|
+ memcpy(dev->dev_addr, addr, 6);
|
|
+
|
|
+ dev->open = conet_open;
|
|
+ dev->stop = conet_stop;
|
|
+ dev->hard_start_xmit = conet_hard_start_xmit;
|
|
+ dev->ethtool_ops = &conet_ethtool_ops;
|
|
+ dev->get_stats = conet_get_stats;
|
|
+ dev->do_ioctl = conet_ioctl;
|
|
+ dev->irq = pdev->irq;
|
|
+
|
|
+ priv = netdev_priv(dev);
|
|
+ priv->unit = unit;
|
|
+ priv->pdev = pdev;
|
|
+
|
|
+ spin_lock_init(&priv->ioctl_lock);
|
|
+ spin_lock_init(&priv->rx_lock);
|
|
+
|
|
+ priv->mii_if.full_duplex = 1;
|
|
+ priv->mii_if.phy_id_mask = 0x1f;
|
|
+ priv->mii_if.reg_num_mask = 0x1f;
|
|
+ priv->mii_if.dev = dev;
|
|
+ priv->mii_if.mdio_read = conet_mdio_read;
|
|
+ priv->mii_if.mdio_write = conet_mdio_write;
|
|
+ priv->mii_if.phy_id = 1;
|
|
+
|
|
+ pci_set_drvdata(pdev, priv);
|
|
+
|
|
+ rc = register_netdev(dev);
|
|
+ if (rc) {
|
|
+ printk(KERN_ERR "conet%d: could not register device; rc: %d\n", unit, rc);
|
|
+ goto error_out_dev;
|
|
+ }
|
|
+
|
|
+ conet_dev[unit] = dev;
|
|
+
|
|
+ printk(KERN_INFO "conet%d: irq %d, HWAddr %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
+ unit, NETWORK_IRQ, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+error_out_dev:
|
|
+ free_netdev(dev);
|
|
+
|
|
+error_out_pdev:
|
|
+ pci_disable_device(pdev);
|
|
+ pci_set_drvdata(pdev, NULL);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static void __devexit conet_pci_remove(struct pci_dev *pdev)
|
|
+{
|
|
+ struct conet_priv *priv = dev_get_drvdata(&pdev->dev);
|
|
+ struct net_device *net_dev = conet_dev[priv->unit];
|
|
+
|
|
+ unregister_netdev(net_dev);
|
|
+ free_netdev(net_dev);
|
|
+ dev_set_drvdata(&pdev->dev, NULL);
|
|
+}
|
|
+
|
|
+static struct pci_driver conet_pci_driver = {
|
|
+ .name = DRV_NAME,
|
|
+ .id_table = conet_pci_ids,
|
|
+ .probe = conet_pci_probe,
|
|
+ .remove = __devexit_p(conet_pci_remove),
|
|
+};
|
|
+
|
|
+static int __init conet_pci_init(void)
|
|
+{
|
|
+ int unit, rc;
|
|
+
|
|
+// printk(KERN_INFO "CONET: Initializing...\n");
|
|
+
|
|
+ rc = request_irq(NETWORK_IRQ, &conet_interrupt, IRQF_SAMPLE_RANDOM, "conet", NULL);
|
|
+ if (rc) {
|
|
+ printk(KERN_ERR "CONET: unable to get irq %d", NETWORK_IRQ);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ /* Init our units */
|
|
+ for (unit=0; unit < CO_MODULE_MAX_CONET; unit++)
|
|
+ conet_dev[unit] = NULL;
|
|
+
|
|
+// printk(KERN_INFO "CONET: registering...\n");
|
|
+ return pci_register_driver(&conet_pci_driver);
|
|
+}
|
|
+
|
|
+static void __exit conet_pci_exit(void)
|
|
+{
|
|
+ pci_unregister_driver(&conet_pci_driver);
|
|
+}
|
|
+
|
|
+module_init(conet_pci_init);
|
|
+module_exit(conet_pci_exit);
|