drivers/ipcc: add Inter Processor Communication driver

This patch adds upper half driver for IPCC character driver. IPCC
is a Inter Processor Communication Controller.

Driver is still immature and not thoroughly tested, thus
CONFIG_EXPERIMENTAL is required.

Signed-off-by: Michał Łyszczek <michal.lyszczek@bofc.pl>
This commit is contained in:
Michał Łyszczek 2022-06-28 15:45:41 +02:00 committed by Xiang Xiao
parent 666c2d255a
commit a9f78a64db
13 changed files with 1493 additions and 0 deletions

View File

@ -14,6 +14,7 @@ source "drivers/clk/Kconfig"
source "drivers/i2c/Kconfig"
source "drivers/spi/Kconfig"
source "drivers/i2s/Kconfig"
source "drivers/ipcc/Kconfig"
source "drivers/timers/Kconfig"
source "drivers/analog/Kconfig"
source "drivers/audio/Kconfig"

View File

@ -36,6 +36,7 @@ include math/Make.defs
include motor/Make.defs
include i2c/Make.defs
include i2s/Make.defs
include ipcc/Make.defs
include input/Make.defs
include ioexpander/Make.defs
include lcd/Make.defs

33
drivers/ipcc/Kconfig Normal file
View File

@ -0,0 +1,33 @@
menuconfig IPCC
bool "IPCC (Inter Processor Communication Controller) driver"
select MM_CIRCBUF
depends on EXPERIMENTAL
default n
---help---
IPCC driver. Generic driver to communicate between two
processors via character device.
comment "IPCC driver requires CONFIG_EXPERIMENTAL"
depends on !EXPERIMENTAL
if IPCC
config IPCC_BUFFERED
bool "Enable buffering in driver"
default y
---help---
If enabled, data read and written to/from IPCC will be
buffered in driver. This will reduce thread waiting for
read and write to complete during busy period, but it
will also increase RAM usage.
Buffer size for tx and rx can be separately defined for
each channel during driver registration.
config IPCC_NPOLLWAITERS
int "Number of poll threads"
default 4
---help---
Maximum number of threads that can be waiting for POLL events.
endif # IPCC

41
drivers/ipcc/Make.defs Normal file
View File

@ -0,0 +1,41 @@
############################################################################
# drivers/ipcc/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifeq ($(CONFIG_IPCC),y)
# Include ipcc driver
CSRCS += ipcc_poll.c
CSRCS += ipcc_read.c
CSRCS += ipcc_write.c
CSRCS += ipcc_register.c
CSRCS += ipcc_open.c
CSRCS += ipcc_close.c
ifneq ($(CONFIG_DISABLE_PSEUDOFS_OPERATIONS),y)
CSRCS += ipcc_unlink.c
endif
# Include pipe build support
DEPPATH += --dep-path ipcc
VPATH += :ipcc
endif # $(CONFIG_IPCC),y

94
drivers/ipcc/ipcc_close.c Normal file
View File

@ -0,0 +1,94 @@
/****************************************************************************
* drivers/ipcc/ipcc_close.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_close
*
* Description:
* Closes the driver device. If this is last reference and file has been
* unlinked, we will also free resources allocated by ipcc_register()
*
* Input Parameters:
* filep - pointer to a file structure to close.
*
* Returned Value:
* OK on successfull close, or negated errno on failure.
*
* Assumptions/Limitations:
*
****************************************************************************/
int ipcc_close(FAR struct file *filep)
{
FAR struct ipcc_driver_s *priv;
int ret;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
priv = filep->f_inode->i_private;
/* Get exclusive access to the IPCC driver state structure */
if ((ret = nxsem_wait(&priv->exclsem)) < 0)
{
return ret;
}
/* Decrement the count of open references on the driver */
DEBUGASSERT(priv->crefs > 0);
priv->crefs--;
if (priv->crefs <= 0 && priv->unlinked)
{
/* If count ref is zero and file has been unlinked, it
* means nobody uses the driver and seems like nobody
* wants to use it anymore, so free up resources.
*/
ipcc_cleanup(priv);
return OK;
}
nxsem_post(&priv->exclsem);
return OK;
}

80
drivers/ipcc/ipcc_open.c Normal file
View File

@ -0,0 +1,80 @@
/****************************************************************************
* drivers/ipcc/ipcc_open.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_open
*
* Description:
* Opens driver for use by userspace applications.
*
* Input Parameters:
* filep - pointer to a file structure to open.
*
* Returned Value:
* OK on successfull open, or negated errno on failure.
*
* Assumptions/Limitations:
*
****************************************************************************/
int ipcc_open(FAR struct file *filep)
{
FAR struct ipcc_driver_s *priv;
int ret;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
priv = filep->f_inode->i_private;
/* Get exclusive access to the IPCC driver state structure */
if ((ret = nxsem_wait(&priv->exclsem)) < 0)
{
return ret;
}
/* Increment the count of open references on the driver */
priv->crefs++;
DEBUGASSERT(priv->crefs > 0);
nxsem_post(&priv->exclsem);
return OK;
}

203
drivers/ipcc/ipcc_poll.c Normal file
View File

@ -0,0 +1,203 @@
/****************************************************************************
* drivers/ipcc/ipcc_poll.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <assert.h>
#include <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_poll
*
* Description:
* Sets up or tears down poll for the driver associated with filep pointer
*
* Input Parameters:
* filep - file associated with the driver instance
* fds - The structure describing the events to be monitored, OR NULL if
* this is a request to stop monitoring events.
* setup - true: Setup up the poll; false: Teardown the poll
*
* Returned Value:
* 0: Success; Negated errno on failure
*
* Assumptions/Limitations:
*
****************************************************************************/
int ipcc_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup)
{
FAR struct ipcc_driver_s *priv;
FAR struct pollfd **slot;
pollevent_t eventset;
int ret;
int i;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
priv = filep->f_inode->i_private;
/* Get exclusive access to driver */
if ((ret = nxsem_wait(&priv->exclsem)) < 0)
{
/* nxsem_wait() will return on signal, we did not start
* any transfer yet, so we can safely return with error
*/
return ret;
}
/* Are we setting up the poll? Or tearing it down? */
if (!setup)
{
/* We are tearing down the poll */
slot = (FAR struct pollfd **)fds->priv;
/* remove all memory of the poll setup */
*slot = NULL;
fds->priv = NULL;
nxsem_post(&priv->exclsem);
return OK;
}
/* This is a request to set up the poll. Find an available slot
* for the poll structure reference
*/
for (i = 0; i < CONFIG_IPCC_NPOLLWAITERS; i++)
{
/* Find an available slot */
if (!priv->fds[i])
{
/* Bind the poll structure and this slot */
priv->fds[i] = fds;
fds->priv = &priv->fds[i];
break;
}
}
if (i >= CONFIG_IPCC_NPOLLWAITERS)
{
/* No free poll slot found */
fds->priv = NULL;
nxsem_post(&priv->exclsem);
return -EBUSY;
}
/* Should immediately notify on any of the requested events? */
eventset = 0;
if (circbuf_used(&priv->ipcc->rxbuf) > 0)
{
eventset |= (fds->events & POLLIN);
}
if (circbuf_space(&priv->ipcc->txbuf) > 0)
{
eventset |= (fds->events & POLLOUT);
}
if (eventset)
{
ipcc_pollnotify(priv, eventset);
}
nxsem_post(&priv->exclsem);
return OK;
}
/****************************************************************************
* Name: ipcc_pollnotify
*
* Description:
* Wakes up all sleeping threads waiting for event associated with ipcc
* driver.
*
* Input Parameters:
* ipcc - driver instance to check for poll events
* eventset - list of events to check for activity
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* This function may be called from interrupt handler.
*
****************************************************************************/
void ipcc_pollnotify(FAR struct ipcc_driver_s *priv, pollevent_t eventset)
{
struct pollfd *fds;
int semcount;
int i;
for (i = 0; i < CONFIG_IPCC_NPOLLWAITERS; i++)
{
if ((fds = priv->fds[i]) == NULL)
{
/* Slot empty, move to next one */
continue;
}
if ((fds->revents |= fds->events & eventset) == 0)
{
/* No event */
continue;
}
finfo("Report events: %08" PRIx32 "\n", fds->revents);
nxsem_get_value(fds->sem, &semcount);
if (semcount < 1)
{
nxsem_post(fds->sem);
}
}
}

63
drivers/ipcc/ipcc_priv.h Normal file
View File

@ -0,0 +1,63 @@
/****************************************************************************
* drivers/ipcc/ipcc_priv.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <stdio.h>
/****************************************************************************
* Public Types
****************************************************************************/
/* Driver state structure */
struct ipcc_driver_s
{
FAR struct pollfd *fds[CONFIG_IPCC_NPOLLWAITERS];
FAR struct ipcc_lower_s *ipcc; /* Lower half driver state */
int crefs; /* Count number of open references */
int unlinked; /* 1 - driver has been unlinked */
sem_t exclsem; /* Mutual exclusion for driver */
sem_t rxsem; /* Data ready to read semaphore */
sem_t txsem; /* Data ready to send semaphore */
};
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
int ipcc_open(FAR struct file *filep);
int ipcc_close(FAR struct file *filep);
int ipcc_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
ssize_t ipcc_read(FAR struct file *filep, FAR char *buffer, size_t buflen);
ssize_t ipcc_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
int ipcc_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
int ipcc_unlink(FAR struct inode *inode);
#endif
void ipcc_pollnotify(FAR struct ipcc_driver_s *ipcc, pollevent_t eventset);
void ipcc_cleanup(FAR struct ipcc_driver_s *priv);

232
drivers/ipcc/ipcc_read.c Normal file
View File

@ -0,0 +1,232 @@
/****************************************************************************
* drivers/ipcc/ipcc_read.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_rxfree_notify
*
* Description:
* Notifies all blocked threads or those waiting in poll/select that
* there is data on buffer to perform reading.
*
* Input Parameters:
* ipcc - pointer to driver instance
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* This function can be called from interrupt handler from lower half.
*
****************************************************************************/
void ipcc_rxfree_notify(FAR struct ipcc_driver_s *priv)
{
int semval;
if (priv == NULL)
{
/* priv can be NULL when ipcc lower half is initialized but
* upper half has not yet been initialized, and rx interrupt
* has been received. In such case we don't wake any reader,
* because since ipcc is not yet initialized there cannot be
* any readers yet. We can safely return here, first read()
* to this ipcc channel will immediately read data.
*/
return;
}
if ((nxsem_get_value(&priv->rxsem, &semval) == 0) && semval > 0)
{
/* Notify all poll/select waiters that they can read from the driver.
* Do it only when there are no already blocked readers to avoid
* situation where thread that is polling gets notified only to
* be blocked in read() because another thread have read from
* buffer before polling thread could.
*/
ipcc_pollnotify(priv, POLLIN);
return;
}
/* Notify all blocked readers that data is available to read */
do
{
nxsem_post(&priv->rxsem);
}
while (nxsem_get_value(&priv->rxsem, &semval) == 0 && semval <= 0);
}
/****************************************************************************
* Name: ipcc_read
*
* Description:
* Reads data from the IPCC lower driver. Will block if no data is
* available, unless O_NONBLOCK flag is set.
*
* Input Parameters:
* filep - file on vfs associated with the driver
* buffer - buffer where read data should be copied to
* buflen - size of the buffer... buffer
*
* Returned Value:
* Will return number of bytes read or negated errno
*
* Assumptions/Limitations:
*
****************************************************************************/
ssize_t ipcc_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct ipcc_driver_s *priv;
ssize_t nread;
int ret;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
priv = filep->f_inode->i_private;
/* Get exclusive access to driver */
if ((ret = nxsem_wait(&priv->exclsem)))
{
/* nxsem_wait() will return on signal, we did not start
* any transfer yet, so we can safely return with error
*/
return ret;
}
for (; ; )
{
#ifdef CONFIG_IPCC_BUFFERED
/* Data is buffered in interrupt handler, so we simply
* have to return buffers content to the user
*/
if ((nread = circbuf_read(&priv->ipcc->rxbuf, buffer, buflen)) > 0)
{
/* got some data */
if (priv->ipcc->overflow)
{
/* We tried to buffer data in previous interrupt, but
* it failed due to rxbuf being full. Now that we took
* some data from buffer, we can try to buffer data again
*/
priv->ipcc->ops.buffer_data(priv->ipcc, &priv->ipcc->rxbuf);
if ((size_t)nread < buflen)
{
/* There is still some space left on buffer, and
* we just added new data to buffer, get more data
* for the user
*/
nread += circbuf_read(&priv->ipcc->rxbuf, buffer + nread,
buflen - nread);
}
}
/* return number of bytes read to the caller */
nxsem_post(&priv->exclsem);
return nread;
}
#else /* CONFIG_IPCC_BUFFERED */
/* Unbuffered read, read data directly from lower driver */
if ((nread = priv->ipcc->ops.read(&priv->ipcc, buffer, buflen)) != 0)
{
/* Got some data, return number of bytes read to the caller
* --or--
* read() returned error in which case return errno value
*/
nxsem_post(&priv->exclsem);
return nread;
}
#endif /* CONFIG_IPCC_BUFFERED */
/* no data on the buffer, should we block? */
if (filep->f_oflags & O_NONBLOCK)
{
/* No, we should not block, so inform caller that
* no data could be read
*/
nxsem_post(&priv->exclsem);
return -EAGAIN;
}
/* We are in blocking mode, so we have to wait for data to arrive */
nxsem_post(&priv->exclsem);
if ((ret = nxsem_wait(&priv->rxsem)))
{
/* We were interrupted by signal, but we have not written
* any data to caller's buffer, so we return with error
*/
return ret;
}
/* Data should now be available, but it's possible that
* another thread will read all data from buffer before
* we can do it, so we will stay in the loop until we
* manage to read something - or interrupt signal occurs
*
* We have released exclusive lock to driver when we were
* waiting for data, so now let's retake it.
*/
nxsem_wait(&priv->exclsem);
continue;
}
}

View File

@ -0,0 +1,189 @@
/****************************************************************************
* drivers/ipcc/ipcc_register.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mm/circbuf.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Device naming ************************************************************/
#define DEVNAME_FMT "/dev/ipcc%d"
#define DEVNAME_FMTLEN (9 + 3 + 1)
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations ipcc_fops =
{
#ifdef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
.unlink = NULL,
#else /* CONFIG_DISABLE_PSEUDOFS_OPERATIONS */
.unlink = ipcc_unlink,
#endif /* CONFIG_DISABLE_PSEUDOFS_OPERATIONS */
.open = ipcc_open,
.close = ipcc_close,
.poll = ipcc_poll,
.read = ipcc_read,
.write = ipcc_write,
.ioctl = NULL,
.seek = NULL
};
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_cleanup
*
* Description:
* Cleans up resources allocated by ipcc_register()
*
* Input Parameters:
* priv - ipcc driver instance to clean up
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* This function should be called only when ipcc_register is run with
* success and all resources in driver instance are properly allocated.
*
****************************************************************************/
void ipcc_cleanup(FAR struct ipcc_driver_s *priv)
{
#ifdef CONFIG_IPCC_BUFFERED
circbuf_uninit(&priv->ipcc->rxbuf);
circbuf_uninit(&priv->ipcc->txbuf);
#endif
nxsem_destroy(&priv->rxsem);
nxsem_destroy(&priv->txsem);
priv->ipcc->ops.cleanup(priv->ipcc);
nxsem_destroy(&priv->exclsem);
kmm_free(priv);
}
/****************************************************************************
* Name: ipcc_register
*
* Description:
* Create and register the IPCC character driver.
*
* IPCC is a simple character driver that supports inter processor
* communication.
*
* Input Parameters:
* ipcc - An instance of the lower half IPCC driver
* chan - IPCC channel. This will be used ad IPCC minor number.
* IPPC will be registered as /dev/ipccN where N is the minor number.
* buflen - Length of tx and rx buffers, 0 for unbuffered communication.
*
* Returned Value:
* OK if the driver was successfully registered, or negated errno on
* failure.
*
* Assumptions/Limitations:
* ipcc is already allocated and initialized by architecture code.
*
****************************************************************************/
#ifdef CONFIG_IPCC_BUFFERED
int ipcc_register(FAR struct ipcc_lower_s *ipcc, size_t rxbuflen,
size_t txbuflen)
#else
int ipcc_register(FAR struct ipcc_lower_s *ipcc)
#endif
{
FAR struct ipcc_driver_s *priv;
char devname[DEVNAME_FMTLEN];
int ret;
/* Allocate a IPCC character device structure */
if ((priv = kmm_zalloc(sizeof(*priv))) == NULL)
{
return -ENOMEM;
}
/* Link upper and lower driver together */
priv->ipcc = ipcc;
ipcc->upper = priv;
#ifdef CONFIG_IPCC_BUFFERED
/* allocate buffers for reading and writing data to IPCC memory */
if (rxbuflen)
{
if ((ret = circbuf_init(&priv->ipcc->rxbuf, NULL, rxbuflen)))
{
goto error;
}
}
if (txbuflen)
{
if ((ret = circbuf_init(&priv->ipcc->txbuf, NULL, txbuflen)))
{
goto error;
}
}
#endif /* CONFIG_IPCC_BUFFERED */
/* Create the character device name */
snprintf(devname, DEVNAME_FMTLEN, DEVNAME_FMT, ipcc->chan);
if ((ret = register_driver(devname, &ipcc_fops, 0666, priv)))
{
goto error;
}
/* nxsem_init can't really fail us if we provide it with valid params */
nxsem_init(&priv->exclsem, 0, 1);
nxsem_init(&priv->rxsem, 0, 0);
nxsem_init(&priv->txsem, 0, 1);
return OK;
error:
ipcc_cleanup(priv);
return ret;
}

View File

@ -0,0 +1,96 @@
/****************************************************************************
* drivers/ipcc/ipcc_unlink.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_unlink
*
* Description:
* Action to take upon file unlinking. Function will free resources if
* noone is using the driver when unlinking occured. If driver is still
* in use, it will be marked as unlinked and resource freeing will take
* place in ipcc_close() function instead, once last reference is closed.
*
* Input Parameters:
* inode - driver inode that is being unlinked
*
* Returned Value:
* OK on successfull close, or negated errno on failure.
*
* Assumptions/Limitations:
*
****************************************************************************/
int ipcc_unlink(FAR struct inode *inode)
{
FAR struct ipcc_driver_s *priv;
int ret;
/* Get our private data structure */
DEBUGASSERT(inode);
priv = inode->i_private;
/* Get exclusive access to the IPCC driver state structure */
if ((ret = nxsem_wait(&priv->exclsem)) < 0)
{
return ret;
}
/* Is anyone still using the driver? */
if (priv->crefs <= 0)
{
/* No, we are free to free resources */
ipcc_cleanup(priv);
return OK;
}
/* Yes, someone is still using the driver, just mark file
* as unlinked and free resources in ipcc_close() once last
* reference is closed
*/
priv->unlinked = true;
nxsem_post(&priv->exclsem);
return OK;
}

256
drivers/ipcc/ipcc_write.c Normal file
View File

@ -0,0 +1,256 @@
/****************************************************************************
* drivers/ipcc/ipcc_write.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/ipcc.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include "ipcc_priv.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipcc_txfree_notify
*
* Description:
* Notifies all blocked threads or those waiting in poll/select that
* there is place on buffer to perform writing.
*
* Input Parameters:
* ipcc - pointer to driver instance
*
* Returned Value:
* None
*
* Assumptions/Limitations:
* This function can be called from interrupt handler from lower half.
*
****************************************************************************/
void ipcc_txfree_notify(FAR struct ipcc_driver_s *priv)
{
int semval;
if (priv == NULL)
{
/* priv can be NULL when ipcc lower half is initialized but
* upper half has not yet been initialized, and tx interrupt
* has been received. In such case we don't wake any writers,
* because since ipcc is not yet initialized there cannot be
* any writers yet. We can safely return here, first write()
* to this ipcc channel will immediately write data.
*/
return;
}
if ((nxsem_get_value(&priv->txsem, &semval) == 0) && semval > 0)
{
/* Notify all poll/select waiters that they can write to the driver.
* Do it only when there are no already blocked writers to avoid
* situation where thread that is polling gets notified only to
* be blocked in write() because another thread have written to
* buffer before polling thread could.
*/
ipcc_pollnotify(priv, POLLOUT);
return;
}
/* Notify all blocked writers that data is available to write */
do
{
nxsem_post(&priv->txsem);
}
while (nxsem_get_value(&priv->txsem, &semval) == 0 && semval <= 0);
}
/****************************************************************************
* Name: ipcc_write
*
* Description:
* Writes data to IPCC memory so that another CPU can read the contents.
* Will block untill whole buffer is copied unless signal is received
* or O_NONBLOCK flag is set.
*
* Input Parameters:
* filep - file on vfs associated with the driver
* buffer - buffer to copy to IPCC memory
* buflen - size of the buffer... buffer
*
* Returned Value:
* Number of successfully written bytes into the IPCC memory or netagted
* errno when no data could be written.
*
* Assumptions/Limitations:
*
****************************************************************************/
ssize_t ipcc_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct ipcc_driver_s *priv;
ssize_t nwritten;
int ret;
/* Get our private data structure */
DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
priv = filep->f_inode->i_private;
/* Get exclusive access to driver */
if ((ret = nxsem_wait(&priv->exclsem)))
{
/* nxsem_wait() will return on signal, we did not start
* any transfer yet, so we can safely return with error
*/
return ret;
}
for (nwritten = 0; ; )
{
#ifdef CONFIG_IPCC_BUFFERED
/* Buffered write, if buffer is empty try to write directly to
* IPCC memory, else buffer data in circbuf - it will be written
* in interrupt handler when IPCC tx channel is free.
*/
if (circbuf_used(&priv->ipcc->txbuf) == 0)
{
/* Write buffer is empty, we can try and write data directly
* to IPCC memory thus omitting copying to buffer.
*/
nwritten += priv->ipcc->ops.write(priv->ipcc, buffer + nwritten,
buflen - nwritten);
if (nwritten == (ssize_t)buflen || nwritten < 0)
{
/* We've managed to write whole buffer to IPCC memory,
* there is nothing else for use to do
* --or--
* lower half driver returned error during write,
*
* either way we return with nwritten which will either
* be number of bytes written or negated errno.
*/
nxsem_post(&priv->exclsem);
return nwritten;
}
}
/* Either, there is already some data on the txbuffer, which
* means IPCC is occupied, or txbuffer is empty, but we could
* not write whole buffer to IPCC memory. In either case we
* copy what is left in data to buffer.
*/
nwritten += circbuf_write(&priv->ipcc->txbuf, buffer + nwritten,
(ssize_t)buflen - nwritten);
/* Notify lower half that new data on circ buffer is available */
priv->ipcc->ops.write_notify(priv->ipcc);
if (nwritten == (ssize_t)buflen)
{
/* All outstanding data has been copied to txbuffer, we're done */
nxsem_post(&priv->exclsem);
return nwritten;
}
#else /* CONFIG_IPCC_BUFFERED */
/* Unbuffered write, write data directly to lower driver */
nwritten += priv->ipcc->ops.write(&priv->ipcc, buffer, buflen);
if (nwritten == (ssize_t)buflen)
{
/* We've managed to write whole buffer to IPCC memory,
* there is nothing else for use to do
* --or--
* lower half driver returned error during write,
*
* either way we return with nwritten which will either
* be number of bytes written or negated errno.
*/
nxsem_post(&priv->exclsem);
return nwritten;
}
#endif /* CONFIG_IPCC_BUFFERED */
/* No space left on buffer, should we block? */
if (filep->f_oflags & O_NONBLOCK)
{
/* No, we should not block, return number of bytes written or
* -EAGAIN when we did not write anything
*/
nxsem_post(&priv->exclsem);
return nwritten ? nwritten : -EAGAIN;
}
/* We are in blocking mode, so we have to wait for space
* to write data
*/
nxsem_post(&priv->exclsem);
if ((ret = nxsem_wait(&priv->txsem)))
{
/* We were interrupted by signal, return error or number
* of bytes written
*/
return nwritten ? nwritten : -EINTR;
}
/* Space should now be available, but it's possible that
* another thread will write data to txbuffer before
* we can do it, so we will stay in the loop until we
* manage to write whole buffer - or interrupt signal occurs.
*
* We have released exclusive lock to driver when we were
* waiting for data, so now let's retake it.
*/
nxsem_wait(&priv->exclsem);
continue;
}
}

204
include/nuttx/ipcc.h Normal file
View File

@ -0,0 +1,204 @@
/****************************************************************************
* include/nuttx/ipcc.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_IPCC_H
#define __INCLUDE_NUTTX_IPCC_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/mm/circbuf.h>
#include <nuttx/semaphore.h>
#include <poll.h>
/****************************************************************************
* Public Types
****************************************************************************/
/* IPCC is a two-part driver:
*
* 1) A common upper half driver, that provides the character device
* interface for the user
* 2) Platform-specific lower half drivers that provide interface
* between upper and lower layer. Lower half also contains
* common objects like semaphore to inform
*/
/* This structure defines all of the operations provided by the
* architecture specific logic. All fields must be provided with
* non-NULL function pointers.
*/
struct ipcc_lower_s;
struct ipcc_ops_s
{
/**************************************************************************
* Name: read
*
* Description:
* Reads data directly from IPCC mbox, function shall not block.
* If no data is available, 0 shall be returned. Partial read
* is possible. In that case function can return value less
* than buflen.
*
* Input Parameters:
* ipcc - ipcc channel to read data from
* buffer - location where data shall be stored
* buflen - size of the buffer
*
* Returned Value:
* Number of bytes actually written to buffer.
*
**************************************************************************/
CODE ssize_t (*read)(FAR struct ipcc_lower_s *ipcc,
FAR char *buffer, size_t buflen);
/**************************************************************************
* Name: write
*
* Description:
* Writes data directly to IPCC mbox, function shall not block.
* If IPCC memory is full, or busy or otherwise unavailable for
* write, 0 shall be returned. Partial write is possible. In
* that case function can return value less then buflen.
*
* Input Parameters:
* ipcc - ipcc channel to write data to
* buffer - location to read data from
* buflen - number of bytes to write from buffer
*
* Returned Value:
* Number of actually written data to IPCC channel.
*
**************************************************************************/
CODE ssize_t (*write)(FAR struct ipcc_lower_s *ipcc,
FAR const char *buffer, size_t buflen);
/**************************************************************************
* Name: buffer_data
*
* Description:
* Lower driver shall copy data from IPCC to cirbuf on demand.
* It does not have to copy all data (in case rxbuf is full), but
* it must keep internal pointer to track uncopied data, and copy
* what is left on the next call to this function.
*
* Input Parameters:
* ipcc - channel to buffer data
* rxbuf - circural buffer where data should be written to
*
* Returned Value:
* Number of bytes that were successfully written to rxbuf.
*
**************************************************************************/
CODE ssize_t (*buffer_data)(FAR struct ipcc_lower_s *ipcc,
FAR struct circbuf_s *rxbuf);
/**************************************************************************
* Name: write_notify
*
* Description:
* Upper half driver notifies lower half driver that new data has been
* copied to circ buffer. This can be used by lower half to transfer
* data from buffer to IPCC memory.
*
* Input Parameters:
* ipcc - ipcc channel that wrote data.
*
* Returned Value:
* 0 on success, or negated errno otherwise
*
**************************************************************************/
CODE ssize_t (*write_notify)(FAR struct ipcc_lower_s *ipcc);
/**************************************************************************
* Name: cleanup
*
* Description:
* Cleans up resources initialized by <arch>_ipcc_init(). If arch code
* malloc()ed any memory, this funciton is responsible of freeing it.
* After this function is called, ipcc driver will not access lower
* half pointer anymore.
*
* Input Parameters:
* ipcc - ipcc channel to cleanup
*
* Returned Value:
* Always OK
*
**************************************************************************/
CODE int (*cleanup)(FAR struct ipcc_lower_s *ipcc);
};
/* This structure defines the interface between upper and lower halves.
* Such instance is passed to ipcc_register() function after driver
* has been initialized, binding the upper and lower halves into one
* driver.
*/
struct ipcc_lower_s
{
/* Pointer to upper half part of the driver. This should be used only
* when calling ipcc_* upper half functions from lower half code.
* There should be no need to access upper half driver's fields from
* lower half context.
*/
FAR struct ipcc_driver_s *upper;
struct ipcc_ops_s ops; /* Arch specific functions */
int chan; /* IPCC channel */
#ifdef CONFIG_IPCC_BUFFERED
struct circbuf_s rxbuf; /* Receive buffer */
struct circbuf_s txbuf; /* Transmit buffer */
/* 1 - not all data could fit into buffer, some unread data is still
* in IPCC mbox. State of this variable is maintained by lower half,
* upper half only reads it to know when to inform lower driver that
* buffer got free.
*/
int overflow;
#endif /* CONFIG_IPCC_BUFFERED */
};
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
void ipcc_txfree_notify(FAR struct ipcc_driver_s *priv);
void ipcc_rxfree_notify(FAR struct ipcc_driver_s *priv);
#ifdef CONFIG_IPCC_BUFFERED
int ipcc_register(FAR struct ipcc_lower_s *ipcc, size_t rxbuflen,
size_t txbuflen);
#else
int ipcc_register(FAR struct ipcc_lower_s *ipcc);
#endif
#endif /* __INCLUDE_NUTTX_IPCC_H */