X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmmc%2Fcard%2Fsdio_uart.c;h=36a8d53ad2a2ba90cf7b5d7dd3fffb647545a1d1;hb=2c5510d4e84988ea95f86488d1e23244284bc1ed;hp=e4d9e85ff5846e599b40bbd6ab8738614056be38;hpb=6e418a9d26ab4fd44b3e07dc1158027cbdf0a919;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index e4d9e85..36a8d53 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -79,6 +80,7 @@ struct sdio_uart_port { struct mutex open_lock; struct sdio_func *func; struct mutex func_lock; + struct task_struct *in_sdio_uart_irq; unsigned int regs_offset; struct circ_buf xmit; spinlock_t write_lock; @@ -186,14 +188,16 @@ static int sdio_uart_claim_func(struct sdio_uart_port *port) mutex_unlock(&port->func_lock); return -ENODEV; } - sdio_claim_host(port->func); + if (likely(port->in_sdio_uart_irq != current)) + sdio_claim_host(port->func); mutex_unlock(&port->func_lock); return 0; } static inline void sdio_uart_release_func(struct sdio_uart_port *port) { - sdio_release_host(port->func); + if (likely(port->in_sdio_uart_irq != current)) + sdio_release_host(port->func); } static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) @@ -383,7 +387,7 @@ static void sdio_uart_stop_rx(struct sdio_uart_port *port) sdio_out(port, UART_IER, port->ier); } -static void sdio_uart_receive_chars(struct sdio_uart_port *port, int *status) +static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status) { struct tty_struct *tty = port->tty; unsigned int ch, flag; @@ -511,15 +515,29 @@ static void sdio_uart_irq(struct sdio_func *func) struct sdio_uart_port *port = sdio_get_drvdata(func); unsigned int iir, lsr; + /* + * In a few places sdio_uart_irq() is called directly instead of + * waiting for the actual interrupt to be raised and the SDIO IRQ + * thread scheduled in order to reduce latency. However, some + * interaction with the tty core may end up calling us back + * (serial echo, flow control, etc.) through those same places + * causing undesirable effects. Let's stop the recursion here. + */ + if (unlikely(port->in_sdio_uart_irq == current)) + return; + iir = sdio_in(port, UART_IIR); if (iir & UART_IIR_NO_INT) return; + + port->in_sdio_uart_irq = current; lsr = sdio_in(port, UART_LSR); if (lsr & UART_LSR_DR) sdio_uart_receive_chars(port, &lsr); sdio_uart_check_modem_status(port); if (lsr & UART_LSR_THRE) sdio_uart_transmit_chars(port); + port->in_sdio_uart_irq = NULL; } static int sdio_uart_startup(struct sdio_uart_port *port) @@ -868,12 +886,14 @@ static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_t sdio_uart_release_func(port); } -static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state) +static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state) { struct sdio_uart_port *port = tty->driver_data; + int result; - if (sdio_uart_claim_func(port) != 0) - return; + result = sdio_uart_claim_func(port); + if (result != 0) + return result; if (break_state == -1) port->lcr |= UART_LCR_SBC; @@ -882,6 +902,7 @@ static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state) sdio_out(port, UART_LCR, port->lcr); sdio_uart_release_func(port); + return 0; } static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file) @@ -913,6 +934,64 @@ static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file, return result; } +static int sdio_uart_proc_show(struct seq_file *m, void *v) +{ + int i; + + seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", + "", "", ""); + for (i = 0; i < UART_NR; i++) { + struct sdio_uart_port *port = sdio_uart_port_get(i); + if (port) { + seq_printf(m, "%d: uart:SDIO", i); + if(capable(CAP_SYS_ADMIN)) { + seq_printf(m, " tx:%d rx:%d", + port->icount.tx, port->icount.rx); + if (port->icount.frame) + seq_printf(m, " fe:%d", + port->icount.frame); + if (port->icount.parity) + seq_printf(m, " pe:%d", + port->icount.parity); + if (port->icount.brk) + seq_printf(m, " brk:%d", + port->icount.brk); + if (port->icount.overrun) + seq_printf(m, " oe:%d", + port->icount.overrun); + if (port->icount.cts) + seq_printf(m, " cts:%d", + port->icount.cts); + if (port->icount.dsr) + seq_printf(m, " dsr:%d", + port->icount.dsr); + if (port->icount.rng) + seq_printf(m, " rng:%d", + port->icount.rng); + if (port->icount.dcd) + seq_printf(m, " dcd:%d", + port->icount.dcd); + } + sdio_uart_port_put(port); + seq_putc(m, '\n'); + } + } + return 0; +} + +static int sdio_uart_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, sdio_uart_proc_show, NULL); +} + +static const struct file_operations sdio_uart_proc_fops = { + .owner = THIS_MODULE, + .open = sdio_uart_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static const struct tty_operations sdio_uart_ops = { .open = sdio_uart_open, .close = sdio_uart_close, @@ -926,6 +1005,7 @@ static const struct tty_operations sdio_uart_ops = { .break_ctl = sdio_uart_break_ctl, .tiocmget = sdio_uart_tiocmget, .tiocmset = sdio_uart_tiocmset, + .proc_fops = &sdio_uart_proc_fops, }; static struct tty_driver *sdio_uart_tty_driver; @@ -1044,6 +1124,8 @@ static int __init sdio_uart_init(void) tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_drv->init_termios = tty_std_termios; tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL; + tty_drv->init_termios.c_ispeed = 4800; + tty_drv->init_termios.c_ospeed = 4800; tty_set_operations(tty_drv, &sdio_uart_ops); ret = tty_register_driver(tty_drv);