switch to multi device pmw33xx driver

This commit is contained in:
Dylan (Luberry) Kozicki 2022-04-16 16:22:27 -04:00
parent f6af5a531f
commit f398f02e57
15 changed files with 1042 additions and 544 deletions

View file

@ -3,4 +3,4 @@
add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery) add_subdirectory_ifdef(CONFIG_ZMK_BATTERY battery)
add_subdirectory_ifdef(CONFIG_EC11 ec11) add_subdirectory_ifdef(CONFIG_EC11 ec11)
add_subdirectory_ifdef(CONFIG_PMW3389 pmw3389) add_subdirectory_ifdef(CONFIG_PMW33xx pmw33xx)

View file

@ -3,4 +3,4 @@
rsource "battery/Kconfig" rsource "battery/Kconfig"
rsource "ec11/Kconfig" rsource "ec11/Kconfig"
rsource "pmw3389/Kconfig" rsource "pmw33xx/Kconfig"

View file

@ -1,9 +0,0 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
zephyr_include_directories(.)
zephyr_library()
zephyr_library_sources(pmw3389.c)
zephyr_library_sources_ifdef(CONFIG_PMW3389_TRIGGER pmw3389_trigger.c)

View file

@ -1,49 +0,0 @@
config PMW3389
bool "PMW3389 Mouse Sensor"
depends on SPI
help
Enable mouse sensor
The PMW3389 is a 16-bit optical mouse sensor
if PMW3389
choice
prompt "Trigger mode"
default PMW3389_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config PMW3389_TRIGGER_NONE
bool "No trigger"
config PMW3389_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select PMW3389_TRIGGER
config PMW3389_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select PMW3389_TRIGGER
endchoice
config PMW3389_TRIGGER
bool
config PMW3389_THREAD_PRIORITY
int "Thread priority"
depends on PMW3389_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config PMW3389_THREAD_STACK_SIZE
int "Thread stack size"
depends on PMW3389_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # PMW3389

View file

@ -1,260 +0,0 @@
#define DT_DRV_COMPAT pixart_pmw3389
#include <drivers/spi.h>
#include <errno.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/byteorder.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include <sys/__assert.h>
#include <logging/log.h>
#include "pmw3389.h"
LOG_MODULE_REGISTER(PMW3389, CONFIG_SENSOR_LOG_LEVEL);
static int pmw3389_access(const struct device *dev, uint8_t reg, uint8_t *value) {
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf;
uint8_t access[1] = {reg};
struct spi_buf bufs[] = {
{
.buf = access,
.len = 1,
},
{
.buf = value,
.len = 1,
},
};
struct spi_buf_set tx = {
.buffers = bufs,
.count = 2,
};
struct spi_buf_set *rx;
if (reg & PMW3389_WR_MASK > 0) {
rx = &(struct spi_buf_set){
.buffers = bufs,
.count = 2,
};
}
return spi_transceive(data->bus, spi_cfg, &tx, rx);
}
static int pmw3389_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value) {
return pmw3389_access(dev, reg, value);
}
static int pmw3389_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value) {
return pmw3389_access(dev, reg & PMW3389_WR_MASK, &value);
}
// converts twos complement data to an int16
static int16_t pmw3389_raw_to_int16(const uint8_t src[2]) {
int16_t res = sys_get_be16(src[2]);
if (res > BIT_MASK(15))
res -= BIT(16);
return res;
}
static int pmw3389_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low,
int16_t *value) {
uint8_t raw[2] = {0x0, 0x0};
int err;
err = pmw3389_read_reg(dev, reg_high, &raw[0]);
if (err) {
LOG_ERR("could not read high byte at %x", reg_high);
return err;
}
err = pmw3389_read_reg(dev, reg_low, &raw[1]);
if (err) {
LOG_ERR("could not read low byte at %x", reg_low);
return err;
}
*value = pmw3389_raw_to_int16(raw);
return 0;
}
static bool pmw3389_spi_check_id(const struct device *dev) {
int err;
uint8_t val;
err = pmw3389_read_reg(dev, &val, PMW3389_REG_PID);
if (err) {
LOG_ERR("could not read PID");
return false;
}
if (val != PMW3389_PID) {
LOG_ERR("invalid PID");
return false;
}
err = pmw3389_read_reg(dev, &val, PMW3389_REG_REV);
if (err) {
LOG_ERR("could not read REV");
return false;
}
if (val != PMW3389_REV) {
LOG_ERR("invalid REV");
return false;
}
return true;
}
int pmw3389_spi_init(const struct device *dev) {
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
const struct pmw3389_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg;
if (spi_cfg->cs_gpios_label != NULL) {
/* handle SPI CS thru GPIO if it is the case */
data->cs_ctrl.gpio_dev = device_get_binding(spi_cfg->cs_gpios_label);
if (!data->cs_ctrl.gpio_dev) {
LOG_ERR("Unable to get GPIO SPI CS device");
return -ENODEV;
}
}
return 0;
}
static int pmw3389_sample_fetch(const struct device *dev, enum sensor_channel chan) {
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
uint16_t dx = 0, dy = 0;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY)
return -ENOTSUP;
if (!pmw3389_spi_check_id(dev))
return -EINVAL;
int err;
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX) {
err = pmw3389_read_raw(dev, PMW3389_REG_DX_H, PMW3389_REG_DX_L, &dx);
if (err) {
LOG_DBG("could not read x motion");
return -EIO;
}
}
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY) {
err = pmw3389_read_raw(dev, PMW3389_REG_DY_H, PMW3389_REG_DY_L, &dy);
if (err) {
LOG_DBG("could not read y motion");
return -EIO;
}
}
data->dx = dx;
data->dy = dy;
return 0;
}
static int pmw3389_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val) {
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
switch (chan) {
case SENSOR_CHAN_POS_DX:
val->val1 = data->dx;
case SENSOR_CHAN_POS_DY:
val->val1 = data->dy;
default:
return -ENOTSUP;
}
return 0;
}
static const struct sensor_driver_api pmw3389_driver_api = {
#ifdef CONFIG_PMW3389_TRIGGER
.trigger_set = pmw3389_trigger_set,
#endif
// .attr_set = pmw3389_attr_set,
.sample_fetch = pmw3389_sample_fetch,
.channel_get = pmw3389_channel_get,
};
static int pmw3389_init_chip(const struct device *dev) {
struct pmw3389_data *data = dev->data;
return 0;
}
static int pmw3389_init(const struct device *dev) {
const struct pmw3389_config *const config = dev->config;
struct pmw3389_data *data = dev->data;
data->bus = device_get_binding(config->bus_name);
if (!data->bus) {
LOG_DBG("master not found: %s", log_strdup(config->bus_name));
return -EINVAL;
}
config->bus_init(dev);
if (pmw3389_init_chip(dev) < 0) {
LOG_DBG("failed to initialize chip");
return -EIO;
}
#ifdef CONFIG_PMW3389_TRIGGER
if (pmw3389_init_interrupt(dev) < 0) {
LOG_DBG("Failed to initialize interrupt!");
return -EIO;
}
#endif
return 0;
}
#define PMW3389_HAS_CS(n) DT_INST_SPI_DEV_HAS_CS_GPIOS(n)
#define PMW3389_DATA_SPI_CS(n) \
{ \
.cs_ctrl = { \
.gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(n), \
.gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(n), \
}, \
}
#define PMW3389_DATA_SPI(n) COND_CODE_1(PMW3389_HAS_CS(n), (PMW3389_DATA_SPI_CS(n)), ({}))
#define PMW3389_SPI_CS_PTR(n) COND_CODE_1(PMW3389_HAS_CS(n), (&(pmw3389_data_##n.cs_ctrl)), (NULL))
#define PMW3389_SPI_CS_LABEL(n) \
COND_CODE_1(PMW3389_HAS_CS(n), (DT_INST_SPI_DEV_CS_GPIOS_LABEL(n)), (NULL))
#define PMW3389_SPI_CFG(n) \
(&(struct pmw3389_spi_cfg){ \
.spi_conf = \
{ \
.frequency = DT_INST_PROP(n, spi_max_frequency), \
.operation = \
(SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \
.slave = DT_INST_REG_ADDR(n), \
.cs = PMW3389_SPI_CS_PTR(n), \
}, \
.cs_gpios_label = PMW3389_SPI_CS_LABEL(n), \
})
#define PMW3389_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
{ \
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \
.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \
.dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \
}
#define PMW3389_CONFIG_SPI(n) \
{ \
.bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw3389_spi_init, \
.bus_cfg = {.spi_cfg = PMW3389_SPI_CFG(n)}, \
COND_CODE_1(CONFIG_MA730_TRIGGER, \
(, PMW3389_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), ()) \
}
#define PMW3389_INST(n) \
static struct pmw3389_data pmw3389_data_##n = PMW3389_DATA_SPI(n); \
static const struct pmw3389_config pmw3389_cfg_##n = PMW3389_CONFIG_SPI(n); \
DEVICE_DT_INST_DEFINE(n, pmw3389_init, device_pm_control_nop, &pmw3389_data_##n, \
&pmw3389_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&pmw3389_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PMW3389_INST)

View file

@ -1,92 +0,0 @@
#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_
#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_
#include <drivers/sensor.h>
#include <zephyr/types.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <drivers/spi.h>
#define PMW3389_WR_MASK 0x80
#define PMW3389_PID 0x47
#define PMW3389_REV 0x01
#define PMW3389_REG_PID 0x00
#define PMW3389_REG_REV 0x01
#define PMW3389_REG_DX_L 0x2A
#define PMW3389_REG_DX_H 0x2B
#define PMW3389_REG_DY_L 0x2C
#define PMW3389_REG_DY_H 0x2D
struct pmw3389_gpio_dt_spec {
const struct device *port;
gpio_pin_t pin;
gpio_dt_flags_t dt_flags;
};
struct pmw3389_spi_cfg {
struct spi_config spi_conf;
const char *cs_gpios_label;
};
union pmw3389_bus_cfg {
struct pmw3389_spi_cfg *spi_cfg;
};
struct pmw3389_config {
char *bus_name;
int (*bus_init)(const struct device *dev);
const union pmw3389_bus_cfg bus_cfg;
int resolution;
struct pmw3389_gpio_dt_spec reset_spec;
#if CONFIG_PMW3389_TRIGGER
struct pmw3389_gpio_dt_spec motswk_spec;
#endif // CONFIG_PMW3389_TRIGGER
};
struct pmw3389_data;
struct pmw3389_transfer_function {
int (*read_data)(const struct device *dev, uint16_t *value);
};
struct pmw3389_data {
const struct device *bus;
struct spi_cs_control cs_ctrl;
uint16_t dx;
uint16_t dy;
const struct pmw3389_transfer_function *hw_tf;
#ifdef CONFIG_PMW3389_TRIGGER
struct gpio_callback motswk_gpio_cb;
const struct device *dev;
sensor_trigger_handler_t handler;
const struct sensor_trigger *trigger;
#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE);
struct k_sem gpio_sem;
struct k_thread thread;
#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
#endif /* CONFIG_PMW3389_TRIGGER */
};
int pmw3389_spi_init(const struct device *dev);
#ifdef CONFIG_PMW3389_TRIGGER
int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int pmw3389_init_interrupt(const struct device *dev);
#endif
#endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW3389_H_ */

View file

@ -1,123 +0,0 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT pixart_pmw3389
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include "pmw3389.h"
// extern struct pmw3389_data pmw3389_driver;
#include <logging/log.h>
LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL);
static inline void setup_int(const struct device *dev, bool enable) {
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin,
enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) {
LOG_WRN("Unable to set MOTSWK GPIO interrupt");
}
}
static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins) {
struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb);
LOG_DBG("");
setup_int(drv_data->dev, false);
#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void pmw3389_thread_cb(const struct device *dev) {
struct pmw3389_data *drv_data = dev->data;
LOG_DBG("%p", drv_data->handler);
drv_data->handler(dev, drv_data->trigger);
// Enable once the wall/spam of interrupts is solved
setup_int(dev, true);
}
#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD
static void pmw3389_thread(int dev_ptr, int unused) {
const struct device *dev = INT_TO_POINTER(dev_ptr);
struct pmw3389_data *drv_data = dev->data;
ARG_UNUSED(unused);
while (1) {
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
pmw3389_thread_cb(dev);
}
}
#endif
#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD
static void pmw3389_work_cb(struct k_work *work) {
struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work);
LOG_DBG("");
pmw3389_thread_cb(drv_data->dev);
}
#endif
int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler) {
struct pmw3389_data *drv_data = dev->data;
setup_int(dev, false);
k_msleep(5);
drv_data->trigger = trig;
drv_data->handler = handler;
setup_int(dev, true);
return 0;
}
int pmw3389_init_interrupt(const struct device *dev) {
struct pmw3389_data *drv_data = dev->data;
const struct pmw3389_config *drv_cfg = dev->config;
drv_data->dev = dev;
/* setup gpio interrupt */
gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback,
BIT(drv_cfg->motswk_spec.pin));
if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) {
LOG_DBG("Failed to set MOTSWK callback!");
return -EIO;
}
#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD)
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE,
(k_thread_entry_t)pmw3389_thread, dev, 0, NULL,
K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT);
#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD)
k_work_init(&drv_data->work, pmw3389_work_cb);
#endif
return 0;
}

1
app/drivers/sensor/pmw33xx/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
pmw3389_srom.h

View file

@ -0,0 +1,10 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT
zephyr_include_directories(.)
zephyr_include_directories_ifdef(CONFIG_PMW33XX_3389 CONFIG_PMW33XX_3389_SROM_INCLUDE_DIR)
zephyr_library()
zephyr_library_sources(pmw33xx.c)
zephyr_library_sources_ifdef(CONFIG_PMW33XX_TRIGGER pmw33xx_trigger.c)

View file

@ -0,0 +1,67 @@
config PMW33XX
bool "PMW33XX Mouse Sensor"
depends on SPI
help
Enable mouse sensor
The PMW33XX is a 16-bit optical mouse sensor
if PMW33XX
choice
prompt "Sensor"
default PMW33XX_3360
config PMW33XX_3360
bool "Enable PMW3360"
config PMW33XX_3389
bool "Enable PMW3389"
help
PMW3389 requires an external srom included in `pmw3389_srom.h` which we cannot currently include in zmk
endchoice
config PMW33XX_3389_SROM_INCLUDE_DIR
string "PMW3389 SROM Include Dir"
default "."
help
directory where the header with the pmw3389 srom file is located
choice
prompt "Trigger mode"
default PMW33XX_TRIGGER_NONE
help
Specify the type of triggering to be used by the driver.
config PMW33XX_TRIGGER_NONE
bool "No trigger"
config PMW33XX_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select PMW33XX_TRIGGER
config PMW33XX_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select PMW33XX_TRIGGER
endchoice
config PMW33XX_TRIGGER
bool
config PMW33XX_THREAD_PRIORITY
int "Thread priority"
depends on PMW33XX_TRIGGER_OWN_THREAD
default 10
help
Priority of thread used by the driver to handle interrupts.
config PMW33XX_THREAD_STACK_SIZE
int "Thread stack size"
depends on PMW33XX_TRIGGER_OWN_THREAD
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif # PMW33XX

View file

@ -0,0 +1,291 @@
/*
This SROM is provided "AS IS" by PixArt Imaging Inc., WITHOUT WARRANTY
OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PIXART
IMAGING INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THIS
SROM OR THE USE OR OTHER DEALINGS IN THIS SROM.
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_
#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_PMW3360_SROM_H_
#include <stdint.h>
static const uint8_t SROM[] = {
0x01, 0x05, 0x8d, 0x92, 0x66, 0x67, 0x1e, 0xbe, 0xfe, 0x5f, 0x1d, 0xb8, 0xf2, 0x66, 0x4e,
0xff, 0x5d, 0x38, 0xf2, 0x46, 0x0c, 0x98, 0x97, 0x8d, 0x79, 0x51, 0x20, 0xc2, 0x06, 0x8e,
0x9e, 0xbe, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xd3, 0x24, 0xca, 0x16, 0x8f, 0x7d, 0x78, 0x53,
0x05, 0x88, 0x73, 0x45, 0xe9, 0x55, 0x22, 0xa7, 0xae, 0xd8, 0x32, 0xe6, 0x2f, 0xbd, 0xf8,
0x72, 0x47, 0x0c, 0x7b, 0x74, 0x6a, 0x56, 0x2e, 0xbf, 0xdd, 0x38, 0xd5, 0x20, 0xca, 0x16,
0xae, 0xde, 0x1f, 0x9d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b, 0x55, 0x28, 0xd2, 0x26, 0xaf, 0xdc,
0x3a, 0xd7, 0x2c, 0xbb, 0xd5, 0x09, 0x90, 0xa2, 0xa7, 0xcc, 0xfb, 0x74, 0x4b, 0xf5, 0x49,
0x10, 0x83, 0x84, 0x8a, 0x77, 0x4d, 0x18, 0xb2, 0xe6, 0x4e, 0x1e, 0x9f, 0xbc, 0xfa, 0x6a,
0xea, 0xab, 0x55, 0x91, 0xa9, 0xda, 0x58, 0xa9, 0xe1, 0x1a, 0xc2, 0x52, 0x1c, 0x0d, 0xef,
0x50, 0x71, 0x04, 0x21, 0x93, 0x87, 0x0e, 0xb1, 0xa9, 0x4a, 0xc0, 0x24, 0xaf, 0xe3, 0x26,
0xa2, 0x8d, 0xa1, 0x6d, 0xf2, 0x88, 0x9e, 0x99, 0xbb, 0xf2, 0x62, 0x38, 0xfe, 0x78, 0x1d,
0x58, 0x19, 0x9b, 0xbe, 0x10, 0x6c, 0x15, 0x9e, 0x50, 0x8b, 0x99, 0x40, 0x92, 0x01, 0x38,
0xb6, 0x97, 0x83, 0x51, 0x59, 0xa6, 0xb7, 0xc9, 0x48, 0xfd, 0x68, 0x21, 0x78, 0xf4, 0x2e,
0x86, 0x61, 0xb0, 0xb2, 0x2b, 0x5b, 0xf0, 0x1b, 0x71, 0xa3, 0x0a, 0xd5, 0xd6, 0x30, 0x7a,
0x0c, 0xf8, 0xaf, 0xc6, 0x54, 0x6b, 0x89, 0x30, 0x58, 0xe8, 0xe2, 0x0d, 0x39, 0x2f, 0x47,
0xa0, 0x41, 0x74, 0x5d, 0xd6, 0xf3, 0x85, 0xb7, 0x66, 0x59, 0x51, 0x74, 0x3d, 0xfe, 0xee,
0x3a, 0x1e, 0x2a, 0x5f, 0x93, 0x0c, 0x3f, 0xd4, 0x4d, 0x24, 0x13, 0x2c, 0xf2, 0x7b, 0x4d,
0x06, 0x60, 0x2e, 0x39, 0xa9, 0xd4, 0x72, 0x36, 0xb6, 0x4d, 0xea, 0x8c, 0xd9, 0xa7, 0xbc,
0x38, 0xe7, 0xbb, 0xb7, 0x7b, 0x04, 0x08, 0x83, 0x5a, 0x27, 0x6f, 0x42, 0xc6, 0x7e, 0xbd,
0x97, 0x0e, 0x55, 0xdc, 0x79, 0x63, 0xd8, 0x89, 0xc1, 0x83, 0x3d, 0x34, 0x01, 0x76, 0xff,
0xde, 0x7f, 0x65, 0x4b, 0xb3, 0x46, 0x56, 0x8d, 0x9e, 0x6f, 0x1f, 0x2b, 0xa4, 0x69, 0x54,
0xde, 0x1c, 0x2d, 0xa8, 0x51, 0x7e, 0xdf, 0xbb, 0x18, 0x90, 0x27, 0xaf, 0x79, 0xd1, 0x86,
0x4f, 0x44, 0x88, 0x7f, 0xb9, 0x80, 0x21, 0xa9, 0x5d, 0x7b, 0xd4, 0x81, 0x21, 0xa9, 0x13,
0x59, 0x72, 0x58, 0x11, 0xe3, 0xf2, 0xf7, 0xae, 0xc3, 0xc2, 0x64, 0x1d, 0xfb, 0xd8, 0x91,
0x2d, 0x13, 0xb5, 0x0e, 0x67, 0xdb, 0xbc, 0x63, 0xde, 0xa0, 0x86, 0x49, 0x6c, 0x40, 0x82,
0x82, 0x3f, 0xeb, 0x38, 0x22, 0xa8, 0x20, 0x1e, 0xc5, 0x5f, 0x25, 0xea, 0xaf, 0xe6, 0x78,
0x1c, 0x41, 0x0b, 0x5a, 0x94, 0xcc, 0x8d, 0x0d, 0xc5, 0xb8, 0x9c, 0x1a, 0x9f, 0x6d, 0xa8,
0x57, 0xe9, 0x1a, 0xf0, 0xad, 0x6c, 0x26, 0x5f, 0xb8, 0x38, 0x16, 0x0e, 0x28, 0xd6, 0x76,
0x5a, 0xc2, 0xb7, 0x6a, 0x9e, 0x26, 0x9d, 0x3e, 0x1f, 0x25, 0xb0, 0x14, 0x46, 0xcd, 0x67,
0x49, 0x93, 0x13, 0x81, 0x01, 0x46, 0x72, 0x31, 0x42, 0x91, 0x87, 0x6c, 0x1c, 0xb8, 0x08,
0x72, 0x6a, 0x33, 0x64, 0xed, 0xc0, 0x99, 0xb9, 0xf6, 0x34, 0xe9, 0xa5, 0x6c, 0x57, 0x9a,
0x72, 0x18, 0x48, 0x62, 0xc1, 0x88, 0xcf, 0xa3, 0x50, 0x53, 0x50, 0xdf, 0xc8, 0x9f, 0xf0,
0xb6, 0xa2, 0x23, 0xc4, 0xaa, 0xe3, 0xd0, 0xe2, 0xab, 0x76, 0xc8, 0x4a, 0x02, 0x21, 0xdd,
0xf3, 0x59, 0xdc, 0x30, 0x6e, 0xb5, 0x42, 0xe2, 0x9d, 0x8a, 0xdf, 0xfd, 0xa5, 0x41, 0x75,
0x01, 0x83, 0xbe, 0x47, 0xa5, 0x21, 0x30, 0x3b, 0x0e, 0x33, 0x2e, 0x74, 0xaf, 0x27, 0x90,
0xf4, 0x20, 0xa7, 0x1b, 0xff, 0x77, 0x01, 0xab, 0x79, 0x0a, 0xfb, 0x86, 0x1e, 0x1f, 0x22,
0x6d, 0x75, 0x75, 0xfc, 0xe0, 0x8c, 0xa2, 0x72, 0x0f, 0x45, 0x48, 0xf9, 0x43, 0x10, 0xf0,
0x57, 0x15, 0x17, 0x74, 0x40, 0x8f, 0x77, 0x69, 0x8a, 0xd4, 0x0f, 0x25, 0x27, 0x0a, 0xe8,
0x84, 0x73, 0x41, 0xb9, 0x5f, 0xb6, 0xc8, 0x2f, 0x02, 0x83, 0xdc, 0x1e, 0x1c, 0xdb, 0x3f,
0x2d, 0x58, 0xf7, 0x5e, 0xfd, 0x6f, 0xda, 0xad, 0xdc, 0x43, 0x01, 0x22, 0xc6, 0x45, 0x9c,
0xec, 0x56, 0x8a, 0x1a, 0x4e, 0xaa, 0xae, 0x3a, 0xa1, 0xf3, 0xf1, 0x9a, 0x97, 0x8d, 0x79,
0x51, 0x01, 0xf5, 0x31, 0x04, 0xcd, 0x2a, 0xbc, 0xfd, 0xac, 0x59, 0x1b, 0x0a, 0xe2, 0x26,
0xdd, 0x2f, 0x96, 0xaa, 0x62, 0xc5, 0x6d, 0xb8, 0xe1, 0x72, 0x4e, 0x4e, 0xb7, 0x4f, 0x9f,
0xaa, 0x33, 0x75, 0x8b, 0x43, 0x44, 0x0a, 0xc3, 0x0a, 0x5e, 0x9c, 0xdb, 0x1e, 0x4e, 0xbd,
0x0f, 0xec, 0x09, 0x96, 0x5a, 0x37, 0x6d, 0x10, 0x44, 0xd4, 0x91, 0xc0, 0xc3, 0x0d, 0x35,
0x62, 0xcf, 0x0a, 0xb1, 0x1a, 0x8a, 0xda, 0xa8, 0xcf, 0xbc, 0x3a, 0xb2, 0x4f, 0x59, 0x29,
0xd9, 0x91, 0x96, 0x0d, 0x6b, 0x9c, 0xcf, 0x34, 0x5b, 0x76, 0x7b, 0xb2, 0xb2, 0xce, 0xbf,
0x72, 0xd9, 0x96, 0x4a, 0xc0, 0x2d, 0x89, 0x33, 0xd0, 0xe4, 0xbe, 0x69, 0x7f, 0x1c, 0x9e,
0x71, 0x19, 0x12, 0xf6, 0x74, 0xfb, 0xd6, 0x41, 0x95, 0x69, 0xf0, 0xe9, 0xa7, 0x50, 0x82,
0x04, 0x78, 0x4d, 0x99, 0xb3, 0xa7, 0xfe, 0xee, 0x7f, 0xed, 0x56, 0x57, 0x57, 0xe9, 0xd3,
0x6a, 0x43, 0xf4, 0x0b, 0x6d, 0xba, 0x34, 0xde, 0xf8, 0x4c, 0x45, 0xae, 0x00, 0x32, 0x1a,
0x4e, 0xff, 0x54, 0x60, 0x6a, 0xd9, 0x59, 0x18, 0xd3, 0xbe, 0x5a, 0x94, 0x16, 0x9d, 0xec,
0xdf, 0xb4, 0x48, 0x8e, 0x6f, 0x19, 0xf8, 0xe6, 0xec, 0x7f, 0xb7, 0x49, 0x92, 0xb3, 0xb6,
0xec, 0x7e, 0xdc, 0x08, 0x3b, 0xe5, 0x5b, 0x88, 0xf9, 0x92, 0xf3, 0x7c, 0x9d, 0x7c, 0x22,
0xf2, 0x5d, 0x7d, 0x76, 0x07, 0x2e, 0x42, 0xd6, 0x9e, 0x5a, 0xbc, 0x7d, 0xfd, 0xb5, 0x92,
0x4a, 0x7f, 0xbf, 0xac, 0xc0, 0xb6, 0x18, 0x8b, 0x02, 0x71, 0xae, 0xc7, 0xa8, 0x8c, 0x19,
0xc1, 0x0a, 0x36, 0x3e, 0xa4, 0x2d, 0xa6, 0xbb, 0xe8, 0x77, 0x34, 0x45, 0xca, 0x60, 0x52,
0xf0, 0x26, 0xf7, 0x58, 0x68, 0x34, 0x28, 0xb5, 0x29, 0xe5, 0xb8, 0x65, 0x0f, 0xc8, 0xc2,
0x64, 0x50, 0x91, 0x12, 0xa1, 0x7b, 0xd7, 0x09, 0x74, 0x5f, 0xcc, 0xac, 0x9d, 0xac, 0x0a,
0xd1, 0x46, 0xbe, 0xc2, 0x2d, 0xa6, 0x67, 0x69, 0x69, 0x55, 0x8a, 0xbd, 0x67, 0xd9, 0x31,
0xc7, 0x80, 0x7c, 0xd5, 0xb2, 0xbe, 0x83, 0x17, 0x7a, 0x2e, 0xf4, 0x3e, 0x14, 0xa5, 0x62,
0x77, 0xa5, 0xdb, 0x77, 0x58, 0x35, 0x5f, 0xc4, 0x6c, 0xd2, 0xf2, 0x38, 0x91, 0x74, 0x80,
0x95, 0x52, 0xd2, 0xaf, 0xe2, 0x1b, 0x12, 0xfe, 0x4b, 0x5f, 0x84, 0x5f, 0x7d, 0x7e, 0x0b,
0xd9, 0x42, 0x72, 0xf1, 0x32, 0x1d, 0x8d, 0x5c, 0xcb, 0x20, 0x05, 0x4f, 0xbe, 0xbf, 0x40,
0xe9, 0xa6, 0x9f, 0xff, 0x05, 0xe1, 0x73, 0x52, 0x65, 0xb4, 0x59, 0x45, 0x04, 0x10, 0xe1,
0xdc, 0x0b, 0x30, 0xaa, 0x23, 0x4d, 0xb9, 0x58, 0xe5, 0xf1, 0xa3, 0x9c, 0xf2, 0xf7, 0xaf,
0xbc, 0xe3, 0xa7, 0x0d, 0x68, 0x97, 0x6c, 0x2b, 0x33, 0x60, 0xe3, 0xce, 0x7c, 0xda, 0xd1,
0x2d, 0x78, 0x52, 0x10, 0xa0, 0xf7, 0x9a, 0xf4, 0x79, 0xdb, 0xa5, 0x2a, 0x71, 0xc6, 0xcf,
0xa0, 0x68, 0xd0, 0x32, 0x2a, 0xd3, 0xb2, 0xe8, 0x03, 0x27, 0xbe, 0x55, 0xfd, 0x0e, 0x90,
0xa2, 0x76, 0x8c, 0xdc, 0xbd, 0x69, 0x9c, 0xb6, 0x18, 0x92, 0xe3, 0xb3, 0x23, 0x1d, 0x3c,
0x85, 0x2a, 0x2b, 0x04, 0x3a, 0xf5, 0xee, 0xf8, 0xd2, 0xdd, 0x6d, 0x52, 0xa2, 0x45, 0x75,
0xd9, 0xa1, 0x03, 0x23, 0x83, 0x24, 0x04, 0xe7, 0xee, 0xa2, 0x14, 0x4e, 0xa9, 0xde, 0x4e,
0x9d, 0x24, 0x1a, 0x63, 0xd3, 0x4b, 0xf7, 0x71, 0xc1, 0xe7, 0xfb, 0x48, 0xa4, 0x7c, 0x39,
0xcb, 0x80, 0xdd, 0x5e, 0xaf, 0x0d, 0xca, 0xef, 0x58, 0x17, 0xf4, 0xca, 0x0c, 0x00, 0x30,
0x68, 0x3e, 0x5d, 0x9b, 0xc3, 0x19, 0x27, 0x7a, 0x35, 0x32, 0x5c, 0x7b, 0x92, 0x16, 0x7f,
0x6e, 0x86, 0x6b, 0x50, 0x7a, 0xd6, 0x15, 0x10, 0x16, 0xa8, 0x28, 0xb1, 0xa1, 0xd8, 0xda,
0xc8, 0xfd, 0x5b, 0xe2, 0xc0, 0xd6, 0x8d, 0x75, 0xbd, 0x1c, 0x0b, 0x27, 0xfe, 0x1d, 0x09,
0x23, 0x13, 0x4f, 0x5a, 0x5a, 0xf8, 0xbe, 0x5d, 0x31, 0x8a, 0x81, 0xaf, 0xbe, 0xed, 0x00,
0x43, 0x14, 0x8f, 0xa5, 0x4a, 0xfe, 0x90, 0xe1, 0x67, 0x25, 0x29, 0x52, 0x2f, 0x26, 0x17,
0xef, 0x6e, 0xe6, 0x1e, 0xaf, 0x7f, 0x4a, 0x40, 0x15, 0xc7, 0x6e, 0x8d, 0x60, 0x92, 0xd6,
0x8d, 0x95, 0xca, 0xff, 0x8e, 0xfe, 0x4b, 0x8d, 0x1a, 0x83, 0x40, 0xe6, 0xb7, 0xbc, 0x6b,
0xd6, 0x39, 0x53, 0xd0, 0x4a, 0xe6, 0x1f, 0x1f, 0xb1, 0x16, 0x6a, 0xae, 0x6a, 0x6d, 0xe3,
0xe6, 0xed, 0x4e, 0xff, 0x49, 0x28, 0xc7, 0xaf, 0xb4, 0x14, 0x9f, 0x85, 0x9f, 0xfe, 0x6a,
0x92, 0x93, 0x9c, 0x6f, 0xff, 0x4a, 0xd4, 0x3e, 0x26, 0xd9, 0x72, 0x4f, 0x01, 0xe2, 0x54,
0x06, 0xc6, 0x7d, 0xb8, 0xef, 0x0b, 0x04, 0x56, 0x3a, 0x51, 0x41, 0xe2, 0x1d, 0xc2, 0x96,
0x95, 0xd1, 0x66, 0x16, 0x3b, 0x50, 0x36, 0x3f, 0x2e, 0xf2, 0xb2, 0x25, 0x2d, 0x59, 0x0f,
0x23, 0xf2, 0x9d, 0xb0, 0xee, 0x59, 0x89, 0xd0, 0x79, 0x30, 0xc7, 0x54, 0x9b, 0xec, 0xf8,
0xd7, 0xdc, 0x5a, 0x11, 0x1d, 0xfb, 0xf0, 0x82, 0x33, 0x3c, 0xff, 0x17, 0xff, 0xfc, 0x92,
0xc6, 0x6f, 0x3d, 0x5d, 0x99, 0x85, 0x2a, 0xa1, 0x04, 0xe8, 0x21, 0xed, 0x12, 0x2b, 0x7d,
0xbc, 0xa3, 0x36, 0x9b, 0x9f, 0xfd, 0x5d, 0x60, 0x66, 0xe9, 0x39, 0x5e, 0x34, 0x29, 0xa7,
0x08, 0x56, 0xf7, 0x4d, 0x19, 0x8d, 0x91, 0xff, 0xc8, 0x8a, 0xe7, 0xe8, 0x22, 0x00, 0x35,
0x68, 0x34, 0x93, 0xe7, 0x3b, 0x41, 0xbb, 0xf6, 0x58, 0x2f, 0x10, 0x66, 0x77, 0x69, 0xcd,
0x94, 0xb9, 0xbe, 0x59, 0x73, 0x4f, 0xc3, 0x11, 0xa1, 0xb3, 0x13, 0xe2, 0x82, 0xf1, 0x05,
0xcb, 0xa7, 0x75, 0xf6, 0x01, 0xb2, 0x24, 0xa9, 0x59, 0x05, 0x59, 0x46, 0xc9, 0x25, 0x39,
0x36, 0xee, 0x48, 0x90, 0x83, 0x89, 0xc4, 0xdb, 0xa3, 0x83, 0xd0, 0xd3, 0x62, 0x3f, 0x5e,
0x0e, 0x0c, 0xb7, 0x77, 0xb3, 0xd6, 0xbb, 0x37, 0x70, 0xb2, 0xb3, 0x87, 0x60, 0xd7, 0x8a,
0xe2, 0x99, 0xa9, 0x0f, 0x18, 0x16, 0xf6, 0xcd, 0x14, 0x4e, 0x2c, 0x7e, 0xfe, 0xf9, 0xa4,
0x48, 0xfb, 0xb9, 0x60, 0x13, 0x01, 0xd2, 0x6e, 0xdc, 0x3f, 0xe7, 0x2d, 0x7f, 0xe1, 0x11,
0x96, 0x12, 0x9a, 0x90, 0x48, 0x2b, 0x76, 0x78, 0xa6, 0xdd, 0x67, 0x48, 0x68, 0x42, 0xfd,
0x74, 0xb6, 0x51, 0xd7, 0x99, 0x07, 0x7d, 0x8b, 0x02, 0xd6, 0x68, 0x36, 0x97, 0x6d, 0x49,
0x28, 0xbf, 0xcc, 0x23, 0x8f, 0xed, 0x78, 0x4c, 0x49, 0xb0, 0xd1, 0x9a, 0x3d, 0x1c, 0xdd,
0xf0, 0xeb, 0xd5, 0xd4, 0xc2, 0x12, 0xae, 0x5d, 0x00, 0xe0, 0xf4, 0x84, 0x10, 0x08, 0x76,
0x28, 0x5a, 0x5e, 0xde, 0xe3, 0xad, 0x4e, 0x56, 0xcc, 0x22, 0x85, 0x5f, 0xa9, 0xca, 0xa5,
0x4e, 0xc6, 0x82, 0xf0, 0xbb, 0x5a, 0x82, 0xbf, 0x32, 0x54, 0xd2, 0x8a, 0x12, 0x05, 0x6f,
0xd8, 0xd1, 0xc0, 0x60, 0x00, 0x41, 0x03, 0xf4, 0xa8, 0xd6, 0xee, 0x9e, 0x5a, 0x75, 0xea,
0x76, 0x88, 0xf1, 0x2d, 0x8d, 0x63, 0xfc, 0x14, 0xe9, 0x7b, 0xeb, 0x0b, 0x49, 0xa4, 0x8b,
0x86, 0xf4, 0x97, 0x61, 0x22, 0x29, 0x32, 0x23, 0x80, 0x2e, 0x59, 0x44, 0xea, 0xf5, 0x56,
0x18, 0x51, 0xc0, 0x60, 0xe1, 0x83, 0x66, 0x1f, 0x1f, 0xa1, 0x13, 0x64, 0xae, 0xbc, 0x59,
0xd0, 0xc4, 0x38, 0x70, 0x3f, 0xb5, 0xb5, 0xa2, 0xdc, 0x89, 0x77, 0x70, 0x5a, 0x86, 0xb7,
0xde, 0x67, 0x3c, 0x22, 0x83, 0xc8, 0x62, 0x77, 0xb6, 0x5d, 0x7e, 0x41, 0x99, 0x70, 0x7d,
0xc3, 0x27, 0x90, 0x7f, 0x3c, 0xc6, 0x90, 0xc7, 0xb9, 0x32, 0xd8, 0x01, 0xdf, 0x02, 0x20,
0x27, 0x8f, 0x7b, 0x76, 0xb3, 0x0d, 0xfa, 0x93, 0x46, 0x6f, 0xde, 0x23, 0x12, 0xb9, 0x4e,
0xb8, 0x36, 0x8e, 0x9c, 0x3a, 0x2f, 0xd4, 0x2b, 0x54, 0xab, 0x63, 0xe6, 0xf1, 0x62, 0x8c,
0xd1, 0x1e, 0x87, 0x48, 0x2d, 0x83, 0x93, 0xfe, 0xac, 0x29, 0x82, 0xc2, 0x3c, 0x4f, 0xd0,
0x19, 0xe7, 0xa3, 0x99, 0x8d, 0x7d, 0x64, 0x15, 0xde, 0x64, 0xdb, 0xc1, 0x73, 0xe0, 0x78,
0x24, 0x23, 0x9e, 0x8a, 0xfb, 0xe2, 0xea, 0xd5, 0x0f, 0x93, 0x51, 0x62, 0x38, 0x41, 0x3e,
0x40, 0xa4, 0x2f, 0x7e, 0xb8, 0x11, 0x5c, 0xd3, 0x46, 0x0a, 0x74, 0xcb, 0xb7, 0xd0, 0x15,
0x96, 0xf1, 0xe7, 0x48, 0x32, 0x05, 0x69, 0xa8, 0xda, 0x56, 0xee, 0x9f, 0x6a, 0x95, 0xe6,
0x0d, 0xd2, 0x2c, 0xc1, 0xc2, 0xa5, 0xf4, 0xd8, 0xc6, 0xd7, 0xf6, 0x1f, 0x6f, 0x88, 0x78,
0xab, 0xd4, 0x1f, 0x53, 0xa0, 0xa1, 0x3b, 0x03, 0xb1, 0x4a, 0xcd, 0xbe, 0x25, 0x1c, 0x6f,
0xb4, 0x30, 0x41, 0xb5, 0x4a, 0xaa, 0xa5, 0x38, 0x91, 0x77, 0xce, 0x48, 0xc8, 0xe1, 0x34,
0xc2, 0x85, 0xdc, 0x9b, 0x24, 0x6f, 0x63, 0x12, 0xaa, 0x97, 0xaf, 0x9e, 0x37, 0x1e, 0xaf,
0x94, 0x2c, 0x5c, 0x43, 0xa6, 0x93, 0x17, 0x7d, 0x1c, 0x77, 0x18, 0x0f, 0xb5, 0xf2, 0x66,
0x4a, 0xaf, 0xd9, 0x92, 0xfb, 0x47, 0xfc, 0x1f, 0x7a, 0xdb, 0x77, 0x0b, 0x11, 0x61, 0xe0,
0x85, 0x12, 0x96, 0xf7, 0x55, 0x09, 0x90, 0xa2, 0x77, 0xdb, 0x9c, 0x9c, 0x8b, 0x04, 0x55,
0x45, 0x39, 0x64, 0x4e, 0x46, 0x5f, 0xa2, 0x98, 0xbc, 0xd6, 0x1b, 0x5d, 0x5f, 0x67, 0x21,
0xc7, 0x12, 0xe5, 0xe6, 0x13, 0x93, 0x7b, 0x83, 0xd8, 0x5b, 0xcc, 0x2e, 0x77, 0xce, 0x56,
0x97, 0xdc, 0x96, 0x70, 0xcc, 0x47, 0x1a, 0x79, 0x1f, 0xb0, 0x6a, 0xe4, 0x7c, 0xd6, 0xa7,
0x28, 0x72, 0x7a, 0x1e, 0xac, 0x54, 0xc0, 0x5d, 0x8c, 0xa1, 0x00, 0xf4, 0x2e, 0x86, 0x28,
0x3a, 0x82, 0xce, 0xbc, 0xf0, 0xfd, 0x2d, 0xb1, 0xc4, 0x9f, 0x6d, 0x6b, 0xba, 0x9e, 0x9b,
0xec, 0xb5, 0x38, 0xe2, 0xe9, 0xf8, 0x57, 0x74, 0xa6, 0x9b, 0x7b, 0xec, 0xe2, 0x90, 0x0d,
0xec, 0x3e, 0xf0, 0x1b, 0x2b, 0x38, 0x9c, 0x82, 0xd9, 0xd1, 0xbe, 0xe1, 0x08, 0x7f, 0x02,
0x54, 0xbc, 0x0b, 0x90, 0x14, 0x57, 0xa2, 0x4e, 0x56, 0xbe, 0x1b, 0xdf, 0x48, 0x9c, 0x17,
0xa1, 0x70, 0xa2, 0x8c, 0x74, 0x74, 0x24, 0xbf, 0xe0, 0x46, 0xb7, 0xdc, 0xa4, 0xb5, 0x66,
0x44, 0xb3, 0x53, 0x0e, 0xa4, 0xe5, 0xb4, 0x83, 0x06, 0x1f, 0x78, 0x2a, 0xe2, 0x16, 0x5c,
0x79, 0x00, 0xab, 0x0a, 0x08, 0x57, 0xe5, 0xbc, 0x2c, 0x9b, 0x91, 0x19, 0xf6, 0x1d, 0x31,
0x25, 0xbb, 0x9b, 0xb5, 0x89, 0x32, 0xb7, 0x53, 0xb9, 0x49, 0xa4, 0x55, 0xc5, 0x9d, 0x2b,
0xa1, 0xeb, 0xd5, 0xec, 0x02, 0x40, 0x14, 0x44, 0x35, 0xc1, 0x65, 0xd7, 0xae, 0x4c, 0xf8,
0x16, 0xf5, 0x70, 0x62, 0x28, 0xcd, 0x58, 0x71, 0x01, 0xcb, 0x85, 0x09, 0x24, 0x91, 0xf0,
0x18, 0xfa, 0xc6, 0x37, 0x07, 0xde, 0xe7, 0x43, 0xed, 0x3a, 0xeb, 0x98, 0x97, 0xf4, 0xc5,
0xf3, 0x60, 0xa5, 0x83, 0x60, 0x1a, 0xf1, 0x81, 0x03, 0x06, 0x2c, 0x1f, 0x38, 0x71, 0x61,
0x30, 0x21, 0x8a, 0x49, 0xb4, 0x2a, 0xa6, 0x03, 0x0e, 0x1d, 0x1d, 0x79, 0xc0, 0xe5, 0x5d,
0xd1, 0x82, 0x37, 0x1b, 0x70, 0x3a, 0xd2, 0x65, 0x48, 0x59, 0xc0, 0xa2, 0xf3, 0x8b, 0x82,
0x24, 0xf5, 0xfe, 0x2a, 0xdd, 0x8d, 0x3a, 0xe9, 0xe5, 0x1c, 0x92, 0xc7, 0x6a, 0x43, 0xde,
0x78, 0xb3, 0x33, 0xa0, 0x9a, 0x78, 0x4a, 0x54, 0xba, 0x01, 0x84, 0xd2, 0xb2, 0xa4, 0x6f,
0x28, 0x21, 0xf2, 0xc4, 0x21, 0x9f, 0x18, 0x72, 0x16, 0x46, 0x6d, 0xfa, 0xf2, 0xa7, 0x7c,
0x99, 0xa4, 0x2c, 0x93, 0x4d, 0xba, 0xdd, 0xa7, 0x59, 0x50, 0x0b, 0x34, 0x48, 0x63, 0x6f,
0x0d, 0x4b, 0x81, 0x22, 0x3b, 0x24, 0x9b, 0x37, 0x4b, 0xd3, 0x94, 0x23, 0xc0, 0x71, 0x39,
0x91, 0xd0, 0x06, 0x2e, 0xd2, 0xae, 0x7c, 0xfd, 0xfd, 0x4d, 0x4a, 0x95, 0x0a, 0xd0, 0x84,
0xbf, 0x8c, 0x5c, 0xe4, 0xc1, 0x10, 0xfa, 0xe6, 0x26, 0x18, 0xea, 0x75, 0x3e, 0x7d, 0x0b,
0xc5, 0x9d, 0xbb, 0x14, 0x3c, 0xa5, 0xe3, 0x19, 0x2b, 0xdd, 0xcd, 0xbb, 0xb4, 0x48, 0x9e,
0x8c, 0xa5, 0xf2, 0xcb, 0x19, 0xa9, 0x8f, 0x0c, 0x0b, 0x36, 0x73, 0xd7, 0x78, 0x2b, 0x64,
0xc9, 0x8c, 0x4a, 0xb2, 0x8f, 0xe9, 0xd9, 0x51, 0xb1, 0x84, 0x49, 0xd1, 0xec, 0x73, 0xa6,
0xb1, 0x41, 0x70, 0xa0, 0xd3, 0xea, 0xb4, 0xf8, 0x50, 0x57, 0xb6, 0x69, 0xda, 0x4a, 0xe6,
0xe0, 0x73, 0xba, 0xdc, 0x98, 0xc4, 0x0e, 0xab, 0xc7, 0xbd, 0x8a, 0xf6, 0xe3, 0xc1, 0x0b,
0xc3, 0x5b, 0x48, 0xa1, 0x79, 0xab, 0xae, 0x9b, 0x17, 0xbd, 0x56, 0x3b, 0x0c, 0x38, 0xbd,
0xed, 0x86, 0x95, 0x05, 0xf4, 0x32, 0xf9, 0xdc, 0x87, 0x4f, 0x78, 0x9a, 0xb0, 0x6f, 0x2b,
0x76, 0xd2, 0xf4, 0x1f, 0x65, 0xf8, 0xd0, 0x9f, 0x8d, 0x3c, 0xb2, 0x53, 0x8c, 0xca, 0xa0,
0x2b, 0x6d, 0xfb, 0x65, 0x96, 0xde, 0x7c, 0x64, 0xbb, 0xf7, 0x7e, 0x3d, 0x8d, 0xfb, 0xd1,
0x2d, 0xf1, 0xc3, 0x1a, 0x66, 0x8a, 0x8f, 0x5e, 0x89, 0x37, 0x19, 0xe7, 0x9b, 0xee, 0xf0,
0xc1, 0xe7, 0xac, 0xe5, 0x15, 0x2f, 0x6d, 0xfb, 0x57, 0xca, 0x78, 0xd0, 0xaa, 0x19, 0xc0,
0x80, 0x67, 0xc9, 0x2f, 0x81, 0x26, 0x7f, 0xbe, 0xdf, 0xde, 0x50, 0x00, 0x2b, 0x1b, 0x17,
0x2a, 0x5d, 0x54, 0xfa, 0x46, 0xc1, 0x3f, 0xb7, 0x2f, 0xc8, 0x31, 0xf4, 0x58, 0x43, 0x36,
0xae, 0x91, 0xc4, 0x46, 0xc4, 0x93, 0xaf, 0x05, 0x9e, 0xa3, 0xba, 0x0c, 0x07, 0x2f, 0x0c,
0xeb, 0x2f, 0x4d, 0x4a, 0x90, 0xd8, 0xc0, 0x51, 0x0f, 0xa5, 0x97, 0x09, 0x16, 0xfa, 0x24,
0xc3, 0x99, 0xe6, 0x1c, 0x9e, 0xed, 0x03, 0x76, 0x57, 0xce, 0x29, 0x73, 0x54, 0x19, 0x5a,
0x20, 0x65, 0x52, 0xa6, 0xfa, 0x97, 0x00, 0x52, 0x23, 0xbb, 0x74, 0x9b, 0x6e, 0xf3, 0x25,
0x94, 0x9c, 0x66, 0x0f, 0x8b, 0x67, 0x62, 0x1a, 0x13, 0xdb, 0xd5, 0xdb, 0xdb, 0xb9, 0x11,
0x02, 0x13, 0x63, 0xe4, 0x6e, 0x41, 0x8a, 0xa0, 0xc2, 0x52, 0x71, 0x66, 0xde, 0x3b, 0x3d,
0x6c, 0xd3, 0xaa, 0x5f, 0x78, 0x3b, 0xf2, 0x7b, 0xad, 0x71, 0x48, 0x2e, 0x06, 0x90, 0x4c,
0x76, 0x89, 0x99, 0xb5, 0x91, 0x61, 0x29, 0x74, 0x8d, 0x90, 0xd6, 0x1b, 0x60, 0x11, 0xf6,
0xc0, 0xa0, 0xc7, 0x3c, 0x1e, 0xf6, 0xec, 0x80, 0x91, 0xe8, 0x6d, 0x46, 0xe0, 0x2c, 0xda,
0x25, 0xdb, 0x18, 0xdb, 0xb1, 0xbe, 0x7c, 0x87, 0x14, 0xa9, 0xa3, 0x46, 0xdb, 0x6e, 0xeb,
0x5d, 0x98, 0xd2, 0x4c, 0xfa, 0x09, 0x13, 0x71, 0xee, 0x51, 0xae, 0x5d, 0x57, 0x79, 0x80,
0x02, 0x43, 0x7b, 0xf7, 0x92, 0x3c, 0xf9, 0x03, 0x07, 0x4f, 0x6a, 0x53, 0x9e, 0x35, 0x2d,
0x40, 0x29, 0x7d, 0x44, 0x39, 0xd2, 0xb4, 0x48, 0xa7, 0x4f, 0xa5, 0x90, 0x9d, 0xb7, 0x4e,
0xa0, 0xb5, 0xb7, 0x92, 0x0f, 0x1c, 0x18, 0xdd, 0x4c, 0xff, 0xd4, 0xf9, 0x53, 0xd1, 0x82,
0x38, 0xc5, 0xcc, 0xe2, 0x36, 0x2d, 0xf7, 0x18, 0x07, 0xda, 0x5b, 0xf6, 0xef, 0xe5, 0xcb,
0xaa, 0x90, 0xd2, 0x84, 0x25, 0xbc, 0x4f, 0x4a, 0x3b, 0x10, 0x0f, 0x81, 0x92, 0xe4, 0xda,
0x84, 0xad, 0x8b, 0xbb, 0x94, 0xc5, 0xf4, 0x4c, 0x25, 0xd2, 0x08, 0x97, 0xe0, 0x98, 0x0b,
0xbe, 0xe7, 0xaa, 0x40, 0x06, 0xfa, 0xe4, 0x79, 0xab, 0x76, 0x1f, 0xb6, 0x7f, 0x72, 0x48,
0x5c, 0x27, 0x8b, 0xab, 0xef, 0x13, 0xe1, 0x0c, 0xb1, 0x79, 0x7b, 0x6d, 0x8f, 0x0b, 0x90,
0x22, 0x7f, 0x2f, 0xa5, 0x71, 0xca, 0xb7, 0xc0, 0xbb, 0x31, 0x23, 0x03, 0x44, 0xd2, 0x51,
0x74, 0x08, 0x85, 0xad, 0x1e, 0xfb, 0xc3, 0xa2, 0xa1, 0xe1, 0x3e, 0x14, 0x0e, 0xf2, 0x0a,
0xb3, 0x9d, 0x74, 0x14, 0x50, 0xff, 0xfe, 0x95, 0xed, 0x02, 0x76, 0xe6, 0xf7, 0x0b, 0x32,
0x4e, 0x40, 0xb0, 0xd2, 0x67, 0x74, 0x92, 0x51, 0x1e, 0x54, 0x6f, 0x30, 0x6f, 0x58, 0x6a,
0xbb, 0x8a, 0x6c, 0xa6, 0x6d, 0x92, 0xe3, 0x3e, 0x0c, 0x33, 0x7c, 0xfc, 0x87, 0x12, 0xb8,
0x61, 0x70, 0xa6, 0x6c, 0x28, 0xad, 0xa2, 0x55, 0x81, 0x58, 0xd5, 0xc7, 0xb6, 0xd1, 0xdf,
0x47, 0x5d, 0x07, 0x1f, 0xed, 0xfd, 0x4e, 0x0f, 0x3c, 0xe3, 0x9d, 0xae, 0xeb, 0xf6, 0x3a,
0xb6, 0x0e, 0x5e, 0xc9, 0x9f, 0xc9, 0x5e, 0xbc, 0x37, 0xcf, 0x81, 0x04, 0x60, 0x26, 0xe4,
0x8b, 0x90, 0xdb, 0xf1, 0xc2, 0x66, 0x87, 0xd2, 0x29, 0x72, 0x01, 0xa6, 0xcd, 0x01, 0xdf,
0x9a, 0xfb, 0x4d, 0x62, 0xcb, 0x4d, 0x59, 0x18, 0xd2, 0x3f, 0x79, 0xf7, 0xaf, 0xda, 0x10,
0x66, 0xb6, 0x3e, 0x7e, 0x7b, 0xcb, 0xb6, 0xa0, 0xd7, 0xa8, 0x5b, 0xe7, 0x8e, 0xa0, 0x24,
0xf4, 0x55, 0x43, 0x00, 0xda, 0x86, 0x50, 0xec, 0x0a, 0x04, 0x27, 0xb1, 0x98, 0x40, 0x5c,
0xb1, 0xa5, 0x18, 0x87, 0xa9, 0x88, 0x87, 0x85, 0xe0, 0x89, 0xd5, 0xe2, 0x24, 0xef, 0x65,
0xac, 0x2c, 0xd1, 0x3a, 0xe7, 0x67, 0xee, 0xc4, 0xd6, 0x3a, 0x76, 0x2a, 0x55, 0x03, 0x5b,
0x0b, 0xde, 0x5e, 0x38, 0x5a, 0xb7, 0x7d, 0xb3, 0x82, 0x39, 0x5c, 0x98, 0xc3, 0x2f, 0x8d,
0x53, 0xa0, 0x41, 0x7d, 0xa8, 0x83, 0x47, 0x4a, 0xb0, 0x42, 0xc8, 0xeb, 0xf4, 0xfb, 0x9e,
0xe9, 0x82, 0xe3, 0xf6, 0x4d, 0x16, 0xe4, 0xd9, 0xd1, 0xca, 0x56, 0xae, 0x09, 0xe7, 0xcf,
0xaa, 0x25, 0x15, 0x91, 0xce, 0x15, 0xa9, 0x87, 0x50, 0x80, 0x38, 0xcd, 0x6a, 0x64, 0x4d,
0xb5, 0x55, 0x63, 0xc7, 0x9c, 0xb0, 0x52, 0xb0, 0x3d, 0x5a, 0x8b, 0x65, 0x19, 0x33, 0x43,
0xa2, 0x97, 0x20, 0xad, 0x79, 0xa6, 0xe8, 0x82, 0x06, 0x39, 0x45, 0x8b, 0x22, 0x35, 0x28,
0x72, 0x45, 0xc8, 0xca, 0xa4, 0xc3, 0x10, 0x20, 0x22, 0x83, 0x8f, 0x39, 0x49, 0x83, 0x72,
0x6d, 0xf4, 0x56, 0xfd, 0x55, 0x43, 0x60, 0x3d, 0x75, 0xa3, 0x4e, 0x14, 0x5b, 0xb8, 0xff,
0x4f, 0x17, 0xd9, 0x93, 0xd7, 0xc0, 0xaf, 0x5c, 0xb9, 0x65, 0xa5, 0x86, 0x8a, 0x9a, 0x35,
0x5d, 0x95, 0x30, 0x21, 0xc3, 0x38, 0x82, 0x05, 0x0b, 0xb8, 0x02, 0x20, 0xf1, 0x8a, 0x37,
0x15, 0x0a, 0xc2, 0x46, 0x8a, 0x16, 0x6f, 0x0d, 0x3a, 0x5b, 0x12, 0x6e, 0xee, 0xbf, 0xaf,
0xfb, 0x04, 0xe5, 0x34, 0xcc, 0x44, 0x2c, 0x3c, 0x22, 0x7c, 0x4f, 0xd7, 0xad, 0xe1, 0xe2,
0x12, 0xe6, 0xda, 0x69, 0x6f, 0x9c, 0xc8, 0x1f, 0xb9, 0x7e, 0x42, 0x20, 0x7d, 0x21, 0x0a,
0x0f, 0xb6, 0x16, 0x59, 0x01, 0x91, 0x16, 0xfe, 0xa4, 0x7b, 0xb6, 0x36, 0x43, 0x72, 0xbf,
0x33, 0x71, 0xb8, 0x5d, 0x8a, 0x4f, 0x91, 0xe0, 0x79, 0xb5, 0x40, 0xda, 0x9f, 0x02, 0xbe,
0x35, 0x60, 0xc0, 0xef, 0xc9, 0x2f, 0x42, 0xd3, 0xcd, 0xef, 0xde, 0x10, 0x71, 0x14, 0x8d,
0x86, 0x29, 0xcd, 0x31, 0x39, 0x90, 0xe0, 0x2d, 0x28, 0x97, 0xe4, 0xb3, 0xe5, 0xd7, 0x61,
0xe3, 0x2b, 0x21, 0xe7, 0xc5, 0x4b, 0xd6, 0xe9, 0xdb, 0x51, 0x03, 0xa2, 0x0e, 0x7c, 0x7e,
0x95, 0xa8, 0xf6, 0x8b, 0x95, 0x8f, 0x6e, 0xfd, 0x54, 0xfd, 0x28, 0x71, 0x4a, 0xfc, 0x75,
0x54, 0x19, 0x8c, 0xd3, 0x8a, 0x9b, 0x94, 0xcd, 0x33, 0x60, 0x80, 0x7a, 0xd4, 0x68, 0x38,
0x29, 0x0e, 0xf0, 0xc1, 0xeb, 0xac, 0xaf, 0xb1, 0xe6, 0xce, 0xbd, 0xd1, 0xfd, 0xbc, 0x23,
0x85, 0x8d, 0x97, 0xf4, 0xc9, 0xf9, 0xbd, 0x69, 0xd2, 0x0c, 0x60, 0x12, 0x05, 0x82, 0x6e,
0x5a, 0x6e, 0xfd, 0x55, 0xcc, 0xd8, 0xe6, 0xed, 0x54, 0xde, 0xdb, 0xcc, 0xf9, 0x85, 0x0b,
0xf6, 0x42, 0x39, 0x76, 0xde, 0xab, 0x08, 0xd7, 0x65, 0xe8, 0x19, 0xbd, 0x72, 0xec, 0x53,
0x7d, 0xc8, 0xa6, 0x00, 0xb3, 0x3e, 0x5b, 0x7d, 0xa0, 0x7b, 0xaa, 0x6e, 0xc5, 0x45, 0x12,
0xe3, 0xac, 0x02, 0x1c, 0x01, 0xb8, 0x2b, 0x2b, 0xd6, 0xf8, 0x2e, 0x96, 0x3a, 0x5e, 0x6e,
0xda, 0xf9, 0x6c, 0xac, 0x36, 0xf8, 0xcd, 0xef, 0x05, 0x8d, 0x8f, 0xe7, 0x6c, 0x39, 0x24,
0xb3, 0x8d, 0xed, 0xd1, 0x70, 0xa6, 0x20, 0x1e, 0x2b, 0x1d, 0x2c, 0x40, 0x16, 0xd6, 0x23,
0xb0, 0x99, 0xaa, 0x74, 0x31, 0xbe, 0xdc, 0x28, 0xd4, 0x64, 0xfa, 0xd2, 0x8d, 0xdb, 0xa1,
0x32, 0xaf, 0xdc, 0xb1, 0x71, 0xb3, 0x4d, 0x0a, 0x3a, 0x3a, 0x53, 0x11, 0x8c, 0x11, 0x8c,
0xe7, 0xd5, 0xdb, 0xdd, 0x67, 0xb8, 0x1a, 0x39, 0x0e, 0xf6, 0x69, 0xd8, 0x1b, 0xcb, 0xe5,
0xc5, 0xa6, 0x3d, 0x3b, 0xfe, 0xe1, 0x05, 0x48, 0x83, 0x67, 0x7f, 0xf1, 0x9c, 0x70, 0x7e,
0x70, 0x5c, 0xc4, 0xd3, 0x78, 0x8b, 0x80, 0x87, 0xf5, 0x94, 0x2d, 0x0a, 0xbf, 0x82, 0x13,
0xad, 0x36, 0x10, 0x0a, 0x60, 0xaa, 0xc4, 0xa3, 0xcd, 0xf1, 0xa9, 0xa1, 0x49, 0xf5, 0x41,
0x14, 0xd6, 0xd1, 0xa7, 0xe0, 0x50, 0xce, 0xe2, 0x76, 0xbe, 0x09, 0xc4, 0x3a, 0x68, 0xc5,
0x8a, 0x1f, 0x8e, 0xd0, 0x4d, 0x90, 0x92, 0xc8, 0x3b, 0x63, 0xae, 0xc2, 0xfe, 0x77, 0x8c,
0x4b, 0xa6, 0x6c, 0x04, 0xff, 0x62, 0xe1, 0x32, 0x04, 0xb8, 0xfd, 0xb3, 0x34, 0x47, 0x85,
0x32, 0x6b, 0x50, 0xce, 0xcf, 0x4f, 0x0f, 0x72, 0x41, 0x09, 0x7f, 0xde, 0x5c, 0xe7, 0x08,
0xdf, 0x63, 0x09, 0x89, 0xd1, 0x23, 0xb6, 0xe3, 0xbc, 0xff, 0xe4, 0x88, 0x61, 0x67, 0x8e,
0x4a, 0xb6, 0x86, 0x08, 0x33, 0xad, 0xac, 0x9f, 0xf3, 0x37, 0xbd, 0xcc, 0xd6, 0x0e, 0x3e,
0x4e, 0x4f, 0xc9, 0xff, 0xc8, 0xf6, 0xd8, 0x2a, 0x43, 0x3d, 0x79, 0xf2, 0x5d, 0x87, 0x33,
0x1d, 0xf4, 0xfd, 0x80, 0x2c, 0x4f, 0xc4, 0xa5, 0x3a, 0x2f, 0x51, 0xb1, 0x19, 0xf8, 0xed,
0xe0, 0xc9, 0x6c, 0x82, 0x2d, 0x43, 0xdd, 0x71, 0x27, 0x32, 0x8b, 0x51, 0x3d, 0xbc, 0x83,
0x6b, 0xa8, 0xda, 0x2b, 0xbb, 0xb0, 0xba, 0x5b, 0x30, 0x4f, 0x44, 0xad, 0x99, 0xb7, 0x47,
0x35, 0x6f, 0x1f, 0xbe, 0x5f, 0x24, 0x4e, 0xba, 0x92, 0x89, 0xc8, 0xb6, 0x45, 0x12, 0x19,
0xee, 0xe7, 0xd8, 0x74, 0xb3, 0x33, 0x41, 0x39, 0x26, 0x4c, 0xc3, 0xb0, 0x22, 0x1e, 0xee,
0xd0, 0xfb, 0xc6, 0xa3, 0x7d, 0xaa, 0x7d, 0xa1, 0x51, 0x98, 0xe8, 0xcc, 0x58, 0x39, 0x22,
0x1e, 0xe7, 0xf0, 0xb4, 0xd3, 0x7c, 0xa4, 0xa6, 0x10, 0xba, 0x2f, 0x41, 0xd6, 0x17, 0x95,
0x76, 0xe1, 0xbb, 0xec, 0x43, 0x99, 0x87, 0xd4, 0x13, 0x9b, 0x85, 0x4f, 0x44, 0x32, 0x5a,
0xe0, 0x9a, 0xee, 0x29, 0x15, 0x45, 0x05, 0xf7, 0xe4, 0xc9, 0x4d, 0x6b, 0x45, 0xad, 0x1f,
0x13, 0x06, 0x29, 0xb4, 0x2b, 0x74, 0x83, 0xe1, 0xdb, 0x3d, 0x99, 0x28, 0xbd, 0x4f, 0xfb,
0x0d, 0x03, 0x4d, 0x79, 0x09, 0x7f, 0x12, 0x91, 0xe9, 0x69, 0x40, 0x84, 0xc7, 0x10, 0xd5,
0xce, 0xae, 0xbb, 0xa5, 0xb2, 0x35, 0xdb, 0x24, 0xa4, 0x0e, 0xe4, 0x6f, 0x80, 0xb4, 0xb6,
0x94, 0xf7, 0x51, 0x5b, 0x0c, 0x5b, 0xae, 0x3f, 0xa5, 0x17, 0xe0, 0x9c, 0xd1, 0x05, 0x31,
0x05, 0xc4, 0x14, 0xdd, 0xde, 0x6f, 0x39, 0xa1, 0x9a, 0x46, 0x3d, 0x68, 0x3c, 0xfe, 0x04,
0x8e, 0xc2, 0x11, 0x9d, 0xc2, 0x3a, 0xaa, 0xcf, 0x22, 0x26, 0x54, 0xcb, 0x8d, 0x47, 0x61,
0x9e, 0xf4, 0x2e, 0xe7, 0x3c, 0xe6, 0xf0, 0xd6, 0x1a, 0xe5, 0x18, 0xbd, 0x9f, 0xa4, 0x3e,
0x47, 0xdb, 0x85, 0xe0, 0x8c, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x63, 0x44, 0x0a, 0x96,
0xae, 0xde, 0x1f, 0xbc, 0xdb, 0x15, 0x89, 0x71, 0x60, 0x23, 0xc4, 0x0a, 0x96, 0xae, 0xbf,
0xfc, 0x7a, 0x76, 0x4f, 0x1c, 0x9b, 0xb4, 0xcb, 0x14, 0x8b, 0x94, 0x8b, 0x94, 0xaa, 0xd6,
0x0f, 0x7d, 0x78, 0x72, 0x47, 0x0c, 0x9a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x56, 0x0f, 0x7d,
0x59, 0x30, 0xc3, 0x04, 0x6b, 0x54, 0x0b, 0x75, 0x49, 0x10, 0xa2, 0xa7, 0xad, 0xd8, 0x13,
0x85, 0x88, 0x73, 0x45, 0xe9, 0x31, 0xc1, 0x00, 0x82, 0x67, 0x4c, 0xfb, 0x74, 0x6a, 0x56,
0x2e, 0xbf, 0xdd, 0x19, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x3a, 0xf6, 0x4f, 0x1c,
0xba, 0xf6, 0x4f, 0xfd, 0x59, 0x11, 0x81, 0x61, 0x21, 0xa1, 0xc0, 0x02, 0x86, 0x6f, 0x3d,
0xf8, 0x53, 0x24, 0xca, 0xf7, 0x6c, 0x5a, 0x36, 0xcf, 0xfd, 0x59, 0x11, 0xa0, 0xa3, 0xc4,
0x0a, 0x96, 0x8f, 0x9c, 0xba, 0xd7, 0x0d, 0x98, 0xb2, 0xc7, 0xed, 0x58, 0x32, 0xc7, 0xed,
0x39, 0xf0, 0x43, 0x04, 0x6b, 0x54, 0x2a, 0xb7, 0xec, 0x5a, 0x36, 0xee, 0x3f, 0xfc, 0x7a,
0x76, 0x6e, 0x5e, 0x1f, 0x9d, 0x99, 0x91, 0x81, 0x80, 0x82, 0x86, 0x6f, 0x5c, 0x1b, 0x95,
0x89, 0x90, 0xa2, 0xa7, 0xcc, 0x1a, 0x97, 0xac, 0xbb, 0xf4, 0x6a, 0x37, 0xec, 0x3b, 0xf4,
0x6a, 0x37, 0xec, 0x3b, 0xf4, 0x4b, 0xf5, 0x49, 0xf1, 0x60, 0x23, 0xa5, 0xa9, 0xb1, 0xc1,
0xe1, 0x21, 0xa1, 0xa1, 0xc0, 0xe3, 0x25, 0xa9, 0xb1, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x74,
0x6a, 0x37, 0xec, 0x5a, 0x17, 0xac, 0xbb, 0xd5, 0x28, 0xd2, 0x07, 0x6d, 0x58, 0x13, 0x85,
0x69, 0x31, 0xe0, 0x42, 0xe7, 0x4c, 0xfb, 0x55, 0x28, 0xd2, 0x07, 0x8c, 0x7b, 0x74, 0x4b,
0x14, 0xaa, 0xd6, 0x2e, 0xde, 0x3e, 0xfe, 0x7e, 0x5f, 0x1d, 0xb8, 0xf2, 0x47, 0x0c, 0x7b,
0x55, 0x09, 0x90, 0x83, 0x65, 0x48, 0x12, 0x87, 0x6d, 0x39, 0xf0, 0x62, 0x46, 0x0e, 0x9e,
0x9f, 0xbc, 0xfa, 0x57, 0x2c, 0xbb, 0xd5, 0x09, 0x71, 0x60, 0x5b, 0x08, 0xf7, 0x2b,
};
#endif

View file

@ -0,0 +1,396 @@
#define DT_DRV_COMPAT pixart_pmw33xx
#include <drivers/spi.h>
#include <errno.h>
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <sys/byteorder.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include <sys/__assert.h>
#include <logging/log.h>
#ifdef CONFIG_PMW33XX_3389
#include <pmw3389_srom.h>
#elif CONFIG_PMW33XX_3360
#include <pmw3360_srom.h>
#endif
#include "pmw33xx.h"
LOG_MODULE_REGISTER(PMW33XX, CONFIG_SENSOR_LOG_LEVEL);
#define PMW33XX_PID COND_CODE_1(CONFIG_PMW33XX_3389, (PMW33XX_3389_PID), (PMW33XX_3360_PID))
struct pmw33xx_motion_burst {
uint8_t motion;
uint8_t observation;
int16_t dx;
int16_t dy;
} __attribute__((packed));
static inline int pmw33xx_cs_select(const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg,
const uint8_t value)
{
return gpio_pin_set(cs_gpio_cfg->port, cs_gpio_cfg->pin, value);
}
static int pmw33xx_access(const struct device *dev, const uint8_t reg, uint8_t *value)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf;
const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec;
uint8_t access[1] = { reg };
struct spi_buf_set tx = {
.buffers = { &(struct spi_buf){
.buf = access,
.len = 1,
} },
.count = 1,
};
uint8_t result[1] = { *value };
struct spi_buf_set rx = {
.buffers = { &(struct spi_buf){
.buf = result,
.len = 1,
} },
.count = 1,
};
pmw33xx_cs_select(cs_gpio_cfg, 0);
int err = spi_write(data->bus, spi_cfg, &tx);
k_sleep(K_USEC(120)); //Tsrad
if (err) {
pmw33xx_cs_select(cs_gpio_cfg, 1);
return err;
}
if ((reg & PMW33XX_WR_MASK))
err = spi_write(data->bus, spi_cfg, &rx);
else
err = spi_read(data->bus, spi_cfg, &rx);
pmw33xx_cs_select(cs_gpio_cfg, 1);
k_sleep(K_USEC(160));
if ((reg & PMW33XX_WR_MASK) == 0)
*value = result[0];
return err;
}
static int pmw33xx_read_reg(const struct device *dev, const uint8_t reg, uint8_t *value)
{
return pmw33xx_access(dev, reg & PMW33XX_RD_MASK, value);
}
static int pmw33xx_write_reg(const struct device *dev, const uint8_t reg, const uint8_t value)
{
uint8_t v = value;
return pmw33xx_access(dev, reg | PMW33XX_WR_MASK, &v);
}
static int pmw33xx_write_srom(const struct device *dev)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf;
const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec;
uint8_t access[1] = { PMW33XX_REG_SROM_BURST | PMW33XX_WR_MASK };
struct spi_buf_set tx = {
.buffers = { &(struct spi_buf){
.buf = access,
.len = 1,
} },
.count = 1,
};
pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_CMD);
k_sleep(K_USEC(15));
pmw33xx_write_reg(dev, PMW33XX_REG_SROM_EN, PMW33XX_SROM_DWNLD_START_CMD);
pmw33xx_cs_select(cs_gpio_cfg, 0);
int err = spi_write(data->bus, spi_cfg, &tx);
k_sleep(K_USEC(15));
if (err) {
pmw33xx_cs_select(cs_gpio_cfg, 1);
return err;
}
for (uint16_t i = 0; i < sizeof(SROM); i++) {
access[0] = SROM[i];
err = spi_write(data->bus, spi_cfg, &tx);
k_sleep(K_USEC(15));
if (err) {
pmw33xx_cs_select(cs_gpio_cfg, 1);
return err;
}
}
pmw33xx_cs_select(cs_gpio_cfg, 1);
k_sleep(K_MSEC(2)); //Tbexit
return err;
}
static int pmw33xx_read_motion_burst(const struct device *dev, struct pmw33xx_motion_burst *burst)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
const struct spi_config *spi_cfg = &cfg->bus_cfg.spi_cfg->spi_conf;
const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec;
uint8_t access[1] = { PMW33XX_REG_BURST };
struct spi_buf_set tx = {
.buffers = { &(struct spi_buf){
.buf = access,
.len = 1,
} },
.count = 1,
};
struct spi_buf_set rx = {
.buffers = { &(struct spi_buf){
.buf = (uint8_t *)burst,
.len = sizeof(struct pmw33xx_motion_burst),
} },
.count = 1,
};
pmw33xx_cs_select(cs_gpio_cfg, 0);
int err = spi_write(data->bus, spi_cfg, &tx);
k_sleep(K_USEC(35)); // tsrad motbr
if (err) {
pmw33xx_cs_select(cs_gpio_cfg, 1);
return err;
}
err = spi_read(data->bus, spi_cfg, &rx);
pmw33xx_cs_select(cs_gpio_cfg, 1);
return err;
}
// converts twos complement data to an int16
static int16_t pmw33xx_raw_to_int16(const uint8_t src[2])
{
int16_t res = sys_get_be16(src);
if (res > BIT_MASK(15))
res -= BIT(16);
return res;
}
static int pmw33xx_read_raw(const struct device *dev, const uint8_t reg_high, const uint8_t reg_low,
int16_t *value)
{
uint8_t raw[2] = { 0x0, 0x0 };
int err;
err = pmw33xx_read_reg(dev, reg_high, &raw[0]);
if (err) {
LOG_ERR("could not read high byte at %x", reg_high);
return err;
}
k_sleep(K_USEC(100));
err = pmw33xx_read_reg(dev, reg_low, &raw[1]);
if (err) {
LOG_ERR("could not read low byte at %x", reg_low);
return err;
}
k_sleep(K_USEC(100));
*value = pmw33xx_raw_to_int16(raw);
return 0;
}
int pmw33xx_spi_init(const struct device *dev)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
const struct pmw33xx_spi_cfg *spi_cfg = cfg->bus_cfg.spi_cfg;
const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &cfg->bus_cfg.spi_cfg->cs_spec;
int err;
err = gpio_pin_configure(cs_gpio_cfg->port, cs_gpio_cfg->pin, GPIO_OUTPUT_ACTIVE);
if (err) {
LOG_ERR("could configure cs pin %d", err);
return -EIO;
}
return 0;
}
static int pmw33xx_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
struct pmw33xx_motion_burst burst;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_POS_DX && chan != SENSOR_CHAN_POS_DY)
return -ENOTSUP;
int err = pmw33xx_read_motion_burst(dev, &burst);
if (err) {
return err;
}
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DX)
data->dx = burst.dx;
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POS_DY)
data->dy = burst.dy;
return 0;
}
static int pmw33xx_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct pmw33xx_data *data = dev->data;
const struct pmw33xx_config *cfg = dev->config;
switch (chan) {
case SENSOR_CHAN_POS_DX:
val->val1 = data->dx;
data->dx = 0;
break;
case SENSOR_CHAN_POS_DY:
val->val1 = data->dy;
data->dy = 0;
break;
default:
return -ENOTSUP;
}
return 0;
}
static const struct sensor_driver_api pmw33xx_driver_api = {
#ifdef CONFIG_PMW33XX_TRIGGER
.trigger_set = pmw33xx_trigger_set,
#endif
// .attr_set = pmw33xx_attr_set,
.sample_fetch = pmw33xx_sample_fetch,
.channel_get = pmw33xx_channel_get,
};
static int pmw33xx_init_chip(const struct device *dev)
{
struct pmw33xx_data *data = dev->data;
return 0;
}
static int pmw33xx_init(const struct device *dev)
{
const struct pmw33xx_config *const config = dev->config;
struct pmw33xx_data *data = dev->data;
data->bus = device_get_binding(config->bus_name);
if (!data->bus) {
LOG_DBG("master not found: %s", log_strdup(config->bus_name));
return -EINVAL;
}
config->bus_init(dev);
if (pmw33xx_init_chip(dev) < 0) {
LOG_DBG("failed to initialize chip");
return -EIO;
}
const struct pmw33xx_gpio_dt_spec *cs_gpio_cfg = &config->bus_cfg.spi_cfg->cs_spec;
#ifdef CONFIG_PMW33XX_TRIGGER
if (pmw33xx_init_interrupt(dev) < 0) {
LOG_DBG("Failed to initialize interrupt!");
return -EIO;
}
#endif
pmw33xx_cs_select(cs_gpio_cfg, 1);
k_sleep(K_MSEC(1));
int err = pmw33xx_write_reg(dev, PMW33XX_REG_PWR_UP_RST, PMW33XX_RESET_CMD);
if (err) {
LOG_ERR("could not reset %d", err);
return -EIO;
}
uint8_t pid = 0x0;
err = pmw33xx_read_reg(dev, PMW33XX_REG_PID, &pid);
if (err) {
LOG_ERR("could not reset %d", err);
return -EIO;
}
if (pid != PMW33XX_PID) {
LOG_ERR("pid does not match expected: got (%x), expected(%x)", pid, PMW33XX_PID);
return -EIO;
}
pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable
err = pmw33xx_write_srom(dev);
if (err) {
LOG_ERR("could not upload srom %d", err);
return -EIO;
}
uint8_t srom_run = 0x0;
err = pmw33xx_read_reg(dev, PMW33XX_REG_OBSERVATION, &srom_run);
if (err) {
LOG_ERR("could not check srom status %d", err);
return -EIO;
}
if (!(srom_run & PMW33XX_SROM_RUN)) {
LOG_ERR("srom status invalid %d", srom_run);
return -EIO;
}
uint8_t srom_id = 0x0;
err = pmw33xx_read_reg(dev, PMW33XX_REG_SROM_ID, &srom_id);
if (err) {
LOG_ERR("could not check srom id %d", err);
return -EIO;
}
if (!srom_id) {
LOG_ERR("srom id invalid %d", srom_id);
return -EIO;
}
pmw33xx_write_reg(dev, PMW33XX_REG_CONFIG2, 0x00); // clear rest enable
pmw33xx_write_reg(dev, PMW33XX_REG_BURST, 0x01); // clear rest enable
struct pmw33xx_motion_burst val;
pmw33xx_read_motion_burst(dev, &val); // read and throwout initial motion data
return 0;
}
#define PMW33XX_DATA_SPI(n) \
{ \
.cs_ctrl = {}, \
}
#define PMW33XX_SPI_CFG(n) \
(&(struct pmw33xx_spi_cfg){ \
.spi_conf = \
{ \
.frequency = DT_INST_PROP(n, spi_max_frequency), \
.operation = \
(SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA), \
.slave = DT_INST_REG_ADDR(n), \
}, \
.cs_spec = PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), cs_gpios, 0), \
})
#define PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(node_id, prop, idx) \
{ \
.port = DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(node_id, prop, idx)), \
.pin = DT_GPIO_PIN_BY_IDX(node_id, prop, idx), \
.dt_flags = DT_GPIO_FLAGS_BY_IDX(node_id, prop, idx), \
}
#define PMW33XX_CONFIG_SPI(n) \
{ \
.bus_name = DT_INST_BUS_LABEL(n), .bus_init = pmw33xx_spi_init, \
.bus_cfg = { .spi_cfg = PMW33XX_SPI_CFG(n) }, \
COND_CODE_1(CONFIG_MA730_TRIGGER, \
(, PMW33XX_GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(n), motswk_gpios, 0)), \
()) \
}
#define PMW33XX_INST(n) \
static struct pmw33xx_data pmw33xx_data_##n = PMW33XX_DATA_SPI(n); \
static const struct pmw33xx_config pmw33xx_cfg_##n = PMW33XX_CONFIG_SPI(n); \
DEVICE_DT_INST_DEFINE(n, pmw33xx_init, device_pm_control_nop, &pmw33xx_data_##n, \
&pmw33xx_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&pmw33xx_driver_api);
DT_INST_FOREACH_STATUS_OKAY(PMW33XX_INST)

View file

@ -0,0 +1,136 @@
#ifndef ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_
#define ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_
#include <drivers/sensor.h>
#include <zephyr/types.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <drivers/spi.h>
#define PMW33XX_WR_MASK 0x80
#define PMW33XX_RD_MASK 0x7F
#define PMW33XX_3389_PID 0x47
#define PMW33XX_3360_PID 0x45
#define PMW33XX_REV 0x01
/* General Registers */
#define PMW33XX_REG_PID 0x00
#define PMW33XX_REG_REV_ID 0x01
#define PMW33XX_REG_PWR_UP_RST 0x3A
/* Motion Registers */
#define PMW33XX_REG_MOTION 0x02
#define PMW33XX_REG_DX_L 0x03
#define PMW33XX_REG_DX_H 0x04
#define PMW33XX_REG_DY_L 0x05
#define PMW33XX_REG_DY_H 0x06
#define PMW33XX_REG_BURST 0x50
/* Motion bits */
#define PMW33XX_MOTION (1 << 8)
#define PMW33XX_OPMODE_RUN (0)
#define PMW33XX_OPMODE_REST1 (0b01 << 1)
#define PMW33XX_OPMODE_REST2 (0b10 << 1)
#define PMW33XX_OPMODE_REST3 (0b11 << 1)
/* SROM Registers */
#define PMW33XX_REG_SROM_EN 0x13
#define PMW33XX_REG_SROM_ID 0x2A
#define PMW33XX_REG_SROM_BURST 0x62
/* SROM CMDs */
#define PMW33XX_SROM_CRC_CMD 0x15
#define PMW33XX_SROM_DWNLD_CMD 0x1D
#define PMW33XX_SROM_DWNLD_START_CMD 0x18
/* CPI Registers */
#define PMW33XX_3360_REG_CPI 0x0F
#define PMW33XX_3389_REG_CPI_L 0x0E
#define PMW33XX_3389_REG_CPI_H 0x0F
/* Config Registers */
#define PMW33XX_REG_CONFIG2 0x10
#define PMW33XX_REG_OBSERVATION 0x24
#define PMW33XX_REG_DOUT_L 0x25
#define PMW33XX_REG_DOUT_H 0x26
/* Config2 Bits */
#define PMW33XX_RESTEN 0x20
#define PMW33XX_RPT_MOD 0x04
/* Observation Bits */
#define PMW33XX_SROM_RUN 0x40
/* power up reset cmd */
#define PMW33XX_RESET_CMD 0x5A
struct pmw33xx_gpio_dt_spec {
const struct device *port;
gpio_pin_t pin;
gpio_dt_flags_t dt_flags;
};
struct pmw33xx_spi_cfg {
struct spi_config spi_conf;
struct pmw33xx_gpio_dt_spec cs_spec;
};
union pmw33xx_bus_cfg {
struct pmw33xx_spi_cfg *spi_cfg;
};
struct pmw33xx_config {
char *bus_name;
int (*bus_init)(const struct device *dev);
const union pmw33xx_bus_cfg bus_cfg;
int resolution;
#if CONFIG_PMW33XX_TRIGGER
struct pmw33xx_gpio_dt_spec motswk_spec;
#endif // CONFIG_PMW33XX_TRIGGER
};
struct pmw33xx_data;
struct pmw33xx_transfer_function {
int (*read_data)(const struct device *dev, uint16_t *value);
};
struct pmw33xx_data {
const struct device *bus;
struct spi_cs_control cs_ctrl;
int16_t dx;
int16_t dy;
const struct pmw33xx_transfer_function *hw_tf;
#ifdef CONFIG_PMW33XX_TRIGGER
struct gpio_callback motswk_gpio_cb;
const struct device *dev;
sensor_trigger_handler_t handler;
const struct sensor_trigger *trigger;
#if defined(CONFIG_PMW33XX_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_PMW33XX_THREAD_STACK_SIZE);
struct k_sem gpio_sem;
struct k_thread thread;
#elif defined(CONFIG_PMW33XX_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif
#endif /* CONFIG_PMW33XX_TRIGGER */
};
int pmw33xx_spi_init(const struct device *dev);
#ifdef CONFIG_PMW33XX_TRIGGER
int pmw33xx_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int pmw33xx_init_interrupt(const struct device *dev);
#endif
#endif /* ZEPHYR_DRIVERS_SENSOR_PIXART_PMW33XX_H_ */

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT pixart_pmw3389
#include <device.h>
#include <drivers/gpio.h>
#include <sys/util.h>
#include <kernel.h>
#include <drivers/sensor.h>
#include "pmw3389.h"
// extern struct pmw3389_data pmw3389_driver;
#include <logging/log.h>
LOG_MODULE_DECLARE(PMW3389, CONFIG_SENSOR_LOG_LEVEL);
static inline void setup_int(const struct device *dev, bool enable)
{
struct pmw3389_data *data = dev->data;
const struct pmw3389_config *cfg = dev->config;
if (gpio_pin_interrupt_configure(cfg->motswk_spec.port, cfg->motswk_spec.pin,
enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE)) {
LOG_WRN("Unable to set MOTSWK GPIO interrupt");
}
}
static void pmw3389_motswk_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct pmw3389_data *drv_data = CONTAINER_OF(cb, struct pmw3389_data, motswk_gpio_cb);
LOG_DBG("");
setup_int(drv_data->dev, false);
#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
static void pmw3389_thread_cb(const struct device *dev)
{
struct pmw3389_data *drv_data = dev->data;
LOG_DBG("%p", drv_data->handler);
drv_data->handler(dev, drv_data->trigger);
// Enable once the wall/spam of interrupts is solved
setup_int(dev, true);
}
#ifdef CONFIG_PMW3389_TRIGGER_OWN_THREAD
static void pmw3389_thread(int dev_ptr, int unused)
{
const struct device *dev = INT_TO_POINTER(dev_ptr);
struct pmw3389_data *drv_data = dev->data;
ARG_UNUSED(unused);
while (1) {
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
pmw3389_thread_cb(dev);
}
}
#endif
#ifdef CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD
static void pmw3389_work_cb(struct k_work *work)
{
struct pmw3389_data *drv_data = CONTAINER_OF(work, struct pmw3389_data, work);
LOG_DBG("");
pmw3389_thread_cb(drv_data->dev);
}
#endif
int pmw3389_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct pmw3389_data *drv_data = dev->data;
setup_int(dev, false);
k_msleep(5);
drv_data->trigger = trig;
drv_data->handler = handler;
setup_int(dev, true);
return 0;
}
int pmw3389_init_interrupt(const struct device *dev)
{
struct pmw3389_data *drv_data = dev->data;
const struct pmw3389_config *drv_cfg = dev->config;
drv_data->dev = dev;
/* setup gpio interrupt */
gpio_init_callback(&drv_data->motswk_gpio_cb, pmw3389_motswk_gpio_callback,
BIT(drv_cfg->motswk_spec.pin));
if (gpio_add_callback(drv_cfg->motswk_spec.port, &drv_data->motswk_gpio_cb) < 0) {
LOG_DBG("Failed to set MOTSWK callback!");
return -EIO;
}
#if defined(CONFIG_PMW3389_TRIGGER_OWN_THREAD)
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
k_thread_create(&drv_data->thread, drv_data->thread_stack, CONFIG_PMW3389_THREAD_STACK_SIZE,
(k_thread_entry_t)pmw3389_thread, dev, 0, NULL,
K_PRIO_COOP(CONFIG_PMW3389_THREAD_PRIORITY), 0, K_NO_WAIT);
#elif defined(CONFIG_PMW3389_TRIGGER_GLOBAL_THREAD)
k_work_init(&drv_data->work, pmw3389_work_cb);
#endif
return 0;
}

View file

@ -1,6 +1,6 @@
description: | description: |
Sensor driver for the pixart PMW3389 optical mouse sensor Sensor driver for the pixart PMW33XX optical mouse sensor supports 3360 built in, and 3389 with external srom
compatible: "pixart,pmw3389" compatible: "pixart,pmw33xx"
include: spi-device.yaml include: spi-device.yaml
@ -8,15 +8,15 @@ properties:
label: label:
type: string type: string
required: true required: true
cs-gpios:
type: phandle-array
required: true
description: chip select pin for the sensor
motswk-gpios: motswk-gpios:
type: phandle-array type: phandle-array
required: false required: false
description: interrupt pin for motion description: interrupt pin for motion
reset-gpios: cpi:
type: phandle-array
required: false
description: A pin for the encoder
resolution:
type: int type: int
description: mouse resolution description: mouse cpi
required: false required: false