summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2017-04-14 03:46:25 -0400
committerTakashi Iwai <tiwai@suse.de>2017-04-14 08:50:26 -0400
commit531f471834227d0321110c071ea352bb14aca36d (patch)
treecbe05e4169d6fbe7d832f6b43bb7a478f75f19d4
parent1900d947b5ebac3c9f67d45b30f1b002131b8057 (diff)
ALSA: firewire-lib/firewire-tascam: localize async midi port
In Linux kernel 4.4, firewire-lib got a feature called as 'async midi port' for transmission of MIDI message via IEEE 1394 asynchronous communication, however actual consumer of this feature is ALSA driver for TASCAM FireWire series only. When adding this feature, I assumed that ALSA driver for Digi00x might also be a consumer, actually it's not. This commit moves the feature from firewire-lib to firewire-tascam module. Two minor kernel APIs are removed. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/firewire/lib.c141
-rw-r--r--sound/firewire/lib.h54
-rw-r--r--sound/firewire/tascam/tascam-transaction.c125
-rw-r--r--sound/firewire/tascam/tascam.h45
4 files changed, 170 insertions, 195 deletions
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 7683238283b6..39dfa74906ef 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -99,147 +99,6 @@ void snd_fw_schedule_registration(struct fw_unit *unit,
99} 99}
100EXPORT_SYMBOL(snd_fw_schedule_registration); 100EXPORT_SYMBOL(snd_fw_schedule_registration);
101 101
102static void async_midi_port_callback(struct fw_card *card, int rcode,
103 void *data, size_t length,
104 void *callback_data)
105{
106 struct snd_fw_async_midi_port *port = callback_data;
107 struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
108
109 /* This port is closed. */
110 if (substream == NULL)
111 return;
112
113 if (rcode == RCODE_COMPLETE)
114 snd_rawmidi_transmit_ack(substream, port->consume_bytes);
115 else if (!rcode_is_permanent_error(rcode))
116 /* To start next transaction immediately for recovery. */
117 port->next_ktime = 0;
118 else
119 /* Don't continue processing. */
120 port->error = true;
121
122 port->idling = true;
123
124 if (!snd_rawmidi_transmit_empty(substream))
125 schedule_work(&port->work);
126}
127
128static void midi_port_work(struct work_struct *work)
129{
130 struct snd_fw_async_midi_port *port =
131 container_of(work, struct snd_fw_async_midi_port, work);
132 struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
133 int generation;
134 int type;
135
136 /* Under transacting or error state. */
137 if (!port->idling || port->error)
138 return;
139
140 /* Nothing to do. */
141 if (substream == NULL || snd_rawmidi_transmit_empty(substream))
142 return;
143
144 /* Do it in next chance. */
145 if (ktime_after(port->next_ktime, ktime_get())) {
146 schedule_work(&port->work);
147 return;
148 }
149
150 /*
151 * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
152 * Later, snd_rawmidi_transmit_ack() is called.
153 */
154 memset(port->buf, 0, port->len);
155 port->consume_bytes = port->fill(substream, port->buf);
156 if (port->consume_bytes <= 0) {
157 /* Do it in next chance, immediately. */
158 if (port->consume_bytes == 0) {
159 port->next_ktime = 0;
160 schedule_work(&port->work);
161 } else {
162 /* Fatal error. */
163 port->error = true;
164 }
165 return;
166 }
167
168 /* Calculate type of transaction. */
169 if (port->len == 4)
170 type = TCODE_WRITE_QUADLET_REQUEST;
171 else
172 type = TCODE_WRITE_BLOCK_REQUEST;
173
174 /* Set interval to next transaction. */
175 port->next_ktime = ktime_add_ns(ktime_get(),
176 port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
177
178 /* Start this transaction. */
179 port->idling = false;
180
181 /*
182 * In Linux FireWire core, when generation is updated with memory
183 * barrier, node id has already been updated. In this module, After
184 * this smp_rmb(), load/store instructions to memory are completed.
185 * Thus, both of generation and node id are available with recent
186 * values. This is a light-serialization solution to handle bus reset
187 * events on IEEE 1394 bus.
188 */
189 generation = port->parent->generation;
190 smp_rmb();
191
192 fw_send_request(port->parent->card, &port->transaction, type,
193 port->parent->node_id, generation,
194 port->parent->max_speed, port->addr,
195 port->buf, port->len, async_midi_port_callback,
196 port);
197}
198
199/**
200 * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
201 * @port: the asynchronous MIDI port to initialize
202 * @unit: the target of the asynchronous transaction
203 * @addr: the address to which transactions are transferred
204 * @len: the length of transaction
205 * @fill: the callback function to fill given buffer, and returns the
206 * number of consumed bytes for MIDI message.
207 *
208 */
209int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
210 struct fw_unit *unit, u64 addr, unsigned int len,
211 snd_fw_async_midi_port_fill fill)
212{
213 port->len = DIV_ROUND_UP(len, 4) * 4;
214 port->buf = kzalloc(port->len, GFP_KERNEL);
215 if (port->buf == NULL)
216 return -ENOMEM;
217
218 port->parent = fw_parent_device(unit);
219 port->addr = addr;
220 port->fill = fill;
221 port->idling = true;
222 port->next_ktime = 0;
223 port->error = false;
224
225 INIT_WORK(&port->work, midi_port_work);
226
227 return 0;
228}
229EXPORT_SYMBOL(snd_fw_async_midi_port_init);
230
231/**
232 * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
233 * @port: the asynchronous MIDI port structure
234 */
235void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
236{
237 snd_fw_async_midi_port_finish(port);
238 cancel_work_sync(&port->work);
239 kfree(port->buf);
240}
241EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
242
243MODULE_DESCRIPTION("FireWire audio helper functions"); 102MODULE_DESCRIPTION("FireWire audio helper functions");
244MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); 103MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
245MODULE_LICENSE("GPL v2"); 104MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index c3768cd494a5..eef70922ed89 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -25,58 +25,4 @@ static inline bool rcode_is_permanent_error(int rcode)
25void snd_fw_schedule_registration(struct fw_unit *unit, 25void snd_fw_schedule_registration(struct fw_unit *unit,
26 struct delayed_work *dwork); 26 struct delayed_work *dwork);
27 27
28struct snd_fw_async_midi_port;
29typedef int (*snd_fw_async_midi_port_fill)(
30 struct snd_rawmidi_substream *substream,
31 u8 *buf);
32
33struct snd_fw_async_midi_port {
34 struct fw_device *parent;
35 struct work_struct work;
36 bool idling;
37 ktime_t next_ktime;
38 bool error;
39
40 u64 addr;
41 struct fw_transaction transaction;
42
43 u8 *buf;
44 unsigned int len;
45
46 struct snd_rawmidi_substream *substream;
47 snd_fw_async_midi_port_fill fill;
48 int consume_bytes;
49};
50
51int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
52 struct fw_unit *unit, u64 addr, unsigned int len,
53 snd_fw_async_midi_port_fill fill);
54void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
55
56/**
57 * snd_fw_async_midi_port_run - run transactions for the async MIDI port
58 * @port: the asynchronous MIDI port
59 * @substream: the MIDI substream
60 */
61static inline void
62snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
63 struct snd_rawmidi_substream *substream)
64{
65 if (!port->error) {
66 port->substream = substream;
67 schedule_work(&port->work);
68 }
69}
70
71/**
72 * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
73 * @port: the asynchronous MIDI port
74 */
75static inline void
76snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
77{
78 port->substream = NULL;
79 port->error = false;
80}
81
82#endif 28#endif
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
index 040a96d1ba8e..8ba006e456e8 100644
--- a/sound/firewire/tascam/tascam-transaction.c
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -144,6 +144,131 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
144 return consume; 144 return consume;
145} 145}
146 146
147static void async_midi_port_callback(struct fw_card *card, int rcode,
148 void *data, size_t length,
149 void *callback_data)
150{
151 struct snd_fw_async_midi_port *port = callback_data;
152 struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
153
154 /* This port is closed. */
155 if (substream == NULL)
156 return;
157
158 if (rcode == RCODE_COMPLETE)
159 snd_rawmidi_transmit_ack(substream, port->consume_bytes);
160 else if (!rcode_is_permanent_error(rcode))
161 /* To start next transaction immediately for recovery. */
162 port->next_ktime = 0;
163 else
164 /* Don't continue processing. */
165 port->error = true;
166
167 port->idling = true;
168
169 if (!snd_rawmidi_transmit_empty(substream))
170 schedule_work(&port->work);
171}
172
173static void midi_port_work(struct work_struct *work)
174{
175 struct snd_fw_async_midi_port *port =
176 container_of(work, struct snd_fw_async_midi_port, work);
177 struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
178 int generation;
179 int type;
180
181 /* Under transacting or error state. */
182 if (!port->idling || port->error)
183 return;
184
185 /* Nothing to do. */
186 if (substream == NULL || snd_rawmidi_transmit_empty(substream))
187 return;
188
189 /* Do it in next chance. */
190 if (ktime_after(port->next_ktime, ktime_get())) {
191 schedule_work(&port->work);
192 return;
193 }
194
195 /*
196 * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
197 * Later, snd_rawmidi_transmit_ack() is called.
198 */
199 memset(port->buf, 0, port->len);
200 port->consume_bytes = port->fill(substream, port->buf);
201 if (port->consume_bytes <= 0) {
202 /* Do it in next chance, immediately. */
203 if (port->consume_bytes == 0) {
204 port->next_ktime = 0;
205 schedule_work(&port->work);
206 } else {
207 /* Fatal error. */
208 port->error = true;
209 }
210 return;
211 }
212
213 /* Calculate type of transaction. */
214 if (port->len == 4)
215 type = TCODE_WRITE_QUADLET_REQUEST;
216 else
217 type = TCODE_WRITE_BLOCK_REQUEST;
218
219 /* Set interval to next transaction. */
220 port->next_ktime = ktime_add_ns(ktime_get(),
221 port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
222
223 /* Start this transaction. */
224 port->idling = false;
225
226 /*
227 * In Linux FireWire core, when generation is updated with memory
228 * barrier, node id has already been updated. In this module, After
229 * this smp_rmb(), load/store instructions to memory are completed.
230 * Thus, both of generation and node id are available with recent
231 * values. This is a light-serialization solution to handle bus reset
232 * events on IEEE 1394 bus.
233 */
234 generation = port->parent->generation;
235 smp_rmb();
236
237 fw_send_request(port->parent->card, &port->transaction, type,
238 port->parent->node_id, generation,
239 port->parent->max_speed, port->addr,
240 port->buf, port->len, async_midi_port_callback,
241 port);
242}
243
244int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
245 struct fw_unit *unit, u64 addr, unsigned int len,
246 snd_fw_async_midi_port_fill fill)
247{
248 port->len = DIV_ROUND_UP(len, 4) * 4;
249 port->buf = kzalloc(port->len, GFP_KERNEL);
250 if (port->buf == NULL)
251 return -ENOMEM;
252
253 port->parent = fw_parent_device(unit);
254 port->addr = addr;
255 port->fill = fill;
256 port->idling = true;
257 port->next_ktime = 0;
258 port->error = false;
259
260 INIT_WORK(&port->work, midi_port_work);
261
262 return 0;
263}
264
265void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
266{
267 snd_fw_async_midi_port_finish(port);
268 cancel_work_sync(&port->work);
269 kfree(port->buf);
270}
271
147static void handle_midi_tx(struct fw_card *card, struct fw_request *request, 272static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
148 int tcode, int destination, int source, 273 int tcode, int destination, int source,
149 int generation, unsigned long long offset, 274 int generation, unsigned long long offset,
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index d3cd4065722b..de76313e5d50 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -45,6 +45,29 @@ struct snd_tscm_spec {
45#define TSCM_MIDI_IN_PORT_MAX 4 45#define TSCM_MIDI_IN_PORT_MAX 4
46#define TSCM_MIDI_OUT_PORT_MAX 4 46#define TSCM_MIDI_OUT_PORT_MAX 4
47 47
48struct snd_fw_async_midi_port;
49typedef int (*snd_fw_async_midi_port_fill)(
50 struct snd_rawmidi_substream *substream,
51 u8 *buf);
52
53struct snd_fw_async_midi_port {
54 struct fw_device *parent;
55 struct work_struct work;
56 bool idling;
57 ktime_t next_ktime;
58 bool error;
59
60 u64 addr;
61 struct fw_transaction transaction;
62
63 u8 *buf;
64 unsigned int len;
65
66 struct snd_rawmidi_substream *substream;
67 snd_fw_async_midi_port_fill fill;
68 int consume_bytes;
69};
70
48struct snd_tscm { 71struct snd_tscm {
49 struct snd_card *card; 72 struct snd_card *card;
50 struct fw_unit *unit; 73 struct fw_unit *unit;
@@ -131,6 +154,28 @@ void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
131int snd_tscm_stream_lock_try(struct snd_tscm *tscm); 154int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
132void snd_tscm_stream_lock_release(struct snd_tscm *tscm); 155void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
133 156
157int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
158 struct fw_unit *unit, u64 addr, unsigned int len,
159 snd_fw_async_midi_port_fill fill);
160void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
161
162static inline void
163snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
164 struct snd_rawmidi_substream *substream)
165{
166 if (!port->error) {
167 port->substream = substream;
168 schedule_work(&port->work);
169 }
170}
171
172static inline void
173snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
174{
175 port->substream = NULL;
176 port->error = false;
177}
178
134int snd_tscm_transaction_register(struct snd_tscm *tscm); 179int snd_tscm_transaction_register(struct snd_tscm *tscm);
135int snd_tscm_transaction_reregister(struct snd_tscm *tscm); 180int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
136void snd_tscm_transaction_unregister(struct snd_tscm *tscm); 181void snd_tscm_transaction_unregister(struct snd_tscm *tscm);