diff options
Diffstat (limited to 'drivers/hid/hid-lg4ff.c')
| -rw-r--r-- | drivers/hid/hid-lg4ff.c | 196 | 
1 files changed, 137 insertions, 59 deletions
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index f3390ee6105..d7947c701f3 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,6 +43,11 @@  #define G27_REV_MAJ 0x12  #define G27_REV_MIN 0x38 +#define DFP_X_MIN 0 +#define DFP_X_MAX 16383 +#define DFP_PEDAL_MIN 0 +#define DFP_PEDAL_MAX 255 +  #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)  static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); @@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at  static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);  struct lg4ff_device_entry { +	__u32 product_id;  	__u16 range;  	__u16 min_range;  	__u16 max_range; @@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {  	{G27_REV_MAJ,  G27_REV_MIN,  &native_g27},	/* G27 */  }; +/* Recalculates X axis value accordingly to currently selected range */ +static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range) +{ +	__u16 max_range; +	__s32 new_value; + +	if (range == 900) +		return value; +	else if (range == 200) +		return value; +	else if (range < 200) +		max_range = 200; +	else +		max_range = 900; + +	new_value = 8192 + mult_frac(value - 8192, max_range, range); +	if (new_value < 0) +		return 0; +	else if (new_value > 16383) +		return 16383; +	else +		return new_value; +} + +int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, +			     struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) +{ +	struct lg4ff_device_entry *entry = drv_data->device_props; +	__s32 new_value = 0; + +	if (!entry) { +		hid_err(hid, "Device properties not found"); +		return 0; +	} + +	switch (entry->product_id) { +	case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: +		switch (usage->code) { +		case ABS_X: +			new_value = lg4ff_adjust_dfp_x_axis(value, entry->range); +			input_event(field->hidinput->input, usage->type, usage->code, new_value); +			return 1; +		default: +			return 0; +		} +	default: +		return 0; +	} +} +  static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)  {  	struct hid_device *hid = input_get_drvdata(dev);  	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;  	int x; -#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff +#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)  	switch (effect->type) {  	case FF_CONSTANT:  		x = effect->u.ramp.start_level + 0x80;	/* 0x80 is no force */  		CLAMP(x); -		report->field[0]->value[0] = 0x11;	/* Slot 1 */ -		report->field[0]->value[1] = 0x08; -		report->field[0]->value[2] = x; -		report->field[0]->value[3] = 0x80; -		report->field[0]->value[4] = 0x00; -		report->field[0]->value[5] = 0x00; -		report->field[0]->value[6] = 0x00; +		value[0] = 0x11;	/* Slot 1 */ +		value[1] = 0x08; +		value[2] = x; +		value[3] = 0x80; +		value[4] = 0x00; +		value[5] = 0x00; +		value[6] = 0x00;  		usbhid_submit_report(hid, report, USB_DIR_OUT);  		break; @@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud  	struct hid_device *hid = input_get_drvdata(dev);  	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; -	report->field[0]->value[0] = 0xfe; -	report->field[0]->value[1] = 0x0d; -	report->field[0]->value[2] = magnitude >> 13; -	report->field[0]->value[3] = magnitude >> 13; -	report->field[0]->value[4] = magnitude >> 8; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0xfe; +	value[1] = 0x0d; +	value[2] = magnitude >> 13; +	value[3] = magnitude >> 13; +	value[4] = magnitude >> 8; +	value[5] = 0x00; +	value[6] = 0x00;  	usbhid_submit_report(hid, report, USB_DIR_OUT);  } @@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)  	struct hid_device *hid = input_get_drvdata(dev);  	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;  	magnitude = magnitude * 90 / 65535; -	 -	report->field[0]->value[0] = 0xfe; -	report->field[0]->value[1] = 0x03; -	report->field[0]->value[2] = magnitude >> 14; -	report->field[0]->value[3] = magnitude >> 14; -	report->field[0]->value[4] = magnitude; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0xfe; +	value[1] = 0x03; +	value[2] = magnitude >> 14; +	value[3] = magnitude >> 14; +	value[4] = magnitude; +	value[5] = 0x00; +	value[6] = 0x00;  	usbhid_submit_report(hid, report, USB_DIR_OUT);  } @@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)  {  	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; +  	dbg_hid("G25/G27/DFGT: setting range to %u\n", range); -	report->field[0]->value[0] = 0xf8; -	report->field[0]->value[1] = 0x81; -	report->field[0]->value[2] = range & 0x00ff; -	report->field[0]->value[3] = (range & 0xff00) >> 8; -	report->field[0]->value[4] = 0x00; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0xf8; +	value[1] = 0x81; +	value[2] = range & 0x00ff; +	value[3] = (range & 0xff00) >> 8; +	value[4] = 0x00; +	value[5] = 0x00; +	value[6] = 0x00;  	usbhid_submit_report(hid, report, USB_DIR_OUT);  } @@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)  	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);  	int start_left, start_right, full_range; +	__s32 *value = report->field[0]->value; +  	dbg_hid("Driving Force Pro: setting range to %u\n", range);  	/* Prepare "coarse" limit command */ -	report->field[0]->value[0] = 0xf8; -	report->field[0]->value[1] = 0x00; 	/* Set later */ -	report->field[0]->value[2] = 0x00; -	report->field[0]->value[3] = 0x00; -	report->field[0]->value[4] = 0x00; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0xf8; +	value[1] = 0x00;	/* Set later */ +	value[2] = 0x00; +	value[3] = 0x00; +	value[4] = 0x00; +	value[5] = 0x00; +	value[6] = 0x00;  	if (range > 200) {  		report->field[0]->value[1] = 0x03; @@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)  	usbhid_submit_report(hid, report, USB_DIR_OUT);  	/* Prepare "fine" limit command */ -	report->field[0]->value[0] = 0x81; -	report->field[0]->value[1] = 0x0b; -	report->field[0]->value[2] = 0x00; -	report->field[0]->value[3] = 0x00; -	report->field[0]->value[4] = 0x00; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0x81; +	value[1] = 0x0b; +	value[2] = 0x00; +	value[3] = 0x00; +	value[4] = 0x00; +	value[5] = 0x00; +	value[6] = 0x00;  	if (range == 200 || range == 900) {	/* Do not apply any fine limit */  		usbhid_submit_report(hid, report, USB_DIR_OUT); @@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)  	start_left = (((full_range - range + 1) * 2047) / full_range);  	start_right = 0xfff - start_left; -	report->field[0]->value[2] = start_left >> 4; -	report->field[0]->value[3] = start_right >> 4; -	report->field[0]->value[4] = 0xff; -	report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); -	report->field[0]->value[6] = 0xff; +	value[2] = start_left >> 4; +	value[3] = start_right >> 4; +	value[4] = 0xff; +	value[5] = (start_right & 0xe) << 4 | (start_left & 0xe); +	value[6] = 0xff;  	usbhid_submit_report(hid, report, USB_DIR_OUT);  } @@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)  {  	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; -	report->field[0]->value[0] = 0xf8; -	report->field[0]->value[1] = 0x12; -	report->field[0]->value[2] = leds; -	report->field[0]->value[3] = 0x00; -	report->field[0]->value[4] = 0x00; -	report->field[0]->value[5] = 0x00; -	report->field[0]->value[6] = 0x00; +	value[0] = 0xf8; +	value[1] = 0x12; +	value[2] = leds; +	value[3] = 0x00; +	value[4] = 0x00; +	value[5] = 0x00; +	value[6] = 0x00;  	usbhid_submit_report(hid, report, USB_DIR_OUT);  } @@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,  {  	struct device *dev = led_cdev->dev->parent;  	struct hid_device *hid = container_of(dev, struct hid_device, dev); -	struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); +	struct lg_drv_data *drv_data = hid_get_drvdata(hid);  	struct lg4ff_device_entry *entry;  	int i, state = 0; @@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde  {  	struct device *dev = led_cdev->dev->parent;  	struct hid_device *hid = container_of(dev, struct hid_device, dev); -	struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); +	struct lg_drv_data *drv_data = hid_get_drvdata(hid);  	struct lg4ff_device_entry *entry;  	int i, value = 0; @@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid)  	/* 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 */ +		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; @@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid)  	}  	drv_data->device_props = entry; +	entry->product_id = lg4ff_devices[i].product_id;  	entry->min_range = lg4ff_devices[i].min_range;  	entry->max_range = lg4ff_devices[i].max_range;  	entry->set_range = lg4ff_devices[i].set_range; @@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid)  		return error;  	dbg_hid("sysfs interface created\n"); +	/* Set default axes parameters */ +	switch (lg4ff_devices[i].product_id) { +	case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: +		dbg_hid("Setting axes parameters for Driving Force Pro\n"); +		input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0); +		input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); +		input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0); +		break; +	default: +		break; +	} +  	/* Set the maximum range to start with */  	entry->range = entry->max_range;  	if (entry->set_range != NULL) @@ -594,6 +670,8 @@ out:  	return 0;  } + +  int lg4ff_deinit(struct hid_device *hid)  {  	struct lg4ff_device_entry *entry;  |