Re: [PATCH] HID: Add support for pressure sensitive buttons

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 14 Nov 2011, Sean Young wrote:

> This driver needs drvdata for each input device, but this has already
> been used for storing a pointer to the hid device. The drvdata of the
> hid device could be used if there was one for each input device, but
> this is not so (HID_QUIRK_MULTI_INPUT with up to four ports). So I've
> reused the private data of the forced feedback to store the data.

First, thanks a lot for your patch. Please find a few comments below.

> 
> Signed-off-by: Sean Young <sean@xxxxxxxx>
> ---
>  drivers/hid/Kconfig        |    3 +-
>  drivers/hid/hid-sjoy.c     |  221 +++++++++++++++++++++++++++++++++++++++++++-
>  drivers/input/ff-memless.c |    8 ++
>  include/linux/input.h      |    1 +
>  4 files changed, 228 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 4d07288..92b1e9c 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -549,8 +549,7 @@ config HID_SMARTJOYPLUS
>  	Support for SmartJoy PLUS PS2/USB adapter, Super Dual Box,
>  	Super Joy Box 3 Pro, Super Dual Box Pro, and Super Joy Box 5 Pro.
>  
> -	Note that DDR (Dance Dance Revolution) mode is not supported, nor
> -	is pressure sensitive buttons on the pro models.
> +	Note that DDR (Dance Dance Revolution) mode is not supported.
>  
>  config SMARTJOYPLUS_FF
>  	bool "SmartJoy PLUS PS2/USB adapter force feedback support"
> diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
> index 670da91..b458470 100644
> --- a/drivers/hid/hid-sjoy.c
> +++ b/drivers/hid/hid-sjoy.c
> @@ -35,8 +35,25 @@
>  #ifdef CONFIG_SMARTJOYPLUS_FF
>  #include "usbhid/usbhid.h"
>  
> +enum mode {
> +	MODE_AUTO,
> +	MODE_ANALOG,
> +	MODE_DIGITAL,
> +	MODE_PRESSURE
> +};
> +
> +static const char * const mode_names[] = {
> +	"auto", "analog", "digital", "pressure"
> +};
> +
> +static const char * const button_names[] = {
> +	"triangle", "circle", "cross", "square", "l1", "r1", "l2", "r2"
> +};
> +
>  struct sjoyff_device {
>  	struct hid_report *report;
> +	enum mode mode;
> +	int buttons[2];
>  };
>  
>  static int hid_sjoyff_play(struct input_dev *dev, void *data,
> @@ -53,14 +70,170 @@ static int hid_sjoyff_play(struct input_dev *dev, void *data,
>  	left = left * 0xff / 0xffff;
>  	right = (right != 0); /* on/off only */
>  
> +	sjoyff->report->field[0]->value[0] = 1;
>  	sjoyff->report->field[0]->value[1] = right;
>  	sjoyff->report->field[0]->value[2] = left;

General comment to the whole patch applicable on many other places as 
well: it'd be nice if you could stick a short comment to the places which 
contain magic contants mandated by the device protocol, so that anyone 
else looking at the driver gets at least a basic idea why are individial 
fields initialized the way they are.

> -	dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right);
> +	dev_dbg(&dev->dev, "port %u running with 0x%02x 0x%02x\n",
> +					sjoyff->report->id, left, right);
>  	usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
>  
>  	return 0;
>  }
>  
> +static void hid_sjoy_set_buttons(struct device *dev)
> +{
> +	struct input_dev *idev = to_input_dev(dev);
> +	struct sjoyff_device *sjoy = input_ff_get_data(idev);
> +	struct hid_device *hid = input_get_drvdata(idev);
> +	struct hid_report *report = sjoy->report;
> +
> +	report->field[0]->value[0] = 6;
> +	report->field[0]->value[1] = sjoy->buttons[0] | (sjoy->buttons[1] << 4);
> +	report->field[0]->value[2] = 1; /* if this field is 0, then pressure
> +		buttons won't be reported as digital button presses any more */

Please make this comment more compliant with the style we usually follow 
in the kernel code.

> +	report->field[0]->value[3] = 0;
> +
> +	usbhid_submit_report(hid, report, USB_DIR_OUT);
> +}
> +
> +static void hid_sjoy_set_mode(struct device *dev, enum mode mode)
> +{
> +	struct input_dev *idev = to_input_dev(dev);
> +	struct sjoyff_device *sjoy = input_ff_get_data(idev);
> +	struct hid_device *hid = input_get_drvdata(idev);
> +	struct hid_report *report = sjoy->report;
> +
> +	sjoy->mode = mode;
> +
> +	report->field[0]->value[0] = 3;
> +
> +	switch (mode) {
> +	case MODE_AUTO:
> +		report->field[0]->value[1] = 0;
> +		report->field[0]->value[2] = 2;
> +		report->field[0]->value[3] = 0;
> +		break;
> +	case MODE_ANALOG:
> +		report->field[0]->value[1] = 1;
> +		report->field[0]->value[2] = 3;
> +		report->field[0]->value[3] = 0;
> +		break;
> +	case MODE_DIGITAL:
> +		report->field[0]->value[1] = 0;
> +		report->field[0]->value[2] = 3;
> +		report->field[0]->value[3] = 0;
> +		break;
> +	case MODE_PRESSURE:
> +		report->field[0]->value[1] = 1;
> +		report->field[0]->value[2] = 3;
> +		report->field[0]->value[3] = 1;
> +		break;
> +	}
> +
> +	dev_dbg(&hid->dev, "switching port %u mode to %s\n", report->id,
> +							mode_names[mode]);
> +	usbhid_submit_report(hid, report, USB_DIR_OUT);
> +}
> +
> +static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
> +						const char *buf, size_t count)
> +{
> +	int i, rc = -EINVAL;
> +
> +	for (i = 0; i < ARRAY_SIZE(mode_names); i++) {
> +		if (sysfs_streq(mode_names[i], buf)) {
> +			hid_sjoy_set_mode(dev, i);
> +			rc = count;
> +			break;
> +		}
> +	}
> +
> +	return rc;
> +}
> +
> +#define store_button(button)	\
> +static ssize_t store_button_##button(struct device *dev,		\
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{									      \
> +	struct input_dev *idev = to_input_dev(dev);			      \
> +	struct sjoyff_device *sjoy = input_ff_get_data(idev);		      \
> +	struct hid_device *hid = input_get_drvdata(idev);		      \
> +	int i, rc = -EINVAL;						      \
> +									      \
> +	for (i = 0; i < ARRAY_SIZE(button_names); i++) {		      \
> +		if (sysfs_streq(button_names[i], buf)) {		      \
> +			if (sjoy->buttons[!button] == i)		      \
> +				break;					      \
> +									      \
> +			dev_dbg(&hid->dev, "port %u pressure button %d: %s\n",\
> +				sjoy->report->id, button, button_names[i]);   \
> +			sjoy->buttons[button] = i;			      \
> +			hid_sjoy_set_buttons(dev);			      \
> +			rc = count;					      \
> +			break;						      \
> +		}							      \
> +	}								      \
> +									      \
> +	return rc;							      \
> +}									      \
> +									      \
> +static ssize_t show_button_##button(struct device *dev,			      \
> +				struct device_attribute *attr, char *buf)     \
> +{									      \
> +	struct input_dev *idev = to_input_dev(dev);			      \
> +	struct sjoyff_device *sjoy = input_ff_get_data(idev);		      \
> +									      \
> +	return snprintf(buf, PAGE_SIZE, "%s\n",				      \
> +				button_names[sjoy->buttons[button]]);	      \
> +}
> +
> +store_button(0);
> +store_button(1);
> +
> +static ssize_t show_mode(struct device *dev, struct device_attribute *attr,
> +								char *buf)
> +{
> +	struct input_dev *idev = to_input_dev(dev);
> +	struct sjoyff_device *sjoy = input_ff_get_data(idev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%s\n", mode_names[sjoy->mode]);
> +}
> +
> +static ssize_t show_modes(struct device *dev, struct device_attribute *attr,
> +								char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "%s\n", "auto analog digital pressure");
> +}
> +
> +static ssize_t show_buttons(struct device *dev, struct device_attribute *attr,
> +								char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "%s\n",
> +		"triangle circle cross square l1 r1 l2 r2");
> +}
> +
> +static DEVICE_ATTR(pressure_button_0, S_IRUGO | S_IWUSR | S_IWGRP,
> +						show_button_0, store_button_0);
> +static DEVICE_ATTR(pressure_button_1, S_IRUGO | S_IWUSR | S_IWGRP,
> +						show_button_1, store_button_1);
> +static DEVICE_ATTR(available_pressure_buttons, S_IRUGO, show_buttons, NULL);
> +static DEVICE_ATTR(controller_mode, S_IRUGO | S_IWUSR | S_IWGRP, show_mode,
> +							store_mode);
> +static DEVICE_ATTR(available_controller_modes, S_IRUGO, show_modes, NULL);

You are adding sysfs attributes, so you need to document those in 
Documentation/ABI

> +
> +static struct attribute *sysfs_attrs[] = {
> +	&dev_attr_controller_mode.attr,
> +	&dev_attr_available_controller_modes.attr,
> +	&dev_attr_pressure_button_0.attr,
> +	&dev_attr_pressure_button_1.attr,
> +	&dev_attr_available_pressure_buttons.attr,
> +	NULL
> +};
> +
> +static struct attribute_group sjoy_attribute_group = {
> +	.attrs = sysfs_attrs
> +};
> +
>  static int sjoyff_init(struct hid_device *hid)
>  {
>  	struct sjoyff_device *sjoyff;
> @@ -115,17 +288,57 @@ static int sjoyff_init(struct hid_device *hid)
>  		sjoyff->report->field[0]->value[1] = 0x00;
>  		sjoyff->report->field[0]->value[2] = 0x00;
>  		usbhid_submit_report(hid, sjoyff->report, USB_DIR_OUT);
> +
> +		/*
> +		 * Only the pro models support changing mode, and the
> +		 * same devices also have the noget quirk.
> +		 */
> +		if (!(hid->quirks & HID_QUIRK_NOGET))
> +			continue;
> +
> +		if (report->field[0]->report_count < 4) {
> +			hid_err(hid, "not enough values in the field\n");
> +			continue;
> +		}
> +
> +		hid_sjoy_set_mode(&dev->dev, MODE_AUTO);
> +
> +		sjoyff->buttons[0] = 2; /* cross */
> +		sjoyff->buttons[1] = 3; /* square */
> +
> +		hid_sjoy_set_buttons(&dev->dev);
> +
> +		error = sysfs_create_group(&dev->dev.kobj,
> +							&sjoy_attribute_group);
> +		if (error)
> +			hid_warn(hid, "failed to create sysfs attributes: %d\n",
> +									error);
>  	}
>  
>  	hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n");
>  
>  	return 0;
>  }
> +
> +static void sjoy_remove(struct hid_device *hid)
> +{
> +	struct hid_input *hidinput;
> +	struct input_dev *dev;
> +
> +	list_for_each_entry(hidinput, &hid->inputs, list) {
> +		dev = hidinput->input;
> +		sysfs_remove_group(&dev->dev.kobj, &sjoy_attribute_group);
> +	}
> +
> +	hid_hw_stop(hid);
> +}
>  #else
>  static inline int sjoyff_init(struct hid_device *hid)
>  {
>  	return 0;
>  }
> +
> +#define sjoy_remove NULL
>  #endif
>  
>  static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id)
> @@ -154,7 +367,8 @@ err:
>  }
>  
>  static const struct hid_device_id sjoy_devices[] = {
> -	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO),
> +		.driver_data = HID_QUIRK_NOGET },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO),
>  		.driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
>  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
> @@ -163,7 +377,7 @@ static const struct hid_device_id sjoy_devices[] = {
>  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD),
> -		.driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET |
> +		.driver_data = HID_QUIRK_MULTI_INPUT |
>  			       HID_QUIRK_SKIP_OUTPUT_REPORTS },
>  	{ }
>  };
> @@ -173,6 +387,7 @@ static struct hid_driver sjoy_driver = {
>  	.name = "smartjoyplus",
>  	.id_table = sjoy_devices,
>  	.probe = sjoy_probe,
> +	.remove = sjoy_remove
>  };
>  
>  static int __init sjoy_init(void)
> diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
> index 117a59a..42c83a5 100644
> --- a/drivers/input/ff-memless.c
> +++ b/drivers/input/ff-memless.c
> @@ -544,3 +544,11 @@ int input_ff_create_memless(struct input_dev *dev, void *data,
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(input_ff_create_memless);
> +
> +void *input_ff_get_data(struct input_dev *input)
> +{
> +	struct ml_device *ml = input->ff->private;
> +
> +	return ml->private;
> +}
> +EXPORT_SYMBOL_GPL(input_ff_get_data);
> diff --git a/include/linux/input.h b/include/linux/input.h
> index 771d6d8..d7a1ce2 100644
> --- a/include/linux/input.h
> +++ b/include/linux/input.h
> @@ -1617,6 +1617,7 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
>  
>  int input_ff_create_memless(struct input_dev *dev, void *data,
>  		int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
> +void *input_ff_get_data(struct input_dev *dev);
>  
>  #endif
>  #endif
> -- 
> 1.7.7.1
> 

-- 
Jiri Kosina
SUSE Labs
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux