diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index f290fbe21d633ef2181989f7fe011e2f2e82b128..673aeda713883ee884fcda84dcb54abb5b0f632c 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -329,6 +329,7 @@ struct sc16is7xx_one { struct kthread_work reg_work; struct kthread_delayed_work ms_work; struct sc16is7xx_one_config config; + unsigned char buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */ bool irda_mode; unsigned int old_mctrl; }; @@ -341,7 +342,6 @@ struct sc16is7xx_port { unsigned long gpio_valid_mask; #endif u8 mctrl_mask; - unsigned char buf[SC16IS7XX_FIFO_SIZE]; struct kthread_worker kworker; struct task_struct *kworker_task; struct sc16is7xx_one p[]; @@ -378,17 +378,15 @@ static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val) regmap_write(one->regmap, reg, val); } -static void sc16is7xx_fifo_read(struct uart_port *port, unsigned int rxlen) +static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); - regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, s->buf, rxlen); + regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen); } -static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) +static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); /* @@ -398,7 +396,7 @@ static void sc16is7xx_fifo_write(struct uart_port *port, u8 to_send) if (unlikely(!to_send)) return; - regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, s->buf, to_send); + regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, txbuf, to_send); } static void sc16is7xx_port_update(struct uart_port *port, u8 reg, @@ -568,18 +566,18 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud) static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, unsigned int iir) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); + struct sc16is7xx_one *one = to_sc16is7xx_one(port, port); unsigned int lsr = 0, bytes_read, i; bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false; u8 ch, flag; - if (unlikely(rxlen >= sizeof(s->buf))) { + if (unlikely(rxlen >= sizeof(one->buf))) { dev_warn_ratelimited(port->dev, "ttySC%i: Possible RX FIFO overrun: %d\n", port->line, rxlen); port->icount.buf_overrun++; /* Ensure sanity of RX level */ - rxlen = sizeof(s->buf); + rxlen = sizeof(one->buf); } while (rxlen) { @@ -592,10 +590,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, lsr = 0; if (read_lsr) { - s->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); + one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG); bytes_read = 1; } else { - sc16is7xx_fifo_read(port, rxlen); + sc16is7xx_fifo_read(port, one->buf, rxlen); bytes_read = rxlen; } @@ -628,7 +626,7 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, } for (i = 0; i < bytes_read; ++i) { - ch = s->buf[i]; + ch = one->buf[i]; if (uart_handle_sysrq_char(port, ch)) continue; @@ -646,10 +644,10 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen, static void sc16is7xx_handle_tx(struct uart_port *port) { - struct sc16is7xx_port *s = dev_get_drvdata(port->dev); - struct circ_buf *xmit = &port->state->xmit; - unsigned int txlen, to_send, i; + struct tty_port *tport = &port->state->port; unsigned long flags; + unsigned int txlen; + unsigned char *tail; if (unlikely(port->x_char)) { sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char); @@ -658,40 +656,31 @@ static void sc16is7xx_handle_tx(struct uart_port *port) return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) { uart_port_lock_irqsave(port, &flags); sc16is7xx_stop_tx(port); uart_port_unlock_irqrestore(port, flags); return; } - /* Get length of data pending in circular buffer */ - to_send = uart_circ_chars_pending(xmit); - if (likely(to_send)) { - /* Limit to size of TX FIFO */ - txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); - if (txlen > SC16IS7XX_FIFO_SIZE) { - dev_err_ratelimited(port->dev, - "chip reports %d free bytes in TX fifo, but it only has %d", - txlen, SC16IS7XX_FIFO_SIZE); - txlen = 0; - } - to_send = (to_send > txlen) ? txlen : to_send; - - /* Convert to linear buffer */ - for (i = 0; i < to_send; ++i) { - s->buf[i] = xmit->buf[xmit->tail]; - uart_xmit_advance(port, 1); - } - - sc16is7xx_fifo_write(port, to_send); + /* Limit to space available in TX FIFO */ + txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG); + if (txlen > SC16IS7XX_FIFO_SIZE) { + dev_err_ratelimited(port->dev, + "chip reports %d free bytes in TX fifo, but it only has %d", + txlen, SC16IS7XX_FIFO_SIZE); + txlen = 0; } + txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen); + sc16is7xx_fifo_write(port, tail, txlen); + uart_xmit_advance(port, txlen); + uart_port_lock_irqsave(port, &flags); - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS) uart_write_wakeup(port); - if (uart_circ_empty(xmit)) + if (kfifo_is_empty(&tport->xmit_fifo)) sc16is7xx_stop_tx(port); else sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT); diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 0b35a41440ff13751277c3356b743eadb341d007..0f2e7effe17c644c65cc0c2c29ce8b5e7b5aaaf0 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -828,6 +828,63 @@ __kfifo_uint_must_check_helper( \ }) \ ) +/** + * kfifo_out_linear - gets a tail of/offset to available data + * @fifo: address of the fifo to be used + * @tail: pointer to an unsigned int to store the value of tail + * @n: max. number of elements to point at + * + * This macro obtains the offset (tail) to the available data in the fifo + * buffer and returns the + * numbers of elements available. It returns the available count till the end + * of data or till the end of the buffer. So that it can be used for linear + * data processing (like memcpy() of (@fifo->data + @tail) with count + * returned). + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out_linear(fifo, tail, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) __tmp = (fifo); \ + unsigned int *__tail = (tail); \ + unsigned long __n = (n); \ + const size_t __recsize = sizeof(*__tmp->rectype); \ + struct __kfifo *__kfifo = &__tmp->kfifo; \ + (__recsize) ? \ + __kfifo_out_linear_r(__kfifo, __tail, __n, __recsize) : \ + __kfifo_out_linear(__kfifo, __tail, __n); \ +}) \ +) + +/** + * kfifo_out_linear_ptr - gets a pointer to the available data + * @fifo: address of the fifo to be used + * @ptr: pointer to data to store the pointer to tail + * @n: max. number of elements to point at + * + * Similarly to kfifo_out_linear(), this macro obtains the pointer to the + * available data in the fifo buffer and returns the numbers of elements + * available. It returns the available count till the end of available data or + * till the end of the buffer. So that it can be used for linear data + * processing (like memcpy() of @ptr with count returned). + * + * Note that with only one concurrent reader and one concurrent + * writer, you don't need extra locking to use these macro. + */ +#define kfifo_out_linear_ptr(fifo, ptr, n) \ +__kfifo_uint_must_check_helper( \ +({ \ + typeof((fifo) + 1) ___tmp = (fifo); \ + unsigned int ___tail; \ + unsigned int ___n = kfifo_out_linear(___tmp, &___tail, (n)); \ + *(ptr) = ___tmp->kfifo.data + ___tail * kfifo_esize(___tmp); \ + ___n; \ +}) \ +) + + extern int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask); @@ -857,6 +914,9 @@ extern unsigned int __kfifo_dma_out_prepare(struct __kfifo *fifo, extern unsigned int __kfifo_out_peek(struct __kfifo *fifo, void *buf, unsigned int len); +extern unsigned int __kfifo_out_linear(struct __kfifo *fifo, + unsigned int *tail, unsigned int n); + extern unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, unsigned int len, size_t recsize); @@ -888,6 +948,9 @@ extern void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize); extern unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize); +extern unsigned int __kfifo_out_linear_r(struct __kfifo *fifo, + unsigned int *tail, unsigned int n, size_t recsize); + extern unsigned int __kfifo_max_r(unsigned int len, size_t recsize); #endif diff --git a/lib/kfifo.c b/lib/kfifo.c index 12f5a347aa137088a5364c3dcb0919a316fd7e88..abad82f6e135f76c1e9beee9edbb344498227e01 100644 --- a/lib/kfifo.c +++ b/lib/kfifo.c @@ -163,6 +163,19 @@ unsigned int __kfifo_out_peek(struct __kfifo *fifo, } EXPORT_SYMBOL(__kfifo_out_peek); +unsigned int __kfifo_out_linear(struct __kfifo *fifo, + unsigned int *tail, unsigned int n) +{ + unsigned int size = fifo->mask + 1; + unsigned int off = fifo->out & fifo->mask; + + if (tail) + *tail = off; + + return min3(n, fifo->in - fifo->out, size - off); +} +EXPORT_SYMBOL(__kfifo_out_linear); + unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len) { @@ -473,6 +486,19 @@ unsigned int __kfifo_out_peek_r(struct __kfifo *fifo, void *buf, } EXPORT_SYMBOL(__kfifo_out_peek_r); +unsigned int __kfifo_out_linear_r(struct __kfifo *fifo, + unsigned int *tail, unsigned int n, size_t recsize) +{ + if (fifo->in == fifo->out) + return 0; + + if (tail) + *tail = fifo->out + recsize; + + return min(n, __kfifo_peek_n(fifo, recsize)); +} +EXPORT_SYMBOL(__kfifo_out_linear_r); + unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize) {