diff --git a/arch/arm/boards/zii-imx6q-rdu2/board.c b/arch/arm/boards/zii-imx6q-rdu2/board.c index c99f993..e174032 100644 --- a/arch/arm/boards/zii-imx6q-rdu2/board.c +++ b/arch/arm/boards/zii-imx6q-rdu2/board.c @@ -97,44 +97,6 @@ */ late_initcall(rdu2_reset_audio_touchscreen_nfc); -static const struct gpio rdu2_front_panel_usb_gpios[] = { - { - .gpio = IMX_GPIO_NR(3, 19), - .flags = GPIOF_OUT_INIT_LOW, - .label = "usb-emulation", - }, - { - .gpio = IMX_GPIO_NR(3, 20), - .flags = GPIOF_OUT_INIT_HIGH, - .label = "usb-mode1", - }, - { - .gpio = IMX_GPIO_NR(3, 23), - .flags = GPIOF_OUT_INIT_HIGH, - .label = "usb-mode2", - }, -}; - -static int rdu2_enable_front_panel_usb(void) -{ - int ret; - - if (!of_machine_is_compatible("zii,imx6q-zii-rdu2") && - !of_machine_is_compatible("zii,imx6qp-zii-rdu2")) - return 0; - - ret = gpio_request_array(rdu2_front_panel_usb_gpios, - ARRAY_SIZE(rdu2_front_panel_usb_gpios)); - if (ret) { - pr_err("Failed to request RDU2 front panel USB gpios: %s\n", - strerror(-ret)); - - } - - return ret; -} -late_initcall(rdu2_enable_front_panel_usb); - static int rdu2_devices_init(void) { if (!of_machine_is_compatible("zii,imx6q-zii-rdu2") && diff --git a/arch/arm/boards/zii-vf610-dev/board.c b/arch/arm/boards/zii-vf610-dev/board.c index 275d0a4..a8fa1ef 100644 --- a/arch/arm/boards/zii-vf610-dev/board.c +++ b/arch/arm/boards/zii-vf610-dev/board.c @@ -64,11 +64,6 @@ { static const struct gpio signals[] = { { - .gpio = 107, - .flags = GPIOF_OUT_INIT_HIGH, - .label = "soc_sw_rstn", - }, - { .gpio = 98, .flags = GPIOF_IN, .label = "e6352_intn", diff --git a/arch/arm/dts/imx51-zii-rdu1.dts b/arch/arm/dts/imx51-zii-rdu1.dts index 93bb344..c649db4 100644 --- a/arch/arm/dts/imx51-zii-rdu1.dts +++ b/arch/arm/dts/imx51-zii-rdu1.dts @@ -24,6 +24,15 @@ device-path = &spinor, "partname:barebox-environment"; }; }; + + aliases { + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch-eeprom = &switch; + }; }; &ecspi1 { @@ -51,6 +60,10 @@ }; }; +&mdio_gpio { + switch: switch@0 {}; +}; + &uart3 { rave-sp { watchdog { diff --git a/arch/arm/dts/imx6qdl-zii-rdu2.dtsi b/arch/arm/dts/imx6qdl-zii-rdu2.dtsi index f63b5d2..a3f6dbd 100644 --- a/arch/arm/dts/imx6qdl-zii-rdu2.dtsi +++ b/arch/arm/dts/imx6qdl-zii-rdu2.dtsi @@ -52,6 +52,12 @@ aliases { ethernet0 = &fec; ethernet1 = &i210; + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch-eeprom = &switch; }; }; @@ -124,3 +130,39 @@ }; }; }; + +&gpio3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio3_hog>; + + usb-emulation { + gpio-hog; + gpios = <19 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "usb-emulation"; + }; + + usb-mode1 { + gpio-hog; + gpios = <20 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb-mode1"; + }; + + usb-mode2 { + gpio-hog; + gpios = <23 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "usb-mode2"; + }; +}; + +&iomuxc { + pinctrl_gpio3_hog: gpio3hoggrp { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40000038 + MX6QDL_PAD_EIM_D20__GPIO3_IO20 0x40000038 + MX6QDL_PAD_EIM_D23__GPIO3_IO23 0x40000038 + >; + }; +}; diff --git a/arch/arm/dts/imx7d-zii-rpu2.dts b/arch/arm/dts/imx7d-zii-rpu2.dts index 6fba73f..24a6d40 100644 --- a/arch/arm/dts/imx7d-zii-rpu2.dts +++ b/arch/arm/dts/imx7d-zii-rpu2.dts @@ -19,6 +19,15 @@ stdout-path = &uart2; }; + aliases { + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch-eeprom = &switch0; + }; + gpio-leds { compatible = "gpio-leds"; pinctrl-0 = <&pinctrl_leds_debug>; diff --git a/arch/arm/dts/vf610-zii-cfu1.dts b/arch/arm/dts/vf610-zii-cfu1.dts index 80d3f54..1493335 100644 --- a/arch/arm/dts/vf610-zii-cfu1.dts +++ b/arch/arm/dts/vf610-zii-cfu1.dts @@ -8,3 +8,14 @@ #include "vf610-zii-dev.dtsi" +/ { + aliases { + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch-eeprom = &switch0; + }; +}; + diff --git a/arch/arm/dts/vf610-zii-dev-rev-c.dts b/arch/arm/dts/vf610-zii-dev-rev-c.dts index 797b31b..c6341a0 100644 --- a/arch/arm/dts/vf610-zii-dev-rev-c.dts +++ b/arch/arm/dts/vf610-zii-dev-rev-c.dts @@ -46,6 +46,18 @@ #include "vf610-zii-dev.dtsi" +/ { + aliases { + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch0-eeprom = &switch0; + switch1-eeprom = &switch1; + }; +}; + &dspi0 { m25p128@0 { partition@0 { diff --git a/arch/arm/dts/vf610-zii-ssmb-spu3.dts b/arch/arm/dts/vf610-zii-ssmb-spu3.dts index e030109..5b2460c 100644 --- a/arch/arm/dts/vf610-zii-ssmb-spu3.dts +++ b/arch/arm/dts/vf610-zii-ssmb-spu3.dts @@ -3,3 +3,14 @@ #include #include "vf610-zii-dev.dtsi" + +/ { + aliases { + /* + * NVMEM device corresponding to EEPROM attached to + * the switch shared DT node with it, so we use that + * fact to create a desirable naming + */ + switch-eeprom = &switch0; + }; +}; diff --git a/commands/gpio.c b/commands/gpio.c index 08ecc15..951ad2c 100644 --- a/commands/gpio.c +++ b/commands/gpio.c @@ -16,14 +16,35 @@ #include #include -static int do_gpio_get_value(int argc, char *argv[]) +static int get_gpio_and_value(int argc, char *argv[], + int *gpio, int *value) { - int gpio, value; + const int count = value ? 3 : 2; + int ret = 0; - if (argc < 2) + if (argc < count) return COMMAND_ERROR_USAGE; - gpio = simple_strtoul(argv[1], NULL, 0); + *gpio = gpio_find_by_label(argv[1]); + if (*gpio < 0) { + ret = kstrtoint(argv[1], 0, gpio); + if (ret < 0) + return ret; + } + + if (value) + ret = kstrtoint(argv[2], 0, value); + + return ret; +} + +static int do_gpio_get_value(int argc, char *argv[]) +{ + int gpio, value, ret; + + ret = get_gpio_and_value(argc, argv, &gpio, NULL); + if (ret) + return ret; value = gpio_get_value(gpio); if (value < 0) @@ -41,13 +62,11 @@ static int do_gpio_set_value(int argc, char *argv[]) { - int gpio, value; + int gpio, value, ret; - if (argc < 3) - return COMMAND_ERROR_USAGE; - - gpio = simple_strtoul(argv[1], NULL, 0); - value = simple_strtoul(argv[2], NULL, 0); + ret = get_gpio_and_value(argc, argv, &gpio, &value); + if (ret) + return ret; gpio_set_value(gpio, value); @@ -65,10 +84,9 @@ { int gpio, ret; - if (argc < 2) - return COMMAND_ERROR_USAGE; - - gpio = simple_strtoul(argv[1], NULL, 0); + ret = get_gpio_and_value(argc, argv, &gpio, NULL); + if (ret) + return ret; ret = gpio_direction_input(gpio); if (ret) @@ -88,11 +106,9 @@ { int gpio, value, ret; - if (argc < 3) - return COMMAND_ERROR_USAGE; - - gpio = simple_strtoul(argv[1], NULL, 0); - value = simple_strtoul(argv[2], NULL, 0); + ret = get_gpio_and_value(argc, argv, &gpio, &value); + if (ret) + return ret; ret = gpio_direction_output(gpio, value); if (ret) diff --git a/drivers/gpio/gpio-dw.c b/drivers/gpio/gpio-dw.c index f145c01..b81e6a7 100644 --- a/drivers/gpio/gpio-dw.c +++ b/drivers/gpio/gpio-dw.c @@ -199,4 +199,4 @@ { return platform_driver_register(&dwgpio_driver); } -core_initcall(dwgpio_init); +postcore_initcall(dwgpio_init); diff --git a/drivers/gpio/gpio-imx.c b/drivers/gpio/gpio-imx.c index d618e60..2827e11 100644 --- a/drivers/gpio/gpio-imx.c +++ b/drivers/gpio/gpio-imx.c @@ -222,4 +222,4 @@ platform_driver_register(&imx_gpio_driver); return 0; } -core_initcall(imx_gpio_add); +postcore_initcall(imx_gpio_add); diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index b2b3ad3..ef78873 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -183,4 +183,4 @@ platform_driver_register(&mxs_gpio_driver); return 0; } -core_initcall(mxs_gpio_add); +postcore_initcall(mxs_gpio_add); diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 2aff62b..ab35310 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -142,9 +142,8 @@ port->chip.base *= VF610_GPIO_PER_PORT; port->chip.dev = dev; - gpiochip_add(&port->chip); - return 0; + return gpiochip_add(&port->chip); free_port: free(port); @@ -161,4 +160,4 @@ { return platform_driver_register(&vf610_gpio_driver); } -core_initcall(gpio_vf610_init); +postcore_initcall(gpio_vf610_init); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b83a27d..4c7aee4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -91,6 +91,23 @@ return ret; } +int gpio_find_by_label(const char *label) +{ + int i; + + for (i = 0; i < ARCH_NR_GPIOS; i++) { + struct gpio_info *info = &gpio_desc[i]; + + if (!info->requested || !info->chip || !info->label) + continue; + + if (!strcmp(info->label, label)) + return i; + } + + return -ENOENT; +} + void gpio_free(unsigned gpio) { struct gpio_info *gi = gpio_to_desc(gpio); @@ -352,12 +369,12 @@ flags |= GPIOF_ACTIVE_LOW; gpio = gpio_get_num(chip->dev, gpio_num); - if (ret == -EPROBE_DEFER) - return ret; + if (gpio == -EPROBE_DEFER) + return gpio; - if (ret < 0) { + if (gpio < 0) { dev_err(chip->dev, "unable to get gpio %u\n", gpio_num); - return ret; + return gpio; } @@ -382,7 +399,10 @@ else return -EINVAL; - of_property_read_string(np, "line-name", &name); + /* The line-name is optional and if not present the node name is used */ + ret = of_property_read_string(np, "line-name", &name); + if (ret < 0) + name = np->name; return gpio_request_one(gpio, flags, name); } diff --git a/drivers/pinctrl/imx-iomux-v1.c b/drivers/pinctrl/imx-iomux-v1.c index a3f0480..61e8f96 100644 --- a/drivers/pinctrl/imx-iomux-v1.c +++ b/drivers/pinctrl/imx-iomux-v1.c @@ -314,4 +314,4 @@ { return platform_driver_register(&imx_iomux_v1_driver); } -postcore_initcall(imx_iomux_v1_init); +core_initcall(imx_iomux_v1_init); diff --git a/drivers/pinctrl/imx-iomux-v2.c b/drivers/pinctrl/imx-iomux-v2.c index 0c985a6..60b635a 100644 --- a/drivers/pinctrl/imx-iomux-v2.c +++ b/drivers/pinctrl/imx-iomux-v2.c @@ -154,4 +154,4 @@ { return platform_driver_register(&imx_iomux_driver); } -postcore_initcall(imx_iomux_init); +core_initcall(imx_iomux_init); diff --git a/drivers/pinctrl/imx-iomux-v3.c b/drivers/pinctrl/imx-iomux-v3.c index 0ab9704..b2a67fc 100644 --- a/drivers/pinctrl/imx-iomux-v3.c +++ b/drivers/pinctrl/imx-iomux-v3.c @@ -266,4 +266,4 @@ { return platform_driver_register(&imx_iomux_v3_driver); } -postcore_initcall(imx_iomux_v3_init); +core_initcall(imx_iomux_v3_init); diff --git a/drivers/pinctrl/mvebu/armada-370.c b/drivers/pinctrl/mvebu/armada-370.c index 2fd07a7..4fde16a 100644 --- a/drivers/pinctrl/mvebu/armada-370.c +++ b/drivers/pinctrl/mvebu/armada-370.c @@ -415,4 +415,4 @@ { return platform_driver_register(&armada_370_pinctrl_driver); } -postcore_initcall(armada_370_pinctrl_init); +core_initcall(armada_370_pinctrl_init); diff --git a/drivers/pinctrl/mvebu/armada-xp.c b/drivers/pinctrl/mvebu/armada-xp.c index 2657db5..089942d 100644 --- a/drivers/pinctrl/mvebu/armada-xp.c +++ b/drivers/pinctrl/mvebu/armada-xp.c @@ -405,4 +405,4 @@ { return platform_driver_register(&armada_xp_pinctrl_driver); } -postcore_initcall(armada_xp_pinctrl_init); +core_initcall(armada_xp_pinctrl_init); diff --git a/drivers/pinctrl/mvebu/dove.c b/drivers/pinctrl/mvebu/dove.c index 8de01e7..2d9d809 100644 --- a/drivers/pinctrl/mvebu/dove.c +++ b/drivers/pinctrl/mvebu/dove.c @@ -742,4 +742,4 @@ { return platform_driver_register(&dove_pinctrl_driver); } -postcore_initcall(dove_pinctrl_init); +core_initcall(dove_pinctrl_init); diff --git a/drivers/pinctrl/mvebu/kirkwood.c b/drivers/pinctrl/mvebu/kirkwood.c index 4b2618c..a347239 100644 --- a/drivers/pinctrl/mvebu/kirkwood.c +++ b/drivers/pinctrl/mvebu/kirkwood.c @@ -456,4 +456,4 @@ { return platform_driver_register(&kirkwood_pinctrl_driver); } -postcore_initcall(kirkwood_pinctrl_init); +core_initcall(kirkwood_pinctrl_init); diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 0303869..84348a0 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -538,7 +538,7 @@ { return platform_driver_register(&at91_pinctrl_driver); } -postcore_initcall(at91_pinctrl_init); +core_initcall(at91_pinctrl_init); static int at91_gpio_get(struct gpio_chip *chip, unsigned offset) { @@ -718,4 +718,4 @@ { return platform_driver_register(&at91_gpio_driver); } -postcore_initcall(at91_gpio_init); +core_initcall(at91_gpio_init); diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index 479c31a..b48ed2a 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -168,4 +168,4 @@ { return platform_driver_register(&mxs_pinctrl_driver); } -postcore_initcall(mxs_pinctrl_init); +core_initcall(mxs_pinctrl_init); diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 15b74cc..3c581ed 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -167,4 +167,4 @@ { return platform_driver_register(&pcs_driver); } -postcore_initcall(pcs_init); +core_initcall(pcs_init); diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c index a7a75bb..e477280 100644 --- a/drivers/pinctrl/pinctrl-tegra-xusb.c +++ b/drivers/pinctrl/pinctrl-tegra-xusb.c @@ -518,4 +518,4 @@ { return platform_driver_register(&pinctrl_tegra_xusb_driver); } -postcore_initcall(pinctrl_tegra_xusb_init); +core_initcall(pinctrl_tegra_xusb_init); diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c index eaaba9e..337992c 100644 --- a/drivers/pinctrl/pinctrl-tegra20.c +++ b/drivers/pinctrl/pinctrl-tegra20.c @@ -350,4 +350,4 @@ { return platform_driver_register(&pinctrl_tegra20_driver); } -postcore_initcall(pinctrl_tegra20_init); +core_initcall(pinctrl_tegra20_init); diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c index 4b271dd..d9b49c5 100644 --- a/drivers/pinctrl/pinctrl-tegra30.c +++ b/drivers/pinctrl/pinctrl-tegra30.c @@ -935,4 +935,4 @@ { return platform_driver_register(&pinctrl_tegra30_driver); } -postcore_initcall(pinctrl_tegra30_init); +core_initcall(pinctrl_tegra30_init); diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c index a46b0e2..662fa9b 100644 --- a/drivers/pinctrl/pinctrl-vf610.c +++ b/drivers/pinctrl/pinctrl-vf610.c @@ -165,4 +165,4 @@ { return platform_driver_register(&pinctrl_vf610_driver); } -postcore_initcall(pinctrl_vf610_init); +core_initcall(pinctrl_vf610_init); diff --git a/include/gpio.h b/include/gpio.h index e42fa23..38d6ba2 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -92,6 +92,11 @@ return 0; } +static inline int gpio_find_by_label(const char *label) +{ + return -ENOSYS; +} + static inline void gpio_free(unsigned gpio) { } @@ -114,6 +119,7 @@ } #else int gpio_request(unsigned gpio, const char *label); +int gpio_find_by_label(const char *label); void gpio_free(unsigned gpio); int gpio_request_one(unsigned gpio, unsigned long flags, const char *label); int gpio_request_array(const struct gpio *array, size_t num); diff --git a/include/linux/ctype.h b/include/linux/ctype.h index 74fb735..633c386 100644 --- a/include/linux/ctype.h +++ b/include/linux/ctype.h @@ -53,4 +53,13 @@ #define tolower(c) __tolower(c) #define toupper(c) __toupper(c) +/* + * Fast implementation of tolower() for internal usage. Do not use in your + * code. + */ +static inline char _tolower(const char c) +{ + return c | 0x20; +} + #endif diff --git a/include/linux/kernel.h b/include/linux/kernel.h index ab713f2..cc6d6f7 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -269,4 +269,99 @@ #define swap(a, b) \ do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0) + +/* Internal, do not use. */ +int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); +int __must_check _kstrtol(const char *s, unsigned int base, long *res); + +int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); +int __must_check kstrtoll(const char *s, unsigned int base, long long *res); + +/** + * kstrtoul - convert a string to an unsigned long + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. +*/ +static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. + */ + if (sizeof(unsigned long) == sizeof(unsigned long long) && + __alignof__(unsigned long) == __alignof__(unsigned long long)) + return kstrtoull(s, base, (unsigned long long *)res); + else + return _kstrtoul(s, base, res); +} + +/** + * kstrtol - convert a string to a long + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) +{ + /* + * We want to shortcut function call, but + * __builtin_types_compatible_p(long, long long) = 0. + */ + if (sizeof(long) == sizeof(long long) && + __alignof__(long) == __alignof__(long long)) + return kstrtoll(s, base, (long long *)res); + else + return _kstrtol(s, base, res); +} + +int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); +int __must_check kstrtoint(const char *s, unsigned int base, int *res); + +static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) +{ + return kstrtoull(s, base, res); +} + +static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) +{ + return kstrtoll(s, base, res); +} + +static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) +{ + return kstrtouint(s, base, res); +} + +static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) +{ + return kstrtoint(s, base, res); +} + +int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); +int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); +int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); +int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); +int __must_check kstrtobool(const char *s, bool *res); + #endif /* _LINUX_KERNEL_H */ diff --git a/include/linux/string.h b/include/linux/string.h index 3418b4f..90fe1b6 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -122,4 +122,6 @@ return memdup(src, len); } +extern int kstrtobool(const char *s, bool *res); + #endif /* _LINUX_STRING_H_ */ diff --git a/lib/Makefile b/lib/Makefile index 693945f..24dc3d9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,6 +5,7 @@ obj-y += display_options.o obj-y += string.o obj-y += strtox.o +obj-y += kstrtox.o obj-y += vsprintf.o pbl-$(CONFIG_PBL_CONSOLE) += vsprintf.o obj-y += div64.o diff --git a/lib/kstrtox.c b/lib/kstrtox.c new file mode 100644 index 0000000..0e4b590 --- /dev/null +++ b/lib/kstrtox.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Convert integer string representation to an integer. + * If an integer doesn't fit into specified type, -E is returned. + * + * Integer starts with optional sign. + * kstrtou*() functions do not accept sign "-". + * + * Radix 0 means autodetection: leading "0x" implies radix 16, + * leading "0" implies radix 8, otherwise radix is 10. + * Autodetection hints work after optional sign, but not before. + * + * If -E is returned, result is not touched. + */ +#include +#include +#include +#include + +#include "kstrtox.h" + +const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) +{ + if (*base == 0) { + if (s[0] == '0') { + if (_tolower(s[1]) == 'x' && isxdigit(s[2])) + *base = 16; + else + *base = 8; + } else + *base = 10; + } + if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') + s += 2; + return s; +} + +/* + * Convert non-negative integer string representation in explicitly given radix + * to an integer. + * Return number of characters consumed maybe or-ed with overflow bit. + * If overflow occurs, result integer (incorrect) is still returned. + * + * Don't you dare use this function. + */ +unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p) +{ + unsigned long long res; + unsigned int rv; + + res = 0; + rv = 0; + while (1) { + unsigned int c = *s; + unsigned int lc = c | 0x20; /* don't tolower() this line */ + unsigned int val; + + if ('0' <= c && c <= '9') + val = c - '0'; + else if ('a' <= lc && lc <= 'f') + val = lc - 'a' + 10; + else + break; + + if (val >= base) + break; + /* + * Check for overflow only if we are within range of + * it in the max base we support (16) + */ + if (unlikely(res & (~0ull << 60))) { + if (res > div_u64(ULLONG_MAX - val, base)) + rv |= KSTRTOX_OVERFLOW; + } + res = res * base + val; + rv++; + s++; + } + *p = res; + return rv; +} + +static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + unsigned long long _res; + unsigned int rv; + + s = _parse_integer_fixup_radix(s, &base); + rv = _parse_integer(s, base, &_res); + if (rv & KSTRTOX_OVERFLOW) + return -ERANGE; + if (rv == 0) + return -EINVAL; + s += rv; + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + *res = _res; + return 0; +} + +/** + * kstrtoull - convert a string to an unsigned long long + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +int kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + if (s[0] == '+') + s++; + return _kstrtoull(s, base, res); +} +EXPORT_SYMBOL(kstrtoull); + +/** + * kstrtoll - convert a string to a long long + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +int kstrtoll(const char *s, unsigned int base, long long *res) +{ + unsigned long long tmp; + int rv; + + if (s[0] == '-') { + rv = _kstrtoull(s + 1, base, &tmp); + if (rv < 0) + return rv; + if ((long long)-tmp > 0) + return -ERANGE; + *res = -tmp; + } else { + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if ((long long)tmp < 0) + return -ERANGE; + *res = tmp; + } + return 0; +} +EXPORT_SYMBOL(kstrtoll); + +/* Internal, do not use. */ +int _kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtoul); + +/* Internal, do not use. */ +int _kstrtol(const char *s, unsigned int base, long *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(long)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(_kstrtol); + +/** + * kstrtouint - convert a string to an unsigned int + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign, but not a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +int kstrtouint(const char *s, unsigned int base, unsigned int *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(unsigned int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtouint); + +/** + * kstrtoint - convert a string to an int + * @s: The start of the string. The string must be null-terminated, and may also + * include a single newline before its terminating null. The first character + * may also be a plus sign or a minus sign. + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion on success. + * + * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. + * Used as a replacement for the obsolete simple_strtoull. Return code must + * be checked. + */ +int kstrtoint(const char *s, unsigned int base, int *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(int)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtoint); + +int kstrtou16(const char *s, unsigned int base, u16 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou16); + +int kstrtos16(const char *s, unsigned int base, s16 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s16)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos16); + +int kstrtou8(const char *s, unsigned int base, u8 *res) +{ + unsigned long long tmp; + int rv; + + rv = kstrtoull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned long long)(u8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtou8); + +int kstrtos8(const char *s, unsigned int base, s8 *res) +{ + long long tmp; + int rv; + + rv = kstrtoll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (long long)(s8)tmp) + return -ERANGE; + *res = tmp; + return 0; +} +EXPORT_SYMBOL(kstrtos8); + +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL(kstrtobool); \ No newline at end of file diff --git a/lib/kstrtox.h b/lib/kstrtox.h new file mode 100644 index 0000000..3b4637b --- /dev/null +++ b/lib/kstrtox.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LIB_KSTRTOX_H +#define _LIB_KSTRTOX_H + +#define KSTRTOX_OVERFLOW (1U << 31) +const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); +unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); + +#endif