diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2005-04-29 10:29:28 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-05-29 04:05:43 -0400 |
commit | b259b10c420a59a2fdbcf5a3498253ebcbdffa1e (patch) | |
tree | 528711b6b9625ca55cf4e9cbf4748870327e611e /sound | |
parent | 6639b6c2367f884ca172b78d69f7da17bfab2e5e (diff) |
[ALSA] usb-audio - add Extigy/Audigy 2 NX remote control support
ALSA Core,USB generic driver
Add an hwdep interface that supports reading remote control data from
Sound Blaster Extigy and Audigy 2 NX devices.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/usbmixer.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index dd045ea6fb01..7ea42d43d7ff 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/usb.h> | 35 | #include <linux/usb.h> |
36 | #include <sound/core.h> | 36 | #include <sound/core.h> |
37 | #include <sound/control.h> | 37 | #include <sound/control.h> |
38 | #include <sound/hwdep.h> | ||
38 | 39 | ||
39 | #include "usbaudio.h" | 40 | #include "usbaudio.h" |
40 | 41 | ||
@@ -57,6 +58,19 @@ struct usb_mixer_interface { | |||
57 | unsigned int ignore_ctl_error; | 58 | unsigned int ignore_ctl_error; |
58 | struct urb *urb; | 59 | struct urb *urb; |
59 | usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */ | 60 | usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */ |
61 | |||
62 | /* Sound Blaster remote control stuff */ | ||
63 | enum { | ||
64 | RC_NONE, | ||
65 | RC_EXTIGY, | ||
66 | RC_AUDIGY2NX, | ||
67 | } rc_type; | ||
68 | unsigned long rc_hwdep_open; | ||
69 | u32 rc_code; | ||
70 | wait_queue_head_t rc_waitq; | ||
71 | struct urb *rc_urb; | ||
72 | struct usb_ctrlrequest *rc_setup_packet; | ||
73 | u8 rc_buffer[6]; | ||
60 | }; | 74 | }; |
61 | 75 | ||
62 | 76 | ||
@@ -1536,6 +1550,9 @@ static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) | |||
1536 | kfree(mixer->urb->transfer_buffer); | 1550 | kfree(mixer->urb->transfer_buffer); |
1537 | usb_free_urb(mixer->urb); | 1551 | usb_free_urb(mixer->urb); |
1538 | } | 1552 | } |
1553 | if (mixer->rc_urb) | ||
1554 | usb_free_urb(mixer->rc_urb); | ||
1555 | kfree(mixer->rc_setup_packet); | ||
1539 | kfree(mixer); | 1556 | kfree(mixer); |
1540 | } | 1557 | } |
1541 | 1558 | ||
@@ -1604,6 +1621,17 @@ static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, | |||
1604 | info->elem_id); | 1621 | info->elem_id); |
1605 | } | 1622 | } |
1606 | 1623 | ||
1624 | static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer, | ||
1625 | int unitid) | ||
1626 | { | ||
1627 | /* SB remote control */ | ||
1628 | if (mixer->rc_type != RC_NONE && unitid == 0) { | ||
1629 | /* read control code from device memory */ | ||
1630 | mixer->rc_urb->dev = mixer->chip->dev; | ||
1631 | usb_submit_urb(mixer->rc_urb, GFP_ATOMIC); | ||
1632 | } | ||
1633 | } | ||
1634 | |||
1607 | static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs) | 1635 | static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs) |
1608 | { | 1636 | { |
1609 | struct usb_mixer_interface *mixer = urb->context; | 1637 | struct usb_mixer_interface *mixer = urb->context; |
@@ -1620,6 +1648,8 @@ static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs) | |||
1620 | continue; | 1648 | continue; |
1621 | if (!(buf[0] & 0x40)) | 1649 | if (!(buf[0] & 0x40)) |
1622 | snd_usb_mixer_notify_id(mixer, buf[1]); | 1650 | snd_usb_mixer_notify_id(mixer, buf[1]); |
1651 | else | ||
1652 | snd_usb_mixer_memory_change(mixer, buf[1]); | ||
1623 | } | 1653 | } |
1624 | } | 1654 | } |
1625 | if (urb->status != -ENOENT && urb->status != -ECONNRESET) { | 1655 | if (urb->status != -ENOENT && urb->status != -ECONNRESET) { |
@@ -1664,6 +1694,126 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) | |||
1664 | return 0; | 1694 | return 0; |
1665 | } | 1695 | } |
1666 | 1696 | ||
1697 | static void snd_usb_soundblaster_remote_complete(struct urb *urb, | ||
1698 | struct pt_regs *regs) | ||
1699 | { | ||
1700 | struct usb_mixer_interface *mixer = urb->context; | ||
1701 | /* | ||
1702 | * format of remote control data: | ||
1703 | * Extigy: xx 00 | ||
1704 | * Audigy 2 NX: 06 80 xx 00 00 00 | ||
1705 | */ | ||
1706 | int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2; | ||
1707 | u32 code; | ||
1708 | |||
1709 | if (urb->status < 0 || urb->actual_length <= offset) | ||
1710 | return; | ||
1711 | code = mixer->rc_buffer[offset]; | ||
1712 | /* the Mute button actually changes the mixer control */ | ||
1713 | if (code == 13) | ||
1714 | snd_usb_mixer_notify_id(mixer, 18); | ||
1715 | mixer->rc_code = code; | ||
1716 | wmb(); | ||
1717 | wake_up(&mixer->rc_waitq); | ||
1718 | } | ||
1719 | |||
1720 | static int snd_usb_sbrc_hwdep_open(snd_hwdep_t *hw, struct file *file) | ||
1721 | { | ||
1722 | struct usb_mixer_interface *mixer = hw->private_data; | ||
1723 | |||
1724 | if (test_and_set_bit(0, &mixer->rc_hwdep_open)) | ||
1725 | return -EBUSY; | ||
1726 | return 0; | ||
1727 | } | ||
1728 | |||
1729 | static int snd_usb_sbrc_hwdep_release(snd_hwdep_t *hw, struct file *file) | ||
1730 | { | ||
1731 | struct usb_mixer_interface *mixer = hw->private_data; | ||
1732 | |||
1733 | clear_bit(0, &mixer->rc_hwdep_open); | ||
1734 | smp_mb__after_clear_bit(); | ||
1735 | return 0; | ||
1736 | } | ||
1737 | |||
1738 | static long snd_usb_sbrc_hwdep_read(snd_hwdep_t *hw, char __user *buf, | ||
1739 | long count, loff_t *offset) | ||
1740 | { | ||
1741 | struct usb_mixer_interface *mixer = hw->private_data; | ||
1742 | int err; | ||
1743 | u32 rc_code; | ||
1744 | |||
1745 | if (count != 1) | ||
1746 | return -EINVAL; | ||
1747 | err = wait_event_interruptible(mixer->rc_waitq, | ||
1748 | (rc_code = xchg(&mixer->rc_code, 0)) != 0); | ||
1749 | if (err == 0) { | ||
1750 | err = put_user(rc_code, buf); | ||
1751 | } | ||
1752 | return err < 0 ? err : count; | ||
1753 | } | ||
1754 | |||
1755 | static unsigned int snd_usb_sbrc_hwdep_poll(snd_hwdep_t *hw, struct file *file, | ||
1756 | poll_table *wait) | ||
1757 | { | ||
1758 | struct usb_mixer_interface *mixer = hw->private_data; | ||
1759 | |||
1760 | poll_wait(file, &mixer->rc_waitq, wait); | ||
1761 | return mixer->rc_code ? POLLIN | POLLRDNORM : 0; | ||
1762 | } | ||
1763 | |||
1764 | static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) | ||
1765 | { | ||
1766 | snd_hwdep_t *hwdep; | ||
1767 | int err, len; | ||
1768 | |||
1769 | switch (le16_to_cpu(mixer->chip->dev->descriptor.idProduct)) { | ||
1770 | case 0x3000: | ||
1771 | mixer->rc_type = RC_EXTIGY; | ||
1772 | len = 2; | ||
1773 | break; | ||
1774 | case 0x3020: | ||
1775 | mixer->rc_type = RC_AUDIGY2NX; | ||
1776 | len = 6; | ||
1777 | break; | ||
1778 | default: | ||
1779 | return 0; | ||
1780 | } | ||
1781 | |||
1782 | init_waitqueue_head(&mixer->rc_waitq); | ||
1783 | err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); | ||
1784 | if (err < 0) | ||
1785 | return err; | ||
1786 | snprintf(hwdep->name, sizeof(hwdep->name), | ||
1787 | "%s remote control", mixer->chip->card->shortname); | ||
1788 | hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC; | ||
1789 | hwdep->private_data = mixer; | ||
1790 | hwdep->ops.read = snd_usb_sbrc_hwdep_read; | ||
1791 | hwdep->ops.open = snd_usb_sbrc_hwdep_open; | ||
1792 | hwdep->ops.release = snd_usb_sbrc_hwdep_release; | ||
1793 | hwdep->ops.poll = snd_usb_sbrc_hwdep_poll; | ||
1794 | |||
1795 | mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
1796 | if (!mixer->rc_urb) | ||
1797 | return -ENOMEM; | ||
1798 | mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL); | ||
1799 | if (!mixer->rc_setup_packet) { | ||
1800 | usb_free_urb(mixer->rc_urb); | ||
1801 | mixer->rc_urb = NULL; | ||
1802 | return -ENOMEM; | ||
1803 | } | ||
1804 | mixer->rc_setup_packet->bRequestType = | ||
1805 | USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE; | ||
1806 | mixer->rc_setup_packet->bRequest = GET_MEM; | ||
1807 | mixer->rc_setup_packet->wValue = cpu_to_le16(0); | ||
1808 | mixer->rc_setup_packet->wIndex = cpu_to_le16(0); | ||
1809 | mixer->rc_setup_packet->wLength = cpu_to_le16(len); | ||
1810 | usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev, | ||
1811 | usb_rcvctrlpipe(mixer->chip->dev, 0), | ||
1812 | (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len, | ||
1813 | snd_usb_soundblaster_remote_complete, mixer); | ||
1814 | return 0; | ||
1815 | } | ||
1816 | |||
1667 | int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) | 1817 | int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) |
1668 | { | 1818 | { |
1669 | static snd_device_ops_t dev_ops = { | 1819 | static snd_device_ops_t dev_ops = { |
@@ -1694,6 +1844,13 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) | |||
1694 | return err; | 1844 | return err; |
1695 | } | 1845 | } |
1696 | 1846 | ||
1847 | if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x041e) { | ||
1848 | if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0) { | ||
1849 | snd_usb_mixer_free(mixer); | ||
1850 | return err; | ||
1851 | } | ||
1852 | } | ||
1853 | |||
1697 | err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); | 1854 | err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops); |
1698 | if (err < 0) { | 1855 | if (err < 0) { |
1699 | snd_usb_mixer_free(mixer); | 1856 | snd_usb_mixer_free(mixer); |
@@ -1710,4 +1867,6 @@ void snd_usb_mixer_disconnect(struct list_head *p) | |||
1710 | mixer = list_entry(p, struct usb_mixer_interface, list); | 1867 | mixer = list_entry(p, struct usb_mixer_interface, list); |
1711 | if (mixer->urb) | 1868 | if (mixer->urb) |
1712 | usb_kill_urb(mixer->urb); | 1869 | usb_kill_urb(mixer->urb); |
1870 | if (mixer->rc_urb) | ||
1871 | usb_kill_urb(mixer->rc_urb); | ||
1713 | } | 1872 | } |