




已阅读5页,还剩33页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
linux设备驱动之8250串口驱动 一:前言前一段时间自己实践了一下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 $Revision: 1.90 $ %d ports, IRQ sharing %sabledn, nr_uarts, share_irqs ? en : dis); for (i = 0; i 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;这段代码涉及到的知识要求,如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/ ttyS0(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 _initserial8250_register_ports(struct uart_driver *drv, struct device *dev) int i; serial8250_isa_init_ports(); for (i = 0; i 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 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 port.iobase = old_serial_porti.port; up-port.irq = irq_canonicalize(old_serial_porti.irq); up-port.uartclk = old_serial_porti.baud_base * 16; up-port.flags = old_serial_porti.flags; up-port.hub6 = old_serial_porti.hub6; up-port.membase = old_serial_porti.iomem_base; up-port.iotype = old_serial_porti.io_type; up-port.regshift = old_serial_porti.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 dev, unable to register port at index %d (IO%lx MEM%llx IRQ%d): %dn, 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 port.iobase = old_serial_porti.port; up-port.irq = irq_canonicalize(old_serial_porti.irq); up-port.uartclk = old_serial_porti.baud_base * 16; up-port.flags = old_serial_porti.flags; up-port.hub6 = old_serial_porti.hub6; up-port.membase = old_serial_porti.iomem_base; up-port.iotype = old_serial_porti.io_type; up-port.regshift = old_serial_porti.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从这里看到,不管是否自己探测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 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 - were 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, * theres 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 IER7: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; theres 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 dont 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寄存器的原值. /* * Were pretty sure theres a port here. Lets find out what * type of port it is. The IIR top two bits allows us to find * out if its 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 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_configup-port.type.flags) printk(KERN_WARNING ttyS%d: detected caps %08x should be %08xn, up-port.line, up-capabilities, uart_configup-port.type.flags); up-port.fifosize = uart_configup-port.type.fifo_size; up-capabilities = uart_configup-port.type.flags; up-tx_loadsz = uart_configup-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=%sn, uart_);最后,复位串口控制器我们假设使用的是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_ou
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 高空探测基础知识培训课件
- 高空作业安全知识培训课件
- 北京期末语文考试题目及答案
- TK-684-生命科学试剂-MCE
- D-Arabitol-d7-生命科学试剂-MCE
- SB-3CT-Standard-生命科学试剂-MCE
- 园林绿化考试题及答案
- 变形观测考试题及答案
- 病原考试题及答案
- 保安考试试卷大题题库及答案
- 《网络综合布线系统工程技术实训教程(第5版)》 课件 第2章 网络综合布线系统工程技术常用标准
- 2024年秋季新外研版七年级英语上册教学计划
- 高一语文开学第一课课件
- 2024-2030年中国汽车金融行业市场深度分析及竞争格局与发展前景展望研究报告
- 光伏组件回收再利用建设项目可行性研究报告写作模板-拿地申报
- JGT163-2013钢筋机械连接用套筒
- HIV感染产妇分娩母婴阻断演练脚本
- 《公路桥梁施工监控技术规程》(JTGT3650-01-2022)
- 血气分析标本采集及结果判读
- 科技园区建设规划
- 2024广西公需课高质量共建“一带一路”谱写人类命运共同体新篇章答案
评论
0/150
提交评论