2010年6月1日 星期二

[Linux] Ethernet driver 骨架

算一算已經自願調到driver team 好一陣子了,從user space 寫到kernel space感覺上似乎沒改變太多,每天還是看code,需要動手大改的機會都不多,大概這就是系統廠的宿命。

不過誰說工作要求才能寫,XD 最近邊debug ethernet driver,乾脆就動手自己寫一個註冊net device...

這樣就更清楚了整個flow了。
底下附上的code都是拿掉硬體IO reg 等等相關,一來只是要紀錄如果從無到有寫一個,二來硬體資料百百款跟vendor拿到spec 在接上就可以,有空再補哩。

net device 的註冊已經是很普遍了,只要自己建好下面幾個function 就可以動了。
當然
不包含NAPI,沒寫 poll,沒有ioctl (通常拿來改MAC), device status, config 等等都沒有XD
-----------------
static void do_bth_tx(u_char *buffer, u_short len)
static void do_bth_rx(void)
static irqreturn_t bth_interrupt(int irq, void *dev_id, struct pt_regs *regs)
static int bth_open(struct net_device *dev)
static int bth_stop(struct net_device *dev)
static int bth_xmit(struct sk_buff *skb, struct net_device *dev)
void bth_tx_timeout(struct net_device *dev)
int bth_init(struct net_device *dev)
static int __init bth_dev_init(void)
static void __exit bth_dev_exit(void)

-----------------------------------------------------------------------------
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/irq.h>
#include <asm/io.h>

struct bth{  

    spinlock_t lock;
}; // 裡面要建構硬體私有的宣告,不需要的都拿掉了。

static struct net_device *bth_dev = NULL;

static void do_bth_tx(u_char *buffer, u_short len)
{
    //接硬體的部份拿掉了
    char shortpkt[ETH_ZLEN];

    if(len < ETH_ZLEN)
    {
        memset(shortpkt, 0X0, ETH_ZLEN);
        memcpy(shortpkt, buffer, len);
        len = ETH_ZLEN;
        buffer = shortpkt;
    }
    printk("%x\n", buffer);
    printk("bth_tx_done the bth dev! \n");
}

static void do_bth_rx(void)
{
    struct net_device *dev = bth_dev;
//    struct bth *bthx = (struct bth *)dev->priv;
    struct sk_buff *skb;
   
    int frame_len = 1500; // it needs read form phy
   
    skb = dev_alloc_skb(frame_len + 2);
    skb_reserve(skb, 2);
    skb->dev = dev;
    skb_put(skb, frame_len);

    skb->protocol = eth_type_trans(skb, dev);
    netif_rx(skb);
   
    dev->trans_start = jiffies;
    printk("bth_packet_receive the bth dev!\n");
}

static irqreturn_t bth_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
//中斷產生時呼叫,內容化簡
    do_bth_rx();
    return IRQ_HANDLED;
}

static int bth_open(struct net_device *dev)
{
// ifconfig up 會呼叫
    if(request_irq(dev->irq, &bth_interrupt, 0, dev->name, dev))
        return -EAGAIN;
    netif_start_queue(dev);
    printk("open the bth dev! \n");
    return 0;
}

static int bth_stop(struct net_device *dev)
{
//ifconfig down 會呼叫
    netif_stop_queue(dev);
    free_irq(dev->irq, dev);
    printk("bth dev STOP!!! \n"); 
    return 0;
}

static int bth_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct bth *bthx = (struct bth *)dev->priv;
    unsigned long flags;
   
    spin_lock_irqsave(&bthx->lock, flags);
   
    dev->stats.tx_bytes += skb->len;
    dev->stats.tx_packets++;
    do_bth_tx(skb->data,skb->len);
    dev->trans_start = jiffies;
   
    spin_unlock_irqrestore(&bthx->lock, flags);
   
    printk("hard start xmit the bth dev! \n");  
    return 0;
}

void bth_tx_timeout(struct net_device *dev)
{
//tx timeout 的時候呼叫
    printk("TIMEOUT the bth dev! \n"); 
    dev->trans_start = jiffies;
    netif_wake_queue(dev);
}
int bth_init(struct net_device *dev)
{
//這裡接上,基本上就可以看到interface了
    struct bth *bthx;
    int i;
    unsigned char mac_addr[6] = {0x00, 0x11, 0x22, 0x33, 0x99, 0x99}; // 通常是從REG或另外的記憶體讀出,這裡寫死
    bthx = (void *)kmalloc(sizeof(struct bth), GFP_KERNEL);
    memset(bthx, 0, sizeof (struct bth));
   
    if (!dev)
    {
        printk(": unable to allocate etherdev\n");
        return 1;
    }
    ether_setup(dev);
   
    dev->priv = bthx;
    dev->open         = bth_open;
    dev->stop         = bth_stop;
    dev->hard_start_xmit  = bth_xmit;
    dev->tx_timeout = bth_tx_timeout;
    for (i = 0; i < 6; i++)
        dev->dev_addr[i] = mac_addr[i];
    return 0;
}

static int __init bth_dev_init(void)
{
    bth_dev = alloc_netdev(sizeof(struct bth), "bth%d", bth_init);
    register_netdev(bth_dev);
    printk("init the bth dev! \n");

    return 0;
}

static void __exit bth_dev_exit(void)
{
    unregister_netdev(bth_dev);
    free_netdev(bth_dev);
    kfree(bth_dev->priv);
    printk("bth dev exit!! \n");
}

module_init(bth_dev_init);
module_exit(bth_dev_exit);