summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);