From 03e11a278286392dc20de57a24cadbc16d9aac3a Mon Sep 17 00:00:00 2001 From: Priya Vijayan Date: Tue, 27 Apr 2010 11:23:00 -0700 Subject: [PATCH] Touchscreen driver for Cypress panels This driver is from aava Signed-off-by: Priya Vijayan --- drivers/input/touchscreen/Kconfig | 8 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cy8ctmg110_ts.c | 815 +++++++++++++++++++++++++++++ 3 files changed, 824 insertions(+), 0 deletions(-) create mode 100644 drivers/input/touchscreen/cy8ctmg110_ts.c diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6dd2674..5ecf00d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -103,6 +103,14 @@ config TOUCHSCREEN_CORGI NOTE: this driver is deprecated, try enable SPI and generic ADS7846-based touchscreen driver. +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + default y + help + Say Y here if you have a cy8ctmg110 touchscreen capasitive touchscreen + If unsure, say N. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 15ad257..e5b5fae 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c new file mode 100644 index 0000000..5587385 --- /dev/null +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,815 @@ +/* + * cy8ctmg110_ts.c Driver for cypress touch screen controller + * Copyright (c) 2009 Aava Mobile + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CY8CTMG110_DRIVER_NAME "cy8ctmg110" +#define CY8CTMG110_DRIVER_NAME_EXT "cy8ctmg110 ext" +/*#define MOORESTOWN_CDK*/ +/*#define CY8CTMG110_DEBUG_INFO*/ +/*#define POLL_TOUCH_EVENTS*/ + + + +/*HW definations*/ + +/*Main touch specific*/ +#define CY8CTMG110_I2C_ADDR 0x38 +#define CY8CTMG110_RESET_PIN_GPIO 43 +#define CY8CTMG110_IRQ_PIN_GPIO 59 + +/*Extended specific*/ +#define CY8CTMG110_I2C_ADDR_EXT 0x39 +#define CY8CTMG110_RESET_PIN_GPIO_EXT 39 +#define CY8CTMG110_IRQ_PIN_GPIO_EXT 31 + + +#define CY8CTMG110_TOUCH_LENGHT 9787 +#define CY8CTMG110_SCREEN_LENGHT 8424 + + +/*Main Touch coordinates*/ +#define CY8CTMG110_X_MIN 0 +#define CY8CTMG110_Y_MIN 0 +#define CY8CTMG110_X_MAX 864 +#define CY8CTMG110_Y_MAX 480 + + +/*cy8ctmg110 registers defination*/ +#define CY8CTMG110_TOUCH_WAKEUP_TIME 0 +#define CY8CTMG110_TOUCH_SLEEP_TIME 2 +#define CY8CTMG110_TOUCH_X1 3 +#define CY8CTMG110_TOUCH_Y1 5 +#define CY8CTMG110_TOUCH_X2 7 +#define CY8CTMG110_TOUCH_Y2 9 +#define CY8CTMG110_FINGERS 11 +#define CY8CTMG110_GESTURE 12 +#define CY8CTMG110_VERSIONID 13 //not supported in touchpanel FW +#define CY8CTMG110_REG_MAX 13 + +#ifdef POLL_TOUCH_EVENTS + #define CY8CTMG110_POLL_TIMER_DELAY 1000*1000*100 + #define TOUCH_MAX_I2C_FAILS 50 +#endif + +#define CY8CTMG110_POLL_TIMER_DELAY 1000*1000*100 + +/* Scale factors for coordinates */ +#define X_SCALE_FACTOR 9387/8424 +#define Y_SCALE_FACTOR 97/100 + +/* For tracing */ +static u16 g_y_trace_coord = 0; + +/*if soutcanyon*/ +static bool isSc = false; + + +/* + * Touchtype + */ +enum touch_type { + TOUCH_KOSKI=1, + TOUCH_SC, + TOUCH_EXT, +}; + +/* + * The touch position structure. + */ +struct ts_event { + int x1; + int y1; + int x2; + int y2; + bool event_sended; +}; + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { + struct input_dev *input; + char phys[32]; + struct ts_event tc; + struct i2c_client *client; + bool pending; + spinlock_t lock; + bool initController; + bool sleepmode; + int irq_gpio; + int reset_gpio; + char driver_name[20]; + struct delayed_work work; + enum touch_type version_id; +#ifdef POLL_TOUCH_EVENTS + struct hrtimer timer; + int i2c_fail_count; +#endif +}; + +/* + * cy8ctmg110_poweroff is the routine that is called when touch hardware + * will powered off + */ +static void cy8ctmg110_power(struct cy8ctmg110 *ts,bool poweron) +{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("%s power:%d\n",ts->driver_name,poweron); +#endif + if (poweron) + gpio_direction_output(ts->reset_gpio, 0); + else + gpio_direction_output(ts->reset_gpio, 1); +} +/* + * cy8ctmg110_write_req write regs to the i2c devices + * + */ +static int cy8ctmg110_write_req(struct cy8ctmg110 *tsc,unsigned char reg,unsigned char len,unsigned char *value) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char i2c_data[]={0,0,0,0,0,0}; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_init_req:\n"); +#endif + + i2c_data[0]=reg; + memcpy(i2c_data+1,value,len); + + { + struct i2c_msg msg[] = { + { client->addr, 0, len+1, i2c_data }, + }; + + ret = i2c_transfer(client->adapter, msg, 1); + + if (ret != 1) { + printk("cy8ctmg110 touch : i2c write data cmd failed \n"); + return ret; + } + } + + return 0; +} +/* + * get_time + * + */ +#ifdef CY8CTMG110_DEBUG_INFO +static inline long cy8ctmg110_get_time(void) +{ + struct timeval t; + do_gettimeofday(&t); + return t.tv_usec; +} +#endif +/* + * cy8ctmg110_read_req read regs from i2c devise + * + */ +static int cy8ctmg110_read_req(struct cy8ctmg110 *tsc,unsigned char *i2c_data,unsigned char len ,unsigned char cmd) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char regs_cmd[2]={0,0}; +#ifdef CY8CTMG110_DEBUG_INFO + long starttime = cy8ctmg110_get_time(); +#endif + regs_cmd[0]=cmd; + + + /* first write slave position to i2c devices*/ + { + struct i2c_msg msg1[] = { + { client->addr, 0, 1, regs_cmd }, + }; + + ret = i2c_transfer(client->adapter, msg1, 1); + + if (ret != 1) { +#ifdef POLL_TOUCH_EVENTS + tsc->i2c_fail_count++; +#endif + return ret; + } + } + + + /* Second read data from position*/ + { + struct i2c_msg msg2[] = { + { client->addr, I2C_M_RD, len, i2c_data }, + }; + + ret = i2c_transfer(client->adapter, msg2, 1); + + + if (ret != 1) { +#ifdef POLL_TOUCH_EVENTS + tsc->i2c_fail_count++; +#endif + return ret; + } + } +#ifdef CY8CTMG110_DEBUG_INFO + printk("%s time to get data bytes read:%d time:%d\n",tsc->driver_name,len,(cy8ctmg110_get_time()-starttime)); +#endif + return 0; +} +/* + * cy8ctmg110_send_event delevery touch event to the userpace + * function use normal input interface + */ +static void cy8ctmg110_send_event(void *tsc,int x,int y) +{ + struct cy8ctmg110 *ts = tsc; + struct input_dev *input = ts->input; + u16 x2, y2; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_send_event\n"); +#endif + + if(ts->tc.event_sended == false){ + + if (ts->client->addr==CY8CTMG110_I2C_ADDR_EXT){ + /*Extended touchpanel*/ + input_report_key(input, BTN_TOUCH, 1); + + + if ( ts->pending == true){ + input_report_rel(input, REL_Y, (ts->tc.x1-x)*2); + input_report_rel(input, REL_X, (y - ts->tc.y1)*3); + ts->tc.y1 = y; + ts->tc.x1 = x; + } + else{ + ts->pending = true; + ts->tc.y1 = y; + ts->tc.x1 = x; + } + + + } + else{ + /*Main touchpanel*/ + ts->tc.y1 = y; + ts->tc.x1 = x; + ts->pending = true; + input_report_key(input, BTN_TOUCH, 1); + + x2 = y; + y2 = x; + + if (isSc == false){ + /*Main touchpanel in koski*/ + x2 = (u16)(y*X_SCALE_FACTOR); + y2 = (u16)(x*Y_SCALE_FACTOR); + } + + input_report_abs(input, ABS_X, x2); + input_report_abs(input, ABS_Y, y2); + } + + input_sync(input); + if(g_y_trace_coord) + printk("%s touch position X:%d (was = %d) Y:%d (was = %d)\n",ts->driver_name, x2, y, y2, x); + } + +} + +/* + * cy8ctmg110_touch_pos check touch position from i2c devices + * + */ +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ + unsigned char reg_p[CY8CTMG110_REG_MAX]; + + memset(reg_p,0,CY8CTMG110_REG_MAX); + + /*Reading coordinates*/ + if (cy8ctmg110_read_req(tsc,reg_p,1,CY8CTMG110_FINGERS)==0){ + + /*number of touch*/ + if (reg_p[0]==0){ + if (tsc->pending == true){ + struct input_dev *input = tsc->input; + + input_report_key(input, BTN_TOUCH, 0); + + input_sync(input); + tsc->tc.event_sended = true; +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_send_event ts->pending = true;\n"); +#endif + tsc->pending = false; + } + } + else { + + if (cy8ctmg110_read_req(tsc,reg_p,4,CY8CTMG110_TOUCH_X1)==0){ + int x = 0,y = 0; + y = reg_p[2]<<8 | reg_p[3]; + x = reg_p[0]<<8 | reg_p[1]; + + if (tsc->tc.x1 != x || tsc->tc.y1 != y){ + tsc->tc.event_sended = false; + cy8ctmg110_send_event(tsc,x,y); + } + } + } + } + else{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110 i2c reading error\n"); +#endif + } + + return 0; +} +/* + * cy8ctmg110_read_versionid delevery touch event to the userpace + * function use normal input interface + */ +static void cy8ctmg110_read_versionid(void *tsc) +{ + struct cy8ctmg110 *ts = tsc; + unsigned char reg_p[2]; + + + if (cy8ctmg110_read_req(ts,reg_p,1,CY8CTMG110_VERSIONID)==0){ + printk("%s id 0x%x\n",ts->driver_name,reg_p[0]); + + /*Ugly hack solution if SC + */ + + if(ts->client->addr==CY8CTMG110_I2C_ADDR_EXT) + isSc = true; + + switch (reg_p[0]){ + case 0x01: + ts->version_id = TOUCH_EXT; + break; + case 0x02: + ts->version_id = TOUCH_SC; + break; + case 0x03: + ts->version_id = TOUCH_KOSKI; + break; + default: + ts->version_id = TOUCH_KOSKI; + break; + } + } +} + + +#ifdef POLL_TOUCH_EVENTS +/* + * if interup is'n in use the touch positions can reads by polling + * + */ +static enum hrtimer_restart cy8ctmg110_timer(struct hrtimer *handle) +{ + struct cy8ctmg110 *ts = container_of(handle, struct cy8ctmg110, timer); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_timer\n"); +#endif + + cy8ctmg110_touch_pos(ts); + + if (ts->i2c_fail_counttimer, ktime_set(0, CY8CTMG110_POLL_TIMER_DELAY), + HRTIMER_MODE_REL); + + spin_unlock_irqrestore(&ts->lock, flags); + + return HRTIMER_NORESTART; +} +#endif +/* + * cy8ctmg110_init_controller set init value to touchcontroller + * + */ +static bool cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts) +{ + unsigned char reg_p[3]; + + if(ts->sleepmode==true){ + reg_p[0] = 0x00; reg_p[1] =0xff; reg_p[2] =5; + }else{ + reg_p[0] = 0x10;reg_p[1] =0xff;reg_p[2] =0; + } + + if (cy8ctmg110_write_req(ts,CY8CTMG110_TOUCH_WAKEUP_TIME,3,reg_p)){ + return false; + } + ts->initController = true; + + return true; +} + + + +static void cy8ctmg110_work(struct work_struct *work) +{ + struct cy8ctmg110 *ts = + container_of(to_delayed_work(work), struct cy8ctmg110, work); + + cy8ctmg110_touch_pos(ts); +} + + +/* + * cy8ctmg110_irq_handler irq handling function + * + */ +static irqreturn_t cy8ctmg110_irq_handler(int irq, void *handle) +{ + struct cy8ctmg110 * tsc = (struct cy8ctmg110 *)handle; + +#ifdef CY8CTMG110_DEBUG_INFO + printk("%s cy8ctmg110_irq_handler\n",tsc->driver_name); +#endif + if (tsc->initController == false){ + if (cy8ctmg110_set_sleepmode(tsc) == true) + tsc->initController = true; + } + else + { + schedule_delayed_work(&tsc->work, + msecs_to_jiffies(1)); + } + +#ifdef POLL_TOUCH_EVENTS + /*if interrupt supported in the touch controller + timer polling need to stop*/ + tsc->i2c_fail_count = TOUCH_MAX_I2C_FAILS; +#endif + return IRQ_HANDLED; +} + + +static int cy8ctmg110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cy8ctmg110 *ts; + struct input_dev *input_dev; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->input = input_dev; + ts->pending = false; + ts->sleepmode = false; + + + if(client->addr==CY8CTMG110_I2C_ADDR){ + ts->reset_gpio = CY8CTMG110_RESET_PIN_GPIO; + input_dev->name = CY8CTMG110_DRIVER_NAME" Touchscreen"; + snprintf(ts->driver_name, sizeof(ts->driver_name),"%s", CY8CTMG110_DRIVER_NAME); + } + else if (client->addr==CY8CTMG110_I2C_ADDR_EXT){ + ts->reset_gpio = CY8CTMG110_RESET_PIN_GPIO_EXT; + input_dev->name = CY8CTMG110_DRIVER_NAME_EXT" Touchscreen"; + snprintf(ts->driver_name, sizeof(ts->driver_name),"%s", CY8CTMG110_DRIVER_NAME_EXT); + } + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + INIT_DELAYED_WORK(&ts->work, cy8ctmg110_work); + + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + spin_lock_init(&ts->lock); + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | + BIT_MASK(EV_REL) | BIT_MASK(EV_ABS); + + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_capability(input_dev, EV_KEY, KEY_F); + + + input_set_abs_params(input_dev, ABS_X, CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + input_dev->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X)| BIT_MASK(REL_Y); + + err = gpio_request(ts->reset_gpio, NULL); + + if (err) { + printk("GPIO pin %d failed to request.\n", ts->reset_gpio); + goto err_free_thread; + } + + cy8ctmg110_power(ts,true); + + ts->initController = false; +#ifdef POLL_TOUCH_EVENTS + ts->i2c_fail_count = 0; + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = cy8ctmg110_timer; + + hrtimer_start(&ts->timer, ktime_set(10, 0), + HRTIMER_MODE_REL); +#endif + err = gpio_request(client->irq, "touch_irq_key"); + + if (err < 0) { + printk("%s gpio-keys: failed to request GPIO %d," + " error %d\n",ts->driver_name,client->irq, err); + goto err_free_thread; + } + + err= gpio_direction_input(client->irq); + + if (err < 0) { + pr_err("%s gpio-keys: failed to configure input" + " direction for GPIO %d, error %d\n",ts->driver_name,client->irq, err); + gpio_free(client->irq); + goto err_free_thread; + } + + ts->irq_gpio = gpio_to_irq(client->irq); + + if (ts->irq_gpio < 0) { + err = ts->irq_gpio; + pr_err("cy8ctmg110 gpio-keys: Unable to get irq number" + " for GPIO %d, error %d\n", + ts->irq_gpio, err); + gpio_free(ts->irq_gpio); + goto err_free_thread; + } + + if (client->addr!=CY8CTMG110_I2C_ADDR_EXT){ + err = request_irq(ts->irq_gpio, cy8ctmg110_irq_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, + "touch_reset_key", + ts); + } + + if (err < 0) { + dev_err(&client->dev, "cy8ctmg110 irq %d busy? error %d\n", ts->irq_gpio ,err); + goto err_free_thread; + } + + err = input_register_device(input_dev); + cy8ctmg110_read_versionid(ts); + + if (err) + goto err_free_irq; + + return 0; + + err_free_irq: + printk("%s err_free_irq\n",ts->driver_name); + free_irq(client->irq, ts); + err_free_thread: + printk("%s err_free_thread\n",ts->driver_name); + err_free_mem: + printk("%s err_free_mem\n",ts->driver_name); + input_free_device(input_dev); + kfree(ts); + + return err; +} +/* + * cy8ctmg110_suspend + * + */ +static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} +/* + * cy8ctmg110_resume + * + */ +static int cy8ctmg110_resume(struct i2c_client *client) +{ + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +/* + * cy8ctmg110_remove + * + */ +static int cy8ctmg110_remove(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_remove\n"); +#endif + + cy8ctmg110_power(ts,false); +#ifdef POLL_TOUCH_EVENTS + hrtimer_cancel(&ts->timer); +#endif + + free_irq(client->irq, ts); + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id cy8ctmg110_idtable[] = { + { CY8CTMG110_DRIVER_NAME, 1 }, + { CY8CTMG110_DRIVER_NAME_EXT, 1 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CY8CTMG110_DRIVER_NAME, + .bus = &i2c_bus_type, + }, + .id_table = cy8ctmg110_idtable, + .probe = cy8ctmg110_probe, + .remove = cy8ctmg110_remove, + .suspend = cy8ctmg110_suspend, + .resume = cy8ctmg110_resume, +}; + + +static int __init cy8ctmg110_init(void) +{ + printk("cy8ctmg110_init\n"); + + return i2c_add_driver(&cy8ctmg110_driver); +} + +static void __exit cy8ctmg110_exit(void) +{ +#ifdef CY8CTMG110_DEBUG_INFO + printk("cy8ctmg110_exit\n"); +#endif + i2c_del_driver(&cy8ctmg110_driver); +} + +module_init(cy8ctmg110_init); +module_exit(cy8ctmg110_exit); + + +struct i2c_board_info __initdata koski_i2c_board_info2[] = { + { + I2C_BOARD_INFO(CY8CTMG110_DRIVER_NAME, CY8CTMG110_I2C_ADDR), + .irq = CY8CTMG110_IRQ_PIN_GPIO + }, + { + I2C_BOARD_INFO(CY8CTMG110_DRIVER_NAME_EXT, CY8CTMG110_I2C_ADDR_EXT), + .irq = CY8CTMG110_IRQ_PIN_GPIO_EXT + }, +}; + + +static int __init koski_i2c_init(void) +{ + printk("init koski board\n"); + +#ifdef MOORESTOWN_CDK + /*init koski i2c*/ + i2c_register_board_info(1, koski_i2c_board_info2, + ARRAY_SIZE(koski_i2c_board_info2)); +#else + /*init koski i2c*/ + i2c_register_board_info(0, koski_i2c_board_info2, + ARRAY_SIZE(koski_i2c_board_info2)); +#endif + return 0; +} + +module_init(koski_i2c_init); + +MODULE_AUTHOR("Samuli Konttila "); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); + + +// Aava access from sysfs begin +static ssize_t aava_query_fw_info_func(struct class *class, char *buf) +{ + ssize_t status; + int i = 0; + unsigned char mrst_fw_ver_info[16]; + + printk("!!! aava_query_fw_info_func() ENTER\n"); + + status = mrst_get_firmware_version(mrst_fw_ver_info); + for (i = 0; i < 16; i++){ + printk("%x\n", mrst_fw_ver_info[i]); + buf[i] = mrst_fw_ver_info[i]; + } + + return 16; +} + +static ssize_t aava_enable_touch_traces_func(struct class *class, \ + const char *buf, size_t len) +{ + ssize_t status; + unsigned long value; + + status = strict_strtoul(buf, 0, &value); + printk("!!! aava_enable_touch_traces_func() = %d\n", (int)value); + + g_y_trace_coord = value; + + return len; +} + +static struct class_attribute aava_class_attrs[] = { + __ATTR(aava_query_fw_info, 0444, aava_query_fw_info_func, NULL), + __ATTR(aava_enable_touch_traces, 0200, NULL, aava_enable_touch_traces_func), + __ATTR_NULL, +}; + +static struct class aava_class = { + .name = "aava", + .owner = THIS_MODULE, + + .class_attrs = aava_class_attrs, +}; + +static int __init aava_sysfs_init(void) +{ + int status; + + status = class_register(&aava_class); + if (status < 0) + return status; + + return status; +} +postcore_initcall(aava_sysfs_init); +// Aava access from sysfs end -- 1.6.2.2