From: johann d. <jd...@us...> - 2002-07-21 11:14:33
|
Update of /cvsroot/linuxconsole/ruby/linux/drivers/usb/input In directory usw-pr-cvs1:/tmp/cvs-serv13450 Modified Files: Config.help Config.in Makefile hid-ff.c hid-lgff.c Removed Files: hid-lg3dff.c Log Message: Put support for Logitech devices into lgff.c. Removed lg3dff.c. Index: Config.help =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/usb/input/Config.help,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- Config.help 18 Jun 2002 18:51:55 -0000 1.2 +++ Config.help 21 Jul 2002 11:14:30 -0000 1.3 @@ -30,15 +30,19 @@ If unsure, say N. -CONFIG_LOGITECH_RUMBLE - Say Y here if you have a Logitech WingMan Cordless rumble pad and if you +CONFIG_LOGITECH_FF + Say Y here if you have a Logitech device with force-feedback and if you want to enable force feedback. Note: if you say N here, this device will still be supported, but without force feedback. + If unsure, say N. + CONFIG_HID_PID Say Y yes if you have a PID-compliant joystick and wish to enable force feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such device. + + If unsure, say N. CONFIG_USB_HIDDEV Say Y here if you want to support HID devices (from the USB Index: Config.in =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/usb/input/Config.in,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- Config.in 18 Jun 2002 18:51:55 -0000 1.5 +++ Config.in 21 Jul 2002 11:14:30 -0000 1.6 @@ -14,10 +14,9 @@ dep_tristate ' USB HIDBP Mouse (basic) support' CONFIG_USB_MOUSE $CONFIG_USB $CONFIG_INPUT dep_tristate ' Griffin Technology PowerMate support' CONFIG_USB_POWERMATE $CONFIG_USB $CONFIG_INPUT fi -dep_mbool ' Force feedback support' CONFIG_HID_FF $CONFIG_USB_HID -dep_mbool ' Logitech RumblePad support' CONFIG_LOGITECH_RUMBLE $CONFIG_USB_HID $CONFIG_HID_FF -dep_mbool ' Logitech WingMan Force 3D support' CONFIG_LOGITECH_3D $CONFIG_USB_HID $CONFIG_HID_FF -dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF +dep_mbool ' Force feedback support' CONFIG_HID_FF $CONFIG_USB_HID $CONFIG_USB_HIDINPUT +dep_mbool ' Logitech devices support' CONFIG_LOGITECH_FF $CONFIG_USB_HID $CONFIG_HID_FF +dep_mbool ' PID Devices' CONFIG_HID_PID $CONFIG_USB_HID $CONFIG_HID_FF dep_tristate ' Aiptek 6000U/8000U tablet support' CONFIG_USB_AIPTEK $CONFIG_USB $CONFIG_INPUT dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB $CONFIG_INPUT Index: Makefile =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/usb/input/Makefile,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- Makefile 18 Jun 2002 18:51:55 -0000 1.5 +++ Makefile 21 Jul 2002 11:14:30 -0000 1.6 @@ -16,12 +16,8 @@ hid-objs += pid.o endif -ifeq ($(CONFIG_LOGITECH_RUMBLE),y) +ifeq ($(CONFIG_LOGITECH_FF),y) hid-objs += hid-lgff.o -endif - -ifeq ($(CONFIG_LOGITECH_3D),y) - hid-objs += hid-lg3dff.o endif ifeq ($(CONFIG_HID_FF),y) Index: hid-ff.c =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/usb/input/hid-ff.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- hid-ff.c 9 Jun 2002 11:06:38 -0000 1.3 +++ hid-ff.c 21 Jul 2002 11:14:30 -0000 1.4 @@ -44,17 +44,15 @@ * devices, you need to add the USB vendor and product ids here. */ struct hid_ff_initializer { - __u16 idVendor; - __u16 idProduct; + u16 idVendor; + u16 idProduct; int (*init)(struct hid_device*); }; static struct hid_ff_initializer inits[] = { -#ifdef CONFIG_LOGITECH_RUMBLE +#ifdef CONFIG_LOGITECH_FF {0x46d, 0xc211, hid_lgff_init}, -#endif -#ifdef CONFIG_LOGITECH_3D - {0x46d, 0xc283, hid_lg3d_init}, + {0x46d, 0xc283, hid_lgff_init}, #endif #ifdef CONFIG_HID_PID {0x45e, 0x001b, hid_pid_init}, @@ -68,8 +66,8 @@ struct hid_ff_initializer *init; for (init = inits; init->idVendor - && !(init->idVendor == idVendor - && init->idProduct == idProduct); + && !(init->idVendor == idVendor + && init->idProduct == idProduct); init++); return init->idVendor? init : NULL; @@ -83,7 +81,7 @@ hid->dev->descriptor.idProduct); if (!init) { - warn("hid_ff_init could not find initializer"); + dbg("hid_ff_init could not find initializer"); return -ENOSYS; } return init->init(hid); Index: hid-lgff.c =================================================================== RCS file: /cvsroot/linuxconsole/ruby/linux/drivers/usb/input/hid-lgff.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- hid-lgff.c 14 Jul 2002 10:31:42 -0000 1.4 +++ hid-lgff.c 21 Jul 2002 11:14:30 -0000 1.5 @@ -4,6 +4,7 @@ * Force feedback support for hid-compliant for some of the devices from * Logitech, namely: * - WingMan Cordless RumblePad + * - WingMan Force 3D * * Copyright (c) 2002 Johann Deneux */ @@ -36,6 +37,11 @@ #include <linux/circ_buf.h> #include "hid.h" +#include "fixp-arith.h" + + +/* Periodicity of the update */ +#define PERIOD (HZ/10) #define RUN_AT(t) (jiffies + (t)) @@ -48,79 +54,97 @@ #define EFFECT_PLAYING 1 /* Effect is being played */ #define EFFECT_USED 2 +// For lgff_device::flags +#define DEVICE_CLOSING 0 /* The driver is being unitialised */ + /* Check that the current process can access an effect */ #define CHECK_OWNERSHIP(effect) (current->pid == 0 \ || effect.owner == current->pid) -/* **************************************************************************/ -/* Implements the protocol used by the Logitech WingMan Cordless RumblePad */ -/* **************************************************************************/ - #define LGFF_CHECK_OWNERSHIP(i, l) \ (i>=0 && i<LGFF_EFFECTS \ && test_bit(EFFECT_USED, l->effects[i].flags) \ && CHECK_OWNERSHIP(l->effects[i])) -#define LGFF_BUFFER_SIZE 64 #define LGFF_EFFECTS 8 -struct lgff_magnitudes { - unsigned char left; - unsigned char right; +struct device_type { + u16 idVendor; + u16 idProduct; + signed short *ff; }; struct lgff_effect { - int id; - struct hid_ff_logitech* lgff; - pid_t owner; - unsigned char left; /* Magnitude of vibration for left motor */ - unsigned char right; /* Magnitude of vibration for right motor */ - struct ff_replay replay; - unsigned int count; /* Number of times to play */ - struct timer_list timer; + + struct ff_effect effect; + unsigned long flags[1]; + unsigned int count; /* Number of times left to play */ + unsigned long started_at; /* When the effect started to play */ }; -struct hid_ff_logitech { +struct lgff_device { struct hid_device* hid; - struct hid_report report; - __s32 value[8]; + struct hid_report* constant; + struct hid_report* rumble; + struct hid_report* condition; struct lgff_effect effects[LGFF_EFFECTS]; spinlock_t lock; /* device-level lock. Having locks on a per-effect basis could be nice, but isn't really necessary */ + + unsigned long flags[1]; /* Contains various information about the + state of the driver for this device */ + + struct timer_list timer; }; +/* Callbacks */ static void hid_lgff_exit(struct hid_device* hid); static int hid_lgff_event(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); -static void hid_lgff_make_rumble(struct hid_device* hid); - static int hid_lgff_flush(struct input_dev *input, struct file *file); static int hid_lgff_upload_effect(struct input_dev *input, struct ff_effect *effect); static int hid_lgff_erase(struct input_dev *input, int id); -static void hid_lgff_ctrl_playback(struct hid_device* hid, struct lgff_effect*, - int play); + +/* Local functions */ +static void hid_lgff_input_init(struct hid_device* hid); static void hid_lgff_timer(unsigned long timer_data); +static struct hid_report* hid_lgff_duplicate_report(struct hid_report*); +static void hid_lgff_delete_report(struct hid_report*); +static signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +static struct device_type devices[] = { + {0x046d, 0xc211, ff_rumble}, + {0x046d, 0xc283, ff_joystick}, + {0x0000, 0x0000, ff_joystick} +}; int hid_lgff_init(struct hid_device* hid) { - struct hid_ff_logitech *private; + struct lgff_device *private; struct hid_report* report; struct hid_field* field; - int i; /* Find the report to use */ if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) { err("No output report found"); return -1; } - /* Check the report looks ok */ + /* Check that the report looks ok */ report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next; if (!report) { err("NULL output report"); @@ -131,60 +155,138 @@ err("NULL field"); return -1; } - if (!field->value) { - err("No space allocated for values"); - return -1; - } - private = kmalloc(sizeof(struct hid_ff_logitech), GFP_KERNEL); + private = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); if (!private) return -1; - memset(private, 0, sizeof(struct hid_ff_logitech)); + memset(private, 0, sizeof(struct lgff_device)); hid->ff_private = private; - private->report = *(struct hid_report*)(hid->report_enum[HID_OUTPUT_REPORT].report_list.next); - private->hid = hid; + /* Input init */ + hid_lgff_input_init(hid); - spin_lock_init(&private->lock); - for (i=0; i<LGFF_EFFECTS; ++i) { - struct lgff_effect* effect = &private->effects[i]; - struct timer_list* timer = &effect->timer; + private->constant = hid_lgff_duplicate_report(report); + if (!private->constant) { + kfree(private); + return -1; + } + private->constant->field[0]->value[0] = 0x51; + private->constant->field[0]->value[1] = 0x08; + private->constant->field[0]->value[2] = 0x7f; + private->constant->field[0]->value[3] = 0x7f; - init_timer(timer); - effect->id = i; - effect->lgff = private; - timer->data = (unsigned long)effect; - timer->function = hid_lgff_timer; + private->rumble = hid_lgff_duplicate_report(report); + if (!private->rumble) { + hid_lgff_delete_report(private->constant); + kfree(private); + return -1; } + private->rumble->field[0]->value[0] = 0x03; + private->rumble->field[0]->value[1] = 0x42; + + + private->condition = hid_lgff_duplicate_report(report); + if (!private->condition) { + hid_lgff_delete_report(private->rumble); + hid_lgff_delete_report(private->constant); + kfree(private); + return -1; + } + + private->hid = hid; + + spin_lock_init(&private->lock); + init_timer(&private->timer); + private->timer.data = (unsigned long)private; + private->timer.function = hid_lgff_timer; /* Event and exit callbacks */ hid->ff_exit = hid_lgff_exit; hid->ff_event = hid_lgff_event; - /* Input init */ + /* Start the update task */ + private->timer.expires = RUN_AT(PERIOD); + add_timer(&private->timer); /*TODO: only run the timer when at least + one effect is playing */ + + printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <de...@if...>\n"); + + return 0; +} + +static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report) +{ + struct hid_report* ret; + + ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL); + if (!ret) return NULL; + *ret = *report; + + ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL); + if (!ret->field[0]) { + kfree(ret); + return NULL; + } + *ret->field[0] = *report->field[0]; + + ret->field[0]->value = kmalloc(sizeof(s32[8]), GFP_KERNEL); + if (!ret->field[0]->value) { + kfree(ret->field[0]); + kfree(ret); + return NULL; + } + memset(ret->field[0]->value, 0, sizeof(s32[8])); + + return ret; +} + +static void hid_lgff_delete_report(struct hid_report* report) +{ + if (report) { + kfree(report->field[0]->value); + kfree(report->field[0]); + kfree(report); + } +} + +static void hid_lgff_input_init(struct hid_device* hid) +{ + struct device_type* dev = devices; + signed short* ff; + u16 idVendor = hid->dev->descriptor.idVendor; + u16 idProduct = hid->dev->descriptor.idProduct; + + while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct)) + dev++; + + ff = dev->ff; + + while (ff && *ff >= 0) { + set_bit(*ff, hid->input.ffbit); + ++ff; + } + hid->input.upload_effect = hid_lgff_upload_effect; hid->input.flush = hid_lgff_flush; - set_bit(FF_RUMBLE, hid->input.ffbit); + set_bit(EV_FF, hid->input.evbit); hid->input.ff_effects_max = LGFF_EFFECTS; - - printk(KERN_INFO "Force feedback for Logitech rumble devices by Johann Deneux <de...@if...>\n"); - - return 0; } static void hid_lgff_exit(struct hid_device* hid) { - struct hid_ff_logitech *lgff = hid->ff_private; + struct lgff_device *lgff = hid->ff_private; - /* At this point, all effects were erased by hid_lgff_flush. - No need to do anything */ + set_bit(DEVICE_CLOSING, lgff->flags); + del_timer_sync(&lgff->timer); + + kfree(lgff); } static int hid_lgff_event(struct hid_device *hid, struct input_dev* input, unsigned int type, unsigned int code, int value) { - struct hid_ff_logitech *lgff = hid->ff_private; + struct lgff_device *lgff = hid->ff_private; struct lgff_effect *effect = lgff->effects + code; unsigned long flags; @@ -206,27 +308,16 @@ effect->count = value; - if (effect->replay.delay) { + if (effect->effect.replay.delay) { set_bit(EFFECT_STARTED, effect->flags); - effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000); } else { - hid_lgff_ctrl_playback(hid, effect, value); - effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000); + set_bit(EFFECT_PLAYING, effect->flags); } - - add_timer(&effect->timer); + effect->started_at = jiffies; } else { /* value == 0 */ - if (test_and_clear_bit(EFFECT_STARTED, effect->flags)) { - del_timer(&effect->timer); - - } else if (test_and_clear_bit(EFFECT_PLAYING, effect->flags)) { - del_timer(&effect->timer); - hid_lgff_ctrl_playback(hid, effect, value); - } - - if (test_bit(EFFECT_PLAYING, effect->flags)) - warn("Effect %d still playing", code); + clear_bit(EFFECT_STARTED, effect->flags); + clear_bit(EFFECT_PLAYING, effect->flags); } spin_unlock_irqrestore(&lgff->lock, flags); @@ -239,7 +330,7 @@ static int hid_lgff_flush(struct input_dev *dev, struct file *file) { struct hid_device *hid = dev->private; - struct hid_ff_logitech *lgff = hid->ff_private; + struct lgff_device *lgff = hid->ff_private; int i; for (i=0; i<dev->ff_effects_max; ++i) { @@ -263,13 +354,12 @@ static int hid_lgff_erase(struct input_dev *dev, int id) { struct hid_device *hid = dev->private; - struct hid_ff_logitech *lgff = hid->ff_private; + struct lgff_device *lgff = hid->ff_private; unsigned long flags; if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES; spin_lock_irqsave(&lgff->lock, flags); - hid_lgff_ctrl_playback(hid, lgff->effects + id, 0); lgff->effects[id].flags[0] = 0; spin_unlock_irqrestore(&lgff->lock, flags); @@ -280,7 +370,7 @@ struct ff_effect* effect) { struct hid_device *hid = input->private; - struct hid_ff_logitech *lgff = hid->ff_private; + struct lgff_device *lgff = hid->ff_private; struct lgff_effect new; int id; unsigned long flags; @@ -289,8 +379,6 @@ if (!test_bit(effect->type, input->ffbit)) return -EINVAL; - if (effect->type != FF_RUMBLE) return -EINVAL; - spin_lock_irqsave(&lgff->lock, flags); if (effect->id == -1) { @@ -315,25 +403,20 @@ id = effect->id; new = lgff->effects[id]; - new.right = effect->u.rumble.strong_magnitude >> 9; - new.left = effect->u.rumble.weak_magnitude >> 9; - new.replay = effect->replay; + new.effect = *effect; - /* If we updated an effect that was being played, we need to remake - the rumble effect */ if (test_bit(EFFECT_STARTED, lgff->effects[id].flags) || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) { /* Changing replay parameters is not allowed (for the time being) */ - if (new.replay.delay != lgff->effects[id].replay.delay - || new.replay.length != lgff->effects[id].replay.length) { + if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay + || new.effect.replay.length != lgff->effects[id].effect.replay.length) { spin_unlock_irqrestore(&lgff->lock, flags); return -ENOSYS; } lgff->effects[id] = new; - hid_lgff_make_rumble(hid); } else { lgff->effects[id] = new; @@ -343,90 +426,99 @@ return 0; } -static void hid_lgff_make_rumble(struct hid_device* hid) +static void hid_lgff_timer(unsigned long timer_data) { - struct hid_ff_logitech *lgff = hid->ff_private; - int left = 0, right = 0; - int i; + struct lgff_device *lgff = (struct lgff_device*)timer_data; + struct hid_device *hid = lgff->hid; unsigned long flags; + int x = 0x7f, y = 0x7f; // Coordinates of constant effects + unsigned int left = 0, right = 0; // Rumbling + int i; - for (i=0; i<LGFF_EFFECTS; ++i) { - if (test_bit(EFFECT_USED, lgff->effects[i].flags) - && test_bit(EFFECT_PLAYING, lgff->effects[i].flags)) { - left += lgff->effects[i].left; - right += lgff->effects[i].right; - } - } - - lgff->report.field[0]->value[0] = 0x03; - lgff->report.field[0]->value[1] = 0x42; - lgff->report.field[0]->value[3] = left; - lgff->report.field[0]->value[4] = right; - hid_submit_report(hid, &lgff->report, USB_DIR_OUT); -} + spin_lock_irqsave(&lgff->lock, flags); -/* Lock must be held by caller */ -static void hid_lgff_ctrl_playback(struct hid_device *hid, - struct lgff_effect *effect, int play) -{ - if (play) { - set_bit(EFFECT_PLAYING, effect->flags); - hid_lgff_make_rumble(hid); + for (i=0; i<LGFF_EFFECTS; ++i) { + struct lgff_effect* effect = lgff->effects +i; - } else { - clear_bit(EFFECT_PLAYING, effect->flags); - hid_lgff_make_rumble(hid); - } -} + if (test_bit(EFFECT_PLAYING, effect->flags)) { -static void hid_lgff_timer(unsigned long timer_data) -{ - struct lgff_effect *effect = (struct lgff_effect*) timer_data; - struct hid_ff_logitech* lgff = effect->lgff; - int id = effect->id; + switch (effect->effect.type) { + case FF_CONSTANT: { + //TODO: handle envelopes + int degrees = effect->effect.direction * 360 >> 16; + x += fixp_mult(fixp_sin(degrees), + fixp_new16(effect->effect.u.constant.level)); + y += fixp_mult(-fixp_cos(degrees), + fixp_new16(effect->effect.u.constant.level)); + } break; + case FF_RUMBLE: + right += effect->effect.u.rumble.strong_magnitude; + left += effect->effect.u.rumble.weak_magnitude; + break; + }; - unsigned long flags; + /* One run of the effect is finished playing */ + if (time_after(jiffies, + effect->started_at + + effect->effect.replay.delay*HZ/1000 + + effect->effect.replay.length*HZ/1000)) { + dbg("Finished playing once %d", i); + if (--effect->count <= 0) { + dbg("Stopped %d", i); + clear_bit(EFFECT_PLAYING, effect->flags); + } + else { + dbg("Start again %d", i); + if (effect->effect.replay.length != 0) { + clear_bit(EFFECT_PLAYING, effect->flags); + set_bit(EFFECT_STARTED, effect->flags); + } + effect->started_at = jiffies; + } + } - dbg("in hid_lgff_timer"); + } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) { + /* Check if we should start playing the effect */ + if (time_after(jiffies, + lgff->effects[i].started_at + + lgff->effects[i].effect.replay.delay*HZ/1000)) { + dbg("Now playing %d", i); + clear_bit(EFFECT_STARTED, lgff->effects[i].flags); + set_bit(EFFECT_PLAYING, lgff->effects[i].flags); + } + } + } - if (id < 0 || id >= LGFF_EFFECTS) { - warn("Bad effect id %d", id); - return; - } +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff - effect = lgff->effects + id; + // Clamp values + CLAMP(x); + CLAMP(y); + CLAMP(left); + CLAMP(right); - spin_lock_irqsave(&lgff->lock, flags); +#undef CLAMP - if (!test_bit(EFFECT_USED, effect->flags)) { - warn("Unused effect id %d", id); + if (x != lgff->constant->field[0]->value[2] + || y != lgff->constant->field[0]->value[3]) { + lgff->constant->field[0]->value[2] = x; + lgff->constant->field[0]->value[3] = y; + dbg("(x,y)=(%04x, %04x)", x, y); + hid_submit_report(hid, lgff->constant, USB_DIR_OUT); + } - } else if (test_bit(EFFECT_STARTED, effect->flags)) { - clear_bit(EFFECT_STARTED, effect->flags); - set_bit(EFFECT_PLAYING, effect->flags); - hid_lgff_ctrl_playback(lgff->hid, effect, 1); - if (effect->replay.length) { - effect->timer.expires = RUN_AT(effect->replay.length * HZ / 1000); - add_timer(&effect->timer); - } - /* else { play for ever } */ + if (left != lgff->rumble->field[0]->value[3] + || right != lgff->rumble->field[0]->value[4]) { + lgff->rumble->field[0]->value[3] = left; + lgff->rumble->field[0]->value[4] = right; + dbg("(left,right)=(%04x, %04x)", left, right); + hid_submit_report(hid, lgff->rumble, USB_DIR_OUT); + } - dbg("Effect %d starts playing", id); - } else if (test_bit(EFFECT_PLAYING, effect->flags)) { - clear_bit(EFFECT_PLAYING, effect->flags); - hid_lgff_ctrl_playback(lgff->hid, effect, 0); - if (--effect->count > 0) { - /*TODO: check that replay.delay is non-null */ - set_bit(EFFECT_STARTED, effect->flags); - effect->timer.expires = RUN_AT(effect->replay.delay * HZ / 1000); - add_timer(&effect->timer); - dbg("Effect %d restarted", id); - } else { - dbg("Effect %d stopped", id); - } - } else { - warn("Effect %d is not started nor playing", id); + if (!test_bit(DEVICE_CLOSING, lgff->flags)) { + lgff->timer.expires = RUN_AT(PERIOD); + add_timer(&lgff->timer); } - spin_unlock_irqrestore(&lgff->lock, flags); + spin_unlock_irqrestore(&lgff->lock, flags); } --- hid-lg3dff.c DELETED --- |