aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2011-09-04 16:14:15 -0400
committerClemens Ladisch <clemens@ladisch.de>2013-10-20 16:07:57 -0400
commit0c29c9180fe14b0abb4bfc68b37dda66254689b3 (patch)
tree0f47ad91b1d042b6ac851057c3d95485a5fd8cc5 /sound
parent4ed31f20bb5bb90f003c91734c6b9d18169ae27e (diff)
ALSA: dice: implement hwdep device
Implement the hwdep locking and notification mechanisms. Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/firewire/dice.c225
1 files changed, 203 insertions, 22 deletions
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index d0575a96ea70..7225878a09cc 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -5,6 +5,7 @@
5 * Licensed under the terms of the GNU General Public License, version 2. 5 * Licensed under the terms of the GNU General Public License, version 2.
6 */ 6 */
7 7
8#include <linux/compat.h>
8#include <linux/delay.h> 9#include <linux/delay.h>
9#include <linux/device.h> 10#include <linux/device.h>
10#include <linux/firewire.h> 11#include <linux/firewire.h>
@@ -13,8 +14,11 @@
13#include <linux/mod_devicetable.h> 14#include <linux/mod_devicetable.h>
14#include <linux/mutex.h> 15#include <linux/mutex.h>
15#include <linux/slab.h> 16#include <linux/slab.h>
17#include <linux/spinlock.h>
18#include <linux/wait.h>
16#include <sound/control.h> 19#include <sound/control.h>
17#include <sound/core.h> 20#include <sound/core.h>
21#include <sound/firewire.h>
18#include <sound/hwdep.h> 22#include <sound/hwdep.h>
19#include <sound/initval.h> 23#include <sound/initval.h>
20#include <sound/pcm.h> 24#include <sound/pcm.h>
@@ -233,13 +237,18 @@
233struct dice { 237struct dice {
234 struct snd_card *card; 238 struct snd_card *card;
235 struct fw_unit *unit; 239 struct fw_unit *unit;
240 spinlock_t lock;
236 struct mutex mutex; 241 struct mutex mutex;
237 unsigned int global_offset; 242 unsigned int global_offset;
238 unsigned int rx_offset; 243 unsigned int rx_offset;
239 struct fw_address_handler notification_handler; 244 struct fw_address_handler notification_handler;
240 int owner_generation; 245 int owner_generation;
246 int dev_lock_count; /* > 0 driver, < 0 userspace */
247 bool dev_lock_changed;
241 bool global_enabled; 248 bool global_enabled;
242 bool stream_running; 249 bool stream_running;
250 wait_queue_head_t hwdep_wait;
251 u32 notification_bits;
243 struct snd_pcm_substream *pcm; 252 struct snd_pcm_substream *pcm;
244 struct fw_iso_resources resources; 253 struct fw_iso_resources resources;
245 struct amdtp_out_stream stream; 254 struct amdtp_out_stream stream;
@@ -259,6 +268,47 @@ static const unsigned int dice_rates[] = {
259 [6] = 192000, 268 [6] = 192000,
260}; 269};
261 270
271static void dice_lock_changed(struct dice *dice)
272{
273 dice->dev_lock_changed = true;
274 wake_up(&dice->hwdep_wait);
275}
276
277static int dice_try_lock(struct dice *dice)
278{
279 int err;
280
281 spin_lock_irq(&dice->lock);
282
283 if (dice->dev_lock_count < 0) {
284 err = -EBUSY;
285 goto out;
286 }
287
288 if (dice->dev_lock_count++ == 0)
289 dice_lock_changed(dice);
290 err = 0;
291
292out:
293 spin_unlock_irq(&dice->lock);
294
295 return err;
296}
297
298static void dice_unlock(struct dice *dice)
299{
300 spin_lock_irq(&dice->lock);
301
302 if (WARN_ON(dice->dev_lock_count <= 0))
303 goto out;
304
305 if (--dice->dev_lock_count == 0)
306 dice_lock_changed(dice);
307
308out:
309 spin_unlock_irq(&dice->lock);
310}
311
262static inline u64 global_address(struct dice *dice, unsigned int offset) 312static inline u64 global_address(struct dice *dice, unsigned int offset)
263{ 313{
264 return DICE_PRIVATE_SPACE + dice->global_offset + offset; 314 return DICE_PRIVATE_SPACE + dice->global_offset + offset;
@@ -496,6 +546,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
496 void *data, size_t length, void *callback_data) 546 void *data, size_t length, void *callback_data)
497{ 547{
498 struct dice *dice = callback_data; 548 struct dice *dice = callback_data;
549 unsigned long flags;
499 550
500 if (tcode != TCODE_WRITE_QUADLET_REQUEST) { 551 if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
501 fw_send_response(card, request, RCODE_TYPE_ERROR); 552 fw_send_response(card, request, RCODE_TYPE_ERROR);
@@ -505,9 +556,11 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
505 fw_send_response(card, request, RCODE_ADDRESS_ERROR); 556 fw_send_response(card, request, RCODE_ADDRESS_ERROR);
506 return; 557 return;
507 } 558 }
508 dev_dbg(&dice->unit->device, 559 spin_lock_irqsave(&dice->lock, flags);
509 "notification: %08x\n", be32_to_cpup(data)); 560 dice->notification_bits |= be32_to_cpup(data);
561 spin_unlock_irqrestore(&dice->lock, flags);
510 fw_send_response(card, request, RCODE_COMPLETE); 562 fw_send_response(card, request, RCODE_COMPLETE);
563 wake_up(&dice->hwdep_wait);
511} 564}
512 565
513static int dice_open(struct snd_pcm_substream *substream) 566static int dice_open(struct snd_pcm_substream *substream)
@@ -531,26 +584,32 @@ static int dice_open(struct snd_pcm_substream *substream)
531 unsigned int rate; 584 unsigned int rate;
532 int err; 585 int err;
533 586
587 err = dice_try_lock(dice);
588 if (err < 0)
589 goto error;
590
534 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, 591 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
535 global_address(dice, GLOBAL_CLOCK_SELECT), 592 global_address(dice, GLOBAL_CLOCK_SELECT),
536 &clock_sel, 4); 593 &clock_sel, 4);
537 if (err < 0) 594 if (err < 0)
538 return err; 595 goto err_lock;
539 rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; 596 rate = (be32_to_cpu(clock_sel) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
540 if (rate >= ARRAY_SIZE(dice_rates)) 597 if (rate >= ARRAY_SIZE(dice_rates)) {
541 return -ENXIO; 598 err = -ENXIO;
599 goto err_lock;
600 }
542 rate = dice_rates[rate]; 601 rate = dice_rates[rate];
543 602
544 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, 603 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
545 rx_address(dice, RX_NUMBER_AUDIO), 604 rx_address(dice, RX_NUMBER_AUDIO),
546 &number_audio, 4); 605 &number_audio, 4);
547 if (err < 0) 606 if (err < 0)
548 return err; 607 goto err_lock;
549 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, 608 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
550 rx_address(dice, RX_NUMBER_MIDI), 609 rx_address(dice, RX_NUMBER_MIDI),
551 &number_midi, 4); 610 &number_midi, 4);
552 if (err < 0) 611 if (err < 0)
553 return err; 612 goto err_lock;
554 613
555 runtime->hw = hardware; 614 runtime->hw = hardware;
556 615
@@ -568,17 +627,26 @@ static int dice_open(struct snd_pcm_substream *substream)
568 SNDRV_PCM_HW_PARAM_PERIOD_TIME, 627 SNDRV_PCM_HW_PARAM_PERIOD_TIME,
569 5000, 8192000); 628 5000, 8192000);
570 if (err < 0) 629 if (err < 0)
571 return err; 630 goto err_lock;
572 631
573 err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); 632 err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
574 if (err < 0) 633 if (err < 0)
575 return err; 634 goto err_lock;
576 635
577 return 0; 636 return 0;
637
638err_lock:
639 dice_unlock(dice);
640error:
641 return err;
578} 642}
579 643
580static int dice_close(struct snd_pcm_substream *substream) 644static int dice_close(struct snd_pcm_substream *substream)
581{ 645{
646 struct dice *dice = substream->private_data;
647
648 dice_unlock(dice);
649
582 return 0; 650 return 0;
583} 651}
584 652
@@ -783,45 +851,156 @@ static int dice_create_pcm(struct dice *dice)
783 return 0; 851 return 0;
784} 852}
785 853
786// TODO: implement these
787
788static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, 854static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
789 long count, loff_t *offset) 855 long count, loff_t *offset)
790{ 856{
791 return -EIO; 857 struct dice *dice = hwdep->private_data;
858 DEFINE_WAIT(wait);
859 union snd_firewire_event event;
860
861 spin_lock_irq(&dice->lock);
862
863 while (!dice->dev_lock_changed && dice->notification_bits == 0) {
864 prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
865 spin_unlock_irq(&dice->lock);
866 schedule();
867 finish_wait(&dice->hwdep_wait, &wait);
868 if (signal_pending(current))
869 return -ERESTARTSYS;
870 spin_lock_irq(&dice->lock);
871 }
872
873 memset(&event, 0, sizeof(event));
874 if (dice->dev_lock_changed) {
875 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
876 event.lock_status.status = dice->dev_lock_count > 0;
877 dice->dev_lock_changed = false;
878
879 count = min(count, (long)sizeof(event.lock_status));
880 } else {
881 event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION;
882 event.dice_notification.notification = dice->notification_bits;
883 dice->notification_bits = 0;
884
885 count = min(count, (long)sizeof(event.dice_notification));
886 }
887
888 spin_unlock_irq(&dice->lock);
889
890 if (copy_to_user(buf, &event, count))
891 return -EFAULT;
892
893 return count;
792} 894}
793 895
794static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file) 896static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
897 poll_table *wait)
795{ 898{
796 return -EIO; 899 struct dice *dice = hwdep->private_data;
900 unsigned int events;
901
902 poll_wait(file, &dice->hwdep_wait, wait);
903
904 spin_lock_irq(&dice->lock);
905 if (dice->dev_lock_changed || dice->notification_bits != 0)
906 events = POLLIN | POLLRDNORM;
907 else
908 events = 0;
909 spin_unlock_irq(&dice->lock);
910
911 return events;
797} 912}
798 913
799static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) 914static int dice_hwdep_get_info(struct dice *dice, void __user *arg)
800{ 915{
916 struct fw_device *dev = fw_parent_device(dice->unit);
917 struct snd_firewire_get_info info;
918
919 memset(&info, 0, sizeof(info));
920 info.type = SNDRV_FIREWIRE_TYPE_DICE;
921 info.card = dev->card->index;
922 *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
923 *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
924 strlcpy(info.device_name, dev_name(&dev->device),
925 sizeof(info.device_name));
926
927 if (copy_to_user(arg, &info, sizeof(info)))
928 return -EFAULT;
929
801 return 0; 930 return 0;
802} 931}
803 932
804static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, 933static int dice_hwdep_lock(struct dice *dice)
805 poll_table *wait) 934{
935 int err;
936
937 spin_lock_irq(&dice->lock);
938
939 if (dice->dev_lock_count == 0) {
940 dice->dev_lock_count = -1;
941 err = 0;
942 } else {
943 err = -EBUSY;
944 }
945
946 spin_unlock_irq(&dice->lock);
947
948 return err;
949}
950
951static int dice_hwdep_unlock(struct dice *dice)
806{ 952{
807 return POLLERR | POLLHUP; 953 int err;
954
955 spin_lock_irq(&dice->lock);
956
957 if (dice->dev_lock_count == -1) {
958 dice->dev_lock_count = 0;
959 err = 0;
960 } else {
961 err = -EBADFD;
962 }
963
964 spin_unlock_irq(&dice->lock);
965
966 return err;
808} 967}
809 968
810static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, 969static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
811 unsigned int cmd, unsigned long arg) 970 unsigned int cmd, unsigned long arg)
812{ 971{
813 return -EIO; 972 struct dice *dice = hwdep->private_data;
973
974 switch (cmd) {
975 case SNDRV_FIREWIRE_IOCTL_GET_INFO:
976 return dice_hwdep_get_info(dice, (void __user *)arg);
977 case SNDRV_FIREWIRE_IOCTL_LOCK:
978 return dice_hwdep_lock(dice);
979 case SNDRV_FIREWIRE_IOCTL_UNLOCK:
980 return dice_hwdep_unlock(dice);
981 default:
982 return -ENOIOCTLCMD;
983 }
984}
985
986#ifdef CONFIG_COMPAT
987static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
988 unsigned int cmd, unsigned long arg)
989{
990 return dice_hwdep_ioctl(hwdep, file, cmd,
991 (unsigned long)compat_ptr(arg));
814} 992}
993#else
994#define dice_hwdep_compat_ioctl NULL
995#endif
815 996
816static int dice_create_hwdep(struct dice *dice) 997static int dice_create_hwdep(struct dice *dice)
817{ 998{
818 static const struct snd_hwdep_ops ops = { 999 static const struct snd_hwdep_ops ops = {
819 .read = dice_hwdep_read, 1000 .read = dice_hwdep_read,
820 .open = dice_hwdep_open,
821 .release = dice_hwdep_release,
822 .poll = dice_hwdep_poll, 1001 .poll = dice_hwdep_poll,
823 .ioctl = dice_hwdep_ioctl, 1002 .ioctl = dice_hwdep_ioctl,
824 .ioctl_compat = dice_hwdep_ioctl, 1003 .ioctl_compat = dice_hwdep_compat_ioctl,
825 }; 1004 };
826 struct snd_hwdep *hwdep; 1005 struct snd_hwdep *hwdep;
827 int err; 1006 int err;
@@ -922,8 +1101,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
922 1101
923 dice = card->private_data; 1102 dice = card->private_data;
924 dice->card = card; 1103 dice->card = card;
1104 spin_lock_init(&dice->lock);
925 mutex_init(&dice->mutex); 1105 mutex_init(&dice->mutex);
926 dice->unit = unit; 1106 dice->unit = unit;
1107 init_waitqueue_head(&dice->hwdep_wait);
927 1108
928 err = dice_init_offsets(dice); 1109 err = dice_init_offsets(dice);
929 if (err < 0) 1110 if (err < 0)