ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

kernel源码(十九)字符设备-serial.c

2022-04-05 03:00:21  阅读:252  来源: 互联网

标签:kernel tty void write outb 源码 显卡 serial port


该源文件主要处理串行数据的发送和接收。

 

0 显卡和CGA

参考:https://blog.csdn.net/cy295957410/article/details/108436730

CGA(Color Graphics Adapter)彩色图形接口。是标识显示卡性能的一种标准,提供基本的彩色文本显示

EGA(Enhance Graphics Adapter)增强图形接口,比CGA显示性能要高,支持彩色文本和图形

VGA(Video Graphics Adapter)视频图形接口,三者中显示性能最高的一种标准。

对于我们接下来要学习的linux0.11,我们只了解CGA就足够了,源码同样适用于EGA和VGA。

 

在console中要显示文字,需要显示器和显卡两种设备。显卡用于提供显示内容,显示器负责将显卡中的内容显示出来。

https://www.cnblogs.com/zhenjingcool/p/15938330.html这片博文中我们介绍过,低1M内存空间有一部分内存被外设占用。其中0xA0000-0xBFFFF被图形视频缓冲区占用。如下图

 

 其中0xB8000-0xBFFFF这32kb空间被CGA使用。也就是说,这个内存区域就是显卡的显存(这里IO设备显卡和内存统一编址),我们把要显示的数据写到这里,显示器负责显示这里的内容。

当显卡初始化的时候,会自动初始化80*25模式(屏幕有25行,每一行80个字符),一个屏幕共可显示2000个字符,一个字符占用2字节,共需要4000字节

从0xB8000开始,每两个字节表示一个字符,从屏幕上第一行开始对应,一行一行的对应下去。

而这两个字节前一个字节是字符的ASCII码,后一个字节控制这个字符的颜色和属性的控制信息,各个位的含义如下:

 

 显卡的显存是和主存统一编址,但是显卡的控制信息是独立编址的(独立编址:每种IO设备都有自己的存储空间,往往控制信息是写在IO设备自己的存储空间中的)。但是显卡自己的存储空间(显卡的控制寄存器)有几百个。因此我们使用0x3D4作为显卡内部寄存器的索引,再通过0x3D5端口设置相应寄存器的值。具体做法是:先向0x3D4端口写入要访问的显卡上的寄存器编号,再通过0x3D5端口来读写寄存器数据。

CGA使用的是MC6845芯片,其端口寄存器如下

 

 

0 UART异步串行通信控制器

PC机上一般有两个符合rs232c标准的串行口,使用UART芯片来进行串行数据的收发工作。UART作用是把数据按照串行方式发送或接收。

传输一个字符的时候由[起始位+数据位+奇偶校验位+停止位]组成

下图中第一列为串口1(括号中是串口2)

 

 

 

1 源码

/*
 *  linux/kernel/serial.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 *    serial.c
 *
 * This module implements the rs232 io functions
 *    void rs_write(struct tty_struct * queue);
 *    void rs_init(void);
 * and all interrupts pertaining to serial IO.
 */

#include <linux/tty.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/io.h>

#define WAKEUP_CHARS (TTY_BUF_SIZE/4)

extern void rs1_interrupt(void);
extern void rs2_interrupt(void);

static void init(int port)
{
    outb_p(0x80,port+3);    /* set DLAB of line control reg */
    outb_p(0x30,port);    /* LS of divisor (48 -> 2400 bps */
    outb_p(0x00,port+1);    /* MS of divisor */
    outb_p(0x03,port+3);    /* reset DLAB */
    outb_p(0x0b,port+4);    /* set DTR,RTS, OUT_2 */
    outb_p(0x0d,port+1);    /* enable all intrs but writes */
    (void)inb(port);    /* read data port to reset things (?) */
}

void rs_init(void)
{
    set_intr_gate(0x24,rs1_interrupt);
    set_intr_gate(0x23,rs2_interrupt);
    init(tty_table[1].read_q.data);
    init(tty_table[2].read_q.data);
    outb(inb_p(0x21)&0xE7,0x21);
}

/*
 * This routine gets called when tty_write has put something into
 * the write_queue. It must check wheter the queue is empty, and
 * set the interrupt register accordingly
 *
 *    void _rs_write(struct tty_struct * tty);
 */
void rs_write(struct tty_struct * tty)
{
    cli();
    if (!EMPTY(tty->write_q))
        outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
    sti();
}
View Code

 

#define WAKEUP_CHARS (TTY_BUF_SIZE/4) //一个宏定义,表示达到TTY_BUF_SIZE/4时就开始传送

extern void rs1_interrupt(void); //串口1和串口2的中断处理程序
extern void rs2_interrupt(void);

初始化串行的端口,从上面介绍的UART,我们以串口1为例,port=0x3f8

static void init(int port)
{
    outb_p(0x80,port+3);    //port+3=0x3fb,为写线路控制寄存器,写入的值为1000 0000,即设置DLAB=1/* set DLAB of line control reg */
    outb_p(0x30,port);    //指定串行端口传输的波特率是2400/* LS of divisor (48 -> 2400 bps */
    outb_p(0x00,port+1);  //波特率因子的高字节  /* MS of divisor */
    outb_p(0x03,port+3);  //DLAB复位,且指定数据位长度11表示8位数据位  /* reset DLAB */
    outb_p(0x0b,port+4);    /* set DTR,RTS, OUT_2 */
    outb_p(0x0d,port+1); //允许所有中断源中断(除了写以外)   /* enable all intrs but writes */
    (void)inb(port); //读数据口   /* read data port to reset things (?) */
}

初始化串行中断程序

void rs_init(void)
{
    set_intr_gate(0x24,rs1_interrupt); //设置串口1的中断向量是int 0x24,中断处理程序是rs1_interrupt
    set_intr_gate(0x23,rs2_interrupt); //设置串口2的中断向量是int 0x23,中断处理程序是rs2_interrupt
    init(tty_table[1].read_q.data);//init是上面定义的函数,分别初始化串口1和串口2
    init(tty_table[2].read_q.data);
    outb(inb_p(0x21)&0xE7,0x21); //允许主8259A响应中断请求
}

在tty_write把数据放到write_queue中时,会调用该函数实现串行数据的发送输出

/*
 * This routine gets called when tty_write has put something into
 * the write_queue. It must check wheter the queue is empty, and
 * set the interrupt register accordingly
 *
 *    void _rs_write(struct tty_struct * tty);
 */
void rs_write(struct tty_struct * tty)
{
    cli();
    if (!EMPTY(tty->write_q))
        outb(inb_p(tty->write_q.data+1)|0x02,tty->write_q.data+1);
    sti();
}

 

标签:kernel,tty,void,write,outb,源码,显卡,serial,port
来源: https://www.cnblogs.com/zhenjingcool/p/16101315.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有