#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
#include <linux/i2c/vsense.h>
#include <linux/gpio.h>
+#define VSENSE_MODE_ABS 0
+#define VSENSE_MODE_MOUSE 1
+#define VSENSE_MODE_SCROLL 2
+
/* hack for Pandora: keep track of usage to prevent reset
* while other nub is in use
*/
struct delayed_work work;
int reset_gpio;
int irq_gpio;
+ int mode;
+ int scroll_counter;
char dev_name[12];
};
static void vsense_work(struct work_struct *work)
{
struct vsense_drvdata *ddata;
+ int ax = 0, ay = 0, rx = 0, ry = 0;
signed char buff[8];
- int ret, x = 0, y = 0;
+ int ret;
ddata = container_of(work, struct vsense_drvdata, work.work);
goto dosync;
}
- x = (signed int)buff[2] * 8;
- y = (signed int)buff[3] * 8;
+ rx = (signed int)buff[0];
+ ry = (signed int)buff[1];
+ ax = (signed int)buff[2];
+ ay = (signed int)buff[3];
schedule_delayed_work(&ddata->work, msecs_to_jiffies(25));
dosync:
- input_report_abs(ddata->input, ABS_X, x);
- input_report_abs(ddata->input, ABS_Y, y);
+ switch (ddata->mode) {
+ case VSENSE_MODE_MOUSE:
+ input_report_rel(ddata->input, REL_X, rx);
+ input_report_rel(ddata->input, REL_Y, -ry);
+ break;
+ case VSENSE_MODE_SCROLL:
+ if (ddata->scroll_counter++ % 16 != 0)
+ return;
+ if (ax < 0)
+ ax = ax < -8 ? ax / 8 : -1;
+ else if (ax > 0)
+ ax = ax > 8 ? ax / 8 : 1;
+ if (ay < 0)
+ ay = ay < -8 ? ay / 8 : -1;
+ else if (ay > 0)
+ ay = ay > 8 ? ay / 8 : 1;
+ input_report_rel(ddata->input, REL_HWHEEL, ax);
+ input_report_rel(ddata->input, REL_WHEEL, -ay);
+ break;
+ default:
+ input_report_abs(ddata->input, ABS_X, ax * 8);
+ input_report_abs(ddata->input, ABS_Y, ay * 8);
+ break;
+ }
input_sync(ddata->input);
}
BUG_ON(reference_count < 0);
}
+static int vsense_input_register(struct vsense_drvdata *ddata, int mode)
+{
+ struct input_dev *input;
+ int ret;
+
+ input = input_allocate_device();
+ if (input == NULL)
+ return -ENOMEM;
+
+ if (mode != VSENSE_MODE_ABS) {
+ /* pretend to be a mouse */
+ input_set_capability(input, EV_REL, REL_X);
+ input_set_capability(input, EV_REL, REL_Y);
+ input_set_capability(input, EV_REL, REL_WHEEL);
+ input_set_capability(input, EV_REL, REL_HWHEEL);
+ /* add fake buttons to fool X that this is a mouse */
+ input_set_capability(input, EV_KEY, BTN_LEFT);
+ input_set_capability(input, EV_KEY, BTN_RIGHT);
+ } else {
+ input->evbit[BIT_WORD(EV_ABS)] = BIT_MASK(EV_ABS);
+ input_set_abs_params(input, ABS_X, -256, 256, 0, 0);
+ input_set_abs_params(input, ABS_Y, -256, 256, 0, 0);
+ }
+
+ input->name = ddata->dev_name;
+ input->dev.parent = &ddata->client->dev;
+
+ input->id.bustype = BUS_I2C;
+ input->id.version = 0x0091;
+
+ input->open = vsense_open;
+ input->close = vsense_close;
+
+ ddata->input = input;
+ input_set_drvdata(input, ddata);
+
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&ddata->client->dev, "failed to register input device,"
+ " error %d\n", ret);
+ input_free_device(input);
+ return ret;
+ }
+
+ ddata->mode = mode;
+ return 0;
+}
+
+static void vsense_input_unregister(struct vsense_drvdata *ddata)
+{
+ cancel_delayed_work_sync(&ddata->work);
+ input_unregister_device(ddata->input);
+}
+
+static int vsense_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ struct vsense_drvdata *ddata = data;
+ char *p = page;
+ int len;
+
+ switch (ddata->mode) {
+ case VSENSE_MODE_MOUSE:
+ len = sprintf(p, "mouse\n");
+ break;
+ case VSENSE_MODE_SCROLL:
+ len = sprintf(p, "scroll\n");
+ break;
+ default:
+ len = sprintf(p, "absolute\n");
+ break;
+ }
+
+ *eof = 1;
+ return len + 1;
+}
+
+static int vsense_proc_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ struct vsense_drvdata *ddata = data;
+ int mode = ddata->mode;
+ char buff[32], *p;
+ int ret;
+
+ count = strncpy_from_user(buff, buffer,
+ count < sizeof(buff) ? count : sizeof(buff) - 1);
+ buff[count] = 0;
+
+ p = buff + strlen(buff) - 1;
+ while (p > buff && isspace(*p))
+ p--;
+ p[1] = 0;
+
+ if (strcasecmp(buff, "mouse") == 0)
+ mode = VSENSE_MODE_MOUSE;
+ else if (strcasecmp(buff, "scroll") == 0)
+ mode = VSENSE_MODE_SCROLL;
+ else if (strcasecmp(buff, "absolute") == 0)
+ mode = VSENSE_MODE_ABS;
+ else
+ dev_err(&ddata->client->dev, "unknown mode: %s\n", buff);
+
+ if (mode != ddata->mode) {
+ disable_irq(ddata->client->irq);
+ vsense_input_unregister(ddata);
+ ret = vsense_input_register(ddata, mode);
+ if (ret)
+ dev_err(&ddata->client->dev, "failed to re-register "
+ "input as %d, code %d\n", mode, ret);
+ else
+ enable_irq(ddata->client->irq);
+ }
+
+ return count;
+}
+
static int vsense_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vsense_platform_data *pdata = client->dev.platform_data;
struct vsense_drvdata *ddata;
- struct input_dev *input;
+ struct proc_dir_entry *pret;
+ char buff[32];
int ret;
if (pdata == NULL) {
return -EIO;
}
- input = input_allocate_device();
- if (input == NULL)
- return -ENOMEM;
-
ddata = kzalloc(sizeof(struct vsense_drvdata), GFP_KERNEL);
- if (ddata == NULL) {
- ret = -ENOMEM;
- goto fail1;
- }
+ if (ddata == NULL)
+ return -ENOMEM;
snprintf(ddata->dev_name, sizeof(ddata->dev_name),
"vsense%02x", client->addr);
- input->evbit[0] = BIT_MASK(EV_ABS);
- input_set_abs_params(input, ABS_X, -256, 256, 0, 0);
- input_set_abs_params(input, ABS_Y, -256, 256, 0, 0);
-
- input->name = ddata->dev_name;
- input->dev.parent = &client->dev;
-
- input->id.bustype = BUS_I2C;
- input->id.version = 0x0091;
-
- input->open = vsense_open;
- input->close = vsense_close;
-
ret = gpio_request(pdata->gpio_irq, client->name);
if (ret < 0) {
dev_err(&client->dev, "failed to request GPIO %d,"
" error %d\n", pdata->gpio_irq, ret);
- goto fail2;
+ goto fail0;
}
ret = gpio_direction_input(pdata->gpio_irq);
if (ret < 0) {
dev_err(&client->dev, "failed to configure input direction "
"for GPIO %d, error %d\n", pdata->gpio_irq, ret);
- goto fail3;
+ goto fail1;
}
ret = gpio_to_irq(pdata->gpio_irq);
if (ret < 0) {
dev_err(&client->dev, "unable to get irq number for GPIO %d, "
"error %d\n", pdata->gpio_irq, ret);
- goto fail3;
+ goto fail1;
}
client->irq = ret;
+ INIT_DELAYED_WORK(&ddata->work, vsense_work);
+ ddata->mode = VSENSE_MODE_ABS;
+ ddata->client = client;
+ ddata->reset_gpio = pdata->gpio_reset;
+ ddata->irq_gpio = pdata->gpio_irq;
+ i2c_set_clientdata(client, ddata);
+
+ ret = vsense_input_register(ddata, ddata->mode);
+ if (ret) {
+ dev_err(&client->dev, "failed to register input device, "
+ "error %d\n", ret);
+ goto fail1;
+ }
+
ret = request_irq(client->irq, vsense_isr,
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
client->name, ddata);
if (ret) {
dev_err(&client->dev, "unable to claim irq %d, error %d\n",
client->irq, ret);
- goto fail3;
+ goto fail1;
}
- INIT_DELAYED_WORK(&ddata->work, vsense_work);
+ dev_dbg(&client->dev, "probe %02x, gpio %i, irq %i, \"%s\"\n",
+ client->addr, pdata->gpio_irq, client->irq, client->name);
- ret = input_register_device(input);
- if (ret) {
- dev_err(&client->dev, "failed to register input device, "
- "error %d\n", ret);
- goto fail4;
+ snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
+ pret = create_proc_entry(buff, S_IWUGO | S_IRUGO, NULL);
+ if (pret == NULL) {
+ proc_mkdir("pandora", NULL);
+ pret = create_proc_entry(buff, S_IWUSR | S_IRUGO, NULL);
+ if (pret == NULL)
+ dev_err(&client->dev, "can't create proc file");
}
- ddata->input = input;
- ddata->client = client;
- ddata->reset_gpio = pdata->gpio_reset;
- ddata->irq_gpio = pdata->gpio_irq;
- i2c_set_clientdata(client, ddata);
- input_set_drvdata(input, ddata);
-
- dev_dbg(&client->dev, "probe %02x, gpio %i, irq %i, \"%s\"\n",
- client->addr, pdata->gpio_irq, client->irq, client->name);
+ pret->data = ddata;
+ pret->read_proc = vsense_proc_read;
+ pret->write_proc = vsense_proc_write;
return 0;
-fail4:
- free_irq(client->irq, ddata);
-fail3:
+fail1:
gpio_free(pdata->gpio_irq);
-fail2:
+fail0:
kfree(ddata);
-fail1:
- input_free_device(input);
return ret;
}
static int __devexit vsense_remove(struct i2c_client *client)
{
struct vsense_drvdata *ddata;
+ char buff[32];
dev_dbg(&client->dev, "remove\n");
ddata = i2c_get_clientdata(client);
+ snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
+ remove_proc_entry(buff, NULL);
free_irq(client->irq, ddata);
- cancel_delayed_work_sync(&ddata->work);
- input_unregister_device(ddata->input);
+ vsense_input_unregister(ddata);
gpio_free(ddata->irq_gpio);
kfree(ddata);