linux/sound/firewire/oxfw/oxfw-scs1x.c
<<
>>
Prefs
   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
  17struct 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
  39static const u8 sysex_escape_prefix[] = {
  40        0xf0,                   /* SysEx begin */
  41        0x00, 0x01, 0x60,       /* Stanton DJ */
  42        0x48, 0x53, 0x53,       /* "HSS" */
  43};
  44
  45static 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
  55static 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
  76static 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
  95static 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;
 121end:
 122        fw_send_response(card, request, rcode);
 123}
 124
 125static 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
 142static bool is_valid_running_status(u8 status)
 143{
 144        return status >= 0x80 && status <= 0xef;
 145}
 146
 147static bool is_one_byte_cmd(u8 status)
 148{
 149        return status == 0xf6 ||
 150               status >= 0xf8;
 151}
 152
 153static bool is_two_bytes_cmd(u8 status)
 154{
 155        return (status >= 0xc0 && status <= 0xdf) ||
 156               status == 0xf1 ||
 157               status == 0xf3;
 158}
 159
 160static bool is_three_bytes_cmd(u8 status)
 161{
 162        return (status >= 0x80 && status <= 0xbf) ||
 163               (status >= 0xe0 && status <= 0xef) ||
 164               status == 0xf2;
 165}
 166
 167static bool is_invalid_cmd(u8 status)
 168{
 169        return status == 0xf4 ||
 170               status == 0xf5 ||
 171               status == 0xf9 ||
 172               status == 0xfd;
 173}
 174
 175static 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;
 267retry:
 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
 278static int midi_capture_open(struct snd_rawmidi_substream *stream)
 279{
 280        return 0;
 281}
 282
 283static int midi_capture_close(struct snd_rawmidi_substream *stream)
 284{
 285        return 0;
 286}
 287
 288static 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
 300static int midi_playback_open(struct snd_rawmidi_substream *stream)
 301{
 302        return 0;
 303}
 304
 305static int midi_playback_close(struct snd_rawmidi_substream *stream)
 306{
 307        return 0;
 308}
 309
 310static 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}
 328static 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
 335static 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
 346static 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
 353void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw)
 354{
 355        register_address(oxfw);
 356}
 357
 358int 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;
 417err_allocated:
 418        fw_core_remove_address_handler(&scs->hss_handler);
 419        return err;
 420}
 421