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 1098250427dSTakashi Sakamoto static int midi_capture_open(struct snd_rawmidi_substream *stream) 1108250427dSTakashi Sakamoto { 1118250427dSTakashi Sakamoto return 0; 1128250427dSTakashi Sakamoto } 1138250427dSTakashi Sakamoto 1148250427dSTakashi Sakamoto static int midi_capture_close(struct snd_rawmidi_substream *stream) 1158250427dSTakashi Sakamoto { 1168250427dSTakashi Sakamoto return 0; 1178250427dSTakashi Sakamoto } 1188250427dSTakashi Sakamoto 1198250427dSTakashi Sakamoto static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up) 1208250427dSTakashi Sakamoto { 1218250427dSTakashi Sakamoto struct fw_scs1x *scs = stream->rmidi->private_data; 1228250427dSTakashi Sakamoto 1238250427dSTakashi Sakamoto if (up) { 1248250427dSTakashi Sakamoto scs->input_escape_count = 0; 1258250427dSTakashi Sakamoto ACCESS_ONCE(scs->input) = stream; 1268250427dSTakashi Sakamoto } else { 1278250427dSTakashi Sakamoto ACCESS_ONCE(scs->input) = NULL; 1288250427dSTakashi Sakamoto } 1298250427dSTakashi Sakamoto } 1308250427dSTakashi Sakamoto 1318250427dSTakashi Sakamoto static struct snd_rawmidi_ops midi_capture_ops = { 1328250427dSTakashi Sakamoto .open = midi_capture_open, 1338250427dSTakashi Sakamoto .close = midi_capture_close, 1348250427dSTakashi Sakamoto .trigger = midi_capture_trigger, 1358250427dSTakashi Sakamoto }; 1368250427dSTakashi Sakamoto 137e3315b43STakashi Sakamoto static int register_address(struct snd_oxfw *oxfw) 138e3315b43STakashi Sakamoto { 139e3315b43STakashi Sakamoto struct fw_scs1x *scs = oxfw->spec; 140e3315b43STakashi Sakamoto __be64 data; 141e3315b43STakashi Sakamoto 142e3315b43STakashi Sakamoto data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | 143e3315b43STakashi Sakamoto scs->hss_handler.offset); 144e3315b43STakashi Sakamoto return snd_fw_transaction(oxfw->unit, TCODE_WRITE_BLOCK_REQUEST, 145e3315b43STakashi Sakamoto HSS1394_ADDRESS, &data, sizeof(data), 0); 146e3315b43STakashi Sakamoto } 147e3315b43STakashi Sakamoto 148e3315b43STakashi Sakamoto static void remove_scs1x(struct snd_rawmidi *rmidi) 149e3315b43STakashi Sakamoto { 150e3315b43STakashi Sakamoto struct fw_scs1x *scs = rmidi->private_data; 151e3315b43STakashi Sakamoto 152e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 153e3315b43STakashi Sakamoto } 154e3315b43STakashi Sakamoto 155e3315b43STakashi Sakamoto void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) 156e3315b43STakashi Sakamoto { 157e3315b43STakashi Sakamoto register_address(oxfw); 158e3315b43STakashi Sakamoto } 159e3315b43STakashi Sakamoto 1603f47152aSTakashi Sakamoto int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) 1613f47152aSTakashi Sakamoto { 1623f47152aSTakashi Sakamoto struct snd_rawmidi *rmidi; 163e3315b43STakashi Sakamoto struct fw_scs1x *scs; 1643f47152aSTakashi Sakamoto int err; 1653f47152aSTakashi Sakamoto 166e3315b43STakashi Sakamoto scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL); 167e3315b43STakashi Sakamoto if (scs == NULL) 168e3315b43STakashi Sakamoto return -ENOMEM; 169e3315b43STakashi Sakamoto oxfw->spec = scs; 170e3315b43STakashi Sakamoto 171e3315b43STakashi Sakamoto /* Allocate own handler for imcoming asynchronous transaction. */ 172e3315b43STakashi Sakamoto scs->hss_handler.length = HSS1394_MAX_PACKET_SIZE; 173e3315b43STakashi Sakamoto scs->hss_handler.address_callback = handle_hss; 174e3315b43STakashi Sakamoto scs->hss_handler.callback_data = scs; 175e3315b43STakashi Sakamoto err = fw_core_add_address_handler(&scs->hss_handler, 176e3315b43STakashi Sakamoto &fw_high_memory_region); 177e3315b43STakashi Sakamoto if (err < 0) 178e3315b43STakashi Sakamoto return err; 179e3315b43STakashi Sakamoto 180e3315b43STakashi Sakamoto err = register_address(oxfw); 181e3315b43STakashi Sakamoto if (err < 0) 182e3315b43STakashi Sakamoto goto err_allocated; 183e3315b43STakashi Sakamoto 1843f47152aSTakashi Sakamoto /* Use unique name for backward compatibility to scs1x module. */ 1858250427dSTakashi Sakamoto err = snd_rawmidi_new(oxfw->card, "SCS.1x", 0, 0, 1, &rmidi); 1863f47152aSTakashi Sakamoto if (err < 0) 187e3315b43STakashi Sakamoto goto err_allocated; 188e3315b43STakashi Sakamoto rmidi->private_data = scs; 189e3315b43STakashi Sakamoto rmidi->private_free = remove_scs1x; 1903f47152aSTakashi Sakamoto 1913f47152aSTakashi Sakamoto snprintf(rmidi->name, sizeof(rmidi->name), 1923f47152aSTakashi Sakamoto "%s MIDI", oxfw->card->shortname); 1933f47152aSTakashi Sakamoto 1948250427dSTakashi Sakamoto rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT; 1958250427dSTakashi Sakamoto snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 1968250427dSTakashi Sakamoto &midi_capture_ops); 1978250427dSTakashi Sakamoto 198e3315b43STakashi Sakamoto return 0; 199e3315b43STakashi Sakamoto err_allocated: 200e3315b43STakashi Sakamoto fw_core_remove_address_handler(&scs->hss_handler); 2013f47152aSTakashi Sakamoto return err; 2023f47152aSTakashi Sakamoto } 203