aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
authorDon Prince <dhprince-devel@yahoo.co.uk>2010-05-12 09:18:59 -0400
committerJiri Kosina <jkosina@suse.cz>2010-05-12 09:18:59 -0400
commit3a370ca1dcf8c80aff7a0a21d6b0f50ca2a151e9 (patch)
treee22cf1ec95ed95255d4d1cb535e5fb892bdda25f /drivers/hid
parent94b849aaf6e22ab7bf54b0d0377a882d4892396d (diff)
HID: Prodikeys PC-MIDI HID Driver
A specialised HID driver for the Creative Prodikeys PC-MIDI USB Keyboard. The Prodikeys PC-MIDI is a multifunction keyboard comprising a qwerty keyboard, multimedia keys and a touch sensitive musical keyboard. The specialised HID driver adds full support for the musical keyboard and extra multimedia keys which are not currently handled by the default HID driver. The specialised HID driver interfaces with ALSA, and presents the midi keyboard as a rawmidi device. Sustain duration, octave shifting and the midi output channel can be read/written form userspace via sysfs. Signed-off-by: Don Prince <dhprince-devel@yahoo.co.uk> ALSA parts: Acked-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig16
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h3
-rw-r--r--drivers/hid/hid-prodikeys.c910
5 files changed, 931 insertions, 0 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 71d4c0703629..0c3eee94f5a7 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -100,6 +100,22 @@ config HID_CHICONY
100 ---help--- 100 ---help---
101 Support for Chicony Tactical pad. 101 Support for Chicony Tactical pad.
102 102
103config HID_PRODIKEYS
104 tristate "Prodikeys PC-MIDI Keyboard support" if EMBEDDED
105 depends on USB_HID && SND
106 select SND_RAWMIDI
107 default !EMBEDDED
108 ---help---
109 Support for Prodikeys PC-MIDI Keyboard device support.
110 Say Y here to enable support for this device.
111 - Prodikeys PC-MIDI keyboard.
112 The Prodikeys PC-MIDI acts as a USB Audio device, with one MIDI
113 input and one MIDI output. These MIDI jacks appear as
114 a sound "card" in the ALSA sound system.
115 Note: if you say N here, this device will still function as a basic
116 multimedia keyboard, but will lack support for the musical keyboard
117 and some additional multimedia keys.
118
103config HID_CYPRESS 119config HID_CYPRESS
104 tristate "Cypress" if EMBEDDED 120 tristate "Cypress" if EMBEDDED
105 depends on USB_HID 121 depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 0b2618f092ca..f637b388030f 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
41obj-$(CONFIG_HID_MOSART) += hid-mosart.o 41obj-$(CONFIG_HID_MOSART) += hid-mosart.o
42obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o 42obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
43obj-$(CONFIG_HID_ORTEK) += hid-ortek.o 43obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
44obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
44obj-$(CONFIG_HID_QUANTA) += hid-quanta.o 45obj-$(CONFIG_HID_QUANTA) += hid-quanta.o
45obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o 46obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
46obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o 47obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 143e788b729b..1c57a9391c8f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1293,6 +1293,7 @@ static const struct hid_device_id hid_blacklist[] = {
1293 { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, 1293 { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
1294 { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, 1294 { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) },
1295 { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, 1295 { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
1296 { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) },
1296 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, 1297 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
1297 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, 1298 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
1298 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, 1299 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 09d27649a0f7..50134431aeee 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -148,6 +148,9 @@
148#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 148#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
149#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff 149#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
150 150
151#define USB_VENDOR_ID_CREATIVELABS 0x041e
152#define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801
153
151#define USB_VENDOR_ID_CYGNAL 0x10c4 154#define USB_VENDOR_ID_CYGNAL 0x10c4
152#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a 155#define USB_DEVICE_ID_CYGNAL_RADIO_SI470X 0x818a
153 156
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
new file mode 100644
index 000000000000..845f428b8090
--- /dev/null
+++ b/drivers/hid/hid-prodikeys.c
@@ -0,0 +1,910 @@
1/*
2 * HID driver for the Prodikeys PC-MIDI Keyboard
3 * providing midi & extra multimedia keys functionality
4 *
5 * Copyright (c) 2009 Don Prince <dhprince.devel@yahoo.co.uk>
6 *
7 * Controls for Octave Shift Up/Down, Channel, and
8 * Sustain Duration available via sysfs.
9 *
10 */
11
12/*
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 2 of the License, or (at your option)
16 * any later version.
17 */
18
19#include <linux/device.h>
20#include <linux/module.h>
21#include <linux/usb.h>
22#include <linux/mutex.h>
23#include <linux/hid.h>
24#include <sound/core.h>
25#include <sound/initval.h>
26#include <sound/rawmidi.h>
27#include "usbhid/usbhid.h"
28#include "hid-ids.h"
29
30
31#define pk_debug(format, arg...) \
32 pr_debug("hid-prodikeys: " format "\n" , ## arg)
33#define pk_error(format, arg...) \
34 pr_err("hid-prodikeys: " format "\n" , ## arg)
35
36struct pcmidi_snd;
37
38struct pk_device {
39 unsigned long quirks;
40
41 struct hid_device *hdev;
42 struct pcmidi_snd *pm; /* pcmidi device context */
43};
44
45struct pcmidi_snd;
46
47struct pcmidi_sustain {
48 unsigned long in_use;
49 struct pcmidi_snd *pm;
50 struct timer_list timer;
51 unsigned char status;
52 unsigned char note;
53 unsigned char velocity;
54};
55
56#define PCMIDI_SUSTAINED_MAX 32
57struct pcmidi_snd {
58 struct pk_device *pk;
59 unsigned short ifnum;
60 struct hid_report *pcmidi_report6;
61 struct input_dev *input_ep82;
62 unsigned short midi_mode;
63 unsigned short midi_sustain_mode;
64 unsigned short midi_sustain;
65 unsigned short midi_channel;
66 short midi_octave;
67 struct pcmidi_sustain sustained_notes[PCMIDI_SUSTAINED_MAX];
68 unsigned short fn_state;
69 unsigned short last_key[24];
70 spinlock_t rawmidi_in_lock;
71 struct snd_card *card;
72 struct snd_rawmidi *rwmidi;
73 struct snd_rawmidi_substream *in_substream;
74 struct snd_rawmidi_substream *out_substream;
75 unsigned long in_triggered;
76 unsigned long out_active;
77};
78
79#define PK_QUIRK_NOGET 0x00010000
80#define PCMIDI_MIDDLE_C 60
81#define PCMIDI_CHANNEL_MIN 0
82#define PCMIDI_CHANNEL_MAX 15
83#define PCMIDI_OCTAVE_MIN (-2)
84#define PCMIDI_OCTAVE_MAX 2
85#define PCMIDI_SUSTAIN_MIN 0
86#define PCMIDI_SUSTAIN_MAX 5000
87
88static const char shortname[] = "PC-MIDI";
89static const char longname[] = "Prodikeys PC-MIDI Keyboard";
90
91static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
92static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
93static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
94
95module_param_array(index, int, NULL, 0444);
96module_param_array(id, charp, NULL, 0444);
97module_param_array(enable, bool, NULL, 0444);
98MODULE_PARM_DESC(index, "Index value for the PC-MIDI virtual audio driver");
99MODULE_PARM_DESC(id, "ID string for the PC-MIDI virtual audio driver");
100MODULE_PARM_DESC(enable, "Enable for the PC-MIDI virtual audio driver");
101
102
103/* Output routine for the sysfs channel file */
104static ssize_t show_channel(struct device *dev,
105 struct device_attribute *attr, char *buf)
106{
107 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
108 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
109
110 dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
111
112 return sprintf(buf, "%u (min:%u, max:%u)\n", pk->pm->midi_channel,
113 PCMIDI_CHANNEL_MIN, PCMIDI_CHANNEL_MAX);
114}
115
116/* Input routine for the sysfs channel file */
117static ssize_t store_channel(struct device *dev,
118 struct device_attribute *attr, const char *buf, size_t count)
119{
120 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
121 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
122
123 unsigned channel = 0;
124
125 if (sscanf(buf, "%u", &channel) > 0 && channel <= PCMIDI_CHANNEL_MAX) {
126 dbg_hid("pcmidi sysfs write channel=%u\n", channel);
127 pk->pm->midi_channel = channel;
128 return strlen(buf);
129 }
130 return -EINVAL;
131}
132
133static DEVICE_ATTR(channel, S_IRUGO | S_IWUGO, show_channel,
134 store_channel);
135
136static struct device_attribute *sysfs_device_attr_channel = {
137 &dev_attr_channel,
138 };
139
140/* Output routine for the sysfs sustain file */
141static ssize_t show_sustain(struct device *dev,
142 struct device_attribute *attr, char *buf)
143{
144 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
145 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
146
147 dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
148
149 return sprintf(buf, "%u (off:%u, max:%u (ms))\n", pk->pm->midi_sustain,
150 PCMIDI_SUSTAIN_MIN, PCMIDI_SUSTAIN_MAX);
151}
152
153/* Input routine for the sysfs sustain file */
154static ssize_t store_sustain(struct device *dev,
155 struct device_attribute *attr, const char *buf, size_t count)
156{
157 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
158 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
159
160 unsigned sustain = 0;
161
162 if (sscanf(buf, "%u", &sustain) > 0 && sustain <= PCMIDI_SUSTAIN_MAX) {
163 dbg_hid("pcmidi sysfs write sustain=%u\n", sustain);
164 pk->pm->midi_sustain = sustain;
165 pk->pm->midi_sustain_mode =
166 (0 == sustain || !pk->pm->midi_mode) ? 0 : 1;
167 return strlen(buf);
168 }
169 return -EINVAL;
170}
171
172static DEVICE_ATTR(sustain, S_IRUGO | S_IWUGO, show_sustain,
173 store_sustain);
174
175static struct device_attribute *sysfs_device_attr_sustain = {
176 &dev_attr_sustain,
177 };
178
179/* Output routine for the sysfs octave file */
180static ssize_t show_octave(struct device *dev,
181 struct device_attribute *attr, char *buf)
182{
183 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
184 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
185
186 dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
187
188 return sprintf(buf, "%d (min:%d, max:%d)\n", pk->pm->midi_octave,
189 PCMIDI_OCTAVE_MIN, PCMIDI_OCTAVE_MAX);
190}
191
192/* Input routine for the sysfs octave file */
193static ssize_t store_octave(struct device *dev,
194 struct device_attribute *attr, const char *buf, size_t count)
195{
196 struct hid_device *hdev = container_of(dev, struct hid_device, dev);
197 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
198
199 int octave = 0;
200
201 if (sscanf(buf, "%d", &octave) > 0 &&
202 octave >= PCMIDI_OCTAVE_MIN && octave <= PCMIDI_OCTAVE_MAX) {
203 dbg_hid("pcmidi sysfs write octave=%d\n", octave);
204 pk->pm->midi_octave = octave;
205 return strlen(buf);
206 }
207 return -EINVAL;
208}
209
210static DEVICE_ATTR(octave, S_IRUGO | S_IWUGO, show_octave,
211 store_octave);
212
213static struct device_attribute *sysfs_device_attr_octave = {
214 &dev_attr_octave,
215 };
216
217
218static void pcmidi_send_note(struct pcmidi_snd *pm,
219 unsigned char status, unsigned char note, unsigned char velocity)
220{
221 unsigned long flags;
222 unsigned char buffer[3];
223
224 buffer[0] = status;
225 buffer[1] = note;
226 buffer[2] = velocity;
227
228 spin_lock_irqsave(&pm->rawmidi_in_lock, flags);
229
230 if (!pm->in_substream)
231 goto drop_note;
232 if (!test_bit(pm->in_substream->number, &pm->in_triggered))
233 goto drop_note;
234
235 snd_rawmidi_receive(pm->in_substream, buffer, 3);
236
237drop_note:
238 spin_unlock_irqrestore(&pm->rawmidi_in_lock, flags);
239
240 return;
241}
242
243void pcmidi_sustained_note_release(unsigned long data)
244{
245 struct pcmidi_sustain *pms = (struct pcmidi_sustain *)data;
246
247 pcmidi_send_note(pms->pm, pms->status, pms->note, pms->velocity);
248 pms->in_use = 0;
249}
250
251void init_sustain_timers(struct pcmidi_snd *pm)
252{
253 struct pcmidi_sustain *pms;
254 unsigned i;
255
256 for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
257 pms = &pm->sustained_notes[i];
258 pms->in_use = 0;
259 pms->pm = pm;
260 setup_timer(&pms->timer, pcmidi_sustained_note_release,
261 (unsigned long)pms);
262 }
263}
264
265void stop_sustain_timers(struct pcmidi_snd *pm)
266{
267 struct pcmidi_sustain *pms;
268 unsigned i;
269
270 for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
271 pms = &pm->sustained_notes[i];
272 pms->in_use = 1;
273 del_timer_sync(&pms->timer);
274 }
275}
276
277static int pcmidi_get_output_report(struct pcmidi_snd *pm)
278{
279 struct hid_device *hdev = pm->pk->hdev;
280 struct hid_report *report;
281
282 list_for_each_entry(report,
283 &hdev->report_enum[HID_OUTPUT_REPORT].report_list, list) {
284 if (!(6 == report->id))
285 continue;
286
287 if (report->maxfield < 1) {
288 dev_err(&hdev->dev, "output report is empty\n");
289 break;
290 }
291 if (report->field[0]->report_count != 2) {
292 dev_err(&hdev->dev, "field count too low\n");
293 break;
294 }
295 pm->pcmidi_report6 = report;
296 return 0;
297 }
298 /* should never get here */
299 return -ENODEV;
300}
301
302static void pcmidi_submit_output_report(struct pcmidi_snd *pm, int state)
303{
304 struct hid_device *hdev = pm->pk->hdev;
305 struct hid_report *report = pm->pcmidi_report6;
306 report->field[0]->value[0] = 0x01;
307 report->field[0]->value[1] = state;
308
309 usbhid_submit_report(hdev, report, USB_DIR_OUT);
310}
311
312static int pcmidi_handle_report1(struct pcmidi_snd *pm, u8 *data)
313{
314 u32 bit_mask;
315
316 bit_mask = data[1];
317 bit_mask = (bit_mask << 8) | data[2];
318 bit_mask = (bit_mask << 8) | data[3];
319
320 dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
321
322 /*KEY_MAIL or octave down*/
323 if (pm->midi_mode && bit_mask == 0x004000) {
324 /* octave down */
325 pm->midi_octave--;
326 if (pm->midi_octave < -2)
327 pm->midi_octave = -2;
328 dbg_hid("pcmidi mode: %d octave: %d\n",
329 pm->midi_mode, pm->midi_octave);
330 return 1;
331 }
332 /*KEY_WWW or sustain*/
333 else if (pm->midi_mode && bit_mask == 0x000004) {
334 /* sustain on/off*/
335 pm->midi_sustain_mode ^= 0x1;
336 return 1;
337 }
338
339 return 0; /* continue key processing */
340}
341
342static int pcmidi_handle_report3(struct pcmidi_snd *pm, u8 *data, int size)
343{
344 struct pcmidi_sustain *pms;
345 unsigned i, j;
346 unsigned char status, note, velocity;
347
348 unsigned num_notes = (size-1)/2;
349 for (j = 0; j < num_notes; j++) {
350 note = data[j*2+1];
351 velocity = data[j*2+2];
352
353 if (note < 0x81) { /* note on */
354 status = 128 + 16 + pm->midi_channel; /* 1001nnnn */
355 note = note - 0x54 + PCMIDI_MIDDLE_C +
356 (pm->midi_octave * 12);
357 if (0 == velocity)
358 velocity = 1; /* force note on */
359 } else { /* note off */
360 status = 128 + pm->midi_channel; /* 1000nnnn */
361 note = note - 0x94 + PCMIDI_MIDDLE_C +
362 (pm->midi_octave*12);
363
364 if (pm->midi_sustain_mode) {
365 for (i = 0; i < PCMIDI_SUSTAINED_MAX; i++) {
366 pms = &pm->sustained_notes[i];
367 if (!pms->in_use) {
368 pms->status = status;
369 pms->note = note;
370 pms->velocity = velocity;
371 pms->in_use = 1;
372
373 mod_timer(&pms->timer,
374 jiffies +
375 msecs_to_jiffies(pm->midi_sustain));
376 return 1;
377 }
378 }
379 }
380 }
381 pcmidi_send_note(pm, status, note, velocity);
382 }
383
384 return 1;
385}
386
387static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data)
388{
389 unsigned key;
390 u32 bit_mask;
391 u32 bit_index;
392
393 bit_mask = data[1];
394 bit_mask = (bit_mask << 8) | data[2];
395 bit_mask = (bit_mask << 8) | data[3];
396
397 /* break keys */
398 for (bit_index = 0; bit_index < 24; bit_index++) {
399 key = pm->last_key[bit_index];
400 if (!((0x01 << bit_index) & bit_mask)) {
401 input_event(pm->input_ep82, EV_KEY,
402 pm->last_key[bit_index], 0);
403 pm->last_key[bit_index] = 0;
404 }
405 }
406
407 /* make keys */
408 for (bit_index = 0; bit_index < 24; bit_index++) {
409 key = 0;
410 switch ((0x01 << bit_index) & bit_mask) {
411 case 0x000010: /* Fn lock*/
412 pm->fn_state ^= 0x000010;
413 if (pm->fn_state)
414 pcmidi_submit_output_report(pm, 0xc5);
415 else
416 pcmidi_submit_output_report(pm, 0xc6);
417 continue;
418 case 0x020000: /* midi launcher..send a key (qwerty) or not? */
419 pcmidi_submit_output_report(pm, 0xc1);
420 pm->midi_mode ^= 0x01;
421
422 dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
423 continue;
424 case 0x100000: /* KEY_MESSENGER or octave up */
425 dbg_hid("pcmidi mode: %d\n", pm->midi_mode);
426 if (pm->midi_mode) {
427 pm->midi_octave++;
428 if (pm->midi_octave > 2)
429 pm->midi_octave = 2;
430 dbg_hid("pcmidi mode: %d octave: %d\n",
431 pm->midi_mode, pm->midi_octave);
432 continue;
433 } else
434 key = KEY_MESSENGER;
435 break;
436 case 0x400000:
437 key = KEY_CALENDAR;
438 break;
439 case 0x080000:
440 key = KEY_ADDRESSBOOK;
441 break;
442 case 0x040000:
443 key = KEY_DOCUMENTS;
444 break;
445 case 0x800000:
446 key = KEY_WORDPROCESSOR;
447 break;
448 case 0x200000:
449 key = KEY_SPREADSHEET;
450 break;
451 case 0x010000:
452 key = KEY_COFFEE;
453 break;
454 case 0x000100:
455 key = KEY_HELP;
456 break;
457 case 0x000200:
458 key = KEY_SEND;
459 break;
460 case 0x000400:
461 key = KEY_REPLY;
462 break;
463 case 0x000800:
464 key = KEY_FORWARDMAIL;
465 break;
466 case 0x001000:
467 key = KEY_NEW;
468 break;
469 case 0x002000:
470 key = KEY_OPEN;
471 break;
472 case 0x004000:
473 key = KEY_CLOSE;
474 break;
475 case 0x008000:
476 key = KEY_SAVE;
477 break;
478 case 0x000001:
479 key = KEY_UNDO;
480 break;
481 case 0x000002:
482 key = KEY_REDO;
483 break;
484 case 0x000004:
485 key = KEY_SPELLCHECK;
486 break;
487 case 0x000008:
488 key = KEY_PRINT;
489 break;
490 }
491 if (key) {
492 input_event(pm->input_ep82, EV_KEY, key, 1);
493 pm->last_key[bit_index] = key;
494 }
495 }
496
497 return 1;
498}
499
500int pcmidi_handle_report(
501 struct pcmidi_snd *pm, unsigned report_id, u8 *data, int size)
502{
503 int ret = 0;
504
505 switch (report_id) {
506 case 0x01: /* midi keys (qwerty)*/
507 ret = pcmidi_handle_report1(pm, data);
508 break;
509 case 0x03: /* midi keyboard (musical)*/
510 ret = pcmidi_handle_report3(pm, data, size);
511 break;
512 case 0x04: /* multimedia/midi keys (qwerty)*/
513 ret = pcmidi_handle_report4(pm, data);
514 break;
515 }
516 return ret;
517}
518
519void pcmidi_setup_extra_keys(struct pcmidi_snd *pm, struct input_dev *input)
520{
521 /* reassigned functionality for N/A keys
522 MY PICTURES => KEY_WORDPROCESSOR
523 MY MUSIC=> KEY_SPREADSHEET
524 */
525 unsigned int keys[] = {
526 KEY_FN,
527 KEY_MESSENGER, KEY_CALENDAR,
528 KEY_ADDRESSBOOK, KEY_DOCUMENTS,
529 KEY_WORDPROCESSOR,
530 KEY_SPREADSHEET,
531 KEY_COFFEE,
532 KEY_HELP, KEY_SEND,
533 KEY_REPLY, KEY_FORWARDMAIL,
534 KEY_NEW, KEY_OPEN,
535 KEY_CLOSE, KEY_SAVE,
536 KEY_UNDO, KEY_REDO,
537 KEY_SPELLCHECK, KEY_PRINT,
538 0
539 };
540
541 unsigned int *pkeys = &keys[0];
542 unsigned short i;
543
544 if (pm->ifnum != 1) /* only set up ONCE for interace 1 */
545 return;
546
547 pm->input_ep82 = input;
548
549 for (i = 0; i < 24; i++)
550 pm->last_key[i] = 0;
551
552 while (*pkeys != 0) {
553 set_bit(*pkeys, pm->input_ep82->keybit);
554 ++pkeys;
555 }
556}
557
558static int pcmidi_set_operational(struct pcmidi_snd *pm)
559{
560 if (pm->ifnum != 1)
561 return 0; /* only set up ONCE for interace 1 */
562
563 pcmidi_get_output_report(pm);
564 pcmidi_submit_output_report(pm, 0xc1);
565 return 0;
566}
567
568static int pcmidi_snd_free(struct snd_device *dev)
569{
570 return 0;
571}
572
573static int pcmidi_in_open(struct snd_rawmidi_substream *substream)
574{
575 struct pcmidi_snd *pm = substream->rmidi->private_data;
576
577 dbg_hid("pcmidi in open\n");
578 pm->in_substream = substream;
579 return 0;
580}
581
582static int pcmidi_in_close(struct snd_rawmidi_substream *substream)
583{
584 dbg_hid("pcmidi in close\n");
585 return 0;
586}
587
588static void pcmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
589{
590 struct pcmidi_snd *pm = substream->rmidi->private_data;
591
592 dbg_hid("pcmidi in trigger %d\n", up);
593
594 pm->in_triggered = up;
595}
596
597static struct snd_rawmidi_ops pcmidi_in_ops = {
598 .open = pcmidi_in_open,
599 .close = pcmidi_in_close,
600 .trigger = pcmidi_in_trigger
601};
602
603int pcmidi_snd_initialise(struct pcmidi_snd *pm)
604{
605 static int dev;
606 struct snd_card *card;
607 struct snd_rawmidi *rwmidi;
608 int err;
609
610 static struct snd_device_ops ops = {
611 .dev_free = pcmidi_snd_free,
612 };
613
614 if (pm->ifnum != 1)
615 return 0; /* only set up midi device ONCE for interace 1 */
616
617 if (dev >= SNDRV_CARDS)
618 return -ENODEV;
619
620 if (!enable[dev]) {
621 dev++;
622 return -ENOENT;
623 }
624
625 /* Setup sound card */
626
627 err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
628 if (err < 0) {
629 pk_error("failed to create pc-midi sound card\n");
630 err = -ENOMEM;
631 goto fail;
632 }
633 pm->card = card;
634
635 /* Setup sound device */
636 err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, pm, &ops);
637 if (err < 0) {
638 pk_error("failed to create pc-midi sound device: error %d\n",
639 err);
640 goto fail;
641 }
642
643 strncpy(card->driver, shortname, sizeof(card->driver));
644 strncpy(card->shortname, shortname, sizeof(card->shortname));
645 strncpy(card->longname, longname, sizeof(card->longname));
646
647 /* Set up rawmidi */
648 err = snd_rawmidi_new(card, card->shortname, 0,
649 0, 1, &rwmidi);
650 if (err < 0) {
651 pk_error("failed to create pc-midi rawmidi device: error %d\n",
652 err);
653 goto fail;
654 }
655 pm->rwmidi = rwmidi;
656 strncpy(rwmidi->name, card->shortname, sizeof(rwmidi->name));
657 rwmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT;
658 rwmidi->private_data = pm;
659
660 snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT,
661 &pcmidi_in_ops);
662
663 snd_card_set_dev(card, &pm->pk->hdev->dev);
664
665 /* create sysfs variables */
666 err = device_create_file(&pm->pk->hdev->dev,
667 sysfs_device_attr_channel);
668 if (err < 0) {
669 pk_error("failed to create sysfs attribute channel: error %d\n",
670 err);
671 goto fail;
672 }
673
674 err = device_create_file(&pm->pk->hdev->dev,
675 sysfs_device_attr_sustain);
676 if (err < 0) {
677 pk_error("failed to create sysfs attribute sustain: error %d\n",
678 err);
679 goto fail_attr_sustain;
680 }
681
682 err = device_create_file(&pm->pk->hdev->dev,
683 sysfs_device_attr_octave);
684 if (err < 0) {
685 pk_error("failed to create sysfs attribute octave: error %d\n",
686 err);
687 goto fail_attr_octave;
688 }
689
690 spin_lock_init(&pm->rawmidi_in_lock);
691
692 init_sustain_timers(pm);
693 pcmidi_set_operational(pm);
694
695 /* register it */
696 err = snd_card_register(card);
697 if (err < 0) {
698 pk_error("failed to register pc-midi sound card: error %d\n",
699 err);
700 goto fail_register;
701 }
702
703 dbg_hid("pcmidi_snd_initialise finished ok\n");
704 return 0;
705
706fail_register:
707 stop_sustain_timers(pm);
708 device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_octave);
709fail_attr_octave:
710 device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_sustain);
711fail_attr_sustain:
712 device_remove_file(&pm->pk->hdev->dev, sysfs_device_attr_channel);
713fail:
714 if (pm->card) {
715 snd_card_free(pm->card);
716 pm->card = NULL;
717 }
718 return err;
719}
720
721int pcmidi_snd_terminate(struct pcmidi_snd *pm)
722{
723 if (pm->card) {
724 stop_sustain_timers(pm);
725
726 device_remove_file(&pm->pk->hdev->dev,
727 sysfs_device_attr_channel);
728 device_remove_file(&pm->pk->hdev->dev,
729 sysfs_device_attr_sustain);
730 device_remove_file(&pm->pk->hdev->dev,
731 sysfs_device_attr_octave);
732
733 snd_card_disconnect(pm->card);
734 snd_card_free_when_closed(pm->card);
735 }
736
737 return 0;
738}
739
740/*
741 * PC-MIDI report descriptor for report id is wrong.
742 */
743static void pk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
744 unsigned int rsize)
745{
746 if (rsize == 178 &&
747 rdesc[111] == 0x06 && rdesc[112] == 0x00 &&
748 rdesc[113] == 0xff) {
749 dev_info(&hdev->dev, "fixing up pc-midi keyboard report "
750 "descriptor\n");
751
752 rdesc[144] = 0x18; /* report 4: was 0x10 report count */
753 }
754}
755
756static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
757 struct hid_field *field, struct hid_usage *usage,
758 unsigned long **bit, int *max)
759{
760 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
761 struct pcmidi_snd *pm;
762
763 pm = pk->pm;
764
765 if (HID_UP_MSVENDOR == (usage->hid & HID_USAGE_PAGE) &&
766 1 == pm->ifnum) {
767 pcmidi_setup_extra_keys(pm, hi->input);
768 return 0;
769 }
770
771 return 0;
772}
773
774
775static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
776 u8 *data, int size)
777{
778 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
779 int ret = 0;
780
781 if (1 == pk->pm->ifnum) {
782 if (report->id == data[0])
783 switch (report->id) {
784 case 0x01: /* midi keys (qwerty)*/
785 case 0x03: /* midi keyboard (musical)*/
786 case 0x04: /* extra/midi keys (qwerty)*/
787 ret = pcmidi_handle_report(pk->pm,
788 report->id, data, size);
789 break;
790 }
791 }
792
793 return ret;
794}
795
796static int pk_probe(struct hid_device *hdev, const struct hid_device_id *id)
797{
798 int ret;
799 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
800 unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
801 unsigned long quirks = id->driver_data;
802 struct pk_device *pk;
803 struct pcmidi_snd *pm = NULL;
804
805 pk = kzalloc(sizeof(*pk), GFP_KERNEL);
806 if (pk == NULL) {
807 dev_err(&hdev->dev, "prodikeys: can't alloc descriptor\n");
808 return -ENOMEM;
809 }
810
811 pk->hdev = hdev;
812
813 pm = kzalloc(sizeof(*pm), GFP_KERNEL);
814 if (pm == NULL) {
815 dev_err(&hdev->dev,
816 "prodikeys: can't alloc descriptor\n");
817 ret = -ENOMEM;
818 goto err_free;
819 }
820
821 pm->pk = pk;
822 pk->pm = pm;
823 pm->ifnum = ifnum;
824
825 hid_set_drvdata(hdev, pk);
826
827 ret = hid_parse(hdev);
828 if (ret) {
829 dev_err(&hdev->dev, "prodikeys: hid parse failed\n");
830 goto err_free;
831 }
832
833 if (quirks & PK_QUIRK_NOGET) { /* hid_parse cleared all the quirks */
834 hdev->quirks |= HID_QUIRK_NOGET;
835 }
836
837 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
838 if (ret) {
839 dev_err(&hdev->dev, "prodikeys: hw start failed\n");
840 goto err_free;
841 }
842
843 ret = pcmidi_snd_initialise(pm);
844 if (ret < 0)
845 goto err_stop;
846
847 return 0;
848err_stop:
849 hid_hw_stop(hdev);
850err_free:
851 if (pm != NULL)
852 kfree(pm);
853
854 kfree(pk);
855 return ret;
856}
857
858static void pk_remove(struct hid_device *hdev)
859{
860 struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
861 struct pcmidi_snd *pm;
862
863 pm = pk->pm;
864 if (pm) {
865 pcmidi_snd_terminate(pm);
866 kfree(pm);
867 }
868
869 hid_hw_stop(hdev);
870
871 kfree(pk);
872}
873
874static const struct hid_device_id pk_devices[] = {
875 {HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS,
876 USB_DEVICE_ID_PRODIKEYS_PCMIDI),
877 .driver_data = PK_QUIRK_NOGET},
878 { }
879};
880MODULE_DEVICE_TABLE(hid, pk_devices);
881
882static struct hid_driver pk_driver = {
883 .name = "prodikeys",
884 .id_table = pk_devices,
885 .report_fixup = pk_report_fixup,
886 .input_mapping = pk_input_mapping,
887 .raw_event = pk_raw_event,
888 .probe = pk_probe,
889 .remove = pk_remove,
890};
891
892static int pk_init(void)
893{
894 int ret;
895
896 ret = hid_register_driver(&pk_driver);
897 if (ret)
898 printk(KERN_ERR "can't register prodikeys driver\n");
899
900 return ret;
901}
902
903static void pk_exit(void)
904{
905 hid_unregister_driver(&pk_driver);
906}
907
908module_init(pk_init);
909module_exit(pk_exit);
910MODULE_LICENSE("GPL");