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