Commit 31ef9134eb52636d383a7d0626cbbd345cb94f2f
Committed by
Takashi Iwai
1 parent
a5abba989d
Exists in
master
and in
39 other branches
ALSA: add LaCie FireWire Speakers/Griffin FireWave Surround driver
Add a driver for two playback-only FireWire devices based on the OXFW970 chip. v2: better AMDTP API abstraction; fix fw_unit leak; small fixes v3: cache the iPCR value v4: FireWave constraints; fix fw_device reference counting; fix PCR caching; small changes and fixes v5: volume/mute support; fix crashing due to pcm stop races v6: fix build; one-channel volume for LaCie v7: use signed values to make volume (range checks) work; fix function block IDs for volume/mute; always use channel 0 for LaCie volume Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Acked-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Tested-by: Jay Fenlason <fenlason@redhat.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 20 changed files with 2651 additions and 4 deletions Side-by-side Diff
- drivers/firewire/core-iso.c
- drivers/firewire/core.h
- include/linux/firewire.h
- sound/Kconfig
- sound/Makefile
- sound/firewire/Kconfig
- sound/firewire/Makefile
- sound/firewire/amdtp.c
- sound/firewire/amdtp.h
- sound/firewire/cmp.c
- sound/firewire/cmp.h
- sound/firewire/fcp.c
- sound/firewire/fcp.h
- sound/firewire/iso-resources.c
- sound/firewire/iso-resources.h
- sound/firewire/lib.c
- sound/firewire/lib.h
- sound/firewire/packets-buffer.c
- sound/firewire/packets-buffer.h
- sound/firewire/speakers.c
drivers/firewire/core-iso.c
drivers/firewire/core.h
... | ... | @@ -147,9 +147,6 @@ |
147 | 147 | /* -iso */ |
148 | 148 | |
149 | 149 | int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); |
150 | -void fw_iso_resource_manage(struct fw_card *card, int generation, | |
151 | - u64 channels_mask, int *channel, int *bandwidth, | |
152 | - bool allocate, __be32 buffer[2]); | |
153 | 150 | |
154 | 151 | |
155 | 152 | /* -topology */ |
include/linux/firewire.h
... | ... | @@ -42,6 +42,10 @@ |
42 | 42 | #define CSR_BROADCAST_CHANNEL 0x234 |
43 | 43 | #define CSR_CONFIG_ROM 0x400 |
44 | 44 | #define CSR_CONFIG_ROM_END 0x800 |
45 | +#define CSR_OMPR 0x900 | |
46 | +#define CSR_OPCR(i) (0x904 + (i) * 4) | |
47 | +#define CSR_IMPR 0x980 | |
48 | +#define CSR_IPCR(i) (0x984 + (i) * 4) | |
45 | 49 | #define CSR_FCP_COMMAND 0xB00 |
46 | 50 | #define CSR_FCP_RESPONSE 0xD00 |
47 | 51 | #define CSR_FCP_END 0xF00 |
... | ... | @@ -441,6 +445,9 @@ |
441 | 445 | int cycle, int sync, int tags); |
442 | 446 | int fw_iso_context_stop(struct fw_iso_context *ctx); |
443 | 447 | void fw_iso_context_destroy(struct fw_iso_context *ctx); |
448 | +void fw_iso_resource_manage(struct fw_card *card, int generation, | |
449 | + u64 channels_mask, int *channel, int *bandwidth, | |
450 | + bool allocate, __be32 buffer[2]); | |
444 | 451 | |
445 | 452 | #endif /* _LINUX_FIREWIRE_H */ |
sound/Kconfig
... | ... | @@ -97,6 +97,8 @@ |
97 | 97 | # here assuming USB is defined before ALSA |
98 | 98 | source "sound/usb/Kconfig" |
99 | 99 | |
100 | +source "sound/firewire/Kconfig" | |
101 | + | |
100 | 102 | # the following will depend on the order of config. |
101 | 103 | # here assuming PCMCIA is defined before ALSA |
102 | 104 | source "sound/pcmcia/Kconfig" |
sound/Makefile
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | obj-$(CONFIG_SOUND_PRIME) += oss/ |
7 | 7 | obj-$(CONFIG_DMASOUND) += oss/ |
8 | 8 | obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ |
9 | - sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ | |
9 | + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ | |
10 | 10 | obj-$(CONFIG_SND_AOA) += aoa/ |
11 | 11 | |
12 | 12 | # This one must be compilable even if sound is configured out |
sound/firewire/Kconfig
1 | +menuconfig SND_FIREWIRE | |
2 | + bool "FireWire sound devices" | |
3 | + depends on FIREWIRE | |
4 | + default y | |
5 | + help | |
6 | + Support for IEEE-1394/FireWire/iLink sound devices. | |
7 | + | |
8 | +if SND_FIREWIRE && FIREWIRE | |
9 | + | |
10 | +config SND_FIREWIRE_LIB | |
11 | + tristate | |
12 | + depends on SND_PCM | |
13 | + | |
14 | +config SND_FIREWIRE_SPEAKERS | |
15 | + tristate "FireWire speakers" | |
16 | + select SND_PCM | |
17 | + select SND_FIREWIRE_LIB | |
18 | + help | |
19 | + Say Y here to include support for the Griffin FireWave Surround | |
20 | + and the LaCie FireWire Speakers. | |
21 | + | |
22 | + To compile this driver as a module, choose M here: the module | |
23 | + will be called snd-firewire-speakers. | |
24 | + | |
25 | +endif # SND_FIREWIRE |
sound/firewire/Makefile
sound/firewire/amdtp.c
1 | +/* | |
2 | + * Audio and Music Data Transmission Protocol (IEC 61883-6) streams | |
3 | + * with Common Isochronous Packet (IEC 61883-1) headers | |
4 | + * | |
5 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
6 | + * Licensed under the terms of the GNU General Public License, version 2. | |
7 | + */ | |
8 | + | |
9 | +#include <linux/device.h> | |
10 | +#include <linux/err.h> | |
11 | +#include <linux/firewire.h> | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/slab.h> | |
14 | +#include <sound/pcm.h> | |
15 | +#include "amdtp.h" | |
16 | + | |
17 | +#define TICKS_PER_CYCLE 3072 | |
18 | +#define CYCLES_PER_SECOND 8000 | |
19 | +#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) | |
20 | + | |
21 | +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 ยตs */ | |
22 | + | |
23 | +#define TAG_CIP 1 | |
24 | + | |
25 | +#define CIP_EOH (1u << 31) | |
26 | +#define CIP_FMT_AM (0x10 << 24) | |
27 | +#define AMDTP_FDF_AM824 (0 << 19) | |
28 | +#define AMDTP_FDF_SFC_SHIFT 16 | |
29 | + | |
30 | +/* TODO: make these configurable */ | |
31 | +#define INTERRUPT_INTERVAL 16 | |
32 | +#define QUEUE_LENGTH 48 | |
33 | + | |
34 | +/** | |
35 | + * amdtp_out_stream_init - initialize an AMDTP output stream structure | |
36 | + * @s: the AMDTP output stream to initialize | |
37 | + * @unit: the target of the stream | |
38 | + * @flags: the packet transmission method to use | |
39 | + */ | |
40 | +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, | |
41 | + enum cip_out_flags flags) | |
42 | +{ | |
43 | + if (flags != CIP_NONBLOCKING) | |
44 | + return -EINVAL; | |
45 | + | |
46 | + s->unit = fw_unit_get(unit); | |
47 | + s->flags = flags; | |
48 | + s->context = ERR_PTR(-1); | |
49 | + mutex_init(&s->mutex); | |
50 | + | |
51 | + return 0; | |
52 | +} | |
53 | +EXPORT_SYMBOL(amdtp_out_stream_init); | |
54 | + | |
55 | +/** | |
56 | + * amdtp_out_stream_destroy - free stream resources | |
57 | + * @s: the AMDTP output stream to destroy | |
58 | + */ | |
59 | +void amdtp_out_stream_destroy(struct amdtp_out_stream *s) | |
60 | +{ | |
61 | + WARN_ON(!IS_ERR(s->context)); | |
62 | + mutex_destroy(&s->mutex); | |
63 | + fw_unit_put(s->unit); | |
64 | +} | |
65 | +EXPORT_SYMBOL(amdtp_out_stream_destroy); | |
66 | + | |
67 | +/** | |
68 | + * amdtp_out_stream_set_rate - set the sample rate | |
69 | + * @s: the AMDTP output stream to configure | |
70 | + * @rate: the sample rate | |
71 | + * | |
72 | + * The sample rate must be set before the stream is started, and must not be | |
73 | + * changed while the stream is running. | |
74 | + */ | |
75 | +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) | |
76 | +{ | |
77 | + static const struct { | |
78 | + unsigned int rate; | |
79 | + unsigned int syt_interval; | |
80 | + } rate_info[] = { | |
81 | + [CIP_SFC_32000] = { 32000, 8, }, | |
82 | + [CIP_SFC_44100] = { 44100, 8, }, | |
83 | + [CIP_SFC_48000] = { 48000, 8, }, | |
84 | + [CIP_SFC_88200] = { 88200, 16, }, | |
85 | + [CIP_SFC_96000] = { 96000, 16, }, | |
86 | + [CIP_SFC_176400] = { 176400, 32, }, | |
87 | + [CIP_SFC_192000] = { 192000, 32, }, | |
88 | + }; | |
89 | + unsigned int sfc; | |
90 | + | |
91 | + if (WARN_ON(!IS_ERR(s->context))) | |
92 | + return; | |
93 | + | |
94 | + for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) | |
95 | + if (rate_info[sfc].rate == rate) { | |
96 | + s->sfc = sfc; | |
97 | + s->syt_interval = rate_info[sfc].syt_interval; | |
98 | + return; | |
99 | + } | |
100 | + WARN_ON(1); | |
101 | +} | |
102 | +EXPORT_SYMBOL(amdtp_out_stream_set_rate); | |
103 | + | |
104 | +/** | |
105 | + * amdtp_out_stream_get_max_payload - get the stream's packet size | |
106 | + * @s: the AMDTP output stream | |
107 | + * | |
108 | + * This function must not be called before the stream has been configured | |
109 | + * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | |
110 | + * amdtp_out_stream_set_midi(). | |
111 | + */ | |
112 | +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) | |
113 | +{ | |
114 | + static const unsigned int max_data_blocks[] = { | |
115 | + [CIP_SFC_32000] = 4, | |
116 | + [CIP_SFC_44100] = 6, | |
117 | + [CIP_SFC_48000] = 6, | |
118 | + [CIP_SFC_88200] = 12, | |
119 | + [CIP_SFC_96000] = 12, | |
120 | + [CIP_SFC_176400] = 23, | |
121 | + [CIP_SFC_192000] = 24, | |
122 | + }; | |
123 | + | |
124 | + s->data_block_quadlets = s->pcm_channels; | |
125 | + s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); | |
126 | + | |
127 | + return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; | |
128 | +} | |
129 | +EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); | |
130 | + | |
131 | +static void amdtp_write_s16(struct amdtp_out_stream *s, | |
132 | + struct snd_pcm_substream *pcm, | |
133 | + __be32 *buffer, unsigned int frames); | |
134 | +static void amdtp_write_s32(struct amdtp_out_stream *s, | |
135 | + struct snd_pcm_substream *pcm, | |
136 | + __be32 *buffer, unsigned int frames); | |
137 | + | |
138 | +/** | |
139 | + * amdtp_out_stream_set_pcm_format - set the PCM format | |
140 | + * @s: the AMDTP output stream to configure | |
141 | + * @format: the format of the ALSA PCM device | |
142 | + * | |
143 | + * The sample format must be set before the stream is started, and must not be | |
144 | + * changed while the stream is running. | |
145 | + */ | |
146 | +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | |
147 | + snd_pcm_format_t format) | |
148 | +{ | |
149 | + if (WARN_ON(!IS_ERR(s->context))) | |
150 | + return; | |
151 | + | |
152 | + switch (format) { | |
153 | + default: | |
154 | + WARN_ON(1); | |
155 | + /* fall through */ | |
156 | + case SNDRV_PCM_FORMAT_S16: | |
157 | + s->transfer_samples = amdtp_write_s16; | |
158 | + break; | |
159 | + case SNDRV_PCM_FORMAT_S32: | |
160 | + s->transfer_samples = amdtp_write_s32; | |
161 | + break; | |
162 | + } | |
163 | +} | |
164 | +EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); | |
165 | + | |
166 | +static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) | |
167 | +{ | |
168 | + unsigned int phase, data_blocks; | |
169 | + | |
170 | + if (!cip_sfc_is_base_44100(s->sfc)) { | |
171 | + /* Sample_rate / 8000 is an integer, and precomputed. */ | |
172 | + data_blocks = s->data_block_state; | |
173 | + } else { | |
174 | + phase = s->data_block_state; | |
175 | + | |
176 | + /* | |
177 | + * This calculates the number of data blocks per packet so that | |
178 | + * 1) the overall rate is correct and exactly synchronized to | |
179 | + * the bus clock, and | |
180 | + * 2) packets with a rounded-up number of blocks occur as early | |
181 | + * as possible in the sequence (to prevent underruns of the | |
182 | + * device's buffer). | |
183 | + */ | |
184 | + if (s->sfc == CIP_SFC_44100) | |
185 | + /* 6 6 5 6 5 6 5 ... */ | |
186 | + data_blocks = 5 + ((phase & 1) ^ | |
187 | + (phase == 0 || phase >= 40)); | |
188 | + else | |
189 | + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ | |
190 | + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); | |
191 | + if (++phase >= (80 >> (s->sfc >> 1))) | |
192 | + phase = 0; | |
193 | + s->data_block_state = phase; | |
194 | + } | |
195 | + | |
196 | + return data_blocks; | |
197 | +} | |
198 | + | |
199 | +static unsigned int calculate_syt(struct amdtp_out_stream *s, | |
200 | + unsigned int cycle) | |
201 | +{ | |
202 | + unsigned int syt_offset, phase, index, syt; | |
203 | + | |
204 | + if (s->last_syt_offset < TICKS_PER_CYCLE) { | |
205 | + if (!cip_sfc_is_base_44100(s->sfc)) | |
206 | + syt_offset = s->last_syt_offset + s->syt_offset_state; | |
207 | + else { | |
208 | + /* | |
209 | + * The time, in ticks, of the n'th SYT_INTERVAL sample is: | |
210 | + * n * SYT_INTERVAL * 24576000 / sample_rate | |
211 | + * Modulo TICKS_PER_CYCLE, the difference between successive | |
212 | + * elements is about 1386.23. Rounding the results of this | |
213 | + * formula to the SYT precision results in a sequence of | |
214 | + * differences that begins with: | |
215 | + * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... | |
216 | + * This code generates _exactly_ the same sequence. | |
217 | + */ | |
218 | + phase = s->syt_offset_state; | |
219 | + index = phase % 13; | |
220 | + syt_offset = s->last_syt_offset; | |
221 | + syt_offset += 1386 + ((index && !(index & 3)) || | |
222 | + phase == 146); | |
223 | + if (++phase >= 147) | |
224 | + phase = 0; | |
225 | + s->syt_offset_state = phase; | |
226 | + } | |
227 | + } else | |
228 | + syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; | |
229 | + s->last_syt_offset = syt_offset; | |
230 | + | |
231 | + syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; | |
232 | + syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; | |
233 | + syt += syt_offset % TICKS_PER_CYCLE; | |
234 | + | |
235 | + return syt & 0xffff; | |
236 | +} | |
237 | + | |
238 | +static void amdtp_write_s32(struct amdtp_out_stream *s, | |
239 | + struct snd_pcm_substream *pcm, | |
240 | + __be32 *buffer, unsigned int frames) | |
241 | +{ | |
242 | + struct snd_pcm_runtime *runtime = pcm->runtime; | |
243 | + unsigned int channels, remaining_frames, frame_step, i, c; | |
244 | + const u32 *src; | |
245 | + | |
246 | + channels = s->pcm_channels; | |
247 | + src = (void *)runtime->dma_area + | |
248 | + s->pcm_buffer_pointer * (runtime->frame_bits / 8); | |
249 | + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | |
250 | + frame_step = s->data_block_quadlets - channels; | |
251 | + | |
252 | + for (i = 0; i < frames; ++i) { | |
253 | + for (c = 0; c < channels; ++c) { | |
254 | + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); | |
255 | + src++; | |
256 | + buffer++; | |
257 | + } | |
258 | + buffer += frame_step; | |
259 | + if (--remaining_frames == 0) | |
260 | + src = (void *)runtime->dma_area; | |
261 | + } | |
262 | +} | |
263 | + | |
264 | +static void amdtp_write_s16(struct amdtp_out_stream *s, | |
265 | + struct snd_pcm_substream *pcm, | |
266 | + __be32 *buffer, unsigned int frames) | |
267 | +{ | |
268 | + struct snd_pcm_runtime *runtime = pcm->runtime; | |
269 | + unsigned int channels, remaining_frames, frame_step, i, c; | |
270 | + const u16 *src; | |
271 | + | |
272 | + channels = s->pcm_channels; | |
273 | + src = (void *)runtime->dma_area + | |
274 | + s->pcm_buffer_pointer * (runtime->frame_bits / 8); | |
275 | + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | |
276 | + frame_step = s->data_block_quadlets - channels; | |
277 | + | |
278 | + for (i = 0; i < frames; ++i) { | |
279 | + for (c = 0; c < channels; ++c) { | |
280 | + *buffer = cpu_to_be32((*src << 8) | 0x40000000); | |
281 | + src++; | |
282 | + buffer++; | |
283 | + } | |
284 | + buffer += frame_step; | |
285 | + if (--remaining_frames == 0) | |
286 | + src = (void *)runtime->dma_area; | |
287 | + } | |
288 | +} | |
289 | + | |
290 | +static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, | |
291 | + __be32 *buffer, unsigned int frames) | |
292 | +{ | |
293 | + unsigned int i, c; | |
294 | + | |
295 | + for (i = 0; i < frames; ++i) { | |
296 | + for (c = 0; c < s->pcm_channels; ++c) | |
297 | + buffer[c] = cpu_to_be32(0x40000000); | |
298 | + buffer += s->data_block_quadlets; | |
299 | + } | |
300 | +} | |
301 | + | |
302 | +static void amdtp_fill_midi(struct amdtp_out_stream *s, | |
303 | + __be32 *buffer, unsigned int frames) | |
304 | +{ | |
305 | + unsigned int i; | |
306 | + | |
307 | + for (i = 0; i < frames; ++i) | |
308 | + buffer[s->pcm_channels + i * s->data_block_quadlets] = | |
309 | + cpu_to_be32(0x80000000); | |
310 | +} | |
311 | + | |
312 | +static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) | |
313 | +{ | |
314 | + __be32 *buffer; | |
315 | + unsigned int data_blocks, syt, ptr; | |
316 | + struct snd_pcm_substream *pcm; | |
317 | + struct fw_iso_packet packet; | |
318 | + int err; | |
319 | + | |
320 | + data_blocks = calculate_data_blocks(s); | |
321 | + syt = calculate_syt(s, cycle); | |
322 | + | |
323 | + buffer = s->buffer.packets[s->packet_counter].buffer; | |
324 | + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | | |
325 | + (s->data_block_quadlets << 16) | | |
326 | + s->data_block_counter); | |
327 | + buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | | |
328 | + (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); | |
329 | + buffer += 2; | |
330 | + | |
331 | + pcm = ACCESS_ONCE(s->pcm); | |
332 | + if (pcm) | |
333 | + s->transfer_samples(s, pcm, buffer, data_blocks); | |
334 | + else | |
335 | + amdtp_fill_pcm_silence(s, buffer, data_blocks); | |
336 | + if (s->midi_ports) | |
337 | + amdtp_fill_midi(s, buffer, data_blocks); | |
338 | + | |
339 | + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; | |
340 | + | |
341 | + packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; | |
342 | + packet.interrupt = IS_ALIGNED(s->packet_counter + 1, | |
343 | + INTERRUPT_INTERVAL); | |
344 | + packet.skip = 0; | |
345 | + packet.tag = TAG_CIP; | |
346 | + packet.sy = 0; | |
347 | + packet.header_length = 0; | |
348 | + | |
349 | + err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, | |
350 | + s->buffer.packets[s->packet_counter].offset); | |
351 | + if (err < 0) | |
352 | + dev_err(&s->unit->device, "queueing error: %d\n", err); | |
353 | + | |
354 | + if (++s->packet_counter >= QUEUE_LENGTH) | |
355 | + s->packet_counter = 0; | |
356 | + | |
357 | + if (pcm) { | |
358 | + ptr = s->pcm_buffer_pointer + data_blocks; | |
359 | + if (ptr >= pcm->runtime->buffer_size) | |
360 | + ptr -= pcm->runtime->buffer_size; | |
361 | + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; | |
362 | + | |
363 | + s->pcm_period_pointer += data_blocks; | |
364 | + if (s->pcm_period_pointer >= pcm->runtime->period_size) { | |
365 | + s->pcm_period_pointer -= pcm->runtime->period_size; | |
366 | + snd_pcm_period_elapsed(pcm); | |
367 | + } | |
368 | + } | |
369 | +} | |
370 | + | |
371 | +static void out_packet_callback(struct fw_iso_context *context, u32 cycle, | |
372 | + size_t header_length, void *header, void *data) | |
373 | +{ | |
374 | + struct amdtp_out_stream *s = data; | |
375 | + unsigned int i, packets = header_length / 4; | |
376 | + | |
377 | + /* | |
378 | + * Compute the cycle of the last queued packet. | |
379 | + * (We need only the four lowest bits for the SYT, so we can ignore | |
380 | + * that bits 0-11 must wrap around at 3072.) | |
381 | + */ | |
382 | + cycle += QUEUE_LENGTH - packets; | |
383 | + | |
384 | + for (i = 0; i < packets; ++i) | |
385 | + queue_out_packet(s, ++cycle); | |
386 | +} | |
387 | + | |
388 | +static int queue_initial_skip_packets(struct amdtp_out_stream *s) | |
389 | +{ | |
390 | + struct fw_iso_packet skip_packet = { | |
391 | + .skip = 1, | |
392 | + }; | |
393 | + unsigned int i; | |
394 | + int err; | |
395 | + | |
396 | + for (i = 0; i < QUEUE_LENGTH; ++i) { | |
397 | + skip_packet.interrupt = IS_ALIGNED(s->packet_counter + 1, | |
398 | + INTERRUPT_INTERVAL); | |
399 | + err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); | |
400 | + if (err < 0) | |
401 | + return err; | |
402 | + if (++s->packet_counter >= QUEUE_LENGTH) | |
403 | + s->packet_counter = 0; | |
404 | + } | |
405 | + | |
406 | + return 0; | |
407 | +} | |
408 | + | |
409 | +/** | |
410 | + * amdtp_out_stream_start - start sending packets | |
411 | + * @s: the AMDTP output stream to start | |
412 | + * @channel: the isochronous channel on the bus | |
413 | + * @speed: firewire speed code | |
414 | + * | |
415 | + * The stream cannot be started until it has been configured with | |
416 | + * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | |
417 | + * amdtp_out_stream_set_midi(); and it must be started before any | |
418 | + * PCM or MIDI device can be started. | |
419 | + */ | |
420 | +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) | |
421 | +{ | |
422 | + static const struct { | |
423 | + unsigned int data_block; | |
424 | + unsigned int syt_offset; | |
425 | + } initial_state[] = { | |
426 | + [CIP_SFC_32000] = { 4, 3072 }, | |
427 | + [CIP_SFC_48000] = { 6, 1024 }, | |
428 | + [CIP_SFC_96000] = { 12, 1024 }, | |
429 | + [CIP_SFC_192000] = { 24, 1024 }, | |
430 | + [CIP_SFC_44100] = { 0, 67 }, | |
431 | + [CIP_SFC_88200] = { 0, 67 }, | |
432 | + [CIP_SFC_176400] = { 0, 67 }, | |
433 | + }; | |
434 | + int err; | |
435 | + | |
436 | + mutex_lock(&s->mutex); | |
437 | + | |
438 | + if (WARN_ON(!IS_ERR(s->context) || | |
439 | + (!s->pcm_channels && !s->midi_ports))) { | |
440 | + err = -EBADFD; | |
441 | + goto err_unlock; | |
442 | + } | |
443 | + | |
444 | + s->data_block_state = initial_state[s->sfc].data_block; | |
445 | + s->syt_offset_state = initial_state[s->sfc].syt_offset; | |
446 | + s->last_syt_offset = TICKS_PER_CYCLE; | |
447 | + | |
448 | + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, | |
449 | + amdtp_out_stream_get_max_payload(s), | |
450 | + DMA_TO_DEVICE); | |
451 | + if (err < 0) | |
452 | + goto err_unlock; | |
453 | + | |
454 | + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, | |
455 | + FW_ISO_CONTEXT_TRANSMIT, | |
456 | + channel, speed, 0, | |
457 | + out_packet_callback, s); | |
458 | + if (IS_ERR(s->context)) { | |
459 | + err = PTR_ERR(s->context); | |
460 | + if (err == -EBUSY) | |
461 | + dev_err(&s->unit->device, | |
462 | + "no free output stream on this controller\n"); | |
463 | + goto err_buffer; | |
464 | + } | |
465 | + | |
466 | + amdtp_out_stream_update(s); | |
467 | + | |
468 | + s->packet_counter = 0; | |
469 | + s->data_block_counter = 0; | |
470 | + err = queue_initial_skip_packets(s); | |
471 | + if (err < 0) | |
472 | + goto err_context; | |
473 | + | |
474 | + err = fw_iso_context_start(s->context, -1, 0, 0); | |
475 | + if (err < 0) | |
476 | + goto err_context; | |
477 | + | |
478 | + mutex_unlock(&s->mutex); | |
479 | + | |
480 | + return 0; | |
481 | + | |
482 | +err_context: | |
483 | + fw_iso_context_destroy(s->context); | |
484 | + s->context = ERR_PTR(-1); | |
485 | +err_buffer: | |
486 | + iso_packets_buffer_destroy(&s->buffer, s->unit); | |
487 | +err_unlock: | |
488 | + mutex_unlock(&s->mutex); | |
489 | + | |
490 | + return err; | |
491 | +} | |
492 | +EXPORT_SYMBOL(amdtp_out_stream_start); | |
493 | + | |
494 | +/** | |
495 | + * amdtp_out_stream_update - update the stream after a bus reset | |
496 | + * @s: the AMDTP output stream | |
497 | + */ | |
498 | +void amdtp_out_stream_update(struct amdtp_out_stream *s) | |
499 | +{ | |
500 | + ACCESS_ONCE(s->source_node_id_field) = | |
501 | + (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; | |
502 | +} | |
503 | +EXPORT_SYMBOL(amdtp_out_stream_update); | |
504 | + | |
505 | +/** | |
506 | + * amdtp_out_stream_stop - stop sending packets | |
507 | + * @s: the AMDTP output stream to stop | |
508 | + * | |
509 | + * All PCM and MIDI devices of the stream must be stopped before the stream | |
510 | + * itself can be stopped. | |
511 | + */ | |
512 | +void amdtp_out_stream_stop(struct amdtp_out_stream *s) | |
513 | +{ | |
514 | + mutex_lock(&s->mutex); | |
515 | + | |
516 | + if (IS_ERR(s->context)) { | |
517 | + mutex_unlock(&s->mutex); | |
518 | + return; | |
519 | + } | |
520 | + | |
521 | + fw_iso_context_stop(s->context); | |
522 | + fw_iso_context_destroy(s->context); | |
523 | + s->context = ERR_PTR(-1); | |
524 | + iso_packets_buffer_destroy(&s->buffer, s->unit); | |
525 | + | |
526 | + mutex_unlock(&s->mutex); | |
527 | +} | |
528 | +EXPORT_SYMBOL(amdtp_out_stream_stop); | |
529 | + | |
530 | +/** | |
531 | + * amdtp_out_stream_pcm_abort - abort the running PCM device | |
532 | + * @s: the AMDTP stream about to be stopped | |
533 | + * | |
534 | + * If the isochronous stream needs to be stopped asynchronously, call this | |
535 | + * function first to stop the PCM device. | |
536 | + */ | |
537 | +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) | |
538 | +{ | |
539 | + struct snd_pcm_substream *pcm; | |
540 | + | |
541 | + pcm = ACCESS_ONCE(s->pcm); | |
542 | + if (pcm) { | |
543 | + snd_pcm_stream_lock_irq(pcm); | |
544 | + if (snd_pcm_running(pcm)) | |
545 | + snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); | |
546 | + snd_pcm_stream_unlock_irq(pcm); | |
547 | + } | |
548 | +} | |
549 | +EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); |
sound/firewire/amdtp.h
1 | +#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_AMDTP_H_INCLUDED | |
3 | + | |
4 | +#include <linux/mutex.h> | |
5 | +#include <linux/spinlock.h> | |
6 | +#include "packets-buffer.h" | |
7 | + | |
8 | +/** | |
9 | + * enum cip_out_flags - describes details of the streaming protocol | |
10 | + * @CIP_NONBLOCKING: In non-blocking mode, each packet contains | |
11 | + * sample_rate/8000 samples, with rounding up or down to adjust | |
12 | + * for clock skew and left-over fractional samples. This should | |
13 | + * be used if supported by the device. | |
14 | + */ | |
15 | +enum cip_out_flags { | |
16 | + CIP_NONBLOCKING = 0, | |
17 | +}; | |
18 | + | |
19 | +/** | |
20 | + * enum cip_sfc - a stream's sample rate | |
21 | + */ | |
22 | +enum cip_sfc { | |
23 | + CIP_SFC_32000 = 0, | |
24 | + CIP_SFC_44100 = 1, | |
25 | + CIP_SFC_48000 = 2, | |
26 | + CIP_SFC_88200 = 3, | |
27 | + CIP_SFC_96000 = 4, | |
28 | + CIP_SFC_176400 = 5, | |
29 | + CIP_SFC_192000 = 6, | |
30 | +}; | |
31 | + | |
32 | +#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ | |
33 | + SNDRV_PCM_FMTBIT_S32) | |
34 | + | |
35 | +struct fw_unit; | |
36 | +struct fw_iso_context; | |
37 | +struct snd_pcm_substream; | |
38 | + | |
39 | +struct amdtp_out_stream { | |
40 | + struct fw_unit *unit; | |
41 | + enum cip_out_flags flags; | |
42 | + struct fw_iso_context *context; | |
43 | + struct mutex mutex; | |
44 | + | |
45 | + enum cip_sfc sfc; | |
46 | + unsigned int data_block_quadlets; | |
47 | + unsigned int pcm_channels; | |
48 | + unsigned int midi_ports; | |
49 | + void (*transfer_samples)(struct amdtp_out_stream *s, | |
50 | + struct snd_pcm_substream *pcm, | |
51 | + __be32 *buffer, unsigned int frames); | |
52 | + | |
53 | + unsigned int syt_interval; | |
54 | + unsigned int source_node_id_field; | |
55 | + struct iso_packets_buffer buffer; | |
56 | + | |
57 | + struct snd_pcm_substream *pcm; | |
58 | + | |
59 | + unsigned int packet_counter; | |
60 | + unsigned int data_block_counter; | |
61 | + | |
62 | + unsigned int data_block_state; | |
63 | + | |
64 | + unsigned int last_syt_offset; | |
65 | + unsigned int syt_offset_state; | |
66 | + | |
67 | + unsigned int pcm_buffer_pointer; | |
68 | + unsigned int pcm_period_pointer; | |
69 | +}; | |
70 | + | |
71 | +int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, | |
72 | + enum cip_out_flags flags); | |
73 | +void amdtp_out_stream_destroy(struct amdtp_out_stream *s); | |
74 | + | |
75 | +void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); | |
76 | +unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); | |
77 | + | |
78 | +int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); | |
79 | +void amdtp_out_stream_update(struct amdtp_out_stream *s); | |
80 | +void amdtp_out_stream_stop(struct amdtp_out_stream *s); | |
81 | + | |
82 | +void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | |
83 | + snd_pcm_format_t format); | |
84 | +void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); | |
85 | + | |
86 | +/** | |
87 | + * amdtp_out_stream_set_pcm - configure format of PCM samples | |
88 | + * @s: the AMDTP output stream to be configured | |
89 | + * @pcm_channels: the number of PCM samples in each data block, to be encoded | |
90 | + * as AM824 multi-bit linear audio | |
91 | + * | |
92 | + * This function must not be called while the stream is running. | |
93 | + */ | |
94 | +static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, | |
95 | + unsigned int pcm_channels) | |
96 | +{ | |
97 | + s->pcm_channels = pcm_channels; | |
98 | +} | |
99 | + | |
100 | +/** | |
101 | + * amdtp_out_stream_set_midi - configure format of MIDI data | |
102 | + * @s: the AMDTP output stream to be configured | |
103 | + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) | |
104 | + * | |
105 | + * This function must not be called while the stream is running. | |
106 | + */ | |
107 | +static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, | |
108 | + unsigned int midi_ports) | |
109 | +{ | |
110 | + s->midi_ports = midi_ports; | |
111 | +} | |
112 | + | |
113 | +/** | |
114 | + * amdtp_out_stream_pcm_prepare - prepare PCM device for running | |
115 | + * @s: the AMDTP output stream | |
116 | + * | |
117 | + * This function should be called from the PCM device's .prepare callback. | |
118 | + */ | |
119 | +static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) | |
120 | +{ | |
121 | + s->pcm_buffer_pointer = 0; | |
122 | + s->pcm_period_pointer = 0; | |
123 | +} | |
124 | + | |
125 | +/** | |
126 | + * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device | |
127 | + * @s: the AMDTP output stream | |
128 | + * @pcm: the PCM device to be started, or %NULL to stop the current device | |
129 | + * | |
130 | + * Call this function on a running isochronous stream to enable the actual | |
131 | + * transmission of PCM data. This function should be called from the PCM | |
132 | + * device's .trigger callback. | |
133 | + */ | |
134 | +static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, | |
135 | + struct snd_pcm_substream *pcm) | |
136 | +{ | |
137 | + ACCESS_ONCE(s->pcm) = pcm; | |
138 | +} | |
139 | + | |
140 | +/** | |
141 | + * amdtp_out_stream_pcm_pointer - get the PCM buffer position | |
142 | + * @s: the AMDTP output stream that transports the PCM data | |
143 | + * | |
144 | + * Returns the current buffer position, in frames. | |
145 | + */ | |
146 | +static inline unsigned long | |
147 | +amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) | |
148 | +{ | |
149 | + return ACCESS_ONCE(s->pcm_buffer_pointer); | |
150 | +} | |
151 | + | |
152 | +static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) | |
153 | +{ | |
154 | + return sfc & 1; | |
155 | +} | |
156 | + | |
157 | +#endif |
sound/firewire/cmp.c
1 | +/* | |
2 | + * Connection Management Procedures (IEC 61883-1) helper functions | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/device.h> | |
9 | +#include <linux/firewire.h> | |
10 | +#include <linux/firewire-constants.h> | |
11 | +#include <linux/module.h> | |
12 | +#include <linux/sched.h> | |
13 | +#include "lib.h" | |
14 | +#include "iso-resources.h" | |
15 | +#include "cmp.h" | |
16 | + | |
17 | +#define IMPR_SPEED_MASK 0xc0000000 | |
18 | +#define IMPR_SPEED_SHIFT 30 | |
19 | +#define IMPR_XSPEED_MASK 0x00000060 | |
20 | +#define IMPR_XSPEED_SHIFT 5 | |
21 | +#define IMPR_PLUGS_MASK 0x0000001f | |
22 | + | |
23 | +#define IPCR_ONLINE 0x80000000 | |
24 | +#define IPCR_BCAST_CONN 0x40000000 | |
25 | +#define IPCR_P2P_CONN_MASK 0x3f000000 | |
26 | +#define IPCR_P2P_CONN_SHIFT 24 | |
27 | +#define IPCR_CHANNEL_MASK 0x003f0000 | |
28 | +#define IPCR_CHANNEL_SHIFT 16 | |
29 | + | |
30 | +enum bus_reset_handling { | |
31 | + ABORT_ON_BUS_RESET, | |
32 | + SUCCEED_ON_BUS_RESET, | |
33 | +}; | |
34 | + | |
35 | +static __attribute__((format(printf, 2, 3))) | |
36 | +void cmp_error(struct cmp_connection *c, const char *fmt, ...) | |
37 | +{ | |
38 | + va_list va; | |
39 | + | |
40 | + va_start(va, fmt); | |
41 | + dev_err(&c->resources.unit->device, "%cPCR%u: %pV", | |
42 | + 'i', c->pcr_index, &(struct va_format){ fmt, &va }); | |
43 | + va_end(va); | |
44 | +} | |
45 | + | |
46 | +static int pcr_modify(struct cmp_connection *c, | |
47 | + __be32 (*modify)(struct cmp_connection *c, __be32 old), | |
48 | + int (*check)(struct cmp_connection *c, __be32 pcr), | |
49 | + enum bus_reset_handling bus_reset_handling) | |
50 | +{ | |
51 | + struct fw_device *device = fw_parent_device(c->resources.unit); | |
52 | + __be32 *buffer = c->resources.buffer; | |
53 | + int generation = c->resources.generation; | |
54 | + int rcode, errors = 0; | |
55 | + __be32 old_arg; | |
56 | + int err; | |
57 | + | |
58 | + buffer[0] = c->last_pcr_value; | |
59 | + for (;;) { | |
60 | + old_arg = buffer[0]; | |
61 | + buffer[1] = modify(c, buffer[0]); | |
62 | + | |
63 | + rcode = fw_run_transaction( | |
64 | + device->card, TCODE_LOCK_COMPARE_SWAP, | |
65 | + device->node_id, generation, device->max_speed, | |
66 | + CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), | |
67 | + buffer, 8); | |
68 | + | |
69 | + if (rcode == RCODE_COMPLETE) { | |
70 | + if (buffer[0] == old_arg) /* success? */ | |
71 | + break; | |
72 | + | |
73 | + if (check) { | |
74 | + err = check(c, buffer[0]); | |
75 | + if (err < 0) | |
76 | + return err; | |
77 | + } | |
78 | + } else if (rcode == RCODE_GENERATION) | |
79 | + goto bus_reset; | |
80 | + else if (rcode_is_permanent_error(rcode) || ++errors >= 3) | |
81 | + goto io_error; | |
82 | + } | |
83 | + c->last_pcr_value = buffer[1]; | |
84 | + | |
85 | + return 0; | |
86 | + | |
87 | +io_error: | |
88 | + cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); | |
89 | + return -EIO; | |
90 | + | |
91 | +bus_reset: | |
92 | + return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; | |
93 | +} | |
94 | + | |
95 | + | |
96 | +/** | |
97 | + * cmp_connection_init - initializes a connection manager | |
98 | + * @c: the connection manager to initialize | |
99 | + * @unit: a unit of the target device | |
100 | + * @ipcr_index: the index of the iPCR on the target device | |
101 | + */ | |
102 | +int cmp_connection_init(struct cmp_connection *c, | |
103 | + struct fw_unit *unit, | |
104 | + unsigned int ipcr_index) | |
105 | +{ | |
106 | + __be32 impr_be; | |
107 | + u32 impr; | |
108 | + int err; | |
109 | + | |
110 | + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, | |
111 | + CSR_REGISTER_BASE + CSR_IMPR, | |
112 | + &impr_be, 4); | |
113 | + if (err < 0) | |
114 | + return err; | |
115 | + impr = be32_to_cpu(impr_be); | |
116 | + | |
117 | + if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) | |
118 | + return -EINVAL; | |
119 | + | |
120 | + c->connected = false; | |
121 | + mutex_init(&c->mutex); | |
122 | + fw_iso_resources_init(&c->resources, unit); | |
123 | + c->last_pcr_value = cpu_to_be32(0x80000000); | |
124 | + c->pcr_index = ipcr_index; | |
125 | + c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; | |
126 | + if (c->max_speed == SCODE_BETA) | |
127 | + c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; | |
128 | + | |
129 | + return 0; | |
130 | +} | |
131 | +EXPORT_SYMBOL(cmp_connection_init); | |
132 | + | |
133 | +/** | |
134 | + * cmp_connection_destroy - free connection manager resources | |
135 | + * @c: the connection manager | |
136 | + */ | |
137 | +void cmp_connection_destroy(struct cmp_connection *c) | |
138 | +{ | |
139 | + WARN_ON(c->connected); | |
140 | + mutex_destroy(&c->mutex); | |
141 | + fw_iso_resources_destroy(&c->resources); | |
142 | +} | |
143 | +EXPORT_SYMBOL(cmp_connection_destroy); | |
144 | + | |
145 | + | |
146 | +static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) | |
147 | +{ | |
148 | + ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | | |
149 | + IPCR_P2P_CONN_MASK | | |
150 | + IPCR_CHANNEL_MASK); | |
151 | + ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); | |
152 | + ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); | |
153 | + | |
154 | + return ipcr; | |
155 | +} | |
156 | + | |
157 | +static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) | |
158 | +{ | |
159 | + if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | | |
160 | + IPCR_P2P_CONN_MASK)) { | |
161 | + cmp_error(c, "plug is already in use\n"); | |
162 | + return -EBUSY; | |
163 | + } | |
164 | + if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { | |
165 | + cmp_error(c, "plug is not on-line\n"); | |
166 | + return -ECONNREFUSED; | |
167 | + } | |
168 | + | |
169 | + return 0; | |
170 | +} | |
171 | + | |
172 | +/** | |
173 | + * cmp_connection_establish - establish a connection to the target | |
174 | + * @c: the connection manager | |
175 | + * @max_payload_bytes: the amount of data (including CIP headers) per packet | |
176 | + * | |
177 | + * This function establishes a point-to-point connection from the local | |
178 | + * computer to the target by allocating isochronous resources (channel and | |
179 | + * bandwidth) and setting the target's input plug control register. When this | |
180 | + * function succeeds, the caller is responsible for starting transmitting | |
181 | + * packets. | |
182 | + */ | |
183 | +int cmp_connection_establish(struct cmp_connection *c, | |
184 | + unsigned int max_payload_bytes) | |
185 | +{ | |
186 | + int err; | |
187 | + | |
188 | + if (WARN_ON(c->connected)) | |
189 | + return -EISCONN; | |
190 | + | |
191 | + c->speed = min(c->max_speed, | |
192 | + fw_parent_device(c->resources.unit)->max_speed); | |
193 | + | |
194 | + mutex_lock(&c->mutex); | |
195 | + | |
196 | +retry_after_bus_reset: | |
197 | + err = fw_iso_resources_allocate(&c->resources, | |
198 | + max_payload_bytes, c->speed); | |
199 | + if (err < 0) | |
200 | + goto err_mutex; | |
201 | + | |
202 | + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, | |
203 | + ABORT_ON_BUS_RESET); | |
204 | + if (err == -EAGAIN) { | |
205 | + fw_iso_resources_free(&c->resources); | |
206 | + goto retry_after_bus_reset; | |
207 | + } | |
208 | + if (err < 0) | |
209 | + goto err_resources; | |
210 | + | |
211 | + c->connected = true; | |
212 | + | |
213 | + mutex_unlock(&c->mutex); | |
214 | + | |
215 | + return 0; | |
216 | + | |
217 | +err_resources: | |
218 | + fw_iso_resources_free(&c->resources); | |
219 | +err_mutex: | |
220 | + mutex_unlock(&c->mutex); | |
221 | + | |
222 | + return err; | |
223 | +} | |
224 | +EXPORT_SYMBOL(cmp_connection_establish); | |
225 | + | |
226 | +/** | |
227 | + * cmp_connection_update - update the connection after a bus reset | |
228 | + * @c: the connection manager | |
229 | + * | |
230 | + * This function must be called from the driver's .update handler to reestablish | |
231 | + * any connection that might have been active. | |
232 | + * | |
233 | + * Returns zero on success, or a negative error code. On an error, the | |
234 | + * connection is broken and the caller must stop transmitting iso packets. | |
235 | + */ | |
236 | +int cmp_connection_update(struct cmp_connection *c) | |
237 | +{ | |
238 | + int err; | |
239 | + | |
240 | + mutex_lock(&c->mutex); | |
241 | + | |
242 | + if (!c->connected) { | |
243 | + mutex_unlock(&c->mutex); | |
244 | + return 0; | |
245 | + } | |
246 | + | |
247 | + err = fw_iso_resources_update(&c->resources); | |
248 | + if (err < 0) | |
249 | + goto err_unconnect; | |
250 | + | |
251 | + err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, | |
252 | + SUCCEED_ON_BUS_RESET); | |
253 | + if (err < 0) | |
254 | + goto err_resources; | |
255 | + | |
256 | + mutex_unlock(&c->mutex); | |
257 | + | |
258 | + return 0; | |
259 | + | |
260 | +err_resources: | |
261 | + fw_iso_resources_free(&c->resources); | |
262 | +err_unconnect: | |
263 | + c->connected = false; | |
264 | + mutex_unlock(&c->mutex); | |
265 | + | |
266 | + return err; | |
267 | +} | |
268 | +EXPORT_SYMBOL(cmp_connection_update); | |
269 | + | |
270 | + | |
271 | +static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr) | |
272 | +{ | |
273 | + return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); | |
274 | +} | |
275 | + | |
276 | +/** | |
277 | + * cmp_connection_break - break the connection to the target | |
278 | + * @c: the connection manager | |
279 | + * | |
280 | + * This function deactives the connection in the target's input plug control | |
281 | + * register, and frees the isochronous resources of the connection. Before | |
282 | + * calling this function, the caller should cease transmitting packets. | |
283 | + */ | |
284 | +void cmp_connection_break(struct cmp_connection *c) | |
285 | +{ | |
286 | + int err; | |
287 | + | |
288 | + mutex_lock(&c->mutex); | |
289 | + | |
290 | + if (!c->connected) { | |
291 | + mutex_unlock(&c->mutex); | |
292 | + return; | |
293 | + } | |
294 | + | |
295 | + err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); | |
296 | + if (err < 0) | |
297 | + cmp_error(c, "plug is still connected\n"); | |
298 | + | |
299 | + fw_iso_resources_free(&c->resources); | |
300 | + | |
301 | + c->connected = false; | |
302 | + | |
303 | + mutex_unlock(&c->mutex); | |
304 | +} | |
305 | +EXPORT_SYMBOL(cmp_connection_break); |
sound/firewire/cmp.h
1 | +#ifndef SOUND_FIREWIRE_CMP_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_CMP_H_INCLUDED | |
3 | + | |
4 | +#include <linux/mutex.h> | |
5 | +#include <linux/types.h> | |
6 | +#include "iso-resources.h" | |
7 | + | |
8 | +struct fw_unit; | |
9 | + | |
10 | +/** | |
11 | + * struct cmp_connection - manages an isochronous connection to a device | |
12 | + * @speed: the connection's actual speed | |
13 | + * | |
14 | + * This structure manages (using CMP) an isochronous stream from the local | |
15 | + * computer to a device's input plug (iPCR). | |
16 | + * | |
17 | + * There is no corresponding oPCR created on the local computer, so it is not | |
18 | + * possible to overlay connections on top of this one. | |
19 | + */ | |
20 | +struct cmp_connection { | |
21 | + int speed; | |
22 | + /* private: */ | |
23 | + bool connected; | |
24 | + struct mutex mutex; | |
25 | + struct fw_iso_resources resources; | |
26 | + __be32 last_pcr_value; | |
27 | + unsigned int pcr_index; | |
28 | + unsigned int max_speed; | |
29 | +}; | |
30 | + | |
31 | +int cmp_connection_init(struct cmp_connection *connection, | |
32 | + struct fw_unit *unit, | |
33 | + unsigned int ipcr_index); | |
34 | +void cmp_connection_destroy(struct cmp_connection *connection); | |
35 | + | |
36 | +int cmp_connection_establish(struct cmp_connection *connection, | |
37 | + unsigned int max_payload); | |
38 | +int cmp_connection_update(struct cmp_connection *connection); | |
39 | +void cmp_connection_break(struct cmp_connection *connection); | |
40 | + | |
41 | +#endif |
sound/firewire/fcp.c
1 | +/* | |
2 | + * Function Control Protocol (IEC 61883-1) helper functions | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/device.h> | |
9 | +#include <linux/firewire.h> | |
10 | +#include <linux/firewire-constants.h> | |
11 | +#include <linux/list.h> | |
12 | +#include <linux/module.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/spinlock.h> | |
15 | +#include <linux/wait.h> | |
16 | +#include "fcp.h" | |
17 | +#include "lib.h" | |
18 | + | |
19 | +#define CTS_AVC 0x00 | |
20 | + | |
21 | +#define ERROR_RETRIES 3 | |
22 | +#define ERROR_DELAY_MS 5 | |
23 | +#define FCP_TIMEOUT_MS 125 | |
24 | + | |
25 | +static DEFINE_SPINLOCK(transactions_lock); | |
26 | +static LIST_HEAD(transactions); | |
27 | + | |
28 | +enum fcp_state { | |
29 | + STATE_PENDING, | |
30 | + STATE_BUS_RESET, | |
31 | + STATE_COMPLETE, | |
32 | +}; | |
33 | + | |
34 | +struct fcp_transaction { | |
35 | + struct list_head list; | |
36 | + struct fw_unit *unit; | |
37 | + void *response_buffer; | |
38 | + unsigned int response_size; | |
39 | + unsigned int response_match_bytes; | |
40 | + enum fcp_state state; | |
41 | + wait_queue_head_t wait; | |
42 | +}; | |
43 | + | |
44 | +/** | |
45 | + * fcp_avc_transaction - send an AV/C command and wait for its response | |
46 | + * @unit: a unit on the target device | |
47 | + * @command: a buffer containing the command frame; must be DMA-able | |
48 | + * @command_size: the size of @command | |
49 | + * @response: a buffer for the response frame | |
50 | + * @response_size: the maximum size of @response | |
51 | + * @response_match_bytes: a bitmap specifying the bytes used to detect the | |
52 | + * correct response frame | |
53 | + * | |
54 | + * This function sends a FCP command frame to the target and waits for the | |
55 | + * corresponding response frame to be returned. | |
56 | + * | |
57 | + * Because it is possible for multiple FCP transactions to be active at the | |
58 | + * same time, the correct response frame is detected by the value of certain | |
59 | + * bytes. These bytes must be set in @response before calling this function, | |
60 | + * and the corresponding bits must be set in @response_match_bytes. | |
61 | + * | |
62 | + * @command and @response can point to the same buffer. | |
63 | + * | |
64 | + * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment. | |
65 | + * | |
66 | + * Returns the actual size of the response frame, or a negative error code. | |
67 | + */ | |
68 | +int fcp_avc_transaction(struct fw_unit *unit, | |
69 | + const void *command, unsigned int command_size, | |
70 | + void *response, unsigned int response_size, | |
71 | + unsigned int response_match_bytes) | |
72 | +{ | |
73 | + struct fcp_transaction t; | |
74 | + int tcode, ret, tries = 0; | |
75 | + | |
76 | + t.unit = unit; | |
77 | + t.response_buffer = response; | |
78 | + t.response_size = response_size; | |
79 | + t.response_match_bytes = response_match_bytes; | |
80 | + t.state = STATE_PENDING; | |
81 | + init_waitqueue_head(&t.wait); | |
82 | + | |
83 | + spin_lock_irq(&transactions_lock); | |
84 | + list_add_tail(&t.list, &transactions); | |
85 | + spin_unlock_irq(&transactions_lock); | |
86 | + | |
87 | + for (;;) { | |
88 | + tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST | |
89 | + : TCODE_WRITE_BLOCK_REQUEST; | |
90 | + ret = snd_fw_transaction(t.unit, tcode, | |
91 | + CSR_REGISTER_BASE + CSR_FCP_COMMAND, | |
92 | + (void *)command, command_size); | |
93 | + if (ret < 0) | |
94 | + break; | |
95 | + | |
96 | + wait_event_timeout(t.wait, t.state != STATE_PENDING, | |
97 | + msecs_to_jiffies(FCP_TIMEOUT_MS)); | |
98 | + | |
99 | + if (t.state == STATE_COMPLETE) { | |
100 | + ret = t.response_size; | |
101 | + break; | |
102 | + } else if (t.state == STATE_BUS_RESET) { | |
103 | + msleep(ERROR_DELAY_MS); | |
104 | + } else if (++tries >= ERROR_RETRIES) { | |
105 | + dev_err(&t.unit->device, "FCP command timed out\n"); | |
106 | + ret = -EIO; | |
107 | + break; | |
108 | + } | |
109 | + } | |
110 | + | |
111 | + spin_lock_irq(&transactions_lock); | |
112 | + list_del(&t.list); | |
113 | + spin_unlock_irq(&transactions_lock); | |
114 | + | |
115 | + return ret; | |
116 | +} | |
117 | +EXPORT_SYMBOL(fcp_avc_transaction); | |
118 | + | |
119 | +/** | |
120 | + * fcp_bus_reset - inform the target handler about a bus reset | |
121 | + * @unit: the unit that might be used by fcp_avc_transaction() | |
122 | + * | |
123 | + * This function must be called from the driver's .update handler to inform | |
124 | + * the FCP transaction handler that a bus reset has happened. Any pending FCP | |
125 | + * transactions are retried. | |
126 | + */ | |
127 | +void fcp_bus_reset(struct fw_unit *unit) | |
128 | +{ | |
129 | + struct fcp_transaction *t; | |
130 | + | |
131 | + spin_lock_irq(&transactions_lock); | |
132 | + list_for_each_entry(t, &transactions, list) { | |
133 | + if (t->unit == unit && | |
134 | + t->state == STATE_PENDING) { | |
135 | + t->state = STATE_BUS_RESET; | |
136 | + wake_up(&t->wait); | |
137 | + } | |
138 | + } | |
139 | + spin_unlock_irq(&transactions_lock); | |
140 | +} | |
141 | +EXPORT_SYMBOL(fcp_bus_reset); | |
142 | + | |
143 | +/* checks whether the response matches the masked bytes in response_buffer */ | |
144 | +static bool is_matching_response(struct fcp_transaction *transaction, | |
145 | + const void *response, size_t length) | |
146 | +{ | |
147 | + const u8 *p1, *p2; | |
148 | + unsigned int mask, i; | |
149 | + | |
150 | + p1 = response; | |
151 | + p2 = transaction->response_buffer; | |
152 | + mask = transaction->response_match_bytes; | |
153 | + | |
154 | + for (i = 0; ; ++i) { | |
155 | + if ((mask & 1) && p1[i] != p2[i]) | |
156 | + return false; | |
157 | + mask >>= 1; | |
158 | + if (!mask) | |
159 | + return true; | |
160 | + if (--length == 0) | |
161 | + return false; | |
162 | + } | |
163 | +} | |
164 | + | |
165 | +static void fcp_response(struct fw_card *card, struct fw_request *request, | |
166 | + int tcode, int destination, int source, | |
167 | + int generation, unsigned long long offset, | |
168 | + void *data, size_t length, void *callback_data) | |
169 | +{ | |
170 | + struct fcp_transaction *t; | |
171 | + unsigned long flags; | |
172 | + | |
173 | + if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC) | |
174 | + return; | |
175 | + | |
176 | + spin_lock_irqsave(&transactions_lock, flags); | |
177 | + list_for_each_entry(t, &transactions, list) { | |
178 | + struct fw_device *device = fw_parent_device(t->unit); | |
179 | + if (device->card != card || | |
180 | + device->generation != generation) | |
181 | + continue; | |
182 | + smp_rmb(); /* node_id vs. generation */ | |
183 | + if (device->node_id != source) | |
184 | + continue; | |
185 | + | |
186 | + if (t->state == STATE_PENDING && | |
187 | + is_matching_response(t, data, length)) { | |
188 | + t->state = STATE_COMPLETE; | |
189 | + t->response_size = min((unsigned int)length, | |
190 | + t->response_size); | |
191 | + memcpy(t->response_buffer, data, t->response_size); | |
192 | + wake_up(&t->wait); | |
193 | + } | |
194 | + } | |
195 | + spin_unlock_irqrestore(&transactions_lock, flags); | |
196 | +} | |
197 | + | |
198 | +static struct fw_address_handler response_register_handler = { | |
199 | + .length = 0x200, | |
200 | + .address_callback = fcp_response, | |
201 | +}; | |
202 | + | |
203 | +static int __init fcp_module_init(void) | |
204 | +{ | |
205 | + static const struct fw_address_region response_register_region = { | |
206 | + .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, | |
207 | + .end = CSR_REGISTER_BASE + CSR_FCP_END, | |
208 | + }; | |
209 | + | |
210 | + fw_core_add_address_handler(&response_register_handler, | |
211 | + &response_register_region); | |
212 | + | |
213 | + return 0; | |
214 | +} | |
215 | + | |
216 | +static void __exit fcp_module_exit(void) | |
217 | +{ | |
218 | + WARN_ON(!list_empty(&transactions)); | |
219 | + fw_core_remove_address_handler(&response_register_handler); | |
220 | +} | |
221 | + | |
222 | +module_init(fcp_module_init); | |
223 | +module_exit(fcp_module_exit); |
sound/firewire/fcp.h
1 | +#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_FCP_H_INCLUDED | |
3 | + | |
4 | +struct fw_unit; | |
5 | + | |
6 | +int fcp_avc_transaction(struct fw_unit *unit, | |
7 | + const void *command, unsigned int command_size, | |
8 | + void *response, unsigned int response_size, | |
9 | + unsigned int response_match_bytes); | |
10 | +void fcp_bus_reset(struct fw_unit *unit); | |
11 | + | |
12 | +#endif |
sound/firewire/iso-resources.c
1 | +/* | |
2 | + * isochronous resources helper functions | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/device.h> | |
9 | +#include <linux/firewire.h> | |
10 | +#include <linux/firewire-constants.h> | |
11 | +#include <linux/jiffies.h> | |
12 | +#include <linux/mutex.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/spinlock.h> | |
15 | +#include "iso-resources.h" | |
16 | + | |
17 | +/** | |
18 | + * fw_iso_resources_init - initializes a &struct fw_iso_resources | |
19 | + * @r: the resource manager to initialize | |
20 | + * @unit: the device unit for which the resources will be needed | |
21 | + * | |
22 | + * If the device does not support all channel numbers, change @r->channels_mask | |
23 | + * after calling this function. | |
24 | + */ | |
25 | +void fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) | |
26 | +{ | |
27 | + r->channels_mask = ~0uLL; | |
28 | + r->unit = fw_unit_get(unit); | |
29 | + mutex_init(&r->mutex); | |
30 | + r->allocated = false; | |
31 | +} | |
32 | + | |
33 | +/** | |
34 | + * fw_iso_resources_destroy - destroy a resource manager | |
35 | + * @r: the resource manager that is no longer needed | |
36 | + */ | |
37 | +void fw_iso_resources_destroy(struct fw_iso_resources *r) | |
38 | +{ | |
39 | + WARN_ON(r->allocated); | |
40 | + mutex_destroy(&r->mutex); | |
41 | + fw_unit_put(r->unit); | |
42 | +} | |
43 | + | |
44 | +static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) | |
45 | +{ | |
46 | + unsigned int bytes, s400_bytes; | |
47 | + | |
48 | + /* iso packets have three header quadlets and quadlet-aligned payload */ | |
49 | + bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); | |
50 | + | |
51 | + /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ | |
52 | + if (speed <= SCODE_400) | |
53 | + s400_bytes = bytes * (1 << (SCODE_400 - speed)); | |
54 | + else | |
55 | + s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); | |
56 | + | |
57 | + return s400_bytes; | |
58 | +} | |
59 | + | |
60 | +static int current_bandwidth_overhead(struct fw_card *card) | |
61 | +{ | |
62 | + /* | |
63 | + * Under the usual pessimistic assumption (cable length 4.5 m), the | |
64 | + * isochronous overhead for N cables is 1.797 ยตs + N * 0.494 ยตs, or | |
65 | + * 88.3 + N * 24.3 in bandwidth units. | |
66 | + * | |
67 | + * The calculation below tries to deduce N from the current gap count. | |
68 | + * If the gap count has been optimized by measuring the actual packet | |
69 | + * transmission time, this derived overhead should be near the actual | |
70 | + * overhead as well. | |
71 | + */ | |
72 | + return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; | |
73 | +} | |
74 | + | |
75 | +static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) | |
76 | +{ | |
77 | + for (;;) { | |
78 | + s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); | |
79 | + if (delay <= 0) | |
80 | + return 0; | |
81 | + if (schedule_timeout_interruptible(delay) > 0) | |
82 | + return -ERESTARTSYS; | |
83 | + } | |
84 | +} | |
85 | + | |
86 | +/** | |
87 | + * fw_iso_resources_allocate - allocate isochronous channel and bandwidth | |
88 | + * @r: the resource manager | |
89 | + * @max_payload_bytes: the amount of data (including CIP headers) per packet | |
90 | + * @speed: the speed (e.g., SCODE_400) at which the packets will be sent | |
91 | + * | |
92 | + * This function allocates one isochronous channel and enough bandwidth for the | |
93 | + * specified packet size. | |
94 | + * | |
95 | + * Returns the channel number that the caller must use for streaming, or | |
96 | + * a negative error code. Due to potentionally long delays, this function is | |
97 | + * interruptible and can return -ERESTARTSYS. On success, the caller is | |
98 | + * responsible for calling fw_iso_resources_update() on bus resets, and | |
99 | + * fw_iso_resources_free() when the resources are not longer needed. | |
100 | + */ | |
101 | +int fw_iso_resources_allocate(struct fw_iso_resources *r, | |
102 | + unsigned int max_payload_bytes, int speed) | |
103 | +{ | |
104 | + struct fw_card *card = fw_parent_device(r->unit)->card; | |
105 | + int bandwidth, channel, err; | |
106 | + | |
107 | + if (WARN_ON(r->allocated)) | |
108 | + return -EBADFD; | |
109 | + | |
110 | + r->bandwidth = packet_bandwidth(max_payload_bytes, speed); | |
111 | + | |
112 | +retry_after_bus_reset: | |
113 | + spin_lock_irq(&card->lock); | |
114 | + r->generation = card->generation; | |
115 | + r->bandwidth_overhead = current_bandwidth_overhead(card); | |
116 | + spin_unlock_irq(&card->lock); | |
117 | + | |
118 | + err = wait_isoch_resource_delay_after_bus_reset(card); | |
119 | + if (err < 0) | |
120 | + return err; | |
121 | + | |
122 | + mutex_lock(&r->mutex); | |
123 | + | |
124 | + bandwidth = r->bandwidth + r->bandwidth_overhead; | |
125 | + fw_iso_resource_manage(card, r->generation, r->channels_mask, | |
126 | + &channel, &bandwidth, true, r->buffer); | |
127 | + if (channel == -EAGAIN) { | |
128 | + mutex_unlock(&r->mutex); | |
129 | + goto retry_after_bus_reset; | |
130 | + } | |
131 | + if (channel >= 0) { | |
132 | + r->channel = channel; | |
133 | + r->allocated = true; | |
134 | + } else { | |
135 | + if (channel == -EBUSY) | |
136 | + dev_err(&r->unit->device, | |
137 | + "isochronous resources exhausted\n"); | |
138 | + else | |
139 | + dev_err(&r->unit->device, | |
140 | + "isochronous resource allocation failed\n"); | |
141 | + } | |
142 | + | |
143 | + mutex_unlock(&r->mutex); | |
144 | + | |
145 | + return channel; | |
146 | +} | |
147 | + | |
148 | +/** | |
149 | + * fw_iso_resources_update - update resource allocations after a bus reset | |
150 | + * @r: the resource manager | |
151 | + * | |
152 | + * This function must be called from the driver's .update handler to reallocate | |
153 | + * any resources that were allocated before the bus reset. It is safe to call | |
154 | + * this function if no resources are currently allocated. | |
155 | + * | |
156 | + * Returns a negative error code on failure. If this happens, the caller must | |
157 | + * stop streaming. | |
158 | + */ | |
159 | +int fw_iso_resources_update(struct fw_iso_resources *r) | |
160 | +{ | |
161 | + struct fw_card *card = fw_parent_device(r->unit)->card; | |
162 | + int bandwidth, channel; | |
163 | + | |
164 | + mutex_lock(&r->mutex); | |
165 | + | |
166 | + if (!r->allocated) { | |
167 | + mutex_unlock(&r->mutex); | |
168 | + return 0; | |
169 | + } | |
170 | + | |
171 | + spin_lock_irq(&card->lock); | |
172 | + r->generation = card->generation; | |
173 | + r->bandwidth_overhead = current_bandwidth_overhead(card); | |
174 | + spin_unlock_irq(&card->lock); | |
175 | + | |
176 | + bandwidth = r->bandwidth + r->bandwidth_overhead; | |
177 | + | |
178 | + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, | |
179 | + &channel, &bandwidth, true, r->buffer); | |
180 | + /* | |
181 | + * When another bus reset happens, pretend that the allocation | |
182 | + * succeeded; we will try again for the new generation later. | |
183 | + */ | |
184 | + if (channel < 0 && channel != -EAGAIN) { | |
185 | + r->allocated = false; | |
186 | + if (channel == -EBUSY) | |
187 | + dev_err(&r->unit->device, | |
188 | + "isochronous resources exhausted\n"); | |
189 | + else | |
190 | + dev_err(&r->unit->device, | |
191 | + "isochronous resource allocation failed\n"); | |
192 | + } | |
193 | + | |
194 | + mutex_unlock(&r->mutex); | |
195 | + | |
196 | + return channel; | |
197 | +} | |
198 | + | |
199 | +/** | |
200 | + * fw_iso_resources_free - frees allocated resources | |
201 | + * @r: the resource manager | |
202 | + * | |
203 | + * This function deallocates the channel and bandwidth, if allocated. | |
204 | + */ | |
205 | +void fw_iso_resources_free(struct fw_iso_resources *r) | |
206 | +{ | |
207 | + struct fw_card *card = fw_parent_device(r->unit)->card; | |
208 | + int bandwidth, channel; | |
209 | + | |
210 | + mutex_lock(&r->mutex); | |
211 | + | |
212 | + if (r->allocated) { | |
213 | + bandwidth = r->bandwidth + r->bandwidth_overhead; | |
214 | + fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, | |
215 | + &channel, &bandwidth, false, r->buffer); | |
216 | + if (channel < 0) | |
217 | + dev_err(&r->unit->device, | |
218 | + "isochronous resource deallocation failed\n"); | |
219 | + | |
220 | + r->allocated = false; | |
221 | + } | |
222 | + | |
223 | + mutex_unlock(&r->mutex); | |
224 | +} |
sound/firewire/iso-resources.h
1 | +#ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED | |
3 | + | |
4 | +#include <linux/mutex.h> | |
5 | +#include <linux/types.h> | |
6 | + | |
7 | +struct fw_unit; | |
8 | + | |
9 | +/** | |
10 | + * struct fw_iso_resources - manages channel/bandwidth allocation | |
11 | + * @channels_mask: if the device does not support all channel numbers, set this | |
12 | + * bit mask to something else than the default (all ones) | |
13 | + * | |
14 | + * This structure manages (de)allocation of isochronous resources (channel and | |
15 | + * bandwidth) for one isochronous stream. | |
16 | + */ | |
17 | +struct fw_iso_resources { | |
18 | + u64 channels_mask; | |
19 | + /* private: */ | |
20 | + struct fw_unit *unit; | |
21 | + struct mutex mutex; | |
22 | + unsigned int channel; | |
23 | + unsigned int bandwidth; /* in bandwidth units, without overhead */ | |
24 | + unsigned int bandwidth_overhead; | |
25 | + int generation; /* in which allocation is valid */ | |
26 | + bool allocated; | |
27 | + __be32 buffer[2]; | |
28 | +}; | |
29 | + | |
30 | +void fw_iso_resources_init(struct fw_iso_resources *r, | |
31 | + struct fw_unit *unit); | |
32 | +void fw_iso_resources_destroy(struct fw_iso_resources *r); | |
33 | + | |
34 | +int fw_iso_resources_allocate(struct fw_iso_resources *r, | |
35 | + unsigned int max_payload_bytes, int speed); | |
36 | +int fw_iso_resources_update(struct fw_iso_resources *r); | |
37 | +void fw_iso_resources_free(struct fw_iso_resources *r); | |
38 | + | |
39 | +#endif |
sound/firewire/lib.c
1 | +/* | |
2 | + * miscellaneous helper functions | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/delay.h> | |
9 | +#include <linux/device.h> | |
10 | +#include <linux/firewire.h> | |
11 | +#include <linux/module.h> | |
12 | +#include "lib.h" | |
13 | + | |
14 | +#define ERROR_RETRY_DELAY_MS 5 | |
15 | + | |
16 | +/** | |
17 | + * rcode_string - convert a firewire result code to a string | |
18 | + * @rcode: the result | |
19 | + */ | |
20 | +const char *rcode_string(unsigned int rcode) | |
21 | +{ | |
22 | + static const char *const names[] = { | |
23 | + [RCODE_COMPLETE] = "complete", | |
24 | + [RCODE_CONFLICT_ERROR] = "conflict error", | |
25 | + [RCODE_DATA_ERROR] = "data error", | |
26 | + [RCODE_TYPE_ERROR] = "type error", | |
27 | + [RCODE_ADDRESS_ERROR] = "address error", | |
28 | + [RCODE_SEND_ERROR] = "send error", | |
29 | + [RCODE_CANCELLED] = "cancelled", | |
30 | + [RCODE_BUSY] = "busy", | |
31 | + [RCODE_GENERATION] = "generation", | |
32 | + [RCODE_NO_ACK] = "no ack", | |
33 | + }; | |
34 | + | |
35 | + if (rcode < ARRAY_SIZE(names) && names[rcode]) | |
36 | + return names[rcode]; | |
37 | + else | |
38 | + return "unknown"; | |
39 | +} | |
40 | +EXPORT_SYMBOL(rcode_string); | |
41 | + | |
42 | +/** | |
43 | + * snd_fw_transaction - send a request and wait for its completion | |
44 | + * @unit: the driver's unit on the target device | |
45 | + * @tcode: the transaction code | |
46 | + * @offset: the address in the target's address space | |
47 | + * @buffer: input/output data | |
48 | + * @length: length of @buffer | |
49 | + * | |
50 | + * Submits an asynchronous request to the target device, and waits for the | |
51 | + * response. The node ID and the current generation are derived from @unit. | |
52 | + * On a bus reset or an error, the transaction is retried a few times. | |
53 | + * Returns zero on success, or a negative error code. | |
54 | + */ | |
55 | +int snd_fw_transaction(struct fw_unit *unit, int tcode, | |
56 | + u64 offset, void *buffer, size_t length) | |
57 | +{ | |
58 | + struct fw_device *device = fw_parent_device(unit); | |
59 | + int generation, rcode, tries = 0; | |
60 | + | |
61 | + for (;;) { | |
62 | + generation = device->generation; | |
63 | + smp_rmb(); /* node_id vs. generation */ | |
64 | + rcode = fw_run_transaction(device->card, tcode, | |
65 | + device->node_id, generation, | |
66 | + device->max_speed, offset, | |
67 | + buffer, length); | |
68 | + | |
69 | + if (rcode == RCODE_COMPLETE) | |
70 | + return 0; | |
71 | + | |
72 | + if (rcode_is_permanent_error(rcode) || ++tries >= 3) { | |
73 | + dev_err(&unit->device, "transaction failed: %s\n", | |
74 | + rcode_string(rcode)); | |
75 | + return -EIO; | |
76 | + } | |
77 | + | |
78 | + msleep(ERROR_RETRY_DELAY_MS); | |
79 | + } | |
80 | +} | |
81 | +EXPORT_SYMBOL(snd_fw_transaction); | |
82 | + | |
83 | +MODULE_DESCRIPTION("FireWire audio helper functions"); | |
84 | +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | |
85 | +MODULE_LICENSE("GPL v2"); |
sound/firewire/lib.h
1 | +#ifndef SOUND_FIREWIRE_LIB_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_LIB_H_INCLUDED | |
3 | + | |
4 | +#include <linux/firewire-constants.h> | |
5 | +#include <linux/types.h> | |
6 | + | |
7 | +struct fw_unit; | |
8 | + | |
9 | +int snd_fw_transaction(struct fw_unit *unit, int tcode, | |
10 | + u64 offset, void *buffer, size_t length); | |
11 | +const char *rcode_string(unsigned int rcode); | |
12 | + | |
13 | +/* returns true if retrying the transaction would not make sense */ | |
14 | +static inline bool rcode_is_permanent_error(int rcode) | |
15 | +{ | |
16 | + return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; | |
17 | +} | |
18 | + | |
19 | +#endif |
sound/firewire/packets-buffer.c
1 | +/* | |
2 | + * helpers for managing a buffer for many packets | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/firewire.h> | |
9 | +#include <linux/slab.h> | |
10 | +#include "packets-buffer.h" | |
11 | + | |
12 | +/** | |
13 | + * iso_packets_buffer_init - allocates the memory for packets | |
14 | + * @b: the buffer structure to initialize | |
15 | + * @unit: the device at the other end of the stream | |
16 | + * @count: the number of packets | |
17 | + * @packet_size: the (maximum) size of a packet, in bytes | |
18 | + * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE | |
19 | + */ | |
20 | +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, | |
21 | + unsigned int count, unsigned int packet_size, | |
22 | + enum dma_data_direction direction) | |
23 | +{ | |
24 | + unsigned int packets_per_page, pages; | |
25 | + unsigned int i, page_index, offset_in_page; | |
26 | + void *p; | |
27 | + int err; | |
28 | + | |
29 | + b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL); | |
30 | + if (!b->packets) { | |
31 | + err = -ENOMEM; | |
32 | + goto error; | |
33 | + } | |
34 | + | |
35 | + packet_size = L1_CACHE_ALIGN(packet_size); | |
36 | + packets_per_page = PAGE_SIZE / packet_size; | |
37 | + if (WARN_ON(!packets_per_page)) { | |
38 | + err = -EINVAL; | |
39 | + goto error; | |
40 | + } | |
41 | + pages = DIV_ROUND_UP(count, packets_per_page); | |
42 | + | |
43 | + err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card, | |
44 | + pages, direction); | |
45 | + if (err < 0) | |
46 | + goto err_packets; | |
47 | + | |
48 | + for (i = 0; i < count; ++i) { | |
49 | + page_index = i / packets_per_page; | |
50 | + p = page_address(b->iso_buffer.pages[page_index]); | |
51 | + offset_in_page = (i % packets_per_page) * packet_size; | |
52 | + b->packets[i].buffer = p + offset_in_page; | |
53 | + b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page; | |
54 | + } | |
55 | + | |
56 | + return 0; | |
57 | + | |
58 | +err_packets: | |
59 | + kfree(b->packets); | |
60 | +error: | |
61 | + return err; | |
62 | +} | |
63 | + | |
64 | +/** | |
65 | + * iso_packets_buffer_destroy - frees packet buffer resources | |
66 | + * @b: the buffer structure to free | |
67 | + * @unit: the device at the other end of the stream | |
68 | + */ | |
69 | +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, | |
70 | + struct fw_unit *unit) | |
71 | +{ | |
72 | + fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); | |
73 | + kfree(b->packets); | |
74 | +} |
sound/firewire/packets-buffer.h
1 | +#ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED | |
2 | +#define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED | |
3 | + | |
4 | +#include <linux/dma-mapping.h> | |
5 | +#include <linux/firewire.h> | |
6 | + | |
7 | +/** | |
8 | + * struct iso_packets_buffer - manages a buffer for many packets | |
9 | + * @iso_buffer: the memory containing the packets | |
10 | + * @packets: an array, with each element pointing to one packet | |
11 | + */ | |
12 | +struct iso_packets_buffer { | |
13 | + struct fw_iso_buffer iso_buffer; | |
14 | + struct { | |
15 | + void *buffer; | |
16 | + unsigned int offset; | |
17 | + } *packets; | |
18 | +}; | |
19 | + | |
20 | +int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, | |
21 | + unsigned int count, unsigned int packet_size, | |
22 | + enum dma_data_direction direction); | |
23 | +void iso_packets_buffer_destroy(struct iso_packets_buffer *b, | |
24 | + struct fw_unit *unit); | |
25 | + | |
26 | +#endif |
sound/firewire/speakers.c
1 | +/* | |
2 | + * OXFW970-based speakers driver | |
3 | + * | |
4 | + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | |
5 | + * Licensed under the terms of the GNU General Public License, version 2. | |
6 | + */ | |
7 | + | |
8 | +#include <linux/device.h> | |
9 | +#include <linux/firewire.h> | |
10 | +#include <linux/firewire-constants.h> | |
11 | +#include <linux/module.h> | |
12 | +#include <linux/mod_devicetable.h> | |
13 | +#include <linux/mutex.h> | |
14 | +#include <linux/slab.h> | |
15 | +#include <sound/control.h> | |
16 | +#include <sound/core.h> | |
17 | +#include <sound/initval.h> | |
18 | +#include <sound/pcm.h> | |
19 | +#include <sound/pcm_params.h> | |
20 | +#include "cmp.h" | |
21 | +#include "fcp.h" | |
22 | +#include "amdtp.h" | |
23 | +#include "lib.h" | |
24 | + | |
25 | +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) | |
26 | +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ | |
27 | + | |
28 | +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) | |
29 | +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 | |
30 | +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 | |
31 | + | |
32 | +#define VENDOR_GRIFFIN 0x001292 | |
33 | +#define VENDOR_LACIE 0x00d04b | |
34 | + | |
35 | +#define SPECIFIER_1394TA 0x00a02d | |
36 | +#define VERSION_AVC 0x010001 | |
37 | + | |
38 | +struct device_info { | |
39 | + const char *driver_name; | |
40 | + const char *short_name; | |
41 | + const char *long_name; | |
42 | + int (*pcm_constraints)(struct snd_pcm_runtime *runtime); | |
43 | + unsigned int mixer_channels; | |
44 | + u8 mute_fb_id; | |
45 | + u8 volume_fb_id; | |
46 | +}; | |
47 | + | |
48 | +struct fwspk { | |
49 | + struct snd_card *card; | |
50 | + struct fw_unit *unit; | |
51 | + const struct device_info *device_info; | |
52 | + struct snd_pcm_substream *pcm; | |
53 | + struct mutex mutex; | |
54 | + struct cmp_connection connection; | |
55 | + struct amdtp_out_stream stream; | |
56 | + bool stream_running; | |
57 | + bool mute; | |
58 | + s16 volume[6]; | |
59 | + s16 volume_min; | |
60 | + s16 volume_max; | |
61 | +}; | |
62 | + | |
63 | +MODULE_DESCRIPTION("FireWire speakers driver"); | |
64 | +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | |
65 | +MODULE_LICENSE("GPL v2"); | |
66 | + | |
67 | +static int firewave_rate_constraint(struct snd_pcm_hw_params *params, | |
68 | + struct snd_pcm_hw_rule *rule) | |
69 | +{ | |
70 | + static unsigned int stereo_rates[] = { 48000, 96000 }; | |
71 | + struct snd_interval *channels = | |
72 | + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | |
73 | + struct snd_interval *rate = | |
74 | + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | |
75 | + | |
76 | + /* two channels work only at 48/96 kHz */ | |
77 | + if (snd_interval_max(channels) < 6) | |
78 | + return snd_interval_list(rate, 2, stereo_rates, 0); | |
79 | + return 0; | |
80 | +} | |
81 | + | |
82 | +static int firewave_channels_constraint(struct snd_pcm_hw_params *params, | |
83 | + struct snd_pcm_hw_rule *rule) | |
84 | +{ | |
85 | + static const struct snd_interval all_channels = { .min = 6, .max = 6 }; | |
86 | + struct snd_interval *rate = | |
87 | + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | |
88 | + struct snd_interval *channels = | |
89 | + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | |
90 | + | |
91 | + /* 32/44.1 kHz work only with all six channels */ | |
92 | + if (snd_interval_max(rate) < 48000) | |
93 | + return snd_interval_refine(channels, &all_channels); | |
94 | + return 0; | |
95 | +} | |
96 | + | |
97 | +static int firewave_constraints(struct snd_pcm_runtime *runtime) | |
98 | +{ | |
99 | + static unsigned int channels_list[] = { 2, 6 }; | |
100 | + static struct snd_pcm_hw_constraint_list channels_list_constraint = { | |
101 | + .count = 2, | |
102 | + .list = channels_list, | |
103 | + }; | |
104 | + int err; | |
105 | + | |
106 | + runtime->hw.rates = SNDRV_PCM_RATE_32000 | | |
107 | + SNDRV_PCM_RATE_44100 | | |
108 | + SNDRV_PCM_RATE_48000 | | |
109 | + SNDRV_PCM_RATE_96000; | |
110 | + runtime->hw.channels_max = 6; | |
111 | + | |
112 | + err = snd_pcm_hw_constraint_list(runtime, 0, | |
113 | + SNDRV_PCM_HW_PARAM_CHANNELS, | |
114 | + &channels_list_constraint); | |
115 | + if (err < 0) | |
116 | + return err; | |
117 | + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | |
118 | + firewave_rate_constraint, NULL, | |
119 | + SNDRV_PCM_HW_PARAM_CHANNELS, -1); | |
120 | + if (err < 0) | |
121 | + return err; | |
122 | + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | |
123 | + firewave_channels_constraint, NULL, | |
124 | + SNDRV_PCM_HW_PARAM_RATE, -1); | |
125 | + if (err < 0) | |
126 | + return err; | |
127 | + | |
128 | + return 0; | |
129 | +} | |
130 | + | |
131 | +static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) | |
132 | +{ | |
133 | + runtime->hw.rates = SNDRV_PCM_RATE_32000 | | |
134 | + SNDRV_PCM_RATE_44100 | | |
135 | + SNDRV_PCM_RATE_48000 | | |
136 | + SNDRV_PCM_RATE_88200 | | |
137 | + SNDRV_PCM_RATE_96000; | |
138 | + | |
139 | + return 0; | |
140 | +} | |
141 | + | |
142 | +static int fwspk_open(struct snd_pcm_substream *substream) | |
143 | +{ | |
144 | + static const struct snd_pcm_hardware hardware = { | |
145 | + .info = SNDRV_PCM_INFO_MMAP | | |
146 | + SNDRV_PCM_INFO_MMAP_VALID | | |
147 | + SNDRV_PCM_INFO_BATCH | | |
148 | + SNDRV_PCM_INFO_INTERLEAVED | | |
149 | + SNDRV_PCM_INFO_BLOCK_TRANSFER, | |
150 | + .formats = AMDTP_OUT_PCM_FORMAT_BITS, | |
151 | + .channels_min = 2, | |
152 | + .channels_max = 2, | |
153 | + .buffer_bytes_max = 4 * 1024 * 1024, | |
154 | + .period_bytes_min = 1, | |
155 | + .period_bytes_max = UINT_MAX, | |
156 | + .periods_min = 1, | |
157 | + .periods_max = UINT_MAX, | |
158 | + }; | |
159 | + struct fwspk *fwspk = substream->private_data; | |
160 | + struct snd_pcm_runtime *runtime = substream->runtime; | |
161 | + int err; | |
162 | + | |
163 | + runtime->hw = hardware; | |
164 | + | |
165 | + err = fwspk->device_info->pcm_constraints(runtime); | |
166 | + if (err < 0) | |
167 | + return err; | |
168 | + err = snd_pcm_limit_hw_rates(runtime); | |
169 | + if (err < 0) | |
170 | + return err; | |
171 | + | |
172 | + err = snd_pcm_hw_constraint_minmax(runtime, | |
173 | + SNDRV_PCM_HW_PARAM_PERIOD_TIME, | |
174 | + 5000, 8192000); | |
175 | + if (err < 0) | |
176 | + return err; | |
177 | + | |
178 | + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); | |
179 | + if (err < 0) | |
180 | + return err; | |
181 | + | |
182 | + return 0; | |
183 | +} | |
184 | + | |
185 | +static int fwspk_close(struct snd_pcm_substream *substream) | |
186 | +{ | |
187 | + return 0; | |
188 | +} | |
189 | + | |
190 | +static void fwspk_stop_stream(struct fwspk *fwspk) | |
191 | +{ | |
192 | + if (fwspk->stream_running) { | |
193 | + amdtp_out_stream_stop(&fwspk->stream); | |
194 | + cmp_connection_break(&fwspk->connection); | |
195 | + fwspk->stream_running = false; | |
196 | + } | |
197 | +} | |
198 | + | |
199 | +static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) | |
200 | +{ | |
201 | + u8 *buf; | |
202 | + int err; | |
203 | + | |
204 | + buf = kmalloc(8, GFP_KERNEL); | |
205 | + if (!buf) | |
206 | + return -ENOMEM; | |
207 | + | |
208 | + buf[0] = 0x00; /* AV/C, CONTROL */ | |
209 | + buf[1] = 0xff; /* unit */ | |
210 | + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ | |
211 | + buf[3] = 0x00; /* plug 0 */ | |
212 | + buf[4] = 0x90; /* format: audio */ | |
213 | + buf[5] = 0x00 | sfc; /* AM824, frequency */ | |
214 | + buf[6] = 0xff; /* SYT (not used) */ | |
215 | + buf[7] = 0xff; | |
216 | + | |
217 | + err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, | |
218 | + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); | |
219 | + if (err < 0) | |
220 | + goto error; | |
221 | + if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { | |
222 | + dev_err(&fwspk->unit->device, "failed to set sample rate\n"); | |
223 | + err = -EIO; | |
224 | + goto error; | |
225 | + } | |
226 | + | |
227 | + err = 0; | |
228 | + | |
229 | +error: | |
230 | + kfree(buf); | |
231 | + | |
232 | + return err; | |
233 | +} | |
234 | + | |
235 | +static int fwspk_hw_params(struct snd_pcm_substream *substream, | |
236 | + struct snd_pcm_hw_params *hw_params) | |
237 | +{ | |
238 | + struct fwspk *fwspk = substream->private_data; | |
239 | + int err; | |
240 | + | |
241 | + mutex_lock(&fwspk->mutex); | |
242 | + fwspk_stop_stream(fwspk); | |
243 | + mutex_unlock(&fwspk->mutex); | |
244 | + | |
245 | + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, | |
246 | + params_buffer_bytes(hw_params)); | |
247 | + if (err < 0) | |
248 | + goto error; | |
249 | + | |
250 | + amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); | |
251 | + amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); | |
252 | + | |
253 | + amdtp_out_stream_set_pcm_format(&fwspk->stream, | |
254 | + params_format(hw_params)); | |
255 | + | |
256 | + err = fwspk_set_rate(fwspk, fwspk->stream.sfc); | |
257 | + if (err < 0) | |
258 | + goto err_buffer; | |
259 | + | |
260 | + return 0; | |
261 | + | |
262 | +err_buffer: | |
263 | + snd_pcm_lib_free_vmalloc_buffer(substream); | |
264 | +error: | |
265 | + return err; | |
266 | +} | |
267 | + | |
268 | +static int fwspk_hw_free(struct snd_pcm_substream *substream) | |
269 | +{ | |
270 | + struct fwspk *fwspk = substream->private_data; | |
271 | + | |
272 | + mutex_lock(&fwspk->mutex); | |
273 | + fwspk_stop_stream(fwspk); | |
274 | + mutex_unlock(&fwspk->mutex); | |
275 | + | |
276 | + return snd_pcm_lib_free_vmalloc_buffer(substream); | |
277 | +} | |
278 | + | |
279 | +static int fwspk_prepare(struct snd_pcm_substream *substream) | |
280 | +{ | |
281 | + struct fwspk *fwspk = substream->private_data; | |
282 | + int err; | |
283 | + | |
284 | + mutex_lock(&fwspk->mutex); | |
285 | + | |
286 | + if (!fwspk->stream_running) { | |
287 | + err = cmp_connection_establish(&fwspk->connection, | |
288 | + amdtp_out_stream_get_max_payload(&fwspk->stream)); | |
289 | + if (err < 0) | |
290 | + goto err_mutex; | |
291 | + | |
292 | + err = amdtp_out_stream_start(&fwspk->stream, | |
293 | + fwspk->connection.resources.channel, | |
294 | + fwspk->connection.speed); | |
295 | + if (err < 0) | |
296 | + goto err_connection; | |
297 | + | |
298 | + fwspk->stream_running = true; | |
299 | + } | |
300 | + | |
301 | + mutex_unlock(&fwspk->mutex); | |
302 | + | |
303 | + amdtp_out_stream_pcm_prepare(&fwspk->stream); | |
304 | + | |
305 | + return 0; | |
306 | + | |
307 | +err_connection: | |
308 | + cmp_connection_break(&fwspk->connection); | |
309 | +err_mutex: | |
310 | + mutex_unlock(&fwspk->mutex); | |
311 | + | |
312 | + return err; | |
313 | +} | |
314 | + | |
315 | +static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) | |
316 | +{ | |
317 | + struct fwspk *fwspk = substream->private_data; | |
318 | + struct snd_pcm_substream *pcm; | |
319 | + | |
320 | + switch (cmd) { | |
321 | + case SNDRV_PCM_TRIGGER_START: | |
322 | + pcm = substream; | |
323 | + break; | |
324 | + case SNDRV_PCM_TRIGGER_STOP: | |
325 | + pcm = NULL; | |
326 | + break; | |
327 | + default: | |
328 | + return -EINVAL; | |
329 | + } | |
330 | + amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); | |
331 | + return 0; | |
332 | +} | |
333 | + | |
334 | +static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) | |
335 | +{ | |
336 | + struct fwspk *fwspk = substream->private_data; | |
337 | + | |
338 | + return amdtp_out_stream_pcm_pointer(&fwspk->stream); | |
339 | +} | |
340 | + | |
341 | +static int fwspk_create_pcm(struct fwspk *fwspk) | |
342 | +{ | |
343 | + static struct snd_pcm_ops ops = { | |
344 | + .open = fwspk_open, | |
345 | + .close = fwspk_close, | |
346 | + .ioctl = snd_pcm_lib_ioctl, | |
347 | + .hw_params = fwspk_hw_params, | |
348 | + .hw_free = fwspk_hw_free, | |
349 | + .prepare = fwspk_prepare, | |
350 | + .trigger = fwspk_trigger, | |
351 | + .pointer = fwspk_pointer, | |
352 | + .page = snd_pcm_lib_get_vmalloc_page, | |
353 | + .mmap = snd_pcm_lib_mmap_vmalloc, | |
354 | + }; | |
355 | + struct snd_pcm *pcm; | |
356 | + int err; | |
357 | + | |
358 | + err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); | |
359 | + if (err < 0) | |
360 | + return err; | |
361 | + pcm->private_data = fwspk; | |
362 | + strcpy(pcm->name, fwspk->device_info->short_name); | |
363 | + fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | |
364 | + fwspk->pcm->ops = &ops; | |
365 | + return 0; | |
366 | +} | |
367 | + | |
368 | +enum control_action { CTL_READ, CTL_WRITE }; | |
369 | +enum control_attribute { | |
370 | + CTL_MIN = 0x02, | |
371 | + CTL_MAX = 0x03, | |
372 | + CTL_CURRENT = 0x10, | |
373 | +}; | |
374 | + | |
375 | +static int fwspk_mute_command(struct fwspk *fwspk, bool *value, | |
376 | + enum control_action action) | |
377 | +{ | |
378 | + u8 *buf; | |
379 | + u8 response_ok; | |
380 | + int err; | |
381 | + | |
382 | + buf = kmalloc(11, GFP_KERNEL); | |
383 | + if (!buf) | |
384 | + return -ENOMEM; | |
385 | + | |
386 | + if (action == CTL_READ) { | |
387 | + buf[0] = 0x01; /* AV/C, STATUS */ | |
388 | + response_ok = 0x0c; /* STABLE */ | |
389 | + } else { | |
390 | + buf[0] = 0x00; /* AV/C, CONTROL */ | |
391 | + response_ok = 0x09; /* ACCEPTED */ | |
392 | + } | |
393 | + buf[1] = 0x08; /* audio unit 0 */ | |
394 | + buf[2] = 0xb8; /* FUNCTION BLOCK */ | |
395 | + buf[3] = 0x81; /* function block type: feature */ | |
396 | + buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ | |
397 | + buf[5] = 0x10; /* control attribute: current */ | |
398 | + buf[6] = 0x02; /* selector length */ | |
399 | + buf[7] = 0x00; /* audio channel number */ | |
400 | + buf[8] = 0x01; /* control selector: mute */ | |
401 | + buf[9] = 0x01; /* control data length */ | |
402 | + if (action == CTL_READ) | |
403 | + buf[10] = 0xff; | |
404 | + else | |
405 | + buf[10] = *value ? 0x70 : 0x60; | |
406 | + | |
407 | + err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); | |
408 | + if (err < 0) | |
409 | + goto error; | |
410 | + if (err < 11) { | |
411 | + dev_err(&fwspk->unit->device, "short FCP response\n"); | |
412 | + err = -EIO; | |
413 | + goto error; | |
414 | + } | |
415 | + if (buf[0] != response_ok) { | |
416 | + dev_err(&fwspk->unit->device, "mute command failed\n"); | |
417 | + err = -EIO; | |
418 | + goto error; | |
419 | + } | |
420 | + if (action == CTL_READ) | |
421 | + *value = buf[10] == 0x70; | |
422 | + | |
423 | + err = 0; | |
424 | + | |
425 | +error: | |
426 | + kfree(buf); | |
427 | + | |
428 | + return err; | |
429 | +} | |
430 | + | |
431 | +static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, | |
432 | + unsigned int channel, | |
433 | + enum control_attribute attribute, | |
434 | + enum control_action action) | |
435 | +{ | |
436 | + u8 *buf; | |
437 | + u8 response_ok; | |
438 | + int err; | |
439 | + | |
440 | + buf = kmalloc(12, GFP_KERNEL); | |
441 | + if (!buf) | |
442 | + return -ENOMEM; | |
443 | + | |
444 | + if (action == CTL_READ) { | |
445 | + buf[0] = 0x01; /* AV/C, STATUS */ | |
446 | + response_ok = 0x0c; /* STABLE */ | |
447 | + } else { | |
448 | + buf[0] = 0x00; /* AV/C, CONTROL */ | |
449 | + response_ok = 0x09; /* ACCEPTED */ | |
450 | + } | |
451 | + buf[1] = 0x08; /* audio unit 0 */ | |
452 | + buf[2] = 0xb8; /* FUNCTION BLOCK */ | |
453 | + buf[3] = 0x81; /* function block type: feature */ | |
454 | + buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ | |
455 | + buf[5] = attribute; /* control attribute */ | |
456 | + buf[6] = 0x02; /* selector length */ | |
457 | + buf[7] = channel; /* audio channel number */ | |
458 | + buf[8] = 0x02; /* control selector: volume */ | |
459 | + buf[9] = 0x02; /* control data length */ | |
460 | + if (action == CTL_READ) { | |
461 | + buf[10] = 0xff; | |
462 | + buf[11] = 0xff; | |
463 | + } else { | |
464 | + buf[10] = *value >> 8; | |
465 | + buf[11] = *value; | |
466 | + } | |
467 | + | |
468 | + err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); | |
469 | + if (err < 0) | |
470 | + goto error; | |
471 | + if (err < 12) { | |
472 | + dev_err(&fwspk->unit->device, "short FCP response\n"); | |
473 | + err = -EIO; | |
474 | + goto error; | |
475 | + } | |
476 | + if (buf[0] != response_ok) { | |
477 | + dev_err(&fwspk->unit->device, "volume command failed\n"); | |
478 | + err = -EIO; | |
479 | + goto error; | |
480 | + } | |
481 | + if (action == CTL_READ) | |
482 | + *value = (buf[10] << 8) | buf[11]; | |
483 | + | |
484 | + err = 0; | |
485 | + | |
486 | +error: | |
487 | + kfree(buf); | |
488 | + | |
489 | + return err; | |
490 | +} | |
491 | + | |
492 | +static int fwspk_mute_get(struct snd_kcontrol *control, | |
493 | + struct snd_ctl_elem_value *value) | |
494 | +{ | |
495 | + struct fwspk *fwspk = control->private_data; | |
496 | + | |
497 | + value->value.integer.value[0] = !fwspk->mute; | |
498 | + | |
499 | + return 0; | |
500 | +} | |
501 | + | |
502 | +static int fwspk_mute_put(struct snd_kcontrol *control, | |
503 | + struct snd_ctl_elem_value *value) | |
504 | +{ | |
505 | + struct fwspk *fwspk = control->private_data; | |
506 | + bool mute; | |
507 | + int err; | |
508 | + | |
509 | + mute = !value->value.integer.value[0]; | |
510 | + | |
511 | + if (mute == fwspk->mute) | |
512 | + return 0; | |
513 | + | |
514 | + err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); | |
515 | + if (err < 0) | |
516 | + return err; | |
517 | + fwspk->mute = mute; | |
518 | + | |
519 | + return 1; | |
520 | +} | |
521 | + | |
522 | +static int fwspk_volume_info(struct snd_kcontrol *control, | |
523 | + struct snd_ctl_elem_info *info) | |
524 | +{ | |
525 | + struct fwspk *fwspk = control->private_data; | |
526 | + | |
527 | + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
528 | + info->count = fwspk->device_info->mixer_channels; | |
529 | + info->value.integer.min = fwspk->volume_min; | |
530 | + info->value.integer.max = fwspk->volume_max; | |
531 | + | |
532 | + return 0; | |
533 | +} | |
534 | + | |
535 | +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; | |
536 | + | |
537 | +static int fwspk_volume_get(struct snd_kcontrol *control, | |
538 | + struct snd_ctl_elem_value *value) | |
539 | +{ | |
540 | + struct fwspk *fwspk = control->private_data; | |
541 | + unsigned int i; | |
542 | + | |
543 | + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) | |
544 | + value->value.integer.value[channel_map[i]] = fwspk->volume[i]; | |
545 | + | |
546 | + return 0; | |
547 | +} | |
548 | + | |
549 | +static int fwspk_volume_put(struct snd_kcontrol *control, | |
550 | + struct snd_ctl_elem_value *value) | |
551 | +{ | |
552 | + struct fwspk *fwspk = control->private_data; | |
553 | + unsigned int i, changed_channels; | |
554 | + bool equal_values = true; | |
555 | + s16 volume; | |
556 | + int err; | |
557 | + | |
558 | + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { | |
559 | + if (value->value.integer.value[i] < fwspk->volume_min || | |
560 | + value->value.integer.value[i] > fwspk->volume_max) | |
561 | + return -EINVAL; | |
562 | + if (value->value.integer.value[i] != | |
563 | + value->value.integer.value[0]) | |
564 | + equal_values = false; | |
565 | + } | |
566 | + | |
567 | + changed_channels = 0; | |
568 | + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) | |
569 | + if (value->value.integer.value[channel_map[i]] != | |
570 | + fwspk->volume[i]) | |
571 | + changed_channels |= 1 << (i + 1); | |
572 | + | |
573 | + if (equal_values && changed_channels != 0) | |
574 | + changed_channels = 1 << 0; | |
575 | + | |
576 | + for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { | |
577 | + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; | |
578 | + if (changed_channels & (1 << i)) { | |
579 | + err = fwspk_volume_command(fwspk, &volume, i, | |
580 | + CTL_CURRENT, CTL_WRITE); | |
581 | + if (err < 0) | |
582 | + return err; | |
583 | + } | |
584 | + if (i > 0) | |
585 | + fwspk->volume[i - 1] = volume; | |
586 | + } | |
587 | + | |
588 | + return changed_channels != 0; | |
589 | +} | |
590 | + | |
591 | +static int fwspk_create_mixer(struct fwspk *fwspk) | |
592 | +{ | |
593 | + static const struct snd_kcontrol_new controls[] = { | |
594 | + { | |
595 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
596 | + .name = "PCM Playback Switch", | |
597 | + .info = snd_ctl_boolean_mono_info, | |
598 | + .get = fwspk_mute_get, | |
599 | + .put = fwspk_mute_put, | |
600 | + }, | |
601 | + { | |
602 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
603 | + .name = "PCM Playback Volume", | |
604 | + .info = fwspk_volume_info, | |
605 | + .get = fwspk_volume_get, | |
606 | + .put = fwspk_volume_put, | |
607 | + }, | |
608 | + }; | |
609 | + unsigned int i, first_ch; | |
610 | + int err; | |
611 | + | |
612 | + err = fwspk_volume_command(fwspk, &fwspk->volume_min, | |
613 | + 0, CTL_MIN, CTL_READ); | |
614 | + if (err < 0) | |
615 | + return err; | |
616 | + err = fwspk_volume_command(fwspk, &fwspk->volume_max, | |
617 | + 0, CTL_MAX, CTL_READ); | |
618 | + if (err < 0) | |
619 | + return err; | |
620 | + | |
621 | + err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); | |
622 | + if (err < 0) | |
623 | + return err; | |
624 | + | |
625 | + first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; | |
626 | + for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { | |
627 | + err = fwspk_volume_command(fwspk, &fwspk->volume[i], | |
628 | + first_ch + i, CTL_CURRENT, CTL_READ); | |
629 | + if (err < 0) | |
630 | + return err; | |
631 | + } | |
632 | + | |
633 | + for (i = 0; i < ARRAY_SIZE(controls); ++i) { | |
634 | + err = snd_ctl_add(fwspk->card, | |
635 | + snd_ctl_new1(&controls[i], fwspk)); | |
636 | + if (err < 0) | |
637 | + return err; | |
638 | + } | |
639 | + | |
640 | + return 0; | |
641 | +} | |
642 | + | |
643 | +static u32 fwspk_read_firmware_version(struct fw_unit *unit) | |
644 | +{ | |
645 | + __be32 data; | |
646 | + int err; | |
647 | + | |
648 | + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, | |
649 | + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); | |
650 | + return err >= 0 ? be32_to_cpu(data) : 0; | |
651 | +} | |
652 | + | |
653 | +static void fwspk_card_free(struct snd_card *card) | |
654 | +{ | |
655 | + struct fwspk *fwspk = card->private_data; | |
656 | + struct fw_device *dev = fw_parent_device(fwspk->unit); | |
657 | + | |
658 | + amdtp_out_stream_destroy(&fwspk->stream); | |
659 | + cmp_connection_destroy(&fwspk->connection); | |
660 | + fw_unit_put(fwspk->unit); | |
661 | + fw_device_put(dev); | |
662 | + mutex_destroy(&fwspk->mutex); | |
663 | +} | |
664 | + | |
665 | +static const struct device_info *__devinit fwspk_detect(struct fw_device *dev) | |
666 | +{ | |
667 | + static const struct device_info griffin_firewave = { | |
668 | + .driver_name = "FireWave", | |
669 | + .short_name = "FireWave", | |
670 | + .long_name = "Griffin FireWave Surround", | |
671 | + .pcm_constraints = firewave_constraints, | |
672 | + .mixer_channels = 6, | |
673 | + .mute_fb_id = 0x01, | |
674 | + .volume_fb_id = 0x02, | |
675 | + }; | |
676 | + static const struct device_info lacie_speakers = { | |
677 | + .driver_name = "FWSpeakers", | |
678 | + .short_name = "FireWire Speakers", | |
679 | + .long_name = "LaCie FireWire Speakers", | |
680 | + .pcm_constraints = lacie_speakers_constraints, | |
681 | + .mixer_channels = 1, | |
682 | + .mute_fb_id = 0x01, | |
683 | + .volume_fb_id = 0x01, | |
684 | + }; | |
685 | + struct fw_csr_iterator i; | |
686 | + int key, value; | |
687 | + | |
688 | + fw_csr_iterator_init(&i, dev->config_rom); | |
689 | + while (fw_csr_iterator_next(&i, &key, &value)) | |
690 | + if (key == CSR_VENDOR) | |
691 | + switch (value) { | |
692 | + case VENDOR_GRIFFIN: | |
693 | + return &griffin_firewave; | |
694 | + case VENDOR_LACIE: | |
695 | + return &lacie_speakers; | |
696 | + } | |
697 | + | |
698 | + return NULL; | |
699 | +} | |
700 | + | |
701 | +static int __devinit fwspk_probe(struct device *unit_dev) | |
702 | +{ | |
703 | + struct fw_unit *unit = fw_unit(unit_dev); | |
704 | + struct fw_device *fw_dev = fw_parent_device(unit); | |
705 | + struct snd_card *card; | |
706 | + struct fwspk *fwspk; | |
707 | + u32 firmware; | |
708 | + int err; | |
709 | + | |
710 | + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card); | |
711 | + if (err < 0) | |
712 | + return err; | |
713 | + snd_card_set_dev(card, unit_dev); | |
714 | + | |
715 | + fwspk = card->private_data; | |
716 | + fwspk->card = card; | |
717 | + mutex_init(&fwspk->mutex); | |
718 | + fw_device_get(fw_dev); | |
719 | + fwspk->unit = fw_unit_get(unit); | |
720 | + fwspk->device_info = fwspk_detect(fw_dev); | |
721 | + if (!fwspk->device_info) { | |
722 | + err = -ENODEV; | |
723 | + goto err_unit; | |
724 | + } | |
725 | + | |
726 | + err = cmp_connection_init(&fwspk->connection, unit, 0); | |
727 | + if (err < 0) | |
728 | + goto err_unit; | |
729 | + | |
730 | + err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); | |
731 | + if (err < 0) | |
732 | + goto err_connection; | |
733 | + | |
734 | + card->private_free = fwspk_card_free; | |
735 | + | |
736 | + strcpy(card->driver, fwspk->device_info->driver_name); | |
737 | + strcpy(card->shortname, fwspk->device_info->short_name); | |
738 | + firmware = fwspk_read_firmware_version(unit); | |
739 | + snprintf(card->longname, sizeof(card->longname), | |
740 | + "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", | |
741 | + fwspk->device_info->long_name, | |
742 | + firmware >> 20, firmware & 0xffff, | |
743 | + fw_dev->config_rom[3], fw_dev->config_rom[4], | |
744 | + dev_name(&unit->device), 100 << fw_dev->max_speed); | |
745 | + strcpy(card->mixername, "OXFW970"); | |
746 | + | |
747 | + err = fwspk_create_pcm(fwspk); | |
748 | + if (err < 0) | |
749 | + goto error; | |
750 | + | |
751 | + err = fwspk_create_mixer(fwspk); | |
752 | + if (err < 0) | |
753 | + goto error; | |
754 | + | |
755 | + err = snd_card_register(card); | |
756 | + if (err < 0) | |
757 | + goto error; | |
758 | + | |
759 | + dev_set_drvdata(unit_dev, fwspk); | |
760 | + | |
761 | + return 0; | |
762 | + | |
763 | +err_connection: | |
764 | + cmp_connection_destroy(&fwspk->connection); | |
765 | +err_unit: | |
766 | + fw_unit_put(fwspk->unit); | |
767 | + fw_device_put(fw_dev); | |
768 | + mutex_destroy(&fwspk->mutex); | |
769 | +error: | |
770 | + snd_card_free(card); | |
771 | + return err; | |
772 | +} | |
773 | + | |
774 | +static int __devexit fwspk_remove(struct device *dev) | |
775 | +{ | |
776 | + struct fwspk *fwspk = dev_get_drvdata(dev); | |
777 | + | |
778 | + snd_card_disconnect(fwspk->card); | |
779 | + | |
780 | + mutex_lock(&fwspk->mutex); | |
781 | + amdtp_out_stream_pcm_abort(&fwspk->stream); | |
782 | + fwspk_stop_stream(fwspk); | |
783 | + mutex_unlock(&fwspk->mutex); | |
784 | + | |
785 | + snd_card_free_when_closed(fwspk->card); | |
786 | + | |
787 | + return 0; | |
788 | +} | |
789 | + | |
790 | +static void fwspk_bus_reset(struct fw_unit *unit) | |
791 | +{ | |
792 | + struct fwspk *fwspk = dev_get_drvdata(&unit->device); | |
793 | + | |
794 | + fcp_bus_reset(fwspk->unit); | |
795 | + | |
796 | + if (cmp_connection_update(&fwspk->connection) < 0) { | |
797 | + mutex_lock(&fwspk->mutex); | |
798 | + amdtp_out_stream_pcm_abort(&fwspk->stream); | |
799 | + fwspk_stop_stream(fwspk); | |
800 | + mutex_unlock(&fwspk->mutex); | |
801 | + return; | |
802 | + } | |
803 | + | |
804 | + amdtp_out_stream_update(&fwspk->stream); | |
805 | +} | |
806 | + | |
807 | +static const struct ieee1394_device_id fwspk_id_table[] = { | |
808 | + { | |
809 | + .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
810 | + IEEE1394_MATCH_MODEL_ID | | |
811 | + IEEE1394_MATCH_SPECIFIER_ID | | |
812 | + IEEE1394_MATCH_VERSION, | |
813 | + .vendor_id = VENDOR_GRIFFIN, | |
814 | + .model_id = 0x00f970, | |
815 | + .specifier_id = SPECIFIER_1394TA, | |
816 | + .version = VERSION_AVC, | |
817 | + }, | |
818 | + { | |
819 | + .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
820 | + IEEE1394_MATCH_MODEL_ID | | |
821 | + IEEE1394_MATCH_SPECIFIER_ID | | |
822 | + IEEE1394_MATCH_VERSION, | |
823 | + .vendor_id = VENDOR_LACIE, | |
824 | + .model_id = 0x00f970, | |
825 | + .specifier_id = SPECIFIER_1394TA, | |
826 | + .version = VERSION_AVC, | |
827 | + }, | |
828 | + { } | |
829 | +}; | |
830 | +MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); | |
831 | + | |
832 | +static struct fw_driver fwspk_driver = { | |
833 | + .driver = { | |
834 | + .owner = THIS_MODULE, | |
835 | + .name = KBUILD_MODNAME, | |
836 | + .bus = &fw_bus_type, | |
837 | + .probe = fwspk_probe, | |
838 | + .remove = __devexit_p(fwspk_remove), | |
839 | + }, | |
840 | + .update = fwspk_bus_reset, | |
841 | + .id_table = fwspk_id_table, | |
842 | +}; | |
843 | + | |
844 | +static int __init alsa_fwspk_init(void) | |
845 | +{ | |
846 | + return driver_register(&fwspk_driver.driver); | |
847 | +} | |
848 | + | |
849 | +static void __exit alsa_fwspk_exit(void) | |
850 | +{ | |
851 | + driver_unregister(&fwspk_driver.driver); | |
852 | +} | |
853 | + | |
854 | +module_init(alsa_fwspk_init); | |
855 | +module_exit(alsa_fwspk_exit); |