C/C++教程

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

本文主要是介绍kernel源码(十九)字符设备-serial.c,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

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

 

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源码(十九)字符设备-serial.c的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!