18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * tascam-transaction.c - a part of driver for TASCAM FireWire series 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Takashi Sakamoto 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "tascam.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * When return minus value, given argument is not MIDI status. 128c2ecf20Sopenharmony_ci * When return 0, given argument is a beginning of system exclusive. 138c2ecf20Sopenharmony_ci * When return the others, given argument is MIDI data. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_cistatic inline int calculate_message_bytes(u8 status) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci switch (status) { 188c2ecf20Sopenharmony_ci case 0xf6: /* Tune request. */ 198c2ecf20Sopenharmony_ci case 0xf8: /* Timing clock. */ 208c2ecf20Sopenharmony_ci case 0xfa: /* Start. */ 218c2ecf20Sopenharmony_ci case 0xfb: /* Continue. */ 228c2ecf20Sopenharmony_ci case 0xfc: /* Stop. */ 238c2ecf20Sopenharmony_ci case 0xfe: /* Active sensing. */ 248c2ecf20Sopenharmony_ci case 0xff: /* System reset. */ 258c2ecf20Sopenharmony_ci return 1; 268c2ecf20Sopenharmony_ci case 0xf1: /* MIDI time code quarter frame. */ 278c2ecf20Sopenharmony_ci case 0xf3: /* Song select. */ 288c2ecf20Sopenharmony_ci return 2; 298c2ecf20Sopenharmony_ci case 0xf2: /* Song position pointer. */ 308c2ecf20Sopenharmony_ci return 3; 318c2ecf20Sopenharmony_ci case 0xf0: /* Exclusive. */ 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci case 0xf7: /* End of exclusive. */ 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case 0xf4: /* Undefined. */ 368c2ecf20Sopenharmony_ci case 0xf5: /* Undefined. */ 378c2ecf20Sopenharmony_ci case 0xf9: /* Undefined. */ 388c2ecf20Sopenharmony_ci case 0xfd: /* Undefined. */ 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci default: 418c2ecf20Sopenharmony_ci switch (status & 0xf0) { 428c2ecf20Sopenharmony_ci case 0x80: /* Note on. */ 438c2ecf20Sopenharmony_ci case 0x90: /* Note off. */ 448c2ecf20Sopenharmony_ci case 0xa0: /* Polyphonic key pressure. */ 458c2ecf20Sopenharmony_ci case 0xb0: /* Control change and Mode change. */ 468c2ecf20Sopenharmony_ci case 0xe0: /* Pitch bend change. */ 478c2ecf20Sopenharmony_ci return 3; 488c2ecf20Sopenharmony_ci case 0xc0: /* Program change. */ 498c2ecf20Sopenharmony_ci case 0xd0: /* Channel pressure. */ 508c2ecf20Sopenharmony_ci return 2; 518c2ecf20Sopenharmony_ci default: 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return -EINVAL; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int fill_message(struct snd_fw_async_midi_port *port, 618c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int i, len, consume; 648c2ecf20Sopenharmony_ci u8 *label, *msg; 658c2ecf20Sopenharmony_ci u8 status; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* The first byte is used for label, the rest for MIDI bytes. */ 688c2ecf20Sopenharmony_ci label = port->buf; 698c2ecf20Sopenharmony_ci msg = port->buf + 1; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci consume = snd_rawmidi_transmit_peek(substream, msg, 3); 728c2ecf20Sopenharmony_ci if (consume == 0) 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* On exclusive message. */ 768c2ecf20Sopenharmony_ci if (port->on_sysex) { 778c2ecf20Sopenharmony_ci /* Seek the end of exclusives. */ 788c2ecf20Sopenharmony_ci for (i = 0; i < consume; ++i) { 798c2ecf20Sopenharmony_ci if (msg[i] == 0xf7) { 808c2ecf20Sopenharmony_ci port->on_sysex = false; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* At the end of exclusive message, use label 0x07. */ 868c2ecf20Sopenharmony_ci if (!port->on_sysex) { 878c2ecf20Sopenharmony_ci consume = i + 1; 888c2ecf20Sopenharmony_ci *label = (substream->number << 4) | 0x07; 898c2ecf20Sopenharmony_ci /* During exclusive message, use label 0x04. */ 908c2ecf20Sopenharmony_ci } else if (consume == 3) { 918c2ecf20Sopenharmony_ci *label = (substream->number << 4) | 0x04; 928c2ecf20Sopenharmony_ci /* We need to fill whole 3 bytes. Go to next change. */ 938c2ecf20Sopenharmony_ci } else { 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci len = consume; 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci /* The beginning of exclusives. */ 1008c2ecf20Sopenharmony_ci if (msg[0] == 0xf0) { 1018c2ecf20Sopenharmony_ci /* Transfer it in next chance in another condition. */ 1028c2ecf20Sopenharmony_ci port->on_sysex = true; 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci } else { 1058c2ecf20Sopenharmony_ci /* On running-status. */ 1068c2ecf20Sopenharmony_ci if ((msg[0] & 0x80) != 0x80) 1078c2ecf20Sopenharmony_ci status = port->running_status; 1088c2ecf20Sopenharmony_ci else 1098c2ecf20Sopenharmony_ci status = msg[0]; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* Calculate consume bytes. */ 1128c2ecf20Sopenharmony_ci len = calculate_message_bytes(status); 1138c2ecf20Sopenharmony_ci if (len <= 0) 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* On running-status. */ 1178c2ecf20Sopenharmony_ci if ((msg[0] & 0x80) != 0x80) { 1188c2ecf20Sopenharmony_ci /* Enough MIDI bytes were not retrieved. */ 1198c2ecf20Sopenharmony_ci if (consume < len - 1) 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci consume = len - 1; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci msg[2] = msg[1]; 1248c2ecf20Sopenharmony_ci msg[1] = msg[0]; 1258c2ecf20Sopenharmony_ci msg[0] = port->running_status; 1268c2ecf20Sopenharmony_ci } else { 1278c2ecf20Sopenharmony_ci /* Enough MIDI bytes were not retrieved. */ 1288c2ecf20Sopenharmony_ci if (consume < len) 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci consume = len; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci port->running_status = msg[0]; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci *label = (substream->number << 4) | (msg[0] >> 4); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (len > 0 && len < 3) 1408c2ecf20Sopenharmony_ci memset(msg + len, 0, 3 - len); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return consume; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void async_midi_port_callback(struct fw_card *card, int rcode, 1468c2ecf20Sopenharmony_ci void *data, size_t length, 1478c2ecf20Sopenharmony_ci void *callback_data) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct snd_fw_async_midi_port *port = callback_data; 1508c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream = READ_ONCE(port->substream); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* This port is closed. */ 1538c2ecf20Sopenharmony_ci if (substream == NULL) 1548c2ecf20Sopenharmony_ci return; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (rcode == RCODE_COMPLETE) 1578c2ecf20Sopenharmony_ci snd_rawmidi_transmit_ack(substream, port->consume_bytes); 1588c2ecf20Sopenharmony_ci else if (!rcode_is_permanent_error(rcode)) 1598c2ecf20Sopenharmony_ci /* To start next transaction immediately for recovery. */ 1608c2ecf20Sopenharmony_ci port->next_ktime = 0; 1618c2ecf20Sopenharmony_ci else 1628c2ecf20Sopenharmony_ci /* Don't continue processing. */ 1638c2ecf20Sopenharmony_ci port->error = true; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci port->idling = true; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!snd_rawmidi_transmit_empty(substream)) 1688c2ecf20Sopenharmony_ci schedule_work(&port->work); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void midi_port_work(struct work_struct *work) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct snd_fw_async_midi_port *port = 1748c2ecf20Sopenharmony_ci container_of(work, struct snd_fw_async_midi_port, work); 1758c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream = READ_ONCE(port->substream); 1768c2ecf20Sopenharmony_ci int generation; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Under transacting or error state. */ 1798c2ecf20Sopenharmony_ci if (!port->idling || port->error) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Nothing to do. */ 1838c2ecf20Sopenharmony_ci if (substream == NULL || snd_rawmidi_transmit_empty(substream)) 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Do it in next chance. */ 1878c2ecf20Sopenharmony_ci if (ktime_after(port->next_ktime, ktime_get())) { 1888c2ecf20Sopenharmony_ci schedule_work(&port->work); 1898c2ecf20Sopenharmony_ci return; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Fill the buffer. The callee must use snd_rawmidi_transmit_peek(). 1948c2ecf20Sopenharmony_ci * Later, snd_rawmidi_transmit_ack() is called. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci memset(port->buf, 0, 4); 1978c2ecf20Sopenharmony_ci port->consume_bytes = fill_message(port, substream); 1988c2ecf20Sopenharmony_ci if (port->consume_bytes <= 0) { 1998c2ecf20Sopenharmony_ci /* Do it in next chance, immediately. */ 2008c2ecf20Sopenharmony_ci if (port->consume_bytes == 0) { 2018c2ecf20Sopenharmony_ci port->next_ktime = 0; 2028c2ecf20Sopenharmony_ci schedule_work(&port->work); 2038c2ecf20Sopenharmony_ci } else { 2048c2ecf20Sopenharmony_ci /* Fatal error. */ 2058c2ecf20Sopenharmony_ci port->error = true; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci return; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Set interval to next transaction. */ 2118c2ecf20Sopenharmony_ci port->next_ktime = ktime_add_ns(ktime_get(), 2128c2ecf20Sopenharmony_ci port->consume_bytes * 8 * (NSEC_PER_SEC / 31250)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Start this transaction. */ 2158c2ecf20Sopenharmony_ci port->idling = false; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * In Linux FireWire core, when generation is updated with memory 2198c2ecf20Sopenharmony_ci * barrier, node id has already been updated. In this module, After 2208c2ecf20Sopenharmony_ci * this smp_rmb(), load/store instructions to memory are completed. 2218c2ecf20Sopenharmony_ci * Thus, both of generation and node id are available with recent 2228c2ecf20Sopenharmony_ci * values. This is a light-serialization solution to handle bus reset 2238c2ecf20Sopenharmony_ci * events on IEEE 1394 bus. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci generation = port->parent->generation; 2268c2ecf20Sopenharmony_ci smp_rmb(); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci fw_send_request(port->parent->card, &port->transaction, 2298c2ecf20Sopenharmony_ci TCODE_WRITE_QUADLET_REQUEST, 2308c2ecf20Sopenharmony_ci port->parent->node_id, generation, 2318c2ecf20Sopenharmony_ci port->parent->max_speed, 2328c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD, 2338c2ecf20Sopenharmony_ci port->buf, 4, async_midi_port_callback, 2348c2ecf20Sopenharmony_ci port); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_civoid snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci port->idling = true; 2408c2ecf20Sopenharmony_ci port->error = false; 2418c2ecf20Sopenharmony_ci port->running_status = 0; 2428c2ecf20Sopenharmony_ci port->on_sysex = false; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic void handle_midi_tx(struct fw_card *card, struct fw_request *request, 2468c2ecf20Sopenharmony_ci int tcode, int destination, int source, 2478c2ecf20Sopenharmony_ci int generation, unsigned long long offset, 2488c2ecf20Sopenharmony_ci void *data, size_t length, void *callback_data) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct snd_tscm *tscm = callback_data; 2518c2ecf20Sopenharmony_ci u32 *buf = (u32 *)data; 2528c2ecf20Sopenharmony_ci unsigned int messages; 2538c2ecf20Sopenharmony_ci unsigned int i; 2548c2ecf20Sopenharmony_ci unsigned int port; 2558c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream; 2568c2ecf20Sopenharmony_ci u8 *b; 2578c2ecf20Sopenharmony_ci int bytes; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (offset != tscm->async_handler.offset) 2608c2ecf20Sopenharmony_ci goto end; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci messages = length / 8; 2638c2ecf20Sopenharmony_ci for (i = 0; i < messages; i++) { 2648c2ecf20Sopenharmony_ci b = (u8 *)(buf + i * 2); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci port = b[0] >> 4; 2678c2ecf20Sopenharmony_ci /* TODO: support virtual MIDI ports. */ 2688c2ecf20Sopenharmony_ci if (port >= tscm->spec->midi_capture_ports) 2698c2ecf20Sopenharmony_ci goto end; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Assume the message length. */ 2728c2ecf20Sopenharmony_ci bytes = calculate_message_bytes(b[1]); 2738c2ecf20Sopenharmony_ci /* On MIDI data or exclusives. */ 2748c2ecf20Sopenharmony_ci if (bytes <= 0) { 2758c2ecf20Sopenharmony_ci /* Seek the end of exclusives. */ 2768c2ecf20Sopenharmony_ci for (bytes = 1; bytes < 4; bytes++) { 2778c2ecf20Sopenharmony_ci if (b[bytes] == 0xf7) 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci if (bytes == 4) 2818c2ecf20Sopenharmony_ci bytes = 3; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci substream = READ_ONCE(tscm->tx_midi_substreams[port]); 2858c2ecf20Sopenharmony_ci if (substream != NULL) 2868c2ecf20Sopenharmony_ci snd_rawmidi_receive(substream, b + 1, bytes); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ciend: 2898c2ecf20Sopenharmony_ci fw_send_response(card, request, RCODE_COMPLETE); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ciint snd_tscm_transaction_register(struct snd_tscm *tscm) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci static const struct fw_address_region resp_register_region = { 2958c2ecf20Sopenharmony_ci .start = 0xffffe0000000ull, 2968c2ecf20Sopenharmony_ci .end = 0xffffe000ffffull, 2978c2ecf20Sopenharmony_ci }; 2988c2ecf20Sopenharmony_ci unsigned int i; 2998c2ecf20Sopenharmony_ci int err; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* 3028c2ecf20Sopenharmony_ci * Usually, two quadlets are transferred by one transaction. The first 3038c2ecf20Sopenharmony_ci * quadlet has MIDI messages, the rest includes timestamp. 3048c2ecf20Sopenharmony_ci * Sometimes, 8 set of the data is transferred by a block transaction. 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_ci tscm->async_handler.length = 8 * 8; 3078c2ecf20Sopenharmony_ci tscm->async_handler.address_callback = handle_midi_tx; 3088c2ecf20Sopenharmony_ci tscm->async_handler.callback_data = tscm; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci err = fw_core_add_address_handler(&tscm->async_handler, 3118c2ecf20Sopenharmony_ci &resp_register_region); 3128c2ecf20Sopenharmony_ci if (err < 0) 3138c2ecf20Sopenharmony_ci return err; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci err = snd_tscm_transaction_reregister(tscm); 3168c2ecf20Sopenharmony_ci if (err < 0) 3178c2ecf20Sopenharmony_ci goto error; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) { 3208c2ecf20Sopenharmony_ci tscm->out_ports[i].parent = fw_parent_device(tscm->unit); 3218c2ecf20Sopenharmony_ci tscm->out_ports[i].next_ktime = 0; 3228c2ecf20Sopenharmony_ci INIT_WORK(&tscm->out_ports[i].work, midi_port_work); 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return err; 3268c2ecf20Sopenharmony_cierror: 3278c2ecf20Sopenharmony_ci fw_core_remove_address_handler(&tscm->async_handler); 3288c2ecf20Sopenharmony_ci tscm->async_handler.callback_data = NULL; 3298c2ecf20Sopenharmony_ci return err; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* At bus reset, these registers are cleared. */ 3338c2ecf20Sopenharmony_ciint snd_tscm_transaction_reregister(struct snd_tscm *tscm) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct fw_device *device = fw_parent_device(tscm->unit); 3368c2ecf20Sopenharmony_ci __be32 reg; 3378c2ecf20Sopenharmony_ci int err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Register messaging address. Block transaction is not allowed. */ 3408c2ecf20Sopenharmony_ci reg = cpu_to_be32((device->card->node_id << 16) | 3418c2ecf20Sopenharmony_ci (tscm->async_handler.offset >> 32)); 3428c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3438c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI, 3448c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3458c2ecf20Sopenharmony_ci if (err < 0) 3468c2ecf20Sopenharmony_ci return err; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci reg = cpu_to_be32(tscm->async_handler.offset); 3498c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3508c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO, 3518c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3528c2ecf20Sopenharmony_ci if (err < 0) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Turn on messaging. */ 3568c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000001); 3578c2ecf20Sopenharmony_ci err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3588c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, 3598c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3608c2ecf20Sopenharmony_ci if (err < 0) 3618c2ecf20Sopenharmony_ci return err; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Turn on FireWire LED. */ 3648c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x0001008e); 3658c2ecf20Sopenharmony_ci return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3668c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER, 3678c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_civoid snd_tscm_transaction_unregister(struct snd_tscm *tscm) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci __be32 reg; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (tscm->async_handler.callback_data == NULL) 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Turn off FireWire LED. */ 3788c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x0000008e); 3798c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3808c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER, 3818c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Turn off messaging. */ 3848c2ecf20Sopenharmony_ci reg = cpu_to_be32(0x00000000); 3858c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3868c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON, 3878c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Unregister the address. */ 3908c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3918c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI, 3928c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3938c2ecf20Sopenharmony_ci snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, 3948c2ecf20Sopenharmony_ci TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO, 3958c2ecf20Sopenharmony_ci ®, sizeof(reg), 0); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci fw_core_remove_address_handler(&tscm->async_handler); 3988c2ecf20Sopenharmony_ci tscm->async_handler.callback_data = NULL; 3998c2ecf20Sopenharmony_ci} 400