二、net_open、net_close和net_interrupt 2.1 net_open與net_close net_open函數(shù)主要完成的工作有:(這段net_open函數(shù)的概要內(nèi)容總結(jié)來(lái)源于網(wǎng)絡(luò),網(wǎng)址:http://www.akae.cn/bbs/archiver/?tid-6657.html) A.獲取私有數(shù)據(jù)指針存放于lp B.啟動(dòng)設(shè)備總線控制功能和啟動(dòng)存儲(chǔ)器 C.調(diào)用request_irq()請(qǐng)求中斷并注冊(cè)net_interrupt為中斷服務(wù)程序; D.寫中斷號(hào)存于設(shè)備中write_irq() E.如果無(wú)法申請(qǐng)中斷號(hào),則返回錯(cuò)誤 F.如果支持DMA則通過(guò)以下函數(shù)初始化DMA: _get_dma_pages(); get_order(); dma_page_eq(); G.申請(qǐng)DMA,requeset_dma() H.初始化設(shè)備結(jié)構(gòu)中關(guān)于DMA的參數(shù),并使能DMA; I.設(shè)置以太網(wǎng)地址,writereg() J.檢測(cè)鏈路,從而確定連接媒體類型 K.配置鏈路: a.10B_T:detect_tp() b.AUI:detect_aui() c.10B_2:detect_bnu() d.AUT從頭檢測(cè)自動(dòng)配置 L.輸出信息 M.啟動(dòng)鏈路串行接收和發(fā)送功能 N.初始化lp相關(guān)參數(shù) O.配置DMA,set_dma_cfg() P.配置芯片相關(guān)寄存器 Q.配置DMA緩沖dma_bufcfg() R.使能芯片中斷; S.啟動(dòng)網(wǎng)絡(luò)傳輸隊(duì)列,netif_start_queue() 這部分內(nèi)容都與cs8900芯片具體操作相關(guān),相對(duì)來(lái)說(shuō)和比較簡(jiǎn)單,下面直接給出net_open與net_close的相關(guān)注解 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);//該函數(shù)選擇cs8900芯片內(nèi)部的中斷線, 二、net_open、net_close和net_interrupt 2.1 net_open與net_close net_open函數(shù)主要完成的工作有:(這段net_open函數(shù)的概要內(nèi)容總結(jié)來(lái)源于網(wǎng)絡(luò),網(wǎng)址:http://www.akae.cn/bbs/archiver/?tid-6657.html) A.獲取私有數(shù)據(jù)指針存放于lp B.啟動(dòng)設(shè)備總線控制功能和啟動(dòng)存儲(chǔ)器 C.調(diào)用request_irq()請(qǐng)求中斷并注冊(cè)net_interrupt為中斷服務(wù)程序; D.寫中斷號(hào)存于設(shè)備中write_irq() E.如果無(wú)法申請(qǐng)中斷號(hào),則返回錯(cuò)誤 F.如果支持DMA則通過(guò)以下函數(shù)初始化DMA: _get_dma_pages(); get_order(); dma_page_eq(); G.申請(qǐng)DMA,requeset_dma() H.初始化設(shè)備結(jié)構(gòu)中關(guān)于DMA的參數(shù),并使能DMA; I.設(shè)置以太網(wǎng)地址,writereg() J.檢測(cè)鏈路,從而確定連接媒體類型 K.配置鏈路: a.10B_T:detect_tp() b.AUI:detect_aui() c.10B_2:detect_bnu() d.AUT從頭檢測(cè)自動(dòng)配置 L.輸出信息 M.啟動(dòng)鏈路串行接收和發(fā)送功能 N.初始化lp相關(guān)參數(shù) O.配置DMA,set_dma_cfg() P.配置芯片相關(guān)寄存器 Q.配置DMA緩沖dma_bufcfg() R.使能芯片中斷; S.啟動(dòng)網(wǎng)絡(luò)傳輸隊(duì)列,netif_start_queue() 這部分內(nèi)容都與cs8900芯片具體操作相關(guān),相對(duì)來(lái)說(shuō)和比較簡(jiǎn)單,下面直接給出net_open與net_close的相關(guān)注解 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); //該函數(shù)選擇cs8900芯片內(nèi)部的中斷線, //見(jiàn)本文件中的write_irq實(shí)現(xiàn) //++++++++++++++++++++++這段代碼為自己添加,內(nèi)核原版中沒(méi)有 #if defined(CONFIG_ARCH_S3C2410) set_irq_type(dev->irq, IRQT_RISING); //該函數(shù)在kernel\irq\chip實(shí)現(xiàn), //可選擇的中斷類型有include\linux\interrupt.h中定義,此處設(shè)置為上升沿觸發(fā)中斷 #endif //++++++++++++++++++++++ ret = request_irq(dev->irq, &net_interrupt, 0, dev->name, dev); //注冊(cè)中斷 if (ret) { if (net_debug) printk(KERN_DEBUG "cs89x0: request_irq(%d) failed\n", dev->irq); goto bad_out; } …… /* set the Ethernet address *///將MAC地址設(shè)置到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域?yàn)?font face="Times New Roman">0, //將關(guān)閉該模式 //以下代碼為選擇cs8900的物理傳輸媒體的類型 /* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */ //由于沒(méi)有eeprom,lp->adapter_cnf在cs89x0_probe1中未設(shè)置,此值為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,導(dǎo)致result=0, //這里額外設(shè)置該值可以根據(jù)需要實(shí)際情況設(shè)置,可設(shè)置的值可在 //cs89x0.h中找到當(dāng)然這里也可以設(shè)置lp->adapter_cnf成想要的值 #endif if (!result) {//result==0時(shí)執(zhí)行此段代碼 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情況,將執(zhí)行default,但未實(shí)現(xiàn)default case A_CNF_MEDIA_10B_T: result = detect_tp(dev); //detect_tp探測(cè)物理傳輸媒體類型是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探測(cè)物理傳輸媒體類型是否為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探測(cè)物理傳輸媒體類型是否為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_AUT 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為默認(rèn) //接收模式。該模式下,只接收Broadcast和Individual的CRC //正確的數(shù)據(jù)包,具體可查看cs8900手冊(cè)。 lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; //接收OK產(chǎn)生中斷, //CRC錯(cuò)產(chǎn)生中斷 if (lp->isa_config & STREAM_TRANSFER)//判斷是否打開(kāi)cs8900的stream傳輸模式 lp->curr_rx_cfg |= RX_STREAM_ENBL;//使用stream模式,此處沒(méi)有啟用。 writereg(dev, PP_RxCFG, lp->curr_rx_cfg); //初始化接收配置控制器RxCFG, //這里確定了接收中斷源 //初始化發(fā)送配置控制器TxCFG,TxCFG寄存器的全部有效位置為1, //也確定了發(fā)送中斷源 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 //開(kāi)中斷 | (dev->mem_start?MEMORY_ON : 0) //沒(méi)有設(shè)置共享內(nèi)存空 //間dev->mem_start為0,memory模式將被關(guān)閉 ); netif_start_queue(dev); //激活設(shè)備發(fā)送隊(duì)列,以便內(nèi)核可以開(kāi)始發(fā)送數(shù)據(jù) 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);//停止設(shè)備發(fā)送隊(duì)列,通知內(nèi)核不能使用該設(shè)備發(fā)送數(shù)據(jù) writereg(dev, PP_RxCFG, 0);//禁用接收 writereg(dev, PP_TxCFG, 0);//禁用發(fā)送 writereg(dev, PP_BufCFG, 0);//關(guān)閉cs8900內(nèi)部緩沖區(qū) writereg(dev, PP_BusCTL, 0);//停止總線 free_irq(dev->irq, dev);//釋放占用的中斷線 ......//略去DMA部分 /* Update the statistics here. */ return 0; } 2.2 net_interrupt 該函數(shù)的大體流程如下:(此段總結(jié)來(lái)源同上) A.獲取設(shè)備私有數(shù)據(jù)net_priv(); B.讀取CS8900的中斷端口狀態(tài)readword(); C.判斷中斷類型: a.接收事件:調(diào)用net_rx()接收數(shù)據(jù); b.傳輸事件:調(diào)用netif_wake_queue()喚醒傳輸隊(duì)列,進(jìn)行異常處理; c.緩沖區(qū)事件:可以發(fā)送數(shù)據(jù),調(diào)用netif_wake_queue()喚醒傳輸隊(duì)列; d.接收包丟失事件:初始化相關(guān)error,錯(cuò)誤計(jì)數(shù) e.傳輸沖突時(shí)間:初始化相關(guān)error D.返回中斷句柄。 net_interrupt中斷處理函數(shù)的實(shí)現(xiàn)非常簡(jiǎn)單,它首先讀出cs8900的ISQ寄存器的值,然后根據(jù)ISQ的值分別處理各種情況。當(dāng)中斷發(fā)生時(shí),這些中斷實(shí)際反映在相應(yīng)的寄存器中,ISQ寄存器用低6位記錄了當(dāng)前寄存器的編號(hào),高10位記錄了當(dāng)前寄存器的實(shí)際內(nèi)容。這些寄存器有:RxEvent(Register 4),TxEvent(Register 8),BufEvent(RegisterC),RxMISS(Register 10) 和 TxCOL(Register 12)。比如,傳輸成功后,cs8900將TxEvent的第0bit置為1,如果允許該事件中斷,那么ISQ寄存器的低6位將記錄TxEvent的編號(hào)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,根據(jù)cs8900的用戶手冊(cè),這里再次說(shuō)明了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位紀(jì)錄了發(fā)生中斷的寄存器 case ISQ_RECEIVER_EVENT: //ISQ_RECEIVER_EVENT=0x04, //中斷源來(lái)自RxEvent,表示接收到了數(shù)據(jù)包 /* Got a packet(s). */ net_rx(dev); break; case ISQ_TRANSMITTER_EVENT: //ISQ_RECEIVER_EVENT=0x08, //中斷源來(lái)自TxEvent,根據(jù) net_open中設(shè)置,有很多發(fā)送事件 //可以產(chǎn)生中斷,需要分別處理 lp->stats.tx_packets++; //累加發(fā)送包的總數(shù) netif_wake_queue(dev); /* Inform upper layers. */ if ((status & ( TX_OK | //ISQ的高10位描述了TxEvent的實(shí)際內(nèi)容, //也即實(shí)際傳輸?shù)男畔?/font>這里似乎status應(yīng)該右移6位?的確應(yīng)該 //這樣,這里之所以沒(méi)這樣做,是因?yàn)?font face="Times New Roman">TX_OK等這些值,在設(shè) //計(jì)時(shí)已經(jīng)左移了6位 TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) { //做些錯(cuò)誤統(tǒng)計(jì)工作 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, //中斷源來(lái)自BufEvent if (status & TX_UNDERRUN) { //這里說(shuō)明估計(jì)的發(fā)送長(zhǎng)度過(guò)短,可能需要做調(diào)整 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時(shí)初始化為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, //中斷來(lái)自于RxMISS,該寄存器的高10位記錄丟失的數(shù)據(jù)包 lp->stats.rx_missed_errors += (status >>6); break; case ISQ_TX_COL_EVENT: //ISQ_TX_COL_EVENT=0x12,中斷來(lái)自于 //TxCOL,該寄存器的高10位記錄發(fā)了生沖突的數(shù)據(jù)包 lp->stats.collisions += (status >>6); break; } } return IRQ_RETVAL(handled); } To be continued…… ------ anmnmnly ------ 2007.12.5 |