ft5x06.c 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. /****************************************************************************
  2. * drivers/input/ft5x06.c
  3. *
  4. * Copyright (C) 2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Some of this driver was developed with input from NXP sample code for
  8. * the LPCXpresso-LPC54628 board. That sample code as a compatible BSD
  9. * license:
  10. *
  11. * Copyright (c) 2016, Freescale Semiconductor, Inc.
  12. * Copyright 2016-2017 NXP
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions
  16. * are met:
  17. *
  18. * 1. Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. * 2. Redistributions in binary form must reproduce the above copyright
  21. * notice, this list of conditions and the following disclaimer in
  22. * the documentation and/or other materials provided with the
  23. * distribution.
  24. * 3. Neither the name NuttX nor the names of its contributors may be
  25. * used to endorse or promote products derived from this software
  26. * without specific prior written permission.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  29. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  30. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  31. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  32. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  33. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  34. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  35. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  36. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  37. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  38. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  39. * POSSIBILITY OF SUCH DAMAGE.
  40. *
  41. ****************************************************************************/
  42. /* References:
  43. * "FT5x06", FocalTech Systems Co., Ltd, D-FT5x06-1212-V4.0, Revised
  44. * Dec. 18, 2012
  45. */
  46. /* The FT5x06 Series ICs are single-chip capacitive touch panel controller
  47. * ICs with a built-in 8 bit Micro-controller unit (MCU). They adopt the
  48. * mutual capacitance approach, which supports true multi-touch capability.
  49. * In conjunction with a mutual capacitive touch panel, the FT5x06 have
  50. * user-friendly input functions, which can be applied on many portable
  51. * devices, such as cellular phones, MIDs, netbook and notebook personal
  52. * computers.
  53. */
  54. /****************************************************************************
  55. * Included Files
  56. ****************************************************************************/
  57. #include <nuttx/config.h>
  58. #include <sys/types.h>
  59. #include <stdbool.h>
  60. #include <stdio.h>
  61. #include <unistd.h>
  62. #include <string.h>
  63. #include <fcntl.h>
  64. #include <poll.h>
  65. #include <errno.h>
  66. #include <assert.h>
  67. #include <debug.h>
  68. #include <nuttx/irq.h>
  69. #include <nuttx/kmalloc.h>
  70. #include <nuttx/arch.h>
  71. #include <nuttx/fs/fs.h>
  72. #include <nuttx/i2c/i2c_master.h>
  73. #include <nuttx/semaphore.h>
  74. #include <nuttx/wqueue.h>
  75. #include <nuttx/wdog.h>
  76. #include <nuttx/input/touchscreen.h>
  77. #include <nuttx/input/ft5x06.h>
  78. #include "ft5x06.h"
  79. /****************************************************************************
  80. * Pre-processor Definitions
  81. ****************************************************************************/
  82. /* Driver support ***********************************************************/
  83. /* This format is used to construct the /dev/input[n] device driver path. It
  84. * defined here so that it will be used consistently in all places.
  85. */
  86. #define DEV_FORMAT "/dev/input%d"
  87. #define DEV_NAMELEN 16
  88. /* In polled mode, the polling rate will decrease when there is no touch
  89. * activity. These definitions represent the maximum and the minimum
  90. * polling rates.
  91. */
  92. #define POLL_MINDELAY MSEC2TICK(50)
  93. #define POLL_MAXDELAY MSEC2TICK(200)
  94. #define POLL_INCREMENT MSEC2TICK(10)
  95. /****************************************************************************
  96. * Private Types
  97. ****************************************************************************/
  98. /* This structure describes the state of one FT5x06 driver instance */
  99. struct ft5x06_dev_s
  100. {
  101. uint8_t crefs; /* Number of times the device
  102. * has been opened */
  103. uint8_t nwaiters; /* Number of threads waiting for
  104. * FT5x06 data */
  105. volatile bool valid; /* True: New, valid touch data
  106. * in touchbuf[] */
  107. #ifdef CONFIG_FT5X06_SINGLEPOINT
  108. uint8_t lastid; /* Last reported touch id */
  109. uint8_t lastevent; /* Last reported event */
  110. int16_t lastx; /* Last reported X position */
  111. int16_t lasty; /* Last reported Y position */
  112. #endif
  113. sem_t devsem; /* Manages exclusive access to
  114. * this structure */
  115. sem_t waitsem; /* Used to wait for the
  116. * availability of data */
  117. uint32_t frequency; /* Current I2C frequency */
  118. #ifdef CONFIG_FT5X06_POLLMODE
  119. uint32_t delay; /* Current poll delay */
  120. #endif
  121. FAR const struct ft5x06_config_s *config; /* Board configuration data */
  122. FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */
  123. struct work_s work; /* Supports the interrupt
  124. * handling "bottom half" */
  125. #ifdef CONFIG_FT5X06_POLLMODE
  126. struct wdog_s polltimer; /* Poll timer */
  127. #endif
  128. uint8_t touchbuf[FT5X06_TOUCH_DATA_LEN]; /* Raw touch data */
  129. /* The following is a list if poll structures of threads waiting for
  130. * driver events. The 'struct pollfd' reference for each open is also
  131. * retained in the f_priv field of the 'struct file'.
  132. */
  133. struct pollfd *fds[CONFIG_FT5X06_NPOLLWAITERS];
  134. };
  135. /****************************************************************************
  136. * Private Function Prototypes
  137. ****************************************************************************/
  138. static void ft5x06_notify(FAR struct ft5x06_dev_s *priv);
  139. static void ft5x06_data_worker(FAR void *arg);
  140. #ifdef CONFIG_FT5X06_POLLMODE
  141. static void ft5x06_poll_timeout(wdparm_t arg);
  142. #else
  143. static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg);
  144. #endif
  145. static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
  146. size_t len);
  147. static ssize_t ft5x06_waitsample(FAR struct ft5x06_dev_s *priv,
  148. FAR char *buffer, size_t len);
  149. static int ft5x06_bringup(FAR struct ft5x06_dev_s *priv);
  150. static void ft5x06_shutdown(FAR struct ft5x06_dev_s *priv);
  151. /* Character driver methods */
  152. static int ft5x06_open(FAR struct file *filep);
  153. static int ft5x06_close(FAR struct file *filep);
  154. static ssize_t ft5x06_read(FAR struct file *filep, FAR char *buffer,
  155. size_t len);
  156. static int ft5x06_ioctl(FAR struct file *filep, int cmd,
  157. unsigned long arg);
  158. static int ft5x06_poll(FAR struct file *filep, struct pollfd *fds,
  159. bool setup);
  160. /****************************************************************************
  161. * Private Data
  162. ****************************************************************************/
  163. /* This the vtable that supports the character driver interface */
  164. static const struct file_operations ft5x06_fops =
  165. {
  166. ft5x06_open, /* open */
  167. ft5x06_close, /* close */
  168. ft5x06_read, /* read */
  169. NULL, /* write */
  170. NULL, /* seek */
  171. ft5x06_ioctl, /* ioctl */
  172. ft5x06_poll /* poll */
  173. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  174. , NULL /* unlink */
  175. #endif
  176. };
  177. /* Maps FT5x06 touch events into bit encoded representation used by NuttX */
  178. static const uint8_t g_event_map[4] =
  179. {
  180. (TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID), /* FT5X06_DOWN */
  181. (TOUCH_UP | TOUCH_ID_VALID), /* FT5X06_UP */
  182. (TOUCH_MOVE | TOUCH_ID_VALID | TOUCH_POS_VALID), /* FT5X06_CONTACT */
  183. TOUCH_ID_VALID /* FT5X06_INVALID */
  184. };
  185. /****************************************************************************
  186. * Private Functions
  187. ****************************************************************************/
  188. /****************************************************************************
  189. * Name: ft5x06_notify
  190. ****************************************************************************/
  191. static void ft5x06_notify(FAR struct ft5x06_dev_s *priv)
  192. {
  193. int i;
  194. /* If there are threads waiting on poll() for FT5x06 data to become
  195. * available, then wake them up now. NOTE: we wake up all waiting threads
  196. * because we do not know that they are going to do. If they all try to
  197. * read the data, then some make end up blocking after all.
  198. */
  199. for (i = 0; i < CONFIG_FT5X06_NPOLLWAITERS; i++)
  200. {
  201. struct pollfd *fds = priv->fds[i];
  202. if (fds)
  203. {
  204. fds->revents |= POLLIN;
  205. iinfo("Report events: %02x\n", fds->revents);
  206. nxsem_post(fds->sem);
  207. }
  208. }
  209. /* If there are threads waiting for read data, then signal one of them
  210. * that the read data is available.
  211. */
  212. if (priv->nwaiters > 0)
  213. {
  214. /* After posting this semaphore, we need to exit because the FT5x06
  215. * is no longer available.
  216. */
  217. nxsem_post(&priv->waitsem);
  218. }
  219. }
  220. /****************************************************************************
  221. * Name: ft5x06_data_worker
  222. ****************************************************************************/
  223. static void ft5x06_data_worker(FAR void *arg)
  224. {
  225. FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg;
  226. FAR const struct ft5x06_config_s *config;
  227. FAR struct ft5x06_touch_data_s *sample;
  228. struct i2c_msg_s msg[2];
  229. uint8_t regaddr;
  230. int ret;
  231. /* Get a pointer the callbacks for convenience */
  232. DEBUGASSERT(priv != NULL && priv->config != NULL);
  233. config = priv->config;
  234. /* We need to have exclusive access to the touchbuf so that we do not
  235. * corrupt any read operation that is in place.
  236. */
  237. do
  238. {
  239. ret = nxsem_wait_uninterruptible(&priv->devsem);
  240. /* This would only fail if something canceled the worker thread?
  241. * That is not expected.
  242. */
  243. DEBUGASSERT(ret == OK || ret == -ECANCELED);
  244. }
  245. while (ret < 0);
  246. /* Read touch data */
  247. /* Set up the address write operation */
  248. regaddr = FT5X06_TOUCH_DATA_STARTREG;
  249. msg[0].frequency = priv->frequency; /* I2C frequency */
  250. msg[0].addr = config->address; /* 7-bit address */
  251. msg[0].flags = 0; /* Write transaction with START */
  252. msg[0].buffer = &regaddr; /* Send one byte of data (no STOP) */
  253. msg[0].length = 1;
  254. /* Set up the data read operation.
  255. *
  256. * REVISIT: If CONFIG_FT5X06_SINGLEPOINT is selected, could we not just
  257. * set the length for one sample? Or is there some reason why we have to
  258. * read all of the points?
  259. */
  260. msg[1].frequency = priv->frequency; /* I2C frequency */
  261. msg[1].addr = config->address; /* 7-bit address */
  262. msg[1].flags = I2C_M_READ; /* Read transaction with Re-START */
  263. msg[1].buffer = priv->touchbuf; /* Read all touch data */
  264. msg[1].length = FT5X06_TOUCH_DATA_LEN;
  265. ret = I2C_TRANSFER(priv->i2c, msg, 2);
  266. if (ret >= 0)
  267. {
  268. /* In polled mode, we may read invalid touch data. If there is
  269. * no touch data, the FT5x06 returns all 0xff the very first time.
  270. * After that, it returns the same old stale data when there is
  271. * no touch data.
  272. */
  273. sample = (FAR struct ft5x06_touch_data_s *)priv->touchbuf;
  274. /* Notify waiters (only if we ready some valid data).
  275. *
  276. * REVISIT: For improved performance consider moving the duplicate
  277. * report and thresholding logic from ft5x06_sample() to here. That
  278. * would save a context switch.
  279. */
  280. if (sample->tdstatus <= FT5X06_MAX_TOUCHES)
  281. {
  282. /* Notify any waiters that new FT5x06 data is available */
  283. priv->valid = true;
  284. ft5x06_notify(priv);
  285. }
  286. #ifdef CONFIG_FT5X06_POLLMODE
  287. /* Update the poll rate */
  288. if (sample->tdstatus > 0 && sample->tdstatus <= FT5X06_MAX_TOUCHES)
  289. {
  290. /* Keep it at the minimum if touches are detected. */
  291. priv->delay = POLL_MINDELAY;
  292. }
  293. else if (priv->delay < POLL_MAXDELAY)
  294. {
  295. /* Otherwise, let the poll rate rise gradually up to the maximum
  296. * if there is no touch.
  297. */
  298. priv->delay += POLL_INCREMENT;
  299. }
  300. #endif
  301. }
  302. #ifdef CONFIG_FT5X06_POLLMODE
  303. /* Exit, re-starting the poll. */
  304. wd_start(&priv->polltimer, priv->delay,
  305. ft5x06_poll_timeout, (wdparm_t)priv);
  306. #else
  307. /* Exit, re-enabling FT5x06 interrupts */
  308. config->enable(config, true);
  309. #endif
  310. nxsem_post(&priv->devsem);
  311. }
  312. /****************************************************************************
  313. * Name: ft5x06_poll_timeout
  314. ****************************************************************************/
  315. #ifdef CONFIG_FT5X06_POLLMODE
  316. static void ft5x06_poll_timeout(wdparm_t arg)
  317. {
  318. FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg;
  319. int ret;
  320. /* Transfer processing to the worker thread. Since FT5x06 poll timer is
  321. * disabled while the work is pending, no special action should be
  322. * required to protected the work queue.
  323. */
  324. DEBUGASSERT(priv->work.worker == NULL);
  325. ret = work_queue(HPWORK, &priv->work, ft5x06_data_worker, priv, 0);
  326. if (ret != 0)
  327. {
  328. ierr("ERROR: Failed to queue work: %d\n", ret);
  329. }
  330. }
  331. #endif
  332. /****************************************************************************
  333. * Name: ft5x06_data_interrupt
  334. ****************************************************************************/
  335. #ifndef CONFIG_FT5X06_POLLMODE
  336. static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg)
  337. {
  338. FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg;
  339. FAR const struct ft5x06_config_s *config;
  340. int ret;
  341. /* Get a pointer the callbacks for convenience (and so the code is not so
  342. * ugly).
  343. */
  344. config = priv->config;
  345. DEBUGASSERT(config != NULL);
  346. /* Disable further interrupts */
  347. config->enable(config, false);
  348. /* Transfer processing to the worker thread. Since FT5x06 interrupts are
  349. * disabled while the work is pending, no special action should be required
  350. * to protected the work queue.
  351. */
  352. DEBUGASSERT(priv->work.worker == NULL);
  353. ret = work_queue(HPWORK, &priv->work, ft5x06_data_worker, priv, 0);
  354. if (ret != 0)
  355. {
  356. ierr("ERROR: Failed to queue work: %d\n", ret);
  357. }
  358. /* Clear any pending interrupts and return success */
  359. config->clear(config);
  360. return OK;
  361. }
  362. #endif
  363. /****************************************************************************
  364. * Name: ft5x06_sample
  365. ****************************************************************************/
  366. #ifdef CONFIG_FT5X06_SINGLEPOINT
  367. static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
  368. size_t len)
  369. {
  370. FAR struct ft5x06_touch_data_s *raw;
  371. FAR struct ft5x06_touch_point_s *touch;
  372. FAR struct touch_sample_s *sample;
  373. FAR struct touch_point_s *point;
  374. int16_t x;
  375. int16_t y;
  376. uint8_t event;
  377. uint8_t id;
  378. if (!priv->valid)
  379. {
  380. return 0; /* Nothing to read */
  381. }
  382. /* Raw data pointers (source) */
  383. raw = (FAR struct ft5x06_touch_data_s *)priv->touchbuf;
  384. touch = raw->touch;
  385. /* Get the reported X and Y positions */
  386. #ifdef CONFIG_FT5X06_SWAPXY
  387. y = TOUCH_POINT_GET_X(touch[0]);
  388. x = TOUCH_POINT_GET_Y(touch[0]);
  389. #else
  390. x = TOUCH_POINT_GET_X(touch[0]);
  391. y = TOUCH_POINT_GET_Y(touch[0]);
  392. #endif
  393. /* Get the touch point ID and event */
  394. event = TOUCH_POINT_GET_EVENT(touch[0]);
  395. id = TOUCH_POINT_GET_ID(touch[0]);
  396. if (event == FT5X06_INVALID)
  397. {
  398. priv->lastevent = FT5X06_INVALID;
  399. goto reset_and_drop;
  400. }
  401. if (id == priv->lastid && event == priv->lastevent)
  402. {
  403. /* Same ID and event.. Is there positional data? */
  404. if (raw->tdstatus == 0 || event == FT5X06_UP)
  405. {
  406. /* No... no new touch data */
  407. goto reset_and_drop;
  408. }
  409. else
  410. {
  411. int16_t deltax;
  412. int16_t deltay;
  413. /* Compare the change in position from the last report. */
  414. deltax = (x - priv->lastx);
  415. if (deltax < 0)
  416. {
  417. deltax = -deltax;
  418. }
  419. if (deltax < CONFIG_FT5X06_THRESHX)
  420. {
  421. /* There as been no significant change in X, try Y */
  422. deltay = (y - priv->lasty);
  423. if (deltay < 0)
  424. {
  425. deltay = -deltay;
  426. }
  427. if (deltax < CONFIG_FT5X06_THRESHX)
  428. {
  429. /* Ignore... no significant change in Y either */
  430. goto drop;
  431. }
  432. }
  433. }
  434. }
  435. priv->lastid = id;
  436. priv->lastevent = event;
  437. priv->lastx = x;
  438. priv->lasty = y;
  439. /* User data buffer points (sink) */
  440. /* Return the number of touches read */
  441. sample = (FAR struct touch_sample_s *)buffer;
  442. sample->npoints = 1;
  443. /* Decode and return the single touch point */
  444. point = sample->point;
  445. point[0].id = id;
  446. point[0].flags = g_event_map[event];
  447. point[0].x = x;
  448. point[0].y = y;
  449. point[0].h = 0;
  450. point[0].w = 0;
  451. point[0].pressure = 0;
  452. priv->valid = false;
  453. return SIZEOF_TOUCH_SAMPLE_S(1);
  454. reset_and_drop:
  455. priv->lastx = 0;
  456. priv->lasty = 0;
  457. drop:
  458. priv->valid = false;
  459. return 0; /* No new touches read. */
  460. }
  461. #else
  462. static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
  463. size_t len)
  464. {
  465. FAR struct ft5x06_touch_data_s *raw;
  466. FAR struct ft5x06_touch_point_s *touch;
  467. FAR struct touch_sample_s *sample;
  468. FAR struct touch_point_s *point;
  469. unsigned int maxtouches;
  470. unsigned int ntouches;
  471. int i;
  472. maxtouches = (len - sizeof(int)) / sizeof(struct touch_point_s);
  473. DEBUGASSERT(maxtouches > 0); /* Already verified */
  474. if (!priv->valid)
  475. {
  476. return 0; /* Nothing to read */
  477. }
  478. /* Raw data pointers (source) */
  479. raw = (FAR struct ft5x06_touch_data_s *)priv->touchbuf;
  480. touch = raw->touch;
  481. /* Decode number of touches */
  482. ntouches = raw->tdstatus;
  483. DEBUGASSERT(ntouches <= FT5X06_MAX_TOUCHES);
  484. if (ntouches > maxtouches)
  485. {
  486. ntouches = maxtouches;
  487. }
  488. if (ntouches < 1)
  489. {
  490. priv->valid = false;
  491. return 0; /* No touches read. */
  492. }
  493. /* User data buffer points (sink) */
  494. sample = (FAR struct touch_sample_s *)buffer;
  495. point = sample->point;
  496. /* Return the number of touches read */
  497. sample->npoints = ntouches;
  498. /* Decode and return the touch points */
  499. for (i = 0; i < ntouches; i++)
  500. {
  501. int event = TOUCH_POINT_GET_EVENT(touch[i]);
  502. point[i].id = TOUCH_POINT_GET_ID(touch[i]);
  503. point[i].flags = g_event_map[event];
  504. #ifdef CONFIG_FT5X06_SWAPXY
  505. point[i].y = TOUCH_POINT_GET_X(touch[i]);
  506. point[i].x = TOUCH_POINT_GET_Y(touch[i]);
  507. #else
  508. point[i].x = TOUCH_POINT_GET_X(touch[i]);
  509. point[i].y = TOUCH_POINT_GET_Y(touch[i]);
  510. #endif
  511. point[i].h = 0;
  512. point[i].w = 0;
  513. point[i].pressure = 0;
  514. }
  515. priv->valid = false;
  516. return SIZEOF_TOUCH_SAMPLE_S(ntouches);
  517. }
  518. #endif /* CONFIG_FT5X06_SINGLEPOINT */
  519. /****************************************************************************
  520. * Name: ft5x06_waitsample
  521. ****************************************************************************/
  522. static ssize_t ft5x06_waitsample(FAR struct ft5x06_dev_s *priv,
  523. FAR char *buffer, size_t len)
  524. {
  525. int ret;
  526. /* Disable pre-emption to prevent other threads from getting control while
  527. * we muck with the semaphores.
  528. */
  529. sched_lock();
  530. /* Now release the semaphore that manages mutually exclusive access to
  531. * the device structure. This may cause other tasks to become ready to
  532. * run, but they cannot run yet because pre-emption is disabled.
  533. */
  534. nxsem_post(&priv->devsem);
  535. /* Try to get the a sample... if we cannot, then wait on the semaphore
  536. * that is posted when new sample data is available.
  537. */
  538. while (!priv->valid)
  539. {
  540. /* Increment the count of waiters */
  541. priv->nwaiters++;
  542. /* Wait for a change in the FT5x06 state */
  543. ret = nxsem_wait(&priv->waitsem);
  544. priv->nwaiters--;
  545. if (ret < 0)
  546. {
  547. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  548. goto errout;
  549. }
  550. }
  551. /* Re-acquire the semaphore that manages mutually exclusive access to
  552. * the device structure. We may have to wait here. But we have our
  553. * sample. Interrupts and pre-emption will be re-enabled while we wait.
  554. */
  555. ret = nxsem_wait(&priv->devsem);
  556. if (ret >= 0)
  557. {
  558. /* Now sample the data.
  559. *
  560. * REVISIT: Is it safe to assume that priv->valid will always be
  561. * true? I think that sched_lock() would protect the setting.
  562. */
  563. ret = ft5x06_sample(priv, buffer, len);
  564. }
  565. errout:
  566. /* Restore pre-emption. We might get suspended here but that is okay
  567. * because we already have our sample. Note: this means that if there
  568. * were two threads reading from the FT5x06 for some reason, the data
  569. * might be read out of order.
  570. */
  571. sched_unlock();
  572. return ret;
  573. }
  574. /****************************************************************************
  575. * Name: ft5x06_bringup
  576. ****************************************************************************/
  577. static int ft5x06_bringup(FAR struct ft5x06_dev_s *priv)
  578. {
  579. FAR const struct ft5x06_config_s *config;
  580. struct i2c_msg_s msg;
  581. uint8_t data[2];
  582. int ret;
  583. /* Get a pointer the callbacks for convenience (and so the code is not so
  584. * ugly).
  585. */
  586. config = priv->config;
  587. DEBUGASSERT(config != NULL);
  588. /* Set device mode to normal operation */
  589. data[0] = FT5X06_TOUCH_MODE_REG; /* Register address */
  590. data[1] = FT5X06_DEV_MODE_WORKING; /* Normal mode */
  591. msg.frequency = priv->frequency; /* I2C frequency */
  592. msg.addr = config->address; /* 7-bit address */
  593. msg.flags = 0; /* Write transaction with START */
  594. msg.buffer = data; /* Send two bytes followed by STOP */
  595. msg.length = 2;
  596. ret = I2C_TRANSFER(priv->i2c, &msg, 1);
  597. if (ret < 0)
  598. {
  599. return ret;
  600. }
  601. #ifndef CONFIG_FT5X06_POLLMODE
  602. /* Enable FT5x06 interrupts */
  603. config->clear(config);
  604. config->enable(config, true);
  605. #endif
  606. return OK;
  607. }
  608. /****************************************************************************
  609. * Name: ft5x06_shutdown
  610. ****************************************************************************/
  611. static void ft5x06_shutdown(FAR struct ft5x06_dev_s *priv)
  612. {
  613. #ifdef CONFIG_FT5X06_POLLMODE
  614. /* Stop the poll timer */
  615. wd_cancel(&priv->polltimer);
  616. #else
  617. FAR const struct ft5x06_config_s *config = priv->config;
  618. /* Make sure that the FT5x06 interrupt is disabled */
  619. config->clear(config);
  620. config->enable(config, false);
  621. #endif
  622. }
  623. /****************************************************************************
  624. * Name: ft5x06_open
  625. ****************************************************************************/
  626. static int ft5x06_open(FAR struct file *filep)
  627. {
  628. FAR struct inode *inode;
  629. FAR struct ft5x06_dev_s *priv;
  630. uint8_t tmp;
  631. int ret;
  632. DEBUGASSERT(filep);
  633. inode = filep->f_inode;
  634. DEBUGASSERT(inode && inode->i_private);
  635. priv = (FAR struct ft5x06_dev_s *)inode->i_private;
  636. /* Get exclusive access to the driver data structure */
  637. ret = nxsem_wait(&priv->devsem);
  638. if (ret < 0)
  639. {
  640. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  641. return ret;
  642. }
  643. /* Increment the reference count */
  644. tmp = priv->crefs + 1;
  645. if (tmp == 0)
  646. {
  647. /* More than 255 opens; uint8_t overflows to zero */
  648. ret = -EMFILE;
  649. goto errout_with_sem;
  650. }
  651. /* When the reference increments to 1, this is the first open event
  652. * on the driver.. and the time when we must initialize the driver.
  653. */
  654. if (tmp == 1)
  655. {
  656. ret = ft5x06_bringup(priv);
  657. if (ret < 0)
  658. {
  659. ierr("ERROR: ft5x06_bringup failed: %d\n", ret);
  660. goto errout_with_sem;
  661. }
  662. }
  663. /* Save the new open count on success */
  664. priv->crefs = tmp;
  665. errout_with_sem:
  666. nxsem_post(&priv->devsem);
  667. return ret;
  668. }
  669. /****************************************************************************
  670. * Name: ft5x06_close
  671. ****************************************************************************/
  672. static int ft5x06_close(FAR struct file *filep)
  673. {
  674. FAR struct inode *inode;
  675. FAR struct ft5x06_dev_s *priv;
  676. int ret;
  677. DEBUGASSERT(filep);
  678. inode = filep->f_inode;
  679. DEBUGASSERT(inode && inode->i_private);
  680. priv = (FAR struct ft5x06_dev_s *)inode->i_private;
  681. /* Get exclusive access to the driver data structure */
  682. ret = nxsem_wait(&priv->devsem);
  683. if (ret < 0)
  684. {
  685. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  686. return ret;
  687. }
  688. /* Decrement the reference count unless it would decrement a negative
  689. * value.
  690. */
  691. if (priv->crefs >= 1)
  692. {
  693. priv->crefs--;
  694. }
  695. /* When the count decrements to zero, there are no further open references
  696. * to the driver and it can be uninitialized.
  697. */
  698. if (priv->crefs == 0)
  699. {
  700. ft5x06_shutdown(priv);
  701. }
  702. nxsem_post(&priv->devsem);
  703. return OK;
  704. }
  705. /****************************************************************************
  706. * Name: ft5x06_read
  707. ****************************************************************************/
  708. static ssize_t ft5x06_read(FAR struct file *filep, FAR char *buffer,
  709. size_t len)
  710. {
  711. FAR struct inode *inode;
  712. FAR struct ft5x06_dev_s *priv;
  713. int ret;
  714. DEBUGASSERT(filep);
  715. inode = filep->f_inode;
  716. DEBUGASSERT(inode && inode->i_private);
  717. priv = (FAR struct ft5x06_dev_s *)inode->i_private;
  718. /* Verify that the caller has provided a buffer large enough to receive
  719. * the touch data.
  720. */
  721. if (len < SIZEOF_TOUCH_SAMPLE_S(1))
  722. {
  723. /* We could provide logic to break up a touch report into segments and
  724. * handle smaller reads... but why?
  725. */
  726. return -ENOSYS;
  727. }
  728. /* Get exclusive access to the driver data structure */
  729. ret = nxsem_wait(&priv->devsem);
  730. if (ret < 0)
  731. {
  732. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  733. return ret;
  734. }
  735. /* Try to read sample data. */
  736. ret = ft5x06_sample(priv, buffer, len);
  737. while (ret == 0)
  738. {
  739. /* Sample data is not available now. We would have to wait to receive
  740. * sample data. If the user has specified the O_NONBLOCK option, then
  741. * just return an error.
  742. */
  743. if (filep->f_oflags & O_NONBLOCK)
  744. {
  745. ret = -EAGAIN;
  746. goto errout;
  747. }
  748. /* Wait for sample data */
  749. ret = ft5x06_waitsample(priv, buffer, len);
  750. if (ret < 0)
  751. {
  752. /* We might have been awakened by a signal */
  753. goto errout;
  754. }
  755. }
  756. ret = SIZEOF_TOUCH_SAMPLE_S(1);
  757. errout:
  758. nxsem_post(&priv->devsem);
  759. return ret;
  760. }
  761. /****************************************************************************
  762. * Name: ft5x06_ioctl
  763. ****************************************************************************/
  764. static int ft5x06_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
  765. {
  766. FAR struct inode *inode;
  767. FAR struct ft5x06_dev_s *priv;
  768. int ret;
  769. iinfo("cmd: %d arg: %ld\n", cmd, arg);
  770. DEBUGASSERT(filep);
  771. inode = filep->f_inode;
  772. DEBUGASSERT(inode && inode->i_private);
  773. priv = (FAR struct ft5x06_dev_s *)inode->i_private;
  774. /* Get exclusive access to the driver data structure */
  775. ret = nxsem_wait(&priv->devsem);
  776. if (ret < 0)
  777. {
  778. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  779. return ret;
  780. }
  781. /* Process the IOCTL by command */
  782. switch (cmd)
  783. {
  784. case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
  785. {
  786. FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
  787. DEBUGASSERT(priv->config != NULL && ptr != NULL);
  788. priv->frequency = *ptr;
  789. }
  790. break;
  791. case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
  792. {
  793. FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
  794. DEBUGASSERT(priv->config != NULL && ptr != NULL);
  795. *ptr = priv->frequency;
  796. }
  797. break;
  798. default:
  799. ret = -ENOTTY;
  800. break;
  801. }
  802. nxsem_post(&priv->devsem);
  803. return ret;
  804. }
  805. /****************************************************************************
  806. * Name: ft5x06_poll
  807. ****************************************************************************/
  808. static int ft5x06_poll(FAR struct file *filep, FAR struct pollfd *fds,
  809. bool setup)
  810. {
  811. FAR struct inode *inode;
  812. FAR struct ft5x06_dev_s *priv;
  813. int ret;
  814. int i;
  815. iinfo("setup: %d\n", (int)setup);
  816. DEBUGASSERT(filep && fds);
  817. inode = filep->f_inode;
  818. DEBUGASSERT(inode && inode->i_private);
  819. priv = (FAR struct ft5x06_dev_s *)inode->i_private;
  820. /* Are we setting up the poll? Or tearing it down? */
  821. ret = nxsem_wait(&priv->devsem);
  822. if (ret < 0)
  823. {
  824. ierr("ERROR: nxsem_wait failed: %d\n", ret);
  825. return ret;
  826. }
  827. if (setup)
  828. {
  829. /* Ignore waits that do not include POLLIN */
  830. if ((fds->events & POLLIN) == 0)
  831. {
  832. ierr("ERROR: Missing POLLIN: revents: %08x\n", fds->revents);
  833. ret = -EDEADLK;
  834. goto errout;
  835. }
  836. /* This is a request to set up the poll. Find an available
  837. * slot for the poll structure reference
  838. */
  839. for (i = 0; i < CONFIG_FT5X06_NPOLLWAITERS; i++)
  840. {
  841. /* Find an available slot */
  842. if (!priv->fds[i])
  843. {
  844. /* Bind the poll structure and this slot */
  845. priv->fds[i] = fds;
  846. fds->priv = &priv->fds[i];
  847. break;
  848. }
  849. }
  850. if (i >= CONFIG_FT5X06_NPOLLWAITERS)
  851. {
  852. ierr("ERROR: No available slot found: %d\n", i);
  853. fds->priv = NULL;
  854. ret = -EBUSY;
  855. goto errout;
  856. }
  857. /* Should we immediately notify on any of the requested events? */
  858. if (priv->valid)
  859. {
  860. ft5x06_notify(priv);
  861. }
  862. }
  863. else if (fds->priv)
  864. {
  865. /* This is a request to tear down the poll. */
  866. struct pollfd **slot = (struct pollfd **)fds->priv;
  867. DEBUGASSERT(slot != NULL);
  868. /* Remove all memory of the poll setup */
  869. *slot = NULL;
  870. fds->priv = NULL;
  871. }
  872. errout:
  873. nxsem_post(&priv->devsem);
  874. return ret;
  875. }
  876. /****************************************************************************
  877. * Public Functions
  878. ****************************************************************************/
  879. /****************************************************************************
  880. * Public Functions
  881. ****************************************************************************/
  882. /****************************************************************************
  883. * Name: ft5x06_register
  884. *
  885. * Description:
  886. * Configure the FT5x06 to use the provided I2C device instance. This
  887. * will register the driver as /dev/inputN where N is the minor device
  888. * number
  889. *
  890. * Input Parameters:
  891. * dev - An I2C driver instance
  892. * config - Persistent board configuration data
  893. * minor - The input device minor number
  894. *
  895. * Returned Value:
  896. * Zero is returned on success. Otherwise, a negated errno value is
  897. * returned to indicate the nature of the failure.
  898. *
  899. ****************************************************************************/
  900. int ft5x06_register(FAR struct i2c_master_s *i2c,
  901. FAR const struct ft5x06_config_s *config, int minor)
  902. {
  903. FAR struct ft5x06_dev_s *priv;
  904. char devname[DEV_NAMELEN];
  905. int ret;
  906. iinfo("i2c: %p minor: %d\n", i2c, minor);
  907. /* Debug-only sanity checks */
  908. DEBUGASSERT(i2c != NULL && config != NULL && minor >= 0 && minor < 100);
  909. #ifdef CONFIG_FT5X06_POLLMODE
  910. DEBUGASSERT(config->wakeup != NULL && config->nreset != NULL);
  911. #else
  912. DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
  913. config->clear != NULL && config->wakeup != NULL &&
  914. config->nreset != NULL);
  915. #endif
  916. /* Create and initialize a FT5x06 device driver instance */
  917. priv = (FAR struct ft5x06_dev_s *)kmm_zalloc(sizeof(struct ft5x06_dev_s));
  918. if (!priv)
  919. {
  920. ierr("ERROR: kmm_zalloc(%d) failed\n", sizeof(struct ft5x06_dev_s));
  921. return -ENOMEM;
  922. }
  923. /* Initialize the FT5x06 device driver instance */
  924. priv->i2c = i2c; /* Save the I2C device handle */
  925. priv->config = config; /* Save the board configuration */
  926. priv->frequency = config->frequency; /* Set the current I2C frequency */
  927. nxsem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
  928. nxsem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */
  929. /* The event wait semaphore is used for signaling and, hence, should not
  930. * have priority inheritance enabled.
  931. */
  932. nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE);
  933. #ifdef CONFIG_FT5X06_POLLMODE
  934. /* Allocate a timer for polling the FT5x06 */
  935. priv->delay = POLL_MAXDELAY;
  936. #else
  937. /* Make sure that the FT5x06 interrupt interrupt is disabled */
  938. config->clear(config);
  939. config->enable(config, false);
  940. /* Attach the interrupt handler */
  941. ret = config->attach(config, ft5x06_data_interrupt,
  942. priv);
  943. if (ret < 0)
  944. {
  945. ierr("ERROR: Failed to attach interrupt\n");
  946. goto errout_with_priv;
  947. }
  948. #endif
  949. /* Register the device as an input device */
  950. snprintf(devname, DEV_NAMELEN, DEV_FORMAT, minor);
  951. iinfo("Registering %s\n", devname);
  952. ret = register_driver(devname, &ft5x06_fops, 0666, priv);
  953. if (ret < 0)
  954. {
  955. ierr("ERROR: register_driver() failed: %d\n", ret);
  956. goto errout_with_priv;
  957. }
  958. /* Schedule work to perform the initial sampling and to set the data
  959. * availability conditions.
  960. */
  961. ret = work_queue(HPWORK, &priv->work, ft5x06_data_worker, priv, 0);
  962. if (ret < 0)
  963. {
  964. ierr("ERROR: Failed to queue work: %d\n", ret);
  965. goto errout_with_priv;
  966. }
  967. /* And return success */
  968. return OK;
  969. errout_with_priv:
  970. nxsem_destroy(&priv->devsem);
  971. kmm_free(priv);
  972. return ret;
  973. }