summaryrefslogtreecommitdiff
path: root/nuttx/drivers/input
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-07-05 11:47:54 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-07-05 11:47:54 -0600
commita649e0e415f32a9daba3799fc400ebcf5400cead (patch)
treec9396395e865f0a7fc170995c319bd12c85b2ba8 /nuttx/drivers/input
parent1d1e833f4da575b36d6f62581c12bd6a894be206 (diff)
downloadnuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.tar.gz
nuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.tar.bz2
nuttx-a649e0e415f32a9daba3799fc400ebcf5400cead.zip
Add very basic support for the Atmel maXTouch touchscreen controller
Diffstat (limited to 'nuttx/drivers/input')
-rw-r--r--nuttx/drivers/input/Kconfig17
-rw-r--r--nuttx/drivers/input/Make.defs4
-rw-r--r--nuttx/drivers/input/mxt.c1752
-rw-r--r--nuttx/drivers/input/tsc2007.c22
-rw-r--r--nuttx/drivers/input/tsc2007.h3
5 files changed, 1786 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 *)&regval, 1);
+ if (ret)
+ {
+ goto errout_with_objtab;
+ }
+
+ info->xsize = regval;
+
+ ret = mxt_getreg(priv, MXT_MATRIX_Y_SIZE, (FAR uint8_t *)&regval, 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