You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/09/21 13:40:49 UTC
[incubator-nuttx] branch master updated: input: Q10 BlackBerry
Keyboard from Solder Party
This is an automated email from the ASF dual-hosted git repository.
gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 5498f72 input: Q10 BlackBerry Keyboard from Solder Party
5498f72 is described below
commit 5498f72fa509475e06c3c735e8eed38f595a3a10
Author: Brennan Ashton <ba...@brennanashton.com>
AuthorDate: Sun Sep 20 02:37:33 2020 -0700
input: Q10 BlackBerry Keyboard from Solder Party
This adds support for the Q10 BlackBerry based keyboard
from Solder Party. https://www.solder.party/docs/keyboard-pmod/
They keyboard device registered at /dev/kbdN is fully compatible
with hidkbd and has been testing with the Keyboard FeatherWing
on the nRF52 platform.
The buttons are added as a standard discrete joystick if
optionally enabled. The PMOD variant of this does not
include these buttons, but the Keyboard FeatherWing does.
This joystick is usually defined at /dev/djoyN and
can be used with the djoy example application.
Signed-off-by: Brennan Ashton <ba...@brennanashton.com>
---
drivers/input/Kconfig | 32 ++
drivers/input/Make.defs | 4 +
drivers/input/spq10kbd.c | 1074 +++++++++++++++++++++++++++++++++++++++
include/nuttx/input/djoystick.h | 6 +
include/nuttx/input/spq10kbd.h | 124 +++++
5 files changed, 1240 insertions(+)
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a23790b..4a898a1 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -514,4 +514,36 @@ config NUNCHUCK_NPOLLWAITERS
endif # INPUT_NUNCHUCK
+config INPUT_SPQ10KBD
+ bool "Solder Party Q10 BlackBerry Keyboard"
+ default n
+ select I2C
+ ---help---
+ Enable the Solder Party Q10 BlackBerry Keyboard support. This
+ exposes itself as a standard keyboard at /dev/kbdN.
+ This keyboard exists both as a standalone module and integrated
+ into the Solder Party Keyboard FeatherWing. Information on this
+ can be found at https://www.solder.party/docs/keyboard-pmod/
+
+if INPUT_SPQ10KBD
+
+config SPQ10KBD_DJOY
+ bool "Joystick Interface for Buttons"
+ select DJOYSTICK
+ default n
+
+config SPQ10KBD_REGDBG
+ bool "Keyboard Register Debug"
+ default n
+
+config SPQ10KBD_BUFSIZE
+ int "Keyboard Buffer Size"
+ default 10
+
+config SPQ10KBD_NPOLLWAITERS
+ int "Max Number of Poll Waiters"
+ default 2
+
+endif # INPUT_SPQ10KBD
+
endif # INPUT
diff --git a/drivers/input/Make.defs b/drivers/input/Make.defs
index f35830f..f8a7054 100644
--- a/drivers/input/Make.defs
+++ b/drivers/input/Make.defs
@@ -99,6 +99,10 @@ ifeq ($(CONFIG_INPUT_NUNCHUCK),y)
CSRCS += nunchuck.c
endif
+ifeq ($(CONFIG_INPUT_SPQ10KBD),y)
+ CSRCS += spq10kbd.c
+endif
+
# Include input device driver build support
DEPPATH += --dep-path input
diff --git a/drivers/input/spq10kbd.c b/drivers/input/spq10kbd.c
new file mode 100644
index 0000000..3af8fca
--- /dev/null
+++ b/drivers/input/spq10kbd.c
@@ -0,0 +1,1074 @@
+/****************************************************************************
+ * drivers/input/sp_q10kbd.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+
+#include <nuttx/input/spq10kbd.h>
+#include <nuttx/input/djoystick.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* This format is used to construct the /dev/kbd[n] device driver path. It
+ * defined here so that it will be used consistently in all places.
+ */
+
+#define DEV_FORMAT "/dev/kbd%c"
+#define DEV_NAMELEN 11
+
+/* Number of Joystick discretes */
+
+#define DJOY_NGPIOS 5
+
+/* Bitset of supported Joystick discretes */
+
+#define DJOY_SUPPORTED (DJOY_UP_BIT | DJOY_DOWN_BIT | DJOY_LEFT_BIT | \
+ DJOY_RIGHT_BIT | DJOY_BUTTON_1_BIT | \
+ DJOY_BUTTON_2_BIT | DJOY_BUTTON_3_BIT | \
+ DJOY_BUTTON_4_BIT)
+
+/* Registers */
+
+#define SPQ10KBD_VER 0x01
+#define SPQ10KBD_CFG 0x02
+#define SPQ10KBD_INT 0x03
+#define SPQ10KBD_KEY 0x04
+#define SPQ10KBD_BKL 0x05
+#define SPQ10KBD_DEB 0x06
+#define SPQ10KBD_FRQ 0x07
+#define SPQ10KBD_RST 0x08
+#define SPQ10KBD_FIF 0x09
+
+/* VER */
+
+#define SPQ10KBD_VER_MAJOR_SHIFT 4
+#define SPQ10KBD_VER_MAJOR_MASK (0xf << SPQ10KBD_VER_MAJOR_SHIFT)
+#define SPQ10KBD_VER_MINOR_SHIFT 0
+#define SPQ10KBD_VER_MINOR_MASK (0xf << SPQ10KBD_VER_MINOR_SHIFT)
+
+#define SPQ10KBD_VER_00_02 0x0002
+
+/* CFG */
+
+#define SPQ10KBD_CFG_OVERFLOW_ON (1 << 0)
+#define SPQ10KBD_CFG_OVERFLOW_INT (1 << 1)
+#define SPQ10KBD_CFG_CAPSLOCK_INT (1 << 2)
+#define SPQ10KBD_CFG_NUMLOCK_INT (1 << 3)
+#define SPQ10KBD_CFG_KEY_INT (1 << 4)
+#define SPQ10KBD_CFG_PANIC_INT (1 << 5)
+#define SPQ10KBD_CFG_REPORT_MODS (1 << 6)
+#define SPQ10KBD_CFG_USE_MODS (1 << 7)
+
+/* INT */
+
+#define SPQ10KBD_INT_OVERFLOW (1 << 0)
+#define SPQ10KBD_INT_CAPSLOCK (1 << 1)
+#define SPQ10KBD_INT_NUMLOCK (1 << 2)
+#define SPQ10KBD_INT_KEY (1 << 3)
+#define SPQ10KBD_INT_PANIC (1 << 4)
+
+/* KEY */
+
+#define SPQ10KBD_KEY_COUNT_SHIFT 0
+#define SPQ10KBD_KEY_COUNT_MASK (0xf << SPQ10KBD_KEY_COUNT_SHIFT)
+#define SPQ10KBD_KEY_CAPSLOCK (1 << 5)
+#define SPQ10KBD_KEY_NUMLOCK (1 << 6)
+
+/* FIF */
+
+#define SPQ10KBD_FIF_STATE_SHIFT 0
+#define SPQ10KBD_FIF_STATE_MASK (0xff << SPQ10KBD_FIF_STATE_SHIFT)
+#define SPQ10KBD_FIF_KEY_SHIFT 8
+#define SPQ10KBD_FIF_KEY_MASK (0xff << SPQ10KBD_FIF_KEY_SHIFT)
+
+#define KEY_PRESS 0x01
+#define KEY_PRESS_HOLD 0x02
+#define KEY_RELEASE 0x03
+
+/* Special Key Encodings */
+
+#define KEY_BUTTON_FIRST 0x01 /* Start of the button region */
+#define KEY_JOY_UP 0x01
+#define KEY_JOY_DOWN 0x02
+#define KEY_JOY_LEFT 0x03
+#define KEY_JOY_RIGHT 0x04
+#define KEY_JOY_CENTER 0x05
+#define KEY_BTN_LEFT1 0x06
+#define KEY_BTN_RIGHT1 0x07
+#define KEY_BACKSPACE 0x08 /* Normal ASCII */
+#define KEY_TAB 0x09 /* Normal ASCII */
+#define KEY_NL 0x0a /* Normal ASCII */
+#define KEY_BTN_LEFT2 0x11
+#define KEY_BTN_RIGHT2 0x12
+#define KEY_BUTTON_LAST 0x12 /* End of the button region */
+
+/* Key to joystick mapping */
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+static const djoy_buttonset_t joystick_map[] =
+{
+ 0, /* No Key */
+ DJOY_UP_BIT, /* KEY_JOY_UP */
+ DJOY_DOWN_BIT, /* KEY_JOY_DOWN */
+ DJOY_LEFT_BIT, /* KEY_JOY_LEFT */
+ DJOY_RIGHT_BIT, /* KEY_JOY_RIGHT */
+ 0, /* KEY_JOY_CENTER */
+ DJOY_BUTTON_1_BIT, /* KEY_BTN_LEFT1 */
+ DJOY_BUTTON_3_BIT, /* KEY_BTN_RIGHT1 */
+ 0, /* KEY_BACKSPACE */
+ 0, /* KEY_TAB */
+ 0, /* KEY_NL */
+ 0, /* No Key */
+ 0, /* No Key */
+ 0, /* No Key */
+ 0, /* No Key */
+ 0, /* No Key */
+ 0, /* No Key */
+ DJOY_BUTTON_2_BIT, /* KEY_BTN_LEFT2 */
+ DJOY_BUTTON_4_BIT, /* KEY_BTN_RIGHT2 */
+};
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct spq10kbd_dev_s
+{
+ FAR const struct spq10kbd_config_s *config; /* Board configuration data */
+ FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+ struct djoy_lowerhalf_s djoylower; /* Digital joystick */
+ djoy_interrupt_t djoyhandle; /* Joystick handler func */
+ FAR void *djoycbarg; /* Joystick callback arg */
+ djoy_buttonset_t djoypressmask; /* Joystick press evts */
+ djoy_buttonset_t djoyreleasemask; /* Joystick release evts */
+ djoy_buttonset_t djoystate; /* Joystick button state */
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+ sem_t exclsem; /* Exclusive access to dev */
+ sem_t waitsem; /* Signal waiting thread */
+ bool waiting; /* Waiting for keyboard data */
+ 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'.
+ */
+
+ struct pollfd *fds[CONFIG_SPQ10KBD_NPOLLWAITERS];
+
+ /* Buffer used to collect and buffer incoming keyboard characters */
+
+ uint16_t headndx; /* Buffer head index */
+ uint16_t tailndx; /* Buffer tail index */
+ uint8_t kbdbuffer[CONFIG_SPQ10KBD_BUFSIZE];
+
+ uint8_t crefs; /* Reference count on the driver instance */
+};
+
+/****************************************************************************
+ * Static Function Prototypes
+ ****************************************************************************/
+
+static int spq10kbd_interrupt(int irq, FAR void *context, FAR void *arg);
+static void spq10kbd_worker(FAR void *arg);
+static void spq10kbd_putreg8(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr, uint8_t regval);
+static uint8_t spq10kbd_getreg8(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr);
+static uint16_t spq10kbd_getreg16(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr);
+static int spq10kbd_checkver(FAR struct spq10kbd_dev_s *priv);
+static int spq10kbd_reset(FAR struct spq10kbd_dev_s *priv);
+static void spq10kbd_putbuffer(FAR struct spq10kbd_dev_s *priv,
+ uint8_t keycode);
+
+/* Digital Joystick methods */
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+static djoy_buttonset_t djoy_supported(
+ FAR const struct djoy_lowerhalf_s *lower);
+static djoy_buttonset_t djoy_sample(
+ FAR const struct djoy_lowerhalf_s *lower);
+static void djoy_enable(FAR const struct djoy_lowerhalf_s *lower,
+ djoy_buttonset_t press, djoy_buttonset_t release,
+ djoy_interrupt_t handler, FAR void *arg);
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+/* Driver methods. We export the keyboard as a standard character driver */
+
+static int spq10kbd_open(FAR struct file *filep);
+static int spq10kbd_close(FAR struct file *filep);
+static ssize_t spq10kbd_read(FAR struct file *filep,
+ FAR char *buffer, size_t len);
+static ssize_t spq10kbd_write(FAR struct file *filep,
+ FAR const char *buffer, size_t len);
+static int spq10kbd_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_hidkbd_fops =
+{
+ spq10kbd_open, /* open */
+ spq10kbd_close, /* close */
+ spq10kbd_read, /* read */
+ spq10kbd_write, /* write */
+ NULL, /* seek */
+ NULL, /* ioctl */
+ spq10kbd_poll /* poll */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+
+/****************************************************************************
+ * Name: djoy_supported
+ *
+ * Description:
+ * Return the set of buttons supported on the discrete joystick device
+ *
+ ****************************************************************************/
+
+static djoy_buttonset_t djoy_supported(
+ FAR const struct djoy_lowerhalf_s *lower)
+{
+ iinfo("Supported: %02x\n", DJOY_SUPPORTED);
+ return (djoy_buttonset_t)DJOY_SUPPORTED;
+}
+
+/****************************************************************************
+ * Name: djoy_sample
+ *
+ * Description:
+ * Return the current state of all discrete joystick buttons
+ *
+ ****************************************************************************/
+
+static djoy_buttonset_t djoy_sample(
+ FAR const struct djoy_lowerhalf_s *lower)
+{
+ FAR struct spq10kbd_dev_s *priv =
+ (FAR struct spq10kbd_dev_s *)(lower->config);
+ return priv->djoystate;
+}
+
+/****************************************************************************
+ * Name: djoy_enable
+ *
+ * Description:
+ * Enable interrupts on the selected set of joystick buttons. An empty
+ * set will disable all interrupts.
+ *
+ ****************************************************************************/
+
+static void djoy_enable(FAR const struct djoy_lowerhalf_s *lower,
+ djoy_buttonset_t press, djoy_buttonset_t release,
+ djoy_interrupt_t handler, FAR void *arg)
+{
+ FAR struct spq10kbd_dev_s *priv =
+ (FAR struct spq10kbd_dev_s *)(lower->config);
+ priv->djoypressmask = press;
+ priv->djoyreleasemask = release;
+ priv->djoyhandle = handler;
+ priv->djoycbarg = arg;
+}
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+/****************************************************************************
+ * Name: spq10kbd_worker
+ ****************************************************************************/
+
+static void spq10kbd_worker(FAR void *arg)
+{
+ FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)arg;
+ uint16_t regval;
+ uint8_t key;
+ uint8_t state;
+ int ret;
+
+ ret = nxsem_wait_uninterruptible(&priv->exclsem);
+ if (ret < 0)
+ {
+ return;
+ }
+
+ regval = spq10kbd_getreg8(priv, SPQ10KBD_INT);
+ if (regval & SPQ10KBD_INT_KEY)
+ {
+ /* There is a keypress in the FIFO */
+
+ while (spq10kbd_getreg8(priv, SPQ10KBD_KEY) & SPQ10KBD_KEY_COUNT_MASK)
+ {
+ regval = spq10kbd_getreg16(priv, SPQ10KBD_FIF);
+ state = (regval & SPQ10KBD_FIF_STATE_MASK) >> \
+ SPQ10KBD_FIF_STATE_SHIFT;
+ key = (regval & SPQ10KBD_FIF_KEY_MASK) >> SPQ10KBD_FIF_KEY_SHIFT;
+ if (key <= KEY_BUTTON_LAST &&
+ !(key == KEY_BACKSPACE ||
+ key == KEY_TAB ||
+ key == KEY_NL))
+ {
+#ifdef CONFIG_SPQ10KBD_DJOY
+ if (joystick_map[key] == 0)
+ {
+ /* Key is not mapped, skip */
+
+ iinfo("Skipping unmapped key %02x\n", key);
+ }
+
+ switch (state)
+ {
+ case KEY_PRESS:
+ iinfo("Button Press: %02x\n", key);
+ priv->djoystate |= joystick_map[key];
+ if (priv->djoypressmask & joystick_map[key])
+ {
+ priv->djoyhandle(&priv->djoylower,
+ priv->djoycbarg);
+ }
+
+ break;
+ case KEY_RELEASE:
+ iinfo("Button Release: %02x\n", key);
+ priv->djoystate &= ~joystick_map[key];
+ if (priv->djoypressmask & joystick_map[key])
+ {
+ priv->djoyhandle(&priv->djoylower,
+ priv->djoycbarg);
+ }
+
+ break;
+ }
+
+ iinfo("Stored state: %02x\n", priv->djoystate);
+#else
+ iinfo("Button Ignored. No joystick interface.\n");
+#endif /* CONFIG_SPQ10KBD_DJOY */
+ }
+ else if(state == KEY_PRESS)
+ {
+ /* key is a normal ascii character */
+
+ spq10kbd_putbuffer(priv, key);
+
+ /* Notify waiting reads */
+
+ if (priv->waiting == true)
+ {
+ priv->waiting = false;
+ nxsem_post(&priv->waitsem);
+ }
+ }
+ }
+ }
+
+ /* Clear interrupt status register */
+
+ spq10kbd_putreg8(priv, SPQ10KBD_INT, 0);
+ nxsem_post(&priv->exclsem);
+}
+
+/****************************************************************************
+ * Name: spq10kbd_interrupt
+ ****************************************************************************/
+
+static int spq10kbd_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+ FAR struct spq10kbd_dev_s *priv = (FAR struct spq10kbd_dev_s *)arg;
+ int ret;
+
+ /* Let the event worker know that it has an interrupt event to handle
+ * It is possbile that we will already have work scheduled from a
+ * previous interrupt event. That is OK we will service all the events
+ * in the same work job.
+ */
+
+ if (work_available(&priv->work))
+ {
+ ret = work_queue(HPWORK, &priv->work, spq10kbd_worker, priv, 0);
+ if (ret != 0)
+ {
+ ierr("ERROR: Failed to queue work: %d\n", ret);
+ }
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_pollnotify
+ ****************************************************************************/
+
+static void spq10kbd_pollnotify(FAR struct spq10kbd_dev_s *priv)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_SPQ10KBD_NPOLLWAITERS; i++)
+ {
+ struct pollfd *fds = priv->fds[i];
+ if (fds)
+ {
+ fds->revents |= (fds->events & POLLIN);
+ if (fds->revents != 0)
+ {
+ uinfo("Report events: %02x\n", fds->revents);
+ nxsem_post(fds->sem);
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: spq10kbd_open
+ *
+ * Description:
+ * Standard character driver open method.
+ *
+ ****************************************************************************/
+
+static int spq10kbd_open(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct spq10kbd_dev_s *priv;
+
+ DEBUGASSERT(filep && filep->f_inode);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Increment the reference count on the driver */
+
+ priv->crefs++;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_close
+ *
+ * Description:
+ * Standard character driver close method.
+ *
+ ****************************************************************************/
+
+static int spq10kbd_close(FAR struct file *filep)
+{
+ FAR struct inode *inode;
+ FAR struct spq10kbd_dev_s *priv;
+
+ DEBUGASSERT(filep && filep->f_inode);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Decrement the reference count on the driver */
+
+ DEBUGASSERT(priv->crefs >= 1);
+
+ priv->crefs--;
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_read
+ *
+ * Description:
+ * Standard character driver read method.
+ *
+ ****************************************************************************/
+
+static ssize_t spq10kbd_read(FAR struct file *filep, FAR char *buffer,
+ size_t len)
+{
+ FAR struct inode *inode;
+ FAR struct spq10kbd_dev_s *priv;
+ size_t nbytes;
+ uint16_t tail;
+ int ret;
+
+ DEBUGASSERT(filep && filep->f_inode && buffer);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Read data from our internal buffer of received characters */
+
+ ret = nxsem_wait_uninterruptible(&priv->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ while (priv->tailndx == priv->headndx)
+ {
+ /* No.. were we open non-blocking? */
+
+ if (filep->f_oflags & O_NONBLOCK)
+ {
+ /* Yes.. then return a failure */
+
+ ret = -EAGAIN;
+ goto errout;
+ }
+ else
+ {
+ priv->waiting = true;
+ nxsem_post(&priv->exclsem);
+ ret = nxsem_wait_uninterruptible(&priv->waitsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = nxsem_wait_uninterruptible(&priv->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+ }
+ }
+
+ for (tail = priv->tailndx, nbytes = 0;
+ tail != priv->headndx && nbytes < len;
+ nbytes++)
+ {
+ /* Copy the next keyboard character into the user buffer */
+
+ *buffer++ = priv->kbdbuffer[tail];
+
+ /* Handle wrap-around of the tail index */
+
+ if (++tail >= CONFIG_SPQ10KBD_BUFSIZE)
+ {
+ tail = 0;
+ }
+ }
+
+ ret = nbytes;
+
+ /* Update the tail index (perhaps marking the buffer empty) */
+
+ priv->tailndx = tail;
+
+errout:
+ nxsem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_write
+ *
+ * Description:
+ * Standard character driver write method.
+ *
+ ****************************************************************************/
+
+static ssize_t spq10kbd_write(FAR struct file *filep, FAR const char *buffer,
+ size_t len)
+{
+ /* We won't try to write to the keyboard */
+
+ return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_poll
+ *
+ * Description:
+ * Standard character driver poll method.
+ *
+ ****************************************************************************/
+
+static int spq10kbd_poll(FAR struct file *filep, FAR struct pollfd *fds,
+ bool setup)
+{
+ FAR struct inode *inode;
+ FAR struct spq10kbd_dev_s *priv;
+ int ret;
+ int i;
+
+ DEBUGASSERT(filep && filep->f_inode && fds);
+ inode = filep->f_inode;
+ priv = inode->i_private;
+
+ /* Make sure that we have exclusive access to the private data structure */
+
+ DEBUGASSERT(priv);
+ ret = nxsem_wait_uninterruptible(&priv->exclsem);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (setup)
+ {
+ /* This is a request to set up the poll. Find an available slot for
+ * the poll structure reference
+ */
+
+ for (i = 0; i < CONFIG_SPQ10KBD_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_SPQ10KBD_NPOLLWAITERS)
+ {
+ fds->priv = NULL;
+ ret = -EBUSY;
+ goto errout;
+ }
+
+ /* Should we immediately notify on any of the requested events? Notify
+ * the POLLIN event if there is buffered keyboard data.
+ */
+
+ if (priv->headndx != priv->tailndx)
+ {
+ spq10kbd_pollnotify(priv);
+ }
+ }
+ else
+ {
+ /* This is a request to tear down the poll. */
+
+ struct pollfd **slot = (struct pollfd **)fds->priv;
+ DEBUGASSERT(slot);
+
+ /* Remove all memory of the poll setup */
+
+ *slot = NULL;
+ fds->priv = NULL;
+ }
+
+errout:
+ nxsem_post(&priv->exclsem);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_putbuffer
+ *
+ * Description:
+ * Add one character to the user buffer.
+ * Expectation is that we already have exclusive use of the device.
+ *
+ * Input Parameters:
+ * priv - Driver internal state
+ * keycode - The value to add to the user buffer
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void spq10kbd_putbuffer(FAR struct spq10kbd_dev_s *priv,
+ uint8_t keycode)
+{
+ uint16_t head;
+ uint16_t tail;
+
+ DEBUGASSERT(priv);
+
+ /* Copy the next keyboard character into the user buffer. */
+
+ head = priv->headndx;
+ priv->kbdbuffer[head] = keycode;
+
+ /* Increment the head index */
+
+ if (++head >= CONFIG_SPQ10KBD_BUFSIZE)
+ {
+ head = 0;
+ }
+
+ /* If the buffer is full, then increment the tail index to make space.
+ * Drop old unread key presses.
+ */
+
+ tail = priv->tailndx;
+ if (tail == head)
+ {
+ if (++tail >= CONFIG_SPQ10KBD_BUFSIZE)
+ {
+ tail = 0;
+ }
+
+ /* Save the updated tail index */
+
+ priv->tailndx = tail;
+ }
+
+ /* Save the updated head index */
+
+ priv->headndx = head;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_checkver
+ *
+ * Description:
+ * Read and verify the Q10 Keyboard Controller Version
+ *
+ ****************************************************************************/
+
+static int spq10kbd_checkver(FAR struct spq10kbd_dev_s *priv)
+{
+ uint8_t version;
+
+ /* Read device version */
+
+ version = spq10kbd_getreg8(priv, SPQ10KBD_VER);
+ iinfo("version: %02x\n", version);
+
+ if (version != SPQ10KBD_VER_00_02)
+ {
+ /* Version is not Correct */
+
+ return -ENODEV;
+ }
+
+ return OK;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_reset
+ *
+ * Description:
+ * Reset the Q10 Keyboard Controller Version
+ *
+ ****************************************************************************/
+
+static int spq10kbd_reset(FAR struct spq10kbd_dev_s *priv)
+{
+ spq10kbd_putreg8(priv, SPQ10KBD_RST, 0xff);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_getreg8
+ *
+ * Description:
+ * Read from an 8-bit Q10 Keyboard register
+ *
+ ****************************************************************************/
+
+static uint8_t spq10kbd_getreg8(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr)
+{
+ /* 8-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - Q10_Reg_Address -
+ * Repeated_Start - I2C_Read_Address - Q10_Read_Data - STOP
+ */
+
+ struct i2c_msg_s msg[2];
+ uint8_t regval;
+ int ret;
+
+ /* Setup 8-bit Q10 Keyboard address write message */
+
+ msg[0].frequency = priv->config->frequency; /* I2C frequency */
+ msg[0].addr = priv->config->address; /* 7-bit address */
+ msg[0].flags = 0; /* Write transaction, beginning with START */
+ msg[0].buffer = ®addr; /* Transfer from this address */
+ msg[0].length = 1; /* Send one byte following the address
+ * (no STOP) */
+
+ /* Set up the 8-bit Q10 Keyboard data read message */
+
+ msg[1].frequency = priv->config->frequency; /* I2C frequency */
+ msg[1].addr = priv->config->address; /* 7-bit address */
+ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
+ msg[1].buffer = ®val; /* Transfer to this address */
+ msg[1].length = 1; /* Receive one byte following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, msg, 2);
+ if (ret < 0)
+ {
+ ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+ return 0;
+ }
+
+#ifdef CONFIG_SPQ10KBD_REGDBG
+ _err("%02x->%02x\n", regaddr, regval);
+#endif
+ return regval;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_getreg16
+ *
+ * Description:
+ * Read from an 8-bit Q10 Keyboard register
+ *
+ ****************************************************************************/
+
+static uint16_t spq10kbd_getreg16(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr)
+{
+ /* 8-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - Q10_Reg_Address -
+ * Repeated_Start - I2C_Read_Address - Q10_Read_Data - STOP
+ */
+
+ struct i2c_msg_s msg[2];
+ uint8_t regval[2];
+ uint16_t ret;
+
+ /* Setup 8-bit Q10 Keyboard address write message */
+
+ msg[0].frequency = priv->config->frequency; /* I2C frequency */
+ msg[0].addr = priv->config->address; /* 7-bit address */
+ msg[0].flags = 0; /* Write transaction, beginning with START */
+ msg[0].buffer = ®addr; /* Transfer from this address */
+ msg[0].length = 1; /* Send one byte following the address
+ * (no STOP) */
+
+ /* Set up the 8-bit Q10 Keyboard data read message */
+
+ msg[1].frequency = priv->config->frequency; /* I2C frequency */
+ msg[1].addr = priv->config->address; /* 7-bit address */
+ msg[1].flags = I2C_M_READ; /* Read transaction, beginning with Re-START */
+ msg[1].buffer = regval; /* Transfer to this address */
+ msg[1].length = 2; /* Receive two bytes following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, msg, 2);
+ if (ret < 0)
+ {
+ ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+ return 0;
+ }
+
+ ret = (regval[1] << 8) | regval[0];
+#ifdef CONFIG_SPQ10KBD_REGDBG
+ _err("%02x->%04x\n", regaddr, ret);
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ * Name: spq10kbd_putreg8
+ *
+ * Description:
+ * Write a value to an 8-bit Q10 Keyboard register
+ *
+ ****************************************************************************/
+
+static void spq10kbd_putreg8(FAR struct spq10kbd_dev_s *priv,
+ uint8_t regaddr, uint8_t regval)
+{
+ /* 8-bit data read sequence:
+ *
+ * Start - I2C_Write_Address - Q10_Reg_Address - Q10_Write_Data - STOP
+ */
+
+ struct i2c_msg_s msg;
+ uint8_t txbuffer[2];
+ int ret;
+
+#ifdef CONFIG_SPQ10KBD_REGDBG
+ _err("%02x<-%02x\n", regaddr, regval);
+#endif
+
+ /* Setup to the data to be transferred. Two bytes: The Q10 Keyboard
+ * register address followed by one byte of data.
+ */
+
+ txbuffer[0] = regaddr;
+ txbuffer[1] = regval;
+
+ /* Setup 8-bit Q10 Keyboard address write message */
+
+ msg.frequency = priv->config->frequency; /* I2C frequency */
+ msg.addr = priv->config->address; /* 7-bit address */
+ msg.flags = 0; /* Write transaction, beginning with START */
+ msg.buffer = txbuffer; /* Transfer from this address */
+ msg.length = 2; /* Send two byte following the address
+ * (then STOP) */
+
+ /* Perform the transfer */
+
+ ret = I2C_TRANSFER(priv->i2c, &msg, 1);
+ if (ret < 0)
+ {
+ ierr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+ }
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spq10kbd_register
+ *
+ * Description:
+ * Configure the Solder Party Q10 Keyboard to use the provided I2C device
+ * instance. This will register the driver as /dev/kbdN where N is the
+ * minor device number, as well as a joystick at joydevname
+ *
+ * Input Parameters:
+ * i2c - An I2C driver instance
+ * config - Persistent board configuration data
+ * kbdminor - The keyboard input device minor number
+ * joydevname - The name of the joystick device /dev/djoyN
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+int spq10kbd_register(FAR struct i2c_master_s *i2c,
+ FAR const struct spq10kbd_config_s *config,
+ char kbdminor, char *joydevname)
+#else
+int spq10kbd_register(FAR struct i2c_master_s *i2c,
+ FAR const struct spq10kbd_config_s *config,
+ char kbdminor)
+#endif
+{
+ FAR struct spq10kbd_dev_s *priv;
+ char kbddevname[DEV_NAMELEN];
+ int ret;
+
+ /* Debug Sanity Checks */
+
+ DEBUGASSERT(i2c != NULL && config != NULL);
+ DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
+ config->clear != NULL);
+
+ priv = (FAR struct spq10kbd_dev_s *)kmm_zalloc(
+ sizeof(struct spq10kbd_dev_s));
+ if (!priv)
+ {
+ ierr("ERROR: kmm_zalloc(%d) failed\n", sizeof(struct spq10kbd_dev_s));
+ return -ENOMEM;
+ }
+
+ /* Initialize the device driver instance */
+
+ priv->i2c = i2c; /* Save the I2C device handle */
+ priv->config = config; /* Save the board configuration */
+ priv->tailndx = 0; /* Reset keypress buffer state */
+ priv->headndx = 0;
+ priv->crefs = 0; /* Reset referece count to 0 */
+ priv->waiting = false;
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+ priv->djoylower.config = (FAR void *)priv;
+ priv->djoylower.dl_supported = djoy_supported;
+ priv->djoylower.dl_sample = djoy_sample;
+ priv->djoylower.dl_enable = djoy_enable;
+ priv->djoyhandle = NULL;
+ priv->djoystate = 0;
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+ nxsem_init(&priv->exclsem, 0, 1); /* Initialize device semaphore */
+ nxsem_init(&priv->waitsem, 0, 0);
+
+ /* The waitsem semaphore is used for signaling and, hence, should
+ * not have priority inheritance enabled.
+ */
+
+ nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE);
+
+ config->clear(config);
+ config->enable(config, false);
+
+ /* Attach the interrupt handler */
+
+ ret = config->attach(config, spq10kbd_interrupt, priv);
+ if (ret < 0)
+ {
+ ierr("ERROR: Failed to attach interrupt\n");
+ goto errout_with_priv;
+ }
+
+ ret = spq10kbd_checkver(priv);
+
+ if (ret != OK)
+ {
+ /* Did not find a supported device on the bus */
+
+ return ret;
+ }
+
+ spq10kbd_reset(priv);
+
+ /* Start servicing events */
+
+ priv->config->enable(priv->config, true);
+
+ snprintf(kbddevname, DEV_NAMELEN, DEV_FORMAT, kbdminor);
+ iinfo("Registering %s\n", kbddevname);
+ ret = register_driver(kbddevname, &g_hidkbd_fops, 0666, priv);
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+ iinfo("Registering %s\n", joydevname);
+ ret = djoy_register(joydevname, &priv->djoylower);
+#endif /* CONFIG_SPQ10KBD_DJOY */
+
+ return OK;
+
+errout_with_priv:
+ nxsem_destroy(&priv->exclsem);
+ kmm_free(priv);
+ return ret;
+}
diff --git a/include/nuttx/input/djoystick.h b/include/nuttx/input/djoystick.h
index d59eb5e..49d5f76 100644
--- a/include/nuttx/input/djoystick.h
+++ b/include/nuttx/input/djoystick.h
@@ -235,6 +235,12 @@ struct djoy_lowerhalf_s
CODE void (*dl_enable)(FAR const struct djoy_lowerhalf_s *lower,
djoy_buttonset_t press, djoy_buttonset_t release,
djoy_interrupt_t handler, FAR void *arg);
+
+ /* Allow for storing implementation specific data to support cases where
+ * their may be more than one joystick
+ */
+
+ FAR void *config;
};
/****************************************************************************
diff --git a/include/nuttx/input/spq10kbd.h b/include/nuttx/input/spq10kbd.h
new file mode 100644
index 0000000..4e6aa3d
--- /dev/null
+++ b/include/nuttx/input/spq10kbd.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+ * include/nuttx/input/spq10kbd.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* The q10 keyboard driver exports a standard character driver interface. By
+ * convention, the keyboard driver is exposed as /dev/kbd[n] device driver
+ * path.
+ */
+
+#ifndef __INCLUDE_NUTTX_INPUT_SPQ10KBD_H
+#define __INCLUDE_NUTTX_INPUT_SPQ10KBD_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <stdbool.h>
+#include <nuttx/irq.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the
+ * Solder Party Q10 Keyboard driver. This structure provides information
+ * about the configuration and provides some board-specific hooks.
+ */
+
+struct spq10kbd_config_s
+{
+ /* Device characterization */
+
+ uint32_t frequency; /* I2C frequency */
+ uint8_t address; /* I2C 7-bit device address */
+
+ /* IRQ/GPIO access callbacks. These operations all hidden behind
+ * callbacks to isolate the Q10 Keyboard driver from differences in GPIO
+ * interrupt handling by varying boards and MCUs.
+ *
+ * attach - Attach the Q10 kbd interrupt handler to the GPIO interrupt
+ * enable - Enable or disable the GPIO interrupt
+ * clear - Acknowledge/clear any pending GPIO interrupt
+ */
+
+ int (*attach)(FAR const struct spq10kbd_config_s *state, xcpt_t isr,
+ FAR void *arg);
+ void (*enable)(FAR const struct spq10kbd_config_s *state, bool enable);
+ void (*clear)(FAR const struct spq10kbd_config_s *state);
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: spq10kbd_register
+ *
+ * Description:
+ * Configure the Solder Party Q10 Keyboard to use the provided I2C device
+ * instance. This will register the driver as /dev/kbdN where N is the
+ * minor device number, as well as a joystick at joydevname
+ *
+ * Input Parameters:
+ * i2c - An I2C driver instance
+ * config - Persistent board configuration data
+ * kbdminor - The keyboard input device minor number
+ * joydevname - The name of the joystick device /dev/djoyN
+ *
+ * Returned Value:
+ * Zero is returned on success. Otherwise, a negated errno value is
+ * returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_SPQ10KBD_DJOY
+int spq10kbd_register(FAR struct i2c_master_s *i2c,
+ FAR const struct spq10kbd_config_s *config,
+ char kbdminor, char *joydevname);
+#else
+int spq10kbd_register(FAR struct i2c_master_s *i2c,
+ FAR const struct spq10kbd_config_s *config,
+ char kbdminor);
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __INCLUDE_NUTTX_INPUT_SPQ10KBD_H */