peripheral_manager: DG400 support, improved sx9512/shift register and exposed config options

This commit is contained in:
Nabeel Sowan 2015-07-10 11:02:58 +02:00
parent fb4b3fd74a
commit 238aaa3ee5
22 changed files with 1907 additions and 543 deletions

View file

@ -53,6 +53,7 @@ define Package/peripheral_manager/install
$(INSTALL_DIR) $(1)/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/peripheral_manager $(1)/sbin/
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/gpio_test $(1)/sbin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/etc/init.d/* $(1)/etc/init.d/
endef

View file

@ -29,7 +29,7 @@ peripheral_manager_SOURCES = \
if BRCM_BOARD
bin_PROGRAMS += vox_test
bin_PROGRAMS += vox_test gpio_test
peripheral_manager_SOURCES += \
src/gpio_led.c \
@ -38,8 +38,12 @@ peripheral_manager_SOURCES += \
src/gpio_button.h \
src/gpio.c \
src/gpio.h \
src/gpio_shift_register.c \
src/gpio_shift_register.h \
src/touch_sx9512.c \
src/touch_sx9512.h \
src/sx9512.c \
src/sx9512.h \
src/vox.h \
src/vox.c \
src/prox_px3220.c
@ -48,6 +52,20 @@ vox_test_SOURCES = \
src/vox_test.c \
src/gpio.c \
src/gpio.h
gpio_test_SOURCES = \
src/gpio_test.c \
src/gpio.c \
src/gpio.h \
src/gpio_shift_register.c \
src/gpio_shift_register.h \
src/smbus.c \
src/smbus.h \
src/i2c.c \
src/i2c.h \
src/sx9512.c \
src/sx9512.h
endif
peripheral_manager_LDADD = $(UCI_LIB) $(UBOX_LIB) $(UBUS_LIB) -lm

View file

@ -8,6 +8,23 @@
#include "touch_sx9512.h"
#include "prox_px3220.h"
static struct ubus_context *global_ubus_ctx;
static struct blob_buf bblob;
void button_ubus_interface_event(struct ubus_context *ubus_ctx, char *button, button_state_t pressed)
{
char s[UBUS_BUTTON_NAME_PREPEND_LEN+BUTTON_MAX_NAME_LEN];
s[0]=0;
strcat(s, UBUS_BUTTON_NAME_PREPEND);
strcat(s, button);
blob_buf_init(&bblob, 0);
blobmsg_add_string(&bblob, "action", pressed ? "pressed" : "released");
ubus_send_event(ubus_ctx, s, bblob.head);
}
/* used to map in the driver buttons to a function button */
struct button_drv_list {
struct list_head list;
@ -21,6 +38,7 @@ struct function_button {
char *name;
int dimming;
char *hotplug;
char *hotplug_long;
int minpress;
int longpress; /* negative value means valid if mintime < time < abs(longpress ) */
/* positive value means valid if time > longpreass */
@ -40,20 +58,18 @@ static LIST_HEAD(drv_buttons_list);
/* list containing all function buttons read from config file */
static LIST_HEAD(buttons);
static struct button_drv *get_drv_button(char *name);
//static struct function_button *get_button(char *name);
void button_add( struct button_drv *drv)
{
struct drv_button_list *drv_node = malloc(sizeof(struct drv_button_list));
DBG(1,"called with led name [%s]", drv->name);
DBG(1,"called with button name [%s]", drv->name);
drv_node->drv = drv;
list_add(&drv_node->list, &drv_buttons_list);
}
static struct button_drv *get_drv_button(char *name)
static struct button_drv *get_drv_button(const char *name)
{
struct list_head *i;
list_for_each(i, &drv_buttons_list) {
@ -65,7 +81,7 @@ static struct button_drv *get_drv_button(char *name)
}
#if 0
static struct function_button *get_button(char *name)
static struct function_button *get_button(const char *name)
{
struct list_head *i;
list_for_each(i, &buttons) {
@ -77,6 +93,82 @@ static struct function_button *get_button(char *name)
}
#endif
//! Read state for single button
static button_state_t read_button_state(const char *name)
{
struct list_head *i;
#ifdef HAVE_BOARD_H
/* sx9512 driver needs to read out all buttons at once */
/* so call it once at beginning of scanning inputs */
sx9512_check();
/* same for px3220 */
px3220_check();
#endif
list_for_each(i, &buttons) {
struct list_head *j;
struct function_button *node = list_entry(i, struct function_button, list);
if(!strcmp(node->name, name)) {
button_state_t state=BUTTON_ERROR;
list_for_each(j, &node->drv_list) {
struct button_drv_list *drv_node = list_entry(j, struct button_drv_list, list);
if(drv_node->drv) {
if(drv_node->drv->func->get_state(drv_node->drv))
return BUTTON_PRESSED;
else
state=BUTTON_RELEASED;
}
}
return state;
}
}
return BUTTON_ERROR;
}
struct button_status {
char name[BUTTON_MAX_NAME_LEN];
button_state_t state;
};
struct button_status_all {
int n;
struct button_status status[BUTTON_MAX];
};
//! Read states for all buttons
static struct button_status_all * read_button_states(void)
{
static struct button_status_all p;
p.n=0;
struct list_head *i;
#ifdef HAVE_BOARD_H
/* sx9512 driver needs to read out all buttons at once */
/* so call it once at beginning of scanning inputs */
sx9512_check();
/* same for px3220 */
px3220_check();
#endif
list_for_each(i, &buttons) {
struct list_head *j;
button_state_t state=BUTTON_ERROR;
struct function_button *node = list_entry(i, struct function_button, list);
strcpy(p.status[p.n].name, node->name);
list_for_each(j, &node->drv_list) {
struct button_drv_list *drv_node = list_entry(j, struct button_drv_list, list);
if(drv_node->drv) {
if(drv_node->drv->func->get_state(drv_node->drv))
state=BUTTON_PRESSED;
else
state=BUTTON_RELEASED;
}
}
p.status[p.n].state = state;
p.n++;
}
return &p;
}
static void dump_drv_list(void)
{
struct list_head *i;
@ -105,6 +197,32 @@ static void dump_buttons_list(void)
}
}
//! Run the hotplug command associated with function button
//! @retval 0 ok
static int button_hotplug_cmd(const char *name, bool longpress)
{
struct list_head *i;
list_for_each(i, &buttons) {
struct function_button *node = list_entry(i, struct function_button, list);
if(!strcmp(node->name, name)) {
char str[512];
char *hotplug = node->hotplug;
if(longpress && node->hotplug_long)
hotplug = node->hotplug_long;
if(!hotplug)
return 1;
DBG(1, "send key %s [%s] to system %s", node->name, hotplug, longpress ? "(longpress)" : "");
snprintf(str, 512, "ACTION=register INTERFACE=%s /sbin/hotplug-call button &", hotplug);
system(str);
syslog(LOG_INFO, "%s",str);
return 0;
}
}
return 1;
}
static int timer_started(struct button_drv_list *button_drv)
{
if (button_drv->pressed_time.tv_sec == 0 )
@ -124,36 +242,26 @@ static void timer_stop(struct button_drv_list *button_drv)
button_drv->pressed_time.tv_nsec = 0;
}
static int timer_valid(struct button_drv_list *button_drv, int mtimeout, int longpress)
static button_press_type_t timer_valid(struct button_drv_list *button_drv, int mtimeout, int longpress)
{
struct timespec now;
int sec;
int nsec;
int time_elapsed;
if (timer_started(button_drv)) {
clock_gettime(CLOCK_MONOTONIC, &now);
sec = now.tv_sec - button_drv->pressed_time.tv_sec;
nsec = now.tv_nsec - button_drv->pressed_time.tv_nsec;
if ( mtimeout < (sec*1000 + nsec/1000000)) {
if (longpress == 0)
return 1;
if (longpress < 0) {
longpress = -1 * longpress;
if ( longpress > (sec*1000 + nsec/1000000)) {
return 1;
} else {
return 0;
}
}
if ( longpress < (sec*1000 + nsec/1000000)) {
return 1;
}
time_elapsed = sec*1000 + nsec/1000000;
if ( mtimeout < time_elapsed) {
if (longpress && (longpress < time_elapsed))
return BUTTON_PRESS_LONG;
return BUTTON_PRESS_SHORT;
}
}
return 0;
return BUTTON_PRESS_NONE;
}
#define BUTTON_TIMEOUT 100
@ -163,6 +271,7 @@ static struct uloop_timeout button_inform_timer = { .cb = button_handler };
static void button_handler(struct uloop_timeout *timeout)
{
struct list_head *i;
int r;
// DBG(1, "");
#ifdef HAVE_BOARD_H
@ -186,37 +295,25 @@ static void button_handler(struct uloop_timeout *timeout)
if (drv_node->drv) {
button_state_t st = drv_node->drv->func->get_state(drv_node->drv);
if (st == PRESSED ) {
if (st == BUTTON_PRESSED ) {
if (! timer_started(drv_node)) {
timer_start(drv_node);
DBG(1, " %s pressed", drv_node->drv->name);
button_ubus_interface_event(global_ubus_ctx, node->name, BUTTON_PRESSED);
}
if ( timer_valid(drv_node, node->minpress, 0)) {
led_pressindicator_set();
}
if(timer_valid(drv_node, node->minpress, 0))
led_pressindicator_set();
}
if (st == RELEASED ) {
if (st == BUTTON_RELEASED ) {
if (timer_started(drv_node)) {
DBG(1, " %s released", drv_node->drv->name);
if ( timer_valid(drv_node, node->minpress, node->longpress) ) {
char str[512];
if(node->dimming)
led_dimming();
DBG(1, "send key %s [%s]to system", node->name, node->hotplug);
snprintf(str,
512,
"ACTION=register INTERFACE=%s /sbin/hotplug-call button &",
node->hotplug);
system(str);
syslog(LOG_INFO, "%s",str);
} else {
// DBG(1, " %s not valid", drv_node->drv->name);
}
if((r=timer_valid(drv_node, node->minpress, node->longpress))) {
button_ubus_interface_event(global_ubus_ctx, node->name, BUTTON_RELEASED);
if(node->dimming)
led_dimming();
button_hotplug_cmd(node->name, r==BUTTON_PRESS_LONG);
}
}
timer_stop(drv_node);
}
@ -227,58 +324,103 @@ static void button_handler(struct uloop_timeout *timeout)
uloop_timeout_set(&button_inform_timer, BUTTON_TIMEOUT);
}
/* in order to support long press there is a need to go over every function button
and find any driver button that is part of a longpress function.
if found then the longpress time for that button needs to be filled in (but negative)
on any other function button that has the same driver button. This to prevent two
function buttons to trigger on one driver button release.
*/
/* Find functions that use driver (drv) that has a zero longpress time and set it to time */
static void longpress_set(int max_time,struct button_drv *drv) {
struct list_head *i;
list_for_each(i, &buttons) {
struct function_button *node = list_entry(i, struct function_button, list);
struct list_head *j;
list_for_each(j, &node->drv_list) {
struct button_drv_list *drv_node = list_entry(j, struct button_drv_list, list);
if(drv_node->drv == drv){
if (node->longpress == 0) {
node->longpress = max_time;
}
}
}
}
}
/* find any use of longpress and set all other to negative longpress time to indicate min max time
for a valid press
*/
static void longpress_find(void) {
struct list_head *i;
list_for_each(i, &buttons) {
struct function_button *node = list_entry(i, struct function_button, list);
struct list_head *j;
list_for_each(j, &node->drv_list) {
struct button_drv_list *drv_node = list_entry(j, struct button_drv_list, list);
if(drv_node->drv != NULL){
if (node->longpress > 0) {
DBG(1,"%13s drv button name = [%s]","",drv_node->drv->name);
DBG(1,"%13s longpress = %d","",node->longpress);
longpress_set(node->longpress * -1, drv_node->drv);
}
}
}
static int button_state_method(struct ubus_context *ubus_ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
blob_buf_init(&bblob, 0);
button_state_t state = read_button_state(obj->name+UBUS_BUTTON_NAME_PREPEND_LEN);
switch(read_button_state(obj->name+UBUS_BUTTON_NAME_PREPEND_LEN)) {
case BUTTON_RELEASED:
blobmsg_add_string(&bblob, "state", "released");
break;
case BUTTON_PRESSED:
blobmsg_add_string(&bblob, "state", "pressed");
break;
default:
blobmsg_add_string(&bblob, "state", "error");
}
ubus_send_reply(ubus_ctx, req, bblob.head);
return 0;
}
static int button_press_method(struct ubus_context *ubus_ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
button_hotplug_cmd(obj->name+UBUS_BUTTON_NAME_PREPEND_LEN, 0);
blob_buf_init(&bblob, 0);
ubus_send_reply(ubus_ctx, req, bblob.head);
return 0;
}
static int button_press_long_method(struct ubus_context *ubus_ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
button_hotplug_cmd(obj->name+UBUS_BUTTON_NAME_PREPEND_LEN, 1);
blob_buf_init(&bblob, 0);
ubus_send_reply(ubus_ctx, req, bblob.head);
return 0;
}
static int buttons_state_method(struct ubus_context *ubus_ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
{
int i;
static struct button_status_all *p;
p = read_button_states();
blob_buf_init(&bblob, 0);
for(i=0;i < p->n; i++) {
switch(p->status[i].state) {
case BUTTON_RELEASED:
blobmsg_add_string(&bblob, p->status[i].name, "released");
break;
case BUTTON_PRESSED:
blobmsg_add_string(&bblob, p->status[i].name, "pressed");
break;
default:
blobmsg_add_string(&bblob, p->status[i].name, "error");
}
}
ubus_send_reply(ubus_ctx, req, bblob.head);
return 0;
}
static const struct ubus_method button_methods[] = {
// { .name = "status", .handler = button_status_method },
{ .name = "state", .handler = button_state_method },
{ .name = "press", .handler = button_press_method },
{ .name = "press_long", .handler = button_press_long_method },
};
static struct ubus_object_type button_object_type = UBUS_OBJECT_TYPE("button", button_methods);
static const struct ubus_method buttons_methods[] = {
{ .name = "state", .handler = buttons_state_method },
};
static struct ubus_object_type buttons_object_type = UBUS_OBJECT_TYPE("buttons", buttons_methods);
static struct ubus_object buttons_object = { .name = "buttons", .type = &buttons_object_type, .methods = buttons_methods, .n_methods = ARRAY_SIZE(buttons_methods), };
void button_init( struct server_ctx *s_ctx)
{
struct ucilist *node;
LIST_HEAD(buttonnames);
int default_minpress = 0;
char *s;
int i,r;
global_ubus_ctx=s_ctx->ubus_ctx;
/* register buttons object with ubus */
if((r=ubus_add_object(s_ctx->ubus_ctx, &buttons_object)))
DBG(1,"Failed to add object: %s", ubus_strerror(r));
/* read out default global options */
s = ucix_get_option(s_ctx->uci_ctx, "hw" , "button_map", "minpress");
@ -328,6 +470,13 @@ void button_init( struct server_ctx *s_ctx)
function->hotplug = s;
}
/* read out hotplug option for longpress */
s = ucix_get_option(s_ctx->uci_ctx, "hw" , function->name, "hotplug_long");
DBG(1, "hotplug_long = [%s]", s);
if (s){
function->hotplug_long = s;
}
INIT_LIST_HEAD(&function->drv_list);
{
@ -362,11 +511,23 @@ void button_init( struct server_ctx *s_ctx)
}
list_add(&function->list, &buttons);
/* register each button with ubus */
struct ubus_object *ubo;
ubo = malloc(sizeof(struct ubus_object));
memset(ubo, 0, sizeof(struct ubus_object));
char name[UBUS_BUTTON_NAME_PREPEND_LEN+BUTTON_MAX_NAME_LEN];
snprintf(name, UBUS_BUTTON_NAME_PREPEND_LEN+BUTTON_MAX_NAME_LEN, "%s%s", UBUS_BUTTON_NAME_PREPEND, node->val);
ubo->name = strdup(name);
ubo->methods = button_methods;
ubo->n_methods = ARRAY_SIZE(button_methods);
ubo->type = &button_object_type;
if((r=ubus_add_object(s_ctx->ubus_ctx, ubo)))
DBG(1,"Failed to add object: %s", ubus_strerror(r));
}
uloop_timeout_set(&button_inform_timer, BUTTON_TIMEOUT);
longpress_find();
dump_drv_list();
dump_buttons_list();
}

View file

@ -2,15 +2,27 @@
#define BUTTON_H
#include "server.h"
#define BUTTON_MAX 32
#define BUTTON_MAX_NAME_LEN 16
#define UBUS_BUTTON_NAME_PREPEND "button."
#define UBUS_BUTTON_NAME_PREPEND_LEN sizeof(UBUS_BUTTON_NAME_PREPEND)
typedef enum {
RELEASED,
PRESSED,
BUTTON_RELEASED,
BUTTON_PRESSED,
BUTTON_ERROR,
} button_state_t;
typedef enum {
BUTTON_PRESS_NONE,
BUTTON_PRESS_SHORT,
BUTTON_PRESS_LONG,
} button_press_type_t;
struct button_drv;
struct button_drv_func {
button_state_t (*get_state)(struct button_drv *); /* Get led state, on,off,flash ... */
button_state_t (*get_state)(struct button_drv *); /* Get button state, on,off ... */
};
struct button_drv {

View file

@ -6,22 +6,24 @@
#include "gpio.h"
#include "log.h"
#define DEVFILE_BRCM_BOARD "/dev/brcmboard"
static int brcmboard = -1;
void gpio_open_ioctl( void ) {
int board_ioctl_init(void) {
if (brcmboard == -1){
brcmboard = open("/dev/brcmboard", O_RDWR);
brcmboard = open(DEVFILE_BRCM_BOARD, O_RDWR);
if ( brcmboard == -1 ) {
DBG(1,"failed to open: /dev/brcmboard\n");
return;
syslog(LOG_ERR, "failed to open: %s", DEVFILE_BRCM_BOARD);
return 1;
}
DBG(1, "fd %d allocated\n", brcmboard);
}
return;
return 0;
}
int board_ioctl(int ioctl_id, int action, int hex, char* string_buf, int string_buf_len, int offset) {
BOARD_IOCTL_PARMS IoctlParms = {0};
@ -32,8 +34,20 @@ int board_ioctl(int ioctl_id, int action, int hex, char* string_buf, int string_
IoctlParms.buf = (char*)"";
if ( ioctl(brcmboard, ioctl_id, &IoctlParms) < 0 ) {
syslog(LOG_INFO, "ioctl: %d failed", ioctl_id);
exit(1);
syslog(LOG_ERR, "ioctl: %d failed", ioctl_id);
return(-255);
}
return IoctlParms.result;
}
gpio_state_t gpio_get_state(gpio_t gpio)
{
return board_ioctl(BOARD_IOCTL_GET_GPIO, 0, 0, NULL, gpio, 0);
}
void gpio_set_state(gpio_t gpio, gpio_state_t state)
{
board_ioctl(BOARD_IOCTL_SET_GPIO, 0, 0, NULL, gpio, state);
}

View file

@ -5,7 +5,17 @@
#include <sys/ioctl.h>
#include <board.h>
void gpio_open_ioctl(void);
typedef int gpio_t;
typedef enum {
GPIO_STATE_LOW,
GPIO_STATE_HIGH,
} gpio_state_t;
int board_ioctl_init(void);
int board_ioctl(int ioctl_id, int action, int hex, char* string_buf, int string_buf_len, int offset);
#define gpio_init() board_ioctl_init()
gpio_state_t gpio_get_state(gpio_t gpio);
void gpio_set_state(gpio_t gpio, gpio_state_t state);
#endif /* GPIO_H */

View file

@ -8,31 +8,31 @@
void gpio_button_init(struct server_ctx *s_ctx);
struct gpio_data {
struct gpio_button_data {
int addr;
int active;
int state;
struct button_drv button;
};
static button_state_t gpio_get_state(struct button_drv *drv)
static button_state_t gpio_button_get_state(struct button_drv *drv)
{
// DBG(1, "state for %s", drv->name);
struct gpio_data *p = (struct gpio_data *)drv->priv;
struct gpio_button_data *p = (struct gpio_button_data *)drv->priv;
int value;
value = board_ioctl( BOARD_IOCTL_GET_GPIO, 0, 0, NULL, p->addr, 0);
if(p->active)
p->state = value ? PRESSED : RELEASED;
p->state = !!value;
else
p->state = value ? RELEASED : PRESSED;
p->state = !value;
return p->state;
}
static struct button_drv_func func = {
.get_state = gpio_get_state,
.get_state = gpio_button_get_state,
};
void gpio_button_init(struct server_ctx *s_ctx) {
@ -43,13 +43,13 @@ void gpio_button_init(struct server_ctx *s_ctx) {
ucix_get_option_list(s_ctx->uci_ctx, "hw" ,"gpio_buttons", "buttons", &buttons);
list_for_each_entry(node, &buttons, list) {
struct gpio_data *data;
struct gpio_button_data *data;
const char *s;
DBG(1, "value = [%s]",node->val);
data = malloc(sizeof(struct gpio_data));
memset(data,0,sizeof(struct gpio_data));
data = malloc(sizeof(struct gpio_button_data));
memset(data,0,sizeof(struct gpio_button_data));
data->button.name = node->val;
@ -76,5 +76,5 @@ void gpio_button_init(struct server_ctx *s_ctx) {
button_add(&data->button);
}
gpio_open_ioctl();
gpio_init();
}

View file

@ -9,24 +9,26 @@
#include "log.h"
#include "server.h"
#include "gpio.h"
#include "gpio_shift_register.h"
gpio_shift_register_t led_gpio_shift_register;
void gpio_led_init(struct server_ctx *s_ctx);
typedef enum {
UNKNOWN,
LOW,
HI,
UNKNOWN,
} active_t;
typedef enum {
MODE_UNKNOWN,
DIRECT,
SHIFTREG2,
SHIFTREG3,
SHIFTREG_BRCM,
SHIFTREG_GPIO,
} gpio_mode_t;
struct gpio_data {
struct gpio_led_data {
int addr;
active_t active;
int state;
@ -34,66 +36,30 @@ struct gpio_data {
struct led_drv led;
};
#define SR_MAX 16
static int shift_register_state[SR_MAX];
static void shift_register3_set(int address, int bit_val) {
int i;
if (address>=SR_MAX-1) {
DBG(1,"address index %d too large\n", address);
return;
}
// Update internal register copy
shift_register_state[address] = bit_val;
// pull down shift register load (load gpio 23)
board_ioctl(BOARD_IOCTL_SET_GPIO, 0, 0, NULL, 23, 0);
// clock in bits
for (i=0 ; i<SR_MAX ; i++) {
//set clock low
board_ioctl( BOARD_IOCTL_SET_GPIO, 0, 0, NULL, 0, 0);
//place bit on data line
board_ioctl( BOARD_IOCTL_SET_GPIO, 0, 0, NULL, 1, shift_register_state[SR_MAX-1-i]);
//set clock high
board_ioctl( BOARD_IOCTL_SET_GPIO, 0, 0, NULL, 0, 1);
}
// issue shift register load
board_ioctl( BOARD_IOCTL_SET_GPIO, 0, 0, NULL, 23, 1);
}
static int gpio_set_state(struct led_drv *drv, led_state_t state)
static int gpio_led_set_state(struct led_drv *drv, led_state_t state)
{
struct gpio_data *p = (struct gpio_data *)drv->priv;
struct gpio_led_data *p = (struct gpio_led_data *)drv->priv;
int bit_val = 0;
if (state == OFF) {
if (p->active == HI)
bit_val = 0;
else if (p->active == LOW)
bit_val = 1;
}else if (state == ON) {
if (p->active == HI)
bit_val = 1;
else if (p->active == LOW)
bit_val = 0;
if(state) {
if(p->active)
bit_val=1;
} else {
if(!p->active)
bit_val=1;
}
p->state = state;
switch (p->mode) {
case DIRECT :
case DIRECT:
board_ioctl( BOARD_IOCTL_SET_GPIO, 0, 0, NULL, p->addr, bit_val);
break;
case SHIFTREG2:
case SHIFTREG_BRCM:
board_ioctl( BOARD_IOCTL_LED_CTRL, 0, 0, NULL, p->addr, bit_val);
break;
case SHIFTREG3:
shift_register3_set(p->addr, bit_val);
case SHIFTREG_GPIO:
gpio_shift_register_cached_set(&led_gpio_shift_register, p->addr, bit_val);
break;
default:
DBG(1,"access mode not supported [%d,%s]", p->mode, p->led.name);
@ -102,35 +68,50 @@ static int gpio_set_state(struct led_drv *drv, led_state_t state)
return p->state;
}
static led_state_t gpio_get_state(struct led_drv *drv)
static led_state_t gpio_led_get_state(struct led_drv *drv)
{
struct gpio_data *p = (struct gpio_data *)drv->priv;
struct gpio_led_data *p = (struct gpio_led_data *)drv->priv;
DBG(1, "state for %s", drv->name);
return p->state;
}
static struct led_drv_func func = {
.set_state = gpio_set_state,
.get_state = gpio_get_state,
.set_state = gpio_led_set_state,
.get_state = gpio_led_get_state,
};
void gpio_led_init(struct server_ctx *s_ctx) {
LIST_HEAD(leds);
struct ucilist *node;
int gpio_shiftreg_clk=0, gpio_shiftreg_dat=1, gpio_shiftreg_mask=2, gpio_shiftreg_bits=0;
char *s;
DBG(1, "");
if((s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "gpio_shiftreg_clk")))
gpio_shiftreg_clk = strtol(s,0,0);
DBG(1, "gpio_shiftreg_clk = [%d]", gpio_shiftreg_clk);
if((s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "gpio_shiftreg_dat")))
gpio_shiftreg_dat = strtol(s,0,0);
DBG(1, "gpio_shiftreg_dat = [%d]", gpio_shiftreg_dat);
if((s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "gpio_shiftreg_mask")))
gpio_shiftreg_mask = strtol(s,0,0);
DBG(1, "gpio_shiftreg_mask = [%d]", gpio_shiftreg_mask);
if((s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "gpio_shiftreg_bits")))
gpio_shiftreg_bits = strtol(s,0,0);
DBG(1, "gpio_shiftreg_bits = [%d]", gpio_shiftreg_bits);
ucix_get_option_list(s_ctx->uci_ctx, "hw" ,"gpio_leds", "leds", &leds);
list_for_each_entry(node,&leds,list){
struct gpio_data *data;
struct gpio_led_data *data;
const char *s;
DBG(1, "value = [%s]",node->val);
data = malloc(sizeof(struct gpio_data));
memset(data,0,sizeof(struct gpio_data));
data = malloc(sizeof(struct gpio_led_data));
memset(data,0,sizeof(struct gpio_led_data));
data->led.name = node->val;
@ -147,9 +128,9 @@ void gpio_led_init(struct server_ctx *s_ctx) {
if (!strncasecmp("direct",s,3))
data->mode = DIRECT;
else if (!strncasecmp("sr",s,5))
data->mode = SHIFTREG2;
data->mode = SHIFTREG_BRCM;
else if (!strncasecmp("csr",s,4))
data->mode = SHIFTREG3;
data->mode = SHIFTREG_GPIO;
else
DBG(1, "Mode %s : Not supported!", s);
}
@ -167,5 +148,6 @@ void gpio_led_init(struct server_ctx *s_ctx) {
data->led.priv = data;
led_add(&data->led);
}
gpio_open_ioctl();
gpio_init();
gpio_shift_register_init(&led_gpio_shift_register, gpio_shiftreg_clk, gpio_shiftreg_dat, gpio_shiftreg_mask, gpio_shiftreg_bits);
}

View file

@ -0,0 +1,48 @@
#include "gpio_shift_register.h"
#include <stdlib.h>
#include "log.h"
int gpio_shift_register_init(gpio_shift_register_t *p, gpio_t gpio_clk, gpio_t gpio_dat, gpio_t gpio_mask, int size)
{
p->clk=gpio_clk;
p->dat=gpio_dat;
p->mask=gpio_mask;
p->size=size;
p->state_cache=0;
gpio_set_state(p->clk, 0);
gpio_set_state(p->dat, 0);
gpio_set_state(p->mask, 0);
return 0;
}
void gpio_shift_register_set(gpio_shift_register_t *p, int state)
{
int i;
if(!p->size)
return;
gpio_set_state(p->mask, 0); //mask low
for(i=p->size; i; i--) {
gpio_set_state(p->clk, 0); //clk low
gpio_set_state(p->dat, (state>>(i-1)) & 1); //place bit
gpio_set_state(p->clk, 1); //clk high
}
gpio_set_state(p->mask, 1); //mask high / sreg load
p->state_cache=state; //update internal register copy
}
void gpio_shift_register_cached_set(gpio_shift_register_t *p, shift_register_index_t index, gpio_state_t state)
{
if(!p->size)
return;
if(!(index < p->size)) {
syslog(LOG_ERR, "index %d out of bounds", index);
return;
}
//update internal register copy
if(state)
p->state_cache |= (1<<index);
else
p->state_cache &= ~(1<<index);
gpio_shift_register_set(p, p->state_cache);
}

View file

@ -0,0 +1,20 @@
#ifndef GPIO_SHIFT_REGISTER_H
#define GPIO_SHIFT_REGISTER_H
#include "gpio.h"
typedef int shift_register_index_t;
typedef struct {
gpio_t clk;
gpio_t dat;
gpio_t mask;
int size;
int state_cache;
} gpio_shift_register_t;
int gpio_shift_register_init(gpio_shift_register_t *p, gpio_t gpio_clk, gpio_t gpio_dat, gpio_t gpio_mask, int size);
void gpio_shift_register_set(gpio_shift_register_t *p, int state);
void gpio_shift_register_cached_set(gpio_shift_register_t *p, shift_register_index_t address, gpio_state_t bit_val);
#endif

View file

@ -0,0 +1,376 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//#include <libgen.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <error.h>
#include <errno.h>
//#include <limits.h>
//#include <sys/types.h>
//#include <sys/stat.h>
//#include <syslog.h>
//#include <config.h>
#include <getopt.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "smbus.h"
#include "i2c.h"
#include "log.h"
#include "gpio.h"
#include "gpio_shift_register.h"
#include "sx9512.h"
#define DEV_I2C "/dev/i2c-0"
int verbose, debug_level;
#define CMDS \
X(NONE, "none", 0, 0, "", "") \
X(GPIO_GET, "gpio_get", 1, 1, "Get pin state", "<pin>") \
X(GPIO_SET, "gpio_set", 2, 2, "Set pin state", "<pin> <state>") \
X(SH_SET, "sh_set", 5, 5, "Set shift register state", "<clk> <dat> <mask> <size> <state>") \
X(SMBUS_READ, "smbus_read", 3, 3, "Read from I2C/SMBUS device", "<addr> <reg> <len>") \
X(SMBUS_WRITE, "smbus_write", 3, 3, "Write to I2C/SMBUS device", "<addr> <reg> <hex_data>") \
X(SX9512_BUTTON, "sx9512_button", 0, 0, "Read SX9512 buttons (endless loop)", "") \
X(SX9512_READ, "sx9512_read", 0, 0, "Look at configuration data (compare to default)", "") \
X(SX9512_INIT, "sx9512_init", 0, 1, "Init SX9512 config to device defaults", "[device]") \
X(SX9512_NVM_LOAD, "sx9512_nvm_load", 0, 0, "SX9512 load values from NVM", "") \
X(SX9512_NVM_STORE, "sx9512_nvm_store", 0, 0, "SX9512 store config to NVM", "") \
X(SX9512_RESET, "sx9512_reset", 0, 0, "Send reset command to SX9512", "")
#define X(id, str, min_arg, max_arg, desc, arg_desc) CMD_##id,
enum { CMDS CMDS_AMOUNT } cmd;
#undef X
struct cmd {
const char *str;
int min_arg, max_arg;
const char *desc, *arg_desc;
};
#define X(id, str, min_arg, max_arg, desc, arg_desc) { str, min_arg, max_arg, desc, arg_desc },
const struct cmd cmd_data[] = { CMDS };
#undef X
#define SX9512_DEVCFGS \
X(NONE, "none" ) \
X(DEFAULT, "default") \
X(CLEAR, "clear" ) \
X(CG300, "cg300" ) \
X(CG301, "cg301" ) \
X(EG300, "eg300" ) \
X(DG400, "dg400" )
#define X(a, b) SX9512_DEVCFG_##a,
enum sx9512_devcfg { SX9512_DEVCFGS SX9512_DEVCFG_AMOUNT };
#undef X
#define X(a, b) b,
const char *sx9512_devcfg_str[] = { SX9512_DEVCFGS };
#undef X
static enum sx9512_devcfg sx9512_devcfg_str_to_id(const char *s)
{
int i;
for(i=0; i<SX9512_DEVCFG_AMOUNT; i++) {
if(!strcmp(s, sx9512_devcfg_str[i]))
return i;
}
return 0;
}
static void print_usage(char *prg_name)
{
int i;
char tmp[64];
printf("Usage: %s [options...] <cmd> <arg(s)>\n", prg_name);
printf("Options:\n");
printf(" -v, --verbose Verbose output\n");
printf(" -h, --help Show this help screen.\n");
printf("Commands:\n");
for(i=0;i<CMDS_AMOUNT;i++) {
sprintf(tmp, "%s %s", cmd_data[i].str, cmd_data[i].arg_desc);
printf(" %-40.40s %s\n", tmp, cmd_data[i].desc);
}
printf("\n");
}
int main(int argc, char **argv)
{
int i, j, ch, r, fd=0;
int pin, state;
int pin_clk, pin_dat, pin_mask;
gpio_shift_register_t p;
int addr=0, s, n, l;
enum sx9512_devcfg devcfg=0;
uint8_t tmp[32];
char *str_value=0, *eptr, str_hex[3];
struct sx9512_reg_nvm nvm, nvm_def;
while(1) {
int option_index = 0;
static struct option long_options[] = {
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
ch = getopt_long(argc, argv, "vh", long_options, &option_index);
if(ch == -1)
break;
switch (ch) {
case 'v':
verbose=1;
break;
case 'h':
default:
print_usage(argv[0]);
exit(-1);
}
}
//i=argc-optind;
if((argc-optind)<1) {
print_usage(argv[0]);
error(-1, errno, "Error: need cmd");
}
for(i=0;i<CMDS_AMOUNT;i++) {
if(!strcmp(argv[optind], cmd_data[i].str))
cmd=i;
}
if(!cmd) {
print_usage(argv[0]);
error(-1, errno, "Error: bad cmd %s", argv[optind]);
}
optind++;
if((argc-optind)<cmd_data[cmd].min_arg) {
print_usage(argv[0]);
error(-1, errno, "Error: too few arguments");
}
if((argc-optind)>cmd_data[cmd].max_arg) {
print_usage(argv[0]);
error(-1, errno, "Error: too many arguments");
}
switch(cmd) {
case CMD_GPIO_GET:
case CMD_GPIO_SET:
case CMD_SH_SET:
gpio_init();
break;
case CMD_SMBUS_READ:
case CMD_SMBUS_WRITE:
addr=strtol(argv[optind],NULL,16);
optind++;
if(verbose)
printf("Open I2C device %s\n", DEV_I2C);
fd = open(DEV_I2C, O_RDWR);
if(fd < 0)
error(-1, errno, "could not open %s", DEV_I2C);
if(verbose)
printf("Set I2C addr=%02x\n", addr);
if(ioctl(fd, I2C_SLAVE, addr) < 0) {
error(0, errno, "could not set address %x for i2c chip", addr);
close(fd);
return -1;
}
break;
case CMD_SX9512_BUTTON:
case CMD_SX9512_READ:
case CMD_SX9512_INIT:
case CMD_SX9512_NVM_LOAD:
case CMD_SX9512_NVM_STORE:
case CMD_SX9512_RESET:
if((fd=sx9512_init(DEV_I2C, SX9512_I2C_ADDRESS, NULL))<0)
error(-1, errno, "could not init SX9512");
break;
default:
break;
}
switch(cmd) {
case CMD_GPIO_GET:
pin=strtol(argv[optind],0,0);
if(verbose)
printf("Get gpio %d state\n", pin);
r=gpio_get_state(pin);
if(verbose)
printf("state=%d\n", r);
return(r);
case CMD_GPIO_SET:
pin=strtol(argv[optind],0,0);
optind++;
state=strtol(argv[optind],0,0);
if(state!=0 && state!=1) {
print_usage(argv[0]);
error(-1, errno, "Error: bad state %d", state);
}
if(verbose)
printf("Set gpio %d state to %d\n", pin, state);
gpio_set_state(pin, state);
break;
case CMD_SH_SET:
pin_clk=strtol(argv[optind],NULL,0);
optind++;
pin_dat=strtol(argv[optind],NULL,0);
optind++;
pin_mask=strtol(argv[optind],NULL,0);
optind++;
s=strtol(argv[optind],NULL,0);
optind++;
state=strtol(argv[optind],NULL,16);
if(verbose)
printf("Set shift register (clk=%d, dat=%d, mask=%d, size=%d) state to %X\n", pin_clk, pin_dat, pin_mask, s, state);
gpio_shift_register_init(&p, pin_clk, pin_dat, pin_mask, s);
gpio_shift_register_set(&p, state);
break;
case CMD_SMBUS_READ:
s=strtol(argv[optind],NULL,16);
optind++;
n=strtol(argv[optind],NULL,0);
if(s+n>256)
n=256-s;
if(verbose)
printf("smbus read start (addr=%02x, reg=%02x, len=%d)\n", addr, s, n);
for(i=s; i<(s+n); i+=32) {
l=n-(i-s);
if(l>32)
l=32;
if(verbose)
printf("smbus read (reg=%02x, len=%d)\n", i, l);
r=i2c_smbus_read_i2c_block_data(fd, i, l, (__u8 *)&tmp);
if(r<0) {
error(0, errno, "I2C read error (%d)", r);
close(fd);
return(-1);
}
printf("%02X:", i/32);
for(j=0; j<l; j++)
printf("%02x", tmp[j]);
printf("\n");
}
close(fd);
if(n==1)
return(tmp[0]);
break;
case CMD_SMBUS_WRITE:
s=strtol(argv[optind],NULL,16);
optind++;
str_value = argv[optind];
n=strlen(str_value);
if(n%2)
error(-1, errno, "Error: odd length hex value %s", str_value);
n>>=1;
if(s+n>256)
n=256-s;
if(verbose)
printf("smbus write start (addr=%02x, reg=%02x, len=%d, val=%s)\n", addr, s, n, str_value);
for(i=0; i<n; i+=32) {
l=n-i;
if(l>32)
l=32;
str_hex[2]=0;
for(j=0; j<l; j++) {
str_hex[0]=str_value[(i+j)<<1];
str_hex[1]=str_value[((i+j)<<1)+1];
tmp[j]=strtol(str_hex, &eptr,16);
if((errno != 0 && tmp[j] == 0) || eptr==str_hex)
error(-1, errno, "hex conversion error at %d (%s)", j, str_hex);
}
if(verbose)
printf("smbus write (reg=%02x, len=%d, val=%.*s)\n", s+i, l, l*2, str_value+(i*2));
r=i2c_smbus_write_i2c_block_data(fd, s+i, l, tmp);
if(r<0) {
error(0, errno, "I2C write error (%d)", r);
close(fd);
return(-1);
}
printf("%02X:", i/32);
for(j=0; j<l; j++)
printf("%02x ", tmp[j]);
printf("\n");
}
close(fd);
break;
case CMD_SX9512_BUTTON:
while(1) {
if(verbose)
printf("Start reading buttons from SX9512\n");
struct sx9512_touch_state touch_state;
if(sx9512_read_status_cached(fd, &touch_state))
error(-1, errno, "I2C read error");
//printf("[state %02X]\n", touch_state.state);
if(touch_state.touched)
printf("[touch %02X]\n", touch_state.touched);
if(touch_state.released)
printf("[release %02X]\n", touch_state.released);
fflush(stdout);
sleep(1);
}
break;
case CMD_SX9512_READ:
if(verbose)
printf("Read SX9512 registers (and compare to defaults)\n");
sx9512_reg_nvm_init_defaults(&nvm_def, 0xff, 0xff);
if(sx9512_reg_nvm_read(fd, &nvm))
error(-1, errno, "while reading nvm registers");
s=sizeof(nvm);
for(i=0; i<s; i++)
printf("%02x: %02x (%02x)0 %s\n", SX9512_REG_NVM_AREA_START+i, ((uint8_t *)&nvm)[i], ((uint8_t *)&nvm_def)[i], sx9512_reg_name(SX9512_REG_NVM_AREA_START+i));
break;
case CMD_SX9512_INIT:
if((argc-optind)==1)
devcfg = sx9512_devcfg_str_to_id(argv[optind]);
switch(devcfg) {
case SX9512_DEVCFG_DEFAULT:
sx9512_reg_nvm_init_defaults(&nvm, 0xff, 0xff);
break;
case SX9512_DEVCFG_CLEAR:
memset(&nvm, 0, sizeof(nvm));
break;
case SX9512_DEVCFG_CG300:
sx9512_reg_nvm_init_cg300(&nvm);
break;
case SX9512_DEVCFG_CG301:
sx9512_reg_nvm_init_cg301(&nvm);
break;
case SX9512_DEVCFG_EG300:
sx9512_reg_nvm_init_eg300(&nvm);
break;
case SX9512_DEVCFG_DG400:
sx9512_reg_nvm_init_dg400(&nvm);
break;
default:
fprintf(stderr, "Error: bad device arg, valid options are:\n");
for(i=0;i<SX9512_DEVCFG_AMOUNT;i++)
fprintf(stderr, "%s ", sx9512_devcfg_str[i]);
fprintf(stderr, "\n");
return -1;
}
if(verbose)
printf("Init SX9512 registers to %s\n", sx9512_devcfg_str[devcfg]);
if(sx9512_reg_nvm_write(fd, &nvm))
error(-1, errno, "while writing nvm registers");
break;
case CMD_SX9512_NVM_LOAD:
if(sx9512_reg_nvm_load(fd))
error(-1, errno, "while loading nvm registers");
break;
case CMD_SX9512_NVM_STORE:
if(sx9512_reg_nvm_store(fd))
error(-1, errno, "while storing nvm registers");
break;
case CMD_SX9512_RESET:
if(sx9512_reset(fd))
error(-1, errno, "while trying to reset");
break;
default:
break;
}
fflush(stdout);
return 0;
}

View file

@ -26,32 +26,31 @@ void dump_i2c(int fd,int start,int stop)
}
}
int i2c_open_dev (const char *bus, int addr, unsigned long needed)
int i2c_open_dev (const char *bus, int addr, unsigned long funcs_needed)
{
int fd = open(bus, O_RDWR);
if (fd < 0) {
syslog(LOG_INFO,"%s: could not open /dev/i2c-0\n",__func__);
syslog(LOG_INFO,"%s: could not open %s\n",__func__, bus);
return -1;
}
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
syslog(LOG_INFO,"%s: could not set address %x for i2c chip\n",
__func__, addr);
error:
error:
close (fd);
return -1;
}
if (needed) {
if(funcs_needed) {
unsigned long funcs;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
syslog(LOG_INFO,"%s: could not get I2C_FUNCS\n",__func__);
goto error;
}
if ( (funcs & needed) != needed) {
if((funcs & funcs_needed) != funcs_needed) {
syslog(LOG_INFO,"%s: lacking I2C capabilities, have %lx, need %lx\n",
__func__, funcs, needed);
__func__, funcs, funcs_needed);
goto error;
}
}
return fd;
}

View file

@ -2,6 +2,6 @@
#define I2C_H
void dump_i2c(int fd,int start,int stop);
int i2c_open_dev (const char *bus, int addr, unsigned long needed);
int i2c_open_dev (const char *bus, int addr, unsigned long functions_needed);
#endif

View file

@ -37,7 +37,7 @@
struct i2c_reg_tab {
char addr;
char value;
char range;
char range; /* if set registers starting from addr to addr+range will be set to the same value */
};
struct button_data {
@ -61,7 +61,6 @@ static const struct i2c_reg_tab i2c_init_tab_vox25[]={
int dev;
int shadow_proximity;
void do_init_tab(const struct i2c_reg_tab *tab, int len );
@ -118,20 +117,18 @@ static button_state_t px3220_button_get_state(struct button_drv *drv)
if (p->addr == 0 ){
if (shadow_proximity & 1 ) {
shadow_proximity &= ~0x1;
p->state = PRESSED;
return PRESSED;
return p->state = BUTTON_PRESSED;
}
}
if (p->addr == 1 ){
if (shadow_proximity & 2 ) {
shadow_proximity &= ~0x2;
p->state = PRESSED;
return PRESSED;
return p->state = BUTTON_PRESSED;
}
}
p->state = RELEASED;
p->state = BUTTON_RELEASED;
return p->state;
}

View file

@ -39,7 +39,7 @@ void server_start(struct uci_context *uci_ctx, struct ubus_context *ubus_ctx)
gpio_led_init(&server);
gpio_button_init(&server);
sx9512_init(&server);
sx9512_handler_init(&server);
px3220_init(&server);

View file

@ -75,10 +75,10 @@ static int sim_set_method(struct ubus_context *ubus_ctx, struct ubus_object *obj
struct sim_data *p = (struct sim_data *)bt->priv;
if(!strcasecmp("pressed", (char *)blobmsg_data(tb[SIM_STATE]))){
p->state = PRESSED;
p->state = BUTTON_PRESSED;
}
if(!strcasecmp("released", (char *)blobmsg_data(tb[SIM_STATE]))){
p->state = RELEASED;
p->state = BUTTON_RELEASED;
}
}else
DBG(1," button = %s not found",(char *)blobmsg_data(tb[SIM_NAME]));
@ -104,7 +104,7 @@ static int sim_status_method(struct ubus_context *ubus_ctx, struct ubus_object *
const char *state;
struct sim_data *p = (struct sim_data *)node->drv->priv;
if(p->state == RELEASED)
if(p->state == BUTTON_RELEASED)
state = "Released";
else
state = "Pressed";

View file

@ -0,0 +1,347 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <error.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include "sx9512.h"
#include "smbus.h"
#include "i2c.h"
#include "gpio.h"
#define X(name, reserved, default) { #name, reserved, default },
const struct sx9512_reg_data sx9512_reg_data[] = { SX9512_REGS };
#undef X
//! init the sx9512 and optionally program the registers
//! @param addr I2C address (0=0x2c)
//! @param nvm compare and if different program the registers and flash the NVM with these contents
//! @return file descriptor for SX9512 I2C device
//! @retval -1 error
int sx9512_init(const char *dev, int addr, struct sx9512_reg_nvm *nvm)
{
int fd, i;
struct sx9512_reg_nvm nvm_read;
if(!addr)
addr=SX9512_I2C_ADDRESS;
if((fd=i2c_open_dev(dev, addr, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE))<0)
return -1;
/*fd = open(dev, O_RDWR);
if(fd < 0) {
error(0, errno, "could not open %s", dev);
return -1;
}
if(ioctl(fd, I2C_SLAVE, addr) < 0) {
error(0, errno, "could not set address %x for i2c chip", SX9512_I2C_ADDRESS);
close(fd);
return -1;
}*/
if(nvm) {
if(sx9512_reg_nvm_read(fd, &nvm_read)) {
close(fd);
return -1;
}
for(i=0;(unsigned int)i<sizeof(struct sx9512_reg_nvm);i++) {
if(sx9512_reg_reserved(i+SX9512_REG_NVM_AREA_START))
continue;
if(((uint8_t *)nvm)[i] != ((uint8_t *)&nvm_read)[i]) {
fprintf(stderr, "sx9512_init: register mismatch, setting default values and burning to NVM\n");
if(sx9512_reg_nvm_write(fd, nvm)) {
close(fd);
return -1;
}
sx9512_reg_nvm_store(fd); //store to NVM
break;
}
}
}
return fd;
}
const char *sx9512_reg_name(sx9512_reg_t reg)
{
return sx9512_reg_data[reg].name;
}
//! SX9512 check if reg is reserved
int sx9512_reg_reserved(sx9512_reg_t reg)
{
if(reg==SX9512_REG_I2C_SOFT_RESET)
return 0;
if(reg>=SX9512_REGS_AMOUNT)
return 1;
return sx9512_reg_data[reg].reserved;
}
//! send reset command
//! @retval 0 ok
int sx9512_reset(int fd)
{
if(i2c_smbus_write_byte_data(fd, SX9512_REG_I2C_SOFT_RESET, 0xde)<0)
return -1;
if(i2c_smbus_write_byte_data(fd, SX9512_REG_I2C_SOFT_RESET, 0x00)<0)
return -1;
return 0;
}
//! send reset command but retain LED values
//! @retval 0 ok
int sx9512_reset_restore_led_state(int fd)
{
int r;
uint8_t p[4];
if((r=i2c_smbus_read_i2c_block_data(fd, SX9512_REG_LED1_ON, 4, (__u8 *)&p))<0)
return r;
if(sx9512_reset(fd))
return -1;
if((r=i2c_smbus_write_i2c_block_data(fd, SX9512_REG_LED1_ON, 4, (__u8 *)&p))<0)
return r;
return 0;
}
//! read interrupt reg
//! @retval -1 error
int sx9512_read_interrupt(int fd)
{
int r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_IRQ_SRC))<0)
return -1;
return r;
}
//! read touch reg
//! @retval -1 error
int sx9512_read_buttons(int fd)
{
int r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_TOUCH_STATUS))<0)
return -1;
return r;
}
//! read prox reg
//! @retval -1 error
int sx9512_read_proximity(int fd)
{
int r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_PROX_STATUS))<0)
return -1;
return r;
}
//! read status regs
//! @retval 0 ok
int sx9512_read_status(int fd, struct sx9512_reg_status *status)
{
//REG_IRQ_SRC not working if using block read for some reason.
//if(i2c_smbus_read_i2c_block_data(fd, SX9512_REG_IRQ_SRC, sizeof(struct sx9512_reg_status), (uint8_t *)status)<0)
//return -1;
int r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_IRQ_SRC))<0)
return -1;
((uint8_t *)status)[0]=r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_TOUCH_STATUS))<0)
return -1;
((uint8_t *)status)[1]=r;
if((r=i2c_smbus_read_byte_data(fd, SX9512_REG_PROX_STATUS))<0)
return -1;
((uint8_t *)status)[2]=r;
return 0;
}
//! read status cached
//! @retval 0 ok
int sx9512_read_status_cached(int fd, struct sx9512_touch_state *touch_state)
{
static uint8_t last_state=0;
struct sx9512_reg_status status;
touch_state->touched=0;
touch_state->released=0;
if(sx9512_read_status(fd, &status))
error(-1, errno, "I2C read error");
touch_state->state=status.touch_status | !!((*(uint8_t *)&status.prox_status) & 0xc0);
if(*(uint8_t *)&status.irq_src) {
if(status.irq_src.touch || status.irq_src.prox_near)
touch_state->touched = (last_state ^ touch_state->state) & touch_state->state;
if(status.irq_src.release || status.irq_src.prox_far)
touch_state->released = (last_state ^ touch_state->state) & last_state;
}
last_state=touch_state->state;
return 0;
}
//! send cmd to load values from NVM to RAM
//! @retval 0 ok
int sx9512_reg_nvm_load(int fd)
{
if(i2c_smbus_write_byte_data(fd, SX9512_REG_NVM_CTRL, 0x08)<0)
return -1;
return 0;
}
//! send cmd to store RAM to NVM
//! @retval 0 ok
int sx9512_reg_nvm_store(int fd)
{
if(i2c_smbus_write_byte_data(fd, SX9512_REG_NVM_CTRL, 0x50)<0)
return -1;
if(i2c_smbus_write_byte_data(fd, SX9512_REG_NVM_CTRL, 0xa0)<0)
return -1;
//Wait 500ms then power off/on
sleep(1);
return 0;
}
//! read whole NVM region
//! @retval 0 ok
int sx9512_reg_nvm_read(int fd, struct sx9512_reg_nvm *p)
{
int r, s, i, rl;
s=sizeof(struct sx9512_reg_nvm);
for(i=0; i<s; i+=32) {
rl=s-i;
if(rl>32)
rl=32;
if((r=i2c_smbus_read_i2c_block_data(fd, SX9512_REG_NVM_AREA_START+i, rl, (uint8_t *)p+i))<0)
return -1;
}
return 0;
}
//! write whole NVM region
//! @retval 0 ok
int sx9512_reg_nvm_write(int fd, struct sx9512_reg_nvm *p)
{
int r, s, i, rl;
s=sizeof(struct sx9512_reg_nvm);
for(i=0; i<s; i+=32) {
rl=s-i;
if(rl>32)
rl=32;
if((r=i2c_smbus_write_i2c_block_data(fd, SX9512_REG_NVM_AREA_START+i, rl, (uint8_t *)p+i))<0)
return -1;
}
return 0;
}
//! init NVM struct
void sx9512_reg_nvm_init(struct sx9512_reg_nvm *p)
{
memset(p, 0, sizeof(struct sx9512_reg_nvm));
p->cap_sense_op.set_to_0x14=0x14;
}
//! init NVM struct to defaults
void sx9512_reg_nvm_init_defaults(struct sx9512_reg_nvm *p, uint8_t capsense_channels, uint8_t led_channels)
{
int i;
sx9512_reg_nvm_init(p);
p->irq_mask.touch=1;
p->irq_mask.release=1;
p->irq_mask.prox_near=1;
p->irq_mask.prox_far=1;
if(led_channels) {
p->led_map[0]=0x00;
p->led_map[1]=led_channels;
p->led_pwm_freq=0x10;
p->led_idle=0xff;
p->led1_on=0xff;
p->led2_on=0xff;
p->led_pwr_idle=0xff;
p->led_pwr_on=0xff;
}
p->cap_sense_enable=capsense_channels;
for(i=0;i<SX9512_CHANNELS+1;i++) {
p->cap_sense_range[i].ls_control=0x01;
p->cap_sense_range[i].delta_cin_range=0x03;
p->cap_sense_thresh[i]=0x04;
}
p->cap_sense_thresh[0]=0x02;
p->cap_sense_op.auto_compensation=0;
p->cap_sense_op.proximity_bl0=1;
p->cap_sense_op.proximity_combined_channels=0;
p->cap_sense_mode.raw_filter=0x03;
p->cap_sense_mode.touch_reporting=1;
p->cap_sense_mode.cap_sense_digital_gain=0;
p->cap_sense_mode.cap_sense_report_mode=0;
p->cap_sense_debounce.cap_sense_prox_near_debounce=0;
p->cap_sense_debounce.cap_sense_prox_far_debounce=0;
p->cap_sense_debounce.cap_sense_touch_debounce=0;
p->cap_sense_debounce.cap_sense_release_debounce=1;
p->cap_sense_neg_comp_thresh=0x80;
p->cap_sense_pos_comp_thresh=0x80;
p->cap_sense_pos_filt.cap_sense_prox_hyst=0;
p->cap_sense_pos_filt.cap_sense_pos_comp_debounce=2;
p->cap_sense_pos_filt.cap_sense_avg_pos_filt_coef=7;
p->cap_sense_neg_filt.cap_sense_touch_hyst=0;
p->cap_sense_neg_filt.cap_sense_neg_comp_debounce=2;
p->cap_sense_neg_filt.cap_sense_avg_neg_filt_coef=5;
p->spo_chan_map=0xff;
}
void sx9512_reg_nvm_init_cg300(struct sx9512_reg_nvm *p)
{
int i;
sx9512_reg_nvm_init_defaults(p, 0x0f, 0x3c);
p->led_map[0]=0x01;
p->led_map[1]=0x3c;
for(i=0;i<SX9512_CHANNELS+1;i++) {
p->cap_sense_range[i].delta_cin_range=0x01;
}
}
void sx9512_reg_nvm_init_cg301(struct sx9512_reg_nvm *p)
{
int i;
sx9512_reg_nvm_init_defaults(p, 0x3b, 0x7f);
p->led_map[1]=0x7f;
for(i=0;i<SX9512_CHANNELS+1;i++) {
p->cap_sense_range[i].delta_cin_range=0x01;
}
}
void sx9512_reg_nvm_init_eg300(struct sx9512_reg_nvm *p)
{
sx9512_reg_nvm_init_defaults(p, 0x0f, 0xff);
}
void sx9512_reg_nvm_init_dg400(struct sx9512_reg_nvm *p)
{
sx9512_reg_nvm_init_defaults(p, 0x3f, 0x00);
}

View file

@ -0,0 +1,605 @@
#ifndef _SX9512_H
#define _SX9512_H
#include <stdint.h>
#if __BYTE_ORDER == __BIG_ENDIAN
#define BIT_ORDER_BIG_ENDIAN
#endif
#define SX9512_I2C_ADDRESS 0x2b
#define SX9512B_I2C_ADDRESS 0x2d
#define SX9512_CHANNELS 8
#define SX9512_REG_NVM_AREA_START 0x07
#define SX9512_REG_NVM_AREA_END 0x62
#define SX9512_REG_I2C_SOFT_RESET 0xff
//Name, reserved, default value
#define SX9512_REGS \
X(IRQ_SRC, 0, 0x00) \
X(TOUCH_STATUS, 0, 0x00) \
X(PROX_STATUS, 0, 0x00) \
X(COMP_STATUS, 0, 0x00) \
X(NVM_CTRL, 0, 0x00) \
X(R_05, 1, 0x00) \
X(R_06, 1, 0x00) \
X(SPO2_MODE, 0, 0x00) \
X(PWR_KEY, 0, 0x00) \
X(IRQ_MASK, 0, 0x00) \
X(R_0A, 1, 0x00) \
X(R_0B, 1, 0x00) \
X(LED_MAP1, 0, 0x00) \
X(LED_MAP2, 0, 0x00) \
X(LED_PWM_FREQ, 0, 0x00) \
X(LED_MODE, 0, 0x00) \
X(LED_IDLE, 0, 0x00) \
X(LED_OFF_DELAY, 0, 0x00) \
X(LED1_ON, 0, 0x00) \
X(LED1_FADE, 0, 0x00) \
X(LED2_ON, 0, 0x00) \
X(LED2_FADE, 0, 0x00) \
X(LED_PWR_IDLE, 0, 0x00) \
X(LED_PWR_ON, 0, 0x00) \
X(LED_PWR_OFF, 0, 0x00) \
X(LED_PWR_FADE, 0, 0x00) \
X(LED_PWR_ON_PW, 0, 0x00) \
X(LED_PWR_MODE, 0, 0x00) \
X(R_1C, 1, 0x00) \
X(R_1D, 1, 0x00) \
X(CAP_SENSE_ENABLE, 0, 0x00) \
X(CAP_SENSE_RANGE0, 0, 0x00) \
X(CAP_SENSE_RANGE1, 0, 0x00) \
X(CAP_SENSE_RANGE2, 0, 0x00) \
X(CAP_SENSE_RANGE3, 0, 0x00) \
X(CAP_SENSE_RANGE4, 0, 0x00) \
X(CAP_SENSE_RANGE5, 0, 0x00) \
X(CAP_SENSE_RANGE6, 0, 0x00) \
X(CAP_SENSE_RANGE7, 0, 0x00) \
X(CAP_SENSE_RANGE_ALL, 0, 0x00) \
X(CAP_SENSE_THRESH0, 0, 0x00) \
X(CAP_SENSE_THRESH1, 0, 0x00) \
X(CAP_SENSE_THRESH2, 0, 0x00) \
X(CAP_SENSE_THRESH3, 0, 0x00) \
X(CAP_SENSE_THRESH4, 0, 0x00) \
X(CAP_SENSE_THRESH5, 0, 0x00) \
X(CAP_SENSE_THRESH6, 0, 0x00) \
X(CAP_SENSE_THRESH7, 0, 0x00) \
X(CAP_SENSE_THRESH_COMB, 0, 0x00) \
X(CAP_SENSE_OP, 0, 0x00) \
X(CAP_SENSE_MODE, 0, 0x00) \
X(CAP_SENSE_DEBOUNCE, 0, 0x00) \
X(CAP_SENSE_NEG_COMP_THRESH, 0, 0x00) \
X(CAP_SENSE_POS_COMP_THRESH, 0, 0x00) \
X(CAP_SENSE_POS_FILT, 0, 0x00) \
X(CAP_SENSE_NEG_FILT, 0, 0x00) \
X(CAP_SENSE_STUCK, 0, 0x00) \
X(CAP_SENSE_FRAME_SKIP, 0, 0x00) \
X(CAP_SENSE_MISC, 0, 0x00) \
X(PROX_COMB_CHAN_MASK, 0, 0x00) \
X(R_3C, 1, 0x00) \
X(R_3D, 1, 0x00) \
X(SPO_CHAN_MAP, 0, 0x00) \
X(SPO_LEVEL_BL0, 0, 0x00) \
X(SPO_LEVEL_BL1, 0, 0x00) \
X(SPO_LEVEL_BL2, 0, 0x00) \
X(SPO_LEVEL_BL3, 0, 0x00) \
X(SPO_LEVEL_BL4, 0, 0x00) \
X(SPO_LEVEL_BL5, 0, 0x00) \
X(SPO_LEVEL_BL6, 0, 0x00) \
X(SPO_LEVEL_BL7, 0, 0x00) \
X(SPO_LEVEL_IDLE, 0, 0x00) \
X(SPO_LEVEL_PROX, 0, 0x00) \
X(R_49, 1, 0x00) \
X(R_4A, 1, 0x00) \
X(BUZZER_TRIGGER, 0, 0x00) \
X(BUZZER_FREQ, 0, 0x00) \
X(R_4D, 1, 0x00) \
X(R_4E, 1, 0x00) \
X(R_4F, 1, 0x00) \
X(R_50, 1, 0x00) \
X(R_51, 1, 0x00) \
X(R_52, 1, 0x00) \
X(R_53, 1, 0x00) \
X(R_54, 1, 0x00) \
X(R_55, 1, 0x00) \
X(R_56, 1, 0x00) \
X(R_57, 1, 0x00) \
X(R_58, 1, 0x00) \
X(R_59, 1, 0x00) \
X(R_5A, 1, 0x00) \
X(R_5B, 1, 0x00) \
X(R_5C, 1, 0x00) \
X(R_5D, 1, 0x00) \
X(R_5E, 1, 0x00) \
X(R_5F, 1, 0x00) \
X(R_60, 1, 0x00) \
X(R_61, 1, 0x00) \
X(CAP_SENSE_CHAN_SELECT, 0, 0x00) \
X(CAP_SENSE_USEFUL_DATA_MSB, 0, 0x00) \
X(CAP_SENSE_USEFUL_DATA_LSB, 0, 0x00) \
X(CAP_SENSE_AVERAGE_DATA_MSB, 0, 0x00) \
X(CAP_SENSE_AVERAGE_DATA_LSB, 0, 0x00) \
X(CAP_SENSE_DIFF_DATA_MSB, 0, 0x00) \
X(CAP_SENSE_DIFF_DATA_LSB, 0, 0x00) \
X(CAP_SENSE_COMP_MSB, 0, 0x00) \
X(CAP_SENSE_COMP_LSB, 0, 0x00)
#define X(name, reserved, default) SX9512_REG_##name,
typedef enum { SX9512_REGS SX9512_REGS_AMOUNT } sx9512_reg_t;
#undef X
struct sx9512_reg_data {
const char *name;
uint8_t reserved, default_value;
};
//! Interrupt source
//! The Irq Source register will indicate that the specified event has occurred since the last read of this register. If the
//! NIRQ function is selected for SPO2 then it will indicate the occurrence of any of these events that are not masked
//! out in register 0x09.
//! The Irq mask in register 0x09 will prevent an Irq from being indicated by the NIRQ pin but it will not prevent the
//! IRQ from being noted in this register.
struct sx9512_reg_irq_src {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t reset:1;
uint8_t touch:1;
uint8_t release:1;
uint8_t prox_near:1; //!< proximity on
uint8_t prox_far:1; //!< proximity off
uint8_t compensation_done:1; //!< write 1 to trigger compensation on all channels
uint8_t conversion_done:1;
uint8_t :1;
#else
uint8_t :1;
uint8_t conversion_done:1;
uint8_t compensation_done:1; //!< write 1 to trigger compensation on all channels
uint8_t prox_far:1; //!< proximity off
uint8_t prox_near:1; //!< proximity on
uint8_t release:1;
uint8_t touch:1;
uint8_t reset:1;
#endif
} __attribute__((__packed__));
//! Proximity status
struct sx9512_reg_prox_status {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t prox_bl0:1; //!< proximity detected on BL0
uint8_t prox_multi:1; //!< proximity detected on combined channels
uint8_t prox_multi_comp_pending:1; //!< compensation pending for combined channel prox sensing
uint8_t :5;
#else
uint8_t :5;
uint8_t prox_multi_comp_pending:1; //!< compensation pending for combined channel prox sensing
uint8_t prox_multi:1; //!< proximity detected on combined channels
uint8_t prox_bl0:1; //!< proximity detected on BL0
#endif
} __attribute__((__packed__));
//! NVM Control
//! The NVM Area field indicates which of the user NVM areas are currently programmed and active (1, 2 or 3). The
//! NVM Read bit gives the ability to manually request that the contents of the NVM be transferred to the registers
//! and NVM Burn field gives the ability to burn the current registers to the next available NVM area.
//! Normally, the transfer of data from the NVM to the registers is done automatically on power up and upon a reset
//! but occasionally a user might want to force a read manually.
//! Registers 0x07 through 0x62 are stored to NVM and loaded from NVM.
//! After writing 0xA0 (ie NVM burn), wait for at least 500ms and then power off-on the IC for changes to be effective.
//! Caution, there are only three user areas and attempts to burn values beyond user area 3 will be ignored.
struct sx9512_reg_nvm_ctrl {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t nvm_burn:4; //!< write 0x50 followed by 0xA0 to initiate transfer of reg 0x07-0x62 to NVM
uint8_t nvm_read:1; //!< trigger NVM read to registers
uint8_t nvm_area:3; //!< indicates current active NVM area
#else
uint8_t nvm_area:3; //!< indicates current active NVM area
uint8_t nvm_read:1; //!< trigger NVM read to registers
uint8_t nvm_burn:4; //!< write 0x50 followed by 0xA0 to initiate transfer of reg 0x07-0x62 to NVM
#endif
} __attribute__((__packed__));
//! SPO2 Mode Control
//! The SPO2 Config field will specify the functionality of the SPO pin. When selected as NIRQ, the open drain output
//! will go low whenever a non-masked Irq occurs and the NIRQ will go back high after a register 0x00 is read over
//! the I2C. When selected as Buzzer, the SPO2 pin will drive a 2 phase 2 frequency signal onto an external buzzer
//! for each specified event (see Buzzer section). When selected as SPO2, pin operates as an analog output similar
//! to SPO1 (see SPO section). If selected as TV power state, the pin is driven from the system PMIC with a high
//! (SPO2 = SVDD) indicating that the system power is on and a low (SPO2 = GND) when the system power is off.
//! The TV Power State bit reads back the current state of SPO2 if SPO2 is selected for TV power state, otherwise
//! the system should write to this bit to indicate the current system power state. The SX9512/12B/13/13B needs to
//! know the current state in able to correctly process some of the LED modes for the Power Button (see LED
//! modes).
struct sx9512_reg_spo2_mode {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t :1;
uint8_t spo2_config:2; //!< set function of SPO2 pin
uint8_t tv_power_state:1; //!< if SPO2 set to TV power state input then TV power state indicated by this bit.
uint8_t :4;
#else
uint8_t :4;
uint8_t tv_power_state:1; //!< if SPO2 set to TV power state input then TV power state indicated by this bit.
uint8_t spo2_config:2; //!< set function of SPO2 pin
uint8_t :1;
#endif
} __attribute__((__packed__));
//! Interrupt Request Mask
//! Set which Irqs will trigger an NIRQ (if enabled on SPO2) and report in reg 0x00
//! 0=Disable IRQ
//! 1=Enable IRQ
struct sx9512_reg_irq_mask {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t reset:1;
uint8_t touch:1;
uint8_t release:1;
uint8_t prox_near:1; //!< proximity on
uint8_t prox_far:1; //!< proximity off
uint8_t compensation_done:1;
uint8_t :2;
#else
uint8_t :2;
uint8_t compensation_done:1;
uint8_t prox_far:1; //!< proximity off
uint8_t prox_near:1; //!< proximity on
uint8_t release:1;
uint8_t touch:1;
uint8_t reset:1;
#endif
} __attribute__((__packed__));
//! LED Mode
struct sx9512_reg_led_mode {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t led_fade_repeat:4;
uint8_t :1;
uint8_t led_fading:1;
uint8_t led_mode:2;
#else
uint8_t led_mode:2;
uint8_t led_fading:1;
uint8_t :1;
uint8_t led_fade_repeat:4;
#endif
} __attribute__((__packed__));
//! LED off delay
struct sx9512_reg_led_off_delay {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t led_engine1_delay_off_time:4;
uint8_t led_engine2_delay_off_time:4;
#else
uint8_t led_engine2_delay_off_time:4;
uint8_t led_engine1_delay_off_time:4;
#endif
} __attribute__((__packed__));
//! LED Engine fade in/out timing
struct sx9512_reg_led_fade {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t led_engine_fade_in_time:4;
uint8_t led_engine_fade_out_time:4;
#else
uint8_t led_engine_fade_out_time:4;
uint8_t led_engine_fade_in_time:4;
#endif
} __attribute__((__packed__));
//! LED power button mode
struct sx9512_reg_led_pwr_mode {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t power_led_off_mode:1;
uint8_t power_led_max_level:1;
uint8_t power_led_breath_max:1;
uint8_t power_led_waveform:1;
uint8_t :2;
uint8_t power_button_enable:1;
uint8_t led_touch_poarity_invert:1;
#else
uint8_t led_touch_poarity_invert:1;
uint8_t power_button_enable:1;
uint8_t :2;
uint8_t power_led_waveform:1;
uint8_t power_led_breath_max:1;
uint8_t power_led_max_level:1;
uint8_t power_led_off_mode:1;
#endif
} __attribute__((__packed__));
//! CapSense delta Cin range and LS control
struct sx9512_reg_cap_sense_range {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t ls_control:2;
uint8_t :4;
uint8_t delta_cin_range:2;
#else
uint8_t delta_cin_range:2;
uint8_t :4;
uint8_t ls_control:2;
#endif
} __attribute__((__packed__));
//! CapSense auto compensation, proximity on BL0 and combined channel proximity
struct sx9512_reg_cap_sense_op {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t auto_compensation:1;
uint8_t proximity_bl0:1;
uint8_t proximity_combined_channels:1;
uint8_t set_to_0x14:5;
#else
uint8_t set_to_0x14:5;
uint8_t proximity_combined_channels:1;
uint8_t proximity_bl0:1;
uint8_t auto_compensation:1;
#endif
} __attribute__((__packed__));
//! CapSense raw data filter coef, digital gain, I2C touch reporting and CapSense
struct sx9512_reg_cap_sense_mode {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t raw_filter:3;
uint8_t touch_reporting:1;
uint8_t cap_sense_digital_gain:2;
uint8_t cap_sense_report_mode:2;
#else
uint8_t cap_sense_report_mode:2;
uint8_t cap_sense_digital_gain:2;
uint8_t touch_reporting:1;
uint8_t raw_filter:3;
#endif
} __attribute__((__packed__));
//! CapSense debounce
struct sx9512_reg_cap_sense_debounce {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t cap_sense_prox_near_debounce:2;
uint8_t cap_sense_prox_far_debounce:2;
uint8_t cap_sense_touch_debounce:2;
uint8_t cap_sense_release_debounce:2;
#else
uint8_t cap_sense_release_debounce:2;
uint8_t cap_sense_touch_debounce:2;
uint8_t cap_sense_prox_far_debounce:2;
uint8_t cap_sense_prox_near_debounce:2;
#endif
} __attribute__((__packed__));
//! CapSense positive filter coef, positive auto compensation debounce and proximity hyst
struct sx9512_reg_cap_sense_pos_filt {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t cap_sense_prox_hyst:3;
uint8_t cap_sense_pos_comp_debounce:2;
uint8_t cap_sense_avg_pos_filt_coef:3;
#else
uint8_t cap_sense_avg_pos_filt_coef:3;
uint8_t cap_sense_pos_comp_debounce:2;
uint8_t cap_sense_prox_hyst:3;
#endif
} __attribute__((__packed__));
//! CapSense negative filter coef, negative auto compensation debounce and touch hyst
struct sx9512_reg_cap_sense_neg_filt {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t cap_sense_touch_hyst:3;
uint8_t cap_sense_neg_comp_debounce:2;
uint8_t cap_sense_avg_neg_filt_coef:3;
#else
uint8_t cap_sense_avg_neg_filt_coef:3;
uint8_t cap_sense_neg_comp_debounce:2;
uint8_t cap_sense_touch_hyst:3;
#endif
} __attribute__((__packed__));
//! CapSense stuck-at timer and periodic compensation timer
struct sx9512_reg_cap_sense_stuck {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t cap_sense_stuck_at_timer:4;
uint8_t cap_sense_periodic_comp:4;
#else
uint8_t cap_sense_periodic_comp:4;
uint8_t cap_sense_stuck_at_timer:4;
#endif
} __attribute__((__packed__));
//! CapSense frame skip setting from active and sleep
struct sx9512_reg_cap_sense_frame_skip {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t cap_sense_active_frame_skip:4;
uint8_t cap_sense_sleep_frame_skip:4;
#else
uint8_t cap_sense_sleep_frame_skip:4;
uint8_t cap_sense_active_frame_skip:4;
#endif
} __attribute__((__packed__));
//! CapSense sleep enable, auto compensation channels threshold, inactive BL control
struct sx9512_reg_cap_sense_misc {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t :2;
uint8_t comp_chan_num_thresh:2;
uint8_t cap_sense_sleep_mode_enable:1;
uint8_t :1;
uint8_t cap_sense_inactive_bl_mode:2;
#else
uint8_t cap_sense_inactive_bl_mode:2;
uint8_t :1;
uint8_t cap_sense_sleep_mode_enable:1;
uint8_t comp_chan_num_thresh:2;
uint8_t :2;
#endif
} __attribute__((__packed__));
//! SPO analog output for proximity
struct sx9512_reg_spo_level_prox {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t spo_report_prox:1;
uint8_t spo_prox_channel_mapping:1;
uint8_t spo_level_prox:6;
#else
uint8_t spo_level_prox:6;
uint8_t spo_prox_channel_mapping:1;
uint8_t spo_report_prox:1;
#endif
} __attribute__((__packed__));
//! Buzzer trigger event selection
struct sx9512_reg_buzzer_trigger {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t :3;
uint8_t buzzer_near:1;
uint8_t buzzer_far:1;
uint8_t buzzer_touch:1;
uint8_t buzzer_release:1;
uint8_t buzzer_idle_level:1;
#else
uint8_t buzzer_idle_level:1;
uint8_t buzzer_release:1;
uint8_t buzzer_touch:1;
uint8_t buzzer_far:1;
uint8_t buzzer_near:1;
uint8_t :3;
#endif
} __attribute__((__packed__));
//! Buzzer duration frequency
struct sx9512_reg_buzzer_freq {
#ifdef BIT_ORDER_BIG_ENDIAN
uint8_t buzzer_phase1_duration:2;
uint8_t buzzer_phase1_freq:2;
uint8_t buzzer_phase2_duration:2;
uint8_t buzzer_phase2_freq:2;
#else
uint8_t buzzer_phase2_freq:2;
uint8_t buzzer_phase2_duration:2;
uint8_t buzzer_phase1_freq:2;
uint8_t buzzer_phase1_duration:2;
#endif
} __attribute__((__packed__));
struct sx9512_reg_nvm {
struct sx9512_reg_spo2_mode spo2_mode; //!< SPO2 mode control
uint8_t pwr_key; //!< Power key control
struct sx9512_reg_irq_mask irq_mask; //!< Interrupt request mask
uint8_t reserved_0a[2];
uint8_t led_map[2]; //!< LED map for engine 1/2
uint8_t led_pwm_freq; //!< LED PWM frequency
struct sx9512_reg_led_mode led_mode; //!< LED mode
uint8_t led_idle; //!< LED idle level
struct sx9512_reg_led_off_delay led_off_delay; //!< LED off delay
uint8_t led1_on; //!< LED engine 1 on level
struct sx9512_reg_led_fade led1_fade; //!< LED engine 1 fade in/out time
uint8_t led2_on; //!< LED engine 2 on level
struct sx9512_reg_led_fade led2_fade; //!< LED engine 2 fade in/out time
uint8_t led_pwr_idle; //!< LED power button idle level
uint8_t led_pwr_on; //!< LED power button on level
uint8_t led_pwr_off; //!< LED power button off level
uint8_t led_pwr_fade; //!< LED power button fade in/out time
uint8_t led_pwr_on_pw; //!< LED power on pulse width
struct sx9512_reg_led_pwr_mode led_pwr_mode; //!< LED power button mode
uint8_t reserved_1c[2];
uint8_t cap_sense_enable; //!< CapSense enable
struct sx9512_reg_cap_sense_range cap_sense_range[9]; //!< CapSense delta Cin range and LS control chan 0-7
//struct sx9512_reg_cap_sense_range cap_sense_range_all; //!< CapSense delta Cin range and LS control for prox sensor (combined channels)
uint8_t cap_sense_thresh[9]; //!< CapSense detection threshold for chan 0-7
//uint8_t cap_sense_thesh_comb; //!< CapSense detection threshold for prox sensor (combined channels)
struct sx9512_reg_cap_sense_op cap_sense_op; //!< CapSense auto compensation, proximity on BL0 and combined channel proximity enable
struct sx9512_reg_cap_sense_mode cap_sense_mode; //!< CapSense raw data filter coef, digital gain, I2C touch reporting and CapSense reporting
struct sx9512_reg_cap_sense_debounce cap_sense_debounce; //!< CapSense debounce
uint8_t cap_sense_neg_comp_thresh; //!< CapSense negative auto compensation threshold
uint8_t cap_sense_pos_comp_thresh; //!< CapSense positive auto compensation threshold
struct sx9512_reg_cap_sense_pos_filt cap_sense_pos_filt; //!< CapSense positive filter coef, positive auto compensation debounce and proximity hyst
struct sx9512_reg_cap_sense_neg_filt cap_sense_neg_filt; //!< CapSense negative filter coef, negative auto compensation debounce and touch hyst
struct sx9512_reg_cap_sense_stuck cap_sense_stuck; //!< CapSense stuck-at timer and periodic compensation timer
struct sx9512_reg_cap_sense_frame_skip cap_sense_frame_skip; //!< CapSense frame skip setting from active and sleep
struct sx9512_reg_cap_sense_misc cap_sense_misc; //!< CapSense sleep enable, auto compensation channels threshold, inactive BL control
uint8_t prox_comb_chan_mask; //!< Proximity combined channel mode channel mapping
uint8_t reserved_3c[2];
uint8_t spo_chan_map; //!< SPO channel mapping
uint8_t spo_level_bl[8]; //!< SPO analog output levels
uint8_t spo_level_idle; //!< SPO analog output level for idle
struct sx9512_reg_spo_level_prox spo_level_prox; //!< SPO analog output for proximity
uint8_t reserved_49[2];
struct sx9512_reg_buzzer_trigger buzzer_trigger; //!< Buzzer trigger event selection
struct sx9512_reg_buzzer_freq buzzer_freq; //!< Buzzer duration frequency
} __attribute__((__packed__));
struct sx9512_reg {
struct sx9512_reg_irq_src irq_src; //!< Interrupt source
uint8_t touch_status; //!< Touch status
struct sx9512_reg_prox_status prox_status; //!< Proximity status
uint8_t comp_status; //!< Compensation status
struct sx9512_reg_nvm_ctrl nvm_ctrl; //!< NVM control
struct sx9512_reg_nvm nvm; //!< Non-volatile memory
uint8_t cap_sense_chan_select; //!< CapSense channel select for readback
uint16_t cap_sense_useful_data; //!< CapSense useful data
uint16_t cap_sense_average_data; //!< CapSense average data
uint16_t cap_sense_diff_data; //!< CapSense diff data
uint16_t cap_sense_comp; //!< CapSense compensation DAC value
} __attribute__((__packed__));
struct sx9512_reg_status {
struct sx9512_reg_irq_src irq_src; //!< Interrupt source
uint8_t touch_status; //!< Touch status
struct sx9512_reg_prox_status prox_status; //!< Proximity status
} __attribute__((__packed__));
struct sx9512_touch_state {
uint8_t state;
uint8_t touched;
uint8_t released;
};
int sx9512_init(const char *dev, int addr, struct sx9512_reg_nvm *nvm);
const char *sx9512_reg_name(sx9512_reg_t reg);
int sx9512_reg_reserved(sx9512_reg_t reg);
int sx9512_reset(int fd);
int sx9512_reset_restore_led_state(int fd);
int sx9512_read_interrupt(int fd);
int sx9512_read_buttons(int fd);
int sx9512_read_proximity(int fd);
int sx9512_read_status(int fd, struct sx9512_reg_status *status);
int sx9512_read_status_cached(int fd, struct sx9512_touch_state *touch_state);
int sx9512_reg_nvm_load(int fd);
int sx9512_reg_nvm_store(int fd);
int sx9512_reg_nvm_read(int fd, struct sx9512_reg_nvm *p);
int sx9512_reg_nvm_write(int fd, struct sx9512_reg_nvm *p);
void sx9512_reg_nvm_init(struct sx9512_reg_nvm *p);
void sx9512_reg_nvm_init_defaults(struct sx9512_reg_nvm *p, uint8_t capsense_channels, uint8_t led_channels);
void sx9512_reg_nvm_init_cg300(struct sx9512_reg_nvm *p);
void sx9512_reg_nvm_init_cg301(struct sx9512_reg_nvm *p);
void sx9512_reg_nvm_init_eg300(struct sx9512_reg_nvm *p);
void sx9512_reg_nvm_init_dg400(struct sx9512_reg_nvm *p);
#endif

View file

@ -21,13 +21,6 @@
#include "touch_sx9512.h"
#include "gpio.h"
/* register names, from page 29, */
#define SX9512_IRQSRC 0
#define SX9512_TOUCHSTATUS 1
#define SX9512_PROXSTATUS 2
#define SX9512_LEDMAP1 0xC
#define SX9512_LEDMAP2 0xD
#define SX9512_IRQ_RESET 1<<7
#define SX9512_IRQ_TOUCH 1<<6
#define SX9512_IRQ_RELEASE 1<<5
@ -36,21 +29,11 @@
#define SX9512_IRQ_COM 1<<2
#define SX9512_IRQ_CONV 1<<1
/* CG300 config:
BL0: Proximity
BL1: Wireless button
BL2: WPS button, WPS LED
BL3: Dect button, Dect LED
BL4: Internet green LED
BL5: Internet red LED
BL6, BL7: Unused.
*/
struct i2c_reg_tab {
char addr;
char value;
char range;
char range; /* if set registers starting from addr to addr+range will be set to the same value */
};
struct i2c_touch{
@ -60,9 +43,6 @@ struct i2c_touch{
int shadow_proximity;
int addr;
int irq_button;
const struct i2c_reg_tab *init_tab;
int init_tab_len;
const char *name;
};
struct led_data {
@ -76,274 +56,12 @@ struct sx9512_list{
struct led_data *drv;
};
/* holds all leds that is configured to be used. needed for reset */
static LIST_HEAD(sx9512_leds);
static struct i2c_touch *i2c_touch_current; /* pointer to current active table */
//static void do_init_tab( struct i2c_touch *i2c_touch);
//static struct i2c_touch * i2c_init(struct uci_context *uci_ctx, const char* i2c_dev_name, struct i2c_touch* i2c_touch_list, int len);
static struct i2c_touch i2c_touch_current; /* pointer to current active table */
static int sx9512_led_set_state(struct led_drv *drv, led_state_t state);
static led_state_t sx9512_led_get_state(struct led_drv *drv);
static int sx9512_set_state(struct led_data *p, led_state_t state);
/*addr,value,range*/
static const struct i2c_reg_tab i2c_init_tab_cg300[]={
{0xFF, 0xDE, 0x00 }, /* Reset chip */
{0xFF, 0x00, 0x00 }, /* Reset chip */
{0x04, 0x00, 0x00 }, /* NVM Control */
{0x07, 0x00, 0x00 }, /* SPO2, set as interrupt */
{0x08, 0x00, 0x00 }, /* Power key ctrl */
{0x09, 0x78, 0x00 }, /* Irq MASK */
{0x0C, 0x01, 0x00 }, /* LED map 1, BL0 (why?) */
{0x0D, 0x3c, 0x00 }, /* LED map 2 BL2 -> BL5*/
{0x0E, 0x10, 0x00 }, /* LED Pwm Frq */
{0x0F, 0x00, 0x00 }, /* LED Mode */
{0x10, 0xFF, 0x00 }, /* Led Idle LED on */
{0x11, 0x00, 0x00 }, /* Led 1 off delay */
{0x12, 0xFF, 0x00 }, /* Led 1 on */
{0x13, 0x00, 0x00 }, /* Led 1 fade */
{0x14, 0xFF, 0x00 }, /* Led 2 on */
{0x15, 0x00, 0x00 }, /* Led 2 fade */
{0x16, 0xFF, 0x00 }, /* Led Pwr Idle */
{0x17, 0xFF, 0x00 }, /* Led Pwr On */
{0x18, 0x00, 0x00 }, /* Led Pwr Off */
{0x19, 0x00, 0x00 }, /* Led Pwr fade */
{0x1A, 0x00, 0x00 }, /* Led Pwr On Pw */
{0x1B, 0x00, 0x00 }, /* Disable BL7 as power button */
{0x1E, 0x0F, 0x00 }, /* Cap sens enabled, bl0-bl3 */
{0x1F, 0x43, 0x00 }, /* Cap sens BL0 */
{0x20, 0x41, 0x07 }, /* Cap sens range 20-27 BL1->BL7 */
{0x28, 0x02, 0x00 }, /* Cap sens thresh BL 0 */
{0x29, 0x04, 0x07 }, /* Cap sens thresh 28-30 */
{0x31, 0x54, 0x00 }, /* Cap sens Op */
{0x32, 0x70, 0x00 }, /* Cap Sens Mode, filter 1-1/8, report all */
{0x33, 0x01, 0x00 }, /* Cap Sens Debounce */
{0x34, 0x80, 0x00 }, /* Cap Sens Neg Comp Thresh */
{0x35, 0x80, 0x00 }, /* Cap Sens Pos Comp Thresh */
{0x36, 0x17, 0x00 }, /* Cap Sens Pos Filt, hyst 2, debounce 4, 1-1/128 */
{0x37, 0x15, 0x00 }, /* Cap Sens Neg Filt, hyst 2, debounce 4, 1-1/32 */
{0x38, 0x00, 0x00 }, /* Cap Sens */
{0x39, 0x00, 0x00 }, /* Cap Sens Frame Skip */
{0x3A, 0x00, 0x00 }, /* Cap Sens Misc */
{0x3B, 0x00, 0x00 }, /* Prox Comb Chan Mask */
{0x3E, 0xFF, 0x00 }, /* SPO Chan Map */
{0x00, 0x04, 0x00 }, /* Trigger compensation */
};
/* CG301 config:
BL0: Proximity, Status white LED
BL1: Service button, Service white LED
BL2: Service red LED
BL3: Pair button, Pair white LED
BL4: Cancel button, Cancel red LED
BL5: Communication button, Communication white LED
BL6: Communication red LED
BL7: Unused.
*/
static const struct i2c_reg_tab i2c_init_tab_cg301[]={
{0xFF, 0xDE, 0x00 }, /* Reset chip */
{0xFF, 0x00, 0x00 }, /* Reset chip */
{0x04, 0x00, 0x00 }, /* NVM Control */
{0x07, 0x00, 0x00 }, /* SPO2, set as interrupt */
{0x08, 0x00, 0x00 }, /* Power key ctrl */
{0x09, 0x78, 0x00 }, /* Irq MASK, touch+release+near+far */
{0x0C, 0x00, 0x00 }, /* LED map 1, none */
{0x0D, 0x7f, 0x00 }, /* LED map 2, BL0 -> BL6 */
{0x0E, 0x10, 0x00 }, /* LED Pwm Frq */
{0x0F, 0x00, 0x00 }, /* LED Mode */
{0x10, 0xFF, 0x00 }, /* Led Idle LED on */
{0x11, 0x00, 0x00 }, /* Led 1 off delay */
{0x12, 0xFF, 0x00 }, /* Led 1 on */
{0x13, 0x00, 0x00 }, /* Led 1 fade */
{0x14, 0xFF, 0x00 }, /* Led 2 on */
{0x15, 0x00, 0x00 }, /* Led 2 fade */
{0x16, 0xFF, 0x00 }, /* Led Pwr Idle */
{0x17, 0xFF, 0x00 }, /* Led Pwr On */
{0x18, 0x00, 0x00 }, /* Led Pwr Off */
{0x19, 0x00, 0x00 }, /* Led Pwr fade */
{0x1A, 0x00, 0x00 }, /* Led Pwr On Pw */
{0x1B, 0x00, 0x00 }, /* Disable BL7 as power button */
{0x1E, 0x3b, 0x00 }, /* Cap sens enabled, bl0,bl1,bl3-bl5 */
{0x1F, 0x43, 0x00 }, /* Cap sens range BL0 */
{0x20, 0x41, 0x07 }, /* Cap sens range BL1->BL7 [20-27] */
{0x28, 0x02, 0x00 }, /* Cap sens thresh BL0 */
{0x29, 0x04, 0x07 }, /* Cap sens thresh BL1->BL7 [29-30] */
{0x31, 0x54, 0x00 }, /* Cap sens Op */
{0x32, 0x70, 0x00 }, /* Cap Sens Mode, filter 1-1/8, report all */
{0x33, 0x01, 0x00 }, /* Cap Sens Debounce */
{0x34, 0x80, 0x00 }, /* Cap Sens Neg Comp Thresh */
{0x35, 0x80, 0x00 }, /* Cap Sens Pos Comp Thresh */
{0x36, 0x17, 0x00 }, /* Cap Sens Pos Filt, hyst 2, debounce 4, 1-1/128 */
{0x37, 0x15, 0x00 }, /* Cap Sens Neg Filt, hyst 2, debounce 4, 1-1/32 */
{0x38, 0x00, 0x00 }, /* Cap Sens */
{0x39, 0x00, 0x00 }, /* Cap Sens Frame Skip */
{0x3A, 0x00, 0x00 }, /* Cap Sens Misc */
{0x3B, 0x00, 0x00 }, /* Prox Comb Chan Mask */
{0x3E, 0xFF, 0x00 }, /* SPO Chan Map */
{0x00, 0x04, 0x00 }, /* Trigger compensation */
};
/* EG300 config:
BL0: Proximity, WAN green LED
BL1: Wireless button, WAN yellow LED
BL2: WPS button, WPS LED
BL3: Dect button, Dect LED
BL4: Internet green LED
BL5: Internet red LED
BL6: Ethernet LED
BL7: Voice LED
Only the led 1 and led2 maps differ from CG300.
*/
static const struct i2c_reg_tab i2c_init_tab_eg300[]={
{0xFF, 0xDE, 0x00 }, /* Reset chip */
{0xFF, 0x00, 0x00 }, /* Reset chip */
{0x04, 0x00, 0x00 }, /* NVM Control */
{0x07, 0x00, 0x00 }, /* SPO2, set as interrupt */
{0x08, 0x00, 0x00 }, /* Power key ctrl */
{0x09, 0x78, 0x00 }, /* Irq MASK */
{0x0C, 0x00, 0x00 }, /* LED map 1, none */
{0x0D, 0xff, 0x00 }, /* LED map 2, all */
{0x0E, 0x10, 0x00 }, /* LED Pwm Frq */
{0x0F, 0x00, 0x00 }, /* LED Mode */
{0x10, 0xFF, 0x00 }, /* Led Idle LED on */
{0x11, 0x00, 0x00 }, /* Led 1 off delay */
{0x12, 0xFF, 0x00 }, /* Led 1 on */
{0x13, 0x00, 0x00 }, /* Led 1 fade */
{0x14, 0xFF, 0x00 }, /* Led 2 on */
{0x15, 0x00, 0x00 }, /* Led 2 fade */
{0x16, 0xFF, 0x00 }, /* Led Pwr Idle */
{0x17, 0xFF, 0x00 }, /* Led Pwr On */
{0x18, 0x00, 0x00 }, /* Led Pwr Off */
{0x19, 0x00, 0x00 }, /* Led Pwr fade */
{0x1A, 0x00, 0x00 }, /* Led Pwr On Pw */
{0x1B, 0x00, 0x00 }, /* Disable BL7 as power button */
{0x1E, 0x0F, 0x00 }, /* Cap sens enabled, bl0-bl3 */
{0x1F, 0x43, 0x00 }, /* Cap sens BL0 */
{0x20, 0x43, 0x07 }, /* Cap sens range 20-27 BL1->BL7 */
{0x28, 0x02, 0x00 }, /* Cap sens thresh BL 0 */
{0x29, 0x04, 0x07 }, /* Cap sens thresh 28-30 */
{0x31, 0x54, 0x00 }, /* Cap sens Op */
{0x32, 0x70, 0x00 }, /* Cap Sens Mode, filter 1-1/8, report all */
{0x33, 0x01, 0x00 }, /* Cap Sens Debounce */
{0x34, 0x80, 0x00 }, /* Cap Sens Neg Comp Thresh */
{0x35, 0x80, 0x00 }, /* Cap Sens Pos Comp Thresh */
{0x36, 0x17, 0x00 }, /* Cap Sens Pos Filt, hyst 2, debounce 4, 1-1/128 */
{0x37, 0x15, 0x00 }, /* Cap Sens Neg Filt, hyst 2, debounce 4, 1-1/32 */
{0x38, 0x00, 0x00 }, /* Cap Sens */
{0x39, 0x00, 0x00 }, /* Cap Sens Frame Skip */
{0x3A, 0x00, 0x00 }, /* Cap Sens Misc */
{0x3B, 0x00, 0x00 }, /* Prox Comb Chan Mask */
{0x3E, 0xFF, 0x00 }, /* SPO Chan Map */
{0x00, 0x04, 0x00 }, /* Trigger compensation */
};
static struct i2c_touch i2c_touch_list[] = {
{.addr = 0x2b,
.name = "CG300",
.irq_button = 1,
.init_tab = i2c_init_tab_cg300,
.init_tab_len = sizeof(i2c_init_tab_cg300)/sizeof(struct i2c_reg_tab),
},
{.addr = 0x2b,
.name = "CG301",
.irq_button = 1,
.init_tab = i2c_init_tab_cg301,
.init_tab_len = sizeof(i2c_init_tab_cg301)/sizeof(struct i2c_reg_tab),
},
{.addr = 0x2b,
.name = "DG200",
.irq_button = 1,
.init_tab = i2c_init_tab_eg300,
.init_tab_len = sizeof(i2c_init_tab_eg300)/sizeof(struct i2c_reg_tab),
},
{.addr = 0x2b,
.name = "EG300",
.irq_button = 1,
.init_tab = i2c_init_tab_eg300,
.init_tab_len = sizeof(i2c_init_tab_eg300)/sizeof(struct i2c_reg_tab),
}
};
static void do_init_tab( struct i2c_touch *i2c_touch)
{
const struct i2c_reg_tab *tab;
int i;
tab = i2c_touch->init_tab;
for (i = 0 ; i < i2c_touch->init_tab_len ; i++){
int y;
int ret;
for ( y = 0 ; y <= tab[i].range; y++ ){
DBG(3,"%s: addr %02X = %02X ",__func__,(unsigned char)tab[i].addr+y, (unsigned char)tab[i].value);
ret = i2c_smbus_write_byte_data(i2c_touch->dev, tab[i].addr+y, tab[i].value);
if (ret < 0){
perror("write to i2c dev\n");
}
}
}
// dump_i2c(i2c_touch->dev,0,13);
}
struct i2c_touch * i2c_init(struct uci_context *uci_ctx, const char* i2c_dev_name, struct i2c_touch* touch_list, int len)
{
const char *p;
int i;
struct i2c_touch *i2c_touch;
p = ucix_get_option(uci_ctx, "hw", "board", "hardware");
if (p == 0){
syslog(LOG_INFO, "%s: Missing Hardware identifier in configuration. I2C is not started\n",__func__);
return 0;
}
i2c_touch = NULL;
for (i = 0; i < len; i++) {
if (!strcmp(touch_list[i].name, p)) {
DBG(1,"I2C hardware platform %s found.\n", p);
i2c_touch = &touch_list[i];
break;
}
}
if (!i2c_touch) {
DBG(1,"No I2C hardware found: %s.\n", p);
return 0;
}
i2c_touch->dev = i2c_open_dev(i2c_dev_name, i2c_touch->addr,
I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE);
if (i2c_touch->dev < 0) {
syslog(LOG_INFO,"%s: could not open i2c touch device\n",__func__);
i2c_touch->dev = 0;
return 0;
}
DBG(1,"Opened device and selected address %x \n", i2c_touch->addr);
do_init_tab(i2c_touch);
return i2c_touch;
}
extern struct uloop_timeout i2c_touch_reset_timer;
@ -355,7 +73,8 @@ struct uloop_timeout i2c_touch_reset_timer = { .cb = sx9512_reset_handler };
static void sx9512_reset_handler(struct uloop_timeout *timeout)
{
struct list_head *i;
do_init_tab(i2c_touch_current);
//do_init_tab(i2c_touch_current);
sx9512_reset_restore_led_state(i2c_touch_current.dev);
list_for_each(i, &sx9512_leds) {
struct sx9512_list *node = list_entry(i, struct sx9512_list, list);
@ -371,7 +90,7 @@ static int sx9512_set_state(struct led_data *p, led_state_t state)
int ret;
int bit = 1 << p->addr;
ret = i2c_smbus_read_byte_data(i2c_touch_current->dev, SX9512_LEDMAP2);
ret = i2c_smbus_read_byte_data(i2c_touch_current.dev, SX9512_REG_LED_MAP2);
if (ret < 0 )
syslog(LOG_ERR, "Could not read from i2c device, LedMap2 register\n");
@ -386,7 +105,7 @@ static int sx9512_set_state(struct led_data *p, led_state_t state)
p->state = state;
ret = i2c_smbus_write_byte_data(i2c_touch_current->dev, SX9512_LEDMAP2, ret);
ret = i2c_smbus_write_byte_data(i2c_touch_current.dev, SX9512_REG_LED_MAP2, ret);
if (ret < 0 ) {
syslog(LOG_ERR, "Could not write to i2c device, LedMap2 register\n");
return -1;
@ -399,7 +118,7 @@ static int sx9512_led_set_state(struct led_drv *drv, led_state_t state)
{
struct led_data *p = (struct led_data *)drv->priv;
if (!i2c_touch_current || !i2c_touch_current->dev)
if (!i2c_touch_current.dev)
return -1;
if (p->addr > 7){
@ -440,32 +159,32 @@ void sx9512_check(void)
int got_irq = 0;
int ret;
if (!i2c_touch_current || !i2c_touch_current->dev)
if (!i2c_touch_current.dev)
return;
if (i2c_touch_current->irq_button) {
if (i2c_touch_current.irq_button) {
int button;
button = board_ioctl( BOARD_IOCTL_GET_GPIO, 0, 0, NULL, i2c_touch_current->irq_button, 0);
button = board_ioctl( BOARD_IOCTL_GET_GPIO, 0, 0, NULL, i2c_touch_current.irq_button, 0);
if (button == 0)
got_irq = 1;
}
if ( got_irq ) {
ret = i2c_smbus_read_byte_data(i2c_touch_current->dev, SX9512_IRQSRC);
ret = i2c_smbus_read_byte_data(i2c_touch_current.dev, SX9512_REG_IRQ_SRC);
if (ret < 0 )
syslog(LOG_ERR, "Could not read from i2c device, irq status register\n");
i2c_touch_current->shadow_irq = ret;
i2c_touch_current.shadow_irq = ret;
ret = i2c_smbus_read_byte_data(i2c_touch_current->dev, SX9512_TOUCHSTATUS);
ret = i2c_smbus_read_byte_data(i2c_touch_current.dev, SX9512_REG_TOUCH_STATUS);
if (ret < 0 )
syslog(LOG_ERR, "Could not read from i2c device, touch register\n");
i2c_touch_current->shadow_touch = ret;
i2c_touch_current.shadow_touch = ret;
ret = i2c_smbus_read_byte_data(i2c_touch_current->dev, SX9512_PROXSTATUS);
ret = i2c_smbus_read_byte_data(i2c_touch_current.dev, SX9512_REG_PROX_STATUS);
if (ret < 0 )
syslog(LOG_ERR, "Could not read from i2c device, proximity register\n");
i2c_touch_current->shadow_proximity = ret;
i2c_touch_current.shadow_proximity = ret;
}
#if 0
DEBUG_PRINT("%02x %02x %02x: irq ->",
@ -498,8 +217,8 @@ void sx9512_check(void)
button address 8 proximity BL0 NEAR
button address 9 proximity BL0 FAR
return RELEASED = no action on this button
return PRESSED = button pressed
return BUTTON_RELEASED = no action on this button
return BUTTON_PRESSED = button pressed
return -1 = error
*/
@ -508,49 +227,45 @@ static button_state_t sx9512_button_get_state(struct button_drv *drv)
struct button_data *p = (struct button_data *)drv->priv;
int bit = 1 << p->addr;
if (!i2c_touch_current || !i2c_touch_current->dev)
if (!i2c_touch_current.dev)
return -1;
if (p->addr < 8) {
if ( bit & i2c_touch_current->shadow_touch ) {
i2c_touch_current->shadow_touch = i2c_touch_current->shadow_touch & ~bit;
p->state = PRESSED;
return PRESSED;
if ( bit & i2c_touch_current.shadow_touch ) {
i2c_touch_current.shadow_touch = i2c_touch_current.shadow_touch & ~bit;
return p->state = BUTTON_PRESSED;
}
/* if the button was already pressed and we don't have a release irq report it as still pressed */
if( p->state == PRESSED){
if (! (i2c_touch_current->shadow_irq & SX9512_IRQ_RELEASE) ) {
return PRESSED;
if( p->state == BUTTON_PRESSED){
if (! (i2c_touch_current.shadow_irq & SX9512_IRQ_RELEASE) ) {
return BUTTON_PRESSED;
}
}
p->state = RELEASED;
return RELEASED;
return p->state = BUTTON_RELEASED;
/* proximity NEAR */
}else if (p->addr == 8 ) {
bit = 1<<7;
if( i2c_touch_current->shadow_irq & SX9512_IRQ_NEAR ) {
i2c_touch_current->shadow_irq &= ~SX9512_IRQ_NEAR;
if ( bit & i2c_touch_current->shadow_proximity ) {
i2c_touch_current->shadow_proximity = i2c_touch_current->shadow_proximity & ~bit;
p->state = PRESSED;
return PRESSED;
if( i2c_touch_current.shadow_irq & SX9512_IRQ_NEAR ) {
i2c_touch_current.shadow_irq &= ~SX9512_IRQ_NEAR;
if ( bit & i2c_touch_current.shadow_proximity ) {
i2c_touch_current.shadow_proximity = i2c_touch_current.shadow_proximity & ~bit;
return p->state = BUTTON_PRESSED;
}
}
return RELEASED;
return BUTTON_RELEASED;
/* proximity FAR */
}else if (p->addr == 9) {
if( i2c_touch_current->shadow_irq & SX9512_IRQ_FAR ) {
i2c_touch_current->shadow_irq &= ~SX9512_IRQ_FAR;
p->state = PRESSED;
return PRESSED;
if( i2c_touch_current.shadow_irq & SX9512_IRQ_FAR ) {
i2c_touch_current.shadow_irq &= ~SX9512_IRQ_FAR;
return p->state = BUTTON_PRESSED;
}
return RELEASED;
return BUTTON_RELEASED;
}else {
DBG(1,"Button address out of range %d\n",p->addr);
return RELEASED;
return BUTTON_RELEASED;
}
}
@ -631,30 +346,88 @@ static void sx9512_led_init(struct server_ctx *s_ctx) {
}
}
void sx9512_init(struct server_ctx *s_ctx) {
struct list_head *i;
DBG(1, "");
i2c_touch_current = i2c_init(s_ctx->uci_ctx,
"/dev/i2c-0",
i2c_touch_list,
sizeof(i2c_touch_list)/sizeof(i2c_touch_list[0]));
if (i2c_touch_current != 0) {
sx9512_button_init(s_ctx);
sx9512_led_init(s_ctx);
/* Force set of initial state for leds. */
list_for_each(i, &sx9512_leds) {
struct sx9512_list *node = list_entry(i, struct sx9512_list, list);
sx9512_set_state(node->drv, node->drv->state);
}
/* start reset timer */
uloop_timeout_set(&i2c_touch_reset_timer, I2C_RESET_TIME);
void sx9512_handler_init(struct server_ctx *s_ctx)
{
char *s, *sx9512_i2c_device;
int i, fd, sx9512_i2c_address, sx9512_irq_pin, sx9512_active_capsense_channels, sx9512_active_led_channels;
struct sx9512_reg_nvm nvm;
struct list_head *il;
if(!(sx9512_i2c_device = ucix_get_option(s_ctx->uci_ctx, "hw", "board", "sx9512_i2c_device"))) {
DBG(0, "Error: option is missing: sx9512_i2c_device");
return;
}
DBG(1, "sx9512_i2c_device = [%s]", sx9512_i2c_device);
if(!(s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "sx9512_i2c_address"))) {
DBG(0, "Warning: option is missing: sx9512_i2c_address, setting to default (%02X)", SX9512_I2C_ADDRESS);
sx9512_i2c_address = SX9512_I2C_ADDRESS;
} else
sx9512_i2c_address = strtol(s,0,16);
DBG(1, "sx9512_i2c_address = [%02X]", sx9512_i2c_address);
if(!(s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "sx9512_irq_pin"))) {
DBG(0, "Error: option is missing: sx9512_irq_pin");
return;
}
sx9512_irq_pin = strtol(s,0,0);
DBG(1, "sx9512_irq_pin = [%d]", sx9512_irq_pin);
if(!(s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "sx9512_active_capsense_channels"))) {
DBG(0, "Error: option is missing: sx9512_active_capsense_channels");
return;
}
sx9512_active_capsense_channels = strtol(s,0,16);
DBG(1, "sx9512_active_capsense_channels = [%02X]", sx9512_active_capsense_channels);
if(!(s=ucix_get_option(s_ctx->uci_ctx, "hw", "board", "sx9512_active_led_channels"))) {
DBG(0, "Error: option is missing: sx9512_active_led_channels");
return;
}
sx9512_active_led_channels = strtol(s,0,16);
DBG(1, "sx9512_active_led_channels = [%02X]", sx9512_active_led_channels);
sx9512_reg_nvm_init_defaults(&nvm, sx9512_active_capsense_channels, sx9512_active_led_channels);
LIST_HEAD(sx9512_init_regs);
struct ucilist *node;
ucix_get_option_list(s_ctx->uci_ctx, "hw","sx9512_init_regs", "regs", &sx9512_init_regs);
list_for_each_entry(node,&sx9512_init_regs,list) {
sx9512_reg_t reg;
uint8_t val;
int repeat;
reg = strtol(node->val,0,16);
if(sx9512_reg_reserved(reg)) {
DBG(0, "Error: invalid sx9512 reg [%02X]", reg);
return;
}
s = ucix_get_option(s_ctx->uci_ctx, "hw", node->val, "val");
val = strtol(s,0,16);
if(!(s = ucix_get_option(s_ctx->uci_ctx, "hw", node->val, "repeat")))
repeat=1;
else
repeat=strtol(s,0,0);
for(i=0;i<repeat;i++) {
DBG(1, "sx9512_init_reg[%02X:%s=%02X]", reg, sx9512_reg_name(reg), val);
((uint8_t *)&nvm)[reg-SX9512_REG_NVM_AREA_START] = val;
reg++;
}
}
if((fd = sx9512_init(sx9512_i2c_device, sx9512_i2c_address, &nvm))<1)
return;
i2c_touch_current.dev=fd;
i2c_touch_current.addr=sx9512_i2c_address;
i2c_touch_current.irq_button=sx9512_irq_pin;
sx9512_button_init(s_ctx);
sx9512_led_init(s_ctx);
/* Force set of initial state for leds. */
list_for_each(il, &sx9512_leds) {
struct sx9512_list *node = list_entry(il, struct sx9512_list, list);
sx9512_set_state(node->drv, node->drv->state);
}
/* start reset timer */
uloop_timeout_set(&i2c_touch_reset_timer, I2C_RESET_TIME);
}

View file

@ -2,8 +2,9 @@
#define TOUCH_SX9512_H
#include "server.h"
#include "sx9512.h"
void sx9512_init(struct server_ctx *);
void sx9512_handler_init(struct server_ctx *);
void sx9512_check(void);
#endif /* TOUCH_SX9512_H */

View file

@ -171,6 +171,6 @@ void vox_init(struct server_ctx *s_ctx) {
/* arg 4 is the spi mode encoded in a string pointer */
/* mode is decribed i/bcm963xx/shared/opensource/include/bcm963xx/bcmSpiRes.h */
board_ioctl(BOARD_IOCTL_SPI_INIT, SPI_SLAVE_SELECT, 0, (char*)0, 0, 391000);
gpio_open_ioctl();
board_ioctl_init();
}
}

View file

@ -138,7 +138,7 @@ int main(int argc, char *argv[])
{
int ch;
gpio_open_ioctl();
board_ioctl_init();
/* arg 4 is the spi mode encoded in a string pointer */
/* mode is decribed i/bcm963xx/shared/opensource/include/bcm963xx/bcmSpiRes.h */
board_ioctl(BOARD_IOCTL_SPI_INIT, SPI_SLAVE_SELECT, 0, (char *)0, 0, 391000);