> On Wed, Aug 10, 2005 at 12:18:42PM -0700, [EMAIL PROTECTED] wrote:
>> I merged my code into the kernel tree and the patch of changes is
>> attached.
>
> Ick, again, please do not send compressed attachments, I can't view them
> directly in the email.
>
> Try just attaching them inline.
>
> thanks,
>
> greg k-h
>
My apologies. Attached is the patch decompressed.
Jimmy Pierce
[EMAIL PROTECTED]
---------------------------------------------
diff -urN linux/drivers/usb/Makefile
linux-2.6.12.1-ax88178/drivers/usb/Makefile
--- linux/drivers/usb/Makefile Wed Aug 10 10:38:36 2005
+++ linux-2.6.12.1-ax88178/drivers/usb/Makefile Wed Aug 10 10:37:37 2005
@@ -45,6 +45,7 @@
obj-$(CONFIG_USB_VICAM) += media/
obj-$(CONFIG_USB_W9968CF) += media/
+obj-$(CONFIG_USB_AX88178) += net/
obj-$(CONFIG_USB_CATC) += net/
obj-$(CONFIG_USB_KAWETH) += net/
obj-$(CONFIG_USB_PEGASUS) += net/
diff -urN linux/drivers/usb/net/Makefile
linux-2.6.12.1-ax88178/drivers/usb/net/Makefile
--- linux/drivers/usb/net/Makefile Wed Aug 10 10:38:44 2005
+++ linux-2.6.12.1-ax88178/drivers/usb/net/Makefile Wed Aug 10 10:37:49 2005
@@ -2,6 +2,7 @@
# Makefile for USB Network drivers
#
+obj-$(CONFIG_USB_AX88178) += ax88178.o
obj-$(CONFIG_USB_CATC) += catc.o
obj-$(CONFIG_USB_KAWETH) += kaweth.o
obj-$(CONFIG_USB_PEGASUS) += pegasus.o
diff -urN linux/drivers/usb/net/Kconfig
linux-2.6.12.1-ax88178/drivers/usb/net/Kconfig
--- linux/drivers/usb/net/Kconfig Wed Aug 10 10:23:45 2005
+++ linux-2.6.12.1-ax88178/drivers/usb/net/Kconfig Wed Aug 10 10:37:53 2005
@@ -7,6 +7,17 @@
menu "USB Network Adapters"
depends on USB && NET
+config USB_AX88178
+ tristate "USB AX88178 Based Ethernet device support"
+ ---help---
+ Say yes here to compile support for the Asix 88178 chipset. The
+ following adapters use this chipset:
+
+ AirLink101 AGIGAUSB
+
+ To compile this driver as a module, choose M here: the
+ module will be called ax88178.
+
config USB_CATC
tristate "USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)"
depends on EXPERIMENTAL
diff -urN linux/drivers/usb/net/ax88178.c
linux-2.6.12.1-ax88178/drivers/usb/net/ax88178.c
--- linux/drivers/usb/net/ax88178.c Wed Dec 31 16:00:00 1969
+++ linux-2.6.12.1-ax88178/drivers/usb/net/ax88178.c Wed Aug 10 10:37:56 2005
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2005 Jimmy Pierce ([EMAIL PROTECTED])
+ *
+ * Based off the rtl8150 driver written by
+ * Petko Manolov <[EMAIL PROTECTED]>
+ * Thanks to Jamie Painter for pointing out the multiple packets
+ * per URB
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.4.0 (2005/07/18)"
+#define DRIVER_AUTHOR "Jimmy Pierce <[EMAIL PROTECTED]>"
+#define DRIVER_DESC "ax88178 usb-ethernet driver driver"
+
+#define INTBUFSIZE 512
+
+#define DEBUG 0
+
+#define AX88178_TX_TIMEOUT (HZ)
+#define RX_SKB_POOL_SIZE 8
+
+#define REG_SSMC_W 0x06
+#define REG_PHY_R 0x07
+#define REG_PHY_W 0x08
+#define REG_SMSR_R 0x09
+#define REG_HSMC_W 0x0a
+#define REG_SROMREAD_R 0x0b
+#define REG_SROMWRITE_W 0x0c
+#define REG_SROMENBL_W 0x0d
+#define REG_SROMDSBL_W 0x0e
+#define REG_RXCTRL_R 0x0f
+#define REG_RXCTRL_W 0x10
+#define REG_IPG_R 0x11
+#define REG_IPG_W 0x12
+#define REG_MAC_R 0x13
+#define REG_MAC_W 0x14
+#define REG_MFAR_R 0x15
+#define REG_MFAR_W 0x16
+#define REG_PHYADDR_R 0x19
+#define REG_MSR_R 0x1a
+#define REG_MMR_W 0x1b
+#define REG_GPIO_R 0x1e
+#define REG_GPIO_W 0x1f
+#define REG_RESET_W 0x20
+
+#define REG_UNKNOWN 0x22
+
+#define AX88178_MTU 16388
+#define AX88178_MAX_MTU 16384
+
+#define AX88178_HW_CRC 0
+#define RX_REG_SET 1
+#define AX88178_UNPLUG 2
+#define RX_URB_FAIL 3
+
+#define AX_READ_REG 0xc0
+#define AX_WRITE_REG 0x40
+
+
+#define STATE_SPEED_CHANGE 0x01
+
+/* table of devices that work with this driver */
+static struct usb_device_id ax88178_table[] = {
+ {USB_DEVICE(0x0b95, 0x1780)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, ax88178_table);
+
+struct ax88178 {
+ unsigned long flags;
+ struct usb_device *udev;
+ wait_queue_head_t speed_queue;
+ pid_t speed_pid;
+ struct tasklet_struct tl;
+ struct net_device_stats stats;
+ struct net_device *netdev;
+ struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb;
+ struct sk_buff *tx_skb, *rx_skb;
+ struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE];
+ spinlock_t rx_pool_lock;
+ struct usb_ctrlrequest dr;
+ int intr_interval;
+ u8 rx_creg[8];
+ u8 *intr_buff;
+ u8 phy;
+ u8 state;
+ u8 ready;
+ u8 rx_skb_fix_data[AX88178_MTU + 16];
+ u8 tx_skb_fix_data[AX88178_MTU + 16];
+};
+
+typedef struct ax88178 ax88178_t;
+
+static unsigned long multicast_filter_limit = 32;
+
+static void fill_skb_pool(ax88178_t *);
+static void free_skb_pool(ax88178_t *);
+static int ax88178_link_up( void* arg );
+static int async_set_registers(ax88178_t * dev, u8 index, u16 regdata1,
u16 regdata2, u16 size);
+static inline struct sk_buff *pull_skb(ax88178_t *);
+static void ax88178_disconnect(struct usb_interface *intf);
+static int ax88178_probe(struct usb_interface *intf,
+ const struct usb_device_id *id);
+
+
+static const char driver_name [] = "ax88178";
+
+static struct usb_driver ax88178_driver = {
+ .owner = THIS_MODULE,
+ .name = driver_name,
+ .probe = ax88178_probe,
+ .disconnect = ax88178_disconnect,
+ .id_table = ax88178_table,
+};
+
+/*
+**
+** device related part of the code
+**
+*/
+static int get_registers(ax88178_t *dev, u8 index, u16 regdata1, u16
regdata2, u16 size, void *data)
+{
+ return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ index, AX_READ_REG,
+ regdata1, regdata2, data, size, HZ / 2);
+}
+
+static int set_registers(ax88178_t *dev, u8 index, u16 regdata1, u16
regdata2, u16 size, void *data)
+{
+
+ return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ index, AX_WRITE_REG,
+ regdata1, regdata2, data, size, HZ / 2);
+}
+
+static int ax88178_link_up( void *arg )
+{
+ ax88178_t *dev = (ax88178_t*)arg;
+ u8 data[8];
+ u8 speed[2];
+ u8 duplex[2];
+ u16 fd;
+
+ while(!test_bit(AX88178_UNPLUG, &dev->flags) )
+ {
+ wait_event_interruptible( dev->speed_queue, dev->state &
STATE_SPEED_CHANGE );
+
+ set_registers( dev, REG_SSMC_W, 0, 0, 0, NULL );
+ get_registers( dev, REG_PHY_R, dev->phy, 0x0005, 2, data );
+
+ duplex[0] = data[0];
+ speed[0] = data[1];
+ get_registers( dev, REG_PHY_R, dev->phy, 0x000a, 2, data );
+
+ speed[1] = data[1];
+ duplex[1] = data[0];
+
+ set_registers( dev, REG_HSMC_W, 0, 0, 0, NULL );
+
+#if DEBUG
+ if( !(speed[0] == 0x45) &&
+ !(speed[0] == 0x00) &&
+ !(speed[0] == 0xc5) )
+ {
+ printk( "Unrecognized speed settings: %02x %02x\n",
speed[0], speed[1] );
+ }
+
+printk( "DUPLEX: %02x %02x, SPEED: %02x %02x\n", duplex[0], duplex[1],
speed[0], speed[1] );
+#endif
+ if( duplex[0] == 0xe1 )
+ {
+ fd = 0x0002;
+#if DEBUG
+printk( "Full Duplex " );
+#endif
+ }
+ else
+ {
+ fd = 0x0000;
+#if DEBUG
+printk( "Half Duplex " );
+#endif
+ }
+ if( (speed[0] & 0xf0) == 0 )
+ {
+ fd |= 0x0104;
+ clear_bit(RX_REG_SET, &dev->flags);
+ set_registers( dev, REG_MMR_W, fd, 0, 0, NULL );
+#if DEBUG
+printk( "10Mbit Link\n" );
+#endif
+ }
+ else if( (speed[0] & 0xf0) == 0x40 )
+ {
+ fd |= 0x0334;
+ clear_bit(RX_REG_SET, &dev->flags);
+ set_registers( dev, REG_MMR_W, fd, 0, 0, NULL );
+#if DEBUG
+printk( "100Mbit Link\n" );
+#endif
+ }
+ else if( (speed[0] & 0xf0) == 0xc0 )
+ {
+ fd |= 0x013f;
+ clear_bit(RX_REG_SET, &dev->flags);
+ set_registers( dev, REG_MMR_W, fd, 0, 0, NULL );
+#if DEBUG
+printk( "1000Mbit Link\n" );
+#endif
+ }
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = 0;
+ data[3] = 0x80;
+ data[4] = 0;
+ data[5] = 0;
+ data[6] = 0;
+ data[7] = 0;
+ set_registers( dev, REG_MFAR_W, 0, 0, 8, data );
+
+ dev->state &= ~STATE_SPEED_CHANGE;
+ if( !dev->ready )
+ {
+ dev->ready = 1;
+ wake_up_interruptible( &dev->speed_queue );
+ }
+ }
+ return 0;
+}
+
+static void set_ctrl_callback(struct urb *urb, struct pt_regs *regs)
+{
+ ax88178_t *dev;
+#if DEBUG
+printk( "SET_CTRL_CALL\n" );
+#endif
+ switch (urb->status) {
+ case 0:
+ break;
+ case -EINPROGRESS:
+ break;
+ case -ENOENT:
+ break;
+ default:
+ warn("ctrl urb status %d", urb->status);
+ }
+ dev = urb->context;
+ clear_bit(RX_REG_SET, &dev->flags);
+}
+
+static int async_set_registers(ax88178_t * dev, u8 index, u16 regdata1,
u16 regdata2, u16 size)
+{
+ int ret;
+
+ if (test_bit(RX_REG_SET, &dev->flags))
+ return -EAGAIN;
+
+ dev->dr.bRequestType = AX_WRITE_REG;
+ dev->dr.bRequest = index;
+ dev->dr.wValue = cpu_to_le16(regdata1);
+ dev->dr.wIndex = cpu_to_le16(regdata2);
+ dev->dr.wLength = cpu_to_le16(size);
+ dev->ctrl_urb->transfer_buffer_length = size;
+ usb_fill_control_urb(dev->ctrl_urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr,
+ dev->rx_creg, size, set_ctrl_callback, dev);
+ if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC)))
+ err("control request submission failed: %d", ret);
+ else
+ set_bit(RX_REG_SET, &dev->flags);
+
+ return ret;
+}
+
+static inline void set_ethernet_addr(ax88178_t * dev)
+{
+ u8 node_id[6];
+
+ get_registers(dev, REG_MAC_R, 0, 0, sizeof(node_id), node_id);
+ memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id));
+}
+
+static int ax88178_set_mac_address(struct net_device *netdev, void *p)
+{
+ struct sockaddr *addr = p;
+ ax88178_t *dev = netdev_priv(netdev);
+ int i;
+
+ if (netif_running(netdev))
+ return -EBUSY;
+
+ memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+ dbg("%s: Setting MAC address to ", netdev->name);
+ for (i = 0; i < 5; i++)
+ dbg("%02X:", netdev->dev_addr[i]);
+ dbg("%02X\n", netdev->dev_addr[i]);
+ return (set_registers(dev, REG_MAC_W, 0, 0, sizeof(netdev->dev_addr),
netdev->dev_addr) < 0);
+}
+
+static int ax88178_reset(ax88178_t * dev)
+{
+ u8 data = 0x4c;
+
+ set_registers(dev, REG_RESET_W, data, 0, 0, 0);
+
+ return 1;
+}
+
+static int alloc_all_urbs(ax88178_t * dev)
+{
+ dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->rx_urb)
+ return 0;
+ dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->tx_urb) {
+ usb_free_urb(dev->rx_urb);
+ return 0;
+ }
+ dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->intr_urb) {
+ usb_free_urb(dev->rx_urb);
+ usb_free_urb(dev->tx_urb);
+ return 0;
+ }
+ dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->intr_urb) {
+ usb_free_urb(dev->rx_urb);
+ usb_free_urb(dev->tx_urb);
+ usb_free_urb(dev->intr_urb);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void free_all_urbs(ax88178_t * dev)
+{
+ usb_free_urb(dev->rx_urb);
+ usb_free_urb(dev->tx_urb);
+ usb_free_urb(dev->intr_urb);
+ usb_free_urb(dev->ctrl_urb);
+}
+
+static void unlink_all_urbs(ax88178_t * dev)
+{
+ usb_kill_urb(dev->rx_urb);
+ usb_kill_urb(dev->tx_urb);
+ usb_kill_urb(dev->intr_urb);
+ usb_kill_urb(dev->ctrl_urb);
+}
+
+static inline struct sk_buff *pull_skb(ax88178_t *dev)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+ if (dev->rx_skb_pool[i]) {
+ skb = dev->rx_skb_pool[i];
+ dev->rx_skb_pool[i] = NULL;
+ return skb;
+ }
+ }
+ return NULL;
+}
+
+static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ ax88178_t *dev = urb->context;
+ unsigned pkt_len, res;
+ unsigned int length;
+ struct net_device *netdev;
+ u8 *pointer;
+#if DEBUG
+printk( "READ CALLBACK!\n" );
+#endif
+ if (!dev)
+ return;
+ if (test_bit(AX88178_UNPLUG, &dev->flags))
+ return;
+ netdev = dev->netdev;
+ if (!netif_device_present(netdev))
+ return;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ return; // the urb is in unlink state
+ case -ETIMEDOUT:
+ warn("may be reset is needed?..");
+ goto goon;
+ default:
+ warn("Rx status %d", urb->status);
+ goto goon;
+ }
+
+ res = urb->actual_length;
+ if( !res )
+ goto goon;
+
+ if (!dev->rx_skb)
+ goto resched;
+ // protect against short packets (tell me why we got some?!?)
+ if (urb->actual_length < 4)
+ goto goon;
+
+ pointer = dev->rx_skb_fix_data;
+
+ length = cpu_to_le16( *((u16*)(pointer)) );
+ pkt_len = res;
+ while( pkt_len > 4 )
+ {
+
+#if DEBUG
+printk( "PROCESSING\n" );
+printk( "count: %d\n", length );
+printk( "data: %02x%02x %02x%02x %02x%02x %02x%02x\n",
+ dev->rx_skb_fix_data[0], dev->rx_skb_fix_data[1],
dev->rx_skb_fix_data[2], dev->rx_skb_fix_data[3],
+ dev->rx_skb_fix_data[4], dev->rx_skb_fix_data[5],
dev->rx_skb_fix_data[6], dev->rx_skb_fix_data[7] );
+printk( "act len: %d last: %02x\n", urb->actual_length,
dev->rx_skb_fix_data[length + 4] );
+#endif
+ if( length <= AX88178_MTU )
+ {
+ memcpy( dev->rx_skb->data, pointer + 4, length );
+
+ skb_put(dev->rx_skb, length);
+ dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev);
+ netif_rx(dev->rx_skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+
+ spin_lock(&dev->rx_pool_lock);
+ dev->rx_skb = pull_skb(dev);
+ spin_unlock(&dev->rx_pool_lock);
+ if (!dev->rx_skb)
+ goto resched;
+
+ }
+ else
+ {
+ printk( "length: %d data: %02x%02x %02x%02x\n", length,
pointer[0],
pointer[1], pointer[2], pointer[3] );
+ }
+
+ if( length & 0x0001 )
+ length++;
+ if( length > pkt_len || length < 0 )
+ printk( "***** urb_len: %d pkt_len: %d %02x%02x
%02x%02x %02x%02x
%02x%02x off: %d\n", urb->actual_length, pkt_len, pointer[-4],
pointer[-3], pointer[-2], pointer[-1], pointer[0], pointer[1], pointer[2],
pointer[3], (int)((int)pointer - (int)dev->rx_skb_fix_data) );
+
+ do{
+ pkt_len -= length + 4;
+ pointer += length + 4;
+ if( pointer > urb->actual_length + dev->rx_skb_fix_data
)
+ {
+ printk( "????? pkt_len: %d %02x%02x %02x%02x
%02x%02x %02x%02x off:
%d\n", pkt_len, pointer[-4], pointer[-3], pointer[-2], pointer[-1],
pointer[0], pointer[1], pointer[2], pointer[3], (int)((int)pointer -
(int)dev->rx_skb_fix_data) );
+ pkt_len = 0;
+ break;
+ }
+ length = cpu_to_le16( *((u16*)(pointer)) );
+ if( length & 0x0001 )
+ length++;
+#if DEBUG
+ printk( "LOOP: length %d pkt: %d\n", length, pkt_len );
+#endif
+ if( pkt_len < 4 )
+ break;
+ }while( length == 0 );
+ }
+goon:
+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2),
+ dev->rx_skb_fix_data, AX88178_MTU, read_bulk_callback,
dev);
+ if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+ set_bit(RX_URB_FAIL, &dev->flags);
+ goto resched;
+ } else {
+ clear_bit(RX_URB_FAIL, &dev->flags);
+ }
+
+ return;
+resched:
+ tasklet_schedule(&dev->tl);
+}
+
+static void rx_fixup(unsigned long data)
+{
+
+ ax88178_t *dev;
+ struct sk_buff *skb;
+
+ dev = (ax88178_t *)data;
+#if DEBUG
+printk( "RX_FIXUP\n" );
+#endif
+ spin_lock_irq(&dev->rx_pool_lock);
+ fill_skb_pool(dev);
+ spin_unlock_irq(&dev->rx_pool_lock);
+ if (test_bit(RX_URB_FAIL, &dev->flags))
+ if (dev->rx_skb)
+ goto try_again;
+ spin_lock_irq(&dev->rx_pool_lock);
+#if DEBUG
+printk( "LOCK\n" );
+#endif
+ skb = pull_skb(dev);
+ spin_unlock_irq(&dev->rx_pool_lock);
+ if (skb == NULL)
+ goto tlsched;
+ dev->rx_skb = skb;
+ usb_fill_bulk_urb(dev->rx_urb, dev->udev,
usb_rcvbulkpipe(dev->udev, 2),
+ dev->rx_skb_fix_data, AX88178_MTU,
read_bulk_callback, dev);
+try_again:
+ if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+ set_bit(RX_URB_FAIL, &dev->flags);
+ if( test_bit(AX88178_UNPLUG, &dev->flags) )
+ return;
+ goto tlsched;
+ } else {
+ clear_bit(RX_URB_FAIL, &dev->flags);
+ }
+
+ return;
+tlsched:
+ tasklet_schedule(&dev->tl);
+
+}
+
+static void write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ ax88178_t *dev;
+#if DEBUG
+printk( "WRITE CALLBACK\n" );
+#endif
+ dev = urb->context;
+ if (!dev)
+ return;
+ dev_kfree_skb_irq(dev->tx_skb);
+ if (!netif_device_present(dev->netdev))
+ return;
+ if (urb->status)
+ info("%s: Tx status %d", dev->netdev->name, urb->status);
+ dev->netdev->trans_start = jiffies;
+ netif_wake_queue(dev->netdev);
+}
+
+static void intr_callback(struct urb *urb, struct pt_regs *regs)
+{
+ ax88178_t *dev;
+ __u8 *d;
+ int status;
+
+ dev = urb->context;
+ if (!dev)
+ return;
+ switch (urb->status) {
+ case 0: // success
+ break;
+ case -ECONNRESET: // unlink
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ // -EPIPE: should clear the halt
+ default:
+ printk("%s: intr status %d", dev->netdev->name, urb->status);
+ goto resubmit;
+ }
+
+ d = urb->transfer_buffer;
+ if (d[2] & 0x04) {
+ dev->stats.tx_errors++;
+ }
+ // Report link status changes to the network stack
+ if ((d[2] & 0x01) == 0) {
+ if (netif_carrier_ok(dev->netdev)) {
+ netif_carrier_off(dev->netdev);
+ printk("%s: LINK LOST\n", __func__);
+ }
+ } else {
+ if (!netif_carrier_ok(dev->netdev)) {
+ dev->state |= STATE_SPEED_CHANGE;
+ wake_up_interruptible( &dev->speed_queue );
+ netif_carrier_on(dev->netdev);
+ printk("%s: LINK CAME BACK\n", __func__);
+ }
+ }
+
+resubmit:
+ status = usb_submit_urb (urb, SLAB_ATOMIC);
+ if (status)
+ err ("can't resubmit intr, %s-%s/input0, status %d",
+ dev->udev->bus->bus_name,
+ dev->udev->devpath, status);
+}
+
+
+//
+//
+// network related part of the code
+//
+
+
+static void fill_skb_pool(ax88178_t *dev)
+{
+ struct sk_buff *skb;
+ int i;
+
+ for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+ if (dev->rx_skb_pool[i])
+ continue;
+ skb = dev_alloc_skb(AX88178_MTU + 6);
+ if (!skb) {
+ return;
+ }
+ skb->dev = dev->netdev;
+ skb_reserve(skb, 2);
+ dev->rx_skb_pool[i] = skb;
+ }
+}
+
+static void free_skb_pool(ax88178_t *dev)
+{
+ int i;
+
+ for (i = 0; i < RX_SKB_POOL_SIZE; i++)
+ if (dev->rx_skb_pool[i])
+ dev_kfree_skb(dev->rx_skb_pool[i]);
+}
+
+static int init_net_registers(ax88178_t * dev)
+{
+ int retval = 0;
+ u8 data[8];
+
+ retval = set_registers( dev, REG_SROMENBL_W, 0, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+ retval = get_registers( dev, REG_SROMREAD_R, 0x0017, 0, 2, data );
+ if( retval < 0 )
+ goto out;
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+#endif
+ retval = set_registers( dev, REG_SROMDSBL_W, 0, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+ retval = set_registers( dev, REG_GPIO_W, 0x008c, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+ retval = set_registers( dev, REG_GPIO_W, 0x001c, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+ retval = set_registers( dev, REG_GPIO_W, 0x003c, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+ retval = set_registers( dev, REG_UNKNOWN, 0, 0, 0, NULL );
+ if( retval < 0 )
+ goto out;
+
+ return 0;
+
+out:
+ printk( "Error encountered while initializing net registers: %d\n",
retval );
+ return retval;
+}
+
+static int enable_net_traffic(ax88178_t * dev)
+{
+
+ int res = 0;
+ u8 data[8];
+
+ res = set_registers( dev, REG_RESET_W, 0x0048, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_RXCTRL_W, 0, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_SSMC_W, 0, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = get_registers( dev, REG_PHYADDR_R, 0, 0, 2, data );
+ if( res < 0 )
+ goto out;
+ dev->phy = data[1];
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+#endif
+ res = get_registers( dev, REG_PHY_R, dev->phy, 0x0002, 2, data );
+ if( res < 0 )
+ goto out;
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+#endif
+ res = get_registers( dev, REG_PHY_R, dev->phy, 0x001b, 2, data );
+ if( res < 0 )
+ goto out;
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+#endif
+ data[0] = 0x00;
+ data[1] = 0x80;
+ res = set_registers( dev, REG_PHY_W, dev->phy, 0, 2, data );
+ if( res < 0 )
+ goto out;
+ res = get_registers( dev, REG_PHY_R, dev->phy, 0x0000, 2, data );
+ if( res < 0 )
+ goto out;
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+ res = get_registers( dev, REG_PHY_R, dev->phy, 0x0018, 2, data );
+#endif
+ if( res < 0 )
+ goto out;
+#if DEBUG
+ printk( "%02x %02x\n", data[0], data[1] );
+#endif
+ data[0] = 0x41;
+ data[1] = 0x43;
+ res = set_registers( dev, REG_PHY_W, dev->phy, 0x0018, 2, data );
+ if( res < 0 )
+ goto out;
+ data[0] = 0xe1;
+ data[1] = 0x05;
+ res = set_registers( dev, REG_PHY_W, dev->phy, 0x0004, 2, data );
+ if( res < 0 )
+ goto out;
+ data[0] = 0x00;
+ data[1] = 0x02;
+ res = set_registers( dev, REG_PHY_W, dev->phy, 0x0009, 2, data );
+ if( res < 0 )
+ goto out;
+ data[0] = 0x00;
+ data[1] = 0x12;
+ res = set_registers( dev, REG_PHY_W, dev->phy, 0x0000, 2, data );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_HSMC_W, 0, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_MMR_W, 0x013f, 0x0000, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_IPG_W, 0x0c15, 0x000e, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_IPG_W, 0x0c15, 0x000e, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_RXCTRL_W, 0x0388, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ res = set_registers( dev, REG_RXCTRL_W, 0x0398, 0, 0, NULL );
+ if( res < 0 )
+ goto out;
+ return 0;
+out:
+ printk( "Error encountered while enabling net traffic: %d\n", res );
+ return res;
+}
+
+static void disable_net_traffic(ax88178_t * dev)
+{
+ set_registers(dev, REG_RXCTRL_W, 0x0318, 0, 0, NULL);
+ set_registers(dev, REG_MMR_W, 0x0276, 0, 0, NULL );
+ set_registers(dev, REG_GPIO_W, 0x0200, 0, 0, NULL );
+}
+
+static struct net_device_stats *ax88178_netdev_stats(struct net_device *dev)
+{
+ return &((ax88178_t *)netdev_priv(dev))->stats;
+}
+
+static void ax88178_tx_timeout(struct net_device *netdev)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ warn("%s: Tx timeout.", netdev->name);
+ dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+ usb_unlink_urb(dev->tx_urb);
+ dev->stats.tx_errors++;
+}
+
+static void ax88178_set_multicast(struct net_device *netdev)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ u16 data = 0x0398;
+ netif_stop_queue(netdev);
+ if (netdev->flags & IFF_PROMISC) {
+ data |= 0x0001;
+ info("%s: promiscuous mode", netdev->name);
+ } else if ((netdev->mc_count > multicast_filter_limit) ||
+ (netdev->flags & IFF_ALLMULTI)) {
+ data |= 0x0002;
+ info("%s: allmulti set", netdev->name);
+ }
+ async_set_registers(dev, REG_RXCTRL_W, data, 0, 0);
+ netif_wake_queue(netdev);
+}
+
+static int ax88178_start_xmit(struct sk_buff *skb, struct net_device
*netdev)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ int count, res;
+ short invheadcount;
+
+
+#if DEBUG
+printk( "XMIT\n" );
+#endif
+
+ netif_stop_queue(netdev);
+ count = skb->len;
+// count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3;
+ memcpy( dev->tx_skb_fix_data + 4, skb->data, count );
+
+ invheadcount = 0xffff - count;
+ memcpy( dev->tx_skb_fix_data, &count, 2 );
+ memcpy( dev->tx_skb_fix_data + 2, &invheadcount, 2 );
+/*
+#if DEBUG
+printk( "count: %d\n", count );
+printk( "data: %02x%02x %02x%02x %02x%02x %02x%02x\n",
+ skb->data[0], skb->data[1], skb->data[2], skb->data[3],
+ skb->data[4], skb->data[5], skb->data[6], skb->data[7] );
+#endif
+*/
+ dev->tx_skb = skb;
+ usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 3),
+ dev->tx_skb_fix_data, count + 4, write_bulk_callback,
dev);
+ if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
+ warn("failed tx_urb %d\n", res);
+ dev->stats.tx_errors++;
+ netif_start_queue(netdev);
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ netdev->trans_start = jiffies;
+ }
+
+ return 0;
+}
+
+
+static int ax88178_open(struct net_device *netdev)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ int res;
+
+ if (dev->rx_skb == NULL)
+ dev->rx_skb = pull_skb(dev);
+ if (!dev->rx_skb)
+ return -ENOMEM;
+
+ dev->ready = 0;
+ set_registers(dev, REG_MAC_W, 0, 0, 6, netdev->dev_addr);
+ dev->state |= STATE_SPEED_CHANGE;
+ dev->speed_pid = kernel_thread(ax88178_link_up, dev,
CLONE_FS|CLONE_FILES );
+ wait_event_interruptible( dev->speed_queue, dev->ready );
+
+ usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 2),
+ dev->rx_skb->data, AX88178_MTU, read_bulk_callback, dev);
+ if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL)))
+ warn("%s: rx_urb submit failed: %d", __FUNCTION__, res);
+ netif_start_queue(netdev);
+ usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 1),
+ dev->intr_buff, INTBUFSIZE, intr_callback,
+ dev, dev->intr_interval);
+ if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL)))
+ warn("%s: intr_urb submit failed: %d", __FUNCTION__, res);
+
+ return res;
+}
+
+static int ax88178_close(struct net_device *netdev)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ int res = 0;
+
+ netif_stop_queue(netdev);
+ unlink_all_urbs(dev);
+ kill_proc( dev->speed_pid, SIGTERM, 1);
+ dev->state = STATE_SPEED_CHANGE;
+ flush_scheduled_work();
+
+ return res;
+}
+
+static void ax88178_get_drvinfo(struct net_device *netdev, struct
ethtool_drvinfo *info)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+
+ strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN);
+ strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
+ usb_make_path(dev->udev, info->bus_info, sizeof info->bus_info);
+}
+
+static int ax88178_get_settings(struct net_device *netdev, struct
ethtool_cmd *ecmd)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ short lpa;
+
+ get_registers( dev, REG_MSR_R, 0, 0, 2, &lpa );
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_TP | SUPPORTED_MII);
+ ecmd->port = PORT_TP;
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->phy_address = dev->phy;
+ ecmd->autoneg = AUTONEG_ENABLE;
+ if( lpa & 0x0001 )
+ ecmd->speed = SPEED_1000;
+ else
+ {
+ if( lpa & 0x0200 )
+ ecmd->speed = SPEED_100;
+ else
+ ecmd->speed = SPEED_10;
+ }
+ if( lpa & 0x0002 )
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static struct ethtool_ops ops = {
+ .get_drvinfo = ax88178_get_drvinfo,
+ .get_settings = ax88178_get_settings,
+ .get_link = ethtool_op_get_link
+};
+
+/*
+static int ax88178_ioctl(struct net_device *netdev, struct ifreq *rq, int
cmd)
+{
+ ax88178_t *dev = netdev_priv(netdev);
+ u16 *data = (u16 *) & rq->ifr_ifru;
+ int res = 0;
+
+ switch (cmd) {
+ case SIOCDEVPRIVATE:
+ data[0] = dev->phy;
+ case SIOCDEVPRIVATE + 1:
+ read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);
+ break;
+ case SIOCDEVPRIVATE + 2:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]);
+ break;
+ default:
+ res = -EOPNOTSUPP;
+ }
+
+ return res;
+}
+*/
+
+static int ax88178_change_mtu( struct net_device *dev, int new_mtu )
+{
+ if( new_mtu > AX88178_MAX_MTU)
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static int ax88178_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ ax88178_t *dev;
+ struct net_device *netdev;
+ int res;
+
+ netdev = alloc_etherdev(sizeof(ax88178_t));
+ if (!netdev) {
+ err("Out of memory");
+ return -ENOMEM;
+ }
+
+ dev = netdev_priv(netdev);
+ memset(dev, 0, sizeof(ax88178_t));
+
+ dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);
+ if (!dev->intr_buff) {
+ free_netdev(netdev);
+ return -ENOMEM;
+ }
+
+ tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev);
+ spin_lock_init(&dev->rx_pool_lock);
+
+ dev->state = 0;
+ init_waitqueue_head( &dev->speed_queue );
+
+
+ dev->udev = udev;
+ dev->netdev = netdev;
+ SET_MODULE_OWNER(netdev);
+
+ netdev->open = ax88178_open;
+ netdev->stop = ax88178_close;
+ netdev->change_mtu = ax88178_change_mtu;
+// netdev->do_ioctl = ax88178_ioctl;
+ netdev->watchdog_timeo = AX88178_TX_TIMEOUT;
+ netdev->tx_timeout = ax88178_tx_timeout;
+ netdev->hard_start_xmit = ax88178_start_xmit;
+ netdev->set_multicast_list = ax88178_set_multicast;
+ netdev->set_mac_address = ax88178_set_mac_address;
+ netdev->get_stats = ax88178_netdev_stats;
+// netdev->mtu = 1500;
+ SET_ETHTOOL_OPS(netdev, &ops);
+ dev->intr_interval = 100; // 100ms
+
+ if (!alloc_all_urbs(dev)) {
+ err("out of memory");
+ goto out;
+ }
+ if (!ax88178_reset(dev)) {
+ err("couldn't reset the device");
+ goto out1;
+ }
+ fill_skb_pool(dev);
+ set_ethernet_addr(dev);
+ info("%s: ax88178 is detected", netdev->name);
+
+ usb_set_intfdata(intf, dev);
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+ if (register_netdev(netdev) != 0) {
+ err("couldn't register the device");
+ goto out2;
+ }
+
+ res = init_net_registers( dev );
+ if( res < 0 )
+ goto out2;
+ enable_net_traffic(dev);
+ return 0;
+
+out2:
+ usb_set_intfdata(intf, NULL);
+ free_skb_pool(dev);
+out1:
+ free_all_urbs(dev);
+out:
+ kfree(dev->intr_buff);
+ free_netdev(netdev);
+ return -EIO;
+}
+
+static void ax88178_disconnect(struct usb_interface *intf)
+{
+ ax88178_t *dev = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (dev) {
+ set_bit(AX88178_UNPLUG, &dev->flags);
+ tasklet_kill( &dev->tl );
+ unregister_netdev(dev->netdev);
+ disable_net_traffic( dev );
+ unlink_all_urbs(dev);
+ free_all_urbs(dev);
+ free_skb_pool(dev);
+ if (dev->rx_skb)
+ dev_kfree_skb(dev->rx_skb);
+ kfree(dev->intr_buff);
+ free_netdev(dev->netdev);
+ }
+}
+
+static int __init usb_ax88178_init(void)
+{
+ info(DRIVER_DESC " " DRIVER_VERSION);
+ return usb_register(&ax88178_driver);
+}
+
+static void __exit usb_ax88178_exit(void)
+{
+ usb_deregister(&ax88178_driver);
+}
+
+module_init(usb_ax88178_init);
+module_exit(usb_ax88178_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel