8250 driver Linux多串列埠驅動解析 xr16v554
一:前言
前一段時間自己實踐了一下8250晶片串列埠驅動的編寫。今天就在此基礎上分析一下 linux kernel 自帶的串列埠驅動。畢竟只有對比專業的驅動程式碼才能更好的進步, 同以往一樣,基於linux kernel2.6.25.
相應驅動程式碼位於: linux-2.6.25/drivers/serial/8250.c。
二:8250串列埠驅動初始化 相應的初始化函式為 serial8250_init().程式碼如下 : static int __init serial8250_init(void) { int ret, i; if (nr_uarts > UART_NR)
nr_uarts = UART_NR; printk(KERN_INFO "Serial: 8250/16550 driver " "%d ports, IRQ sharing %sabled/n", nr_uarts, share_irqs ? "en" : "dis"); for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock); ret = uart_register_driver(&serial8250_reg);
if (ret == 0) goto out; platform_device_del(serial8250_isa_devs); put_dev: platform_device_put(serial8250_isa_devs); unreg_uart_drv: uart_unregister_driver(&serial8250_reg); out: return ret; } 這段程式碼涉及到的知識要求,如platform ,uart等我們在之前都已經做過詳細的分析。這裡不再重複。在程式碼中 UART_NR: 表示串列埠的個數。這個引數在編譯核心的時候可以自己配置,預設為32 。 我們按照程式碼中的流程一步一步進行研究。 1: 註冊uart_driver. 對應uart-driver的結構為 serial8250_reg. 定義如下: static struct uart_driver serial8250_reg = { .owner = THIS_MODULE, .driver_name = "serial", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, .nr = UART_NR, .cons = SERIAL8250_CONSOLE, };
TTY_MAJOR定義如下: #define TTY_MAJOR 4 從上面可以看出。串列埠對應的裝置節點為/dev/ ttyS0 ~ /dev/ ttyS(UART_NR).裝置節點號為( 4,64 )起始的 UART_NR 個節點。 2:初始化並註冊 platform_device 相關程式碼如下: serial8250_isa_devs = platform_device_alloc("serial8250", PAT8250_DEV_LEGACY); platform_device_add(serial8250_isa_devs); 可以看出。serial8250_isa_devs->name為 serial8250。這個引數是在匹配 platform_device 和platform_driver使用的。 3:為uart-driver 新增 port. 相關程式碼如下: serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev) 跟進這個函式看一下: static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) {
int i; serial8250_isa_init_ports(); for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; up->port.dev = dev; uart_add_one_port(drv, &up->port); } }
在這裡函式裡,初始化了port.然後將它新增到 uart-driver 中。我們還注意到。生成的 deivce 節點,在 sysfs 中是位於 platform_deivce 對應目錄的下面 . serial8250_isa_init_ports()程式碼如下所示: static void __init serial8250_isa_init_ports(void) { struct uart_8250_port *up; static int first = 1; int i; if (!first) return; first = 0; for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; up->port.line = i; spin_lock_init(&up->port.lock); init_timer(&up->timer); up->timer.function = serial8250_timeout; /* * ALPHA_KLUDGE_MCR needs to be killed. */ up->mcr_mask = ~ALPHA_KLUDGE_MCR; up->mcr_force = ALPHA_KLUDGE_MCR; up->port.ops = &serial8250_pops; }
for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;i++, up++) { up->port.iobase= old_serial_port[i].port; up->port.irq= irq_canonicalize(old_serial_port[i].irq); up->port.uartclk = old_serial_port[i].baud_base * 16; up->port.flags = old_serial_port[i].flags; up->port.hub6= old_serial_port[i].hub6; up->port.membase= old_serial_port[i].iomem_base; up->port.iotype= old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; if (share_irqs)
up->port.flags |= UPF_SHARE_IRQ;
}
}
在這裡,我們關注一下注要成員的初始化。 Uart_port 的各項操作位於 serial8250_pops 中。 Iobase 、 irq 等成員是從 old_serial_por 這個結構中得來的,這個結構如下所示: static const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */ }
#define SERIAL_PORT_DFNS / /* UART CLK PORT IRQ FLAGS */ / { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ / { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ / { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ / { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ 從上面看到。前兩項對應了 com1、 com2的各項引數。如暫存器首始地址, Irq 號等。後面兩項不太清楚。在上面的程式碼中,我們看到了 uart_port 各項成員的初始化。在後面很多操作中需要用到這個成員。我們等分析相關部份的時候,再到這個地方來看相關成員的值。
4:註冊platform_driver 相關程式碼如下: platform_driver_register(&serial8250_isa_driver); serial8250_isa_driver定義如下: static struct platform_driver serial8250_isa_driver = { .probe= serial8250_probe, .remove= __devexit_p(serial8250_remove), .suspend= serial8250_suspend, .resume= serial8250_resume, .driver = { .name = "serial8250", .owner = THIS_MODULE, }, }
為了以後把分析集中到具體的驅動部份.我們先把這個 platform_driver 引出的問題講述完 . 經過前面有關platform的分 析我們知道,這個 platform 的 name 為 ” serial8250” 。剛好跟前面註冊的 platform_device 相匹配 . 會呼叫 platform_driver-> probe. 在這裡 , 對應的介面為 : serial8250_probe().程式碼如下: static int __devinit serial8250_probe(struct platform_device *dev) { struct plat_serial8250_port *p = dev->dev.platform_data; struct uart_port port; int ret, i; memset(&port, 0, sizeof(struct uart_port)); for (i = 0; p && p->flags != 0; p++, i++) { port.iobase= p->iobase; port.membase= p->membase; port.irq= p->irq; port.uartclk= p->uartclk; port.regshift= p->regshift; port.iotype= p->iotype; port.flags= p->flags; port.mapbase= p->mapbase; port.hub6= p->hub6; port.private_data= p->private_data; port.dev= &dev->dev; if (share_irqs) port.flags |= UPF_SHARE_IRQ; ret = serial8250_register_port(&port); if (ret < 0) { dev_err(&dev->dev, "unable to register port at index %d " "(IO%lx MEM%llx IRQ%d): %d/n", i, p->iobase, (unsigned long long)p->mapbase, p->irq, ret); } } return 0; } 從上述程式碼可以看出.會 將 dev->dev.platform_data 所代表的 port 新增到 uart_driver 中。這個dev->dev.platform_data 究竟代表什麼. 我們在看到的時候再來研究它. 現在,我們把精力集中到 uart_port 的操作上。
三:config_port 過程 在初始化uart_port的過程中 , 在以下程式碼片段 : serial8250_isa_init_ports(void) { ......
for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++, up++) { up->port.iobase = old_serial_port[i].port; up->port.irq = irq_canonicalize(old_serial_port[i].irq); up->port.uartclk = old_serial_port[i].baud_base * 16; up->port.flags = old_serial_port[i].flags; up->port.hub6 = old_serial_port[i].hub6; up->port.membase = old_serial_port[i].iomem_base; up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; if (share_irqs) up->port.flags |= UPF_SHARE_IRQ; } } 而old_serial_port又定義如下 : static const struct old_serial_port old_serial_port[] = { SERIAL_PORT_DFNS /* defined in asm/serial.h */ }; #define SERIAL_PORT_DFNS / /* UART CLK PORT IRQ FLAGS */ / { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ / { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ / { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ / { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ 由此可見,port->flags被定義成了 STD_COM_FLAGS, 定義如下: #ifdef CONFIG_SERIAL_DETECT_IRQ #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) #define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) #else #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASY NC_SKIP_TEST) #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF #endif 從這裡看到,不管是否自己探測 IRQ, 都會定義 ASYNC_BOOT_AUTOCONF 。這樣 , 在uart_add_one_port()的時候。就會進入到port->config_port 來配置埠,在 8250 中 , 對應的介面為 : serial8250_config_port(). 程式碼如下: static void serial8250_config_port(struct uart_port *port, int flags) { struct uart_8250_port *up = (struct uart_8250_port *)port; int probeflags = PROBE_ANY;
int ret; /* * Find the region that we can probe for. This in turn * tells us whether we can probe for the type of port. */ ret = serial8250_request_std_resource(up); if (ret < 0) return; ret = serial8250_request_rsa_resource(up); if (ret < 0) probeflags &= ~PROBE_RSA; if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) serial8250_release_rsa_resource(up); if (up->port.type == PORT_UNKNOWN) serial8250_release_std_resource(up); }serial8250_request_std_resource 和serial8250_request_rsa_resource 都是分配操作的埠,回顧在前面的分析中,port的相關引數會從 old_serial_port 中取得 . 而 old_serial_port 中又沒有定義 port->iotype 和 port-> regshift. 也就是說對應這兩項全為 0. 而 #define UPIO_PORT (0) 即表示是要操作I/O埠。自己閱 讀這兩個函式,會發現在 serial8250_request_rsa_resource() 中是會返回失敗的。另外, 在uart_add_one_port() 在進行埠匹配時 , 會先置 flags 為 UART_CONFIG_TYPE 。這樣 , 在本次操作中 , if (flags & UART_CONFIG_TYPE)是會滿足的,相應的就會進入 autoconfig() 。 程式碼如下,這段程式碼比較長 , 分段分析如下 : static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) { unsigned char status1, scratch, scratch2, scratch3; unsigned char save_lcr, save_mcr; unsigned long flags; if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return; DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", up->port.line, up->port.iobase, up->port.membase); /* * We really do need global IRQs disabled here - we're going to * be frobbing the chips IRQ enable register to see if it exists. */ spin_lock_irqsave(&up->port.lock, flags); up->capabilities = 0; up->bugs = 0; if (!(up->port.flags & UPF_BUGGY_UART)) { /* * Do a simple existence test first; if we fail this, * there's no point trying anything else. * * 0x80 is used as a nonsense port to prevent against * false positives due to ISA bus float. The * assumption is that 0x80 is a non-existent port; * which should be safe since include/asm/io.h also * makes this assumption. * * Note: this is safe as long as MCR bit 4 is clear * and the device is in "PC" mode. */ scratch = serial_inp(up, UART_IER); serial_outp(up, UART_IER, 0); #ifdef __i386__ outb(0xff, 0x080); #endif /* * Mask out IER[7:4] bits for test as some UARTs (e.g. TL * 16C754B) allow only to modify them if an EFR bit is set. */ scratch2 = serial_inp(up, UART_IER) & 0x0f; serial_outp(up, UART_IER, 0x0F); #ifdef __i386__ outb(0, 0x080); #endif scratch3 = serial_inp(up, UART_IER) & 0x0f; serial_outp(up, UART_IER, scratch); if (scratch2 != 0 || scratch3 != 0x0F) { /* * We failed; there's nothing here */ DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", scratch2, scratch3); goto out; } } 在這裡,先對 8250 是否存在做一個簡單的判斷 ,先將IER中的值取得 , 這樣可以在測試之後恢復 IER 中的值 ,然後往IER中寫放 0 , 再將IER 中的值取出 , 又往IER 中寫入 0xOF. 然後再將 IER 中的值取出 . 最後將 IER 中的值恢復到原值 。 這樣就可以根據寫入的值和讀出的值是否相等來判斷該暫存器是否存在. save_mcr = serial_in(up, UART_MCR); save_lcr = serial_in(up, UART_LCR); 在這裡,先將 MCR 和 LCR 中的值取出 ,因為在後面的操作中會使用這兩個暫存器 , 方便使用完了恢復 /* * Check to see if a UART is really there. Certain broken * internal modems based on the Rockwell chipset fail this * test, because they apparently don't implement the loopback * test mode. So this test is skipped on the COM 1 through * COM 4 ports. This *should* be safe, since no board * manufacturer would be stupid enough to design a board * that conflicts with COM 1-4 --- we hope! */ if (!(up->port.flags & UPF_SKIP_TEST)) { serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); status1 = serial_inp(up, UART_MSR) & 0xF0; serial_outp(up, UART_MCR, save_mcr); if (status1 != 0x90) { DEBUG_AUTOCONF("LOOP test failed (%02x) ", status1); goto out; } } 在這裡,將 MCR的自檢位置位 , 並允許向中斷控制器產生中斷 ,而且產生RTS訊號 ,這樣MSR暫存器應該可以檢測到這個訊號 . 如果沒有檢測到 . 自測失 敗 !MCR 暫存器已經操作完了 , 恢復 MCR 暫存器的原值 。 /* * We're pretty sure there's a port here. Lets find out what * type of port it is. The IIR top two bits allows us to find * out if it's 8250 or 16450, 16550, 16550A or later. This * determines what we test for next. * * We also initialise the EFR (if any) to zero for later. The * EFR occupies the same register location as the FCR and IIR. */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, 0); serial_outp(up, UART_LCR, 0); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); scratch = serial_in(up, UART_IIR) >> 6; DEBUG_AUTOCONF("iir=%d ", scratch); switch (scratch) { case 0: autoconfig_8250(up); break; case 1: up->port.type = PORT_UNKNOWN; break; case 2: up->port.type = PORT_16550; break; case 3: autoconfig_16550a(up); break; } 在這裡,先允許使用 FIFO 暫存器 , 然後通過 IIR 寄存的高二位來判斷晶片的型別 #ifdef CONFIG_SERIAL_8250_RSA /* * Only probe for RSA ports if we got the region. */ if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { int i; for (i = 0 ; i < probe_rsa_count; ++i) { if (probe_rsa[i] == up->port.iobase && __enable_rsa(up)) { up->port.type = PORT_RSA; break; } } } #endif #ifdef CONFIG_SERIAL_8250_AU1X00 /* if access method is AU, it is a 16550 with a quirk */ if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) up->bugs |= UART_BUG_NOMSR; #endif serial_outp(up, UART_LCR, save_lcr); if (up->capabilities != uart_config[up->port.type].flags) { printk(KERN_WARNING "ttyS%d: detected caps %08x should be %08x/n", up->port.line, up->capabilities, uart_config[up->port.type].flags); } up->port.fifosize = uart_config[up->port.type].fifo_size; up->capabilities = uart_config[up->port.type].flags; up->tx_loadsz = uart_config[up->port.type].tx_loadsz; if (up->port.type == PORT_UNKNOWN) goto out; /* * Reset the UART. */ #ifdef CONFIG_SERIAL_8250_RSA if (up->port.type == PORT_RSA) serial_outp(up, UART_RSA_FRR, 0); #endif serial_outp(up, UART_MCR, save_mcr); serial8250_clear_fifos(up); serial_in(up, UART_RX); if (up->capabilities & UART_CAP_UUE) serial_outp(up, UART_IER, UART_IER_UUE); else serial_outp(up, UART_IER, 0); out: spin_unlock_irqrestore(&up->port.lock, flags); DEBUG_AUTOCONF("type=%s/n", uart_config[up->port.type].name); } 最後,復位串列埠控制器,我們假設使用的是 8250 串列埠晶片 ,在晶片型別判斷的時候就會進入autoconfig_8250().程式碼如 下 : static void autoconfig_8250(struct uart_8250_port *up) { unsigned char scratch, status1, status2; up->port.type = PORT_8250; scratch = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0xa5); status1 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, 0x5a); status2 = serial_in(up, UART_SCR); serial_outp(up, UART_SCR, scratch); if (status1 == 0xa5 && status2 == 0x5a) up->port.type = PORT_16450; } 如果存在SCR暫存器 , 則晶片是 16450 型別的 ,這不是我們需要研究的晶片 。 回到serial8250_config_port()中 ,程式碼片 段如下所示 : static void serial8250_config_port(struct uart_port *port, int flags) { …… if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) serial8250_release_rsa_resource(up); if (up->port.type == PORT_UNKNOWN) serial8250_release_std_resource(up); } 如果定義了自己控測IRQ號 (CONFIG_SERIAL_8250_DETECT_IRQ), 一般情況下, 編譯核心的時候一般都將其賦值為 CONFIG_SERIAL_8250_DETECT_IRQ = y, 此時就會進入autoconfig_irq(). 程式碼如下 : static void autoconfig_irq(struct uart_8250_port *up) { unsigned char save_mcr, save_ier; unsigned char save_ICP = 0; unsigned int ICP = 0; unsigned long irqs; int irq; if (up->port.flags & UPF_FOURPORT) {
ICP = (up->port.iobase & 0xfe0) | 0x1f; save_ICP = inb_p(ICP); outb_p(0x80, ICP); (void) inb_p(ICP); } /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_inp(up, UART_MCR); save_ier = serial_inp(up, UART_IER); serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); irqs = probe_irq_on(); serial_outp(up, UART_MCR, 0); udelay(10); if (up->port.flags & UPF_FOURPORT) { serial_outp(up, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); } else { serial_outp(up, UART_MCR, UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); } serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ (void)serial_inp(up, UART_LSR); (void)serial_inp(up, UART_RX); (void)serial_inp(up, UART_IIR); (void)serial_inp(up, UART_MSR); serial_outp(up, UART_TX, 0xFF); udelay(20); irq = probe_irq_off(irqs); serial_outp(up, UART_MCR, save_mcr); serial_outp(up, UART_IER, save_ier); if (up->port.flags & UPF_FOURPORT) outb_p(save_ICP, ICP); up->port.irq = (irq > 0) ? irq : 0; } 在上述程式碼的操作中,先將 8250 相關中斷允許暫存器全開啟 ,然後呼叫驅動使用的函式, 當它不得不探測來決定哪個中斷線被裝置在使用 ,probe_irq_on()將中斷暫時關掉, 然後配置MCR 暫存器使之傳送 DTR 和 RTS. 之後再用 probe_irq_off() 來檢測 IRQ 號 , 如果檢測成功, 則值賦值給 port->irq ,進行到這裡,conifg_port動作就完成了 。經過這個config_port過程後 , 我們發現 , 並沒有對 serial8250_isa_devs->dev-> platform_data 賦值, 也就是說platform_driver->probe 函式並無實質性的處理 , 在第一次for 迴圈的時 , 就會因條件不符而退出 . 四: startup操作 在前面分析uart驅動架構的時候 , 曾說過 , 在 open 的時候 , 會呼叫 port->startup() , 在本次分析的驅動中 , 對應介面為serial8250_startup() 分段分析如下 : static int serial8250_startup(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; unsigned char lsr, iir; int retval; up->capabilities = uart_config[up->port.type].flags; up->mcr = 0; if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_IER, 0); serial_outp(up, UART_LCR, 0); serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_LCR, 0); } #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the * higher speed clock. */ enable_rsa(up); #endif /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) */ serial8250_clear_fifos(up); 上面的程式碼都不是對應8250晶片的情況 /* * Clear the interrupt registers. */ (void) serial_inp(up, UART_LSR); (void) serial_inp(up, UART_RX); (void) serial_inp(up, UART_IIR); (void) serial_inp(up, UART_MSR); 復位LSR,RX,IIR,MSR暫存器 /* * At this point, there's no way the LSR could still be 0xff; * if it is, then bail out, because there's likely no UART * here. */ if (!(up->port.flags & UPF_BUGGY_UART) && (serial_inp(up, UART_LSR) == 0xff)) { printk("ttyS%d: LSR safety check engaged!/n", up->port.line); return -ENODEV; } 若LSR暫存器中的值為 0xFF. 異常 /* * For a XR16C850, we need to set the trigger levels */ if (up->port.type == PORT_16850) { unsigned char fctr; serial_outp(up, UART_LCR, 0xbf); fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); serial_outp(up, UART_TRG, UART_TRG_96); serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); serial_outp(up, UART_TRG, UART_TRG_96); serial_outp(up, UART_LCR, 0); } 16850系列晶片的處理, 忽略 if (is_real_interrupt(up->port.irq)) { /* * Test for UARTs that do not reassert THRE when the * transmitter is idle and the interrupt has already * been cleared. Real 16550s should always reassert * this interrupt whenever the transmitter is idle and * the interrupt is enabled. Delays are necessary to * allow register changes to become visible. */ spin_lock_irqsave(&up->port.lock, flags); wait_for_xmitr(up, UART_LSR_THRE); serial_out_sync(up, UART_IER, UART_IER_THRI); udelay(1); /* allow THRE to set */ serial_in(up, UART_IIR); serial_out(up, UART_IER, 0); serial_out_sync(up, UART_IER, UART_IER_THRI); udelay(1); /* allow a working UART time to re-assert THRE */ iir = serial_in(up, UART_IIR); serial_out(up, UART_IER, 0); spin_unlock_irqrestore(&up->port.lock, flags); /* * If the interrupt is not reasserted, setup a timer to * kick the UART on a regular basis. */ if (iir & UART_IIR_NO_INT) { pr_debug("ttyS%d - using backup timer/n", port->line); up->timer.function = serial8250_backup_timeout; up->timer.data = (unsigned long)up; mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout) + HZ / 5); } } 如果中斷號有效,還要進一步判斷這 箇中斷號是否有效 . 具體操作為 , 先等待 8250 傳送暫存器空 . 然後允許傳送中斷空的中斷 . 然後判斷 IIR 暫存器是 否收到中斷 . 如果有沒有收到中斷 , 則說明這根中斷線無效 。只能採用輪詢的方式.關於輪詢方式 , 我們在之後再以獨立章節的形式給出分析 /* * If the "interrupt" for this port doesn't correspond with any * hardware interrupt, we use a timer-based system. The original * driver used to do this with IRQ0. */ if (!is_real_interrupt(up->port.irq)) { up->timer.data = (unsigned long)up; mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); } else { retval = serial_link_irq_chain(up); if (retval) return retval; } 如果沒有設定中斷號, 則採用輪詢方式. 如果中斷後有效. 流程轉入serial_link_irq_chain(). 在這個裡面 .會註冊中斷處理函 數. /* * Now, initialize the UART */ serial_outp(up, UART_LCR, UART_LCR_WLEN8); spin_lock_irqsave(&up->port.lock, flags); if (up->port.flags & UPF_FOURPORT) { if (!is_real_interrupt(up->port.irq)) up->port.mctrl |= TIOCM_OUT1; } else /* * Most PC uarts need OUT2 raised to enable interrupts. */ if (is_real_interrupt(up->port.irq)) up->port.mctrl |= TIOCM_OUT2; serial8250_set_mctrl(&up->port, up->port.mctrl); /* * Do a quick test to see if we receive an * interrupt when we enable the TX irq. */ serial_outp(up, UART_IER, UART_IER_THRI); lsr = serial_in(up, UART_LSR); iir = serial_in(up, UART_IIR); serial_outp(up, UART_IER, 0); if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds/n", port->line); } } else { up->bugs &= ~UART_BUG_TXEN; } spin_unlock_irqrestore(&up->port.lock, flags); /* * Clear the interrupt registers again for luck, and clear the * saved flags to avoid getting false values from polling * routines or the previous session. */ serial_inp(up, UART_LSR); serial_inp(up, UART_RX); serial_inp(up, UART_IIR); serial_inp(up, UART_MSR); up->lsr_saved_flags = 0; up->msr_saved_flags = 0; /* * Finally, enable interrupts. Note: Modem status interrupts * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here. */ up->ier = UART_IER_RLSI | UART_IER_RDI; serial_outp(up, UART_IER, up->ier); if (up->port.flags & UPF_FOURPORT) { &nbs p; unsigned int icp; /* * Enable interrupts on the AST Fourport board */ icp = (up->port.iobase & 0xfe0) | 0x01f; outb_p(0x80, icp); (void) inb_p(icp); } return 0; } 最後,就是對 8250 晶片的初始化了 ,包括:在 LCR中設定資料格式 , 在 MCR 中設定允許中斷到 8259. 在 IER 中設定相關允許位 。另外在open的時候 , 還會呼叫 port-> enable_ms () 介面 , 在本例中對應為 : serial8250_enable_ms(). 程式碼如下: static