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; 21e3315b43STakashi Sakamoto }; 22e3315b43STakashi Sakamoto 2313b8b78cSTakashi Sakamoto static const u8 sysex_escape_prefix[] = { 2413b8b78cSTakashi Sakamoto 0xf0, /* SysEx begin */ 2513b8b78cSTakashi Sakamoto 0x00, 0x01, 0x60, /* Stanton DJ */ 2613b8b78cSTakashi Sakamoto 0x48, 0x53, 0x53, /* "HSS" */ 2713b8b78cSTakashi Sakamoto }; 2813b8b78cSTakashi Sakamoto 2913b8b78cSTakashi Sakamoto static void midi_input_escaped_byte(struct snd_rawmidi_substream *stream, 3013b8b78cSTakashi Sakamoto u8 byte) 3113b8b78cSTakashi Sakamoto { 3213b8b78cSTakashi Sakamoto u8 nibbles[2]; 3313b8b78cSTakashi Sakamoto 3413b8b78cSTakashi Sakamoto nibbles[0] = byte >> 4; 3513b8b78cSTakashi Sakamoto nibbles[1] = byte & 0x0f; 3613b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, nibbles, 2); 3713b8b78cSTakashi Sakamoto } 3813b8b78cSTakashi Sakamoto 3913b8b78cSTakashi Sakamoto static void midi_input_byte(struct fw_scs1x *scs, 4013b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream, u8 byte) 4113b8b78cSTakashi Sakamoto { 4213b8b78cSTakashi Sakamoto const u8 eox = 0xf7; 4313b8b78cSTakashi Sakamoto 4413b8b78cSTakashi Sakamoto if (scs->input_escape_count > 0) { 4513b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, byte); 4613b8b78cSTakashi Sakamoto scs->input_escape_count--; 4713b8b78cSTakashi Sakamoto if (scs->input_escape_count == 0) 4813b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &eox, sizeof(eox)); 4913b8b78cSTakashi Sakamoto } else if (byte == 0xf9) { 5013b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, sysex_escape_prefix, 5113b8b78cSTakashi Sakamoto ARRAY_SIZE(sysex_escape_prefix)); 5213b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, 0x00); 5313b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, 0xf9); 5413b8b78cSTakashi Sakamoto scs->input_escape_count = 3; 5513b8b78cSTakashi Sakamoto } else { 5613b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &byte, 1); 5713b8b78cSTakashi Sakamoto } 5813b8b78cSTakashi Sakamoto } 5913b8b78cSTakashi Sakamoto 6013b8b78cSTakashi Sakamoto static void midi_input_packet(struct fw_scs1x *scs, 6113b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream, 6213b8b78cSTakashi Sakamoto const u8 *data, unsigned int bytes) 6313b8b78cSTakashi Sakamoto { 6413b8b78cSTakashi Sakamoto unsigned int i; 6513b8b78cSTakashi Sakamoto const u8 eox = 0xf7; 6613b8b78cSTakashi Sakamoto 6713b8b78cSTakashi Sakamoto if (data[0] == HSS1394_TAG_USER_DATA) { 6813b8b78cSTakashi Sakamoto for (i = 1; i < bytes; ++i) 6913b8b78cSTakashi Sakamoto midi_input_byte(scs, stream, data[i]); 7013b8b78cSTakashi Sakamoto } else { 7113b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, sysex_escape_prefix, 7213b8b78cSTakashi Sakamoto ARRAY_SIZE(sysex_escape_prefix)); 7313b8b78cSTakashi Sakamoto for (i = 0; i < bytes; ++i) 7413b8b78cSTakashi Sakamoto midi_input_escaped_byte(stream, data[i]); 7513b8b78cSTakashi Sakamoto snd_rawmidi_receive(stream, &eox, sizeof(eox)); 7613b8b78cSTakashi Sakamoto } 7713b8b78cSTakashi Sakamoto } 7813b8b78cSTakashi Sakamoto 79e3315b43STakashi Sakamoto static void handle_hss(struct fw_card *card, struct fw_request *request, 80e3315b43STakashi Sakamoto int tcode, int destination, int source, int generation, 81e3315b43STakashi Sakamoto unsigned long long offset, void *data, size_t length, 82e3315b43STakashi Sakamoto void *callback_data) 83e3315b43STakashi Sakamoto { 8413b8b78cSTakashi Sakamoto struct fw_scs1x *scs = callback_data; 8513b8b78cSTakashi Sakamoto struct snd_rawmidi_substream *stream; 8613b8b78cSTakashi Sakamoto int rcode; 8713b8b78cSTakashi Sakamoto 8813b8b78cSTakashi Sakamoto if (offset != scs->hss_handler.offset) { 8913b8b78cSTakashi Sakamoto rcode = RCODE_ADDRESS_ERROR; 9013b8b78cSTakashi Sakamoto goto end; 9113b8b78cSTakashi Sakamoto } 9213b8b78cSTakashi Sakamoto if (tcode != TCODE_WRITE_QUADLET_REQUEST && 9313b8b78cSTakashi Sakamoto tcode != TCODE_WRITE_BLOCK_REQUEST) { 9413b8b78cSTakashi Sakamoto rcode = RCODE_TYPE_ERROR; 9513b8b78cSTakashi Sakamoto goto end; 9613b8b78cSTakashi Sakamoto } 9713b8b78cSTakashi Sakamoto 9813b8b78cSTakashi Sakamoto if (length >= 1) { 9913b8b78cSTakashi Sakamoto stream = ACCESS_ONCE(scs->input); 10013b8b78cSTakashi Sakamoto if (stream) 10113b8b78cSTakashi Sakamoto midi_input_packet(scs, stream, data, length); 10213b8b78cSTakashi Sakamoto } 10313b8b78cSTakashi Sakamoto 10413b8b78cSTakashi Sakamoto rcode = RCODE_COMPLETE; 10513b8b78cSTakashi Sakamoto end: 10613b8b78cSTakashi Sakamoto fw_send_response(card, request, rcode); 107e3315b43STakashi Sakamoto } 108e3315b43STakashi Sakamoto 109e3315b43STakashi Sakamoto static int register_address(struct snd_oxfw *oxfw) 110e3315b43STakashi Sakamoto { 111e3315b43STakashi Sakamoto struct fw_scs1x *scs = oxfw->spec; 112e3315b43STakashi Sakamoto __be64 data; 113e3315b43STakashi Sakamoto 114e3315b43STakashi Sakamoto data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | 115e3315b43STakashi Sakamoto scs->hss_handler.offset); 116e3315b43STakashi Sakamoto return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST, 117e3315b43STakashi Sakamoto HSS1394_ADDRESS, &data, sizeof(data), 0); 118e3315b43STakashi Sakamoto } 119e3315b43STakashi Sakamoto 120e3315b43STakashi Sakamoto static void remove_scs1x(struct snd_rawmidi *rmidi) 121e3315b43STakashi Sakamoto { 122e3315b43STakashi Sakamoto struct fw_scs1x *scs = rmidi->private_data; 123e3315b43STakashi Sakamoto 124e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 125e3315b43STakashi Sakamoto } 126e3315b43STakashi Sakamoto 127e3315b43STakashi Sakamoto void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) 128e3315b43STakashi Sakamoto { 129e3315b43STakashi Sakamoto register_address(oxfw); 130e3315b43STakashi Sakamoto } 131e3315b43STakashi Sakamoto 1323f47152aSTakashi Sakamoto int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) 1333f47152aSTakashi Sakamoto { 1343f47152aSTakashi Sakamoto struct snd_rawmidi *rmidi; 135e3315b43STakashi Sakamoto struct fw_scs1x *scs; 1363f47152aSTakashi Sakamoto int err; 1373f47152aSTakashi Sakamoto 138e3315b43STakashi Sakamoto scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL); 139e3315b43STakashi Sakamoto if (scs == NULL) 140e3315b43STakashi Sakamoto return -ENOMEM; 141e3315b43STakashi Sakamoto oxfw->spec = scs; 142e3315b43STakashi Sakamoto 143e3315b43STakashi Sakamoto /* Allocate own handler for imcoming asynchronous transaction. */ 144e3315b43STakashi Sakamoto scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE; 145e3315b43STakashi Sakamoto scs->hss_handler.address_callback = handle_hss; 146e3315b43STakashi Sakamoto scs->hss_handler.callback_data = scs; 147e3315b43STakashi Sakamoto err = fw_core_add_address_handler(&scs->hss_handler, 148e3315b43STakashi Sakamoto &fw_high_memory_region); 149e3315b43STakashi Sakamoto if (err < 0) 150e3315b43STakashi Sakamoto return err; 151e3315b43STakashi Sakamoto 152e3315b43STakashi Sakamoto err = register_address(oxfw); 153e3315b43STakashi Sakamoto if (err < 0) 154e3315b43STakashi Sakamoto goto err_allocated; 155e3315b43STakashi Sakamoto 1563f47152aSTakashi Sakamoto /* Use unique name for backward compatibility to scs1x module. */ 1573f47152aSTakashi Sakamoto err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 0, 0, &rmidi); 1583f47152aSTakashi Sakamoto if (err < 0) 159e3315b43STakashi Sakamoto goto err_allocated; 160e3315b43STakashi Sakamoto rmidi->private_data = scs; 161e3315b43STakashi Sakamoto rmidi->private_free = remove_scs1x; 1623f47152aSTakashi Sakamoto 1633f47152aSTakashi Sakamoto snprintf(rmidi->name, sizeof(rmidi->name), 1643f47152aSTakashi Sakamoto "%s MIDI", oxfw->card->shortname); 1653f47152aSTakashi Sakamoto 166e3315b43STakashi Sakamoto return 0; 167e3315b43STakashi Sakamoto err_allocated: 168e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 1693f47152aSTakashi Sakamoto return err; 1703f47152aSTakashi Sakamoto } 171