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