Skip to content

Commit e5e0c3d

Browse files
takaswietiwai
authored andcommitted
ALSA: firewire-tascam: add hwdep interface
This commit adds hwdep interface so as the other IEEE 1394 sound devices has. This interface is designed for mixer/control applications. By using this interface, an application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming. Signed-off-by: Takashi Sakamoto <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent e453df4 commit e5e0c3d

File tree

9 files changed

+278
-5
lines changed

9 files changed

+278
-5
lines changed

include/uapi/sound/asound.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ enum {
101101
SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */
102102
SNDRV_HWDEP_IFACE_FW_OXFW, /* Oxford OXFW970/971 based device */
103103
SNDRV_HWDEP_IFACE_FW_DIGI00X, /* Digidesign Digi 002/003 family */
104+
SNDRV_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */
104105

105106
/* Don't forget to change the following: */
106-
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DIGI00X
107+
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
107108
};
108109

109110
struct snd_hwdep_info {

include/uapi/sound/firewire.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ union snd_firewire_event {
6464
#define SNDRV_FIREWIRE_TYPE_BEBOB 3
6565
#define SNDRV_FIREWIRE_TYPE_OXFW 4
6666
#define SNDRV_FIREWIRE_TYPE_DIGI00X 5
67+
#define SNDRV_FIREWIRE_TYPE_TASCAM 6
6768
/* RME, MOTU, ... */
6869

6970
struct snd_firewire_get_info {

sound/firewire/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ config SND_FIREWIRE_DIGI00X
138138
config SND_FIREWIRE_TASCAM
139139
tristate "TASCAM FireWire series support"
140140
select SND_FIREWIRE_LIB
141+
select SND_HWDEP
141142
help
142143
Say Y here to include support for TASCAM.
143144
* FW-1884

sound/firewire/tascam/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
2-
tascam-pcm.o tascam.o
2+
tascam-pcm.o tascam-hwdep.o tascam.o
33
obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* tascam-hwdep.c - a part of driver for TASCAM FireWire series
3+
*
4+
* Copyright (c) 2015 Takashi Sakamoto
5+
*
6+
* Licensed under the terms of the GNU General Public License, version 2.
7+
*/
8+
9+
/*
10+
* This codes give three functionality.
11+
*
12+
* 1.get firewire node information
13+
* 2.get notification about starting/stopping stream
14+
* 3.lock/unlock stream
15+
*/
16+
17+
#include "tascam.h"
18+
19+
static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
20+
long count)
21+
{
22+
union snd_firewire_event event;
23+
24+
memset(&event, 0, sizeof(event));
25+
26+
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
27+
event.lock_status.status = (tscm->dev_lock_count > 0);
28+
tscm->dev_lock_changed = false;
29+
30+
count = min_t(long, count, sizeof(event.lock_status));
31+
32+
if (copy_to_user(buf, &event, count))
33+
return -EFAULT;
34+
35+
return count;
36+
}
37+
38+
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
39+
loff_t *offset)
40+
{
41+
struct snd_tscm *tscm = hwdep->private_data;
42+
DEFINE_WAIT(wait);
43+
union snd_firewire_event event;
44+
45+
spin_lock_irq(&tscm->lock);
46+
47+
while (!tscm->dev_lock_changed) {
48+
prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
49+
spin_unlock_irq(&tscm->lock);
50+
schedule();
51+
finish_wait(&tscm->hwdep_wait, &wait);
52+
if (signal_pending(current))
53+
return -ERESTARTSYS;
54+
spin_lock_irq(&tscm->lock);
55+
}
56+
57+
memset(&event, 0, sizeof(event));
58+
count = hwdep_read_locked(tscm, buf, count);
59+
spin_unlock_irq(&tscm->lock);
60+
61+
return count;
62+
}
63+
64+
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
65+
poll_table *wait)
66+
{
67+
struct snd_tscm *tscm = hwdep->private_data;
68+
unsigned int events;
69+
70+
poll_wait(file, &tscm->hwdep_wait, wait);
71+
72+
spin_lock_irq(&tscm->lock);
73+
if (tscm->dev_lock_changed)
74+
events = POLLIN | POLLRDNORM;
75+
else
76+
events = 0;
77+
spin_unlock_irq(&tscm->lock);
78+
79+
return events;
80+
}
81+
82+
static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
83+
{
84+
struct fw_device *dev = fw_parent_device(tscm->unit);
85+
struct snd_firewire_get_info info;
86+
87+
memset(&info, 0, sizeof(info));
88+
info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
89+
info.card = dev->card->index;
90+
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
91+
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
92+
strlcpy(info.device_name, dev_name(&dev->device),
93+
sizeof(info.device_name));
94+
95+
if (copy_to_user(arg, &info, sizeof(info)))
96+
return -EFAULT;
97+
98+
return 0;
99+
}
100+
101+
static int hwdep_lock(struct snd_tscm *tscm)
102+
{
103+
int err;
104+
105+
spin_lock_irq(&tscm->lock);
106+
107+
if (tscm->dev_lock_count == 0) {
108+
tscm->dev_lock_count = -1;
109+
err = 0;
110+
} else {
111+
err = -EBUSY;
112+
}
113+
114+
spin_unlock_irq(&tscm->lock);
115+
116+
return err;
117+
}
118+
119+
static int hwdep_unlock(struct snd_tscm *tscm)
120+
{
121+
int err;
122+
123+
spin_lock_irq(&tscm->lock);
124+
125+
if (tscm->dev_lock_count == -1) {
126+
tscm->dev_lock_count = 0;
127+
err = 0;
128+
} else {
129+
err = -EBADFD;
130+
}
131+
132+
spin_unlock_irq(&tscm->lock);
133+
134+
return err;
135+
}
136+
137+
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
138+
{
139+
struct snd_tscm *tscm = hwdep->private_data;
140+
141+
spin_lock_irq(&tscm->lock);
142+
if (tscm->dev_lock_count == -1)
143+
tscm->dev_lock_count = 0;
144+
spin_unlock_irq(&tscm->lock);
145+
146+
return 0;
147+
}
148+
149+
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
150+
unsigned int cmd, unsigned long arg)
151+
{
152+
struct snd_tscm *tscm = hwdep->private_data;
153+
154+
switch (cmd) {
155+
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
156+
return hwdep_get_info(tscm, (void __user *)arg);
157+
case SNDRV_FIREWIRE_IOCTL_LOCK:
158+
return hwdep_lock(tscm);
159+
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
160+
return hwdep_unlock(tscm);
161+
default:
162+
return -ENOIOCTLCMD;
163+
}
164+
}
165+
166+
#ifdef CONFIG_COMPAT
167+
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
168+
unsigned int cmd, unsigned long arg)
169+
{
170+
return hwdep_ioctl(hwdep, file, cmd,
171+
(unsigned long)compat_ptr(arg));
172+
}
173+
#else
174+
#define hwdep_compat_ioctl NULL
175+
#endif
176+
177+
static const struct snd_hwdep_ops hwdep_ops = {
178+
.read = hwdep_read,
179+
.release = hwdep_release,
180+
.poll = hwdep_poll,
181+
.ioctl = hwdep_ioctl,
182+
.ioctl_compat = hwdep_compat_ioctl,
183+
};
184+
185+
int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
186+
{
187+
struct snd_hwdep *hwdep;
188+
int err;
189+
190+
err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
191+
if (err < 0)
192+
return err;
193+
194+
strcpy(hwdep->name, "Tascam");
195+
hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
196+
hwdep->ops = hwdep_ops;
197+
hwdep->private_data = tscm;
198+
hwdep->exclusive = true;
199+
200+
return err;
201+
}

sound/firewire/tascam/tascam-pcm.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,39 @@ static int pcm_open(struct snd_pcm_substream *substream)
7272
unsigned int rate;
7373
int err;
7474

75+
err = snd_tscm_stream_lock_try(tscm);
76+
if (err < 0)
77+
goto end;
78+
7579
err = pcm_init_hw_params(tscm, substream);
7680
if (err < 0)
77-
return err;
81+
goto err_locked;
7882

7983
err = snd_tscm_stream_get_clock(tscm, &clock);
8084
if (clock != SND_TSCM_CLOCK_INTERNAL ||
8185
amdtp_stream_pcm_running(&tscm->rx_stream) ||
8286
amdtp_stream_pcm_running(&tscm->tx_stream)) {
8387
err = snd_tscm_stream_get_rate(tscm, &rate);
8488
if (err < 0)
85-
return err;
89+
goto err_locked;
8690
substream->runtime->hw.rate_min = rate;
8791
substream->runtime->hw.rate_max = rate;
8892
}
8993

9094
snd_pcm_set_sync(substream);
91-
95+
end:
96+
return err;
97+
err_locked:
98+
snd_tscm_stream_lock_release(tscm);
9299
return err;
93100
}
94101

95102
static int pcm_close(struct snd_pcm_substream *substream)
96103
{
104+
struct snd_tscm *tscm = substream->private_data;
105+
106+
snd_tscm_stream_lock_release(tscm);
107+
97108
return 0;
98109
}
99110

sound/firewire/tascam/tascam-stream.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,42 @@ void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
455455
finish_session(tscm);
456456
release_resources(tscm);
457457
}
458+
459+
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
460+
{
461+
tscm->dev_lock_changed = true;
462+
wake_up(&tscm->hwdep_wait);
463+
}
464+
465+
int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
466+
{
467+
int err;
468+
469+
spin_lock_irq(&tscm->lock);
470+
471+
/* user land lock this */
472+
if (tscm->dev_lock_count < 0) {
473+
err = -EBUSY;
474+
goto end;
475+
}
476+
477+
/* this is the first time */
478+
if (tscm->dev_lock_count++ == 0)
479+
snd_tscm_stream_lock_changed(tscm);
480+
err = 0;
481+
end:
482+
spin_unlock_irq(&tscm->lock);
483+
return err;
484+
}
485+
486+
void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
487+
{
488+
spin_lock_irq(&tscm->lock);
489+
490+
if (WARN_ON(tscm->dev_lock_count <= 0))
491+
goto end;
492+
if (--tscm->dev_lock_count == 0)
493+
snd_tscm_stream_lock_changed(tscm);
494+
end:
495+
spin_unlock_irq(&tscm->lock);
496+
}

sound/firewire/tascam/tascam.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ static int snd_tscm_probe(struct fw_unit *unit,
110110
tscm->spec = (const struct snd_tscm_spec *)entry->driver_data;
111111

112112
mutex_init(&tscm->mutex);
113+
spin_lock_init(&tscm->lock);
114+
init_waitqueue_head(&tscm->hwdep_wait);
113115

114116
err = check_name(tscm);
115117
if (err < 0)
@@ -125,6 +127,10 @@ static int snd_tscm_probe(struct fw_unit *unit,
125127
if (err < 0)
126128
goto error;
127129

130+
err = snd_tscm_create_hwdep_device(tscm);
131+
if (err < 0)
132+
goto error;
133+
128134
err = snd_card_register(card);
129135
if (err < 0)
130136
goto error;

sound/firewire/tascam/tascam.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <sound/info.h>
2424
#include <sound/pcm.h>
2525
#include <sound/pcm_params.h>
26+
#include <sound/firewire.h>
27+
#include <sound/hwdep.h>
2628

2729
#include "../lib.h"
2830
#include "../amdtp-stream.h"
@@ -44,6 +46,7 @@ struct snd_tscm {
4446
struct fw_unit *unit;
4547

4648
struct mutex mutex;
49+
spinlock_t lock;
4750

4851
const struct snd_tscm_spec *spec;
4952

@@ -52,6 +55,10 @@ struct snd_tscm {
5255
struct amdtp_stream tx_stream;
5356
struct amdtp_stream rx_stream;
5457
unsigned int substreams_counter;
58+
59+
int dev_lock_count;
60+
bool dev_lock_changed;
61+
wait_queue_head_t hwdep_wait;
5562
};
5663

5764
#define TSCM_ADDR_BASE 0xffff00000000ull
@@ -97,8 +104,14 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
97104
int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
98105
void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
99106

107+
void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
108+
int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
109+
void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
110+
100111
void snd_tscm_proc_init(struct snd_tscm *tscm);
101112

102113
int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
103114

115+
int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
116+
104117
#endif

0 commit comments

Comments
 (0)