精华内容
下载资源
问答
  • TTY 2

    千次阅读 2009-05-25 17:43:00
    TAB2 和 TAB3 BSDLY 空格输出延迟,可以取 BS0 或 BS1 VTDLY 垂直制表符输出延迟,可以取 VT0 或 VT1 ...

    6.4.4  使用tcgetattr函数与tcsetattr函数控制终端

    为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。这两个函数的具体信息如表6.2所示。

    表6.2   tcgetattr函数和tcsetattr函数

    头文件

    <termios.h>

    <unistd.h>

    函数形式

    int tcgetattr(int fd, struct termios *termios_p);

    int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

    返回值

    成功

    失败

    是否设置 errno

    0

    −1

    说明:tcgetattr函数用于获取与终端相关的参数。参数fd为终端的文件描述符,返回的结果保存在termios结构体中,该结构体一般包括如下的成员:

        

    tcflag_t c_iflag;     
    tcflag_t c_oflag;     
    tcflag_t c_cflag;     
    tcflag_t c_lflag;    
    cc_t     c_cc[NCCS];  

    其具体意义如下。
     
    c_iflag:输入模式标志,控制终端输入方式,具体参数如表6.3所示。

    表6.3   c_iflag参数表

       

       

    IGNBRK

    忽略 BREAK 键输入

    BRKINT

    如果设置了 IGNBRK BREAK 键的输入将被忽略,如果设置了 BRKINT ,将产生 SIGINT 中断

    IGNPAR

    忽略奇偶校验错误

    PARMRK

    标识奇偶校验错误

    INPCK

    允许输入奇偶校验

    ISTRIP

    去除字符的第 8 个比特

    INLCR

    将输入的 NL (换行)转换成 CR (回车)

    IGNCR

    忽略输入的回车

    ICRNL

    将输入的回车转化成换行(如果 IGNCR 未设置的情况下)

    IUCLC

    将输入的大写字符转换成小写字符(非 POSIX

    IXON

    允许输入时对 XON/XOFF 流进行控制

    IXANY

    输入任何字符将重启停止的输出

    IXOFF

    允许输入时对 XON/XOFF 流进行控制

    IMAXBEL

    当输入队列满的时候开始响铃, Linux 在使用该参数而是认为该参数总是已经设置

    c_oflag:输出模式标志,控制终端输出方式,具体参数如表6.4所示。

    表6.4   c_oflag参数

       

       

    OPOST

    处理后输出

    OLCUC

    将输入的小写字符转换成大写字符(非 POSIX

    ONLCR

    将输入的 NL (换行)转换成 CR (回车)及 NL (换行)

    OCRNL

    将输入的 CR (回车)转换成 NL (换行)

    ONOCR

    第一行不输出回车符

    ONLRET

    不输出回车符

    OFILL

    发送填充字符以延迟终端输出

    OFDEL

    ASCII 码的 DEL 作为填充字符,如果未设置该参数,填充字符将是 NUL ‘/0’ )(非 POSIX

    NLDLY

    换行输出延时,可以取 NL0 (不延迟)或 NL1 (延迟 0.1s

    CRDLY

    回车延迟,取值范围为: CR0 CR1 CR2 CR3

    TABDLY

    水平制表符输出延迟,取值范围为: TAB0 TAB1 TAB2 TAB3

    BSDLY

    空格输出延迟,可以取 BS0 BS1

    VTDLY

    垂直制表符输出延迟,可以取 VT0 VT1

    FFDLY

    换页延迟,可以取 FF0 FF1

    c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表6.5所示。

    表6.5   c_oflag参数

       

       

    CBAUD

    波特率( 4+1 位)(非 POSIX

    CBAUDEX

    附加波特率( 1 位)(非 POSIX

    CSIZE

    字符长度,取值范围为 CS5 CS6 CS7 CS8

    CSTOPB

    设置两个停止位

    CREAD

    使用接收器

    PARENB

    使用奇偶校验

    PARODD

    对输入使用奇偶校验,对输出使用偶校验

    HUPCL

    关闭设备时挂起

    CLOCAL

    忽略调制解调器线路状态

    CRTSCTS

    使用 RTS/CTS 流控制

    c_lflag:本地模式标志,控制终端编辑功能,具体参数如表6.6所示。

    表6.6   c_lflag参数

       

       

    ISIG

    当输入 INTR QUIT SUSP DSUSP 时,产生相应的信号

    ICANON

    使用标准输入模式

    XCASE

    ICANON XCASE 同时设置的情况下,终端只使用大写。如果只设置了 XCASE ,则输入字符将被转换为小写字符,除非字符使用了转义字符(非 POSIX ,且 Linux 不支持该参数)

    ECHO

    显示输入字符

    ECHOE

    如果 ICANON 同时设置, ERASE 将删除输入的字符, WERASE 将删除输入的单词

    ECHOK

    如果 ICANON 同时设置, KILL 将删除当前行

    ECHONL

    如果 ICANON 同时设置,即使 ECHO 没有设置依然显示换行符

    ECHOPRT

    如果 ECHO ICANON 同时设置,将删除打印出的字符(非 POSIX

    TOSTOP

    向后台输出发送 SIGTTOU 信号

    c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如表6.7所示的控制字符。

    表6.7   c_cc支持的控制字符

       

       

    VINTR

    Interrupt 字符

    VEOL

    附加的 End-of-file 字符

    VQUIT

    Quit 字符

    VTIME

    非规范模式读取时的超时时间

    VERASE

    Erase 字符

    VSTOP

    Stop 字符

    VKILL

    Kill 字符

    VSTART

    Start 字符

    VEOF

    End-of-file 字符

    VSUSP

    Suspend 字符

    VMIN

    非规范模式读取时的最小字符数

     

     

    tcsetattr函数用于设置终端的相关参数。参数fd为打开的终端文件描述符,参数optional_actions用于控制修改起作用的时间,而结构体termios_p中保存了要修改的参数。
    optional_actions可以取如下的值。
     
    TCSANOW:不等数据传输完毕就立即改变属性。
    TCSADRAIN:等待所有数据传输结束才改变属性。
    TCSAFLUSH:清空输入输出缓冲区才改变属性。

    错误信息:
    EBADF:非法的文件描述符。
    EINTR:tcsetattr函数调用被信号中断。
    EINVAL:参数optional_actions使用了非法值,或参数termios中使用了非法值。
    ENCTTY:非终端的文件描述符。

    实例演练:
    程序p6.2.c通过修改终端控制字符,将终端输入结束符由“Ctrl+D”,修改成了“Ctrl+G”。首先,程序调用 tcgetattr函数获得标准输入的termios信息,将termios结构体中的c_cc[VEOF]控制字符的修改成0x07(即 Ctrl+G);然后,使用tcsetattr函数将修改后的termios参数设置到终端中。具体代码如下所示:

        

    //p6.2.c 修改终端控制字符示例
    #include <stdio.h>
    #include <termios.h>
    #include <unistd.h>
    #include <errno.h>

    int main(void){
    //term用于存储获得的终端参数信息
    struct termios term;
    int err;

    //获得标准输入的终端参数,将获得的信息保存在term变量中
    if(tcgetattr(STDIN_FILENO,&term)==-1){
    perror("Cannot get standard input description");
    return 1;
    }

    //修改获得的终端信息的结束控制字符
    term.c_cc[VEOF]=(cc_t)0x07;

    //使用tcsetattr函数将修改后的终端参数设置到标准输入中
    //err用于保存函数调用后的结果
    err=tcsetattr(STDIN_FILENO,TCSAFLUSH,&term);

    //如果err为-1或是出现EINTR错误(函数执行被信号中断),
    //给出相关出错信息
    if(err==-1 && err==EINTR){
    perror("Failed to change EOF character");
    return 1;
    }

    return 0;
    }

    使用gcc编译p6.2.c程序,得到名为p6.2的可执行程序。在执行p6.2程序前,按“Ctrl+D”可以使终端结束。执行p6.2程序后,按“Ctrl+D”失去了作用,而输入“Ctrl+G”实现了原来“Ctrl+D”的功能。

     

     

     

     

     

     

    串口配置时一些重要且应注意的事项:

           除了一般的波特率、数据位、校验位、停止位、超时配置外,还有一些配置也是很重要的,否则有可能出错丢码或者不正常的现象,在此做一份记录;

    本人测试过的正常代码实例:

    // 打开串口:

    static INT32 Open_Dev(char *dev)

    {

           INT32 fd;

           if((fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY )) == -1)

           {

                  //perror(">can not open the dev!");

                  return -1;

           }

           return fd;

    }

     

    // 关闭串口:

    static void Close_Dev(INT32 fd)

    {

           if(fd > 0)

           {

                  close(fd);

           }

    }

     

    // 设置波特率:

    static INT32 Set_Baudrate(INT32 fd,UINT32 baudrate)

    {

           UINT32 speed_arr[] = {B57600,B38400,B19200,B9600,B4800,B2400,B1200,B600,B300,};    //define in termios.h

           UINT32 baud_arr[] = {57600,38400,19200,9600,4800,2400,1200,600,300,};

     

           UINT32 i;

           UINT32 status;

           struct termios opt;

     

           tcgetattr(fd,&opt);              //get attr

           for(i=0; i<sizeof(baud_arr)/sizeof(UINT32); i++)

           {

                  if(baudrate == baud_arr[i])

                  {

                         tcflush(fd,TCIOFLUSH);                           //flush

                         cfsetispeed(&opt,speed_arr[i]);            //set  in baudrate

                         cfsetospeed(&opt,speed_arr[i]);           //set out baudrate

                         status = tcsetattr(fd,TCSANOW,&opt);

                         if(status != 0)

                         {

                                //perror(">Set_Baudrate: tcseattr failed!/n");

                                return RET_ERR;

                         }

                         tcflush(fd,TCIOFLUSH);

                         return RET_OK;

                  }

           }

     

           return RET_ERR;

          

    }

     

    // 设置数据位、校验位、停止位和其它重要配置:

    static INT32 Set_Parity(INT32 fd,UINT32 databits,UINT32 stopbits,char parity)

    {

           struct termios opt;

           if(tcgetattr(fd,&opt) != 0)

           {

                  //perror(">Set_Parity: tcgetattr failed!/n");

                  return (RET_ERR);

           }

     

           //data bits

           opt.c_cflag &= ~CSIZE;

           switch (databits)

           {

                  case 5:

                     opt.c_cflag |= CS5;

                     break;

                  case 6:

                     opt.c_cflag |= CS6;

                     break;

                  case 7:

                     opt.c_cflag |= CS7;

                     break;

                case 8:

                     opt.c_cflag |= CS8;

                     break;

                default:

                     //perror(">Unsupported databits/n");

                     return (RET_ERR);

                     break;

           }

          

           //parity

           switch (parity)

           {

               case 'n':

               case 'N':

                     opt.c_cflag &= ~PARENB;          //Clear parity enable

                     opt.c_iflag &= ~INPCK;               // Disnable parity checking

                     break;

               case 'o':

               case 'O':

                     opt.c_cflag |= (PARODD | PARENB);

                     opt.c_iflag |= INPCK;             // Enable parity checking

                     break;

                case 'e':

               case 'E':

                     opt.c_cflag |= PARENB;        // Enable parity

                     opt.c_cflag &= ~PARODD;               

                     opt.c_iflag |= INPCK;           // Enable parity checking

                     break;

               case 'S':

               case 's':                                            //as no parity

                     opt.c_cflag &= ~PARENB;

                     opt.c_cflag &= ~CSTOPB;

                     break;

               default:

                     //perror(">Unsupported parity/n");

                     return (RET_ERR);

                     break;

           }

          

           // stop bits 

           switch (stopbits)

           {

               case 1:

                     opt.c_cflag &= ~CSTOPB;

                     break;

               case 2:

                     opt.c_cflag |= CSTOPB;

                     break;

               default:

                     //perror(">Unsupported stopbits/n");

                     return (RET_ERR);

                     break;

           }

          

          

           tcflush(fd,TCIFLUSH);     // Update the options and do it NOW

           //time out(here no use  because "open(o_NDELAY)")

           opt.c_cc[VTIME] = 150;         // 15 seconds     (1/10sec)

           opt.c_cc[VMIN] = 0;

     

      /*----------------------  重要 ----------------------*/  

           // 保证本程序不会成为端口的所有者,从而妨碍控制工作和挂起信号 .

            opt.c_cflag     |= (CLOCAL | CREAD);

     

    // 选择行方式输入 : 行式输入是不经处理的 .

    opt.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG); 

          

    // 选择行式输出

    opt.c_oflag  &= ~OPOST;  

                                      

           // 取消软件流控制(不设置可能存在丢码)

           opt.c_iflag &= ~(IXON | IXOFF | IXANY);                  

       /*----------------------------------------------------*/

     

           if (tcsetattr(fd,TCSANOW,&opt) != 0)

           {

                  //perror(">Set_Parity: tcsetattr failed!/n");

                  return (RET_ERR);

           }

          

           return (RET_OK);

    }

     

        注意上面实例是中的红色字样(重要的设置);

        设置数据位、校验位、停止位和其它重要配置中的红字部分:因为有些版本的 linux 可能存已默认配置,所以不用配置也可能,但如果默认配置不是这样而又没有进行配置则就可能会出现丢码或者收发码不正常的情况 (本人曾遇到过)!!!

    展开全文
  • tty2oled 软件,基于运行中的内核,在由Arduino驱动的(OLED)显示器上显示文本或图片,该Arduino通过USB连接到MiSTer。 跳转到维基 :grinning_face_with_big_eyes: 了解更多信息。 游乐场补遗: 在MiSTer设备上...
  • tty2gif, 将脚本记录到gif中 介绍tty2gif 让你把脚本和它们的输出记录进二进制和gif格式。和原来的script 一样,tty2gif 首先设置一个临时 shell,你可以在其中键入 shell 命令。 同时 tty2gif 会记录你的输入和输出...
  • 之前autoremove了一下,再开机发现不能自动登录xfce,就切到tty2,startx,之后每次开机默认从tty2登录图形界面,切到tty7只有光标闪,不能输入,怎么解决
  • tty2oled 软件基于(OLED)显示器上加载的内核显示文本或图片,该显示器由Arduino驱动,并通过Arduino的USB串行端口连接。 新的! SD版本,可从SD卡实时下载图片(更多信息请参见)。 重要的! 两种Arduino版本...
  • 由于要修改开发板中的IP地址,于是想到要修改/etc/init.d/rcS,可是由于使用vi rcS后出现乱码,于是想到将rcS复制到主机上修改后在放回...can't open /dev/tty2: No such file or directory can't open /dev/tty3: N

    由于要修改开发板中的IP地址,于是想到要修改/etc/init.d/rcS,可是由于使用vi rcS后出现乱码,于是想到将rcS复制到主机上修改后在放回到开发板上。

    于是就悲剧了,修改后放回到init.d目录下,然后重启,就一直不断的出现

    can't open /dev/tty2: No such file or directory

    can't open /dev/tty3: No such file or directory

    看了它的输出信息,主要是没有访问权限。

    后来发现其实已经启动了系统,只是没有显示QT界面,那就好办了。

    解决方法:

    在DNW中使用命令,不管他输出

    can't open /dev/tty2: No such file or directory

    的这些,只管按照自己想要输出的命令即可(会被

    can't open /dev/tty2: No such file or directory

    隔断,没关系)

    #cd etc

    #cd init.d

    #chmod 777 rcS

    于是重启开发板,一切OK.

     

    这个问题主要是rcS是初始化脚本用的,如果不能访问,就不能访问/dev等设备,也就无法开启界面等。

    所以如果没有的话,需要复制rcS到init.d目录下,然后修改权限

     

    若以上方法不能解决,则说明不是权限问题,那就

    can't open /dev/tty3: No such file or directory
    can't open /dev/tty2: No such file or directory
    can't open /dev/tty4: No such file or directory

    解决办法:

     ln -sf /dev/null /dev/tty2
     ln -sf /dev/null /dev/tty3
     ln -sf /dev/null /dev/tty4
    展开全文
  • 在前面的一篇文章中,我们分析了一个 uart_driver 的向上注册过程,主要是 tty 的一些东西,知道了 tty 注册了一个字符设备驱动,我们在用户空间 open 时将调用到 uart_port.ops.startup ,在用户空间 write 则调用 ...

     在前面的一篇文章中,我们分析了一个 uart_driver 的向上注册过程,主要是 tty 的一些东西,知道了 tty 注册了一个字符设备驱动,我们在用户空间 open 时将调用到 uart_port.ops.startup ,在用户空间 write 则调用 uart_port.ops.start_tx ,还知道了如何 read 数据等等。但是,这些都是内核帮我们实现好的,在真正的驱动开发过程中几乎不涉及那些代码的修改移植工作,真正需要我们触碰的是 uart_port 这个结构体,它真正的对应于一个物理的串口。

        其实,真正需要我们做的工作就是 分配一个 uart_port 结构,然后 uart_add_one_port 。分析过 s3c2440 uart 的驱动代码之后,我发现,这么一个简单的目标简直就是经历了山路十八弯。

        先说一下大体的思路,uart_port 的注册过程是基于 platform 平台设备驱动模型,device 侧提供 3 个串口的硬件信息,并注册到 platform_bus_type 中去。然后 driver 也注册到 platform_bus_type 时,就会根据名字进行匹配,从而调用 driver->probe 函数,在 probe 函数里进行 uart_add_one_port 。思路也是很简单的,复杂在 s3c2440 注册 device 之前的工作扯了太多东西。

        先秀个最终分析的图:



    一、Linux 启动过程回忆

        在 uboot 启动内核的时候,内核刚刚启动我们就看到串口各种信息就输出来了,也就是说串口驱动的初始化工作是在 Linux 启动过程中一个比较靠前的位置。内核启动的时候首先会去判断 cpu id 是否支持,接着判断是否支持uboot 传递进来的单板 Id ,然后 start_kernel -》setup_arch 进行一系列的初始化工作,其中必然包含串口相关初始化。

    内核中所有支持的单板都用 MACHINE_START 和 MACHINE_END 来定义

    1. MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board")
    2. .phys_io = S3C2410_PA_UART,
    3. .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    4. .boot_params = S3C2410_SDRAM_PA + 0x100,
    5. .init_irq = s3c24xx_init_irq,
    6. .map_io = mini2440_map_io,
    7. .init_machine = mini2440_machine_init,
    8. .timer = &s3c24xx_timer,
    9. MACHINE_END
        但是,里面的这些函数是何时被调用的,调用的先后顺序是怎样的,我们需要分析 Linux 的启动流程才能知道,信息量还是比较大的,在前面的一篇文章中我分析过了

    请参考:http://blog.csdn.net/lizuobin2/article/details/51779064

        如果你自己分析一遍的话,调用先后顺序应该是这样的:

        start_kernel -》setup_arch -》 map_io -》 init_irq -》 timer -》 init_machine -》 s3c_arch_init -》 s3c24xx_serial_modinit -》s3c2440_serial_init

        后面三个函数是通过类似于 module_init 等被组织进内核里去的放在一个特殊的段里,内核启动到一定时候就去把这个段里的每一个函数取出来去调用,也是与串口相关的,分析过程就不再赘述了。


    二、platform device 的注册之路

        分析出了整个的串口驱动的初始化、设置、注册流程,问题就简单多了,挨个函数分析便是。

    1. static void __init mini2440_map_io(void)
    2. {
    3. s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));
    4. s3c24xx_init_clocks(12000000);
    5. s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));
    6. }
        能一看瞅出来的,外部晶振的频率 12M ,如果我们在移植其它单板的时候不是,记得修改。
    1. void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
    2. {
    3. ....
    4. s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
    5. }
    1. static struct cpu_table *cpu;
    2. void __init s3c_init_cpu(unsigned long idcode,
    3. struct cpu_table *cputab, unsigned int cputab_size)
    4. {
    5. cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);
    6. cpu->map_io();
    7. }
    1. static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
    2. struct cpu_table *tab,
    3. unsigned int count)
    4. {
    5. for (; count != 0; count--, tab++) {
    6. if ((idcode & tab->idmask) == tab->idcode)
    7. return tab;
    8. }
    9. return NULL;
    10. }
    1. static struct cpu_table cpu_ids[] __initdata = {
    2. {
    3. .idcode = 0x32440000,
    4. .idmask = 0xffffffff,
    5. .map_io = s3c244x_map_io,
    6. .init_clocks = s3c244x_init_clocks,
    7. .init_uarts = s3c244x_init_uarts,
    8. .init = s3c2440_init,
    9. .name = name_s3c2440
    10. },
    11. };
        上边四段代码费尽周折,只为调用 cpu_ids 数组里的 s3c244x_map_io 函数。
    1. void __init s3c244x_map_io(void)
    2. {
    3. /* register our io-tables */
    4. iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
    5. /* rename any peripherals used differing from the s3c2410 */
    6. s3c_device_sdi.name = "s3c2440-sdi";
    7. s3c_device_i2c0.name = "s3c2440-i2c";
    8. s3c_device_nand.name = "s3c2440-nand";
    9. s3c_device_usbgadget.name = "s3c2440-usbgadget";
    10. }
        也是醉醉的,竟然跟串口毫无关系。下面看 s3c24xx_init_uarts
    1. void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
    2. {
    3. (cpu->init_uarts)(cfg, no);
    4. }
        呵,前边的工作果然也不是完全白做的,至少帮我们找到了 cpu ,那么就是调用 s3c244x_init_uarts 咯
    1. void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no)
    2. {
    3. s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no);
    4. }
        继续往下看之前,我们先看一下参数 cfg , no ,s3c2410_uart_resources
    s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));</span>
    1. static struct s3c2410_uartcfg mini2440_uartcfgs[] __initdata = {
    2. [0] = {
    3. .hwport = 0,
    4. .flags = 0,
    5. .ucon = 0x3c5,
    6. .ulcon = 0x03,
    7. .ufcon = 0x51,
    8. },
    9. /* 此处略去了 1、2 两个串口的信息 */
    10. };

    1. struct s3c24xx_uart_resources s3c2410_uart_resources[] __initdata = {
    2. [0] = {
    3. .resources = s3c2410_uart0_resource,
    4. .nr_resources = ARRAY_SIZE(s3c2410_uart0_resource),
    5. },
    6. /* 此处略去了 1、2 串口的信息 */
    7. };
    1. static struct resource s3c2410_uart0_resource[] = {
    2. [0] = {
    3. .start = S3C2410_PA_UART0,
    4. .end = S3C2410_PA_UART0 + 0x3fff,
    5. .flags = IORESOURCE_MEM,
    6. },
    7. [1] = {
    8. .start = IRQ_S3CUART_RX0,
    9. .end = IRQ_S3CUART_ERR0,
    10. .flags = IORESOURCE_IRQ,
    11. }
    12. };
        万事俱备,开始构建 device

    1. struct platform_device *s3c24xx_uart_src[4] = {
    2. &s3c24xx_uart_device0,
    3. &s3c24xx_uart_device1,
    4. &s3c24xx_uart_device2,
    5. &s3c24xx_uart_device3,
    6. };

    1. static struct s3c2410_uartcfg uart_cfgs[CONFIG_SERIAL_SAMSUNG_UARTS];
    2. /* 填充平台设备的过程,未注册 */
    3. void __init s3c24xx_init_uartdevs(char *name, struct s3c24xx_uart_resources *res,
    4. struct s3c2410_uartcfg *cfg, int no)
    5. {
    6. struct platform_device *platdev;
    7. struct s3c2410_uartcfg *cfgptr = uart_cfgs;
    8. struct s3c24xx_uart_resources *resp;
    9. int uart;
    10. /* 将 mini2440_uartcfgs 数组里的参数拷贝到 cfgptr */
    11. memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
    12. for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
    13. /* 从 s3c24xx_uart_src 数组里取出平台设备 */
    14. platdev = s3c24xx_uart_src[cfgptr->hwport];
    15. /* 获得对应的 resource ,物理寄存器和中断 */
    16. resp = res + cfgptr->hwport;
    17. /* 将 s3c24xx_uart_src 的平台设备 放到 平台设备数组 s3c24xx_uart_devs */
    18. s3c24xx_uart_devs[uart] = platdev;
    19. /* 设置名字 资源 */
    20. platdev->name = name;
    21. platdev->resource = resp->resources;
    22. platdev->num_resources = resp->nr_resources;
    23. /* 设置平台数据 mini2440_uartcfgs 数组里的东西 */
    24. platdev->dev.platform_data = cfgptr;
    25. }
    26. nr_uarts = no;
    27. }
        至此,device 构建设置完毕,等待注册:

        1、3 个串口的 device 存放在 s3c24xx_uart_devs 数组里,后边肯定会从数组里取出来注册。

        2、3 个串口的 device 的名字都是 “s3c2440-uart”。

        3、3 个串口的 device 资源文件里存放好了 io 物理地址,Irq 等信息。

        4、3 个串口的 device 资源数据。
        移植过程中可能需要修改的文件:mini2440_uartcfgs 、s3c2410_uart0_resource 、s3c24xx_uart_src 还有那个晶振频率。

        s3c_arch_init 函数中,将 device 注册到 platform_bus_type

    1. static int __init s3c_arch_init(void)
    2. {
    3. int ret;
    4. ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
    5. return ret;
    6. }

    三、uart_driver 的注册

        注意,是 uart_driver 的注册,是上一篇文章讲的过程,并不是对应于平台设备的平台驱动。为什么在这个时候注册 uart_driver,因为如果先注册平台设备的 driver 的话,那么在probe函数里 uart_add_one_port ,uart_prot 没地方注册!!因此,要先注册 uart_driver ,简单贴下代码,不在分析。

    1. static struct uart_driver s3c24xx_uart_drv = {
    2. .owner = THIS_MODULE,
    3. .dev_name = "s3c2410_serial",
    4. .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
    5. .cons = S3C24XX_SERIAL_CONSOLE,
    6. .driver_name = S3C24XX_SERIAL_NAME,
    7. .major = S3C24XX_SERIAL_MAJOR,
    8. .minor = S3C24XX_SERIAL_MINOR,
    9. };
    10. static int __init s3c24xx_serial_modinit(void)
    11. {
    12. int ret;
    13. ret = uart_register_driver(&s3c24xx_uart_drv);
    14. if (ret < 0) {
    15. printk(KERN_ERR "failed to register UART driver\n");
    16. return -1;
    17. }
    18. return 0;
    19. }
    附上上一篇文章的地址:http://blog.csdn.net/lizuobin2/article/details/51773305

    四、platform driver 的注册以及 probe 函数

    1. static int s3c2440_serial_probe(struct platform_device *dev)
    2. {
    3. dbg("s3c2440_serial_probe: dev=%p\n", dev);
    4. return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
    5. }
    6. static struct platform_driver s3c2440_serial_driver = {
    7. .probe = s3c2440_serial_probe,
    8. .remove = __devexit_p(s3c24xx_serial_remove),
    9. .driver = {
    10. .name = "s3c2440-uart",
    11. .owner = THIS_MODULE,
    12. },
    13. };
    14. s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
    15. static int __init s3c2440_serial_init(void)
    16. {
    17. return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
    18. }
        将驱动注册到 platform_bus_type ,此时会遍历 platform_bus_type 的 deivce 链表,取出 device 进行名字比较,我们前边注册的三个device的名字是一样的,没关系 Linux 允许这样做,每次匹配到一个都调用一次 Probe 函数。

    1. static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    2. [0] = {
    3. .port = {
    4. .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
    5. .iotype = UPIO_MEM,
    6. .irq = IRQ_S3CUART_RX0,
    7. .uartclk = 0,
    8. .fifosize = 16,
    9. .ops = &s3c24xx_serial_ops,/* 底层的操作函数 */
    10. .flags = UPF_BOOT_AUTOCONF,
    11. .line = 0,
    12. }
    13. },
    14. /* 此处略去了两个串口的信息 */
    15. };
    1. int s3c24xx_serial_probe(struct platform_device *dev,
    2. struct s3c24xx_uart_info *info)
    3. {
    4. struct s3c24xx_uart_port *ourport;
    5. int ret;
    6. /* 取出 uart_port */
    7. ourport = &s3c24xx_serial_ports[probe_index];
    8. probe_index++;
    9. /* 对 uart_port 进一步设置 */
    10. ret = s3c24xx_serial_init_port(ourport, info, dev);
    11. /* 将 uart_port 注册到 uart_driver */
    12. uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
    13. platform_set_drvdata(dev, &ourport->port);
    14. ret = device_create_file(&dev->dev, &dev_attr_clock_source);
    15. ret = s3c24xx_serial_cpufreq_register(ourport);
    16. return 0;
    17. }
    1. static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
    2. struct s3c24xx_uart_info *info,
    3. struct platform_device *platdev)
    4. {
    5. struct uart_port *port = &ourport->port;
    6. struct s3c2410_uartcfg *cfg;
    7. struct resource *res;
    8. int ret;
    9. cfg = s3c24xx_dev_to_cfg(&platdev->dev);
    10. /* setup info for port */
    11. port->dev = &platdev->dev;
    12. ourport->info = info;
    13. /* copy the info in from provided structure */
    14. ourport->port.fifosize = info->fifosize;
    15. <span style="white-space:pre"> </span>/* 设置时钟 */
    16. port->uartclk = 1;
    17. /* sort our the physical and virtual addresses for each UART */
    18. res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
    19. <span style="white-space:pre"> </span>/* 设置物理地址,虚拟地址 */
    20. port->mapbase = res->start;
    21. port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
    22. ret = platform_get_irq(platdev, 0);
    23. if (ret < 0)
    24. port->irq = 0;
    25. else {
    26. port->irq = ret;/* 设置中断号 */
    27. ourport->rx_irq = ret;
    28. ourport->tx_irq = ret + 1;
    29. }
    30. ret = platform_get_irq(platdev, 1);
    31. ourport->clk = clk_get(&platdev->dev, "uart");
    32. /* reset the fifos (and setup the uart) */
    33. s3c24xx_serial_resetport(port, cfg);
    34. return 0;
    35. }
    1. int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
    2. {
    3. struct uart_state *state;
    4. struct tty_port *port;
    5. int ret = 0;
    6. struct device *tty_dev;
    7. BUG_ON(in_interrupt());
    8. if (uport->line >= drv->nr)
    9. return -EINVAL;
    10. state = drv->state + uport->line;
    11. port = &state->port;
    12. mutex_lock(&port_mutex);
    13. mutex_lock(&port->mutex);
    14. if (state->uart_port) {
    15. ret = -EINVAL;
    16. goto out;
    17. }
    18. <span style="white-space:pre"> </span>/* 将 uart_prot 绑定到 uart_driver 对应的 state */
    19. state->uart_port = uport;
    20. state->pm_state = -1;
    21. uport->cons = drv->cons;
    22. uport->state = state;
    23. /*
    24. * If this port is a console, then the spinlock is already
    25. * initialised.
    26. */
    27. if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
    28. spin_lock_init(&uport->lock);
    29. lockdep_set_class(&uport->lock, &port_lock_key);
    30. }
    31. /* 实际调用 port->ops->config_port(port, flags) 稍后再看 */
    32. uart_configure_port(drv, state, uport);
    33. /*
    34. * 上一篇文章中,我们提到tty注册了一个字符设备 “ttySAC ”
    35. * 那么,我们平时看到的 “ttySAC0”“ttySAC1”等就是在这里注册的
    36. */
    37. tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
    38. if (likely(!IS_ERR(tty_dev))) {
    39. device_init_wakeup(tty_dev, 1);
    40. device_set_wakeup_enable(tty_dev, 0);
    41. } else
    42. printk(KERN_ERR "Cannot register tty device on line %d\n",
    43. uport->line);
    44. /*
    45. * Ensure UPF_DEAD is not set.
    46. */
    47. uport->flags &= ~UPF_DEAD;
    48. out:
    49. mutex_unlock(&port->mutex);
    50. mutex_unlock(&port_mutex);
    51. return ret;
    52. }
    1. struct device *tty_register_device(struct tty_driver *driver, unsigned index,
    2. struct device *device)
    3. {
    4. char name[64];
    5. dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
    6. if (index >= driver->num) {
    7. printk(KERN_ERR "Attempt to register invalid tty line number "
    8. " (%d).\n", index);
    9. return ERR_PTR(-EINVAL);
    10. }
    11. if (driver->type == TTY_DRIVER_TYPE_PTY)
    12. pty_line_name(driver, index, name);
    13. else
    14. tty_line_name(driver, index, name);
    15. return device_create(tty_class, device, dev, NULL, name);
    16. }
    1. <span style="font-family:SimSun;font-size:18px;">static void tty_line_name(struct tty_driver *driver, int index, char *p)
    2. {
    3. sprintf(p, "%s%d", driver->name, index + driver->name_base);
    4. }
       tty_driver->name == "ttySAC",在此基础上加上 uart_port.line ,就组成了具体串口的设备节点的名字,例如“ttySAC0”。

        分析到这里,完了么?没有,还有一个非常重要的东西没有分析呢,那就是底层的操作函数。

    1. static struct uart_ops s3c24xx_serial_ops = {
    2. .pm = s3c24xx_serial_pm,
    3. .tx_empty = s3c24xx_serial_tx_empty,
    4. .get_mctrl = s3c24xx_serial_get_mctrl,
    5. .set_mctrl = s3c24xx_serial_set_mctrl,
    6. .stop_tx = s3c24xx_serial_stop_tx,
    7. .start_tx = s3c24xx_serial_start_tx,
    8. .stop_rx = s3c24xx_serial_stop_rx,
    9. .enable_ms = s3c24xx_serial_enable_ms,
    10. .break_ctl = s3c24xx_serial_break_ctl,
    11. .startup = s3c24xx_serial_startup,
    12. .shutdown = s3c24xx_serial_shutdown,
    13. .set_termios = s3c24xx_serial_set_termios,
    14. .type = s3c24xx_serial_type,
    15. .release_port = s3c24xx_serial_release_port,
    16. .request_port = s3c24xx_serial_request_port,
    17. .config_port = s3c24xx_serial_config_port,
    18. .verify_port = s3c24xx_serial_verify_port,
    19. };
        这么多的函数,如果让我们自己来实现,那相比真得头都大了。一般芯片厂家会帮我们搞得吧。其他的不分析了,分析一个 startup 函数,因为我们在用户空间 open 的时候会调用它,那么必然有一些初始化的工作。
    1. static int s3c24xx_serial_startup(struct uart_port *port)
    2. {
    3. struct s3c24xx_uart_port *ourport = to_ourport(port);
    4. int ret;
    5. dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
    6. port->mapbase, port->membase);
    7. rx_enabled(port) = 1;
    8. ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
    9. s3c24xx_serial_portname(port), ourport);
    10. if (ret != 0) {
    11. printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
    12. return ret;
    13. }
    14. ourport->rx_claimed = 1;
    15. dbg("requesting tx irq...\n");
    16. tx_enabled(port) = 1;
    17. ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
    18. s3c24xx_serial_portname(port), ourport);
    19. if (ret) {
    20. printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
    21. goto err;
    22. }
    23. ourport->tx_claimed = 1;
    24. dbg("s3c24xx_serial_startup ok\n");
    25. /* the port reset code should have done the correct
    26. * register setup for the port controls */
    27. if (port->line == 2) {
    28. s3c2410_gpio_cfgpin(S3C2410_GPH(6), S3C2410_GPH6_TXD2);
    29. s3c2410_gpio_pullup(S3C2410_GPH(6), 1);
    30. s3c2410_gpio_cfgpin(S3C2410_GPH(7), S3C2410_GPH7_RXD2);
    31. s3c2410_gpio_pullup(S3C2410_GPH(7), 1);
    32. }
    33. return ret;
    34. err:
    35. s3c24xx_serial_shutdown(port);
    36. return ret;
    37. }
        主要工作是注册了两个中断,发送中断,接收中断,来看看一个和我们上篇文章的猜测是否一致。

    1. static irqreturn_t
    2. s3c24xx_serial_rx_chars(int irq, void *dev_id)
    3. {
    4. ..../* 调用线路规程的...和上篇文章一致 */
    5. tty_flip_buffer_push(tty);
    6. out:
    7. return IRQ_HANDLED;
    8. }
    1. static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
    2. {
    3. ....
    4. if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    5. uart_write_wakeup(port);
    6. if (uart_circ_empty(xmit))
    7. s3c24xx_serial_stop_tx(port);
    8. out:
    9. return IRQ_HANDLED;
    10. }
    1. void uart_write_wakeup(struct uart_port *port)
    2. {
    3. struct uart_state *state = port->state;
    4. /*
    5. * This means you called this function _after_ the port was
    6. * closed. No cookie for you.
    7. */
    8. BUG_ON(!state);
    9. tasklet_schedule(&state->tlet);/* 也是一致的 */
    10. }
    展开全文
  • 加载ramdisk出现can't open /dev/tty2: No such file or directory, 解决方法: ln -sf /dev/null /dev/tty2

    加载ramdisk出现can't open /dev/tty2: No such file or directory,

    解决方法: ln -sf /dev/null /dev/tty2


    展开全文
  • Ubuntu 改变tty2字体大小

    千次阅读 2013-04-16 17:05:24
    今天ubuntu更新后,开机后居然默认进入了tty1界面,没图形了。想编程,可是字体太小,看得好累。用上面的方法居然奏效了。可是每次开机都要重新设置一次。 下面是转载过来的,没试过。 在Ubuntu Linux操作...
  • 于是就悲剧了,修改后放回到init.d目录下,然后重启,就一直不断的出现can't open /dev/tty2: No such file or directorycan't open /dev/tty3: No such file or directory看了它的输出信息,主要是没有访问权限。...
  • 在使用 tty 的时候遇到了一个事情,那就是主文件夹下面的中文文件是乱码: 【备注】tty 是 通过 CTRL + ALT +F2~F6 获得的, 这与桌面系统中的终端不是一个概念, 望看到这篇博客的网友可以“醍醐灌顶”一下。...
  • 打开两个终端,使用tty命名分别查看它们的id,如/dev/pts/1 /dev/pts/2 然后我在/dev/pts/1中输入echo "info" > /dev/pts/2,这样/dev/pts/2中就会显示info 你可以尝试一下
  • 进程当前的终端是/dev/tty,那么tty2,tty3这些设备是当前进程的什么设备?
  • <p style="text-align:center"><img alt="" height="307" src="https://img-ask.csdnimg.cn/upload/1606481094027.png" width="535" /></p> 如图,请问我该怎样才能进入图形化界面啊。。...
  • 问题描述:  在制作嵌入式文件系统时,使用nfs挂载模式后,启动开发板(本人用的... 2. 除此之外还要使用 chmod 777 rcS(rcS为init.d文件夹目录下的文件,请进入init.d文件夹后再使用此指令)  
  • tty

    2018-05-16 22:54:00
    使用Alt+[F1—F6]组合键时,我们就可以切换到tty2、tty3等上面去。tty1–tty6等 称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上(这时也叫控制台终端)。因此不管当前...
  • TTY

    2017-02-24 21:51:27
    TTYtty是Teletype的缩写,Teletype是最早出现的一种终端设备,很象电传打字机,是由Teletype公司生产的。后来,出现了各种类型的终端设备,这些终端设备都统称为TTY,即终端..../dev/tty /dev/tty16 /dev/tty2
  • /dev/tty设备 这个设备表示的是控制终端,如果当前的shell登录环境有关联控制终端,那么执行它就可以看到回显。 echo test > /dev/tty 它其实是一个当前控制终端的一个...其中tty1和tty2为X窗口系统,其余为虚拟字符
  • tty相关

    2019-09-27 17:35:47
    tty相关 ttyn:虚拟终端,有tty1-tty6 ...如tty1、tty2等。 在命令行模式下,是映射到虚拟终端。 在图形界面下,被映射到/dev/pts伪终端上。如/dev/pts/0、/dev/pts/1等。 pty:伪终端,逻辑上的终端设备。(pseud...
  • linux tty

    2018-01-07 13:20:54
    who查看系统连接多少个登录终端,tty查看当前的终端号: [root@system1 ~]# tty /dev/pts/3 tty1-6是文本型控制台,7是x-...比如可以在tty1用root登录,再在tty2登录root,tty1查询的结果跟tty2查询的结果进行对比.
  • tty实验

    2015-06-23 15:08:46
    同理,ctrl+alt+shift+F2四个组合键按下后进入tty2的终端界面,退出方式同上。 2.执行sudo ps -A 查询当前所有进程可见:已经打开的tty1 和tty2 都显示为login状态,而其他的tty3等则显示为getty 表示等
  • linux tty理解

    千次阅读 2019-06-24 21:07:32
    1.各终端之间没有区别的,他就是为了方便用户的登录。比如说我可以同时利用其同一用户或其他用户同时登录,切换用户的...2,比如说,当用tty1 登录后,出现死机时,可切换到tty2(alt+ctrl+f2),利用另一个用户...
  • tty 虚拟终端

    2013-06-19 19:40:43
    如果VMware下 ,启动tty  首先设置VMware 的快捷键ctrl+alt调整成ctrl+shift+alt  在命令行下 ctrl+alt+f1 创建tty1虚拟终端 ...ctrl+alt+f2 tty2 ctrl+alt+f3 tty3 ctrl+alt+f4 tty4 ctrl+alt+f5 tty
  • linux tty个数修改

    2020-12-21 10:28:09
    自动启用的tty@.service数量,默认为6指tty2-tty7 ReserveVT 不包含在NAutoVTs个数中,启用的tty@.service eg: NAutoVTs=3,ReserveVT=6表示启用tty2,3,7 + tty6 关于默认具体是哪个tty,系统不同可能有差异。 2...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,099
精华内容 2,839
关键字:

tty2