亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊(cè) 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 2406 | 回復(fù): 0
打印 上一主題 下一主題

linux設(shè)備驅(qū)動(dòng)之8250串口驅(qū)動(dòng)1 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2010-01-25 15:01 |只看該作者 |倒序?yàn)g覽

                ------------------------------------------
本文系本站原創(chuàng),歡迎轉(zhuǎn)載!
轉(zhuǎn)載請(qǐng)注明出處:http://ericxiao.cublog.cn/
------------------------------------------
一:前言
前一段時(shí)間自己實(shí)踐了一下8250芯片串口驅(qū)動(dòng)的編寫。今天就在此基礎(chǔ)上分析一下linux
kernel自帶的串口驅(qū)動(dòng)。畢竟只有對(duì)比專業(yè)的驅(qū)動(dòng)代碼才能更好的進(jìn)步,同以往一樣,基于linix
kernel2.6.25.相應(yīng)驅(qū)動(dòng)代碼位于:linux-2.6.25/drivers/serial/8250.c。
二:8250串口驅(qū)動(dòng)初始化
相應(yīng)的初始化函數(shù)為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 $Revision: 1.90 $ "
         "%d ports, IRQ sharing %sabled\n", nr_uarts,
         share_irqs ? "en" : "dis");

     for (i = 0; i  NR_IRQS; i++)
         spin_lock_init(&irq_lists.lock);

     ret = uart_register_driver(&serial8250_reg);
     if (ret)
         goto out;

     serial8250_isa_devs = platform_device_alloc("serial8250",
                                PLAT8250_DEV_LEGACY);
     if (!serial8250_isa_devs) {
         ret = -ENOMEM;
         goto unreg_uart_drv;
     }

     ret = platform_device_add(serial8250_isa_devs);
     if (ret)
         goto put_dev;

     serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);

     ret = platform_driver_register(&serial8250_isa_driver);
     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;
}
這段代碼涉及到的知識(shí)要求,如platform ,uart等我們?cè)谥岸家呀?jīng)做過詳細(xì)的分析。這里不再重復(fù)。在代碼中UART_NR:表示串口的個(gè)數(shù)。這個(gè)參數(shù)在編譯內(nèi)核的時(shí)候可以自己配置,默認(rèn)為32。
我們按照代碼中的流程一步一步進(jìn)行研究。
1:注冊(cè)u(píng)art_driver.
對(duì)應(yīng)uart-driver的結(jié)構(gòu)為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
從上面可以看出。串口對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)為/dev/ ttyS0 ~ /dev/ ttyS0(UART_NR).設(shè)備節(jié)點(diǎn)號(hào)為(4。64)起始的UART_NR個(gè)節(jié)點(diǎn)..
  
2:初始化并注冊(cè)platform_device
相關(guān)代碼如下:
serial8250_isa_devs = platform_device_alloc("serial8250",    PAT8250_DEV_LEGACY);
platform_device_add(serial8250_isa_devs);
可以看出。serial8250_isa_devs.->name為serial8250。這個(gè)參數(shù)是在匹配platform_device和platform_driver使用的.

3:為uart-driver添加port.
相關(guān)代碼如下:
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev)
跟進(jìn)這個(gè)函數(shù)看一下:
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;

         up->port.dev = dev;
         uart_add_one_port(drv, &up->port);
     }
}
在這里函數(shù)里,初始化了port.然后將掛添加到uart-driver中。我們還注意到。生成的deivce節(jié)點(diǎn),在sysfs中是位于platform_deivce對(duì)應(yīng)目錄的下面.
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;

         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.port;
         up->port.irq      = irq_canonicalize(old_serial_port.irq);
         up->port.uartclk  = old_serial_port.baud_base * 16;
         up->port.flags    = old_serial_port.flags;
         up->port.hub6     = old_serial_port.hub6;
         up->port.membase  = old_serial_port.iomem_base;
         up->port.iotype   = old_serial_port.io_type;
         up->port.regshift = old_serial_port.iomem_reg_shift;
         if (share_irqs)
              up->port.flags |= UPF_SHARE_IRQ;
     }
}
在這里,我們關(guān)注一下注要成員的初始化。Uart_port的各項(xiàng)操作位于serial8250_pops中.iobase irq等成員是從old_serial_por這個(gè)結(jié)構(gòu)中得來的,這個(gè)結(jié)構(gòu)如下所示:
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 */
從上面看到。前兩項(xiàng)對(duì)應(yīng)了com1 com2的各項(xiàng)參數(shù)。如寄存器首始地址,Irq號(hào)等。后面兩項(xiàng)不太清楚。
  
在上面的代碼中,我們看到了uart_port各項(xiàng)成員的初始化。在后面很多操作中需要用到這個(gè)成員。我們等分析相關(guān)部份的時(shí)候,再到這個(gè)地方來看相關(guān)成員的值。
  
4:注冊(cè)platform_driver
相關(guān)代碼如下:
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,
     },
}
為了以后把分析集中到具體的驅(qū)動(dòng)部份.我們先把這個(gè)platform_driver引會(huì)的事件講述完.
經(jīng)過前面有關(guān)platform的分析我們知道.這個(gè)platform的name為” serial8250”.剛好跟前面注冊(cè)的platform_device相匹配.會(huì)調(diào)用platform_driver-> probe.在這里,對(duì)應(yīng)的接口為:
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;
}
從上述代碼可以看出.會(huì)將dev->dev.platform_data所代表的port添加到uart_driver中.這個(gè)dev->dev.platform_data究竟代表什么.我們?cè)诳吹降臅r(shí)候再來研究它.
現(xiàn)在,我們把精力集中到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.port;
         up->port.irq      = irq_canonicalize(old_serial_port.irq);
         up->port.uartclk  = old_serial_port.baud_base * 16;
         up->port.flags    = old_serial_port.flags;
         up->port.hub6     = old_serial_port.hub6;
         up->port.membase  = old_serial_port.iomem_base;
         up->port.iotype   = old_serial_port.io_type;
         up->port.regshift = old_serial_port.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 | ASYNC_SKIP_TEST)
#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
#endif
從這里看到,不管是否自己探測(cè)IRQ,都會(huì)定義ASYNC_BOOT_AUTOCONF.這樣,在uart_add_one_port()的時(shí)候.就會(huì)進(jìn)入到port->config_port來配置端口.在8250中,對(duì)應(yīng)的接口為: 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的相關(guān)參數(shù)會(huì)從old_serial_port中取得.而old_serial_port中又沒有定義
port->iotype和port-> regshift.也就是說對(duì)應(yīng)這兩項(xiàng)全為0.而
#define UPIO_PORT      (0)
即表示是要操作I/O端口.
自己閱讀這兩個(gè)函數(shù)代表.會(huì)發(fā)現(xiàn)在serial8250_request_rsa_resource()中是會(huì)返回失敗的.
另外,在uart_add_one_port()在進(jìn)行端口匹配時(shí),會(huì)先置flags為UART_CONFIG_TYPE.
這樣,在本次操作中, if (flags & UART_CONFIG_TYPE)是會(huì)滿足的.相應(yīng)的就會(huì)進(jìn)入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;
         }
     }
在這里,先對(duì)8250是否存在做一個(gè)簡(jiǎn)單的判斷.先將IER中的值取得,這樣可以在測(cè)試之后恢復(fù)IER中的值.然后往IER中寫放0.再將IER中的值取
出.又往IER中寫入0xOF.然后再將IER中的值取出.最后將IER中的值恢復(fù)到原值.這樣就可以根據(jù)寫入的值和讀出的值是否相等來判斷該寄存器是否
存在.
     save_mcr = serial_in(up, UART_MCR);
     save_lcr = serial_in(up, UART_LCR);
在這里,先將MCR和LCR中的值取出.因?yàn)樵诤竺娴牟僮髦袝?huì)使用這兩個(gè)寄存器.方便使用完了恢復(fù)
     /*
      * 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的自檢位置位,并允許向中斷控制器產(chǎn)生中斷.而且產(chǎn)生RTS信號(hào).這樣MSR寄存器應(yīng)該可以檢測(cè)到這個(gè)信號(hào).如果沒有檢測(cè)到.自測(cè)失敗!MCR寄存器已經(jīng)操作完了,恢復(fù)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 == 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);
}
最后,復(fù)位串口控制器
  
我們假設(shè)使用的是8250串口芯片.在芯片類型判斷的時(shí)候就會(huì)進(jìn)入autoconfig_8250().代碼如下:
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);
}
如果定義了自己控測(cè)IRQ號(hào)(CONFIG_SERIAL_8250_DETECT_IRQ).一般情況下,編譯內(nèi)核的時(shí)候一般都將其賦值為
CONFIG_SERIAL_8250_DETECT_IRQ = y.此時(shí)就會(huì)進(jìn)入autoconfig_irq().代碼如下:
               
               

本文來自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u3/92500/showart_2159902.html
您需要登錄后才可以回帖 登錄 | 注冊(cè)

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請(qǐng)注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP