linuxシリアルポート駆動を独自に実現
20774 ワード
実はdriver/tty/serialディレクトリの下には多くのメーカー独自のuart駆動があり、spiシリアルポートの駆動もあります.
しかし、これらはすべて周辺ハードウェアに設計されています.私のところには周辺デバイスがありません.自分でシミュレーションして游びに来ただけです.これからハードウェアを使うときに中に入れればいいです.駆動は以下の通りです.
上位レベルのテストコードは次のとおりです.
デバッグ中に発生した問題は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にデータを書き込み、そのまま送信する機能です.
しかし、これらはすべて周辺ハードウェアに設計されています.私のところには周辺デバイスがありません.自分でシミュレーションして游びに来ただけです.これからハードウェアを使うときに中に入れればいいです.駆動は以下の通りです.
/*
* 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);