~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/oxfw/oxfw-scs1x.c

Version: ~ [ linux-5.15-rc1 ] ~ [ linux-5.14.5 ] ~ [ linux-5.13.18 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.66 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.147 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.206 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.246 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.282 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.283 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 }
421 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp