13f47152aSTakashi Sakamoto /* 23f47152aSTakashi Sakamoto * oxfw-scs1x.c - a part of driver for OXFW970/971 based devices 33f47152aSTakashi Sakamoto * 43f47152aSTakashi Sakamoto * Copyright (c) Clemens Ladisch <clemens@ladisch.de> 53f47152aSTakashi Sakamoto * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp> 63f47152aSTakashi Sakamoto * 73f47152aSTakashi Sakamoto * Licensed under the terms of the GNU General Public License, version 2. 83f47152aSTakashi Sakamoto */ 93f47152aSTakashi Sakamoto 103f47152aSTakashi Sakamoto #include "oxfw.h" 113f47152aSTakashi Sakamoto 12e3315b43STakashi Sakamoto #define HSS1394_ADDRESS 0xc007dedadadaULL 13e3315b43STakashi Sakamoto #define HSS1394_MAX_PACKET_SIZE 64 1413b8b78cSTakashi Sakamoto #define HSS1394_TAG_USER_DATA 0x00 15e3315b43STakashi Sakamoto #define HSS1394_TAG_CHANGE_ADDRESS 0xf1 16e3315b43STakashi Sakamoto 17e3315b43STakashi Sakamoto struct fw_scs1x { 18e3315b43STakashi Sakamoto struct fw_address_handler hss_handler; 1913b8b78cSTakashi Sakamoto u8 input_escape_count; 2013b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *input; 21d7d20e77STakashi Sakamoto 22d7d20e77STakashi Sakamoto /* For MIDI playback. */ 23d7d20e77STakashi Sakamoto struct snd_rawmidi_substream *output; 24d7d20e77STakashi Sakamoto bool output_idle; 25d7d20e77STakashi Sakamoto u8 output_status; 26d7d20e77STakashi Sakamoto u8 output_bytes; 27d7d20e77STakashi Sakamoto bool output_escaped; 28d7d20e77STakashi Sakamoto bool output_escape_high_nibble; 29ea790053STakashi Sakamoto struct work_struct work; 30d7d20e77STakashi Sakamoto wait_queue_head_t idle_wait; 31d7d20e77STakashi Sakamoto u8 buffer[HSS1394_MAX_PACKET_SIZE]; 32d7d20e77STakashi Sakamoto bool transaction_running; 33d7d20e77STakashi Sakamoto struct fw_transaction transaction; 34d7d20e77STakashi Sakamoto struct fw_device *fw_dev; 35e3315b43STakashi Sakamoto }; 36e3315b43STakashi Sakamoto 3713b8b78cSTakashi Sakamoto static const u8 sysex_escape_prefix[] = { 3813b8b78cSTakashi Sakamoto 0xf0, /* SysEx begin */ 3913b8b78cSTakashi Sakamoto 0x00, 0x01, 0x60, /* Stanton DJ */ 4013b8b78cSTakashi Sakamoto 0x48, 0x53, 0x53, /* "HSS" */ 4113b8b78cSTakashi Sakamoto }; 4213b8b78cSTakashi Sakamoto 4313b8b78cSTakashi Sakamoto static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream, 4413b8b78cSTakashi Sakamoto u8 byte) 4513b8b78cSTakashi Sakamoto { 4613b8b78cSTakashi Sakamoto u8 nibbles[2]; 4713b8b78cSTakashi Sakamoto 4813b8b78cSTakashi Sakamoto nibbles[0] = byte >> 4; 4913b8b78cSTakashi Sakamoto nibbles[1] = byte & 0x0f; 5013b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, nibbles, 2); 5113b8b78cSTakashi Sakamoto } 5213b8b78cSTakashi Sakamoto 5313b8b78cSTakashi Sakamoto static void midi_input_byte(struct fw_scs1x *scs, 5413b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream, u8 byte) 5513b8b78cSTakashi Sakamoto { 5613b8b78cSTakashi Sakamoto const u8 eox = 0xf7; 5713b8b78cSTakashi Sakamoto 5813b8b78cSTakashi Sakamoto if (scs->input_escape_count > 0) { 5913b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, byte); 6013b8b78cSTakashi Sakamoto scs->input_escape_count--; 6113b8b78cSTakashi Sakamoto if (scs->input_escape_count == 0) 6213b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &eox, sizeof(eox)); 6313b8b78cSTakashi Sakamoto } else if (byte == 0xf9) { 6413b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, sysex_escape_prefix, 6513b8b78cSTakashi Sakamoto ARRAY_SIZE(sysex_escape_prefix)); 6613b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, 0x00); 6713b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, 0xf9); 6813b8b78cSTakashi Sakamoto scs->input_escape_count = 3; 6913b8b78cSTakashi Sakamoto } else { 7013b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &byte, 1); 7113b8b78cSTakashi Sakamoto } 7213b8b78cSTakashi Sakamoto } 7313b8b78cSTakashi Sakamoto 7413b8b78cSTakashi Sakamoto static void midi_input_packet(struct fw_scs1x *scs, 7513b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream, 7613b8b78cSTakashi Sakamoto const u8 *data, unsigned int bytes) 7713b8b78cSTakashi Sakamoto { 7813b8b78cSTakashi Sakamoto unsigned int i; 7913b8b78cSTakashi Sakamoto const u8 eox = 0xf7; 8013b8b78cSTakashi Sakamoto 8113b8b78cSTakashi Sakamoto if (data[0] == HSS1394_TAG_USER_DATA) { 8213b8b78cSTakashi Sakamoto for (i = 1; i < bytes; ++i) 8313b8b78cSTakashi Sakamoto midi_input_byte(scs, stream, data[i]); 8413b8b78cSTakashi Sakamoto } else { 8513b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, sysex_escape_prefix, 8613b8b78cSTakashi Sakamoto ARRAY_SIZE(sysex_escape_prefix)); 8713b8b78cSTakashi Sakamoto for (i = 0; i < bytes; ++i) 8813b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, data[i]); 8913b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &eox, sizeof(eox)); 9013b8b78cSTakashi Sakamoto } 9113b8b78cSTakashi Sakamoto } 9213b8b78cSTakashi Sakamoto 93e3315b43STakashi Sakamoto static void handle_hss(struct fw_card *card, struct fw_request *request, 94e3315b43STakashi Sakamoto int tcode, int destination, int source, int generation, 95e3315b43STakashi Sakamoto unsigned long long offset, void *data, size_t length, 96e3315b43STakashi Sakamoto void *callback_data) 97e3315b43STakashi Sakamoto { 9813b8b78cSTakashi Sakamoto struct fw_scs1x *scs = callback_data; 9913b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream; 10013b8b78cSTakashi Sakamoto int rcode; 10113b8b78cSTakashi Sakamoto 10213b8b78cSTakashi Sakamoto if (offset != scs->hss_handler.offset) { 10313b8b78cSTakashi Sakamoto rcode = RCODE_ADDRESS_ERROR; 10413b8b78cSTakashi Sakamoto goto end; 10513b8b78cSTakashi Sakamoto } 10613b8b78cSTakashi Sakamoto if (tcode != TCODE_WRITE_QUADLET_REQUEST && 10713b8b78cSTakashi Sakamoto tcode != TCODE_WRITE_BLOCK_REQUEST) { 10813b8b78cSTakashi Sakamoto rcode = RCODE_TYPE_ERROR; 10913b8b78cSTakashi Sakamoto goto end; 11013b8b78cSTakashi Sakamoto } 11113b8b78cSTakashi Sakamoto 11213b8b78cSTakashi Sakamoto if (length >= 1) { 11313b8b78cSTakashi Sakamoto stream = ACCESS_ONCE(scs->input); 11413b8b78cSTakashi Sakamoto if (stream) 11513b8b78cSTakashi Sakamoto midi_input_packet(scs, stream, data, length); 11613b8b78cSTakashi Sakamoto } 11713b8b78cSTakashi Sakamoto 11813b8b78cSTakashi Sakamoto rcode = RCODE_COMPLETE; 11913b8b78cSTakashi Sakamoto end: 12013b8b78cSTakashi Sakamoto fw_send_response(card, request, rcode); 121e3315b43STakashi Sakamoto } 122e3315b43STakashi Sakamoto 123d7d20e77STakashi Sakamoto static void scs_write_callback(struct fw_card *card, int rcode, 124d7d20e77STakashi Sakamoto void *data, size_t length, void *callback_data) 125d7d20e77STakashi Sakamoto { 126d7d20e77STakashi Sakamoto struct fw_scs1x *scs = callback_data; 127d7d20e77STakashi Sakamoto 128d7d20e77STakashi Sakamoto if (rcode == RCODE_GENERATION) 129d7d20e77STakashi Sakamoto ; /* TODO: retry this packet */ 130d7d20e77STakashi Sakamoto 131d7d20e77STakashi Sakamoto scs->transaction_running = false; 132ea790053STakashi Sakamoto schedule_work(&scs->work); 133d7d20e77STakashi Sakamoto } 134d7d20e77STakashi Sakamoto 135d7d20e77STakashi Sakamoto static bool is_valid_running_status(u8 status) 136d7d20e77STakashi Sakamoto { 137d7d20e77STakashi Sakamoto return status >= 0x80 && status <= 0xef; 138d7d20e77STakashi Sakamoto } 139d7d20e77STakashi Sakamoto 140d7d20e77STakashi Sakamoto static bool is_one_byte_cmd(u8 status) 141d7d20e77STakashi Sakamoto { 142d7d20e77STakashi Sakamoto return status == 0xf6 || 143d7d20e77STakashi Sakamoto status >= 0xf8; 144d7d20e77STakashi Sakamoto } 145d7d20e77STakashi Sakamoto 146d7d20e77STakashi Sakamoto static bool is_two_bytes_cmd(u8 status) 147d7d20e77STakashi Sakamoto { 148d7d20e77STakashi Sakamoto return (status >= 0xc0 && status <= 0xdf) || 149d7d20e77STakashi Sakamoto status == 0xf1 || 150d7d20e77STakashi Sakamoto status == 0xf3; 151d7d20e77STakashi Sakamoto } 152d7d20e77STakashi Sakamoto 153d7d20e77STakashi Sakamoto static bool is_three_bytes_cmd(u8 status) 154d7d20e77STakashi Sakamoto { 155d7d20e77STakashi Sakamoto return (status >= 0x80 && status <= 0xbf) || 156d7d20e77STakashi Sakamoto (status >= 0xe0 && status <= 0xef) || 157d7d20e77STakashi Sakamoto status == 0xf2; 158d7d20e77STakashi Sakamoto } 159d7d20e77STakashi Sakamoto 160d7d20e77STakashi Sakamoto static bool is_invalid_cmd(u8 status) 161d7d20e77STakashi Sakamoto { 162d7d20e77STakashi Sakamoto return status == 0xf4 || 163d7d20e77STakashi Sakamoto status == 0xf5 || 164d7d20e77STakashi Sakamoto status == 0xf9 || 165d7d20e77STakashi Sakamoto status == 0xfd; 166d7d20e77STakashi Sakamoto } 167d7d20e77STakashi Sakamoto 168ea790053STakashi Sakamoto static void scs_output_work(struct work_struct *work) 169d7d20e77STakashi Sakamoto { 170ea790053STakashi Sakamoto struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work); 171d7d20e77STakashi Sakamoto struct snd_rawmidi_substream *stream; 172d7d20e77STakashi Sakamoto unsigned int i; 173d7d20e77STakashi Sakamoto u8 byte; 174d7d20e77STakashi Sakamoto int generation; 175d7d20e77STakashi Sakamoto 176d7d20e77STakashi Sakamoto if (scs->transaction_running) 177d7d20e77STakashi Sakamoto return; 178d7d20e77STakashi Sakamoto 179d7d20e77STakashi Sakamoto stream = ACCESS_ONCE(scs->output); 180d7d20e77STakashi Sakamoto if (!stream) { 181d7d20e77STakashi Sakamoto scs->output_idle = true; 182d7d20e77STakashi Sakamoto wake_up(&scs->idle_wait); 183d7d20e77STakashi Sakamoto return; 184d7d20e77STakashi Sakamoto } 185d7d20e77STakashi Sakamoto 186d7d20e77STakashi Sakamoto i = scs->output_bytes; 187d7d20e77STakashi Sakamoto for (;;) { 188d7d20e77STakashi Sakamoto if (snd_rawmidi_transmit(stream, &byte, 1) != 1) { 189d7d20e77STakashi Sakamoto scs->output_bytes = i; 190d7d20e77STakashi Sakamoto scs->output_idle = true; 191d7d20e77STakashi Sakamoto wake_up(&scs->idle_wait); 192d7d20e77STakashi Sakamoto return; 193d7d20e77STakashi Sakamoto } 194d7d20e77STakashi Sakamoto /* 195d7d20e77STakashi Sakamoto * Convert from real MIDI to what I think the device expects (no 196d7d20e77STakashi Sakamoto * running status, one command per packet, unescaped SysExs). 197d7d20e77STakashi Sakamoto */ 198d7d20e77STakashi Sakamoto if (scs->output_escaped && byte < 0x80) { 199d7d20e77STakashi Sakamoto if (scs->output_escape_high_nibble) { 200d7d20e77STakashi Sakamoto if (i < HSS1394_MAX_PACKET_SIZE) { 201d7d20e77STakashi Sakamoto scs->buffer[i] = byte << 4; 202d7d20e77STakashi Sakamoto scs->output_escape_high_nibble = false; 203d7d20e77STakashi Sakamoto } 204d7d20e77STakashi Sakamoto } else { 205d7d20e77STakashi Sakamoto scs->buffer[i++] |= byte & 0x0f; 206d7d20e77STakashi Sakamoto scs->output_escape_high_nibble = true; 207d7d20e77STakashi Sakamoto } 208d7d20e77STakashi Sakamoto } else if (byte < 0x80) { 209d7d20e77STakashi Sakamoto if (i == 1) { 210d7d20e77STakashi Sakamoto if (!is_valid_running_status( 211d7d20e77STakashi Sakamoto scs->output_status)) 212d7d20e77STakashi Sakamoto continue; 213d7d20e77STakashi Sakamoto scs->buffer[0] = HSS1394_TAG_USER_DATA; 214d7d20e77STakashi Sakamoto scs->buffer[i++] = scs->output_status; 215d7d20e77STakashi Sakamoto } 216d7d20e77STakashi Sakamoto scs->buffer[i++] = byte; 217d7d20e77STakashi Sakamoto if ((i == 3 && is_two_bytes_cmd(scs->output_status)) || 218d7d20e77STakashi Sakamoto (i == 4 && is_three_bytes_cmd(scs->output_status))) 219d7d20e77STakashi Sakamoto break; 220d7d20e77STakashi Sakamoto if (i == 1 + ARRAY_SIZE(sysex_escape_prefix) && 221d7d20e77STakashi Sakamoto !memcmp(scs->buffer + 1, sysex_escape_prefix, 222d7d20e77STakashi Sakamoto ARRAY_SIZE(sysex_escape_prefix))) { 223d7d20e77STakashi Sakamoto scs->output_escaped = true; 224d7d20e77STakashi Sakamoto scs->output_escape_high_nibble = true; 225d7d20e77STakashi Sakamoto i = 0; 226d7d20e77STakashi Sakamoto } 227d7d20e77STakashi Sakamoto if (i >= HSS1394_MAX_PACKET_SIZE) 228d7d20e77STakashi Sakamoto i = 1; 229d7d20e77STakashi Sakamoto } else if (byte == 0xf7) { 230d7d20e77STakashi Sakamoto if (scs->output_escaped) { 231d7d20e77STakashi Sakamoto if (i >= 1 && scs->output_escape_high_nibble && 232d7d20e77STakashi Sakamoto scs->buffer[0] != 233d7d20e77STakashi Sakamoto HSS1394_TAG_CHANGE_ADDRESS) 234d7d20e77STakashi Sakamoto break; 235d7d20e77STakashi Sakamoto } else { 236d7d20e77STakashi Sakamoto if (i > 1 && scs->output_status == 0xf0) { 237d7d20e77STakashi Sakamoto scs->buffer[i++] = 0xf7; 238d7d20e77STakashi Sakamoto break; 239d7d20e77STakashi Sakamoto } 240d7d20e77STakashi Sakamoto } 241d7d20e77STakashi Sakamoto i = 1; 242d7d20e77STakashi Sakamoto scs->output_escaped = false; 243d7d20e77STakashi Sakamoto } else if (!is_invalid_cmd(byte) && byte < 0xf8) { 244d7d20e77STakashi Sakamoto i = 1; 245d7d20e77STakashi Sakamoto scs->buffer[0] = HSS1394_TAG_USER_DATA; 246d7d20e77STakashi Sakamoto scs->buffer[i++] = byte; 247d7d20e77STakashi Sakamoto scs->output_status = byte; 248d7d20e77STakashi Sakamoto scs->output_escaped = false; 249d7d20e77STakashi Sakamoto if (is_one_byte_cmd(byte)) 250d7d20e77STakashi Sakamoto break; 251d7d20e77STakashi Sakamoto } 252d7d20e77STakashi Sakamoto } 253d7d20e77STakashi Sakamoto scs->output_bytes = 1; 254d7d20e77STakashi Sakamoto scs->output_escaped = false; 255d7d20e77STakashi Sakamoto 256d7d20e77STakashi Sakamoto scs->transaction_running = true; 257d7d20e77STakashi Sakamoto generation = scs->fw_dev->generation; 258d7d20e77STakashi Sakamoto smp_rmb(); /* node_id vs. generation */ 259d7d20e77STakashi Sakamoto fw_send_request(scs->fw_dev->card, &scs->transaction, 260d7d20e77STakashi Sakamoto TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id, 261d7d20e77STakashi Sakamoto generation, scs->fw_dev->max_speed, HSS1394_ADDRESS, 262d7d20e77STakashi Sakamoto scs->buffer, i, scs_write_callback, scs); 263d7d20e77STakashi Sakamoto } 264d7d20e77STakashi Sakamoto 2658250427dSTakashi Sakamoto static int midi_capture_open(struct snd_rawmidi_substream *stream) 2668250427dSTakashi Sakamoto { 2678250427dSTakashi Sakamoto return 0; 2688250427dSTakashi Sakamoto } 2698250427dSTakashi Sakamoto 2708250427dSTakashi Sakamoto static int midi_capture_close(struct snd_rawmidi_substream *stream) 2718250427dSTakashi Sakamoto { 2728250427dSTakashi Sakamoto return 0; 2738250427dSTakashi Sakamoto } 2748250427dSTakashi Sakamoto 2758250427dSTakashi Sakamoto static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up) 2768250427dSTakashi Sakamoto { 2778250427dSTakashi Sakamoto struct fw_scs1x *scs = stream->rmidi->private_data; 2788250427dSTakashi Sakamoto 2798250427dSTakashi Sakamoto if (up) { 2808250427dSTakashi Sakamoto scs->input_escape_count = 0; 2818250427dSTakashi Sakamoto ACCESS_ONCE(scs->input) = stream; 2828250427dSTakashi Sakamoto } else { 2838250427dSTakashi Sakamoto ACCESS_ONCE(scs->input) = NULL; 2848250427dSTakashi Sakamoto } 2858250427dSTakashi Sakamoto } 2868250427dSTakashi Sakamoto 2878250427dSTakashi Sakamoto static struct snd_rawmidi_ops midi_capture_ops = { 2888250427dSTakashi Sakamoto .open = midi_capture_open, 2898250427dSTakashi Sakamoto .close = midi_capture_close, 2908250427dSTakashi Sakamoto .trigger = midi_capture_trigger, 2918250427dSTakashi Sakamoto }; 2928250427dSTakashi Sakamoto 2936f5dcb28STakashi Sakamoto static int midi_playback_open(struct snd_rawmidi_substream *stream) 2946f5dcb28STakashi Sakamoto { 2956f5dcb28STakashi Sakamoto return 0; 2966f5dcb28STakashi Sakamoto } 2976f5dcb28STakashi Sakamoto 2986f5dcb28STakashi Sakamoto static int midi_playback_close(struct snd_rawmidi_substream *stream) 2996f5dcb28STakashi Sakamoto { 3006f5dcb28STakashi Sakamoto return 0; 3016f5dcb28STakashi Sakamoto } 3026f5dcb28STakashi Sakamoto 3036f5dcb28STakashi Sakamoto static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up) 3046f5dcb28STakashi Sakamoto { 3056f5dcb28STakashi Sakamoto struct fw_scs1x *scs = stream->rmidi->private_data; 3066f5dcb28STakashi Sakamoto 3076f5dcb28STakashi Sakamoto if (up) { 3086f5dcb28STakashi Sakamoto scs->output_status = 0; 3096f5dcb28STakashi Sakamoto scs->output_bytes = 1; 3106f5dcb28STakashi Sakamoto scs->output_escaped = false; 3116f5dcb28STakashi Sakamoto scs->output_idle = false; 3126f5dcb28STakashi Sakamoto 3136f5dcb28STakashi Sakamoto ACCESS_ONCE(scs->output) = stream; 314ea790053STakashi Sakamoto schedule_work(&scs->work); 3156f5dcb28STakashi Sakamoto } else { 3166f5dcb28STakashi Sakamoto ACCESS_ONCE(scs->output) = NULL; 3176f5dcb28STakashi Sakamoto } 3186f5dcb28STakashi Sakamoto } 3196f5dcb28STakashi Sakamoto static void midi_playback_drain(struct snd_rawmidi_substream *stream) 3206f5dcb28STakashi Sakamoto { 3216f5dcb28STakashi Sakamoto struct fw_scs1x *scs = stream->rmidi->private_data; 3226f5dcb28STakashi Sakamoto 3236f5dcb28STakashi Sakamoto wait_event(scs->idle_wait, scs->output_idle); 3246f5dcb28STakashi Sakamoto } 3256f5dcb28STakashi Sakamoto 3266f5dcb28STakashi Sakamoto static struct snd_rawmidi_ops midi_playback_ops = { 3276f5dcb28STakashi Sakamoto .open = midi_playback_open, 3286f5dcb28STakashi Sakamoto .close = midi_playback_close, 3296f5dcb28STakashi Sakamoto .trigger = midi_playback_trigger, 3306f5dcb28STakashi Sakamoto .drain = midi_playback_drain, 3316f5dcb28STakashi Sakamoto }; 332e3315b43STakashi Sakamoto static int register_address(struct snd_oxfw *oxfw) 333e3315b43STakashi Sakamoto { 334e3315b43STakashi Sakamoto struct fw_scs1x *scs = oxfw->spec; 335e3315b43STakashi Sakamoto __be64 data; 336e3315b43STakashi Sakamoto 337e3315b43STakashi Sakamoto data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | 338e3315b43STakashi Sakamoto scs->hss_handler.offset); 339e3315b43STakashi Sakamoto return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST, 340e3315b43STakashi Sakamoto HSS1394_ADDRESS, &data, sizeof(data), 0); 341e3315b43STakashi Sakamoto } 342e3315b43STakashi Sakamoto 343e3315b43STakashi Sakamoto static void remove_scs1x(struct snd_rawmidi *rmidi) 344e3315b43STakashi Sakamoto { 345e3315b43STakashi Sakamoto struct fw_scs1x *scs = rmidi->private_data; 346e3315b43STakashi Sakamoto 347e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 348e3315b43STakashi Sakamoto } 349e3315b43STakashi Sakamoto 350e3315b43STakashi Sakamoto void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) 351e3315b43STakashi Sakamoto { 352e3315b43STakashi Sakamoto register_address(oxfw); 353e3315b43STakashi Sakamoto } 354e3315b43STakashi Sakamoto 3553f47152aSTakashi Sakamoto int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) 3563f47152aSTakashi Sakamoto { 3573f47152aSTakashi Sakamoto struct snd_rawmidi *rmidi; 358e3315b43STakashi Sakamoto struct fw_scs1x *scs; 3593f47152aSTakashi Sakamoto int err; 3603f47152aSTakashi Sakamoto 361e3315b43STakashi Sakamoto scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL); 362e3315b43STakashi Sakamoto if (scs == NULL) 363e3315b43STakashi Sakamoto return -ENOMEM; 364d7d20e77STakashi Sakamoto scs->fw_dev = fw_parent_device(oxfw->unit); 365e3315b43STakashi Sakamoto oxfw->spec = scs; 366e3315b43STakashi Sakamoto 367e3315b43STakashi Sakamoto /* Allocate own handler for imcoming asynchronous transaction. */ 368e3315b43STakashi Sakamoto scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE; 369e3315b43STakashi Sakamoto scs->hss_handler.address_callback = handle_hss; 370e3315b43STakashi Sakamoto scs->hss_handler.callback_data = scs; 371e3315b43STakashi Sakamoto err = fw_core_add_address_handler(&scs->hss_handler, 372e3315b43STakashi Sakamoto &fw_high_memory_region); 373e3315b43STakashi Sakamoto if (err < 0) 374e3315b43STakashi Sakamoto return err; 375e3315b43STakashi Sakamoto 376e3315b43STakashi Sakamoto err = register_address(oxfw); 377e3315b43STakashi Sakamoto if (err < 0) 378e3315b43STakashi Sakamoto goto err_allocated; 379e3315b43STakashi Sakamoto 3803f47152aSTakashi Sakamoto /* Use unique name for backward compatibility to scs1x module. */ 3816f5dcb28STakashi Sakamoto err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 1, 1, &rmidi); 3823f47152aSTakashi Sakamoto if (err < 0) 383e3315b43STakashi Sakamoto goto err_allocated; 384e3315b43STakashi Sakamoto rmidi->private_data = scs; 385e3315b43STakashi Sakamoto rmidi->private_free = remove_scs1x; 3863f47152aSTakashi Sakamoto 3873f47152aSTakashi Sakamoto snprintf(rmidi->name, sizeof(rmidi->name), 3883f47152aSTakashi Sakamoto "%s MIDI", oxfw->card->shortname); 3893f47152aSTakashi Sakamoto 3906f5dcb28STakashi Sakamoto rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT | 3916f5dcb28STakashi Sakamoto SNDRV_RAWMIDI_INFO_OUTPUT | 3926f5dcb28STakashi Sakamoto SNDRV_RAWMIDI_INFO_DUPLEX; 3938250427dSTakashi Sakamoto snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 3948250427dSTakashi Sakamoto &midi_capture_ops); 3956f5dcb28STakashi Sakamoto snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 3966f5dcb28STakashi Sakamoto &midi_playback_ops); 3978250427dSTakashi Sakamoto 398ea790053STakashi Sakamoto INIT_WORK(&scs->work, scs_output_work); 399d7d20e77STakashi Sakamoto init_waitqueue_head(&scs->idle_wait); 400d7d20e77STakashi Sakamoto scs->output_idle = true; 401d7d20e77STakashi Sakamoto 402e3315b43STakashi Sakamoto return 0; 403e3315b43STakashi Sakamoto err_allocated: 404e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 4053f47152aSTakashi Sakamoto return err; 4063f47152aSTakashi Sakamoto } 407