[PATCH 0/6] Provide basic support for a mouse

From: Simon Glass <sjg@chromium.org> This series introduces a mouse uclass and provides drivers for two types of mouse: sandbox (using SDL) and USB (potentially usable on any hardware). Note that this was written four years ago, so may need some tweaking. I have tidied it up so that it builds and that (after running sandbox with the -Dl flags), 'mouse dump' shows mouse events. Simon Glass (6): Add a few floating-point routines input: Add mouse support sandbox: sdl: Add support for mouse input usb: Add a USB mouse driver input: Add a command to show mouse input sandbox: Enable the mouse arch/sandbox/cpu/sdl.c | 48 ++++- arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/include/asm/sdl.h | 4 + cmd/Kconfig | 9 + cmd/Makefile | 1 + cmd/mouse.c | 70 +++++++ drivers/input/Kconfig | 17 ++ drivers/input/Makefile | 4 + drivers/input/mouse-uclass.c | 28 +++ drivers/input/sandbox_mouse.c | 35 ++++ drivers/input/usb_mouse.c | 325 +++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/mouse.h | 78 ++++++++ include/usb.h | 1 + include/vsprintf.h | 25 +++ lib/Kconfig | 13 ++ lib/Makefile | 1 + lib/strtof.c | 200 ++++++++++++++++++++ 18 files changed, 859 insertions(+), 5 deletions(-) create mode 100644 cmd/mouse.c create mode 100644 drivers/input/mouse-uclass.c create mode 100644 drivers/input/sandbox_mouse.c create mode 100644 drivers/input/usb_mouse.c create mode 100644 include/mouse.h create mode 100644 lib/strtof.c -- 2.43.0 base-commit: 03cd5e055b153666b836bbc0c3d3b3c7ade52bf0 branch: gu

From: Simon Glass <sjg@chromium.org> U-Boot does not normally make use of floating point and it is generally possible and desirable to avoid it within a bootloader. However there are libraries which need it. For example, U-Boot's Truetype font implementation needs floating point and some features such as the Nuklear GUI make use of it also. Enable this to compile in some basic library functions for floating point. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/vsprintf.h | 25 ++++++ lib/Kconfig | 13 +++ lib/Makefile | 1 + lib/strtof.c | 200 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 lib/strtof.c diff --git a/include/vsprintf.h b/include/vsprintf.h index 9da6ce7cc4d..9e86f395331 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -364,4 +364,29 @@ int vsscanf(const char *inp, char const *fmt0, va_list ap); */ int sscanf(const char *buf, const char *fmt, ...); +/** + * strtod() - Convert text floating-point number to double + * + * @str: String to convert + * @entptr: If non-NULL, set to point to the character after the last one that + * was part of the floating-point number + * @return double-precision floating-point representation of the characters in + * @str. + */ +double strtod(const char *str, char **endptr); + +/** + * strtod() - Convert text floating-point number to double + * + * @str: decimal ASCII floating-point number, optionally preceded by whitespace. + * Must have form "-I.FE-X", where I is the integer part of the mantissa, F is + * the fractional part of the mantissa, and X is the exponent. Either of the + * signs may be "+", "-", or omitted. Either I or F may be omitted, or both. + * The decimal point isn't necessary unless F is present. The "E" may actually + * be an "e". E and X may both be omitted (but not just one). + * @return double-precision floating-point representation of the characters in + * @str. + */ +double atof(const char *str); + #endif diff --git a/lib/Kconfig b/lib/Kconfig index ed35c1f0b30..b9866619c5d 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -1246,6 +1246,19 @@ config PHANDLE_CHECK_SEQ enable this config option to distinguish them using phandles in fdtdec_get_alias_seq() function. +config FLOAT + bool "Support floating-point functions" + help + U-Boot does not normally make use of floating point and it is + generally possible and desirable to avoid it within a bootloader. + + However there are libraries which need it. For example, U-Boot's + Truetype font implementation needs floating point and some features + such as the Nuklear GUI make use of it also. + + Enable this to compile in some basic library functions for floating + point. + endmenu source "lib/fwu_updates/Kconfig" diff --git a/lib/Makefile b/lib/Makefile index 84b94d78680..b9a5378e8aa 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -155,6 +155,7 @@ obj-y += vsprintf.o strto.o obj-$(CONFIG_SSCANF) += sscanf.o endif obj-$(CONFIG_$(PHASE_)OID_REGISTRY) += oid_registry.o +obj-$(CONFIG_FLOAT) += strtof.o obj-y += abuf.o obj-y += alist.o diff --git a/lib/strtof.c b/lib/strtof.c new file mode 100644 index 00000000000..f26eca5ab79 --- /dev/null +++ b/lib/strtof.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * Copyright 2020 Google LLC + * Relicensed as GPL-2.0+ for U-Boot + * + * Modified from commit fae05bc at:: + * github.com/embeddedartistry/embedded-resources/tree/master/examples/libc/stdlib + */ + +#include <linux/ctype.h> +#include <stdbool.h> +#include <stddef.h> + +/* + * Largest possible base--10 exponent. Any exponent larger than this will + * already produce underflow or overflow, so there's no need to worry about + * additional digits. + */ +static int maxExponent = 511; + +/* + * Table giving binary powers of 10. Entry is 10^2^i. Used to convert decimal + * exponents into floating-point numbers + */ +static double powersOf10[] = { + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +/* + * Details on @str: + */ +double strtod(const char *string, char **endptr) +{ + int sign, expsign = false; + double fraction, dblexp, *d; + const char *p; + int c; + int exp = 0; /* Exponent read from "EX" field */ + /* + * Exponent that derives from the fractional part. Under normal + * circumstatnces, it is the negative of the number of digits in F. + * However if I is very long, the last digits of I get dropped + * (otherwise a long I with a large negative exponent could cause an + * unnecessary overflow on I alone). In this case, fracexp is + * incremented one for each dropped digit. + */ + int fracexp = 0; + int mantsize; /* Number of digits in mantissa */ + int decpt; /* Number of mantissa digits BEFORE decimal point */ + /* Temporarily holds location of exponent in string */ + const char *pexp; + + /* Strip off leading blanks and check for a sign */ + p = string; + while (isspace(*p)) + p += 1; + if (*p == '-') { + sign = true; + p += 1; + } else { + if (*p == '+') + p += 1; + sign = false; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + decpt = -1; + for (mantsize = 0;; mantsize += 1) { + c = *p; + if (!isdigit(c)) { + if (c != '.' || decpt >= 0) + break; + decpt = mantsize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + pexp = p; + p -= mantsize; + if (decpt < 0) + decpt = mantsize; + else + mantsize -= 1; /* One of the digits was the point */ + if (mantsize > 18) { + fracexp = decpt - 18; + mantsize = 18; + } else { + fracexp = decpt - mantsize; + } + if (!mantsize) { + fraction = 0.0; + p = string; + goto done; + } else { + int frac1, frac2; + + frac1 = 0; + for (; mantsize > 9; mantsize -= 1) { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10 * frac1 + (c - '0'); + } + frac2 = 0; + for (; mantsize > 0; mantsize -= 1) { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10 * frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* Skim off the exponent */ + p = pexp; + if (*p == 'E' || *p == 'e') { + p += 1; + if (*p == '-') { + expsign = true; + p += 1; + } else { + if (*p == '+') + p += 1; + expsign = false; + } + if (!isdigit(*p)) { + p = pexp; + goto done; + } + while (isdigit(*p)) { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expsign) + exp = fracexp - exp; + else + exp = fracexp + exp; + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + if (exp < 0) { + expsign = true; + exp = -exp; + } else { + expsign = false; + } + if (exp > maxExponent) + exp = maxExponent; /* errno = ERANGE; */ + dblexp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + if (exp & 01) + dblexp *= *d; + } + if (expsign) + fraction /= dblexp; + else + fraction *= dblexp; + +done: + if (endptr) + *endptr = (char *)p; + + if (sign) + return -fraction; + + return fraction; +} + +double atof(const char *str) +{ + return strtod(str, NULL); +} -- 2.43.0

From: Simon Glass <sjg@chromium.org> When running a simple GUI it is useful to support a mouse. This is similar to what is provided in UEFI's boot menu. Add a simple uclass and a way to read the mouse position. For sandbox add a driver that reads the position from SDL. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/input/Kconfig | 9 ++++ drivers/input/Makefile | 3 ++ drivers/input/mouse-uclass.c | 28 +++++++++++++ drivers/input/sandbox_mouse.c | 35 ++++++++++++++++ include/dm/uclass-id.h | 1 + include/mouse.h | 78 +++++++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+) create mode 100644 drivers/input/mouse-uclass.c create mode 100644 drivers/input/sandbox_mouse.c create mode 100644 include/mouse.h diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index c2b365af11d..6bfee40ccac 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -100,3 +100,12 @@ config TWL4030_INPUT bool "Enable TWL4030 Input controller" help Enable TWL4030 Input controller + +config MOUSE + bool "Support for mice and other pointing devices" + default y if SANDBOX + help + This allows U-Boot to access mouse input, typically needed for + graphics boot menus and the like. The driver can provide mouse + events based on user interaction and these can be used to control + U-Boot's operation. diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 8d4107b8848..a4938d19903 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -15,3 +15,6 @@ obj-$(CONFIG_I8042_KEYB) += i8042.o obj-$(CONFIG_TEGRA_KEYBOARD) += input.o tegra-kbc.o obj-$(CONFIG_TWL4030_INPUT) += twl4030.o endif + +obj-$(CONFIG_MOUSE) += mouse-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox_mouse.o diff --git a/drivers/input/mouse-uclass.c b/drivers/input/mouse-uclass.c new file mode 100644 index 00000000000..ddf948f7e78 --- /dev/null +++ b/drivers/input/mouse-uclass.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <dm.h> +#include <mouse.h> + +int mouse_get_event(struct udevice *dev, struct mouse_event *evt) +{ + struct mouse_ops *ops = mouse_get_ops(dev); + int ret; + + if (!ops->get_event) + return -ENOSYS; + + ret = ops->get_event(dev, evt); + if (ret) + return ret; + + return 0; +} + +UCLASS_DRIVER(mouse) = { + .id = UCLASS_MOUSE, + .name = "mouse", +}; diff --git a/drivers/input/sandbox_mouse.c b/drivers/input/sandbox_mouse.c new file mode 100644 index 00000000000..4aedf0fdf2d --- /dev/null +++ b/drivers/input/sandbox_mouse.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <dm.h> +#include <mouse.h> +#include <asm/sdl.h> + +static int mouse_sandbox_get_event(struct udevice *dev, + struct mouse_event *event) +{ + int ret; + + ret = sandbox_sdl_get_mouse_event(event); + + return ret; +} + +const struct mouse_ops mouse_sandbox_ops = { + .get_event = mouse_sandbox_get_event, +}; + +static const struct udevice_id mouse_sandbox_ids[] = { + { .compatible = "sandbox,mouse" }, + { } +}; + +U_BOOT_DRIVER(mouse_sandbox) = { + .name = "mouse_sandbox", + .id = UCLASS_MOUSE, + .of_match = mouse_sandbox_ids, + .ops = &mouse_sandbox_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index c558c95f465..a424ef00fc9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -97,6 +97,7 @@ enum uclass_id { UCLASS_MISC, /* Miscellaneous device */ UCLASS_MMC, /* SD / MMC card or chip */ UCLASS_MOD_EXP, /* RSA Mod Exp device */ + UCLASS_MOUSE, /* Mouse, trackpad or other pointing device */ UCLASS_MTD, /* Memory Technology Device (MTD) device */ UCLASS_MUX, /* Multiplexer device */ UCLASS_NOP, /* No-op devices */ diff --git a/include/mouse.h b/include/mouse.h new file mode 100644 index 00000000000..81704047f5d --- /dev/null +++ b/include/mouse.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Mouse/trackpad/touchscreen input uclass + * + * Copyright 2020 Google LLC + */ + +#ifndef _MOUSE_H +#define _MOUSE_H + +enum mouse_ev_t { + MOUSE_EV_NULL, + MOUSE_EV_MOTION, + MOUSE_EV_BUTTON, +}; + +enum mouse_state_t { + BUTTON_LEFT = 1 << 0, + BUTTON_MIDDLE = 1 << 1, + BUTTON_RIGHT = 1 << 2, + BUTTON_SCROLL_PLUS = 1 << 3, + BUTTON_SCROLL_MINUS = 1 << 4, +}; + +enum mouse_press_state_t { + BUTTON_RELEASED = 0, + BUTTON_PRESSED, +}; + +/** + * struct mouse_event - information about a mouse event + * + * @type: Mouse event ype + */ +struct mouse_event { + enum mouse_ev_t type; + union { + /** + * @state: Mouse state (enum mouse_state_t bitmask) + * @x: X position of mouse + * @y: Y position of mouse + * @xrel: Relative motion in X direction + * @yrel: Relative motion in Y direction + */ + struct mouse_motion { + unsigned char state; + unsigned short x; + unsigned short y; + short xrel; + short yrel; + } motion; + + /** + * @button: Button number that was pressed/released (BUTTON_...) + * @state: BUTTON_PRESSED / BUTTON_RELEASED + * @clicks: number of clicks (normally 1; 2 = double-click) + * @x: X position of mouse + * @y: Y position of mouse + */ + struct mouse_button { + unsigned char button; + unsigned char press_state; + unsigned char clicks; + unsigned short x; + unsigned short y; + } button; + }; +}; + +struct mouse_ops { + int (*get_event)(struct udevice *dev, struct mouse_event *event); +}; + +#define mouse_get_ops(dev) ((struct mouse_ops *)(dev)->driver->ops) + +int mouse_get_event(struct udevice *dev, struct mouse_event *event); + +#endif -- 2.43.0

From: Simon Glass <sjg@chromium.org> Allow mouse input to be reported from sandbox using SDL. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/sdl.c | 48 ++++++++++++++++++++++++++++++---- arch/sandbox/include/asm/sdl.h | 4 +++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c index ed84646bdab..b705e15cf9c 100644 --- a/arch/sandbox/cpu/sdl.c +++ b/arch/sandbox/cpu/sdl.c @@ -4,6 +4,7 @@ */ #include <errno.h> +#include <mouse.h> #include <unistd.h> #include <stdbool.h> #include <sysreset.h> @@ -67,15 +68,11 @@ static struct sdl_info { SDL_Renderer *renderer; SDL_Window *screen; int src_depth; + struct mouse_event mouse; } sdl; static void sandbox_sdl_poll_events(void) { - /* - * We don't want to include cpu_func.h in this file since it uses - * system headers. So add a declation here. - */ - extern void reset_cpu(void); SDL_Event event; while (SDL_PollEvent(&event)) { @@ -84,10 +81,50 @@ static void sandbox_sdl_poll_events(void) puts("LCD window closed - quitting\n"); sysreset_walk(SYSRESET_POWER_OFF); break; + case SDL_MOUSEMOTION: { + struct mouse_event *m = &sdl.mouse; + + m->type = MOUSE_EV_MOTION; + m->motion.state = 0; + m->motion.x = event.motion.x; + m->motion.y = event.motion.y; + m->motion.xrel = event.motion.xrel; + m->motion.yrel = event.motion.yrel; + break; + } + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: { + struct mouse_event *m = &sdl.mouse; + + m->type = MOUSE_EV_BUTTON; + if (event.button.button == SDL_BUTTON_LEFT) + m->button.button = BUTTON_LEFT; + else if (event.button.button == SDL_BUTTON_MIDDLE) + m->button.button = BUTTON_MIDDLE; + else if (event.button.button == SDL_BUTTON_RIGHT) + m->button.button = BUTTON_RIGHT; + m->button.press_state = event.type == + SDL_MOUSEBUTTONDOWN ? + BUTTON_PRESSED : BUTTON_RELEASED; + m->button.x = event.button.x; + m->button.y = event.motion.y; + break; + } } } } +int sandbox_sdl_get_mouse_event(struct mouse_event *evt) +{ + if (sdl.mouse.type == MOUSE_EV_NULL) + return -EAGAIN; + + *evt = sdl.mouse; + sdl.mouse.type = MOUSE_EV_NULL; + + return 0; +} + static int sandbox_sdl_ensure_init(void) { if (!sdl.inited) { @@ -521,6 +558,7 @@ int sandbox_sdl_sound_init(int rate, int channels) sdl.sample_rate = wanted.freq; sdl.cur_buf = 0; sdl.running = false; + sdl.mouse.type = MOUSE_EV_NULL; return 0; diff --git a/arch/sandbox/include/asm/sdl.h b/arch/sandbox/include/asm/sdl.h index ee4991f7c24..190b52bd407 100644 --- a/arch/sandbox/include/asm/sdl.h +++ b/arch/sandbox/include/asm/sdl.h @@ -9,6 +9,8 @@ #include <errno.h> #include <video.h> +struct mouse_event; + #ifdef CONFIG_SANDBOX_SDL /** @@ -104,6 +106,8 @@ int sandbox_sdl_sound_init(int rate, int channels); */ int sandbox_sdl_set_bpp(struct udevice *dev, enum video_log2_bpp l2bpp); +int sandbox_sdl_get_mouse_event(struct mouse_event *evt); + #else static inline int sandbox_sdl_init_display(int width, int height, int log2_bpp, bool double_size) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add a basic mouse driver for USB mice. It only handles very basic mice so assumes that the reports are in the basic format described by the USB HID specification 1.11. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/input/Kconfig | 8 + drivers/input/Makefile | 1 + drivers/input/usb_mouse.c | 325 ++++++++++++++++++++++++++++++++++++++ include/usb.h | 1 + 4 files changed, 335 insertions(+) create mode 100644 drivers/input/usb_mouse.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 6bfee40ccac..bcab0408d70 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -109,3 +109,11 @@ config MOUSE graphics boot menus and the like. The driver can provide mouse events based on user interaction and these can be used to control U-Boot's operation. + +config USB_MOUSE + bool "USB mouse support" + help + This enables using a USB mouse to control a feature in U-Boot, + typically a boot menu. The driver uses the USB boot interface of + the mouse and attempts to auto-configure itself for normal + operation. diff --git a/drivers/input/Makefile b/drivers/input/Makefile index a4938d19903..f1c136b214c 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -18,3 +18,4 @@ endif obj-$(CONFIG_MOUSE) += mouse-uclass.o obj-$(CONFIG_SANDBOX) += sandbox_mouse.o +obj-$(CONFIG_USB_MOUSE) += usb_mouse.o diff --git a/drivers/input/usb_mouse.c b/drivers/input/usb_mouse.c new file mode 100644 index 00000000000..b5b5ba2f894 --- /dev/null +++ b/drivers/input/usb_mouse.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * USB mouse driver (parts taken from usb_kbd.c) + * + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Part of this source has been derived from the Linux USB + * project. + * + * Copyright 2020 Google LLC + */ + +#define LOG_CATEGORY UCLASS_MOUSE + +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <mouse.h> +#include <time.h> +#include <usb.h> + +enum { + RPT_BUTTON, + RPT_XREL, + RPT_YREL, + RPT_SCROLLY, +}; + +struct usb_mouse_priv { + ulong intpipe; + int intpktsize; + int intinterval; + ulong last_report; + struct int_queue *intq; + u32 repeat_delay; + int xrel; + int yrel; + int x; + int y; + int buttons; + int old_buttons; + int yscroll; + /* + * TODO(sjg@chromium.org): Use an array instead, with the + * DM_FLAG_ALLOC_PRIV_DMA flag + */ + s8 *buf; + u8 flags; +}; + +/* Interrupt service routine */ +static int usb_mouse_irq_worker(struct udevice *dev) +{ + struct usb_mouse_priv *priv = dev_get_priv(dev); + s8 *buf = priv->buf; + + priv->buttons = buf[RPT_BUTTON]; + priv->xrel = buf[RPT_XREL]; + if (priv->xrel < -127) + priv->xrel = 0; + priv->yrel = buf[RPT_YREL]; + if (priv->yrel < -127) + priv->yrel = 0; + priv->yscroll = buf[RPT_SCROLLY]; + + return 1; +} + +/* Mouse interrupt handler */ +static int usb_mouse_irq(struct usb_device *udev) +{ + struct udevice *dev = udev->dev; + + if (udev->irq_status || udev->irq_act_len != USB_MOUSE_BOOT_REPORT_SIZE) { + log_warning("Error %lx, len %d\n", udev->irq_status, + udev->irq_act_len); + return 1; + } + + return usb_mouse_irq_worker(dev); +} + +/* Interrupt polling */ +static void usb_mouse_poll_for_event(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_mouse_priv *priv = dev_get_priv(dev); + int ret; + + if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL)) { + /* Submit an interrupt transfer request */ + if (usb_int_msg(udev, priv->intpipe, priv->buf, + priv->intpktsize, priv->intinterval, true) >= 0) + usb_mouse_irq_worker(dev); + } else if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) || + IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE)) { + bool got_report = false; + + if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP)) { + struct usb_interface *iface; + + iface = &udev->config.if_desc[0]; + ret = usb_get_report(udev, iface->desc.bInterfaceNumber, + 1, 0, priv->buf, + USB_MOUSE_BOOT_REPORT_SIZE); + printf("control ret=%d\b", ret); + } else { + if (poll_int_queue(udev, priv->intq)) { + usb_mouse_irq_worker(dev); + /* We've consumed all queued int packets, create new */ + destroy_int_queue(udev, priv->intq); + priv->intq = create_int_queue(udev, + priv->intpipe, 1, + USB_MOUSE_BOOT_REPORT_SIZE, priv->buf, + priv->intinterval); + got_report = true; + } + } + if (got_report) + priv->last_report = get_timer(0); + } +} + +static int usb_mouse_get_event(struct udevice *dev, struct mouse_event *event) +{ + struct usb_mouse_priv *priv = dev_get_priv(dev); + + if (priv->buttons != priv->old_buttons) { + struct mouse_button *but = &event->button; + u8 diff; + int i; + + event->type = MOUSE_EV_BUTTON; + diff = priv->buttons ^ priv->old_buttons; + log_debug("buttons=%d, old=%d, diff=%d\n", priv->buttons, + priv->old_buttons, diff); + for (i = 0; i < 3; i++) { + u8 mask = 1 << i; + + if (diff && mask) { + but->button = i; + but->press_state = priv->buttons & mask; + but->clicks = 1; + but->x = priv->x; + but->y = priv->y; + priv->old_buttons ^= mask; + break; + } + } + log_debug(" end: buttons=%d, old=%d, diff=%d\n", priv->buttons, + priv->old_buttons, diff); + } else if (priv->xrel || priv->yrel) { + struct mouse_motion *motion = &event->motion; + + priv->x += priv->xrel; + priv->x = max(priv->x, 0); + priv->x = min(priv->x, 0xffff); + + priv->y += priv->yrel; + priv->y = max(priv->y, 0); + priv->y = min(priv->y, 0xffff); + + event->type = MOUSE_EV_MOTION; + motion->state = priv->buttons; + motion->x = priv->x; + motion->y = priv->y; + motion->xrel = priv->xrel; + motion->yrel = priv->yrel; + priv->xrel = 0; + priv->yrel = 0; + } else { + usb_mouse_poll_for_event(dev); + return -EAGAIN; + } + + return 0; +} + +static int check_mouse(struct usb_device *udev, int ifnum) +{ + struct usb_endpoint_descriptor *ep; + struct usb_interface *iface; + + if (udev->descriptor.bNumConfigurations != 1) + return log_msg_ret("numcfg", -EINVAL); + + iface = &udev->config.if_desc[ifnum]; + + if (iface->desc.bInterfaceClass != USB_CLASS_HID) + return log_msg_ret("if class", -EINVAL); + + if (iface->desc.bInterfaceSubClass != USB_SUB_HID_BOOT) + return log_msg_ret("if subclass", -EINVAL); + + if (iface->desc.bInterfaceProtocol != USB_PROT_HID_MOUSE) + return log_msg_ret("if protocol", -EINVAL); + + if (iface->desc.bNumEndpoints != 1) + return log_msg_ret("num endpoints", -EINVAL); + + ep = &iface->ep_desc[0]; + + /* Check if endpoint 1 is interrupt endpoint */ + if (!(ep->bEndpointAddress & 0x80)) + return log_msg_ret("ep not irq", -EINVAL); + + if ((ep->bmAttributes & 3) != 3) + return log_msg_ret("ep attr", -EINVAL); + + return 0; +} + +/* probes the USB device dev for mouse type */ +static int usb_mouse_probe(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_mouse_priv *priv = dev_get_priv(dev); + struct usb_endpoint_descriptor *ep; + struct usb_interface *iface; + const int ifnum = 0; + int ret; + + ret = check_mouse(udev, ifnum); + if (ret) { + log_warning("Mouse detect fail (err=%d)\n", ret); + return log_msg_ret("probe", ret); + } + log_debug("USB mouse: found set protocol...\n"); + + /* allocate input buffer aligned and sized to USB DMA alignment */ + priv->buf = memalign(USB_DMA_MINALIGN, + roundup(USB_MOUSE_BOOT_REPORT_SIZE, USB_DMA_MINALIGN)); + + /* Insert private data into USB device structure */ + udev->privptr = priv; + + /* Set IRQ handler */ + udev->irq_handle = usb_mouse_irq; + + iface = &udev->config.if_desc[ifnum]; + ep = &iface->ep_desc[0]; + priv->intpipe = usb_rcvintpipe(udev, ep->bEndpointAddress); + priv->intpktsize = min(usb_maxpacket(udev, priv->intpipe), + USB_MOUSE_BOOT_REPORT_SIZE); + priv->intinterval = ep->bInterval; + priv->last_report = -1; + + /* We found a USB Keyboard, install it. */ + usb_set_protocol(udev, iface->desc.bInterfaceNumber, 0); + + log_debug("Found set idle...\n"); + usb_set_idle(udev, iface->desc.bInterfaceNumber, 0, 0); + + log_debug("Enable interrupt pipe...\n"); + if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE)) { + priv->intq = create_int_queue(udev, priv->intpipe, 1, + USB_MOUSE_BOOT_REPORT_SIZE, + priv->buf, priv->intinterval); + printf("priv->intq %p\n", priv->intq); + ret = priv->intq ? 0 : -EBUSY; + } else if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP)) { + ret = usb_get_report(udev, iface->desc.bInterfaceNumber, 1, 0, + priv->buf, USB_MOUSE_BOOT_REPORT_SIZE); + } else { + ret = usb_int_msg(udev, priv->intpipe, priv->buf, + priv->intpktsize, priv->intinterval, false); + } + if (ret < 0) { + log_warning("Failed to get mouse state from device %04x:%04x (err=%d)\n", + udev->descriptor.idVendor, + udev->descriptor.idProduct, ret); + /* Abort, we don't want to use that non-functional keyboard */ + return ret; + } + log_info("USB mouse OK\n"); + + /* Success */ + return 0; +} + +static int usb_mouse_remove(struct udevice *dev) +{ + struct usb_device *udev = dev_get_parent_priv(dev); + struct usb_mouse_priv *priv = dev_get_priv(dev); + + if (IS_ENABLED(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE)) + destroy_int_queue(udev, priv->intq); + free(priv->buf); + + return 0; +} + +const struct mouse_ops usb_mouse_ops = { + .get_event = usb_mouse_get_event, +}; + +static const struct udevice_id usb_mouse_ids[] = { + { .compatible = "usb-mouse" }, + { } +}; + +U_BOOT_DRIVER(usb_mouse) = { + .name = "usb_mouse", + .id = UCLASS_MOUSE, + .of_match = usb_mouse_ids, + .ops = &usb_mouse_ops, + .probe = usb_mouse_probe, + .remove = usb_mouse_remove, + .priv_auto = sizeof(struct usb_mouse_priv), +}; + +static const struct usb_device_id mouse_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = USB_SUB_HID_BOOT, + .bInterfaceProtocol = USB_PROT_HID_MOUSE, + }, + { } /* Terminating entry */ +}; + +U_BOOT_USB_DEVICE(usb_mouse, mouse_id_table); diff --git a/include/usb.h b/include/usb.h index be37ed272e1..7e05ef26d6b 100644 --- a/include/usb.h +++ b/include/usb.h @@ -255,6 +255,7 @@ int usb_host_eth_scan(int mode); * Appendix B of HID Device Class Definition 1.11 */ #define USB_KBD_BOOT_REPORT_SIZE 8 +#define USB_MOUSE_BOOT_REPORT_SIZE 8 /* * usb_init() - initialize the USB Controllers -- 2.43.0

From: Simon Glass <sjg@chromium.org> This reads mouse input and shows it on the terminal. It is useful for testing mice. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/Kconfig | 9 +++++++ cmd/Makefile | 1 + cmd/mouse.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 cmd/mouse.c diff --git a/cmd/Kconfig b/cmd/Kconfig index 882a4ee02e3..1a879d1fe90 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -2934,6 +2934,15 @@ config CMD_LOG maximum log level for emitting of records). It also provides access to a command used for testing the log system. +config CMD_MOUSE + bool "mouse - Show mouse input" + default y if MOUSE + help + This shows the data produced by a mouse. If a mouse device is + available records are printed when the mouse is moved. This can be + useful for checking that a mouse is working correctly within + U-Boot. + config CMD_TRACE bool "trace - Support tracing of function calls and timing" depends on TRACE diff --git a/cmd/Makefile b/cmd/Makefile index 407056b778b..2a9c39f18e2 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -128,6 +128,7 @@ obj-$(CONFIG_CMD_CLONE) += clone.o ifneq ($(CONFIG_CMD_NAND)$(CONFIG_CMD_SF),) obj-y += legacy-mtd-utils.o endif +obj-$(CONFIG_CMD_MOUSE) += mouse.o obj-$(CONFIG_CMD_MUX) += mux.o obj-$(CONFIG_CMD_NAND) += nand.o ifdef CONFIG_NET diff --git a/cmd/mouse.c b/cmd/mouse.c new file mode 100644 index 00000000000..94434afa0c4 --- /dev/null +++ b/cmd/mouse.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mouse testing + * + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <command.h> +#include <console.h> +#include <dm.h> +#include <mouse.h> +#include <u-boot/schedule.h> + +static int do_mouse_dump(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + bool running; + int count; + int ret; + + ret = uclass_first_device_err(UCLASS_MOUSE, &dev); + if (ret) { + printf("Mouse not found (err=%d)\n", ret); + return CMD_RET_FAILURE; + } + for (running = true, count = 0; running;) { + struct mouse_event evt; + + ret = mouse_get_event(dev, &evt); + if (!ret) { + switch (evt.type) { + case MOUSE_EV_BUTTON: { + struct mouse_button *but = &evt.button; + + printf("button: button==%d, press=%d, clicks=%d, X=%d, Y=%d\n", + but->button, but->press_state, + but->clicks, but->x, but->y); + break; + } + case MOUSE_EV_MOTION: { + struct mouse_motion *motion = &evt.motion; + + printf("motion: Xrel=%d, Yrel=%d, X=%d, Y=%d, but=%d\n", + motion->xrel, motion->yrel, motion->x, + motion->y, motion->state); + break; + } + case MOUSE_EV_NULL: + break; + } + count++; + } else if (ret != -EAGAIN) { + return log_msg_ret("get_event", ret); + } + schedule(); + if (ctrlc()) + running = false; + } + printf("%d events received\n", count); + + return 0; +} + +static char mouse_help_text[] = + "dump - Dump input from a mouse"; + +U_BOOT_CMD_WITH_SUBCMDS(mouse, "Mouse input", mouse_help_text, + U_BOOT_SUBCMD_MKENT(dump, 1, 1, do_mouse_dump)); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Enable support for an mouse in sandbox, implemented by SDL. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/dts/sandbox.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index dd18bd26352..48e34a7aee5 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -203,6 +203,10 @@ }; }; + mouse { + compatible = "sandbox,mouse"; + }; + pci@0 { pci@1e,0 { compatible = "sandbox,pmc"; -- 2.43.0
participants (1)
-
Simon Glass