1 /* 2 * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family 3 * 4 * Copyright (c) 2014-2015 Takashi Sakamoto 5 * 6 * Licensed under the terms of the GNU General Public License, version 2. 7 */ 8 9 #include <sound/asound.h> 10 #include "digi00x.h" 11 12 static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf) 13 { 14 int bytes; 15 16 buf[0] = 0x80; 17 bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2); 18 if (bytes >= 0) 19 buf[3] = 0xc0 | bytes; 20 21 return bytes; 22 } 23 24 static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf, 25 unsigned int length) 26 { 27 struct snd_rawmidi_substream *substream; 28 unsigned int i; 29 unsigned int len; 30 u8 *b; 31 32 substream = ACCESS_ONCE(dg00x->in_control); 33 if (substream == NULL) 34 return; 35 36 length /= 4; 37 38 for (i = 0; i < length; i++) { 39 b = (u8 *)&buf[i]; 40 len = b[3] & 0xf; 41 if (len > 0) 42 snd_rawmidi_receive(dg00x->in_control, b + 1, len); 43 } 44 } 45 46 static void handle_unknown_message(struct snd_dg00x *dg00x, 47 unsigned long long offset, __be32 *buf) 48 { 49 unsigned long flags; 50 51 spin_lock_irqsave(&dg00x->lock, flags); 52 dg00x->msg = be32_to_cpu(*buf); 53 spin_unlock_irqrestore(&dg00x->lock, flags); 54 55 wake_up(&dg00x->hwdep_wait); 56 } 57 58 static void handle_message(struct fw_card *card, struct fw_request *request, 59 int tcode, int destination, int source, 60 int generation, unsigned long long offset, 61 void *data, size_t length, void *callback_data) 62 { 63 struct snd_dg00x *dg00x = callback_data; 64 __be32 *buf = (__be32 *)data; 65 66 if (offset == dg00x->async_handler.offset) 67 handle_unknown_message(dg00x, offset, buf); 68 else if (offset == dg00x->async_handler.offset + 4) 69 handle_midi_control(dg00x, buf, length); 70 71 fw_send_response(card, request, RCODE_COMPLETE); 72 } 73 74 int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x) 75 { 76 struct fw_device *device = fw_parent_device(dg00x->unit); 77 __be32 data[2]; 78 int err; 79 80 /* Unknown. 4bytes. */ 81 data[0] = cpu_to_be32((device->card->node_id << 16) | 82 (dg00x->async_handler.offset >> 32)); 83 data[1] = cpu_to_be32(dg00x->async_handler.offset); 84 err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, 85 DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR, 86 &data, sizeof(data), 0); 87 if (err < 0) 88 return err; 89 90 /* Asynchronous transactions for MIDI control message. */ 91 data[0] = cpu_to_be32((device->card->node_id << 16) | 92 (dg00x->async_handler.offset >> 32)); 93 data[1] = cpu_to_be32(dg00x->async_handler.offset + 4); 94 return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST, 95 DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR, 96 &data, sizeof(data), 0); 97 } 98 99 int snd_dg00x_transaction_register(struct snd_dg00x *dg00x) 100 { 101 static const struct fw_address_region resp_register_region = { 102 .start = 0xffffe0000000ull, 103 .end = 0xffffe000ffffull, 104 }; 105 int err; 106 107 dg00x->async_handler.length = 12; 108 dg00x->async_handler.address_callback = handle_message; 109 dg00x->async_handler.callback_data = dg00x; 110 111 err = fw_core_add_address_handler(&dg00x->async_handler, 112 &resp_register_region); 113 if (err < 0) 114 return err; 115 116 err = snd_dg00x_transaction_reregister(dg00x); 117 if (err < 0) 118 goto error; 119 120 err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit, 121 DG00X_ADDR_BASE + DG00X_OFFSET_MMC, 122 4, fill_midi_message); 123 if (err < 0) 124 goto error; 125 126 return err; 127 error: 128 fw_core_remove_address_handler(&dg00x->async_handler); 129 dg00x->async_handler.callback_data = NULL; 130 return err; 131 } 132 133 void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x) 134 { 135 if (dg00x->async_handler.callback_data == NULL) 136 return; 137 138 snd_fw_async_midi_port_destroy(&dg00x->out_control); 139 fw_core_remove_address_handler(&dg00x->async_handler); 140 141 dg00x->async_handler.callback_data = NULL; 142 } 143