From 488a8fa9d5c85bd95b4e6ba8e6468f830d3b5a9d Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 30 Jul 2011 17:41:22 +0000 Subject: [PATCH] Completes first cut of TSC2007 driver git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3831 42af7a65-404d-4744-a932-0658087f49c3 --- ChangeLog | 3 + drivers/input/tsc2007.c | 212 ++++++++++++++++++++++++++++------ include/nuttx/i2c.h | 4 +- include/nuttx/input/tsc2007.h | 2 +- include/nuttx/ioctl.h | 6 + 5 files changed, 190 insertions(+), 37 deletions(-) diff --git a/ChangeLog b/ChangeLog index ea4a769a77..0650dac62c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1955,4 +1955,7 @@ * graphics/nxfonts/nxfonts_*.h: Add serveral more new fonts * arch/z80/src/ez80/ez80_serial.c: Fix some errors in serial driver setup for UART1 (submitted by Paul Osmialowski). + * drivers/input/tsc2007.c and include/nuttx/input/*: Add a generic NuttX + touchscreen interface. Add a driver for the TI TSC2007 touchscreen + controller. diff --git a/drivers/input/tsc2007.c b/drivers/input/tsc2007.c index 7af6711bc2..e79bb35682 100644 --- a/drivers/input/tsc2007.c +++ b/drivers/input/tsc2007.c @@ -67,6 +67,8 @@ #include #include #include + +#include #include #include "tsc2007.h" @@ -74,6 +76,13 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ +/* Configuration ************************************************************/ +/* Reference counting is partially implemented, but not needed in the + * current design. + */ + +#undef CONFIG_TSC2007_REFCNT + /* Driver support ***********************************************************/ /* This format is used to construct the /dev/skel[n] device driver path. It * defined here so that it will be used consistently in all places. @@ -85,11 +94,22 @@ /**************************************************************************** * Private Types ****************************************************************************/ + +/* This describes the state of one contact */ + +enum tsc2007_contact_3 +{ + CONTACT_NONE = 0, /* No contact */ + CONTACT_DOWN, /* First contact */ + CONTACT_MOVE, /* Same contact, possibly different position */ + CONTACT_UP, /* Contact lost */ +}; + /* This structure describes the results of one TSC2007 sample */ struct tsc2007_sample_s { - bool pendown; /* Pen down state */ + uint8_t contact; /* Contact state (see enum tsc2007_contact_e) */ uint16_t x; /* Measured X position */ uint16_t y; /* Measured Y position */ uint16_t pressure; /* Calculated pressure */ @@ -102,8 +122,11 @@ struct tsc2007_dev_s #ifdef CONFIG_TSC2007_MULTIPLE FAR struct tsc2007_dev_s *flink; /* Supports a singly linked list of drivers */ #endif +#ifdef CONFIG_TSC2007_REFCNT uint8_t crefs; /* Number of times the device has been opened */ +#endif uint8_t nwaiters; /* Number of threads waiting for TSC2007 data */ + uint8_t id; /* Current touch point ID */ volatile bool penchange; /* 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 */ @@ -111,7 +134,7 @@ struct tsc2007_dev_s FAR struct tsc2007_config_s *config; /* Board configuration data */ FAR struct i2c_dev_s *i2c; /* Saved I2C driver instance */ struct work_s work; /* Supports the interrupt handling "bottom half" */ - struct tsc2007_sample_s sample; /* Last sampled data */ + struct tsc2007_sample_s sample; /* Last sampled touch point data */ /* The following is a list if poll structures of threads waiting for * driver events. The 'struct pollfd' reference for each open is also @@ -251,6 +274,23 @@ static int tsc2007_sample(FAR struct tsc2007_dev_s *priv, */ memcpy(sample, &priv->sample, sizeof(struct tsc2007_sample_s )); + + /* Now manage state transitions */ + + if (sample->contact == CONTACT_UP) + { + /* Next.. no contract. Increment the ID so that next contact will be unique */ + + priv->sample.contact = CONTACT_NONE; + priv->id++; + } + else if (sample->contact == CONTACT_DOWN) + { + /* First report -- next report will be a movement */ + + priv->sample.contact = CONTACT_MOVE; + } + priv->penchange = false; ret = OK; } @@ -455,9 +495,11 @@ static void tsc2007_worker(FAR void *arg) if (!pendown) { - /* Ignore the interrupt if the pen was already down */ + /* Ignore the interrupt if the pen was already down (CONTACT_NONE == pen up and + * already reported. CONTACT_UP == pen up, but not reported) + */ - if (!priv->sample.pendown) + if (priv->sample.contact == CONTACT_NONE) { goto errout; } @@ -556,8 +598,31 @@ static void tsc2007_worker(FAR void *arg) /* Note the availability of new measurements */ - priv->sample.pendown = pendown; - priv->penchange = true; + if (pendown) + { + /* If this is the first (acknowledged) pend 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. + */ + + if (priv->sample.contact != CONTACT_MOVE) + { + /* First contact */ + + priv->sample.contact = CONTACT_DOWN; + } + } + else /* if (priv->sample.contact != CONTACT_NONE) */ + { + /* The pen is up. NOTE: We know from a previous test, that this is a + * loss of contact condition. This will be changed to CONTACT_NONE + * after the loss of contact is sampled. + */ + + priv->sample.contact = CONTACT_UP; + } + + priv->penchange = true; /* Notify any waiters that nes TSC2007 data is available */ @@ -626,6 +691,7 @@ static int tsc2007_interrupt(int irq, FAR void *context) static int tsc2007_open(FAR struct file *filep) { +#ifdef CONFIG_TSC2007_REFCNT FAR struct inode *inode; FAR struct tsc2007_dev_s *priv; uint8_t tmp; @@ -659,17 +725,9 @@ static int tsc2007_open(FAR struct file *filep) goto errout_with_sem; } - /* Check if this is the first time that the driver has been opened. */ - - if (tmp == 1) - { - irqstate_t flags = irqsave(); - - /* Perform one time hardware initialization */ - -#warning "Missing logic" - irqrestore(flags); - } + /* When the reference increments to 1, this is the first open event + * on the driver.. and an opportunity to do any one-time initialization. + */ /* Save the new open count on success */ @@ -678,6 +736,9 @@ static int tsc2007_open(FAR struct file *filep) errout_with_sem: sem_post(&priv->devsem); return ret; +#else + return OK; +#endif } /**************************************************************************** @@ -686,6 +747,7 @@ errout_with_sem: static int tsc2007_close(FAR struct file *filep) { +#ifdef CONFIG_TSC2007_REFCNT FAR struct inode *inode; FAR struct tsc2007_dev_s *priv; int ret; @@ -707,22 +769,18 @@ static int tsc2007_close(FAR struct file *filep) return -EINTR; } - /* Decrement the reference count unless it would decrement to zero */ - if (priv->crefs > 1) + /* 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) { priv->crefs--; - sem_post(&priv->devsem); - return OK; } - /* There are no more references to the port */ - - priv->crefs = 0; - - /* Perform driver teardown */ -#warning "Missing logic" - sem_post(&priv->devsem); +#endif return OK; } @@ -732,10 +790,11 @@ static int tsc2007_close(FAR struct file *filep) static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len) { - FAR struct inode *inode; - FAR struct tsc2007_dev_s *priv; - struct tsc2007_sample_s sample; - int ret; + FAR struct inode *inode; + FAR struct tsc2007_dev_s *priv; + FAR struct touch_sample_s *report; + struct tsc2007_sample_s sample; + int ret; DEBUGASSERT(filep); inode = filep->f_inode; @@ -743,6 +802,19 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len DEBUGASSERT(inode && inode->i_private); priv = (FAR struct tsc2007_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); @@ -785,7 +857,36 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len * to the caller. */ -#warning "Missing logic" + report = (FAR struct touch_sample_s *)buffer; + memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1)); + report->npoints = 1; + report->point[0].id = priv->id; + report->point[0].x = sample.x; + report->point[0].y = sample.y; + report->point[0].pressure = sample.pressure; + + /* Report the appropriate flags */ + + if (sample.contact == CONTACT_UP) + { + /* Pen is now up */ + + report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID; + } + else if (sample.contact == CONTACT_DOWN) + { + /* First contact */ + + report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID; + } + else /* if (sample->contact == CONTACT_MOVE) */ + { + /* Movement of the same contact */ + + report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID; + } + + ret = SIZEOF_TOUCH_SAMPLE_S(1); errout: sem_post(&priv->devsem); @@ -824,7 +925,37 @@ static int tsc2007_ioctl(FAR struct file *filep, int cmd, unsigned long arg) switch (cmd) { - /* Add support for ioctl commands here */ + case TSIOC_SETCALIB: /* arg: Pointer to int calibration value */ + { + FAR int *ptr = (FAR int *)((uintptr_t)arg); + DEBUGASSERT(priv->config != NULL && ptr != NULL); + priv->config->rxplate = *ptr; + } + break; + + case TSIOC_GETCALIB: /* arg: Pointer to int calibration value */ + { + FAR int *ptr = (FAR int *)((uintptr_t)arg); + DEBUGASSERT(priv->config != NULL && ptr != NULL); + *ptr = priv->config->rxplate; + } + break; + + case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */ + { + FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg); + DEBUGASSERT(priv->config != NULL && ptr != NULL); + priv->config->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->config != NULL && ptr != NULL); + *ptr = priv->config->frequency; + } + break; default: ret = -ENOTTY; @@ -996,6 +1127,19 @@ int tsc2007_register(FAR struct i2c_dev_s *dev, sem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */ sem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */ + /* Set the I2C frequency (saving the actual frequency) */ + + config->frequency = I2C_SETFREQUENCY(dev, config->frequency); + + /* Set the I2C address and address size */ + + ret = I2C_SETADDRESS(dev, config->address, 7); + if (ret < 0) + { + idbg("I2C_SETADDRESS failed: %d\n", ret); + goto errout_with_priv; + } + /* Make sure that interrupts are disabled */ config->clear(config); diff --git a/include/nuttx/i2c.h b/include/nuttx/i2c.h index 56c1d81018..d72b54b29e 100644 --- a/include/nuttx/i2c.h +++ b/include/nuttx/i2c.h @@ -114,7 +114,7 @@ * ****************************************************************************/ -#define I2C_SETADDRESS(d,f,b) ((d)->ops->setaddress(d,f,b)) +#define I2C_SETADDRESS(d,a,n) ((d)->ops->setaddress(d,a,n)) /**************************************************************************** * Name: I2C_SETOWNADDRESS @@ -140,7 +140,7 @@ * ****************************************************************************/ -#define I2C_SETOWNADDRESS(d,f,b) ((d)->ops->setownaddress(d,f,b)) +#define I2C_SETOWNADDRESS(d,a,n) ((d)->ops->setownaddress(d,a,n)) /**************************************************************************** * Name: I2C_WRITE diff --git a/include/nuttx/input/tsc2007.h b/include/nuttx/input/tsc2007.h index 8956c61b9e..449b46b222 100644 --- a/include/nuttx/input/tsc2007.h +++ b/include/nuttx/input/tsc2007.h @@ -121,7 +121,7 @@ struct tsc2007_config_s * pendown - Return the state of the pen down GPIO input */ - int (*attach)(FAR struct tsc2007_config_s *state, xcpt_t isr); + int (*attach)(FAR struct tsc2007_config_s *state, xcpt_t isr); void (*enable)(FAR struct tsc2007_config_s *state, bool enable); void (*clear)(FAR struct tsc2007_config_s *state); bool (*pendown)(FAR struct tsc2007_config_s *state); diff --git a/include/nuttx/ioctl.h b/include/nuttx/ioctl.h index a13518a7a0..0ce363f245 100644 --- a/include/nuttx/ioctl.h +++ b/include/nuttx/ioctl.h @@ -59,6 +59,7 @@ #define _MTDIOCBASE (0x8a00) /* MTD ioctl commands */ #define _SIOCBASE (0x8b00) /* Socket ioctl commands */ #define _ARPBASE (0x8c00) /* ARP ioctl commands */ +#define _TSBASE (0x8d00) /* Touchscreen ioctl commands */ /* Macros used to manage ioctl commands */ @@ -160,6 +161,11 @@ #define _ARPIOCVALID(c) (_IOC_TYPE(c)==_ARPBASE) #define _ARPIOC(nr) _IOC(_ARPBASE,nr) +/* NuttX ARP touchscrren ioctl definitions (see nuttx/input/touchscreen.h) **/ + +#define _TSIOCVALID(c) (_IOC_TYPE(c)==_TSBASE) +#define _TSIOC(nr) _IOC(_TSBASE,nr) + /**************************************************************************** * Public Type Definitions ****************************************************************************/