二、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.AUT從頭檢測自動配置
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_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為默認
//接收模式。該模式下,只接收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);
}
To be continued……
------ anmnmnly
------ 2007.12.5