Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1691831 > unrolled thread

Re: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver

Started byJoel Stanley <joel@jms.id.au>
First post2017-07-19 17:10 +0200
Last post2017-07-20 03:30 +0200
Articles 3 — 3 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver Joel Stanley <joel@jms.id.au> - 2017-07-19 17:10 +0200
    Re: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver Guenter Roeck <linux@roeck-us.net> - 2017-07-19 20:20 +0200
      Re: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver Andrew Jeffery <andrew@aj.id.au> - 2017-07-20 03:30 +0200

#1691831 — Re: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver

FromJoel Stanley <joel@jms.id.au>
Date2017-07-19 17:10 +0200
SubjectRe: [RFC PATCH v2 3/3] pmbus: Add MAX31785 driver
Message-ID<u4YLV-1Q5-41@gated-at.bofh.it>
On Tue, Jul 18, 2017 at 1:06 PM, Andrew Jeffery <andrew@aj.id.au> wrote:
> The driver features fan control and basic dual-tachometer support.

Say something about the hardware?

 max31785 is a fancy fan controller with temp measurement, pwm, tach,
breakfast making from Maxim.


>
> The fan control makes use of the new virtual registers exposed by the
> pmbus core, mixing use of the default implementations with some
> overrides via the read/write handlers. FAN_COMMAND_1 on the MAX31785
> breaks the values into bands that depend on the RPM or PWM control mode.
>
> The dual tachometer feature is implemented in hardware with a TACHSEL
> input to indicate the rotor under measurement, and exposed on the bus by
> extending the READ_FAN_SPEED_1 word with two extra bytes*.
> The need to read the non-standard four-byte response leads to a cut-down
> implementation of i2c_smbus_xfer_emulated() included in the driver.
> Further, to expose the second rotor tachometer value to userspace,
> virtual fans are implemented by re-routing the FAN_CONFIG_1_2 register
> from the undefined (in hardware) pages 23-28 to the same register on
> pages 0-5, and similarly re-routing READ_FAN_SPEED_1 requests on 23-28
> to pages 0-5 but extracting the second word of the four-byte response.
>
> * The documentation recommends the slower rotor be associated with
> TACHSEL=0, which is the input used by the controller's closed-loop fan
> management.
>
> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> ---
> v1 -> v2:
>
> * Implement in terms of virtual registers
> * Add support for dual-tachometer readings through 'virtual fans' (unused pages)
>
>  drivers/hwmon/pmbus/Kconfig    |  10 ++
>  drivers/hwmon/pmbus/Makefile   |   1 +
>  drivers/hwmon/pmbus/max31785.c | 372 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 383 insertions(+)
>  create mode 100644 drivers/hwmon/pmbus/max31785.c
>
> diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> index cad1229b7e17..5f2f3c6c7499 100644
> --- a/drivers/hwmon/pmbus/Kconfig
> +++ b/drivers/hwmon/pmbus/Kconfig
> @@ -95,6 +95,16 @@ config SENSORS_MAX20751
>           This driver can also be built as a module. If so, the module will
>           be called max20751.
>
> +config SENSORS_MAX31785
> +       tristate "Maxim MAX31785 and compatibles"
> +       default n
> +       help
> +         If you say yes here you get hardware monitoring support for Maxim
> +         MAX31785.
> +
> +         This driver can also be built as a module. If so, the module will
> +         be called max31785.
> +
>  config SENSORS_MAX34440
>         tristate "Maxim MAX34440 and compatibles"
>         default n
> diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
> index 562132054aaf..4ea548a8af46 100644
> --- a/drivers/hwmon/pmbus/Makefile
> +++ b/drivers/hwmon/pmbus/Makefile
> @@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
>  obj-$(CONFIG_SENSORS_LTC3815)  += ltc3815.o
>  obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
>  obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
> +obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
>  obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
>  obj-$(CONFIG_SENSORS_MAX8688)  += max8688.o
>  obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
> diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
> new file mode 100644
> index 000000000000..1baa961f2eb1
> --- /dev/null
> +++ b/drivers/hwmon/pmbus/max31785.c
> @@ -0,0 +1,372 @@
> +/*
> + * Copyright (C) 2017 IBM Corp.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include "pmbus.h"
> +
> +#define MFR_FAN_CONFIG_DUAL_TACH       BIT(12)
> +#define MFR_FAN_CONFIG_TSFO            BIT(9)
> +#define MFR_FAN_CONFIG_TACHO           BIT(8)
> +
> +#define MAX31785_CAP_DUAL_TACH         BIT(0)
> +
> +struct max31785 {
> +       struct pmbus_driver_info info;
> +
> +       u32 capabilities;
> +};
> +
> +enum max31785_regs {
> +       PMBUS_MFR_FAN_CONFIG            = 0xF1,
> +       PMBUS_MFR_READ_FAN_PWM          = 0xF3,
> +       PMBUS_MFR_FAN_FAULT_LIMIT       = 0xF5,
> +       PMBUS_MFR_FAN_WARN_LIMIT        = 0xF6,
> +       PMBUS_MFR_FAN_PWM_AVG           = 0xF8,
> +};
> +
> +#define to_max31785(_c) container_of(pmbus_get_info(_c), struct max31785, info)
> +
> +static int max31785_read_byte_data(struct i2c_client *client, int page,
> +                                  int reg)
> +{
> +       struct max31785 *chip = to_max31785(client);
> +       int rv = -ENODATA;

I'd drop this.

> +
> +       switch (reg) {
> +       case PMBUS_VOUT_MODE:
> +               if (page < 23)
> +                       return -ENODATA;
> +
> +               return -ENOTSUPP;
> +       case PMBUS_FAN_CONFIG_12:
> +               if (page < 23)
> +                       return -ENODATA;
> +
> +               if (WARN_ON(!(chip->capabilities & MAX31785_CAP_DUAL_TACH)))

Not sure about the WARN_ON here - won't users get spammed every time
someone reads a byte of data? When do we expect this case to happen?

> +                       return -ENOTSUPP;
> +
> +               rv = pmbus_read_byte_data(client, page - 23, reg);

return pmbus_read_byte_data(client, page - 23, reg);
> +               break;
> +       }
> +
> +       return rv;

return -ENODATA;

Or something like ESUPP or EINVAL?

Interestingly max34440_read_byte_data (may) return zero in this case.

> +}
> +
> +static long max31785_read_long_data(struct i2c_client *client, int page,
> +                                   int reg)
> +{
> +       unsigned char cmdbuf[1];
> +       unsigned char rspbuf[4];
> +       s64 rc;

This should be the same type as the function returns.

> +
> +       struct i2c_msg msg[2] = {
> +               {
> +                       .addr = client->addr,
> +                       .flags = 0,
> +                       .len = sizeof(cmdbuf),
> +                       .buf = cmdbuf,
> +               },
> +               {
> +                       .addr = client->addr,
> +                       .flags = I2C_M_RD,
> +                       .len = sizeof(rspbuf),
> +                       .buf = rspbuf,
> +               },
> +       };
> +
> +       cmdbuf[0] = reg;
> +
> +       rc = pmbus_set_page(client, page);
> +       if (rc < 0)
> +               return rc;
> +
> +       rc = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
> +       if (rc < 0)
> +               return rc;
> +
> +       rc = (rspbuf[0] << (0 * 8)) | (rspbuf[1] << (1 * 8)) |
> +            (rspbuf[2] << (2 * 8)) | (rspbuf[3] << (3 * 8));

It's getting late, so I might have confused myself. But can you just do:

rc = le32_to_cpup(rspbuf);

> +
> +       return rc;
> +}
> +
> +static int max31785_get_pwm(struct i2c_client *client, int page)
> +{
> +       int config;
> +       int command;
> +
> +       config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
> +       if (config < 0)
> +               return config;
> +
> +       command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
> +       if (command < 0)
> +               return command;
> +
> +       if (!(config & PB_FAN_1_RPM)) {
> +               if (command >= 0x8000)
> +                       return 0;
> +               else if (command >= 0x2711)
> +                       return 0x2710;

What are the magic numbers?

> +               else
> +                       return command;
> +       }
> +
> +       return 0;
> +}
> +
> +static int max31785_get_pwm_mode(struct i2c_client *client, int page)
> +{
> +       int config;
> +       int command;
> +
> +       config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
> +       if (config < 0)
> +               return config;
> +
> +       command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
> +       if (command < 0)
> +               return command;
> +
> +       if (!(config & PB_FAN_1_RPM)) {
> +               if (command >= 0x8000)
> +                       return 2;
> +               else if (command >= 0x2711)
> +                       return 0;
> +               else
> +                       return 1;
> +       }
> +
> +       return (command >= 0x8000) ? 2 : 1;
> +}
> +
> +static int max31785_read_word_data(struct i2c_client *client, int page,
> +                                  int reg)
> +{
> +       struct max31785 *chip = to_max31785(client);
> +       long rv = -ENODATA;
> +
> +       switch (reg) {
> +       case PMBUS_READ_FAN_SPEED_1:
> +               if (likely(page < 23))
> +                       return -ENODATA;
> +
> +               if (WARN_ON(!(chip->capabilities & MAX31785_CAP_DUAL_TACH)))
> +                       return -ENOTSUPP;
> +
> +               rv = max31785_read_long_data(client, page - 23, reg);
> +               if (rv < 0)
> +                       return rv;
> +
> +               rv = (rv >> 16) & 0xffff;
> +               break;
> +       case PMBUS_VIRT_PWM_1:
> +               rv = max31785_get_pwm(client, page);
> +               rv *= 255;
> +               rv /= 100;
> +               break;
> +       case PMBUS_VIRT_PWM_ENABLE_1:
> +               rv = max31785_get_pwm_mode(client, page);
> +               break;
> +       }
> +
> +       if (rv == -ENODATA && page >= 23)
> +               return -ENXIO;
> +
> +       return rv;
> +}
> +
> +static const int max31785_pwm_modes[] = { 0x7fff, 0x2710, 0xffff };
> +
> +static int max31785_write_word_data(struct i2c_client *client, int page,
> +                                   int reg, u16 word)
> +{
> +       int rv = -ENODATA;
> +
> +       if (page >= 23)
> +               return -ENXIO;
> +
> +       switch (reg) {
> +       case PMBUS_VIRT_PWM_ENABLE_1:
> +               if (word >= ARRAY_SIZE(max31785_pwm_modes))
> +                       return -ENOTSUPP;
> +
> +               rv = pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
> +                                        max31785_pwm_modes[word]);
> +               break;
> +       }
> +
> +       return rv;
> +}
> +
> +static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
> +{
> +       if (page < 23)
> +               return -ENODATA;
> +
> +       return -ENOTSUPP;
> +}
> +
> +static struct pmbus_driver_info max31785_info = {
> +       .pages = 23,
> +
> +       .write_word_data = max31785_write_word_data,
> +       .read_byte_data = max31785_read_byte_data,
> +       .read_word_data = max31785_read_word_data,
> +       .write_byte = max31785_write_byte,
> +
> +       /* RPM */
> +       .format[PSC_FAN] = direct,
> +       .m[PSC_FAN] = 1,
> +       .b[PSC_FAN] = 0,
> +       .R[PSC_FAN] = 0,
> +       /* PWM */
> +       .format[PSC_PWM] = direct,
> +       .m[PSC_PWM] = 1,
> +       .b[PSC_PWM] = 0,
> +       .R[PSC_PWM] = 2,
> +       .func[0] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +       .func[1] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +       .func[2] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +       .func[3] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +       .func[4] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +       .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> +
> +       .format[PSC_TEMPERATURE] = direct,
> +       .m[PSC_TEMPERATURE] = 1,
> +       .b[PSC_TEMPERATURE] = 0,
> +       .R[PSC_TEMPERATURE] = 2,
> +       .func[6]  = PMBUS_HAVE_STATUS_TEMP,
> +       .func[7]  = PMBUS_HAVE_STATUS_TEMP,
> +       .func[8]  = PMBUS_HAVE_STATUS_TEMP,
> +       .func[9]  = PMBUS_HAVE_STATUS_TEMP,
> +       .func[10] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[11] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[12] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[13] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[14] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[15] = PMBUS_HAVE_STATUS_TEMP,
> +       .func[16] = PMBUS_HAVE_STATUS_TEMP,
> +
> +       .format[PSC_VOLTAGE_OUT] = direct,
> +       .m[PSC_VOLTAGE_OUT] = 1,
> +       .b[PSC_VOLTAGE_OUT] = 0,
> +       .R[PSC_VOLTAGE_OUT] = 0,
> +       .func[17] = PMBUS_HAVE_STATUS_VOUT,
> +       .func[18] = PMBUS_HAVE_STATUS_VOUT,
> +       .func[19] = PMBUS_HAVE_STATUS_VOUT,
> +       .func[20] = PMBUS_HAVE_STATUS_VOUT,
> +       .func[21] = PMBUS_HAVE_STATUS_VOUT,
> +       .func[22] = PMBUS_HAVE_STATUS_VOUT,
> +};
> +
> +static int max31785_probe(struct i2c_client *client,
> +                         const struct i2c_device_id *id)
> +{
> +       struct max31785 *chip;
> +       int rv;
> +       int i;
> +
> +       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> +       if (!chip)
> +               return -ENOMEM;
> +
> +       chip->info = max31785_info;
> +
> +       /*
> +        * Identify the chip firmware and configure capabilities.
> +        *
> +        * Bootstrap with i2c_smbus_*() calls as we need to understand the chip
> +        * capabilities for before invoking pmbus_do_probe(). The pmbus_*()
> +        * calls need access to memory that is only valid after
> +        * pmbus_do_probe().

I think this is common enough in pmbus drivers that the comment is redundant.

> +        */
> +       rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
> +       if (rv < 0)
> +               return rv;
> +
> +       rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION);
> +       if (rv < 0)
> +               return rv;
> +
> +       if ((rv & 0xff) == 0x40) {

DUAL_TACH_REV? Or a better name from the docs we have?

> +               chip->capabilities |= MAX31785_CAP_DUAL_TACH;
> +
> +               /*
> +                * Punt the dual tach virtual fans to non-existent pages. This
> +                * ensures the pwm attributes appear in a contiguous block
> +                */
> +               chip->info.pages = 29;
> +               chip->info.func[23] = PMBUS_HAVE_FAN12;
> +               chip->info.func[24] = PMBUS_HAVE_FAN12;
> +               chip->info.func[25] = PMBUS_HAVE_FAN12;
> +               chip->info.func[26] = PMBUS_HAVE_FAN12;
> +               chip->info.func[27] = PMBUS_HAVE_FAN12;
> +               chip->info.func[28] = PMBUS_HAVE_FAN12;
> +       }
> +
> +       rv = pmbus_do_probe(client, id, &chip->info);
> +       if (rv < 0)
> +               return rv;
> +
> +       for (i = 0; i < max31785_info.pages; i++) {
> +               int reg;
> +
> +               if (!(max31785_info.func[i] & (PMBUS_HAVE_FAN12)))
> +                       continue;
> +
> +               reg = pmbus_read_word_data(client, i, PMBUS_MFR_FAN_CONFIG);
> +               if (reg < 0)
> +                       continue;
> +
> +               /*
> +                * XXX: Purely for RFC/testing purposes, don't ramp fans on fan
> +                * or temperature sensor fault, or a failure to write
> +                * FAN_COMMAND_1 inside a 10s window (watchdog mode).
> +                *
> +                * The TSFO bit controls both ramping on temp sensor failure
> +                * AND whether FAN_COMMAND_1 is in watchdog mode.
> +                */
> +               reg |= MFR_FAN_CONFIG_TSFO | MFR_FAN_CONFIG_TACHO;
> +               if (chip->capabilities & MAX31785_CAP_DUAL_TACH)
> +                       reg |= MFR_FAN_CONFIG_DUAL_TACH;
> +               reg &= 0xffff;
> +
> +               rv = pmbus_write_word_data(client, i, PMBUS_MFR_FAN_CONFIG,
> +                                          reg);
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct i2c_device_id max31785_id[] = {
> +       { "max31785", 0 },
> +       { },
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, max31785_id);
> +
> +static struct i2c_driver max31785_driver = {
> +       .driver = {
> +               .name = "max31785",
> +       },
> +       .probe = max31785_probe,
> +       .remove = pmbus_do_remove,
> +       .id_table = max31785_id,
> +};
> +
> +module_i2c_driver(max31785_driver);
> +
> +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> +MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
> +MODULE_LICENSE("GPL");
> --
> 2.11.0
>

[toc] | [next] | [standalone]


#1692080

FromGuenter Roeck <linux@roeck-us.net>
Date2017-07-19 20:20 +0200
Message-ID<u51JL-3Ue-7@gated-at.bofh.it>
In reply to#1691831
On Thu, Jul 20, 2017 at 12:35:09AM +0930, Joel Stanley wrote:
> On Tue, Jul 18, 2017 at 1:06 PM, Andrew Jeffery <andrew@aj.id.au> wrote:
> > The driver features fan control and basic dual-tachometer support.
> 
> Say something about the hardware?
> 
>  max31785 is a fancy fan controller with temp measurement, pwm, tach,
> breakfast making from Maxim.
> 
> 
> >
> > The fan control makes use of the new virtual registers exposed by the
> > pmbus core, mixing use of the default implementations with some
> > overrides via the read/write handlers. FAN_COMMAND_1 on the MAX31785
> > breaks the values into bands that depend on the RPM or PWM control mode.
> >
> > The dual tachometer feature is implemented in hardware with a TACHSEL
> > input to indicate the rotor under measurement, and exposed on the bus by
> > extending the READ_FAN_SPEED_1 word with two extra bytes*.
> > The need to read the non-standard four-byte response leads to a cut-down
> > implementation of i2c_smbus_xfer_emulated() included in the driver.
> > Further, to expose the second rotor tachometer value to userspace,
> > virtual fans are implemented by re-routing the FAN_CONFIG_1_2 register
> > from the undefined (in hardware) pages 23-28 to the same register on
> > pages 0-5, and similarly re-routing READ_FAN_SPEED_1 requests on 23-28
> > to pages 0-5 but extracting the second word of the four-byte response.
> >
> > * The documentation recommends the slower rotor be associated with
> > TACHSEL=0, which is the input used by the controller's closed-loop fan
> > management.
> >
> > Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
> > ---
> > v1 -> v2:
> >
> > * Implement in terms of virtual registers
> > * Add support for dual-tachometer readings through 'virtual fans' (unused pages)
> >
> >  drivers/hwmon/pmbus/Kconfig    |  10 ++
> >  drivers/hwmon/pmbus/Makefile   |   1 +
> >  drivers/hwmon/pmbus/max31785.c | 372 +++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 383 insertions(+)
> >  create mode 100644 drivers/hwmon/pmbus/max31785.c
> >
> > diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
> > index cad1229b7e17..5f2f3c6c7499 100644
> > --- a/drivers/hwmon/pmbus/Kconfig
> > +++ b/drivers/hwmon/pmbus/Kconfig
> > @@ -95,6 +95,16 @@ config SENSORS_MAX20751
> >           This driver can also be built as a module. If so, the module will
> >           be called max20751.
> >
> > +config SENSORS_MAX31785
> > +       tristate "Maxim MAX31785 and compatibles"
> > +       default n
> > +       help
> > +         If you say yes here you get hardware monitoring support for Maxim
> > +         MAX31785.
> > +
> > +         This driver can also be built as a module. If so, the module will
> > +         be called max31785.
> > +
> >  config SENSORS_MAX34440
> >         tristate "Maxim MAX34440 and compatibles"
> >         default n
> > diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
> > index 562132054aaf..4ea548a8af46 100644
> > --- a/drivers/hwmon/pmbus/Makefile
> > +++ b/drivers/hwmon/pmbus/Makefile
> > @@ -10,6 +10,7 @@ obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
> >  obj-$(CONFIG_SENSORS_LTC3815)  += ltc3815.o
> >  obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
> >  obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
> > +obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
> >  obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
> >  obj-$(CONFIG_SENSORS_MAX8688)  += max8688.o
> >  obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
> > diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
> > new file mode 100644
> > index 000000000000..1baa961f2eb1
> > --- /dev/null
> > +++ b/drivers/hwmon/pmbus/max31785.c
> > @@ -0,0 +1,372 @@
> > +/*
> > + * Copyright (C) 2017 IBM Corp.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + */
> > +
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/init.h>
> > +#include <linux/err.h>
> > +#include <linux/i2c.h>
> > +#include "pmbus.h"
> > +
> > +#define MFR_FAN_CONFIG_DUAL_TACH       BIT(12)
> > +#define MFR_FAN_CONFIG_TSFO            BIT(9)
> > +#define MFR_FAN_CONFIG_TACHO           BIT(8)
> > +
> > +#define MAX31785_CAP_DUAL_TACH         BIT(0)
> > +
> > +struct max31785 {
> > +       struct pmbus_driver_info info;
> > +
> > +       u32 capabilities;
> > +};
> > +
> > +enum max31785_regs {
> > +       PMBUS_MFR_FAN_CONFIG            = 0xF1,
> > +       PMBUS_MFR_READ_FAN_PWM          = 0xF3,
> > +       PMBUS_MFR_FAN_FAULT_LIMIT       = 0xF5,
> > +       PMBUS_MFR_FAN_WARN_LIMIT        = 0xF6,
> > +       PMBUS_MFR_FAN_PWM_AVG           = 0xF8,
> > +};
> > +
> > +#define to_max31785(_c) container_of(pmbus_get_info(_c), struct max31785, info)
> > +
> > +static int max31785_read_byte_data(struct i2c_client *client, int page,
> > +                                  int reg)
> > +{
> > +       struct max31785 *chip = to_max31785(client);
> > +       int rv = -ENODATA;
> 
> I'd drop this.
> 
> > +
> > +       switch (reg) {
> > +       case PMBUS_VOUT_MODE:
> > +               if (page < 23)
> > +                       return -ENODATA;
> > +
> > +               return -ENOTSUPP;
> > +       case PMBUS_FAN_CONFIG_12:
> > +               if (page < 23)
> > +                       return -ENODATA;
> > +
> > +               if (WARN_ON(!(chip->capabilities & MAX31785_CAP_DUAL_TACH)))
> 
> Not sure about the WARN_ON here - won't users get spammed every time
> someone reads a byte of data? When do we expect this case to happen?
> 
Presumably this would be an implementation error (there is no page numbered 23
or larger if the bit is not set). This can't happen, thus the check is really
unnecessary and just bloats the code.

This also means that the 'capabilities' variable is also unecessary, since it
is only used to verify that the PMBus core really understood that there are
only 24 pages if MAX31785_CAP_DUAL_TACH is not set (and, to be _really_ sure,
it keeps verifying it over and over again).

> > +                       return -ENOTSUPP;
> > +
> > +               rv = pmbus_read_byte_data(client, page - 23, reg);
> 
> return pmbus_read_byte_data(client, page - 23, reg);
> > +               break;
> > +       }
> > +
> > +       return rv;
> 
> return -ENODATA;
> 

-ENODATA: Calling code is expected to handle the command.

> Or something like ESUPP or EINVAL?
> 

Only if you want to redefine the entire internal PMBus driver API.

> Interestingly max34440_read_byte_data (may) return zero in this case.
> 

zero: No error. Command has been handled.

> > +}
> > +
> > +static long max31785_read_long_data(struct i2c_client *client, int page,
> > +                                   int reg)
> > +{
> > +       unsigned char cmdbuf[1];
> > +       unsigned char rspbuf[4];
> > +       s64 rc;
> 
> This should be the same type as the function returns.
> 

Also, long is likely s32 on 32-bit architectures, making it quite pointless
as return type. In this case, it may be better to handle data and error return
as separate parameters, such as in

static init max31785_read32(struct i2c_client *client, int page, int reg,
			    u32 *data)

> > +
> > +       struct i2c_msg msg[2] = {
> > +               {
> > +                       .addr = client->addr,
> > +                       .flags = 0,
> > +                       .len = sizeof(cmdbuf),
> > +                       .buf = cmdbuf,
> > +               },
> > +               {
> > +                       .addr = client->addr,
> > +                       .flags = I2C_M_RD,
> > +                       .len = sizeof(rspbuf),
> > +                       .buf = rspbuf,
> > +               },
> > +       };
> > +
> > +       cmdbuf[0] = reg;
> > +
> > +       rc = pmbus_set_page(client, page);
> > +       if (rc < 0)
> > +               return rc;
> > +
> > +       rc = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
> > +       if (rc < 0)
> > +               return rc;
> > +
> > +       rc = (rspbuf[0] << (0 * 8)) | (rspbuf[1] << (1 * 8)) |
> > +            (rspbuf[2] << (2 * 8)) | (rspbuf[3] << (3 * 8));
> 
> It's getting late, so I might have confused myself. But can you just do:
> 
> rc = le32_to_cpup(rspbuf);
> 
> > +
> > +       return rc;
> > +}
> > +
> > +static int max31785_get_pwm(struct i2c_client *client, int page)
> > +{
> > +       int config;
> > +       int command;
> > +
> > +       config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
> > +       if (config < 0)
> > +               return config;
> > +
> > +       command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
> > +       if (command < 0)
> > +               return command;
> > +
> > +       if (!(config & PB_FAN_1_RPM)) {
> > +               if (command >= 0x8000)
> > +                       return 0;
> > +               else if (command >= 0x2711)
> > +                       return 0x2710;
> 
> What are the magic numbers?
> 
> > +               else

unnecessary else

> > +                       return command;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int max31785_get_pwm_mode(struct i2c_client *client, int page)
> > +{
> > +       int config;
> > +       int command;
> > +
> > +       config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
> > +       if (config < 0)
> > +               return config;
> > +
> > +       command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
> > +       if (command < 0)
> > +               return command;
> > +
> > +       if (!(config & PB_FAN_1_RPM)) {
> > +               if (command >= 0x8000)
> > +                       return 2;
> > +               else if (command >= 0x2711)
> > +                       return 0;
> > +               else

Unecessary else

> > +                       return 1;
> > +       }
> > +
> > +       return (command >= 0x8000) ? 2 : 1;
> > +}
> > +
> > +static int max31785_read_word_data(struct i2c_client *client, int page,
> > +                                  int reg)
> > +{
> > +       struct max31785 *chip = to_max31785(client);
> > +       long rv = -ENODATA;
> > +
> > +       switch (reg) {
> > +       case PMBUS_READ_FAN_SPEED_1:
> > +               if (likely(page < 23))
> > +                       return -ENODATA;
> > +
> > +               if (WARN_ON(!(chip->capabilities & MAX31785_CAP_DUAL_TACH)))
> > +                       return -ENOTSUPP;
> > +
> > +               rv = max31785_read_long_data(client, page - 23, reg);
> > +               if (rv < 0)
> > +                       return rv;
> > +
> > +               rv = (rv >> 16) & 0xffff;
> > +               break;
> > +       case PMBUS_VIRT_PWM_1:
> > +               rv = max31785_get_pwm(client, page);

And ignore the error ?

> > +               rv *= 255;
> > +               rv /= 100;

max31785_get_pwm() returns 0..0x2710 (or 10000). This is translated to 0..25500.
Not really sure if that makes sense. We don't report pwm values in fractions of
100 to user space.

> > +               break;
> > +       case PMBUS_VIRT_PWM_ENABLE_1:
> > +               rv = max31785_get_pwm_mode(client, page);
> > +               break;

Not sure if PMBUS_VIRT_PWM_1 and PMBUS_VIRT_PWM_ENABLE_1 should return
-ENOTSUPP for pages 23+.

> > +       }
> > +
> > +       if (rv == -ENODATA && page >= 23)
> > +               return -ENXIO;

That looks like another verification. Please drop.

> > +
> > +       return rv;
> > +}
> > +
> > +static const int max31785_pwm_modes[] = { 0x7fff, 0x2710, 0xffff };
> > +
> > +static int max31785_write_word_data(struct i2c_client *client, int page,
> > +                                   int reg, u16 word)
> > +{
> > +       int rv = -ENODATA;
> > +
> > +       if (page >= 23)
> > +               return -ENXIO;
> > +
> > +       switch (reg) {
> > +       case PMBUS_VIRT_PWM_ENABLE_1:
> > +               if (word >= ARRAY_SIZE(max31785_pwm_modes))
> > +                       return -ENOTSUPP;

That doesn't look correct. -EINVAL, possibly.

> > +
> > +               rv = pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
> > +                                        max31785_pwm_modes[word]);
> > +               break;
> > +       }
> > +
> > +       return rv;
> > +}
> > +
> > +static int max31785_write_byte(struct i2c_client *client, int page, u8 value)
> > +{
> > +       if (page < 23)
> > +               return -ENODATA;
> > +
> > +       return -ENOTSUPP;
> > +}
> > +
> > +static struct pmbus_driver_info max31785_info = {
> > +       .pages = 23,
> > +
> > +       .write_word_data = max31785_write_word_data,
> > +       .read_byte_data = max31785_read_byte_data,
> > +       .read_word_data = max31785_read_word_data,
> > +       .write_byte = max31785_write_byte,
> > +
> > +       /* RPM */
> > +       .format[PSC_FAN] = direct,
> > +       .m[PSC_FAN] = 1,
> > +       .b[PSC_FAN] = 0,
> > +       .R[PSC_FAN] = 0,
> > +       /* PWM */
> > +       .format[PSC_PWM] = direct,
> > +       .m[PSC_PWM] = 1,
> > +       .b[PSC_PWM] = 0,
> > +       .R[PSC_PWM] = 2,
> > +       .func[0] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +       .func[1] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +       .func[2] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +       .func[3] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +       .func[4] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +       .func[5] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12,
> > +
> > +       .format[PSC_TEMPERATURE] = direct,
> > +       .m[PSC_TEMPERATURE] = 1,
> > +       .b[PSC_TEMPERATURE] = 0,
> > +       .R[PSC_TEMPERATURE] = 2,
> > +       .func[6]  = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[7]  = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[8]  = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[9]  = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[10] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[11] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[12] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[13] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[14] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[15] = PMBUS_HAVE_STATUS_TEMP,
> > +       .func[16] = PMBUS_HAVE_STATUS_TEMP,
> > +
> > +       .format[PSC_VOLTAGE_OUT] = direct,
> > +       .m[PSC_VOLTAGE_OUT] = 1,
> > +       .b[PSC_VOLTAGE_OUT] = 0,
> > +       .R[PSC_VOLTAGE_OUT] = 0,
> > +       .func[17] = PMBUS_HAVE_STATUS_VOUT,
> > +       .func[18] = PMBUS_HAVE_STATUS_VOUT,
> > +       .func[19] = PMBUS_HAVE_STATUS_VOUT,
> > +       .func[20] = PMBUS_HAVE_STATUS_VOUT,
> > +       .func[21] = PMBUS_HAVE_STATUS_VOUT,
> > +       .func[22] = PMBUS_HAVE_STATUS_VOUT,
> > +};
> > +
> > +static int max31785_probe(struct i2c_client *client,
> > +                         const struct i2c_device_id *id)
> > +{
> > +       struct max31785 *chip;
> > +       int rv;
> > +       int i;
> > +
> > +       chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
> > +       if (!chip)
> > +               return -ENOMEM;
> > +
> > +       chip->info = max31785_info;
> > +
> > +       /*
> > +        * Identify the chip firmware and configure capabilities.
> > +        *
> > +        * Bootstrap with i2c_smbus_*() calls as we need to understand the chip
> > +        * capabilities for before invoking pmbus_do_probe(). The pmbus_*()
> > +        * calls need access to memory that is only valid after
> > +        * pmbus_do_probe().
> 
> I think this is common enough in pmbus drivers that the comment is redundant.
> 
> > +        */
> > +       rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 255);
> > +       if (rv < 0)
> > +               return rv;
> > +
> > +       rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION);
> > +       if (rv < 0)
> > +               return rv;
> > +
> > +       if ((rv & 0xff) == 0x40) {
> 
> DUAL_TACH_REV? Or a better name from the docs we have?
> 
> > +               chip->capabilities |= MAX31785_CAP_DUAL_TACH;
> > +
> > +               /*
> > +                * Punt the dual tach virtual fans to non-existent pages. This
> > +                * ensures the pwm attributes appear in a contiguous block
> > +                */
> > +               chip->info.pages = 29;
> > +               chip->info.func[23] = PMBUS_HAVE_FAN12;
> > +               chip->info.func[24] = PMBUS_HAVE_FAN12;
> > +               chip->info.func[25] = PMBUS_HAVE_FAN12;
> > +               chip->info.func[26] = PMBUS_HAVE_FAN12;
> > +               chip->info.func[27] = PMBUS_HAVE_FAN12;
> > +               chip->info.func[28] = PMBUS_HAVE_FAN12;
> > +       }
> > +
> > +       rv = pmbus_do_probe(client, id, &chip->info);
> > +       if (rv < 0)
> > +               return rv;
> > +
> > +       for (i = 0; i < max31785_info.pages; i++) {
> > +               int reg;
> > +
> > +               if (!(max31785_info.func[i] & (PMBUS_HAVE_FAN12)))
> > +                       continue;
> > +
> > +               reg = pmbus_read_word_data(client, i, PMBUS_MFR_FAN_CONFIG);
> > +               if (reg < 0)
> > +                       continue;
> > +
> > +               /*
> > +                * XXX: Purely for RFC/testing purposes, don't ramp fans on fan
> > +                * or temperature sensor fault, or a failure to write
> > +                * FAN_COMMAND_1 inside a 10s window (watchdog mode).
> > +                *
> > +                * The TSFO bit controls both ramping on temp sensor failure
> > +                * AND whether FAN_COMMAND_1 is in watchdog mode.
> > +                */
> > +               reg |= MFR_FAN_CONFIG_TSFO | MFR_FAN_CONFIG_TACHO;
> > +               if (chip->capabilities & MAX31785_CAP_DUAL_TACH)
> > +                       reg |= MFR_FAN_CONFIG_DUAL_TACH;
> > +               reg &= 0xffff;
> > +
> > +               rv = pmbus_write_word_data(client, i, PMBUS_MFR_FAN_CONFIG,
> > +                                          reg);
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static const struct i2c_device_id max31785_id[] = {
> > +       { "max31785", 0 },
> > +       { },
> > +};
> > +
> > +MODULE_DEVICE_TABLE(i2c, max31785_id);
> > +
> > +static struct i2c_driver max31785_driver = {
> > +       .driver = {
> > +               .name = "max31785",
> > +       },
> > +       .probe = max31785_probe,
> > +       .remove = pmbus_do_remove,
> > +       .id_table = max31785_id,
> > +};
> > +
> > +module_i2c_driver(max31785_driver);
> > +
> > +MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
> > +MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.11.0
> >

[toc] | [prev] | [next] | [standalone]


#1692333

FromAndrew Jeffery <andrew@aj.id.au>
Date2017-07-20 03:30 +0200
Message-ID<u58rU-8rs-5@gated-at.bofh.it>
In reply to#1692080

[Multipart message — attachments visible in raw view] — view raw

T24gV2VkLCAyMDE3LTA3LTE5IGF0IDExOjExIC0wNzAwLCBHdWVudGVyIFJvZWNrIHdyb3RlOgo+
IE9uIFRodSwgSnVsIDIwLCAyMDE3IGF0IDEyOjM1OjA5QU0gKzA5MzAsIEpvZWwgU3RhbmxleSB3
cm90ZToKPiA+ID4gPiBPbiBUdWUsIEp1bCAxOCwgMjAxNyBhdCAxOjA2IFBNLCBBbmRyZXcgSmVm
ZmVyeSA8YW5kcmV3QGFqLmlkLmF1PiB3cm90ZToKPiA+ID4gVGhlIGRyaXZlciBmZWF0dXJlcyBm
YW4gY29udHJvbCBhbmQgYmFzaWMgZHVhbC10YWNob21ldGVyIHN1cHBvcnQuCj4gPiAKPiA+IFNh
eSBzb21ldGhpbmcgYWJvdXQgdGhlIGhhcmR3YXJlPwo+ID4gCj4gPiDCoG1heDMxNzg1IGlzIGEg
ZmFuY3kgZmFuIGNvbnRyb2xsZXIgd2l0aCB0ZW1wIG1lYXN1cmVtZW50LCBwd20sIHRhY2gsCj4g
PiBicmVha2Zhc3QgbWFraW5nIGZyb20gTWF4aW0uCj4gPiAKPiA+IAo+ID4gPiAKPiA+ID4gVGhl
IGZhbiBjb250cm9sIG1ha2VzIHVzZSBvZiB0aGUgbmV3IHZpcnR1YWwgcmVnaXN0ZXJzIGV4cG9z
ZWQgYnkgdGhlCj4gPiA+IHBtYnVzIGNvcmUsIG1peGluZyB1c2Ugb2YgdGhlIGRlZmF1bHQgaW1w
bGVtZW50YXRpb25zIHdpdGggc29tZQo+ID4gPiBvdmVycmlkZXMgdmlhIHRoZSByZWFkL3dyaXRl
IGhhbmRsZXJzLiBGQU5fQ09NTUFORF8xIG9uIHRoZSBNQVgzMTc4NQo+ID4gPiBicmVha3MgdGhl
IHZhbHVlcyBpbnRvIGJhbmRzIHRoYXQgZGVwZW5kIG9uIHRoZSBSUE0gb3IgUFdNIGNvbnRyb2wg
bW9kZS4KPiA+ID4gCj4gPiA+IFRoZSBkdWFsIHRhY2hvbWV0ZXIgZmVhdHVyZSBpcyBpbXBsZW1l
bnRlZCBpbiBoYXJkd2FyZSB3aXRoIGEgVEFDSFNFTAo+ID4gPiBpbnB1dCB0byBpbmRpY2F0ZSB0
aGUgcm90b3IgdW5kZXIgbWVhc3VyZW1lbnQsIGFuZCBleHBvc2VkIG9uIHRoZSBidXMgYnkKPiA+
ID4gZXh0ZW5kaW5nIHRoZSBSRUFEX0ZBTl9TUEVFRF8xIHdvcmQgd2l0aCB0d28gZXh0cmEgYnl0
ZXMqLgo+ID4gPiBUaGUgbmVlZCB0byByZWFkIHRoZSBub24tc3RhbmRhcmQgZm91ci1ieXRlIHJl
c3BvbnNlIGxlYWRzIHRvIGEgY3V0LWRvd24KPiA+ID4gaW1wbGVtZW50YXRpb24gb2YgaTJjX3Nt
YnVzX3hmZXJfZW11bGF0ZWQoKSBpbmNsdWRlZCBpbiB0aGUgZHJpdmVyLgo+ID4gPiBGdXJ0aGVy
LCB0byBleHBvc2UgdGhlIHNlY29uZCByb3RvciB0YWNob21ldGVyIHZhbHVlIHRvIHVzZXJzcGFj
ZSwKPiA+ID4gdmlydHVhbCBmYW5zIGFyZSBpbXBsZW1lbnRlZCBieSByZS1yb3V0aW5nIHRoZSBG
QU5fQ09ORklHXzFfMiByZWdpc3Rlcgo+ID4gPiBmcm9tIHRoZSB1bmRlZmluZWQgKGluIGhhcmR3
YXJlKSBwYWdlcyAyMy0yOCB0byB0aGUgc2FtZSByZWdpc3RlciBvbgo+ID4gPiBwYWdlcyAwLTUs
IGFuZCBzaW1pbGFybHkgcmUtcm91dGluZyBSRUFEX0ZBTl9TUEVFRF8xIHJlcXVlc3RzIG9uIDIz
LTI4Cj4gPiA+IHRvIHBhZ2VzIDAtNSBidXQgZXh0cmFjdGluZyB0aGUgc2Vjb25kIHdvcmQgb2Yg
dGhlIGZvdXItYnl0ZSByZXNwb25zZS4KPiA+ID4gCj4gPiA+ICogVGhlIGRvY3VtZW50YXRpb24g
cmVjb21tZW5kcyB0aGUgc2xvd2VyIHJvdG9yIGJlIGFzc29jaWF0ZWQgd2l0aAo+ID4gPiBUQUNI
U0VMPTAsIHdoaWNoIGlzIHRoZSBpbnB1dCB1c2VkIGJ5IHRoZSBjb250cm9sbGVyJ3MgY2xvc2Vk
LWxvb3AgZmFuCj4gPiA+IG1hbmFnZW1lbnQuCj4gPiA+IAo+ID4gPiA+ID4gPiBTaWduZWQtb2Zm
LWJ5OiBBbmRyZXcgSmVmZmVyeSA8YW5kcmV3QGFqLmlkLmF1Pgo+ID4gPiAtLS0KPiA+ID4gdjEg
LT4gdjI6Cj4gPiA+IAo+ID4gPiAqIEltcGxlbWVudCBpbiB0ZXJtcyBvZiB2aXJ0dWFsIHJlZ2lz
dGVycwo+ID4gPiAqIEFkZCBzdXBwb3J0IGZvciBkdWFsLXRhY2hvbWV0ZXIgcmVhZGluZ3MgdGhy
b3VnaCAndmlydHVhbCBmYW5zJyAodW51c2VkIHBhZ2VzKQo+ID4gPiAKPiA+ID4gwqBkcml2ZXJz
L2h3bW9uL3BtYnVzL0tjb25maWfCoMKgwqDCoHzCoMKgMTAgKysKPiA+ID4gwqBkcml2ZXJzL2h3
bW9uL3BtYnVzL01ha2VmaWxlwqDCoMKgfMKgwqDCoDEgKwo+ID4gPiDCoGRyaXZlcnMvaHdtb24v
cG1idXMvbWF4MzE3ODUuYyB8IDM3MiArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr
KysrKysrKwo+ID4gPiDCoDMgZmlsZXMgY2hhbmdlZCwgMzgzIGluc2VydGlvbnMoKykKPiA+ID4g
wqBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9od21vbi9wbWJ1cy9tYXgzMTc4NS5jCj4gPiA+
IAo+ID4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9od21vbi9wbWJ1cy9LY29uZmlnIGIvZHJpdmVy
cy9od21vbi9wbWJ1cy9LY29uZmlnCj4gPiA+IGluZGV4IGNhZDEyMjliN2UxNy4uNWYyZjNjNmM3
NDk5IDEwMDY0NAo+ID4gPiAtLS0gYS9kcml2ZXJzL2h3bW9uL3BtYnVzL0tjb25maWcKPiA+ID4g
KysrIGIvZHJpdmVycy9od21vbi9wbWJ1cy9LY29uZmlnCj4gPiA+IEBAIC05NSw2ICs5NSwxNiBA
QCBjb25maWcgU0VOU09SU19NQVgyMDc1MQo+ID4gPiDCoMKgwqDCoMKgwqDCoMKgwqDCoFRoaXMg
ZHJpdmVyIGNhbiBhbHNvIGJlIGJ1aWx0IGFzIGEgbW9kdWxlLiBJZiBzbywgdGhlIG1vZHVsZSB3
aWxsCj4gPiA+IMKgwqDCoMKgwqDCoMKgwqDCoMKgYmUgY2FsbGVkIG1heDIwNzUxLgo+ID4gPiAK
PiA+ID4gK2NvbmZpZyBTRU5TT1JTX01BWDMxNzg1Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoHRyaXN0
YXRlICJNYXhpbSBNQVgzMTc4NSBhbmQgY29tcGF0aWJsZXMiCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oGRlZmF1bHQgbgo+ID4gPiArwqDCoMKgwqDCoMKgwqBoZWxwCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oMKgwqBJZiB5b3Ugc2F5IHllcyBoZXJlIHlvdSBnZXQgaGFyZHdhcmUgbW9uaXRvcmluZyBzdXBw
b3J0IGZvciBNYXhpbQo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgTUFYMzE3ODUuCj4gPiA+ICsK
PiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoFRoaXMgZHJpdmVyIGNhbiBhbHNvIGJlIGJ1aWx0IGFz
IGEgbW9kdWxlLiBJZiBzbywgdGhlIG1vZHVsZSB3aWxsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKg
wqBiZSBjYWxsZWQgbWF4MzE3ODUuCj4gPiA+ICsKPiA+ID4gwqBjb25maWcgU0VOU09SU19NQVgz
NDQ0MAo+ID4gPiDCoMKgwqDCoMKgwqDCoMKgdHJpc3RhdGUgIk1heGltIE1BWDM0NDQwIGFuZCBj
b21wYXRpYmxlcyIKPiA+ID4gwqDCoMKgwqDCoMKgwqDCoGRlZmF1bHQgbgo+ID4gPiBkaWZmIC0t
Z2l0IGEvZHJpdmVycy9od21vbi9wbWJ1cy9NYWtlZmlsZSBiL2RyaXZlcnMvaHdtb24vcG1idXMv
TWFrZWZpbGUKPiA+ID4gaW5kZXggNTYyMTMyMDU0YWFmLi40ZWE1NDhhOGFmNDYgMTAwNjQ0Cj4g
PiA+IC0tLSBhL2RyaXZlcnMvaHdtb24vcG1idXMvTWFrZWZpbGUKPiA+ID4gKysrIGIvZHJpdmVy
cy9od21vbi9wbWJ1cy9NYWtlZmlsZQo+ID4gPiBAQCAtMTAsNiArMTAsNyBAQCBvYmotJChDT05G
SUdfU0VOU09SU19MVEMyOTc4KSArPSBsdGMyOTc4Lm8KPiA+ID4gwqBvYmotJChDT05GSUdfU0VO
U09SU19MVEMzODE1KcKgwqArPSBsdGMzODE1Lm8KPiA+ID4gwqBvYmotJChDT05GSUdfU0VOU09S
U19NQVgxNjA2NCkgKz0gbWF4MTYwNjQubwo+ID4gPiDCoG9iai0kKENPTkZJR19TRU5TT1JTX01B
WDIwNzUxKSArPSBtYXgyMDc1MS5vCj4gPiA+ICtvYmotJChDT05GSUdfU0VOU09SU19NQVgzMTc4
NSkgKz0gbWF4MzE3ODUubwo+ID4gPiDCoG9iai0kKENPTkZJR19TRU5TT1JTX01BWDM0NDQwKSAr
PSBtYXgzNDQ0MC5vCj4gPiA+IMKgb2JqLSQoQ09ORklHX1NFTlNPUlNfTUFYODY4OCnCoMKgKz0g
bWF4ODY4OC5vCj4gPiA+IMKgb2JqLSQoQ09ORklHX1NFTlNPUlNfVFBTNDA0MjIpICs9IHRwczQw
NDIyLm8KPiA+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvaHdtb24vcG1idXMvbWF4MzE3ODUuYyBi
L2RyaXZlcnMvaHdtb24vcG1idXMvbWF4MzE3ODUuYwo+ID4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0
NAo+ID4gPiBpbmRleCAwMDAwMDAwMDAwMDAuLjFiYWE5NjFmMmViMQo+ID4gPiAtLS0gL2Rldi9u
dWxsCj4gPiA+ICsrKyBiL2RyaXZlcnMvaHdtb24vcG1idXMvbWF4MzE3ODUuYwo+ID4gPiBAQCAt
MCwwICsxLDM3MiBAQAo+ID4gPiArLyoKPiA+ID4gKyAqIENvcHlyaWdodCAoQykgMjAxNyBJQk0g
Q29ycC4KPiA+ID4gKyAqCj4gPiA+ICsgKiBUaGlzIHByb2dyYW0gaXMgZnJlZSBzb2Z0d2FyZTsg
eW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeQo+ID4gPiArICogaXQgdW5kZXIg
dGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQg
YnkKPiA+ID4gKyAqIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9u
IDIgb2YgdGhlIExpY2Vuc2UsIG9yCj4gPiA+ICsgKiAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRl
ciB2ZXJzaW9uLgo+ID4gPiArICovCj4gPiA+ICsKPiA+ID4gKyNpbmNsdWRlIDxsaW51eC9rZXJu
ZWwuaD4KPiA+ID4gKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4KPiA+ID4gKyNpbmNsdWRlIDxs
aW51eC9pbml0Lmg+Cj4gPiA+ICsjaW5jbHVkZSA8bGludXgvZXJyLmg+Cj4gPiA+ICsjaW5jbHVk
ZSA8bGludXgvaTJjLmg+Cj4gPiA+ICsjaW5jbHVkZSAicG1idXMuaCIKPiA+ID4gKwo+ID4gPiAr
I2RlZmluZSBNRlJfRkFOX0NPTkZJR19EVUFMX1RBQ0jCoMKgwqDCoMKgwqDCoEJJVCgxMikKPiA+
ID4gKyNkZWZpbmUgTUZSX0ZBTl9DT05GSUdfVFNGT8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoEJJ
VCg5KQo+ID4gPiArI2RlZmluZSBNRlJfRkFOX0NPTkZJR19UQUNIT8KgwqDCoMKgwqDCoMKgwqDC
oMKgwqBCSVQoOCkKPiA+ID4gKwo+ID4gPiArI2RlZmluZSBNQVgzMTc4NV9DQVBfRFVBTF9UQUNI
wqDCoMKgwqDCoMKgwqDCoMKgQklUKDApCj4gPiA+ICsKPiA+ID4gK3N0cnVjdCBtYXgzMTc4NSB7
Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoHN0cnVjdCBwbWJ1c19kcml2ZXJfaW5mbyBpbmZvOwo+ID4g
PiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHUzMiBjYXBhYmlsaXRpZXM7Cj4gPiA+ICt9Owo+ID4g
PiArCj4gPiA+ICtlbnVtIG1heDMxNzg1X3JlZ3Mgewo+ID4gPiArwqDCoMKgwqDCoMKgwqBQTUJV
U19NRlJfRkFOX0NPTkZJR8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoD0gMHhGMSwKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgUE1CVVNfTUZSX1JFQURfRkFOX1BXTcKgwqDCoMKgwqDCoMKgwqDCoMKgPSAw
eEYzLAo+ID4gPiArwqDCoMKgwqDCoMKgwqBQTUJVU19NRlJfRkFOX0ZBVUxUX0xJTUlUwqDCoMKg
wqDCoMKgwqA9IDB4RjUsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoFBNQlVTX01GUl9GQU5fV0FSTl9M
SU1JVMKgwqDCoMKgwqDCoMKgwqA9IDB4RjYsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoFBNQlVTX01G
Ul9GQU5fUFdNX0FWR8KgwqDCoMKgwqDCoMKgwqDCoMKgwqA9IDB4RjgsCj4gPiA+ICt9Owo+ID4g
PiArCj4gPiA+ICsjZGVmaW5lIHRvX21heDMxNzg1KF9jKSBjb250YWluZXJfb2YocG1idXNfZ2V0
X2luZm8oX2MpLCBzdHJ1Y3QgbWF4MzE3ODUsIGluZm8pCj4gPiA+ICsKPiA+ID4gK3N0YXRpYyBp
bnQgbWF4MzE3ODVfcmVhZF9ieXRlX2RhdGEoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwgaW50
IHBhZ2UsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGludCByZWcpCj4gPiA+ICt7Cj4gPiA+ICvCoMKgwqDC
oMKgwqDCoHN0cnVjdCBtYXgzMTc4NSAqY2hpcCA9IHRvX21heDMxNzg1KGNsaWVudCk7Cj4gPiA+
ICvCoMKgwqDCoMKgwqDCoGludCBydiA9IC1FTk9EQVRBOwo+ID4gCj4gPiBJJ2QgZHJvcCB0aGlz
Lgo+ID4gCj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgc3dpdGNoIChyZWcpIHsKPiA+ID4g
K8KgwqDCoMKgwqDCoMKgY2FzZSBQTUJVU19WT1VUX01PREU6Cj4gPiA+ICvCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqBpZiAocGFnZSA8IDIzKQo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldHVybiAtRU5PREFUQTsKPiA+ID4gKwo+ID4g
PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIC1FTk9UU1VQUDsKPiA+ID4g
K8KgwqDCoMKgwqDCoMKgY2FzZSBQTUJVU19GQU5fQ09ORklHXzEyOgo+ID4gPiArwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKHBhZ2UgPCAyMykKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqByZXR1cm4gLUVOT0RBVEE7Cj4gPiA+ICsK
PiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGlmIChXQVJOX09OKCEoY2hpcC0+
Y2FwYWJpbGl0aWVzICYgTUFYMzE3ODVfQ0FQX0RVQUxfVEFDSCkpKQo+ID4gCj4gPiBOb3Qgc3Vy
ZSBhYm91dCB0aGUgV0FSTl9PTiBoZXJlIC0gd29uJ3QgdXNlcnMgZ2V0IHNwYW1tZWQgZXZlcnkg
dGltZQo+ID4gc29tZW9uZSByZWFkcyBhIGJ5dGUgb2YgZGF0YT8gV2hlbiBkbyB3ZSBleHBlY3Qg
dGhpcyBjYXNlIHRvIGhhcHBlbj8KPiA+IAo+IAo+IFByZXN1bWFibHkgdGhpcyB3b3VsZCBiZSBh
biBpbXBsZW1lbnRhdGlvbiBlcnJvciAodGhlcmUgaXMgbm8gcGFnZSBudW1iZXJlZCAyMwo+IG9y
IGxhcmdlciBpZiB0aGUgYml0IGlzIG5vdCBzZXQpLiBUaGlzIGNhbid0IGhhcHBlbiwgdGh1cyB0
aGUgY2hlY2sgaXMgcmVhbGx5Cj4gdW5uZWNlc3NhcnkgYW5kIGp1c3QgYmxvYXRzIHRoZSBjb2Rl
Lgo+IAo+IFRoaXMgYWxzbyBtZWFucyB0aGF0IHRoZSAnY2FwYWJpbGl0aWVzJyB2YXJpYWJsZSBp
cyBhbHNvIHVuZWNlc3NhcnksIHNpbmNlIGl0Cj4gaXMgb25seSB1c2VkIHRvIHZlcmlmeSB0aGF0
IHRoZSBQTUJ1cyBjb3JlIHJlYWxseSB1bmRlcnN0b29kIHRoYXQgdGhlcmUgYXJlCj4gb25seSAy
NCBwYWdlcyBpZiBNQVgzMTc4NV9DQVBfRFVBTF9UQUNIIGlzIG5vdCBzZXQgKGFuZCwgdG8gYmUg
X3JlYWxseV8gc3VyZSwKPiBpdCBrZWVwcyB2ZXJpZnlpbmcgaXQgb3ZlciBhbmQgb3ZlciBhZ2Fp
bikuCgpUaGlzIHdhcyBtYWlubHkgc29tZSAob3ZlcnplYWxvdXMpIHNhbml0eSBjaGVja2luZyB3
aGlsc3QgSSB3YXMKZGV2ZWxvcGluZyB0aGUgZHJpdmVyIChJIGhhdmUgYWxzbyBkZXZlbG9wZWQg
YSBQTUJ1cyBRRU1VIG1vZGVsIGluIHRoZQpwcm9jZXNzIC0gd2FzIHRyeWluZyB0byBjYXRjaCB0
aGlua29zIGluIG9uIGJvdGggc2lkZXMpLiBBcyB5b3UgcG9pbnQKb3V0IGl0J3MgdW5uZWNlc3Nh
cnkgaWYgZXZlcnl0aGluZyB3b3JrcyBhcyBpdCBzaG91bGQuCgpJJ2xsIHJlbW92ZSB0aGlzIGFu
ZCBvdGhlciB1bm5lY2Vzc2FyeSB2ZXJpZmljYXRpb25zIGJlZm9yZSBwb3N0aW5nIHRoZQpuZXh0
IHJldmlzaW9uLgoKPiAKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqByZXR1cm4gLUVOT1RTVVBQOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqBydiA9IHBtYnVzX3JlYWRfYnl0ZV9kYXRhKGNsaWVudCwgcGFnZSAt
IDIzLCByZWcpOwo+ID4gCj4gPiByZXR1cm4gcG1idXNfcmVhZF9ieXRlX2RhdGEoY2xpZW50LCBw
YWdlIC0gMjMsIHJlZyk7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBicmVh
azsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgfQo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHJl
dHVybiBydjsKPiA+IAo+ID4gcmV0dXJuIC1FTk9EQVRBOwo+ID4gCj4gCj4gLUVOT0RBVEE6IENh
bGxpbmcgY29kZSBpcyBleHBlY3RlZCB0byBoYW5kbGUgdGhlIGNvbW1hbmQuCj4gCj4gPiBPciBz
b21ldGhpbmcgbGlrZSBFU1VQUCBvciBFSU5WQUw/Cj4gPiAKPiAKPiBPbmx5IGlmIHlvdSB3YW50
IHRvIHJlZGVmaW5lIHRoZSBlbnRpcmUgaW50ZXJuYWwgUE1CdXMgZHJpdmVyIEFQSS4KPiAKPiA+
IEludGVyZXN0aW5nbHkgbWF4MzQ0NDBfcmVhZF9ieXRlX2RhdGEgKG1heSkgcmV0dXJuIHplcm8g
aW4gdGhpcyBjYXNlLgo+ID4gCj4gCj4gemVybzogTm8gZXJyb3IuIENvbW1hbmQgaGFzIGJlZW4g
aGFuZGxlZC4KPiAKPiA+ID4gK30KPiA+ID4gKwo+ID4gPiArc3RhdGljIGxvbmcgbWF4MzE3ODVf
cmVhZF9sb25nX2RhdGEoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwgaW50IHBhZ2UsCj4gPiA+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgaW50IHJlZykKPiA+ID4gK3sKPiA+ID4gK8KgwqDCoMKgwqDCoMKgdW5z
aWduZWQgY2hhciBjbWRidWZbMV07Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoHVuc2lnbmVkIGNoYXIg
cnNwYnVmWzRdOwo+ID4gPiArwqDCoMKgwqDCoMKgwqBzNjQgcmM7Cj4gPiAKPiA+IFRoaXMgc2hv
dWxkIGJlIHRoZSBzYW1lIHR5cGUgYXMgdGhlIGZ1bmN0aW9uIHJldHVybnMuCj4gPiAKPiAKPiBB
bHNvLCBsb25nIGlzIGxpa2VseSBzMzIgb24gMzItYml0IGFyY2hpdGVjdHVyZXMsIG1ha2luZyBp
dCBxdWl0ZSBwb2ludGxlc3MKPiBhcyByZXR1cm4gdHlwZS4gSW4gdGhpcyBjYXNlLCBpdCBtYXkg
YmUgYmV0dGVyIHRvIGhhbmRsZSBkYXRhIGFuZCBlcnJvciByZXR1cm4KPiBhcyBzZXBhcmF0ZSBw
YXJhbWV0ZXJzLCBzdWNoIGFzIGluCj4gCj4gc3RhdGljIGluaXQgbWF4MzE3ODVfcmVhZDMyKHN0
cnVjdCBpMmNfY2xpZW50ICpjbGllbnQsIGludCBwYWdlLCBpbnQgcmVnLAo+IAkJCcKgwqDCoMKg
dTMyICpkYXRhKQoKU3VyZSwgSSdsbCB0YWtlIHRoYXQgYXBwcm9hY2guCgo+IAo+ID4gPiArCj4g
PiA+ICvCoMKgwqDCoMKgwqDCoHN0cnVjdCBpMmNfbXNnIG1zZ1syXSA9IHsKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAuYWRkciA9IGNsaWVudC0+YWRkciwKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAuZmxhZ3MgPSAwLAo+ID4g
PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoC5sZW4gPSBz
aXplb2YoY21kYnVmKSwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqAuYnVmID0gY21kYnVmLAo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgfSwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHsKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAuYWRkciA9IGNsaWVu
dC0+YWRkciwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqAuZmxhZ3MgPSBJMkNfTV9SRCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqAubGVuID0gc2l6ZW9mKHJzcGJ1ZiksCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgLmJ1ZiA9IHJzcGJ1ZiwKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoH0sCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oH07Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgY21kYnVmWzBdID0gcmVnOwo+ID4gPiAr
Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoHJjID0gcG1idXNfc2V0X3BhZ2UoY2xpZW50LCBwYWdlKTsK
PiA+ID4gK8KgwqDCoMKgwqDCoMKgaWYgKHJjIDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoHJldHVybiByYzsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqByYyA9
IGkyY190cmFuc2ZlcihjbGllbnQtPmFkYXB0ZXIsIG1zZywgQVJSQVlfU0laRShtc2cpKTsKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgaWYgKHJjIDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoHJldHVybiByYzsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqByYyA9IChy
c3BidWZbMF0gPDwgKDAgKiA4KSkgfCAocnNwYnVmWzFdIDw8ICgxICogOCkpIHwKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoChyc3BidWZbMl0gPDwgKDIgKiA4KSkgfCAocnNwYnVmWzNd
IDw8ICgzICogOCkpOwo+ID4gCj4gPiBJdCdzIGdldHRpbmcgbGF0ZSwgc28gSSBtaWdodCBoYXZl
IGNvbmZ1c2VkIG15c2VsZi4gQnV0IGNhbiB5b3UganVzdCBkbzoKPiA+IAo+ID4gcmMgPSBsZTMy
X3RvX2NwdXAocnNwYnVmKTsKPiA+IAo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHJldHVy
biByYzsKPiA+ID4gK30KPiA+ID4gKwo+ID4gPiArc3RhdGljIGludCBtYXgzMTc4NV9nZXRfcHdt
KHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQsIGludCBwYWdlKQo+ID4gPiArewo+ID4gPiArwqDC
oMKgwqDCoMKgwqBpbnQgY29uZmlnOwo+ID4gPiArwqDCoMKgwqDCoMKgwqBpbnQgY29tbWFuZDsK
PiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqBjb25maWcgPSBwbWJ1c19yZWFkX2J5dGVfZGF0
YShjbGllbnQsIHBhZ2UsIFBNQlVTX0ZBTl9DT05GSUdfMTIpOwo+ID4gPiArwqDCoMKgwqDCoMKg
wqBpZiAoY29uZmlnIDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJl
dHVybiBjb25maWc7Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgY29tbWFuZCA9IHBtYnVz
X3JlYWRfd29yZF9kYXRhKGNsaWVudCwgcGFnZSwgUE1CVVNfRkFOX0NPTU1BTkRfMSk7Cj4gPiA+
ICvCoMKgwqDCoMKgwqDCoGlmIChjb21tYW5kIDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoHJldHVybiBjb21tYW5kOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oGlmICghKGNvbmZpZyAmIFBCX0ZBTl8xX1JQTSkpIHsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoGlmIChjb21tYW5kID49IDB4ODAwMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqByZXR1cm4gMDsKPiA+ID4gK8KgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoGVsc2UgaWYgKGNvbW1hbmQgPj0gMHgyNzExKQo+ID4gPiAr
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldHVybiAweDI3
MTA7Cj4gPiAKPiA+IFdoYXQgYXJlIHRoZSBtYWdpYyBudW1iZXJzPwoKTWFnaWMgZnJvbSB0aGUg
TWF4aW0gZGF0YXNoZWV0OiBTZWUgRkFOX0NPTU1BTkRfMSwgcGFnZSAzMFsxXS4gSSBmZWx0Cm1h
Y3JvcyB3b3VsZCBiZSBtb3JlIHBhaW4gdGhhbiBnYWluIGJ1dCBhbSBoYXBweSB0byBiaWtlc2hl
ZC4gSQpwcm9iYWJseSBqdXN0IHdhc24ndCBiZWluZyBjcmVhdGl2ZSBlbm91Z2guIEluIGdlbmVy
YWwgSSBhZ3JlZSBtYWdpYwpudW1iZXJzIGFyZSBub3QgdGhlIHdheSB0byBnby4KClsxXSBodHRw
czovL2RhdGFzaGVldHMubWF4aW1pbnRlZ3JhdGVkLmNvbS9lbi9kcy9NQVgzMTc4NS5wZGYKCj4g
PiAKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGVsc2UKPiAKPiB1bm5lY2Vz
c2FyeSBlbHNlCgpJbmRlZWQuIFdpbGwgZml4IHVwIG90aGVyIGluc3RhbmNlcy4KCj4gCj4gPiA+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIGNv
bW1hbmQ7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoH0KPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKg
wqByZXR1cm4gMDsKPiA+ID4gK30KPiA+ID4gKwo+ID4gPiArc3RhdGljIGludCBtYXgzMTc4NV9n
ZXRfcHdtX21vZGUoc3RydWN0IGkyY19jbGllbnQgKmNsaWVudCwgaW50IHBhZ2UpCj4gPiA+ICt7
Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoGludCBjb25maWc7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoGlu
dCBjb21tYW5kOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoGNvbmZpZyA9IHBtYnVzX3Jl
YWRfYnl0ZV9kYXRhKGNsaWVudCwgcGFnZSwgUE1CVVNfRkFOX0NPTkZJR18xMik7Cj4gPiA+ICvC
oMKgwqDCoMKgwqDCoGlmIChjb25maWcgPCAwKQo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgcmV0dXJuIGNvbmZpZzsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqBjb21t
YW5kID0gcG1idXNfcmVhZF93b3JkX2RhdGEoY2xpZW50LCBwYWdlLCBQTUJVU19GQU5fQ09NTUFO
RF8xKTsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgaWYgKGNvbW1hbmQgPCAwKQo+ID4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIGNvbW1hbmQ7Cj4gPiA+ICsKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgaWYgKCEoY29uZmlnICYgUEJfRkFOXzFfUlBNKSkgewo+ID4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKGNvbW1hbmQgPj0gMHg4MDAwKQo+ID4gPiArwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldHVybiAyOwo+ID4g
PiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgZWxzZSBpZiAoY29tbWFuZCA+PSAweDI3
MTEpCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
cmV0dXJuIDA7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBlbHNlCj4gCj4g
VW5lY2Vzc2FyeSBlbHNlCj4gCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgcmV0dXJuIDE7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoH0KPiA+ID4gKwo+
ID4gPiArwqDCoMKgwqDCoMKgwqByZXR1cm4gKGNvbW1hbmQgPj0gMHg4MDAwKSA/IDIgOiAxOwo+
ID4gPiArfQo+ID4gPiArCj4gPiA+ICtzdGF0aWMgaW50IG1heDMxNzg1X3JlYWRfd29yZF9kYXRh
KHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQsIGludCBwYWdlLAo+ID4gPiArwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBp
bnQgcmVnKQo+ID4gPiArewo+ID4gPiArwqDCoMKgwqDCoMKgwqBzdHJ1Y3QgbWF4MzE3ODUgKmNo
aXAgPSB0b19tYXgzMTc4NShjbGllbnQpOwo+ID4gPiArwqDCoMKgwqDCoMKgwqBsb25nIHJ2ID0g
LUVOT0RBVEE7Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgc3dpdGNoIChyZWcpIHsKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgY2FzZSBQTUJVU19SRUFEX0ZBTl9TUEVFRF8xOgo+ID4gPiArwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKGxpa2VseShwYWdlIDwgMjMpKQo+ID4gPiAr
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoHJldHVybiAtRU5P
REFUQTsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKFdB
Uk5fT04oIShjaGlwLT5jYXBhYmlsaXRpZXMgJiBNQVgzMTc4NV9DQVBfRFVBTF9UQUNIKSkpCj4g
PiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJu
IC1FTk9UU1VQUDsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
cnYgPSBtYXgzMTc4NV9yZWFkX2xvbmdfZGF0YShjbGllbnQsIHBhZ2UgLSAyMywgcmVnKTsKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGlmIChydiA8IDApCj4gPiA+ICvCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIHJ2Owo+ID4g
PiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBydiA9IChydiA+PiAxNikg
JiAweGZmZmY7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBicmVhazsKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgY2FzZSBQTUJVU19WSVJUX1BXTV8xOgo+ID4gPiArwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgcnYgPSBtYXgzMTc4NV9nZXRfcHdtKGNsaWVudCwgcGFnZSk7
Cj4gCj4gQW5kIGlnbm9yZSB0aGUgZXJyb3IgPwoKSGFoLiBBcHBhcmVudGx5LiBXaWxsIGZpeC4K
Cj4gCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBydiAqPSAyNTU7Cj4gPiA+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBydiAvPSAxMDA7Cj4gCj4gbWF4MzE3ODVf
Z2V0X3B3bSgpIHJldHVybnMgMC4uMHgyNzEwIChvciAxMDAwMCkuIFRoaXMgaXMgdHJhbnNsYXRl
ZCB0byAwLi4yNTUwMC4KPiBOb3QgcmVhbGx5IHN1cmUgaWYgdGhhdCBtYWtlcyBzZW5zZS4gV2Ug
ZG9uJ3QgcmVwb3J0IHB3bSB2YWx1ZXMgaW4gZnJhY3Rpb25zIG9mCj4gMTAwIHRvIHVzZXIgc3Bh
Y2UuCgpSaWdodCwgYnV0IHRoaXMgZ29lcyB0aHJvdWdoIERJUkVDVCBjb252ZXJzaW9uIHRvIGRp
dmlkZSBieSAxMDAgKG0gPSAxLApiID0gMCwgUiA9IDIpIGJlZm9yZSB3ZSBoaXQgdXNlcnNwYWNl
LgoKPiAKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGJyZWFrOwo+ID4gPiAr
wqDCoMKgwqDCoMKgwqBjYXNlIFBNQlVTX1ZJUlRfUFdNX0VOQUJMRV8xOgo+ID4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcnYgPSBtYXgzMTc4NV9nZXRfcHdtX21vZGUoY2xpZW50
LCBwYWdlKTsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGJyZWFrOwo+IAo+
IE5vdCBzdXJlIGlmIFBNQlVTX1ZJUlRfUFdNXzEgYW5kIFBNQlVTX1ZJUlRfUFdNX0VOQUJMRV8x
IHNob3VsZCByZXR1cm4KPiAtRU5PVFNVUFAgZm9yIHBhZ2VzIDIzKy4KClllYWgsIHRoYXQncyBy
ZWFzb25hYmxlLCBob3dldmVyIGJ5IHNpbWlsYXIgcmVhc29uaW5nIHlvdSB1c2VkIHRvCmVsaW1p
bmF0ZSB0aGUgY2FwYWJpbGl0aWVzIHZhcmlhYmxlIHRoaXMgY2FzZSBzaG91bGQgbmV2ZXIgYmUg
aGl0IGFzCnRob3NlIGF0dHJpYnV0ZXMgc2hvdWxkbid0IGV4aXN0LgoKPiAKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgfQo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoGlmIChydiA9PSAtRU5PREFU
QSAmJiBwYWdlID49IDIzKQo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0
dXJuIC1FTlhJTzsKPiAKPiBUaGF0IGxvb2tzIGxpa2UgYW5vdGhlciB2ZXJpZmljYXRpb24uIFBs
ZWFzZSBkcm9wLgo+IAo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHJldHVybiBydjsKPiA+
ID4gK30KPiA+ID4gKwo+ID4gPiArc3RhdGljIGNvbnN0IGludCBtYXgzMTc4NV9wd21fbW9kZXNb
XSA9IHsgMHg3ZmZmLCAweDI3MTAsIDB4ZmZmZiB9Owo+ID4gPiArCj4gPiA+ICtzdGF0aWMgaW50
IG1heDMxNzg1X3dyaXRlX3dvcmRfZGF0YShzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50LCBpbnQg
cGFnZSwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBpbnQgcmVnLCB1MTYgd29yZCkKPiA+ID4gK3sKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgaW50IHJ2ID0gLUVOT0RBVEE7Cj4gPiA+ICsKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgaWYgKHBhZ2UgPj0gMjMpCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqByZXR1cm4gLUVOWElPOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHN3aXRjaCAo
cmVnKSB7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoGNhc2UgUE1CVVNfVklSVF9QV01fRU5BQkxFXzE6
Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBpZiAod29yZCA+PSBBUlJBWV9T
SVpFKG1heDMxNzg1X3B3bV9tb2RlcykpCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIC1FTk9UU1VQUDsKPiAKPiBUaGF0IGRvZXNuJ3Qg
bG9vayBjb3JyZWN0LiAtRUlOVkFMLCBwb3NzaWJseS4KClllcywgLUVJTlZBTCB3b3VsZCBiZSBi
ZXR0ZXIuCgo+IAo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBy
diA9IHBtYnVzX3VwZGF0ZV9mYW4oY2xpZW50LCBwYWdlLCAwLCAwLCBQQl9GQU5fMV9SUE0sCj4g
PiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoG1heDMxNzg1X3B3bV9tb2Rlc1t3b3JkXSk7Cj4g
PiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBicmVhazsKPiA+ID4gK8KgwqDCoMKg
wqDCoMKgfQo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoHJldHVybiBydjsKPiA+ID4gK30K
PiA+ID4gKwo+ID4gPiArc3RhdGljIGludCBtYXgzMTc4NV93cml0ZV9ieXRlKHN0cnVjdCBpMmNf
Y2xpZW50ICpjbGllbnQsIGludCBwYWdlLCB1OCB2YWx1ZSkKPiA+ID4gK3sKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgaWYgKHBhZ2UgPCAyMykKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoHJldHVybiAtRU5PREFUQTsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqByZXR1cm4g
LUVOT1RTVVBQOwo+ID4gPiArfQo+ID4gPiArCj4gPiA+ICtzdGF0aWMgc3RydWN0IHBtYnVzX2Ry
aXZlcl9pbmZvIG1heDMxNzg1X2luZm8gPSB7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5wYWdlcyA9
IDIzLAo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC53cml0ZV93b3JkX2RhdGEgPSBtYXgz
MTc4NV93cml0ZV93b3JkX2RhdGEsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5yZWFkX2J5dGVfZGF0
YSA9IG1heDMxNzg1X3JlYWRfYnl0ZV9kYXRhLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAucmVhZF93
b3JkX2RhdGEgPSBtYXgzMTc4NV9yZWFkX3dvcmRfZGF0YSwKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
LndyaXRlX2J5dGUgPSBtYXgzMTc4NV93cml0ZV9ieXRlLAo+ID4gPiArCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC8qIFJQTSAqLwo+ID4gPiArwqDCoMKgwqDCoMKgwqAuZm9ybWF0W1BTQ19GQU5dID0g
ZGlyZWN0LAo+ID4gPiArwqDCoMKgwqDCoMKgwqAubVtQU0NfRkFOXSA9IDEsCj4gPiA+ICvCoMKg
wqDCoMKgwqDCoC5iW1BTQ19GQU5dID0gMCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLlJbUFNDX0ZB
Tl0gPSAwLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAvKiBQV00gKi8KPiA+ID4gK8KgwqDCoMKgwqDC
oMKgLmZvcm1hdFtQU0NfUFdNXSA9IGRpcmVjdCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLm1bUFND
X1BXTV0gPSAxLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuYltQU0NfUFdNXSA9IDAsCj4gPiA+ICvC
oMKgwqDCoMKgwqDCoC5SW1BTQ19QV01dID0gMiwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNb
MF0gPSBQTUJVU19IQVZFX0ZBTjEyIHwgUE1CVVNfSEFWRV9TVEFUVVNfRkFOMTIsCj4gPiA+ICvC
oMKgwqDCoMKgwqDCoC5mdW5jWzFdID0gUE1CVVNfSEFWRV9GQU4xMiB8IFBNQlVTX0hBVkVfU1RB
VFVTX0ZBTjEyLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuZnVuY1syXSA9IFBNQlVTX0hBVkVfRkFO
MTIgfCBQTUJVU19IQVZFX1NUQVRVU19GQU4xMiwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNb
M10gPSBQTUJVU19IQVZFX0ZBTjEyIHwgUE1CVVNfSEFWRV9TVEFUVVNfRkFOMTIsCj4gPiA+ICvC
oMKgwqDCoMKgwqDCoC5mdW5jWzRdID0gUE1CVVNfSEFWRV9GQU4xMiB8IFBNQlVTX0hBVkVfU1RB
VFVTX0ZBTjEyLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuZnVuY1s1XSA9IFBNQlVTX0hBVkVfRkFO
MTIgfCBQTUJVU19IQVZFX1NUQVRVU19GQU4xMiwKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKg
wqAuZm9ybWF0W1BTQ19URU1QRVJBVFVSRV0gPSBkaXJlY3QsCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oC5tW1BTQ19URU1QRVJBVFVSRV0gPSAxLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuYltQU0NfVEVN
UEVSQVRVUkVdID0gMCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLlJbUFNDX1RFTVBFUkFUVVJFXSA9
IDIsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5mdW5jWzZdwqDCoD0gUE1CVVNfSEFWRV9TVEFUVVNf
VEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbN13CoMKgPSBQTUJVU19IQVZFX1NUQVRV
U19URU1QLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuZnVuY1s4XcKgwqA9IFBNQlVTX0hBVkVfU1RB
VFVTX1RFTVAsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5mdW5jWzldwqDCoD0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTBdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTFdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTJdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTNdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTRdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTVdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgLmZ1bmNbMTZdID0gUE1CVVNfSEFWRV9T
VEFUVVNfVEVNUCwKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqAuZm9ybWF0W1BTQ19WT0xU
QUdFX09VVF0gPSBkaXJlY3QsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5tW1BTQ19WT0xUQUdFX09V
VF0gPSAxLAo+ID4gPiArwqDCoMKgwqDCoMKgwqAuYltQU0NfVk9MVEFHRV9PVVRdID0gMCwKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgLlJbUFNDX1ZPTFRBR0VfT1VUXSA9IDAsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzE3XSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzE4XSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzE5XSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzIwXSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzIxXSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoC5mdW5jWzIyXSA9IFBNQlVTX0hBVkVfU1RBVFVTX1ZPVVQsCj4gPiA+ICt9Owo+ID4g
PiArCj4gPiA+ICtzdGF0aWMgaW50IG1heDMxNzg1X3Byb2JlKHN0cnVjdCBpMmNfY2xpZW50ICpj
bGllbnQsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoGNvbnN0IHN0cnVjdCBpMmNfZGV2aWNlX2lkICppZCkKPiA+ID4gK3sKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgc3RydWN0IG1heDMxNzg1ICpjaGlwOwo+ID4gPiArwqDCoMKgwqDCoMKgwqBp
bnQgcnY7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoGludCBpOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoGNoaXAgPSBkZXZtX2t6YWxsb2MoJmNsaWVudC0+ZGV2LCBzaXplb2YoKmNoaXApLCBH
RlBfS0VSTkVMKTsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgaWYgKCFjaGlwKQo+ID4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIC1FTk9NRU07Cj4gPiA+ICsKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgY2hpcC0+aW5mbyA9IG1heDMxNzg1X2luZm87Cj4gPiA+ICsKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgLyoKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqAqIElkZW50aWZ5IHRoZSBjaGlw
IGZpcm13YXJlIGFuZCBjb25maWd1cmUgY2FwYWJpbGl0aWVzLgo+ID4gPiArwqDCoMKgwqDCoMKg
wqDCoCoKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqAqIEJvb3RzdHJhcCB3aXRoIGkyY19zbWJ1c18q
KCkgY2FsbHMgYXMgd2UgbmVlZCB0byB1bmRlcnN0YW5kIHRoZSBjaGlwCj4gPiA+ICvCoMKgwqDC
oMKgwqDCoMKgKiBjYXBhYmlsaXRpZXMgZm9yIGJlZm9yZSBpbnZva2luZyBwbWJ1c19kb19wcm9i
ZSgpLiBUaGUgcG1idXNfKigpCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgKiBjYWxscyBuZWVkIGFj
Y2VzcyB0byBtZW1vcnkgdGhhdCBpcyBvbmx5IHZhbGlkIGFmdGVyCj4gPiA+ICvCoMKgwqDCoMKg
wqDCoMKgKiBwbWJ1c19kb19wcm9iZSgpLgo+ID4gCj4gPiBJIHRoaW5rIHRoaXMgaXMgY29tbW9u
IGVub3VnaCBpbiBwbWJ1cyBkcml2ZXJzIHRoYXQgdGhlIGNvbW1lbnQgaXMgcmVkdW5kYW50LgoK
V2lsbCBkby4KCj4gPiAKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqAqLwo+ID4gPiArwqDCoMKgwqDC
oMKgwqBydiA9IGkyY19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2xpZW50LCBQTUJVU19QQUdFLCAy
NTUpOwo+ID4gPiArwqDCoMKgwqDCoMKgwqBpZiAocnYgPCAwKQo+ID4gPiArwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgcmV0dXJuIHJ2Owo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oHJ2ID0gaTJjX3NtYnVzX3JlYWRfd29yZF9kYXRhKGNsaWVudCwgUE1CVVNfTUZSX1JFVklTSU9O
KTsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgaWYgKHJ2IDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoHJldHVybiBydjsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqBp
ZiAoKHJ2ICYgMHhmZikgPT0gMHg0MCkgewo+ID4gCj4gPiBEVUFMX1RBQ0hfUkVWPyBPciBhIGJl
dHRlciBuYW1lIGZyb20gdGhlIGRvY3Mgd2UgaGF2ZT8KCkknbGwgaGF2ZSBhIHRoaW5rIGFib3V0
IGEgbmFtZS4KClRoYW5rcyBmb3IgdGhlIGZlZWRiYWNrIGd1eXMuCgpBbmRyZXcKCj4gPiAKPiA+
ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGNoaXAtPmNhcGFiaWxpdGllcyB8PSBN
QVgzMTc4NV9DQVBfRFVBTF9UQUNIOwo+ID4gPiArCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqAvKgo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAqIFB1
bnQgdGhlIGR1YWwgdGFjaCB2aXJ0dWFsIGZhbnMgdG8gbm9uLWV4aXN0ZW50IHBhZ2VzLiBUaGlz
Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoCogZW5zdXJlcyB0aGUgcHdt
IGF0dHJpYnV0ZXMgYXBwZWFyIGluIGEgY29udGlndW91cyBibG9jawo+ID4gPiArwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAqLwo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgY2hpcC0+aW5mby5wYWdlcyA9IDI5Owo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgY2hpcC0+aW5mby5mdW5jWzIzXSA9IFBNQlVTX0hBVkVfRkFOMTI7Cj4gPiA+ICvC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBjaGlwLT5pbmZvLmZ1bmNbMjRdID0gUE1CVVNf
SEFWRV9GQU4xMjsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGNoaXAtPmlu
Zm8uZnVuY1syNV0gPSBQTUJVU19IQVZFX0ZBTjEyOwo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgY2hpcC0+aW5mby5mdW5jWzI2XSA9IFBNQlVTX0hBVkVfRkFOMTI7Cj4gPiA+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBjaGlwLT5pbmZvLmZ1bmNbMjddID0gUE1C
VVNfSEFWRV9GQU4xMjsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoGNoaXAt
PmluZm8uZnVuY1syOF0gPSBQTUJVU19IQVZFX0ZBTjEyOwo+ID4gPiArwqDCoMKgwqDCoMKgwqB9
Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgcnYgPSBwbWJ1c19kb19wcm9iZShjbGllbnQs
IGlkLCAmY2hpcC0+aW5mbyk7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoGlmIChydiA8IDApCj4gPiA+
ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqByZXR1cm4gcnY7Cj4gPiA+ICsKPiA+ID4g
K8KgwqDCoMKgwqDCoMKgZm9yIChpID0gMDsgaSA8IG1heDMxNzg1X2luZm8ucGFnZXM7IGkrKykg
ewo+ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaW50IHJlZzsKPiA+ID4gKwo+
ID4gPiArwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgaWYgKCEobWF4MzE3ODVfaW5mby5m
dW5jW2ldICYgKFBNQlVTX0hBVkVfRkFOMTIpKSkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBjb250aW51ZTsKPiA+ID4gKwo+ID4gPiArwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgcmVnID0gcG1idXNfcmVhZF93b3JkX2RhdGEoY2xpZW50
LCBpLCBQTUJVU19NRlJfRkFOX0NPTkZJRyk7Cj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqBpZiAocmVnIDwgMCkKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqBjb250aW51ZTsKPiA+ID4gKwo+ID4gPiArwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgLyoKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
KiBYWFg6IFB1cmVseSBmb3IgUkZDL3Rlc3RpbmcgcHVycG9zZXMsIGRvbid0IHJhbXAgZmFucyBv
biBmYW4KPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgKiBvciB0ZW1wZXJh
dHVyZSBzZW5zb3IgZmF1bHQsIG9yIGEgZmFpbHVyZSB0byB3cml0ZQo+ID4gPiArwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqAqIEZBTl9DT01NQU5EXzEgaW5zaWRlIGEgMTBzIHdpbmRv
dyAod2F0Y2hkb2cgbW9kZSkuCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oCoKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgKiBUaGUgVFNGTyBiaXQg
Y29udHJvbHMgYm90aCByYW1waW5nIG9uIHRlbXAgc2Vuc29yIGZhaWx1cmUKPiA+ID4gK8KgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgKiBBTkQgd2hldGhlciBGQU5fQ09NTUFORF8xIGlz
IGluIHdhdGNoZG9nIG1vZGUuCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oCovCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqByZWcgfD0gTUZSX0ZBTl9D
T05GSUdfVFNGTyB8IE1GUl9GQU5fQ09ORklHX1RBQ0hPOwo+ID4gPiArwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgaWYgKGNoaXAtPmNhcGFiaWxpdGllcyAmIE1BWDMxNzg1X0NBUF9EVUFM
X1RBQ0gpCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDC
oMKgcmVnIHw9IE1GUl9GQU5fQ09ORklHX0RVQUxfVEFDSDsKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoHJlZyAmPSAweGZmZmY7Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDC
oMKgwqDCoMKgwqDCoMKgwqDCoHJ2ID0gcG1idXNfd3JpdGVfd29yZF9kYXRhKGNsaWVudCwgaSwg
UE1CVVNfTUZSX0ZBTl9DT05GSUcsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqBy
ZWcpOwo+ID4gPiArwqDCoMKgwqDCoMKgwqB9Cj4gPiA+ICsKPiA+ID4gK8KgwqDCoMKgwqDCoMKg
cmV0dXJuIDA7Cj4gPiA+ICt9Cj4gPiA+ICsKPiA+ID4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaTJj
X2RldmljZV9pZCBtYXgzMTc4NV9pZFtdID0gewo+ID4gPiArwqDCoMKgwqDCoMKgwqB7ICJtYXgz
MTc4NSIsIDAgfSwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgeyB9LAo+ID4gPiArfTsKPiA+ID4gKwo+
ID4gPiArTU9EVUxFX0RFVklDRV9UQUJMRShpMmMsIG1heDMxNzg1X2lkKTsKPiA+ID4gKwo+ID4g
PiArc3RhdGljIHN0cnVjdCBpMmNfZHJpdmVyIG1heDMxNzg1X2RyaXZlciA9IHsKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgLmRyaXZlciA9IHsKPiA+ID4gK8KgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoC5uYW1lID0gIm1heDMxNzg1IiwKPiA+ID4gK8KgwqDCoMKgwqDCoMKgfSwKPiA+ID4gK8Kg
wqDCoMKgwqDCoMKgLnByb2JlID0gbWF4MzE3ODVfcHJvYmUsCj4gPiA+ICvCoMKgwqDCoMKgwqDC
oC5yZW1vdmUgPSBwbWJ1c19kb19yZW1vdmUsCj4gPiA+ICvCoMKgwqDCoMKgwqDCoC5pZF90YWJs
ZSA9IG1heDMxNzg1X2lkLAo+ID4gPiArfTsKPiA+ID4gKwo+ID4gPiArbW9kdWxlX2kyY19kcml2
ZXIobWF4MzE3ODVfZHJpdmVyKTsKPiA+ID4gKwo+ID4gPiA+ID4gPiArTU9EVUxFX0FVVEhPUigi
QW5kcmV3IEplZmZlcnkgPGFuZHJld0Bhai5pZC5hdT4iKTsKPiA+ID4gK01PRFVMRV9ERVNDUklQ
VElPTigiUE1CdXMgZHJpdmVyIGZvciB0aGUgTWF4aW0gTUFYMzE3ODUiKTsKPiA+ID4gK01PRFVM
RV9MSUNFTlNFKCJHUEwiKTsKPiA+ID4gLS0KPiA+ID4gMi4xMS4wCj4gPiA+ID==

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web