#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/proc_fs.h>
+#include <linux/idr.h>
#include <linux/i2c/vsense.h>
#include <linux/gpio.h>
+#define VSENSE_INTERVAL 25
+
#define VSENSE_MODE_ABS 0
#define VSENSE_MODE_MOUSE 1
#define VSENSE_MODE_SCROLL 2
+#define VSENSE_MODE_MBUTTONS 3
+
+static DEFINE_IDR(vsense_proc_id);
+static DEFINE_MUTEX(vsense_mutex);
/* Reset state is shared between both nubs, so we keep
* track of it here.
static int vsense_reset_state;
struct vsense_drvdata {
+ char dev_name[12];
struct input_dev *input;
struct i2c_client *client;
struct delayed_work work;
int reset_gpio;
int irq_gpio;
int mode;
+ int proc_id;
+ struct proc_dir_entry *proc_root;
+ int mouse_multiplier; /* 24.8 */
+ int scrollx_multiplier;
+ int scrolly_multiplier;
int scroll_counter;
- char dev_name[12];
+ int scroll_steps;
+ int mbutton_threshold;
+ int mbutton_stage;
};
static void vsense_work(struct work_struct *work)
struct vsense_drvdata *ddata;
int ax = 0, ay = 0, rx = 0, ry = 0;
signed char buff[4];
- int ret;
+ int ret, l, r;
ddata = container_of(work, struct vsense_drvdata, work.work);
ax = (signed int)buff[2];
ay = (signed int)buff[3];
- schedule_delayed_work(&ddata->work, msecs_to_jiffies(30));
+ schedule_delayed_work(&ddata->work, msecs_to_jiffies(VSENSE_INTERVAL));
dosync:
switch (ddata->mode) {
case VSENSE_MODE_MOUSE:
+ rx = rx * ddata->mouse_multiplier / 256;
+ ry = -ry * ddata->mouse_multiplier / 256;
input_report_rel(ddata->input, REL_X, rx);
- input_report_rel(ddata->input, REL_Y, -ry);
+ input_report_rel(ddata->input, REL_Y, ry);
break;
case VSENSE_MODE_SCROLL:
- if (ddata->scroll_counter++ % 16 != 0)
+ if (++(ddata->scroll_counter) < ddata->scroll_steps)
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;
+ ddata->scroll_counter = 0;
+ ax = ax * ddata->scrollx_multiplier / 256;
+ ay = ay * ddata->scrolly_multiplier / 256;
input_report_rel(ddata->input, REL_HWHEEL, ax);
- input_report_rel(ddata->input, REL_WHEEL, -ay);
+ input_report_rel(ddata->input, REL_WHEEL, ay);
+ break;
+ case VSENSE_MODE_MBUTTONS:
+ l = r = 0;
+ if (ax <= -ddata->mbutton_threshold)
+ l = 1;
+ else if (ax >= ddata->mbutton_threshold)
+ r = 1;
+ if (ay >= ddata->mbutton_threshold) {
+ ddata->mbutton_stage++;
+ l = r = 0;
+ switch (ddata->mbutton_stage) {
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ l = 1;
+ break;
+ }
+ } else
+ ddata->mbutton_stage = 0;
+ input_report_key(ddata->input, BTN_LEFT, l);
+ input_report_key(ddata->input, BTN_RIGHT, r);
break;
default:
input_report_abs(ddata->input, ABS_X, ax * 8);
return ret;
}
- ddata->mode = mode;
return 0;
}
input_unregister_device(ddata->input);
}
-static int vsense_proc_read(char *page, char **start, off_t off, int count,
+static int vsense_proc_mode_read(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
struct vsense_drvdata *ddata = data;
case VSENSE_MODE_SCROLL:
len = sprintf(p, "scroll\n");
break;
+ case VSENSE_MODE_MBUTTONS:
+ len = sprintf(p, "mbuttons\n");
+ break;
default:
len = sprintf(p, "absolute\n");
break;
return len + 1;
}
-static int vsense_proc_write(struct file *file, const char __user *buffer,
+static int vsense_proc_mode_write(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
struct vsense_drvdata *ddata = data;
mode = VSENSE_MODE_MOUSE;
else if (strcasecmp(buff, "scroll") == 0)
mode = VSENSE_MODE_SCROLL;
+ else if (strcasecmp(buff, "mbuttons") == 0)
+ mode = VSENSE_MODE_MBUTTONS;
else if (strcasecmp(buff, "absolute") == 0)
mode = VSENSE_MODE_ABS;
- else
+ else {
dev_err(&ddata->client->dev, "unknown mode: %s\n", buff);
+ return -EINVAL;
+ }
- if (mode != ddata->mode) {
+ if ((mode == VSENSE_MODE_ABS && ddata->mode != VSENSE_MODE_ABS) ||
+ (mode != VSENSE_MODE_ABS && ddata->mode == VSENSE_MODE_ABS)) {
disable_irq(ddata->client->irq);
vsense_input_unregister(ddata);
ret = vsense_input_register(ddata, mode);
else
enable_irq(ddata->client->irq);
}
+ ddata->mode = mode;
+
+ return count;
+}
+
+static int vsense_proc_int_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int *val = data;
+ int len;
+
+ len = sprintf(page, "%d\n", *val);
+ *eof = 1;
+ return len + 1;
+}
+
+static int vsense_proc_int_write(const char __user *buffer,
+ unsigned long count, int *value)
+{
+ char buff[32];
+ long val;
+ int ret;
+
+ count = strncpy_from_user(buff, buffer,
+ count < sizeof(buff) ? count : sizeof(buff) - 1);
+ buff[count] = 0;
+ ret = strict_strtol(buff, 0, &val);
+ if (ret < 0)
+ return ret;
+ *value = val;
return count;
}
+static int vsense_proc_mult_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int *multiplier = data;
+ int val = *multiplier * 100 / 256;
+ return vsense_proc_int_read(page, start, off, count, eof, &val);
+}
+
+static int vsense_proc_mult_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ int *multiplier = data;
+ int ret, val;
+
+ ret = vsense_proc_int_write(buffer, count, &val);
+ if (ret < 0)
+ return ret;
+ if (val == 0)
+ return -EINVAL;
+
+ *multiplier = val * 256 / 100;
+ return ret;
+}
+
+static int vsense_proc_rate_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int *steps = data;
+ int val = 1000 / VSENSE_INTERVAL / *steps;
+ return vsense_proc_int_read(page, start, off, count, eof, &val);
+}
+
+static int vsense_proc_rate_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ int *steps = data;
+ int ret, val;
+
+ ret = vsense_proc_int_write(buffer, count, &val);
+ if (ret < 0)
+ return ret;
+ if (val < 1)
+ return -EINVAL;
+
+ *steps = 1000 / VSENSE_INTERVAL / val;
+ if (*steps < 1)
+ *steps = 1;
+ return ret;
+}
+
+static int vsense_proc_treshold_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ int *value = data;
+ int ret, val;
+
+ ret = vsense_proc_int_write(buffer, count, &val);
+ if (ret < 0)
+ return ret;
+ if (val < 1 || val > 32)
+ return -EINVAL;
+
+ *value = val;
+ return ret;
+}
+
static ssize_t
vsense_show_reset(struct device *dev, struct device_attribute *attr, char *buf)
{
static DEVICE_ATTR(reset, S_IRUGO | S_IWUSR,
vsense_show_reset, vsense_set_reset);
+static void vsense_create_proc(struct vsense_drvdata *ddata,
+ void *pdata, const char *name,
+ read_proc_t *read_proc, write_proc_t *write_proc)
+{
+ struct proc_dir_entry *pret;
+
+ pret = create_proc_entry(name, S_IWUGO | S_IRUGO, ddata->proc_root);
+ if (pret == NULL) {
+ dev_err(&ddata->client->dev, "failed to create proc file %s\n", name);
+ return;
+ }
+
+ pret->data = pdata;
+ pret->read_proc = read_proc;
+ pret->write_proc = write_proc;
+}
+
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 proc_dir_entry *pret;
char buff[32];
int ret;
if (ddata == NULL)
return -ENOMEM;
- snprintf(ddata->dev_name, sizeof(ddata->dev_name),
- "vsense%02x", client->addr);
+ ret = idr_pre_get(&vsense_proc_id, GFP_KERNEL);
+ if (ret == 0) {
+ ret = -ENOMEM;
+ goto fail0;
+ }
+
+ mutex_lock(&vsense_mutex);
+ ret = idr_get_new(&vsense_proc_id, client, &ddata->proc_id);
+ mutex_unlock(&vsense_mutex);
+ if (ret < 0)
+ goto fail0;
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 fail0;
+ goto fail1;
}
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 fail1;
+ goto fail2;
}
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 fail1;
+ goto fail2;
}
client->irq = ret;
+ snprintf(ddata->dev_name, sizeof(ddata->dev_name),
+ "nub%d", ddata->proc_id);
+
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;
+ ddata->mouse_multiplier = 170 * 256 / 100;
+ ddata->scrollx_multiplier =
+ ddata->scrolly_multiplier = 8 * 256 / 100;
+ ddata->scroll_steps = 1000 / VSENSE_INTERVAL / 3;
+ ddata->mbutton_threshold = 20;
i2c_set_clientdata(client, ddata);
vsense_reset_state = 1;
if (ret) {
dev_err(&client->dev, "failed to register input device, "
"error %d\n", ret);
- goto fail1;
+ goto fail2;
}
ret = request_irq(client->irq, vsense_isr,
if (ret) {
dev_err(&client->dev, "unable to claim irq %d, error %d\n",
client->irq, ret);
- goto fail1;
+ goto fail3;
}
dev_dbg(&client->dev, "probe %02x, gpio %i, irq %i, \"%s\"\n",
client->addr, pdata->gpio_irq, client->irq, client->name);
- snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
- pret = create_proc_entry(buff, S_IWUGO | S_IRUGO, NULL);
- if (pret == NULL) {
+ snprintf(buff, sizeof(buff), "pandora/nub%d", ddata->proc_id);
+ ddata->proc_root = proc_mkdir(buff, NULL);
+ if (ddata->proc_root == 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->proc_root = proc_mkdir(buff, NULL);
}
- ret = device_create_file(&client->dev, &dev_attr_reset);
+ if (ddata->proc_root != NULL) {
+ vsense_create_proc(ddata, ddata, "mode",
+ vsense_proc_mode_read, vsense_proc_mode_write);
+ vsense_create_proc(ddata, &ddata->mouse_multiplier, "mouse_sensitivity",
+ vsense_proc_mult_read, vsense_proc_mult_write);
+ vsense_create_proc(ddata, &ddata->scrollx_multiplier, "scrollx_sensitivity",
+ vsense_proc_mult_read, vsense_proc_mult_write);
+ vsense_create_proc(ddata, &ddata->scrolly_multiplier, "scrolly_sensitivity",
+ vsense_proc_mult_read, vsense_proc_mult_write);
+ vsense_create_proc(ddata, &ddata->scroll_steps, "scroll_rate",
+ vsense_proc_rate_read, vsense_proc_rate_write);
+ vsense_create_proc(ddata, &ddata->mbutton_threshold, "mbutton_threshold",
+ vsense_proc_int_read, vsense_proc_treshold_write);
+ } else
+ dev_err(&client->dev, "can't create proc dir");
- pret->data = ddata;
- pret->read_proc = vsense_proc_read;
- pret->write_proc = vsense_proc_write;
+ ret = device_create_file(&client->dev, &dev_attr_reset);
return 0;
-fail1:
+fail3:
+ vsense_input_unregister(ddata);
+fail2:
gpio_free(pdata->gpio_irq);
+fail1:
+ idr_remove(&vsense_proc_id, ddata->proc_id);
fail0:
kfree(ddata);
return ret;
vsense_reset(ddata, 1);
device_remove_file(&client->dev, &dev_attr_reset);
- snprintf(buff, sizeof(buff), "pandora/vsense%02x", client->addr);
+
+ remove_proc_entry("mode", ddata->proc_root);
+ remove_proc_entry("mouse_sensitivity", ddata->proc_root);
+ remove_proc_entry("scrollx_sensitivity", ddata->proc_root);
+ remove_proc_entry("scrolly_sensitivity", ddata->proc_root);
+ remove_proc_entry("scroll_rate", ddata->proc_root);
+ remove_proc_entry("mbutton_threshold", ddata->proc_root);
+ snprintf(buff, sizeof(buff), "pandora/nub%d", ddata->proc_id);
remove_proc_entry(buff, NULL);
+ idr_remove(&vsense_proc_id, ddata->proc_id);
+
free_irq(client->irq, ddata);
vsense_input_unregister(ddata);
gpio_free(ddata->irq_gpio);