serial: add zero-copy dma i/o api
This commit is contained in:
parent
c59bd3f7c6
commit
dca3e716ca
3 changed files with 47 additions and 21 deletions
|
@ -84,31 +84,42 @@ void arch_serial_exit(struct serial_device *dev)
|
|||
|
||||
ssize_t arch_serial_write(struct serial_device *dev, const void *buf, size_t len)
|
||||
{
|
||||
struct dmabuf *dmabuf = NULL;
|
||||
int ret;
|
||||
struct dmabuf *dmabuf = dmabuf_create(&dev->device, len);
|
||||
if (dmabuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(dmabuf->data, buf, len);
|
||||
ret = serial_write_dma(dev, dmabuf);
|
||||
dmabuf_put(dmabuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t serial_write_dma(struct serial_device *dev, struct dmabuf *buf)
|
||||
{
|
||||
uint16_t len;
|
||||
struct arch_serial_device *arch_dev = to_arch_serial_device(dev);
|
||||
|
||||
dmabuf_get(buf);
|
||||
|
||||
if (arch_dev->tx_next != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
if (len >= (1 << 16)) /* DMA uses 16-bit counters */
|
||||
if (buf->len >= 0xffff)
|
||||
len = 0xffff;
|
||||
|
||||
dmabuf = dmabuf_create(&dev->device, len);
|
||||
if (dmabuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&dmabuf->data[0], buf, len);
|
||||
else
|
||||
len = (uint16_t)buf->len;
|
||||
|
||||
if (arch_dev->tx_current == NULL) {
|
||||
arch_dev->tx_current = dmabuf;
|
||||
REG_UART_PDC_TPR = (uint32_t)&dmabuf->data[0];
|
||||
REG_UART_PDC_TCR = (uint16_t)dmabuf->len;
|
||||
arch_dev->tx_current = buf;
|
||||
REG_UART_PDC_TPR = (uint32_t)buf->data;
|
||||
REG_UART_PDC_TCR = len;
|
||||
/* we weren't transmitting, so the interrupt was masked */
|
||||
REG_UART_IER = REG_UART_IER_ENDTX_MASK;
|
||||
} else {
|
||||
arch_dev->tx_next = dmabuf;
|
||||
REG_UART_PDC_TNPR = (uint32_t)&dmabuf->data[0];
|
||||
REG_UART_PDC_TNCR = (uint16_t)dmabuf->len;
|
||||
arch_dev->tx_next = buf;
|
||||
REG_UART_PDC_TNPR = (uint32_t)buf->data;
|
||||
REG_UART_PDC_TNCR = len;
|
||||
}
|
||||
|
||||
return (ssize_t)len;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <arch/arch_include.h>
|
||||
|
||||
#include <ardix/dma.h>
|
||||
#include <ardix/serial.h>
|
||||
|
||||
int arch_serial_init(struct serial_device *dev);
|
||||
|
@ -21,6 +22,18 @@ void arch_serial_exit(struct serial_device *dev);
|
|||
*/
|
||||
ssize_t arch_serial_write(struct serial_device *dev, const void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Directly enqueue a DMA buffer to a serial device, resulting in a zero-copy
|
||||
* write. This will increment the buffer's refcount and decrement it again when
|
||||
* it has been written out completely, so the caller is responsible for calling
|
||||
* `dmabuf_put` as well in order to prevent a memory leak.
|
||||
*
|
||||
* @param dev: serial device to write to
|
||||
* @param buf: raw DMA buffer to append
|
||||
* @returns actual bytes that will be written, or a negative error code
|
||||
*/
|
||||
ssize_t serial_write_dma(struct serial_device *dev, struct dmabuf *buf);
|
||||
|
||||
#include ARCH_INCLUDE(serial.h)
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
/* See the end of this file for copyright, license, and warranty information. */
|
||||
|
||||
#include <arch/serial.h>
|
||||
|
||||
#include <ardix/dma.h>
|
||||
#include <ardix/malloc.h>
|
||||
#include <ardix/serial.h>
|
||||
#include <ardix/syscall.h>
|
||||
|
@ -13,20 +16,19 @@
|
|||
ssize_t sys_write(int fd, __user const void *buf, size_t len)
|
||||
{
|
||||
ssize_t ret;
|
||||
void *copy;
|
||||
struct dmabuf *dma;
|
||||
|
||||
if (fd != 1) /* we only support stdout (serial console) right now */
|
||||
return -EBADF;
|
||||
|
||||
copy = malloc(len);
|
||||
if (copy == NULL)
|
||||
dma = dmabuf_create(&serial_default_device->device, len);
|
||||
if (dma == NULL)
|
||||
return -ENOMEM;
|
||||
ret = (ssize_t)copy_from_user(copy, buf, len);
|
||||
|
||||
copy_from_user(dma->data, buf, len);
|
||||
/* TODO: reschedule if blocking */
|
||||
ret = serial_write(serial_default_device, copy, (size_t)ret);
|
||||
|
||||
free(copy);
|
||||
ret = serial_write_dma(serial_default_device, dma);
|
||||
dmabuf_put(dma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue