顯示具有 Linux Kernel 標籤的文章。 顯示所有文章
顯示具有 Linux Kernel 標籤的文章。 顯示所有文章

2016年6月24日 星期五

[Linux Kernel][Gentoo] open ASUS WMI Driver at gentoo (4.4.6-gentoo)

I check each item from “depends on ACPI_WMI” to “depends on ACPI_VIDEO || ACPI_VIDEO = n”.
When I check “depends on HOTPLUG_PCI”, then I find this item wont tick up.
I tick up this function.
I go back to X86 Platform Specific Device Drivers, I find out the item “Asus Notebook WMI Driver” appear on my screen.
I am so happy.
vim drivers/platform/x86/Kconfig +545
config ASUS_WMI
        tristate "ASUS WMI Driver"
        depends on ACPI_WMI
        depends on INPUT
        depends on HWMON
        depends on BACKLIGHT_CLASS_DEVICE
        depends on RFKILL || RFKILL = n
        depends on HOTPLUG_PCI
        depends on ACPI_VIDEO || ACPI_VIDEO = n
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
        select NEW_LEDS
> Bus options (PCI etc.)
    [*] Support for PCI Hotplug  --->
    [*]     PCI Express Hotplug driver

> Device Drivers > X86 Platform Specific Device Drivers
    <M>   ASUS WMI Driver
    <M>     Asus Notebook WMI Driver
Compile my kernel code
sudo make
install module
sudo make modules_install

2016年6月20日 星期一

[Linux Kernel] How to compile ASUS_WMI & ASUS_NB_WMI


I want Fn+F9 can work. So the follow step is procedure for my testing. It work.

make menuconfig

Device Drivers → X86 Platform Specific Device Drivers

Tick up
<M>   WMI  (will open - < >   ASUS WMI Driver)
Tick up
    <M>   ASUS WMI Driver (when tick up this time will tick up another item was called Asus Notebook WMI Driver)
        <M>     Asus Notebook WMI Driver

Check .config

[happy@localhost linux-4.6.2]$ grep -inr "asus_wmi"  .config
3177:CONFIG_ASUS_WMI=m
[happy@localhost linux-4.6.2]$ grep -inr "asus_nb_wmi"  .config
3178:CONFIG_ASUS_NB_WMI=m


How to find the method. I check my .config in my archlinux. The command like below.
zcat /proc/config.gz > .config

And I find my settings in my archlinux have two option have been opened.
CONFIG_ASUS_WMI=m
CONFIG_ASUS_NB_WMI=m

So I decide to download the linux kernel to find how to make those two option appear on my .config.

Why I want try this option, because my touchpad(Fn+F9) no work. I search other information that told me maybe I need this kernel module. So I want to compile it, and try if this module can let my touchpad work or not.

After set up the .config, then we need to start compile our kernel.
There have two method can do this job.

Method 1:
Use genkernel to compile kernel.

A: No specific .config
    genkernel all (Builds all stages — the initrd, kernel image and modules.)

B: Specific .config
    genkernel --config=/path/to/genkernel.conf all

Method 2:
Copy the bzImage to /boot
/usr/src/linux/arch/x86/boot/bzImage –> /boot

Generate initramfs
genkernel --install initramfs

Edit /boot/loader/entries/gentoo.conf
title Gentoo Linux
initrd /initramfs-genkernel-x86_64-4.4.6-gentoo
linux /bzImage
options root=/dev/sda2 rw

Reference:

2016年3月3日 星期四

[Kernel][Linux] How to write kenel driver

Template of Kernel Code
/* example.c */
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int example_init(void) {
    printk("<1>EXAMPLE: init\n");
    return 0;
}

static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");
}

module_init(example_init);
module_exit(example_exit);


Makefile
obj-m := example.o

ifeq ($(KERNELDIR),)
KERNELDIR=/lib/modules/$(shell uname -r)/build
endif

all:
    make -C $(KERNELDIR) M=$(PWD) modules

clean:
    make -C $(KERNELDIR) M=$(PWD) clean

$ sudo insmod ./example.ko
$ sudo rmmod example


Use following comamnd to show any message from our kernel.
$ sudo dmesg | tail






Register as Character Device
Add following code into example.c.
#include <linux/fs.h>


static int example_open(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: open\n");
    return 0;
}

static int example_close(struct inode *inode, struct file *filp) {
    printk("<1>EXAMPLE: close\n");
    return 0;
}

static ssize_t example_read(struct file *filp, char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: read  (size=%zu)\n", size);
    return 0;
}

static ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    return size;
}

static struct file_operations example_fops = {
    .open = example_open,
    .release = example_close,
    .read = example_read,
    .write = example_write,
};
Change example_init
#define EXAMPLE_MAJOR 60
#define EXAMPLE_NAME "example"

static int example_init(void) {
    int result;
    printk("<1>EXAMPLE: init\n");

    /* Register character device */
    result = register_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME, &example_fops);
    if (result < 0) {
        printk("<1>EXAMPLE: Failed to register character device\n");
        return result;
    }

    return 0;
}
Change example_exit
static void example_exit(void) {
    printk("<1>EXAMPLE: exit\n");

    /* Unregister character device */
    unregister_chrdev(EXAMPLE_MAJOR, EXAMPLE_NAME);
}
Compile code (example.c)


Remove the example from kernel
$ sudo rmmod example


Insert the kernel
$ sudo insmod ./example.ko


Create a node under /dev
$ sudo mknod /dev/example c 60 0
# /dev/example (the path of file),c represent Character Device,60 (Major ID),0 (Minor ID)。

$ sudo chmod 666 /dev/example (permission - all can read and write)


Write message to /dev/example
$ echo -n 'abcd' > /dev/example


Show message
$ sudo dmesg | tail


Read from user space.
Kernel space and user space have different storage address, so can’t load them directly.
Must use the API of copy_from_user() to read from user space.


Add follow code to modify example.c
#include <asm/uaccess.h>

ssize_t example_write(struct file *filp, const char *buf, size_t size, loff_t *f_pos) {
    size_t pos;
    uint8_t byte;
    printk("<1>EXAMPLE: write  (size=%zu)\n", size);
    for (pos = 0; pos < size; ++pos) {
        if (copy_from_user(&byte, buf + pos, 1) != 0) {
            break;
        }
        printk("<1>EXAMPLE: write  (buf[%zu] = %02x)\n", pos, (unsigned)byte);
    }
    return pos;
}


If data want copy from kernel space to user space must use copy_to_user().


Remove the example from kernel
$ sudo rmmod example


Insert the kernel
$ sudo insmod ./example.ko


Show message
$ sudo dmesg | tail

Reference:

2015年12月16日 星期三

[Virtualbox][Archlinux] Upgrade vboxdrv.ko after upgrade Linux kernel


  1. search “virtualbox-modules gentoo” on google
  2. Go to “Git repository browser”


    Find out the source code of virtualbox-modules

  3. Download vbox-kernel-module-src-5.0.10.tar.xz from vbox-kernel-module-src
  4. Method 1: pacman -S linux-headers
    Method 2: find out the kernel version
    uname -a
    Ex: my kernel version is 4.10.13.
    Download from internet.

    This is how to compiler kernel code
    Decompress the tarball to build.
    tar xvf linux-4.10.13.tar.gz /lib/modules/4.10.13-1-ARCH/build
    tar to above folder is very import, otherwise ko won't able to insert cause verimage.

    include/generated/autoconf.h is generated in the make prepare step.
    If you are trying to build a kernel module, you will also need the make scripts step:
    cd /lib/modules/4.10.13-1-ARCH/build/linux-4.10.13

    gunzip < /proc/config.gz > .config
    make oldconfig
    make prepare
    make scripts


    tar xvf vbox-kernel-module-src-5.0.10.tar.xz
    type "make" will produce vboxdrv.ko

  5. After reboot will auto mount the kernel module.
    pacman -S virtualbox-host-dkms
    echo vboxdrv >> /etc/modules-load.d/virtualbox.conf
    echo vboxnetflt >> /etc/modules-load.d/virtualbox.conf

Other Work:

Reference:

Q:

=== Building 'vboxdrv' module ===
make[1]: Entering directory '/usr/src/vboxhost-5.2.12_OSE/vboxdrv'
make V= CONFIG_MODULE_SIG= -C /lib/modules/4.16.10-1-ARCH/build SUBDIRS=/usr/src/vboxhost-5.2.12_OSE/vboxdrv SRCROOT=/usr/src/vboxhost-5.2.12_OSE/vboxdrv -j4 modules
make[2]: Entering directory '/usr/lib/modules/4.16.10-1-ARCH/build'
/usr/src/vboxhost-5.2.12_OSE/vboxdrv/Makefile.include.header:141: *** Error: unable to find the headers of the Linux kernel to build against. Specify KERN_VER=<version> (currently 4.16.10-ARCH) and run Make again.  Stop.
make[2]: *** [Makefile:1561: _module_/usr/src/vboxhost-5.2.12_OSE/vboxdrv] Error 2

A:
Patch Makefile

--- Makefile 2017-11-29 00:57:47.000000000 +0800
+++ /tmp/Makefile 2018-05-31 22:08:40.502607341 +0800
@@ -43,10 +43,11 @@
 # convenience Makefile without KBUILD_EXTMOD
 
 KBUILD_VERBOSE =
+KERN_VER := $(shell uname -r)
 
 all:
  @echo "=== Building 'vboxdrv' module ==="
- @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) KERN_VER=$(KERN_VER)  -C vboxdrv
  @cp vboxdrv/vboxdrv.ko .
  @echo
  @if [ -d vboxnetflt ]; then \
@@ -54,7 +55,7 @@
          cp vboxdrv/Module.symvers vboxnetflt; \
      fi; \
      echo "=== Building 'vboxnetflt' module ==="; \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt || exit 1; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxnetflt || exit 1; \
      cp vboxnetflt/vboxnetflt.ko .; \
      echo; \
  fi
@@ -63,7 +64,7 @@
          cp vboxdrv/Module.symvers vboxnetadp; \
      fi; \
      echo "=== Building 'vboxnetadp' module ==="; \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetadp || exit 1; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxnetadp || exit 1; \
      cp vboxnetadp/vboxnetadp.ko .; \
      echo; \
  fi
@@ -72,21 +73,21 @@
          cp vboxdrv/Module.symvers vboxpci; \
      fi; \
      echo "=== Building 'vboxpci' module ==="; \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxpci || exit 1; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxpci || exit 1; \
      cp vboxpci/vboxpci.ko .; \
      echo; \
  fi
 
 install:
- @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv install
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxdrv install
  @if [ -d vboxnetflt ]; then \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetflt install; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxnetflt install; \
  fi
  @if [ -d vboxnetadp ]; then \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxnetadp install; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxnetadp install; \
  fi
  @if [ -d vboxpci ]; then \
-     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxpci install; \
+     $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxpci install; \
  fi
 
 clean:
@@ -103,7 +104,7 @@
  rm -f vboxdrv.ko vboxnetflt.ko vboxnetadp.ko vboxpci.ko
 
 check:
- @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) -C vboxdrv check
+ @$(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE)  KERN_VER=$(KERN_VER) -C vboxdrv check
 
 unload:
  @for module in vboxpci vboxnetadp vboxnetflt vboxdrv; do \


2015年12月12日 星期六

[Kernel][Archlinux] Replace new kernel method

  1. Install package
    pacman -S bc

  2. Download kernel source
    tar xvf linux-4.3.2.tar.xz -C /usr/src/
    cd /usr/src//linux-4.3.2/
    make mrproper (Ensures that the kernel tree is absolutely clean.)

  3. Use system configuration file to compile kernel
    zcat /proc/config.gz > .config
  4. Start to compile
    make
    make modules_install
    This copies the compiled modules into /lib/modules/ARCH-X.X.X/
  5. Copy the kernel to /boot directory
    cp -v arch/x86/boot/bzImage /boot/vmlinuz-ARCH-4.3.2
  6. Make initial RAM disk
    mkinitcpio -k FullKernelName -c /etc/mkinitcpio.conf -g /boot/initramfs-YourKernelName.img

    Check out which is FullKernelName under /lib/modules
    I have 4.3.2-ARCH-4.3.2 which I create and under my /lib/modules.

    Ex:
    sudo mkinitcpio -k 4.3.2-ARCH-4.3.2 -c /etc/mkinitcpio.conf -g /boot/initramfs-linux-ARCH-4.3.2
    ==> Starting build: 4.3.2-ARCH-4.3.2
    -> Running build hook: [base]
    -> Running build hook: [udev]
    -> Running build hook: [autodetect]
    -> Running build hook: [modconf]
    -> Running build hook: [block]
    -> Running build hook: [filesystems]
    -> Running build hook: [keyboard]
    -> Running build hook: [fsck]
    ==> Generating module dependencies
    ==> Creating gzip-compressed initcpio image: /boot/initramfs-linux-ARCH-4.3.2
    ==> Image generation successful
  7. Switch kernel to new one (Quick method)
    initramfs-linux-ARCH-4.3.2 (size: 3.6M) (Initial RAM disk)
    vmlinuz-ARCH-4.3.2 (size: 4.4M) (Kernel)

    cd /boot

    Backup the original one
    cp vmlinuz-linux{,.ori}
    cp initramfs-linux.img{,.ori}


    Use new kernel to replace old one
    cp vmlinuz-ARCH-4.3.2 vmlinuz-linux
    cp initramfs-linux-ARCH-4.3.2 initramfs-linux.img


    Reference:

Virtualbox:

2015年8月16日 星期日

[Net Driver][uptech USB ether][Archlinux] dm9620 driver


Pre install kernel header

sudo pacman -S –noconfirm linux-headers

uptech USB ether - NET 101 USB 2.0

dm9620.c - Source code

/*
 * Davicom DM9620 USB 2.0 10/100Mbps ethernet devices
 *
 * Peter Korsgaard <jacmet@sunsite.dk>
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2.  This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 * V1.0 - ftp fail fixed
 * V1.1 - model name checking, & ether plug function enhancement [0x4f, 0x20]
 * V1.2 - init tx/rx checksum
 *      - fix dm_write_shared_word(), bug fix
 *      - fix 10 Mbps link at power saving mode fail  
 * V1.3 - Support kernel 2.6.31
 * V1.4 - Support eeprom write of ethtool 
 *        Support DM9685
 *        Transmit Check Sum Control by Optopn (Source Code Default: Disable)
 *        Recieve Drop Check Sum Error Packet Disable as chip default
 */

//#define DEBUG


#include <linux/module.h>
//#include <linux/kernel.h> // new v1.3
#include <linux/sched.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/usb.h>
#include <linux/crc32.h>
#include <linux/usb/usbnet.h>
#include <linux/ctype.h>
#include <linux/skbuff.h>   
#include <linux/version.h> // new v1.3

/* datasheet:
 http://www.davicom.com.tw
*/

/* control requests */
#define DM_READ_REGS    0x00
#define DM_WRITE_REGS   0x01
#define DM_READ_MEMS    0x02
#define DM_WRITE_REG    0x03
#define DM_WRITE_MEMS   0x05
#define DM_WRITE_MEM    0x07

/* registers */
#define DM_NET_CTRL 0x00
#define DM_RX_CTRL  0x05
#define DM_SHARED_CTRL  0x0b
#define DM_SHARED_ADDR  0x0c
#define DM_SHARED_DATA  0x0d    /* low + high */
#define DM_EE_PHY_L 0x0d
#define DM_EE_PHY_H 0x0e
#define DM_WAKEUP_CTRL  0x0f
#define DM_PHY_ADDR 0x10    /* 6 bytes */
#define DM_MCAST_ADDR   0x16    /* 8 bytes */
#define DM_GPR_CTRL 0x1e
#define DM_GPR_DATA 0x1f
#define DM_XPHY_CTRL    0x2e
#define DM_TX_CRC_CTRL  0x31
#define DM_RX_CRC_CTRL  0x32 
#define DM_SMIREG       0x91
#define USB_CTRL    0xf4
#define PHY_SPEC_CFG    20

#define MD96XX_EEPROM_MAGIC 0x9620
#define DM_MAX_MCAST    64
#define DM_MCAST_SIZE   8
#define DM_EEPROM_LEN   256
#define DM_TX_OVERHEAD  2   /* 2 byte header */
#define DM_RX_OVERHEAD_9601 7   /* 3 byte header + 4 byte crc tail */
#define DM_RX_OVERHEAD      8   /* 4 byte header + 4 byte crc tail */
#define DM_TIMEOUT  1000
#define DM_MODE9620     0x80
#define DM_TX_CS_EN 0        /* Transmit Check Sum Control */

struct dm96xx_priv {
    int flag_fail_count;
    u8     mode_9620;   
};     


static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
{
    devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
    return usb_control_msg(dev->udev,
                   usb_rcvctrlpipe(dev->udev, 0),
                   DM_READ_REGS,
                   USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                   0, reg, data, length, USB_CTRL_SET_TIMEOUT);
}

static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
{
    return dm_read(dev, reg, 1, value);
}

static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
{
    devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
    return usb_control_msg(dev->udev,
                   usb_sndctrlpipe(dev->udev, 0),
                   DM_WRITE_REGS,
                   USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
                   0, reg, data, length, USB_CTRL_SET_TIMEOUT);
}

static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
{
    devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
    return usb_control_msg(dev->udev,
                   usb_sndctrlpipe(dev->udev, 0),
                   DM_WRITE_REG,
                   USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE,
                   value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT);
}

static void dm_write_async_callback(struct urb *urb)
{
    struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;

    if (urb->status < 0)
        printk(KERN_DEBUG "dm_write_async_callback() failed with %d\n",
               urb->status);

    kfree(req);
    usb_free_urb(urb);
}

static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
                  u16 length, void *data)
{
    struct usb_ctrlrequest *req;
    struct urb *urb;
    int status;

    urb = usb_alloc_urb(0, GFP_ATOMIC);
    if (!urb) {
        deverr(dev, "Error allocating URB in dm_write_async_helper!");
        return;
    }

    req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
    if (!req) {
        deverr(dev, "Failed to allocate memory for control request");
        usb_free_urb(urb);
        return;
    }

    req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
    req->bRequest = length ? DM_WRITE_REGS : DM_WRITE_REG;
    req->wValue = cpu_to_le16(value);
    req->wIndex = cpu_to_le16(reg);
    req->wLength = cpu_to_le16(length);

    usb_fill_control_urb(urb, dev->udev,
                 usb_sndctrlpipe(dev->udev, 0),
                 (void *)req, data, length,
                 dm_write_async_callback, req);

    status = usb_submit_urb(urb, GFP_ATOMIC);
    if (status < 0) {
        deverr(dev, "Error submitting the control message: status=%d",
               status);
        kfree(req);
        usb_free_urb(urb);
    }
}

static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
{
    devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);

    dm_write_async_helper(dev, reg, 0, length, data);
}

static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
{
    devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
           reg, value);

    dm_write_async_helper(dev, reg, value, 0, NULL);
}

static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
{
    int ret, i;

    mutex_lock(&dev->phy_mutex);

    dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
    dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4);

    for (i = 0; i < DM_TIMEOUT; i++) {
        u8 tmp;

        udelay(1);
        ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
        if (ret < 0)
            goto out;

        /* ready */
        if ((tmp & 1) == 0)
            break;
    }

    if (i == DM_TIMEOUT) {
        deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }

    dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
    ret = dm_read(dev, DM_SHARED_DATA, 2, value);

    devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
           phy, reg, *value, ret);

 out:
    mutex_unlock(&dev->phy_mutex);
    return ret;
}

static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 value)
{
    int ret, i;

    mutex_lock(&dev->phy_mutex);

    ret = dm_write(dev, DM_SHARED_DATA, 2, &value);
    if (ret < 0)
        goto out;

    dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
    //dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x0a : 0x12);
    if (!phy) dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
    dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x0a : 0x12);
    dm_write_reg(dev, DM_SHARED_CTRL, 0x10);

    for (i = 0; i < DM_TIMEOUT; i++) {
        u8 tmp;

        udelay(1);
        ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
        if (ret < 0)
            goto out;

        /* ready */
        if ((tmp & 1) == 0)
            break;
    }

    if (i == DM_TIMEOUT) {
        deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }

    dm_write_reg(dev, DM_SHARED_CTRL, 0x0);

out:
    mutex_unlock(&dev->phy_mutex);
    return ret;
}

static int dm_write_eeprom_word(struct usbnet *dev, int phy, u8 offset, u8 value)
{
    int ret, i;
    u8  reg,dloc;
    __le16 eeword;

    //devwarn(dev, " offset =0x%x value = 0x%x ", offset,value);

    /* hank: from offset to determin eeprom word register location,reg */
    reg = (offset >> 1)&0xff;

    /* hank:  high/low byte by odd/even of offset  */
    dloc = (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L;

    /* retrieve high and low byte from the corresponding reg*/
    ret=dm_read_shared_word(dev,0,reg,&eeword);
    //devwarn(dev, " reg =0x%x dloc = 0x%x eeword = 0x%4x", reg,dloc,eeword);

    mutex_lock(&dev->phy_mutex);
    /* hank: write data to eeprom high/low byte reg */
     dm_write(dev, (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L, 1, &value);

    /* load the unaffected word to value */
    (offset & 0x01)? (value = eeword << 8):(value = eeword >> 8);

    /* write the not modified 8 bits back to its origional high/low byte reg */ 
    dm_write(dev, (offset & 0x01)? DM_EE_PHY_L:DM_EE_PHY_H, 1, &value);
    if (ret < 0)
        goto out;

    /* hank : write word location to reg 0x0c  */
    ret = dm_write_reg(dev, DM_SHARED_ADDR, reg);

    if (!phy) dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
    dm_write_reg(dev, DM_SHARED_CTRL, 0x12);
    dm_write_reg(dev, DM_SHARED_CTRL, 0x10);

    for (i = 0; i < DM_TIMEOUT; i++) {
        u8 tmp;

        udelay(1);
        ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp);
        if (ret < 0)
            goto out;

        /* ready */
        if ((tmp & 1) == 0)
            break;
    }

    if (i == DM_TIMEOUT) {
        deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }

    //dm_write_reg(dev, DM_SHARED_CTRL, 0x0);

out:
    mutex_unlock(&dev->phy_mutex);
    return ret;
}
static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
{
    return dm_read_shared_word(dev, 0, offset, value);
}


static int dm9620_set_eeprom(struct net_device *net,struct ethtool_eeprom *eeprom, u8 *data)
{
    struct usbnet *dev = netdev_priv(net);

    devwarn(dev, "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
    if (eeprom->magic != MD96XX_EEPROM_MAGIC) {
        devwarn(dev, "EEPROM: magic value mismatch, magic = 0x%x",
            eeprom->magic);
        return -EINVAL;
    }

        if(dm_write_eeprom_word(dev, 0, eeprom->offset, *data) < 0)  
        return -EINVAL;

    return 0;
}

static int dm9620_get_eeprom_len(struct net_device *dev)
{
    return DM_EEPROM_LEN;
}

static int dm9620_get_eeprom(struct net_device *net,
                 struct ethtool_eeprom *eeprom, u8 * data)
{
    struct usbnet *dev = netdev_priv(net);
    __le16 *ebuf = (__le16 *) data;
    int i;

    /* access is 16bit */
    if ((eeprom->offset % 2) || (eeprom->len % 2))
        return -EINVAL;

    for (i = 0; i < eeprom->len / 2; i++) {
        if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i,
                    &ebuf[i]) < 0)
            return -EINVAL;
    }
    return 0;
}

static int dm9620_mdio_read(struct net_device *netdev, int phy_id, int loc)
{
    struct usbnet *dev = netdev_priv(netdev);

    __le16 res;

    if (phy_id) {
        devdbg(dev, "Only internal phy supported");
        return 0;
    }

    dm_read_shared_word(dev, 1, loc, &res);

    devdbg(dev,
           "dm9620_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
           phy_id, loc, le16_to_cpu(res));

    return le16_to_cpu(res);
}

static void dm9620_mdio_write(struct net_device *netdev, int phy_id, int loc,
                  int val)
{
    struct usbnet *dev = netdev_priv(netdev);
    __le16 res = cpu_to_le16(val);
    int mdio_val;

    if (phy_id) {
        devdbg(dev, "Only internal phy supported");
        return;
    }

    devdbg(dev,"dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
           phy_id, loc, val);

    dm_write_shared_word(dev, 1, loc, res);
    mdelay(1);
    mdio_val = dm9620_mdio_read(netdev, phy_id, loc);

}

static void dm9620_get_drvinfo(struct net_device *net,
                   struct ethtool_drvinfo *info)
{
    /* Inherit standard device info */
    usbnet_get_drvinfo(net, info);
    info->eedump_len = DM_EEPROM_LEN;
}

static u32 dm9620_get_link(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);

    return mii_link_ok(&dev->mii);
}

static int dm9620_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
    struct usbnet *dev = netdev_priv(net);

    return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}


#define DM_LINKEN  (1<<5)
#define DM_MAGICEN (1<<3)
#define DM_LINKST  (1<<2)
#define DM_MAGICST (1<<0)

static void
dm9620_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
    struct usbnet *dev = netdev_priv(net);
    u8 opt;

    if (dm_read_reg(dev, DM_WAKEUP_CTRL, &opt) < 0) {
        wolinfo->supported = 0;
        wolinfo->wolopts = 0;
        return;
    }
    wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
    wolinfo->wolopts = 0;

    if (opt & DM_LINKEN)
        wolinfo->wolopts |= WAKE_PHY;
    if (opt & DM_MAGICEN)
        wolinfo->wolopts |= WAKE_MAGIC;
}


static int
dm9620_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
{
    struct usbnet *dev = netdev_priv(net);
    u8 opt = 0;

    if (wolinfo->wolopts & WAKE_PHY)
        opt |= DM_LINKEN;
    if (wolinfo->wolopts & WAKE_MAGIC)
        opt |= DM_MAGICEN;

    dm_write_reg(dev, DM_NET_CTRL, 0x48);  // enable WAKEEN 

    dm_write_reg(dev, 0x92, 0x3f); //keep clock on Hank Jun 30

    return dm_write_reg(dev, DM_WAKEUP_CTRL, opt);
}

static struct ethtool_ops dm9620_ethtool_ops = {
    .get_drvinfo    = dm9620_get_drvinfo,
    .get_link   = dm9620_get_link,
    .get_msglevel   = usbnet_get_msglevel,
    .set_msglevel   = usbnet_set_msglevel,
    .get_eeprom_len = dm9620_get_eeprom_len,
    .get_eeprom = dm9620_get_eeprom,
    .set_eeprom = dm9620_set_eeprom,
    .get_settings   = usbnet_get_settings,
    .set_settings   = usbnet_set_settings,
    .nway_reset = usbnet_nway_reset,
    .get_wol    = dm9620_get_wol,
    .set_wol    = dm9620_set_wol,
};

static void dm9620_set_multicast(struct net_device *net)
{
    struct usbnet *dev = netdev_priv(net);
    /* We use the 20 byte dev->data for our 8 byte filter buffer
     * to avoid allocating memory that is tricky to free later */
    u8 *hashes = (u8 *) & dev->data;
    u8 rx_ctl = 0x31;

    memset(hashes, 0x00, DM_MCAST_SIZE);
    hashes[DM_MCAST_SIZE - 1] |= 0x80;  /* broadcast address */

    if (net->flags & IFF_PROMISC) {
        rx_ctl |= 0x02;
    } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) {
        rx_ctl |= 0x04;
    } else if (net->mc_count) {
        struct dev_mc_list *mc_list = net->mc_list;
        int i;

        for (i = 0; i < net->mc_count; i++, mc_list = mc_list->next) {
            u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
            hashes[crc >> 3] |= 1 << (crc & 0x7);
        }
    }

    dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
    dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
}


 static void __dm9620_set_mac_address(struct usbnet *dev)
 {
         dm_write_async(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr);
 }

 static int dm9620_set_mac_address(struct net_device *net, void *p)
 {
         struct sockaddr *addr = p;
         struct usbnet *dev = netdev_priv(net);
     int i;

#if 1
     printk("[dm96] Set mac addr %pM\n", addr->sa_data);  // %x:%x:...
     printk("[dm96] ");
     for (i=0; i<net->addr_len; i++)
     printk("[%02x] ", addr->sa_data[i]);
     printk("\n");
 #endif

         if (!is_valid_ether_addr(addr->sa_data)) {
                 dev_err(&net->dev, "not setting invalid mac address %pM\n",
                                                                 addr->sa_data);
                 return -EINVAL;
         }

         memcpy(net->dev_addr, addr->sa_data, net->addr_len);
         __dm9620_set_mac_address(dev);

         return 0;
 }

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 

static const struct net_device_ops vm_netdev_ops= { // new kernel 2.6.31  (20091217JJ)

            .ndo_open               = usbnet_open,  
            .ndo_stop               = usbnet_stop,  
            .ndo_start_xmit         = usbnet_start_xmit, 
            .ndo_tx_timeout         = usbnet_tx_timeout, 
            .ndo_change_mtu         = usbnet_change_mtu, 
            .ndo_validate_addr      = eth_validate_addr, 
        .ndo_do_ioctl       = dm9620_ioctl,   
        .ndo_set_multicast_list = dm9620_set_multicast,   
            .ndo_set_mac_address    = dm9620_set_mac_address,  
};
#endif

static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
{
    int ret,mdio_val,i;
    struct dm96xx_priv* priv;
    u8 temp;

    ret = usbnet_get_endpoints(dev, intf);
    if (ret)
        goto out;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
    dev->net->netdev_ops = &vm_netdev_ops; // new kernel 2.6.31  (20091217JJ)
    dev->net->ethtool_ops = &dm9620_ethtool_ops;
#else
    dev->net->do_ioctl = dm9620_ioctl;  
    dev->net->set_multicast_list = dm9620_set_multicast;
    dev->net->ethtool_ops = &dm9620_ethtool_ops;
#endif
    dev->net->hard_header_len += DM_TX_OVERHEAD;
    dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
    dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD+1; // ftp fail fixed

    dev->mii.dev = dev->net;
    dev->mii.mdio_read = dm9620_mdio_read;
    dev->mii.mdio_write = dm9620_mdio_write;
    dev->mii.phy_id_mask = 0x1f;
    dev->mii.reg_num_mask = 0x1f;

    /* reset */
    dm_write_reg(dev, DM_XPHY_CTRL, 0); // dm9622/dm9685, bit[5](EXTERNAL), clear-it, add clk & limit to internal PHY
    dm_write_reg(dev, DM_NET_CTRL, 1);
    udelay(20);



    /* Add V1.1, Enable auto link while plug in RJ45, Hank July 20, 2009*/
    dm_write_reg(dev, USB_CTRL, 0x20); 
    /* read MAC */
    if (dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr) < 0) {
        printk(KERN_ERR "Error reading MAC address\n");
        ret = -ENODEV;
        goto out;
    }

#if 1
     printk("[dm96] Chk mac addr %pM\n", dev->net->dev_addr);  // %x:%x...
     printk("[dm96] ");
     for (i=0; i<ETH_ALEN; i++)
     printk("[%02x] ", dev->net->dev_addr[i]);
     printk("\n");
#endif

    /* read SMI mode register */
        priv = dev->driver_priv = kmalloc(sizeof(struct dm96xx_priv), GFP_ATOMIC);
    if (!priv) {
        deverr(dev, "Failed to allocate memory for dm96xx_priv");
        ret = -ENOMEM;
        goto out;
    }

        /* work-around for 9620 mode */
    printk("[dm96] Fixme: work around for 9620 mode\n");
    printk("[dm96] Add tx_fixup() debug...\n");
    dm_write_reg(dev, DM_MCAST_ADDR, 0);     // clear data bus to 0s
    dm_read_reg(dev, DM_MCAST_ADDR, &temp);  // clear data bus to 0s
    ret = dm_read_reg(dev, DM_SMIREG, &temp);   // Must clear data bus before we can read the 'MODE9620' bit

    priv->flag_fail_count= 0;
    if (ret<0) {
        printk(KERN_ERR "[dm96] Error read SMI register\n");
    }
    else priv->mode_9620 = temp & DM_MODE9620;

    printk(KERN_WARNING "[dm96] 9620 Mode = %d\n", priv->mode_9620);

    /* power up phy */
    dm_write_reg(dev, DM_GPR_CTRL, 1);
    dm_write_reg(dev, DM_GPR_DATA, 0);

    /* Init tx/rx checksum */
#if     DM_TX_CS_EN
    dm_write_reg(dev, DM_TX_CRC_CTRL, 7);
#endif 
    dm_write_reg(dev, DM_RX_CRC_CTRL, 2);

    /* receive broadcast packets */
    dm9620_set_multicast(dev->net);
    dm9620_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);

    /* Hank add, work for comapubility issue (10M Power control) */ 

    dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
    //printk("[dm96] dm962++ write phy[20]= 0x800\n");
    mdio_val = dm9620_mdio_read(dev->net, dev->mii.phy_id, PHY_SPEC_CFG);
    //printk("[dm96] dm962++ read  phy[20]= %x\n",mdio_val );

    dm9620_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
              ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
    mii_nway_restart(&dev->mii); 

out:
    return ret;
}

void dm9620_unbind(struct usbnet *dev, struct usb_interface *intf)
{
struct dm96xx_priv* priv= dev->driver_priv;
//u8 opt=0;
//int i;
    printk("dm9620_unbind():\n");

    printk("flag_fail_count  %lu\n", (long unsigned int)priv->flag_fail_count);
    kfree(dev->driver_priv); // displayed dev->.. above, then can free dev 

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
    printk("rx_length_errors %lu\n",dev->net->stats.rx_length_errors);
    printk("rx_over_errors   %lu\n",dev->net->stats.rx_over_errors  );
    printk("rx_crc_errors    %lu\n",dev->net->stats.rx_crc_errors   );
    printk("rx_frame_errors  %lu\n",dev->net->stats.rx_frame_errors );
    printk("rx_fifo_errors   %lu\n",dev->net->stats.rx_fifo_errors  );
    printk("rx_missed_errors %lu\n",dev->net->stats.rx_missed_errors);  
#else
    printk("rx_length_errors %lu\n",dev->stats.rx_length_errors);
    printk("rx_over_errors   %lu\n",dev->stats.rx_over_errors  );
    printk("rx_crc_errors    %lu\n",dev->stats.rx_crc_errors   );
    printk("rx_frame_errors  %lu\n",dev->stats.rx_frame_errors );
    printk("rx_fifo_errors   %lu\n",dev->stats.rx_fifo_errors  );
    printk("rx_missed_errors %lu\n",dev->stats.rx_missed_errors);   
#endif

// check if dm9620 receive magic packet
//i=dm_read_reg(dev, DM_WAKEUP_CTRL, &opt);
//  printk("rx_magic_packet   %lu\n",i);

}

static int dm9620_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
    u8 status;
    int len;
    struct dm96xx_priv* priv = (struct dm96xx_priv *)dev->driver_priv;

    /* 9620 format:
       b0: rx status
       b1: packet length (incl crc) low
       b2: packet length (incl crc) high
       b3..n-4: packet data
       bn-3..bn: ethernet crc
     */

    /* 9620 format:
       one additional byte then 9620 : 
       rx_flag in the first pos
     */

    if (unlikely(skb->len < DM_RX_OVERHEAD_9601)) {   // 20090623
        dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
        return 0;
    }

    if (priv->mode_9620) {
        /* mode 9620 */

        if (unlikely(skb->len < DM_RX_OVERHEAD)) {  // 20090623
            dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
            return 0;
        }

    //. struct dm96xx_priv* priv= dev->driver_priv;
    //  if (skb->data[0]!=0x01)
    //      priv->flag_fail_count++;

        status = skb->data[1];
        len = (skb->data[2] | (skb->data[3] << 8)) - 4;

        if (unlikely(status & 0xbf)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
            if (status & 0x01) dev->net->stats.rx_fifo_errors++;
            if (status & 0x02) dev->net->stats.rx_crc_errors++;
            if (status & 0x04) dev->net->stats.rx_frame_errors++;
            if (status & 0x20) dev->net->stats.rx_missed_errors++;
            if (status & 0x90) dev->net->stats.rx_length_errors++;
#else
            if (status & 0x01) dev->stats.rx_fifo_errors++;
            if (status & 0x02) dev->stats.rx_crc_errors++;
            if (status & 0x04) dev->stats.rx_frame_errors++;
            if (status & 0x20) dev->stats.rx_missed_errors++;
            if (status & 0x90) dev->stats.rx_length_errors++;
#endif
            return 0;
        }

        skb_pull(skb, 4);
        skb_trim(skb, len);

    }
    else { /* mode 9620 (original driver code) */
        status = skb->data[0];
        len = (skb->data[1] | (skb->data[2] << 8)) - 4;

        if (unlikely(status & 0xbf)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
            if (status & 0x01) dev->net->stats.rx_fifo_errors++;
            if (status & 0x02) dev->net->stats.rx_crc_errors++;
            if (status & 0x04) dev->net->stats.rx_frame_errors++;
            if (status & 0x20) dev->net->stats.rx_missed_errors++;
            if (status & 0x90) dev->net->stats.rx_length_errors++;
#else
            if (status & 0x01) dev->stats.rx_fifo_errors++;
            if (status & 0x02) dev->stats.rx_crc_errors++;
            if (status & 0x04) dev->stats.rx_frame_errors++;
            if (status & 0x20) dev->stats.rx_missed_errors++;
            if (status & 0x90) dev->stats.rx_length_errors++;
#endif
            return 0;
        }

        skb_pull(skb, 3);
        skb_trim(skb, len);
    }

    return 1;
} // 'priv'

static struct sk_buff *dm9620_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
                       gfp_t flags)
{
    int len;

    /* format:
       b0: packet length low
       b1: packet length high
       b3..n: packet data
    */

    len = skb->len;

    if (skb_headroom(skb) < DM_TX_OVERHEAD) {
        struct sk_buff *skb2;
        skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
        dev_kfree_skb_any(skb);
        skb = skb2;
        if (!skb)
            return NULL;
    }

    __skb_push(skb, DM_TX_OVERHEAD);

    /* usbnet adds padding if length is a multiple of packet size
       if so, adjust length value in header */
    if ((skb->len % dev->maxpacket) == 0)
        len++;

    skb->data[0] = len;
    skb->data[1] = len >> 8;

    /* hank, recalcute checksum of TCP */


    return skb;
} // 'kb'

static void dm9620_status(struct usbnet *dev, struct urb *urb)
{
    int link;
    u8 *buf;

    /* format:
       b0: net status
       b1: tx status 1
       b2: tx status 2
       b3: rx status
       b4: rx overflow
       b5: rx count
       b6: tx count
       b7: gpr
    */

    if (urb->actual_length < 8)
        return;

    buf = urb->transfer_buffer;

    link = !!(buf[0] & 0x40);
    if (netif_carrier_ok(dev->net) != link) {
        if (link) {
            netif_carrier_on(dev->net);
            usbnet_defer_kevent (dev, EVENT_LINK_RESET);
        }
        else
            netif_carrier_off(dev->net);
        devdbg(dev, "Link Status is: %d", link);
    }
}

static int dm9620_link_reset(struct usbnet *dev)
{
    struct ethtool_cmd ecmd;
    mii_check_media(&dev->mii, 1, 1);
    mii_ethtool_gset(&dev->mii, &ecmd);
    /* hank add*/
dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
    devdbg(dev, "link_reset() speed: %d duplex: %d",
           ecmd.speed, ecmd.duplex);

    return 0;
}

static const struct driver_info dm9620_info = {
    .description    = "Davicom DM9620 USB Ethernet",
    .flags      = FLAG_ETHER,
    .bind       = dm9620_bind,
    .rx_fixup   = dm9620_rx_fixup,
    .tx_fixup   = dm9620_tx_fixup,
    .status     = dm9620_status,
    .link_reset = dm9620_link_reset,
    .reset      = dm9620_link_reset,
    .unbind     = dm9620_unbind,
};

static const struct usb_device_id products[] = {
    {
     USB_DEVICE(0x07aa, 0x9601),    /* Corega FEther USB-TXC */
     .driver_info = (unsigned long)&dm9620_info,
     },
    {
     USB_DEVICE(0x0a46, 0x9601),    /* Davicom USB-100 */
     .driver_info = (unsigned long)&dm9620_info,
     },
    {
     USB_DEVICE(0x0a46, 0x6688),    /* ZT6688 USB NIC */
     .driver_info = (unsigned long)&dm9620_info,
     },
    {
     USB_DEVICE(0x0a46, 0x0268),    /* ShanTou ST268 USB NIC */
     .driver_info = (unsigned long)&dm9620_info,
     },
    {
     USB_DEVICE(0x0a46, 0x8515),    /* ADMtek ADM8515 USB NIC */
     .driver_info = (unsigned long)&dm9620_info,
     },
    {
    USB_DEVICE(0x0a47, 0x9601), /* Hirose USB-100 */
    .driver_info = (unsigned long)&dm9620_info,
     },
        {
        USB_DEVICE(0x0a46, 0x9620),     /* Davicom 9620 */
        .driver_info = (unsigned long)&dm9620_info,
         },
        {
        USB_DEVICE(0x0a46, 0x9621),     /* Davicom 9621 */
        .driver_info = (unsigned long)&dm9620_info,
         },
        {
        USB_DEVICE(0x0a46, 0x9622),     /* Davicom 9622 */
        .driver_info = (unsigned long)&dm9620_info,
         },
        {
    USB_DEVICE(0x0fe6, 0x8101),     /* Davicom 9601 USB to Fast Ethernet Adapter */
        .driver_info = (unsigned long)&dm9620_info,
         },
    {},         // END
};

MODULE_DEVICE_TABLE(usb, products);

static struct usb_driver dm9620_driver = {
//  .name = "dm9601",
    .name = "dm9620",
    .id_table = products,
    .probe = usbnet_probe,
    .disconnect = usbnet_disconnect,
    .suspend = usbnet_suspend,
    .resume = usbnet_resume,
};




static int __init dm9620_init(void)
{
    return usb_register(&dm9620_driver);
}

static void __exit dm9620_exit(void)
{
    usb_deregister(&dm9620_driver);
}

module_init(dm9620_init);
module_exit(dm9620_exit);

MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
MODULE_DESCRIPTION("Davicom DM9620 USB 2.0 ethernet devices");
MODULE_LICENSE("GPL");

Makefile

##================================================================
##     Davicom Semiconductor Inc.   For DM9620 V0.00
##   --------------------------------------------------------
## Description:
##              Compile driver dm9620.c to dm9620.ko
##
## Modification List:
## 09/05/2000   Fixed SMPFALGS wrong on smp & smp_mod
## 08/02/2000   Changed some description string & include file path
## 07/25/2000   Append smp_mod and changed some descriptions
## 01/25/2000   by Sten Wang
## 03/24/2009   Modifiy for Linux kernel 2.6.28
##================================================================
# Comment/uncomment the following line to disable/enable debugging
# DEBUG = y

# Add your debugging flag (or not) to CFLAGS
#ifeq ($(DEBUG),y)
#  DEBFLAGS = -O -g # "-O" is needed to expand inlines
#else
#  DEBFLAGS = -O2
#endif

#CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR)

#ifneq ($(KERNELRELEASE),)
# call from kernel build system

obj-m   := dm9620.o

#else
MODULE_INSTALDIR ?= /lib/modules/$(shell uname -r)/kernel/drivers/net/usb
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

default:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)  

#endif



clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

install:
    #modprobe -r dm9620
    install -c -m 0644 dm9620.ko $(MODULE_INSTALDIR)
    depmod -a -e

#depend .depend dep:
#   $(CC) $(CFLAGS) -M *.c > .depend


#ifeq (.depend,$(wildcard .depend))
#include .depend
#endif

For Kernel 4.1.4 patch

--- /home/happy/Downloads/Linux_DM962x_2.6/Linux_DM962x_2.6/dm9620.c    2010-07-09 15:08:48.000000000 +0800
+++ /home/test/dm9620.c 2015-08-17 12:47:40.293327000 +0800
@@ -88,7 +88,7 @@

 static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-   devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
+   printk (KERN_ERR "dm_read() reg=0x%02x length=%d", reg, length);
    return usb_control_msg(dev->udev,
                   usb_rcvctrlpipe(dev->udev, 0),
                   DM_READ_REGS,
@@ -103,7 +103,7 @@

 static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-   devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
+   printk (KERN_ERR "dm_write() reg=0x%02x, length=%d", reg, length);
    return usb_control_msg(dev->udev,
                   usb_sndctrlpipe(dev->udev, 0),
                   DM_WRITE_REGS,
@@ -113,7 +113,7 @@

 static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
 {
-   devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+   printk (KERN_ERR "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
    return usb_control_msg(dev->udev,
                   usb_sndctrlpipe(dev->udev, 0),
                   DM_WRITE_REG,
@@ -142,13 +142,13 @@

    urb = usb_alloc_urb(0, GFP_ATOMIC);
    if (!urb) {
-       deverr(dev, "Error allocating URB in dm_write_async_helper!");
+       printk (KERN_ERR "Error allocating URB in dm_write_async_helper!");
        return;
    }

    req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
    if (!req) {
-       deverr(dev, "Failed to allocate memory for control request");
+       printk (KERN_ERR "Failed to allocate memory for control request");
        usb_free_urb(urb);
        return;
    }
@@ -166,7 +166,7 @@

    status = usb_submit_urb(urb, GFP_ATOMIC);
    if (status < 0) {
-       deverr(dev, "Error submitting the control message: status=%d",
+       printk (KERN_ERR "Error submitting the control message: status=%d",
               status);
        kfree(req);
        usb_free_urb(urb);
@@ -175,14 +175,14 @@

 static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-   devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
+   printk (KERN_ERR "dm_write_async() reg=0x%02x length=%d", reg, length);

    dm_write_async_helper(dev, reg, 0, length, data);
 }

 static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
 {
-   devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
+   printk (KERN_ERR "dm_write_reg_async() reg=0x%02x value=0x%02x",
           reg, value);

    dm_write_async_helper(dev, reg, value, 0, NULL);
@@ -211,7 +211,7 @@
    }

    if (i == DM_TIMEOUT) {
-       deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+       printk (KERN_ERR "%s read timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }
@@ -219,7 +219,7 @@
    dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
    ret = dm_read(dev, DM_SHARED_DATA, 2, value);

-   devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+   printk (KERN_ERR "read shared %d 0x%02x returned 0x%04x, %d",
           phy, reg, *value, ret);

  out:
@@ -257,7 +257,7 @@
    }

    if (i == DM_TIMEOUT) {
-       deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+       printk (KERN_ERR "%s write timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }
@@ -320,7 +320,7 @@
    }

    if (i == DM_TIMEOUT) {
-       deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+       printk (KERN_ERR "%s write timed out!", phy ? "phy" : "eeprom");
        ret = -EIO;
        goto out;
    }
@@ -341,9 +341,9 @@
 {
    struct usbnet *dev = netdev_priv(net);

-   devwarn(dev, "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
+   printk (KERN_ERR "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
    if (eeprom->magic != MD96XX_EEPROM_MAGIC) {
-       devwarn(dev, "EEPROM: magic value mismatch, magic = 0x%x",
+       printk (KERN_ERR "EEPROM: magic value mismatch, magic = 0x%x",
            eeprom->magic);
        return -EINVAL;
    }
@@ -385,13 +385,13 @@
    __le16 res;

    if (phy_id) {
-       devdbg(dev, "Only internal phy supported");
+       printk (KERN_ERR "Only internal phy supported");
        return 0;
    }

    dm_read_shared_word(dev, 1, loc, &res);

-   devdbg(dev,
+   printk (KERN_ERR
           "dm9620_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
           phy_id, loc, le16_to_cpu(res));

@@ -406,11 +406,11 @@
    int mdio_val;

    if (phy_id) {
-       devdbg(dev, "Only internal phy supported");
+       printk (KERN_ERR "Only internal phy supported");
        return;
    }

-   devdbg(dev,"dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+   printk (KERN_ERR"dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
           phy_id, loc, val);

    dm_write_shared_word(dev, 1, loc, res);
@@ -514,15 +514,14 @@

    if (net->flags & IFF_PROMISC) {
        rx_ctl |= 0x02;
-   } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) {
+   } else if (net->flags & IFF_ALLMULTI || netdev_mc_count(net) > DM_MAX_MCAST) {
        rx_ctl |= 0x04;
-   } else if (net->mc_count) {
-       struct dev_mc_list *mc_list = net->mc_list;
-       int i;
-
-       for (i = 0; i < net->mc_count; i++, mc_list = mc_list->next) {
-           u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26;
-           hashes[crc >> 3] |= 1 << (crc & 0x7);
+   } else if (netdev_mc_count(net)) {
+       struct netdev_hw_addr *hw_addr;
+
+       netdev_for_each_mc_addr(hw_addr, net) {
+           u32 crc = ether_crc(ETH_ALEN, hw_addr->addr) >> 26;
+           hashes[crc >> 3] |= 1 << (crc & 0x7);
        }
    }

@@ -573,7 +572,7 @@
             .ndo_change_mtu         = usbnet_change_mtu, 
             .ndo_validate_addr      = eth_validate_addr, 
        .ndo_do_ioctl       = dm9620_ioctl,   
-       .ndo_set_multicast_list = dm9620_set_multicast,   
+        .ndo_set_rx_mode = dm9620_set_multicast,   
             .ndo_set_mac_address    = dm9620_set_mac_address,  
 };
 #endif
@@ -633,7 +632,7 @@
    /* read SMI mode register */
         priv = dev->driver_priv = kmalloc(sizeof(struct dm96xx_priv), GFP_ATOMIC);
    if (!priv) {
-       deverr(dev, "Failed to allocate memory for dm96xx_priv");
+       printk (KERN_ERR "Failed to allocate memory for dm96xx_priv");
        ret = -ENOMEM;
        goto out;
    }
@@ -869,7 +868,7 @@
        }
        else
            netif_carrier_off(dev->net);
-       devdbg(dev, "Link Status is: %d", link);
+       printk (KERN_ERR "Link Status is: %d", link);
    }
 }

@@ -880,7 +879,7 @@
    mii_ethtool_gset(&dev->mii, &ecmd);
    /* hank add*/
 dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
-   devdbg(dev, "link_reset() speed: %d duplex: %d",
+   printk (KERN_ERR "link_reset() speed: %d duplex: %d",
           ecmd.speed, ecmd.duplex);

    return 0;

Install kernel module

sudo insmod dm9620.ko
Reference:

2014年7月16日 星期三

[Compile Linux Kernel] Compile Linux kernel 3.15.5 on Fedora 20


 




  1. Install compile package

    sudo yum install gcc perl ncurses-devel


  2. Download Linux kernel source code

    linux-3.15.5.tar.xz


  3. tar linux source code into specific folder

    sudo tar -xvf linux-3.15.5.tar.xz -C /usr/src/


  4. Copy configuration file from /boot to “linux kernel folder”

    cp /boot/config-3.11.6-201.fc19.x86_64 /usr/src/linux-3.15.5


  5. Start to configuration

    sudo make menuconfig


  6. Start to make

    sudo make

    sudo make modules

    sudo make modules_install install



Reference:




  1. Compiling Fedora 16/17/18/19 Kernel


  2. The Linux Kernel Archives


2014年7月2日 星期三

[Kernel Module] My first Kernel Module




Kernel Code 1


#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
static ssize_t drv_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
printk
("device read\n");
return count;
}

static ssize_t drv_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
{
printk
("device write \n");
return count;
}

static int drv_open(struct inode *inode, struct file *filp)
{
printk
("device open \n");
return 0;
}


int drv_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
printk
("device ioctl \n");
return 0;
}

static int drv_release(struct inode *inode, struct file *filp)
{
printk
("device close \n");
return 0;
}

struct file_operations drv_fops =
{
read
: drv_read,
write
: drv_write,
compat_ioctl
: drv_ioctl,
open
: drv_open,
release
: drv_release,
};

#define MAJOR_NUM 60
#define MODULE_NAME "DEMO"
static int demo_init(void) {
if (register_chrdev(MAJOR_NUM, "demo", &drv_fops) < 0) {
printk
("<1>%s: can't get major %d\n", MODULE_NAME, MAJOR_NUM);
return (-EBUSY);
}

printk
("<1>%s:started\n", MODULE_NAME);
return 0;
}

static void demo_exit(void) {
unregister_chrdev
(MAJOR_NUM, "demo");
printk
("<1>%s: removed\n", MODULE_NAME);
}

module_init
(demo_init);
module_exit
(demo_exit);


Compile Kernel Module


make -C /lib/modules/3.8.0-33-generic/build M=`pwd` test.ko


User Space Code


#include <stdio.h>
int main()
{
char buf[512];
FILE
*fp = fopen("/dev/demo", "w+");
if(fp == NULL) {
printf
("can't open device!\n");
return 0;
}
fread
(buf, sizeof(buf), 1, fp);
fwrite
(buf, sizeof(buf), 1, fp);
fclose
(fp);
return 0;
}


Compile User Space Code


gcc test.c


Execute Code


sudo ./a.out


Result


tail -n 10 /var/log/kern.log
Jul 3 12:14:36 happy-Desktop kernel: [12404.120783] device close
Jul 3 12:14:37 happy-Desktop kernel: [12405.044410] device open
Jul 3 12:14:37 happy-Desktop kernel: [12405.044447] device read
Jul 3 12:14:37 happy-Desktop kernel: [12405.044477] device close
Jul 3 12:14:38 happy-Desktop kernel: [12406.056777] device open
Jul 3 12:14:38 happy-Desktop kernel: [12406.056814] device read
Jul 3 12:14:38 happy-Desktop kernel: [12406.056839] device close
Jul 3 12:15:10 happy-Desktop kernel: [12438.189191] device open
Jul 3 12:15:10 happy-Desktop kernel: [12438.189233] device read
Jul 3 12:15:10 happy-Desktop kernel: [12438.189262] device close