This commit is contained in:
Jorge Guzman 2026-07-31 19:47:55 +08:00 committed by GitHub
commit 8a229ecb6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 666 additions and 21 deletions

View file

@ -93,6 +93,49 @@ To launch the game, just run:
and replace the path with your WAD file's path.
Tuning the display for a board
------------------------------
DOOM renders a 320x200 image with 8 bits per pixel and a palette, and the port
scales that up and converts it to whatever the frame buffer wants. How much
that costs depends a great deal on the board, so the following options are
available. All of them are disabled by default, which leaves the behaviour
unchanged, and each is worth enabling only where it pays.
``CONFIG_GAMES_NXDOOM_FB_CMAP``
Load the DOOM palette into the frame buffer's colour map and blit the
palette indices unconverted, letting the display convert them while it scans
out. This needs a frame buffer that is 8 bits per pixel and supports
``FBIOPUT_CMAP``, such as an STM32 LTDC layer configured for L8. It removes
the conversion from the blit and halves the amount of data written per
frame.
``CONFIG_GAMES_NXDOOM_FILLSCREEN``
Stretch the image over the whole display instead of scaling it by the
largest whole number that fits. This fills a display whose size is not an
exact multiple of 320x200, at the cost of a scale factor that is not uniform
across the image. Without it the image is scaled by a whole number and
centred, leaving a border.
``CONFIG_GAMES_NXDOOM_ROWSTAGE``
Build each output row in a staging buffer and copy it out, rather than
writing the frame buffer a pixel at a time, so that the frame buffer only
ever sees burst-friendly copies. Costs a few kilobytes of ``.bss``. This is
worth a lot when the frame buffer is external memory and the data cache is
in write-through mode, and little otherwise.
``CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF``
Place the buffer DOOM renders into in ``.bss`` rather than taking it from the
heap, which puts it in internal RAM on a board whose heap is mostly external
memory. The renderer draws in vertical columns, so consecutive writes are
one screen width apart and none of them coalesce, which makes external memory
close to the worst case for it. Costs 64000 bytes of RAM whether the game
runs or not.
As an illustration of the effect, on an STM32H753 driving a 1024x600 panel
whose frame buffer lives in SDRAM, ``ROWSTAGE`` and ``STATIC_SCRNBUF`` are
worth 2.7 times the frame rate between them.
Other Notes
-----------

View file

@ -1087,6 +1087,158 @@ that the USB host and the SDMMC peripheral coexist::
nsh> mount -t vfat /dev/mmcsd0 /data
nsh> ls /data
nxdoom
------
**Purpose:** runs ``NXDoom``, the NuttX port of Chocolate DOOM, on the board's
LCD, played with a USB HID keyboard and reading the game data from the microSD
card. It brings together the LTDC framebuffer, the OTG FS USB host and the
SDMMC peripheral, and uses the external SDRAM for the game's zone memory.
.. figure:: linum-stm32h753bi-nxdoom.jpg
:figwidth: 60%
:align: center
:alt: DOOM running on the LINUM-STM32H753BI LCD
DOOM running on the board's 1024x600 LCD
DOOM renders to a 320x200 8-bit paletted buffer. This configuration stretches
that over the whole 1024x600 panel (``CONFIG_GAMES_NXDOOM_FILLSCREEN``) and
runs the LTDC layer in L8 (``CONFIG_STM32_LTDC_L1_L8`` with
``CONFIG_GAMES_NXDOOM_FB_CMAP``), so the palette indices are written to the
framebuffer unconverted and the display applies the palette from its colour
map as it scans out. That keeps the conversion off the CPU and
halves the amount of data written per frame.
Two further options matter on this board, both because the framebuffer and the
heap are in external SDRAM: ``CONFIG_GAMES_NXDOOM_ROWSTAGE`` builds each row
in internal RAM so the SDRAM only sees burst copies, and
``CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF`` keeps the buffer DOOM renders into out
of the SDRAM altogether. Between them they are worth 2.7 times the frame rate.
The game needs about 4 MiB of contiguous memory for its zone. That comes from
the SDRAM region, which is 6 MiB when the LTDC is enabled (the last 2 MiB of
the 8 MiB SDRAM is reserved for the framebuffer).
**Requirements:**
* A microSD card, formatted as FAT, holding a DOOM IWAD. The shareware
``doom1.wad`` and ``freedoom1.wad`` both work, as do the commercial IWADs.
* A USB HID keyboard on the OTG FS service connector.
**Build and flash:**
.. code-block:: console
$ ./tools/configure.sh linum-stm32h753bi:nxdoom
$ make -j
Flash the resulting ``nuttx.bin`` to the board.
**How to test:** copy the IWAD to the SD card, plug the card and the keyboard,
reset the board and check that all three devices came up::
nsh> ls /dev
/dev:
console
fb0
kbda
mmcsd0
null
rtc0
ttyS0
zero
Mount the card and start the game. ``nxdoom`` looks for the IWAD in the
current directory, so either ``cd`` to the mount point first or point it at
the file with ``-iwad``::
nsh> mount -t vfat /dev/mmcsd0 /mnt
nsh> nxdoom -iwad /mnt/doom1.wad
NXDoom v0.0.0
z_init: Init zone memory allocation daemon.
zone memory: Using native C allocator.
Using /mnt/ for configuration and saves
v_init: allocate screens.
m_load_defaults: Load system defaults.
saving config in /mnt/default.cfg
W_Init: Init WADfiles.
adding /mnt/doom1.wad
=========================================================================
DOOM Shareware
=========================================================================
NXDoom is free software, covered by the GNU General Public
License. There is NO warranty; not even for MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. You are welcome to change and distribute
copies under certain conditions. See the source for more information.
=========================================================================
i_init: Setting up machine state.
m_init: Init miscellaneous info.
r_init: Init DOOM refresh daemon - [...................]
p_init: Init Playloop state.
d_check_net_game: Checking network game status.
startskill 2 deathmatch: 0 startmap: 1 startepisode: 1
player 1 of 1 (1 nodes)
Emulating the behavior of the 'Doom 1.9' executable.
hu_init: Setting up heads up display.
st_init: Init status bar.
**Copying the IWAD with zmodem:** the configuration also enables the ``rz``
and ``sz`` commands, so the IWAD can be copied over the serial console
instead of moving the SD card to a card reader.
``CONFIG_SYSTEM_ZMODEM_MOUNTPOINT`` is set to ``/mnt``, so a received file
lands on the SD card::
nsh> mount -t vfat /dev/mmcsd0 /mnt
nsh> rz
and, from the host, with the console closed in any terminal program::
$ sz -b --zmodem -w 1024 doom1.wad < /dev/ttyACM0 > /dev/ttyACM0
Note that USART1 has no RTS/CTS on this board, so there is no hardware flow
control to throttle the sender. ``CONFIG_USART1_RXBUFSIZE`` is raised to 4096
to compensate, and the ``-w`` window above makes the host wait for
acknowledgements. Even so this runs at roughly 10 KiB/s, so a 4 MiB IWAD takes
about seven minutes; a card reader is much faster if one is available.
The game runs on the LCD and is played from the USB keyboard: arrow keys to
move, Ctrl to fire, Shift to run, Alt to strafe, Space to open doors and Esc
for the menu. Configuration and saved games are written to ``/mnt``, so the
card must be mounted read/write.
.. note::
The keyboard is registered through the keyboard upper-half driver
(``CONFIG_HIDKBD_KBDUPPER``) rather than as a byte stream. That is what
makes key *release* events available, without which a movement key would
never stop being held down. Note that the byte-stream interface is not
available in this mode, so ``/dev/kbda`` cannot be read as characters while
this configuration is in use.
.. note::
``CONFIG_HIDKBD_NOGETREPORT`` (and the ``CONFIG_USBHOST_ASYNCH`` it needs)
is required. By default the HID keyboard driver asks the keyboard for its
input report over the control pipe with GET_REPORT. Many keyboards accept
that request but always answer with an empty report, and only ever deliver
key data on their interrupt IN endpoint. The symptom is a keyboard that
enumerates as ``/dev/kbda`` and reports no error at all, while no key is
ever seen. With this option the driver reads the interrupt endpoint
instead.
.. note::
``CONFIG_FAT_FORCE_INDIRECT`` is required. Without it the FAT layer reads
whole sectors straight into the caller's buffer, and the SDMMC IDMA cannot
reach the caller's buffer when it lives in external SDRAM. DOOM reads its
lumps into ``malloc()``\ ed memory, and once the internal SRAM regions fill
up those allocations come from SDRAM, so the failure appears part way
through startup rather than immediately::
r_init: Init DOOM refresh daemon - [ ]w_read_lump: only read 0 of 5192 on lump 1070
Forcing indirect transfers routes every read through the DMA-capable
sector buffer that ``CONFIG_FAT_DMAMEMORY`` allocates, at the cost of one
extra copy per sector.
tone
----

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View file

@ -30,6 +30,7 @@
#include <nuttx/config.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
@ -2712,7 +2713,8 @@ static int stm32_setchromakey(struct fb_vtable_s *vtable,
# ifdef CONFIG_STM32_FB_CMAP
if (oinfo->chromakey >= g_vtable.cmap.len)
{
lcderr("ERROR: Clut index %d is out of range\n", oinfo->chromakey);
lcderr("ERROR: Clut index %" PRIu32 " is out of range\n",
oinfo->chromakey);
ret = -EINVAL;
}
else

View file

@ -0,0 +1,105 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_MMCSD_HAVE_WRITEPROTECT is not set
# CONFIG_MMCSD_MMCSUPPORT is not set
# CONFIG_STANDARD_SERIAL is not set
# CONFIG_STM32_LTDC_L1_CHROMAKEYEN is not set
# CONFIG_STM32_LTDC_L2 is not set
CONFIG_ALLOW_GPL_COMPONENTS=y
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="linum-stm32h753bi"
CONFIG_ARCH_BOARD_LINUM_STM32H753BI=y
CONFIG_ARCH_CHIP="stm32h7"
CONFIG_ARCH_CHIP_STM32=y
CONFIG_ARCH_CHIP_STM32H753BI=y
CONFIG_ARCH_CHIP_STM32H7=y
CONFIG_ARCH_CHIP_STM32H7_CORTEXM7=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_ARMV7M_DCACHE=y
CONFIG_ARMV7M_DCACHE_WRITETHROUGH=y
CONFIG_ARMV7M_DTCM=y
CONFIG_ARMV7M_ICACHE=y
CONFIG_BOARD_LOOPSPERMSEC=43103
CONFIG_BUILTIN=y
CONFIG_CRYPTO=y
CONFIG_DEBUG_FEATURES=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DRIVERS_VIDEO=y
CONFIG_FAT_DMAMEMORY=y
CONFIG_FAT_FORCE_INDIRECT=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
CONFIG_FB_OVERLAY=y
CONFIG_FS_FAT=y
CONFIG_FS_PROCFS=y
CONFIG_GAMES_NXDOOM=y
CONFIG_GAMES_NXDOOM_ENDOOM=y
CONFIG_GAMES_NXDOOM_FB_CMAP=y
CONFIG_GAMES_NXDOOM_FILLSCREEN=y
CONFIG_GAMES_NXDOOM_KBDPATH="/dev/kbda"
CONFIG_GAMES_NXDOOM_PREFDIR="/mnt"
CONFIG_GAMES_NXDOOM_ROWSTAGE=y
CONFIG_GAMES_NXDOOM_STACKSIZE=16384
CONFIG_GAMES_NXDOOM_STATIC_SCRNBUF=y
CONFIG_GRAN=y
CONFIG_GRAN_INTR=y
CONFIG_HIDKBD_KBDUPPER=y
CONFIG_HIDKBD_NOGETREPORT=y
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_LOCALE=y
CONFIG_LIBM=y
CONFIG_LINE_MAX=64
CONFIG_MMCSD=y
CONFIG_MMCSD_SDIO=y
CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE=y
CONFIG_MM_REGIONS=5
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_DISABLE_IFUPDOWN=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=245760
CONFIG_RAM_START=0x20010000
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_RTC_ALARM=y
CONFIG_RTC_DATETIME=y
CONFIG_RTC_DRIVER=y
CONFIG_SCHED_HPWORK=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDMMC1_SDIO_MODE=y
CONFIG_START_DAY=6
CONFIG_START_MONTH=12
CONFIG_START_YEAR=2011
CONFIG_STM32_FMC=y
CONFIG_STM32_HSI48=y
CONFIG_STM32_I2C3=y
CONFIG_STM32_LTDC=y
CONFIG_STM32_LTDC_FB_BASE=0xC0600000
CONFIG_STM32_LTDC_FB_SIZE=2097152
CONFIG_STM32_OTGFS=y
CONFIG_STM32_PWR=y
CONFIG_STM32_RTC=y
CONFIG_STM32_SDMMC1=y
CONFIG_STM32_USART1=y
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_ZMODEM=y
CONFIG_SYSTEM_ZMODEM_MOUNTPOINT="/mnt"
CONFIG_SYSTEM_ZMODEM_PKTBUFSIZE=1024
CONFIG_SYSTEM_ZMODEM_RCVBUFSIZE=1024
CONFIG_SYSTEM_ZMODEM_SNDBUFSIZE=1024
CONFIG_TASK_NAME_SIZE=0
CONFIG_USART1_RXBUFSIZE=4096
CONFIG_USART1_SERIAL_CONSOLE=y
CONFIG_USART1_TXBUFSIZE=1024
CONFIG_USBHOST=y
CONFIG_USBHOST_ASYNCH=y
CONFIG_USBHOST_HIDKBD=y
CONFIG_USEC_PER_TICK=1000
CONFIG_VIDEO_FB=y

View file

@ -399,6 +399,35 @@ config HIDKBD_ENCODED
keys, etc. If this not defined, only 7-bit print-able and control
ASCII characters will be provided to the user.
config HIDKBD_KBDUPPER
bool "Register as a keyboard upper-half device"
default n
depends on !HIDKBD_RAWSCANCODES
select INPUT
select INPUT_KEYBOARD
---help---
Register /dev/kbd[n] through the keyboard upper-half driver rather
than as a raw character device. Applications then read
struct keyboard_event_s samples instead of a byte stream, which
reports key release events in addition to key press events, and
reports modifiers (Ctrl, Shift, Alt, GUI) as keys of their own.
This is what an application needs when it must know how long a key
is held down, such as a game. Note that the byte stream interface
is not available at the same time, so this breaks applications that
expect to read characters from /dev/kbd[n].
if HIDKBD_KBDUPPER
config HIDKBD_KBDUPPER_BUFNUMBER
int "Keyboard upper-half buffer size"
default 8
---help---
The number of keyboard events buffered by the upper-half driver
for each opened instance.
endif # HIDKBD_KBDUPPER
config HIDKBD_ALLSCANCODES
bool "Use All Scancodes"
default n

View file

@ -56,11 +56,16 @@
#include <nuttx/usb/hid.h>
#include <nuttx/usb/usbhost_devaddr.h>
#ifdef CONFIG_HIDKBD_ENCODED
#if defined(CONFIG_HIDKBD_ENCODED) || defined(CONFIG_HIDKBD_KBDUPPER)
# include <nuttx/streams.h>
# include <nuttx/input/kbd_codec.h>
#endif
#ifdef CONFIG_HIDKBD_KBDUPPER
# include <nuttx/nuttx.h>
# include <nuttx/input/keyboard.h>
#endif
/* Don't compile if prerequisites are not met */
#if defined(CONFIG_USBHOST) && !defined(CONFIG_USBHOST_INT_DISABLE)
@ -133,6 +138,19 @@
#ifdef CONFIG_HIDKBD_RAWSCANCODES
# undef CONFIG_HIDKBD_ENCODED
# undef CONFIG_HIDKBD_KBDUPPER
#endif
/* The table that maps scancodes to special key encodings is shared by the
* encoded byte stream and by the keyboard upper-half interface.
*/
#if defined(CONFIG_HIDKBD_ENCODED) || defined(CONFIG_HIDKBD_KBDUPPER)
# define HAVE_KBD_ENCODING 1
#endif
#ifndef CONFIG_HIDKBD_KBDUPPER_BUFNUMBER
# define CONFIG_HIDKBD_KBDUPPER_BUFNUMBER 8
#endif
/* Driver support ***********************************************************/
@ -238,8 +256,12 @@ struct usbhost_state_s
struct work_s rwork; /* For interrupt transfer work */
int16_t nbytes; /* # of bytes actually transferred */
#endif
#ifndef CONFIG_HIDKBD_NODEBOUNCE
uint8_t lastkey[6]; /* For debouncing */
#if !defined(CONFIG_HIDKBD_NODEBOUNCE) || defined(CONFIG_HIDKBD_KBDUPPER)
uint8_t lastkey[6]; /* Keys down in the previous report */
#endif
#ifdef CONFIG_HIDKBD_KBDUPPER
struct keyboard_lowerhalf_s lower; /* Keyboard upper-half interface */
uint8_t lastmod; /* Modifiers held in previous report */
#endif
};
@ -272,8 +294,10 @@ static inline void usbhost_mkdevname(FAR struct usbhost_state_s *priv,
/* Keyboard polling thread */
static void usbhost_destroy(FAR void *arg);
#ifndef CONFIG_HIDKBD_KBDUPPER
static void usbhost_putbuffer(FAR struct usbhost_state_s *priv,
uint8_t keycode);
#endif
#ifdef CONFIG_HIDKBD_ENCODED
static void usbhost_putstream(FAR struct lib_outstream_s *self, int ch);
#endif
@ -335,8 +359,17 @@ static int usbhost_connect(FAR struct usbhost_class_s *usbclass,
FAR const uint8_t *configdesc, int desclen);
static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass);
/* Driver methods. We export the keyboard as a standard character driver */
/* Driver methods. We export the keyboard either as a standard character
* driver or through the keyboard upper-half driver.
*/
static int usbhost_open_priv(FAR struct usbhost_state_s *priv);
static int usbhost_close_priv(FAR struct usbhost_state_s *priv);
#ifdef CONFIG_HIDKBD_KBDUPPER
static int usbhost_kbdupper_open(FAR struct keyboard_lowerhalf_s *lower);
static int usbhost_kbdupper_close(FAR struct keyboard_lowerhalf_s *lower);
#else
static int usbhost_open(FAR struct file *filep);
static int usbhost_close(FAR struct file *filep);
static ssize_t usbhost_read(FAR struct file *filep,
@ -345,6 +378,7 @@ static ssize_t usbhost_write(FAR struct file *filep,
FAR const char *buffer, size_t len);
static int usbhost_poll(FAR struct file *filep, FAR struct pollfd *fds,
bool setup);
#endif
/****************************************************************************
* Private Data
@ -374,6 +408,7 @@ static struct usbhost_registry_s g_hidkbd =
&g_hidkbd_id /* id[] */
};
#ifndef CONFIG_HIDKBD_KBDUPPER
static const struct file_operations g_hidkbd_fops =
{
usbhost_open, /* open */
@ -386,6 +421,24 @@ static const struct file_operations g_hidkbd_fops =
NULL, /* truncate */
usbhost_poll /* poll */
};
#endif
#ifdef CONFIG_HIDKBD_KBDUPPER
/* Maps the bits of the HID report modifier byte onto keycodes, so that a
* modifier can be reported as a key in its own right. Indexed by bit
* number: the modifier byte is defined by the HID specification to hold
* LCtrl, LShift, LAlt, LGUI, RCtrl, RShift, RAlt and RGUI, in that order.
*/
#define USBHID_NMODIFIERS 8
static const uint8_t g_modkeycode[USBHID_NMODIFIERS] =
{
KEYCODE_LCTRL, KEYCODE_LSHIFT, KEYCODE_LALT, KEYCODE_LGUI,
KEYCODE_RCTRL, KEYCODE_RSHIFT, KEYCODE_RALT, KEYCODE_RGUI
};
#endif
/* This is a bitmap that is used to allocate device names /dev/kbda-z. */
@ -403,7 +456,7 @@ static bool g_caps_lock = false;
*/
#ifndef CONFIG_HIDKBD_RAWSCANCODES
#ifdef CONFIG_HIDKBD_ENCODED
#ifdef HAVE_KBD_ENCODING
/* The first and last scancode values with encode-able values */
@ -800,7 +853,11 @@ static void usbhost_destroy(FAR void *arg)
uinfo("Unregister driver\n");
usbhost_mkdevname(priv, devname);
#ifdef CONFIG_HIDKBD_KBDUPPER
keyboard_unregister(&priv->lower, devname);
#else
unregister_driver(devname);
#endif
/* Release the device name used by this connection */
@ -860,6 +917,7 @@ static void usbhost_destroy(FAR void *arg)
*
****************************************************************************/
#ifndef CONFIG_HIDKBD_KBDUPPER
static void usbhost_putbuffer(FAR struct usbhost_state_s *priv,
uint8_t keycode)
{
@ -899,6 +957,7 @@ static void usbhost_putbuffer(FAR struct usbhost_state_s *priv,
priv->headndx = head;
}
#endif /* CONFIG_HIDKBD_KBDUPPER */
/****************************************************************************
* Name: usbhost_putstream
@ -1076,6 +1135,161 @@ static inline void usbhost_toggle_capslock(void)
spin_unlock_irqrestore(&g_lock, flags);
}
/****************************************************************************
* Name: usbhost_kbdupper_keycode
*
* Description:
* Map a HID scancode onto the keycode space used by the keyboard
* upper-half driver: 7-bit ASCII for printable keys, and the values of
* enum kbd_keycode_e for everything else.
*
* Input Parameters:
* scancode - Scan code to be mapped.
* modifier - Ctrl, Alt, Shift, GUI modifier bits
*
* Returned Value:
* The keycode, or zero if the scancode has no representation.
*
****************************************************************************/
#ifdef CONFIG_HIDKBD_KBDUPPER
static uint32_t usbhost_kbdupper_keycode(uint8_t scancode, uint8_t modifier)
{
/* Check for a special key first. Otherwise a key such as ENTER would be
* reported as the control character that it also maps to.
*/
if (scancode >= FIRST_ENCODING && scancode <= LAST_ENCODING)
{
uint8_t encoded = encoding[scancode - FIRST_ENCODING];
if (encoded != 0)
{
return encoded;
}
}
/* Otherwise fall back to the printable ASCII mapping. Note that Ctrl is
* deliberately not folded into a control character here: modifiers are
* reported as keys of their own, so the application still knows that Ctrl
* is down and can see which key it is being held with.
*/
return usbhost_mapscancode(scancode, modifier);
}
/****************************************************************************
* Name: usbhost_kbdupper_report
*
* Description:
* Turn a HID keyboard report into key press and key release events and
* hand them to the keyboard upper-half driver.
*
* The HID specification gives no significance to the order of the
* keycodes in the report array, so the only way to know what changed is
* to compare the report against the previous one.
*
* Input Parameters:
* priv - Driver internal state
*
* Returned Value:
* None
*
****************************************************************************/
static void usbhost_kbdupper_report(FAR struct usbhost_state_s *priv)
{
FAR struct usbhid_kbdreport_s *rpt =
(FAR struct usbhid_kbdreport_s *)priv->tbuffer;
uint32_t keycode;
uint8_t changed;
int i;
int j;
/* Report the modifiers whose state changed since the last report */
changed = rpt->modifier ^ priv->lastmod;
for (i = 0; changed != 0 && i < USBHID_NMODIFIERS; i++)
{
if ((changed & (1 << i)) != 0)
{
keyboard_event(&priv->lower, g_modkeycode[i],
(rpt->modifier & (1 << i)) != 0 ?
KEYBOARD_PRESS : KEYBOARD_RELEASE);
}
}
/* Report the keys that were released: down in the last report, but not
* in this one.
*/
for (i = 0; i < 6; i++)
{
if (priv->lastkey[i] == USBHID_KBDUSE_NONE)
{
continue;
}
for (j = 0; j < 6; j++)
{
if (rpt->key[j] == priv->lastkey[i])
{
break;
}
}
if (j >= 6)
{
keycode = usbhost_kbdupper_keycode(priv->lastkey[i],
rpt->modifier);
if (keycode != 0)
{
keyboard_event(&priv->lower, keycode, KEYBOARD_RELEASE);
}
}
}
/* Report the keys that were pressed: down in this report, but not in the
* last one.
*/
for (i = 0; i < 6; i++)
{
if (rpt->key[i] == USBHID_KBDUSE_NONE)
{
continue;
}
for (j = 0; j < 6; j++)
{
if (priv->lastkey[j] == rpt->key[i])
{
break;
}
}
if (j >= 6)
{
if (rpt->key[i] == USBHID_KBDUSE_CAPSLOCK)
{
usbhost_toggle_capslock();
}
keycode = usbhost_kbdupper_keycode(rpt->key[i], rpt->modifier);
if (keycode != 0)
{
keyboard_event(&priv->lower, keycode, KEYBOARD_PRESS);
}
}
}
/* Remember this report so that the next one can be compared against it */
memcpy(priv->lastkey, rpt->key, sizeof(priv->lastkey));
priv->lastmod = rpt->modifier;
}
#endif /* CONFIG_HIDKBD_KBDUPPER */
/****************************************************************************
* Name: usbhost_extract_keys
*
@ -1095,6 +1309,14 @@ static inline void usbhost_toggle_capslock(void)
static int usbhost_extract_keys(FAR struct usbhost_state_s *priv)
{
#ifdef CONFIG_HIDKBD_KBDUPPER
/* The upper-half driver does its own buffering and poll notification, so
* there is nothing else to do here.
*/
usbhost_kbdupper_report(priv);
return OK;
#else
struct usbhid_kbdreport_s *rpt =
(struct usbhid_kbdreport_s *)priv->tbuffer;
uint8_t keycode;
@ -1234,6 +1456,7 @@ static int usbhost_extract_keys(FAR struct usbhost_state_s *priv)
nxmutex_unlock(&priv->lock);
return 0;
#endif /* CONFIG_HIDKBD_KBDUPPER */
}
/****************************************************************************
@ -1991,7 +2214,20 @@ static inline int usbhost_devinit(FAR struct usbhost_state_s *priv)
uinfo("Register driver\n");
usbhost_mkdevname(priv, devname);
#ifdef CONFIG_HIDKBD_KBDUPPER
/* Do not set lower.priv here: keyboard_register() claims that field for
* the upper half. We recover our own state with container_of().
*/
priv->lower.open = usbhost_kbdupper_open;
priv->lower.close = usbhost_kbdupper_close;
priv->lower.write = NULL;
ret = keyboard_register(&priv->lower, devname,
CONFIG_HIDKBD_KBDUPPER_BUFNUMBER);
#else
ret = register_driver(devname, &g_hidkbd_fops, 0600, priv);
#endif
/* We now have to be concerned about asynchronous modification of crefs
* because the driver has been registered.
@ -2468,23 +2704,21 @@ static int usbhost_disconnected(FAR struct usbhost_class_s *usbclass)
}
/****************************************************************************
* Name: usbhost_open
* Name: usbhost_open_priv
*
* Description:
* Standard character driver open method.
* Take a reference on the driver instance. This is the common part of
* opening the device, shared by the character driver and the keyboard
* upper-half interfaces.
*
****************************************************************************/
static int usbhost_open(FAR struct file *filep)
static int usbhost_open_priv(FAR struct usbhost_state_s *priv)
{
FAR struct inode *inode;
FAR struct usbhost_state_s *priv;
irqstate_t flags;
int ret;
uinfo("Entry\n");
inode = filep->f_inode;
priv = inode->i_private;
/* Make sure that we have exclusive access to the private data structure */
@ -2526,23 +2760,22 @@ static int usbhost_open(FAR struct file *filep)
}
/****************************************************************************
* Name: usbhost_close
* Name: usbhost_close_priv
*
* Description:
* Standard character driver close method.
* Drop a reference on the driver instance, destroying it if this was the
* last reference to a disconnected device. This is the common part of
* closing the device, shared by the character driver and the keyboard
* upper-half interfaces.
*
****************************************************************************/
static int usbhost_close(FAR struct file *filep)
static int usbhost_close_priv(FAR struct usbhost_state_s *priv)
{
FAR struct inode *inode;
FAR struct usbhost_state_s *priv;
irqstate_t flags;
int ret;
uinfo("Entry\n");
inode = filep->f_inode;
priv = inode->i_private;
/* Decrement the reference count on the driver */
@ -2621,6 +2854,68 @@ static int usbhost_close(FAR struct file *filep)
return OK;
}
#ifdef CONFIG_HIDKBD_KBDUPPER
/****************************************************************************
* Name: usbhost_kbdupper_open
*
* Description:
* Keyboard upper-half open method.
*
****************************************************************************/
static int usbhost_kbdupper_open(FAR struct keyboard_lowerhalf_s *lower)
{
/* Note that lower->priv cannot be used to find our state: it belongs to
* the upper half, which overwrites it in keyboard_register().
*/
return usbhost_open_priv(container_of(lower, struct usbhost_state_s,
lower));
}
/****************************************************************************
* Name: usbhost_kbdupper_close
*
* Description:
* Keyboard upper-half close method.
*
****************************************************************************/
static int usbhost_kbdupper_close(FAR struct keyboard_lowerhalf_s *lower)
{
return usbhost_close_priv(container_of(lower, struct usbhost_state_s,
lower));
}
#else /* CONFIG_HIDKBD_KBDUPPER */
/****************************************************************************
* Name: usbhost_open
*
* Description:
* Standard character driver open method.
*
****************************************************************************/
static int usbhost_open(FAR struct file *filep)
{
return usbhost_open_priv(filep->f_inode->i_private);
}
/****************************************************************************
* Name: usbhost_close
*
* Description:
* Standard character driver close method.
*
****************************************************************************/
static int usbhost_close(FAR struct file *filep)
{
return usbhost_close_priv(filep->f_inode->i_private);
}
/****************************************************************************
* Name: usbhost_read
*
@ -2851,6 +3146,8 @@ errout:
return ret;
}
#endif /* CONFIG_HIDKBD_KBDUPPER */
/****************************************************************************
* Public Functions
****************************************************************************/

View file

@ -166,7 +166,24 @@ enum kbd_keycode_e
KEYCODE_F21, /* Function key 21 */
KEYCODE_F22, /* Function key 22 */
KEYCODE_F23, /* Function key 23 */
KEYCODE_F24 /* Function key 24 */
KEYCODE_F24, /* Function key 24 */
/* Modifier keys. Drivers that are able to track modifier state (a USB
* HID keyboard, for example) may report these as key press and key
* release events in their own right. That allows an application to bind
* an action to a modifier, or to know that a modifier is being held down,
* which cannot be expressed by folding the modifier into the character
* that it produces.
*/
KEYCODE_LCTRL, /* Left Ctrl */
KEYCODE_RCTRL, /* Right Ctrl */
KEYCODE_LSHIFT, /* Left Shift */
KEYCODE_RSHIFT, /* Right Shift */
KEYCODE_LALT, /* Left Alt */
KEYCODE_RALT, /* Right Alt */
KEYCODE_LGUI, /* Left GUI (Windows/Command) */
KEYCODE_RGUI /* Right GUI (Windows/Command) */
};
#define FIRST_KEYCODE KEYCODE_FWDDEL