Chinaunix首页 | 论坛 | 博客
  • 博客访问: 517075
  • 博文数量: 87
  • 博客积分: 4086
  • 博客等级: 上校
  • 技术积分: 900
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-23 15:55
文章分类

全部博文(87)

文章存档

2012年(3)

2010年(13)

2009年(7)

2008年(64)

我的朋友

分类: LINUX

2008-08-01 17:20:33

前面分析了dm9000a网卡的probe部分,接下来继续其他部分。
 
当用户在命令行下使用ifconfig等命令的时候,网卡设备将打开,系统将调用open函数。dm9000a的open函数如下

static int dmfe_open(struct net_device *dev)
{
        board_info_t *db = (board_info_t *)dev->priv;
        u8 reg_nsr;
        int i;
       
        if (request_irq(dev->irq,&dmfe_interrupt,IRQF_SHARED,dev->name,dev))
                return -EAGAIN;
        /* Grab the IRQ */
        set_irq_type(dev->irq, IRQ_TYPE_EDGE_RISING);
        /* Initilize DM910X board */
        dmfe_init_dm9000(dev);
 
        /* Init driver variable */
        db->reset_counter = 0;
        db->reset_tx_timeout = 0;
        db->cont_rx_pkt_cnt = 0;
        
        /* check link state and media speed */
        db->Speed =10;
        i=0;
        do {
                reg_nsr = ior(db,0x1);
                if(reg_nsr & 0x40) /* link OK!! */
                {
                        /* wait for detected Speed */
                        mdelay(200);
                        reg_nsr = ior(db,0x1);
                        if(reg_nsr & 0x80)
                                db->Speed =10;
                        else
                                db->Speed =100;
                        break;
                }
                i++;
                mdelay(1);
        }while(i<3000); /* wait 3 second */
        //printk("i=%d Speed=%d\n",i,db->Speed);

        /* set and active a timer process */
        init_timer(&db->timer);
        db->timer.expires = DMFE_TIMER_WUT * 2;
        db->timer.data = (unsigned long)dev;
        db->timer.function = &dmfe_timer;
        add_timer(&db->timer); //Move to DM9000 initiallization was finished.

         
        netif_start_queue(dev);
        return 0;
}

函数首先向系统申请中断,利用内核提供的request_irq函数。该函数声明于include/linux/interrupt.h中,而它的定于位于kernel/irq/manage.c中。关于它的说明和原型如下

/**
 *    request_irq - allocate an interrupt line
 *    @irq: Interrupt line to allocate
 *    @handler: Function to be called when the IRQ occurs
 *    @irqflags: Interrupt type flags
 *    @devname: An ascii name for the claiming device
 *    @dev_id: A cookie passed back to the handler function
 *
 *    This call allocates interrupt resources and enables the
 *    interrupt line and IRQ handling. From the point this
 *    call is made your handler function may be invoked. Since
 *    your handler function must clear any interrupt the board
 *    raises, you must take care both to initialise your hardware
 *    and to set up the interrupt handler in the right order.
 *
 *    Dev_id must be globally unique. Normally the address of the
 *    device data structure is used as the cookie. Since the handler
 *    receives this value it makes sense to use it.
 *
 *    If your interrupt is shared you must pass a non NULL dev_id
 *    as this is required when freeing the interrupt.
 *
 *    Flags:
 *
 *    IRQF_SHARED        Interrupt is shared
 *    IRQF_DISABLED    Disable local interrupts while processing
 *    IRQF_SAMPLE_RANDOM    The interrupt can be used for entropy
 *
 */

int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)

可见,传递给该函数的第一个参数是申请的中断号;第二个参数是一个函数指针,当发生相应的中断时,系统将调用该函数处理中断;第三个参数是中断的标志,可以是它说明文档中提到的三种中的任何一种,我们用的是IRQF_SHARED,表示中断可以共享;第四个参数就是设备的简称,可以在/proc/interrupts列表中找到。

申请完成中断后,驱动程序设置了中断的类型,使用set_irq_type函数。该函数声明于include/linux/interrupt.h,而定义于kernel/irq/chip.c,原型如下

/**
 *    set_irq_type - set the irq type for an irq
 *    @irq:    irq number
 *    @type:    interrupt type - see include/linux/interrupt.h
 */

int set_irq_type(unsigned int irq, unsigned int type)

第一个参数是中断号,第二歌参数是中断类型,表示上升沿触发、下降沿触发、高电平触发或者低电平触发。我们使用的是IRQ_TYPE_EDGE_RISING,即上升沿触发。

中断设置完成后,驱动程序需要对dm9000a芯片进行初始化,调用dmfe_init_dm9000函数,如下

static void dmfe_init_dm9000(struct net_device *dev)
{
        board_info_t *db = (board_info_t *)dev->priv;
       
        /* set the internal PHY power-on, GPIOs normal, and wait 2ms */
        iow(db, DM9KS_GPR, 1); /* Power-Down PHY */
        udelay(500);
        iow(db, DM9KS_GPR, 0); /* GPR (reg_1Fh)bit GPIO0=0 pre-activate PHY */
        udelay(20); /* wait 2ms for PHY power-on ready */
        /* do a software reset and wait 20us */
        iow(db, DM9KS_NCR, 3);
        udelay(20); /* wait 20us at least for software reset ok */
        iow(db, DM9KS_NCR, 3); /* NCR (reg_00h) bit[0] RST=1 & Loopback=1, reset on */
        udelay(20); /* wait 20us at least for software reset ok */
        /* I/O mode */
        db->io_mode = ior(db, DM9KS_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */
        /* Set PHY */
        db->op_mode = media_mode;
        set_PHY_mode(db);
        /* Program operating register */
        iow(db, DM9KS_NCR, 0);
        iow(db, DM9KS_TCR, 0); /* TX Polling clear */
        iow(db, DM9KS_BPTR, 0x3f); /* Less 3kb, 600us */
        iow(db, DM9KS_SMCR, 0); /* Special Mode */
        iow(db, DM9KS_NSR, 0x2c); /* clear TX status */
        iow(db, DM9KS_ISR, 0x0f); /* Clear interrupt status */
        /* Added by jackal at 03/29/2004 */
 
        /* Set address filter table */
        dm9000_hash_table(dev);
        /* Activate DM9000A/DM9010 */
        iow(db, DM9KS_RXCR, DM9KS_REG05 | 1); /* RX enable */
        iow(db, DM9KS_IMR, DM9KS_REGFF); // Enable TX/RX interrupt mask

 
        /* Init Driver variable */
        db->tx_pkt_cnt = 0;
                
        netif_carrier_on(dev);
        spin_lock_init(&db->lock);
}

该函数中使用了iow函数,看一看它的实现

static void iow(board_info_t *db, int reg, u8 value)
{
        PUTB(reg, db->io_addr);
        PUTB(value, db->io_data);
}

即将value写入reg表示的寄存器中。dmfe_init_dm9000函数的具体功能函数里面已经有注释,更详细的可以查看datasheet。dmfe_init_dm9000函数返回后,open函数还做了一些工作。首先,初始化一些设备变量

        db->reset_counter = 0;
        db->reset_tx_timeout = 0;
        db->cont_rx_pkt_cnt = 0;

这些值在发送和接收数据的时候将会使用到,到讨论那些函数的时候将详细介绍。接下来,驱动程序需要为自己增加一个timer

        init_timer(&db->timer);
        db->timer.expires = DMFE_TIMER_WUT * 2;
        db->timer.data = (unsigned long)dev;
        db->timer.function = &dmfe_timer;
        add_timer(&db->timer); //Move to DM9000 initiallization was finished.

timer的expires变量决定定时时间,当定时时间到的时候,就会执行function指定的函数。最后,使用add_timer()函数将初始化的timer插入挂起定时器全局队列。关于function指定的函数,将在后面说明。

最后,open函数调用netif_start_queue,该函数的定义位于include/linux/netdevice.h中

/**
 *    netif_start_queue - allow transmit
 *    @dev: network device
 *
 *    Allow upper layers to call the device hard_start_xmit routine.
 */

static inline void netif_start_queue(struct net_device *dev)
{
    clear_bit(__LINK_STATE_XOFF, &dev->state);
}

可见,该函数告诉系统可以使用hard_start_xmit进行数据发送了。

open函数到此就结束了。

-----------------------------To be continued  2008-8-1

由于篇幅限制,下面的部分见:

http://blog.chinaunix.net/u1/57747/showart_1101019.html

阅读(2111) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~