diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2014-07-05 11:47:54 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2014-07-05 11:47:54 -0600 |
commit | a649e0e415f32a9daba3799fc400ebcf5400cead (patch) | |
tree | c9396395e865f0a7fc170995c319bd12c85b2ba8 | |
parent | 1d1e833f4da575b36d6f62581c12bd6a894be206 (diff) | |
download | px4-nuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.tar.gz px4-nuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.tar.bz2 px4-nuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.zip |
Add very basic support for the Atmel maXTouch touchscreen controller
-rw-r--r-- | nuttx/drivers/input/Kconfig | 17 | ||||
-rw-r--r-- | nuttx/drivers/input/Make.defs | 4 | ||||
-rw-r--r-- | nuttx/drivers/input/mxt.c | 1752 | ||||
-rw-r--r-- | nuttx/drivers/input/tsc2007.c | 22 | ||||
-rw-r--r-- | nuttx/drivers/input/tsc2007.h | 3 | ||||
-rw-r--r-- | nuttx/include/nuttx/input/mxt.h | 207 |
6 files changed, 1993 insertions, 12 deletions
diff --git a/nuttx/drivers/input/Kconfig b/nuttx/drivers/input/Kconfig index 9677e0ec7..19ac43850 100644 --- a/nuttx/drivers/input/Kconfig +++ b/nuttx/drivers/input/Kconfig @@ -128,6 +128,23 @@ config ADS7843E_THRESHY endif +config INPUT_MXT + bool "Atmel maXTouch Driver" + default n + ---help--- + Enables support for the Atmel maXTouch driver + +if INPUT_MXT + +config MXT_NPOLLWAITERS + int "Number poll waiters" + default 4 + depends on !DISABLE_POLL + ---help--- + Maximum number of threads that can be waiting on poll() + +endif # INPUT_MXT + config INPUT_STMPE811 bool "STMicro STMPE811 Driver" default n diff --git a/nuttx/drivers/input/Make.defs b/nuttx/drivers/input/Make.defs index 81cf2f696..2071fcb47 100644 --- a/nuttx/drivers/input/Make.defs +++ b/nuttx/drivers/input/Make.defs @@ -51,6 +51,10 @@ ifeq ($(CONFIG_INPUT_MAX11802),y) CSRCS += max11802.c endif +ifeq ($(CONFIG_INPUT_MXT),y) + CSRCS += mxt.c +endif + ifeq ($(CONFIG_INPUT_STMPE811),y) CSRCS += stmpe811_base.c ifneq ($(CONFIG_INPUT_STMPE811_TSC_DISABLE),y) diff --git a/nuttx/drivers/input/mxt.c b/nuttx/drivers/input/mxt.c new file mode 100644 index 000000000..05ab05bb0 --- /dev/null +++ b/nuttx/drivers/input/mxt.c @@ -0,0 +1,1752 @@ +/**************************************************************************** + * drivers/input/mxt.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 + * COPYRIGHT OWNER OR CONTRIBUTORS 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <semaphore.h> +#include <poll.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/kmalloc.h> +#include <nuttx/arch.h> +#include <nuttx/fs/fs.h> +#include <nuttx/i2c.h> +#include <nuttx/wqueue.h> + +#include <nuttx/input/touchscreen.h> +#include <nuttx/input/mxt.h> + +#include "mxt.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Driver support ***********************************************************/ +/* This format is used to construct the /dev/input[n] device driver path. It + * defined here so that it will be used consistently in all places. + */ + +#define DEV_FORMAT "/dev/input%d" +#define DEV_NAMELEN 16 + +/* Get a 16-bit value in little endian order (not necessarily aligned). The + * source data is in little endian order. The host byte order does not + * matter in this case. + */ + +#define MXT_GETUINT16(p) \ + (((uint16_t)(((FAR uint8_t*)(p))[0]) << 8) | \ + (uint16_t)(((FAR uint8_t*)(p))[1])) + +/**************************************************************************** + * Private Types + ****************************************************************************/ +/* This describes the state of one contact */ + +enum mxt_contact_e +{ + CONTACT_NONE = 0, /* No contact */ + CONTACT_NEW, /* New contact */ + CONTACT_MOVE, /* Same contact, possibly different position */ + CONTACT_REPORT, /* Contact reported*/ + CONTACT_LOST, /* Contact lost */ +}; + +/* This structure describes the results of one MXT sample */ + +struct mxt_sample_s +{ + uint8_t id; /* Sampled touch point ID */ + uint8_t contact; /* Contact state (see enum mxt_contact_e) */ + bool valid; /* True: x,y,pressure contain valid, sampled data */ + uint16_t x; /* Measured X position */ + uint16_t y; /* Measured Y position */ + uint8_t area; /* Contact area */ + uint8_t pressure; /* Contact pressure */ +}; + +/* This 7-bit 'info' data read from the MXT and that describes the + * characteristics of the particular maXTouch chip + */ + +struct mxt_info_s +{ + uint8_t family; /* MXT family ID */ + uint8_t variant; /* MXT variant ID */ + uint8_t version; /* MXT version number */ + uint8_t build; /* MXT build number */ + uint8_t xsize; /* Matrix X size */ + uint8_t ysize; /* Matrix Y size */ + uint8_t nobjects; /* Number of objects */ +}; +#define MXT_INFO_SIZE 7 + +struct mxt_object_s +{ + uint8_t type; /* Object type */ + uint8_t addr[2]; /* Start address */ + uint8_t size; /* Size of each instance - 1 */ + uint8_t ninstances; /* Number of instances - 1 */ + uint8_t nids; /* Number of report IDs */ +}; + +struct mxt_msg_s +{ + uint8_t id; /* Report ID */ + uint8_t body[7]; /* Message body */ +}; + +/* Describes the state of the MXT driver */ + +struct mxt_dev_s +{ + /* These are the retained references to the I2C device and to the + * lower half configuration data. + */ + + FAR struct i2c_dev_s *i2c; + FAR const struct mxt_lower_s *lower; + + /* This is the allocated array of object information */ + + FAR struct mxt_object_s *objtab; + + /* This is an allocated array of sample data, one for each possible touch */ + + FAR struct mxt_sample_s *sample; /* Last sampled touch point data */ + + uint8_t nwaiters; /* Number of threads waiting for MXT data */ + uint8_t id; /* Current touch point ID */ + uint8_t nslots; /* Number of slots */ + uint8_t crefs; /* Reference count */ + + /* Cached parameters from object table */ + +#ifdef MXT_SUPPORT_T6 + uint8_t t6id; /* T6 report ID */ +#endif + uint8_t t9idmin; /* T9 touch event report IDs */ + uint8_t t9idmax; +#ifdef CONFIG_MXT_BUTTONS + uint8_t t19id; /* T19 button report ID */ +#endif + + volatile bool event; /* True: An unreported event is buffered */ + sem_t devsem; /* Manages exclusive access to this structure */ + sem_t waitsem; /* Used to wait for the availability of data */ + uint16_t xres; /* X resolution */ + uint16_t yres; /* Y resolution */ + uint32_t frequency; /* Current I2C frequency */ + + char phys[64]; /* Device physical location */ + struct mxt_info_s info; /* Configuration info read from device */ + struct work_s work; /* Supports the interrupt handling "bottom half" */ + + /* The following is a list if poll structures of threads waiting for + * driver events. The 'struct pollfd' reference for each open is also + * retained in the f_priv field of the 'struct file'. + */ + +#ifndef CONFIG_DISABLE_POLL + struct pollfd *fds[CONFIG_MXT_NPOLLWAITERS]; +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/* MXT register access */ + +static int mxt_getreg(FAR struct mxt_dev_s *priv, uint16_t regaddr, + FAR uint8_t *buffer, size_t buflen); +static int mxt_putreg(FAR struct mxt_dev_s *priv, uint16_t regaddr, + FAR const uint8_t *buffer, size_t buflen); + +/* MXT object access */ + +static FAR struct mxt_object_s *mxt_object(FAR struct mxt_dev_s *priv, + uint8_t type); + +/* Poll support */ + +static void mxt_notify(FAR struct mxt_dev_s *priv); + +/* Touch event waiting */ + +static inline int mxt_checksample(FAR struct mxt_dev_s *priv); +static inline int mxt_waitsample(FAR struct mxt_dev_s *priv); + +/* Interrupt handling/position sampling */ + +#ifdef CONFIG_MXT_BUTTONS +static void mxt_button_event(FAR struct mxt_dev_s *priv, + FAR struct mxt_msg_s *msg); +#endif +static void mxt_touch_event(FAR struct mxt_dev_s *priv, + FAR struct mxt_msg_s *msg, int ndx); +static void mxt_worker(FAR void *arg); +static int mxt_interrupt(FAR const struct mxt_lower_s *lower, + FAR void *arg); + +/* Character driver methods */ + +static int mxt_open(FAR struct file *filep); +static int mxt_close(FAR struct file *filep); +static ssize_t mxt_read(FAR struct file *filep, FAR char *buffer, + size_t len); +static int mxt_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +#ifndef CONFIG_DISABLE_POLL +static int mxt_poll(FAR struct file *filep, struct pollfd *fds, bool setup); +#endif + +/* Initialization */ + +static int mxt_getinfo(struct mxt_dev_s *priv); +static int mxt_getobjtab(FAR struct mxt_dev_s *priv); +static int mxt_chghigh(FAR struct mxt_dev_s *priv); +static int mxt_hwinitialize(FAR struct mxt_dev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ +/* This the vtable that supports the character driver interface */ + +static const struct file_operations mxt_fops = +{ + mxt_open, /* open */ + mxt_close, /* close */ + mxt_read, /* read */ + 0, /* write */ + 0, /* seek */ + mxt_ioctl /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , mxt_poll /* poll */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mxt_getreg + ****************************************************************************/ + +static int mxt_getreg(FAR struct mxt_dev_s *priv, uint16_t regaddr, + FAR uint8_t *buffer, size_t buflen) +{ + struct i2c_msg_s msg[2]; + uint8_t addrbuf[2]; + int ret; + + /* Set up to write the address */ + + addrbuf[0] = regaddr & 0xff; + addrbuf[1] = (regaddr >> 8) & 0xff; + + msg[0].addr = priv->lower->address; + msg[0].flags = 0; + msg[0].buffer = addrbuf; + msg[0].length = 2; + + /* Followed by the read data */ + + msg[1].addr = priv->lower->address; + msg[1].flags = I2C_M_READ; + msg[1].buffer = buffer; + msg[1].length = buflen; + + /* Read the register data. The returned value is the number messages + * completed. + */ + + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret == 2) + { + ret = OK; + } + + /* The return value was an error or some number of messages other than 2 */ + + else + { + if (ret >= 0) + { + ret = -EIO; + } + + idbg("ERROR: I2C_TRANSFER failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mxt_putreg + ****************************************************************************/ + +static int mxt_putreg(FAR struct mxt_dev_s *priv, uint16_t regaddr, + FAR const uint8_t *buffer, size_t buflen) +{ + struct i2c_msg_s msg[2]; + uint8_t addrbuf[2]; + int ret; + + /* Set up to write the address */ + + addrbuf[0] = regaddr & 0xff; + addrbuf[1] = (regaddr >> 8) & 0xff; + + msg[0].addr = priv->lower->address; + msg[0].flags = 0; + msg[0].buffer = addrbuf; + msg[0].length = 2; + + /* Followed by the write data (with no repeated start) */ + + msg[1].addr = priv->lower->address; + msg[1].flags = I2C_M_NORESTART; + msg[1].buffer = (FAR uint8_t *)buffer; + msg[1].length = buflen; + + /* Read the register data. The returned value is the number messages + * completed. + */ + + ret = I2C_TRANSFER(priv->i2c, msg, 2); + if (ret == 2) + { + ret = OK; + } + + /* The return value was an error or some number of messages other than 2 */ + + else + { + if (ret >= 0) + { + ret = -EIO; + } + + idbg("ERROR: I2C_TRANSFER failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: mxt_object + ****************************************************************************/ + +static FAR struct mxt_object_s *mxt_object(FAR struct mxt_dev_s *priv, + uint8_t type) +{ + struct mxt_object_s *object; + int i; + + /* Search the object table for the entry matching the type */ + + for (i = 0; i < priv->info.nobjects; i++) + { + object = &priv->objtab[i]; + if (object->type == type) + { + /* Found it.. return the pointer to the object structure */ + + return object; + } + } + + idbg("ERROR: Invalid object type\n"); + return NULL; +} + +/**************************************************************************** + * Name: mxt_getmessage + ****************************************************************************/ + +static int mxt_getmessage(FAR struct mxt_dev_s *priv, + FAR struct mxt_msg_s *msg) +{ + struct mxt_object_s *object; + uint16_t regaddr; + + object = mxt_object(priv, MXT_GEN_MESSAGE_T5); + if (!object) + { + return -EINVAL; + } + + regaddr = MXT_GETUINT16(object->addr); + return mxt_getreg(priv, regaddr, (FAR uint8_t *)msg, + sizeof(struct mxt_msg_s)); +} + +/**************************************************************************** + * Name: mxt_putobject + ****************************************************************************/ + +static int mxt_putobject(FAR struct mxt_dev_s *priv, uint8_t type, + uint8_t offset, uint8_t value) +{ + FAR struct mxt_object_s *object; + uint16_t regaddr; + + object = mxt_object(priv, type); + if (!object || offset >= object->size + 1) + { + return -EINVAL; + } + + regaddr = MXT_GETUINT16(object->addr); + return mxt_putreg(priv, regaddr + offset, (FAR const uint8_t *)&value, 1); +} + +/**************************************************************************** + * Name: mxt_notify + ****************************************************************************/ + +static void mxt_notify(FAR struct mxt_dev_s *priv) +{ +#ifndef CONFIG_DISABLE_POLL + int i; +#endif + + /* If there are threads waiting for read data, then signal one of them + * that the read data is available. + */ + + if (priv->nwaiters > 0) + { + /* After posting this semaphore, we need to exit because the maXTouch + * is no longer available. + */ + + sem_post(&priv->waitsem); + } + + /* If there are threads waiting on poll() for maXTouch data to become available, + * then wake them up now. NOTE: we wake up all waiting threads because we + * do not know that they are going to do. If they all try to read the data, + * then some make end up blocking after all. + */ + +#ifndef CONFIG_DISABLE_POLL + for (i = 0; i < CONFIG_MXT_NPOLLWAITERS; i++) + { + struct pollfd *fds = priv->fds[i]; + if (fds) + { + fds->revents |= POLLIN; + ivdbg("Report events: %02x\n", fds->revents); + sem_post(fds->sem); + } + } +#endif +} + +/**************************************************************************** + * Name: mxt_checksample + * + * Description: + * This function implements a test and clear of the priv->event flag. + * Called only from mxt_waitsample. + * + * Assumptions: + * - Scheduler must be locked to prevent the worker thread from running + * while this thread runs. The sample data is, of course, updated from + * the worker thread. + * - Interrupts must be disabled when this is called to (1) prevent posting + * of semaphores from interrupt handlers, and (2) to prevent sampled data + * from changing until it has been reported. + * + ****************************************************************************/ + +static inline int mxt_checksample(FAR struct mxt_dev_s *priv) +{ + /* Is there new maXTouch sample data available? */ + + if (priv->event) + { + /* Yes.. clear the flag and return success */ + + priv->event = false; + return OK; + } + + /* No.. return failure */ + + return -EAGAIN; +} + +/**************************************************************************** + * Name: mxt_waitsample + * + * Wait until sample data is available. Called only from mxt_read. + * + * Assumptions: + * - Scheduler must be locked to prevent the worker thread from running + * while this thread runs. The sample data is, of course, updated from + * the worker thread. + * + ****************************************************************************/ + +static inline int mxt_waitsample(FAR struct mxt_dev_s *priv) +{ + irqstate_t flags; + int ret; + + /* Interrupts me be disabled when this is called to (1) prevent posting + * of semaphores from interrupt handlers, and (2) to prevent sampled data + * from changing until it has been reported. + */ + + flags = irqsave(); + + /* Now release the semaphore that manages mutually exclusive access to + * the device structure. This may cause other tasks to become ready to + * run, but they cannot run yet because pre-emption is disabled. + */ + + sem_post(&priv->devsem); + + /* Try to get the a sample... if we cannot, then wait on the semaphore + * that is posted when new sample data is available. + */ + + while (mxt_checksample(priv) < 0) + { + /* Wait for a change in the maXTouch state */ + + priv->nwaiters++; + ret = sem_wait(&priv->waitsem); + priv->nwaiters--; + + if (ret < 0) + { + /* If we are awakened by a signal, then we need to return + * the failure now. + */ + + DEBUGASSERT(errno == EINTR); + ret = -EINTR; + goto errout; + } + } + + /* Re-acquire the semaphore that manages mutually exclusive access to + * the device structure. We may have to wait here. But we have our sample. + * Interrupts and pre-emption will be re-enabled while we wait. + */ + + ret = sem_wait(&priv->devsem); + +errout: + /* Then re-enable interrupts. We might get interrupt here and there + * could be a new sample. But no new threads will run because we still + * have pre-emption disabled. + */ + + irqrestore(flags); + return ret; +} + +/**************************************************************************** + * Name: mxt_button_event + ****************************************************************************/ + +#ifdef CONFIG_MXT_BUTTONS +static void mxt_button_event(FAR struct mxt_dev_s *priv, + FAR struct mxt_msg_s *msg) +{ + bool button; + int i; + + /* REVISIT: Button inputs are currently ignored */ + + /* Buttons are active low and determined by the GPIO bit + * settings in byte 0 of the message data: A button is + * pressed if the corresponding bit is zero. + */ + + for (i = 0; i < priv->lower->nbuttons; i++) + { + uint8_t bit = (MXT_GPIO0_MASK << i); + + /* Does this implementation support the button? */ + + if ((priv->lower->bmask & bit) != 0) + { + /* Yes.. get the button state */ + + button = (msg->body[0] & mask) == 0; + + /* Now what? */ + } + } +} +#endif + +/**************************************************************************** + * Name: mxt_touch_event + ****************************************************************************/ + +static void mxt_touch_event(FAR struct mxt_dev_s *priv, + FAR struct mxt_msg_s *msg, int ndx) +{ + FAR struct mxt_sample_s *sample; + uint16_t x; + uint16_t y; + uint8_t area; + uint8_t pressure; + uint8_t status; + + /* Extract the 12-bit X and Y positions */ + + x = ((uint16_t)msg->body[1] << 4) | (((uint16_t)msg->body[3] >> 4) & 0x0f); + y = ((uint16_t)msg->body[2] << 4) | (((uint16_t)msg->body[3] & 0x0f)); + + /* Swap X/Y as necessary */ + + if (priv->lower->swapxy) + { + uint16_t tmp = x; + y = x; + x = tmp; + } + + /* Extract area pressure and status */ + + area = msg->body[4]; + pressure = msg->body[5]; + + status = msg->body[0]; + ivdbg("ndx=%u status=%02x pos(%u,%u) area=%u pressure=%u\n", + ndx, status, x, y, area, pressure); + + /* Is this a loss of contact? */ + + sample = &priv->sample[ndx]; + if ((status & MXT_DETECT) == 0) + { + /* Ignore the event if the if there was no contact: + * + * CONTACT_NONE = No touch and already reported + * CONTACT_LOST = No touch, but not reported) + */ + + if (sample->contact == CONTACT_NONE) + { + goto errout; + } + + sample->contact = CONTACT_LOST; + } + else + { + /* It is a touch event. If the last loss-of-contact event has not + * been processed yet, then have to bump up the touch identifier and + * hope that the client is smart enough to infer the loss-of-contact + * event for the preceding touch. + */ + + if (sample->contact == CONTACT_LOST) + { + priv->id++; + } + + /* Save the measurements */ + + sample->x = x; + sample->y = y; + sample->area = area; + sample->pressure = pressure; + sample->valid = true; + + /* If the last loss-of-contact event has not been processed yet, then + * let's bump up the touch identifier and hope that the client is smart + * enough to infer the loss-of-contact event for the preceding touch. + */ + + if (sample->contact == CONTACT_LOST) + { + priv->id++; + } + + /* If this is not the first touch report, then report it a move: + * Same contact, same ID, but a potentially new position. If + * The move event has not been reported, then just overwrite the + * move. That is harmless. + */ + + if (sample->contact == CONTACT_REPORT || + sample->contact == CONTACT_MOVE) + { + /* Not a new contact. Indicate a contact move event */ + + sample->contact = CONTACT_MOVE; + + /* This state will be set to CONTACT_REPORT after it + * been reported. + */ + } + + /* If we have seen this contact before but it has not yet been + * reported, then do nothing other than overwrite the positional + * data. + * + * This the state must be one of CONTACT_NONE or CONTACT_LOST (see + * above) and we have a new contact with a new ID> + */ + + else if (sample->contact != CONTACT_NEW) + { + /* First contact. Save the contact event and assign a new + * ID to the contact. + */ + + sample->contact = CONTACT_NEW; + sample->id = priv->id++; + + /* This state will be set to CONTACT_REPORT after it + * been reported. + */ + } + } + + /* Indicate the availability of new sample data for this ID and notify + * any waiters that new maXTouch data is available + */ + + priv->event = true; + mxt_notify(priv); + + /* Exit, re-enabling maXTouch interrupts */ + +errout: + priv->lower->enable(priv->lower, true); +} + +/**************************************************************************** + * Name: mxt_worker + ****************************************************************************/ + +static void mxt_worker(FAR void *arg) +{ + FAR struct mxt_dev_s *priv = (FAR struct mxt_dev_s *)arg; + FAR const struct mxt_lower_s *lower; + struct mxt_msg_s msg; + uint8_t id; + + ASSERT(priv != NULL); + + /* Get a pointer the callbacks for convenience (and so the code is not so + * ugly). + */ + + lower = priv->lower; + DEBUGASSERT(lower != NULL); + + /* Loop, processing each message from the maXTouch */ + + do + { + /* Retrieve the next message from the maXTouch */ + + if (mxt_getmessage(priv, &msg)) + { + idbg("ERROR: Failed to read msg\n"); + return; + } + + id = msg.id; + +#ifdef MXT_SUPPORT_T6 + /* Check for T6 */ + + if (id == priv->t6id) + { + uint32_t chksum; + int status; + + status = msg_body[0] + chksum = (uint32_t)msg.body[1] | + ((uint32_t)msg.body[2] << 8) | + ((uint32_t)msg.body[3] << 16); + + ivdbg("T6: status: %02x checksum: %06lx\n", + status, (unsigned long)chksum); + } + else +#endif + + /* Check for T9 */ + + if (id >= priv->t9idmin && id <= priv->t9idmax) + { + mxt_touch_event(priv, &msg, id - priv->t9idmin); + } + +#ifdef CONFIG_MXT_BUTTONS + /* Check for T19 */ + + else if (msg.id == priv->t19id) + { + mxt_button_event(priv, &msg); + } +#endif + /* Any others ignored */ + + else + { + ivdbg("Ignored: id=%u message={%02x %02x %02x %02x %02x %02x %02x}\n", + msg->id, msg->body[0], msg->body[1], msg->body[2], msg->body[3], + msg->body[4], msg->body[5], msg->body[6], msg->body[7]); + } + } + while (id != 0xff); +} + +/**************************************************************************** + * Name: mxt_interrupt + ****************************************************************************/ + +static int mxt_interrupt(FAR const struct mxt_lower_s *lower, FAR void *arg) +{ + FAR struct mxt_dev_s *priv = (FAR struct mxt_dev_s *)arg; + int ret; + + /* Get a pointer the callbacks for convenience (and so the code is not so + * ugly). + */ + + DEBUGASSERT(lower != NULL && priv != NULL); + + /* Disable further interrupts */ + + lower->enable(lower, false); + + /* Transfer processing to the worker thread. Since maXTouch interrupts are + * disabled while the work is pending, no special action should be required + * to protected the work queue. + */ + + DEBUGASSERT(priv->work.worker == NULL); + ret = work_queue(HPWORK, &priv->work, mxt_worker, priv, 0); + if (ret != 0) + { + illdbg("Failed to queue work: %d\n", ret); + } + + /* Clear any pending interrupts and return success */ + + lower->clear(lower); + return OK; +} + +/**************************************************************************** + * Name: mxt_open + ****************************************************************************/ + +static int mxt_open(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct mxt_dev_s *priv; + uint8_t tmp; + int ret; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct mxt_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + /* This should only happen if the wait was cancelled by an signal */ + + DEBUGASSERT(errno == EINTR); + return -EINTR; + } + + /* Increment the reference count */ + + tmp = priv->crefs + 1; + if (tmp == 0) + { + /* More than 255 opens; uint8_t overflows to zero */ + + ret = -EMFILE; + goto errout_with_sem; + } + + /* When the reference increments to 1, this is the first open event + * on the driver.. and an opportunity to do any one-time initialization. + */ + + if (tmp == 1) + { + /* Touch enable */ + + mxt_putobject(priv, MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + } + + /* Save the new open count on success */ + + priv->crefs = tmp; + +errout_with_sem: + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: mxt_close + ****************************************************************************/ + +static int mxt_close(FAR struct file *filep) +{ + FAR struct inode *inode; + FAR struct mxt_dev_s *priv; + int ret; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct mxt_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + /* This should only happen if the wait was cancelled by an signal */ + + DEBUGASSERT(errno == EINTR); + return -EINTR; + } + + /* Decrement the reference count unless it would decrement a negative + * value. When the count decrements to zero, there are no further + * open references to the driver. + */ + + if (priv->crefs >= 1) + { + if (--priv->crefs < 1) + { + /* Touch disable */ + + mxt_putobject(priv, MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + } + } + + sem_post(&priv->devsem); + return OK; +} + +/**************************************************************************** + * Name: mxt_read + ****************************************************************************/ + +static ssize_t mxt_read(FAR struct file *filep, FAR char *buffer, size_t len) +{ + FAR struct inode *inode; + FAR struct mxt_dev_s *priv; + ssize_t samplesize; + int ncontacts; + int ret; + int i; + int j; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct mxt_dev_s *)inode->i_private; + + /* Verify that the caller has provided a buffer large enough to receive + * the touch data. + */ + + if (len < SIZEOF_TOUCH_SAMPLE_S(1)) + { + /* We could provide logic to break up a touch report into segments and + * handle smaller reads... but why? + */ + + return -ENOSYS; + } + + /* Get exclusive access to the driver data structure */ + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + /* This should only happen if the wait was cancelled by an signal */ + + DEBUGASSERT(errno == EINTR); + return -EINTR; + } + + /* Locking the scheduler will prevent the worker thread from running + * until we finish here. + */ + + sched_lock(); + + /* Try to read sample data. */ + + ret = mxt_checksample(priv); + if (ret < 0) + { + /* Sample data is not available now. We would ave to wait to get + * receive sample data. If the user has specified the O_NONBLOCK + * option, then just return an error. + */ + + if (filep->f_oflags & O_NONBLOCK) + { + ret = -EAGAIN; + goto errout; + } + + /* Wait for sample data */ + + ret = mxt_waitsample(priv); + if (ret < 0) + { + /* We might have been awakened by a signal */ + + goto errout; + } + } + + /* In any event, we now have sampled maXTouch data that we can report + * to the caller. First, count the number of valid contacts. + */ + + samplesize = 0; + ncontacts = 0; + + for (i = 0; i < priv->nslots; i++) + { + FAR struct mxt_sample_s *sample = &priv->sample[i]; + + /* Do we need to report this? We need to report the event if + * it is CONTACT_LOST or CONTACT_REPORT or CONTACT_MOVE (with new, + * valid positional data). + */ + + if (sample->contact == CONTACT_LOST || + sample->contact == CONTACT_NEW || + sample->contact == CONTACT_MOVE) + { + int newcount = ncontacts + 1; + ssize_t newsize = SIZEOF_TOUCH_SAMPLE_S(ncontacts); + + /* Would this sample exceed the buffer size provided by the + * caller? + */ + + if (newsize > len) + { + /* Yes.. break out of the loop using the previous size and + * count. + */ + + break; + } + + /* Save the new size and count */ + + ncontacts = newcount; + samplesize = newsize; + } + } + + /* Did we find any valid samples? */ + + if (ncontacts > 0) + { + FAR struct touch_sample_s *report = (FAR struct touch_sample_s *)buffer; + + /* Yes, copy the sample data into the user bufer */ + + memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(ncontacts)); + report->npoints = ncontacts; + + for (i = 0, j= 0; i < priv->nslots && j < ncontacts; i++) + { + FAR struct mxt_sample_s *sample = &priv->sample[i]; + + /* Do we need to report this? We need to report the event if + * it is CONTACT_LOST or CONTACT_REPORT or CONTACT_MOVE (with new, + * valid positional data). + */ + + if (sample->contact == CONTACT_LOST || + sample->contact == CONTACT_NEW || + sample->contact == CONTACT_MOVE) + { + /* Yes.. transfer the sample data */ + + FAR struct touch_point_s *point = &report->point[j]; + j++; + + /* REVISIT: height and width are not set, area is + * not used. + */ + + point->id = sample->id; + point->x = sample->x; + point->y = sample->y; + point->pressure = sample->pressure; + + /* Report the appropriate flags */ + + if (sample->contact == CONTACT_LOST) + { + /* The contact was lost. Is the positional data + * valid? This is important to know because the release + * will be sent to the window based on its last positional + * data. + */ + + if (sample->valid) + { + point->flags = TOUCH_UP | TOUCH_ID_VALID | + TOUCH_POS_VALID | TOUCH_PRESSURE_VALID; + } + else + { + point->flags = TOUCH_UP | TOUCH_ID_VALID; + } + + /* Change to CONTACT_NONE to indicate that the sample + * has been reported. From here it can change only + * to CONTACT_REPORT (with a new ID). + */ + + sample->contact = CONTACT_NONE; + } + else + { + if (sample->contact == CONTACT_REPORT) + { + /* First loss of contact */ + + point->flags = TOUCH_DOWN | TOUCH_ID_VALID | + TOUCH_POS_VALID; + } + else /* if (sample->contact == CONTACT_MOVE) */ + { + /* Movement of the same contact */ + + point->flags = TOUCH_MOVE | TOUCH_ID_VALID | + TOUCH_POS_VALID; + } + + /* Change to CONTACT_REPORT to indicate that the sample + * has been reported. From here is can change to + * CONTACT_LOST (same ID), CONTACT_MOVE (same ID) or back + * to CONTACT_NEW (new ID). + */ + + sample->contact = CONTACT_REPORT; + + /* A pressure measurement of zero means that pressure is not + * available */ + + if (point->pressure != 0) + { + point->flags |= TOUCH_PRESSURE_VALID; + } + } + + /* In any case, the sample data has been reported and is no + * longer valid. + */ + + sample->valid = false; + } + } + } + + ret = samplesize; + +errout: + sched_unlock(); + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name:mxt_ioctl + ****************************************************************************/ + +static int mxt_ioctl(FAR struct file *filep, int cmd, unsigned long arg) +{ + FAR struct inode *inode; + FAR struct mxt_dev_s *priv; + int ret; + + ivdbg("cmd: %d arg: %ld\n", cmd, arg); + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct mxt_dev_s *)inode->i_private; + + /* Get exclusive access to the driver data structure */ + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + /* This should only happen if the wait was cancelled by an signal */ + + DEBUGASSERT(errno == EINTR); + return -EINTR; + } + + /* Process the IOCTL by command */ + + switch (cmd) + { + case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */ + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(priv->lower != NULL && ptr != NULL); + priv->frequency = I2C_SETFREQUENCY(priv->i2c, *ptr); + } + break; + + case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */ + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(priv->lower != NULL && ptr != NULL); + *ptr = priv->frequency; + } + break; + + default: + ret = -ENOTTY; + break; + } + + sem_post(&priv->devsem); + return ret; +} + +/**************************************************************************** + * Name: mxt_poll + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_POLL +static int mxt_poll(FAR struct file *filep, FAR struct pollfd *fds, + bool setup) +{ + FAR struct inode *inode; + FAR struct mxt_dev_s *priv; + int ret; + int i; + + ivdbg("setup: %d\n", (int)setup); + DEBUGASSERT(filep && fds); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct mxt_dev_s *)inode->i_private; + + /* Are we setting up the poll? Or tearing it down? */ + + ret = sem_wait(&priv->devsem); + if (ret < 0) + { + /* This should only happen if the wait was cancelled by an signal */ + + DEBUGASSERT(errno == EINTR); + return -EINTR; + } + + if (setup) + { + /* Ignore waits that do not include POLLIN */ + + if ((fds->events & POLLIN) == 0) + { + idbg("Missing POLLIN: revents: %08x\n", fds->revents); + ret = -EDEADLK; + goto errout; + } + + /* This is a request to set up the poll. Find an available + * slot for the poll structure reference + */ + + for (i = 0; i < CONFIG_MXT_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_MXT_NPOLLWAITERS) + { + idbg("No availabled slot found: %d\n", i); + fds->priv = NULL; + ret = -EBUSY; + goto errout; + } + + /* Should we immediately notify on any of the requested events? */ + + if (priv->event) + { + mxt_notify(priv); + } + } + else if (fds->priv) + { + /* This is a request to tear down the poll. */ + + struct pollfd **slot = (struct pollfd **)fds->priv; + DEBUGASSERT(slot != NULL); + + /* Remove all memory of the poll setup */ + + *slot = NULL; + fds->priv = NULL; + } + +errout: + sem_post(&priv->devsem); + return ret; +} +#endif + +/**************************************************************************** + * Name: mxt_getinfo + ****************************************************************************/ + +static int mxt_getinfo(struct mxt_dev_s *priv) +{ + int ret; + + /* Read 7-byte information block starting at address MXT_INFO */ + + ret = mxt_getreg(priv, MXT_INFO, (FAR uint8_t *)&priv->info, + sizeof(struct mxt_info_s)); + if (ret < 0) + { + idbg("ERROR: mxt_getreg failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: mxt_getobjtab + ****************************************************************************/ + +static int mxt_getobjtab(FAR struct mxt_dev_s *priv) +{ + FAR struct mxt_object_s *object; + size_t tabsize; + uint8_t idmin; + uint8_t idmax; + uint8_t id; + int ret; + int i; + + /* Read the size of the object table */ + + tabsize = priv->info.nobjects * sizeof(struct mxt_object_s); + ret = mxt_getreg(priv, MXT_OBJECT_START, (FAR uint8_t *)priv->objtab, + tabsize); + if (ret) + { + return ret; + } + + /* Search through the object table. Find the values associated with + * certain object types and save those ID.Valid report IDs start at ID=1. + */ + + for (i = 0, id = 1; i < priv->info.nobjects; i++) + { + object = &priv->objtab[i]; + if (object->nids > 0) + { + idmin = id; + id += object->nids * (object->ninstances + 1); + idmax = id - 1; + } + else + { + idmin = 0; + idmax = 0; + } + + ivdbg("%2d. Type %d Start %d size: %d instances: %d IDs: %u-%u\n", + i, object->type, MXT_GETUINT16(object->addr), object->size + 1, + object->ninstances + 1, idmin, idmax); + + switch (object->type) + { +#ifdef MXT_SUPPORT_T6 + case MXT_GEN_COMMAND_T6: + priv->t6id = idmin; + break; +#endif + + case MXT_TOUCH_MULTI_T9: + priv->t9idmin = idmin; + priv->t9idmax = idmax; + break; + +#ifdef CONFIG_MXT_BUTTONS + case MXT_SPT_GPIOPWM_T19: + priv->t19id = idmin; + break; +#endif + default: + break; + } + } + + return OK; +} + +/**************************************************************************** + * Name: mxt_chghigh + ****************************************************************************/ + +static int mxt_chghigh(FAR struct mxt_dev_s *priv) +{ + struct mxt_msg_s msg; + int retries = 10; + int ret; + + /* Read dummy message to make high CHG pin */ + + do + { + ret = mxt_getmessage(priv, &msg); + if (ret < 0) + { + return ret; + } + } + while (msg.id != 0xff && --retries > 0); + + /* Check for a timeout */ + + if (retries <= 0) + { + idbg("ERROR: CHG pin did not clear\n"); + return -EBUSY; + } + + return 0; +} + +/**************************************************************************** + * Name: mxt_hwinitialize + ****************************************************************************/ + +static int mxt_hwinitialize(FAR struct mxt_dev_s *priv) +{ + struct mxt_info_s *info = &priv->info; + unsigned int nslots; + uint16_t xres; + uint16_t yres; + uint8_t regval; + int ret; + + /* Set the selected I2C frequency */ + + priv->frequency = I2C_SETFREQUENCY(priv->i2c, priv->lower->frequency); + + /* Read the info registers from the device */ + + ret = mxt_getinfo(priv); + if (ret < 0) + { + idbg("ERROR: Failed to read info registers: %d\n", ret); + return ret; + } + + /* Allocate memory for the object table */ + + priv->objtab = kzalloc(info->nobjects * sizeof(struct mxt_object_s)); + if (priv->objtab != NULL) + { + idbg("ERROR: Failed to allocate object table\n"); + return -ENOMEM; + } + + /* Get object table information */ + + ret = mxt_getobjtab(priv); + if (ret) + { + goto errout_with_objtab; + } + + /* Perform a soft reset */ + + mxt_putobject(priv, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, 1); + usleep(MXT_RESET_TIME); + + /* Update matrix size in the info structure */ + + ret = mxt_getreg(priv, MXT_MATRIX_X_SIZE, (FAR uint8_t *)®val, 1); + if (ret) + { + goto errout_with_objtab; + } + + info->xsize = regval; + + ret = mxt_getreg(priv, MXT_MATRIX_Y_SIZE, (FAR uint8_t *)®val, 1); + if (ret) + { + goto errout_with_objtab; + } + + info->ysize = regval; + + ivdbg("family: %u variant: %u version: %u.%u.%02x\n", + info->family, info->variant, info->version >> 4, info->version & 0xf, + info->build); + ivdbg("Matrix size: (%u,%u) objects: %u\n", + info->xsize, info->ysize, info->nobjects); + + /* Set up the touchscreen resolution */ + + xres = info->xsize - 1; + yres = info->ysize - 1; + + if (priv->lower->swapxy) + { + priv->xres = yres; + priv->yres = xres; + } + else + { + priv->xres = xres; + priv->yres = yres; + } + + /* How many multi touch "slots" */ + + nslots = priv->t9idmax - priv->t9idmin + 1; + DEBUGASSERT(nslots > 0 && nslots < 256); + + priv->nslots = nslots; + + /* Allocate a place to hold sample data for each slot */ + + priv->sample = (FAR struct mxt_sample_s *) + kzalloc(nslots * sizeof(struct mxt_sample_s)); + if (!priv->sample) + { + idbg("ERROR: Failed to allocate object table\n"); + goto errout_with_objtab; + } + + /* Force the CHG pin to the high state */ + + ret = mxt_chghigh(priv); + if (ret) + { + goto errout_with_sample; + } + + return OK; + +errout_with_sample: + kfree(priv->sample); + priv->sample = NULL; +errout_with_objtab: + kfree(priv->objtab); + priv->objtab = NULL; + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mxt_register + * + * Description: + * Configure the maXTouch to use the provided I2C device instance. This + * will register the driver as /dev/inputN where N is the minor device + * number + * + * Input Parameters: + * i2c - An I2C driver instance + * lower - Persistent board configuration data + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int mxt_register(FAR struct i2c_dev_s *i2c, + FAR const struct mxt_lower_s * const lower, int minor) +{ + FAR struct mxt_dev_s *priv; + char devname[DEV_NAMELEN]; + int ret; + + ivdbg("i2c: %p minor: %d\n", i2c, minor); + + /* Debug-only sanity checks */ + + DEBUGASSERT(i2c != NULL && lower != NULL && minor >= 0 && minor < 100); + + /* Create and initialize a maXTouch device driver instance */ + + priv = (FAR struct mxt_dev_s *)kzalloc(sizeof(struct mxt_dev_s)); + if (!priv) + { + idbg("ERROR: Failed allocate device structure\n"); + return -ENOMEM; + } + + /* Initialize the ADS7843E device driver instance */ + + memset(priv, 0, sizeof(struct mxt_dev_s)); + priv->i2c = i2c; /* Save the SPI device handle */ + priv->lower = lower; /* Save the board configuration */ + + sem_init(&priv->devsem, 0, 1); /* Initialize device semaphore */ + sem_init(&priv->waitsem, 0, 0); /* Initialize event wait semaphore */ + + /* Make sure that interrupts are disabled */ + + MXT_CLEAR(lower); + MXT_ENABLE(lower); + + /* Attach the interrupt handler */ + + ret = MXT_ATTACH(lower, mxt_interrupt, priv); + if (ret < 0) + { + idbg("Failed to attach interrupt\n"); + goto errout_with_priv; + } + + /* Configure the MXT hardware */ + + ret = mxt_hwinitialize(priv); + if (ret < 0) + { + idbg("ERROR: mxt_hwinitialize failed: %d\n", reg); + goto errout_with_irq; + } + + /* Register the device as an input device */ + + (void)snprintf(devname, DEV_NAMELEN, DEV_FORMAT, minor); + ivdbg("Registering %s\n", devname); + + ret = register_driver(devname, &mxt_fops, 0666, priv); + if (ret < 0) + { + idbg("register_driver() failed: %d\n", ret); + goto errout_with_hwinit; + } + + /* Schedule work to perform the initial sampling and to set the data + * availability conditions. */ + + ret = work_queue(HPWORK, &priv->work, mxt_worker, priv, 0); + if (ret != 0) + { + idbg("Failed to queue work: %d\n", ret); + goto errout_with_dev; + } + + /* And return success */ + + return OK; + + /* Error clean-up exits */ + +errout_with_dev: + (void)unregister_driver(devname); +errout_with_hwinit: + kfree(priv->objtab); + kfree(priv->sample); +errout_with_irq: + MXT_DETACH(lower); +errout_with_priv: + sem_destroy(&priv->devsem); + sem_destroy(&priv->waitsem); + kfree(priv); + return ret; +} diff --git a/nuttx/drivers/input/tsc2007.c b/nuttx/drivers/input/tsc2007.c index f4155e456..89bad68ba 100644 --- a/nuttx/drivers/input/tsc2007.c +++ b/nuttx/drivers/input/tsc2007.c @@ -295,7 +295,7 @@ static int tsc2007_sample(FAR struct tsc2007_dev_s *priv, int ret = -EAGAIN; /* Interrupts me be disabled when this is called to (1) prevent posting - * of semphores from interrupt handlers, and (2) to prevent sampled data + * of semaphores from interrupt handlers, and (2) to prevent sampled data * from changing until it has been reported. */ @@ -349,7 +349,7 @@ static int tsc2007_waitsample(FAR struct tsc2007_dev_s *priv, int ret; /* Interrupts me be disabled when this is called to (1) prevent posting - * of semphores from interrupt handlers, and (2) to prevent sampled data + * of semaphores from interrupt handlers, and (2) to prevent sampled data * from changing until it has been reported. * * In addition, we will also disable pre-emption to prevent other threads @@ -367,7 +367,7 @@ static int tsc2007_waitsample(FAR struct tsc2007_dev_s *priv, sem_post(&priv->devsem); /* Try to get the a sample... if we cannot, then wait on the semaphore - * that is posted when new sample data is availble. + * that is posted when new sample data is available. */ while (tsc2007_sample(priv, sample) < 0) @@ -699,7 +699,7 @@ static void tsc2007_worker(FAR void *arg) if (pendown) { - /* If this is the first (acknowledged) pend down report, then report + /* If this is the first (acknowledged) pen down report, then report * this as the first contact. If contact == CONTACT_DOWN, it will be * set to set to CONTACT_MOVE after the contact is first sampled. */ @@ -726,7 +726,7 @@ static void tsc2007_worker(FAR void *arg) priv->sample.id = priv->id; priv->penchange = true; - /* Notify any waiters that nes TSC2007 data is available */ + /* Notify any waiters that new TSC2007 data is available */ tsc2007_notify(priv); @@ -810,7 +810,7 @@ static int tsc2007_open(FAR struct file *filep) ret = sem_wait(&priv->devsem); if (ret < 0) { - /* This should only happen if the wait was canceled by an signal */ + /* This should only happen if the wait was cancelled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; @@ -865,7 +865,7 @@ static int tsc2007_close(FAR struct file *filep) ret = sem_wait(&priv->devsem); if (ret < 0) { - /* This should only happen if the wait was canceled by an signal */ + /* This should only happen if the wait was cancelled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; @@ -922,7 +922,7 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len ret = sem_wait(&priv->devsem); if (ret < 0) { - /* This should only happen if the wait was canceled by an signal */ + /* This should only happen if the wait was cancelled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; @@ -990,7 +990,7 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len { if (sample.contact == CONTACT_DOWN) { - /* First contact */ + /* Loss of contact */ report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID; } @@ -1038,7 +1038,7 @@ static int tsc2007_ioctl(FAR struct file *filep, int cmd, unsigned long arg) ret = sem_wait(&priv->devsem); if (ret < 0) { - /* This should only happen if the wait was canceled by an signal */ + /* This should only happen if the wait was cancelled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; @@ -1114,7 +1114,7 @@ static int tsc2007_poll(FAR struct file *filep, FAR struct pollfd *fds, ret = sem_wait(&priv->devsem); if (ret < 0) { - /* This should only happen if the wait was canceled by an signal */ + /* This should only happen if the wait was cancelled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; diff --git a/nuttx/drivers/input/tsc2007.h b/nuttx/drivers/input/tsc2007.h index 76d5962bf..107eb701d 100644 --- a/nuttx/drivers/input/tsc2007.h +++ b/nuttx/drivers/input/tsc2007.h @@ -107,7 +107,8 @@ #ifdef __cplusplus #define EXTERN extern "C" -extern "C" { +extern "C" +{ #else #define EXTERN extern #endif diff --git a/nuttx/include/nuttx/input/mxt.h b/nuttx/include/nuttx/input/mxt.h new file mode 100644 index 000000000..a1d48e75d --- /dev/null +++ b/nuttx/include/nuttx/input/mxt.h @@ -0,0 +1,207 @@ +/**************************************************************************** + * include/nuttx/input/mxt.h + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <gnutt@nuttx.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 + * COPYRIGHT OWNER OR CONTRIBUTORS 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_INPUT_MXT_H +#define __INCLUDE_NUTTX_INPUT_MXT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> + +#include <nuttx/i2c.h> + +#if defined(CONFIG_INPUT) && defined(CONFIG_INPUT_MXT) + +/**************************************************************************** + * Pre-Processor Definitions + ****************************************************************************/ +/* Configuration ************************************************************/ +/* Maximum number of threads than can be waiting for POLL events */ + +#ifndef CONFIG_MXT_NPOLLWAITERS +# define CONFIG_MXT_NPOLLWAITERS 2 +#endif + +/* Buttons are not supported */ + +#undef CONFIG_MXT_BUTTONS + +/* Check for some required settings. This can save the user a lot of time + * in getting the right configuration. + */ + +#ifndef CONFIG_I2C_TRANSFER +# error "CONFIG_I2C_TRANSFER is required in the I2C configuration" +#endif + +#ifdef CONFIG_DISABLE_SIGNALS +# error "Signals are required. CONFIG_DISABLE_SIGNALS must not be selected." +#endif + +#ifndef CONFIG_SCHED_WORKQUEUE +# error "Work queue support required. CONFIG_SCHED_WORKQUEUE must be selected." +#endif + +/* I2C addresses ************************************************************/ + +#define MXT_APP_LOW 0x4a +#define MXT_APP_HIGH 0x4b +#define MXT_BOOT_LOW 0x24 +#define MXT_BOOT_HIGH 0x25 + +/* Helper macros ************************************************************/ + +#define MXT_ATTACH(s,isr,arg) ((s)->attach(s,isr,arg)) +#define MXT_DETACH(s) ((s)->attach(s,NULL,NULL)) +#define MXT_ENABLE(s) ((s)->enable(s,true)) +#define MXT_DISABLE(s) ((s)->enable(s,false)) +#define MXT_CLEAR(s) ((s)->clear(s)) +#define MXT_PENDOWN(s) ((s)->pendown(s)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This is the type of the MXT interrupt handler. The lower level code will + * intercept the interrupt and provide the upper level with the private data + * that was provided when the interrupt was attached. + */ + +struct mxt_lower_s; +typedef CODE int (*mxt_handler_t)(FAR const struct mxt_lower_s *lower, + FAR void *arg); + +/* A reference to a structure of this type must be passed to the MXT + * driver. This structure provides information about the configuration + * of the MXT and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. + */ + +struct mxt_lower_s +{ + /* Device characterization */ + + uint8_t address; /* 7-bit I2C address (only bits 0-6 used) */ + uint32_t frequency; /* I2C frequency */ + +#ifndef CONFIG_MXT_MULTIPLE + /* If multiple MXT devices are supported, then an IRQ number must + * be provided for each so that their interrupts can be distinguished. + */ + + int irq; /* IRQ number received by interrupt handler. */ +#endif + + /* True: Swap X and Y values */ + + bool swapxy; + +#ifdef CONFIG_MXT_BUTTONS + /* Buttons are not currently supported. But if they were we + * would need to know which which. + */ + + uint8_t bmask; /* Bit encoded, see MXT_GPIOn_MASK */ +#endif + + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the MXT driver from differences in GPIO + * interrupt handling by varying boards and MCUs. If possible, + * interrupts should be configured on both rising and falling edges + * so that contact and loss-of-contact events can be detected. + * + * attach - Attach the MXT interrupt handler to the GPIO interrupt + * enable - Enable or disable the GPIO interrupt + * clear - Acknowledge/clear any pending GPIO interrupt + */ + + int (*attach)(FAR const struct mxt_lower_s *lower, mxt_handler_t isr, + FAR void *arg); + void (*enable)(FAR const struct mxt_lower_s *lower, bool enable); + void (*clear)(FAR const struct mxt_lower_s *lower); +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mxt_register + * + * Description: + * Configure the maXTouch to use the provided I2C device instance. This + * will register the driver as /dev/inputN where N is the minor device + * number + * + * Input Parameters: + * i2c - An I2C driver instance + * lower - Persistent board configuration data + * minor - The input device minor number + * + * Returned Value: + * Zero is returned on success. Otherwise, a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int mxt_register(FAR struct i2c_dev_s *i2c, + FAR const struct mxt_lower_s *lower, int minor); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_INPUT && CONFIG_INPUT_MXT */ +#endif /* __INCLUDE_NUTTX_INPUT_MXT_H */ |