linux-tty_ioctl.c

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

#include <errno.h>
#include <termios.h>

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/tty.h>

#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>

extern int session_of_pgrp(int pgrp);
extern int tty_signal(int sig, struct tty_struct *tty);

static unsigned short quotient[] = {
    0, 2304, 1536, 1047, 857,
    768, 576, 384, 192, 96,
    64, 48, 24, 12, 6, 3
};

static void change_speed(struct tty_struct * tty)        //--修改传输波特率
{
    unsigned short port,quot;

    if (!(port = tty->read_q->data))
        return;
    quot = quotient[tty->termios.c_cflag & CBAUD];
    cli();
    outb_p(0x80,port+3);        /* set DLAB */
    outb_p(quot & 0xff,port);    /* LS of divisor */
    outb_p(quot >> 8,port+1);    /* MS of divisor */
    outb(0x03,port+3);        /* reset DLAB */
    sti();
}

static void flush(struct tty_queue * queue)            //--刷新tty缓冲队列
{
    cli();
    queue->head = queue->tail;
    sti();
}

static void wait_until_sent(struct tty_struct * tty)    //--等待字符发送出去
{
    /* do nothing - not implemented */
}

static void send_break(struct tty_struct * tty)        //--发送BREAK控制符
{
    /* do nothing - not implemented */
}

static int get_termios(struct tty_struct * tty, struct termios * termios)
{                                                    //--取终端termios结构信息
    int i;

    verify_area(termios, sizeof (*termios));
    for (i=0 ; i< (sizeof (*termios)) ; i++)
        put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios );
    return 0;
}

static int set_termios(struct tty_struct * tty, struct termios * termios,
            int channel)                            //--设置终端termios结构信息
{
    int i, retsig;

    /* If we try to set the state of terminal and we're not in the
       foreground, send a SIGTTOU.  If the signal is blocked or
       ignored, go ahead and perform the operation.  POSIX 7.2) */
    if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
        retsig = tty_signal(SIGTTOU, tty);
        if (retsig == -ERESTARTSYS || retsig == -EINTR)
            return retsig;
    }
    for (i=0 ; i< (sizeof (*termios)) ; i++)
        ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios);
    change_speed(tty);
    return 0;
}

static int get_termio(struct tty_struct * tty, struct termio * termio)
{                                                //--读取termmio结构中的信息
    int i;
    struct termio tmp_termio;

    verify_area(termio, sizeof (*termio));
    tmp_termio.c_iflag = tty->termios.c_iflag;
    tmp_termio.c_oflag = tty->termios.c_oflag;
    tmp_termio.c_cflag = tty->termios.c_cflag;
    tmp_termio.c_lflag = tty->termios.c_lflag;
    tmp_termio.c_line = tty->termios.c_line;
    for(i=0 ; i < NCC ; i++)
        tmp_termio.c_cc[i] = tty->termios.c_cc[i];
    for (i=0 ; i< (sizeof (*termio)) ; i++)
        put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
    return 0;
}

/*
 * This only works as the 386 is low-byt-first    !!!
 */
static int set_termio(struct tty_struct * tty, struct termio * termio,
            int channel)                        //--设置终端termio结构信息
{
    int i, retsig;
    struct termio tmp_termio;

    if ((current->tty == channel) && (tty->pgrp != current->pgrp)) {
        retsig = tty_signal(SIGTTOU, tty);
        if (retsig == -ERESTARTSYS || retsig == -EINTR)
            return retsig;
    }
    for (i=0 ; i< (sizeof (*termio)) ; i++)
        ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
    *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag;
    *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag;
    *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag;
    *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag;
    tty->termios.c_line = tmp_termio.c_line;
    for(i=0 ; i < NCC ; i++)
        tty->termios.c_cc[i] = tmp_termio.c_cc[i];
    change_speed(tty);
    return 0;
}

int tty_ioctl(int dev, int cmd, int arg)    //--tty终端设备输入输出控制函数
{
    struct tty_struct * tty;
    int    pgrp;

    if (MAJOR(dev) == 5) {
        dev=current->tty;
        if (dev<0)
            panic("tty_ioctl: dev<0");
    } else
        dev=MINOR(dev);
    tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console);
    switch (cmd) {
        case TCGETS:
            return get_termios(tty,(struct termios *) arg);
        case TCSETSF:
            flush(tty->read_q); /* fallthrough */
        case TCSETSW:
            wait_until_sent(tty); /* fallthrough */
        case TCSETS:
            return set_termios(tty,(struct termios *) arg, dev);
        case TCGETA:
            return get_termio(tty,(struct termio *) arg);
        case TCSETAF:
            flush(tty->read_q); /* fallthrough */
        case TCSETAW:
            wait_until_sent(tty); /* fallthrough */
        case TCSETA:
            return set_termio(tty,(struct termio *) arg, dev);
        case TCSBRK:
            if (!arg) {
                wait_until_sent(tty);
                send_break(tty);
            }
            return 0;
        case TCXONC:
            switch (arg) {
            case TCOOFF:
                tty->stopped = 1;
                tty->write(tty);
                return 0;
            case TCOON:
                tty->stopped = 0;
                tty->write(tty);
                return 0;
            case TCIOFF:
                if (STOP_CHAR(tty))
                    PUTCH(STOP_CHAR(tty),tty->write_q);
                return 0;
            case TCION:
                if (START_CHAR(tty))
                    PUTCH(START_CHAR(tty),tty->write_q);
                return 0;
            }
            return -EINVAL; /* not implemented */
        case TCFLSH:
            if (arg==0)
                flush(tty->read_q);
            else if (arg==1)
                flush(tty->write_q);
            else if (arg==2) {
                flush(tty->read_q);
                flush(tty->write_q);
            } else
                return -EINVAL;
            return 0;
        case TIOCEXCL:
            return -EINVAL; /* not implemented */
        case TIOCNXCL:
            return -EINVAL; /* not implemented */
        case TIOCSCTTY:
            return -EINVAL; /* set controlling term NI */
        case TIOCGPGRP:
            verify_area((void *) arg,4);
            put_fs_long(tty->pgrp,(unsigned long *) arg);
            return 0;
        case TIOCSPGRP:
            if ((current->tty < 0) ||
                (current->tty != dev) ||
                (tty->session != current->session))
                return -ENOTTY;
            pgrp=get_fs_long((unsigned long *) arg);
            if (pgrp < 0)
                return -EINVAL;
            if (session_of_pgrp(pgrp) != current->session)
                return -EPERM;
            tty->pgrp = pgrp;           
            return 0;
        case TIOCOUTQ:
            verify_area((void *) arg,4);
            put_fs_long(CHARS(tty->write_q),(unsigned long *) arg);
            return 0;
        case TIOCINQ:
            verify_area((void *) arg,4);
            put_fs_long(CHARS(tty->secondary),
                (unsigned long *) arg);
            return 0;
        case TIOCSTI:
            return -EINVAL; /* not implemented */
        case TIOCGWINSZ:
            return -EINVAL; /* not implemented */
        case TIOCSWINSZ:
            return -EINVAL; /* not implemented */
        case TIOCMGET:
            return -EINVAL; /* not implemented */
        case TIOCMBIS:
            return -EINVAL; /* not implemented */
        case TIOCMBIC:
            return -EINVAL; /* not implemented */
        case TIOCMSET:
            return -EINVAL; /* not implemented */
        case TIOCGSOFTCAR:
            return -EINVAL; /* not implemented */
        case TIOCSSOFTCAR:
            return -EINVAL; /* not implemented */
        default:
            return -EINVAL;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值