/**************************************************************************** * drivers/input/touchscreen_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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Private Types ****************************************************************************/ struct touch_openpriv_s { struct circbuf_s circbuf; /* Store touch 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 touchscreen upper half driver */ struct touch_upperhalf_s { uint8_t nums; /* Number of touch point structure */ mutex_t lock; /* Manages exclusive access to this structure */ struct list_node head; /* Opened file buffer chain header node */ FAR struct touch_lowerhalf_s *lower; /* A pointer of lower half instance */ FAR struct touch_openpriv_s *grab; /* A pointer of grab file */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int touch_open(FAR struct file *filep); static int touch_close(FAR struct file *filep); static ssize_t touch_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t touch_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int touch_ioctl(FAR struct file *filep, int cmd, unsigned long arg); static int touch_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); static void touch_event_notify(FAR struct touch_openpriv_s *openpriv, FAR const struct touch_sample_s *sample); /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_touch_fops = { touch_open, /* open */ touch_close, /* close */ touch_read, /* read */ touch_write, /* write */ NULL, /* seek */ touch_ioctl, /* ioctl */ NULL, /* mmap */ NULL, /* truncate */ touch_poll /* poll */ }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: touch_open ****************************************************************************/ static int touch_open(FAR struct file *filep) { FAR struct touch_openpriv_s *openpriv; FAR struct inode *inode = filep->f_inode; FAR struct touch_upperhalf_s *upper = inode->i_private; FAR struct touch_lowerhalf_s *lower = upper->lower; int ret; openpriv = kmm_zalloc(sizeof(struct touch_openpriv_s)); if (openpriv == NULL) { return -ENOMEM; } ret = circbuf_init(&openpriv->circbuf, NULL, upper->nums * SIZEOF_TOUCH_SAMPLE_S(lower->maxpoint)); if (ret < 0) { kmm_free(openpriv); return ret; } ret = nxmutex_lock(&upper->lock); if (ret < 0) { circbuf_uninit(&openpriv->circbuf); kmm_free(openpriv); 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: touch_close ****************************************************************************/ static int touch_close(FAR struct file *filep) { FAR struct touch_openpriv_s *openpriv = filep->f_priv; FAR struct inode *inode = filep->f_inode; FAR struct touch_upperhalf_s *upper = inode->i_private; int ret; ret = nxmutex_lock(&upper->lock); if (ret < 0) { return ret; } if (upper->grab == openpriv) { upper->grab = NULL; } 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: touch_write ****************************************************************************/ static ssize_t touch_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct touch_upperhalf_s *upper = inode->i_private; FAR struct touch_lowerhalf_s *lower = upper->lower; if (!lower->write) { return -ENOSYS; } return lower->write(lower, buffer, buflen); } /**************************************************************************** * Name: touch_read ****************************************************************************/ static ssize_t touch_read(FAR struct file *filep, FAR char *buffer, size_t len) { FAR struct touch_openpriv_s *openpriv = filep->f_priv; int 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: touch_ioctl ****************************************************************************/ static int touch_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct touch_openpriv_s *openpriv = filep->f_priv; FAR struct inode *inode = filep->f_inode; FAR struct touch_upperhalf_s *upper = inode->i_private; FAR struct touch_lowerhalf_s *lower = upper->lower; int ret; ret = nxmutex_lock(&upper->lock); if (ret < 0) { return ret; } switch (cmd) { case TSIOC_GRAB: { int enable = (int)arg; ret = OK; if (enable) { if (upper->grab != NULL) { ret = -EBUSY; } else { upper->grab = openpriv; } } else { if (upper->grab != openpriv) { ret = -EINVAL; } else { upper->grab = NULL; } } } break; default: { if (lower->control) { ret = lower->control(lower, cmd, arg); } else { ret = -ENOTTY; } } break; } nxmutex_unlock(&upper->lock); return ret; } /**************************************************************************** * Name: touch_poll ****************************************************************************/ static int touch_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct touch_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; } /**************************************************************************** * Name: touch_event_notify ****************************************************************************/ static void touch_event_notify(FAR struct touch_openpriv_s *openpriv, FAR const struct touch_sample_s *sample) { int semcount; nxmutex_lock(&openpriv->lock); circbuf_overwrite(&openpriv->circbuf, sample, SIZEOF_TOUCH_SAMPLE_S(sample->npoints)); nxsem_get_value(&openpriv->waitsem, &semcount); if (semcount < 1) { nxsem_post(&openpriv->waitsem); } poll_notify(&openpriv->fds, 1, POLLIN); nxmutex_unlock(&openpriv->lock); } /**************************************************************************** * Public Function ****************************************************************************/ /**************************************************************************** * Name: touch_event ****************************************************************************/ void touch_event(FAR void *priv, FAR const struct touch_sample_s *sample) { FAR struct touch_upperhalf_s *upper = priv; FAR struct touch_openpriv_s *openpriv; if (nxmutex_lock(&upper->lock) < 0) { return; } if (upper->grab) { touch_event_notify(upper->grab, sample); } else { list_for_every_entry(&upper->head, openpriv, struct touch_openpriv_s, node) { touch_event_notify(openpriv, sample); } } nxmutex_unlock(&upper->lock); } /**************************************************************************** * Name: touch_register ****************************************************************************/ int touch_register(FAR struct touch_lowerhalf_s *lower, FAR const char *path, uint8_t nums) { FAR struct touch_upperhalf_s *upper; int ret; iinfo("Registering %s\n", path); if (lower == NULL || nums == 0) { ierr("ERROR: invalid touchscreen device\n"); return -EINVAL; } upper = kmm_zalloc(sizeof(struct touch_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_touch_fops, 0666, upper); if (ret < 0) { nxmutex_destroy(&upper->lock); kmm_free(upper); return ret; } return ret; } /**************************************************************************** * Name: touch_unregister ****************************************************************************/ void touch_unregister(FAR struct touch_lowerhalf_s *lower, FAR const char *path) { FAR struct touch_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); }