diff options
-rw-r--r-- | sound/firewire/lib.c | 141 | ||||
-rw-r--r-- | sound/firewire/lib.h | 54 | ||||
-rw-r--r-- | sound/firewire/tascam/tascam-transaction.c | 125 | ||||
-rw-r--r-- | sound/firewire/tascam/tascam.h | 45 |
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 | } |
100 | EXPORT_SYMBOL(snd_fw_schedule_registration); | 100 | EXPORT_SYMBOL(snd_fw_schedule_registration); |
101 | 101 | ||
102 | static 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 | |||
128 | static 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 | */ | ||
209 | int 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 | } | ||
229 | EXPORT_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 | */ | ||
235 | void 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 | } | ||
241 | EXPORT_SYMBOL(snd_fw_async_midi_port_destroy); | ||
242 | |||
243 | MODULE_DESCRIPTION("FireWire audio helper functions"); | 102 | MODULE_DESCRIPTION("FireWire audio helper functions"); |
244 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | 103 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); |
245 | MODULE_LICENSE("GPL v2"); | 104 | MODULE_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) | |||
25 | void snd_fw_schedule_registration(struct fw_unit *unit, | 25 | void snd_fw_schedule_registration(struct fw_unit *unit, |
26 | struct delayed_work *dwork); | 26 | struct delayed_work *dwork); |
27 | 27 | ||
28 | struct snd_fw_async_midi_port; | ||
29 | typedef int (*snd_fw_async_midi_port_fill)( | ||
30 | struct snd_rawmidi_substream *substream, | ||
31 | u8 *buf); | ||
32 | |||
33 | struct 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 | |||
51 | int 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); | ||
54 | void 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 | */ | ||
61 | static inline void | ||
62 | snd_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 | */ | ||
75 | static inline void | ||
76 | snd_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 | ||
147 | static 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 | |||
173 | static 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 | |||
244 | int 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 | |||
265 | void 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 | |||
147 | static void handle_midi_tx(struct fw_card *card, struct fw_request *request, | 272 | static 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 | ||
48 | struct snd_fw_async_midi_port; | ||
49 | typedef int (*snd_fw_async_midi_port_fill)( | ||
50 | struct snd_rawmidi_substream *substream, | ||
51 | u8 *buf); | ||
52 | |||
53 | struct 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 | |||
48 | struct snd_tscm { | 71 | struct 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); | |||
131 | int snd_tscm_stream_lock_try(struct snd_tscm *tscm); | 154 | int snd_tscm_stream_lock_try(struct snd_tscm *tscm); |
132 | void snd_tscm_stream_lock_release(struct snd_tscm *tscm); | 155 | void snd_tscm_stream_lock_release(struct snd_tscm *tscm); |
133 | 156 | ||
157 | int 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); | ||
160 | void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port); | ||
161 | |||
162 | static inline void | ||
163 | snd_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 | |||
172 | static inline void | ||
173 | snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port) | ||
174 | { | ||
175 | port->substream = NULL; | ||
176 | port->error = false; | ||
177 | } | ||
178 | |||
134 | int snd_tscm_transaction_register(struct snd_tscm *tscm); | 179 | int snd_tscm_transaction_register(struct snd_tscm *tscm); |
135 | int snd_tscm_transaction_reregister(struct snd_tscm *tscm); | 180 | int snd_tscm_transaction_reregister(struct snd_tscm *tscm); |
136 | void snd_tscm_transaction_unregister(struct snd_tscm *tscm); | 181 | void snd_tscm_transaction_unregister(struct snd_tscm *tscm); |