From 98000fc8f72fd9d35ae2fea0528e756febbe0916 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Wed, 4 Feb 2009 22:57:58 +0200 Subject: [PATCH] vsense: add new modes of operation This allows the device to act as mouse or scrolling device. Mode can be switched by writing to special file in /proc/pandora ("absolute", "mouse" and "scroll"). --- drivers/input/misc/vsense.c | 250 ++++++++++++++++++++++++++++-------- 1 file changed, 196 insertions(+), 54 deletions(-) diff --git a/drivers/input/misc/vsense.c b/drivers/input/misc/vsense.c index 4cbc5482d36c..db32b5279c80 100644 --- a/drivers/input/misc/vsense.c +++ b/drivers/input/misc/vsense.c @@ -15,9 +15,16 @@ #include #include #include +#include +#include +#include #include #include +#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 */ @@ -29,14 +36,17 @@ struct vsense_drvdata { 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); @@ -49,14 +59,38 @@ static void vsense_work(struct work_struct *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); } @@ -114,12 +148,130 @@ static void vsense_close(struct input_dev *dev) 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) { @@ -132,106 +284,96 @@ static int vsense_probe(struct i2c_client *client, 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); -- 2.39.2