linuxシリアルポート駆動を独自に実現

20774 ワード

実はdriver/tty/serialディレクトリの下には多くのメーカー独自のuart駆動があり、spiシリアルポートの駆動もあります.
しかし、これらはすべて周辺ハードウェアに設計されています.私のところには周辺デバイスがありません.自分でシミュレーションして游びに来ただけです.これからハードウェアを使うときに中に入れればいいです.駆動は以下の通りです.
/*
 * drivers/tty/serial/sw_uart.c
 * (C) Copyright 2007-2011
 * Reuuimlla Technology Co., Ltd. <www.reuuimllatech.com>
 * Aaron.Maoye <[email protected]>
 *
 * description for this code
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/platform_device.h>

#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/workqueue.h>
#include <linux/serial.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/ratelimit.h>
#include <linux/tty_flip.h>
#include <linux/serial_reg.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/nmi.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>

#include <asm/io.h>
#include <asm/irq.h>



#define SW_UART_NR	8
static struct timer_list *timer;
struct platform_device *pdev;
struct work_struct q_work;
struct workqueue_struct *workqueue;
volatile static int force_enable_work;


//         ,               
static void sw_uart_handle_rx(unsigned long data)
{
#if 1
    struct uart_port * port;
    struct tty_struct *tty;
    unsigned char ch = 0;
    int i;
    int flag;
    char *send_buff = "i am sclu, this is from serial rx";

    flag = TTY_NORMAL;
    printk("%s, %d, %s
", __FILE__, __LINE__, __func__); port = (struct uart_port *)data; if (!port) return; if (!port->state) return; tty = port->state->port.tty; //tty->flags |= TTY_HW_COOK_IN; //tty = port->info->tty; if (!tty) return; // for (i = 0; i < strlen(send_buff) + 1; i++) { // tty_insert_flip_char // ch = send_buff[i]; // uart_insert_char(port, 0, 0, ch, flag); // } tty_insert_flip_string(tty, send_buff, strlen(send_buff) + 1); // uart_insert_char(port, 0, 0, '
', flag); // uart_insert_char(port, 0, 0, '\t', 0); tty_flip_buffer_push(tty); #endif timer->expires = jiffies + 3 * HZ; add_timer(timer); } static void sw_uart_stop_tx(struct uart_port *port) { //del_timer(timer); printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } // static void sw_uart_start_tx(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; // printk("%s, %d, %s
", __FILE__, __LINE__, __func__); // if(!force_enable_work || uart_circ_empty(xmit) || uart_tx_stopped(port)) queue_work(workqueue, &q_work); } static unsigned int sw_uart_tx_empty(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); // fifo , TIOCSER_TEMI, , echo return TIOCSER_TEMT; } static void sw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static unsigned int sw_uart_get_mctrl(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); return 0; } static void sw_uart_stop_rx(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static void sw_uart_enable_ms(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static void sw_uart_break_ctl(struct uart_port *port, int break_state) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static int sw_uart_startup(struct uart_port *port) { /* this is the first time this port is opened */ /* do any hardware initialization needed here */ /* create our timer and submit it */ force_enable_work = 0; if (!timer) { timer = kmalloc(sizeof(*timer), GFP_KERNEL); if (!timer) return -ENOMEM; } setup_timer(timer, sw_uart_handle_rx, (unsigned long)port); timer->expires = jiffies + HZ * 3; #if 0 timer->data = (unsigned long)port; timer->expires = jiffies + DELAY_TIME; timer->function = tiny_timer; #endif add_timer(timer); printk("%s, %d, %s
", __FILE__, __LINE__, __func__); return 0; } static void sw_uart_shutdown(struct uart_port *port) { /* The port is being closed by the last user. */ /* Do any hardware specific stuff here */ /* shut down our timer */ printk("%s, %d, %s
", __FILE__, __LINE__, __func__); force_enable_work = 1; cancel_work_sync(&q_work); } static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static const char *sw_uart_type(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); return "SW"; } static void sw_uart_release_port(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static int sw_uart_request_port(struct uart_port *port) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); return 0; } static void sw_uart_config_port(struct uart_port *port, int flags) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static int sw_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); return 0; } static void sw_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); } static struct uart_driver sw_uart_driver; static struct uart_ops sw_uart_ops = { #if 1 .tx_empty = sw_uart_tx_empty, .set_mctrl = sw_uart_set_mctrl, .get_mctrl = sw_uart_get_mctrl, .stop_tx = sw_uart_stop_tx, .enable_ms = sw_uart_enable_ms, .break_ctl = sw_uart_break_ctl, .set_termios = sw_uart_set_termios, .type = sw_uart_type, .release_port = sw_uart_release_port, .request_port = sw_uart_request_port, .config_port = sw_uart_config_port, .verify_port = sw_uart_verify_port, .pm = sw_uart_pm, #endif .startup = sw_uart_startup, .start_tx = sw_uart_start_tx, .stop_rx = sw_uart_stop_rx, .shutdown = sw_uart_shutdown, }; static struct uart_port tiny_port = { .ops = &sw_uart_ops, .type = PORT_8250, .fifosize = 64, .flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF, }; #ifdef CONFIG_CONSOLE_POLL static void sw_console_write(struct console *co, const char *s, unsigned int count) { } static int __init sw_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n'; port = &tiny_port; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(port, co, baud, parity, bits, flow); } #endif static void tx_work(struct work_struct *work) { #if 1 struct uart_port *port = &tiny_port; struct circ_buf *xmit = &port->state->xmit; int count; // printk("%s, %d, %s
", __FILE__, __LINE__, __func__); if (port->x_char) { printk("x_char %2x", port->x_char); port->icount.tx++; port->x_char = 0; return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { return; } count = port->fifosize / 2; // printk("sclu %s, count = %d
", __func__, count); // // serial_out(&sw_uport->port, xmit->buf[xmit->tail], SW_UART_THR); printk("get data:
"); do { printk("%c", xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) { break; } } while (--count > 0); printk("
"); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { // spin_unlock(&port->lock); uart_write_wakeup(port); // spin_lock(&port->lock); } if (uart_circ_empty(xmit)) sw_uart_stop_tx(port); #endif } #ifdef CONFIG_CONSOLE_POLL static struct console sw_console = { .name = "ttyVirtual", .write = sw_console_write, .device = uart_console_device, .setup = sw_console_setup, .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, .data = &sw_uart_driver, }; #define SERIAL_CONSOLE &sw_console #else #define SERIAL_CONSOLE NULL #endif static struct uart_driver sw_uart_driver = { .owner = THIS_MODULE, .driver_name = "Virtual_serial", .dev_name = "ttyVirtual", .nr = SW_UART_NR, .cons = SERIAL_CONSOLE, }; static int __devinit sw_uart_probe(struct platform_device *pdev) { printk("%s, %d, %s
", __FILE__, __LINE__, __func__); workqueue = create_singlethread_workqueue("ttyVirtual_work"); INIT_WORK(&q_work, tx_work); return uart_add_one_port(&sw_uart_driver, &tiny_port); } static int __devexit sw_uart_remove(struct platform_device *pdev) { return 0; } #define SERIAL_SW_PM_OPS NULL static struct platform_driver sw_uport_platform_driver = { .probe = sw_uart_probe, .remove = __devexit_p(sw_uart_remove), .driver.name = "ttyVirtual", .driver.pm = SERIAL_SW_PM_OPS, .driver.owner = THIS_MODULE, }; static int __init sw_uart_init(void) { int ret; printk("serial driver initializied
"); ret = uart_register_driver(&sw_uart_driver); if (unlikely(ret)) { printk("serial driver initializied err
"); return ret; } pdev = platform_device_register_simple("ttyVirtual", 0, NULL, 0); return platform_driver_register(&sw_uport_platform_driver); } static void __exit sw_uart_exit(void) { if (timer) { del_timer(timer); timer = NULL; } if (workqueue) { flush_workqueue(workqueue); destroy_workqueue(workqueue); workqueue = NULL; } printk("driver exit
"); uart_remove_one_port(&sw_uart_driver, &tiny_port); platform_driver_unregister(&sw_uport_platform_driver); if (pdev) platform_device_unregister(pdev); uart_unregister_driver(&sw_uart_driver); } module_init(sw_uart_init); module_exit(sw_uart_exit); MODULE_AUTHOR("Aaron<[email protected]>"); MODULE_DESCRIPTION("Driver for SW serial device"); MODULE_LICENSE("GPL");

上位レベルのテストコードは次のとおりです.
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>
#include <assert.h>
#include <errno.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <sys/socket.h>
#include <utils/Log.h>
#include <stdlib.h>

static int epoll_register(int  epoll_fd, int  fd) {
    struct epoll_event  ev;
    int  ret, flags;

    /* important: make the fd non-blocking */
    flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    //fcntl(fd, F_SETOWN, getpid()); //sclu add
    ev.events  = EPOLLIN;
    ev.data.fd = fd;
    do {
        ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
    } while (ret < 0 && errno == EINTR);
    return ret;
}

int main(int argc, char **argv)
{
    struct termios uart_termios;
    int baudrate = atoi(argv[2]);
    int  epoll_fd = epoll_create(1);
    int ret, err;
    char read_buf[1024];

    int fd = open(argv[1], O_RDWR|O_NOCTTY|O_NDELAY);
    if(fd < 0) {
        LOGE("open %s failure, err = %s
", argv[1], strerror(errno)); return fd; } fcntl(fd, F_SETFL, 0); tcflush(fd, TCIOFLUSH); if ((err = tcgetattr(fd, &uart_termios)) != 0) { LOGE("tcgetattr %s, err = %s
", argv[1], strerror(errno)); close(fd); return err; } uart_termios.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); uart_termios.c_oflag &= ~OPOST; uart_termios.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); uart_termios.c_cflag &= ~(CSIZE|PARENB); uart_termios.c_cflag |= CS8; uart_termios.c_cflag &= ~CRTSCTS;//no flow control tcsetattr(fd, TCSANOW, &uart_termios); tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &uart_termios); tcflush(fd, TCIOFLUSH); if (cfsetispeed(&uart_termios, baudrate)) { LOGE("cfsetispeed, err = %s
", strerror(errno)); close(fd); return fd; } if (cfsetospeed(&uart_termios, baudrate)) { LOGE("cfsetospeed err = %s
", strerror(errno)); close(fd); return fd; } tcsetattr (fd, TCSANOW, &uart_termios); epoll_register(epoll_fd, fd); ret = write(fd, "$$$$", 4); LOGE("ret = %d", ret); if ((ret = read(fd, read_buf, sizeof(read_buf))) > 0) { int i; for (i = 0; i < ret; i++) { LOGE("read:%c", read_buf[i]); } LOGE("
"); } for (;;) { struct epoll_event events[1]; int ne, nevents, ret, size; LOGE("poll start"); nevents = epoll_wait(epoll_fd, events, 1, -1); LOGE("poll get data"); if (nevents < 0) { if (errno != EINTR) LOGE("epoll_wait() unexpected error: %s", strerror(errno)); continue; } for (ne = 0; ne < nevents; ne++) { if ((events[ne].events & (EPOLLERR | EPOLLHUP)) != 0) { LOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?
"); return -1; } if ((events[ne].events & EPOLLIN) != 0) { int event_fd = events[ne].data.fd; int i; if (event_fd == fd) { ret = read(fd, read_buf, sizeof(read_buf)); if (ret) { LOGE("data:%s", read_buf); char *p = "this is from userspace!"; write(fd, p, strlen(p) + 1); } } } } } return 0; }

デバッグ中に発生した問題はsw_です.uart_tx_Empty関数では0を返すことはできません.TIOCSER_を返すにはTEMT、そうでなければ上層部はずっと待っています
また、ノードを開くときにシリアル情報を設定し、上のテストコードに示すように、最初はcat/dev/ttyVirtualで情報を見ていましたが、下位層から送られてきたデータがttyコア層に届いたことに気づき、そのまま送り返しました.上位層と同じように受信できますが、望む結果ではありません.
それから、linux系はもう私たちのためにserialを書いてくれました.core.cアーキテクチャ、私たちのシリアルポートデバイスはそのフォーマットに従って相応の関数を実現することを学ぶだけでいいが、私たちは自分で1つを実現することができて、相応のtty_を実現する限りoperations構造体関数を使用してttyコア層に登録し、linuxのデフォルトのserial_を必要としない場合は、後で独自のシリアルドライバを使用します.core、登録時に自分のtty driverを指定すればいいです.
コードは、tty driverにデータを書き込み、そのまま送信する機能です.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/tty.h>
#include <linux/fs.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
#include <linux/serial_reg.h>
#include <linux/slab.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/tty.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("sclu");

#define TTY_LAN_MINORS_NUM    1
#define TTY_LAN_MAJOR        240
static int open_count = 0;
static struct tty_driver *tty_lan_driver;
static struct tty_struct *tty_lan_struct;

static int tty_lan_open(struct tty_struct *tty, struct file *filp);
static void tty_lan_close(struct tty_struct *tty, struct file *filp);
static int tty_lan_write(struct tty_struct *tty, const unsigned char *buffer, int count);
static int tty_lan_write_room(struct tty_struct *tty);
static void tty_lan_set_termios(struct tty_struct *tty, struct ktermios * old);
static int tty_lan_put_char(struct tty_struct *tty, unsigned char ch);
static void uart_wait_until_sent(struct tty_struct *tty, int timeout);

static struct tty_operations tty_lan_ops = {
    .open = tty_lan_open,
    .close = tty_lan_close,
    .write = tty_lan_write,
    .put_char = tty_lan_put_char,
    .write_room = tty_lan_write_room,
    .set_termios = tty_lan_set_termios,
    .wait_until_sent = uart_wait_until_sent,
};


static int __init tty_lan_init(void)
{
    int i;
    int retval;

    tty_lan_driver = alloc_tty_driver(TTY_LAN_MINORS_NUM);
    if(!tty_lan_driver)
        return -ENOMEM;

    tty_lan_driver->owner = THIS_MODULE;
    tty_lan_driver->driver_name = "tty_sclu";
    tty_lan_driver->name = "ttty_sclu";
    tty_lan_driver->major = TTY_LAN_MAJOR,
        tty_lan_driver->minor_start = 0;    
    tty_lan_driver->type = TTY_DRIVER_TYPE_SERIAL;
    tty_lan_driver->subtype = SERIAL_TYPE_NORMAL;
    //TTY_DRIVER_DYNAMIC_DEV        device  , tty_io.c    ,       ,   tty_register_device,
    //         device   
    tty_lan_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
    tty_lan_driver->init_termios = tty_std_termios;
    tty_lan_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
    tty_set_operations(tty_lan_driver, &tty_lan_ops);

    retval = tty_register_driver(tty_lan_driver);
    if(retval){
        printk(KERN_ERR"Failed to register tty_lan_driver!
"); put_tty_driver(tty_lan_driver); return retval; } tty_register_device(tty_lan_driver, 0, NULL); return 0; } static int tty_lan_open(struct tty_struct *tty, struct file *filp) { if(open_count == 0){ printk("Open OK!
"); } tty_lan_struct = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); tty->low_latency = 1; tty_lan_struct = tty; return 0; } static void tty_lan_close(struct tty_struct *tty, struct file *filp) { printk("ClOSE OK!
"); kfree(tty_lan_struct); return; } static int tty_lan_write(struct tty_struct *tty, const unsigned char *buffer, int count) { int retval = count; tty = tty_lan_struct; printk(KERN_DEBUG "%s -
", __FUNCTION__); printk("count :%d
", count); printk("user write: %s ", buffer); printk("
"); tty_insert_flip_string(tty, buffer, count); tty_flip_buffer_push(tty); return retval; } static int tty_lan_put_char(struct tty_struct *tty, unsigned char ch) { printk("%c", ch); return 0; } static int tty_lan_write_room(struct tty_struct *tty) { int room; room = 255; return room; } static void tty_lan_set_termios(struct tty_struct *tty, struct ktermios *old) { tty = tty_lan_struct; if(tty->termios->c_cflag == old->c_cflag){ printk("Nothing to change!
"); return ; } printk("There is something to Change............
"); return ; } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; printk("%s, %d
", __func__, __LINE__); } static void __exit tty_lan_exit(void) { int i; tty_unregister_device(tty_lan_driver, 0); tty_unregister_driver(tty_lan_driver); } module_init(tty_lan_init);