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