Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1422831AbXBUTeJ (ORCPT ); Wed, 21 Feb 2007 14:34:09 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1422832AbXBUTeJ (ORCPT ); Wed, 21 Feb 2007 14:34:09 -0500 Received: from webmail18.mail.yandex.net ([213.180.200.48]:39661 "EHLO webmail18.mail.yandex.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1422831AbXBUTeH (ORCPT ); Wed, 21 Feb 2007 14:34:07 -0500 Date: Wed, 21 Feb 2007 22:33:52 +0300 (MSK) From: "Mockern" Message-Id: <45DC9EA0.000007.11000@webmail18.yandex.ru> MIME-Version: 1.0 X-Mailer: Yamail [ http://yandex.ru ] To: lsorense@csclub.uwaterloo.ca Cc: linux-kernel@vger.kernel.org Subject: Re: cat problem in tiny_tty driver (the source included) X-MsgDayCount: 3 X-BornDate: 1134853200 In-Reply-To: <20070221151554.GA22466@csclub.uwaterloo.ca> Reply-To: Mockern@yandex.ru References: <45DC5F34.000008.04893@tide.yandex.ru> <20070221151554.GA22466@csclub.uwaterloo.ca> X-Source-Ip: 84.185.219.101 Content-Type: text/plain; charset="KOI8-R" Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 21177 Lines: 689 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 >> Process 1432 detached >> root@andy:/home# >> >> ------------------------------------------------------------------------------------ >> >> /* >> * Tiny TTY driver >> * >> * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com) >> * >> * 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 >> #include >> #include >> #include >> #include >> #include >> #include >> #include >> #include >> #include >> #include >> #include >> >> >> #define DRIVER_VERSION "v2.0" >> #define DRIVER_AUTHOR "Greg Kroah-Hartman " >> #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 majordomo@vger.kernel.org >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 - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/