nuttx/drivers/input/mouse_upper.c
Xiang Xiao eddd90de78 poll: pollsetup should notify only one fd passd by caller
since it's redundant to iterate the whole fds array in setup

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2023-11-21 09:07:17 +01:00

365 lines
9.9 KiB
C

/****************************************************************************
* drivers/input/mouse_upper.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 <debug.h>
#include <fcntl.h>
#include <poll.h>
#include <nuttx/input/mouse.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mutex.h>
#include <nuttx/list.h>
#include <nuttx/mm/circbuf.h>
/****************************************************************************
* Private Types
****************************************************************************/
struct mouse_openpriv_s
{
struct circbuf_s circbuf; /* Store mouse point data in circle buffer */
struct list_node node; /* Opened file buffer linked list node */
FAR struct pollfd *fds; /* Polling structure of waiting thread */
sem_t waitsem; /* Used to wait for the availability of data */
mutex_t lock; /* Manages exclusive access to this structure */
};
/* This structure is for mouse upper half driver */
struct mouse_upperhalf_s
{
uint8_t nums; /* Number of mouse point structure */
mutex_t lock; /* Manages exclusive access to this structure */
struct list_node head; /* Opened file buffer chain header node */
FAR struct mouse_lowerhalf_s *lower; /* A pointer of lower half instance */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int mouse_open(FAR struct file *filep);
static int mouse_close(FAR struct file *filep);
static ssize_t mouse_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static int mouse_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_mouse_fops =
{
mouse_open, /* open */
mouse_close, /* close */
mouse_read, /* read */
NULL, /* write */
NULL, /* seek */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* truncate */
mouse_poll /* poll */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mouse_open
****************************************************************************/
static int mouse_open(FAR struct file *filep)
{
FAR struct mouse_openpriv_s *openpriv;
FAR struct inode *inode = filep->f_inode;
FAR struct mouse_upperhalf_s *upper = inode->i_private;
int ret;
ret = nxmutex_lock(&upper->lock);
if (ret < 0)
{
return ret;
}
openpriv = kmm_zalloc(sizeof(struct mouse_openpriv_s));
if (openpriv == NULL)
{
nxmutex_unlock(&upper->lock);
return -ENOMEM;
}
ret = circbuf_init(&openpriv->circbuf, NULL,
upper->nums * sizeof(struct mouse_report_s));
if (ret < 0)
{
kmm_free(openpriv);
nxmutex_unlock(&upper->lock);
return ret;
}
nxsem_init(&openpriv->waitsem, 0, 0);
nxmutex_init(&openpriv->lock);
list_add_tail(&upper->head, &openpriv->node);
/* Save the buffer node pointer so that it can be used directly
* in the read operation.
*/
filep->f_priv = openpriv;
nxmutex_unlock(&upper->lock);
return ret;
}
/****************************************************************************
* Name: mouse_close
****************************************************************************/
static int mouse_close(FAR struct file *filep)
{
FAR struct mouse_openpriv_s *openpriv = filep->f_priv;
FAR struct inode *inode = filep->f_inode;
FAR struct mouse_upperhalf_s *upper = inode->i_private;
int ret;
ret = nxmutex_lock(&upper->lock);
if (ret < 0)
{
return ret;
}
list_delete(&openpriv->node);
circbuf_uninit(&openpriv->circbuf);
nxsem_destroy(&openpriv->waitsem);
nxmutex_destroy(&openpriv->lock);
kmm_free(openpriv);
nxmutex_unlock(&upper->lock);
return ret;
}
/****************************************************************************
* Name: mouse_read
****************************************************************************/
static ssize_t mouse_read(FAR struct file *filep, FAR char *buffer,
size_t len)
{
FAR struct mouse_openpriv_s *openpriv = filep->f_priv;
ssize_t ret;
if (!buffer || !len)
{
return -EINVAL;
}
ret = nxmutex_lock(&openpriv->lock);
if (ret < 0)
{
return ret;
}
while (circbuf_is_empty(&openpriv->circbuf))
{
if (filep->f_oflags & O_NONBLOCK)
{
ret = -EAGAIN;
goto out;
}
else
{
nxmutex_unlock(&openpriv->lock);
ret = nxsem_wait_uninterruptible(&openpriv->waitsem);
if (ret < 0)
{
return ret;
}
ret = nxmutex_lock(&openpriv->lock);
if (ret < 0)
{
return ret;
}
}
}
ret = circbuf_read(&openpriv->circbuf, buffer, len);
out:
nxmutex_unlock(&openpriv->lock);
return ret;
}
/****************************************************************************
* Name: mouse_poll
****************************************************************************/
static int mouse_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
FAR struct mouse_openpriv_s *openpriv = filep->f_priv;
pollevent_t eventset = 0;
int ret;
ret = nxmutex_lock(&openpriv->lock);
if (ret < 0)
{
return ret;
}
if (setup)
{
if (openpriv->fds == NULL)
{
openpriv->fds = fds;
fds->priv = &openpriv->fds;
}
else
{
ret = -EBUSY;
goto errout;
}
if (!circbuf_is_empty(&openpriv->circbuf))
{
eventset |= POLLIN;
}
poll_notify(&fds, 1, eventset);
}
else if (fds->priv)
{
openpriv->fds = NULL;
fds->priv = NULL;
}
errout:
nxmutex_unlock(&openpriv->lock);
return ret;
}
/****************************************************************************
* Public Function
****************************************************************************/
/****************************************************************************
* Name: mouse_event
****************************************************************************/
void mouse_event(FAR void *priv, FAR const struct mouse_report_s *sample)
{
FAR struct mouse_upperhalf_s *upper = priv;
FAR struct mouse_openpriv_s *openpriv;
int semcount;
if (nxmutex_lock(&upper->lock) < 0)
{
return;
}
list_for_every_entry(&upper->head, openpriv, struct mouse_openpriv_s, node)
{
circbuf_overwrite(&openpriv->circbuf, sample,
sizeof(struct mouse_report_s));
nxsem_get_value(&openpriv->waitsem, &semcount);
if (semcount < 1)
{
nxsem_post(&openpriv->waitsem);
}
if (openpriv->fds && openpriv->fds->fd >= 0)
{
poll_notify(&openpriv->fds, 1, POLLIN);
}
}
nxmutex_unlock(&upper->lock);
}
/****************************************************************************
* Name: mouse_register
****************************************************************************/
int mouse_register(FAR struct mouse_lowerhalf_s *lower,
FAR const char *path, uint8_t nums)
{
FAR struct mouse_upperhalf_s *upper;
int ret;
iinfo("Registering %s\n", path);
if (lower == NULL || nums == 0)
{
ierr("ERROR: invalid mouse device\n");
return -EINVAL;
}
upper = kmm_zalloc(sizeof(struct mouse_upperhalf_s));
if (!upper)
{
ierr("ERROR: Failed to mem alloc!\n");
return -ENOMEM;
}
lower->priv = upper;
upper->lower = lower;
upper->nums = nums;
list_initialize(&upper->head);
nxmutex_init(&upper->lock);
ret = register_driver(path, &g_mouse_fops, 0666, upper);
if (ret < 0)
{
nxmutex_destroy(&upper->lock);
kmm_free(upper);
return ret;
}
return ret;
}
/****************************************************************************
* Name: mouse_unregister
****************************************************************************/
void mouse_unregister(FAR struct mouse_lowerhalf_s *lower,
FAR const char *path)
{
FAR struct mouse_upperhalf_s *upper;
DEBUGASSERT(lower != NULL);
DEBUGASSERT(lower->priv != NULL);
upper = lower->priv;
iinfo("UnRegistering %s\n", path);
unregister_driver(path);
nxmutex_destroy(&upper->lock);
kmm_free(upper);
}