1/* 2 * fireworks_hwdep.c - a part of driver for Fireworks based devices 3 * 4 * Copyright (c) 2013-2014 Takashi Sakamoto 5 * 6 * Licensed under the terms of the GNU General Public License, version 2. 7 */ 8 9/* 10 * This codes have five functionalities. 11 * 12 * 1.get information about firewire node 13 * 2.get notification about starting/stopping stream 14 * 3.lock/unlock streaming 15 * 4.transmit command of EFW transaction 16 * 5.receive response of EFW transaction 17 * 18 */ 19 20#include "fireworks.h" 21 22static long 23hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained, 24 loff_t *offset) 25{ 26 unsigned int length, till_end, type; 27 struct snd_efw_transaction *t; 28 long count = 0; 29 30 if (remained < sizeof(type) + sizeof(struct snd_efw_transaction)) 31 return -ENOSPC; 32 33 /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */ 34 type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE; 35 if (copy_to_user(buf, &type, sizeof(type))) 36 return -EFAULT; 37 remained -= sizeof(type); 38 buf += sizeof(type); 39 40 /* write into buffer as many responses as possible */ 41 while (efw->resp_queues > 0) { 42 t = (struct snd_efw_transaction *)(efw->pull_ptr); 43 length = be32_to_cpu(t->length) * sizeof(__be32); 44 45 /* confirm enough space for this response */ 46 if (remained < length) 47 break; 48 49 /* copy from ring buffer to user buffer */ 50 while (length > 0) { 51 till_end = snd_efw_resp_buf_size - 52 (unsigned int)(efw->pull_ptr - efw->resp_buf); 53 till_end = min_t(unsigned int, length, till_end); 54 55 if (copy_to_user(buf, efw->pull_ptr, till_end)) 56 return -EFAULT; 57 58 efw->pull_ptr += till_end; 59 if (efw->pull_ptr >= efw->resp_buf + 60 snd_efw_resp_buf_size) 61 efw->pull_ptr -= snd_efw_resp_buf_size; 62 63 length -= till_end; 64 buf += till_end; 65 count += till_end; 66 remained -= till_end; 67 } 68 69 efw->resp_queues--; 70 } 71 72 return count; 73} 74 75static long 76hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count, 77 loff_t *offset) 78{ 79 union snd_firewire_event event; 80 81 memset(&event, 0, sizeof(event)); 82 83 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 84 event.lock_status.status = (efw->dev_lock_count > 0); 85 efw->dev_lock_changed = false; 86 87 count = min_t(long, count, sizeof(event.lock_status)); 88 89 if (copy_to_user(buf, &event, count)) 90 return -EFAULT; 91 92 return count; 93} 94 95static long 96hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 97 loff_t *offset) 98{ 99 struct snd_efw *efw = hwdep->private_data; 100 DEFINE_WAIT(wait); 101 102 spin_lock_irq(&efw->lock); 103 104 while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) { 105 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 106 spin_unlock_irq(&efw->lock); 107 schedule(); 108 finish_wait(&efw->hwdep_wait, &wait); 109 if (signal_pending(current)) 110 return -ERESTARTSYS; 111 spin_lock_irq(&efw->lock); 112 } 113 114 if (efw->dev_lock_changed) 115 count = hwdep_read_locked(efw, buf, count, offset); 116 else if (efw->resp_queues > 0) 117 count = hwdep_read_resp_buf(efw, buf, count, offset); 118 119 spin_unlock_irq(&efw->lock); 120 121 return count; 122} 123 124static long 125hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, 126 loff_t *offset) 127{ 128 struct snd_efw *efw = hwdep->private_data; 129 u32 seqnum; 130 u8 *buf; 131 132 if (count < sizeof(struct snd_efw_transaction) || 133 SND_EFW_RESPONSE_MAXIMUM_BYTES < count) 134 return -EINVAL; 135 136 buf = memdup_user(data, count); 137 if (IS_ERR(buf)) 138 return PTR_ERR(buf); 139 140 /* check seqnum is not for kernel-land */ 141 seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum); 142 if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) { 143 count = -EINVAL; 144 goto end; 145 } 146 147 if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0) 148 count = -EIO; 149end: 150 kfree(buf); 151 return count; 152} 153 154static unsigned int 155hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) 156{ 157 struct snd_efw *efw = hwdep->private_data; 158 unsigned int events; 159 160 poll_wait(file, &efw->hwdep_wait, wait); 161 162 spin_lock_irq(&efw->lock); 163 if (efw->dev_lock_changed || (efw->resp_queues > 0)) 164 events = POLLIN | POLLRDNORM; 165 else 166 events = 0; 167 spin_unlock_irq(&efw->lock); 168 169 return events | POLLOUT; 170} 171 172static int 173hwdep_get_info(struct snd_efw *efw, void __user *arg) 174{ 175 struct fw_device *dev = fw_parent_device(efw->unit); 176 struct snd_firewire_get_info info; 177 178 memset(&info, 0, sizeof(info)); 179 info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS; 180 info.card = dev->card->index; 181 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 182 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 183 strlcpy(info.device_name, dev_name(&dev->device), 184 sizeof(info.device_name)); 185 186 if (copy_to_user(arg, &info, sizeof(info))) 187 return -EFAULT; 188 189 return 0; 190} 191 192static int 193hwdep_lock(struct snd_efw *efw) 194{ 195 int err; 196 197 spin_lock_irq(&efw->lock); 198 199 if (efw->dev_lock_count == 0) { 200 efw->dev_lock_count = -1; 201 err = 0; 202 } else { 203 err = -EBUSY; 204 } 205 206 spin_unlock_irq(&efw->lock); 207 208 return err; 209} 210 211static int 212hwdep_unlock(struct snd_efw *efw) 213{ 214 int err; 215 216 spin_lock_irq(&efw->lock); 217 218 if (efw->dev_lock_count == -1) { 219 efw->dev_lock_count = 0; 220 err = 0; 221 } else { 222 err = -EBADFD; 223 } 224 225 spin_unlock_irq(&efw->lock); 226 227 return err; 228} 229 230static int 231hwdep_release(struct snd_hwdep *hwdep, struct file *file) 232{ 233 struct snd_efw *efw = hwdep->private_data; 234 235 spin_lock_irq(&efw->lock); 236 if (efw->dev_lock_count == -1) 237 efw->dev_lock_count = 0; 238 spin_unlock_irq(&efw->lock); 239 240 return 0; 241} 242 243static int 244hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 245 unsigned int cmd, unsigned long arg) 246{ 247 struct snd_efw *efw = hwdep->private_data; 248 249 switch (cmd) { 250 case SNDRV_FIREWIRE_IOCTL_GET_INFO: 251 return hwdep_get_info(efw, (void __user *)arg); 252 case SNDRV_FIREWIRE_IOCTL_LOCK: 253 return hwdep_lock(efw); 254 case SNDRV_FIREWIRE_IOCTL_UNLOCK: 255 return hwdep_unlock(efw); 256 default: 257 return -ENOIOCTLCMD; 258 } 259} 260 261#ifdef CONFIG_COMPAT 262static int 263hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 264 unsigned int cmd, unsigned long arg) 265{ 266 return hwdep_ioctl(hwdep, file, cmd, 267 (unsigned long)compat_ptr(arg)); 268} 269#else 270#define hwdep_compat_ioctl NULL 271#endif 272 273static const struct snd_hwdep_ops hwdep_ops = { 274 .read = hwdep_read, 275 .write = hwdep_write, 276 .release = hwdep_release, 277 .poll = hwdep_poll, 278 .ioctl = hwdep_ioctl, 279 .ioctl_compat = hwdep_compat_ioctl, 280}; 281 282int snd_efw_create_hwdep_device(struct snd_efw *efw) 283{ 284 struct snd_hwdep *hwdep; 285 int err; 286 287 err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep); 288 if (err < 0) 289 goto end; 290 strcpy(hwdep->name, "Fireworks"); 291 hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; 292 hwdep->ops = hwdep_ops; 293 hwdep->private_data = efw; 294 hwdep->exclusive = true; 295end: 296 return err; 297} 298 299