Blame view

drivers/input/ff-core.c 8.92 KB
509ca1a93   Anssi Hannula   Input: implement ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   *  Force feedback support for Linux input subsystem
   *
   *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
   *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
  /* #define DEBUG */
da0c49011   Joe Perches   Input: use pr_fmt...
25
  #define pr_fmt(fmt) KBUILD_BASENAME ": " fmt
509ca1a93   Anssi Hannula   Input: implement ...
26
27
28
29
  
  #include <linux/input.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
83680cdbc   Geert Uytterhoeven   drivers/input/ff-...
30
  #include <linux/sched.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
31
  #include <linux/slab.h>
509ca1a93   Anssi Hannula   Input: implement ...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  
  /*
   * Check that the effect_id is a valid effect and whether the user
   * is the owner
   */
  static int check_effect_access(struct ff_device *ff, int effect_id,
  				struct file *file)
  {
  	if (effect_id < 0 || effect_id >= ff->max_effects ||
  	    !ff->effect_owners[effect_id])
  		return -EINVAL;
  
  	if (file && ff->effect_owners[effect_id] != file)
  		return -EACCES;
  
  	return 0;
  }
  
  /*
   * Checks whether 2 effects can be combined together
   */
  static inline int check_effects_compatible(struct ff_effect *e1,
  					   struct ff_effect *e2)
  {
  	return e1->type == e2->type &&
  	       (e1->type != FF_PERIODIC ||
  		e1->u.periodic.waveform == e2->u.periodic.waveform);
  }
  
  /*
   * Convert an effect into compatible one
   */
  static int compat_effect(struct ff_device *ff, struct ff_effect *effect)
  {
  	int magnitude;
  
  	switch (effect->type) {
  	case FF_RUMBLE:
  		if (!test_bit(FF_PERIODIC, ff->ffbit))
  			return -EINVAL;
  
  		/*
  		 * calculate manginude of sine wave as average of rumble's
  		 * 2/3 of strong magnitude and 1/3 of weak magnitude
  		 */
  		magnitude = effect->u.rumble.strong_magnitude / 3 +
  			    effect->u.rumble.weak_magnitude / 6;
  
  		effect->type = FF_PERIODIC;
  		effect->u.periodic.waveform = FF_SINE;
  		effect->u.periodic.period = 50;
  		effect->u.periodic.magnitude = max(magnitude, 0x7fff);
  		effect->u.periodic.offset = 0;
  		effect->u.periodic.phase = 0;
  		effect->u.periodic.envelope.attack_length = 0;
  		effect->u.periodic.envelope.attack_level = 0;
  		effect->u.periodic.envelope.fade_length = 0;
  		effect->u.periodic.envelope.fade_level = 0;
  
  		return 0;
  
  	default:
  		/* Let driver handle conversion */
  		return 0;
  	}
  }
  
  /**
   * input_ff_upload() - upload effect into force-feedback device
   * @dev: input device
   * @effect: effect to be uploaded
   * @file: owner of the effect
   */
  int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
  		    struct file *file)
  {
  	struct ff_device *ff = dev->ff;
  	struct ff_effect *old;
  	int ret = 0;
  	int id;
  
  	if (!test_bit(EV_FF, dev->evbit))
  		return -ENOSYS;
  
  	if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
  	    !test_bit(effect->type, dev->ffbit)) {
da0c49011   Joe Perches   Input: use pr_fmt...
118
119
  		pr_debug("invalid or not supported effect type in upload
  ");
509ca1a93   Anssi Hannula   Input: implement ...
120
121
122
123
124
125
126
  		return -EINVAL;
  	}
  
  	if (effect->type == FF_PERIODIC &&
  	    (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
  	     effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
  	     !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
da0c49011   Joe Perches   Input: use pr_fmt...
127
128
  		pr_debug("invalid or not supported wave form in upload
  ");
509ca1a93   Anssi Hannula   Input: implement ...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  		return -EINVAL;
  	}
  
  	if (!test_bit(effect->type, ff->ffbit)) {
  		ret = compat_effect(ff, effect);
  		if (ret)
  			return ret;
  	}
  
  	mutex_lock(&ff->mutex);
  
  	if (effect->id == -1) {
  		for (id = 0; id < ff->max_effects; id++)
  		     if (!ff->effect_owners[id])
  			break;
  
  		if (id >= ff->max_effects) {
  			ret = -ENOSPC;
  			goto out;
  		}
  
  		effect->id = id;
  		old = NULL;
  
  	} else {
  		id = effect->id;
  
  		ret = check_effect_access(ff, id, file);
  		if (ret)
  			goto out;
  
  		old = &ff->effects[id];
  
  		if (!check_effects_compatible(effect, old)) {
  			ret = -EINVAL;
  			goto out;
  		}
  	}
  
  	ret = ff->upload(dev, effect, old);
  	if (ret)
  		goto out;
656acd2bb   Dmitry Torokhov   Input: fix lockin...
171
  	spin_lock_irq(&dev->event_lock);
509ca1a93   Anssi Hannula   Input: implement ...
172
173
  	ff->effects[id] = *effect;
  	ff->effect_owners[id] = file;
656acd2bb   Dmitry Torokhov   Input: fix lockin...
174
  	spin_unlock_irq(&dev->event_lock);
509ca1a93   Anssi Hannula   Input: implement ...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  
   out:
  	mutex_unlock(&ff->mutex);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(input_ff_upload);
  
  /*
   * Erases the effect if the requester is also the effect owner. The mutex
   * should already be locked before calling this function.
   */
  static int erase_effect(struct input_dev *dev, int effect_id,
  			struct file *file)
  {
  	struct ff_device *ff = dev->ff;
  	int error;
  
  	error = check_effect_access(ff, effect_id, file);
  	if (error)
  		return error;
656acd2bb   Dmitry Torokhov   Input: fix lockin...
195
  	spin_lock_irq(&dev->event_lock);
509ca1a93   Anssi Hannula   Input: implement ...
196
  	ff->playback(dev, effect_id, 0);
656acd2bb   Dmitry Torokhov   Input: fix lockin...
197
198
  	ff->effect_owners[effect_id] = NULL;
  	spin_unlock_irq(&dev->event_lock);
509ca1a93   Anssi Hannula   Input: implement ...
199
200
201
  
  	if (ff->erase) {
  		error = ff->erase(dev, effect_id);
656acd2bb   Dmitry Torokhov   Input: fix lockin...
202
203
204
205
  		if (error) {
  			spin_lock_irq(&dev->event_lock);
  			ff->effect_owners[effect_id] = file;
  			spin_unlock_irq(&dev->event_lock);
509ca1a93   Anssi Hannula   Input: implement ...
206
  			return error;
656acd2bb   Dmitry Torokhov   Input: fix lockin...
207
  		}
509ca1a93   Anssi Hannula   Input: implement ...
208
  	}
509ca1a93   Anssi Hannula   Input: implement ...
209
210
211
212
  	return 0;
  }
  
  /**
e4477d2d1   Randy Dunlap   Input: add to ker...
213
   * input_ff_erase - erase a force-feedback effect from device
509ca1a93   Anssi Hannula   Input: implement ...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
   * @dev: input device to erase effect from
   * @effect_id: id of the ffect to be erased
   * @file: purported owner of the request
   *
   * This function erases a force-feedback effect from specified device.
   * The effect will only be erased if it was uploaded through the same
   * file handle that is requesting erase.
   */
  int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
  {
  	struct ff_device *ff = dev->ff;
  	int ret;
  
  	if (!test_bit(EV_FF, dev->evbit))
  		return -ENOSYS;
  
  	mutex_lock(&ff->mutex);
  	ret = erase_effect(dev, effect_id, file);
  	mutex_unlock(&ff->mutex);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(input_ff_erase);
  
  /*
   * flush_effects - erase all effects owned by a file handle
   */
  static int flush_effects(struct input_dev *dev, struct file *file)
  {
  	struct ff_device *ff = dev->ff;
  	int i;
da0c49011   Joe Perches   Input: use pr_fmt...
245
246
  	pr_debug("flushing now
  ");
509ca1a93   Anssi Hannula   Input: implement ...
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  
  	mutex_lock(&ff->mutex);
  
  	for (i = 0; i < ff->max_effects; i++)
  		erase_effect(dev, i, file);
  
  	mutex_unlock(&ff->mutex);
  
  	return 0;
  }
  
  /**
   * input_ff_event() - generic handler for force-feedback events
   * @dev: input device to send the effect to
   * @type: event type (anything but EV_FF is ignored)
   * @code: event code
   * @value: event value
   */
  int input_ff_event(struct input_dev *dev, unsigned int type,
  		   unsigned int code, int value)
  {
  	struct ff_device *ff = dev->ff;
  
  	if (type != EV_FF)
  		return 0;
509ca1a93   Anssi Hannula   Input: implement ...
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  	switch (code) {
  	case FF_GAIN:
  		if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff)
  			break;
  
  		ff->set_gain(dev, value);
  		break;
  
  	case FF_AUTOCENTER:
  		if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff)
  			break;
  
  		ff->set_autocenter(dev, value);
  		break;
  
  	default:
dec3eb01c   Dmitry Torokhov   Input: force feed...
288
289
  		if (check_effect_access(ff, code, NULL) == 0)
  			ff->playback(dev, code, value);
509ca1a93   Anssi Hannula   Input: implement ...
290
291
  		break;
  	}
509ca1a93   Anssi Hannula   Input: implement ...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  	return 0;
  }
  EXPORT_SYMBOL_GPL(input_ff_event);
  
  /**
   * input_ff_create() - create force-feedback device
   * @dev: input device supporting force-feedback
   * @max_effects: maximum number of effects supported by the device
   *
   * This function allocates all necessary memory for a force feedback
   * portion of an input device and installs all default handlers.
   * @dev->ffbit should be already set up before calling this function.
   * Once ff device is created you need to setup its upload, erase,
   * playback and other handlers before registering input device
   */
05be8b81a   Dan Carpenter   Input: force feed...
307
  int input_ff_create(struct input_dev *dev, unsigned int max_effects)
509ca1a93   Anssi Hannula   Input: implement ...
308
309
  {
  	struct ff_device *ff;
05be8b81a   Dan Carpenter   Input: force feed...
310
  	size_t ff_dev_size;
509ca1a93   Anssi Hannula   Input: implement ...
311
312
313
  	int i;
  
  	if (!max_effects) {
da0c49011   Joe Perches   Input: use pr_fmt...
314
315
  		pr_err("cannot allocate device without any effects
  ");
509ca1a93   Anssi Hannula   Input: implement ...
316
317
  		return -EINVAL;
  	}
05be8b81a   Dan Carpenter   Input: force feed...
318
319
320
321
322
323
  	ff_dev_size = sizeof(struct ff_device) +
  				max_effects * sizeof(struct file *);
  	if (ff_dev_size < max_effects) /* overflow */
  		return -EINVAL;
  
  	ff = kzalloc(ff_dev_size, GFP_KERNEL);
509ca1a93   Anssi Hannula   Input: implement ...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  	if (!ff)
  		return -ENOMEM;
  
  	ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),
  			      GFP_KERNEL);
  	if (!ff->effects) {
  		kfree(ff);
  		return -ENOMEM;
  	}
  
  	ff->max_effects = max_effects;
  	mutex_init(&ff->mutex);
  
  	dev->ff = ff;
  	dev->flush = flush_effects;
  	dev->event = input_ff_event;
bf3204cbf   Dmitry Torokhov   Input: fix lockin...
340
  	__set_bit(EV_FF, dev->evbit);
509ca1a93   Anssi Hannula   Input: implement ...
341
342
343
344
  
  	/* Copy "true" bits into ff device bitmap */
  	for (i = 0; i <= FF_MAX; i++)
  		if (test_bit(i, dev->ffbit))
bf3204cbf   Dmitry Torokhov   Input: fix lockin...
345
  			__set_bit(i, ff->ffbit);
509ca1a93   Anssi Hannula   Input: implement ...
346
347
348
  
  	/* we can emulate RUMBLE with periodic effects */
  	if (test_bit(FF_PERIODIC, ff->ffbit))
bf3204cbf   Dmitry Torokhov   Input: fix lockin...
349
  		__set_bit(FF_RUMBLE, dev->ffbit);
509ca1a93   Anssi Hannula   Input: implement ...
350
351
352
353
354
355
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(input_ff_create);
  
  /**
721a730ec   Roger Quadros   Input: force feed...
356
   * input_ff_destroy() - frees force feedback portion of input device
e4477d2d1   Randy Dunlap   Input: add to ker...
357
   * @dev: input device supporting force feedback
509ca1a93   Anssi Hannula   Input: implement ...
358
359
360
361
362
363
364
   *
   * This function is only needed in error path as input core will
   * automatically free force feedback structures when device is
   * destroyed.
   */
  void input_ff_destroy(struct input_dev *dev)
  {
bf3204cbf   Dmitry Torokhov   Input: fix lockin...
365
366
367
368
369
370
371
  	struct ff_device *ff = dev->ff;
  
  	__clear_bit(EV_FF, dev->evbit);
  	if (ff) {
  		if (ff->destroy)
  			ff->destroy(ff);
  		kfree(ff->private);
6a47081c3   Jari Vanhala   Input: fix memory...
372
  		kfree(ff->effects);
bf3204cbf   Dmitry Torokhov   Input: fix lockin...
373
  		kfree(ff);
509ca1a93   Anssi Hannula   Input: implement ...
374
375
376
377
  		dev->ff = NULL;
  	}
  }
  EXPORT_SYMBOL_GPL(input_ff_destroy);