From 7a6065c264dbab78e1758685fd1eda498ab9b73d Mon Sep 17 00:00:00 2001 From: Jiri Svoboda Date: Wed, 22 Nov 2017 20:55:36 +0100 Subject: [PATCH] Convert ipc/char.h users to chardev. --- uspace/drv/char/msim-con/msim-con.c | 117 +++++++++------ uspace/drv/char/msim-con/msim-con.h | 12 ++ uspace/drv/char/ski-con/ski-con.c | 139 +++++++++++------- uspace/drv/char/ski-con/ski-con.h | 11 ++ uspace/drv/char/sun4v-con/sun4v-con.c | 110 ++++++-------- uspace/drv/char/sun4v-con/sun4v-con.h | 2 + uspace/lib/c/Makefile | 2 + uspace/lib/c/generic/adt/circ_buf.c | 113 ++++++++++++++ uspace/lib/c/include/adt/circ_buf.h | 65 ++++++++ uspace/lib/c/test/adt/circ_buf.c | 81 ++++++++++ uspace/lib/c/test/main.c | 1 + uspace/srv/hid/input/port/chardev.c | 78 +++++----- .../srv/hw/char/s3c24xx_uart/s3c24xx_uart.c | 126 ++++++++++------ .../srv/hw/char/s3c24xx_uart/s3c24xx_uart.h | 21 ++- 14 files changed, 617 insertions(+), 261 deletions(-) create mode 100644 uspace/lib/c/generic/adt/circ_buf.c create mode 100644 uspace/lib/c/include/adt/circ_buf.h create mode 100644 uspace/lib/c/test/adt/circ_buf.c diff --git a/uspace/drv/char/msim-con/msim-con.c b/uspace/drv/char/msim-con/msim-con.c index c90f2ed847..0bfb80f22c 100644 --- a/uspace/drv/char/msim-con/msim-con.c +++ b/uspace/drv/char/msim-con/msim-con.c @@ -36,12 +36,20 @@ #include #include #include -#include +#include #include "msim-con.h" static void msim_con_connection(ipc_callid_t, ipc_call_t *, void *); +static int msim_con_read(chardev_srv_t *, void *, size_t, size_t *); +static int msim_con_write(chardev_srv_t *, const void *, size_t, size_t *); + +static chardev_ops_t msim_con_chardev_ops = { + .read = msim_con_read, + .write = msim_con_write +}; + static irq_cmd_t msim_cmds_proto[] = { { .cmd = CMD_PIO_READ_8, @@ -57,14 +65,17 @@ static void msim_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *arg) { msim_con_t *con = (msim_con_t *) arg; uint8_t c; + int rc; + + fibril_mutex_lock(&con->buf_lock); c = IPC_GET_ARG2(*call); + rc = circ_buf_push(&con->cbuf, &c); + if (rc != EOK) + ddf_msg(LVL_ERROR, "Buffer overrun"); - if (con->client_sess != NULL) { - async_exch_t *exch = async_exchange_begin(con->client_sess); - async_msg_1(exch, CHAR_NOTIF_BYTE, c); - async_exchange_end(exch); - } + fibril_mutex_unlock(&con->buf_lock); + fibril_condvar_broadcast(&con->buf_cv); } /** Add msim console device. */ @@ -75,6 +86,10 @@ int msim_con_add(msim_con_t *con, msim_con_res_t *res) irq_cmd_t *msim_cmds = NULL; int rc; + circ_buf_init(&con->cbuf, con->buf, msim_con_buf_size, 1); + fibril_mutex_initialize(&con->buf_lock); + fibril_condvar_initialize(&con->buf_cv); + msim_cmds = malloc(sizeof(msim_cmds_proto)); if (msim_cmds == NULL) { rc = ENOMEM; @@ -106,6 +121,10 @@ int msim_con_add(msim_con_t *con, msim_con_res_t *res) async_irq_subscribe(res->irq, msim_irq_handler, con, &con->irq_code); subscribed = true; + chardev_srvs_init(&con->cds); + con->cds.ops = &msim_con_chardev_ops; + con->cds.sarg = con; + rc = ddf_fun_bind(fun); if (rc != EOK) { ddf_msg(LVL_ERROR, "Error binding function 'a'."); @@ -139,49 +158,57 @@ static void msim_con_putchar(msim_con_t *con, uint8_t ch) { } +/** Read from msim console device */ +static int msim_con_read(chardev_srv_t *srv, void *buf, size_t size, + size_t *nread) +{ + msim_con_t *con = (msim_con_t *) srv->srvs->sarg; + size_t p; + uint8_t *bp = (uint8_t *) buf; + int rc; + + fibril_mutex_lock(&con->buf_lock); + + while (circ_buf_nused(&con->cbuf) == 0) + fibril_condvar_wait(&con->buf_cv, &con->buf_lock); + + p = 0; + while (p < size) { + rc = circ_buf_pop(&con->cbuf, &bp[p]); + if (rc != EOK) + break; + ++p; + } + + fibril_mutex_unlock(&con->buf_lock); + + *nread = p; + return EOK; +} + +/** Write to msim console device */ +static int msim_con_write(chardev_srv_t *srv, const void *data, size_t size, + size_t *nwr) +{ + msim_con_t *con = (msim_con_t *) srv->srvs->sarg; + size_t i; + uint8_t *dp = (uint8_t *) data; + + for (i = 0; i < size; i++) + msim_con_putchar(con, dp[i]); + + *nwr = size; + return EOK; +} + /** Character device connection handler. */ static void msim_con_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { - msim_con_t *con; - - /* Answer the IPC_M_CONNECT_ME_TO call. */ - async_answer_0(iid, EOK); - - con = (msim_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg)); - - while (true) { - ipc_call_t call; - ipc_callid_t callid = async_get_call(&call); - sysarg_t method = IPC_GET_IMETHOD(call); - - if (!method) { - /* The other side has hung up. */ - async_answer_0(callid, EOK); - return; - } - - async_sess_t *sess = - async_callback_receive_start(EXCHANGE_SERIALIZE, &call); - if (sess != NULL) { - if (con->client_sess == NULL) { - con->client_sess = sess; - async_answer_0(callid, EOK); - } else - async_answer_0(callid, ELIMIT); - } else { - switch (method) { - case CHAR_WRITE_BYTE: - ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n", - IPC_GET_ARG1(call)); - msim_con_putchar(con, (uint8_t) IPC_GET_ARG1(call)); - async_answer_0(callid, EOK); - break; - default: - async_answer_0(callid, EINVAL); - } - } - } + msim_con_t *con = (msim_con_t *) ddf_dev_data_get( + ddf_fun_get_dev((ddf_fun_t *) arg)); + + chardev_conn(iid, icall, &con->cds); } /** @} diff --git a/uspace/drv/char/msim-con/msim-con.h b/uspace/drv/char/msim-con/msim-con.h index 73c58db67a..a86b70c03f 100644 --- a/uspace/drv/char/msim-con/msim-con.h +++ b/uspace/drv/char/msim-con/msim-con.h @@ -35,11 +35,18 @@ #ifndef MSIM_CON_H #define MSIM_CON_H +#include #include #include +#include +#include #include #include +enum { + msim_con_buf_size = 64 +}; + /** MSIM console resources */ typedef struct { uintptr_t base; @@ -50,9 +57,14 @@ typedef struct { typedef struct { async_sess_t *client_sess; ddf_dev_t *dev; + chardev_srvs_t cds; msim_con_res_t res; irq_pio_range_t irq_range[1]; irq_code_t irq_code; + circ_buf_t cbuf; + uint8_t buf[msim_con_buf_size]; + fibril_mutex_t buf_lock; + fibril_condvar_t buf_cv; } msim_con_t; extern int msim_con_add(msim_con_t *, msim_con_res_t *); diff --git a/uspace/drv/char/ski-con/ski-con.c b/uspace/drv/char/ski-con/ski-con.c index d9159b4db2..359c88f27d 100644 --- a/uspace/drv/char/ski-con/ski-con.c +++ b/uspace/drv/char/ski-con/ski-con.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2005 Jakub Jermar - * Copyright (c) 2011 Jiri Svoboda + * Copyright (c) 2017 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,10 +33,10 @@ #include #include #include -#include +#include +#include #include #include -#include #include #include "ski-con.h" @@ -45,18 +45,30 @@ #define POLL_INTERVAL 10000 -static void ski_con_thread_impl(void *arg); +static int ski_con_fibril(void *arg); static int32_t ski_con_getchar(void); static void ski_con_connection(ipc_callid_t, ipc_call_t *, void *); +static int ski_con_read(chardev_srv_t *, void *, size_t, size_t *); +static int ski_con_write(chardev_srv_t *, const void *, size_t, size_t *); + +static chardev_ops_t ski_con_chardev_ops = { + .read = ski_con_read, + .write = ski_con_write +}; + /** Add ski console device. */ int ski_con_add(ski_con_t *con) { - thread_id_t tid; + fid_t fid; ddf_fun_t *fun = NULL; bool bound = false; int rc; + circ_buf_init(&con->cbuf, con->buf, ski_con_buf_size, 1); + fibril_mutex_initialize(&con->buf_lock); + fibril_condvar_initialize(&con->buf_cv); + fun = ddf_fun_create(con->dev, fun_exposed, "a"); if (fun == NULL) { ddf_msg(LVL_ERROR, "Error creating function 'a'."); @@ -66,6 +78,10 @@ int ski_con_add(ski_con_t *con) ddf_fun_set_conn_handler(fun, ski_con_connection); + chardev_srvs_init(&con->cds); + con->cds.ops = &ski_con_chardev_ops; + con->cds.sarg = con; + rc = ddf_fun_bind(fun); if (rc != EOK) { ddf_msg(LVL_ERROR, "Error binding function 'a'."); @@ -74,11 +90,14 @@ int ski_con_add(ski_con_t *con) bound = true; - rc = thread_create(ski_con_thread_impl, con, "kbd_poll", &tid); - if (rc != 0) { - return rc; + fid = fibril_create(ski_con_fibril, con); + if (fid == 0) { + ddf_msg(LVL_ERROR, "Error creating fibril."); + rc = ENOMEM; + goto error; } + fibril_add_ready(fid); return EOK; error: if (bound) @@ -101,11 +120,12 @@ int ski_con_gone(ski_con_t *con) return ENOTSUP; } -/** Thread to poll Ski for keypresses. */ -static void ski_con_thread_impl(void *arg) +/** Poll Ski for keypresses. */ +static int ski_con_fibril(void *arg) { int32_t c; ski_con_t *con = (ski_con_t *) arg; + int rc; while (1) { while (1) { @@ -113,15 +133,20 @@ static void ski_con_thread_impl(void *arg) if (c == 0) break; - if (con->client_sess != NULL) { - async_exch_t *exch = async_exchange_begin(con->client_sess); - async_msg_1(exch, CHAR_NOTIF_BYTE, c); - async_exchange_end(exch); - } + fibril_mutex_lock(&con->buf_lock); + + rc = circ_buf_push(&con->cbuf, &c); + if (rc != EOK) + ddf_msg(LVL_ERROR, "Buffer overrun"); + + fibril_mutex_unlock(&con->buf_lock); + fibril_condvar_broadcast(&con->buf_cv); } - thread_usleep(POLL_INTERVAL); + fibril_usleep(POLL_INTERVAL); } + + return 0; } /** Ask Ski if a key was pressed. @@ -156,49 +181,57 @@ static void ski_con_putchar(ski_con_t *con, char ch) } -/** Character device connection handler. */ -static void ski_con_connection(ipc_callid_t iid, ipc_call_t *icall, - void *arg) +/** Read from Ski console device */ +static int ski_con_read(chardev_srv_t *srv, void *buf, size_t size, + size_t *nread) { - ski_con_t *con; + ski_con_t *con = (ski_con_t *) srv->srvs->sarg; + size_t p; + uint8_t *bp = (uint8_t *) buf; + int rc; - /* Answer the IPC_M_CONNECT_ME_TO call. */ - async_answer_0(iid, EOK); + fibril_mutex_lock(&con->buf_lock); - con = (ski_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg)); + while (circ_buf_nused(&con->cbuf) == 0) + fibril_condvar_wait(&con->buf_cv, &con->buf_lock); - while (true) { - ipc_call_t call; - ipc_callid_t callid = async_get_call(&call); - sysarg_t method = IPC_GET_IMETHOD(call); + p = 0; + while (p < size) { + rc = circ_buf_pop(&con->cbuf, &bp[p]); + if (rc != EOK) + break; + ++p; + } - if (!method) { - /* The other side has hung up. */ - async_answer_0(callid, EOK); - return; - } + fibril_mutex_unlock(&con->buf_lock); - async_sess_t *sess = - async_callback_receive_start(EXCHANGE_SERIALIZE, &call); - if (sess != NULL) { - if (con->client_sess == NULL) { - con->client_sess = sess; - async_answer_0(callid, EOK); - } else - async_answer_0(callid, ELIMIT); - } else { - switch (method) { - case CHAR_WRITE_BYTE: - ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n", - IPC_GET_ARG1(call)); - ski_con_putchar(con, (uint8_t) IPC_GET_ARG1(call)); - async_answer_0(callid, EOK); - break; - default: - async_answer_0(callid, EINVAL); - } - } - } + *nread = p; + return EOK; +} + +/** Write to Ski console device */ +static int ski_con_write(chardev_srv_t *srv, const void *data, size_t size, + size_t *nwr) +{ + ski_con_t *con = (ski_con_t *) srv->srvs->sarg; + size_t i; + uint8_t *dp = (uint8_t *) data; + + for (i = 0; i < size; i++) + ski_con_putchar(con, dp[i]); + + *nwr = size; + return EOK; +} + +/** Character device connection handler. */ +static void ski_con_connection(ipc_callid_t iid, ipc_call_t *icall, + void *arg) +{ + ski_con_t *con = (ski_con_t *) ddf_dev_data_get( + ddf_fun_get_dev((ddf_fun_t *) arg)); + + chardev_conn(iid, icall, &con->cds); } /** @} diff --git a/uspace/drv/char/ski-con/ski-con.h b/uspace/drv/char/ski-con/ski-con.h index 3475f9ca36..b4d1304e29 100644 --- a/uspace/drv/char/ski-con/ski-con.h +++ b/uspace/drv/char/ski-con/ski-con.h @@ -35,15 +35,26 @@ #ifndef SKI_CON_H #define SKI_CON_H +#include #include #include +#include #include #include +enum { + ski_con_buf_size = 64 +}; + /** Ski console */ typedef struct { async_sess_t *client_sess; ddf_dev_t *dev; + chardev_srvs_t cds; + circ_buf_t cbuf; + uint8_t buf[ski_con_buf_size]; + fibril_mutex_t buf_lock; + fibril_condvar_t buf_cv; } ski_con_t; extern int ski_con_add(ski_con_t *); diff --git a/uspace/drv/char/sun4v-con/sun4v-con.c b/uspace/drv/char/sun4v-con/sun4v-con.c index 824bd03a3f..326faecca0 100644 --- a/uspace/drv/char/sun4v-con/sun4v-con.c +++ b/uspace/drv/char/sun4v-con/sun4v-con.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2008 Pavel Rimsky - * Copyright (c) 2011 Jiri Svoboda + * Copyright (c) 2017 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include @@ -61,7 +61,13 @@ typedef volatile struct { /* virtual address of the shared buffer */ static input_buffer_t input_buffer; -static void sun4v_thread_impl(void *arg); +static int sun4v_con_read(chardev_srv_t *, void *, size_t, size_t *); +static int sun4v_con_write(chardev_srv_t *, const void *, size_t, size_t *); + +static chardev_ops_t sun4v_con_chardev_ops = { + .read = sun4v_con_read, + .write = sun4v_con_write +}; static void sun4v_con_putchar(sun4v_con_t *con, uint8_t data) { @@ -85,6 +91,10 @@ int sun4v_con_add(sun4v_con_t *con, sun4v_con_res_t *res) goto error; } + chardev_srvs_init(&con->cds); + con->cds.ops = &sun4v_con_chardev_ops; + con->cds.sarg = con; + ddf_fun_set_conn_handler(fun, sun4v_con_connection); rc = physmem_map(res->base, 1, AS_AREA_READ | AS_AREA_WRITE, @@ -94,11 +104,6 @@ int sun4v_con_add(sun4v_con_t *con, sun4v_con_res_t *res) goto error; } - thread_id_t tid; - rc = thread_create(sun4v_thread_impl, con, "kbd_poll", &tid); - if (rc != EOK) - goto error; - rc = ddf_fun_bind(fun); if (rc != EOK) { ddf_msg(LVL_ERROR, "Error binding function 'a'."); @@ -130,83 +135,52 @@ int sun4v_con_gone(sun4v_con_t *con) return ENOTSUP; } -/** - * Called regularly by the polling thread. Reads codes of all the - * pressed keys from the buffer. - */ -static void sun4v_key_pressed(sun4v_con_t *con) +/** Read from Sun4v console device */ +static int sun4v_con_read(chardev_srv_t *srv, void *buf, size_t size, + size_t *nread) { + size_t p; + uint8_t *bp = (uint8_t *) buf; char c; - while (input_buffer->read_ptr != input_buffer->write_ptr) { + while (input_buffer->read_ptr == input_buffer->write_ptr) + fibril_usleep(POLL_INTERVAL); + + p = 0; + while (p < size && input_buffer->read_ptr != input_buffer->write_ptr) { c = input_buffer->data[input_buffer->read_ptr]; input_buffer->read_ptr = ((input_buffer->read_ptr) + 1) % INPUT_BUFFER_SIZE; - if (con->client_sess != NULL) { - async_exch_t *exch = async_exchange_begin(con->client_sess); - async_msg_1(exch, CHAR_NOTIF_BYTE, c); - async_exchange_end(exch); - } - (void) c; + bp[p++] = c; } + + *nread = p; + return EOK; } -/** - * Thread to poll Sun4v console for keypresses. - */ -static void sun4v_thread_impl(void *arg) +/** Write to Sun4v console device */ +static int sun4v_con_write(chardev_srv_t *srv, const void *data, size_t size, + size_t *nwr) { - sun4v_con_t *con = (sun4v_con_t *) arg; + sun4v_con_t *con = (sun4v_con_t *) srv->srvs->sarg; + size_t i; + uint8_t *dp = (uint8_t *) data; - while (true) { - sun4v_key_pressed(con); - thread_usleep(POLL_INTERVAL); - } + for (i = 0; i < size; i++) + sun4v_con_putchar(con, dp[i]); + + *nwr = size; + return EOK; } /** Character device connection handler. */ static void sun4v_con_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { - sun4v_con_t *con; - - /* Answer the IPC_M_CONNECT_ME_TO call. */ - async_answer_0(iid, EOK); - - con = (sun4v_con_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg)); - - while (true) { - ipc_call_t call; - ipc_callid_t callid = async_get_call(&call); - sysarg_t method = IPC_GET_IMETHOD(call); - - if (!method) { - /* The other side has hung up. */ - async_answer_0(callid, EOK); - return; - } - - async_sess_t *sess = - async_callback_receive_start(EXCHANGE_SERIALIZE, &call); - if (sess != NULL) { - if (con->client_sess == NULL) { - con->client_sess = sess; - async_answer_0(callid, EOK); - } else - async_answer_0(callid, ELIMIT); - } else { - switch (method) { - case CHAR_WRITE_BYTE: - ddf_msg(LVL_DEBUG, "Write %" PRIun " to device\n", - IPC_GET_ARG1(call)); - sun4v_con_putchar(con, (uint8_t) IPC_GET_ARG1(call)); - async_answer_0(callid, EOK); - break; - default: - async_answer_0(callid, EINVAL); - } - } - } + sun4v_con_t *con = (sun4v_con_t *) ddf_dev_data_get( + ddf_fun_get_dev((ddf_fun_t *) arg)); + + chardev_conn(iid, icall, &con->cds); } /** @} diff --git a/uspace/drv/char/sun4v-con/sun4v-con.h b/uspace/drv/char/sun4v-con/sun4v-con.h index 9ca1c5780c..35c640a42a 100644 --- a/uspace/drv/char/sun4v-con/sun4v-con.h +++ b/uspace/drv/char/sun4v-con/sun4v-con.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -49,6 +50,7 @@ typedef struct { typedef struct { async_sess_t *client_sess; ddf_dev_t *dev; + chardev_srvs_t cds; sun4v_con_res_t res; } sun4v_con_t; diff --git a/uspace/lib/c/Makefile b/uspace/lib/c/Makefile index bafd9152c9..453e53044f 100644 --- a/uspace/lib/c/Makefile +++ b/uspace/lib/c/Makefile @@ -142,6 +142,7 @@ GENERIC_SOURCES = \ generic/loader.c \ generic/getopt.c \ generic/adt/checksum.c \ + generic/adt/circ_buf.c \ generic/adt/list.c \ generic/adt/hash_table.c \ generic/adt/odict.c \ @@ -180,6 +181,7 @@ SOURCES = \ $(ARCH_SOURCES) TEST_SOURCES = \ + test/adt/circ_buf.c \ test/fibril/timer.c \ test/main.c \ test/io/table.c \ diff --git a/uspace/lib/c/generic/adt/circ_buf.c b/uspace/lib/c/generic/adt/circ_buf.c new file mode 100644 index 0000000000..64a1a7b7b3 --- /dev/null +++ b/uspace/lib/c/generic/adt/circ_buf.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Jiri Svoboda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup libc + * @{ + */ + +/** @file Circular buffer + */ + +#include +#include +#include +#include + +/** Initialize circular buffer. + * + * @param cbuf Circular buffer + * @param buf Buffer for storing data + * @param nmemb Number of entries in @a buf + * @param size Size of individual buffer entry + */ +void circ_buf_init(circ_buf_t *cbuf, void *buf, size_t nmemb, size_t size) +{ + cbuf->buf = buf; + cbuf->nmemb = nmemb; + cbuf->size = size; + cbuf->rp = 0; + cbuf->wp = 0; + cbuf->nused = 0; +} + +/** Return number of free buffer entries. + * + * @param cbuf Circular buffer + * @return Number of free buffer entries + */ +size_t circ_buf_nfree(circ_buf_t *cbuf) +{ + return cbuf->nmemb - cbuf->nused; +} + +/** Return number of used buffer entries. + * + * @param cbuf Circular buffer + * @return Number of used buffer entries + */ +size_t circ_buf_nused(circ_buf_t *cbuf) +{ + return cbuf->nused; +} + +/** Push new entry into circular buffer. + * + * @param cbuf Circular buffer + * @param data Pointer to entry data + * @return EOK on success, EAGAIN if circular buffer is full + */ +int circ_buf_push(circ_buf_t *cbuf, const void *data) +{ + if (circ_buf_nfree(cbuf) == 0) + return EAGAIN; + + memcpy(cbuf->buf + cbuf->size * cbuf->wp, data, cbuf->size); + cbuf->wp = (cbuf->wp + 1) % cbuf->nmemb; + cbuf->nused++; + return EOK; +} + +/** Pop entry from circular buffer. + * + * @param cbuf Circular buffer + * @param datab Pointer to data buffer for storing entry + * @return EOK on success, EAGAIN if circular buffer is full + */ +int circ_buf_pop(circ_buf_t *cbuf, void *datab) +{ + if (cbuf->nused == 0) + return EAGAIN; + + memcpy(datab, cbuf->buf + cbuf->size * cbuf->rp, cbuf->size); + cbuf->rp = (cbuf->rp + 1) % cbuf->nmemb; + cbuf->nused--; + return EOK; +} + +/** @} + */ diff --git a/uspace/lib/c/include/adt/circ_buf.h b/uspace/lib/c/include/adt/circ_buf.h new file mode 100644 index 0000000000..5b2c7dd570 --- /dev/null +++ b/uspace/lib/c/include/adt/circ_buf.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Jiri Svoboda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** @addtogroup libc + * @{ + */ +/** @file Circular buffer + */ + +#ifndef LIBC_CIRC_BUF_H_ +#define LIBC_CIRC_BUF_H_ + +#include + +/** Circular buffer */ +typedef struct { + /** Buffer */ + void *buf; + /** Number of buffer members */ + size_t nmemb; + /** Member size */ + size_t size; + /** Read position */ + size_t rp; + /** Write position */ + size_t wp; + /** Number of used entries */ + size_t nused; +} circ_buf_t; + +extern void circ_buf_init(circ_buf_t *, void *, size_t, size_t); +extern size_t circ_buf_nfree(circ_buf_t *); +extern size_t circ_buf_nused(circ_buf_t *); +extern int circ_buf_push(circ_buf_t *, const void *); +extern int circ_buf_pop(circ_buf_t *, void *); + +#endif + +/** @} + */ diff --git a/uspace/lib/c/test/adt/circ_buf.c b/uspace/lib/c/test/adt/circ_buf.c new file mode 100644 index 0000000000..302d6b3386 --- /dev/null +++ b/uspace/lib/c/test/adt/circ_buf.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 Jiri Svoboda + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * - The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +PCUT_INIT + +PCUT_TEST_SUITE(circ_buf); + +enum { + buffer_size = 16 +}; + +static int buffer[buffer_size]; + +/** Basic insertion/deletion test. + * + * Test initialization, emptiness, pushing buffer until it is full, + * then emptying it again. + */ +PCUT_TEST(push_pop) +{ + circ_buf_t cbuf; + int i; + int j; + int rc; + + circ_buf_init(&cbuf, buffer, buffer_size, sizeof(int)); + + for (i = 0; i < buffer_size; i++) { + PCUT_ASSERT_INT_EQUALS(buffer_size - i, circ_buf_nfree(&cbuf)); + PCUT_ASSERT_INT_EQUALS(i, circ_buf_nused(&cbuf)); + rc = circ_buf_push(&cbuf, &i); + PCUT_ASSERT_ERRNO_VAL(EOK, rc); + } + + rc = circ_buf_push(&cbuf, &i); + PCUT_ASSERT_ERRNO_VAL(EAGAIN, rc); + + for (i = 0; i < buffer_size; i++) { + PCUT_ASSERT_INT_EQUALS(i, circ_buf_nfree(&cbuf)); + PCUT_ASSERT_INT_EQUALS(buffer_size - i, circ_buf_nused(&cbuf)); + rc = circ_buf_pop(&cbuf, &j); + PCUT_ASSERT_ERRNO_VAL(EOK, rc); + PCUT_ASSERT_INT_EQUALS(i, j); + } + + PCUT_ASSERT_INT_EQUALS(buffer_size, circ_buf_nfree(&cbuf)); + PCUT_ASSERT_INT_EQUALS(0, circ_buf_nused(&cbuf)); + + rc = circ_buf_pop(&cbuf, &j); + PCUT_ASSERT_ERRNO_VAL(EAGAIN, rc); +} + +PCUT_EXPORT(circ_buf); diff --git a/uspace/lib/c/test/main.c b/uspace/lib/c/test/main.c index 58a23fe5b3..4f32c86cfa 100644 --- a/uspace/lib/c/test/main.c +++ b/uspace/lib/c/test/main.c @@ -31,6 +31,7 @@ PCUT_INIT +PCUT_IMPORT(circ_buf); PCUT_IMPORT(fibril_timer); PCUT_IMPORT(odict); PCUT_IMPORT(qsort); diff --git a/uspace/srv/hid/input/port/chardev.c b/uspace/srv/hid/input/port/chardev.c index 5224d0e4f2..d695b35ca7 100644 --- a/uspace/srv/hid/input/port/chardev.c +++ b/uspace/srv/hid/input/port/chardev.c @@ -34,19 +34,20 @@ * @brief Chardev keyboard port driver. */ -#include #include -#include #include +#include +#include +#include #include #include "../input.h" #include "../kbd_port.h" #include "../kbd.h" -static void kbd_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg); +static int kbd_port_fibril(void *); static int chardev_port_init(kbd_dev_t *); -static void chardev_port_write(uint8_t data); +static void chardev_port_write(uint8_t); kbd_port_ops_t chardev_port = { .init = chardev_port_init, @@ -55,6 +56,7 @@ kbd_port_ops_t chardev_port = { static kbd_dev_t *kbd_dev; static async_sess_t *dev_sess; +static chardev_t *chardev; /** List of devices to try connecting to. */ static const char *in_devs[] = { @@ -69,8 +71,8 @@ static const unsigned int num_devs = sizeof(in_devs) / sizeof(in_devs[0]); static int chardev_port_init(kbd_dev_t *kdev) { service_id_t service_id; - async_exch_t *exch; unsigned int i; + fid_t fid; int rc; kbd_dev = kdev; @@ -95,24 +97,22 @@ static int chardev_port_init(kbd_dev_t *kdev) return ENOENT; } - exch = async_exchange_begin(dev_sess); - if (exch == NULL) { - printf("%s: Failed starting exchange with device\n", NAME); + rc = chardev_open(dev_sess, &chardev); + if (rc != EOK) { + printf("%s: Failed opening character device\n", NAME); async_hangup(dev_sess); return ENOMEM; } - port_id_t port; - rc = async_create_callback_port(exch, INTERFACE_CHAR_CB, 0, 0, - kbd_port_events, NULL, &port); - - async_exchange_end(exch); - - if (rc != 0) { - printf("%s: Failed to create callback from device\n", NAME); + fid = fibril_create(kbd_port_fibril, NULL); + if (fid == 0) { + printf("%s: Failed creating fibril\n", NAME); + chardev_close(chardev); async_hangup(dev_sess); - return -1; + return ENOMEM; } + + fibril_add_ready(fid); printf("%s: Found input device '%s'\n", NAME, in_devs[i]); return 0; @@ -120,42 +120,34 @@ static int chardev_port_init(kbd_dev_t *kdev) static void chardev_port_write(uint8_t data) { - async_exch_t *exch = async_exchange_begin(dev_sess); - if (exch == NULL) { - printf("%s: Failed starting exchange with device\n", NAME); + int rc; + size_t nwr; + + rc = chardev_write(chardev, &data, sizeof(data), &nwr); + if (rc != EOK || nwr != sizeof(data)) { + printf("%s: Failed writing to character device\n", NAME); return; } - - async_msg_1(exch, CHAR_WRITE_BYTE, data); - async_exchange_end(exch); } -static void kbd_port_events(ipc_callid_t iid, ipc_call_t *icall, void *arg) +static int kbd_port_fibril(void *arg) { - /* Ignore parameters, the connection is already opened */ - while (true) { + int rc; + size_t nread; + uint8_t b; - ipc_call_t call; - ipc_callid_t callid = async_get_call(&call); - - if (!IPC_GET_IMETHOD(call)) { - /* TODO: Handle hangup */ - return; + while (true) { + rc = chardev_read(chardev, &b, sizeof(b), &nread); + if (rc != EOK || nread != sizeof(b)) { + printf("%s: Error reading data", NAME); + continue; } - int retval = EOK; - - switch (IPC_GET_IMETHOD(call)) { - case CHAR_NOTIF_BYTE: - kbd_push_data(kbd_dev, IPC_GET_ARG1(call)); - break; - default: - retval = ENOENT; - } - async_answer_0(callid, retval); + kbd_push_data(kbd_dev, b); } -} + return 0; +} /** * @} diff --git a/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c b/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c index 8f236b168f..9a396df49d 100644 --- a/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c +++ b/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.c @@ -26,9 +26,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** @addtogroup driver_serial - * @{ - */ /** * @file * @brief Samsung S3C24xx on-chip UART driver. @@ -36,15 +33,15 @@ * This UART is present on the Samsung S3C24xx CPU (on the gta02 platform). */ +#include #include +#include +#include +#include #include -#include -#include #include #include #include -#include -#include #include "s3c24xx_uart.h" #define NAME "s3c24ser" @@ -71,11 +68,19 @@ static void s3c24xx_uart_irq_handler(ipc_callid_t, ipc_call_t *, void *); static int s3c24xx_uart_init(s3c24xx_uart_t *); static void s3c24xx_uart_sendb(s3c24xx_uart_t *, uint8_t); +static int s3c24xx_uart_read(chardev_srv_t *, void *, size_t, size_t *); +static int s3c24xx_uart_write(chardev_srv_t *, const void *, size_t, size_t *); + +static chardev_ops_t s3c24xx_uart_chardev_ops = { + .read = s3c24xx_uart_read, + .write = s3c24xx_uart_write +}; + int main(int argc, char *argv[]) { printf("%s: S3C24xx on-chip UART driver\n", NAME); - async_set_fallback_port_handler(s3c24xx_uart_connection, NULL); + async_set_fallback_port_handler(s3c24xx_uart_connection, uart); int rc = loc_server_register(NAME); if (rc != EOK) { printf("%s: Unable to register server.\n", NAME); @@ -110,46 +115,17 @@ int main(int argc, char *argv[]) static void s3c24xx_uart_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg) { - /* Answer the IPC_M_CONNECT_ME_TO call. */ - async_answer_0(iid, EOK); - - while (true) { - ipc_call_t call; - ipc_callid_t callid = async_get_call(&call); - sysarg_t method = IPC_GET_IMETHOD(call); - - if (!method) { - /* The other side has hung up. */ - async_answer_0(callid, EOK); - return; - } - - async_sess_t *sess = - async_callback_receive_start(EXCHANGE_SERIALIZE, &call); - if (sess != NULL) { - if (uart->client_sess == NULL) { - uart->client_sess = sess; - async_answer_0(callid, EOK); - } else - async_answer_0(callid, ELIMIT); - } else { - switch (method) { - case CHAR_WRITE_BYTE: - printf(NAME ": write %" PRIun " to device\n", - IPC_GET_ARG1(call)); - s3c24xx_uart_sendb(uart, (uint8_t) IPC_GET_ARG1(call)); - async_answer_0(callid, EOK); - break; - default: - async_answer_0(callid, EINVAL); - } - } - } + s3c24xx_uart_t *uart = (s3c24xx_uart_t *) arg; + + chardev_conn(iid, icall, &uart->cds); } + static void s3c24xx_uart_irq_handler(ipc_callid_t iid, ipc_call_t *call, void *arg) { + int rc; + (void) iid; (void) call; (void) arg; @@ -158,11 +134,14 @@ static void s3c24xx_uart_irq_handler(ipc_callid_t iid, ipc_call_t *call, uint32_t data = pio_read_32(&uart->io->urxh) & 0xff; uint32_t status = pio_read_32(&uart->io->uerstat); - if (uart->client_sess != NULL) { - async_exch_t *exch = async_exchange_begin(uart->client_sess); - async_msg_1(exch, CHAR_NOTIF_BYTE, data); - async_exchange_end(exch); - } + fibril_mutex_lock(&uart->buf_lock); + + rc = circ_buf_push(&uart->cbuf, &data); + if (rc != EOK) + printf(NAME ": Buffer overrun\n"); + + fibril_mutex_unlock(&uart->buf_lock); + fibril_condvar_broadcast(&uart->buf_cv); if (status != 0) printf(NAME ": Error status 0x%x\n", status); @@ -175,6 +154,10 @@ static int s3c24xx_uart_init(s3c24xx_uart_t *uart) void *vaddr; sysarg_t inr; + circ_buf_init(&uart->cbuf, uart->buf, s3c24xx_uart_buf_size, 1); + fibril_mutex_initialize(&uart->buf_lock); + fibril_condvar_initialize(&uart->buf_cv); + if (sysinfo_get_value("s3c24xx_uart.address.physical", &uart->paddr) != EOK) return -1; @@ -187,7 +170,6 @@ static int s3c24xx_uart_init(s3c24xx_uart_t *uart) return -1; uart->io = vaddr; - uart->client_sess = NULL; printf(NAME ": device at physical address %p, inr %" PRIun ".\n", (void *) uart->paddr, inr); @@ -202,6 +184,10 @@ static int s3c24xx_uart_init(s3c24xx_uart_t *uart) pio_write_32(&uart->io->ucon, pio_read_32(&uart->io->ucon) & ~UCON_RX_INT_LEVEL); + chardev_srvs_init(&uart->cds); + uart->cds.ops = &s3c24xx_uart_chardev_ops; + uart->cds.sarg = uart; + return EOK; } @@ -215,5 +201,47 @@ static void s3c24xx_uart_sendb(s3c24xx_uart_t *uart, uint8_t byte) pio_write_32(&uart->io->utxh, byte); } +static int s3c24xx_uart_read(chardev_srv_t *srv, void *buf, size_t size, + size_t *nread) +{ + s3c24xx_uart_t *uart = (s3c24xx_uart_t *) srv->srvs->sarg; + size_t p; + uint8_t *bp = (uint8_t *) buf; + int rc; + + fibril_mutex_lock(&uart->buf_lock); + + while (circ_buf_nused(&uart->cbuf) == 0) + fibril_condvar_wait(&uart->buf_cv, &uart->buf_lock); + + p = 0; + while (p < size) { + rc = circ_buf_pop(&uart->cbuf, &bp[p]); + if (rc != EOK) + break; + ++p; + } + + fibril_mutex_unlock(&uart->buf_lock); + + *nread = p; + return EOK; +} + +static int s3c24xx_uart_write(chardev_srv_t *srv, const void *data, size_t size, + size_t *nwr) +{ + s3c24xx_uart_t *uart = (s3c24xx_uart_t *) srv->srvs->sarg; + size_t i; + uint8_t *dp = (uint8_t *) data; + + for (i = 0; i < size; i++) + s3c24xx_uart_sendb(uart, dp[i]); + + *nwr = size; + return EOK; +} + + /** @} */ diff --git a/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h b/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h index b88768010f..82328b200e 100644 --- a/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h +++ b/uspace/srv/hw/char/s3c24xx_uart/s3c24xx_uart.h @@ -37,8 +37,11 @@ #ifndef S3C24XX_UART_H_ #define S3C24XX_UART_H_ -#include +#include #include +#include +#include +#include /** S3C24xx UART I/O */ typedef struct { @@ -75,6 +78,9 @@ typedef struct { #define UFCON_RX_FIFO_TLEVEL_1B 0x00 #define UFCON_FIFO_ENABLE 0x01 +enum { + s3c24xx_uart_buf_size = 64 +}; /** S3C24xx UART instance */ typedef struct { @@ -84,11 +90,20 @@ typedef struct { /** Device I/O structure */ s3c24xx_uart_io_t *io; - /** Callback session to the client */ - async_sess_t *client_sess; + /** Character device service */ + chardev_srvs_t cds; /** Service ID */ service_id_t service_id; + + /** Circular buffer */ + circ_buf_t cbuf; + /** Buffer */ + uint8_t buf[s3c24xx_uart_buf_size]; + /** Buffer lock */ + fibril_mutex_t buf_lock; + /** Signal newly added data in buffer */ + fibril_condvar_t buf_cv; } s3c24xx_uart_t; #endif