diff options
Diffstat (limited to 'drivers/hid/hid-lg4ff.c')
| -rw-r--r-- | drivers/hid/hid-lg4ff.c | 125 | 
1 files changed, 91 insertions, 34 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 0ddae2a00d5..cc2bd202219 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,6 +43,7 @@  #define G25_REV_MIN 0x22  #define G27_REV_MAJ 0x12  #define G27_REV_MIN 0x38 +#define G27_2_REV_MIN 0x39  #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) @@ -51,7 +52,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);  static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);  static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); +static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store);  struct lg4ff_device_entry {  	__u32 product_id; @@ -130,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {  	{DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},	/* Driving Force Pro */  	{G25_REV_MAJ,  G25_REV_MIN,  &native_g25},	/* G25 */  	{G27_REV_MAJ,  G27_REV_MIN,  &native_g27},	/* G27 */ +	{G27_REV_MAJ,  G27_2_REV_MIN,  &native_g27},	/* G27 v2 */  };  /* Recalculates X axis value accordingly to currently selected range */ @@ -196,6 +198,21 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e  	case FF_CONSTANT:  		x = effect->u.ramp.start_level + 0x80;	/* 0x80 is no force */  		CLAMP(x); + +		if (x == 0x80) { +			/* De-activate force in slot-1*/ +			value[0] = 0x13; +			value[1] = 0x00; +			value[2] = 0x00; +			value[3] = 0x00; +			value[4] = 0x00; +			value[5] = 0x00; +			value[6] = 0x00; + +			hid_hw_request(hid, report, HID_REQ_SET_REPORT); +			return 0; +		} +  		value[0] = 0x11;	/* Slot 1 */  		value[1] = 0x08;  		value[2] = x; @@ -218,12 +235,70 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud  	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;  	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);  	__s32 *value = report->field[0]->value; +	__u32 expand_a, expand_b; +	struct lg4ff_device_entry *entry; +	struct lg_drv_data *drv_data; + +	drv_data = hid_get_drvdata(hid); +	if (!drv_data) { +		hid_err(hid, "Private driver data not found!\n"); +		return; +	} + +	entry = drv_data->device_props; +	if (!entry) { +		hid_err(hid, "Device properties not found!\n"); +		return; +	} + +	/* De-activate Auto-Center */ +	if (magnitude == 0) { +		value[0] = 0xf5; +		value[1] = 0x00; +		value[2] = 0x00; +		value[3] = 0x00; +		value[4] = 0x00; +		value[5] = 0x00; +		value[6] = 0x00; + +		hid_hw_request(hid, report, HID_REQ_SET_REPORT); +		return; +	} + +	if (magnitude <= 0xaaaa) { +		expand_a = 0x0c * magnitude; +		expand_b = 0x80 * magnitude; +	} else { +		expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa); +		expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa); +	} + +	/* Adjust for non-MOMO wheels */ +	switch (entry->product_id) { +	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: +	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: +		break; +	default: +		expand_a = expand_a >> 1; +		break; +	}  	value[0] = 0xfe;  	value[1] = 0x0d; -	value[2] = magnitude >> 13; -	value[3] = magnitude >> 13; -	value[4] = magnitude >> 8; +	value[2] = expand_a / 0xaaaa; +	value[3] = expand_a / 0xaaaa; +	value[4] = expand_b / 0xaaaa; +	value[5] = 0x00; +	value[6] = 0x00; + +	hid_hw_request(hid, report, HID_REQ_SET_REPORT); + +	/* Activate Auto-Center */ +	value[0] = 0x14; +	value[1] = 0x00; +	value[2] = 0x00; +	value[3] = 0x00; +	value[4] = 0x00;  	value[5] = 0x00;  	value[6] = 0x00; @@ -484,34 +559,16 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde  int lg4ff_init(struct hid_device *hid)  {  	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); -	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;  	struct input_dev *dev = hidinput->input; -	struct hid_report *report; -	struct hid_field *field;  	struct lg4ff_device_entry *entry;  	struct lg_drv_data *drv_data;  	struct usb_device_descriptor *udesc;  	int error, i, j;  	__u16 bcdDevice, rev_maj, rev_min; -	/* Find the report to use */ -	if (list_empty(report_list)) { -		hid_err(hid, "No output report found\n"); -		return -1; -	} -  	/* Check that the report looks ok */ -	report = list_entry(report_list->next, struct hid_report, list); -	if (!report) { -		hid_err(hid, "NULL output report\n"); +	if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))  		return -1; -	} - -	field = report->field[0]; -	if (!field) { -		hid_err(hid, "NULL field\n"); -		return -1; -	}  	/* Check what wheel has been connected */  	for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { @@ -558,17 +615,6 @@ int lg4ff_init(struct hid_device *hid)  	if (error)  		return error; -	/* Check if autocentering is available and -	 * set the centering force to zero by default */ -	if (test_bit(FF_AUTOCENTER, dev->ffbit)) { -		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */ -			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; -		else -			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; - -		dev->ff->set_autocenter(dev, 0); -	} -  	/* Get private driver data */  	drv_data = hid_get_drvdata(hid);  	if (!drv_data) { @@ -589,6 +635,17 @@ int lg4ff_init(struct hid_device *hid)  	entry->max_range = lg4ff_devices[i].max_range;  	entry->set_range = lg4ff_devices[i].set_range; +	/* Check if autocentering is available and +	 * set the centering force to zero by default */ +	if (test_bit(FF_AUTOCENTER, dev->ffbit)) { +		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */ +			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; +		else +			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; + +		dev->ff->set_autocenter(dev, 0); +	} +  	/* Create sysfs interface */  	error = device_create_file(&hid->dev, &dev_attr_range);  	if (error)  | 
