Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/tascam/tascam-transaction.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* tascam-transaction.c - a part of driver for TASCAM FireWire series
4
*
5
* Copyright (c) 2015 Takashi Sakamoto
6
*/
7
8
#include "tascam.h"
9
10
/*
11
* When return minus value, given argument is not MIDI status.
12
* When return 0, given argument is a beginning of system exclusive.
13
* When return the others, given argument is MIDI data.
14
*/
15
static inline int calculate_message_bytes(u8 status)
16
{
17
switch (status) {
18
case 0xf6: /* Tune request. */
19
case 0xf8: /* Timing clock. */
20
case 0xfa: /* Start. */
21
case 0xfb: /* Continue. */
22
case 0xfc: /* Stop. */
23
case 0xfe: /* Active sensing. */
24
case 0xff: /* System reset. */
25
return 1;
26
case 0xf1: /* MIDI time code quarter frame. */
27
case 0xf3: /* Song select. */
28
return 2;
29
case 0xf2: /* Song position pointer. */
30
return 3;
31
case 0xf0: /* Exclusive. */
32
return 0;
33
case 0xf7: /* End of exclusive. */
34
break;
35
case 0xf4: /* Undefined. */
36
case 0xf5: /* Undefined. */
37
case 0xf9: /* Undefined. */
38
case 0xfd: /* Undefined. */
39
break;
40
default:
41
switch (status & 0xf0) {
42
case 0x80: /* Note on. */
43
case 0x90: /* Note off. */
44
case 0xa0: /* Polyphonic key pressure. */
45
case 0xb0: /* Control change and Mode change. */
46
case 0xe0: /* Pitch bend change. */
47
return 3;
48
case 0xc0: /* Program change. */
49
case 0xd0: /* Channel pressure. */
50
return 2;
51
default:
52
break;
53
}
54
break;
55
}
56
57
return -EINVAL;
58
}
59
60
static int fill_message(struct snd_fw_async_midi_port *port,
61
struct snd_rawmidi_substream *substream)
62
{
63
int i, len, consume;
64
u8 *label, *msg;
65
u8 status;
66
67
/* The first byte is used for label, the rest for MIDI bytes. */
68
label = port->buf;
69
msg = port->buf + 1;
70
71
consume = snd_rawmidi_transmit_peek(substream, msg, 3);
72
if (consume == 0)
73
return 0;
74
75
/* On exclusive message. */
76
if (port->on_sysex) {
77
/* Seek the end of exclusives. */
78
for (i = 0; i < consume; ++i) {
79
if (msg[i] == 0xf7) {
80
port->on_sysex = false;
81
break;
82
}
83
}
84
85
/* At the end of exclusive message, use label 0x07. */
86
if (!port->on_sysex) {
87
consume = i + 1;
88
*label = (substream->number << 4) | 0x07;
89
/* During exclusive message, use label 0x04. */
90
} else if (consume == 3) {
91
*label = (substream->number << 4) | 0x04;
92
/* We need to fill whole 3 bytes. Go to next change. */
93
} else {
94
return 0;
95
}
96
97
len = consume;
98
} else {
99
/* The beginning of exclusives. */
100
if (msg[0] == 0xf0) {
101
/* Transfer it in next chance in another condition. */
102
port->on_sysex = true;
103
return 0;
104
} else {
105
/* On running-status. */
106
if ((msg[0] & 0x80) != 0x80)
107
status = port->running_status;
108
else
109
status = msg[0];
110
111
/* Calculate consume bytes. */
112
len = calculate_message_bytes(status);
113
if (len <= 0)
114
return 0;
115
116
/* On running-status. */
117
if ((msg[0] & 0x80) != 0x80) {
118
/* Enough MIDI bytes were not retrieved. */
119
if (consume < len - 1)
120
return 0;
121
consume = len - 1;
122
123
msg[2] = msg[1];
124
msg[1] = msg[0];
125
msg[0] = port->running_status;
126
} else {
127
/* Enough MIDI bytes were not retrieved. */
128
if (consume < len)
129
return 0;
130
consume = len;
131
132
port->running_status = msg[0];
133
}
134
}
135
136
*label = (substream->number << 4) | (msg[0] >> 4);
137
}
138
139
if (len > 0 && len < 3)
140
memset(msg + len, 0, 3 - len);
141
142
return consume;
143
}
144
145
static void async_midi_port_callback(struct fw_card *card, int rcode,
146
void *data, size_t length,
147
void *callback_data)
148
{
149
struct snd_fw_async_midi_port *port = callback_data;
150
struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
151
152
/* This port is closed. */
153
if (substream == NULL)
154
return;
155
156
if (rcode == RCODE_COMPLETE)
157
snd_rawmidi_transmit_ack(substream, port->consume_bytes);
158
else if (!rcode_is_permanent_error(rcode))
159
/* To start next transaction immediately for recovery. */
160
port->next_ktime = 0;
161
else
162
/* Don't continue processing. */
163
port->error = true;
164
165
port->idling = true;
166
167
if (!snd_rawmidi_transmit_empty(substream))
168
schedule_work(&port->work);
169
}
170
171
static void midi_port_work(struct work_struct *work)
172
{
173
struct snd_fw_async_midi_port *port =
174
container_of(work, struct snd_fw_async_midi_port, work);
175
struct snd_rawmidi_substream *substream = READ_ONCE(port->substream);
176
int generation;
177
178
/* Under transacting or error state. */
179
if (!port->idling || port->error)
180
return;
181
182
/* Nothing to do. */
183
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
184
return;
185
186
/* Do it in next chance. */
187
if (ktime_after(port->next_ktime, ktime_get())) {
188
schedule_work(&port->work);
189
return;
190
}
191
192
/*
193
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
194
* Later, snd_rawmidi_transmit_ack() is called.
195
*/
196
memset(port->buf, 0, 4);
197
port->consume_bytes = fill_message(port, substream);
198
if (port->consume_bytes <= 0) {
199
/* Do it in next chance, immediately. */
200
if (port->consume_bytes == 0) {
201
port->next_ktime = 0;
202
schedule_work(&port->work);
203
} else {
204
/* Fatal error. */
205
port->error = true;
206
}
207
return;
208
}
209
210
/* Set interval to next transaction. */
211
port->next_ktime = ktime_add_ns(ktime_get(),
212
port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
213
214
/* Start this transaction. */
215
port->idling = false;
216
217
/*
218
* In Linux FireWire core, when generation is updated with memory
219
* barrier, node id has already been updated. In this module, After
220
* this smp_rmb(), load/store instructions to memory are completed.
221
* Thus, both of generation and node id are available with recent
222
* values. This is a light-serialization solution to handle bus reset
223
* events on IEEE 1394 bus.
224
*/
225
generation = port->parent->generation;
226
smp_rmb();
227
228
fw_send_request(port->parent->card, &port->transaction,
229
TCODE_WRITE_QUADLET_REQUEST,
230
port->parent->node_id, generation,
231
port->parent->max_speed,
232
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
233
port->buf, 4, async_midi_port_callback,
234
port);
235
}
236
237
void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
238
{
239
port->idling = true;
240
port->error = false;
241
port->running_status = 0;
242
port->on_sysex = false;
243
}
244
245
static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
246
int tcode, int destination, int source,
247
int generation, unsigned long long offset,
248
void *data, size_t length, void *callback_data)
249
{
250
struct snd_tscm *tscm = callback_data;
251
u32 *buf = (u32 *)data;
252
unsigned int messages;
253
unsigned int i;
254
unsigned int port;
255
struct snd_rawmidi_substream *substream;
256
u8 *b;
257
int bytes;
258
259
if (offset != tscm->async_handler.offset)
260
goto end;
261
262
messages = length / 8;
263
for (i = 0; i < messages; i++) {
264
b = (u8 *)(buf + i * 2);
265
266
port = b[0] >> 4;
267
/* TODO: support virtual MIDI ports. */
268
if (port >= tscm->spec->midi_capture_ports)
269
goto end;
270
271
/* Assume the message length. */
272
bytes = calculate_message_bytes(b[1]);
273
/* On MIDI data or exclusives. */
274
if (bytes <= 0) {
275
/* Seek the end of exclusives. */
276
for (bytes = 1; bytes < 4; bytes++) {
277
if (b[bytes] == 0xf7)
278
break;
279
}
280
if (bytes == 4)
281
bytes = 3;
282
}
283
284
substream = READ_ONCE(tscm->tx_midi_substreams[port]);
285
if (substream != NULL)
286
snd_rawmidi_receive(substream, b + 1, bytes);
287
}
288
end:
289
fw_send_response(card, request, RCODE_COMPLETE);
290
}
291
292
int snd_tscm_transaction_register(struct snd_tscm *tscm)
293
{
294
static const struct fw_address_region resp_register_region = {
295
.start = 0xffffe0000000ull,
296
.end = 0xffffe000ffffull,
297
};
298
unsigned int i;
299
int err;
300
301
/*
302
* Usually, two quadlets are transferred by one transaction. The first
303
* quadlet has MIDI messages, the rest includes timestamp.
304
* Sometimes, 8 set of the data is transferred by a block transaction.
305
*/
306
tscm->async_handler.length = 8 * 8;
307
tscm->async_handler.address_callback = handle_midi_tx;
308
tscm->async_handler.callback_data = tscm;
309
310
err = fw_core_add_address_handler(&tscm->async_handler,
311
&resp_register_region);
312
if (err < 0)
313
return err;
314
315
err = snd_tscm_transaction_reregister(tscm);
316
if (err < 0)
317
goto error;
318
319
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
320
tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
321
tscm->out_ports[i].next_ktime = 0;
322
INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
323
}
324
325
return err;
326
error:
327
fw_core_remove_address_handler(&tscm->async_handler);
328
tscm->async_handler.callback_data = NULL;
329
return err;
330
}
331
332
/* At bus reset, these registers are cleared. */
333
int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
334
{
335
struct fw_device *device = fw_parent_device(tscm->unit);
336
__be32 reg;
337
int err;
338
339
/* Register messaging address. Block transaction is not allowed. */
340
reg = cpu_to_be32((device->card->node_id << 16) |
341
(tscm->async_handler.offset >> 32));
342
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
343
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
344
&reg, sizeof(reg), 0);
345
if (err < 0)
346
return err;
347
348
reg = cpu_to_be32(tscm->async_handler.offset);
349
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
350
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
351
&reg, sizeof(reg), 0);
352
if (err < 0)
353
return err;
354
355
/* Turn on messaging. */
356
reg = cpu_to_be32(0x00000001);
357
err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
358
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
359
&reg, sizeof(reg), 0);
360
if (err < 0)
361
return err;
362
363
/* Turn on FireWire LED. */
364
reg = cpu_to_be32(0x0001008e);
365
return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
366
TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
367
&reg, sizeof(reg), 0);
368
}
369
370
void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
371
{
372
__be32 reg;
373
374
if (tscm->async_handler.callback_data == NULL)
375
return;
376
377
/* Turn off FireWire LED. */
378
reg = cpu_to_be32(0x0000008e);
379
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
380
TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
381
&reg, sizeof(reg), 0);
382
383
/* Turn off messaging. */
384
reg = cpu_to_be32(0x00000000);
385
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
386
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
387
&reg, sizeof(reg), 0);
388
389
/* Unregister the address. */
390
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
391
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
392
&reg, sizeof(reg), 0);
393
snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
394
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
395
&reg, sizeof(reg), 0);
396
397
fw_core_remove_address_handler(&tscm->async_handler);
398
tscm->async_handler.callback_data = NULL;
399
}
400
401