mirror of https://gitee.com/openkylin/linux.git
greybus: uart: Use a fifo to send data to the modules
The firmware now buffers data instead of blocking while the transfer is sent, and the write operation cannot sleep. Instead of using gb_transfer_sync (which sleeps) in the write callback, buffer data in a fifo and send it from from a work queue. The write_room callback will will report 1 byte less that what is really available in the fifo, to leave space for extra characters that may be added by the tty layer. Signed-off-by: Axel Haslam <ahaslam@baylibre.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
219ffcf3a5
commit
8d6fbe9bf9
drivers/staging/greybus
|
@ -27,6 +27,8 @@
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kdev_t.h>
|
#include <linux/kdev_t.h>
|
||||||
|
#include <linux/kfifo.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include "greybus.h"
|
#include "greybus.h"
|
||||||
#include "gbphy.h"
|
#include "gbphy.h"
|
||||||
|
@ -34,6 +36,9 @@
|
||||||
#define GB_NUM_MINORS 16 /* 16 is is more than enough */
|
#define GB_NUM_MINORS 16 /* 16 is is more than enough */
|
||||||
#define GB_NAME "ttyGB"
|
#define GB_NAME "ttyGB"
|
||||||
|
|
||||||
|
#define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE
|
||||||
|
#define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */
|
||||||
|
|
||||||
struct gb_tty_line_coding {
|
struct gb_tty_line_coding {
|
||||||
__le32 rate;
|
__le32 rate;
|
||||||
__u8 format;
|
__u8 format;
|
||||||
|
@ -61,6 +66,9 @@ struct gb_tty {
|
||||||
u8 ctrlin; /* input control lines */
|
u8 ctrlin; /* input control lines */
|
||||||
u8 ctrlout; /* output control lines */
|
u8 ctrlout; /* output control lines */
|
||||||
struct gb_tty_line_coding line_coding;
|
struct gb_tty_line_coding line_coding;
|
||||||
|
struct work_struct tx_work;
|
||||||
|
struct kfifo write_fifo;
|
||||||
|
bool close_pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct tty_driver *gb_tty_driver;
|
static struct tty_driver *gb_tty_driver;
|
||||||
|
@ -166,25 +174,52 @@ static int gb_uart_request_handler(struct gb_operation *op)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_data(struct gb_tty *tty, u16 size, const u8 *data)
|
static void gb_uart_tx_write_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct gb_uart_send_data_request *request;
|
struct gb_uart_send_data_request *request;
|
||||||
|
struct gb_tty *gb_tty;
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned int send_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!data || !size)
|
gb_tty = container_of(work, struct gb_tty, tx_work);
|
||||||
return 0;
|
request = gb_tty->buffer;
|
||||||
|
|
||||||
if (size > tty->buffer_payload_max)
|
while (1) {
|
||||||
size = tty->buffer_payload_max;
|
if (gb_tty->close_pending)
|
||||||
request = tty->buffer;
|
break;
|
||||||
request->size = cpu_to_le16(size);
|
|
||||||
memcpy(&request->data[0], data, size);
|
spin_lock_irqsave(&gb_tty->write_lock, flags);
|
||||||
ret = gb_operation_sync(tty->connection, GB_UART_TYPE_SEND_DATA,
|
send_size = kfifo_out_peek(&gb_tty->write_fifo,
|
||||||
request, sizeof(*request) + size, NULL, 0);
|
&request->data[0],
|
||||||
if (ret)
|
gb_tty->buffer_payload_max);
|
||||||
return ret;
|
if (!send_size) {
|
||||||
else
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
return size;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
|
|
||||||
|
request->size = cpu_to_le16(send_size);
|
||||||
|
ret = gb_operation_sync(gb_tty->connection,
|
||||||
|
GB_UART_TYPE_SEND_DATA,
|
||||||
|
request, sizeof(*request) + send_size,
|
||||||
|
NULL, 0);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&gb_tty->gbphy_dev->dev,
|
||||||
|
"send data error: %d\n", ret);
|
||||||
|
if (!gb_tty->close_pending)
|
||||||
|
schedule_work(work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gb_tty->write_lock, flags);
|
||||||
|
ret = kfifo_out(&gb_tty->write_fifo, &request->data[0],
|
||||||
|
send_size);
|
||||||
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
|
|
||||||
|
tty_port_tty_wakeup(&gb_tty->port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_line_coding(struct gb_tty *tty)
|
static int send_line_coding(struct gb_tty *tty)
|
||||||
|
@ -318,19 +353,42 @@ static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf,
|
||||||
{
|
{
|
||||||
struct gb_tty *gb_tty = tty->driver_data;
|
struct gb_tty *gb_tty = tty->driver_data;
|
||||||
|
|
||||||
return send_data(gb_tty, count, buf);
|
count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count,
|
||||||
|
&gb_tty->write_lock);
|
||||||
|
if (count && !gb_tty->close_pending)
|
||||||
|
schedule_work(&gb_tty->tx_work);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gb_tty_write_room(struct tty_struct *tty)
|
static int gb_tty_write_room(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct gb_tty *gb_tty = tty->driver_data;
|
struct gb_tty *gb_tty = tty->driver_data;
|
||||||
|
unsigned long flags;
|
||||||
|
int room;
|
||||||
|
|
||||||
return gb_tty->buffer_payload_max;
|
spin_lock_irqsave(&gb_tty->write_lock, flags);
|
||||||
|
room = kfifo_avail(&gb_tty->write_fifo);
|
||||||
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
|
|
||||||
|
room -= GB_UART_WRITE_ROOM_MARGIN;
|
||||||
|
if (room < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gb_tty_chars_in_buffer(struct tty_struct *tty)
|
static int gb_tty_chars_in_buffer(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
return 0;
|
struct gb_tty *gb_tty = tty->driver_data;
|
||||||
|
unsigned long flags;
|
||||||
|
int chars;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gb_tty->write_lock, flags);
|
||||||
|
chars = kfifo_len(&gb_tty->write_fifo);
|
||||||
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
|
|
||||||
|
return chars;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gb_tty_break_ctl(struct tty_struct *tty, int state)
|
static int gb_tty_break_ctl(struct tty_struct *tty, int state)
|
||||||
|
@ -621,6 +679,24 @@ static void gb_tty_dtr_rts(struct tty_port *port, int on)
|
||||||
send_control(gb_tty, newctrl);
|
send_control(gb_tty, newctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gb_tty_port_shutdown(struct tty_port *port)
|
||||||
|
{
|
||||||
|
struct gb_tty *gb_tty;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
gb_tty = container_of(port, struct gb_tty, port);
|
||||||
|
|
||||||
|
gb_tty->close_pending = true;
|
||||||
|
|
||||||
|
cancel_work_sync(&gb_tty->tx_work);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&gb_tty->write_lock, flags);
|
||||||
|
kfifo_reset_out(&gb_tty->write_fifo);
|
||||||
|
spin_unlock_irqrestore(&gb_tty->write_lock, flags);
|
||||||
|
|
||||||
|
gb_tty->close_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct tty_operations gb_ops = {
|
static const struct tty_operations gb_ops = {
|
||||||
.install = gb_tty_install,
|
.install = gb_tty_install,
|
||||||
.open = gb_tty_open,
|
.open = gb_tty_open,
|
||||||
|
@ -641,6 +717,7 @@ static const struct tty_operations gb_ops = {
|
||||||
|
|
||||||
static struct tty_port_operations gb_port_ops = {
|
static struct tty_port_operations gb_port_ops = {
|
||||||
.dtr_rts = gb_tty_dtr_rts,
|
.dtr_rts = gb_tty_dtr_rts,
|
||||||
|
.shutdown = gb_tty_port_shutdown,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int gb_uart_probe(struct gbphy_device *gbphy_dev,
|
static int gb_uart_probe(struct gbphy_device *gbphy_dev,
|
||||||
|
@ -680,6 +757,13 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
|
||||||
goto exit_connection_destroy;
|
goto exit_connection_destroy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work);
|
||||||
|
|
||||||
|
retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (retval)
|
||||||
|
goto exit_buf_free;
|
||||||
|
|
||||||
minor = alloc_minor(gb_tty);
|
minor = alloc_minor(gb_tty);
|
||||||
if (minor < 0) {
|
if (minor < 0) {
|
||||||
if (minor == -ENOSPC) {
|
if (minor == -ENOSPC) {
|
||||||
|
@ -689,7 +773,7 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
|
||||||
} else {
|
} else {
|
||||||
retval = minor;
|
retval = minor;
|
||||||
}
|
}
|
||||||
goto exit_buf_free;
|
goto exit_kfifo_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_tty->minor = minor;
|
gb_tty->minor = minor;
|
||||||
|
@ -741,6 +825,8 @@ static int gb_uart_probe(struct gbphy_device *gbphy_dev,
|
||||||
gb_connection_disable(connection);
|
gb_connection_disable(connection);
|
||||||
exit_release_minor:
|
exit_release_minor:
|
||||||
release_minor(gb_tty);
|
release_minor(gb_tty);
|
||||||
|
exit_kfifo_free:
|
||||||
|
kfifo_free(&gb_tty->write_fifo);
|
||||||
exit_buf_free:
|
exit_buf_free:
|
||||||
kfree(gb_tty->buffer);
|
kfree(gb_tty->buffer);
|
||||||
exit_connection_destroy:
|
exit_connection_destroy:
|
||||||
|
@ -777,6 +863,7 @@ static void gb_uart_remove(struct gbphy_device *gbphy_dev)
|
||||||
gb_connection_disable(connection);
|
gb_connection_disable(connection);
|
||||||
tty_port_destroy(&gb_tty->port);
|
tty_port_destroy(&gb_tty->port);
|
||||||
gb_connection_destroy(connection);
|
gb_connection_destroy(connection);
|
||||||
|
kfifo_free(&gb_tty->write_fifo);
|
||||||
kfree(gb_tty->buffer);
|
kfree(gb_tty->buffer);
|
||||||
kfree(gb_tty);
|
kfree(gb_tty);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue