diff options
122 files changed, 7455 insertions, 422 deletions
diff --git a/arch/arm/cpu/armv7/omap-common/Makefile b/arch/arm/cpu/armv7/omap-common/Makefile index 464a5d1d73..87a7ac03f9 100644 --- a/arch/arm/cpu/armv7/omap-common/Makefile +++ b/arch/arm/cpu/armv7/omap-common/Makefile @@ -6,7 +6,13 @@ # obj-y := reset.o +ifeq ($(CONFIG_TIMER),) obj-y += timer.o +else +ifdef CONFIG_SPL_BUILD +obj-y += timer.o +endif +endif obj-y += utils.o ifneq ($(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),) diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index e4f8aaef55..7706b41b2d 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -93,7 +93,7 @@ dtb-$(CONFIG_TARGET_BEAGLE_X15) += am57xx-beagle-x15.dtb dtb-$(CONFIG_TARGET_STV0991) += stv0991.dtb dtb-$(CONFIG_LS102XA) += ls1021a-qds.dtb \ - ls1021a-twr.dtb + ls1021a-twr-duart.dtb ls1021a-twr-lpuart.dtb dtb-$(CONFIG_FSL_LSCH3) += fsl-ls2080a-qds.dtb \ fsl-ls2080a-rdb.dtb dtb-$(CONFIG_FSL_LSCH2) += fsl-ls1043a-qds.dtb \ diff --git a/arch/arm/dts/am335x-boneblack.dts b/arch/arm/dts/am335x-boneblack.dts index 679248aa02..27ebe4a65d 100644 --- a/arch/arm/dts/am335x-boneblack.dts +++ b/arch/arm/dts/am335x-boneblack.dts @@ -15,6 +15,7 @@ compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; chosen { stdout-path = &uart0; + tick-timer = &timer2; }; }; diff --git a/arch/arm/dts/am335x-evm.dts b/arch/arm/dts/am335x-evm.dts index e1c5d4f76c..c0bc2af9a5 100644 --- a/arch/arm/dts/am335x-evm.dts +++ b/arch/arm/dts/am335x-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; cpus { diff --git a/arch/arm/dts/am437x-gp-evm.dts b/arch/arm/dts/am437x-gp-evm.dts index b5f0b4ee69..8e23b96609 100644 --- a/arch/arm/dts/am437x-gp-evm.dts +++ b/arch/arm/dts/am437x-gp-evm.dts @@ -26,6 +26,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; vmmcsd_fixed: fixedregulator-sd { diff --git a/arch/arm/dts/am437x-sk-evm.dts b/arch/arm/dts/am437x-sk-evm.dts index 89feaf3eb7..260edb93ec 100644 --- a/arch/arm/dts/am437x-sk-evm.dts +++ b/arch/arm/dts/am437x-sk-evm.dts @@ -26,6 +26,7 @@ chosen { stdout-path = &uart0; + tick-timer = &timer2; }; backlight { diff --git a/arch/arm/dts/dra7-evm.dts b/arch/arm/dts/dra7-evm.dts index 797d411d6f..242fd53516 100644 --- a/arch/arm/dts/dra7-evm.dts +++ b/arch/arm/dts/dra7-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart1; + tick-timer = &timer2; }; memory { diff --git a/arch/arm/dts/dra72-evm.dts b/arch/arm/dts/dra72-evm.dts index a62550f0e0..fc2d167dee 100644 --- a/arch/arm/dts/dra72-evm.dts +++ b/arch/arm/dts/dra72-evm.dts @@ -16,6 +16,7 @@ chosen { stdout-path = &uart1; + tick-timer = &timer2; }; memory { diff --git a/arch/arm/dts/ls1021a-twr-duart.dts b/arch/arm/dts/ls1021a-twr-duart.dts new file mode 100644 index 0000000000..aaf72966f9 --- /dev/null +++ b/arch/arm/dts/ls1021a-twr-duart.dts @@ -0,0 +1,16 @@ +/* + * Freescale ls1021a TWR board device tree source + * + * Copyright 2013-2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; +#include "ls1021a-twr.dtsi" + +/ { + chosen { + stdout-path = &uart0; + }; +}; diff --git a/arch/arm/dts/ls1021a-twr-lpuart.dts b/arch/arm/dts/ls1021a-twr-lpuart.dts new file mode 100644 index 0000000000..2941ec0164 --- /dev/null +++ b/arch/arm/dts/ls1021a-twr-lpuart.dts @@ -0,0 +1,16 @@ +/* + * Freescale ls1021a TWR board device tree source + * + * Copyright 2013-2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/dts-v1/; +#include "ls1021a-twr.dtsi" + +/ { + chosen { + stdout-path = &lpuart0; + }; +}; diff --git a/arch/arm/dts/ls1021a-twr.dts b/arch/arm/dts/ls1021a-twr.dtsi index 6ccd33279b..d1be9ae5c2 100644 --- a/arch/arm/dts/ls1021a-twr.dts +++ b/arch/arm/dts/ls1021a-twr.dtsi @@ -1,12 +1,11 @@ /* - * Freescale ls1021a TWR board device tree source + * Freescale ls1021a TWR board common device tree source * * Copyright 2013-2015 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ */ -/dts-v1/; #include "ls1021a.dtsi" / { @@ -19,6 +18,10 @@ spi0 = &qspi; spi1 = &dspi1; }; + + chosen { + stdout-path = &uart0; + }; }; &qspi { diff --git a/arch/arm/dts/ls1021a.dtsi b/arch/arm/dts/ls1021a.dtsi index 7fadd7ca57..ee0e55461d 100644 --- a/arch/arm/dts/ls1021a.dtsi +++ b/arch/arm/dts/ls1021a.dtsi @@ -218,7 +218,6 @@ compatible = "fsl,16550-FIFO64", "ns16550a"; reg = <0x21c0500 0x100>; interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; - clock-frequency = <0>; fifo-size = <15>; status = "disabled"; }; @@ -227,7 +226,6 @@ compatible = "fsl,16550-FIFO64", "ns16550a"; reg = <0x21c0600 0x100>; interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; - clock-frequency = <0>; fifo-size = <15>; status = "disabled"; }; @@ -236,7 +234,6 @@ compatible = "fsl,16550-FIFO64", "ns16550a"; reg = <0x21d0500 0x100>; interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; - clock-frequency = <0>; fifo-size = <15>; status = "disabled"; }; @@ -245,7 +242,6 @@ compatible = "fsl,16550-FIFO64", "ns16550a"; reg = <0x21d0600 0x100>; interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; - clock-frequency = <0>; fifo-size = <15>; status = "disabled"; }; diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index d2addb4cf0..e3f02bf13c 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -117,6 +117,7 @@ }; lcd { + u-boot,dm-pre-reloc; compatible = "sandbox,lcd-sdl"; xres = <1366>; yres = <768>; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2e0d320b1e..9b8d658bf3 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -195,6 +195,13 @@ vss-microvolts = <0>; }; + lcd { + u-boot,dm-pre-reloc; + compatible = "sandbox,lcd-sdl"; + xres = <1366>; + yres = <768>; + }; + leds { compatible = "gpio-leds"; diff --git a/board/sandbox/sandbox.c b/board/sandbox/sandbox.c index 592f7728c0..b41e9decb3 100644 --- a/board/sandbox/sandbox.c +++ b/board/sandbox/sandbox.c @@ -47,23 +47,6 @@ int dram_init(void) return 0; } -#ifdef CONFIG_BOARD_EARLY_INIT_F -int board_early_init_f(void) -{ -#ifdef CONFIG_VIDEO_SANDBOX_SDL - int ret; - - ret = sandbox_lcd_sdl_early_init(); - if (ret) { - puts("Could not init sandbox LCD emulation\n"); - return ret; - } -#endif - - return 0; -} -#endif - #ifdef CONFIG_BOARD_LATE_INIT int board_late_init(void) { diff --git a/common/Makefile b/common/Makefile index 2a1d9f8331..249227597a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -205,7 +205,9 @@ obj-$(CONFIG_I2C_EDID) += edid.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-y += splash.o obj-$(CONFIG_SPLASH_SOURCE) += splash_source.o +ifndef CONFIG_DM_VIDEO obj-$(CONFIG_LCD) += lcd.o lcd_console.o +endif obj-$(CONFIG_LCD_ROTATION) += lcd_console_rotation.o obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o obj-$(CONFIG_LYNXKDI) += lynxkdi.o diff --git a/common/board_f.c b/common/board_f.c index 8094ac4efe..c470b5921a 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -46,6 +46,7 @@ #include <spi.h> #include <status_led.h> #include <trace.h> +#include <video.h> #include <watchdog.h> #include <asm/errno.h> #include <asm/io.h> @@ -437,36 +438,41 @@ static int reserve_mmu(void) } #endif -#ifdef CONFIG_LCD +#ifdef CONFIG_DM_VIDEO +static int reserve_video(void) +{ + ulong addr; + int ret; + + addr = gd->relocaddr; + ret = video_reserve(&addr); + if (ret) + return ret; + gd->relocaddr = addr; + + return 0; +} +#else + +# ifdef CONFIG_LCD static int reserve_lcd(void) { -#ifdef CONFIG_FB_ADDR +# ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; -#else +# else /* reserve memory for LCD display (always full pages) */ gd->relocaddr = lcd_setmem(gd->relocaddr); gd->fb_base = gd->relocaddr; -#endif /* CONFIG_FB_ADDR */ - return 0; -} -#endif /* CONFIG_LCD */ - -static int reserve_trace(void) -{ -#ifdef CONFIG_TRACE - gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE; - gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE); - debug("Reserving %dk for trace data at: %08lx\n", - CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr); -#endif +# endif /* CONFIG_FB_ADDR */ return 0; } +# endif /* CONFIG_LCD */ -#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ +# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \ !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K) -static int reserve_video(void) +static int reserve_legacy_video(void) { /* reserve memory for video display (always full pages) */ gd->relocaddr = video_setmem(gd->relocaddr); @@ -474,8 +480,21 @@ static int reserve_video(void) return 0; } +# endif +#endif /* !CONFIG_DM_VIDEO */ + +static int reserve_trace(void) +{ +#ifdef CONFIG_TRACE + gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE; + gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE); + debug("Reserving %dk for trace data at: %08lx\n", + CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr); #endif + return 0; +} + static int reserve_uboot(void) { /* @@ -957,16 +976,20 @@ static init_fnc_t init_sequence_f[] = { defined(CONFIG_ARM) reserve_mmu, #endif -#ifdef CONFIG_LCD +#ifdef CONFIG_DM_VIDEO + reserve_video, +#else +# ifdef CONFIG_LCD reserve_lcd, -#endif - reserve_trace, +# endif /* TODO: Why the dependency on CONFIG_8xx? */ -#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ +# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \ !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K) - reserve_video, -#endif + reserve_legacy_video, +# endif +#endif /* CONFIG_DM_VIDEO */ + reserve_trace, #if !defined(CONFIG_BLACKFIN) reserve_uboot, #endif diff --git a/common/cmd_bmp.c b/common/cmd_bmp.c index cb1f07119b..fd5b7db288 100644 --- a/common/cmd_bmp.c +++ b/common/cmd_bmp.c @@ -10,11 +10,14 @@ */ #include <common.h> +#include <dm.h> #include <lcd.h> +#include <mapmem.h> #include <bmp_layout.h> #include <command.h> #include <asm/byteorder.h> #include <malloc.h> +#include <mapmem.h> #include <splash.h> #include <video.h> @@ -57,7 +60,8 @@ struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, /* align to 32-bit-aligned-address + 2 */ bmp = (struct bmp_image *)((((unsigned int)dst + 1) & ~3) + 2); - if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, (uchar *)addr, &len) != 0) { + if (gunzip(bmp, CONFIG_SYS_VIDEO_LOGO_MAX_SIZE, map_sysmem(addr, 0), + &len) != 0) { free(dst); return NULL; } @@ -187,7 +191,7 @@ U_BOOT_CMD( */ static int bmp_info(ulong addr) { - struct bmp_image *bmp = (struct bmp_image *)addr; + struct bmp_image *bmp = (struct bmp_image *)map_sysmem(addr, 0); void *bmp_alloc_addr = NULL; unsigned long len; @@ -223,8 +227,11 @@ static int bmp_info(ulong addr) */ int bmp_display(ulong addr, int x, int y) { +#ifdef CONFIG_DM_VIDEO + struct udevice *dev; +#endif int ret; - struct bmp_image *bmp = (struct bmp_image *)addr; + struct bmp_image *bmp = map_sysmem(addr, 0); void *bmp_alloc_addr = NULL; unsigned long len; @@ -236,11 +243,27 @@ int bmp_display(ulong addr, int x, int y) printf("There is no valid bmp file at the given address\n"); return 1; } - -#if defined(CONFIG_LCD) - ret = lcd_display_bitmap((ulong)bmp, x, y); + addr = map_to_sysmem(bmp); + +#ifdef CONFIG_DM_VIDEO + ret = uclass_first_device(UCLASS_VIDEO, &dev); + if (!ret) { + if (!dev) + ret = -ENODEV; + if (!ret) { + bool align = false; + +# ifdef CONFIG_SPLASH_SCREEN_ALIGN + align = true; +# endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + ret = video_bmp_display(dev, addr, x, y, align); + } + } + return ret ? CMD_RET_FAILURE : 0; +#elif defined(CONFIG_LCD) + ret = lcd_display_bitmap(addr, x, y); #elif defined(CONFIG_VIDEO) - ret = video_display_bitmap((unsigned long)bmp, x, y); + ret = video_display_bitmap(addr, x, y); #else # error bmp_display() requires CONFIG_LCD or CONFIG_VIDEO #endif diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 552c875f62..b3bb64408f 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -1809,7 +1809,8 @@ static int do_i2c_bus_num(cmd_tbl_t *cmdtp, int flag, int argc, if (ret) printf("Failure changing bus number (%d)\n", ret); } - return ret; + + return ret ? CMD_RET_FAILURE : 0; } #endif /* defined(CONFIG_SYS_I2C) */ @@ -1852,7 +1853,8 @@ static int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char * const if (ret) printf("Failure changing bus speed (%d)\n", ret); } - return ret; + + return ret ? CMD_RET_FAILURE : 0; } /** diff --git a/common/fdt_support.c b/common/fdt_support.c index 09f923716c..75d0858e76 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -131,18 +131,6 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff) OF_STDOUT_PATH, strlen(OF_STDOUT_PATH) + 1); } #elif defined(CONFIG_OF_STDOUT_VIA_ALIAS) && defined(CONFIG_CONS_INDEX) -static void fdt_fill_multisername(char *sername, size_t maxlen) -{ - const char *outname = stdio_devices[stdout]->name; - - if (strcmp(outname, "serial") > 0) - strncpy(sername, outname, maxlen); - - /* eserial? */ - if (strcmp(outname + 1, "serial") > 0) - strncpy(sername, outname + 1, maxlen); -} - static int fdt_fixup_stdout(void *fdt, int chosenoff) { int err; @@ -152,9 +140,7 @@ static int fdt_fixup_stdout(void *fdt, int chosenoff) int len; char tmp[256]; /* long enough */ - fdt_fill_multisername(sername, sizeof(sername) - 1); - if (!sername[0]) - sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); + sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1); aliasoff = fdt_path_offset(fdt, "/aliases"); if (aliasoff < 0) { diff --git a/common/lcd.c b/common/lcd.c index d29308aeb6..2f3594a417 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -31,10 +31,6 @@ #endif #endif -#ifdef CONFIG_SANDBOX -#include <asm/sdl.h> -#endif - #ifndef CONFIG_LCD_ALIGNMENT #define CONFIG_LCD_ALIGNMENT PAGE_SIZE #endif @@ -72,13 +68,6 @@ void lcd_sync(void) if (lcd_flush_dcache) flush_dcache_range((u32)lcd_base, (u32)(lcd_base + lcd_get_size(&line_length))); -#elif defined(CONFIG_SANDBOX) && defined(CONFIG_VIDEO_SANDBOX_SDL) - static ulong last_sync; - - if (get_timer(last_sync) > 10) { - sandbox_sdl_sync(lcd_base); - last_sync = get_timer(0); - } #endif } diff --git a/common/stdio.c b/common/stdio.c index 8311ac768c..7252bab1f6 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -281,12 +281,23 @@ int stdio_add_devices(void) i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); #endif #endif -#ifdef CONFIG_LCD +#ifdef CONFIG_DM_VIDEO + struct udevice *vdev; + + for (ret = uclass_first_device(UCLASS_VIDEO, &vdev); + vdev; + ret = uclass_next_device(&vdev)) + ; + if (ret) + printf("%s: Video device failed (ret=%d)\n", __func__, ret); +#else +# if defined(CONFIG_LCD) drv_lcd_init (); -#endif -#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) +# endif +# if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) drv_video_init (); -#endif +# endif +#endif /* CONFIG_DM_VIDEO */ #if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD) drv_keyboard_init (); #endif diff --git a/common/usb_storage.c b/common/usb_storage.c index 4fdb55f9fa..8737cf7cea 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -65,7 +65,6 @@ static const unsigned char us_direction[256/8] = { static ccb usb_ccb __attribute__((aligned(ARCH_DMA_MINALIGN))); static __u32 CBWTag; -#define USB_MAX_STOR_DEV 7 static int usb_max_devs; /* number of highest available usb device */ static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; diff --git a/configs/am335x_boneblack_vboot_defconfig b/configs/am335x_boneblack_vboot_defconfig index 5dcb942f39..060aa1cd98 100644 --- a/configs/am335x_boneblack_vboot_defconfig +++ b/configs/am335x_boneblack_vboot_defconfig @@ -22,3 +22,5 @@ CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_SYS_NS16550=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/am335x_gp_evm_defconfig b/configs/am335x_gp_evm_defconfig index 74d9ffb677..49461e2cda 100644 --- a/configs/am335x_gp_evm_defconfig +++ b/configs/am335x_gp_evm_defconfig @@ -16,3 +16,5 @@ CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_SYS_NS16550=y CONFIG_RSA=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/am437x_gp_evm_defconfig b/configs/am437x_gp_evm_defconfig index 7155c98f8f..1d79ba19eb 100644 --- a/configs/am437x_gp_evm_defconfig +++ b/configs/am437x_gp_evm_defconfig @@ -18,3 +18,5 @@ CONFIG_SPI_FLASH=y CONFIG_SPI_FLASH_MACRONIX=y CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/am437x_sk_evm_defconfig b/configs/am437x_sk_evm_defconfig index 8f78eeb8b9..9eb41f9322 100644 --- a/configs/am437x_sk_evm_defconfig +++ b/configs/am437x_sk_evm_defconfig @@ -21,3 +21,5 @@ CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y CONFIG_SPI_FLASH_BAR=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/colibri_vf_defconfig b/configs/colibri_vf_defconfig index f8441e351b..45917c837f 100644 --- a/configs/colibri_vf_defconfig +++ b/configs/colibri_vf_defconfig @@ -8,3 +8,4 @@ CONFIG_CMD_GPIO=y CONFIG_DM=y CONFIG_NAND_VF610_NFC=y CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES=y +CONFIG_FSL_LPUART=y diff --git a/configs/colibri_vf_dtb_defconfig b/configs/colibri_vf_dtb_defconfig index 3596cecb7d..b1a843a787 100644 --- a/configs/colibri_vf_dtb_defconfig +++ b/configs/colibri_vf_dtb_defconfig @@ -11,3 +11,4 @@ CONFIG_OF_CONTROL=y CONFIG_DM=y CONFIG_NAND_VF610_NFC=y CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES=y +CONFIG_FSL_LPUART=y diff --git a/configs/dra72_evm_defconfig b/configs/dra72_evm_defconfig index b57ecca696..32d1dc1d2b 100644 --- a/configs/dra72_evm_defconfig +++ b/configs/dra72_evm_defconfig @@ -22,3 +22,5 @@ CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/dra74_evm_defconfig b/configs/dra74_evm_defconfig index 6e5a7051a4..9946261909 100644 --- a/configs/dra74_evm_defconfig +++ b/configs/dra74_evm_defconfig @@ -21,3 +21,5 @@ CONFIG_SYS_NS16550=y CONFIG_TI_QSPI=y CONFIG_DM_SPI=y CONFIG_DM_SPI_FLASH=y +CONFIG_TIMER=y +CONFIG_OMAP_TIMER=y diff --git a/configs/ls1021aqds_ddr4_nor_lpuart_defconfig b/configs/ls1021aqds_ddr4_nor_lpuart_defconfig index 68bd117b1c..44b2a0d1e5 100644 --- a/configs/ls1021aqds_ddr4_nor_lpuart_defconfig +++ b/configs/ls1021aqds_ddr4_nor_lpuart_defconfig @@ -4,3 +4,4 @@ CONFIG_SYS_EXTRA_OPTIONS="SYS_FSL_DDR4,LPUART" # CONFIG_CMD_SETEXPR is not set CONFIG_NETDEVICES=y CONFIG_E1000=y +CONFIG_FSL_LPUART=y diff --git a/configs/ls1021aqds_nor_lpuart_defconfig b/configs/ls1021aqds_nor_lpuart_defconfig index b2f6832303..1186af2a14 100644 --- a/configs/ls1021aqds_nor_lpuart_defconfig +++ b/configs/ls1021aqds_nor_lpuart_defconfig @@ -4,3 +4,4 @@ CONFIG_SYS_EXTRA_OPTIONS="LPUART" # CONFIG_CMD_SETEXPR is not set CONFIG_NETDEVICES=y CONFIG_E1000=y +CONFIG_FSL_LPUART=y diff --git a/configs/ls1021atwr_nor_defconfig b/configs/ls1021atwr_nor_defconfig index aa874fdcfd..b7b3a8df09 100644 --- a/configs/ls1021atwr_nor_defconfig +++ b/configs/ls1021atwr_nor_defconfig @@ -1,6 +1,10 @@ CONFIG_ARM=y CONFIG_TARGET_LS1021ATWR=y +CONFIG_DM_SERIAL=y +CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart" # CONFIG_CMD_SETEXPR is not set +CONFIG_OF_CONTROL=y +CONFIG_DM=y CONFIG_NETDEVICES=y CONFIG_E1000=y CONFIG_SYS_NS16550=y diff --git a/configs/ls1021atwr_nor_lpuart_defconfig b/configs/ls1021atwr_nor_lpuart_defconfig index d7afca9bc5..599342f1e4 100644 --- a/configs/ls1021atwr_nor_lpuart_defconfig +++ b/configs/ls1021atwr_nor_lpuart_defconfig @@ -1,6 +1,11 @@ CONFIG_ARM=y CONFIG_TARGET_LS1021ATWR=y +CONFIG_DM_SERIAL=y +CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-lpuart" CONFIG_SYS_EXTRA_OPTIONS="LPUART" # CONFIG_CMD_SETEXPR is not set +CONFIG_OF_CONTROL=y +CONFIG_DM=y CONFIG_NETDEVICES=y CONFIG_E1000=y +CONFIG_FSL_LPUART=y diff --git a/configs/ls1021atwr_qspi_defconfig b/configs/ls1021atwr_qspi_defconfig index 0c71df64aa..d7c7e4cc9a 100644 --- a/configs/ls1021atwr_qspi_defconfig +++ b/configs/ls1021atwr_qspi_defconfig @@ -1,7 +1,7 @@ CONFIG_ARM=y CONFIG_TARGET_LS1021ATWR=y CONFIG_DM_SPI=y -CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr" +CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart" CONFIG_SYS_EXTRA_OPTIONS="QSPI_BOOT" # CONFIG_CMD_IMLS is not set # CONFIG_CMD_SETEXPR is not set diff --git a/configs/ls1021atwr_sdcard_qspi_defconfig b/configs/ls1021atwr_sdcard_qspi_defconfig index 2b4ebd9510..453a3bb7a2 100644 --- a/configs/ls1021atwr_sdcard_qspi_defconfig +++ b/configs/ls1021atwr_sdcard_qspi_defconfig @@ -1,7 +1,7 @@ CONFIG_ARM=y CONFIG_TARGET_LS1021ATWR=y CONFIG_DM_SPI=y -CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr" +CONFIG_DEFAULT_DEVICE_TREE="ls1021a-twr-duart" CONFIG_SPL=y CONFIG_SYS_EXTRA_OPTIONS="RAMBOOT_PBL,SPL_FSL_PBL,SD_BOOT,SD_BOOT_QSPI" CONFIG_OF_CONTROL=y diff --git a/configs/pcm052_defconfig b/configs/pcm052_defconfig index 9125645832..26ab733148 100644 --- a/configs/pcm052_defconfig +++ b/configs/pcm052_defconfig @@ -3,3 +3,4 @@ CONFIG_TARGET_PCM052=y CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/phytec/pcm052/imximage.cfg,ENV_IS_IN_NAND" CONFIG_NAND_VF610_NFC=y CONFIG_SYS_NAND_BUSWIDTH_16BIT=y +CONFIG_FSL_LPUART=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index caa7336280..09ced0184a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -1,6 +1,7 @@ CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_PCI=y CONFIG_DEFAULT_DEVICE_TREE="sandbox" +CONFIG_DM_PCI_COMPAT=y CONFIG_FIT=y CONFIG_FIT_VERBOSE=y CONFIG_FIT_SIGNATURE=y @@ -51,7 +52,6 @@ CONFIG_SPI_FLASH_SST=y CONFIG_SPI_FLASH_WINBOND=y CONFIG_DM_ETH=y CONFIG_DM_PCI=y -CONFIG_DM_PCI_COMPAT=y CONFIG_PCI_SANDBOX=y CONFIG_PINCTRL=y CONFIG_PINCONF=y @@ -76,6 +76,9 @@ CONFIG_USB_EMUL=y CONFIG_USB_STORAGE=y CONFIG_USB_KEYBOARD=y CONFIG_SYS_USB_EVENT_POLL=y +CONFIG_DM_VIDEO=y +CONFIG_VIDEO_ROTATION=y +CONFIG_VIDEO_SANDBOX_SDL=y CONFIG_CMD_DHRYSTONE=y CONFIG_TPM=y CONFIG_LZ4=y diff --git a/configs/vf610twr_defconfig b/configs/vf610twr_defconfig index dc8df5c997..d51c93b477 100644 --- a/configs/vf610twr_defconfig +++ b/configs/vf610twr_defconfig @@ -6,3 +6,4 @@ CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_I CONFIG_NAND_VF610_NFC=y CONFIG_SYS_NAND_BUSWIDTH_16BIT=y CONFIG_SPI_FLASH=y +CONFIG_FSL_LPUART=y diff --git a/configs/vf610twr_nand_defconfig b/configs/vf610twr_nand_defconfig index 98880f3a8b..299fa8f916 100644 --- a/configs/vf610twr_nand_defconfig +++ b/configs/vf610twr_nand_defconfig @@ -6,3 +6,4 @@ CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_I CONFIG_NAND_VF610_NFC=y CONFIG_SYS_NAND_BUSWIDTH_16BIT=y CONFIG_SPI_FLASH=y +CONFIG_FSL_LPUART=y diff --git a/doc/device-tree-bindings/chosen.txt b/doc/device-tree-bindings/chosen.txt new file mode 100644 index 0000000000..bf9a30a8f9 --- /dev/null +++ b/doc/device-tree-bindings/chosen.txt @@ -0,0 +1,43 @@ +The chosen node +--------------- +The chosen node does not represent a real device, but serves as a place +for passing data like which serial device to used to print the logs etc + + +stdout-path property +-------------------- +Device trees may specify the device to be used for boot console output +with a stdout-path property under /chosen. + +Example +------- +/ { + chosen { + stdout-path = "/serial@f00:115200"; + }; + + serial@f00 { + compatible = "vendor,some-uart"; + reg = <0xf00 0x10>; + }; +}; + +tick-timer property +------------------- +In a system there are multiple timers, specify which timer to be used +as the tick-timer. Earlier it was hardcoded in the timer driver now +since device tree has all the timer nodes. Specify which timer to be +used as tick timer. + +Example +------- +/ { + chosen { + tick-timer = "/timer2@f00"; + }; + + timer2@f00 { + compatible = "vendor,some-timer"; + reg = <0xf00 0x10>; + }; +}; diff --git a/doc/driver-model/serial-howto.txt b/doc/driver-model/serial-howto.txt index 4706d56ea7..c933b9081b 100644 --- a/doc/driver-model/serial-howto.txt +++ b/doc/driver-model/serial-howto.txt @@ -11,7 +11,6 @@ is time for maintainers to start converting over the remaining serial drivers: opencores_yanu.c serial_bfin.c serial_imx.c - serial_lpuart.c serial_max3100.c serial_pxa.c serial_s3c24x0.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 890f22f48e..9fcde39b71 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -1,3 +1,5 @@ +menu "Clock" + config CLK bool "Enable clock driver support" depends on DM @@ -17,3 +19,5 @@ config SPL_CLK SPL, enable this option. It might provide a cleaner interface to setting up clocks within SPL, and allows the same drivers to be used as U-Boot proper. + +endmenu diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 73dfd7d016..19f6f07c6f 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -32,6 +32,16 @@ ulong clk_set_rate(struct udevice *dev, ulong rate) return ops->set_rate(dev, rate); } +int clk_enable(struct udevice *dev, int periph) +{ + struct clk_ops *ops = clk_get_ops(dev); + + if (!ops->enable) + return -ENOSYS; + + return ops->enable(dev, periph); +} + ulong clk_get_periph_rate(struct udevice *dev, int periph) { struct clk_ops *ops = clk_get_ops(dev); diff --git a/drivers/clk/clk_rk3036.c b/drivers/clk/clk_rk3036.c index 6c802b6283..f650810250 100644 --- a/drivers/clk/clk_rk3036.c +++ b/drivers/clk/clk_rk3036.c @@ -314,7 +314,7 @@ static ulong rk3036_clk_set_rate(struct udevice *dev, ulong rate) return 0; } -ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3036_clk_priv *priv = dev_get_priv(dev); ulong new_rate; diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c index 54d49305b0..0172ad13bb 100644 --- a/drivers/clk/clk_rk3288.c +++ b/drivers/clk/clk_rk3288.c @@ -508,7 +508,7 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate, return rockchip_spi_get_clk(cru, clk_general_rate, periph); } -ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) { struct rk3288_clk_priv *priv = dev_get_priv(dev); ulong new_rate; diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c index 058225a766..367130f8b7 100644 --- a/drivers/clk/clk_sandbox.c +++ b/drivers/clk/clk_sandbox.c @@ -32,7 +32,7 @@ static ulong sandbox_clk_set_rate(struct udevice *dev, ulong rate) return 0; } -ulong sandbox_get_periph_rate(struct udevice *dev, int periph) +static ulong sandbox_get_periph_rate(struct udevice *dev, int periph) { struct sandbox_clk_priv *priv = dev_get_priv(dev); @@ -41,7 +41,8 @@ ulong sandbox_get_periph_rate(struct udevice *dev, int periph) return priv->periph_rate[periph]; } -ulong sandbox_set_periph_rate(struct udevice *dev, int periph, ulong rate) +static ulong sandbox_set_periph_rate(struct udevice *dev, int periph, + ulong rate) { struct sandbox_clk_priv *priv = dev_get_priv(dev); ulong old_rate; diff --git a/drivers/core/device.c b/drivers/core/device.c index 818d03fac1..1e5584a7ce 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -135,6 +135,11 @@ int device_bind(struct udevice *parent, const struct driver *drv, if (ret) goto fail_child_post_bind; } + if (uc->uc_drv->post_bind) { + ret = uc->uc_drv->post_bind(dev); + if (ret) + goto fail_uclass_post_bind; + } if (parent) dm_dbg("Bound device %s to %s\n", dev->name, parent->name); @@ -145,6 +150,8 @@ int device_bind(struct udevice *parent, const struct driver *drv, return 0; +fail_uclass_post_bind: + /* There is no child unbind() method, so no clean-up required */ fail_child_post_bind: if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) { if (drv->unbind && drv->unbind(dev)) { diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index 1af09472a2..e1acefe727 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -278,6 +278,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node, return -ENODEV; } +#if CONFIG_IS_ENABLED(OF_CONTROL) static int uclass_find_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, @@ -308,6 +309,7 @@ static int uclass_find_device_by_phandle(enum uclass_id id, return -ENODEV; } +#endif int uclass_get_device_tail(struct udevice *dev, int ret, struct udevice **devp) @@ -374,6 +376,7 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node, return uclass_get_device_tail(dev, ret, devp); } +#if CONFIG_IS_ENABLED(OF_CONTROL) int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp) { @@ -384,6 +387,7 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, ret = uclass_find_device_by_phandle(id, parent, name, &dev); return uclass_get_device_tail(dev, ret, devp); } +#endif int uclass_first_device(enum uclass_id id, struct udevice **devp) { @@ -426,11 +430,6 @@ int uclass_bind_device(struct udevice *dev) goto err; } } - if (uc->uc_drv->post_bind) { - ret = uc->uc_drv->post_bind(dev); - if (ret) - goto err; - } return 0; err: diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index 4cce11fe21..3ed4d8914c 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -154,6 +154,7 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label) static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) { +#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF) va_list args; char buf[40]; @@ -161,6 +162,9 @@ static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...) vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return dm_gpio_request(desc, buf); +#else + return dm_gpio_request(desc, fmt); +#endif } /** @@ -199,6 +203,7 @@ int gpio_request(unsigned gpio, const char *label) */ int gpio_requestf(unsigned gpio, const char *fmt, ...) { +#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF) va_list args; char buf[40]; @@ -206,6 +211,9 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...) vscnprintf(buf, sizeof(buf), fmt, args); va_end(args); return gpio_request(gpio, buf); +#else + return gpio_request(gpio, fmt); +#endif } int _dm_gpio_free(struct udevice *dev, uint offset) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e6028d503f..ede5d6eeec 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1780,18 +1780,28 @@ static int mmc_probe(bd_t *bis) #elif defined(CONFIG_DM_MMC) static int mmc_probe(bd_t *bis) { - int ret; + int ret, i; struct uclass *uc; - struct udevice *m; + struct udevice *dev; ret = uclass_get(UCLASS_MMC, &uc); if (ret) return ret; - uclass_foreach_dev(m, uc) { - ret = device_probe(m); + /* + * Try to add them in sequence order. Really with driver model we + * should allow holes, but the current MMC list does not allow that. + * So if we request 0, 1, 3 we will get 0, 1, 2. + */ + for (i = 0; ; i++) { + ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev); + if (ret == -ENODEV) + break; + } + uclass_foreach_dev(dev, uc) { + ret = device_probe(dev); if (ret) - printf("%s - probe failed: %d\n", m->name, ret); + printf("%s - probe failed: %d\n", dev->name, ret); } return 0; diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index b5fdcd12a8..c42b312ddd 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -111,12 +111,16 @@ static int pinconfig_post_bind(struct udevice *dev) { const void *fdt = gd->fdt_blob; int offset = dev->of_offset; + bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); const char *name; int ret; for (offset = fdt_first_subnode(fdt, offset); offset > 0; offset = fdt_next_subnode(fdt, offset)) { + if (pre_reloc_only && + !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL)) + continue; /* * If this node has "compatible" property, this is not * a pin configuration node, but a normal device. skip. diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 92084a2908..83068cfd50 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -228,6 +228,12 @@ config ALTERA_UART Select this to enable an UART for Altera devices. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config FSL_LPUART + bool "Freescale LPUART support" + help + Select this to enable a Low Power UART for Freescale VF610 and + QorIQ Layerscape devices. + config SYS_NS16550 bool "NS16550 UART or compatible" help diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index cd2f91e28e..45dff98d9d 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -16,6 +16,7 @@ #include <lcd.h> #include <os.h> #include <serial.h> +#include <video.h> #include <linux/compiler.h> #include <asm/state.h> @@ -114,9 +115,7 @@ static int sandbox_serial_pending(struct udevice *dev, bool input) return 0; os_usleep(100); -#ifdef CONFIG_LCD - lcd_sync(); -#endif + video_sync_all(); if (next_index == serial_buf_read) return 1; /* buffer full */ diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c index 63fc388b26..3f9c4d14ea 100644 --- a/drivers/serial/serial_lpuart.c +++ b/drivers/serial/serial_lpuart.c @@ -5,6 +5,7 @@ */ #include <common.h> +#include <dm.h> #include <watchdog.h> #include <asm/io.h> #include <serial.h> @@ -12,15 +13,15 @@ #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> -#define US1_TDRE (1 << 7) -#define US1_RDRF (1 << 5) -#define US1_OR (1 << 3) -#define UC2_TE (1 << 3) -#define UC2_RE (1 << 2) -#define CFIFO_TXFLUSH (1 << 7) -#define CFIFO_RXFLUSH (1 << 6) -#define SFIFO_RXOF (1 << 2) -#define SFIFO_RXUF (1 << 0) +#define US1_TDRE (1 << 7) +#define US1_RDRF (1 << 5) +#define US1_OR (1 << 3) +#define UC2_TE (1 << 3) +#define UC2_RE (1 << 2) +#define CFIFO_TXFLUSH (1 << 7) +#define CFIFO_RXFLUSH (1 << 6) +#define SFIFO_RXOF (1 << 2) +#define SFIFO_RXUF (1 << 0) #define STAT_LBKDIF (1 << 31) #define STAT_RXEDGIF (1 << 30) @@ -34,7 +35,7 @@ #define STAT_MA1F (1 << 15) #define STAT_MA2F (1 << 14) #define STAT_FLAGS (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \ - STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F) + STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F) #define CTRL_TE (1 << 19) #define CTRL_RE (1 << 18) @@ -49,23 +50,24 @@ DECLARE_GLOBAL_DATA_PTR; struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE; +struct lpuart_serial_platdata { + struct lpuart_fsl *reg; +}; + #ifndef CONFIG_LPUART_32B_REG -static void lpuart_serial_setbrg(void) +static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate) { u32 clk = mxc_get_clock(MXC_UART_CLK); u16 sbr; - if (!gd->baudrate) - gd->baudrate = CONFIG_BAUDRATE; + sbr = (u16)(clk / (16 * baudrate)); - sbr = (u16)(clk / (16 * gd->baudrate)); /* place adjustment later - n/32 BRFA */ - __raw_writeb(sbr >> 8, &base->ubdh); __raw_writeb(sbr & 0xff, &base->ubdl); } -static int lpuart_serial_getc(void) +static int _lpuart_serial_getc(struct lpuart_fsl *base) { while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR))) WATCHDOG_RESET(); @@ -75,10 +77,10 @@ static int lpuart_serial_getc(void) return __raw_readb(&base->ud); } -static void lpuart_serial_putc(const char c) +static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c) { if (c == '\n') - serial_putc('\r'); + _lpuart_serial_putc(base, '\r'); while (!(__raw_readb(&base->us1) & US1_TDRE)) WATCHDOG_RESET(); @@ -86,10 +88,8 @@ static void lpuart_serial_putc(const char c) __raw_writeb(c, &base->ud); } -/* - * Test whether a character is in the RX buffer - */ -static int lpuart_serial_tstc(void) +/* Test whether a character is in the RX buffer */ +static int _lpuart_serial_tstc(struct lpuart_fsl *base) { if (__raw_readb(&base->urcfifo) == 0) return 0; @@ -101,7 +101,7 @@ static int lpuart_serial_tstc(void) * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. */ -static int lpuart_serial_init(void) +static int _lpuart_serial_init(struct lpuart_fsl *base) { u8 ctrl; @@ -120,14 +120,39 @@ static int lpuart_serial_init(void) __raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo); /* provide data bits, parity, stop bit, etc */ - - serial_setbrg(); + _lpuart_serial_setbrg(base, gd->baudrate); __raw_writeb(UC2_RE | UC2_TE, &base->uc2); return 0; } +#ifndef CONFIG_DM_SERIAL +static void lpuart_serial_setbrg(void) +{ + _lpuart_serial_setbrg(base, gd->baudrate); +} + +static int lpuart_serial_getc(void) +{ + return _lpuart_serial_getc(base); +} + +static void lpuart_serial_putc(const char c) +{ + _lpuart_serial_putc(base, c); +} + +static int lpuart_serial_tstc(void) +{ + return _lpuart_serial_tstc(base); +} + +static int lpuart_serial_init(void) +{ + return _lpuart_serial_init(base); +} + static struct serial_device lpuart_serial_drv = { .name = "lpuart_serial", .start = lpuart_serial_init, @@ -138,22 +163,67 @@ static struct serial_device lpuart_serial_drv = { .getc = lpuart_serial_getc, .tstc = lpuart_serial_tstc, }; +#else /* CONFIG_DM_SERIAL */ +static int lpuart_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + _lpuart_serial_setbrg(reg, baudrate); + + return 0; +} + +static int lpuart_serial_getc(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + return _lpuart_serial_getc(reg); +} + +static int lpuart_serial_putc(struct udevice *dev, const char c) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + _lpuart_serial_putc(reg, c); + + return 0; +} + +static int lpuart_serial_pending(struct udevice *dev, bool input) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + if (input) + return _lpuart_serial_tstc(reg); + else + return __raw_readb(®->us1) & US1_TDRE ? 0 : 1; +} + +static int lpuart_serial_probe(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + return _lpuart_serial_init(reg); +} +#endif /* CONFIG_DM_SERIAL */ #else -static void lpuart32_serial_setbrg(void) +static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate) { u32 clk = CONFIG_SYS_CLK_FREQ; u32 sbr; - if (!gd->baudrate) - gd->baudrate = CONFIG_BAUDRATE; + sbr = (clk / (16 * baudrate)); - sbr = (clk / (16 * gd->baudrate)); /* place adjustment later - n/32 BRFA */ - out_be32(&base->baud, sbr); } -static int lpuart32_serial_getc(void) +static int _lpuart32_serial_getc(struct lpuart_fsl *base) { u32 stat; @@ -165,10 +235,10 @@ static int lpuart32_serial_getc(void) return in_be32(&base->data) & 0x3ff; } -static void lpuart32_serial_putc(const char c) +static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c) { if (c == '\n') - serial_putc('\r'); + _lpuart32_serial_putc(base, '\r'); while (!(in_be32(&base->stat) & STAT_TDRE)) WATCHDOG_RESET(); @@ -176,10 +246,8 @@ static void lpuart32_serial_putc(const char c) out_be32(&base->data, c); } -/* - * Test whether a character is in the RX buffer - */ -static int lpuart32_serial_tstc(void) +/* Test whether a character is in the RX buffer */ +static int _lpuart32_serial_tstc(struct lpuart_fsl *base) { if ((in_be32(&base->water) >> 24) == 0) return 0; @@ -191,7 +259,7 @@ static int lpuart32_serial_tstc(void) * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. */ -static int lpuart32_serial_init(void) +static int _lpuart32_serial_init(struct lpuart_fsl *base) { u8 ctrl; @@ -204,15 +272,41 @@ static int lpuart32_serial_init(void) out_be32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE)); out_be32(&base->match, 0); - /* provide data bits, parity, stop bit, etc */ - serial_setbrg(); + /* provide data bits, parity, stop bit, etc */ + _lpuart32_serial_setbrg(base, gd->baudrate); out_be32(&base->ctrl, CTRL_RE | CTRL_TE); return 0; } +#ifndef CONFIG_DM_SERIAL +static void lpuart32_serial_setbrg(void) +{ + _lpuart32_serial_setbrg(base, gd->baudrate); +} + +static int lpuart32_serial_getc(void) +{ + return _lpuart32_serial_getc(base); +} + +static void lpuart32_serial_putc(const char c) +{ + _lpuart32_serial_putc(base, c); +} + +static int lpuart32_serial_tstc(void) +{ + return _lpuart32_serial_tstc(base); +} + +static int lpuart32_serial_init(void) +{ + return _lpuart32_serial_init(base); +} + static struct serial_device lpuart32_serial_drv = { .name = "lpuart32_serial", .start = lpuart32_serial_init, @@ -223,8 +317,57 @@ static struct serial_device lpuart32_serial_drv = { .getc = lpuart32_serial_getc, .tstc = lpuart32_serial_tstc, }; +#else /* CONFIG_DM_SERIAL */ +static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + _lpuart32_serial_setbrg(reg, baudrate); + + return 0; +} + +static int lpuart32_serial_getc(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + return _lpuart32_serial_getc(reg); +} + +static int lpuart32_serial_putc(struct udevice *dev, const char c) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + _lpuart32_serial_putc(reg, c); + + return 0; +} + +static int lpuart32_serial_pending(struct udevice *dev, bool input) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + if (input) + return _lpuart32_serial_tstc(reg); + else + return in_be32(®->stat) & STAT_TDRE ? 0 : 1; +} + +static int lpuart32_serial_probe(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + struct lpuart_fsl *reg = plat->reg; + + return _lpuart32_serial_init(reg); +} +#endif /* CONFIG_DM_SERIAL */ #endif +#ifndef CONFIG_DM_SERIAL void lpuart_serial_initialize(void) { #ifdef CONFIG_LPUART_32B_REG @@ -242,3 +385,66 @@ __weak struct serial_device *default_serial_console(void) return &lpuart_serial_drv; #endif } +#else /* CONFIG_DM_SERIAL */ +static int lpuart_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct lpuart_serial_platdata *plat = dev->platdata; + fdt_addr_t addr; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->reg = (struct lpuart_fsl *)addr; + + return 0; +} + +#ifndef CONFIG_LPUART_32B_REG +static const struct dm_serial_ops lpuart_serial_ops = { + .putc = lpuart_serial_putc, + .pending = lpuart_serial_pending, + .getc = lpuart_serial_getc, + .setbrg = lpuart_serial_setbrg, +}; + +static const struct udevice_id lpuart_serial_ids[] = { + { .compatible = "fsl,vf610-lpuart" }, + { } +}; + +U_BOOT_DRIVER(serial_lpuart) = { + .name = "serial_lpuart", + .id = UCLASS_SERIAL, + .of_match = lpuart_serial_ids, + .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata), + .probe = lpuart_serial_probe, + .ops = &lpuart_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; +#else /* CONFIG_LPUART_32B_REG */ +static const struct dm_serial_ops lpuart32_serial_ops = { + .putc = lpuart32_serial_putc, + .pending = lpuart32_serial_pending, + .getc = lpuart32_serial_getc, + .setbrg = lpuart32_serial_setbrg, +}; + +static const struct udevice_id lpuart32_serial_ids[] = { + { .compatible = "fsl,ls1021a-lpuart" }, + { } +}; + +U_BOOT_DRIVER(serial_lpuart32) = { + .name = "serial_lpuart32", + .id = UCLASS_SERIAL, + .of_match = lpuart32_serial_ids, + .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata), + .probe = lpuart32_serial_probe, + .ops = &lpuart32_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; +#endif /* CONFIG_LPUART_32B_REG */ +#endif /* CONFIG_DM_SERIAL */ diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig index 2b10d2bc6c..ff65a731de 100644 --- a/drivers/timer/Kconfig +++ b/drivers/timer/Kconfig @@ -30,4 +30,10 @@ config X86_TSC_TIMER help Select this to enable Time-Stamp Counter (TSC) timer for x86. +config OMAP_TIMER + bool "Omap timer support" + depends on TIMER + help + Select this to enable an timer for Omap devices. + endmenu diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile index fe954eca9a..f351fbb4e0 100644 --- a/drivers/timer/Makefile +++ b/drivers/timer/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_TIMER) += timer-uclass.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o +obj-$(CONFIG_OMAP_TIMER) += omap-timer.o diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c new file mode 100644 index 0000000000..3bb38c522c --- /dev/null +++ b/drivers/timer/omap-timer.c @@ -0,0 +1,108 @@ +/* + * TI OMAP timer driver + * + * Copyright (C) 2015, Texas Instruments, Incorporated + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <timer.h> +#include <asm/io.h> +#include <asm/arch/clock.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Timer register bits */ +#define TCLR_START BIT(0) /* Start=1 */ +#define TCLR_AUTO_RELOAD BIT(1) /* Auto reload */ +#define TCLR_PRE_EN BIT(5) /* Pre-scaler enable */ +#define TCLR_PTV_SHIFT (2) /* Pre-scaler shift value */ + +#define TIMER_CLOCK (V_SCLK / (2 << CONFIG_SYS_PTV)) + +struct omap_gptimer_regs { + unsigned int tidr; /* offset 0x00 */ + unsigned char res1[12]; + unsigned int tiocp_cfg; /* offset 0x10 */ + unsigned char res2[12]; + unsigned int tier; /* offset 0x20 */ + unsigned int tistatr; /* offset 0x24 */ + unsigned int tistat; /* offset 0x28 */ + unsigned int tisr; /* offset 0x2c */ + unsigned int tcicr; /* offset 0x30 */ + unsigned int twer; /* offset 0x34 */ + unsigned int tclr; /* offset 0x38 */ + unsigned int tcrr; /* offset 0x3c */ + unsigned int tldr; /* offset 0x40 */ + unsigned int ttgr; /* offset 0x44 */ + unsigned int twpc; /* offset 0x48 */ + unsigned int tmar; /* offset 0x4c */ + unsigned int tcar1; /* offset 0x50 */ + unsigned int tscir; /* offset 0x54 */ + unsigned int tcar2; /* offset 0x58 */ +}; + +/* Omap Timer Priv */ +struct omap_timer_priv { + struct omap_gptimer_regs *regs; +}; + +static int omap_timer_get_count(struct udevice *dev, u64 *count) +{ + struct omap_timer_priv *priv = dev_get_priv(dev); + + *count = readl(&priv->regs->tcrr); + + return 0; +} + +static int omap_timer_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct omap_timer_priv *priv = dev_get_priv(dev); + + uc_priv->clock_rate = TIMER_CLOCK; + + /* start the counter ticking up, reload value on overflow */ + writel(0, &priv->regs->tldr); + /* enable timer */ + writel((CONFIG_SYS_PTV << 2) | TCLR_PRE_EN | TCLR_AUTO_RELOAD | + TCLR_START, &priv->regs->tclr); + + return 0; +} + +static int omap_timer_ofdata_to_platdata(struct udevice *dev) +{ + struct omap_timer_priv *priv = dev_get_priv(dev); + + priv->regs = (struct omap_gptimer_regs *)dev_get_addr(dev); + + return 0; +} + + +static const struct timer_ops omap_timer_ops = { + .get_count = omap_timer_get_count, +}; + +static const struct udevice_id omap_timer_ids[] = { + { .compatible = "ti,am335x-timer" }, + { .compatible = "ti,am4372-timer" }, + { .compatible = "ti,omap5430-timer" }, + {} +}; + +U_BOOT_DRIVER(omap_timer) = { + .name = "omap_timer", + .id = UCLASS_TIMER, + .of_match = omap_timer_ids, + .ofdata_to_platdata = omap_timer_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct omap_timer_priv), + .probe = omap_timer_probe, + .ops = &omap_timer_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c index 00a9944f78..a8da936349 100644 --- a/drivers/timer/sandbox_timer.c +++ b/drivers/timer/sandbox_timer.c @@ -27,6 +27,11 @@ static int sandbox_timer_get_count(struct udevice *dev, u64 *count) static int sandbox_timer_probe(struct udevice *dev) { + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->clock_rate) + uc_priv->clock_rate = 1000000; + return 0; } @@ -47,3 +52,8 @@ U_BOOT_DRIVER(sandbox_timer) = { .ops = &sandbox_timer_ops, .flags = DM_FLAG_PRE_RELOC, }; + +/* This is here in case we don't have a device tree */ +U_BOOT_DEVICE(sandbox_timer_non_fdt) = { + .name = "sandbox_timer", +}; diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c index aca421bdea..83d1a35e06 100644 --- a/drivers/timer/timer-uclass.c +++ b/drivers/timer/timer-uclass.c @@ -6,6 +6,8 @@ #include <common.h> #include <dm.h> +#include <dm/lists.h> +#include <dm/device-internal.h> #include <errno.h> #include <timer.h> @@ -47,6 +49,16 @@ static int timer_pre_probe(struct udevice *dev) return 0; } +static int timer_post_probe(struct udevice *dev) +{ + struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->clock_rate) + return -EINVAL; + + return 0; +} + u64 timer_conv_64(u32 count) { /* increment tbh if tbl has rolled over */ @@ -56,9 +68,53 @@ u64 timer_conv_64(u32 count) return ((u64)gd->timebase_h << 32) | gd->timebase_l; } +int notrace dm_timer_init(void) +{ + const void *blob = gd->fdt_blob; + struct udevice *dev = NULL; + int node; + int ret; + + if (gd->timer) + return 0; + + /* Check for a chosen timer to be used for tick */ + node = fdtdec_get_chosen_node(blob, "tick-timer"); + if (node < 0) { + /* No chosen timer, trying first available timer */ + ret = uclass_first_device(UCLASS_TIMER, &dev); + if (ret) + return ret; + if (!dev) + return -ENODEV; + } else { + if (uclass_get_device_by_of_offset(UCLASS_TIMER, node, &dev)) { + /* + * If the timer is not marked to be bound before + * relocation, bind it anyway. + */ + if (node > 0 && + !lists_bind_fdt(gd->dm_root, blob, node, &dev)) { + ret = device_probe(dev); + if (ret) + return ret; + } + } + } + + if (dev) { + gd->timer = dev; + return 0; + } + + return -ENODEV; +} + UCLASS_DRIVER(timer) = { .id = UCLASS_TIMER, .name = "timer", .pre_probe = timer_pre_probe, + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_probe = timer_post_probe, .per_device_auto_alloc_size = sizeof(struct timer_dev_priv), }; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index caf1efcbb3..ae122daa04 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -4,6 +4,59 @@ menu "Graphics support" +config DM_VIDEO + bool "Enable driver model support for LCD/video" + depends on DM + help + This enables driver model for LCD and video devices. These support + a bitmap display of various sizes and depths which can be drawn on + to display a command-line console or splash screen. Enabling this + option compiles in the video uclass and routes all LCD/video access + through this. + +config VIDEO_BPP8 + bool "Support 8-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 8-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config VIDEO_BPP16 + bool "Support 16-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 16-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config VIDEO_BPP32 + bool "Support 32-bit-per-pixel displays" + depends on DM_VIDEO + default y if DM_VIDEO + help + Support drawing text and bitmaps onto a 32-bit-per-pixel display. + Enabling this will include code to support this display. Without + this option, such displays will not be supported and console output + will be empty. + +config VIDEO_ROTATION + bool "Support rotated displays" + depends on DM_VIDEO + help + Sometimes, for example if the display is mounted in portrait + mode or even if it's mounted landscape but rotated by 180degree, + we need to rotate our content of the display relative to the + framebuffer, so that user can read the messages which are + printed out. Enable this option to include a text driver which can + support this. The rotation is set by the 'rot' parameter in + struct video_priv: 0=unrotated, 1=90 degrees clockwise, 2=180 + degrees, 3=270 degrees. + config VIDEO_VESA bool "Enable VESA video driver support" default n @@ -247,6 +300,15 @@ config DISPLAY_PORT to drive LCD panels. This framework provides support for enabling these displays where supported by the video hardware. +config VIDEO_SANDBOX_SDL + bool "Enable sandbox video console using SDL" + depends on SANDBOX + help + When using sandbox you can enable an emulated LCD display which + appears as an SDL (Simple DirectMedia Layer) window. This is a + console device and can display stdout output. Within U-Boot is is + a normal bitmap display and can display images as well as text. + config VIDEO_TEGRA124 bool "Enable video support on Tegra124" help diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e85fd8a677..ee046296e6 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -7,6 +7,9 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o +obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o +obj-$(CONFIG_DM_VIDEO) += video_bmp.o +obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o endif obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c new file mode 100644 index 0000000000..d1031c8ed1 --- /dev/null +++ b/drivers/video/console_normal.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015 Google, Inc + * (C) Copyright 2001-2015 + * DENX Software Engineering -- wd@denx.de + * Compulab Ltd - http://compulab.co.il/ + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <video.h> +#include <video_console.h> +#include <video_font.h> /* Get font data, width and height */ + +static int console_normal_set_row(struct udevice *dev, uint row, int clr) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *line; + int pixels = VIDEO_FONT_HEIGHT * vid_priv->line_length; + int i; + + line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * vid_priv->line_length; + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif + default: + return -ENOSYS; + } + + return 0; +} + +static int console_normal_move_rows(struct udevice *dev, uint rowdst, + uint rowsrc, uint count) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *dst; + void *src; + + dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * vid_priv->line_length; + src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * vid_priv->line_length; + memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count); + + return 0; +} + +static int console_normal_putc_xy(struct udevice *dev, uint x, uint y, char ch) +{ + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + int i, row; + void *line = vid_priv->fb + y * vid_priv->line_length + + x * VNBYTES(vid_priv->bpix); + + for (row = 0; row < VIDEO_FONT_HEIGHT; row++) { + uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row]; + + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst++ = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst++ = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst++ = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif + default: + return -ENOSYS; + } + line += vid_priv->line_length; + } + + return 0; +} + +struct vidconsole_ops console_normal_ops = { + .putc_xy = console_normal_putc_xy, + .move_rows = console_normal_move_rows, + .set_row = console_normal_set_row, +}; + +U_BOOT_DRIVER(vidconsole_normal) = { + .name = "vidconsole0", + .id = UCLASS_VIDEO_CONSOLE, + .ops = &console_normal_ops, +}; diff --git a/drivers/video/console_rotate.c b/drivers/video/console_rotate.c new file mode 100644 index 0000000000..ebb31d8cd0 --- /dev/null +++ b/drivers/video/console_rotate.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2015 Google, Inc + * (C) Copyright 2015 + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <video.h> +#include <video_console.h> +#include <video_font.h> /* Get font data, width and height */ + +static int console_set_row_1(struct udevice *dev, uint row, int clr) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + int pbytes = VNBYTES(vid_priv->bpix); + void *line; + int i, j; + + line = vid_priv->fb + vid_priv->line_length - + (row + 1) * VIDEO_FONT_HEIGHT * pbytes; + for (j = 0; j < vid_priv->ysize; j++) { + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif + default: + return -ENOSYS; + } + line += vid_priv->line_length; + } + + return 0; +} + +static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc, + uint count) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *dst; + void *src; + int pbytes = VNBYTES(vid_priv->bpix); + int j; + + dst = vid_priv->fb + vid_priv->line_length - + (rowdst + count) * VIDEO_FONT_HEIGHT * pbytes; + src = vid_priv->fb + vid_priv->line_length - + (rowsrc + count) * VIDEO_FONT_HEIGHT * pbytes; + + for (j = 0; j < vid_priv->ysize; j++) { + memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count); + src += vid_priv->line_length; + dst += vid_priv->line_length; + } + + return 0; +} + +static int console_putc_xy_1(struct udevice *dev, uint x, uint y, char ch) +{ + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + int pbytes = VNBYTES(vid_priv->bpix); + int i, col; + int mask = 0x80; + void *line = vid_priv->fb + (x + 1) * vid_priv->line_length - + (y + 1) * pbytes; + uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT; + + for (col = 0; col < VIDEO_FONT_HEIGHT; col++) { + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif + default: + return -ENOSYS; + } + line += vid_priv->line_length; + mask >>= 1; + } + + return 0; +} + + +static int console_set_row_2(struct udevice *dev, uint row, int clr) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *line; + int pixels = VIDEO_FONT_HEIGHT * vid_priv->xsize; + int i; + + line = vid_priv->fb + vid_priv->ysize * vid_priv->line_length - + (row + 1) * VIDEO_FONT_HEIGHT * vid_priv->line_length; + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < pixels; i++) + *dst++ = clr; + break; + } +#endif + default: + return -ENOSYS; + } + + return 0; +} + +static int console_move_rows_2(struct udevice *dev, uint rowdst, uint rowsrc, + uint count) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *dst; + void *src; + void *end; + + end = vid_priv->fb + vid_priv->ysize * vid_priv->line_length; + dst = end - (rowdst + count) * VIDEO_FONT_HEIGHT * + vid_priv->line_length; + src = end - (rowsrc + count) * VIDEO_FONT_HEIGHT * + vid_priv->line_length; + memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count); + + return 0; +} + +static int console_putc_xy_2(struct udevice *dev, uint x, uint y, char ch) +{ + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + int i, row; + void *line; + + line = vid_priv->fb + (vid_priv->ysize - y - 1) * + vid_priv->line_length + + (vid_priv->xsize - x - VIDEO_FONT_WIDTH - 1) * + VNBYTES(vid_priv->bpix); + + for (row = 0; row < VIDEO_FONT_HEIGHT; row++) { + uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row]; + + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst-- = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst-- = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_WIDTH; i++) { + *dst-- = (bits & 0x80) ? vid_priv->colour_fg + : vid_priv->colour_bg; + bits <<= 1; + } + break; + } +#endif + default: + return -ENOSYS; + } + line -= vid_priv->line_length; + } + + return 0; +} + +static int console_set_row_3(struct udevice *dev, uint row, int clr) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + int pbytes = VNBYTES(vid_priv->bpix); + void *line; + int i, j; + + line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * pbytes; + for (j = 0; j < vid_priv->ysize; j++) { + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) + *dst++ = clr; + break; + } +#endif + default: + return -ENOSYS; + } + line += vid_priv->line_length; + } + + return 0; +} + +static int console_move_rows_3(struct udevice *dev, uint rowdst, uint rowsrc, + uint count) +{ + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + void *dst; + void *src; + int pbytes = VNBYTES(vid_priv->bpix); + int j; + + dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * pbytes; + src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * pbytes; + + for (j = 0; j < vid_priv->ysize; j++) { + memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count); + src += vid_priv->line_length; + dst += vid_priv->line_length; + } + + return 0; +} + +static int console_putc_xy_3(struct udevice *dev, uint x, uint y, char ch) +{ + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + int pbytes = VNBYTES(vid_priv->bpix); + int i, col; + int mask = 0x80; + void *line = vid_priv->fb + (vid_priv->ysize - x - 1) * + vid_priv->line_length + y * pbytes; + uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT; + + for (col = 0; col < VIDEO_FONT_HEIGHT; col++) { + switch (vid_priv->bpix) { +#ifdef CONFIG_VIDEO_BPP8 + case VIDEO_BPP8: { + uint8_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP16 + case VIDEO_BPP16: { + uint16_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif +#ifdef CONFIG_VIDEO_BPP32 + case VIDEO_BPP32: { + uint32_t *dst = line; + + for (i = 0; i < VIDEO_FONT_HEIGHT; i++) { + *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg + : vid_priv->colour_bg; + } + break; + } +#endif + default: + return -ENOSYS; + } + line -= vid_priv->line_length; + mask >>= 1; + } + + return 0; +} + + +static int console_probe_1_3(struct udevice *dev) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent); + + priv->cols = vid_priv->ysize / VIDEO_FONT_WIDTH; + priv->rows = vid_priv->xsize / VIDEO_FONT_HEIGHT; + + return 0; +} + +struct vidconsole_ops console_ops_1 = { + .putc_xy = console_putc_xy_1, + .move_rows = console_move_rows_1, + .set_row = console_set_row_1, +}; + +struct vidconsole_ops console_ops_2 = { + .putc_xy = console_putc_xy_2, + .move_rows = console_move_rows_2, + .set_row = console_set_row_2, +}; + +struct vidconsole_ops console_ops_3 = { + .putc_xy = console_putc_xy_3, + .move_rows = console_move_rows_3, + .set_row = console_set_row_3, +}; + +U_BOOT_DRIVER(vidconsole_1) = { + .name = "vidconsole1", + .id = UCLASS_VIDEO_CONSOLE, + .ops = &console_ops_1, + .probe = console_probe_1_3, +}; + +U_BOOT_DRIVER(vidconsole_2) = { + .name = "vidconsole2", + .id = UCLASS_VIDEO_CONSOLE, + .ops = &console_ops_2, +}; + +U_BOOT_DRIVER(vidconsole_3) = { + .name = "vidconsole3", + .id = UCLASS_VIDEO_CONSOLE, + .ops = &console_ops_3, + .probe = console_probe_1_3, +}; diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c index ba4578e9d1..21448a1411 100644 --- a/drivers/video/sandbox_sdl.c +++ b/drivers/video/sandbox_sdl.c @@ -5,75 +5,67 @@ */ #include <common.h> +#include <dm.h> #include <fdtdec.h> -#include <lcd.h> -#include <malloc.h> +#include <video.h> #include <asm/sdl.h> #include <asm/u-boot-sandbox.h> +#include <dm/test.h> DECLARE_GLOBAL_DATA_PTR; enum { - /* Maximum LCD size we support */ + /* Default LCD size we support */ LCD_MAX_WIDTH = 1366, LCD_MAX_HEIGHT = 768, - LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ }; -vidinfo_t panel_info; - -void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +static int sandbox_sdl_probe(struct udevice *dev) { -} + struct sandbox_sdl_plat *plat = dev_get_platdata(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + int ret; -void lcd_ctrl_init(void *lcdbase) -{ - /* - * Allocate memory to keep BMP color conversion map. This is required - * for 8 bit BMPs only (hence 256 colors). If malloc fails - keep - * going, it is not even clear if displyaing the bitmap will be - * required on the way up. - */ - panel_info.cmap = malloc(256 * NBITS(panel_info.vl_bpix) / 8); -} - -void lcd_enable(void) -{ - if (sandbox_sdl_init_display(panel_info.vl_col, panel_info.vl_row, - panel_info.vl_bpix)) + ret = sandbox_sdl_init_display(plat->xres, plat->yres, plat->bpix); + if (ret) { puts("LCD init failed\n"); + return ret; + } + uc_priv->xsize = plat->xres; + uc_priv->ysize = plat->yres; + uc_priv->bpix = plat->bpix; + uc_priv->rot = plat->rot; + + return 0; } -int sandbox_lcd_sdl_early_init(void) +static int sandbox_sdl_bind(struct udevice *dev) { + struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev); + struct sandbox_sdl_plat *plat = dev_get_platdata(dev); const void *blob = gd->fdt_blob; - int xres = LCD_MAX_WIDTH, yres = LCD_MAX_HEIGHT; - int node; + int node = dev->of_offset; int ret = 0; - /* - * The code in common/lcd.c does not cope with not being able to - * set up a frame buffer. It will just happily keep writing to - * invalid memory. So here we make sure that at least some buffer - * is available even if it actually won't be displayed. - */ - node = fdtdec_next_compatible(blob, 0, COMPAT_SANDBOX_LCD_SDL); - if (node >= 0) { - xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH); - yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT); - if (xres < 0 || xres > LCD_MAX_WIDTH) { - xres = LCD_MAX_WIDTH; - ret = -EINVAL; - } - if (yres < 0 || yres > LCD_MAX_HEIGHT) { - yres = LCD_MAX_HEIGHT; - ret = -EINVAL; - } - } - - panel_info.vl_col = xres; - panel_info.vl_row = yres; - panel_info.vl_bpix = LCD_COLOR16; + plat->xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH); + plat->yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT); + plat->bpix = VIDEO_BPP16; + uc_plat->size = plat->xres * plat->yres * (1 << plat->bpix) / 8; + debug("%s: Frame buffer size %x\n", __func__, uc_plat->size); return ret; } + +static const struct udevice_id sandbox_sdl_ids[] = { + { .compatible = "sandbox,lcd-sdl" }, + { } +}; + +U_BOOT_DRIVER(sdl_sandbox) = { + .name = "sdl_sandbox", + .id = UCLASS_VIDEO, + .of_match = sandbox_sdl_ids, + .bind = sandbox_sdl_bind, + .probe = sandbox_sdl_probe, + .platdata_auto_alloc_size = sizeof(struct sandbox_sdl_plat), +}; diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c new file mode 100644 index 0000000000..ea10189432 --- /dev/null +++ b/drivers/video/vidconsole-uclass.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2015 Google, Inc + * (C) Copyright 2001-2015 + * DENX Software Engineering -- wd@denx.de + * Compulab Ltd - http://compulab.co.il/ + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <video.h> +#include <video_console.h> +#include <video_font.h> /* Get font data, width and height */ + +/* By default we scroll by a single line */ +#ifndef CONFIG_CONSOLE_SCROLL_LINES +#define CONFIG_CONSOLE_SCROLL_LINES 1 +#endif + +int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + + if (!ops->putc_xy) + return -ENOSYS; + return ops->putc_xy(dev, x, y, ch); +} + +int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc, + uint count) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + + if (!ops->move_rows) + return -ENOSYS; + return ops->move_rows(dev, rowdst, rowsrc, count); +} + +int vidconsole_set_row(struct udevice *dev, uint row, int clr) +{ + struct vidconsole_ops *ops = vidconsole_get_ops(dev); + + if (!ops->set_row) + return -ENOSYS; + return ops->set_row(dev, row, clr); +} + +/* Move backwards one space */ +static void vidconsole_back(struct udevice *dev) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + + if (--priv->curr_col < 0) { + priv->curr_col = priv->cols - 1; + if (--priv->curr_row < 0) + priv->curr_row = 0; + } + + vidconsole_putc_xy(dev, priv->curr_col * VIDEO_FONT_WIDTH, + priv->curr_row * VIDEO_FONT_HEIGHT, ' '); +} + +/* Move to a newline, scrolling the display if necessary */ +static void vidconsole_newline(struct udevice *dev) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct udevice *vid_dev = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev); + const int rows = CONFIG_CONSOLE_SCROLL_LINES; + int i; + + priv->curr_col = 0; + + /* Check if we need to scroll the terminal */ + if (++priv->curr_row >= priv->rows) { + vidconsole_move_rows(dev, 0, rows, priv->rows - rows); + for (i = 0; i < rows; i++) + vidconsole_set_row(dev, priv->rows - i - 1, + vid_priv->colour_bg); + priv->curr_row -= rows; + } + video_sync(dev->parent); +} + +int vidconsole_put_char(struct udevice *dev, char ch) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + int ret; + + switch (ch) { + case '\r': + priv->curr_col = 0; + break; + case '\n': + vidconsole_newline(dev); + break; + case '\t': /* Tab (8 chars alignment) */ + priv->curr_col += 8; + priv->curr_col &= ~7; + + if (priv->curr_col >= priv->cols) + vidconsole_newline(dev); + break; + case '\b': + vidconsole_back(dev); + break; + default: + /* + * Failure of this function normally indicates an unsupported + * colour depth. Check this and return an error to help with + * diagnosis. + */ + ret = vidconsole_putc_xy(dev, + priv->curr_col * VIDEO_FONT_WIDTH, + priv->curr_row * VIDEO_FONT_HEIGHT, + ch); + if (ret) + return ret; + if (++priv->curr_col >= priv->cols) + vidconsole_newline(dev); + break; + } + + return 0; +} + +static void vidconsole_putc(struct stdio_dev *sdev, const char ch) +{ + struct udevice *dev = sdev->priv; + + vidconsole_put_char(dev, ch); +} + +static void vidconsole_puts(struct stdio_dev *sdev, const char *s) +{ + struct udevice *dev = sdev->priv; + + while (*s) + vidconsole_put_char(dev, *s++); +} + +/* Set up the number of rows and colours (rotated drivers override this) */ +static int vidconsole_pre_probe(struct udevice *dev) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct udevice *vid = dev->parent; + struct video_priv *vid_priv = dev_get_uclass_priv(vid); + + priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT; + priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH; + + return 0; +} + +/* Register the device with stdio */ +static int vidconsole_post_probe(struct udevice *dev) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &priv->sdev; + int ret; + + strlcpy(sdev->name, dev->name, sizeof(sdev->name)); + sdev->flags = DEV_FLAGS_OUTPUT; + sdev->putc = vidconsole_putc; + sdev->puts = vidconsole_puts; + sdev->priv = dev; + ret = stdio_register(sdev); + if (ret) + return ret; + + return 0; +} + +UCLASS_DRIVER(vidconsole) = { + .id = UCLASS_VIDEO_CONSOLE, + .name = "vidconsole0", + .pre_probe = vidconsole_pre_probe, + .post_probe = vidconsole_post_probe, + .per_device_auto_alloc_size = sizeof(struct vidconsole_priv), +}; + +void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row) +{ + struct vidconsole_priv *priv = dev_get_uclass_priv(dev); + + priv->curr_col = min_t(short, col, priv->cols - 1); + priv->curr_row = min_t(short, row, priv->rows - 1); +} + +static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + unsigned int col, row; + struct udevice *dev; + + if (argc != 3) + return CMD_RET_USAGE; + + uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev); + if (!dev) + return CMD_RET_FAILURE; + col = simple_strtoul(argv[1], NULL, 10); + row = simple_strtoul(argv[2], NULL, 10); + vidconsole_position_cursor(dev, col, row); + + return 0; +} + +static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + const char *s; + + if (argc != 2) + return CMD_RET_USAGE; + + uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev); + if (!dev) + return CMD_RET_FAILURE; + for (s = argv[1]; *s; s++) + vidconsole_put_char(dev, *s); + + return 0; +} + +U_BOOT_CMD( + setcurs, 3, 1, do_video_setcursor, + "set cursor position within screen", + " <col> <row> in character" +); + +U_BOOT_CMD( + lcdputs, 2, 1, do_video_puts, + "print string on video framebuffer", + " <string>" +); diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c new file mode 100644 index 0000000000..63d0d9d7d3 --- /dev/null +++ b/drivers/video/video-uclass.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <stdio_dev.h> +#include <video.h> +#include <video_console.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#ifdef CONFIG_SANDBOX +#include <asm/sdl.h> +#endif + +/* + * Theory of operation: + * + * Before relocation each device is bound. The driver for each device must + * set the @align and @size values in struct video_uc_platdata. This + * information represents the requires size and alignment of the frame buffer + * for the device. The values can be an over-estimate but cannot be too + * small. The actual values will be suppled (in the same manner) by the bind() + * method after relocation. + * + * This information is then picked up by video_reserve() which works out how + * much memory is needed for all devices. This is allocated between + * gd->video_bottom and gd->video_top. + * + * After relocation the same process occurs. The driver supplies the same + * @size and @align information and this time video_post_bind() checks that + * the drivers does not overflow the allocated memory. + * + * The frame buffer address is actually set (to plat->base) in + * video_post_probe(). This function also clears the frame buffer and + * allocates a suitable text console device. This can then be used to write + * text to the video device. + */ +DECLARE_GLOBAL_DATA_PTR; + +static ulong alloc_fb(struct udevice *dev, ulong *addrp) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + ulong base, align, size; + + align = plat->align ? plat->align : 1 << 20; + base = *addrp - plat->size; + base &= ~(align - 1); + plat->base = base; + size = *addrp - base; + *addrp = base; + + return size; +} + +int video_reserve(ulong *addrp) +{ + struct udevice *dev; + ulong size; + + gd->video_top = *addrp; + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + size = alloc_fb(dev, addrp); + debug("%s: Reserving %lx bytes at %lx for video device '%s'\n", + __func__, size, *addrp, dev->name); + } + gd->video_bottom = *addrp; + debug("Video frame buffers from %lx to %lx\n", gd->video_bottom, + gd->video_top); + + return 0; +} + +static int video_clear(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + if (priv->bpix == VIDEO_BPP32) { + u32 *ppix = priv->fb; + u32 *end = priv->fb + priv->fb_size; + + while (ppix < end) + *ppix++ = priv->colour_bg; + } else { + memset(priv->fb, priv->colour_bg, priv->fb_size); + } + + return 0; +} + +/* Flush video activity to the caches */ +void video_sync(struct udevice *vid) +{ + /* + * flush_dcache_range() is declared in common.h but it seems that some + * architectures do not actually implement it. Is there a way to find + * out whether it exists? For now, ARM is safe. + */ +#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF) + struct video_priv *priv = dev_get_uclass_priv(vid); + + if (priv->flush_dcache) { + flush_dcache_range((ulong)priv->fb, + (ulong)priv->fb + priv->fb_size); + } +#elif defined(CONFIG_VIDEO_SANDBOX_SDL) + struct video_priv *priv = dev_get_uclass_priv(vid); + static ulong last_sync; + + if (get_timer(last_sync) > 10) { + sandbox_sdl_sync(priv->fb); + last_sync = get_timer(0); + } +#endif +} + +void video_sync_all(void) +{ + struct udevice *dev; + + for (uclass_find_first_device(UCLASS_VIDEO, &dev); + dev; + uclass_find_next_device(&dev)) { + if (device_active(dev)) + video_sync(dev); + } +} + +int video_get_xsize(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + return priv->xsize; +} + +int video_get_ysize(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + return priv->ysize; +} + +/* Set up the colour map */ +static int video_pre_probe(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + priv->cmap = calloc(256, sizeof(ushort)); + if (!priv->cmap) + return -ENOMEM; + + return 0; +} + +static int video_pre_remove(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + + free(priv->cmap); + + return 0; +} + +/* Set up the display ready for use */ +static int video_post_probe(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + struct video_priv *priv = dev_get_uclass_priv(dev); + char name[30], drv[15], *str; + struct udevice *cons; + int ret; + + /* Set up the line and display size */ + priv->fb = map_sysmem(plat->base, plat->size); + priv->line_length = priv->xsize * VNBYTES(priv->bpix); + priv->fb_size = priv->line_length * priv->ysize; + + /* Set up colours - we could in future support other colours */ +#ifdef CONFIG_SYS_WHITE_ON_BLACK + priv->colour_fg = 0xffffff; +#else + priv->colour_bg = 0xffffff; +#endif + video_clear(dev); + + /* + * Create a text console devices. For now we always do this, although + * it might be useful to support only bitmap drawing on the device + * for boards that don't need to display text. + */ + snprintf(name, sizeof(name), "%s.vidconsole", dev->name); + str = strdup(name); + if (!str) + return -ENOMEM; + snprintf(drv, sizeof(drv), "vidconsole%d", priv->rot); + ret = device_bind_driver(dev, drv, str, &cons); + if (ret) { + debug("%s: Cannot bind console driver\n", __func__); + return ret; + } + ret = device_probe(cons); + if (ret) { + debug("%s: Cannot probe console driver\n", __func__); + return ret; + } + + return 0; +}; + +/* Post-relocation, allocate memory for the frame buffer */ +static int video_post_bind(struct udevice *dev) +{ + ulong addr = gd->video_top; + ulong size; + + /* Before relocation there is nothing to do here */ + if ((!gd->flags & GD_FLG_RELOC)) + return 0; + size = alloc_fb(dev, &addr); + if (addr < gd->video_bottom) { + /* Device tree node may need the 'u-boot,dm-pre-reloc' tag */ + printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n", + dev->name); + return -ENOSPC; + } + debug("%s: Claiming %lx bytes at %lx for video device '%s'\n", + __func__, size, addr, dev->name); + gd->video_bottom = addr; + + return 0; +} + +UCLASS_DRIVER(video) = { + .id = UCLASS_VIDEO, + .name = "video", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .post_bind = video_post_bind, + .pre_probe = video_pre_probe, + .post_probe = video_post_probe, + .pre_remove = video_pre_remove, + .per_device_auto_alloc_size = sizeof(struct video_priv), + .per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata), +}; diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c new file mode 100644 index 0000000000..c9075d62dd --- /dev/null +++ b/drivers/video/video_bmp.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <bmp_layout.h> +#include <dm.h> +#include <mapmem.h> +#include <video.h> +#include <watchdog.h> +#include <asm/unaligned.h> + +#ifdef CONFIG_VIDEO_BMP_RLE8 +#define BMP_RLE8_ESCAPE 0 +#define BMP_RLE8_EOL 0 +#define BMP_RLE8_EOBMP 1 +#define BMP_RLE8_DELTA 2 + +static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, + int cnt) +{ + while (cnt > 0) { + *(*fbp)++ = cmap[*bmap++]; + cnt--; + } +} + +static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt) +{ + ushort *fb = *fbp; + + while (cnt > 0) { + *fb++ = col; + cnt--; + } + *fbp = fb; +} + +static void video_display_rle8_bitmap(struct udevice *dev, + struct bmp_image *bmp, ushort *cmap, + uchar *fb, int x_off, int y_off) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + uchar *bmap; + ulong width, height; + ulong cnt, runlen; + int x, y; + int decode = 1; + + debug("%s\n", __func__); + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + + x = 0; + y = height - 1; + + while (decode) { + if (bmap[0] == BMP_RLE8_ESCAPE) { + switch (bmap[1]) { + case BMP_RLE8_EOL: + /* end of line */ + bmap += 2; + x = 0; + y--; + /* 16bpix, 2-byte per pixel, width should *2 */ + fb -= (width * 2 + priv->line_length); + break; + case BMP_RLE8_EOBMP: + /* end of bitmap */ + decode = 0; + break; + case BMP_RLE8_DELTA: + /* delta run */ + x += bmap[2]; + y -= bmap[3]; + /* 16bpix, 2-byte per pixel, x should *2 */ + fb = (uchar *)(priv->fb + (y + y_off - 1) + * priv->line_length + (x + x_off) * 2); + bmap += 4; + break; + default: + /* unencoded run */ + runlen = bmap[1]; + bmap += 2; + if (y < height) { + if (x < width) { + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_unencoded_bitmap( + (ushort **)&fb, + bmap, cmap, cnt); + } + x += runlen; + } + bmap += runlen; + if (runlen & 1) + bmap++; + } + } else { + /* encoded run */ + if (y < height) { + runlen = bmap[0]; + if (x < width) { + /* aggregate the same code */ + while (bmap[0] == 0xff && + bmap[2] != BMP_RLE8_ESCAPE && + bmap[1] == bmap[3]) { + runlen += bmap[2]; + bmap += 2; + } + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_encoded_bitmap((ushort **)&fb, + cmap[bmap[1]], cnt); + } + x += runlen; + } + bmap += 2; + } + } +} +#endif + +__weak void fb_put_byte(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; +} + +#if defined(CONFIG_BMP_16BPP) +__weak void fb_put_word(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; + *(*fb)++ = *(*from)++; +} +#endif /* CONFIG_BMP_16BPP */ + +#define BMP_ALIGN_CENTER 0x7fff + +/** + * video_splash_align_axis() - Align a single coordinate + * + *- if a coordinate is 0x7fff then the image will be centred in + * that direction + *- if a coordinate is -ve then it will be offset to the + * left/top of the centre by that many pixels + *- if a coordinate is positive it will be used unchnaged. + * + * @axis: Input and output coordinate + * @panel_size: Size of panel in pixels for that axis + * @picture_size: Size of bitmap in pixels for that axis + */ +static void video_splash_align_axis(int *axis, unsigned long panel_size, + unsigned long picture_size) +{ + unsigned long panel_picture_delta = panel_size - picture_size; + unsigned long axis_alignment; + + if (*axis == BMP_ALIGN_CENTER) + axis_alignment = panel_picture_delta / 2; + else if (*axis < 0) + axis_alignment = panel_picture_delta + *axis + 1; + else + return; + + *axis = max(0, (int)axis_alignment); +} + +static void video_set_cmap(struct udevice *dev, + struct bmp_color_table_entry *cte, unsigned colours) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + int i; + ushort *cmap = priv->cmap; + + debug("%s: colours=%d\n", __func__, colours); + for (i = 0; i < colours; ++i) { + *cmap = ((cte->red << 8) & 0xf800) | + ((cte->green << 3) & 0x07e0) | + ((cte->blue >> 3) & 0x001f); + cmap++; + cte++; + } +} + +int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, + bool align) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + ushort *cmap_base = NULL; + ushort i, j; + uchar *fb; + struct bmp_image *bmp = map_sysmem(bmp_image, 0); + uchar *bmap; + ushort padded_width; + unsigned long width, height, byte_width; + unsigned long pwidth = priv->xsize; + unsigned colours, bpix, bmp_bpix; + struct bmp_color_table_entry *palette; + int hdr_size; + + if (!bmp || !(bmp->header.signature[0] == 'B' && + bmp->header.signature[1] == 'M')) { + printf("Error: no valid bmp image at %lx\n", bmp_image); + + return -EINVAL; + } + + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); + hdr_size = get_unaligned_le16(&bmp->header.size); + debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix); + palette = (void *)bmp + 14 + hdr_size; + + colours = 1 << bmp_bpix; + + bpix = VNBITS(priv->bpix); + + if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { + printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, bmp_bpix); + + return -EINVAL; + } + + /* + * We support displaying 8bpp BMPs on 16bpp LCDs + * and displaying 24bpp BMPs on 32bpp LCDs + * */ + if (bpix != bmp_bpix && + !(bmp_bpix == 8 && bpix == 16) && + !(bmp_bpix == 24 && bpix == 32)) { + printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, get_unaligned_le16(&bmp->header.bit_count)); + return -EPERM; + } + + debug("Display-bmp: %d x %d with %d colours, display %d\n", + (int)width, (int)height, (int)colours, 1 << bpix); + + if (bmp_bpix == 8) + video_set_cmap(dev, palette, colours); + + padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); + + if (align) { + video_splash_align_axis(&x, priv->xsize, width); + video_splash_align_axis(&y, priv->ysize, height); + } + + if ((x + width) > pwidth) + width = pwidth - x; + if ((y + height) > priv->ysize) + height = priv->ysize - y; + + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + fb = (uchar *)(priv->fb + + (y + height - 1) * priv->line_length + x * bpix / 8); + + switch (bmp_bpix) { + case 1: + case 8: { + cmap_base = priv->cmap; +#ifdef CONFIG_VIDEO_BMP_RLE8 + u32 compression = get_unaligned_le32(&bmp->header.compression); + debug("compressed %d %d\n", compression, BMP_BI_RLE8); + if (compression == BMP_BI_RLE8) { + if (bpix != 16) { + /* TODO implement render code for bpix != 16 */ + printf("Error: only support 16 bpix"); + return -EPROTONOSUPPORT; + } + video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x, + y); + break; + } +#endif + + if (bpix != 16) + byte_width = width; + else + byte_width = width * 2; + + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + if (bpix != 16) { + fb_put_byte(&fb, &bmap); + } else { + *(uint16_t *)fb = cmap_base[*bmap]; + bmap++; + fb += sizeof(uint16_t) / sizeof(*fb); + } + } + bmap += (padded_width - width); + fb -= byte_width + priv->line_length; + } + break; + } +#if defined(CONFIG_BMP_16BPP) + case 16: + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) + fb_put_word(&fb, &bmap); + + bmap += (padded_width - width) * 2; + fb -= width * 2 + lcd_line_length; + } + break; +#endif /* CONFIG_BMP_16BPP */ +#if defined(CONFIG_BMP_24BMP) + case 24: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = 0; + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_24BMP */ +#if defined(CONFIG_BMP_32BPP) + case 32: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_32BPP */ + default: + break; + }; + + video_sync(dev); + + return 0; +} + diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 5d8b043f14..a587d3c203 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -122,6 +122,10 @@ typedef struct global_data { struct membuff console_out; /* console output */ struct membuff console_in; /* console input */ #endif +#ifdef CONFIG_DM_VIDEO + ulong video_top; /* Top of video frame buffer area */ + ulong video_bottom; /* Bottom of video frame buffer area */ +#endif } gd_t; #endif diff --git a/include/bzlib.h b/include/bzlib.h index 2d864d56b7..19314f8f66 100644 --- a/include/bzlib.h +++ b/include/bzlib.h @@ -68,7 +68,10 @@ /* Configure for U-Boot environment */ #define BZ_NO_STDIO + +#ifndef CONFIG_SANDBOX #define BZ_NO_COMPRESS +#endif /* End of configuration for U-Boot environment */ #ifdef __cplusplus diff --git a/include/clk.h b/include/clk.h index 254ad2b876..941808a50e 100644 --- a/include/clk.h +++ b/include/clk.h @@ -8,6 +8,10 @@ #ifndef _CLK_H_ #define _CLK_H_ +#include <linux/types.h> + +struct udevice; + int soc_clk_dump(void); struct clk_ops { @@ -29,19 +33,28 @@ struct clk_ops { ulong (*set_rate)(struct udevice *dev, ulong rate); /** - * clk_set_periph_rate() - Set clock rate for a peripheral - * - * @dev: Device to adjust (UCLASS_CLK) - * @rate: New clock rate in Hz - * @return new clock rate in Hz, or -ve error code - */ + * enable() - Enable the clock for a peripheral + * + * @dev: clock provider + * @periph: Peripheral ID to enable + * @return zero on success, or -ve error code + */ + int (*enable)(struct udevice *dev, int periph); + + /** + * get_periph_rate() - Get clock rate for a peripheral + * + * @dev: Device to check (UCLASS_CLK) + * @periph: Peripheral ID to check + * @return clock rate in Hz, or -ve error code + */ ulong (*get_periph_rate)(struct udevice *dev, int periph); /** - * clk_set_periph_rate() - Set current clock rate for a peripheral + * set_periph_rate() - Set current clock rate for a peripheral * * @dev: Device to update (UCLASS_CLK) - * @periph: Peripheral ID to cupdate + * @periph: Peripheral ID to update * @return new clock rate in Hz, or -ve error code */ ulong (*set_periph_rate)(struct udevice *dev, int periph, ulong rate); @@ -58,7 +71,7 @@ struct clk_ops { ulong clk_get_rate(struct udevice *dev); /** - * set_rate() - Set current clock rate + * clk_set_rate() - Set current clock rate * * @dev: Device to adjust * @rate: New clock rate in Hz @@ -67,6 +80,15 @@ ulong clk_get_rate(struct udevice *dev); ulong clk_set_rate(struct udevice *dev, ulong rate); /** + * clk_enable() - Enable the clock for a peripheral + * + * @dev: clock provider + * @periph: Peripheral ID to enable + * @return zero on success, or -ve error code + */ +int clk_enable(struct udevice *dev, int periph); + +/** * clk_get_periph_rate() - Get current clock rate for a peripheral * * @dev: Device to check (UCLASS_CLK) @@ -78,7 +100,7 @@ ulong clk_get_periph_rate(struct udevice *dev, int periph); * clk_set_periph_rate() - Set current clock rate for a peripheral * * @dev: Device to update (UCLASS_CLK) - * @periph: Peripheral ID to cupdate + * @periph: Peripheral ID to update * @return new clock rate in Hz, or -ve error code */ ulong clk_set_periph_rate(struct udevice *dev, int periph, ulong rate); diff --git a/include/configs/am335x_evm.h b/include/configs/am335x_evm.h index cf6a6063b5..6ebe0b3866 100644 --- a/include/configs/am335x_evm.h +++ b/include/configs/am335x_evm.h @@ -357,6 +357,7 @@ */ #ifdef CONFIG_SPL_BUILD #undef CONFIG_DM_MMC +#undef CONFIG_TIMER #endif #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_USBETH_SUPPORT) diff --git a/include/configs/am43xx_evm.h b/include/configs/am43xx_evm.h index de7538fc9e..c3867efe9a 100644 --- a/include/configs/am43xx_evm.h +++ b/include/configs/am43xx_evm.h @@ -144,6 +144,7 @@ #undef CONFIG_DM_MMC #undef CONFIG_DM_SPI #undef CONFIG_DM_SPI_FLASH +#undef CONFIG_TIMER #endif #ifndef CONFIG_SPL_BUILD diff --git a/include/configs/colibri_vf.h b/include/configs/colibri_vf.h index 708c79af97..5aed3a5fcd 100644 --- a/include/configs/colibri_vf.h +++ b/include/configs/colibri_vf.h @@ -36,7 +36,6 @@ #define CONFIG_BOARD_EARLY_INIT_F -#define CONFIG_FSL_LPUART #define LPUART_BASE UART0_BASE /* Allow to overwrite serial and ethaddr */ diff --git a/include/configs/ls1021aqds.h b/include/configs/ls1021aqds.h index 2e8dbc7a78..e8b1ecaeb1 100644 --- a/include/configs/ls1021aqds.h +++ b/include/configs/ls1021aqds.h @@ -371,7 +371,6 @@ unsigned long get_board_ddr_clk(void); * Serial Port */ #ifdef CONFIG_LPUART -#define CONFIG_FSL_LPUART #define CONFIG_LPUART_32B_REG #else #define CONFIG_CONS_INDEX 1 diff --git a/include/configs/ls1021atwr.h b/include/configs/ls1021atwr.h index c12ba3ac91..317ba62d3d 100644 --- a/include/configs/ls1021atwr.h +++ b/include/configs/ls1021atwr.h @@ -266,12 +266,13 @@ * Serial Port */ #ifdef CONFIG_LPUART -#define CONFIG_FSL_LPUART #define CONFIG_LPUART_32B_REG #else #define CONFIG_CONS_INDEX 1 #define CONFIG_SYS_NS16550_SERIAL +#ifndef CONFIG_DM_SERIAL #define CONFIG_SYS_NS16550_REG_SIZE 1 +#endif #define CONFIG_SYS_NS16550_CLK get_serial_clock() #endif diff --git a/include/configs/pcm052.h b/include/configs/pcm052.h index b851bba25d..891bdb0ecf 100644 --- a/include/configs/pcm052.h +++ b/include/configs/pcm052.h @@ -27,7 +27,6 @@ #define CONFIG_BOARD_EARLY_INIT_F -#define CONFIG_FSL_LPUART #define LPUART_BASE UART1_BASE /* Allow to overwrite serial and ethaddr */ diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index d3112e1760..6498981cef 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -158,24 +158,23 @@ /* LCD and keyboard require SDL support */ #ifdef CONFIG_SANDBOX_SDL -#define CONFIG_LCD -#define CONFIG_VIDEO_SANDBOX_SDL #define CONFIG_CMD_BMP -#define CONFIG_BOARD_EARLY_INIT_F #define CONFIG_CONSOLE_MUX #define CONFIG_SYS_CONSOLE_IS_IN_ENV #define LCD_BPP LCD_COLOR16 #define CONFIG_LCD_BMP_RLE8 +#define CONFIG_VIDEO_BMP_RLE8 +#define CONFIG_SPLASH_SCREEN_ALIGN #define CONFIG_KEYBOARD #define SANDBOX_SERIAL_SETTINGS "stdin=serial,cros-ec-keyb,usbkbd\0" \ - "stdout=serial,lcd\0" \ - "stderr=serial,lcd\0" + "stdout=serial,lcd.vidconsole\0" \ + "stderr=serial,lcd.vidconsole\0" #else #define SANDBOX_SERIAL_SETTINGS "stdin=serial\0" \ - "stdout=serial,lcd\0" \ - "stderr=serial,lcd\0" + "stdout=serial,lcd.vidconsole\0" \ + "stderr=serial,lcd.vidconsole\0" #endif #define SANDBOX_ETH_SETTINGS "ethaddr=00:00:11:22:33:44\0" \ diff --git a/include/configs/ti_omap5_common.h b/include/configs/ti_omap5_common.h index 2d492f8ba7..d164e6abd4 100644 --- a/include/configs/ti_omap5_common.h +++ b/include/configs/ti_omap5_common.h @@ -164,6 +164,7 @@ */ #ifdef CONFIG_SPL_BUILD #undef CONFIG_DM_MMC +#undef CONFIG_TIMER #endif #endif /* __CONFIG_TI_OMAP5_COMMON_H */ diff --git a/include/configs/vf610twr.h b/include/configs/vf610twr.h index 34df6f0352..dcfafaf631 100644 --- a/include/configs/vf610twr.h +++ b/include/configs/vf610twr.h @@ -34,7 +34,6 @@ #define CONFIG_BOARD_EARLY_INIT_F -#define CONFIG_FSL_LPUART #define LPUART_BASE UART1_BASE /* Allow to overwrite serial and ethaddr */ diff --git a/include/dm/test.h b/include/dm/test.h index a4bc5c8404..ca924d9237 100644 --- a/include/dm/test.h +++ b/include/dm/test.h @@ -155,6 +155,14 @@ enum { /* Declare a new driver model test */ #define DM_TEST(_name, _flags) UNIT_TEST(_name, _flags, dm_test) +/* This platform data is needed in tests, so declare it here */ +struct sandbox_sdl_plat { + int xres; + int yres; + int bpix; + int rot; +}; + /* Declare ping methods for the drivers */ int test_ping(struct udevice *dev, int pingval, int *pingret); int testfdt_ping(struct udevice *dev, int pingval, int *pingret); diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 27fa0b68db..a0a3a79aac 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -66,7 +66,9 @@ enum uclass_id { UCLASS_USB, /* USB bus */ UCLASS_USB_DEV_GENERIC, /* USB generic device */ UCLASS_USB_HUB, /* USB hub */ + UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ + UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/fdtdec.h b/include/fdtdec.h index 27b350e241..25e98c9c9e 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -151,7 +151,6 @@ enum fdt_compat_id { COMPAT_GENERIC_SPI_FLASH, /* Generic SPI Flash chip */ COMPAT_MAXIM_98095_CODEC, /* MAX98095 Codec */ COMPAT_SAMSUNG_EXYNOS5_I2C, /* Exynos5 High Speed I2C Controller */ - COMPAT_SANDBOX_LCD_SDL, /* Sandbox LCD emulation with SDL */ COMPAT_SAMSUNG_EXYNOS_SYSMMU, /* Exynos sysmmu */ COMPAT_INTEL_MICROCODE, /* Intel microcode update */ COMPAT_MEMORY_SPD, /* Memory SPD information */ diff --git a/include/lcd.h b/include/lcd.h index 59202b7e59..d7651a8f08 100644 --- a/include/lcd.h +++ b/include/lcd.h @@ -18,6 +18,12 @@ #include <asm/byteorder.h> #endif +int bmp_display(ulong addr, int x, int y); +struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, + void **alloc_addr); + +#ifndef CONFIG_DM_VIDEO + extern char lcd_is_enabled; extern int lcd_line_length; extern struct vidinfo panel_info; @@ -26,10 +32,6 @@ void lcd_ctrl_init(void *lcdbase); void lcd_enable(void); void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue); -struct bmp_image *gunzip_bmp(unsigned long addr, unsigned long *lenp, - void **alloc_addr); -int bmp_display(ulong addr, int x, int y); - /** * Set whether we need to flush the dcache when changing the LCD image. This * defaults to off. @@ -209,4 +211,6 @@ void lcd_sync(void); #define PAGE_SIZE 4096 #endif +#endif /* !CONFIG_DM_VIDEO */ + #endif /* _LCD_H_ */ diff --git a/include/timer.h b/include/timer.h index 7fee17e3d2..f14725cc28 100644 --- a/include/timer.h +++ b/include/timer.h @@ -8,6 +8,15 @@ #define _TIMER_H_ /* + * dm_timer_init - initialize a timer for time keeping. On success + * initializes gd->timer so that lib/timer can use it for future + * referrence. + * + * @return - 0 on success or error number + */ +int dm_timer_init(void); + +/* * timer_conv_64 - convert 32-bit counter value to 64-bit * * @count: 32-bit counter value diff --git a/include/video.h b/include/video.h index 65e4ec1e1a..b20f06f335 100644 --- a/include/video.h +++ b/include/video.h @@ -1,14 +1,167 @@ /* -** MPC823 Video Controller -** ======================= -** (C) 2000 by Paolo Scaffardi (arsenio@tin.it) -** AIRVENT SAM s.p.a - RIMINI(ITALY) -** -*/ + * Video uclass and legacy implementation + * + * Copyright (c) 2015 Google, Inc + * + * MPC823 Video Controller + * ======================= + * (C) 2000 by Paolo Scaffardi (arsenio@tin.it) + * AIRVENT SAM s.p.a - RIMINI(ITALY) + * + */ #ifndef _VIDEO_H_ #define _VIDEO_H_ +#ifdef CONFIG_DM_VIDEO + +#include <stdio_dev.h> + +struct video_uc_platdata { + uint align; + uint size; + ulong base; +}; + +/* + * Bits per pixel selector. Each value n is such that the bits-per-pixel is + * 2 ^ n + */ +enum video_log2_bpp { + VIDEO_BPP1 = 0, + VIDEO_BPP2, + VIDEO_BPP4, + VIDEO_BPP8, + VIDEO_BPP16, + VIDEO_BPP32, +}; + +/* + * Convert enum video_log2_bpp to bytes and bits. Note we omit the outer + * brackets to allow multiplication by fractional pixels. + */ +#define VNBYTES(bpix) (1 << (bpix)) / 8 + +#define VNBITS(bpix) (1 << (bpix)) + +/** + * struct video_priv - Device information used by the video uclass + * + * @xsize: Number of pixel columns (e.g. 1366) + * @ysize: Number of pixels rows (e.g.. 768) + * @tor: Display rotation (0=none, 1=90 degrees clockwise, etc.) + * @bpix: Encoded bits per pixel + * @fb: Frame buffer + * @fb_size: Frame buffer size + * @line_length: Length of each frame buffer line, in bytes + * @colour_fg: Foreground colour (pixel value) + * @colour_bg: Background colour (pixel value) + * @flush_dcache: true to enable flushing of the data cache after + * the LCD is updated + * @cmap: Colour map for 8-bit-per-pixel displays + */ +struct video_priv { + /* Things set up by the driver: */ + ushort xsize; + ushort ysize; + ushort rot; + enum video_log2_bpp bpix; + + /* + * Things that are private to the uclass: don't use these in the + * driver + */ + void *fb; + int fb_size; + int line_length; + int colour_fg; + int colour_bg; + bool flush_dcache; + ushort *cmap; +}; + +/* Placeholder - there are no video operations at present */ +struct video_ops { +}; + +#define video_get_ops(dev) ((struct video_ops *)(dev)->driver->ops) + +/** + * video_reserve() - Reserve frame-buffer memory for video devices + * + * Note: This function is for internal use. + * + * This uses the uclass platdata's @size and @align members to figure out + * a size and position for each frame buffer as part of the pre-relocation + * process of determining the post-relocation memory layout. + * + * gd->video_top is set to the initial value of *@addrp and gd->video_bottom + * is set to the final value. + * + * @addrp: On entry, the top of available memory. On exit, the new top, + * after allocating the required memory. + * @return 0 + */ +int video_reserve(ulong *addrp); + +/** + * video_sync() - Sync a device's frame buffer with its hardware + * + * Some frame buffers are cached or have a secondary frame buffer. This + * function syncs these up so that the current contents of the U-Boot frame + * buffer are displayed to the user. + * + * @dev: Device to sync + */ +void video_sync(struct udevice *vid); + +/** + * video_sync_all() - Sync all devices' frame buffers with there hardware + * + * This calls video_sync() on all active video devices. + */ +void video_sync_all(void); + +/** + * video_bmp_display() - Display a BMP file + * + * @dev: Device to display the bitmap on + * @bmp_image: Address of bitmap image to display + * @x: X position in pixels from the left + * @y: Y position in pixels from the top + * @align: true to adjust the coordinates to centre the image. If false + * the coordinates are used as is. If true: + * + * - if a coordinate is 0x7fff then the image will be centred in + * that direction + * - if a coordinate is -ve then it will be offset to the + * left/top of the centre by that many pixels + * - if a coordinate is positive it will be used unchnaged. + * @return 0 if OK, -ve on error + */ +int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, + bool align); + +/** + * video_get_xsize() - Get the width of the display in pixels + * + * @dev: Device to check + * @return device frame buffer width in pixels + */ +int video_get_xsize(struct udevice *dev); + +/** + * video_get_ysize() - Get the height of the display in pixels + * + * @dev: Device to check + * @return device frame buffer height in pixels + */ +int video_get_ysize(struct udevice *dev); + +#endif /* CONFIG_DM_VIDEO */ + +#ifndef CONFIG_DM_VIDEO + /* Video functions */ struct stdio_dev; @@ -73,4 +226,7 @@ int kwh043st20_f01_spi_startup(unsigned int bus, unsigned int cs, int lg4573_spi_startup(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int spi_mode); #endif + +#endif /* CONFIG_DM_VIDEO */ + #endif diff --git a/include/video_console.h b/include/video_console.h new file mode 100644 index 0000000000..c0fc79273a --- /dev/null +++ b/include/video_console.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __video_console_h +#define __video_console_h + +/** + * struct vidconsole_priv - uclass-private data about a console device + * + * @sdev: stdio device, acting as an output sink + * @curr_col: Current text column (0=left) + * @curr_row: Current row (0=top) + * @rows: Number of text rows + * @cols: Number of text columns + */ +struct vidconsole_priv { + struct stdio_dev sdev; + int curr_col; + int curr_row; + int rows; + int cols; +}; + +/** + * struct vidconsole_ops - Video console operations + * + * These operations work on either an absolute console position (measured + * in pixels) or a text row number (measured in rows, where each row consists + * of an entire line of text - typically 16 pixels). + */ +struct vidconsole_ops { + /** + * putc_xy() - write a single character to a position + * + * @dev: Device to write to + * @x: Pixel X position (0=left-most pixel) + * @y: Pixel Y position (0=top-most pixel) + * @ch: Character to write + * @return 0 if OK, -ve on error + */ + int (*putc_xy)(struct udevice *dev, uint x, uint y, char ch); + + /** + * move_rows() - Move text rows from one place to another + * + * @dev: Device to adjust + * @rowdst: Destination text row (0=top) + * @rowsrc: Source start text row + * @count: Number of text rows to move + * @return 0 if OK, -ve on error + */ + int (*move_rows)(struct udevice *dev, uint rowdst, uint rowsrc, + uint count); + + /** + * set_row() - Set the colour of a text row + * + * Every pixel contained within the text row is adjusted + * + * @dev: Device to adjust + * @row: Text row to adjust (0=top) + * @clr: Raw colour (pixel value) to write to each pixel + * @return 0 if OK, -ve on error + */ + int (*set_row)(struct udevice *dev, uint row, int clr); +}; + +/* Get a pointer to the driver operations for a video console device */ +#define vidconsole_get_ops(dev) ((struct vidconsole_ops *)(dev)->driver->ops) + +/** + * vidconsole_putc_xy() - write a single character to a position + * + * @dev: Device to write to + * @x: Pixel X position (0=left-most pixel) + * @y: Pixel Y position (0=top-most pixel) + * @ch: Character to write + * @return 0 if OK, -ve on error + */ +int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch); + +/** + * vidconsole_move_rows() - Move text rows from one place to another + * + * @dev: Device to adjust + * @rowdst: Destination text row (0=top) + * @rowsrc: Source start text row + * @count: Number of text rows to move + * @return 0 if OK, -ve on error + */ +int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc, + uint count); + +/** + * vidconsole_set_row() - Set the colour of a text row + * + * Every pixel contained within the text row is adjusted + * + * @dev: Device to adjust + * @row: Text row to adjust (0=top) + * @clr: Raw colour (pixel value) to write to each pixel + * @return 0 if OK, -ve on error + */ +int vidconsole_set_row(struct udevice *dev, uint row, int clr); + +/** + * vidconsole_put_char() - Output a character to the current console position + * + * Outputs a character to the console and advances the cursor. This function + * handles wrapping to new lines and scrolling the console. Special + * characters are handled also: \n, \r, \b and \t. + * + * The device always starts with the cursor at position 0,0 (top left). It + * can be adjusted manually using vidconsole_position_cursor(). + * + * @dev: Device to adjust + * @ch: Character to write + * @return 0 if OK, -ve on error + */ +int vidconsole_put_char(struct udevice *dev, char ch); + +/** + * vidconsole_position_cursor() - Move the text cursor + * + * @dev: Device to adjust + * @col: New cursor text column + * @row: New cursor text row + * @return 0 if OK, -ve on error + */ +void vidconsole_position_cursor(struct udevice *dev, unsigned col, + unsigned row); + +#endif diff --git a/lib/bzip2/Makefile b/lib/bzip2/Makefile index f0b81ad2c2..585d776ba8 100644 --- a/lib/bzip2/Makefile +++ b/lib/bzip2/Makefile @@ -4,3 +4,4 @@ obj-y += bzlib.o bzlib_crctable.o bzlib_decompress.o \ bzlib_randtable.o bzlib_huffman.o +obj-$(CONFIG_SANDBOX) += bzlib_compress.o bzlib_blocksort.o diff --git a/lib/bzip2/bzlib_blocksort.c b/lib/bzip2/bzlib_blocksort.c new file mode 100644 index 0000000000..2785521502 --- /dev/null +++ b/lib/bzip2/bzlib_blocksort.c @@ -0,0 +1,1134 @@ + +/*-------------------------------------------------------------*/ +/*--- Block sorting machinery ---*/ +/*--- blocksort.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2002 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@acm.org + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +#include "bzlib_private.h" + +/*---------------------------------------------*/ +/*--- Fallback O(N log(N)^2) sorting ---*/ +/*--- algorithm, for repetitive blocks ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +void fallbackSimpleSort ( UInt32* fmap, + UInt32* eclass, + Int32 lo, + Int32 hi ) +{ + Int32 i, j, tmp; + UInt32 ec_tmp; + + if (lo == hi) return; + + if (hi - lo > 3) { + for ( i = hi-4; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) + fmap[j-4] = fmap[j]; + fmap[j-4] = tmp; + } + } + + for ( i = hi-1; i >= lo; i-- ) { + tmp = fmap[i]; + ec_tmp = eclass[tmp]; + for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) + fmap[j-1] = fmap[j]; + fmap[j-1] = tmp; + } +} + + +/*---------------------------------------------*/ +#define fswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define fvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + fswap(fmap[yyp1], fmap[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + + +#define fmin(a,b) ((a) < (b)) ? (a) : (b) + +#define fpush(lz,hz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + sp++; } + +#define fpop(lz,hz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; } + +#define FALLBACK_QSORT_SMALL_THRESH 10 +#define FALLBACK_QSORT_STACK_SIZE 100 + + +static +void fallbackQSort3 ( UInt32* fmap, + UInt32* eclass, + Int32 loSt, + Int32 hiSt ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m; + Int32 sp, lo, hi; + UInt32 med, r, r3; + Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; + Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; + + r = 0; + + sp = 0; + fpush ( loSt, hiSt ); + + while (sp > 0) { + + AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); + + fpop ( lo, hi ); + if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { + fallbackSimpleSort ( fmap, eclass, lo, hi ); + continue; + } + + /* Random partitioning. Median of 3 sometimes fails to + avoid bad cases. Median of 9 seems to help but + looks rather expensive. This too seems to work but + is cheaper. Guidance for the magic constants + 7621 and 32768 is taken from Sedgewick's algorithms + book, chapter 35. + */ + r = ((r * 7621) + 1) % 32768; + r3 = r % 3; + if (r3 == 0) med = eclass[fmap[lo]]; else + if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else + med = eclass[fmap[hi]]; + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (1) { + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unLo]] - (Int32)med; + if (n == 0) { + fswap(fmap[unLo], fmap[ltLo]); + ltLo++; unLo++; + continue; + }; + if (n > 0) break; + unLo++; + } + while (1) { + if (unLo > unHi) break; + n = (Int32)eclass[fmap[unHi]] - (Int32)med; + if (n == 0) { + fswap(fmap[unHi], fmap[gtHi]); + gtHi--; unHi--; + continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); + + if (gtHi < ltLo) continue; + + n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); + m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + if (n - lo > hi - m) { + fpush ( lo, n ); + fpush ( m, hi ); + } else { + fpush ( m, hi ); + fpush ( lo, n ); + } + } +} + +#undef fmin +#undef fpush +#undef fpop +#undef fswap +#undef fvswap +#undef FALLBACK_QSORT_SMALL_THRESH +#undef FALLBACK_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + eclass exists for [0 .. nblock-1] + ((UChar*)eclass) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)eclass) [0 .. nblock-1] holds block + All other areas of eclass destroyed + fmap [0 .. nblock-1] holds sorted order + bhtab [ 0 .. 2+(nblock/32) ] destroyed +*/ + +#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) +#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) +#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) +#define WORD_BH(zz) bhtab[(zz) >> 5] +#define UNALIGNED_BH(zz) ((zz) & 0x01f) + +static +void fallbackSort ( UInt32* fmap, + UInt32* eclass, + UInt32* bhtab, + Int32 nblock, + Int32 verb ) +{ + Int32 ftab[257]; + Int32 ftabCopy[256]; + Int32 H, i, j, k, l, r, cc, cc1; + Int32 nNotDone; + Int32 nBhtab; + UChar* eclass8 = (UChar*)eclass; + + /*-- + Initial 1-char radix sort to generate + initial fmap and initial BH bits. + --*/ + if (verb >= 4) + VPrintf0 ( " bucket sorting ...\n" ); + for (i = 0; i < 257; i++) ftab[i] = 0; + for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; + for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; + for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; + + for (i = 0; i < nblock; i++) { + j = eclass8[i]; + k = ftab[j] - 1; + ftab[j] = k; + fmap[k] = i; + } + + nBhtab = 2 + (nblock / 32); + for (i = 0; i < nBhtab; i++) bhtab[i] = 0; + for (i = 0; i < 256; i++) SET_BH(ftab[i]); + + /*-- + Inductively refine the buckets. Kind-of an + "exponential radix sort" (!), inspired by the + Manber-Myers suffix array construction algorithm. + --*/ + + /*-- set sentinel bits for block-end detection --*/ + for (i = 0; i < 32; i++) { + SET_BH(nblock + 2*i); + CLEAR_BH(nblock + 2*i + 1); + } + + /*-- the log(N) loop --*/ + H = 1; + while (1) { + + if (verb >= 4) + VPrintf1 ( " depth %6d has ", H ); + + j = 0; + for (i = 0; i < nblock; i++) { + if (ISSET_BH(i)) j = i; + k = fmap[i] - H; if (k < 0) k += nblock; + eclass[k] = j; + } + + nNotDone = 0; + r = -1; + while (1) { + + /*-- find the next non-singleton bucket --*/ + k = r + 1; + while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (ISSET_BH(k)) { + while (WORD_BH(k) == 0xffffffff) k += 32; + while (ISSET_BH(k)) k++; + } + l = k - 1; + if (l >= nblock) break; + while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; + if (!ISSET_BH(k)) { + while (WORD_BH(k) == 0x00000000) k += 32; + while (!ISSET_BH(k)) k++; + } + r = k - 1; + if (r >= nblock) break; + + /*-- now [l, r] bracket current bucket --*/ + if (r > l) { + nNotDone += (r - l + 1); + fallbackQSort3 ( fmap, eclass, l, r ); + + /*-- scan bucket and generate header bits-- */ + cc = -1; + for (i = l; i <= r; i++) { + cc1 = eclass[fmap[i]]; + if (cc != cc1) { SET_BH(i); cc = cc1; }; + } + } + } + + if (verb >= 4) + VPrintf1 ( "%6d unresolved strings\n", nNotDone ); + + H *= 2; + if (H > nblock || nNotDone == 0) break; + } + + /*-- + Reconstruct the original block in + eclass8 [0 .. nblock-1], since the + previous phase destroyed it. + --*/ + if (verb >= 4) + VPrintf0 ( " reconstructing block ...\n" ); + j = 0; + for (i = 0; i < nblock; i++) { + while (ftabCopy[j] == 0) j++; + ftabCopy[j]--; + eclass8[fmap[i]] = (UChar)j; + } + AssertH ( j < 256, 1005 ); +} + +#undef SET_BH +#undef CLEAR_BH +#undef ISSET_BH +#undef WORD_BH +#undef UNALIGNED_BH + + +/*---------------------------------------------*/ +/*--- The main, O(N^2 log(N)) sorting ---*/ +/*--- algorithm. Faster for "normal" ---*/ +/*--- non-repetitive blocks. ---*/ +/*---------------------------------------------*/ + +/*---------------------------------------------*/ +static +__inline__ +Bool mainGtU ( UInt32 i1, + UInt32 i2, + UChar* block, + UInt16* quadrant, + UInt32 nblock, + Int32* budget ) +{ + Int32 k; + UChar c1, c2; + UInt16 s1, s2; + + AssertD ( i1 != i2, "mainGtU" ); + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 9 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 10 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 11 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + /* 12 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + i1++; i2++; + + k = nblock + 8; + + do { + /* 1 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 2 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 3 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 4 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 5 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 6 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 7 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + /* 8 */ + c1 = block[i1]; c2 = block[i2]; + if (c1 != c2) return (c1 > c2); + s1 = quadrant[i1]; s2 = quadrant[i2]; + if (s1 != s2) return (s1 > s2); + i1++; i2++; + + if (i1 >= nblock) i1 -= nblock; + if (i2 >= nblock) i2 -= nblock; + + k -= 8; + (*budget)--; + } + while (k >= 0); + + return False; +} + + +/*---------------------------------------------*/ +/*-- + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. +--*/ +static +Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + +static +void mainSimpleSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 lo, + Int32 hi, + Int32 d, + Int32* budget ) +{ + Int32 i, j, h, bigN, hp; + UInt32 v; + + bigN = hi - lo + 1; + if (bigN < 2) return; + + hp = 0; + while (incs[hp] < bigN) hp++; + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (True) { + + /*-- copy 1 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 2 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + /*-- copy 3 --*/ + if (i > hi) break; + v = ptr[i]; + j = i; + while ( mainGtU ( + ptr[j-h]+d, v+d, block, quadrant, nblock, budget + ) ) { + ptr[j] = ptr[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + ptr[j] = v; + i++; + + if (*budget < 0) return; + } + } +} + + +/*---------------------------------------------*/ +/*-- + The following is an implementation of + an elegant 3-way quicksort for strings, + described in a paper "Fast Algorithms for + Sorting and Searching Strings", by Robert + Sedgewick and Jon L. Bentley. +--*/ + +#define mswap(zz1, zz2) \ + { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } + +#define mvswap(zzp1, zzp2, zzn) \ +{ \ + Int32 yyp1 = (zzp1); \ + Int32 yyp2 = (zzp2); \ + Int32 yyn = (zzn); \ + while (yyn > 0) { \ + mswap(ptr[yyp1], ptr[yyp2]); \ + yyp1++; yyp2++; yyn--; \ + } \ +} + +static +__inline__ +UChar mmed3 ( UChar a, UChar b, UChar c ) +{ + UChar t; + if (a > b) { t = a; a = b; b = t; }; + if (b > c) { + b = c; + if (a > b) b = a; + } + return b; +} + +#define mmin(a,b) ((a) < (b)) ? (a) : (b) + +#define mpush(lz,hz,dz) { stackLo[sp] = lz; \ + stackHi[sp] = hz; \ + stackD [sp] = dz; \ + sp++; } + +#define mpop(lz,hz,dz) { sp--; \ + lz = stackLo[sp]; \ + hz = stackHi[sp]; \ + dz = stackD [sp]; } + + +#define mnextsize(az) (nextHi[az]-nextLo[az]) + +#define mnextswap(az,bz) \ + { Int32 tz; \ + tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ + tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ + tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } + + +#define MAIN_QSORT_SMALL_THRESH 20 +#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) +#define MAIN_QSORT_STACK_SIZE 100 + +static +void mainQSort3 ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + Int32 nblock, + Int32 loSt, + Int32 hiSt, + Int32 dSt, + Int32* budget ) +{ + Int32 unLo, unHi, ltLo, gtHi, n, m, med; + Int32 sp, lo, hi, d; + + Int32 stackLo[MAIN_QSORT_STACK_SIZE]; + Int32 stackHi[MAIN_QSORT_STACK_SIZE]; + Int32 stackD [MAIN_QSORT_STACK_SIZE]; + + Int32 nextLo[3]; + Int32 nextHi[3]; + Int32 nextD [3]; + + sp = 0; + mpush ( loSt, hiSt, dSt ); + + while (sp > 0) { + + AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); + + mpop ( lo, hi, d ); + if (hi - lo < MAIN_QSORT_SMALL_THRESH || + d > MAIN_QSORT_DEPTH_THRESH) { + mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); + if (*budget < 0) return; + continue; + } + + med = (Int32) + mmed3 ( block[ptr[ lo ]+d], + block[ptr[ hi ]+d], + block[ptr[ (lo+hi)>>1 ]+d] ); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (True) { + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unLo]+d]) - med; + if (n == 0) { + mswap(ptr[unLo], ptr[ltLo]); + ltLo++; unLo++; continue; + }; + if (n > 0) break; + unLo++; + } + while (True) { + if (unLo > unHi) break; + n = ((Int32)block[ptr[unHi]+d]) - med; + if (n == 0) { + mswap(ptr[unHi], ptr[gtHi]); + gtHi--; unHi--; continue; + }; + if (n < 0) break; + unHi--; + } + if (unLo > unHi) break; + mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; + } + + AssertD ( unHi == unLo-1, "mainQSort3(2)" ); + + if (gtHi < ltLo) { + mpush(lo, hi, d+1 ); + continue; + } + + n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); + m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; + nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; + nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; + + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); + if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); + + AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); + AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); + + mpush (nextLo[0], nextHi[0], nextD[0]); + mpush (nextLo[1], nextHi[1], nextD[1]); + mpush (nextLo[2], nextHi[2], nextD[2]); + } +} + +#undef mswap +#undef mvswap +#undef mpush +#undef mpop +#undef mmin +#undef mnextsize +#undef mnextswap +#undef MAIN_QSORT_SMALL_THRESH +#undef MAIN_QSORT_DEPTH_THRESH +#undef MAIN_QSORT_STACK_SIZE + + +/*---------------------------------------------*/ +/* Pre: + nblock > N_OVERSHOOT + block32 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)block32) [0 .. nblock-1] holds block + ptr exists for [0 .. nblock-1] + + Post: + ((UChar*)block32) [0 .. nblock-1] holds block + All other areas of block32 destroyed + ftab [0 .. 65536 ] destroyed + ptr [0 .. nblock-1] holds sorted order + if (*budget < 0), sorting was abandoned +*/ + +#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) +#define SETMASK (1 << 21) +#define CLEARMASK (~(SETMASK)) + +static +void mainSort ( UInt32* ptr, + UChar* block, + UInt16* quadrant, + UInt32* ftab, + Int32 nblock, + Int32 verb, + Int32* budget ) +{ + Int32 i, j, k, ss, sb; + Int32 runningOrder[256]; + Bool bigDone[256]; + Int32 copyStart[256]; + Int32 copyEnd [256]; + UChar c1; + Int32 numQSorted; + UInt16 s; + if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); + + /*-- set up the 2-byte frequency table --*/ + for (i = 65536; i >= 0; i--) ftab[i] = 0; + + j = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + quadrant[i-1] = 0; + j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); + ftab[j]++; + quadrant[i-2] = 0; + j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); + ftab[j]++; + quadrant[i-3] = 0; + j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); + ftab[j]++; + } + for (; i >= 0; i--) { + quadrant[i] = 0; + j = (j >> 8) | ( ((UInt16)block[i]) << 8); + ftab[j]++; + } + + /*-- (emphasises close relationship of block & quadrant) --*/ + for (i = 0; i < BZ_N_OVERSHOOT; i++) { + block [nblock+i] = block[i]; + quadrant[nblock+i] = 0; + } + + if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); + + /*-- Complete the initial radix sort --*/ + for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; + + s = block[0] << 8; + i = nblock-1; + for (; i >= 3; i -= 4) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + s = (s >> 8) | (block[i-1] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-1; + s = (s >> 8) | (block[i-2] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-2; + s = (s >> 8) | (block[i-3] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i-3; + } + for (; i >= 0; i--) { + s = (s >> 8) | (block[i] << 8); + j = ftab[s] -1; + ftab[s] = j; + ptr[j] = i; + } + + /*-- + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + --*/ + for (i = 0; i <= 255; i++) { + bigDone [i] = False; + runningOrder[i] = i; + } + + { + Int32 vv; + Int32 h = 1; + do h = 3 * h + 1; while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { + runningOrder[j] = runningOrder[j-h]; + j = j - h; + if (j <= (h - 1)) goto zero; + } + zero: + runningOrder[j] = vv; + } + } while (h != 1); + } + + /*-- + The main sorting loop. + --*/ + + numQSorted = 0; + + for (i = 0; i <= 255; i++) { + + /*-- + Process big buckets, starting with the least full. + Basically this is a 3-step process in which we call + mainQSort3 to sort the small buckets [ss, j], but + also make a big effort to avoid the calls if we can. + --*/ + ss = runningOrder[i]; + + /*-- + Step 1: + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j], for j != ss. + Hopefully previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + --*/ + for (j = 0; j <= 255; j++) { + if (j != ss) { + sb = (ss << 8) + j; + if ( ! (ftab[sb] & SETMASK) ) { + Int32 lo = ftab[sb] & CLEARMASK; + Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; + if (hi > lo) { + if (verb >= 4) + VPrintf4 ( " qsort [0x%x, 0x%x] " + "done %d this %d\n", + ss, j, numQSorted, hi - lo + 1 ); + mainQSort3 ( + ptr, block, quadrant, nblock, + lo, hi, BZ_N_RADIX, budget + ); + numQSorted += (hi - lo + 1); + if (*budget < 0) return; + } + } + ftab[sb] |= SETMASK; + } + } + + AssertH ( !bigDone[ss], 1006 ); + + /*-- + Step 2: + Now scan this big bucket [ss] so as to synthesise the + sorted order for small buckets [t, ss] for all t, + including, magically, the bucket [ss,ss] too. + This will avoid doing Real Work in subsequent Step 1's. + --*/ + { + for (j = 0; j <= 255; j++) { + copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; + copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; + } + for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyStart[c1]++ ] = k; + } + for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { + k = ptr[j]-1; if (k < 0) k += nblock; + c1 = block[k]; + if (!bigDone[c1]) + ptr[ copyEnd[c1]-- ] = k; + } + } + + AssertH ( (copyStart[ss]-1 == copyEnd[ss]) + || + /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. + Necessity for this case is demonstrated by compressing + a sequence of approximately 48.5 million of character + 251; 1.0.0/1.0.1 will then die here. */ + (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), + 1007 ) + + for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; + + /*-- + Step 3: + The [ss] big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + + The quadrant array provides a way to incrementally + cache sort orderings, as they appear, so as to + make subsequent comparisons in fullGtU() complete + faster. For repetitive blocks this makes a big + difference (but not big enough to be able to avoid + the fallback sorting mechanism, exponential radix sort). + + The precise meaning is: at all times: + + for 0 <= i < nblock and 0 <= j <= nblock + + if block[i] != block[j], + + then the relative values of quadrant[i] and + quadrant[j] are meaningless. + + else { + if quadrant[i] < quadrant[j] + then the string starting at i lexicographically + precedes the string starting at j + + else if quadrant[i] > quadrant[j] + then the string starting at j lexicographically + precedes the string starting at i + + else + the relative ordering of the strings starting + at i and j has not yet been determined. + } + --*/ + bigDone[ss] = True; + + if (i < 255) { + Int32 bbStart = ftab[ss << 8] & CLEARMASK; + Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; + Int32 shifts = 0; + + while ((bbSize >> shifts) > 65534) shifts++; + + for (j = bbSize-1; j >= 0; j--) { + Int32 a2update = ptr[bbStart + j]; + UInt16 qVal = (UInt16)(j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZ_N_OVERSHOOT) + quadrant[a2update + nblock] = qVal; + } + AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); + } + + } + + if (verb >= 4) + VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", + nblock, numQSorted, nblock - numQSorted ); +} + +#undef BIGFREQ +#undef SETMASK +#undef CLEARMASK + + +/*---------------------------------------------*/ +/* Pre: + nblock > 0 + arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] + ((UChar*)arr2) [0 .. nblock-1] holds block + arr1 exists for [0 .. nblock-1] + + Post: + ((UChar*)arr2) [0 .. nblock-1] holds block + All other areas of block destroyed + ftab [ 0 .. 65536 ] destroyed + arr1 [0 .. nblock-1] holds sorted order +*/ +void BZ2_blockSort ( EState* s ) +{ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt32* ftab = s->ftab; + Int32 nblock = s->nblock; + Int32 verb = s->verbosity; + Int32 wfact = s->workFactor; + UInt16* quadrant; + Int32 budget; + Int32 budgetInit; + Int32 i; + + if (nblock < 10000) { + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } else { + /* Calculate the location for quadrant, remembering to get + the alignment right. Assumes that &(block[0]) is at least + 2-byte aligned -- this should be ok since block is really + the first section of arr2. + */ + i = nblock+BZ_N_OVERSHOOT; + if (i & 1) i++; + quadrant = (UInt16*)(&(block[i])); + + /* (wfact-1) / 3 puts the default-factor-30 + transition point at very roughly the same place as + with v0.1 and v0.9.0. + Not that it particularly matters any more, since the + resulting compressed stream is now the same regardless + of whether or not we use the main sort or fallback sort. + */ + if (wfact < 1 ) wfact = 1; + if (wfact > 100) wfact = 100; + budgetInit = nblock * ((wfact-1) / 3); + budget = budgetInit; + + mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); + if (verb >= 3) + VPrintf3 ( " %d work, %d block, ratio %5.2f\n", + budgetInit - budget, + nblock, + (float)(budgetInit - budget) / + (float)(nblock==0 ? 1 : nblock) ); + if (budget < 0) { + if (verb >= 2) + VPrintf0 ( " too repetitive; using fallback" + " sorting algorithm\n" ); + fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); + } + } + + s->origPtr = -1; + for (i = 0; i < s->nblock; i++) + if (ptr[i] == 0) + { s->origPtr = i; break; }; + + AssertH( s->origPtr != -1, 1003 ); +} + + +/*-------------------------------------------------------------*/ +/*--- end blocksort.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/lib/bzip2/bzlib_compress.c b/lib/bzip2/bzlib_compress.c new file mode 100644 index 0000000000..c8da1c72e9 --- /dev/null +++ b/lib/bzip2/bzlib_compress.c @@ -0,0 +1,714 @@ + +/*-------------------------------------------------------------*/ +/*--- Compression machinery (not incl block sorting) ---*/ +/*--- compress.c ---*/ +/*-------------------------------------------------------------*/ + +/*-- + This file is a part of bzip2 and/or libbzip2, a program and + library for lossless, block-sorting data compression. + + Copyright (C) 1996-2002 Julian R Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Julian Seward, Cambridge, UK. + jseward@acm.org + bzip2/libbzip2 version 1.0.6 of 6 September 2010 + Copyright (C) 1996-2010 Julian Seward <jseward@bzip.org> + + This program is based on (at least) the work of: + Mike Burrows + David Wheeler + Peter Fenwick + Alistair Moffat + Radford Neal + Ian H. Witten + Robert Sedgewick + Jon L. Bentley + + For more information on these sources, see the manual. +--*/ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- changed setting of nGroups in sendMTFValues() + so as to do a bit better on small files +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Bit stream I/O ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +void BZ2_bsInitWrite ( EState* s ) +{ + s->bsLive = 0; + s->bsBuff = 0; +} + + +/*---------------------------------------------------*/ +static +void bsFinishWrite ( EState* s ) +{ + while (s->bsLive > 0) { + s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24); + s->numZ++; + s->bsBuff <<= 8; + s->bsLive -= 8; + } +} + + +/*---------------------------------------------------*/ +#define bsNEEDW(nz) \ +{ \ + while (s->bsLive >= 8) { \ + s->zbits[s->numZ] \ + = (UChar)(s->bsBuff >> 24); \ + s->numZ++; \ + s->bsBuff <<= 8; \ + s->bsLive -= 8; \ + } \ +} + + +/*---------------------------------------------------*/ +static +__inline__ +void bsW ( EState* s, Int32 n, UInt32 v ) +{ + bsNEEDW ( n ); + s->bsBuff |= (v << (32 - s->bsLive - n)); + s->bsLive += n; +} + + +/*---------------------------------------------------*/ +static +void bsPutUInt32 ( EState* s, UInt32 u ) +{ + bsW ( s, 8, (u >> 24) & 0xffL ); + bsW ( s, 8, (u >> 16) & 0xffL ); + bsW ( s, 8, (u >> 8) & 0xffL ); + bsW ( s, 8, u & 0xffL ); +} + + +/*---------------------------------------------------*/ +static +void bsPutUChar ( EState* s, UChar c ) +{ + bsW( s, 8, (UInt32)c ); +} + + +/*---------------------------------------------------*/ +/*--- The back end proper ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +static +void makeMaps_e ( EState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->unseqToSeq[i] = s->nInUse; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +static +void generateMTFValues ( EState* s ) +{ + UChar yy[256]; + Int32 i, j; + Int32 zPend; + Int32 wr; + Int32 EOB; + + /* + After sorting (eg, here), + s->arr1 [ 0 .. s->nblock-1 ] holds sorted order, + and + ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] + holds the original block data. + + The first thing to do is generate the MTF values, + and put them in + ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ]. + Because there are strictly fewer or equal MTF values + than block values, ptr values in this area are overwritten + with MTF values only when they are no longer needed. + + The final compressed bitstream is generated into the + area starting at + (UChar*) (&((UChar*)s->arr2)[s->nblock]) + + These storage aliases are set up in bzCompressInit(), + except for the last one, which is arranged in + compressBlock(). + */ + UInt32* ptr = s->ptr; + UChar* block = s->block; + UInt16* mtfv = s->mtfv; + + makeMaps_e ( s ); + EOB = s->nInUse+1; + + for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0; + + wr = 0; + zPend = 0; + for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i; + + for (i = 0; i < s->nblock; i++) { + UChar ll_i; + AssertD ( wr <= i, "generateMTFValues(1)" ); + j = ptr[i]-1; if (j < 0) j += s->nblock; + ll_i = s->unseqToSeq[block[j]]; + AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" ); + + if (yy[0] == ll_i) { + zPend++; + } else { + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + { + register UChar rtmp; + register UChar* ryy_j; + register UChar rll_i; + rtmp = yy[1]; + yy[1] = yy[0]; + ryy_j = &(yy[1]); + rll_i = ll_i; + while ( rll_i != rtmp ) { + register UChar rtmp2; + ryy_j++; + rtmp2 = rtmp; + rtmp = *ryy_j; + *ryy_j = rtmp2; + }; + yy[0] = rtmp; + j = ryy_j - &(yy[0]); + mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++; + } + + } + } + + if (zPend > 0) { + zPend--; + while (True) { + if (zPend & 1) { + mtfv[wr] = BZ_RUNB; wr++; + s->mtfFreq[BZ_RUNB]++; + } else { + mtfv[wr] = BZ_RUNA; wr++; + s->mtfFreq[BZ_RUNA]++; + } + if (zPend < 2) break; + zPend = (zPend - 2) / 2; + }; + zPend = 0; + } + + mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++; + + s->nMTF = wr; +} + + +/*---------------------------------------------------*/ +#define BZ_LESSER_ICOST 0 +#define BZ_GREATER_ICOST 15 + +static +void sendMTFValues ( EState* s ) +{ + Int32 v, t, i, j, gs, ge, totc, bt, bc, iter; + Int32 nSelectors, alphaSize, minLen, maxLen, selCtr; + Int32 nGroups, nBytes; + + /*-- + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + is a global since the decoder also needs it. + + Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + are also globals only used in this proc. + Made global to keep stack frame size small. + --*/ + + + UInt16 cost[BZ_N_GROUPS]; + Int32 fave[BZ_N_GROUPS]; + + UInt16* mtfv = s->mtfv; + + if (s->verbosity >= 3) + VPrintf3( " %d in block, %d after MTF & 1-2 coding, " + "%d+2 syms in use\n", + s->nblock, s->nMTF, s->nInUse ); + + alphaSize = s->nInUse+2; + for (t = 0; t < BZ_N_GROUPS; t++) + for (v = 0; v < alphaSize; v++) + s->len[t][v] = BZ_GREATER_ICOST; + + /*--- Decide how many coding tables to use ---*/ + AssertH ( s->nMTF > 0, 3001 ); + if (s->nMTF < 200) nGroups = 2; else + if (s->nMTF < 600) nGroups = 3; else + if (s->nMTF < 1200) nGroups = 4; else + if (s->nMTF < 2400) nGroups = 5; else + nGroups = 6; + + /*--- Generate an initial set of coding tables ---*/ + { + Int32 nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = s->nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs-1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize-1) { + ge++; + aFreq += s->mtfFreq[ge]; + } + + if (ge > gs + && nPart != nGroups && nPart != 1 + && ((nGroups-nPart) % 2 == 1)) { + aFreq -= s->mtfFreq[ge]; + ge--; + } + + if (s->verbosity >= 3) + VPrintf5( " initial group %d, [%d .. %d], " + "has %d syms (%4.1f%%)\n", + nPart, gs, ge, aFreq, + (100.0 * (float)aFreq) / (float)(s->nMTF) ); + + for (v = 0; v < alphaSize; v++) + if (v >= gs && v <= ge) + s->len[nPart-1][v] = BZ_LESSER_ICOST; else + s->len[nPart-1][v] = BZ_GREATER_ICOST; + + nPart--; + gs = ge+1; + remF -= aFreq; + } + } + + /*--- + Iterate up to BZ_N_ITERS times to improve the tables. + ---*/ + for (iter = 0; iter < BZ_N_ITERS; iter++) { + + for (t = 0; t < nGroups; t++) fave[t] = 0; + + for (t = 0; t < nGroups; t++) + for (v = 0; v < alphaSize; v++) + s->rfreq[t][v] = 0; + + /*--- + Set up an auxiliary length table which is used to fast-track + the common case (nGroups == 6). + ---*/ + if (nGroups == 6) { + for (v = 0; v < alphaSize; v++) { + s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v]; + s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v]; + s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v]; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (True) { + + /*--- Set group start & end marks. --*/ + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + + /*-- + Calculate the cost of this group as coded + by each of the coding tables. + --*/ + for (t = 0; t < nGroups; t++) cost[t] = 0; + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + register UInt32 cost01, cost23, cost45; + register UInt16 icv; + cost01 = cost23 = cost45 = 0; + +# define BZ_ITER(nn) \ + icv = mtfv[gs+(nn)]; \ + cost01 += s->len_pack[icv][0]; \ + cost23 += s->len_pack[icv][1]; \ + cost45 += s->len_pack[icv][2]; \ + + BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4); + BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9); + BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14); + BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19); + BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24); + BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29); + BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34); + BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39); + BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44); + BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49); + +# undef BZ_ITER + + cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16; + cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16; + cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16; + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + UInt16 icv = mtfv[i]; + for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv]; + } + } + + /*-- + Find the coding table which is best for this group, + and record its identity in the selector table. + --*/ + bc = 999999999; bt = -1; + for (t = 0; t < nGroups; t++) + if (cost[t] < bc) { bc = cost[t]; bt = t; }; + totc += bc; + fave[bt]++; + s->selector[nSelectors] = bt; + nSelectors++; + + /*-- + Increment the symbol frequencies for the selected table. + --*/ + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + +# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++ + + BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4); + BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9); + BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14); + BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19); + BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24); + BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29); + BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34); + BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39); + BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44); + BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49); + +# undef BZ_ITUR + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) + s->rfreq[bt][ mtfv[i] ]++; + } + + gs = ge+1; + } + if (s->verbosity >= 3) { + VPrintf2 ( " pass %d: size is %d, grp uses are ", + iter+1, totc/8 ); + for (t = 0; t < nGroups; t++) + VPrintf1 ( "%d ", fave[t] ); + VPrintf0 ( "\n" ); + } + + /*-- + Recompute the tables based on the accumulated frequencies. + --*/ + /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See + comment in huffman.c for details. */ + for (t = 0; t < nGroups; t++) + BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), + alphaSize, 17 /*20*/ ); + } + + + AssertH( nGroups < 8, 3002 ); + AssertH( nSelectors < 32768 && + nSelectors <= (2 + (900000 / BZ_G_SIZE)), + 3003 ); + + + /*--- Compute MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) pos[i] = i; + for (i = 0; i < nSelectors; i++) { + ll_i = s->selector[i]; + j = 0; + tmp = pos[j]; + while ( ll_i != tmp ) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + }; + pos[0] = tmp; + s->selectorMtf[i] = j; + } + }; + + /*--- Assign actual codes for the tables. --*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + AssertH ( !(maxLen > 17 /*20*/ ), 3004 ); + AssertH ( !(minLen < 1), 3005 ); + BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), + minLen, maxLen, alphaSize ); + } + + /*--- Transmit the mapping table. ---*/ + { + Bool inUse16[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = False; + for (j = 0; j < 16; j++) + if (s->inUse[i * 16 + j]) inUse16[i] = True; + } + + nBytes = s->numZ; + for (i = 0; i < 16; i++) + if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0); + + for (i = 0; i < 16; i++) + if (inUse16[i]) + for (j = 0; j < 16; j++) { + if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0); + } + + if (s->verbosity >= 3) + VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes ); + } + + /*--- Now the selectors. ---*/ + nBytes = s->numZ; + bsW ( s, 3, nGroups ); + bsW ( s, 15, nSelectors ); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1); + bsW(s,1,0); + } + if (s->verbosity >= 3) + VPrintf1( "selectors %d, ", s->numZ-nBytes ); + + /*--- Now the coding tables. ---*/ + nBytes = s->numZ; + + for (t = 0; t < nGroups; t++) { + Int32 curr = s->len[t][0]; + bsW ( s, 5, curr ); + for (i = 0; i < alphaSize; i++) { + while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ }; + while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ }; + bsW ( s, 1, 0 ); + } + } + + if (s->verbosity >= 3) + VPrintf1 ( "code lengths %d, ", s->numZ-nBytes ); + + /*--- And finally, the block data proper ---*/ + nBytes = s->numZ; + selCtr = 0; + gs = 0; + while (True) { + if (gs >= s->nMTF) break; + ge = gs + BZ_G_SIZE - 1; + if (ge >= s->nMTF) ge = s->nMTF-1; + AssertH ( s->selector[selCtr] < nGroups, 3006 ); + + if (nGroups == 6 && 50 == ge-gs+1) { + /*--- fast track the common case ---*/ + UInt16 mtfv_i; + UChar* s_len_sel_selCtr + = &(s->len[s->selector[selCtr]][0]); + Int32* s_code_sel_selCtr + = &(s->code[s->selector[selCtr]][0]); + +# define BZ_ITAH(nn) \ + mtfv_i = mtfv[gs+(nn)]; \ + bsW ( s, \ + s_len_sel_selCtr[mtfv_i], \ + s_code_sel_selCtr[mtfv_i] ) + + BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4); + BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9); + BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14); + BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19); + BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24); + BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29); + BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34); + BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39); + BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44); + BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49); + +# undef BZ_ITAH + + } else { + /*--- slow version which correctly handles all situations ---*/ + for (i = gs; i <= ge; i++) { + bsW ( s, + s->len [s->selector[selCtr]] [mtfv[i]], + s->code [s->selector[selCtr]] [mtfv[i]] ); + } + } + + + gs = ge+1; + selCtr++; + } + AssertH( selCtr == nSelectors, 3007 ); + + if (s->verbosity >= 3) + VPrintf1( "codes %d\n", s->numZ-nBytes ); + else /* squash compiler 'used but not set' warning */ + nBytes = nBytes; +} + + +/*---------------------------------------------------*/ +void BZ2_compressBlock ( EState* s, Bool is_last_block ) +{ + if (s->nblock > 0) { + + BZ_FINALISE_CRC ( s->blockCRC ); + s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31); + s->combinedCRC ^= s->blockCRC; + if (s->blockNo > 1) s->numZ = 0; + + if (s->verbosity >= 2) + VPrintf4( " block %d: crc = 0x%08x, " + "combined CRC = 0x%08x, size = %d\n", + s->blockNo, s->blockCRC, s->combinedCRC, s->nblock ); + + BZ2_blockSort ( s ); + } + + s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]); + + /*-- If this is the first block, create the stream header. --*/ + if (s->blockNo == 1) { + BZ2_bsInitWrite ( s ); + bsPutUChar ( s, BZ_HDR_B ); + bsPutUChar ( s, BZ_HDR_Z ); + bsPutUChar ( s, BZ_HDR_h ); + bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) ); + } + + if (s->nblock > 0) { + + bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 ); + bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 ); + bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 ); + + /*-- Now the block's CRC, so it is in a known place. --*/ + bsPutUInt32 ( s, s->blockCRC ); + + /*-- + Now a single bit indicating (non-)randomisation. + As of version 0.9.5, we use a better sorting algorithm + which makes randomisation unnecessary. So always set + the randomised bit to 'no'. Of course, the decoder + still needs to be able to handle randomised blocks + so as to maintain backwards compatibility with + older versions of bzip2. + --*/ + bsW(s,1,0); + + bsW ( s, 24, s->origPtr ); + generateMTFValues ( s ); + sendMTFValues ( s ); + } + + + /*-- If this is the last block, add the stream trailer. --*/ + if (is_last_block) { + + bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 ); + bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 ); + bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 ); + bsPutUInt32 ( s, s->combinedCRC ); + if (s->verbosity >= 2) + VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC ); + bsFinishWrite ( s ); + } +} + + +/*-------------------------------------------------------------*/ +/*--- end compress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index b50d105161..d56e1b11f0 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -56,7 +56,6 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(GENERIC_SPI_FLASH, "spi-flash"), COMPAT(MAXIM_98095_CODEC, "maxim,max98095-codec"), COMPAT(SAMSUNG_EXYNOS5_I2C, "samsung,exynos5-hsi2c"), - COMPAT(SANDBOX_LCD_SDL, "sandbox,lcd-sdl"), COMPAT(SAMSUNG_EXYNOS_SYSMMU, "samsung,sysmmu-v3.3"), COMPAT(INTEL_MICROCODE, "intel,microcode"), COMPAT(MEMORY_SPD, "memory-spd"), diff --git a/lib/time.c b/lib/time.c index f37a6628d6..e9f6861b98 100644 --- a/lib/time.c +++ b/lib/time.c @@ -41,23 +41,6 @@ extern unsigned long __weak timer_read_counter(void); #endif #ifdef CONFIG_TIMER -static int notrace dm_timer_init(void) -{ - struct udevice *dev; - int ret; - - if (!gd->timer) { - ret = uclass_first_device(UCLASS_TIMER, &dev); - if (ret) - return ret; - if (!dev) - return -ENODEV; - gd->timer = dev; - } - - return 0; -} - ulong notrace get_tbclk(void) { int ret; diff --git a/lib/tiny-printf.c b/lib/tiny-printf.c index 403b134cd1..a06abed495 100644 --- a/lib/tiny-printf.c +++ b/lib/tiny-printf.c @@ -82,13 +82,21 @@ int vprintf(const char *fmt, va_list va) num = -(int)num; out('-'); } - for (div = 1000000000; div; div /= 10) - div_out(&num, div); + if (!num) { + out_dgt(0); + } else { + for (div = 1000000000; div; div /= 10) + div_out(&num, div); + } break; case 'x': num = va_arg(va, unsigned int); - for (div = 0x10000000; div; div /= 0x10) - div_out(&num, div); + if (!num) { + out_dgt(0); + } else { + for (div = 0x10000000; div; div /= 0x10) + div_out(&num, div); + } break; case 'c': out((char)(va_arg(va, int))); @@ -108,8 +116,10 @@ int vprintf(const char *fmt, va_list va) w--; while (w-- > 0) putc(lz ? '0' : ' '); - while ((ch = *p++)) - putc(ch); + if (p) { + while ((ch = *p++)) + putc(ch); + } } } diff --git a/test/cmd_repeat.sh b/test/cmd_repeat.sh deleted file mode 100755 index 990e79900f..0000000000 --- a/test/cmd_repeat.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -# Test for U-Boot cli including command repeat - -BASE="$(dirname $0)" -. $BASE/common.sh - -run_test() { - ./${OUTPUT_DIR}/u-boot <<END -setenv ctrlc_ignore y -md 0 - -reset -END -} -check_results() { - echo "Check results" - - grep -q 00000100 ${tmp} || fail "Command did not repeat" -} - -echo "Test CLI repeat" -echo -tmp="$(tempfile)" -build_uboot -run_test >${tmp} -check_results ${tmp} -rm ${tmp} -echo "Test passed" diff --git a/test/command_ut.c b/test/command_ut.c index 926573a395..54bf62b9bc 100644 --- a/test/command_ut.c +++ b/test/command_ut.c @@ -7,9 +7,6 @@ #define DEBUG #include <common.h> -#ifdef CONFIG_SANDBOX -#include <os.h> -#endif static const char test_cmd[] = "setenv list 1\n setenv list ${list}2; " "setenv list ${list}3\0" @@ -20,21 +17,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) printf("%s: Testing commands\n", __func__); run_command("env default -f -a", 0); - /* run a single command */ - run_command("setenv single 1", 0); - assert(!strcmp("1", getenv("single"))); - - /* make sure that compound statements work */ -#ifdef CONFIG_SYS_HUSH_PARSER - run_command("if test -n ${single} ; then setenv check 1; fi", 0); - assert(!strcmp("1", getenv("check"))); - run_command("setenv check", 0); -#endif - - /* commands separated by ; */ - run_command_list("setenv list 1; setenv list ${list}1", -1, 0); - assert(!strcmp("11", getenv("list"))); - /* commands separated by \n */ run_command_list("setenv list 1\n setenv list ${list}1", -1, 0); assert(!strcmp("11", getenv("list"))); @@ -43,11 +25,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) run_command_list("setenv list 1${list}\n", -1, 0); assert(!strcmp("111", getenv("list"))); - /* three commands in a row */ - run_command_list("setenv list 1\n setenv list ${list}2; " - "setenv list ${list}3", -1, 0); - assert(!strcmp("123", getenv("list"))); - /* a command string with \0 in it. Stuff after \0 should be ignored */ run_command("setenv list", 0); run_command_list(test_cmd, sizeof(test_cmd), 0); @@ -66,13 +43,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) assert(run_command_list("false", -1, 0) == 1); assert(run_command_list("echo", -1, 0) == 0); - run_command("setenv foo 'setenv monty 1; setenv python 2'", 0); - run_command("run foo", 0); - assert(getenv("monty") != NULL); - assert(!strcmp("1", getenv("monty"))); - assert(getenv("python") != NULL); - assert(!strcmp("2", getenv("python"))); - #ifdef CONFIG_SYS_HUSH_PARSER run_command("setenv foo 'setenv black 1\nsetenv adder 2'", 0); run_command("run foo", 0); @@ -80,112 +50,6 @@ static int do_ut_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) assert(!strcmp("1", getenv("black"))); assert(getenv("adder") != NULL); assert(!strcmp("2", getenv("adder"))); - - /* Test the 'test' command */ - -#define HUSH_TEST(name, expr, expected_result) \ - run_command("if test " expr " ; then " \ - "setenv " #name "_" #expected_result " y; else " \ - "setenv " #name "_" #expected_result " n; fi", 0); \ - assert(!strcmp(#expected_result, getenv(#name "_" #expected_result))); \ - setenv(#name "_" #expected_result, NULL); - - /* Basic operators */ - HUSH_TEST(streq, "aaa = aaa", y); - HUSH_TEST(streq, "aaa = bbb", n); - - HUSH_TEST(strneq, "aaa != bbb", y); - HUSH_TEST(strneq, "aaa != aaa", n); - - HUSH_TEST(strlt, "aaa < bbb", y); - HUSH_TEST(strlt, "bbb < aaa", n); - - HUSH_TEST(strgt, "bbb > aaa", y); - HUSH_TEST(strgt, "aaa > bbb", n); - - HUSH_TEST(eq, "123 -eq 123", y); - HUSH_TEST(eq, "123 -eq 456", n); - - HUSH_TEST(ne, "123 -ne 456", y); - HUSH_TEST(ne, "123 -ne 123", n); - - HUSH_TEST(lt, "123 -lt 456", y); - HUSH_TEST(lt_eq, "123 -lt 123", n); - HUSH_TEST(lt, "456 -lt 123", n); - - HUSH_TEST(le, "123 -le 456", y); - HUSH_TEST(le_eq, "123 -le 123", y); - HUSH_TEST(le, "456 -le 123", n); - - HUSH_TEST(gt, "456 -gt 123", y); - HUSH_TEST(gt_eq, "123 -gt 123", n); - HUSH_TEST(gt, "123 -gt 456", n); - - HUSH_TEST(ge, "456 -ge 123", y); - HUSH_TEST(ge_eq, "123 -ge 123", y); - HUSH_TEST(ge, "123 -ge 456", n); - - HUSH_TEST(z, "-z \"\"", y); - HUSH_TEST(z, "-z \"aaa\"", n); - - HUSH_TEST(n, "-n \"aaa\"", y); - HUSH_TEST(n, "-n \"\"", n); - - /* Inversion of simple tests */ - HUSH_TEST(streq_inv, "! aaa = aaa", n); - HUSH_TEST(streq_inv, "! aaa = bbb", y); - - HUSH_TEST(streq_inv_inv, "! ! aaa = aaa", y); - HUSH_TEST(streq_inv_inv, "! ! aaa = bbb", n); - - /* Binary operators */ - HUSH_TEST(or_0_0, "aaa != aaa -o bbb != bbb", n); - HUSH_TEST(or_0_1, "aaa != aaa -o bbb = bbb", y); - HUSH_TEST(or_1_0, "aaa = aaa -o bbb != bbb", y); - HUSH_TEST(or_1_1, "aaa = aaa -o bbb = bbb", y); - - HUSH_TEST(and_0_0, "aaa != aaa -a bbb != bbb", n); - HUSH_TEST(and_0_1, "aaa != aaa -a bbb = bbb", n); - HUSH_TEST(and_1_0, "aaa = aaa -a bbb != bbb", n); - HUSH_TEST(and_1_1, "aaa = aaa -a bbb = bbb", y); - - /* Inversion within binary operators */ - HUSH_TEST(or_0_0_inv, "! aaa != aaa -o ! bbb != bbb", y); - HUSH_TEST(or_0_1_inv, "! aaa != aaa -o ! bbb = bbb", y); - HUSH_TEST(or_1_0_inv, "! aaa = aaa -o ! bbb != bbb", y); - HUSH_TEST(or_1_1_inv, "! aaa = aaa -o ! bbb = bbb", n); - - HUSH_TEST(or_0_0_inv_inv, "! ! aaa != aaa -o ! ! bbb != bbb", n); - HUSH_TEST(or_0_1_inv_inv, "! ! aaa != aaa -o ! ! bbb = bbb", y); - HUSH_TEST(or_1_0_inv_inv, "! ! aaa = aaa -o ! ! bbb != bbb", y); - HUSH_TEST(or_1_1_inv_inv, "! ! aaa = aaa -o ! ! bbb = bbb", y); - - setenv("ut_var_nonexistent", NULL); - setenv("ut_var_exists", "1"); - HUSH_TEST(z_varexp_quoted, "-z \"$ut_var_nonexistent\"", y); - HUSH_TEST(z_varexp_quoted, "-z \"$ut_var_exists\"", n); - setenv("ut_var_exists", NULL); - - run_command("setenv ut_var_space \" \"", 0); - assert(!strcmp(getenv("ut_var_space"), " ")); - run_command("setenv ut_var_test $ut_var_space", 0); - assert(!getenv("ut_var_test")); - run_command("setenv ut_var_test \"$ut_var_space\"", 0); - assert(!strcmp(getenv("ut_var_test"), " ")); - run_command("setenv ut_var_test \" 1${ut_var_space}${ut_var_space} 2 \"", 0); - assert(!strcmp(getenv("ut_var_test"), " 1 2 ")); - setenv("ut_var_space", NULL); - setenv("ut_var_test", NULL); - -#ifdef CONFIG_SANDBOX - /* File existence */ - HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", n); - run_command("sb save hostfs - creating_this_file_breaks_uboot_unit_test 0 1", 0); - HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", y); - /* Perhaps this could be replaced by an "rm" shell command one day */ - assert(!os_unlink("creating_this_file_breaks_uboot_unit_test")); - HUSH_TEST(e, "-e hostfs - creating_this_file_breaks_uboot_unit_test", n); -#endif #endif assert(run_command("", 0) == 0); diff --git a/test/dm/Makefile b/test/dm/Makefile index 3ff1b75e6f..d4f3f22e58 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -34,5 +34,6 @@ obj-$(CONFIG_DM_USB) += usb.o obj-$(CONFIG_DM_PMIC) += pmic.o obj-$(CONFIG_DM_REGULATOR) += regulator.o obj-$(CONFIG_TIMER) += timer.o +obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o endif diff --git a/test/dm/video.c b/test/dm/video.c new file mode 100644 index 0000000000..9f5e7fce37 --- /dev/null +++ b/test/dm/video.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2014 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <bzlib.h> +#include <dm.h> +#include <mapmem.h> +#include <os.h> +#include <video.h> +#include <video_console.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> + +/* + * These tests use the standard sandbox frame buffer, the resolution of which + * is defined in the device tree. This only supports 16bpp so the tests only + * test that code path. It would be possible to adjust this fairly easily, + * by adjusting the bpix value in struct sandbox_sdl_plat. However the code + * in sandbox_sdl_sync() would also need to change to handle the different + * surface depth. + */ +DECLARE_GLOBAL_DATA_PTR; + +/* Basic test of the video uclass */ +static int dm_test_video_base(struct unit_test_state *uts) +{ + struct video_priv *priv; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_asserteq(1366, video_get_xsize(dev)); + ut_asserteq(768, video_get_ysize(dev)); + priv = dev_get_uclass_priv(dev); + ut_asserteq(priv->fb_size, 1366 * 768 * 2); + + return 0; +} +DM_TEST(dm_test_video_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/** + * compress_frame_buffer() - Compress the frame buffer and return its size + * + * We want to write tests which perform operations on the video console and + * check that the frame buffer ends up with the correct contents. But it is + * painful to store 'known good' images for comparison with the frame + * buffer. As an alternative, we can compress the frame buffer and check the + * size of the compressed data. This provides a pretty good level of + * certainty and the resulting tests need only check a single value. + * + * @dev: Video device + * @return compressed size of the frame buffer, or -ve on error + */ +static int compress_frame_buffer(struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + uint destlen; + void *dest; + int ret; + + destlen = priv->fb_size; + dest = malloc(priv->fb_size); + if (!dest) + return -ENOMEM; + ret = BZ2_bzBuffToBuffCompress(dest, &destlen, + priv->fb, priv->fb_size, + 3, 0, 0); + free(dest); + if (ret) + return ret; + + return destlen; +} + +/* + * Call this function at any point to halt and show the current display. Be + * sure to run the test with the -l flag. + */ +static void __maybe_unused see_output(void) +{ + video_sync_all(); + while (1); +} + +/* Test text output works on the video console */ +static int dm_test_video_text(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + int i; + +#define WHITE 0xffff +#define SCROLL_LINES 100 + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_asserteq(46, compress_frame_buffer(dev)); + + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_putc_xy(con, 0, 0, 'a'); + ut_asserteq(79, compress_frame_buffer(dev)); + + vidconsole_putc_xy(con, 0, 0, ' '); + ut_asserteq(46, compress_frame_buffer(dev)); + + for (i = 0; i < 20; i++) + vidconsole_putc_xy(con, i * 8, 0, ' ' + i); + ut_asserteq(273, compress_frame_buffer(dev)); + + vidconsole_set_row(con, 0, WHITE); + ut_asserteq(46, compress_frame_buffer(dev)); + + for (i = 0; i < 20; i++) + vidconsole_putc_xy(con, i * 8, 0, ' ' + i); + ut_asserteq(273, compress_frame_buffer(dev)); + + return 0; +} +DM_TEST(dm_test_video_text, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test handling of special characters in the console */ +static int dm_test_video_chars(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very modest \bman\n\t\tand Has much to\b\bto be modest about."; + const char *s; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + for (s = test_string; *s; s++) + vidconsole_put_char(con, *s); + ut_asserteq(466, compress_frame_buffer(dev)); + + return 0; +} +DM_TEST(dm_test_video_chars, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/** + * check_vidconsole_output() - Run a text console test + * + * @uts: Test state + * @rot: Console rotation (0, 90, 180, 270) + * @wrap_size: Expected size of compressed frame buffer for the wrap test + * @scroll_size: Same for the scroll test + * @return 0 on success + */ +static int check_vidconsole_output(struct unit_test_state *uts, int rot, + int wrap_size, int scroll_size) +{ + struct udevice *dev, *con; + struct sandbox_sdl_plat *plat; + int i; + + ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev)); + ut_assert(!device_active(dev)); + plat = dev_get_platdata(dev); + plat->rot = rot; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + ut_asserteq(46, compress_frame_buffer(dev)); + + /* Check display wrap */ + for (i = 0; i < 120; i++) + vidconsole_put_char(con, 'A' + i % 50); + ut_asserteq(wrap_size, compress_frame_buffer(dev)); + + /* Check display scrolling */ + for (i = 0; i < SCROLL_LINES; i++) { + vidconsole_put_char(con, 'A' + i % 50); + vidconsole_put_char(con, '\n'); + } + ut_asserteq(scroll_size, compress_frame_buffer(dev)); + + /* If we scroll enough, the screen becomes blank again */ + for (i = 0; i < SCROLL_LINES; i++) + vidconsole_put_char(con, '\n'); + ut_asserteq(46, compress_frame_buffer(dev)); + + return 0; +} + +/* Test text output through the console uclass */ +static int dm_test_video_context(struct unit_test_state *uts) +{ + return check_vidconsole_output(uts, 0, 788, 453); +} +DM_TEST(dm_test_video_context, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation1(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 1, 1112, 680)); + + return 0; +} +DM_TEST(dm_test_video_rotation1, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation2(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 2, 785, 446)); + + return 0; +} +DM_TEST(dm_test_video_rotation2, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation3(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 3, 1134, 681)); + + return 0; +} +DM_TEST(dm_test_video_rotation3, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Read a file into memory and return a pointer to it */ +static int read_file(struct unit_test_state *uts, const char *fname, + ulong *addrp) +{ + int buf_size = 100000; + ulong addr = 0; + int size, fd; + char *buf; + + buf = map_sysmem(addr, 0); + ut_assert(buf != NULL); + fd = os_open(fname, OS_O_RDONLY); + ut_assert(fd >= 0); + size = os_read(fd, buf, buf_size); + ut_assert(size >= 0); + ut_assert(size < buf_size); + os_close(fd); + *addrp = addr; + + return 0; +} + +/* Test drawing a bitmap file */ +static int dm_test_video_bmp(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1368, compress_frame_buffer(dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test drawing a compressed bitmap file */ +static int dm_test_video_bmp_comp(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1368, compress_frame_buffer(dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp_comp, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/py/.gitignore b/test/py/.gitignore new file mode 100644 index 0000000000..0d20b6487c --- /dev/null +++ b/test/py/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/test/py/README.md b/test/py/README.md new file mode 100644 index 0000000000..8036299d07 --- /dev/null +++ b/test/py/README.md @@ -0,0 +1,300 @@ +# U-Boot pytest suite + +## Introduction + +This tool aims to test U-Boot by executing U-Boot shell commands using the +console interface. A single top-level script exists to execute or attach to the +U-Boot console, run the entire script of tests against it, and summarize the +results. Advantages of this approach are: + +- Testing is performed in the same way a user or script would interact with + U-Boot; there can be no disconnect. +- There is no need to write or embed test-related code into U-Boot itself. + It is asserted that writing test-related code in Python is simpler and more + flexible that writing it all in C. +- It is reasonably simple to interact with U-Boot in this way. + +## Requirements + +The test suite is implemented using pytest. Interaction with the U-Boot console +involves executing some binary and interacting with its stdin/stdout. You will +need to implement various "hook" scripts that are called by the test suite at +the appropriate time. + +On Debian or Debian-like distributions, the following packages are required. +Similar package names should exist in other distributions. + +| Package | Version tested (Ubuntu 14.04) | +| -------------- | ----------------------------- | +| python | 2.7.5-5ubuntu3 | +| python-pytest | 2.5.1-1 | + +The test script supports either: + +- Executing a sandbox port of U-Boot on the local machine as a sub-process, + and interacting with it over stdin/stdout. +- Executing an external "hook" scripts to flash a U-Boot binary onto a + physical board, attach to the board's console stream, and reset the board. + Further details are described later. + +### Using `virtualenv` to provide requirements + +Older distributions (e.g. Ubuntu 10.04) may not provide all the required +packages, or may provide versions that are too old to run the test suite. One +can use the Python `virtualenv` script to locally install more up-to-date +versions of the required packages without interfering with the OS installation. +For example: + +```bash +$ cd /path/to/u-boot +$ sudo apt-get install python python-virtualenv +$ virtualenv venv +$ . ./venv/bin/activate +$ pip install pytest +``` + +## Testing sandbox + +To run the testsuite on the sandbox port (U-Boot built as a native user-space +application), simply execute: + +``` +./test/py/test.py --bd sandbox --build +``` + +The `--bd` option tells the test suite which board type is being tested. This +lets the test suite know which features the board has, and hence exactly what +can be tested. + +The `--build` option tells U-Boot to compile U-Boot. Alternatively, you may +omit this option and build U-Boot yourself, in whatever way you choose, before +running the test script. + +The test script will attach to U-Boot, execute all valid tests for the board, +then print a summary of the test process. A complete log of the test session +will be written to `${build_dir}/test-log.html`. This is best viewed in a web +browser, but may be read directly as plain text, perhaps with the aid of the +`html2text` utility. + +## Command-line options + +- `--board-type`, `--bd`, `-B` set the type of the board to be tested. For + example, `sandbox` or `seaboard`. +- `--board-identity`, `--id` set the identity of the board to be tested. + This allows differentiation between multiple instances of the same type of + physical board that are attached to the same host machine. This parameter is + not interpreted by the test script in any way, but rather is simply passed + to the hook scripts described below, and may be used in any site-specific + way deemed necessary. +- `--build` indicates that the test script should compile U-Boot itself + before running the tests. If using this option, make sure that any + environment variables required by the build process are already set, such as + `$CROSS_COMPILE`. +- `--build-dir` sets the directory containing the compiled U-Boot binaries. + If omitted, this is `${source_dir}/build-${board_type}`. +- `--result-dir` sets the directory to write results, such as log files, + into. If omitted, the build directory is used. +- `--persistent-data-dir` sets the directory used to store persistent test + data. This is test data that may be re-used across test runs, such as file- + system images. + +`pytest` also implements a number of its own command-line options. Please see +`pytest` documentation for complete details. Execute `py.test --version` for +a brief summary. Note that U-Boot's test.py script passes all command-line +arguments directly to `pytest` for processing. + +## Testing real hardware + +The tools and techniques used to interact with real hardware will vary +radically between different host and target systems, and the whims of the user. +For this reason, the test suite does not attempt to directly interact with real +hardware in any way. Rather, it executes a standardized set of "hook" scripts +via `$PATH`. These scripts implement certain actions on behalf of the test +suite. This keeps the test suite simple and isolated from system variances +unrelated to U-Boot features. + +### Hook scripts + +#### Environment variables + +The following environment variables are set when running hook scripts: + +- `UBOOT_BOARD_TYPE` the board type being tested. +- `UBOOT_BOARD_IDENTITY` the board identity being tested, or `na` if none was + specified. +- `UBOOT_SOURCE_DIR` the U-Boot source directory. +- `UBOOT_TEST_PY_DIR` the full path to `test/py/` in the source directory. +- `UBOOT_BUILD_DIR` the U-Boot build directory. +- `UBOOT_RESULT_DIR` the test result directory. +- `UBOOT_PERSISTENT_DATA_DIR` the test peristent data directory. + +#### `u-boot-test-console` + +This script provides access to the U-Boot console. The script's stdin/stdout +should be connected to the board's console. This process should continue to run +indefinitely, until killed. The test suite will run this script in parallel +with all other hooks. + +This script may be implemented e.g. by exec()ing `cu`, `kermit`, `conmux`, etc. + +If you are able to run U-Boot under a hardware simulator such as qemu, then +you would likely spawn that simulator from this script. However, note that +`u-boot-test-reset` may be called multiple times per test script run, and must +cause U-Boot to start execution from scratch each time. Hopefully your +simulator includes a virtual reset button! If not, you can launch the +simulator from `u-boot-test-reset` instead, while arranging for this console +process to always communicate with the current simulator instance. + +#### `u-boot-test-flash` + +Prior to running the test suite against a board, some arrangement must be made +so that the board executes the particular U-Boot binary to be tested. Often, +this involves writing the U-Boot binary to the board's flash ROM. The test +suite calls this hook script for that purpose. + +This script should perform the entire flashing process synchronously; the +script should only exit once flashing is complete, and a board reset will +cause the newly flashed U-Boot binary to be executed. + +It is conceivable that this script will do nothing. This might be useful in +the following cases: + +- Some other process has already written the desired U-Boot binary into the + board's flash prior to running the test suite. +- The board allows U-Boot to be downloaded directly into RAM, and executed + from there. Use of this feature will reduce wear on the board's flash, so + may be preferable if available, and if cold boot testing of U-Boot is not + required. If this feature is used, the `u-boot-test-reset` script should + peform this download, since the board could conceivably be reset multiple + times in a single test run. + +It is up to the user to determine if those situations exist, and to code this +hook script appropriately. + +This script will typically be implemented by calling out to some SoC- or +board-specific vendor flashing utility. + +#### `u-boot-test-reset` + +Whenever the test suite needs to reset the target board, this script is +executed. This is guaranteed to happen at least once, prior to executing the +first test function. If any test fails, the test infra-structure will execute +this script again to restore U-Boot to an operational state before running the +next test function. + +This script will likely be implemented by communicating with some form of +relay or electronic switch attached to the board's reset signal. + +The semantics of this script require that when it is executed, U-Boot will +start running from scratch. If the U-Boot binary to be tested has been written +to flash, pulsing the board's reset signal is likely all this script need do. +However, in some scenarios, this script may perform other actions. For +example, it may call out to some SoC- or board-specific vendor utility in order +to download the U-Boot binary directly into RAM and execute it. This would +avoid the need for `u-boot-test-flash` to actually write U-Boot to flash, thus +saving wear on the flash chip(s). + +### Board-type-specific configuration + +Each board has a different configuration and behaviour. Many of these +differences can be automatically detected by parsing the `.config` file in the +build directory. However, some differences can't yet be handled automatically. + +For each board, an optional Python module `u_boot_board_${board_type}` may exist +to provide board-specific information to the test script. Any global value +defined in these modules is available for use by any test function. The data +contained in these scripts must be purely derived from U-Boot source code. +Hence, these configuration files are part of the U-Boot source tree too. + +### Execution environment configuration + +Each user's hardware setup may enable testing different subsets of the features +implemented by a particular board's configuration of U-Boot. For example, a +U-Boot configuration may support USB device mode and USB Mass Storage, but this +can only be tested if a USB cable is connected between the board and the host +machine running the test script. + +For each board, optional Python modules `u_boot_boardenv_${board_type}` and +`u_boot_boardenv_${board_type}_${board_identity}` may exist to provide +board-specific and board-identity-specific information to the test script. Any +global value defined in these modules is available for use by any test +function. The data contained in these is specific to a particular user's +hardware configuration. Hence, these configuration files are not part of the +U-Boot source tree, and should be installed outside of the source tree. Users +should set `$PYTHONPATH` prior to running the test script to allow these +modules to be loaded. + +### Board module parameter usage + +The test scripts rely on the following variables being defined by the board +module: + +- None at present. + +### U-Boot `.config` feature usage + +The test scripts rely on various U-Boot `.config` features, either directly in +order to test those features, or indirectly in order to query information from +the running U-Boot instance in order to test other features. + +One example is that testing of the `md` command requires knowledge of a RAM +address to use for the test. This data is parsed from the output of the +`bdinfo` command, and hence relies on CONFIG_CMD_BDI being enabled. + +For a complete list of dependencies, please search the test scripts for +instances of: + +- `buildconfig.get(...` +- `@pytest.mark.buildconfigspec(...` + +### Complete invocation example + +Assuming that you have installed the hook scripts into $HOME/ubtest/bin, and +any required environment configuration Python modules into $HOME/ubtest/py, +then you would likely invoke the test script as follows: + +If U-Boot has already been built: + +```bash +PATH=$HOME/ubtest/bin:$PATH \ + PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ + ./test/py/test.py --bd seaboard +``` + +If you want the test script to compile U-Boot for you too, then you likely +need to set `$CROSS_COMPILE` to allow this, and invoke the test script as +follow: + +```bash +CROSS_COMPILE=arm-none-eabi- \ + PATH=$HOME/ubtest/bin:$PATH \ + PYTHONPATH=${HOME}/ubtest/py:${PYTHONPATH} \ + ./test/py/test.py --bd seaboard --build +``` + +## Writing tests + +Please refer to the pytest documentation for details of writing pytest tests. +Details specific to the U-Boot test suite are described below. + +A test fixture named `u_boot_console` should be used by each test function. This +provides the means to interact with the U-Boot console, and retrieve board and +environment configuration information. + +The function `u_boot_console.run_command()` executes a shell command on the +U-Boot console, and returns all output from that command. This allows +validation or interpretation of the command output. This function validates +that certain strings are not seen on the U-Boot console. These include shell +error messages and the U-Boot sign-on message (in order to detect unexpected +board resets). See the source of `u_boot_console_base.py` for a complete list of +"bad" strings. Some test scenarios are expected to trigger these strings. Use +`u_boot_console.disable_check()` to temporarily disable checking for specific +strings. See `test_unknown_cmd.py` for an example. + +Board- and board-environment configuration values may be accessed as sub-fields +of the `u_boot_console.config` object, for example +`u_boot_console.config.ram_base`. + +Build configuration values (from `.config`) may be accessed via the dictionary +`u_boot_console.config.buildconfig`, with keys equal to the Kconfig variable +names. diff --git a/test/py/conftest.py b/test/py/conftest.py new file mode 100644 index 0000000000..e1674dfce0 --- /dev/null +++ b/test/py/conftest.py @@ -0,0 +1,422 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Implementation of pytest run-time hook functions. These are invoked by +# pytest at certain points during operation, e.g. startup, for each executed +# test, at shutdown etc. These hooks perform functions such as: +# - Parsing custom command-line options. +# - Pullilng in user-specified board configuration. +# - Creating the U-Boot console test fixture. +# - Creating the HTML log file. +# - Monitoring each test's results. +# - Implementing custom pytest markers. + +import atexit +import errno +import os +import os.path +import pexpect +import pytest +from _pytest.runner import runtestprotocol +import ConfigParser +import StringIO +import sys + +# Globals: The HTML log file, and the connection to the U-Boot console. +log = None +console = None + +def mkdir_p(path): + '''Create a directory path. + + This includes creating any intermediate/parent directories. Any errors + caused due to already extant directories are ignored. + + Args: + path: The directory path to create. + + Returns: + Nothing. + ''' + + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +def pytest_addoption(parser): + '''pytest hook: Add custom command-line options to the cmdline parser. + + Args: + parser: The pytest command-line parser. + + Returns: + Nothing. + ''' + + parser.addoption('--build-dir', default=None, + help='U-Boot build directory (O=)') + parser.addoption('--result-dir', default=None, + help='U-Boot test result/tmp directory') + parser.addoption('--persistent-data-dir', default=None, + help='U-Boot test persistent generated data directory') + parser.addoption('--board-type', '--bd', '-B', default='sandbox', + help='U-Boot board type') + parser.addoption('--board-identity', '--id', default='na', + help='U-Boot board identity/instance') + parser.addoption('--build', default=False, action='store_true', + help='Compile U-Boot before running tests') + +def pytest_configure(config): + '''pytest hook: Perform custom initialization at startup time. + + Args: + config: The pytest configuration. + + Returns: + Nothing. + ''' + + global log + global console + global ubconfig + + test_py_dir = os.path.dirname(os.path.abspath(__file__)) + source_dir = os.path.dirname(os.path.dirname(test_py_dir)) + + board_type = config.getoption('board_type') + board_type_filename = board_type.replace('-', '_') + + board_identity = config.getoption('board_identity') + board_identity_filename = board_identity.replace('-', '_') + + build_dir = config.getoption('build_dir') + if not build_dir: + build_dir = source_dir + '/build-' + board_type + mkdir_p(build_dir) + + result_dir = config.getoption('result_dir') + if not result_dir: + result_dir = build_dir + mkdir_p(result_dir) + + persistent_data_dir = config.getoption('persistent_data_dir') + if not persistent_data_dir: + persistent_data_dir = build_dir + '/persistent-data' + mkdir_p(persistent_data_dir) + + import multiplexed_log + log = multiplexed_log.Logfile(result_dir + '/test-log.html') + + if config.getoption('build'): + if build_dir != source_dir: + o_opt = 'O=%s' % build_dir + else: + o_opt = '' + cmds = ( + ['make', o_opt, '-s', board_type + '_defconfig'], + ['make', o_opt, '-s', '-j8'], + ) + runner = log.get_runner('make', sys.stdout) + for cmd in cmds: + runner.run(cmd, cwd=source_dir) + runner.close() + + class ArbitraryAttributeContainer(object): + pass + + ubconfig = ArbitraryAttributeContainer() + ubconfig.brd = dict() + ubconfig.env = dict() + + modules = [ + (ubconfig.brd, 'u_boot_board_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' + + board_identity_filename), + ] + for (dict_to_fill, module_name) in modules: + try: + module = __import__(module_name) + except ImportError: + continue + dict_to_fill.update(module.__dict__) + + ubconfig.buildconfig = dict() + + for conf_file in ('.config', 'include/autoconf.mk'): + dot_config = build_dir + '/' + conf_file + if not os.path.exists(dot_config): + raise Exception(conf_file + ' does not exist; ' + + 'try passing --build option?') + + with open(dot_config, 'rt') as f: + ini_str = '[root]\n' + f.read() + ini_sio = StringIO.StringIO(ini_str) + parser = ConfigParser.RawConfigParser() + parser.readfp(ini_sio) + ubconfig.buildconfig.update(parser.items('root')) + + ubconfig.test_py_dir = test_py_dir + ubconfig.source_dir = source_dir + ubconfig.build_dir = build_dir + ubconfig.result_dir = result_dir + ubconfig.persistent_data_dir = persistent_data_dir + ubconfig.board_type = board_type + ubconfig.board_identity = board_identity + + env_vars = ( + 'board_type', + 'board_identity', + 'source_dir', + 'test_py_dir', + 'build_dir', + 'result_dir', + 'persistent_data_dir', + ) + for v in env_vars: + os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v) + + if board_type == 'sandbox': + import u_boot_console_sandbox + console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig) + else: + import u_boot_console_exec_attach + console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) + +def pytest_generate_tests(metafunc): + '''pytest hook: parameterize test functions based on custom rules. + + If a test function takes parameter(s) (fixture names) of the form brd__xxx + or env__xxx, the brd and env configuration dictionaries are consulted to + find the list of values to use for those parameters, and the test is + parametrized so that it runs once for each combination of values. + + Args: + metafunc: The pytest test function. + + Returns: + Nothing. + ''' + + subconfigs = { + 'brd': console.config.brd, + 'env': console.config.env, + } + for fn in metafunc.fixturenames: + parts = fn.split('__') + if len(parts) < 2: + continue + if parts[0] not in subconfigs: + continue + subconfig = subconfigs[parts[0]] + vals = [] + val = subconfig.get(fn, []) + # If that exact name is a key in the data source: + if val: + # ... use the dict value as a single parameter value. + vals = (val, ) + else: + # ... otherwise, see if there's a key that contains a list of + # values to use instead. + vals = subconfig.get(fn + 's', []) + metafunc.parametrize(fn, vals) + +@pytest.fixture(scope='session') +def u_boot_console(request): + '''Generate the value of a test's u_boot_console fixture. + + Args: + request: The pytest request. + + Returns: + The fixture value. + ''' + + return console + +tests_not_run = set() +tests_failed = set() +tests_skipped = set() +tests_passed = set() + +def pytest_itemcollected(item): + '''pytest hook: Called once for each test found during collection. + + This enables our custom result analysis code to see the list of all tests + that should eventually be run. + + Args: + item: The item that was collected. + + Returns: + Nothing. + ''' + + tests_not_run.add(item.name) + +def cleanup(): + '''Clean up all global state. + + Executed (via atexit) once the entire test process is complete. This + includes logging the status of all tests, and the identity of any failed + or skipped tests. + + Args: + None. + + Returns: + Nothing. + ''' + + if console: + console.close() + if log: + log.status_pass('%d passed' % len(tests_passed)) + if tests_skipped: + log.status_skipped('%d skipped' % len(tests_skipped)) + for test in tests_skipped: + log.status_skipped('... ' + test) + if tests_failed: + log.status_fail('%d failed' % len(tests_failed)) + for test in tests_failed: + log.status_fail('... ' + test) + if tests_not_run: + log.status_fail('%d not run' % len(tests_not_run)) + for test in tests_not_run: + log.status_fail('... ' + test) + log.close() +atexit.register(cleanup) + +def setup_boardspec(item): + '''Process any 'boardspec' marker for a test. + + Such a marker lists the set of board types that a test does/doesn't + support. If tests are being executed on an unsupported board, the test is + marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + mark = item.get_marker('boardspec') + if not mark: + return + required_boards = [] + for board in mark.args: + if board.startswith('!'): + if ubconfig.board_type == board[1:]: + pytest.skip('board not supported') + return + else: + required_boards.append(board) + if required_boards and ubconfig.board_type not in required_boards: + pytest.skip('board not supported') + +def setup_buildconfigspec(item): + '''Process any 'buildconfigspec' marker for a test. + + Such a marker lists some U-Boot configuration feature that the test + requires. If tests are being executed on an U-Boot build that doesn't + have the required feature, the test is marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + mark = item.get_marker('buildconfigspec') + if not mark: + return + for option in mark.args: + if not ubconfig.buildconfig.get('config_' + option.lower(), None): + pytest.skip('.config feature not enabled') + +def pytest_runtest_setup(item): + '''pytest hook: Configure (set up) a test item. + + Called once for each test to perform any custom configuration. This hook + is used to skip the test if certain conditions apply. + + Args: + item: The pytest test item. + + Returns: + Nothing. + ''' + + log.start_section(item.name) + setup_boardspec(item) + setup_buildconfigspec(item) + +def pytest_runtest_protocol(item, nextitem): + '''pytest hook: Called to execute a test. + + This hook wraps the standard pytest runtestprotocol() function in order + to acquire visibility into, and record, each test function's result. + + Args: + item: The pytest test item to execute. + nextitem: The pytest test item that will be executed after this one. + + Returns: + A list of pytest reports (test result data). + ''' + + reports = runtestprotocol(item, nextitem=nextitem) + failed = None + skipped = None + for report in reports: + if report.outcome == 'failed': + failed = report + break + if report.outcome == 'skipped': + if not skipped: + skipped = report + + if failed: + tests_failed.add(item.name) + elif skipped: + tests_skipped.add(item.name) + else: + tests_passed.add(item.name) + tests_not_run.remove(item.name) + + try: + if failed: + msg = 'FAILED:\n' + str(failed.longrepr) + log.status_fail(msg) + elif skipped: + msg = 'SKIPPED:\n' + str(skipped.longrepr) + log.status_skipped(msg) + else: + log.status_pass('OK') + except: + # If something went wrong with logging, it's better to let the test + # process continue, which may report other exceptions that triggered + # the logging issue (e.g. console.log wasn't created). Hence, just + # squash the exception. If the test setup failed due to e.g. syntax + # error somewhere else, this won't be seen. However, once that issue + # is fixed, if this exception still exists, it will then be logged as + # part of the test's stdout. + import traceback + print 'Exception occurred while logging runtest status:' + traceback.print_exc() + # FIXME: Can we force a test failure here? + + log.end_section(item.name) + + if failed: + console.cleanup_spawn() + + return reports diff --git a/test/py/multiplexed_log.css b/test/py/multiplexed_log.css new file mode 100644 index 0000000000..50f7b90929 --- /dev/null +++ b/test/py/multiplexed_log.css @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015 Stephen Warren + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +/* + * This provides pretty formatting of the HTML log file, e.g. + * - colored bars beside/above log sections for easily parsed delineation. + * - color highlighting of various messages. + */ + +body { + background-color: black; + color: #ffffff; +} + +pre { + margin-top: 0px; + margin-bottom: 0px; +} + +.implicit { + color: #808080; +} + +.section { + border-style: solid; + border-color: #303030; + border-width: 0px 0px 0px 5px; + padding-left: 5px +} + +.section-header { + background-color: #303030; + margin-left: -5px; + margin-top: 5px; +} + +.section-trailer { + display: none; +} + +.stream { + border-style: solid; + border-color: #303030; + border-width: 0px 0px 0px 5px; + padding-left: 5px +} + +.stream-header { + background-color: #303030; + margin-left: -5px; + margin-top: 5px; +} + +.stream-trailer { + display: none; +} + +.error { + color: #ff0000 +} + +.warning { + color: #ffff00 +} + +.info { + color: #808080 +} + +.action { + color: #8080ff +} + +.status-pass { + color: #00ff00 +} + +.status-skipped { + color: #ffff00 +} + +.status-fail { + color: #ff0000 +} diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py new file mode 100644 index 0000000000..48f2b51de1 --- /dev/null +++ b/test/py/multiplexed_log.py @@ -0,0 +1,515 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Generate an HTML-formatted log file containing multiple streams of data, +# each represented in a well-delineated/-structured fashion. + +import cgi +import os.path +import shutil +import subprocess + +mod_dir = os.path.dirname(os.path.abspath(__file__)) + +class LogfileStream(object): + '''A file-like object used to write a single logical stream of data into + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.''' + + def __init__(self, logfile, name, chained_file): + '''Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream. + chained_file: The file-like object to which all stream data should be + logged to in addition to logfile. Can be None. + + Returns: + Nothing. + ''' + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + + def close(self): + '''Dummy function so that this class is "file-like". + + Args: + None. + + Returns: + Nothing. + ''' + + pass + + def write(self, data, implicit=False): + '''Write data to the log stream. + + Args: + data: The data to write tot he file. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + ''' + + self.logfile.write(self, data, implicit) + if self.chained_file: + self.chained_file.write(data) + + def flush(self): + '''Flush the log stream, to ensure correct log interleaving. + + Args: + None. + + Returns: + Nothing. + ''' + + self.logfile.flush() + if self.chained_file: + self.chained_file.flush() + +class RunAndLog(object): + '''A utility object used to execute sub-processes and log their output to + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.''' + + def __init__(self, logfile, name, chained_file): + '''Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream or sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + Nothing. + ''' + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + + def close(self): + '''Clean up any resources managed by this object.''' + pass + + def run(self, cmd, cwd=None): + '''Run a command as a sub-process, and log the results. + + Args: + cmd: The command to execute. + cwd: The directory to run the command in. Can be None to use the + current directory. + + Returns: + Nothing. + ''' + + msg = "+" + " ".join(cmd) + "\n" + if self.chained_file: + self.chained_file.write(msg) + self.logfile.write(self, msg) + + try: + p = subprocess.Popen(cmd, cwd=cwd, + stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (stdout, stderr) = p.communicate() + output = '' + if stdout: + if stderr: + output += 'stdout:\n' + output += stdout + if stderr: + if stdout: + output += 'stderr:\n' + output += stderr + exit_status = p.returncode + exception = None + except subprocess.CalledProcessError as cpe: + output = cpe.output + exit_status = cpe.returncode + exception = cpe + except Exception as e: + output = '' + exit_status = 0 + exception = e + if output and not output.endswith('\n'): + output += '\n' + if exit_status and not exception: + exception = Exception('Exit code: ' + str(exit_status)) + if exception: + output += str(exception) + '\n' + self.logfile.write(self, output) + if self.chained_file: + self.chained_file.write(output) + if exception: + raise exception + +class SectionCtxMgr(object): + '''A context manager for Python's "with" statement, which allows a certain + portion of test code to be logged to a separate section of the log file. + Objects of this type should be created by factory functions in the Logfile + class rather than directly.''' + + def __init__(self, log, marker): + '''Initialize a new object. + + Args: + log: The Logfile object to log to. + marker: The name of the nested log section. + + Returns: + Nothing. + ''' + + self.log = log + self.marker = marker + + def __enter__(self): + self.log.start_section(self.marker) + + def __exit__(self, extype, value, traceback): + self.log.end_section(self.marker) + +class Logfile(object): + '''Generates an HTML-formatted log file containing multiple streams of + data, each represented in a well-delineated/-structured fashion.''' + + def __init__(self, fn): + '''Initialize a new object. + + Args: + fn: The filename to write to. + + Returns: + Nothing. + ''' + + self.f = open(fn, "wt") + self.last_stream = None + self.blocks = [] + self.cur_evt = 1 + shutil.copy(mod_dir + "/multiplexed_log.css", os.path.dirname(fn)) + self.f.write("""\ +<html> +<head> +<link rel="stylesheet" type="text/css" href="multiplexed_log.css"> +</head> +<body> +<tt> +""") + + def close(self): + '''Close the log file. + + After calling this function, no more data may be written to the log. + + Args: + None. + + Returns: + Nothing. + ''' + + self.f.write("""\ +</tt> +</body> +</html> +""") + self.f.close() + + # The set of characters that should be represented as hexadecimal codes in + # the log file. + _nonprint = ("%" + "".join(chr(c) for c in range(0, 32) if c not in (9, 10)) + + "".join(chr(c) for c in range(127, 256))) + + def _escape(self, data): + '''Render data format suitable for inclusion in an HTML document. + + This includes HTML-escaping certain characters, and translating + control characters to a hexadecimal representation. + + Args: + data: The raw string data to be escaped. + + Returns: + An escaped version of the data. + ''' + + data = data.replace(chr(13), "") + data = "".join((c in self._nonprint) and ("%%%02x" % ord(c)) or + c for c in data) + data = cgi.escape(data) + return data + + def _terminate_stream(self): + '''Write HTML to the log file to terminate the current stream's data. + + Args: + None. + + Returns: + Nothing. + ''' + + self.cur_evt += 1 + if not self.last_stream: + return + self.f.write("</pre>\n") + self.f.write("<div class=\"stream-trailer\" id=\"" + + self.last_stream.name + "\">End stream: " + + self.last_stream.name + "</div>\n") + self.f.write("</div>\n") + self.last_stream = None + + def _note(self, note_type, msg): + '''Write a note or one-off message to the log file. + + Args: + note_type: The type of note. This must be a value supported by the + accompanying multiplexed_log.css. + msg: The note/message to log. + + Returns: + Nothing. + ''' + + self._terminate_stream() + self.f.write("<div class=\"" + note_type + "\">\n<pre>") + self.f.write(self._escape(msg)) + self.f.write("\n</pre></div>\n") + + def start_section(self, marker): + '''Begin a new nested section in the log file. + + Args: + marker: The name of the section that is starting. + + Returns: + Nothing. + ''' + + self._terminate_stream() + self.blocks.append(marker) + blk_path = "/".join(self.blocks) + self.f.write("<div class=\"section\" id=\"" + blk_path + "\">\n") + self.f.write("<div class=\"section-header\" id=\"" + blk_path + + "\">Section: " + blk_path + "</div>\n") + + def end_section(self, marker): + '''Terminate the current nested section in the log file. + + This function validates proper nesting of start_section() and + end_section() calls. If a mismatch is found, an exception is raised. + + Args: + marker: The name of the section that is ending. + + Returns: + Nothing. + ''' + + if (not self.blocks) or (marker != self.blocks[-1]): + raise Exception("Block nesting mismatch: \"%s\" \"%s\"" % + (marker, "/".join(self.blocks))) + self._terminate_stream() + blk_path = "/".join(self.blocks) + self.f.write("<div class=\"section-trailer\" id=\"section-trailer-" + + blk_path + "\">End section: " + blk_path + "</div>\n") + self.f.write("</div>\n") + self.blocks.pop() + + def section(self, marker): + '''Create a temporary section in the log file. + + This function creates a context manager for Python's "with" statement, + which allows a certain portion of test code to be logged to a separate + section of the log file. + + Usage: + with log.section("somename"): + some test code + + Args: + marker: The name of the nested section. + + Returns: + A context manager object. + ''' + + return SectionCtxMgr(self, marker) + + def error(self, msg): + '''Write an error note to the log file. + + Args: + msg: A message describing the error. + + Returns: + Nothing. + ''' + + self._note("error", msg) + + def warning(self, msg): + '''Write an warning note to the log file. + + Args: + msg: A message describing the warning. + + Returns: + Nothing. + ''' + + self._note("warning", msg) + + def info(self, msg): + '''Write an informational note to the log file. + + Args: + msg: An informational message. + + Returns: + Nothing. + ''' + + self._note("info", msg) + + def action(self, msg): + '''Write an action note to the log file. + + Args: + msg: A message describing the action that is being logged. + + Returns: + Nothing. + ''' + + self._note("action", msg) + + def status_pass(self, msg): + '''Write a note to the log file describing test(s) which passed. + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-pass", msg) + + def status_skipped(self, msg): + '''Write a note to the log file describing skipped test(s). + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-skipped", msg) + + def status_fail(self, msg): + '''Write a note to the log file describing failed test(s). + + Args: + msg: A message describing passed test(s). + + Returns: + Nothing. + ''' + + self._note("status-fail", msg) + + def get_stream(self, name, chained_file=None): + '''Create an object to log a single stream's data into the log file. + + This creates a "file-like" object that can be written to in order to + write a single stream's data to the log file. The implementation will + handle any required interleaving of data (from multiple streams) in + the log, in a way that makes it obvious which stream each bit of data + came from. + + Args: + name: The name of the stream. + chained_file: The file-like object to which all stream data should + be logged to in addition to this log. Can be None. + + Returns: + A file-like object. + ''' + + return LogfileStream(self, name, chained_file) + + def get_runner(self, name, chained_file=None): + '''Create an object that executes processes and logs their output. + + Args: + name: The name of this sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + A RunAndLog object. + ''' + + return RunAndLog(self, name, chained_file) + + def write(self, stream, data, implicit=False): + '''Write stream data into the log file. + + This function should only be used by instances of LogfileStream or + RunAndLog. + + Args: + stream: The stream whose data is being logged. + data: The data to log. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + ''' + + if stream != self.last_stream: + self._terminate_stream() + self.f.write("<div class=\"stream\" id=\"%s\">\n" % stream.name) + self.f.write("<div class=\"stream-header\" id=\"" + stream.name + + "\">Stream: " + stream.name + "</div>\n") + self.f.write("<pre>") + if implicit: + self.f.write("<span class=\"implicit\">") + self.f.write(self._escape(data)) + if implicit: + self.f.write("</span>") + self.last_stream = stream + + def flush(self): + '''Flush the log stream, to ensure correct log interleaving. + + Args: + None. + + Returns: + Nothing. + ''' + + self.f.flush() diff --git a/test/py/pytest.ini b/test/py/pytest.ini new file mode 100644 index 0000000000..67e514f420 --- /dev/null +++ b/test/py/pytest.ini @@ -0,0 +1,11 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Static configuration data for pytest. pytest reads this at startup time. + +[pytest] +markers = + boardspec: U-Boot: Describes the set of boards a test can/can't run on. + buildconfigspec: U-Boot: Describes Kconfig/config-header constraints. diff --git a/test/py/test.py b/test/py/test.py new file mode 100755 index 0000000000..9c23898774 --- /dev/null +++ b/test/py/test.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Wrapper script to invoke pytest with the directory name that contains the +# U-Boot tests. + +import os +import os.path +import sys + +# Get rid of argv[0] +sys.argv.pop(0) + +# argv; py.test test_directory_name user-supplied-arguments +args = ["py.test", os.path.dirname(__file__) + "/tests"] +args.extend(sys.argv) + +try: + os.execvp("py.test", args) +except: + # Log full details of any exception for detailed analysis + import traceback + traceback.print_exc() + # Hint to the user that they likely simply haven't installed the required + # dependencies. + print >>sys.stderr, """ +exec(py.test) failed; perhaps you are missing some dependencies? +See test/py/README.md for the list.""" diff --git a/test/py/tests/test_000_version.py b/test/py/tests/test_000_version.py new file mode 100644 index 0000000000..d262f0534e --- /dev/null +++ b/test/py/tests/test_000_version.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# pytest runs tests the order of their module path, which is related to the +# filename containing the test. This file is named such that it is sorted +# first, simply as a very basic sanity check of the functionality of the U-Boot +# command prompt. + +def test_version(u_boot_console): + '''Test that the "version" command prints the U-Boot version.''' + + # "version" prints the U-Boot sign-on message. This is usually considered + # an error, so that any unexpected reboot causes an error. Here, this + # error detection is disabled since the sign-on message is expected. + with u_boot_console.disable_check('main_signon'): + response = u_boot_console.run_command('version') + # Ensure "version" printed what we expected. + u_boot_console.validate_version_string_in_text(response) diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py new file mode 100644 index 0000000000..a3e8dd3033 --- /dev/null +++ b/test/py/tests/test_env.py @@ -0,0 +1,221 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Test operation of shell commands relating to environment variables. + +import pytest + +# FIXME: This might be useful for other tests; +# perhaps refactor it into ConsoleBase or some other state object? +class StateTestEnv(object): + '''Container that represents the state of all U-Boot environment variables. + This enables quick determination of existant/non-existant variable + names. + ''' + + def __init__(self, u_boot_console): + '''Initialize a new StateTestEnv object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + ''' + + self.u_boot_console = u_boot_console + self.get_env() + self.set_var = self.get_non_existent_var() + + def get_env(self): + '''Read all current environment variables from U-Boot. + + Args: + None. + + Returns: + Nothing. + ''' + + response = self.u_boot_console.run_command('printenv') + self.env = {} + for l in response.splitlines(): + if not '=' in l: + continue + (var, value) = l.strip().split('=', 1) + self.env[var] = value + + def get_existent_var(self): + '''Return the name of an environment variable that exists. + + Args: + None. + + Returns: + The name of an environment variable. + ''' + + for var in self.env: + return var + + def get_non_existent_var(self): + '''Return the name of an environment variable that does not exist. + + Args: + None. + + Returns: + The name of an environment variable. + ''' + + n = 0 + while True: + var = 'test_env_' + str(n) + if var not in self.env: + return var + n += 1 + +@pytest.fixture(scope='module') +def state_test_env(u_boot_console): + '''pytest fixture to provide a StateTestEnv object to tests.''' + + return StateTestEnv(u_boot_console) + +def unset_var(state_test_env, var): + '''Unset an environment variable. + + This both executes a U-Boot shell command and updates a StateTestEnv + object. + + Args: + state_test_env: The StateTestEnv object to updata. + var: The variable name to unset. + + Returns: + Nothing. + ''' + + state_test_env.u_boot_console.run_command('setenv %s' % var) + if var in state_test_env.env: + del state_test_env.env[var] + +def set_var(state_test_env, var, value): + '''Set an environment variable. + + This both executes a U-Boot shell command and updates a StateTestEnv + object. + + Args: + state_test_env: The StateTestEnv object to updata. + var: The variable name to set. + value: The value to set the variable to. + + Returns: + Nothing. + ''' + + state_test_env.u_boot_console.run_command('setenv %s "%s"' % (var, value)) + state_test_env.env[var] = value + +def validate_empty(state_test_env, var): + '''Validate that a variable is not set, using U-Boot shell commands. + + Args: + var: The variable name to test. + + Returns: + Nothing. + ''' + + response = state_test_env.u_boot_console.run_command('echo $%s' % var) + assert response == '' + +def validate_set(state_test_env, var, value): + '''Validate that a variable is set, using U-Boot shell commands. + + Args: + var: The variable name to test. + value: The value the variable is expected to have. + + Returns: + Nothing. + ''' + + # echo does not preserve leading, internal, or trailing whitespace in the + # value. printenv does, and hence allows more complete testing. + response = state_test_env.u_boot_console.run_command('printenv %s' % var) + assert response == ('%s=%s' % (var, value)) + +def test_env_echo_exists(state_test_env): + '''Test echoing a variable that exists.''' + + var = state_test_env.get_existent_var() + value = state_test_env.env[var] + validate_set(state_test_env, var, value) + +def test_env_echo_non_existent(state_test_env): + '''Test echoing a variable that doesn't exist.''' + + var = state_test_env.set_var + validate_empty(state_test_env, var) + +def test_env_printenv_non_existent(state_test_env): + '''Test printenv error message for non-existant variables.''' + + var = state_test_env.set_var + c = state_test_env.u_boot_console + with c.disable_check('error_notification'): + response = c.run_command('printenv %s' % var) + assert(response == '## Error: "%s" not defined' % var) + +def test_env_unset_non_existent(state_test_env): + '''Test unsetting a nonexistent variable.''' + + var = state_test_env.get_non_existent_var() + unset_var(state_test_env, var) + validate_empty(state_test_env, var) + +def test_env_set_non_existent(state_test_env): + '''Test set a non-existant variable.''' + + var = state_test_env.set_var + value = 'foo' + set_var(state_test_env, var, value) + validate_set(state_test_env, var, value) + +def test_env_set_existing(state_test_env): + '''Test setting an existant variable.''' + + var = state_test_env.set_var + value = 'bar' + set_var(state_test_env, var, value) + validate_set(state_test_env, var, value) + +def test_env_unset_existing(state_test_env): + '''Test unsetting a variable.''' + + var = state_test_env.set_var + unset_var(state_test_env, var) + validate_empty(state_test_env, var) + +def test_env_expansion_spaces(state_test_env): + '''Test expanding a variable that contains a space in its value.''' + + var_space = None + var_test = None + try: + var_space = state_test_env.get_non_existent_var() + set_var(state_test_env, var_space, ' ') + + var_test = state_test_env.get_non_existent_var() + value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals() + set_var(state_test_env, var_test, value) + value = ' 1 2 ' + validate_set(state_test_env, var_test, value) + finally: + if var_space: + unset_var(state_test_env, var_space) + if var_test: + unset_var(state_test_env, var_test) diff --git a/test/py/tests/test_help.py b/test/py/tests/test_help.py new file mode 100644 index 0000000000..894f3b5f17 --- /dev/null +++ b/test/py/tests/test_help.py @@ -0,0 +1,9 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +def test_help(u_boot_console): + '''Test that the "help" command can be executed.''' + + u_boot_console.run_command('help') diff --git a/test/py/tests/test_hush_if_test.py b/test/py/tests/test_hush_if_test.py new file mode 100644 index 0000000000..cf4c3aeeb7 --- /dev/null +++ b/test/py/tests/test_hush_if_test.py @@ -0,0 +1,154 @@ +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Test operation of the "if" shell command. + +import os +import os.path +import pytest + +# The list of "if test" conditions to test. +subtests = ( + # Base if functionality. + + ('true', True), + ('false', False), + + # Basic operators. + + ('test aaa = aaa', True), + ('test aaa = bbb', False), + + ('test aaa != bbb', True), + ('test aaa != aaa', False), + + ('test aaa < bbb', True), + ('test bbb < aaa', False), + + ('test bbb > aaa', True), + ('test aaa > bbb', False), + + ('test 123 -eq 123', True), + ('test 123 -eq 456', False), + + ('test 123 -ne 456', True), + ('test 123 -ne 123', False), + + ('test 123 -lt 456', True), + ('test 123 -lt 123', False), + ('test 456 -lt 123', False), + + ('test 123 -le 456', True), + ('test 123 -le 123', True), + ('test 456 -le 123', False), + + ('test 456 -gt 123', True), + ('test 123 -gt 123', False), + ('test 123 -gt 456', False), + + ('test 456 -ge 123', True), + ('test 123 -ge 123', True), + ('test 123 -ge 456', False), + + ('test -z ""', True), + ('test -z "aaa"', False), + + ('test -n "aaa"', True), + ('test -n ""', False), + + # Inversion of simple tests. + + ('test ! aaa = aaa', False), + ('test ! aaa = bbb', True), + ('test ! ! aaa = aaa', True), + ('test ! ! aaa = bbb', False), + + # Binary operators. + + ('test aaa != aaa -o bbb != bbb', False), + ('test aaa != aaa -o bbb = bbb', True), + ('test aaa = aaa -o bbb != bbb', True), + ('test aaa = aaa -o bbb = bbb', True), + + ('test aaa != aaa -a bbb != bbb', False), + ('test aaa != aaa -a bbb = bbb', False), + ('test aaa = aaa -a bbb != bbb', False), + ('test aaa = aaa -a bbb = bbb', True), + + # Inversion within binary operators. + + ('test ! aaa != aaa -o ! bbb != bbb', True), + ('test ! aaa != aaa -o ! bbb = bbb', True), + ('test ! aaa = aaa -o ! bbb != bbb', True), + ('test ! aaa = aaa -o ! bbb = bbb', False), + + ('test ! ! aaa != aaa -o ! ! bbb != bbb', False), + ('test ! ! aaa != aaa -o ! ! bbb = bbb', True), + ('test ! ! aaa = aaa -o ! ! bbb != bbb', True), + ('test ! ! aaa = aaa -o ! ! bbb = bbb', True), + + # -z operator. + + ('test -z "$ut_var_nonexistent"', True), + ('test -z "$ut_var_exists"', False), +) + +def exec_hush_if(u_boot_console, expr, result): + '''Execute a shell "if" command, and validate its result.''' + + cmd = 'if ' + expr + '; then echo true; else echo false; fi' + response = u_boot_console.run_command(cmd) + assert response.strip() == str(result).lower() + +@pytest.mark.buildconfigspec('sys_hush_parser') +def test_hush_if_test_setup(u_boot_console): + '''Set up environment variables used during the "if" tests.''' + + u_boot_console.run_command('setenv ut_var_nonexistent') + u_boot_console.run_command('setenv ut_var_exists 1') + +@pytest.mark.buildconfigspec('sys_hush_parser') +@pytest.mark.parametrize('expr,result', subtests) +def test_hush_if_test(u_boot_console, expr, result): + '''Test a single "if test" condition.''' + + exec_hush_if(u_boot_console, expr, result) + +@pytest.mark.buildconfigspec('sys_hush_parser') +def test_hush_if_test_teardown(u_boot_console): + '''Clean up environment variables used during the "if" tests.''' + + u_boot_console.run_command('setenv ut_var_exists') + +@pytest.mark.buildconfigspec('sys_hush_parser') +# We might test this on real filesystems via UMS, DFU, 'save', etc. +# Of those, only UMS currently allows file removal though. +@pytest.mark.boardspec('sandbox') +def test_hush_if_test_host_file_exists(u_boot_console): + '''Test the "if test -e" shell command.''' + + test_file = u_boot_console.config.result_dir + \ + '/creating_this_file_breaks_u_boot_tests' + + try: + os.unlink(test_file) + except: + pass + assert not os.path.exists(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, False) + + try: + with file(test_file, 'wb'): + pass + assert os.path.exists(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, True) + finally: + os.unlink(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, False) diff --git a/test/py/tests/test_md.py b/test/py/tests/test_md.py new file mode 100644 index 0000000000..94603c7df6 --- /dev/null +++ b/test/py/tests/test_md.py @@ -0,0 +1,36 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +import pytest + +@pytest.mark.buildconfigspec('cmd_memory') +def test_md(u_boot_console): + '''Test that md reads memory as expected, and that memory can be modified + using the mw command.''' + + ram_base = u_boot_console.find_ram_base() + addr = '%08x' % ram_base + val = 'a5f09876' + expected_response = addr + ': ' + val + u_boot_console.run_command('mw ' + addr + ' 0 10') + response = u_boot_console.run_command('md ' + addr + ' 10') + assert(not (expected_response in response)) + u_boot_console.run_command('mw ' + addr + ' ' + val) + response = u_boot_console.run_command('md ' + addr + ' 10') + assert(expected_response in response) + +@pytest.mark.buildconfigspec('cmd_memory') +def test_md_repeat(u_boot_console): + '''Test command repeat (via executing an empty command) operates correctly + for "md"; the command must repeat and dump an incrementing address.''' + + ram_base = u_boot_console.find_ram_base() + addr_base = '%08x' % ram_base + words = 0x10 + addr_repeat = '%08x' % (ram_base + (words * 4)) + u_boot_console.run_command('md %s %x' % (addr_base, words)) + response = u_boot_console.run_command('') + expected_response = addr_repeat + ': ' + assert(expected_response in response) diff --git a/test/py/tests/test_sandbox_exit.py b/test/py/tests/test_sandbox_exit.py new file mode 100644 index 0000000000..2aa8eb4abc --- /dev/null +++ b/test/py/tests/test_sandbox_exit.py @@ -0,0 +1,24 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +import pytest +import signal + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('reset') +def test_reset(u_boot_console): + '''Test that the "reset" command exits sandbox process.''' + + u_boot_console.run_command('reset', wait_for_prompt=False) + assert(u_boot_console.validate_exited()) + u_boot_console.ensure_spawned() + +@pytest.mark.boardspec('sandbox') +def test_ctrl_c(u_boot_console): + '''Test that sending SIGINT to sandbox causes it to exit.''' + + u_boot_console.kill(signal.SIGINT) + assert(u_boot_console.validate_exited()) + u_boot_console.ensure_spawned() diff --git a/test/py/tests/test_shell_basics.py b/test/py/tests/test_shell_basics.py new file mode 100644 index 0000000000..719ce611d7 --- /dev/null +++ b/test/py/tests/test_shell_basics.py @@ -0,0 +1,42 @@ +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Test basic shell functionality, such as commands separate by semi-colons. + +def test_shell_execute(u_boot_console): + '''Test any shell command.''' + + response = u_boot_console.run_command('echo hello') + assert response.strip() == 'hello' + +def test_shell_semicolon_two(u_boot_console): + '''Test two shell commands separate by a semi-colon.''' + + cmd = 'echo hello; echo world' + response = u_boot_console.run_command(cmd) + # This validation method ignores the exact whitespace between the strings + assert response.index('hello') < response.index('world') + +def test_shell_semicolon_three(u_boot_console): + '''Test three shell commands separate by a semi-colon, with variable + expansion dependencies between them.''' + + cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \ + 'echo ${list}' + response = u_boot_console.run_command(cmd) + assert response.strip() == '123' + u_boot_console.run_command('setenv list') + +def test_shell_run(u_boot_console): + '''Test the "run" shell command.''' + + u_boot_console.run_command('setenv foo \"setenv monty 1; setenv python 2\"') + u_boot_console.run_command('run foo') + response = u_boot_console.run_command('echo $monty') + assert response.strip() == '1' + response = u_boot_console.run_command('echo $python') + assert response.strip() == '2' + u_boot_console.run_command('setenv foo') + u_boot_console.run_command('setenv monty') + u_boot_console.run_command('setenv python') diff --git a/test/py/tests/test_sleep.py b/test/py/tests/test_sleep.py new file mode 100644 index 0000000000..64f1ddf9a0 --- /dev/null +++ b/test/py/tests/test_sleep.py @@ -0,0 +1,24 @@ +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +import pytest +import time + +def test_sleep(u_boot_console): + '''Test the sleep command, and validate that it sleeps for approximately + the correct amount of time.''' + + # Do this before we time anything, to make sure U-Boot is already running. + # Otherwise, the system boot time is included in the time measurement. + u_boot_console.ensure_spawned() + + # 3s isn't too long, but is enough to cross a few second boundaries. + sleep_time = 3 + tstart = time.time() + u_boot_console.run_command('sleep %d' % sleep_time) + tend = time.time() + elapsed = tend - tstart + delta_to_expected = abs(elapsed - sleep_time) + # 0.25s margin is hopefully enough to account for any system overhead. + assert delta_to_expected < 0.25 diff --git a/test/py/tests/test_ums.py b/test/py/tests/test_ums.py new file mode 100644 index 0000000000..a137221c7a --- /dev/null +++ b/test/py/tests/test_ums.py @@ -0,0 +1,94 @@ +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Test U-Boot's "ums" command. At present, this test only ensures that a UMS +# device can be enumerated by the host/test machine. In the future, this test +# should be enhanced to validate disk IO. + +import os +import pytest +import time + +''' +Note: This test relies on: + +a) boardenv_* to contain configuration values to define which USB ports are +available for testing. Without this, this test will be automatically skipped. +For example: + +env__usb_dev_ports = ( + {'tgt_usb_ctlr': '0', 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0'}, +) + +env__block_devs = ( + {'type': 'mmc', 'id': '0'}, # eMMC; always present + {'type': 'mmc', 'id': '1'}, # SD card; present since I plugged one in +) + +b) udev rules to set permissions on devices nodes, so that sudo is not +required. For example: + +ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666" + +(You may wish to change the group ID instead of setting the permissions wide +open. All that matters is that the user ID running the test can access the +device.) +''' + +def open_ums_device(host_ums_dev_node): + '''Attempt to open a device node, returning either the opened file handle, + or None on any error.''' + + try: + return open(host_ums_dev_node, 'rb') + except: + return None + +def wait_for_ums_device(host_ums_dev_node): + '''Continually attempt to open the device node exported by the "ums" + command, and either return the opened file handle, or raise an exception + after a timeout.''' + + for i in xrange(100): + fh = open_ums_device(host_ums_dev_node) + if fh: + return fh + time.sleep(0.1) + raise Exception('UMS device did not appear') + +def wait_for_ums_device_gone(host_ums_dev_node): + '''Continually attempt to open the device node exported by the "ums" + command, and either return once the device has disappeared, or raise an + exception if it does not before a timeout occurs.''' + + for i in xrange(100): + fh = open_ums_device(host_ums_dev_node) + if not fh: + return + fh.close() + time.sleep(0.1) + raise Exception('UMS device did not disappear') + +@pytest.mark.buildconfigspec('cmd_usb_mass_storage') +def test_ums(u_boot_console, env__usb_dev_port, env__block_devs): + '''Test the "ums" command; the host system must be able to enumerate a UMS + device when "ums" is running, and this device must disappear when "ums" is + aborted.''' + + tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr'] + host_ums_dev_node = env__usb_dev_port['host_ums_dev_node'] + + # We're interested in testing USB device mode on each port, not the cross- + # product of that with each device. So, just pick the first entry in the + # device list here. We'll test each block device somewhere else. + tgt_dev_type = env__block_devs[0]['type'] + tgt_dev_id = env__block_devs[0]['id'] + + cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id) + u_boot_console.run_command('ums 0 mmc 0', wait_for_prompt=False) + fh = wait_for_ums_device(host_ums_dev_node) + fh.read(4096) + fh.close() + u_boot_console.ctrlc() + wait_for_ums_device_gone(host_ums_dev_node) diff --git a/test/py/tests/test_unknown_cmd.py b/test/py/tests/test_unknown_cmd.py new file mode 100644 index 0000000000..2de93e0026 --- /dev/null +++ b/test/py/tests/test_unknown_cmd.py @@ -0,0 +1,14 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +def test_unknown_command(u_boot_console): + '''Test that executing an unknown command causes U-Boot to print an + error.''' + + # The "unknown command" error is actively expected here, + # so error detection for it is disabled. + with u_boot_console.disable_check('unknown_command'): + response = u_boot_console.run_command('non_existent_cmd') + assert('Unknown command \'non_existent_cmd\' - try \'help\'' in response) diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py new file mode 100644 index 0000000000..520f9a9e9f --- /dev/null +++ b/test/py/u_boot_console_base.py @@ -0,0 +1,360 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Common logic to interact with U-Boot via the console. This class provides +# the interface that tests use to execute U-Boot shell commands and wait for +# their results. Sub-classes exist to perform board-type-specific setup +# operations, such as spawning a sub-process for Sandbox, or attaching to the +# serial console of real hardware. + +import multiplexed_log +import os +import pytest +import re +import sys + +# Regexes for text we expect U-Boot to send to the console. +pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}-[^\r\n]*)') +pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}-[^\r\n]*)') +pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ') +pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'') +pattern_error_notification = re.compile('## Error: ') + +class ConsoleDisableCheck(object): + '''Context manager (for Python's with statement) that temporarily disables + the specified console output error check. This is useful when deliberately + executing a command that is known to trigger one of the error checks, in + order to test that the error condition is actually raised. This class is + used internally by ConsoleBase::disable_check(); it is not intended for + direct usage.''' + + def __init__(self, console, check_type): + self.console = console + self.check_type = check_type + + def __enter__(self): + self.console.disable_check_count[self.check_type] += 1 + + def __exit__(self, extype, value, traceback): + self.console.disable_check_count[self.check_type] -= 1 + +class ConsoleBase(object): + '''The interface through which test functions interact with the U-Boot + console. This primarily involves executing shell commands, capturing their + results, and checking for common error conditions. Some common utilities + are also provided too.''' + + def __init__(self, log, config, max_fifo_fill): + '''Initialize a U-Boot console connection. + + Can only usefully be called by sub-classes. + + Args: + log: A mulptiplex_log.Logfile object, to which the U-Boot output + will be logged. + config: A configuration data structure, as built by conftest.py. + max_fifo_fill: The maximum number of characters to send to U-Boot + command-line before waiting for U-Boot to echo the characters + back. For UART-based HW without HW flow control, this value + should be set less than the UART RX FIFO size to avoid + overflow, assuming that U-Boot can't keep up with full-rate + traffic at the baud rate. + + Returns: + Nothing. + ''' + + self.log = log + self.config = config + self.max_fifo_fill = max_fifo_fill + + self.logstream = self.log.get_stream('console', sys.stdout) + + # Array slice removes leading/trailing quotes + self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1] + self.prompt_escaped = re.escape(self.prompt) + self.p = None + self.disable_check_count = { + 'spl_signon': 0, + 'main_signon': 0, + 'unknown_command': 0, + 'error_notification': 0, + } + + self.at_prompt = False + self.at_prompt_logevt = None + self.ram_base = None + + def close(self): + '''Terminate the connection to the U-Boot console. + + This function is only useful once all interaction with U-Boot is + complete. Once this function is called, data cannot be sent to or + received from U-Boot. + + Args: + None. + + Returns: + Nothing. + ''' + + if self.p: + self.p.close() + self.logstream.close() + + def run_command(self, cmd, wait_for_echo=True, send_nl=True, + wait_for_prompt=True): + '''Execute a command via the U-Boot console. + + The command is always sent to U-Boot. + + U-Boot echoes any command back to its output, and this function + typically waits for that to occur. The wait can be disabled by setting + wait_for_echo=False, which is useful e.g. when sending CTRL-C to + interrupt a long-running command such as "ums". + + Command execution is typically triggered by sending a newline + character. This can be disabled by setting send_nl=False, which is + also useful when sending CTRL-C. + + This function typically waits for the command to finish executing, and + returns the console output that it generated. This can be disabled by + setting wait_for_prompt=False, which is useful when invoking a long- + running command such as "ums". + + Args: + cmd: The command to send. + wait_for_each: Boolean indicating whether to wait for U-Boot to + echo the command text back to its output. + send_nl: Boolean indicating whether to send a newline character + after the command string. + wait_for_prompt: Boolean indicating whether to wait for the + command prompt to be sent by U-Boot. This typically occurs + immediately after the command has been executed. + + Returns: + If wait_for_prompt == False: + Nothing. + Else: + The output from U-Boot during command execution. In other + words, the text U-Boot emitted between the point it echod the + command string and emitted the subsequent command prompts. + ''' + + self.ensure_spawned() + + if self.at_prompt and \ + self.at_prompt_logevt != self.logstream.logfile.cur_evt: + self.logstream.write(self.prompt, implicit=True) + + bad_patterns = [] + bad_pattern_ids = [] + if (self.disable_check_count['spl_signon'] == 0 and + self.u_boot_spl_signon): + bad_patterns.append(self.u_boot_spl_signon_escaped) + bad_pattern_ids.append('SPL signon') + if self.disable_check_count['main_signon'] == 0: + bad_patterns.append(self.u_boot_main_signon_escaped) + bad_pattern_ids.append('U-Boot main signon') + if self.disable_check_count['unknown_command'] == 0: + bad_patterns.append(pattern_unknown_command) + bad_pattern_ids.append('Unknown command') + if self.disable_check_count['error_notification'] == 0: + bad_patterns.append(pattern_error_notification) + bad_pattern_ids.append('Error notification') + try: + self.at_prompt = False + if send_nl: + cmd += '\n' + while cmd: + # Limit max outstanding data, so UART FIFOs don't overflow + chunk = cmd[:self.max_fifo_fill] + cmd = cmd[self.max_fifo_fill:] + self.p.send(chunk) + if not wait_for_echo: + continue + chunk = re.escape(chunk) + chunk = chunk.replace('\\\n', '[\r\n]') + m = self.p.expect([chunk] + bad_patterns) + if m != 0: + self.at_prompt = False + raise Exception('Bad pattern found on console: ' + + bad_pattern_ids[m - 1]) + if not wait_for_prompt: + return + m = self.p.expect([self.prompt_escaped] + bad_patterns) + if m != 0: + self.at_prompt = False + raise Exception('Bad pattern found on console: ' + + bad_pattern_ids[m - 1]) + self.at_prompt = True + self.at_prompt_logevt = self.logstream.logfile.cur_evt + # Only strip \r\n; space/TAB might be significant if testing + # indentation. + return self.p.before.strip('\r\n') + except Exception as ex: + self.log.error(str(ex)) + self.cleanup_spawn() + raise + + def ctrlc(self): + '''Send a CTRL-C character to U-Boot. + + This is useful in order to stop execution of long-running synchronous + commands such as "ums". + + Args: + None. + + Returns: + Nothing. + ''' + + self.run_command(chr(3), wait_for_echo=False, send_nl=False) + + def ensure_spawned(self): + '''Ensure a connection to a correctly running U-Boot instance. + + This may require spawning a new Sandbox process or resetting target + hardware, as defined by the implementation sub-class. + + This is an internal function and should not be called directly. + + Args: + None. + + Returns: + Nothing. + ''' + + if self.p: + return + try: + self.at_prompt = False + self.log.action('Starting U-Boot') + self.p = self.get_spawn() + # Real targets can take a long time to scroll large amounts of + # text if LCD is enabled. This value may need tweaking in the + # future, possibly per-test to be optimal. This works for 'help' + # on board 'seaboard'. + self.p.timeout = 30000 + self.p.logfile_read = self.logstream + if self.config.buildconfig.get('CONFIG_SPL', False) == 'y': + self.p.expect([pattern_u_boot_spl_signon]) + self.u_boot_spl_signon = self.p.after + self.u_boot_spl_signon_escaped = re.escape(self.p.after) + else: + self.u_boot_spl_signon = None + self.p.expect([pattern_u_boot_main_signon]) + self.u_boot_main_signon = self.p.after + self.u_boot_main_signon_escaped = re.escape(self.p.after) + build_idx = self.u_boot_main_signon.find(', Build:') + if build_idx == -1: + self.u_boot_version_string = self.u_boot_main_signon + else: + self.u_boot_version_string = self.u_boot_main_signon[:build_idx] + while True: + match = self.p.expect([self.prompt_escaped, + pattern_stop_autoboot_prompt]) + if match == 1: + self.p.send(chr(3)) # CTRL-C + continue + break + self.at_prompt = True + self.at_prompt_logevt = self.logstream.logfile.cur_evt + except Exception as ex: + self.log.error(str(ex)) + self.cleanup_spawn() + raise + + def cleanup_spawn(self): + '''Shut down all interaction with the U-Boot instance. + + This is used when an error is detected prior to re-establishing a + connection with a fresh U-Boot instance. + + This is an internal function and should not be called directly. + + Args: + None. + + Returns: + Nothing. + ''' + + try: + if self.p: + self.p.close() + except: + pass + self.p = None + + def validate_version_string_in_text(self, text): + '''Assert that a command's output includes the U-Boot signon message. + + This is primarily useful for validating the "version" command without + duplicating the signon text regex in a test function. + + Args: + text: The command output text to check. + + Returns: + Nothing. An exception is raised if the validation fails. + ''' + + assert(self.u_boot_version_string in text) + + def disable_check(self, check_type): + '''Temporarily disable an error check of U-Boot's output. + + Create a new context manager (for use with the "with" statement) which + temporarily disables a particular console output error check. + + Args: + check_type: The type of error-check to disable. Valid values may + be found in self.disable_check_count above. + + Returns: + A context manager object. + ''' + + return ConsoleDisableCheck(self, check_type) + + def find_ram_base(self): + '''Find the running U-Boot's RAM location. + + Probe the running U-Boot to determine the address of the first bank + of RAM. This is useful for tests that test reading/writing RAM, or + load/save files that aren't associated with some standard address + typically represented in an environment variable such as + ${kernel_addr_r}. The value is cached so that it only needs to be + actively read once. + + Args: + None. + + Returns: + The address of U-Boot's first RAM bank, as an integer. + ''' + + if self.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': + pytest.skip('bdinfo command not supported') + if self.ram_base == -1: + pytest.skip('Previously failed to find RAM bank start') + if self.ram_base is not None: + return self.ram_base + + with self.log.section('find_ram_base'): + response = self.run_command('bdinfo') + for l in response.split('\n'): + if '-> start' in l: + self.ram_base = int(l.split('=')[1].strip(), 16) + break + if self.ram_base is None: + self.ram_base = -1 + raise Exception('Failed to find RAM bank start in `bdinfo`') + + return self.ram_base diff --git a/test/py/u_boot_console_exec_attach.py b/test/py/u_boot_console_exec_attach.py new file mode 100644 index 0000000000..0ca9e7c178 --- /dev/null +++ b/test/py/u_boot_console_exec_attach.py @@ -0,0 +1,65 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Logic to interact with U-Boot running on real hardware, typically via a +# physical serial port. + +import sys +from u_boot_spawn import Spawn +from u_boot_console_base import ConsoleBase + +class ConsoleExecAttach(ConsoleBase): + '''Represents a physical connection to a U-Boot console, typically via a + serial port. This implementation executes a sub-process to attach to the + console, expecting that the stdin/out of the sub-process will be forwarded + to/from the physical hardware. This approach isolates the test infra- + structure from the user-/installation-specific details of how to + communicate with, and the identity of, serial ports etc.''' + + def __init__(self, log, config): + '''Initialize a U-Boot console connection. + + Args: + log: A multiplexed_log.Logfile instance. + config: A "configuration" object as defined in conftest.py. + + Returns: + Nothing. + ''' + + # The max_fifo_fill value might need tweaking per-board/-SoC? + # 1 would be safe anywhere, but is very slow (a pexpect issue?). + # 16 is a common FIFO size. + # HW flow control would mean this could be infinite. + super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16) + + self.log.action('Flashing U-Boot') + cmd = ['u-boot-test-flash', config.board_type, config.board_identity] + runner = self.log.get_runner(cmd[0], sys.stdout) + runner.run(cmd) + runner.close() + + def get_spawn(self): + '''Connect to a fresh U-Boot instance. + + The target board is reset, so that U-Boot begins running from scratch. + + Args: + None. + + Returns: + A u_boot_spawn.Spawn object that is attached to U-Boot. + ''' + + args = [self.config.board_type, self.config.board_identity] + s = Spawn(['u-boot-test-console'] + args) + + self.log.action('Resetting board') + cmd = ['u-boot-test-reset'] + args + runner = self.log.get_runner(cmd[0], sys.stdout) + runner.run(cmd) + runner.close() + + return s diff --git a/test/py/u_boot_console_sandbox.py b/test/py/u_boot_console_sandbox.py new file mode 100644 index 0000000000..88b137e8c3 --- /dev/null +++ b/test/py/u_boot_console_sandbox.py @@ -0,0 +1,79 @@ +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Logic to interact with the sandbox port of U-Boot, running as a sub-process. + +import time +from u_boot_spawn import Spawn +from u_boot_console_base import ConsoleBase + +class ConsoleSandbox(ConsoleBase): + '''Represents a connection to a sandbox U-Boot console, executed as a sub- + process.''' + + def __init__(self, log, config): + '''Initialize a U-Boot console connection. + + Args: + log: A multiplexed_log.Logfile instance. + config: A "configuration" object as defined in conftest.py. + + Returns: + Nothing. + ''' + + super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024) + + def get_spawn(self): + '''Connect to a fresh U-Boot instance. + + A new sandbox process is created, so that U-Boot begins running from + scratch. + + Args: + None. + + Returns: + A u_boot_spawn.Spawn object that is attached to U-Boot. + ''' + + return Spawn([self.config.build_dir + '/u-boot']) + + def kill(self, sig): + '''Send a specific Unix signal to the sandbox process. + + Args: + sig: The Unix signal to send to the process. + + Returns: + Nothing. + ''' + + self.ensure_spawned() + self.log.action('kill %d' % sig) + self.p.kill(sig) + + def validate_exited(self): + '''Determine whether the sandbox process has exited. + + If required, this function waits a reasonable time for the process to + exit. + + Args: + None. + + Returns: + Boolean indicating whether the process has exited. + ''' + + p = self.p + self.p = None + for i in xrange(100): + ret = not p.isalive() + if ret: + break + time.sleep(0.1) + p.close() + return ret diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py new file mode 100644 index 0000000000..1baee63df2 --- /dev/null +++ b/test/py/u_boot_spawn.py @@ -0,0 +1,174 @@ +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 + +# Logic to spawn a sub-process and interact with its stdio. + +import os +import re +import pty +import signal +import select +import time + +class Timeout(Exception): + '''An exception sub-class that indicates that a timeout occurred.''' + pass + +class Spawn(object): + '''Represents the stdio of a freshly created sub-process. Commands may be + sent to the process, and responses waited for. + ''' + + def __init__(self, args): + '''Spawn (fork/exec) the sub-process. + + Args: + args: array of processs arguments. argv[0] is the command to execute. + + Returns: + Nothing. + ''' + + self.waited = False + self.buf = '' + self.logfile_read = None + self.before = '' + self.after = '' + self.timeout = None + + (self.pid, self.fd) = pty.fork() + if self.pid == 0: + try: + # For some reason, SIGHUP is set to SIG_IGN at this point when + # run under "go" (www.go.cd). Perhaps this happens under any + # background (non-interactive) system? + signal.signal(signal.SIGHUP, signal.SIG_DFL) + os.execvp(args[0], args) + except: + print 'CHILD EXECEPTION:' + import traceback + traceback.print_exc() + finally: + os._exit(255) + + self.poll = select.poll() + self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL) + + def kill(self, sig): + '''Send unix signal "sig" to the child process. + + Args: + sig: The signal number to send. + + Returns: + Nothing. + ''' + + os.kill(self.pid, sig) + + def isalive(self): + '''Determine whether the child process is still running. + + Args: + None. + + Returns: + Boolean indicating whether process is alive. + ''' + + if self.waited: + return False + + w = os.waitpid(self.pid, os.WNOHANG) + if w[0] == 0: + return True + + self.waited = True + return False + + def send(self, data): + '''Send data to the sub-process's stdin. + + Args: + data: The data to send to the process. + + Returns: + Nothing. + ''' + + os.write(self.fd, data) + + def expect(self, patterns): + '''Wait for the sub-process to emit specific data. + + This function waits for the process to emit one pattern from the + supplied list of patterns, or for a timeout to occur. + + Args: + patterns: A list of strings or regex objects that we expect to + see in the sub-process' stdout. + + Returns: + The index within the patterns array of the pattern the process + emitted. + + Notable exceptions: + Timeout, if the process did not emit any of the patterns within + the expected time. + ''' + + for pi in xrange(len(patterns)): + if type(patterns[pi]) == type(''): + patterns[pi] = re.compile(patterns[pi]) + + try: + while True: + earliest_m = None + earliest_pi = None + for pi in xrange(len(patterns)): + pattern = patterns[pi] + m = pattern.search(self.buf) + if not m: + continue + if earliest_m and m.start() > earliest_m.start(): + continue + earliest_m = m + earliest_pi = pi + if earliest_m: + pos = earliest_m.start() + posafter = earliest_m.end() + 1 + self.before = self.buf[:pos] + self.after = self.buf[pos:posafter] + self.buf = self.buf[posafter:] + return earliest_pi + events = self.poll.poll(self.timeout) + if not events: + raise Timeout() + c = os.read(self.fd, 1024) + if not c: + raise EOFError() + if self.logfile_read: + self.logfile_read.write(c) + self.buf += c + finally: + if self.logfile_read: + self.logfile_read.flush() + + def close(self): + '''Close the stdio connection to the sub-process. + + This also waits a reasonable time for the sub-process to stop running. + + Args: + None. + + Returns: + Nothing. + ''' + + os.close(self.fd) + for i in xrange(100): + if not self.isalive(): + break + time.sleep(0.1) diff --git a/tools/logos/denx-comp.bmp b/tools/logos/denx-comp.bmp Binary files differnew file mode 100644 index 0000000000..89d0f471c8 --- /dev/null +++ b/tools/logos/denx-comp.bmp diff --git a/tools/patman/gitutil.py b/tools/patman/gitutil.py index 9e739d89b6..5f1b4f6e76 100644 --- a/tools/patman/gitutil.py +++ b/tools/patman/gitutil.py @@ -328,7 +328,7 @@ def BuildEmailList(in_list, tag=None, alias=None, raise_on_error=True): return result def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, - self_only=False, alias=None, in_reply_to=None): + self_only=False, alias=None, in_reply_to=None, thread=False): """Email a patch series. Args: @@ -342,6 +342,8 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, self_only: True to just email to yourself as a test in_reply_to: If set we'll pass this to git as --in-reply-to. Should be a message ID that this is in reply to. + thread: True to add --thread to git send-email (make + all patches reply to cover-letter or first patch in series) Returns: Git command that was/would be run @@ -400,6 +402,8 @@ def EmailPatches(series, cover_fname, args, dry_run, raise_on_error, cc_fname, cmd = ['git', 'send-email', '--annotate'] if in_reply_to: cmd.append('--in-reply-to="%s"' % in_reply_to) + if thread: + cmd.append('--thread') cmd += to cmd += cc diff --git a/tools/patman/patman.py b/tools/patman/patman.py index 6fe8fe068c..d05c5ff8e1 100755 --- a/tools/patman/patman.py +++ b/tools/patman/patman.py @@ -61,6 +61,8 @@ parser.add_option('--no-check', action='store_false', dest='check_patch', help="Don't check for patch compliance") parser.add_option('--no-tags', action='store_false', dest='process_tags', default=True, help="Don't process subject tags as aliaes") +parser.add_option('-T', '--thread', action='store_true', dest='thread', + default=False, help='Create patches as a single thread') parser.usage += """ @@ -161,7 +163,7 @@ else: if its_a_go: cmd = gitutil.EmailPatches(series, cover_fname, args, options.dry_run, not options.ignore_bad_tags, cc_file, - in_reply_to=options.in_reply_to) + in_reply_to=options.in_reply_to, thread=options.thread) else: print col.Color(col.RED, "Not sending emails due to errors/warnings") |