Linux-2.6.20的cs8900驱动分析(二)

来源:互联网 发布:mac系统没了怎么办 编辑:程序博客网 时间:2024/05/16 12:05

2.1 net_open  net_close

net_open 函数主要完成的工作有:(这段 net_open 函数的概要内容总结来源于网络,网址: http://www.akae.cn/bbs/archiver/?tid-6657.html 

A .获取私有数据指针存放于 lp

B .启动设备总线控制功能和启动存储器

C .调用 request_irq() 请求中断并注册 net_interrupt 为中断服务程序;

D .写中断号存于设备中 write_irq()

E .如果无法申请中断号,则返回错误

F .如果支持 DMA 则通过以下函数初始化 DMA 

              _get_dma_pages();

              get_order();

               dma_page_eq();

G .申请 DMA  requeset_dma()

H .初始化设备结构中关于 DMA 的参数,并使能 DMA 

I .设置以太网地址, writereg()

J .检测链路,从而确定连接媒体类型

K .配置链路:

               a.10B_T:detect_tp()

              b.AUI:detect_aui()

              c.10B_2:detect_bnu()

               d.AUTO: 从头检测自动配置

L .输出信息

M .启动链路串行接收和发送功能

N .初始化 lp 相关参数

O .配置 DMA  set_dma_cfg()

P .配置芯片相关寄存器

Q .配置 DMA 缓冲 dma_bufcfg()

R .使能芯片中断;

S .启动网络传输队列, netif_start_queue()

       这部分内容都与 cs8900 芯片具体操作相关,相对来说和比较简单,下面直接给出 net_open  net_close 的相关注解

static int net_open(struct net_device *dev)

{

       struct net_local *lp = netdev_priv(dev);

       int result = 0;

       int i;

       int ret;

 

       ......// 省略一些信息

/* FIXME: Cirrus' release had this: */

       writereg(dev, PP_BusCTL, readreg(dev,PP_BusCTL)|ENABLE_IRQ);// 使能 cs8900 中断

     write_irq(dev, lp->chip_type, dev->irq);// 该函数选择 cs8900 芯片内部的中断线,

二、 net_open  net_close  net_interrupt

2.1 net_open  net_close

net_open 函数主要完成的工作有:(这段 net_open 函数的概要内容总结来源于网络,网址: http://www.akae.cn/bbs/archiver/?tid-6657.html 

A .获取私有数据指针存放于 lp

B .启动设备总线控制功能和启动存储器

C .调用 request_irq() 请求中断并注册 net_interrupt 为中断服务程序;

D .写中断号存于设备中 write_irq()

E .如果无法申请中断号,则返回错误

F .如果支持 DMA 则通过以下函数初始化 DMA 

              _get_dma_pages();

              get_order();

               dma_page_eq();

G .申请 DMA  requeset_dma()

H .初始化设备结构中关于 DMA 的参数,并使能 DMA 

I .设置以太网地址, writereg()

J .检测链路,从而确定连接媒体类型

K .配置链路:

               a.10B_T:detect_tp()

              b.AUI:detect_aui()

              c.10B_2:detect_bnu()

               d.AUTO: 从头检测自动配置

L .输出信息

M .启动链路串行接收和发送功能

N .初始化 lp 相关参数

O .配置 DMA  set_dma_cfg()

P .配置芯片相关寄存器

Q .配置 DMA 缓冲 dma_bufcfg()

R .使能芯片中断;

S .启动网络传输队列, netif_start_queue()

       这部分内容都与 cs8900 芯片具体操作相关,相对来说和比较简单,下面直接给出 net_open  net_close 的相关注解

static int net_open(struct net_device *dev)

{

       struct net_local *lp = netdev_priv(dev);

       int result = 0;

       int i;

       int ret;

 

       ......// 省略一些信息

/* FIXME: Cirrus' release had this: */

       writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );                                                           // 使能 cs8900 中断

             

     write_irq(dev, lp->chip_type, dev->irq);     // 该函数选择 cs8900 芯片内部的中断线,

                                                                       // 见本文件中的 write_irq 实现

//++++++++++++++++++++++ 这段代码为自己添加,内核原版中没有

#if defined(CONFIG_ARCH_S3C2410)

           set_irq_type(dev->irq, IRQT_RISING);  // 该函数在 kernel/irq/chip 实现,

        // 可选择的中断类型有 include/linux/interrupt.h 中定义,此处设置为上升沿触发中断

#endif

//++++++++++++++++++++++

              ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); // 注册中断

              if (ret) {

                     if (net_debug)

                            printk(KERN_DEBUG "cs89x0: request_irq(%d) failed/n", dev->irq);

                     goto bad_out;

              }

……

 

       /* set the Ethernet address *///  MAC 地址设置到 cs8900  Individual Address 寄存器

       for (i=0; i < ETH_ALEN/2; i++)

              writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));

 

       /* while we're testing the interface, leave interrupts disabled */

       writereg(dev, PP_BusCTL, MEMORY_ON);  // 使 cd8900 工作到 memory 模式,

                                                                               // 如果 dev->mem_start 域为 0 

     // 将关闭该模式

 

 

       // 以下代码为选择 cs8900 的物理传输媒体的类型

       /* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */

       // 由于没有 eeprom  lp->adapter_cnf  cs89x0_probe1 中未设置,此值为 0.

       if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) && (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))

                lp->linectl = LOW_RX_SQUELCH;

       else

                lp->linectl = 0;

 

        /* check to make sure that they have the "right" hardware available */

       switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {

       case A_CNF_MEDIA_10B_T: result = lp->adapter_cnf & A_CNF_10B_T; break;

       case A_CNF_MEDIA_AUI:   result = lp->adapter_cnf & A_CNF_AUI; break;

       case A_CNF_MEDIA_10B_2: result = lp->adapter_cnf & A_CNF_10B_2; break;

        default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);

        }

 

#if defined(CONFIG_ARCH_PNX0105) || defined(CONFIG_ARCH_S3C2410)//+++++++++

       result = A_CNF_10B_T;     // 上面由于 lp->adapter_cnf=0 ,导致 result=0 

         // 这里额外设置 该值可以根据需要实际情况设置,可设置的值可在

        // cs89x0.h 中找到 当然这里也可以设置 lp->adapter_cnf 成想要的值

#endif

        if (!result) {//result==0 时执行此段代码

                printk(KERN_ERR "%s: EEPROM is configured for unavailable media/n", dev->name);

        release_irq:

        ......

                writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));

                free_irq(dev->irq, dev);

              ret = -EAGAIN;

              goto bad_out;

       }

 

        /* set the hardware to the configured choice */

       switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {//lp->adapter_cnf & A_CNF_MEDIA_TYPE==0,

                                                   // 不符合任何 case 情况,将执行 default ,但未实现 default

       case A_CNF_MEDIA_10B_T:

                result = detect_tp(dev);  //detect_tp 探测物理传输媒体类型是 RJ-45H,

                                                     // 还是 RJ-45F

                if (result==DETECTED_NONE) {

                        printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable/n", dev->name);

                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */

                                 result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */

                }

              break;

       case A_CNF_MEDIA_AUI:

                result = detect_aui(dev);//detect_tp 探测物理传输媒体类型是否为 AUI 

                if (result==DETECTED_NONE) {

                        printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable/n", dev->name);

                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */

                                result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */

                }

              break;

       case A_CNF_MEDIA_10B_2:

                result = detect_bnc(dev);  //detect_tp 探测物理传输媒体类型是否为 BNC 

                if (result==DETECTED_NONE) {

                        printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable/n", dev->name);

                        if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */

                                result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */

                }

              break;

       case A_CNF_MEDIA_AUTO:

              writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);

              if (lp->adapter_cnf & A_CNF_10B_T)

                     if ((result = detect_tp(dev)) != DETECTED_NONE)

                            break;

              if (lp->adapter_cnf & A_CNF_AUI)

                     if ((result = detect_aui(dev)) != DETECTED_NONE)

                            break;

              if (lp->adapter_cnf & A_CNF_10B_2)

                     if ((result = detect_bnc(dev)) != DETECTED_NONE)

                            break;

              printk(KERN_ERR "%s: no media detected/n", dev->name);

                goto release_irq;

       }

       switch(result) {     // 上面将 result 赋成了 A_CNF_10B_T ,该值为 1 ,刚好等于

                 // DETECTED_RJ45H 所以也可以在上面的 result 中直接赋成

                 // DETECTED_RJ45H 或者其他类型的接口

       case DETECTED_NONE:

              printk(KERN_ERR "%s: no network cable attached to configured media/n", dev->name);

                goto release_irq;

       case DETECTED_RJ45H:

              printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)/n", dev->name);

              break;

       case DETECTED_RJ45F:

              printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)/n", dev->name);

              break;

       case DETECTED_AUI:

              printk(KERN_INFO "%s: using 10Base-5 (AUI)/n", dev->name);

              break;

       case DETECTED_BNC:

              printk(KERN_INFO "%s: using 10Base-2 (BNC)/n", dev->name);

              break;

       }

 

       /* Turn on both receive and transmit operations */

       writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);

 

       /* Receive only error free packets addressed to this card */

       lp->rx_mode = 0;// 确定接收模式, 0 表示接收广播  0 表示全部接收

       writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);    // 初始化接收控制器 RxCTL 为默认

                      // 接收模式。 该模式下,只接收 Broadcast  Individual  CRC

                                            // 正确的数据包, 具体可查看 cs8900 手册。

 

       lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;   // 接收 OK 产生中断,

                                                      // CRC 错产生中断

 

       if (lp->isa_config & STREAM_TRANSFER)// 判断是否打开 cs8900  stream 传输模式

              lp->curr_rx_cfg |= RX_STREAM_ENBL;// 使用 stream 模式 , 此处没有启用。

 

       writereg(dev, PP_RxCFG, lp->curr_rx_cfg);       // 初始化接收配置控制器 RxCFG 

                                                                                  // 这里确定了接收中断源

 

  // 初始化发送配置控制器 TxCFG  TxCFG 寄存器的全部有效位置为 1 

  // 也确定了发送中断源

       writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |

              TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);

 

       writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |

 

              TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);

 

       /* now that we've got our act together, enable everything */

       writereg(dev, PP_BusCTL, ENABLE_IRQ          // 开中断

                | (dev->mem_start?MEMORY_ON : 0)      // 没有设置共享内存空

                                                              // 间 dev->mem_start  0  memory 模式将被关闭

 

                 );

        netif_start_queue(dev);  // 激活设备发送队列,以便内核可以开始发送数据

       if (net_debug > 1)

              printk("cs89x0: net_open() succeeded/n");

       return 0;

bad_out:

       return ret;

}

 

 

net_close(struct net_device *dev)

{

  ......// 略去 DMA 部分

 

       netif_stop_queue(dev);// 停止设备发送队列,通知内核不能使用该设备发送数据

 

       writereg(dev, PP_RxCFG, 0);// 禁用接收

       writereg(dev, PP_TxCFG, 0);// 禁用发送

       writereg(dev, PP_BufCFG, 0);// 关闭 cs8900 内部缓冲区

       writereg(dev, PP_BusCTL, 0);// 停止总线

 

       free_irq(dev->irq, dev);// 释放占用的中断线

 

  ......// 略去 DMA 部分

 

       /* Update the statistics here. */

       return 0;

}

2.2 net_interrupt

       该函数的大体流程如下:(此段总结来源同上)

A .获取设备私有数据 net_priv() 

B .读取 CS8900 的中断端口状态 readword() 

C .判断中断类型:

              a. 接收事件:调用 net_rx() 接收数据;

              b. 传输事件:调用 netif_wake_queue() 唤醒传输队列,进行异常处理;

              c. 缓冲区事件:可以发送数据,调用 netif_wake_queue() 唤醒传输队列;

              d. 接收包丢失事件:初始化相关 error ,错误计数

e. 传输冲突时间:初始化相关 error

D .返回中断句柄。

net_interrupt 中断处理函数的实现非常简单,它首先读出 cs8900  ISQ 寄存器的值,然后根据 ISQ 的值分别处理各种情况。当中断发生时,这些中断实际反映在相应的寄存器中, ISQ 寄存器用低 6 位记录了当前寄存器的编号,高 10 位记录了当前寄存器的实际内容。这些寄存器有: RxEvent(Register 4)  TxEvent(Register 8)  BufEvent(RegisterC)  RxMISS(Register 10)  TxCOL(Register 12) 。比如,传输成功后, cs8900  TxEvent 的第 0bit 置为 1 ,如果允许该事件中断,那么 ISQ 寄存器的低 6 位将记录 TxEvent 的编号 8 ,并且将 TxEvent 寄存器的高 10  copy 到它的高 10 位中。

       net_interrupt 注解如下:

 

static irqreturn_t net_interrupt(int irq, void *dev_id)

{

       struct net_device *dev = dev_id;

       struct net_local *lp;

       int ioaddr, status;

       int handled = 0;

 

       ioaddr = dev->base_addr;

       lp = netdev_priv(dev);

 

       /* we MUST read all the events out of the ISQ, otherwise we'll never

           get interrupted again.  As a consequence, we can't have any limit

           on the number of times we loop in the interrupt handler.  The

           hardware guarantees that eventually we'll run out of events.  Of

           course, if you're on a slow machine, and packets are arriving

           faster than you can read them off, you're screwed.  Hasta la

           vista, baby!  */

       while ((status = readword(dev->base_addr, ISQ_PORT))) {

   //ISQ_PORT=08h, 根据 cs8900 的用户手册, 这里再次说明了 cs8900 工作在 I/O 模式

              if (net_debug > 4)printk("%s: event=%04x/n", dev->name, status);

              handled = 1;

              switch(status & ISQ_EVENT_MASK) {    //ISQ_EVENT_MASK=0x3f 

                                   // 确定 ISQ 的低 6 位,  6 位纪录了发生中断的寄存器

              case ISQ_RECEIVER_EVENT:     //ISQ_RECEIVER_EVENT=0x04,

                                         // 中断源来自 RxEvent  表示接收到了数据包

                     /* Got a packet(s). */

                     net_rx(dev);

                     break;

              case ISQ_TRANSMITTER_EVENT:   //ISQ_RECEIVER_EVENT=0x08,

                       // 中断源来自 TxEvent ,根据 net_open 中设置,有很多发送事件

                     // 可以产生中断,需要分别处理

                     lp->stats.tx_packets++;    // 累加发送包的总数

                     netif_wake_queue(dev);  /* Inform upper layers. */ 

                     if ((status & ( TX_OK |    //ISQ 的高 10 位描述了 TxEvent 的实际内容,

                              // 也即实际传输的信息 这里似乎 status 应该右移 6 位?的确应该

                              // 这样,这里之所以没这样做, 是因为 TX_OK 等这些值,在设

                              // 计时已经左移了 6 

                                   TX_LOST_CRS |

                                   TX_SQE_ERROR |

                                   TX_LATE_COL |

                                   TX_16_COL)) != TX_OK) {  // 做些错误统计工作

                            if ((status & TX_OK) == 0) lp->stats.tx_errors++;

                            if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;

                            if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;

                            if (status & TX_LATE_COL) lp->stats.tx_window_errors++;

                            if (status & TX_16_COL) lp->stats.tx_aborted_errors++;

                     }

                     break;

              case ISQ_BUFFER_EVENT:  //ISQ_RECEIVER_EVENT=0x0c,

                                                              // 中断源来自 BufEvent

      if (status & TX_UNDERRUN) {    // 这里说明估计的发送长度过短,可能需要做调整

                            if (net_debug > 0) printk("%s: transmit underrun/n", dev->name);

                                lp->send_underrun++;

                                if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; // 此值 cs89x0_probe1 时初始化为 5 ,这里修正。

                                else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL;

                            /* transmit cycle is done, although

                               frame wasn't transmitted - this

                               avoids having to wait for the upper

                               layers to timeout on us, in the

                               event of a tx underrun */

                            netif_wake_queue(dev); /* Inform upper layers. */

                        }

      ......//DMA 部分

                     break;

              case ISQ_RX_MISS_EVENT:   //ISQ_RX_MISS_EVENT=0x10,

                                         // 中断来自于 RxMISS  该寄存器的高 10 位记录丢失的数据包

                     lp->stats.rx_missed_errors += (status >>6);

                     break;

              case ISQ_TX_COL_EVENT:        //ISQ_TX_COL_EVENT=0x12, 中断来自于

                // TxCOL  该寄存器的高 10 位记录发了生冲突的数据包

                     lp->stats.collisions += (status >>6);

                     break;

              }

       }

       return IRQ_RETVAL(handled);

}


 

原创粉丝点击