linux系列的系统,Linux子系统系列-TTY

处理过几个串口的问题,这几天才稍微有了些时间来整理一下这一块的东西。

目录暂时大致分为以下几部分吧:

0,背景

1,串口驱动的结构

2,串口驱动的实现

3,关于TTY core

---------------------------------------------------------------------------------------------------

0,背景

往常review这块代码的时候,经常会被linux代码树中东一片西一片的uart,tty,serial device的代码搞的晕头转向。参照linux的驱动设备模型,其实串口驱动被一层层的完美的组装在一起。

于用于,直接接触的是串口(serial

interface),这是在计算机或嵌入式设备上一种应用非常广泛的一个设备接口。我们嘴里经常唠叨的UART其实只是串口通信的一种硬件实现方式而

已,只不过最早最广泛使用的是这一种方式,所以大家听到的比较多。现在在串口后面的硬件实现方式越来越多:红外设备,usb设备等等,口那几根线后面接的

chip的种类也越来越多,每种片子都对需要不同的驱动来工作。Linux领域的大师们就根据这些情况以及发展趋势,将一些通用的接口抽象出来,形成一个

支持多种串行通信的子系统-TTY Core,当然也提供了通用的支持linux 设备模型的接口方便编写符合linux设备模型规范的驱动代码,:).

note:tty并不是完全指代串口的底层抽象,后面会稍加阐述。

1,串口驱动的结构

对于一般的UART串口,它的实现分为三层,最底层是TTY Core,中间是UART Core,最上层是特定的芯片驱动。

最上层的驱动使用UART Core的API简单直观的完成设备和驱动的注册,UART Core利用TTY Core中API最终实现设备驱动的注册。

2,串口驱动的实现

系统启动时,tty会作为一个char设备早早的注册到kernel(driver/char/tty_io.c),因为它是抽象的最纯粹的,:)。

UART Core没有作为一个模块注册到kernel,它实现了一些通用的API来供使用到UART的串口设备来调用。

上层的芯片驱动就根据芯片的具体特性和参考串行通信的基本模式依赖UART

Core的一些API实现相应的功能供用户层配置和使用。主要是实现一些tty_operations里面声明的一些回调函数,如果使用UART设备,有

一些实现也会在UART Core里面实现。

struct tty_operations {

struct tty_struct * (*lookup)(struct tty_driver *driver,

struct inode *inode, int idx);

int  (*install)(struct tty_driver *driver, struct tty_struct *tty);

void (*remove)(struct tty_driver *driver, struct tty_struct *tty);

int  (*open)(struct tty_struct * tty, struct file * filp);

void (*close)(struct tty_struct * tty, struct file * filp);

void (*shutdown)(struct tty_struct *tty);

void (*cleanup)(struct tty_struct *tty);

int  (*write)(struct tty_struct * tty,

const unsigned char *buf, int count);

int  (*put_char)(struct tty_struct *tty, unsigned char ch);

void (*flush_chars)(struct tty_struct *tty);

int  (*write_room)(struct tty_struct *tty);

int  (*chars_in_buffer)(struct tty_struct *tty);

int  (*ioctl)(struct tty_struct *tty, struct file * file,

unsigned int cmd, unsigned long arg);

long (*compat_ioctl)(struct tty_struct *tty, struct file * file,

unsigned int cmd, unsigned long arg);

void (*set_termios)(struct tty_struct *tty, struct ktermios * old);

void (*throttle)(struct tty_struct * tty);

void (*unthrottle)(struct tty_struct * tty);

void (*stop)(struct tty_struct *tty);

void (*start)(struct tty_struct *tty);

void (*hangup)(struct tty_struct *tty);

int (*break_ctl)(struct tty_struct *tty, int state);

void (*flush_buffer)(struct tty_struct *tty);

void (*set_ldisc)(struct tty_struct *tty);

void (*wait_until_sent)(struct tty_struct *tty, int timeout);

void (*send_xchar)(struct tty_struct *tty, char ch);

int (*tiocmget)(struct tty_struct *tty, struct file *file);

int (*tiocmset)(struct tty_struct *tty, struct file *file,

unsigned int set, unsigned int clear);

int (*resize)(struct tty_struct *tty, struct winsize *ws);

int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);

#ifdef CONFIG_CONSOLE_POLL

int (*poll_init)(struct tty_driver *driver, int line, char *options,

void *rx_callback);

int (*poll_get_char)(struct tty_driver *driver, int line);

void (*poll_put_char)(struct tty_driver *driver, int line, char ch);

#endif

const struct file_operations *proc_fops;

};

3,关于TTY core

TTY在linux领域指的是终端,它可以指代任意终端设备,包括串口设备,console设备等等。

在/dev目录下,大家如果看到ttyS*就表示指代的是串口设备(S'serial' tty),而pty就指的是虚拟/伪终端设备(Pseudo tty)

TTY 子系统使用tty_driver来表示一个在它这个层次抽象出来的设备驱动,TTY实现了以下API来完成一个终端设备驱动的注册:

1)alloc_tty_driver()

2)tty_set_operations():建立tty entry point

3)tty_register_driver()

4)tty_register_device()

这样让一个终端驱动的注册变的十分清晰。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 内核中,串口 485 驱动的实现主要是通过软件方式实现的。在使用 485 通信时,需要通过控制 RTS 和 DTR 两个信号线来控制 485 转换器的收发切换。由于这两个信号线在串口协议中已经有了固定的功能,因此需要通过软件方式来实现对它们的控制。 下面是一个简单的 485 驱动示例,实现了对 RTS 和 DTR 两个信号线的控制。该示例代码实现了一个基于 tty 驱动的 485 设备驱动程序,使用了 tty 驱动中的 `ioctl()` 函数来实现对 RTS 和 DTR 信号线的控制。 ```c #include <linux/module.h> #include <linux/serial.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/tty_driver.h> #include <linux/tty_port.h> #define MY_DRIVER_NAME "my485" struct my485_port { struct tty_port port; spinlock_t lock; }; static struct my485_port myport; static int my485_open(struct tty_struct *tty, struct file *file) { return tty_port_open(&myport.port, tty, file); } static void my485_close(struct tty_struct *tty, struct file *file) { tty_port_close(&myport.port, tty, file); } static int my485_write(struct tty_struct *tty, const unsigned char *buf, int len) { int ret; spin_lock(&myport.lock); tty->driver->ioctl(tty, TIOCMGET, &ret); ret &= ~(TIOCM_DTR | TIOCM_RTS); tty->driver->ioctl(tty, TIOCMSET, &ret); ret |= TIOCM_DTR; tty->driver->ioctl(tty, TIOCMSET, &ret); usleep_range(1000, 2000); ret |= TIOCM_RTS; tty->driver->ioctl(tty, TIOCMSET, &ret); spin_unlock(&myport.lock); return tty_port_write(&myport.port, buf, len); } static const struct tty_operations my485_ops = { .open = my485_open, .close = my485_close, .write = my485_write, }; static struct tty_driver *my485_drv; static int __init my485_init(void) { int ret; memset(&myport, 0, sizeof(myport)); spin_lock_init(&myport.lock); tty_port_init(&myport.port); myport.port.ops = &my485_ops; my485_drv = alloc_tty_driver(1); if (!my485_drv) return -ENOMEM; my485_drv->owner = THIS_MODULE; my485_drv->driver_name = MY_DRIVER_NAME; my485_drv->name = "ttyMy485"; my485_drv->major = TTY_MAJOR; my485_drv->minor_start = 0; my485_drv->type = TTY_DRIVER_TYPE_SERIAL; my485_drv->subtype = SERIAL_TYPE_NORMAL; my485_drv->init_termios = tty_std_termios; tty_set_operations(my485_drv, &my485_ops); ret = tty_register_driver(my485_drv); if (ret) { printk(KERN_ERR "%s: failed to register driver\n", MY_DRIVER_NAME); put_tty_driver(my485_drv); return ret; } printk(KERN_INFO "%s: driver registered\n", MY_DRIVER_NAME); return 0; } static void __exit my485_exit(void) { tty_unregister_driver(my485_drv); put_tty_driver(my485_drv); printk(KERN_INFO "%s: driver unregistered\n", MY_DRIVER_NAME); } module_init(my485_init); module_exit(my485_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("485 driver for Linux tty"); ``` 在上述示例代码中,首先定义了一个 `struct my485_port` 结构体来表示 485 设备的端口信息。然后,实现了 `open()`、`close()` 和 `write()` 函数,分别用来处理设备文件的打开、关闭和写入操作。在 `write()` 函数中,首先使用 `ioctl()` 函数获取当前 DTR 和 RTS 信号线的状态,并将它们清零。然后,通过 `usleep_range()` 函数等待一段时间,最后将 RTS 信号线置为高电平,表示进入发送状态。 最后,在 `my485_init()` 函数中注册了一个 tty 驱动程序,并将其挂载到 tty 子系统中。在 `my485_exit()` 函数中,注销了该 tty 驱动程序。 请注意,上述示例代码仅作为演示用途,实际应用中还需要根据具体的硬件和通信协议进行相应的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值