mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Merge 0d102f1cff into d960bc39ee
This commit is contained in:
commit
8a229ecb6d
8 changed files with 666 additions and 21 deletions
|
|
@ -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
|
||||
-----------
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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
|
||||
|
|
|
|||
105
boards/arm/stm32h7/linum-stm32h753bi/configs/nxdoom/defconfig
Normal file
105
boards/arm/stm32h7/linum-stm32h753bi/configs/nxdoom/defconfig
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
****************************************************************************/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue