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