Loading sound/firewire/dice.c +203 −22 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ #include <linux/compat.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firewire.h> Loading @@ -13,8 +14,11 @@ #include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/wait.h> #include <sound/control.h> #include <sound/core.h> #include <sound/firewire.h> #include <sound/hwdep.h> #include <sound/initval.h> #include <sound/pcm.h> Loading Loading @@ -233,13 +237,18 @@ struct dice { struct snd_card *card; struct fw_unit *unit; spinlock_t lock; struct mutex mutex; unsigned int global_offset; unsigned int rx_offset; struct fw_address_handler notification_handler; int owner_generation; int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; bool global_enabled; bool stream_running; wait_queue_head_t hwdep_wait; u32 notification_bits; struct snd_pcm_substream *pcm; struct fw_iso_resources resources; struct amdtp_out_stream stream; Loading @@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = { [6] = 192000, }; static void dice_lock_changed(struct dice *dice) { dice->dev_lock_changed = true; wake_up(&dice->hwdep_wait); } static int dice_try_lock(struct dice *dice) { int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count < 0) { err = -EBUSY; goto out; } if (dice->dev_lock_count++ == 0) dice_lock_changed(dice); err = 0; out: spin_unlock_irq(&dice->lock); return err; } static void dice_unlock(struct dice *dice) { spin_lock_irq(&dice->lock); if (WARN_ON(dice->dev_lock_count <= 0)) goto out; if (--dice->dev_lock_count == 0) dice_lock_changed(dice); out: spin_unlock_irq(&dice->lock); } static inline u64 global_address(struct dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; Loading Loading @@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, void *data, size_t length, void *callback_data) { struct dice *dice = callback_data; unsigned long flags; if (tcode != TCODE_WRITE_QUADLET_REQUEST) { fw_send_response(card, request, RCODE_TYPE_ERROR); Loading @@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_ADDRESS_ERROR); return; } dev_dbg(&dice->unit->device, "notification: %08x\n", be32_to_cpup(data)); spin_lock_irqsave(&dice->lock, flags); dice->notification_bits |= be32_to_cpup(data); spin_unlock_irqrestore(&dice->lock, flags); fw_send_response(card, request, RCODE_COMPLETE); wake_up(&dice->hwdep_wait); } static int dice_open(struct snd_pcm_substream *substream) Loading @@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream) unsigned int rate; int err; err = dice_try_lock(dice); if (err < 0) goto error; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), &clock_sel, 4); if (err < 0) return err; goto err_lock; rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; if (rate >= ARRAY_SIZE(dice_rates)) return -ENXIO; if (rate >= ARRAY_SIZE(dice_rates)) { err = -ENXIO; goto err_lock; } rate = dice_rates[rate]; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), &number_audio, 4); if (err < 0) return err; goto err_lock; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_MIDI), &number_midi, 4); if (err < 0) return err; goto err_lock; runtime->hw = hardware; Loading @@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, 8192000); if (err < 0) return err; goto err_lock; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; goto err_lock; return 0; err_lock: dice_unlock(dice); error: return err; } static int dice_close(struct snd_pcm_substream *substream) { struct dice *dice = substream->private_data; dice_unlock(dice); return 0; } Loading Loading @@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice) return 0; } // TODO: implement these static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { return -EIO; struct dice *dice = hwdep->private_data; DEFINE_WAIT(wait); union snd_firewire_event event; spin_lock_irq(&dice->lock); while (!dice->dev_lock_changed && dice->notification_bits == 0) { prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&dice->lock); schedule(); finish_wait(&dice->hwdep_wait, &wait); if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irq(&dice->lock); } static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) memset(&event, 0, sizeof(event)); if (dice->dev_lock_changed) { event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; event.lock_status.status = dice->dev_lock_count > 0; dice->dev_lock_changed = false; count = min(count, (long)sizeof(event.lock_status)); } else { event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; event.dice_notification.notification = dice->notification_bits; dice->notification_bits = 0; count = min(count, (long)sizeof(event.dice_notification)); } spin_unlock_irq(&dice->lock); if (copy_to_user(buf, &event, count)) return -EFAULT; return count; } static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) { return -EIO; struct dice *dice = hwdep->private_data; unsigned int events; poll_wait(file, &dice->hwdep_wait, wait); spin_lock_irq(&dice->lock); if (dice->dev_lock_changed || dice->notification_bits != 0) events = POLLIN | POLLRDNORM; else events = 0; spin_unlock_irq(&dice->lock); return events; } static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) static int dice_hwdep_get_info(struct dice *dice, void __user *arg) { struct fw_device *dev = fw_parent_device(dice->unit); struct snd_firewire_get_info info; memset(&info, 0, sizeof(info)); info.type = SNDRV_FIREWIRE_TYPE_DICE; info.card = dev->card->index; *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); strlcpy(info.device_name, dev_name(&dev->device), sizeof(info.device_name)); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0; } static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) static int dice_hwdep_lock(struct dice *dice) { int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count == 0) { dice->dev_lock_count = -1; err = 0; } else { err = -EBUSY; } spin_unlock_irq(&dice->lock); return err; } static int dice_hwdep_unlock(struct dice *dice) { return POLLERR | POLLHUP; int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count == -1) { dice->dev_lock_count = 0; err = 0; } else { err = -EBADFD; } spin_unlock_irq(&dice->lock); return err; } static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { return -EIO; struct dice *dice = hwdep->private_data; switch (cmd) { case SNDRV_FIREWIRE_IOCTL_GET_INFO: return dice_hwdep_get_info(dice, (void __user *)arg); case SNDRV_FIREWIRE_IOCTL_LOCK: return dice_hwdep_lock(dice); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return dice_hwdep_unlock(dice); default: return -ENOIOCTLCMD; } } #ifdef CONFIG_COMPAT static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { return dice_hwdep_ioctl(hwdep, file, cmd, (unsigned long)compat_ptr(arg)); } #else #define dice_hwdep_compat_ioctl NULL #endif static int dice_create_hwdep(struct dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, .open = dice_hwdep_open, .release = dice_hwdep_release, .poll = dice_hwdep_poll, .ioctl = dice_hwdep_ioctl, .ioctl_compat = dice_hwdep_ioctl, .ioctl_compat = dice_hwdep_compat_ioctl, }; struct snd_hwdep *hwdep; int err; Loading Loading @@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice = card->private_data; dice->card = card; spin_lock_init(&dice->lock); mutex_init(&dice->mutex); dice->unit = unit; init_waitqueue_head(&dice->hwdep_wait); err = dice_init_offsets(dice); if (err < 0) Loading Loading
sound/firewire/dice.c +203 −22 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ #include <linux/compat.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/firewire.h> Loading @@ -13,8 +14,11 @@ #include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/wait.h> #include <sound/control.h> #include <sound/core.h> #include <sound/firewire.h> #include <sound/hwdep.h> #include <sound/initval.h> #include <sound/pcm.h> Loading Loading @@ -233,13 +237,18 @@ struct dice { struct snd_card *card; struct fw_unit *unit; spinlock_t lock; struct mutex mutex; unsigned int global_offset; unsigned int rx_offset; struct fw_address_handler notification_handler; int owner_generation; int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; bool global_enabled; bool stream_running; wait_queue_head_t hwdep_wait; u32 notification_bits; struct snd_pcm_substream *pcm; struct fw_iso_resources resources; struct amdtp_out_stream stream; Loading @@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = { [6] = 192000, }; static void dice_lock_changed(struct dice *dice) { dice->dev_lock_changed = true; wake_up(&dice->hwdep_wait); } static int dice_try_lock(struct dice *dice) { int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count < 0) { err = -EBUSY; goto out; } if (dice->dev_lock_count++ == 0) dice_lock_changed(dice); err = 0; out: spin_unlock_irq(&dice->lock); return err; } static void dice_unlock(struct dice *dice) { spin_lock_irq(&dice->lock); if (WARN_ON(dice->dev_lock_count <= 0)) goto out; if (--dice->dev_lock_count == 0) dice_lock_changed(dice); out: spin_unlock_irq(&dice->lock); } static inline u64 global_address(struct dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; Loading Loading @@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, void *data, size_t length, void *callback_data) { struct dice *dice = callback_data; unsigned long flags; if (tcode != TCODE_WRITE_QUADLET_REQUEST) { fw_send_response(card, request, RCODE_TYPE_ERROR); Loading @@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_ADDRESS_ERROR); return; } dev_dbg(&dice->unit->device, "notification: %08x\n", be32_to_cpup(data)); spin_lock_irqsave(&dice->lock, flags); dice->notification_bits |= be32_to_cpup(data); spin_unlock_irqrestore(&dice->lock, flags); fw_send_response(card, request, RCODE_COMPLETE); wake_up(&dice->hwdep_wait); } static int dice_open(struct snd_pcm_substream *substream) Loading @@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream) unsigned int rate; int err; err = dice_try_lock(dice); if (err < 0) goto error; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, global_address(dice, GLOBAL_CLOCK_SELECT), &clock_sel, 4); if (err < 0) return err; goto err_lock; rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; if (rate >= ARRAY_SIZE(dice_rates)) return -ENXIO; if (rate >= ARRAY_SIZE(dice_rates)) { err = -ENXIO; goto err_lock; } rate = dice_rates[rate]; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_AUDIO), &number_audio, 4); if (err < 0) return err; goto err_lock; err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, rx_address(dice, RX_NUMBER_MIDI), &number_midi, 4); if (err < 0) return err; goto err_lock; runtime->hw = hardware; Loading @@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5000, 8192000); if (err < 0) return err; goto err_lock; err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); if (err < 0) return err; goto err_lock; return 0; err_lock: dice_unlock(dice); error: return err; } static int dice_close(struct snd_pcm_substream *substream) { struct dice *dice = substream->private_data; dice_unlock(dice); return 0; } Loading Loading @@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice) return 0; } // TODO: implement these static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { return -EIO; struct dice *dice = hwdep->private_data; DEFINE_WAIT(wait); union snd_firewire_event event; spin_lock_irq(&dice->lock); while (!dice->dev_lock_changed && dice->notification_bits == 0) { prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&dice->lock); schedule(); finish_wait(&dice->hwdep_wait, &wait); if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irq(&dice->lock); } static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) memset(&event, 0, sizeof(event)); if (dice->dev_lock_changed) { event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; event.lock_status.status = dice->dev_lock_count > 0; dice->dev_lock_changed = false; count = min(count, (long)sizeof(event.lock_status)); } else { event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; event.dice_notification.notification = dice->notification_bits; dice->notification_bits = 0; count = min(count, (long)sizeof(event.dice_notification)); } spin_unlock_irq(&dice->lock); if (copy_to_user(buf, &event, count)) return -EFAULT; return count; } static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) { return -EIO; struct dice *dice = hwdep->private_data; unsigned int events; poll_wait(file, &dice->hwdep_wait, wait); spin_lock_irq(&dice->lock); if (dice->dev_lock_changed || dice->notification_bits != 0) events = POLLIN | POLLRDNORM; else events = 0; spin_unlock_irq(&dice->lock); return events; } static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) static int dice_hwdep_get_info(struct dice *dice, void __user *arg) { struct fw_device *dev = fw_parent_device(dice->unit); struct snd_firewire_get_info info; memset(&info, 0, sizeof(info)); info.type = SNDRV_FIREWIRE_TYPE_DICE; info.card = dev->card->index; *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); strlcpy(info.device_name, dev_name(&dev->device), sizeof(info.device_name)); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; return 0; } static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) static int dice_hwdep_lock(struct dice *dice) { int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count == 0) { dice->dev_lock_count = -1; err = 0; } else { err = -EBUSY; } spin_unlock_irq(&dice->lock); return err; } static int dice_hwdep_unlock(struct dice *dice) { return POLLERR | POLLHUP; int err; spin_lock_irq(&dice->lock); if (dice->dev_lock_count == -1) { dice->dev_lock_count = 0; err = 0; } else { err = -EBADFD; } spin_unlock_irq(&dice->lock); return err; } static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { return -EIO; struct dice *dice = hwdep->private_data; switch (cmd) { case SNDRV_FIREWIRE_IOCTL_GET_INFO: return dice_hwdep_get_info(dice, (void __user *)arg); case SNDRV_FIREWIRE_IOCTL_LOCK: return dice_hwdep_lock(dice); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return dice_hwdep_unlock(dice); default: return -ENOIOCTLCMD; } } #ifdef CONFIG_COMPAT static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { return dice_hwdep_ioctl(hwdep, file, cmd, (unsigned long)compat_ptr(arg)); } #else #define dice_hwdep_compat_ioctl NULL #endif static int dice_create_hwdep(struct dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, .open = dice_hwdep_open, .release = dice_hwdep_release, .poll = dice_hwdep_poll, .ioctl = dice_hwdep_ioctl, .ioctl_compat = dice_hwdep_ioctl, .ioctl_compat = dice_hwdep_compat_ioctl, }; struct snd_hwdep *hwdep; int err; Loading Loading @@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice = card->private_data; dice->card = card; spin_lock_init(&dice->lock); mutex_init(&dice->mutex); dice->unit = unit; init_waitqueue_head(&dice->hwdep_wait); err = dice_init_offsets(dice); if (err < 0) Loading