Compare commits

...

6 Commits

Author SHA1 Message Date
Simon Glass
679685c315 sandbox: Enable the mouse
Enable support for an mouse in sandbox, implemented by SDL.

Series-to: concept
Provide basic support for a mouse
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.
END

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-08-25 14:29:41 -06:00
Simon Glass
d3102c8d7b input: Add a command to show mouse input
This reads mouse input and shows it on the terminal. It is useful for
testing mice.

Change-Id: Ifebe6452f38904689e3e2142fc08a623131ed0de
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-08-25 14:24:32 -06:00
Simon Glass
051aebb340 usb: Add a USB mouse driver
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.

Change-Id: I74dbe55eb065be1782737c8b2b86558e53e0292a
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-08-25 14:24:18 -06:00
Simon Glass
c19cf8dd72 sandbox: sdl: Add support for mouse input
Allow mouse input to be reported from sandbox using SDL.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-08-25 14:24:17 -06:00
Simon Glass
796a83018f input: Add mouse support
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>
2025-08-25 14:09:43 -06:00
Simon Glass
5245848110 Add a few floating-point routines
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>
2025-08-25 13:54:43 -06:00
18 changed files with 861 additions and 5 deletions

View File

@@ -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;

View File

@@ -203,6 +203,10 @@
};
};
mouse {
compatible = "sandbox,mouse";
};
pci@0 {
pci@1e,0 {
compatible = "sandbox,pmc";

View File

@@ -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)

View File

@@ -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

View File

@@ -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

70
cmd/mouse.c Normal file
View File

@@ -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));

View File

@@ -100,3 +100,20 @@ 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.
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.

View File

@@ -15,3 +15,7 @@ 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
obj-$(CONFIG_USB_MOUSE) += usb_mouse.o

View File

@@ -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",
};

View File

@@ -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,
};

327
drivers/input/usb_mouse.c Normal file
View File

@@ -0,0 +1,327 @@
// 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 <usb.h>
enum {
RPT_BUTTON,
RPT_XREL,
RPT_YREL,
RPT_SCROLLY,
};
struct usb_mouse_priv {
unsigned long intpipe;
int intpktsize;
int intinterval;
unsigned long 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);

View File

@@ -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 */

78
include/mouse.h Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

200
lib/strtof.c Normal file
View File

@@ -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);
}