/* * This is part of the Sequans SQN1130 driver. * Copyright 2008 SEQUANS Communications * Written by Andy Shevchenko , * Dmitriy Chumak * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. */ #include #include #include #include #include #include #include #include "sdio-netdev.h" #include "version.h" #include "msg.h" #include "thp.h" #include "sdio.h" #include "sdio-pm.h" #include "sdio-fw.h" #include "sdio-driver.h" #define DRIVER_DEBUG 0 #define SKB_DEBUG 0 #define IGNORE_CARRIER_STATE 1 /*******************************************************************/ /* Module parameter variables */ /*******************************************************************/ /** firmware_name - specifies the name of firmware binary */ char *firmware_name = SQN_DEFAULT_FW_NAME; /** * load_firmware - boolean flag, controls whether firmware * should be loaded or not */ int load_firmware = 1; bool drop_packet = false; module_param(firmware_name, charp, S_IRUGO); module_param(load_firmware, bool, S_IRUGO); struct sqn_private *g_priv = 0; //reference sdio-driver.c extern const uint8_t ss_macaddr[ETH_ALEN]; /*******************************************************************/ /* Network interface functions */ /*******************************************************************/ static int sqn_dev_open(struct net_device *dev) { struct sqn_private *priv = netdev_priv(dev); sqn_pr_enter(); spin_lock(&priv->drv_lock); netif_wake_queue(dev); spin_unlock(&priv->drv_lock); sqn_pr_leave(); return 0; } static int sqn_dev_stop(struct net_device *dev) { struct sqn_private *priv = netdev_priv(dev); sqn_pr_enter(); spin_lock(&priv->drv_lock); netif_stop_queue(dev); spin_unlock(&priv->drv_lock); sqn_pr_leave(); return 0; } /*******************************************************************/ /* TX queue handlers */ /*******************************************************************/ int sqn_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long irq_flags = 0; struct ethhdr *eth; struct sqn_private *priv = netdev_priv(dev); #if DRIVER_DEBUG printk(KERN_WARNING "sqn_hard_start_xmit \n"); #endif sqn_pr_enter(); sqn_pr_dbg("skb->len = %d\n", skb->len); #if SKB_DEBUG sqn_pr_info("%s: got skb [0x%p] from kernel, users %d\n", __func__, skb, atomic_read(&skb->users)); #endif spin_lock_irqsave(&priv->drv_lock, irq_flags); //HTC code: for DDTM if(drop_packet){ eth = (struct ethhdr*) skb->data; if(memcmp(eth->h_dest, ss_macaddr, ETH_ALEN) != 0){ sqn_pr_dbg("HTC drop_packet enabled: not THP, drop it\n"); #if DRIVER_DEBUG printk(KERN_WARNING "sqn_hard_start_xmit: network packet\n"); #endif priv->stats.tx_dropped++; priv->stats.tx_errors++; dev_kfree_skb_any(skb); goto out; }else{ sqn_pr_dbg("HTC drop_packet enabled: THP, let it live\n"); #if DRIVER_DEBUG printk(KERN_WARNING "sqn_hard_start_xmit: thp packet\n"); #endif } } if (priv->removed) goto out; if (skb->len < 1 || (skb->len > SQN_MAX_PDU_LEN)) { sqn_pr_dbg("skb length %d not in range (1, %d)\n", skb->len, SQN_MAX_PDU_LEN); /* * We'll never manage to send this one; * drop it and return 'OK' */ priv->stats.tx_dropped++; priv->stats.tx_errors++; spin_unlock_irqrestore(&priv->drv_lock, irq_flags); goto out; } /* netif_stop_queue(priv->dev); */ priv->add_skb_to_tx_queue(priv, skb, 1); priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len; dev->trans_start = jiffies; spin_unlock_irqrestore(&priv->drv_lock, irq_flags); wake_up_interruptible(&priv->tx_waitq); out: sqn_pr_leave(); return NETDEV_TX_OK; } static void sqn_tx_timeout(struct net_device *dev) { /* struct sqn_private *priv = netdev_priv(dev); */ sqn_pr_enter(); sqn_pr_err("TX watch dog timeout\n"); sqn_pr_leave(); } static int sqn_tx_thread(void *data) { struct net_device *dev = (struct net_device *) data; struct sqn_private *priv = netdev_priv(dev); int rv = 0; unsigned long irq_flags = 0; sqn_pr_enter(); /* * Set PF_NOFREEZE to prevent kernel to freeze this thread * when going to suspend. We will manually stop it from * driver's suspend handler. */ current->flags |= PF_NOFREEZE; for (;;) { spin_lock_irqsave(&priv->drv_lock, irq_flags); if (!(priv->is_tx_queue_empty(priv)) || priv->removed) spin_unlock_irqrestore(&priv->drv_lock, irq_flags); else { int rv = 0; sqn_pr_dbg("wait for skb\n"); spin_unlock_irqrestore(&priv->drv_lock, irq_flags); rv = wait_event_interruptible(priv->tx_waitq , !(priv->is_tx_queue_empty(priv)) || kthread_should_stop() || priv->removed); /* * If we've been interrupted by a signal, then we * should stop a thread */ if (0 != rv) { sqn_pr_dbg("got a signal from kernel %d\n", rv); break; } } sqn_pr_dbg("got skb to send, wake up\n"); if (kthread_should_stop()) { sqn_pr_dbg("break from main thread\n"); break; } spin_lock_irqsave(&priv->drv_lock, irq_flags); if (priv->removed) { sqn_pr_dbg("adapter removed; wait to die...\n"); spin_unlock_irqrestore(&priv->drv_lock, irq_flags); mdelay(1); continue; } spin_unlock_irqrestore(&priv->drv_lock, irq_flags); rv= priv->hw_host_to_card(priv); if (rv) sqn_pr_dbg("failed to send PDU: %d\n", rv); } sqn_pr_leave(); return 0; } int sqn_start_tx_thread(struct sqn_private *priv) { int rv = 0; sqn_pr_enter(); priv->tx_thread = kthread_run(sqn_tx_thread, priv->dev, "sqn_tx"); if (IS_ERR(priv->tx_thread)) { sqn_pr_dbg("error creating TX thread.\n"); rv = 1; goto out; } sqn_pr_leave(); out: return rv; } int sqn_stop_tx_thread(struct sqn_private *priv) { int rv = 0; sqn_pr_enter(); kthread_stop(priv->tx_thread); wake_up_interruptible(&priv->tx_waitq); sqn_pr_leave(); return rv; } /*******************************************************************/ /* RX queue handlers */ /*******************************************************************/ int sqn_rx_process(struct net_device *dev, struct sk_buff *skb) { int rc = 0; struct sqn_private *priv = netdev_priv(dev); #if DRIVER_DEBUG printk(KERN_WARNING "sqn_rx_process \n"); #endif sqn_pr_enter(); dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; priv->stats.rx_packets++; priv->stats.rx_bytes += skb->len; #if SKB_DEBUG sqn_pr_info("%s: push skb [0x%p] to kernel, users %d\n", __func__, skb, atomic_read(&skb->users)); #endif netif_rx(skb); /* netif_receive_skb(skb); */ sqn_pr_leave(); return rc; } /*******************************************************************/ /* Interface statistics */ /*******************************************************************/ static struct net_device_stats *sqn_get_stats(struct net_device *dev) { struct sqn_private *priv = netdev_priv(dev); sqn_pr_enter(); sqn_pr_leave(); return &priv->stats; } /*******************************************************************/ /* Adding and removing procedures */ /*******************************************************************/ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) static const struct net_device_ops sqn_netdev_ops = { .ndo_open = sqn_dev_open , .ndo_stop = sqn_dev_stop , .ndo_start_xmit = sqn_hard_start_xmit , .ndo_validate_addr = eth_validate_addr , .ndo_tx_timeout = sqn_tx_timeout , .ndo_get_stats = sqn_get_stats }; #endif struct sqn_private *sqn_add_card(void *card, struct device *realdev) { struct sqn_private *priv = 0; u8 dummy_wimax_mac_addr[ETH_ALEN] = { 0x00, 0x16, 0x08, 0x00, 0x06, 0x53 }; /* Allocate an Ethernet device and register it */ struct net_device *dev = alloc_netdev(sizeof(struct sqn_private), "wimax%d", ether_setup); sqn_pr_enter(); if (!dev) { sqn_pr_err("init wimaxX device failed\n"); goto done; } priv = netdev_priv(dev); g_priv = priv; memset(priv, 0, sizeof(struct sqn_private)); /* * Use dummy WiMAX mac address for development version (boot from * flash) of WiMAX SDIO cards. Production cards use mac address from * firmware which is loaded by driver. Random ethernet address can't be * used if IPv4 convergence layer is enabled on WiMAX base station. */ memcpy(priv->mac_addr, dummy_wimax_mac_addr, ETH_ALEN); spin_lock_init(&priv->drv_lock); /* Fill the private stucture */ priv->dev = dev; priv->card = card; /* Setup the OS Interface to our functions */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) dev->open = sqn_dev_open; dev->stop = sqn_dev_stop; dev->hard_start_xmit = sqn_hard_start_xmit; dev->tx_timeout = sqn_tx_timeout; dev->get_stats = sqn_get_stats; #else dev->netdev_ops = &sqn_netdev_ops; #endif /* TODO: Make multicast possible */ dev->flags &= ~IFF_MULTICAST; //wimax interface mtu must be 1400 (in spec) dev->mtu = 1400; SET_NETDEV_DEV(dev, realdev); done: sqn_pr_leave(); return priv; } int sqn_remove_card(struct sqn_private *priv) { struct net_device *dev = priv->dev; unsigned long irq_flags = 0; sqn_pr_enter(); dev = priv->dev; spin_lock_irqsave(&priv->drv_lock, irq_flags); priv->removed = 1; priv->dev = NULL; spin_unlock_irqrestore(&priv->drv_lock, irq_flags); /* kthread_stop(priv->tx_thread); */ /* wake_up_interruptible(&priv->tx_waitq); */ free_netdev(dev); sqn_pr_leave(); return 0; } int sqn_start_card(struct sqn_private *priv) { struct net_device *dev = priv->dev; sqn_pr_enter(); if (register_netdev(dev)) { sqn_pr_err("cannot register ethX device\n"); return -1; } sqn_pr_dbg("starting TX thread...\n"); /* TODO: move waitq initializatio to add_card() */ init_waitqueue_head(&priv->tx_waitq); init_waitqueue_head(&priv->rx_waitq); if (sqn_start_tx_thread(priv)) goto err_init_adapter; sqn_pr_info("%s: Sequans WiMAX adapter\n", dev->name); #if IGNORE_CARRIER_STATE netif_carrier_on(priv->dev); #else /* In release version this should be uncommented */ /* netif_carrier_off(priv->dev); */ #endif done: sqn_pr_leave(); return 0; err_init_adapter: /* TODO: Free allocated resources */ sqn_pr_err("error while init adapter\n"); free_netdev(dev); priv = NULL; goto done; } int sqn_stop_card(struct sqn_private *priv) { struct net_device *dev = priv->dev; sqn_pr_enter(); unregister_netdev(dev); sqn_pr_leave(); return 0; }