aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2005-04-29 10:29:28 -0400
committerJaroslav Kysela <perex@suse.cz>2005-05-29 04:05:43 -0400
commitb259b10c420a59a2fdbcf5a3498253ebcbdffa1e (patch)
tree528711b6b9625ca55cf4e9cbf4748870327e611e /sound/usb
parent6639b6c2367f884ca172b78d69f7da17bfab2e5e (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/usb')
-rw-r--r--sound/usb/usbmixer.c159
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
1624static 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
1607static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs) 1635static 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
1697static 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
1720static 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
1729static 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
1738static 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
1755static 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
1764static 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
1667int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif) 1817int 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}