]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices | |
3 | * | |
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp> | |
6 | * | |
7 | * Licensed under the terms of the GNU General Public License, version 2. | |
8 | */ | |
9 | ||
10 | #include "oxfw.h" | |
11 | ||
12 | #define HSS1394_ADDRESS 0xc007dedadadaULL | |
13 | #define HSS1394_MAX_PACKET_SIZE 64 | |
14 | #define HSS1394_TAG_USER_DATA 0x00 | |
15 | #define HSS1394_TAG_CHANGE_ADDRESS 0xf1 | |
16 | ||
17 | struct fw_scs1x { | |
18 | struct fw_address_handler hss_handler; | |
19 | u8 input_escape_count; | |
20 | struct snd_rawmidi_substream *input; | |
21 | ||
22 | /* For MIDI playback. */ | |
23 | struct snd_rawmidi_substream *output; | |
24 | bool output_idle; | |
25 | u8 output_status; | |
26 | u8 output_bytes; | |
27 | bool output_escaped; | |
28 | bool output_escape_high_nibble; | |
29 | struct work_struct work; | |
30 | wait_queue_head_t idle_wait; | |
31 | u8 buffer[HSS1394_MAX_PACKET_SIZE]; | |
32 | bool transaction_running; | |
33 | struct fw_transaction transaction; | |
34 | unsigned int transaction_bytes; | |
35 | bool error; | |
36 | struct fw_device *fw_dev; | |
37 | }; | |
38 | ||
39 | static const u8 sysex_escape_prefix[] = { | |
40 | 0xf0, /* SysEx begin */ | |
41 | 0x00, 0x01, 0x60, /* Stanton DJ */ | |
42 | 0x48, 0x53, 0x53, /* "HSS" */ | |
43 | }; | |
44 | ||
45 | static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream, | |
46 | u8 byte) | |
47 | { | |
48 | u8 nibbles[2]; | |
49 | ||
50 | nibbles[0] = byte >> 4; | |
51 | nibbles[1] = byte & 0x0f; | |
52 | snd_rawmidi_receive(stream, nibbles, 2); | |
53 | } | |
54 | ||
55 | static void midi_input_byte(struct fw_scs1x *scs, | |
56 | struct snd_rawmidi_substream *stream, u8 byte) | |
57 | { | |
58 | const u8 eox = 0xf7; | |
59 | ||
60 | if (scs->input_escape_count > 0) { | |
61 | midi_input_escaped_byte(stream, byte); | |
62 | scs->input_escape_count--; | |
63 | if (scs->input_escape_count == 0) | |
64 | snd_rawmidi_receive(stream, &eox, sizeof(eox)); | |
65 | } else if (byte == 0xf9) { | |
66 | snd_rawmidi_receive(stream, sysex_escape_prefix, | |
67 | ARRAY_SIZE(sysex_escape_prefix)); | |
68 | midi_input_escaped_byte(stream, 0x00); | |
69 | midi_input_escaped_byte(stream, 0xf9); | |
70 | scs->input_escape_count = 3; | |
71 | } else { | |
72 | snd_rawmidi_receive(stream, &byte, 1); | |
73 | } | |
74 | } | |
75 | ||
76 | static void midi_input_packet(struct fw_scs1x *scs, | |
77 | struct snd_rawmidi_substream *stream, | |
78 | const u8 *data, unsigned int bytes) | |
79 | { | |
80 | unsigned int i; | |
81 | const u8 eox = 0xf7; | |
82 | ||
83 | if (data[0] == HSS1394_TAG_USER_DATA) { | |
84 | for (i = 1; i < bytes; ++i) | |
85 | midi_input_byte(scs, stream, data[i]); | |
86 | } else { | |
87 | snd_rawmidi_receive(stream, sysex_escape_prefix, | |
88 | ARRAY_SIZE(sysex_escape_prefix)); | |
89 | for (i = 0; i < bytes; ++i) | |
90 | midi_input_escaped_byte(stream, data[i]); | |
91 | snd_rawmidi_receive(stream, &eox, sizeof(eox)); | |
92 | } | |
93 | } | |
94 | ||
95 | static void handle_hss(struct fw_card *card, struct fw_request *request, | |
96 | int tcode, int destination, int source, int generation, | |
97 | unsigned long long offset, void *data, size_t length, | |
98 | void *callback_data) | |
99 | { | |
100 | struct fw_scs1x *scs = callback_data; | |
101 | struct snd_rawmidi_substream *stream; | |
102 | int rcode; | |
103 | ||
104 | if (offset != scs->hss_handler.offset) { | |
105 | rcode = RCODE_ADDRESS_ERROR; | |
106 | goto end; | |
107 | } | |
108 | if (tcode != TCODE_WRITE_QUADLET_REQUEST && | |
109 | tcode != TCODE_WRITE_BLOCK_REQUEST) { | |
110 | rcode = RCODE_TYPE_ERROR; | |
111 | goto end; | |
112 | } | |
113 | ||
114 | if (length >= 1) { | |
115 | stream = READ_ONCE(scs->input); | |
116 | if (stream) | |
117 | midi_input_packet(scs, stream, data, length); | |
118 | } | |
119 | ||
120 | rcode = RCODE_COMPLETE; | |
121 | end: | |
122 | fw_send_response(card, request, rcode); | |
123 | } | |
124 | ||
125 | static void scs_write_callback(struct fw_card *card, int rcode, | |
126 | void *data, size_t length, void *callback_data) | |
127 | { | |
128 | struct fw_scs1x *scs = callback_data; | |
129 | ||
130 | if (!rcode_is_permanent_error(rcode)) { | |
131 | /* Don't retry for this data. */ | |
132 | if (rcode == RCODE_COMPLETE) | |
133 | scs->transaction_bytes = 0; | |
134 | } else { | |
135 | scs->error = true; | |
136 | } | |
137 | ||
138 | scs->transaction_running = false; | |
139 | schedule_work(&scs->work); | |
140 | } | |
141 | ||
142 | static bool is_valid_running_status(u8 status) | |
143 | { | |
144 | return status >= 0x80 && status <= 0xef; | |
145 | } | |
146 | ||
147 | static bool is_one_byte_cmd(u8 status) | |
148 | { | |
149 | return status == 0xf6 || | |
150 | status >= 0xf8; | |
151 | } | |
152 | ||
153 | static bool is_two_bytes_cmd(u8 status) | |
154 | { | |
155 | return (status >= 0xc0 && status <= 0xdf) || | |
156 | status == 0xf1 || | |
157 | status == 0xf3; | |
158 | } | |
159 | ||
160 | static bool is_three_bytes_cmd(u8 status) | |
161 | { | |
162 | return (status >= 0x80 && status <= 0xbf) || | |
163 | (status >= 0xe0 && status <= 0xef) || | |
164 | status == 0xf2; | |
165 | } | |
166 | ||
167 | static bool is_invalid_cmd(u8 status) | |
168 | { | |
169 | return status == 0xf4 || | |
170 | status == 0xf5 || | |
171 | status == 0xf9 || | |
172 | status == 0xfd; | |
173 | } | |
174 | ||
175 | static void scs_output_work(struct work_struct *work) | |
176 | { | |
177 | struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work); | |
178 | struct snd_rawmidi_substream *stream; | |
179 | unsigned int i; | |
180 | u8 byte; | |
181 | int generation; | |
182 | ||
183 | if (scs->transaction_running) | |
184 | return; | |
185 | ||
186 | stream = READ_ONCE(scs->output); | |
187 | if (!stream || scs->error) { | |
188 | scs->output_idle = true; | |
189 | wake_up(&scs->idle_wait); | |
190 | return; | |
191 | } | |
192 | ||
193 | if (scs->transaction_bytes > 0) | |
194 | goto retry; | |
195 | ||
196 | i = scs->output_bytes; | |
197 | for (;;) { | |
198 | if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { | |
199 | scs->output_bytes = i; | |
200 | scs->output_idle = true; | |
201 | wake_up(&scs->idle_wait); | |
202 | return; | |
203 | } | |
204 | /* | |
205 | * Convert from real MIDI to what I think the device expects (no | |
206 | * running status, one command per packet, unescaped SysExs). | |
207 | */ | |
208 | if (scs->output_escaped && byte < 0x80) { | |
209 | if (scs->output_escape_high_nibble) { | |
210 | if (i < HSS1394_MAX_PACKET_SIZE) { | |
211 | scs->buffer[i] = byte << 4; | |
212 | scs->output_escape_high_nibble = false; | |
213 | } | |
214 | } else { | |
215 | scs->buffer[i++] |= byte & 0x0f; | |
216 | scs->output_escape_high_nibble = true; | |
217 | } | |
218 | } else if (byte < 0x80) { | |
219 | if (i == 1) { | |
220 | if (!is_valid_running_status( | |
221 | scs->output_status)) | |
222 | continue; | |
223 | scs->buffer[0] = HSS1394_TAG_USER_DATA; | |
224 | scs->buffer[i++] = scs->output_status; | |
225 | } | |
226 | scs->buffer[i++] = byte; | |
227 | if ((i == 3 && is_two_bytes_cmd(scs->output_status)) || | |
228 | (i == 4 && is_three_bytes_cmd(scs->output_status))) | |
229 | break; | |
230 | if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) && | |
231 | !memcmp(scs->buffer + 1, sysex_escape_prefix, | |
232 | ARRAY_SIZE(sysex_escape_prefix))) { | |
233 | scs->output_escaped = true; | |
234 | scs->output_escape_high_nibble = true; | |
235 | i = 0; | |
236 | } | |
237 | if (i >= HSS1394_MAX_PACKET_SIZE) | |
238 | i = 1; | |
239 | } else if (byte == 0xf7) { | |
240 | if (scs->output_escaped) { | |
241 | if (i >= 1 && scs->output_escape_high_nibble && | |
242 | scs->buffer[0] != | |
243 | HSS1394_TAG_CHANGE_ADDRESS) | |
244 | break; | |
245 | } else { | |
246 | if (i > 1 && scs->output_status == 0xf0) { | |
247 | scs->buffer[i++] = 0xf7; | |
248 | break; | |
249 | } | |
250 | } | |
251 | i = 1; | |
252 | scs->output_escaped = false; | |
253 | } else if (!is_invalid_cmd(byte) && byte < 0xf8) { | |
254 | i = 1; | |
255 | scs->buffer[0] = HSS1394_TAG_USER_DATA; | |
256 | scs->buffer[i++] = byte; | |
257 | scs->output_status = byte; | |
258 | scs->output_escaped = false; | |
259 | if (is_one_byte_cmd(byte)) | |
260 | break; | |
261 | } | |
262 | } | |
263 | scs->output_bytes = 1; | |
264 | scs->output_escaped = false; | |
265 | ||
266 | scs->transaction_bytes = i; | |
267 | retry: | |
268 | scs->transaction_running = true; | |
269 | generation = scs->fw_dev->generation; | |
270 | smp_rmb(); /* node_id vs. generation */ | |
271 | fw_send_request(scs->fw_dev->card, &scs->transaction, | |
272 | TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, | |
273 | generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, | |
274 | scs->buffer, scs->transaction_bytes, | |
275 | scs_write_callback, scs); | |
276 | } | |
277 | ||
278 | static int midi_capture_open(struct snd_rawmidi_substream *stream) | |
279 | { | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static int midi_capture_close(struct snd_rawmidi_substream *stream) | |
284 | { | |
285 | return 0; | |
286 | } | |
287 | ||
288 | static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up) | |
289 | { | |
290 | struct fw_scs1x *scs = stream->rmidi->private_data; | |
291 | ||
292 | if (up) { | |
293 | scs->input_escape_count = 0; | |
294 | WRITE_ONCE(scs->input, stream); | |
295 | } else { | |
296 | WRITE_ONCE(scs->input, NULL); | |
297 | } | |
298 | } | |
299 | ||
300 | static int midi_playback_open(struct snd_rawmidi_substream *stream) | |
301 | { | |
302 | return 0; | |
303 | } | |
304 | ||
305 | static int midi_playback_close(struct snd_rawmidi_substream *stream) | |
306 | { | |
307 | return 0; | |
308 | } | |
309 | ||
310 | static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) | |
311 | { | |
312 | struct fw_scs1x *scs = stream->rmidi->private_data; | |
313 | ||
314 | if (up) { | |
315 | scs->output_status = 0; | |
316 | scs->output_bytes = 1; | |
317 | scs->output_escaped = false; | |
318 | scs->output_idle = false; | |
319 | scs->transaction_bytes = 0; | |
320 | scs->error = false; | |
321 | ||
322 | WRITE_ONCE(scs->output, stream); | |
323 | schedule_work(&scs->work); | |
324 | } else { | |
325 | WRITE_ONCE(scs->output, NULL); | |
326 | } | |
327 | } | |
328 | static void midi_playback_drain(struct snd_rawmidi_substream *stream) | |
329 | { | |
330 | struct fw_scs1x *scs = stream->rmidi->private_data; | |
331 | ||
332 | wait_event(scs->idle_wait, scs->output_idle); | |
333 | } | |
334 | ||
335 | static int register_address(struct snd_oxfw *oxfw) | |
336 | { | |
337 | struct fw_scs1x *scs = oxfw->spec; | |
338 | __be64 data; | |
339 | ||
340 | data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | | |
341 | scs->hss_handler.offset); | |
342 | return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST, | |
343 | HSS1394_ADDRESS, &data, sizeof(data), 0); | |
344 | } | |
345 | ||
346 | static void remove_scs1x(struct snd_rawmidi *rmidi) | |
347 | { | |
348 | struct fw_scs1x *scs = rmidi->private_data; | |
349 | ||
350 | fw_core_remove_address_handler(&scs->hss_handler); | |
351 | } | |
352 | ||
353 | void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) | |
354 | { | |
355 | register_address(oxfw); | |
356 | } | |
357 | ||
358 | int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) | |
359 | { | |
360 | static const struct snd_rawmidi_ops midi_capture_ops = { | |
361 | .open = midi_capture_open, | |
362 | .close = midi_capture_close, | |
363 | .trigger = midi_capture_trigger, | |
364 | }; | |
365 | static const struct snd_rawmidi_ops midi_playback_ops = { | |
366 | .open = midi_playback_open, | |
367 | .close = midi_playback_close, | |
368 | .trigger = midi_playback_trigger, | |
369 | .drain = midi_playback_drain, | |
370 | }; | |
371 | struct snd_rawmidi *rmidi; | |
372 | struct fw_scs1x *scs; | |
373 | int err; | |
374 | ||
375 | scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL); | |
376 | if (scs == NULL) | |
377 | return -ENOMEM; | |
378 | scs->fw_dev = fw_parent_device(oxfw->unit); | |
379 | oxfw->spec = scs; | |
380 | ||
381 | /* Allocate own handler for imcoming asynchronous transaction. */ | |
382 | scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE; | |
383 | scs->hss_handler.address_callback = handle_hss; | |
384 | scs->hss_handler.callback_data = scs; | |
385 | err = fw_core_add_address_handler(&scs->hss_handler, | |
386 | &fw_high_memory_region); | |
387 | if (err < 0) | |
388 | return err; | |
389 | ||
390 | err = register_address(oxfw); | |
391 | if (err < 0) | |
392 | goto err_allocated; | |
393 | ||
394 | /* Use unique name for backward compatibility to scs1x module. */ | |
395 | err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi); | |
396 | if (err < 0) | |
397 | goto err_allocated; | |
398 | rmidi->private_data = scs; | |
399 | rmidi->private_free = remove_scs1x; | |
400 | ||
401 | snprintf(rmidi->name, sizeof(rmidi->name), | |
402 | "%s MIDI", oxfw->card->shortname); | |
403 | ||
404 | rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT | | |
405 | SNDRV_RAWMIDI_INFO_OUTPUT | | |
406 | SNDRV_RAWMIDI_INFO_DUPLEX; | |
407 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, | |
408 | &midi_capture_ops); | |
409 | snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, | |
410 | &midi_playback_ops); | |
411 | ||
412 | INIT_WORK(&scs->work, scs_output_work); | |
413 | init_waitqueue_head(&scs->idle_wait); | |
414 | scs->output_idle = true; | |
415 | ||
416 | return 0; | |
417 | err_allocated: | |
418 | fw_core_remove_address_handler(&scs->hss_handler); | |
419 | return err; | |
420 | } |