2007-02-21 15:03:38

by Mockern

[permalink] [raw]
Subject: cat problem in tiny_tty driver (the source included)

I tried to check cat operations for tiny_tty driver from LDD book.

What is wrong with cat operation here?

Here is the output from strace cat hello > /dev/my_tty1

root@andy:/home# strace cat hello > /dev/my_tty1
execve("/bin/cat", ["cat", "hello"], [/* 12 vars */]) = 0
brk(0) = 0x7d000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=5664, ...}) = 0
old_mmap(NULL, 5664, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
close(3) = 0
open("/lib/libm.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\250B\0\000"..., 512) = 51
2
fstat64(3, {st_mode=S_IFREG|0755, st_size=480324, ...}) = 0
old_mmap(NULL, 506412, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40020000
mprotect(0x40093000, 35372, PROT_NONE) = 0
old_mmap(0x40098000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x70
000) = 0x40098000
close(3) = 0
open("/lib/libcrypt.so.1", O_RDONLY) = 3
read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\260\10\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=19940, ...}) = 0
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0
x40019000
old_mmap(NULL, 211220, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4009c000
mprotect(0x400a1000, 190740, PROT_NONE) = 0
old_mmap(0x400a4000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0) =
0x400a4000
old_mmap(0x400a9000, 157972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANO
NYMOUS, -1, 0) = 0x400a9000
close(3) = 0
open("/lib/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\330p\1\000"..., 512) = 51
2
fstat64(3, {st_mode=S_IFREG|0755, st_size=1240024, ...}) = 0
old_mmap(NULL, 1257088, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x400d0000
mprotect(0x401f5000, 56960, PROT_NONE) = 0
old_mmap(0x401f8000, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12
0000) = 0x401f8000
old_mmap(0x40201000, 7808, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONY
MOUS, -1, 0) = 0x40201000
close(3) = 0
munmap(0x40017000, 5664) = 0
getuid32() = 0
getgid32() = 0
setgid32(0) = 0
setuid32(0) = 0
brk(0) = 0x7d000
brk(0x9e000) = 0x9e000
brk(0) = 0x9e000
open("hello", O_RDONLY) = 3
read(3, "123456789", 8192) = 9
write(1, "123456789", 9) = -1 EINVAL (Invalid argument)//??????????
write(2, "cat: ", 5cat: ) = 5
write(2, "Write Error", 11Write Error) = 11
write(2, ": Invalid argument\n", 19: Invalid argument
) = 19
close(3) = 0
io_submit(0, 0x40200164, 0 <unfinished ... exit status 0>
Process 1432 detached
root@andy:/home#

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

/*
* Tiny TTY driver
*
* Copyright (C) 2002-2004 Greg Kroah-Hartman ([email protected])
*
* 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, version 2 of the License.
*
* This driver shows how to create a minimal tty driver. It does not rely on
* any backing hardware, but creates a timer that emulates data being received
* from some kind of hardware.
*/

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <asm/uaccess.h>


#define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <[email protected]>"
#define DRIVER_DESC "Tiny TTY driver"

/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");

#define DELAY_TIME HZ * 2 /* 2 seconds per character */
#define TINY_DATA_CHARACTER 't'

#define TINY_TTY_MAJOR 240 /* experimental range */
#define TINY_TTY_MINORS 4 /* only have 4 devices */

struct tiny_serial {
struct tty_struct *tty; /* pointer to the tty for this device */
int open_count; /* number of times this port has been opened */
struct semaphore sem; /* locks this structure */
struct timer_list *timer;

/* for tiocmget and tiocmset functions */
int msr; /* MSR shadow */
int mcr; /* MCR shadow */

/* for ioctl fun */
struct serial_struct serial;
wait_queue_head_t wait;
struct async_icount icount;
};

static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */


static void tiny_timer(unsigned long timer_data)
{
struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
struct tty_struct *tty;
int i;
char data[1] = {TINY_DATA_CHARACTER};
int data_size = 1;

if (!tiny)
return;

tty = tiny->tty;

/* send the data to the tty layer for users to read. This doesn't
* actually push the data through unless tty->low_latency is set */
for (i = 0; i < data_size; ++i) {
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);

/* resubmit the timer again */
tiny->timer->expires = jiffies + DELAY_TIME;
add_timer(tiny->timer);
}

static int tiny_open(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;

/* initialize the pointer in case something fails */
tty->driver_data = NULL;

/* get the serial object associated with this tty pointer */
index = tty->index;
tiny = tiny_table[index];
if (tiny == NULL) {
/* first time accessing this device, let's create it */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;

init_MUTEX(&tiny->sem);
tiny->open_count = 0;
tiny->timer = NULL;

tiny_table[index] = tiny;
}

down(&tiny->sem);

/* save our structure within the tty structure */
tty->driver_data = tiny;
tiny->tty = tty;

++tiny->open_count;
if (tiny->open_count == 1) {
/* this is the first time this port is opened */
/* do any hardware initialization needed here */

/* create our timer and submit it */
if (!tiny->timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!timer) {
up(&tiny->sem);
return -ENOMEM;
}
tiny->timer = timer;
}
tiny->timer->data = (unsigned long )tiny;
tiny->timer->expires = jiffies + DELAY_TIME;
tiny->timer->function = tiny_timer;
add_timer(tiny->timer);
}

up(&tiny->sem);
return 0;
}

static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem);

if (!tiny->open_count) {
/* port was never opened */
goto exit;
}

--tiny->open_count;
if (tiny->open_count <= 0) {
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */

/* shut down our timer */
del_timer(tiny->timer);
}
exit:
up(&tiny->sem);
}

static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;

if (tiny)
do_close(tiny);
}

static int tiny_write(struct tty_struct *tty, const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
int retval = -EINVAL;

if (!tiny)
return -ENODEV;

down(&tiny->sem);

if (!tiny->open_count)
/* port was not opened */
goto exit;

/* fake sending the data out a hardware port by
* writing it to the kernel debug log.
*/
printk(KERN_DEBUG "%s - ", __FUNCTION__);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("\n");

exit:
up(&tiny->sem);
return retval;
}

static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data;
int room = -EINVAL;

if (!tiny)
return -ENODEV;

down(&tiny->sem);

if (!tiny->open_count) {
/* port was not opened */
goto exit;
}

/* calculate how much room is left in the device */
room = 255;

exit:
up(&tiny->sem);
return room;
}

#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))

static void tiny_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
unsigned int cflag;

cflag = tty->termios->c_cflag;

/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag))) {
printk(KERN_DEBUG " - nothing to change...\n");
return;
}
}

/* get the byte size */
switch (cflag & CSIZE) {
case CS5:
printk(KERN_DEBUG " - data bits = 5\n");
break;
case CS6:
printk(KERN_DEBUG " - data bits = 6\n");
break;
case CS7:
printk(KERN_DEBUG " - data bits = 7\n");
break;
default:
case CS8:
printk(KERN_DEBUG " - data bits = 8\n");
break;
}

/* determine the parity */
if (cflag & PARENB)
if (cflag & PARODD)
printk(KERN_DEBUG " - parity = odd\n");
else
printk(KERN_DEBUG " - parity = even\n");
else
printk(KERN_DEBUG " - parity = none\n");

/* figure out the stop bits requested */
if (cflag & CSTOPB)
printk(KERN_DEBUG " - stop bits = 2\n");
else
printk(KERN_DEBUG " - stop bits = 1\n");

/* figure out the hardware flow control settings */
if (cflag & CRTSCTS)
printk(KERN_DEBUG " - RTS/CTS is enabled\n");
else
printk(KERN_DEBUG " - RTS/CTS is disabled\n");

/* determine software flow control */
/* if we are implementing XON/XOFF, set the start and
* stop character in the device */
if (I_IXOFF(tty) || I_IXON(tty)) {
unsigned char stop_char = STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);

/* if we are implementing INBOUND XON/XOFF */
if (I_IXOFF(tty))
printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");

/* if we are implementing OUTBOUND XON/XOFF */
if (I_IXON(tty))
printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
}

/* get the baud rate wanted */
printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
}

/* Our fake UART values */
#define MCR_DTR 0x01
#define MCR_RTS 0x02
#define MCR_LOOP 0x04
#define MSR_CTS 0x08
#define MSR_CD 0x10
#define MSR_RI 0x20
#define MSR_DSR 0x40

static int tiny_tiocmget(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;

unsigned int result = 0;
unsigned int msr = tiny->msr;
unsigned int mcr = tiny->mcr;

result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */
((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/
((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */

return result;
}

static int tiny_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear)
{
struct tiny_serial *tiny = tty->driver_data;
unsigned int mcr = tiny->mcr;

if (set & TIOCM_RTS)
mcr |= MCR_RTS;
if (set & TIOCM_DTR)
mcr |= MCR_RTS;

if (clear & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (clear & TIOCM_DTR)
mcr &= ~MCR_RTS;

/* set the new MCR value in the device */
tiny->mcr = mcr;
return 0;
}

static int tiny_read_proc(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
struct tiny_serial *tiny;
off_t begin = 0;
int length = 0;
int i;

length += sprintf(page, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
for (i = 0; i < TINY_TTY_MINORS && length < PAGE_SIZE; ++i) {
tiny = tiny_table[i];
if (tiny == NULL)
continue;

length += sprintf(page+length, "%d\n", i);
if ((length + begin) > (off + count))
goto done;
if ((length + begin) < off) {
begin += length;
length = 0;
}
}
*eof = 1;
done:
if (off >= (length + begin))
return 0;
*start = page + (off-begin);
return (count < begin+length-off) ? count : begin + length-off;
}

#define tiny_ioctl tiny_ioctl_tiocgserial
static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;

if (cmd == TIOCGSERIAL) {
struct serial_struct tmp;

if (!arg)
return -EFAULT;

memset(&tmp, 0, sizeof(tmp));

tmp.type = tiny->serial.type;
tmp.line = tiny->serial.line;
tmp.port = tiny->serial.port;
tmp.irq = tiny->serial.irq;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
tmp.baud_base = tiny->serial.baud_base;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
tmp.custom_divisor = tiny->serial.custom_divisor;
tmp.hub6 = tiny->serial.hub6;
tmp.io_type = tiny->serial.io_type;

if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
#undef tiny_ioctl

#define tiny_ioctl tiny_ioctl_tiocmiwait
static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;

if (cmd == TIOCMIWAIT) {
DECLARE_WAITQUEUE(wait, current);
struct async_icount cnow;
struct async_icount cprev;

cprev = tiny->icount;
while (1) {
add_wait_queue(&tiny->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&tiny->wait, &wait);

/* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS;

cnow = tiny->icount;
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
return 0;
}
cprev = cnow;
}

}
return -ENOIOCTLCMD;
}
#undef tiny_ioctl

#define tiny_ioctl tiny_ioctl_tiocgicount
static int tiny_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;

if (cmd == TIOCGICOUNT) {
struct async_icount cnow = tiny->icount;
struct serial_icounter_struct icount;

icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;

if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
#undef tiny_ioctl

/* the real tiny_ioctl function. The above is done to get the small functions in the book */
static int tiny_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case TIOCGSERIAL:
return tiny_ioctl_tiocgserial(tty, file, cmd, arg);
case TIOCMIWAIT:
return tiny_ioctl_tiocmiwait(tty, file, cmd, arg);
case TIOCGICOUNT:
return tiny_ioctl_tiocgicount(tty, file, cmd, arg);
}

return -ENOIOCTLCMD;
}

static struct tty_operations serial_ops = {
.open = tiny_open,
.close = tiny_close,
.write = tiny_write,
.write_room = tiny_write_room,
.set_termios = tiny_set_termios,
};

static struct tty_driver *tiny_tty_driver;

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

/* allocate the tty driver */
tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
if (!tiny_tty_driver)
return -ENOMEM;

/* initialize the tty driver */
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "tiny_tty";
tiny_tty_driver->name = "ttty";
tiny_tty_driver->devfs_name = "tts/ttty%d";
tiny_tty_driver->major = TINY_TTY_MAJOR,
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
tiny_tty_driver->init_termios = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver, &serial_ops);

/* hack to make the book purty, yet still use these functions in the
* real driver. They really should be set up in the serial_ops
* structure above... */
tiny_tty_driver->read_proc = tiny_read_proc;
tiny_tty_driver->tiocmget = tiny_tiocmget;
tiny_tty_driver->tiocmset = tiny_tiocmset;
tiny_tty_driver->ioctl = tiny_ioctl;

/* register the tty driver */
retval = tty_register_driver(tiny_tty_driver);
if (retval) {
printk(KERN_ERR "failed to register tiny tty driver");
put_tty_driver(tiny_tty_driver);
return retval;
}

for (i = 0; i < TINY_TTY_MINORS; ++i)
tty_register_device(tiny_tty_driver, i, NULL);

printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
return retval;
}

static void __exit tiny_exit(void)
{
struct tiny_serial *tiny;
int i;

for (i = 0; i < TINY_TTY_MINORS; ++i)
tty_unregister_device(tiny_tty_driver, i);
tty_unregister_driver(tiny_tty_driver);

/* shut down all of the timers and free the memory */
for (i = 0; i < TINY_TTY_MINORS; ++i) {
tiny = tiny_table[i];
if (tiny) {
/* close the port */
while (tiny->open_count)
do_close(tiny);

/* shut down our timer and free the memory */
del_timer(tiny->timer);
kfree(tiny->timer);
kfree(tiny);
tiny_table[i] = NULL;
}
}
}

module_init(tiny_init);
module_exit(tiny_exit);
--------------------------------------------------------------------------------------


2007-02-21 19:22:34

by Lennart Sorensen

[permalink] [raw]
Subject: Re: cat problem in tiny_tty driver (the source included)

On Wed, Feb 21, 2007 at 06:03:16PM +0300, Mockern wrote:
> I tried to check cat operations for tiny_tty driver from LDD book.
>
> What is wrong with cat operation here?
>
> Here is the output from strace cat hello > /dev/my_tty1
>
> root@andy:/home# strace cat hello > /dev/my_tty1
> execve("/bin/cat", ["cat", "hello"], [/* 12 vars */]) = 0
> brk(0) = 0x7d000
> open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
> open("/etc/ld.so.cache", O_RDONLY) = 3
> fstat64(3, {st_mode=S_IFREG|0644, st_size=5664, ...}) = 0
> old_mmap(NULL, 5664, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
> close(3) = 0
> open("/lib/libm.so.6", O_RDONLY) = 3
> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\250B\0\000"..., 512) = 51
> 2
> fstat64(3, {st_mode=S_IFREG|0755, st_size=480324, ...}) = 0
> old_mmap(NULL, 506412, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40020000
> mprotect(0x40093000, 35372, PROT_NONE) = 0
> old_mmap(0x40098000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x70
> 000) = 0x40098000
> close(3) = 0
> open("/lib/libcrypt.so.1", O_RDONLY) = 3
> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\260\10\0"..., 512) = 512
> fstat64(3, {st_mode=S_IFREG|0755, st_size=19940, ...}) = 0
> old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0
> x40019000
> old_mmap(NULL, 211220, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4009c000
> mprotect(0x400a1000, 190740, PROT_NONE) = 0
> old_mmap(0x400a4000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0) =
> 0x400a4000
> old_mmap(0x400a9000, 157972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANO
> NYMOUS, -1, 0) = 0x400a9000
> close(3) = 0
> open("/lib/libc.so.6", O_RDONLY) = 3
> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\330p\1\000"..., 512) = 51
> 2
> fstat64(3, {st_mode=S_IFREG|0755, st_size=1240024, ...}) = 0
> old_mmap(NULL, 1257088, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x400d0000
> mprotect(0x401f5000, 56960, PROT_NONE) = 0
> old_mmap(0x401f8000, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12
> 0000) = 0x401f8000
> old_mmap(0x40201000, 7808, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONY
> MOUS, -1, 0) = 0x40201000
> close(3) = 0
> munmap(0x40017000, 5664) = 0
> getuid32() = 0
> getgid32() = 0
> setgid32(0) = 0
> setuid32(0) = 0
> brk(0) = 0x7d000
> brk(0x9e000) = 0x9e000
> brk(0) = 0x9e000
> open("hello", O_RDONLY) = 3
> read(3, "123456789", 8192) = 9
> write(1, "123456789", 9) = -1 EINVAL (Invalid argument)//??????????
> write(2, "cat: ", 5cat: ) = 5
> write(2, "Write Error", 11Write Error) = 11
> write(2, ": Invalid argument\n", 19: Invalid argument
> ) = 19
> close(3) = 0
> io_submit(0, 0x40200164, 0 <unfinished ... exit status 0>
> Process 1432 detached
> root@andy:/home#
>
> ------------------------------------------------------------------------------------
>
> /*
> * Tiny TTY driver
> *
> * Copyright (C) 2002-2004 Greg Kroah-Hartman ([email protected])
> *
> * 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, version 2 of the License.
> *
> * This driver shows how to create a minimal tty driver. It does not rely on
> * any backing hardware, but creates a timer that emulates data being received
> * from some kind of hardware.
> */
>
> #include <linux/config.h>
> #include <linux/kernel.h>
> #include <linux/errno.h>
> #include <linux/init.h>
> #include <linux/module.h>
> #include <linux/slab.h>
> #include <linux/wait.h>
> #include <linux/tty.h>
> #include <linux/tty_driver.h>
> #include <linux/tty_flip.h>
> #include <linux/serial.h>
> #include <asm/uaccess.h>
>
>
> #define DRIVER_VERSION "v2.0"
> #define DRIVER_AUTHOR "Greg Kroah-Hartman <[email protected]>"
> #define DRIVER_DESC "Tiny TTY driver"
>
> /* Module information */
> MODULE_AUTHOR( DRIVER_AUTHOR );
> MODULE_DESCRIPTION( DRIVER_DESC );
> MODULE_LICENSE("GPL");
>
> #define DELAY_TIME HZ * 2 /* 2 seconds per character */
> #define TINY_DATA_CHARACTER 't'
>
> #define TINY_TTY_MAJOR 240 /* experimental range */
> #define TINY_TTY_MINORS 4 /* only have 4 devices */
>
> struct tiny_serial {
> struct tty_struct *tty; /* pointer to the tty for this device */
> int open_count; /* number of times this port has been opened */
> struct semaphore sem; /* locks this structure */
> struct timer_list *timer;
>
> /* for tiocmget and tiocmset functions */
> int msr; /* MSR shadow */
> int mcr; /* MCR shadow */
>
> /* for ioctl fun */
> struct serial_struct serial;
> wait_queue_head_t wait;
> struct async_icount icount;
> };
>
> static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */
>
>
> static void tiny_timer(unsigned long timer_data)
> {
> struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
> struct tty_struct *tty;
> int i;
> char data[1] = {TINY_DATA_CHARACTER};
> int data_size = 1;
>
> if (!tiny)
> return;
>
> tty = tiny->tty;
>
> /* send the data to the tty layer for users to read. This doesn't
> * actually push the data through unless tty->low_latency is set */
> for (i = 0; i < data_size; ++i) {
> if (tty->flip.count >= TTY_FLIPBUF_SIZE)
> tty_flip_buffer_push(tty);
> tty_insert_flip_char(tty, data[i], TTY_NORMAL);
> }
> tty_flip_buffer_push(tty);
>
> /* resubmit the timer again */
> tiny->timer->expires = jiffies + DELAY_TIME;
> add_timer(tiny->timer);
> }
>
> static int tiny_open(struct tty_struct *tty, struct file *file)
> {
> struct tiny_serial *tiny;
> struct timer_list *timer;
> int index;
>
> /* initialize the pointer in case something fails */
> tty->driver_data = NULL;
>
> /* get the serial object associated with this tty pointer */
> index = tty->index;
> tiny = tiny_table[index];
> if (tiny == NULL) {
> /* first time accessing this device, let's create it */
> tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
> if (!tiny)
> return -ENOMEM;
>
> init_MUTEX(&tiny->sem);
> tiny->open_count = 0;
> tiny->timer = NULL;
>
> tiny_table[index] = tiny;
> }
>
> down(&tiny->sem);
>
> /* save our structure within the tty structure */
> tty->driver_data = tiny;
> tiny->tty = tty;
>
> ++tiny->open_count;
> if (tiny->open_count == 1) {
> /* this is the first time this port is opened */
> /* do any hardware initialization needed here */
>
> /* create our timer and submit it */
> if (!tiny->timer) {
> timer = kmalloc(sizeof(*timer), GFP_KERNEL);
> if (!timer) {
> up(&tiny->sem);
> return -ENOMEM;
> }
> tiny->timer = timer;
> }
> tiny->timer->data = (unsigned long )tiny;
> tiny->timer->expires = jiffies + DELAY_TIME;
> tiny->timer->function = tiny_timer;
> add_timer(tiny->timer);
> }
>
> up(&tiny->sem);
> return 0;
> }
>
> static void do_close(struct tiny_serial *tiny)
> {
> down(&tiny->sem);
>
> if (!tiny->open_count) {
> /* port was never opened */
> goto exit;
> }
>
> --tiny->open_count;
> if (tiny->open_count <= 0) {
> /* The port is being closed by the last user. */
> /* Do any hardware specific stuff here */
>
> /* shut down our timer */
> del_timer(tiny->timer);
> }
> exit:
> up(&tiny->sem);
> }
>
> static void tiny_close(struct tty_struct *tty, struct file *file)
> {
> struct tiny_serial *tiny = tty->driver_data;
>
> if (tiny)
> do_close(tiny);
> }
>
> static int tiny_write(struct tty_struct *tty, const unsigned char *buffer, int count)
> {
> struct tiny_serial *tiny = tty->driver_data;
> int i;
> int retval = -EINVAL;

Here you set retval to EINVAL.

> if (!tiny)
> return -ENODEV;
>
> down(&tiny->sem);
>
> if (!tiny->open_count)
> /* port was not opened */
> goto exit;
>
> /* fake sending the data out a hardware port by
> * writing it to the kernel debug log.
> */
> printk(KERN_DEBUG "%s - ", __FUNCTION__);
> for (i = 0; i < count; ++i)
> printk("%02x ", buffer[i]);
> printk("\n");
>
> exit:
> up(&tiny->sem);
> return retval;

Now you return it. You never changed it. No wonder cat receives
'EINVAL' as a response to its write request. This is obviously not what
you intended.

> }
>
> static int tiny_write_room(struct tty_struct *tty)
> {
> struct tiny_serial *tiny = tty->driver_data;
> int room = -EINVAL;
>
> if (!tiny)
> return -ENODEV;
>
> down(&tiny->sem);
>
> if (!tiny->open_count) {
> /* port was not opened */
> goto exit;
> }
>
> /* calculate how much room is left in the device */
> room = 255;
>
> exit:
> up(&tiny->sem);
> return room;
> }
>
> #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
>
> static void tiny_set_termios(struct tty_struct *tty, struct termios *old_termios)
> {
> unsigned int cflag;
>
> cflag = tty->termios->c_cflag;
>
> /* check that they really want us to change something */
> if (old_termios) {
> if ((cflag == old_termios->c_cflag) &&
> (RELEVANT_IFLAG(tty->termios->c_iflag) ==
> RELEVANT_IFLAG(old_termios->c_iflag))) {
> printk(KERN_DEBUG " - nothing to change...\n");
> return;
> }
> }
>
> /* get the byte size */
> switch (cflag & CSIZE) {
> case CS5:
> printk(KERN_DEBUG " - data bits = 5\n");
> break;
> case CS6:
> printk(KERN_DEBUG " - data bits = 6\n");
> break;
> case CS7:
> printk(KERN_DEBUG " - data bits = 7\n");
> break;
> default:
> case CS8:
> printk(KERN_DEBUG " - data bits = 8\n");
> break;
> }
>
> /* determine the parity */
> if (cflag & PARENB)
> if (cflag & PARODD)
> printk(KERN_DEBUG " - parity = odd\n");
> else
> printk(KERN_DEBUG " - parity = even\n");
> else
> printk(KERN_DEBUG " - parity = none\n");
>
> /* figure out the stop bits requested */
> if (cflag & CSTOPB)
> printk(KERN_DEBUG " - stop bits = 2\n");
> else
> printk(KERN_DEBUG " - stop bits = 1\n");
>
> /* figure out the hardware flow control settings */
> if (cflag & CRTSCTS)
> printk(KERN_DEBUG " - RTS/CTS is enabled\n");
> else
> printk(KERN_DEBUG " - RTS/CTS is disabled\n");
>
> /* determine software flow control */
> /* if we are implementing XON/XOFF, set the start and
> * stop character in the device */
> if (I_IXOFF(tty) || I_IXON(tty)) {
> unsigned char stop_char = STOP_CHAR(tty);
> unsigned char start_char = START_CHAR(tty);
>
> /* if we are implementing INBOUND XON/XOFF */
> if (I_IXOFF(tty))
> printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
> "XON = %2x, XOFF = %2x", start_char, stop_char);
> else
> printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");
>
> /* if we are implementing OUTBOUND XON/XOFF */
> if (I_IXON(tty))
> printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
> "XON = %2x, XOFF = %2x", start_char, stop_char);
> else
> printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
> }
>
> /* get the baud rate wanted */
> printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
> }
>
> /* Our fake UART values */
> #define MCR_DTR 0x01
> #define MCR_RTS 0x02
> #define MCR_LOOP 0x04
> #define MSR_CTS 0x08
> #define MSR_CD 0x10
> #define MSR_RI 0x20
> #define MSR_DSR 0x40
>
> static int tiny_tiocmget(struct tty_struct *tty, struct file *file)
> {
> struct tiny_serial *tiny = tty->driver_data;
>
> unsigned int result = 0;
> unsigned int msr = tiny->msr;
> unsigned int mcr = tiny->mcr;
>
> result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
> ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
> ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
> ((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */
> ((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/
> ((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
> ((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */
>
> return result;
> }
>
> static int tiny_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear)
> {
> struct tiny_serial *tiny = tty->driver_data;
> unsigned int mcr = tiny->mcr;
>
> if (set & TIOCM_RTS)
> mcr |= MCR_RTS;
> if (set & TIOCM_DTR)
> mcr |= MCR_RTS;
>
> if (clear & TIOCM_RTS)
> mcr &= ~MCR_RTS;
> if (clear & TIOCM_DTR)
> mcr &= ~MCR_RTS;
>
> /* set the new MCR value in the device */
> tiny->mcr = mcr;
> return 0;
> }
>
> static int tiny_read_proc(char *page, char **start, off_t off, int count,
> int *eof, void *data)
> {
> struct tiny_serial *tiny;
> off_t begin = 0;
> int length = 0;
> int i;
>
> length += sprintf(page, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
> for (i = 0; i < TINY_TTY_MINORS && length < PAGE_SIZE; ++i) {
> tiny = tiny_table[i];
> if (tiny == NULL)
> continue;
>
> length += sprintf(page+length, "%d\n", i);
> if ((length + begin) > (off + count))
> goto done;
> if ((length + begin) < off) {
> begin += length;
> length = 0;
> }
> }
> *eof = 1;
> done:
> if (off >= (length + begin))
> return 0;
> *start = page + (off-begin);
> return (count < begin+length-off) ? count : begin + length-off;
> }
>
> #define tiny_ioctl tiny_ioctl_tiocgserial
> static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
> {
> struct tiny_serial *tiny = tty->driver_data;
>
> if (cmd == TIOCGSERIAL) {
> struct serial_struct tmp;
>
> if (!arg)
> return -EFAULT;
>
> memset(&tmp, 0, sizeof(tmp));
>
> tmp.type = tiny->serial.type;
> tmp.line = tiny->serial.line;
> tmp.port = tiny->serial.port;
> tmp.irq = tiny->serial.irq;
> tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
> tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
> tmp.baud_base = tiny->serial.baud_base;
> tmp.close_delay = 5*HZ;
> tmp.closing_wait = 30*HZ;
> tmp.custom_divisor = tiny->serial.custom_divisor;
> tmp.hub6 = tiny->serial.hub6;
> tmp.io_type = tiny->serial.io_type;
>
> if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
> return -EFAULT;
> return 0;
> }
> return -ENOIOCTLCMD;
> }
> #undef tiny_ioctl
>
> #define tiny_ioctl tiny_ioctl_tiocmiwait
> static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
> {
> struct tiny_serial *tiny = tty->driver_data;
>
> if (cmd == TIOCMIWAIT) {
> DECLARE_WAITQUEUE(wait, current);
> struct async_icount cnow;
> struct async_icount cprev;
>
> cprev = tiny->icount;
> while (1) {
> add_wait_queue(&tiny->wait, &wait);
> set_current_state(TASK_INTERRUPTIBLE);
> schedule();
> remove_wait_queue(&tiny->wait, &wait);
>
> /* see if a signal woke us up */
> if (signal_pending(current))
> return -ERESTARTSYS;
>
> cnow = tiny->icount;
> if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
> cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
> return -EIO; /* no change => error */
> if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
> ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
> ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
> ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
> return 0;
> }
> cprev = cnow;
> }
>
> }
> return -ENOIOCTLCMD;
> }
> #undef tiny_ioctl
>
> #define tiny_ioctl tiny_ioctl_tiocgicount
> static int tiny_ioctl(struct tty_struct *tty, struct file *file,
> unsigned int cmd, unsigned long arg)
> {
> struct tiny_serial *tiny = tty->driver_data;
>
> if (cmd == TIOCGICOUNT) {
> struct async_icount cnow = tiny->icount;
> struct serial_icounter_struct icount;
>
> icount.cts = cnow.cts;
> icount.dsr = cnow.dsr;
> icount.rng = cnow.rng;
> icount.dcd = cnow.dcd;
> icount.rx = cnow.rx;
> icount.tx = cnow.tx;
> icount.frame = cnow.frame;
> icount.overrun = cnow.overrun;
> icount.parity = cnow.parity;
> icount.brk = cnow.brk;
> icount.buf_overrun = cnow.buf_overrun;
>
> if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
> return -EFAULT;
> return 0;
> }
> return -ENOIOCTLCMD;
> }
> #undef tiny_ioctl
>
> /* the real tiny_ioctl function. The above is done to get the small functions in the book */
> static int tiny_ioctl(struct tty_struct *tty, struct file *file,
> unsigned int cmd, unsigned long arg)
> {
> switch (cmd) {
> case TIOCGSERIAL:
> return tiny_ioctl_tiocgserial(tty, file, cmd, arg);
> case TIOCMIWAIT:
> return tiny_ioctl_tiocmiwait(tty, file, cmd, arg);
> case TIOCGICOUNT:
> return tiny_ioctl_tiocgicount(tty, file, cmd, arg);
> }
>
> return -ENOIOCTLCMD;
> }
>
> static struct tty_operations serial_ops = {
> .open = tiny_open,
> .close = tiny_close,
> .write = tiny_write,
> .write_room = tiny_write_room,
> .set_termios = tiny_set_termios,
> };
>
> static struct tty_driver *tiny_tty_driver;
>
> static int __init tiny_init(void)
> {
> int retval;
> int i;
>
> /* allocate the tty driver */
> tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
> if (!tiny_tty_driver)
> return -ENOMEM;
>
> /* initialize the tty driver */
> tiny_tty_driver->owner = THIS_MODULE;
> tiny_tty_driver->driver_name = "tiny_tty";
> tiny_tty_driver->name = "ttty";
> tiny_tty_driver->devfs_name = "tts/ttty%d";
> tiny_tty_driver->major = TINY_TTY_MAJOR,
> tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
> tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
> tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
> tiny_tty_driver->init_termios = tty_std_termios;
> tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
> tty_set_operations(tiny_tty_driver, &serial_ops);
>
> /* hack to make the book purty, yet still use these functions in the
> * real driver. They really should be set up in the serial_ops
> * structure above... */
> tiny_tty_driver->read_proc = tiny_read_proc;
> tiny_tty_driver->tiocmget = tiny_tiocmget;
> tiny_tty_driver->tiocmset = tiny_tiocmset;
> tiny_tty_driver->ioctl = tiny_ioctl;
>
> /* register the tty driver */
> retval = tty_register_driver(tiny_tty_driver);
> if (retval) {
> printk(KERN_ERR "failed to register tiny tty driver");
> put_tty_driver(tiny_tty_driver);
> return retval;
> }
>
> for (i = 0; i < TINY_TTY_MINORS; ++i)
> tty_register_device(tiny_tty_driver, i, NULL);
>
> printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
> return retval;
> }
>
> static void __exit tiny_exit(void)
> {
> struct tiny_serial *tiny;
> int i;
>
> for (i = 0; i < TINY_TTY_MINORS; ++i)
> tty_unregister_device(tiny_tty_driver, i);
> tty_unregister_driver(tiny_tty_driver);
>
> /* shut down all of the timers and free the memory */
> for (i = 0; i < TINY_TTY_MINORS; ++i) {
> tiny = tiny_table[i];
> if (tiny) {
> /* close the port */
> while (tiny->open_count)
> do_close(tiny);
>
> /* shut down our timer and free the memory */
> del_timer(tiny->timer);
> kfree(tiny->timer);
> kfree(tiny);
> tiny_table[i] = NULL;
> }
> }
> }
>
> module_init(tiny_init);
> module_exit(tiny_exit);

--
Len Sorensen

2007-02-21 19:34:09

by Mockern

[permalink] [raw]
Subject: Re: cat problem in tiny_tty driver (the source included)

Thank you very much for you help.

BTW, for cat < /dev/my_tty1 can see here something wrong?
as I understand tiny_timer function sends data to tty level
by calling tty_flip_buffer_push(tty).

Is this enough to support cat < /dev/my_tty1?


>On Wed, Feb 21, 2007 at 06:03:16PM +0300, Mockern wrote:
>> I tried to check cat operations for tiny_tty driver from LDD book.
>>
>> What is wrong with cat operation here?
>>
>> Here is the output from strace cat hello > /dev/my_tty1
>>
>> root@andy:/home# strace cat hello > /dev/my_tty1
>> execve("/bin/cat", ["cat", "hello"], [/* 12 vars */]) = 0
>> brk(0) = 0x7d000
>> open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
>> open("/etc/ld.so.cache", O_RDONLY) = 3
>> fstat64(3, {st_mode=S_IFREG|0644, st_size=5664, ...}) = 0
>> old_mmap(NULL, 5664, PROT_READ, MAP_PRIVATE, 3, 0) = 0x40017000
>> close(3) = 0
>> open("/lib/libm.so.6", O_RDONLY) = 3
>> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\250B\0\000"..., 512) = 51
>> 2
>> fstat64(3, {st_mode=S_IFREG|0755, st_size=480324, ...}) = 0
>> old_mmap(NULL, 506412, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x40020000
>> mprotect(0x40093000, 35372, PROT_NONE) = 0
>> old_mmap(0x40098000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x70
>> 000) = 0x40098000
>> close(3) = 0
>> open("/lib/libcrypt.so.1", O_RDONLY) = 3
>> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\260\10\0"..., 512) = 512
>> fstat64(3, {st_mode=S_IFREG|0755, st_size=19940, ...}) = 0
>> old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0
>> x40019000
>> old_mmap(NULL, 211220, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x4009c000
>> mprotect(0x400a1000, 190740, PROT_NONE) = 0
>> old_mmap(0x400a4000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0) =
>> 0x400a4000
>> old_mmap(0x400a9000, 157972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANO
>> NYMOUS, -1, 0) = 0x400a9000
>> close(3) = 0
>> open("/lib/libc.so.6", O_RDONLY) = 3
>> read(3, "\177ELF\1\1\1a\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\330p\1\000"..., 512) = 51
>> 2
>> fstat64(3, {st_mode=S_IFREG|0755, st_size=1240024, ...}) = 0
>> old_mmap(NULL, 1257088, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0) = 0x400d0000
>> mprotect(0x401f5000, 56960, PROT_NONE) = 0
>> old_mmap(0x401f8000, 36864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x12
>> 0000) = 0x401f8000
>> old_mmap(0x40201000, 7808, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONY
>> MOUS, -1, 0) = 0x40201000
>> close(3) = 0
>> munmap(0x40017000, 5664) = 0
>> getuid32() = 0
>> getgid32() = 0
>> setgid32(0) = 0
>> setuid32(0) = 0
>> brk(0) = 0x7d000
>> brk(0x9e000) = 0x9e000
>> brk(0) = 0x9e000
>> open("hello", O_RDONLY) = 3
>> read(3, "123456789", 8192) = 9
>> write(1, "123456789", 9) = -1 EINVAL (Invalid argument)//??????????
>> write(2, "cat: ", 5cat: ) = 5
>> write(2, "Write Error", 11Write Error) = 11
>> write(2, ": Invalid argument\n", 19: Invalid argument
>> ) = 19
>> close(3) = 0
>> io_submit(0, 0x40200164, 0 <unfinished ... exit status 0>
>> Process 1432 detached
>> root@andy:/home#
>>
>> ------------------------------------------------------------------------------------
>>
>> /*
>> * Tiny TTY driver
>> *
>> * Copyright (C) 2002-2004 Greg Kroah-Hartman ([email protected])
>> *
>> * 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, version 2 of the License.
>> *
>> * This driver shows how to create a minimal tty driver. It does not rely on
>> * any backing hardware, but creates a timer that emulates data being received
>> * from some kind of hardware.
>> */
>>
>> #include <linux/config.h>
>> #include <linux/kernel.h>
>> #include <linux/errno.h>
>> #include <linux/init.h>
>> #include <linux/module.h>
>> #include <linux/slab.h>
>> #include <linux/wait.h>
>> #include <linux/tty.h>
>> #include <linux/tty_driver.h>
>> #include <linux/tty_flip.h>
>> #include <linux/serial.h>
>> #include <asm/uaccess.h>
>>
>>
>> #define DRIVER_VERSION "v2.0"
>> #define DRIVER_AUTHOR "Greg Kroah-Hartman <[email protected]>"
>> #define DRIVER_DESC "Tiny TTY driver"
>>
>> /* Module information */
>> MODULE_AUTHOR( DRIVER_AUTHOR );
>> MODULE_DESCRIPTION( DRIVER_DESC );
>> MODULE_LICENSE("GPL");
>>
>> #define DELAY_TIME HZ * 2 /* 2 seconds per character */
>> #define TINY_DATA_CHARACTER 't'
>>
>> #define TINY_TTY_MAJOR 240 /* experimental range */
>> #define TINY_TTY_MINORS 4 /* only have 4 devices */
>>
>> struct tiny_serial {
>> struct tty_struct *tty; /* pointer to the tty for this device */
>> int open_count; /* number of times this port has been opened */
>> struct semaphore sem; /* locks this structure */
>> struct timer_list *timer;
>>
>> /* for tiocmget and tiocmset functions */
>> int msr; /* MSR shadow */
>> int mcr; /* MCR shadow */
>>
>> /* for ioctl fun */
>> struct serial_struct serial;
>> wait_queue_head_t wait;
>> struct async_icount icount;
>> };
>>
>> static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */
>>
>>
>> static void tiny_timer(unsigned long timer_data)
>> {
>> struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
>> struct tty_struct *tty;
>> int i;
>> char data[1] = {TINY_DATA_CHARACTER};
>> int data_size = 1;
>>
>> if (!tiny)
>> return;
>>
>> tty = tiny->tty;
>>
>> /* send the data to the tty layer for users to read. This doesn't
>> * actually push the data through unless tty->low_latency is set */
>> for (i = 0; i < data_size; ++i) {
>> if (tty->flip.count >= TTY_FLIPBUF_SIZE)
>> tty_flip_buffer_push(tty);
>> tty_insert_flip_char(tty, data[i], TTY_NORMAL);
>> }
>> tty_flip_buffer_push(tty);
>>
>> /* resubmit the timer again */
>> tiny->timer->expires = jiffies + DELAY_TIME;
>> add_timer(tiny->timer);
>> }
>>
>> static int tiny_open(struct tty_struct *tty, struct file *file)
>> {
>> struct tiny_serial *tiny;
>> struct timer_list *timer;
>> int index;
>>
>> /* initialize the pointer in case something fails */
>> tty->driver_data = NULL;
>>
>> /* get the serial object associated with this tty pointer */
>> index = tty->index;
>> tiny = tiny_table[index];
>> if (tiny == NULL) {
>> /* first time accessing this device, let's create it */
>> tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
>> if (!tiny)
>> return -ENOMEM;
>>
>> init_MUTEX(&tiny->sem);
>> tiny->open_count = 0;
>> tiny->timer = NULL;
>>
>> tiny_table[index] = tiny;
>> }
>>
>> down(&tiny->sem);
>>
>> /* save our structure within the tty structure */
>> tty->driver_data = tiny;
>> tiny->tty = tty;
>>
>> ++tiny->open_count;
>> if (tiny->open_count == 1) {
>> /* this is the first time this port is opened */
>> /* do any hardware initialization needed here */
>>
>> /* create our timer and submit it */
>> if (!tiny->timer) {
>> timer = kmalloc(sizeof(*timer), GFP_KERNEL);
>> if (!timer) {
>> up(&tiny->sem);
>> return -ENOMEM;
>> }
>> tiny->timer = timer;
>> }
>> tiny->timer->data = (unsigned long )tiny;
>> tiny->timer->expires = jiffies + DELAY_TIME;
>> tiny->timer->function = tiny_timer;
>> add_timer(tiny->timer);
>> }
>>
>> up(&tiny->sem);
>> return 0;
>> }
>>
>> static void do_close(struct tiny_serial *tiny)
>> {
>> down(&tiny->sem);
>>
>> if (!tiny->open_count) {
>> /* port was never opened */
>> goto exit;
>> }
>>
>> --tiny->open_count;
>> if (tiny->open_count <= 0) {
>> /* The port is being closed by the last user. */
>> /* Do any hardware specific stuff here */
>>
>> /* shut down our timer */
>> del_timer(tiny->timer);
>> }
>> exit:
>> up(&tiny->sem);
>> }
>>
>> static void tiny_close(struct tty_struct *tty, struct file *file)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>>
>> if (tiny)
>> do_close(tiny);
>> }
>>
>> static int tiny_write(struct tty_struct *tty, const unsigned char *buffer, int count)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>> int i;
>> int retval = -EINVAL;
>
>Here you set retval to EINVAL.
>
>> if (!tiny)
>> return -ENODEV;
>>
>> down(&tiny->sem);
>>
>> if (!tiny->open_count)
>> /* port was not opened */
>> goto exit;
>>
>> /* fake sending the data out a hardware port by
>> * writing it to the kernel debug log.
>> */
>> printk(KERN_DEBUG "%s - ", __FUNCTION__);
>> for (i = 0; i < count; ++i)
>> printk("%02x ", buffer[i]);
>> printk("\n");
>>
>> exit:
>> up(&tiny->sem);
>> return retval;
>
>Now you return it. You never changed it. No wonder cat receives
>'EINVAL' as a response to its write request. This is obviously not what
>you intended.
>
>> }
>>
>> static int tiny_write_room(struct tty_struct *tty)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>> int room = -EINVAL;
>>
>> if (!tiny)
>> return -ENODEV;
>>
>> down(&tiny->sem);
>>
>> if (!tiny->open_count) {
>> /* port was not opened */
>> goto exit;
>> }
>>
>> /* calculate how much room is left in the device */
>> room = 255;
>>
>> exit:
>> up(&tiny->sem);
>> return room;
>> }
>>
>> #define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
>>
>> static void tiny_set_termios(struct tty_struct *tty, struct termios *old_termios)
>> {
>> unsigned int cflag;
>>
>> cflag = tty->termios->c_cflag;
>>
>> /* check that they really want us to change something */
>> if (old_termios) {
>> if ((cflag == old_termios->c_cflag) &&
>> (RELEVANT_IFLAG(tty->termios->c_iflag) ==
>> RELEVANT_IFLAG(old_termios->c_iflag))) {
>> printk(KERN_DEBUG " - nothing to change...\n");
>> return;
>> }
>> }
>>
>> /* get the byte size */
>> switch (cflag & CSIZE) {
>> case CS5:
>> printk(KERN_DEBUG " - data bits = 5\n");
>> break;
>> case CS6:
>> printk(KERN_DEBUG " - data bits = 6\n");
>> break;
>> case CS7:
>> printk(KERN_DEBUG " - data bits = 7\n");
>> break;
>> default:
>> case CS8:
>> printk(KERN_DEBUG " - data bits = 8\n");
>> break;
>> }
>>
>> /* determine the parity */
>> if (cflag & PARENB)
>> if (cflag & PARODD)
>> printk(KERN_DEBUG " - parity = odd\n");
>> else
>> printk(KERN_DEBUG " - parity = even\n");
>> else
>> printk(KERN_DEBUG " - parity = none\n");
>>
>> /* figure out the stop bits requested */
>> if (cflag & CSTOPB)
>> printk(KERN_DEBUG " - stop bits = 2\n");
>> else
>> printk(KERN_DEBUG " - stop bits = 1\n");
>>
>> /* figure out the hardware flow control settings */
>> if (cflag & CRTSCTS)
>> printk(KERN_DEBUG " - RTS/CTS is enabled\n");
>> else
>> printk(KERN_DEBUG " - RTS/CTS is disabled\n");
>>
>> /* determine software flow control */
>> /* if we are implementing XON/XOFF, set the start and
>> * stop character in the device */
>> if (I_IXOFF(tty) || I_IXON(tty)) {
>> unsigned char stop_char = STOP_CHAR(tty);
>> unsigned char start_char = START_CHAR(tty);
>>
>> /* if we are implementing INBOUND XON/XOFF */
>> if (I_IXOFF(tty))
>> printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
>> "XON = %2x, XOFF = %2x", start_char, stop_char);
>> else
>> printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");
>>
>> /* if we are implementing OUTBOUND XON/XOFF */
>> if (I_IXON(tty))
>> printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
>> "XON = %2x, XOFF = %2x", start_char, stop_char);
>> else
>> printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
>> }
>>
>> /* get the baud rate wanted */
>> printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
>> }
>>
>> /* Our fake UART values */
>> #define MCR_DTR 0x01
>> #define MCR_RTS 0x02
>> #define MCR_LOOP 0x04
>> #define MSR_CTS 0x08
>> #define MSR_CD 0x10
>> #define MSR_RI 0x20
>> #define MSR_DSR 0x40
>>
>> static int tiny_tiocmget(struct tty_struct *tty, struct file *file)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>>
>> unsigned int result = 0;
>> unsigned int msr = tiny->msr;
>> unsigned int mcr = tiny->mcr;
>>
>> result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | /* DTR is set */
>> ((mcr & MCR_RTS) ? TIOCM_RTS : 0) | /* RTS is set */
>> ((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | /* LOOP is set */
>> ((msr & MSR_CTS) ? TIOCM_CTS : 0) | /* CTS is set */
>> ((msr & MSR_CD) ? TIOCM_CAR : 0) | /* Carrier detect is set*/
>> ((msr & MSR_RI) ? TIOCM_RI : 0) | /* Ring Indicator is set */
>> ((msr & MSR_DSR) ? TIOCM_DSR : 0); /* DSR is set */
>>
>> return result;
>> }
>>
>> static int tiny_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>> unsigned int mcr = tiny->mcr;
>>
>> if (set & TIOCM_RTS)
>> mcr |= MCR_RTS;
>> if (set & TIOCM_DTR)
>> mcr |= MCR_RTS;
>>
>> if (clear & TIOCM_RTS)
>> mcr &= ~MCR_RTS;
>> if (clear & TIOCM_DTR)
>> mcr &= ~MCR_RTS;
>>
>> /* set the new MCR value in the device */
>> tiny->mcr = mcr;
>> return 0;
>> }
>>
>> static int tiny_read_proc(char *page, char **start, off_t off, int count,
>> int *eof, void *data)
>> {
>> struct tiny_serial *tiny;
>> off_t begin = 0;
>> int length = 0;
>> int i;
>>
>> length += sprintf(page, "tinyserinfo:1.0 driver:%s\n", DRIVER_VERSION);
>> for (i = 0; i < TINY_TTY_MINORS && length < PAGE_SIZE; ++i) {
>> tiny = tiny_table[i];
>> if (tiny == NULL)
>> continue;
>>
>> length += sprintf(page+length, "%d\n", i);
>> if ((length + begin) > (off + count))
>> goto done;
>> if ((length + begin) < off) {
>> begin += length;
>> length = 0;
>> }
>> }
>> *eof = 1;
>> done:
>> if (off >= (length + begin))
>> return 0;
>> *start = page + (off-begin);
>> return (count < begin+length-off) ? count : begin + length-off;
>> }
>>
>> #define tiny_ioctl tiny_ioctl_tiocgserial
>> static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>>
>> if (cmd == TIOCGSERIAL) {
>> struct serial_struct tmp;
>>
>> if (!arg)
>> return -EFAULT;
>>
>> memset(&tmp, 0, sizeof(tmp));
>>
>> tmp.type = tiny->serial.type;
>> tmp.line = tiny->serial.line;
>> tmp.port = tiny->serial.port;
>> tmp.irq = tiny->serial.irq;
>> tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
>> tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
>> tmp.baud_base = tiny->serial.baud_base;
>> tmp.close_delay = 5*HZ;
>> tmp.closing_wait = 30*HZ;
>> tmp.custom_divisor = tiny->serial.custom_divisor;
>> tmp.hub6 = tiny->serial.hub6;
>> tmp.io_type = tiny->serial.io_type;
>>
>> if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
>> return -EFAULT;
>> return 0;
>> }
>> return -ENOIOCTLCMD;
>> }
>> #undef tiny_ioctl
>>
>> #define tiny_ioctl tiny_ioctl_tiocmiwait
>> static int tiny_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>>
>> if (cmd == TIOCMIWAIT) {
>> DECLARE_WAITQUEUE(wait, current);
>> struct async_icount cnow;
>> struct async_icount cprev;
>>
>> cprev = tiny->icount;
>> while (1) {
>> add_wait_queue(&tiny->wait, &wait);
>> set_current_state(TASK_INTERRUPTIBLE);
>> schedule();
>> remove_wait_queue(&tiny->wait, &wait);
>>
>> /* see if a signal woke us up */
>> if (signal_pending(current))
>> return -ERESTARTSYS;
>>
>> cnow = tiny->icount;
>> if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
>> cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
>> return -EIO; /* no change => error */
>> if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
>> ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
>> ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
>> ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
>> return 0;
>> }
>> cprev = cnow;
>> }
>>
>> }
>> return -ENOIOCTLCMD;
>> }
>> #undef tiny_ioctl
>>
>> #define tiny_ioctl tiny_ioctl_tiocgicount
>> static int tiny_ioctl(struct tty_struct *tty, struct file *file,
>> unsigned int cmd, unsigned long arg)
>> {
>> struct tiny_serial *tiny = tty->driver_data;
>>
>> if (cmd == TIOCGICOUNT) {
>> struct async_icount cnow = tiny->icount;
>> struct serial_icounter_struct icount;
>>
>> icount.cts = cnow.cts;
>> icount.dsr = cnow.dsr;
>> icount.rng = cnow.rng;
>> icount.dcd = cnow.dcd;
>> icount.rx = cnow.rx;
>> icount.tx = cnow.tx;
>> icount.frame = cnow.frame;
>> icount.overrun = cnow.overrun;
>> icount.parity = cnow.parity;
>> icount.brk = cnow.brk;
>> icount.buf_overrun = cnow.buf_overrun;
>>
>> if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
>> return -EFAULT;
>> return 0;
>> }
>> return -ENOIOCTLCMD;
>> }
>> #undef tiny_ioctl
>>
>> /* the real tiny_ioctl function. The above is done to get the small functions in the book */
>> static int tiny_ioctl(struct tty_struct *tty, struct file *file,
>> unsigned int cmd, unsigned long arg)
>> {
>> switch (cmd) {
>> case TIOCGSERIAL:
>> return tiny_ioctl_tiocgserial(tty, file, cmd, arg);
>> case TIOCMIWAIT:
>> return tiny_ioctl_tiocmiwait(tty, file, cmd, arg);
>> case TIOCGICOUNT:
>> return tiny_ioctl_tiocgicount(tty, file, cmd, arg);
>> }
>>
>> return -ENOIOCTLCMD;
>> }
>>
>> static struct tty_operations serial_ops = {
>> .open = tiny_open,
>> .close = tiny_close,
>> .write = tiny_write,
>> .write_room = tiny_write_room,
>> .set_termios = tiny_set_termios,
>> };
>>
>> static struct tty_driver *tiny_tty_driver;
>>
>> static int __init tiny_init(void)
>> {
>> int retval;
>> int i;
>>
>> /* allocate the tty driver */
>> tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
>> if (!tiny_tty_driver)
>> return -ENOMEM;
>>
>> /* initialize the tty driver */
>> tiny_tty_driver->owner = THIS_MODULE;
>> tiny_tty_driver->driver_name = "tiny_tty";
>> tiny_tty_driver->name = "ttty";
>> tiny_tty_driver->devfs_name = "tts/ttty%d";
>> tiny_tty_driver->major = TINY_TTY_MAJOR,
>> tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
>> tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
>> tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
>> tiny_tty_driver->init_termios = tty_std_termios;
>> tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
>> tty_set_operations(tiny_tty_driver, &serial_ops);
>>
>> /* hack to make the book purty, yet still use these functions in the
>> * real driver. They really should be set up in the serial_ops
>> * structure above... */
>> tiny_tty_driver->read_proc = tiny_read_proc;
>> tiny_tty_driver->tiocmget = tiny_tiocmget;
>> tiny_tty_driver->tiocmset = tiny_tiocmset;
>> tiny_tty_driver->ioctl = tiny_ioctl;
>>
>> /* register the tty driver */
>> retval = tty_register_driver(tiny_tty_driver);
>> if (retval) {
>> printk(KERN_ERR "failed to register tiny tty driver");
>> put_tty_driver(tiny_tty_driver);
>> return retval;
>> }
>>
>> for (i = 0; i < TINY_TTY_MINORS; ++i)
>> tty_register_device(tiny_tty_driver, i, NULL);
>>
>> printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION);
>> return retval;
>> }
>>
>> static void __exit tiny_exit(void)
>> {
>> struct tiny_serial *tiny;
>> int i;
>>
>> for (i = 0; i < TINY_TTY_MINORS; ++i)
>> tty_unregister_device(tiny_tty_driver, i);
>> tty_unregister_driver(tiny_tty_driver);
>>
>> /* shut down all of the timers and free the memory */
>> for (i = 0; i < TINY_TTY_MINORS; ++i) {
>> tiny = tiny_table[i];
>> if (tiny) {
>> /* close the port */
>> while (tiny->open_count)
>> do_close(tiny);
>>
>> /* shut down our timer and free the memory */
>> del_timer(tiny->timer);
>> kfree(tiny->timer);
>> kfree(tiny);
>> tiny_table[i] = NULL;
>> }
>> }
>> }
>>
>> module_init(tiny_init);
>> module_exit(tiny_exit);
>
>--
>Len Sorensen
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at http://www.tux.org/lkml/


--
??????? ??????? ????, ????? ??????? ????? ?? ??????? http://mail.yandex.ru

2007-02-21 20:09:13

by Lennart Sorensen

[permalink] [raw]
Subject: Re: cat problem in tiny_tty driver (the source included)

On Wed, Feb 21, 2007 at 10:33:52PM +0300, Mockern wrote:
> Thank you very much for you help.
>
> BTW, for cat < /dev/my_tty1 can see here something wrong?
> as I understand tiny_timer function sends data to tty level
> by calling tty_flip_buffer_push(tty).
>
> Is this enough to support cat < /dev/my_tty1?

I don't really know the tty layer very much. I just saw a very obvious
bug in the code.

Does cat from the device give an error or does it just not do anything?

What does strace say for that?

--
Len Sorensen

2007-02-21 20:21:27

by Mockern

[permalink] [raw]
Subject: Re: cat problem in tiny_tty driver (the source included)

cat < /dev/my_tty does nothing, just stopped for reading. I tried to send a data,
but there was no respond, just waiting.


>On Wed, Feb 21, 2007 at 10:33:52PM +0300, Mockern wrote:
>> Thank you very much for you help.
>>
>> BTW, for cat < /dev/my_tty1 can see here something wrong?
>> as I understand tiny_timer function sends data to tty level
>> by calling tty_flip_buffer_push(tty).
>>
>> Is this enough to support cat < /dev/my_tty1?
>
>I don't really know the tty layer very much. I just saw a very obvious
>bug in the code.
>
>Does cat from the device give an error or does it just not do anything?
>
>What does strace say for that?
>
>--
>Len Sorensen
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to [email protected]
>More majordomo info at http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at http://www.tux.org/lkml/


--
??????? ??????? ????, ????? ??????? ????? ?? ??????? http://mail.yandex.ru

2007-02-21 20:28:29

by Lennart Sorensen

[permalink] [raw]
Subject: Re: cat problem in tiny_tty driver (the source included)

On Wed, Feb 21, 2007 at 11:21:10PM +0300, Mockern wrote:
> cat < /dev/my_tty does nothing, just stopped for reading. I tried to send a data,
> but there was no respond, just waiting.

Well certainly using the jsm driver (which is what I use for the pci
card I play with), doing cat to the port send data and cat from the port
receives, as long as both ports are set to the right baud rates and with
sensible stty settings.

Here are the stty settings I was using when cat worked fine:

speed 9600 baud; rows 0; columns 0; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;
werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -ixon
-ixoff -iuclc -ixany -imaxbel
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0
vt0 ff0
-isig -icanon iexten -echo -echoe echok -echonl -noflsh -xcase -tostop
-echoprt echoctl echoke

You can read yours using 'stty -a -F /dev/serialdevice'.

Maybe you can find something that has to be changed to do raw reads.

Also make sure there are newlines in the stuff you send, otherwise cat
might not show it for a while, if at all since I think it tries to wait
for a newline before showing the line.

--
Len Sorensen