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