162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * motu-hwdep.c - a part of driver for MOTU FireWire series 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci * This codes have five functionalities. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * 1.get information about firewire node 1262306a36Sopenharmony_ci * 2.get notification about starting/stopping stream 1362306a36Sopenharmony_ci * 3.lock/unlock streaming 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "motu.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic bool has_dsp_event(struct snd_motu *motu) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) 2262306a36Sopenharmony_ci return (snd_motu_register_dsp_message_parser_count_event(motu) > 0); 2362306a36Sopenharmony_ci else 2462306a36Sopenharmony_ci return false; 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 2862306a36Sopenharmony_ci loff_t *offset) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct snd_motu *motu = hwdep->private_data; 3162306a36Sopenharmony_ci DEFINE_WAIT(wait); 3262306a36Sopenharmony_ci union snd_firewire_event event; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { 3762306a36Sopenharmony_ci prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 3862306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 3962306a36Sopenharmony_ci schedule(); 4062306a36Sopenharmony_ci finish_wait(&motu->hwdep_wait, &wait); 4162306a36Sopenharmony_ci if (signal_pending(current)) 4262306a36Sopenharmony_ci return -ERESTARTSYS; 4362306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci memset(&event, 0, sizeof(event)); 4762306a36Sopenharmony_ci if (motu->dev_lock_changed) { 4862306a36Sopenharmony_ci event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 4962306a36Sopenharmony_ci event.lock_status.status = (motu->dev_lock_count > 0); 5062306a36Sopenharmony_ci motu->dev_lock_changed = false; 5162306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci count = min_t(long, count, sizeof(event)); 5462306a36Sopenharmony_ci if (copy_to_user(buf, &event, count)) 5562306a36Sopenharmony_ci return -EFAULT; 5662306a36Sopenharmony_ci } else if (motu->msg > 0) { 5762306a36Sopenharmony_ci event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION; 5862306a36Sopenharmony_ci event.motu_notification.message = motu->msg; 5962306a36Sopenharmony_ci motu->msg = 0; 6062306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci count = min_t(long, count, sizeof(event)); 6362306a36Sopenharmony_ci if (copy_to_user(buf, &event, count)) 6462306a36Sopenharmony_ci return -EFAULT; 6562306a36Sopenharmony_ci } else if (has_dsp_event(motu)) { 6662306a36Sopenharmony_ci size_t consumed = 0; 6762306a36Sopenharmony_ci u32 __user *ptr; 6862306a36Sopenharmony_ci u32 ev; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci // Header is filled later. 7362306a36Sopenharmony_ci consumed += sizeof(event.motu_register_dsp_change); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci while (consumed < count && 7662306a36Sopenharmony_ci snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { 7762306a36Sopenharmony_ci ptr = (u32 __user *)(buf + consumed); 7862306a36Sopenharmony_ci if (put_user(ev, ptr)) 7962306a36Sopenharmony_ci return -EFAULT; 8062306a36Sopenharmony_ci consumed += sizeof(ev); 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; 8462306a36Sopenharmony_ci event.motu_register_dsp_change.count = 8562306a36Sopenharmony_ci (consumed - sizeof(event.motu_register_dsp_change)) / 4; 8662306a36Sopenharmony_ci if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) 8762306a36Sopenharmony_ci return -EFAULT; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci count = consumed; 9062306a36Sopenharmony_ci } else { 9162306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci count = 0; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return count; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 10062306a36Sopenharmony_ci poll_table *wait) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct snd_motu *motu = hwdep->private_data; 10362306a36Sopenharmony_ci __poll_t events; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci poll_wait(file, &motu->hwdep_wait, wait); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 10862306a36Sopenharmony_ci if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) 10962306a36Sopenharmony_ci events = EPOLLIN | EPOLLRDNORM; 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci events = 0; 11262306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return events | EPOLLOUT; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int hwdep_get_info(struct snd_motu *motu, void __user *arg) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct fw_device *dev = fw_parent_device(motu->unit); 12062306a36Sopenharmony_ci struct snd_firewire_get_info info; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 12362306a36Sopenharmony_ci info.type = SNDRV_FIREWIRE_TYPE_MOTU; 12462306a36Sopenharmony_ci info.card = dev->card->index; 12562306a36Sopenharmony_ci *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); 12662306a36Sopenharmony_ci *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); 12762306a36Sopenharmony_ci strscpy(info.device_name, dev_name(&dev->device), 12862306a36Sopenharmony_ci sizeof(info.device_name)); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (copy_to_user(arg, &info, sizeof(info))) 13162306a36Sopenharmony_ci return -EFAULT; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return 0; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int hwdep_lock(struct snd_motu *motu) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci int err; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (motu->dev_lock_count == 0) { 14362306a36Sopenharmony_ci motu->dev_lock_count = -1; 14462306a36Sopenharmony_ci err = 0; 14562306a36Sopenharmony_ci } else { 14662306a36Sopenharmony_ci err = -EBUSY; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int hwdep_unlock(struct snd_motu *motu) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int err; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (motu->dev_lock_count == -1) { 16162306a36Sopenharmony_ci motu->dev_lock_count = 0; 16262306a36Sopenharmony_ci err = 0; 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci err = -EBADFD; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return err; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int hwdep_release(struct snd_hwdep *hwdep, struct file *file) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct snd_motu *motu = hwdep->private_data; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci spin_lock_irq(&motu->lock); 17762306a36Sopenharmony_ci if (motu->dev_lock_count == -1) 17862306a36Sopenharmony_ci motu->dev_lock_count = 0; 17962306a36Sopenharmony_ci spin_unlock_irq(&motu->lock); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 18562306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct snd_motu *motu = hwdep->private_data; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci switch (cmd) { 19062306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_GET_INFO: 19162306a36Sopenharmony_ci return hwdep_get_info(motu, (void __user *)arg); 19262306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_LOCK: 19362306a36Sopenharmony_ci return hwdep_lock(motu); 19462306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_UNLOCK: 19562306a36Sopenharmony_ci return hwdep_unlock(motu); 19662306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER: 19762306a36Sopenharmony_ci { 19862306a36Sopenharmony_ci struct snd_firewire_motu_register_dsp_meter *meter; 19962306a36Sopenharmony_ci int err; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) 20262306a36Sopenharmony_ci return -ENXIO; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci meter = kzalloc(sizeof(*meter), GFP_KERNEL); 20562306a36Sopenharmony_ci if (!meter) 20662306a36Sopenharmony_ci return -ENOMEM; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci snd_motu_register_dsp_message_parser_copy_meter(motu, meter); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); 21162306a36Sopenharmony_ci kfree(meter); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci return -EFAULT; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER: 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci struct snd_firewire_motu_command_dsp_meter *meter; 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)) 22462306a36Sopenharmony_ci return -ENXIO; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci meter = kzalloc(sizeof(*meter), GFP_KERNEL); 22762306a36Sopenharmony_ci if (!meter) 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci snd_motu_command_dsp_message_parser_copy_meter(motu, meter); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); 23362306a36Sopenharmony_ci kfree(meter); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (err) 23662306a36Sopenharmony_ci return -EFAULT; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER: 24162306a36Sopenharmony_ci { 24262306a36Sopenharmony_ci struct snd_firewire_motu_register_dsp_parameter *param; 24362306a36Sopenharmony_ci int err; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) 24662306a36Sopenharmony_ci return -ENXIO; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci param = kzalloc(sizeof(*param), GFP_KERNEL); 24962306a36Sopenharmony_ci if (!param) 25062306a36Sopenharmony_ci return -ENOMEM; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci snd_motu_register_dsp_message_parser_copy_parameter(motu, param); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci err = copy_to_user((void __user *)arg, param, sizeof(*param)); 25562306a36Sopenharmony_ci kfree(param); 25662306a36Sopenharmony_ci if (err) 25762306a36Sopenharmony_ci return -EFAULT; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci default: 26262306a36Sopenharmony_ci return -ENOIOCTLCMD; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 26762306a36Sopenharmony_cistatic int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, 26862306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci return hwdep_ioctl(hwdep, file, cmd, 27162306a36Sopenharmony_ci (unsigned long)compat_ptr(arg)); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci#else 27462306a36Sopenharmony_ci#define hwdep_compat_ioctl NULL 27562306a36Sopenharmony_ci#endif 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciint snd_motu_create_hwdep_device(struct snd_motu *motu) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci static const struct snd_hwdep_ops ops = { 28062306a36Sopenharmony_ci .read = hwdep_read, 28162306a36Sopenharmony_ci .release = hwdep_release, 28262306a36Sopenharmony_ci .poll = hwdep_poll, 28362306a36Sopenharmony_ci .ioctl = hwdep_ioctl, 28462306a36Sopenharmony_ci .ioctl_compat = hwdep_compat_ioctl, 28562306a36Sopenharmony_ci }; 28662306a36Sopenharmony_ci struct snd_hwdep *hwdep; 28762306a36Sopenharmony_ci int err; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep); 29062306a36Sopenharmony_ci if (err < 0) 29162306a36Sopenharmony_ci return err; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci strcpy(hwdep->name, "MOTU"); 29462306a36Sopenharmony_ci hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU; 29562306a36Sopenharmony_ci hwdep->ops = ops; 29662306a36Sopenharmony_ci hwdep->private_data = motu; 29762306a36Sopenharmony_ci hwdep->exclusive = true; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci motu->hwdep = hwdep; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 303