Merge branch 'fix/misc' into topic/usb
authorTakashi Iwai <tiwai@suse.de>
Sat, 10 Apr 2010 19:34:56 +0000 (21:34 +0200)
committerTakashi Iwai <tiwai@suse.de>
Sat, 10 Apr 2010 19:34:56 +0000 (21:34 +0200)
91 files changed:
arch/x86/kernel/cpu/perf_event_amd.c
drivers/char/virtio_console.c
drivers/edac/edac_mce_amd.c
drivers/net/arm/ks8695net.c
drivers/net/igb/e1000_82575.c
drivers/net/igb/e1000_hw.h
drivers/net/igb/igb_main.c
drivers/net/ixgbe/ixgbe_82599.c
drivers/net/ixgbe/ixgbe_fcoe.c
drivers/net/ixgbe/ixgbe_main.c
drivers/net/ixgbe/ixgbe_type.h
drivers/net/ixgbevf/ethtool.c
drivers/net/ixgbevf/ixgbevf_main.c
drivers/net/ixgbevf/vf.h
drivers/net/jme.c
drivers/net/jme.h
drivers/net/ks8851.c
drivers/net/usb/smsc95xx.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/iwlwifi/iwl-tx.c
drivers/net/wireless/wl12xx/wl1251_debugfs.c
drivers/vhost/net.c
drivers/vhost/vhost.c
fs/afs/security.c
include/linux/if_tunnel.h
include/linux/netdevice.h
include/linux/netfilter/nfnetlink.h
include/linux/netlink.h
include/linux/syscalls.h
include/linux/usb/audio-v2.h [new file with mode: 0644]
include/linux/usb/audio.h
include/net/bluetooth/bluetooth.h
include/net/netlink.h
ipc/syscall.c
net/8021q/vlan_core.c
net/bluetooth/hci_sysfs.c
net/bluetooth/l2cap.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/sco.c
net/core/dev.c
net/ipv4/fib_trie.c
net/ipv4/ip_gre.c
net/ipv4/ipmr.c
net/ipv4/route.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv6/ip6mr.c
net/ipv6/route.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nfnetlink.c
net/netlink/af_netlink.c
net/rxrpc/ar-accept.c
sound/usb/Kconfig
sound/usb/Makefile
sound/usb/caiaq/control.c
sound/usb/caiaq/device.c
sound/usb/caiaq/device.h
sound/usb/caiaq/input.c
sound/usb/card.c [new file with mode: 0644]
sound/usb/card.h [new file with mode: 0644]
sound/usb/debug.h [new file with mode: 0644]
sound/usb/endpoint.c [new file with mode: 0644]
sound/usb/endpoint.h [new file with mode: 0644]
sound/usb/format.c [new file with mode: 0644]
sound/usb/format.h [new file with mode: 0644]
sound/usb/helper.c [new file with mode: 0644]
sound/usb/helper.h [new file with mode: 0644]
sound/usb/midi.c [moved from sound/usb/usbmidi.c with 99% similarity]
sound/usb/midi.h [new file with mode: 0644]
sound/usb/misc/Makefile [new file with mode: 0644]
sound/usb/misc/ua101.c [moved from sound/usb/ua101.c with 99% similarity]
sound/usb/mixer.c [moved from sound/usb/usbmixer.c with 72% similarity]
sound/usb/mixer.h [new file with mode: 0644]
sound/usb/mixer_maps.c [moved from sound/usb/usbmixer_maps.c with 98% similarity]
sound/usb/mixer_quirks.c [new file with mode: 0644]
sound/usb/mixer_quirks.h [new file with mode: 0644]
sound/usb/pcm.c [new file with mode: 0644]
sound/usb/pcm.h [new file with mode: 0644]
sound/usb/proc.c [new file with mode: 0644]
sound/usb/proc.h [new file with mode: 0644]
sound/usb/quirks-table.h [moved from sound/usb/usbquirks.h with 97% similarity]
sound/usb/quirks.c [new file with mode: 0644]
sound/usb/quirks.h [new file with mode: 0644]
sound/usb/urb.c [new file with mode: 0644]
sound/usb/urb.h [new file with mode: 0644]
sound/usb/usbaudio.c [deleted file]
sound/usb/usbaudio.h
sound/usb/usx2y/us122l.c
sound/usb/usx2y/usbusx2y.h

index 573458f..b87e0b6 100644 (file)
@@ -348,10 +348,12 @@ static void amd_pmu_cpu_offline(int cpu)
 
        raw_spin_lock(&amd_nb_lock);
 
-       if (--cpuhw->amd_nb->refcnt == 0)
-               kfree(cpuhw->amd_nb);
+       if (cpuhw->amd_nb) {
+               if (--cpuhw->amd_nb->refcnt == 0)
+                       kfree(cpuhw->amd_nb);
 
-       cpuhw->amd_nb = NULL;
+               cpuhw->amd_nb = NULL;
+       }
 
        raw_spin_unlock(&amd_nb_lock);
 }
index f404ccf..44288ce 100644 (file)
@@ -681,6 +681,10 @@ static void resize_console(struct port *port)
        struct virtio_device *vdev;
        struct winsize ws;
 
+       /* The port could have been hot-unplugged */
+       if (!port)
+               return;
+
        vdev = port->portdev->vdev;
        if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
                vdev->config->get(vdev,
@@ -947,11 +951,18 @@ static void handle_control_message(struct ports_device *portdev,
                 */
                err = sysfs_create_group(&port->dev->kobj,
                                         &port_attribute_group);
-               if (err)
+               if (err) {
                        dev_err(port->dev,
                                "Error %d creating sysfs device attributes\n",
                                err);
-
+               } else {
+                       /*
+                        * Generate a udev event so that appropriate
+                        * symlinks can be created based on udev
+                        * rules.
+                        */
+                       kobject_uevent(&port->dev->kobj, KOBJ_CHANGE);
+               }
                break;
        case VIRTIO_CONSOLE_PORT_REMOVE:
                /*
index 8fc91a0..f5b6d9f 100644 (file)
@@ -316,7 +316,12 @@ void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors)
                if (regs->nbsh & K8_NBSH_ERR_CPU_VAL)
                        pr_cont(", core: %u\n", (u8)(regs->nbsh & 0xf));
        } else {
-               pr_cont(", core: %d\n", fls((regs->nbsh & 0xf) - 1));
+               u8 assoc_cpus = regs->nbsh & 0xf;
+
+               if (assoc_cpus > 0)
+                       pr_cont(", core: %d", fls(assoc_cpus) - 1);
+
+               pr_cont("\n");
        }
 
        pr_emerg("%s.\n", EXT_ERR_MSG(xec));
index a1d4188..e7810b7 100644 (file)
@@ -449,11 +449,10 @@ ks8695_rx_irq(int irq, void *dev_id)
 }
 
 /**
- *     ks8695_rx - Receive packets  called by NAPI poll method
+ *     ks8695_rx - Receive packets called by NAPI poll method
  *     @ksp: Private data for the KS8695 Ethernet
- *     @budget: The max packets would be receive
+ *     @budget: Number of packets allowed to process
  */
-
 static int ks8695_rx(struct ks8695_priv *ksp, int budget)
 {
        struct net_device *ndev = ksp->ndev;
@@ -461,7 +460,6 @@ static int ks8695_rx(struct ks8695_priv *ksp, int budget)
        int buff_n;
        u32 flags;
        int pktlen;
-       int last_rx_processed = -1;
        int received = 0;
 
        buff_n = ksp->next_rx_desc_read;
@@ -471,6 +469,7 @@ static int ks8695_rx(struct ks8695_priv *ksp, int budget)
                                        cpu_to_le32(RDES_OWN)))) {
                        rmb();
                        flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+
                        /* Found an SKB which we own, this means we
                         * received a packet
                         */
@@ -533,23 +532,18 @@ rx_failure:
                        ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
 rx_finished:
                        received++;
-                       /* And note this as processed so we can start
-                        * from here next time
-                        */
-                       last_rx_processed = buff_n;
                        buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
-                       /*And note which RX descriptor we last did */
-                       if (likely(last_rx_processed != -1))
-                               ksp->next_rx_desc_read =
-                                       (last_rx_processed + 1) &
-                                       MAX_RX_DESC_MASK;
        }
+
+       /* And note which RX descriptor we last did */
+       ksp->next_rx_desc_read = buff_n;
+
        /* And refill the buffers */
        ks8695_refill_rxbuffers(ksp);
 
-       /* Kick the RX DMA engine, in case it became
-        *  suspended */
+       /* Kick the RX DMA engine, in case it became suspended */
        ks8695_writereg(ksp, KS8695_DRSC, 0);
+
        return received;
 }
 
index 9d7fa2f..0bc990e 100644 (file)
@@ -94,6 +94,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
        case E1000_DEV_ID_82576_FIBER:
        case E1000_DEV_ID_82576_SERDES:
        case E1000_DEV_ID_82576_QUAD_COPPER:
+       case E1000_DEV_ID_82576_QUAD_COPPER_ET2:
        case E1000_DEV_ID_82576_SERDES_QUAD:
                mac->type = e1000_82576;
                break;
index 4480052..82a533f 100644 (file)
@@ -41,6 +41,7 @@ struct e1000_hw;
 #define E1000_DEV_ID_82576_FIBER              0x10E6
 #define E1000_DEV_ID_82576_SERDES             0x10E7
 #define E1000_DEV_ID_82576_QUAD_COPPER        0x10E8
+#define E1000_DEV_ID_82576_QUAD_COPPER_ET2    0x1526
 #define E1000_DEV_ID_82576_NS                 0x150A
 #define E1000_DEV_ID_82576_NS_SERDES          0x1518
 #define E1000_DEV_ID_82576_SERDES_QUAD        0x150D
index 0ed25f0..45a0e4f 100644 (file)
@@ -72,6 +72,7 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = {
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_FIBER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_SERDES_QUAD), board_82575 },
+       { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER_ET2), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82576_QUAD_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 },
        { PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 },
index 1f30e16..b405a00 100644 (file)
@@ -39,6 +39,7 @@
 #define IXGBE_82599_MC_TBL_SIZE   128
 #define IXGBE_82599_VFT_TBL_SIZE  128
 
+void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw);
 s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                                           ixgbe_link_speed speed,
                                           bool autoneg,
@@ -68,7 +69,9 @@ static void ixgbe_init_mac_link_ops_82599(struct ixgbe_hw *hw)
        if (hw->phy.multispeed_fiber) {
                /* Set up dual speed SFP+ support */
                mac->ops.setup_link = &ixgbe_setup_mac_link_multispeed_fiber;
+               mac->ops.flap_tx_laser = &ixgbe_flap_tx_laser_multispeed_fiber;
        } else {
+               mac->ops.flap_tx_laser = NULL;
                if ((mac->ops.get_media_type(hw) ==
                     ixgbe_media_type_backplane) &&
                    (hw->phy.smart_speed == ixgbe_smart_speed_auto ||
@@ -413,6 +416,41 @@ s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw,
 }
 
 /**
+ *  ixgbe_flap_tx_laser_multispeed_fiber - Flap Tx laser
+ *  @hw: pointer to hardware structure
+ *
+ *  When the driver changes the link speeds that it can support,
+ *  it sets autotry_restart to true to indicate that we need to
+ *  initiate a new autotry session with the link partner.  To do
+ *  so, we set the speed then disable and re-enable the tx laser, to
+ *  alert the link partner that it also needs to restart autotry on its
+ *  end.  This is consistent with true clause 37 autoneg, which also
+ *  involves a loss of signal.
+ **/
+void ixgbe_flap_tx_laser_multispeed_fiber(struct ixgbe_hw *hw)
+{
+       u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP);
+
+       hw_dbg(hw, "ixgbe_flap_tx_laser_multispeed_fiber\n");
+
+       if (hw->mac.autotry_restart) {
+               /* Disable tx laser; allow 100us to go dark per spec */
+               esdp_reg |= IXGBE_ESDP_SDP3;
+               IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
+               IXGBE_WRITE_FLUSH(hw);
+               udelay(100);
+
+               /* Enable tx laser; allow 100ms to light up */
+               esdp_reg &= ~IXGBE_ESDP_SDP3;
+               IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
+               IXGBE_WRITE_FLUSH(hw);
+               msleep(100);
+
+               hw->mac.autotry_restart = false;
+       }
+}
+
+/**
  *  ixgbe_setup_mac_link_multispeed_fiber - Set MAC link speed
  *  @hw: pointer to hardware structure
  *  @speed: new link speed
@@ -440,16 +478,6 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
        speed &= phy_link_speed;
 
        /*
-        * When the driver changes the link speeds that it can support,
-        * it sets autotry_restart to true to indicate that we need to
-        * initiate a new autotry session with the link partner.  To do
-        * so, we set the speed then disable and re-enable the tx laser, to
-        * alert the link partner that it also needs to restart autotry on its
-        * end.  This is consistent with true clause 37 autoneg, which also
-        * involves a loss of signal.
-        */
-
-       /*
         * Try each speed one by one, highest priority first.  We do this in
         * software because 10gb fiber doesn't support speed autonegotiation.
         */
@@ -466,6 +494,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                /* Set the module link speed */
                esdp_reg |= (IXGBE_ESDP_SDP5_DIR | IXGBE_ESDP_SDP5);
                IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
+               IXGBE_WRITE_FLUSH(hw);
 
                /* Allow module to change analog characteristics (1G->10G) */
                msleep(40);
@@ -478,19 +507,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                        return status;
 
                /* Flap the tx laser if it has not already been done */
-               if (hw->mac.autotry_restart) {
-                       /* Disable tx laser; allow 100us to go dark per spec */
-                       esdp_reg |= IXGBE_ESDP_SDP3;
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       udelay(100);
-
-                       /* Enable tx laser; allow 2ms to light up per spec */
-                       esdp_reg &= ~IXGBE_ESDP_SDP3;
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       msleep(2);
-
-                       hw->mac.autotry_restart = false;
-               }
+               hw->mac.ops.flap_tx_laser(hw);
 
                /*
                 * Wait for the controller to acquire link.  Per IEEE 802.3ap,
@@ -525,6 +542,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                esdp_reg &= ~IXGBE_ESDP_SDP5;
                esdp_reg |= IXGBE_ESDP_SDP5_DIR;
                IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
+               IXGBE_WRITE_FLUSH(hw);
 
                /* Allow module to change analog characteristics (10G->1G) */
                msleep(40);
@@ -537,19 +555,7 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw,
                        return status;
 
                /* Flap the tx laser if it has not already been done */
-               if (hw->mac.autotry_restart) {
-                       /* Disable tx laser; allow 100us to go dark per spec */
-                       esdp_reg |= IXGBE_ESDP_SDP3;
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       udelay(100);
-
-                       /* Enable tx laser; allow 2ms to light up per spec */
-                       esdp_reg &= ~IXGBE_ESDP_SDP3;
-                       IXGBE_WRITE_REG(hw, IXGBE_ESDP, esdp_reg);
-                       msleep(2);
-
-                       hw->mac.autotry_restart = false;
-               }
+               hw->mac.ops.flap_tx_laser(hw);
 
                /* Wait for the link partner to also set speed */
                msleep(100);
index 4123dec..700cfc0 100644 (file)
@@ -614,9 +614,9 @@ int ixgbe_fcoe_enable(struct net_device *netdev)
        netdev->vlan_features |= NETIF_F_FSO;
        netdev->vlan_features |= NETIF_F_FCOE_MTU;
        netdev->fcoe_ddp_xid = IXGBE_FCOE_DDP_MAX - 1;
-       netdev_features_change(netdev);
 
        ixgbe_init_interrupt_scheme(adapter);
+       netdev_features_change(netdev);
 
        if (netif_running(netdev))
                netdev->netdev_ops->ndo_open(netdev);
@@ -660,11 +660,11 @@ int ixgbe_fcoe_disable(struct net_device *netdev)
        netdev->vlan_features &= ~NETIF_F_FSO;
        netdev->vlan_features &= ~NETIF_F_FCOE_MTU;
        netdev->fcoe_ddp_xid = 0;
-       netdev_features_change(netdev);
 
        ixgbe_cleanup_fcoe(adapter);
-
        ixgbe_init_interrupt_scheme(adapter);
+       netdev_features_change(netdev);
+
        if (netif_running(netdev))
                netdev->netdev_ops->ndo_open(netdev);
        rc = 0;
index 684af37..d75c46f 100644 (file)
@@ -935,10 +935,12 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
                        if (skb->prev)
                                skb = ixgbe_transform_rsc_queue(skb, &(rx_ring->rsc_count));
                        if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) {
-                               if (IXGBE_RSC_CB(skb)->dma)
+                               if (IXGBE_RSC_CB(skb)->dma) {
                                        pci_unmap_single(pdev, IXGBE_RSC_CB(skb)->dma,
                                                         rx_ring->rx_buf_len,
                                                         PCI_DMA_FROMDEVICE);
+                                       IXGBE_RSC_CB(skb)->dma = 0;
+                               }
                                if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)
                                        rx_ring->rsc_count += skb_shinfo(skb)->nr_frags;
                                else
@@ -3126,10 +3128,12 @@ static void ixgbe_clean_rx_ring(struct ixgbe_adapter *adapter,
                        rx_buffer_info->skb = NULL;
                        do {
                                struct sk_buff *this = skb;
-                               if (IXGBE_RSC_CB(this)->dma)
+                               if (IXGBE_RSC_CB(this)->dma) {
                                        pci_unmap_single(pdev, IXGBE_RSC_CB(this)->dma,
                                                         rx_ring->rx_buf_len,
                                                         PCI_DMA_FROMDEVICE);
+                                       IXGBE_RSC_CB(this)->dma = 0;
+                               }
                                skb = skb->prev;
                                dev_kfree_skb(this);
                        } while (skb);
@@ -5018,6 +5022,7 @@ static void ixgbe_multispeed_fiber_task(struct work_struct *work)
        autoneg = hw->phy.autoneg_advertised;
        if ((!autoneg) && (hw->mac.ops.get_link_capabilities))
                hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiation);
+       hw->mac.autotry_restart = false;
        if (hw->mac.ops.setup_link)
                hw->mac.ops.setup_link(hw, autoneg, negotiation, true);
        adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
@@ -6245,9 +6250,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev,
        case IXGBE_DEV_ID_82599_KX4:
                adapter->wol = (IXGBE_WUFC_MAG | IXGBE_WUFC_EX |
                                IXGBE_WUFC_MC | IXGBE_WUFC_BC);
-               /* Enable ACPI wakeup in GRC */
-               IXGBE_WRITE_REG(hw, IXGBE_GRC,
-                            (IXGBE_READ_REG(hw, IXGBE_GRC) & ~IXGBE_GRC_APME));
                break;
        default:
                adapter->wol = 0;
@@ -6380,6 +6382,16 @@ static void __devexit ixgbe_remove(struct pci_dev *pdev)
        del_timer_sync(&adapter->sfp_timer);
        cancel_work_sync(&adapter->watchdog_task);
        cancel_work_sync(&adapter->sfp_task);
+       if (adapter->hw.phy.multispeed_fiber) {
+               struct ixgbe_hw *hw = &adapter->hw;
+               /*
+                * Restart clause 37 autoneg, disable and re-enable
+                * the tx laser, to clear & alert the link partner
+                * that it needs to restart autotry
+                */
+               hw->mac.autotry_restart = true;
+               hw->mac.ops.flap_tx_laser(hw);
+       }
        cancel_work_sync(&adapter->multispeed_fiber_task);
        cancel_work_sync(&adapter->sfp_config_module_task);
        if (adapter->flags & IXGBE_FLAG_FDIR_HASH_CAPABLE ||
index 2be9074..0ed5ab3 100644 (file)
@@ -2397,6 +2397,7 @@ struct ixgbe_mac_operations {
        s32 (*enable_rx_dma)(struct ixgbe_hw *, u32);
 
        /* Link */
+       void (*flap_tx_laser)(struct ixgbe_hw *);
        s32 (*setup_link)(struct ixgbe_hw *, ixgbe_link_speed, bool, bool);
        s32 (*check_link)(struct ixgbe_hw *, ixgbe_link_speed *, bool *, bool);
        s32 (*get_link_capabilities)(struct ixgbe_hw *, ixgbe_link_speed *,
index 399be0c..6fdd651 100644 (file)
@@ -46,22 +46,32 @@ struct ixgbe_stats {
        int sizeof_stat;
        int stat_offset;
        int base_stat_offset;
+       int saved_reset_offset;
 };
 
-#define IXGBEVF_STAT(m, b)  sizeof(((struct ixgbevf_adapter *)0)->m), \
-                           offsetof(struct ixgbevf_adapter, m),      \
-                           offsetof(struct ixgbevf_adapter, b)
+#define IXGBEVF_STAT(m, b, r)  sizeof(((struct ixgbevf_adapter *)0)->m), \
+                           offsetof(struct ixgbevf_adapter, m),         \
+                           offsetof(struct ixgbevf_adapter, b),         \
+                           offsetof(struct ixgbevf_adapter, r)
 static struct ixgbe_stats ixgbe_gstrings_stats[] = {
-       {"rx_packets", IXGBEVF_STAT(stats.vfgprc, stats.base_vfgprc)},
-       {"tx_packets", IXGBEVF_STAT(stats.vfgptc, stats.base_vfgptc)},
-       {"rx_bytes", IXGBEVF_STAT(stats.vfgorc, stats.base_vfgorc)},
-       {"tx_bytes", IXGBEVF_STAT(stats.vfgotc, stats.base_vfgotc)},
-       {"tx_busy", IXGBEVF_STAT(tx_busy, zero_base)},
-       {"multicast", IXGBEVF_STAT(stats.vfmprc, stats.base_vfmprc)},
-       {"rx_csum_offload_good", IXGBEVF_STAT(hw_csum_rx_good, zero_base)},
-       {"rx_csum_offload_errors", IXGBEVF_STAT(hw_csum_rx_error, zero_base)},
-       {"tx_csum_offload_ctxt", IXGBEVF_STAT(hw_csum_tx_good, zero_base)},
-       {"rx_header_split", IXGBEVF_STAT(rx_hdr_split, zero_base)},
+       {"rx_packets", IXGBEVF_STAT(stats.vfgprc, stats.base_vfgprc,
+                                   stats.saved_reset_vfgprc)},
+       {"tx_packets", IXGBEVF_STAT(stats.vfgptc, stats.base_vfgptc,
+                                   stats.saved_reset_vfgptc)},
+       {"rx_bytes", IXGBEVF_STAT(stats.vfgorc, stats.base_vfgorc,
+                                 stats.saved_reset_vfgorc)},
+       {"tx_bytes", IXGBEVF_STAT(stats.vfgotc, stats.base_vfgotc,
+                                 stats.saved_reset_vfgotc)},
+       {"tx_busy", IXGBEVF_STAT(tx_busy, zero_base, zero_base)},
+       {"multicast", IXGBEVF_STAT(stats.vfmprc, stats.base_vfmprc,
+                                  stats.saved_reset_vfmprc)},
+       {"rx_csum_offload_good", IXGBEVF_STAT(hw_csum_rx_good, zero_base,
+                                             zero_base)},
+       {"rx_csum_offload_errors", IXGBEVF_STAT(hw_csum_rx_error, zero_base,
+                                               zero_base)},
+       {"tx_csum_offload_ctxt", IXGBEVF_STAT(hw_csum_tx_good, zero_base,
+                                             zero_base)},
+       {"rx_header_split", IXGBEVF_STAT(rx_hdr_split, zero_base, zero_base)},
 };
 
 #define IXGBE_QUEUE_STATS_LEN 0
@@ -455,10 +465,14 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev,
                        ixgbe_gstrings_stats[i].stat_offset;
                char *b = (char *)adapter +
                        ixgbe_gstrings_stats[i].base_stat_offset;
+               char *r = (char *)adapter +
+                       ixgbe_gstrings_stats[i].saved_reset_offset;
                data[i] = ((ixgbe_gstrings_stats[i].sizeof_stat ==
                            sizeof(u64)) ? *(u64 *)p : *(u32 *)p) -
                          ((ixgbe_gstrings_stats[i].sizeof_stat ==
-                           sizeof(u64)) ? *(u64 *)b : *(u32 *)b);
+                           sizeof(u64)) ? *(u64 *)b : *(u32 *)b) +
+                         ((ixgbe_gstrings_stats[i].sizeof_stat ==
+                           sizeof(u64)) ? *(u64 *)r : *(u32 *)r);
        }
 }
 
index ca653c4..d6cbd94 100644 (file)
@@ -965,7 +965,7 @@ static irqreturn_t ixgbevf_msix_mbx(int irq, void *data)
 
        if ((msg & IXGBE_MBVFICR_VFREQ_MASK) == IXGBE_PF_CONTROL_MSG)
                mod_timer(&adapter->watchdog_timer,
-                         round_jiffies(jiffies + 10));
+                         round_jiffies(jiffies + 1));
 
        return IRQ_HANDLED;
 }
@@ -1610,6 +1610,44 @@ static inline void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter,
                                (adapter->rx_ring[rxr].count - 1));
 }
 
+static void ixgbevf_save_reset_stats(struct ixgbevf_adapter *adapter)
+{
+       /* Only save pre-reset stats if there are some */
+       if (adapter->stats.vfgprc || adapter->stats.vfgptc) {
+               adapter->stats.saved_reset_vfgprc += adapter->stats.vfgprc -
+                       adapter->stats.base_vfgprc;
+               adapter->stats.saved_reset_vfgptc += adapter->stats.vfgptc -
+                       adapter->stats.base_vfgptc;
+               adapter->stats.saved_reset_vfgorc += adapter->stats.vfgorc -
+                       adapter->stats.base_vfgorc;
+               adapter->stats.saved_reset_vfgotc += adapter->stats.vfgotc -
+                       adapter->stats.base_vfgotc;
+               adapter->stats.saved_reset_vfmprc += adapter->stats.vfmprc -
+                       adapter->stats.base_vfmprc;
+       }
+}
+
+static void ixgbevf_init_last_counter_stats(struct ixgbevf_adapter *adapter)
+{
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       adapter->stats.last_vfgprc = IXGBE_READ_REG(hw, IXGBE_VFGPRC);
+       adapter->stats.last_vfgorc = IXGBE_READ_REG(hw, IXGBE_VFGORC_LSB);
+       adapter->stats.last_vfgorc |=
+               (((u64)(IXGBE_READ_REG(hw, IXGBE_VFGORC_MSB))) << 32);
+       adapter->stats.last_vfgptc = IXGBE_READ_REG(hw, IXGBE_VFGPTC);
+       adapter->stats.last_vfgotc = IXGBE_READ_REG(hw, IXGBE_VFGOTC_LSB);
+       adapter->stats.last_vfgotc |=
+               (((u64)(IXGBE_READ_REG(hw, IXGBE_VFGOTC_MSB))) << 32);
+       adapter->stats.last_vfmprc = IXGBE_READ_REG(hw, IXGBE_VFMPRC);
+
+       adapter->stats.base_vfgprc = adapter->stats.last_vfgprc;
+       adapter->stats.base_vfgorc = adapter->stats.last_vfgorc;
+       adapter->stats.base_vfgptc = adapter->stats.last_vfgptc;
+       adapter->stats.base_vfgotc = adapter->stats.last_vfgotc;
+       adapter->stats.base_vfmprc = adapter->stats.last_vfmprc;
+}
+
 static int ixgbevf_up_complete(struct ixgbevf_adapter *adapter)
 {
        struct net_device *netdev = adapter->netdev;
@@ -1656,6 +1694,9 @@ static int ixgbevf_up_complete(struct ixgbevf_adapter *adapter)
        /* enable transmits */
        netif_tx_start_all_queues(netdev);
 
+       ixgbevf_save_reset_stats(adapter);
+       ixgbevf_init_last_counter_stats(adapter);
+
        /* bring the link up in the watchdog, this could race with our first
         * link up interrupt but shouldn't be a problem */
        adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE;
@@ -2228,27 +2269,6 @@ out:
        return err;
 }
 
-static void ixgbevf_init_last_counter_stats(struct ixgbevf_adapter *adapter)
-{
-       struct ixgbe_hw *hw = &adapter->hw;
-
-       adapter->stats.last_vfgprc = IXGBE_READ_REG(hw, IXGBE_VFGPRC);
-       adapter->stats.last_vfgorc = IXGBE_READ_REG(hw, IXGBE_VFGORC_LSB);
-       adapter->stats.last_vfgorc |=
-               (((u64)(IXGBE_READ_REG(hw, IXGBE_VFGORC_MSB))) << 32);
-       adapter->stats.last_vfgptc = IXGBE_READ_REG(hw, IXGBE_VFGPTC);
-       adapter->stats.last_vfgotc = IXGBE_READ_REG(hw, IXGBE_VFGOTC_LSB);
-       adapter->stats.last_vfgotc |=
-               (((u64)(IXGBE_READ_REG(hw, IXGBE_VFGOTC_MSB))) << 32);
-       adapter->stats.last_vfmprc = IXGBE_READ_REG(hw, IXGBE_VFMPRC);
-
-       adapter->stats.base_vfgprc = adapter->stats.last_vfgprc;
-       adapter->stats.base_vfgorc = adapter->stats.last_vfgorc;
-       adapter->stats.base_vfgptc = adapter->stats.last_vfgptc;
-       adapter->stats.base_vfgotc = adapter->stats.last_vfgotc;
-       adapter->stats.base_vfmprc = adapter->stats.last_vfmprc;
-}
-
 #define UPDATE_VF_COUNTER_32bit(reg, last_counter, counter)    \
        {                                                       \
                u32 current_counter = IXGBE_READ_REG(hw, reg);  \
@@ -2399,7 +2419,7 @@ static void ixgbevf_watchdog_task(struct work_struct *work)
                if (!netif_carrier_ok(netdev)) {
                        hw_dbg(&adapter->hw, "NIC Link is Up %s, ",
                               ((link_speed == IXGBE_LINK_SPEED_10GB_FULL) ?
-                               "10 Gbps" : "1 Gbps"));
+                               "10 Gbps\n" : "1 Gbps\n"));
                        netif_carrier_on(netdev);
                        netif_tx_wake_all_queues(netdev);
                } else {
@@ -2416,9 +2436,9 @@ static void ixgbevf_watchdog_task(struct work_struct *work)
                }
        }
 
-pf_has_reset:
        ixgbevf_update_stats(adapter);
 
+pf_has_reset:
        /* Force detection of hung controller every watchdog period */
        adapter->detect_tx_hung = true;
 
@@ -2675,7 +2695,7 @@ static int ixgbevf_open(struct net_device *netdev)
                if (hw->adapter_stopped) {
                        err = IXGBE_ERR_MBX;
                        printk(KERN_ERR "Unable to start - perhaps the PF"
-                              "Driver isn't up yet\n");
+                              " Driver isn't up yet\n");
                        goto err_setup_reset;
                }
        }
@@ -3390,8 +3410,6 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev,
        /* setup the private structure */
        err = ixgbevf_sw_init(adapter);
 
-       ixgbevf_init_last_counter_stats(adapter);
-
 #ifdef MAX_SKB_FRAGS
        netdev->features = NETIF_F_SG |
                           NETIF_F_IP_CSUM |
@@ -3449,6 +3467,8 @@ static int __devinit ixgbevf_probe(struct pci_dev *pdev,
 
        adapter->netdev_registered = true;
 
+       ixgbevf_init_last_counter_stats(adapter);
+
        /* print the MAC address */
        hw_dbg(hw, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
               netdev->dev_addr[0],
index 799600e..1f31b05 100644 (file)
@@ -157,6 +157,12 @@ struct ixgbevf_hw_stats {
        u64 vfgorc;
        u64 vfgotc;
        u64 vfmprc;
+
+       u64 saved_reset_vfgprc;
+       u64 saved_reset_vfgptc;
+       u64 saved_reset_vfgorc;
+       u64 saved_reset_vfgotc;
+       u64 saved_reset_vfmprc;
 };
 
 struct ixgbevf_info {
index 0f31497..c0b59a5 100644 (file)
@@ -946,6 +946,8 @@ jme_alloc_and_feed_skb(struct jme_adapter *jme, int idx)
                                jme->jme_vlan_rx(skb, jme->vlgrp,
                                        le16_to_cpu(rxdesc->descwb.vlan));
                                NET_STAT(jme).rx_bytes += 4;
+                       } else {
+                               dev_kfree_skb(skb);
                        }
                } else {
                        jme->jme_rx(skb);
@@ -2081,12 +2083,45 @@ jme_tx_timeout(struct net_device *netdev)
        jme_reset_link(jme);
 }
 
+static inline void jme_pause_rx(struct jme_adapter *jme)
+{
+       atomic_dec(&jme->link_changing);
+
+       jme_set_rx_pcc(jme, PCC_OFF);
+       if (test_bit(JME_FLAG_POLL, &jme->flags)) {
+               JME_NAPI_DISABLE(jme);
+       } else {
+               tasklet_disable(&jme->rxclean_task);
+               tasklet_disable(&jme->rxempty_task);
+       }
+}
+
+static inline void jme_resume_rx(struct jme_adapter *jme)
+{
+       struct dynpcc_info *dpi = &(jme->dpi);
+
+       if (test_bit(JME_FLAG_POLL, &jme->flags)) {
+               JME_NAPI_ENABLE(jme);
+       } else {
+               tasklet_hi_enable(&jme->rxclean_task);
+               tasklet_hi_enable(&jme->rxempty_task);
+       }
+       dpi->cur                = PCC_P1;
+       dpi->attempt            = PCC_P1;
+       dpi->cnt                = 0;
+       jme_set_rx_pcc(jme, PCC_P1);
+
+       atomic_inc(&jme->link_changing);
+}
+
 static void
 jme_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp)
 {
        struct jme_adapter *jme = netdev_priv(netdev);
 
+       jme_pause_rx(jme);
        jme->vlgrp = grp;
+       jme_resume_rx(jme);
 }
 
 static void
index c19db91..07ad3a4 100644 (file)
@@ -25,7 +25,7 @@
 #define __JME_H_INCLUDED__
 
 #define DRV_NAME       "jme"
-#define DRV_VERSION    "1.0.5"
+#define DRV_VERSION    "1.0.6"
 #define PFX            DRV_NAME ": "
 
 #define PCI_DEVICE_ID_JMICRON_JMC250   0x0250
index 0573e0b..13cc1ca 100644 (file)
@@ -976,7 +976,6 @@ static void ks8851_set_rx_mode(struct net_device *dev)
                        crc >>= (32 - 6);  /* get top six bits */
 
                        rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf));
-                       mcptr = mcptr->next;
                }
 
                rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXPAFMA;
index d222d7e..73f9a31 100644 (file)
@@ -1189,9 +1189,21 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
        }
 
        if (csum) {
-               u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
-               skb_push(skb, 4);
-               memcpy(skb->data, &csum_preamble, 4);
+               if (skb->len <= 45) {
+                       /* workaround - hardware tx checksum does not work
+                        * properly with extremely small packets */
+                       long csstart = skb->csum_start - skb_headroom(skb);
+                       __wsum calc = csum_partial(skb->data + csstart,
+                               skb->len - csstart, 0);
+                       *((__sum16 *)(skb->data + csstart
+                               + skb->csum_offset)) = csum_fold(calc);
+
+                       csum = false;
+               } else {
+                       u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
+                       skb_push(skb, 4);
+                       memcpy(skb->data, &csum_preamble, 4);
+               }
        }
 
        skb_push(skb, 4);
index b2c8207..294b486 100644 (file)
@@ -1353,25 +1353,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
        return htype;
 }
 
-static bool is_pae(struct sk_buff *skb)
-{
-       struct ieee80211_hdr *hdr;
-       __le16 fc;
-
-       hdr = (struct ieee80211_hdr *)skb->data;
-       fc = hdr->frame_control;
-
-       if (ieee80211_is_data(fc)) {
-               if (ieee80211_is_nullfunc(fc) ||
-                   /* Port Access Entity (IEEE 802.1X) */
-                   (skb->protocol == cpu_to_be16(ETH_P_PAE))) {
-                       return true;
-               }
-       }
-
-       return false;
-}
-
 static int get_hw_crypto_keytype(struct sk_buff *skb)
 {
        struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -1696,7 +1677,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
                        goto tx_done;
                }
 
-               if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !is_pae(skb)) {
+               if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
                        /*
                         * Try aggregation if it's a unicast data frame
                         * and the destination is HT capable.
index 1ed5206..8c12311 100644 (file)
@@ -124,7 +124,7 @@ void iwl_free_tfds_in_queue(struct iwl_priv *priv,
        if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
                priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
        else {
-               IWL_ERR(priv, "free more than tfds_in_queue (%u:%d)\n",
+               IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n",
                        priv->stations[sta_id].tid[tid].tfds_in_queue,
                        freed);
                priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
index 0ccba57..05e4d68 100644 (file)
@@ -466,7 +466,8 @@ out:
 
 void wl1251_debugfs_reset(struct wl1251 *wl)
 {
-       memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
+       if (wl->stats.fw_stats != NULL)
+               memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
        wl->stats.retry_count = 0;
        wl->stats.excessive_retries = 0;
 }
index ad37da2..a6a88df 100644 (file)
@@ -125,7 +125,7 @@ static void handle_tx(struct vhost_net *net)
        mutex_lock(&vq->mutex);
        vhost_disable_notify(vq);
 
-       if (wmem < sock->sk->sk_sndbuf * 2)
+       if (wmem < sock->sk->sk_sndbuf / 2)
                tx_poll_stop(net);
        hdr_size = vq->hdr_size;
 
@@ -508,12 +508,12 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
        /* Verify that ring has been setup correctly. */
        if (!vhost_vq_access_ok(vq)) {
                r = -EFAULT;
-               goto err;
+               goto err_vq;
        }
        sock = get_socket(fd);
        if (IS_ERR(sock)) {
                r = PTR_ERR(sock);
-               goto err;
+               goto err_vq;
        }
 
        /* start polling new socket */
@@ -524,12 +524,14 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
        vhost_net_disable_vq(n, vq);
        rcu_assign_pointer(vq->private_data, sock);
        vhost_net_enable_vq(n, vq);
-       mutex_unlock(&vq->mutex);
 done:
        if (oldsock) {
                vhost_net_flush_vq(n, index);
                fput(oldsock->file);
        }
+
+err_vq:
+       mutex_unlock(&vq->mutex);
 err:
        mutex_unlock(&n->dev.mutex);
        return r;
index 7cd55e0..7bd7a1e 100644 (file)
@@ -476,8 +476,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                if (r < 0)
                        break;
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
-               if (IS_ERR(eventfp))
-                       return PTR_ERR(eventfp);
+               if (IS_ERR(eventfp)) {
+                       r = PTR_ERR(eventfp);
+                       break;
+               }
                if (eventfp != vq->kick) {
                        pollstop = filep = vq->kick;
                        pollstart = vq->kick = eventfp;
@@ -489,8 +491,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                if (r < 0)
                        break;
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
-               if (IS_ERR(eventfp))
-                       return PTR_ERR(eventfp);
+               if (IS_ERR(eventfp)) {
+                       r = PTR_ERR(eventfp);
+                       break;
+               }
                if (eventfp != vq->call) {
                        filep = vq->call;
                        ctx = vq->call_ctx;
@@ -505,8 +509,10 @@ static long vhost_set_vring(struct vhost_dev *d, int ioctl, void __user *argp)
                if (r < 0)
                        break;
                eventfp = f.fd == -1 ? NULL : eventfd_fget(f.fd);
-               if (IS_ERR(eventfp))
-                       return PTR_ERR(eventfp);
+               if (IS_ERR(eventfp)) {
+                       r = PTR_ERR(eventfp);
+                       break;
+               }
                if (eventfp != vq->error) {
                        filep = vq->error;
                        vq->error = eventfp;
index 3ef5043..bb4ed14 100644 (file)
@@ -189,8 +189,9 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, long acl_order)
        if (!permits)
                goto out_unlock;
 
-       memcpy(permits->permits, xpermits->permits,
-              count * sizeof(struct afs_permit));
+       if (xpermits)
+               memcpy(permits->permits, xpermits->permits,
+                       count * sizeof(struct afs_permit));
 
        _debug("key %x access %x",
               key_serial(key), vnode->status.caller_access);
index 1822d63..16b92d0 100644 (file)
@@ -2,6 +2,7 @@
 #define _IF_TUNNEL_H_
 
 #include <linux/types.h>
+#include <asm/byteorder.h>
 
 #ifdef __KERNEL__
 #include <linux/ip.h>
index c79a88b..fa8b476 100644 (file)
@@ -2059,12 +2059,12 @@ static inline void skb_bond_set_mac_by_master(struct sk_buff *skb,
  * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and
  * ARP on active-backup slaves with arp_validate enabled.
  */
-static inline int skb_bond_should_drop(struct sk_buff *skb)
+static inline int skb_bond_should_drop(struct sk_buff *skb,
+                                      struct net_device *master)
 {
-       struct net_device *dev = skb->dev;
-       struct net_device *master = dev->master;
-
        if (master) {
+               struct net_device *dev = skb->dev;
+
                if (master->priv_flags & IFF_MASTER_ARPMON)
                        dev->last_rx = jiffies;
 
index 5392386..361d6b5 100644 (file)
@@ -76,7 +76,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
 extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
 extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group,
                          int echo, gfp_t flags);
-extern void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
+extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
 extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
 
 extern void nfnl_lock(void);
index fde27c0..6eaca5e 100644 (file)
@@ -188,7 +188,7 @@ extern int netlink_has_listeners(struct sock *sk, unsigned int group);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
                             __u32 group, gfp_t allocation);
-extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
+extern int netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
 extern int netlink_register_notifier(struct notifier_block *nb);
 extern int netlink_unregister_notifier(struct notifier_block *nb);
 
index f994ae5..057929b 100644 (file)
@@ -688,7 +688,7 @@ asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg);
 asmlinkage long sys_shmget(key_t key, size_t size, int flag);
 asmlinkage long sys_shmdt(char __user *shmaddr);
 asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf);
-asmlinkage long sys_ipc(unsigned int call, int first, int second,
+asmlinkage long sys_ipc(unsigned int call, int first, unsigned long second,
                unsigned long third, void __user *ptr, long fifth);
 
 asmlinkage long sys_mq_open(const char __user *name, int oflag, mode_t mode, struct mq_attr __user *attr);
diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h
new file mode 100644 (file)
index 0000000..0952231
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2010 Daniel Mack <daniel@caiaq.de>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This file holds USB constants and structures defined
+ * by the USB Device Class Definition for Audio Devices in version 2.0.
+ * Comments below reference relevant sections of the documents contained
+ * in http://www.usb.org/developers/devclass_docs/Audio2.0_final.zip
+ */
+
+#ifndef __LINUX_USB_AUDIO_V2_H
+#define __LINUX_USB_AUDIO_V2_H
+
+#include <linux/types.h>
+
+/* v1.0 and v2.0 of this standard have many things in common. For the rest
+ * of the definitions, please refer to audio.h */
+
+/* 4.7.2.1 Clock Source Descriptor */
+
+struct uac_clock_source_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bClockID;
+       __u8 bmAttributes;
+       __u8 bmControls;
+       __u8 bAssocTerminal;
+       __u8 iClockSource;
+} __attribute__((packed));
+
+/* 4.7.2.2 Clock Source Descriptor */
+
+struct uac_clock_selector_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bClockID;
+       __u8 bNrInPins;
+       __u8 bmControls;
+       __u8 baCSourceID[];
+} __attribute__((packed));
+
+/* 4.7.2.4 Input terminal descriptor */
+
+struct uac2_input_terminal_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalID;
+       __u16 wTerminalType;
+       __u8 bAssocTerminal;
+       __u8 bCSourceID;
+       __u8 bNrChannels;
+       __u32 bmChannelConfig;
+       __u8 iChannelNames;
+       __u16 bmControls;
+       __u8 iTerminal;
+} __attribute__((packed));
+
+/* 4.7.2.5 Output terminal descriptor */
+
+struct uac2_output_terminal_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalID;
+       __u16 wTerminalType;
+       __u8 bAssocTerminal;
+       __u8 bSourceID;
+       __u8 bCSourceID;
+       __u16 bmControls;
+       __u8 iTerminal;
+} __attribute__((packed));
+
+
+
+/* 4.7.2.8 Feature Unit Descriptor */
+
+struct uac2_feature_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bSourceID;
+       /* bmaControls is actually u32,
+        * but u8 is needed for the hybrid parser */
+       __u8 bmaControls[0]; /* variable length */
+} __attribute__((packed));
+
+/* 4.9.2 Class-Specific AS Interface Descriptor */
+
+struct uac_as_header_descriptor_v2 {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bTerminalLink;
+       __u8 bmControls;
+       __u8 bFormatType;
+       __u32 bmFormats;
+       __u8 bNrChannels;
+       __u32 bmChannelConfig;
+       __u8 iChannelNames;
+} __attribute__((packed));
+
+
+/* A.7 Audio Function Category Codes */
+#define UAC2_FUNCTION_SUBCLASS_UNDEFINED       0x00
+#define UAC2_FUNCTION_DESKTOP_SPEAKER          0x01
+#define UAC2_FUNCTION_HOME_THEATER             0x02
+#define UAC2_FUNCTION_MICROPHONE               0x03
+#define UAC2_FUNCTION_HEADSET                  0x04
+#define UAC2_FUNCTION_TELEPHONE                        0x05
+#define UAC2_FUNCTION_CONVERTER                        0x06
+#define UAC2_FUNCTION_SOUND_RECORDER           0x07
+#define UAC2_FUNCTION_IO_BOX                   0x08
+#define UAC2_FUNCTION_MUSICAL_INSTRUMENT       0x09
+#define UAC2_FUNCTION_PRO_AUDIO                        0x0a
+#define UAC2_FUNCTION_AUDIO_VIDEO              0x0b
+#define UAC2_FUNCTION_CONTROL_PANEL            0x0c
+#define UAC2_FUNCTION_OTHER                    0xff
+
+/* A.9 Audio Class-Specific AC Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_EFFECT_UNIT                       0x07
+#define UAC2_PROCESSING_UNIT_V2                0x08
+#define UAC2_EXTENSION_UNIT_V2         0x09
+#define UAC2_CLOCK_SOURCE              0x0a
+#define UAC2_CLOCK_SELECTOR            0x0b
+#define UAC2_CLOCK_MULTIPLIER          0x0c
+#define UAC2_SAMPLE_RATE_CONVERTER     0x0d
+
+/* A.10 Audio Class-Specific AS Interface Descriptor Subtypes */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_ENCODER                   0x03
+#define UAC2_DECODER                   0x04
+
+/* A.11 Effect Unit Effect Types */
+#define UAC2_EFFECT_UNDEFINED          0x00
+#define UAC2_EFFECT_PARAM_EQ           0x01
+#define UAC2_EFFECT_REVERB             0x02
+#define UAC2_EFFECT_MOD_DELAY          0x03
+#define UAC2_EFFECT_DYN_RANGE_COMP     0x04
+
+/* A.12 Processing Unit Process Types */
+#define UAC2_PROCESS_UNDEFINED         0x00
+#define UAC2_PROCESS_UP_DOWNMIX                0x01
+#define UAC2_PROCESS_DOLBY_PROLOCIC    0x02
+#define UAC2_PROCESS_STEREO_EXTENDER   0x03
+
+/* A.14 Audio Class-Specific Request Codes */
+#define UAC2_CS_CUR                    0x01
+#define UAC2_CS_RANGE                  0x02
+
+/* A.15 Encoder Type Codes */
+#define UAC2_ENCODER_UNDEFINED         0x00
+#define UAC2_ENCODER_OTHER             0x01
+#define UAC2_ENCODER_MPEG              0x02
+#define UAC2_ENCODER_AC3               0x03
+#define UAC2_ENCODER_WMA               0x04
+#define UAC2_ENCODER_DTS               0x05
+
+/* A.16 Decoder Type Codes */
+#define UAC2_DECODER_UNDEFINED         0x00
+#define UAC2_DECODER_OTHER             0x01
+#define UAC2_DECODER_MPEG              0x02
+#define UAC2_DECODER_AC3               0x03
+#define UAC2_DECODER_WMA               0x04
+#define UAC2_DECODER_DTS               0x05
+
+/* A.17.1 Clock Source Control Selectors */
+#define UAC2_CS_UNDEFINED              0x00
+#define UAC2_CS_CONTROL_SAM_FREQ       0x01
+#define UAC2_CS_CONTROL_CLOCK_VALID    0x02
+
+/* A.17.2 Clock Selector Control Selectors */
+#define UAC2_CX_UNDEFINED              0x00
+#define UAC2_CX_CLOCK_SELECTOR         0x01
+
+/* A.17.3 Clock Multiplier Control Selectors */
+#define UAC2_CM_UNDEFINED              0x00
+#define UAC2_CM_NUMERATOR              0x01
+#define UAC2_CM_DENOMINTATOR           0x02
+
+/* A.17.4 Terminal Control Selectors */
+#define UAC2_TE_UNDEFINED              0x00
+#define UAC2_TE_COPY_PROTECT           0x01
+#define UAC2_TE_CONNECTOR              0x02
+#define UAC2_TE_OVERLOAD               0x03
+#define UAC2_TE_CLUSTER                        0x04
+#define UAC2_TE_UNDERFLOW              0x05
+#define UAC2_TE_OVERFLOW               0x06
+#define UAC2_TE_LATENCY                        0x07
+
+/* A.17.5 Mixer Control Selectors */
+#define UAC2_MU_UNDEFINED              0x00
+#define UAC2_MU_MIXER                  0x01
+#define UAC2_MU_CLUSTER                        0x02
+#define UAC2_MU_UNDERFLOW              0x03
+#define UAC2_MU_OVERFLOW               0x04
+#define UAC2_MU_LATENCY                        0x05
+
+/* A.17.6 Selector Control Selectors */
+#define UAC2_SU_UNDEFINED              0x00
+#define UAC2_SU_SELECTOR               0x01
+#define UAC2_SU_LATENCY                        0x02
+
+/* A.17.7 Feature Unit Control Selectors */
+/* see audio.h for the rest, which is identical to v1 */
+#define UAC2_FU_INPUT_GAIN             0x0b
+#define UAC2_FU_INPUT_GAIN_PAD         0x0c
+#define UAC2_FU_PHASE_INVERTER         0x0d
+#define UAC2_FU_UNDERFLOW              0x0e
+#define UAC2_FU_OVERFLOW               0x0f
+#define UAC2_FU_LATENCY                        0x10
+
+/* A.17.8.1 Parametric Equalizer Section Effect Unit Control Selectors */
+#define UAC2_PE_UNDEFINED              0x00
+#define UAC2_PE_ENABLE                 0x01
+#define UAC2_PE_CENTERFREQ             0x02
+#define UAC2_PE_QFACTOR                        0x03
+#define UAC2_PE_GAIN                   0x04
+#define UAC2_PE_UNDERFLOW              0x05
+#define UAC2_PE_OVERFLOW               0x06
+#define UAC2_PE_LATENCY                        0x07
+
+/* A.17.8.2 Reverberation Effect Unit Control Selectors */
+#define UAC2_RV_UNDEFINED              0x00
+#define UAC2_RV_ENABLE                 0x01
+#define UAC2_RV_TYPE                   0x02
+#define UAC2_RV_LEVEL                  0x03
+#define UAC2_RV_TIME                   0x04
+#define UAC2_RV_FEEDBACK               0x05
+#define UAC2_RV_PREDELAY               0x06
+#define UAC2_RV_DENSITY                        0x07
+#define UAC2_RV_HIFREQ_ROLLOFF         0x08
+#define UAC2_RV_UNDERFLOW              0x09
+#define UAC2_RV_OVERFLOW               0x0a
+#define UAC2_RV_LATENCY                        0x0b
+
+/* A.17.8.3 Modulation Delay Effect Control Selectors */
+#define UAC2_MD_UNDEFINED              0x00
+#define UAC2_MD_ENABLE                 0x01
+#define UAC2_MD_BALANCE                        0x02
+#define UAC2_MD_RATE                   0x03
+#define UAC2_MD_DEPTH                  0x04
+#define UAC2_MD_TIME                   0x05
+#define UAC2_MD_FEEDBACK               0x06
+#define UAC2_MD_UNDERFLOW              0x07
+#define UAC2_MD_OVERFLOW               0x08
+#define UAC2_MD_LATENCY                        0x09
+
+/* A.17.8.4 Dynamic Range Compressor Effect Unit Control Selectors */
+#define UAC2_DR_UNDEFINED              0x00
+#define UAC2_DR_ENABLE                 0x01
+#define UAC2_DR_COMPRESSION_RATE       0x02
+#define UAC2_DR_MAXAMPL                        0x03
+#define UAC2_DR_THRESHOLD              0x04
+#define UAC2_DR_ATTACK_TIME            0x05
+#define UAC2_DR_RELEASE_TIME           0x06
+#define UAC2_DR_UNDEFLOW               0x07
+#define UAC2_DR_OVERFLOW               0x08
+#define UAC2_DR_LATENCY                        0x09
+
+/* A.17.9.1 Up/Down-mix Processing Unit Control Selectors */
+#define UAC2_UD_UNDEFINED              0x00
+#define UAC2_UD_ENABLE                 0x01
+#define UAC2_UD_MODE_SELECT            0x02
+#define UAC2_UD_CLUSTER                        0x03
+#define UAC2_UD_UNDERFLOW              0x04
+#define UAC2_UD_OVERFLOW               0x05
+#define UAC2_UD_LATENCY                        0x06
+
+/* A.17.9.2 Dolby Prologic[tm] Processing Unit Control Selectors */
+#define UAC2_DP_UNDEFINED              0x00
+#define UAC2_DP_ENABLE                 0x01
+#define UAC2_DP_MODE_SELECT            0x02
+#define UAC2_DP_CLUSTER                        0x03
+#define UAC2_DP_UNDERFFLOW             0x04
+#define UAC2_DP_OVERFLOW               0x05
+#define UAC2_DP_LATENCY                        0x06
+
+/* A.17.9.3 Stereo Expander Processing Unit Control Selectors */
+#define UAC2_ST_EXT_UNDEFINED          0x00
+#define UAC2_ST_EXT_ENABLE             0x01
+#define UAC2_ST_EXT_WIDTH              0x02
+#define UAC2_ST_EXT_UNDEFLOW           0x03
+#define UAC2_ST_EXT_OVERFLOW           0x04
+#define UAC2_ST_EXT_LATENCY            0x05
+
+/* A.17.10 Extension Unit Control Selectors */
+#define UAC2_XU_UNDEFINED              0x00
+#define UAC2_XU_ENABLE                 0x01
+#define UAC2_XU_CLUSTER                        0x02
+#define UAC2_XU_UNDERFLOW              0x03
+#define UAC2_XU_OVERFLOW               0x04
+#define UAC2_XU_LATENCY                        0x05
+
+/* A.17.11 AudioStreaming Interface Control Selectors */
+#define UAC2_AS_UNDEFINED              0x00
+#define UAC2_AS_ACT_ALT_SETTING                0x01
+#define UAC2_AS_VAL_ALT_SETTINGS       0x02
+#define UAC2_AS_AUDIO_DATA_FORMAT      0x03
+
+/* A.17.12 Encoder Control Selectors */
+#define UAC2_EN_UNDEFINED              0x00
+#define UAC2_EN_BIT_RATE               0x01
+#define UAC2_EN_QUALITY                        0x02
+#define UAC2_EN_VBR                    0x03
+#define UAC2_EN_TYPE                   0x04
+#define UAC2_EN_UNDERFLOW              0x05
+#define UAC2_EN_OVERFLOW               0x06
+#define UAC2_EN_ENCODER_ERROR          0x07
+#define UAC2_EN_PARAM1                 0x08
+#define UAC2_EN_PARAM2                 0x09
+#define UAC2_EN_PARAM3                 0x0a
+#define UAC2_EN_PARAM4                 0x0b
+#define UAC2_EN_PARAM5                 0x0c
+#define UAC2_EN_PARAM6                 0x0d
+#define UAC2_EN_PARAM7                 0x0e
+#define UAC2_EN_PARAM8                 0x0f
+
+/* A.17.13.1 MPEG Decoder Control Selectors */
+#define UAC2_MPEG_UNDEFINED            0x00
+#define UAC2_MPEG_DUAL_CHANNEL         0x01
+#define UAC2_MPEG_SECOND_STEREO                0x02
+#define UAC2_MPEG_MULTILINGUAL         0x03
+#define UAC2_MPEG_DYN_RANGE            0x04
+#define UAC2_MPEG_SCALING              0x05
+#define UAC2_MPEG_HILO_SCALING         0x06
+#define UAC2_MPEG_UNDERFLOW            0x07
+#define UAC2_MPEG_OVERFLOW             0x08
+#define UAC2_MPEG_DECODER_ERROR                0x09
+
+/* A17.13.2 AC3 Decoder Control Selectors */
+#define UAC2_AC3_UNDEFINED             0x00
+#define UAC2_AC3_MODE                  0x01
+#define UAC2_AC3_DYN_RANGE             0x02
+#define UAC2_AC3_SCALING               0x03
+#define UAC2_AC3_HILO_SCALING          0x04
+#define UAC2_AC3_UNDERFLOW             0x05
+#define UAC2_AC3_OVERFLOW              0x06
+#define UAC2_AC3_DECODER_ERROR         0x07
+
+/* A17.13.3 WMA Decoder Control Selectors */
+#define UAC2_WMA_UNDEFINED             0x00
+#define UAC2_WMA_UNDERFLOW             0x01
+#define UAC2_WMA_OVERFLOW              0x02
+#define UAC2_WMA_DECODER_ERROR         0x03
+
+/* A17.13.4 DTS Decoder Control Selectors */
+#define UAC2_DTS_UNDEFINED             0x00
+#define UAC2_DTS_UNDERFLOW             0x01
+#define UAC2_DTS_OVERFLOW              0x02
+#define UAC2_DTS_DECODER_ERROR         0x03
+
+/* A17.14 Endpoint Control Selectors */
+#define UAC2_EP_CS_UNDEFINED           0x00
+#define UAC2_EP_CS_PITCH               0x01
+#define UAC2_EP_CS_DATA_OVERRUN                0x02
+#define UAC2_EP_CS_DATA_UNDERRUN       0x03
+
+#endif /* __LINUX_USB_AUDIO_V2_H */
+
index 4d3e450..905a87c 100644 (file)
@@ -13,6 +13,9 @@
  * Comments below reference relevant sections of that document:
  *
  * http://www.usb.org/developers/devclass_docs/audio10.pdf
+ *
+ * Types and defines in this file are either specific to version 1.0 of
+ * this standard or common for newer versions.
  */
 
 #ifndef __LINUX_USB_AUDIO_H
 
 #include <linux/types.h>
 
+/* bInterfaceProtocol values to denote the version of the standard used */
+#define UAC_VERSION_1                  0x00
+#define UAC_VERSION_2                  0x20
+
 /* A.2 Audio Interface Subclass Codes */
 #define USB_SUBCLASS_AUDIOCONTROL      0x01
 #define USB_SUBCLASS_AUDIOSTREAMING    0x02
 #define USB_SUBCLASS_MIDISTREAMING     0x03
 
-#define UAC_VERSION_1                  0x00
-#define UAC_VERSION_2                  0x20
-
 /* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
 #define UAC_HEADER                     0x01
 #define UAC_INPUT_TERMINAL             0x02
 #define UAC_PROCESSING_UNIT_V1         0x07
 #define UAC_EXTENSION_UNIT_V1          0x08
 
-/* UAC v2.0 types */
-#define UAC_EFFECT_UNIT                        0x07
-#define UAC_PROCESSING_UNIT_V2         0x08
-#define UAC_EXTENSION_UNIT_V2          0x09
-#define UAC_CLOCK_SOURCE               0x0a
-#define UAC_CLOCK_SELECTOR             0x0b
-#define UAC_CLOCK_MULTIPLIER           0x0c
-#define UAC_SAMPLE_RATE_CONVERTER      0x0d
-
 /* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
 #define UAC_AS_GENERAL                 0x01
 #define UAC_FORMAT_TYPE                        0x02
 
 #define UAC_GET_STAT                   0xff
 
-/* Audio class v2.0 handles all the parameter calls differently */
-#define UAC2_CS_CUR                    0x01
-#define UAC2_CS_RANGE                  0x02
-
 /* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
 #define UAC_MS_HEADER                  0x01
 #define UAC_MIDI_IN_JACK               0x02
@@ -190,6 +181,156 @@ struct uac_feature_unit_descriptor_##ch {                 \
        __u8  iFeature;                                         \
 } __attribute__ ((packed))
 
+/* 4.3.2.3 Mixer Unit Descriptor */
+struct uac_mixer_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_mixer_unit_bNrChannels(struct uac_mixer_unit_descriptor *desc)
+{
+       return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u32 uac_mixer_unit_wChannelConfig(struct uac_mixer_unit_descriptor *desc,
+                                                 int protocol)
+{
+       if (protocol == UAC_VERSION_1)
+               return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+                       desc->baSourceID[desc->bNrInPins + 1];
+       else
+               return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+                       (desc->baSourceID[desc->bNrInPins + 3] << 16) |
+                       (desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+                       (desc->baSourceID[desc->bNrInPins + 1]);
+}
+
+static inline __u8 uac_mixer_unit_iChannelNames(struct uac_mixer_unit_descriptor *desc,
+                                               int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 3] :
+               desc->baSourceID[desc->bNrInPins + 5];
+}
+
+static inline __u8 *uac_mixer_unit_bmControls(struct uac_mixer_unit_descriptor *desc,
+                                             int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               &desc->baSourceID[desc->bNrInPins + 4] :
+               &desc->baSourceID[desc->bNrInPins + 6];
+}
+
+static inline __u8 uac_mixer_unit_iMixer(struct uac_mixer_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.4 Selector Unit Descriptor */
+struct uac_selector_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUintID;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.5 Feature Unit Descriptor */
+struct uac_feature_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u8 bSourceID;
+       __u8 bControlSize;
+       __u8 bmaControls[0]; /* variable length */
+} __attribute__((packed));
+
+static inline __u8 uac_feature_unit_iFeature(struct uac_feature_unit_descriptor *desc)
+{
+       __u8 *raw = (__u8 *) desc;
+       return raw[desc->bLength - 1];
+}
+
+/* 4.3.2.6 Processing Unit Descriptors */
+struct uac_processing_unit_descriptor {
+       __u8 bLength;
+       __u8 bDescriptorType;
+       __u8 bDescriptorSubtype;
+       __u8 bUnitID;
+       __u16 wProcessType;
+       __u8 bNrInPins;
+       __u8 baSourceID[];
+} __attribute__ ((packed));
+
+static inline __u8 uac_processing_unit_bNrChannels(struct uac_processing_unit_descriptor *desc)
+{
+       return desc->baSourceID[desc->bNrInPins];
+}
+
+static inline __u32 uac_processing_unit_wChannelConfig(struct uac_processing_unit_descriptor *desc,
+                                                      int protocol)
+{
+       if (protocol == UAC_VERSION_1)
+               return (desc->baSourceID[desc->bNrInPins + 2] << 8) |
+                       desc->baSourceID[desc->bNrInPins + 1];
+       else
+               return  (desc->baSourceID[desc->bNrInPins + 4] << 24) |
+                       (desc->baSourceID[desc->bNrInPins + 3] << 16) |
+                       (desc->baSourceID[desc->bNrInPins + 2] << 8)  |
+                       (desc->baSourceID[desc->bNrInPins + 1]);
+}
+
+static inline __u8 uac_processing_unit_iChannelNames(struct uac_processing_unit_descriptor *desc,
+                                                    int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 3] :
+               desc->baSourceID[desc->bNrInPins + 5];
+}
+
+static inline __u8 uac_processing_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
+                                                   int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               desc->baSourceID[desc->bNrInPins + 4] :
+               desc->baSourceID[desc->bNrInPins + 6];
+}
+
+static inline __u8 *uac_processing_unit_bmControls(struct uac_processing_unit_descriptor *desc,
+                                                  int protocol)
+{
+       return (protocol == UAC_VERSION_1) ?
+               &desc->baSourceID[desc->bNrInPins + 5] :
+               &desc->baSourceID[desc->bNrInPins + 7];
+}
+
+static inline __u8 uac_processing_unit_iProcessing(struct uac_processing_unit_descriptor *desc,
+                                                  int protocol)
+{
+       __u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
+       return desc->baSourceID[desc->bNrInPins + control_size];
+}
+
+static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_descriptor *desc,
+                                                int protocol)
+{
+       __u8 control_size = uac_processing_unit_bControlSize(desc, protocol);
+       return &desc->baSourceID[desc->bNrInPins + control_size + 1];
+}
+
 /* 4.5.2 Class-Specific AS Interface Descriptor */
 struct uac_as_header_descriptor_v1 {
        __u8  bLength;                  /* in bytes: 7 */
@@ -200,19 +341,6 @@ struct uac_as_header_descriptor_v1 {
        __le16 wFormatTag;              /* The Audio Data Format */
 } __attribute__ ((packed));
 
-struct uac_as_header_descriptor_v2 {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bTerminalLink;
-       __u8 bmControls;
-       __u8 bFormatType;
-       __u32 bmFormats;
-       __u8 bNrChannels;
-       __u32 bmChannelConfig;
-       __u8 iChannelNames;
-} __attribute__((packed));
-
 #define UAC_DT_AS_HEADER_SIZE          7
 
 /* Formats - A.1.1 Audio Data Format Type I Codes */
@@ -277,7 +405,6 @@ struct uac_format_type_i_ext_descriptor {
        __u8 bSideBandProtocol;
 } __attribute__((packed));
 
-
 /* Formats - Audio Data Format Type I Codes */
 
 #define UAC_FORMAT_TYPE_II_MPEG        0x1001
@@ -336,31 +463,8 @@ struct uac_iso_endpoint_descriptor {
 #define UAC_EP_CS_ATTR_PITCH_CONTROL   0x02
 #define UAC_EP_CS_ATTR_FILL_MAX                0x80
 
-/* Audio class v2.0: CLOCK_SOURCE descriptor */
-
-struct uac_clock_source_descriptor {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bClockID;
-       __u8 bmAttributes;
-       __u8 bmControls;
-       __u8 bAssocTerminal;
-       __u8 iClockSource;
-} __attribute__((packed));
-
 /* A.10.2 Feature Unit Control Selectors */
 
-struct uac_feature_unit_descriptor {
-       __u8 bLength;
-       __u8 bDescriptorType;
-       __u8 bDescriptorSubtype;
-       __u8 bUnitID;
-       __u8 bSourceID;
-       __u8 bControlSize;
-       __u8 controls[0]; /* variable length */
-} __attribute__((packed));
-
 #define UAC_FU_CONTROL_UNDEFINED       0x00
 #define UAC_MUTE_CONTROL               0x01
 #define UAC_VOLUME_CONTROL             0x02
index 04a6908..ff77e8f 100644 (file)
@@ -176,6 +176,6 @@ extern void hci_sock_cleanup(void);
 extern int bt_sysfs_init(void);
 extern void bt_sysfs_cleanup(void);
 
-extern struct class *bt_class;
+extern struct dentry *bt_debugfs;
 
 #endif /* __BLUETOOTH_H */
index f82e463..4fc05b5 100644 (file)
@@ -945,7 +945,11 @@ static inline u64 nla_get_u64(const struct nlattr *nla)
  */
 static inline __be64 nla_get_be64(const struct nlattr *nla)
 {
-       return *(__be64 *) nla_data(nla);
+       __be64 tmp;
+
+       nla_memcpy(&tmp, nla, sizeof(tmp));
+
+       return tmp;
 }
 
 /**
index 355a3da..1d6f53f 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/syscalls.h>
 #include <linux/uaccess.h>
 
-SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, int, second,
+SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
                unsigned long, third, void __user *, ptr, long, fifth)
 {
        int version, ret;
index c0316e0..c584a0a 100644 (file)
@@ -11,7 +11,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
        if (netpoll_rx(skb))
                return NET_RX_DROP;
 
-       if (skb_bond_should_drop(skb))
+       if (skb_bond_should_drop(skb, ACCESS_ONCE(skb->dev->master)))
                goto drop;
 
        skb->skb_iif = skb->dev->ifindex;
@@ -83,7 +83,7 @@ vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp,
 {
        struct sk_buff *p;
 
-       if (skb_bond_should_drop(skb))
+       if (skb_bond_should_drop(skb, ACCESS_ONCE(skb->dev->master)))
                goto drop;
 
        skb->skb_iif = skb->dev->ifindex;
index cafb55b..05fd125 100644 (file)
@@ -8,8 +8,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-struct class *bt_class = NULL;
-EXPORT_SYMBOL_GPL(bt_class);
+static struct class *bt_class;
 
 struct dentry *bt_debugfs = NULL;
 EXPORT_SYMBOL_GPL(bt_debugfs);
index 4db7ae2..7794a2e 100644 (file)
@@ -40,6 +40,8 @@
 #include <linux/skbuff.h>
 #include <linux/list.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/uaccess.h>
 #include <linux/crc16.h>
 #include <net/sock.h>
@@ -2830,6 +2832,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
                        int len = cmd->len - sizeof(*rsp);
                        char req[64];
 
+                       if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
+                               l2cap_send_disconn_req(conn, sk);
+                               goto done;
+                       }
+
                        /* throw out any old stored conf requests */
                        result = L2CAP_CONF_SUCCESS;
                        len = l2cap_parse_conf_rsp(sk, rsp->data,
@@ -3937,31 +3944,42 @@ drop:
        return 0;
 }
 
-static ssize_t l2cap_sysfs_show(struct class *dev,
-                               struct class_attribute *attr,
-                               char *buf)
+static int l2cap_debugfs_show(struct seq_file *f, void *p)
 {
        struct sock *sk;
        struct hlist_node *node;
-       char *str = buf;
 
        read_lock_bh(&l2cap_sk_list.lock);
 
        sk_for_each(sk, node, &l2cap_sk_list.head) {
                struct l2cap_pinfo *pi = l2cap_pi(sk);
 
-               str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
-                               batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
-                               sk->sk_state, __le16_to_cpu(pi->psm), pi->scid,
-                               pi->dcid, pi->imtu, pi->omtu, pi->sec_level);
+               seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
+                                       batostr(&bt_sk(sk)->src),
+                                       batostr(&bt_sk(sk)->dst),
+                                       sk->sk_state, __le16_to_cpu(pi->psm),
+                                       pi->scid, pi->dcid,
+                                       pi->imtu, pi->omtu, pi->sec_level);
        }
 
        read_unlock_bh(&l2cap_sk_list.lock);
 
-       return str - buf;
+       return 0;
 }
 
-static CLASS_ATTR(l2cap, S_IRUGO, l2cap_sysfs_show, NULL);
+static int l2cap_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, l2cap_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations l2cap_debugfs_fops = {
+       .open           = l2cap_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *l2cap_debugfs;
 
 static const struct proto_ops l2cap_sock_ops = {
        .family         = PF_BLUETOOTH,
@@ -4021,8 +4039,12 @@ static int __init l2cap_init(void)
                goto error;
        }
 
-       if (class_create_file(bt_class, &class_attr_l2cap) < 0)
-               BT_ERR("Failed to create L2CAP info file");
+       if (bt_debugfs) {
+               l2cap_debugfs = debugfs_create_file("l2cap", 0444,
+                                       bt_debugfs, NULL, &l2cap_debugfs_fops);
+               if (!l2cap_debugfs)
+                       BT_ERR("Failed to create L2CAP debug file");
+       }
 
        BT_INFO("L2CAP ver %s", VERSION);
        BT_INFO("L2CAP socket layer initialized");
@@ -4036,7 +4058,7 @@ error:
 
 static void __exit l2cap_exit(void)
 {
-       class_remove_file(bt_class, &class_attr_l2cap);
+       debugfs_remove(l2cap_debugfs);
 
        if (bt_sock_unregister(BTPROTO_L2CAP) < 0)
                BT_ERR("L2CAP socket unregistration failed");
index db8a68e..13f114e 100644 (file)
@@ -33,6 +33,8 @@
 #include <linux/init.h>
 #include <linux/wait.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/net.h>
 #include <linux/mutex.h>
 #include <linux/kthread.h>
@@ -2098,13 +2100,10 @@ static struct hci_cb rfcomm_cb = {
        .security_cfm   = rfcomm_security_cfm
 };
 
-static ssize_t rfcomm_dlc_sysfs_show(struct class *dev,
-                                    struct class_attribute *attr,
-                                    char *buf)
+static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
 {
        struct rfcomm_session *s;
        struct list_head *pp, *p;
-       char *str = buf;
 
        rfcomm_lock();
 
@@ -2114,18 +2113,32 @@ static ssize_t rfcomm_dlc_sysfs_show(struct class *dev,
                        struct sock *sk = s->sock->sk;
                        struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
 
-                       str += sprintf(str, "%s %s %ld %d %d %d %d\n",
-                                       batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
-                                       d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits);
+                       seq_printf(f, "%s %s %ld %d %d %d %d\n",
+                                               batostr(&bt_sk(sk)->src),
+                                               batostr(&bt_sk(sk)->dst),
+                                               d->state, d->dlci, d->mtu,
+                                               d->rx_credits, d->tx_credits);
                }
        }
 
        rfcomm_unlock();
 
-       return (str - buf);
+       return 0;
 }
 
-static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
+static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations rfcomm_dlc_debugfs_fops = {
+       .open           = rfcomm_dlc_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *rfcomm_dlc_debugfs;
 
 /* ---- Initialization ---- */
 static int __init rfcomm_init(void)
@@ -2142,8 +2155,12 @@ static int __init rfcomm_init(void)
                goto unregister;
        }
 
-       if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
-               BT_ERR("Failed to create RFCOMM info file");
+       if (bt_debugfs) {
+               rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444,
+                               bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops);
+               if (!rfcomm_dlc_debugfs)
+                       BT_ERR("Failed to create RFCOMM debug file");
+       }
 
        err = rfcomm_init_ttys();
        if (err < 0)
@@ -2171,7 +2188,7 @@ unregister:
 
 static void __exit rfcomm_exit(void)
 {
-       class_remove_file(bt_class, &class_attr_rfcomm_dlc);
+       debugfs_remove(rfcomm_dlc_debugfs);
 
        hci_unregister_cb(&rfcomm_cb);
 
index ca87d6a..7f43976 100644 (file)
@@ -40,6 +40,8 @@
 #include <linux/skbuff.h>
 #include <linux/list.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <net/sock.h>
 
 #include <asm/system.h>
@@ -1061,28 +1063,38 @@ done:
        return result;
 }
 
-static ssize_t rfcomm_sock_sysfs_show(struct class *dev,
-                                     struct class_attribute *attr,
-                                     char *buf)
+static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
 {
        struct sock *sk;
        struct hlist_node *node;
-       char *str = buf;
 
        read_lock_bh(&rfcomm_sk_list.lock);
 
        sk_for_each(sk, node, &rfcomm_sk_list.head) {
-               str += sprintf(str, "%s %s %d %d\n",
-                               batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
+               seq_printf(f, "%s %s %d %d\n",
+                               batostr(&bt_sk(sk)->src),
+                               batostr(&bt_sk(sk)->dst),
                                sk->sk_state, rfcomm_pi(sk)->channel);
        }
 
        read_unlock_bh(&rfcomm_sk_list.lock);
 
-       return (str - buf);
+       return 0;
 }
 
-static CLASS_ATTR(rfcomm, S_IRUGO, rfcomm_sock_sysfs_show, NULL);
+static int rfcomm_sock_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, rfcomm_sock_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations rfcomm_sock_debugfs_fops = {
+       .open           = rfcomm_sock_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *rfcomm_sock_debugfs;
 
 static const struct proto_ops rfcomm_sock_ops = {
        .family         = PF_BLUETOOTH,
@@ -1122,8 +1134,12 @@ int __init rfcomm_init_sockets(void)
        if (err < 0)
                goto error;
 
-       if (class_create_file(bt_class, &class_attr_rfcomm) < 0)
-               BT_ERR("Failed to create RFCOMM info file");
+       if (bt_debugfs) {
+               rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444,
+                               bt_debugfs, NULL, &rfcomm_sock_debugfs_fops);
+               if (!rfcomm_sock_debugfs)
+                       BT_ERR("Failed to create RFCOMM debug file");
+       }
 
        BT_INFO("RFCOMM socket layer initialized");
 
@@ -1137,7 +1153,7 @@ error:
 
 void rfcomm_cleanup_sockets(void)
 {
-       class_remove_file(bt_class, &class_attr_rfcomm);
+       debugfs_remove(rfcomm_sock_debugfs);
 
        if (bt_sock_unregister(BTPROTO_RFCOMM) < 0)
                BT_ERR("RFCOMM socket layer unregistration failed");
index f93b939..e5b16b7 100644 (file)
@@ -38,6 +38,8 @@
 #include <linux/socket.h>
 #include <linux/skbuff.h>
 #include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/list.h>
 #include <net/sock.h>
 
@@ -953,28 +955,36 @@ drop:
        return 0;
 }
 
-static ssize_t sco_sysfs_show(struct class *dev,
-                               struct class_attribute *attr,
-                               char *buf)
+static int sco_debugfs_show(struct seq_file *f, void *p)
 {
        struct sock *sk;
        struct hlist_node *node;
-       char *str = buf;
 
        read_lock_bh(&sco_sk_list.lock);
 
        sk_for_each(sk, node, &sco_sk_list.head) {
-               str += sprintf(str, "%s %s %d\n",
-                               batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
-                               sk->sk_state);
+               seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src),
+                               batostr(&bt_sk(sk)->dst), sk->sk_state);
        }
 
        read_unlock_bh(&sco_sk_list.lock);
 
-       return (str - buf);
+       return 0;
 }
 
-static CLASS_ATTR(sco, S_IRUGO, sco_sysfs_show, NULL);
+static int sco_debugfs_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, sco_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations sco_debugfs_fops = {
+       .open           = sco_debugfs_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static struct dentry *sco_debugfs;
 
 static const struct proto_ops sco_sock_ops = {
        .family         = PF_BLUETOOTH,
@@ -1032,8 +1042,12 @@ static int __init sco_init(void)
                goto error;
        }
 
-       if (class_create_file(bt_class, &class_attr_sco) < 0)
-               BT_ERR("Failed to create SCO info file");
+       if (bt_debugfs) {
+               sco_debugfs = debugfs_create_file("sco", 0444,
+                                       bt_debugfs, NULL, &sco_debugfs_fops);
+               if (!sco_debugfs)
+                       BT_ERR("Failed to create SCO debug file");
+       }
 
        BT_INFO("SCO (Voice Link) ver %s", VERSION);
        BT_INFO("SCO socket layer initialized");
@@ -1047,7 +1061,7 @@ error:
 
 static void __exit sco_exit(void)
 {
-       class_remove_file(bt_class, &class_attr_sco);
+       debugfs_remove(sco_debugfs);
 
        if (bt_sock_unregister(BTPROTO_SCO) < 0)
                BT_ERR("SCO socket unregistration failed");
index bcc490c..59d4394 100644 (file)
@@ -2483,6 +2483,7 @@ int netif_receive_skb(struct sk_buff *skb)
 {
        struct packet_type *ptype, *pt_prev;
        struct net_device *orig_dev;
+       struct net_device *master;
        struct net_device *null_or_orig;
        struct net_device *null_or_bond;
        int ret = NET_RX_DROP;
@@ -2503,11 +2504,12 @@ int netif_receive_skb(struct sk_buff *skb)
 
        null_or_orig = NULL;
        orig_dev = skb->dev;
-       if (orig_dev->master) {
-               if (skb_bond_should_drop(skb))
+       master = ACCESS_ONCE(orig_dev->master);
+       if (master) {
+               if (skb_bond_should_drop(skb, master))
                        null_or_orig = orig_dev; /* deliver only exact match */
                else
-                       skb->dev = orig_dev->master;
+                       skb->dev = master;
        }
 
        __get_cpu_var(netdev_rx_stat).total++;
index af5d897..01ef8ba 100644 (file)
@@ -961,7 +961,9 @@ fib_find_node(struct trie *t, u32 key)
        struct node *n;
 
        pos = 0;
-       n = rcu_dereference(t->trie);
+       n = rcu_dereference_check(t->trie,
+                                 rcu_read_lock_held() ||
+                                 lockdep_rtnl_is_held());
 
        while (n != NULL &&  NODE_TYPE(n) == T_TNODE) {
                tn = (struct tnode *) n;
index f47c9f7..f78402d 100644 (file)
@@ -810,11 +810,13 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
                        tunnel->err_count = 0;
        }
 
-       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen;
+       max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + rt->u.dst.header_len;
 
        if (skb_headroom(skb) < max_headroom || skb_shared(skb)||
            (skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
                struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+               if (max_headroom > dev->needed_headroom)
+                       dev->needed_headroom = max_headroom;
                if (!new_skb) {
                        ip_rt_put(rt);
                        txq->tx_dropped++;
index 8582e12..0b9d03c 100644 (file)
@@ -802,6 +802,9 @@ static int ipmr_mfc_add(struct net *net, struct mfcctl *mfc, int mrtsock)
        int line;
        struct mfc_cache *uc, *c, **cp;
 
+       if (mfc->mfcc_parent >= MAXVIFS)
+               return -ENFILE;
+
        line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
 
        for (cp = &net->ipv4.mfc_cache_array[line];
index a770df2..54fd68c 100644 (file)
@@ -1441,7 +1441,7 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
                                        dev_hold(rt->u.dst.dev);
                                if (rt->idev)
                                        in_dev_hold(rt->idev);
-                               rt->u.dst.obsolete      = 0;
+                               rt->u.dst.obsolete      = -1;
                                rt->u.dst.lastuse       = jiffies;
                                rt->u.dst.path          = &rt->u.dst;
                                rt->u.dst.neighbour     = NULL;
@@ -1506,11 +1506,12 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
        struct dst_entry *ret = dst;
 
        if (rt) {
-               if (dst->obsolete) {
+               if (dst->obsolete > 0) {
                        ip_rt_put(rt);
                        ret = NULL;
                } else if ((rt->rt_flags & RTCF_REDIRECTED) ||
-                          rt->u.dst.expires) {
+                          (rt->u.dst.expires &&
+                           time_after_eq(jiffies, rt->u.dst.expires))) {
                        unsigned hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src,
                                                rt->fl.oif,
                                                rt_genid(dev_net(dst->dev)));
@@ -1726,7 +1727,9 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, u32 mtu)
 
 static struct dst_entry *ipv4_dst_check(struct dst_entry *dst, u32 cookie)
 {
-       return NULL;
+       if (rt_is_expired((struct rtable *)dst))
+               return NULL;
+       return dst;
 }
 
 static void ipv4_dst_destroy(struct dst_entry *dst)
@@ -1888,7 +1891,8 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
        if (!rth)
                goto e_nobufs;
 
-       rth->u.dst.output= ip_rt_bug;
+       rth->u.dst.output = ip_rt_bug;
+       rth->u.dst.obsolete = -1;
 
        atomic_set(&rth->u.dst.__refcnt, 1);
        rth->u.dst.flags= DST_HOST;
@@ -2054,6 +2058,7 @@ static int __mkroute_input(struct sk_buff *skb,
        rth->fl.oif     = 0;
        rth->rt_spec_dst= spec_dst;
 
+       rth->u.dst.obsolete = -1;
        rth->u.dst.input = ip_forward;
        rth->u.dst.output = ip_output;
        rth->rt_genid = rt_genid(dev_net(rth->u.dst.dev));
@@ -2218,6 +2223,7 @@ local_input:
                goto e_nobufs;
 
        rth->u.dst.output= ip_rt_bug;
+       rth->u.dst.obsolete = -1;
        rth->rt_genid = rt_genid(net);
 
        atomic_set(&rth->u.dst.__refcnt, 1);
@@ -2444,6 +2450,7 @@ static int __mkroute_output(struct rtable **result,
        rth->rt_spec_dst= fl->fl4_src;
 
        rth->u.dst.output=ip_output;
+       rth->u.dst.obsolete = -1;
        rth->rt_genid = rt_genid(dev_net(dev_out));
 
        RT_CACHE_STAT_INC(out_slow_tot);
index 5901010..6afb6d8 100644 (file)
@@ -429,7 +429,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
                if (tp->urg_seq == tp->copied_seq &&
                    !sock_flag(sk, SOCK_URGINLINE) &&
                    tp->urg_data)
-                       target--;
+                       target++;
 
                /* Potential race condition. If read of tp below will
                 * escape above sk->sk_state, we can be illegally awaken
@@ -1254,6 +1254,39 @@ static void tcp_prequeue_process(struct sock *sk)
        tp->ucopy.memory = 0;
 }
 
+#ifdef CONFIG_NET_DMA
+static void tcp_service_net_dma(struct sock *sk, bool wait)
+{
+       dma_cookie_t done, used;
+       dma_cookie_t last_issued;
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       if (!tp->ucopy.dma_chan)
+               return;
+
+       last_issued = tp->ucopy.dma_cookie;
+       dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+
+       do {
+               if (dma_async_memcpy_complete(tp->ucopy.dma_chan,
+                                             last_issued, &done,
+                                             &used) == DMA_SUCCESS) {
+                       /* Safe to free early-copied skbs now */
+                       __skb_queue_purge(&sk->sk_async_wait_queue);
+                       break;
+               } else {
+                       struct sk_buff *skb;
+                       while ((skb = skb_peek(&sk->sk_async_wait_queue)) &&
+                              (dma_async_is_complete(skb->dma_cookie, done,
+                                                     used) == DMA_SUCCESS)) {
+                               __skb_dequeue(&sk->sk_async_wait_queue);
+                               kfree_skb(skb);
+                       }
+               }
+       } while (wait);
+}
+#endif
+
 static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
 {
        struct sk_buff *skb;
@@ -1546,6 +1579,10 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        /* __ Set realtime policy in scheduler __ */
                }
 
+#ifdef CONFIG_NET_DMA
+               if (tp->ucopy.dma_chan)
+                       dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+#endif
                if (copied >= target) {
                        /* Do not sleep, just process backlog. */
                        release_sock(sk);
@@ -1554,6 +1591,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                        sk_wait_data(sk, &timeo);
 
 #ifdef CONFIG_NET_DMA
+               tcp_service_net_dma(sk, false);  /* Don't block */
                tp->ucopy.wakeup = 0;
 #endif
 
@@ -1633,6 +1671,9 @@ do_prequeue:
                                                copied = -EFAULT;
                                        break;
                                }
+
+                               dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+
                                if ((offset + used) == skb->len)
                                        copied_early = 1;
 
@@ -1702,27 +1743,9 @@ skip_copy:
        }
 
 #ifdef CONFIG_NET_DMA
-       if (tp->ucopy.dma_chan) {
-               dma_cookie_t done, used;
-
-               dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
-
-               while (dma_async_memcpy_complete(tp->ucopy.dma_chan,
-                                                tp->ucopy.dma_cookie, &done,
-                                                &used) == DMA_IN_PROGRESS) {
-                       /* do partial cleanup of sk_async_wait_queue */
-                       while ((skb = skb_peek(&sk->sk_async_wait_queue)) &&
-                              (dma_async_is_complete(skb->dma_cookie, done,
-                                                     used) == DMA_SUCCESS)) {
-                               __skb_dequeue(&sk->sk_async_wait_queue);
-                               kfree_skb(skb);
-                       }
-               }
+       tcp_service_net_dma(sk, true);  /* Wait for queue to drain */
+       tp->ucopy.dma_chan = NULL;
 
-               /* Safe to free early-copied skbs now */
-               __skb_queue_purge(&sk->sk_async_wait_queue);
-               tp->ucopy.dma_chan = NULL;
-       }
        if (tp->ucopy.pinned_list) {
                dma_unpin_iovec_pages(tp->ucopy.pinned_list);
                tp->ucopy.pinned_list = NULL;
index 788851c..c096a42 100644 (file)
@@ -2511,6 +2511,9 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
        int err;
        unsigned int mss;
 
+       if (packets == 0)
+               return;
+
        WARN_ON(packets > tp->packets_out);
        if (tp->lost_skb_hint) {
                skb = tp->lost_skb_hint;
index 70df409..f4df5f9 100644 (file)
@@ -370,6 +370,11 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
+       if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto out;
+       }
+
        icsk = inet_csk(sk);
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
index 52e0f74..23e4ac0 100644 (file)
@@ -1113,6 +1113,9 @@ static int ip6mr_mfc_add(struct net *net, struct mf6cctl *mfc, int mrtsock)
        unsigned char ttls[MAXMIFS];
        int i;
 
+       if (mfc->mf6cc_parent >= MAXMIFS)
+               return -ENFILE;
+
        memset(ttls, 255, MAXMIFS);
        for (i = 0; i < MAXMIFS; i++) {
                if (IF_ISSET(i, &mfc->mf6cc_ifset))
index 52cd3ef..7fcb0e5 100644 (file)
@@ -879,7 +879,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 
        rt = (struct rt6_info *) dst;
 
-       if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
+       if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
                return dst;
 
        return NULL;
index 2b2af63..569410a 100644 (file)
@@ -582,7 +582,9 @@ nla_put_failure:
 nlmsg_failure:
        kfree_skb(skb);
 errout:
-       nfnetlink_set_err(net, 0, group, -ENOBUFS);
+       if (nfnetlink_set_err(net, 0, group, -ENOBUFS) > 0)
+               return -ENOBUFS;
+
        return 0;
 }
 #endif /* CONFIG_NF_CONNTRACK_EVENTS */
index 8eb0cc2..6afa3d5 100644 (file)
@@ -113,9 +113,9 @@ int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
 }
 EXPORT_SYMBOL_GPL(nfnetlink_send);
 
-void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
+int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
 {
-       netlink_set_err(net->nfnl, pid, group, error);
+       return netlink_set_err(net->nfnl, pid, group, error);
 }
 EXPORT_SYMBOL_GPL(nfnetlink_set_err);
 
index 320d042..acbbae1 100644 (file)
@@ -1093,6 +1093,7 @@ static inline int do_one_set_err(struct sock *sk,
                                 struct netlink_set_err_data *p)
 {
        struct netlink_sock *nlk = nlk_sk(sk);
+       int ret = 0;
 
        if (sk == p->exclude_sk)
                goto out;
@@ -1104,10 +1105,15 @@ static inline int do_one_set_err(struct sock *sk,
            !test_bit(p->group - 1, nlk->groups))
                goto out;
 
+       if (p->code == ENOBUFS && nlk->flags & NETLINK_RECV_NO_ENOBUFS) {
+               ret = 1;
+               goto out;
+       }
+
        sk->sk_err = p->code;
        sk->sk_error_report(sk);
 out:
-       return 0;
+       return ret;
 }
 
 /**
@@ -1116,12 +1122,16 @@ out:
  * @pid: the PID of a process that we want to skip (if any)
  * @groups: the broadcast group that will notice the error
  * @code: error code, must be negative (as usual in kernelspace)
+ *
+ * This function returns the number of broadcast listeners that have set the
+ * NETLINK_RECV_NO_ENOBUFS socket option.
  */
-void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
+int netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
 {
        struct netlink_set_err_data info;
        struct hlist_node *node;
        struct sock *sk;
+       int ret = 0;
 
        info.exclude_sk = ssk;
        info.pid = pid;
@@ -1132,9 +1142,10 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
        read_lock(&nl_table_lock);
 
        sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)
-               do_one_set_err(sk, &info);
+               ret += do_one_set_err(sk, &info);
 
        read_unlock(&nl_table_lock);
+       return ret;
 }
 EXPORT_SYMBOL(netlink_set_err);
 
index 77228f2..2d744f2 100644 (file)
@@ -88,6 +88,11 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
 
        /* get a notification message to send to the server app */
        notification = alloc_skb(0, GFP_NOFS);
+       if (!notification) {
+               _debug("no memory");
+               ret = -ENOMEM;
+               goto error_nofree;
+       }
        rxrpc_new_skb(notification);
        notification->mark = RXRPC_SKB_MARK_NEW_CALL;
 
@@ -189,6 +194,7 @@ invalid_service:
        ret = -ECONNREFUSED;
 error:
        rxrpc_free_skb(notification);
+error_nofree:
        _leave(" = %d", ret);
        return ret;
 }
index c570ae3..44d6d2e 100644 (file)
@@ -22,8 +22,7 @@ config SND_USB_AUDIO
          will be called snd-usb-audio.
 
 config SND_USB_UA101
-       tristate "Edirol UA-101/UA-1000 driver (EXPERIMENTAL)"
-       depends on EXPERIMENTAL
+       tristate "Edirol UA-101/UA-1000 driver"
        select SND_PCM
        select SND_RAWMIDI
        help
@@ -65,6 +64,7 @@ config SND_USB_CAIAQ
            * Native Instruments Audio 8 DJ
            * Native Instruments Guitar Rig Session I/O
            * Native Instruments Guitar Rig mobile
+           * Native Instruments Traktor Kontrol X1
 
           To compile this driver as a module, choose M here: the module
           will be called snd-usb-caiaq.
index 5bf64ae..e7ac7f4 100644 (file)
@@ -2,14 +2,24 @@
 # Makefile for ALSA
 #
 
-snd-usb-audio-objs := usbaudio.o usbmixer.o
-snd-usb-lib-objs := usbmidi.o
-snd-ua101-objs := ua101.o
+snd-usb-audio-objs :=  card.o \
+                       mixer.o \
+                       mixer_quirks.o \
+                       proc.o \
+                       quirks.o \
+                       format.o \
+                       endpoint.o \
+                       urb.o \
+                       pcm.o \
+                       helper.o
+
+snd-usbmidi-lib-objs := midi.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
-obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o
-obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
-obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usbmidi-lib.o
+
+obj-$(CONFIG_SND_USB_UA101) += snd-usbmidi-lib.o
+obj-$(CONFIG_SND_USB_USX2Y) += snd-usbmidi-lib.o
+obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o
 
-obj-$(CONFIG_SND) += usx2y/ caiaq/
+obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/
index 537102b..36ed703 100644 (file)
@@ -35,33 +35,41 @@ static int control_info(struct snd_kcontrol *kcontrol,
        struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
        int pos = kcontrol->private_value;
        int is_intval = pos & CNT_INTVAL;
-       unsigned int id = dev->chip.usb_id;
+       int maxval = 63;
 
        uinfo->count = 1;
        pos &= ~CNT_INTVAL;
 
-       if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ)
-               && (pos == 0)) {
-               /* current input mode of A8DJ */
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = 2;
-               return 0;
-       }
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO8DJ):
+               if (pos == 0) {
+                       /* current input mode of A8DJ */
+                       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+                       uinfo->value.integer.min = 0;
+                       uinfo->value.integer.max = 2;
+                       return 0;
+               }
+               break;
 
-       if (id == USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)
-               && (pos == 0)) {
-               /* current input mode of A4DJ */
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = 1;
-               return 0;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
+               if (pos == 0) {
+                       /* current input mode of A4DJ */
+                       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+                       uinfo->value.integer.min = 0;
+                       uinfo->value.integer.max = 1;
+                       return 0;
+               }
+               break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               maxval = 127;
+               break;
        }
 
        if (is_intval) {
                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
                uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = 64;
+               uinfo->value.integer.max = maxval;
        } else {
                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
                uinfo->value.integer.min = 0;
@@ -102,9 +110,10 @@ static int control_put(struct snd_kcontrol *kcontrol,
        struct snd_usb_audio *chip = snd_kcontrol_chip(kcontrol);
        struct snd_usb_caiaqdev *dev = caiaqdev(chip->card);
        int pos = kcontrol->private_value;
+       unsigned char cmd = EP1_CMD_WRITE_IO;
 
-       if (dev->chip.usb_id ==
-               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ)) {
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ): {
                /* A4DJ has only one control */
                /* do not expose hardware input mode 0 */
                dev->control_state[0] = ucontrol->value.integer.value[0] + 1;
@@ -113,10 +122,15 @@ static int control_put(struct snd_kcontrol *kcontrol,
                return 1;
        }
 
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               cmd = EP1_CMD_DIMM_LEDS;
+               break;
+       }
+
        if (pos & CNT_INTVAL) {
                dev->control_state[pos & ~CNT_INTVAL]
                        = ucontrol->value.integer.value[0];
-               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
+               snd_usb_caiaq_send_command(dev, cmd,
                                dev->control_state, sizeof(dev->control_state));
        } else {
                if (ucontrol->value.integer.value[0])
@@ -124,7 +138,7 @@ static int control_put(struct snd_kcontrol *kcontrol,
                else
                        dev->control_state[pos / 8] &= ~(1 << (pos % 8));
 
-               snd_usb_caiaq_send_command(dev, EP1_CMD_WRITE_IO,
+               snd_usb_caiaq_send_command(dev, cmd,
                                dev->control_state, sizeof(dev->control_state));
        }
 
@@ -273,6 +287,43 @@ static struct caiaq_controller a4dj_controller[] = {
        { "Current input mode", 0 | CNT_INTVAL  }
 };
 
+static struct caiaq_controller kontrolx1_controller[] = {
+       { "LED FX A: ON",               7 | CNT_INTVAL  },
+       { "LED FX A: 1",                6 | CNT_INTVAL  },
+       { "LED FX A: 2",                5 | CNT_INTVAL  },
+       { "LED FX A: 3",                4 | CNT_INTVAL  },
+       { "LED FX B: ON",               3 | CNT_INTVAL  },
+       { "LED FX B: 1",                2 | CNT_INTVAL  },
+       { "LED FX B: 2",                1 | CNT_INTVAL  },
+       { "LED FX B: 3",                0 | CNT_INTVAL  },
+
+       { "LED Hotcue",                 28 | CNT_INTVAL },
+       { "LED Shift (white)",          29 | CNT_INTVAL },
+       { "LED Shift (green)",          30 | CNT_INTVAL },
+
+       { "LED Deck A: FX1",            24 | CNT_INTVAL },
+       { "LED Deck A: FX2",            25 | CNT_INTVAL },
+       { "LED Deck A: IN",             17 | CNT_INTVAL },
+       { "LED Deck A: OUT",            16 | CNT_INTVAL },
+       { "LED Deck A: < BEAT",         19 | CNT_INTVAL },
+       { "LED Deck A: BEAT >",         18 | CNT_INTVAL },
+       { "LED Deck A: CUE/ABS",        21 | CNT_INTVAL },
+       { "LED Deck A: CUP/REL",        20 | CNT_INTVAL },
+       { "LED Deck A: PLAY",           23 | CNT_INTVAL },
+       { "LED Deck A: SYNC",           22 | CNT_INTVAL },
+
+       { "LED Deck B: FX1",            26 | CNT_INTVAL },
+       { "LED Deck B: FX2",            27 | CNT_INTVAL },
+       { "LED Deck B: IN",             15 | CNT_INTVAL },
+       { "LED Deck B: OUT",            14 | CNT_INTVAL },
+       { "LED Deck B: < BEAT",         13 | CNT_INTVAL },
+       { "LED Deck B: BEAT >",         12 | CNT_INTVAL },
+       { "LED Deck B: CUE/ABS",        11 | CNT_INTVAL },
+       { "LED Deck B: CUP/REL",        10 | CNT_INTVAL },
+       { "LED Deck B: PLAY",           9  | CNT_INTVAL },
+       { "LED Deck B: SYNC",           8  | CNT_INTVAL },
+};
+
 static int __devinit add_controls(struct caiaq_controller *c, int num,
                                  struct snd_usb_caiaqdev *dev)
 {
@@ -321,10 +372,16 @@ int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev)
                ret = add_controls(a8dj_controller,
                        ARRAY_SIZE(a8dj_controller), dev);
                break;
+
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AUDIO4DJ):
                ret = add_controls(a4dj_controller,
                        ARRAY_SIZE(a4dj_controller), dev);
                break;
+
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               ret = add_controls(kontrolx1_controller,
+                       ARRAY_SIZE(kontrolx1_controller), dev);
+               break;
        }
 
        return ret;
index a3f02dd..08ee254 100644 (file)
@@ -46,7 +46,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
                         "{Native Instruments, Audio 4 DJ},"
                         "{Native Instruments, Audio 8 DJ},"
                         "{Native Instruments, Session I/O},"
-                        "{Native Instruments, GuitarRig mobile}");
+                        "{Native Instruments, GuitarRig mobile}"
+                        "{Native Instruments, Traktor Kontrol X1}");
 
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
 static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
@@ -127,6 +128,11 @@ static struct usb_device_id snd_usb_id_table[] = {
                .idVendor =     USB_VID_NATIVEINSTRUMENTS,
                .idProduct =    USB_PID_AUDIO2DJ
        },
+       {
+               .match_flags =  USB_DEVICE_ID_MATCH_DEVICE,
+               .idVendor =     USB_VID_NATIVEINSTRUMENTS,
+               .idProduct =    USB_PID_TRAKTORKONTROLX1
+       },
        { /* terminator */ }
 };
 
index 44e3edf..f1117ec 100644 (file)
@@ -5,18 +5,20 @@
 
 #define USB_VID_NATIVEINSTRUMENTS 0x17cc
 
-#define USB_PID_RIGKONTROL2    0x1969
-#define USB_PID_RIGKONTROL3    0x1940
-#define USB_PID_KORECONTROLLER 0x4711
-#define USB_PID_KORECONTROLLER2        0x4712
-#define USB_PID_AK1            0x0815
-#define USB_PID_AUDIO2DJ       0x041c
-#define USB_PID_AUDIO4DJ       0x0839
-#define USB_PID_AUDIO8DJ       0x1978
-#define USB_PID_SESSIONIO      0x1915
-#define USB_PID_GUITARRIGMOBILE        0x0d8d
+#define USB_PID_RIGKONTROL2            0x1969
+#define USB_PID_RIGKONTROL3            0x1940
+#define USB_PID_KORECONTROLLER         0x4711
+#define USB_PID_KORECONTROLLER2                0x4712
+#define USB_PID_AK1                    0x0815
+#define USB_PID_AUDIO2DJ               0x041c
+#define USB_PID_AUDIO4DJ               0x0839
+#define USB_PID_AUDIO8DJ               0x1978
+#define USB_PID_SESSIONIO              0x1915
+#define USB_PID_GUITARRIGMOBILE                0x0d8d
+#define USB_PID_TRAKTORKONTROLX1       0x2305
 
 #define EP1_BUFSIZE 64
+#define EP4_BUFSIZE 512
 #define CAIAQ_USB_STR_LEN 0xff
 #define MAX_STREAMS 32
 
@@ -104,6 +106,8 @@ struct snd_usb_caiaqdev {
        struct input_dev *input_dev;
        char phys[64];                  /* physical device path */
        unsigned short keycode[64];
+       struct urb *ep4_in_urb;
+       unsigned char ep4_in_buf[EP4_BUFSIZE];
 #endif
 
        /* ALSA */
index a48d309..8bbfbfd 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 */
 
+#include <linux/gfp.h>
 #include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/usb/input.h>
+#include <sound/core.h>
 #include <sound/pcm.h>
 
 #include "device.h"
@@ -65,6 +67,8 @@ static unsigned short keycode_kore[] = {
        KEY_BRL_DOT5
 };
 
+#define KONTROLX1_INPUTS 40
+
 #define DEG90          (range / 2)
 #define DEG180         (range)
 #define DEG270         (DEG90 + DEG180)
@@ -162,6 +166,17 @@ static void snd_caiaq_input_read_analog(struct snd_usb_caiaqdev *dev,
                input_report_abs(input_dev, ABS_Z, (buf[4] << 8) | buf[5]);
                input_sync(input_dev);
                break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               input_report_abs(input_dev, ABS_HAT0X, (buf[8] << 8)  | buf[9]);
+               input_report_abs(input_dev, ABS_HAT0Y, (buf[4] << 8)  | buf[5]);
+               input_report_abs(input_dev, ABS_HAT1X, (buf[12] << 8) | buf[13]);
+               input_report_abs(input_dev, ABS_HAT1Y, (buf[2] << 8)  | buf[3]);
+               input_report_abs(input_dev, ABS_HAT2X, (buf[15] << 8) | buf[15]);
+               input_report_abs(input_dev, ABS_HAT2Y, (buf[0] << 8)  | buf[1]);
+               input_report_abs(input_dev, ABS_HAT3X, (buf[10] << 8) | buf[11]);
+               input_report_abs(input_dev, ABS_HAT3Y, (buf[6] << 8)  | buf[7]);
+               input_sync(input_dev);
+               break;
        }
 }
 
@@ -201,7 +216,7 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
 }
 
 static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
-                                   char *buf, unsigned int len)
+                                   unsigned char *buf, unsigned int len)
 {
        struct input_dev *input_dev = dev->input_dev;
        unsigned short *keycode = input_dev->keycode;
@@ -218,15 +233,84 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *dev,
                input_report_key(input_dev, keycode[i],
                                 buf[i / 8] & (1 << (i % 8)));
 
-       if (dev->chip.usb_id ==
-               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER) ||
-           dev->chip.usb_id ==
-               USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2))
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER):
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2):
                input_report_abs(dev->input_dev, ABS_MISC, 255 - buf[4]);
+               break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               /* rotary encoders */
+               input_report_abs(dev->input_dev, ABS_X, buf[5] & 0xf);
+               input_report_abs(dev->input_dev, ABS_Y, buf[5] >> 4);
+               input_report_abs(dev->input_dev, ABS_Z, buf[6] & 0xf);
+               input_report_abs(dev->input_dev, ABS_MISC, buf[6] >> 4);
+               break;
+       }
 
        input_sync(input_dev);
 }
 
+static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
+{
+       struct snd_usb_caiaqdev *dev = urb->context;
+       unsigned char *buf = urb->transfer_buffer;
+       int ret;
+
+       if (urb->status || !dev || urb != dev->ep4_in_urb)
+               return;
+
+       if (urb->actual_length < 24)
+               goto requeue;
+
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               if (buf[0] & 0x3)
+                       snd_caiaq_input_read_io(dev, buf + 1, 7);
+
+               if (buf[0] & 0x4)
+                       snd_caiaq_input_read_analog(dev, buf + 8, 16);
+
+               break;
+       }
+
+requeue:
+       dev->ep4_in_urb->actual_length = 0;
+       ret = usb_submit_urb(dev->ep4_in_urb, GFP_ATOMIC);
+       if (ret < 0)
+               log("unable to submit urb. OOM!?\n");
+}
+
+static int snd_usb_caiaq_input_open(struct input_dev *idev)
+{
+       struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+
+       if (!dev)
+               return -EINVAL;
+
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
+                       return -EIO;
+               break;
+       }
+
+       return 0;
+}
+
+static void snd_usb_caiaq_input_close(struct input_dev *idev)
+{
+       struct snd_usb_caiaqdev *dev = input_get_drvdata(idev);
+
+       if (!dev)
+               return;
+
+       switch (dev->chip.usb_id) {
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               usb_kill_urb(dev->ep4_in_urb);
+               break;
+       }
+}
+
 void snd_usb_caiaq_input_dispatch(struct snd_usb_caiaqdev *dev,
                                  char *buf,
                                  unsigned int len)
@@ -251,7 +335,7 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
 {
        struct usb_device *usb_dev = dev->chip.dev;
        struct input_dev *input;
-       int i, ret;
+       int i, ret = 0;
 
        input = input_allocate_device();
        if (!input)
@@ -265,7 +349,9 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
        usb_to_input_id(usb_dev, &input->id);
        input->dev.parent = &usb_dev->dev;
 
-        switch (dev->chip.usb_id) {
+       input_set_drvdata(input, dev);
+
+       switch (dev->chip.usb_id) {
        case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
                input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
                input->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
@@ -326,25 +412,72 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
                input_set_abs_params(input, ABS_MISC, 0, 255, 0, 1);
                snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
                break;
+       case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
+               input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+               input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
+                                  BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
+                                  BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
+                                  BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
+                                  BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
+                                  BIT_MASK(ABS_Z);
+               input->absbit[BIT_WORD(ABS_MISC)] |= BIT_MASK(ABS_MISC);
+               BUILD_BUG_ON(sizeof(dev->keycode) < KONTROLX1_INPUTS);
+               for (i = 0; i < KONTROLX1_INPUTS; i++)
+                       dev->keycode[i] = BTN_MISC + i;
+               input->keycodemax = KONTROLX1_INPUTS;
+
+               /* analog potentiometers */
+               input_set_abs_params(input, ABS_HAT0X, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT0Y, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT1X, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT1Y, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT2X, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT2Y, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT3X, 0, 4096, 0, 10);
+               input_set_abs_params(input, ABS_HAT3Y, 0, 4096, 0, 10);
+
+               /* rotary encoders */
+               input_set_abs_params(input, ABS_X, 0, 0xf, 0, 1);
+               input_set_abs_params(input, ABS_Y, 0, 0xf, 0, 1);
+               input_set_abs_params(input, ABS_Z, 0, 0xf, 0, 1);
+               input_set_abs_params(input, ABS_MISC, 0, 0xf, 0, 1);
+
+               dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!dev->ep4_in_urb) {
+                       ret = -ENOMEM;
+                       goto exit_free_idev;
+               }
+
+               usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
+                                 usb_rcvbulkpipe(usb_dev, 0x4),
+                                 dev->ep4_in_buf, EP4_BUFSIZE,
+                                 snd_usb_caiaq_ep4_reply_dispatch, dev);
+
+               snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
+
+               break;
        default:
                /* no input methods supported on this device */
-               input_free_device(input);
-               return 0;
+               goto exit_free_idev;
        }
 
+       input->open = snd_usb_caiaq_input_open;
+       input->close = snd_usb_caiaq_input_close;
        input->keycode = dev->keycode;
        input->keycodesize = sizeof(unsigned short);
        for (i = 0; i < input->keycodemax; i++)
                __set_bit(dev->keycode[i], input->keybit);
 
        ret = input_register_device(input);
-       if (ret < 0) {
-               input_free_device(input);
-               return ret;
-       }
+       if (ret < 0)
+               goto exit_free_idev;
 
        dev->input_dev = input;
        return 0;
+
+exit_free_idev:
+       input_free_device(input);
+       return ret;
 }
 
 void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
@@ -352,6 +485,10 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
        if (!dev || !dev->input_dev)
                return;
 
+       usb_kill_urb(dev->ep4_in_urb);
+       usb_free_urb(dev->ep4_in_urb);
+       dev->ep4_in_urb = NULL;
+
        input_unregister_device(dev->input_dev);
        dev->input_dev = NULL;
 }
diff --git a/sound/usb/card.c b/sound/usb/card.c
new file mode 100644 (file)
index 0000000..da1346b
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ *   (Tentative) USB Audio Driver for ALSA
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by
+ *         Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *         Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *
+ *  NOTES:
+ *
+ *   - async unlink should be used for avoiding the sleep inside lock.
+ *     2.4.22 usb-uhci seems buggy for async unlinking and results in
+ *     oops.  in such a cse, pass async_unlink=0 option.
+ *   - the linked URBs would be preferred but not used so far because of
+ *     the instability of unlinking.
+ *   - type II is not supported properly.  there is no device which supports
+ *     this type *correctly*.  SB extigy looks as if it supports, but it's
+ *     indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
+ */
+
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "midi.h"
+#include "mixer.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "helper.h"
+#include "debug.h"
+#include "pcm.h"
+#include "urb.h"
+#include "format.h"
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("USB Audio");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+/* Vendor/product IDs for this card */
+static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
+static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
+static int nrpacks = 8;                /* max. number of packets per urb */
+static int async_unlink = 1;
+static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
+static int ignore_ctl_error;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
+module_param_array(vid, int, NULL, 0444);
+MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
+module_param_array(pid, int, NULL, 0444);
+MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
+module_param(nrpacks, int, 0644);
+MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
+module_param(async_unlink, bool, 0444);
+MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
+module_param_array(device_setup, int, NULL, 0444);
+MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
+module_param(ignore_ctl_error, bool, 0444);
+MODULE_PARM_DESC(ignore_ctl_error,
+                "Ignore errors from USB controller for mixer interfaces.");
+
+/*
+ * we keep the snd_usb_audio_t instances by ourselves for merging
+ * the all interfaces on the same card as one sound device.
+ */
+
+static DEFINE_MUTEX(register_mutex);
+static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
+static struct usb_driver usb_audio_driver;
+
+/*
+ * disconnect streams
+ * called from snd_usb_audio_disconnect()
+ */
+static void snd_usb_stream_disconnect(struct list_head *head)
+{
+       int idx;
+       struct snd_usb_stream *as;
+       struct snd_usb_substream *subs;
+
+       as = list_entry(head, struct snd_usb_stream, list);
+       for (idx = 0; idx < 2; idx++) {
+               subs = &as->substream[idx];
+               if (!subs->num_formats)
+                       return;
+               snd_usb_release_substream_urbs(subs, 1);
+               subs->interface = -1;
+       }
+}
+
+static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
+{
+       struct usb_device *dev = chip->dev;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
+
+       if (!iface) {
+               snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
+                          dev->devnum, ctrlif, interface);
+               return -EINVAL;
+       }
+
+       if (usb_interface_claimed(iface)) {
+               snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
+                                               dev->devnum, ctrlif, interface);
+               return -EINVAL;
+       }
+
+       alts = &iface->altsetting[0];
+       altsd = get_iface_desc(alts);
+       if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
+            altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
+           altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
+               int err = snd_usbmidi_create(chip->card, iface,
+                                            &chip->midi_list, NULL);
+               if (err < 0) {
+                       snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
+                                               dev->devnum, ctrlif, interface);
+                       return -EINVAL;
+               }
+               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
+
+               return 0;
+       }
+
+       if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
+            altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+           altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
+               snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
+                                       dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
+               /* skip non-supported classes */
+               return -EINVAL;
+       }
+
+       if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
+               snd_printk(KERN_ERR "low speed audio streaming not supported\n");
+               return -EINVAL;
+       }
+
+       if (! snd_usb_parse_audio_endpoints(chip, interface)) {
+               usb_set_interface(dev, interface, 0); /* reset the current interface */
+               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * parse audio control descriptor and create pcm/midi streams
+ */
+static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
+{
+       struct usb_device *dev = chip->dev;
+       struct usb_host_interface *host_iface;
+       struct usb_interface_descriptor *altsd;
+       void *control_header;
+       int i, protocol;
+
+       /* find audiocontrol interface */
+       host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
+       control_header = snd_usb_find_csint_desc(host_iface->extra,
+                                                host_iface->extralen,
+                                                NULL, UAC_HEADER);
+       altsd = get_iface_desc(host_iface);
+       protocol = altsd->bInterfaceProtocol;
+
+       if (!control_header) {
+               snd_printk(KERN_ERR "cannot find UAC_HEADER\n");
+               return -EINVAL;
+       }
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_ac_header_descriptor_v1 *h1 = control_header;
+
+               if (!h1->bInCollection) {
+                       snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
+                       return -EINVAL;
+               }
+
+               if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
+                       snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n");
+                       return -EINVAL;
+               }
+
+               for (i = 0; i < h1->bInCollection; i++)
+                       snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
+
+               break;
+       }
+
+       case UAC_VERSION_2: {
+               struct uac_clock_source_descriptor *cs;
+               struct usb_interface_assoc_descriptor *assoc =
+                       usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
+
+               if (!assoc) {
+                       snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
+                       return -EINVAL;
+               }
+
+               /* FIXME: for now, we expect there is at least one clock source
+                * descriptor and we always take the first one.
+                * We should properly support devices with multiple clock sources,
+                * clock selectors and sample rate conversion units. */
+
+               cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
+                                               NULL, UAC2_CLOCK_SOURCE);
+
+               if (!cs) {
+                       snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
+                       return -EINVAL;
+               }
+
+               chip->clock_id = cs->bClockID;
+
+               for (i = 0; i < assoc->bInterfaceCount; i++) {
+                       int intf = assoc->bFirstInterface + i;
+
+                       if (intf != ctrlif)
+                               snd_usb_create_stream(chip, ctrlif, intf);
+               }
+
+               break;
+       }
+
+       default:
+               snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * free the chip instance
+ *
+ * here we have to do not much, since pcm and controls are already freed
+ *
+ */
+
+static int snd_usb_audio_free(struct snd_usb_audio *chip)
+{
+       kfree(chip);
+       return 0;
+}
+
+static int snd_usb_audio_dev_free(struct snd_device *device)
+{
+       struct snd_usb_audio *chip = device->device_data;
+       return snd_usb_audio_free(chip);
+}
+
+
+/*
+ * create a chip instance and set its names.
+ */
+static int snd_usb_audio_create(struct usb_device *dev, int idx,
+                               const struct snd_usb_audio_quirk *quirk,
+                               struct snd_usb_audio **rchip)
+{
+       struct snd_card *card;
+       struct snd_usb_audio *chip;
+       int err, len;
+       char component[14];
+       static struct snd_device_ops ops = {
+               .dev_free =     snd_usb_audio_dev_free,
+       };
+
+       *rchip = NULL;
+
+       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
+           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
+           snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
+               snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
+               return -ENXIO;
+       }
+
+       err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
+               return err;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (! chip) {
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+
+       chip->index = idx;
+       chip->dev = dev;
+       chip->card = card;
+       chip->setup = device_setup[idx];
+       chip->nrpacks = nrpacks;
+       chip->async_unlink = async_unlink;
+
+       chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                             le16_to_cpu(dev->descriptor.idProduct));
+       INIT_LIST_HEAD(&chip->pcm_list);
+       INIT_LIST_HEAD(&chip->midi_list);
+       INIT_LIST_HEAD(&chip->mixer_list);
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+               snd_usb_audio_free(chip);
+               snd_card_free(card);
+               return err;
+       }
+
+       strcpy(card->driver, "USB-Audio");
+       sprintf(component, "USB%04x:%04x",
+               USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
+       snd_component_add(card, component);
+
+       /* retrieve the device string as shortname */
+       if (quirk && quirk->product_name) {
+               strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
+       } else {
+               if (!dev->descriptor.iProduct ||
+                   usb_string(dev, dev->descriptor.iProduct,
+                   card->shortname, sizeof(card->shortname)) <= 0) {
+                       /* no name available from anywhere, so use ID */
+                       sprintf(card->shortname, "USB Device %#04x:%#04x",
+                               USB_ID_VENDOR(chip->usb_id),
+                               USB_ID_PRODUCT(chip->usb_id));
+               }
+       }
+
+       /* retrieve the vendor and device strings as longname */
+       if (quirk && quirk->vendor_name) {
+               len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
+       } else {
+               if (dev->descriptor.iManufacturer)
+                       len = usb_string(dev, dev->descriptor.iManufacturer,
+                                        card->longname, sizeof(card->longname));
+               else
+                       len = 0;
+               /* we don't really care if there isn't any vendor string */
+       }
+       if (len > 0)
+               strlcat(card->longname, " ", sizeof(card->longname));
+
+       strlcat(card->longname, card->shortname, sizeof(card->longname));
+
+       len = strlcat(card->longname, " at ", sizeof(card->longname));
+
+       if (len < sizeof(card->longname))
+               usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
+
+       strlcat(card->longname,
+               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
+               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
+               ", high speed",
+               sizeof(card->longname));
+
+       snd_usb_audio_create_proc(chip);
+
+       *rchip = chip;
+       return 0;
+}
+
+/*
+ * probe the active usb device
+ *
+ * note that this can be called multiple times per a device, when it
+ * includes multiple audio control interfaces.
+ *
+ * thus we check the usb device pointer and creates the card instance
+ * only at the first time.  the successive calls of this function will
+ * append the pcm interface to the corresponding card.
+ */
+static void *snd_usb_audio_probe(struct usb_device *dev,
+                                struct usb_interface *intf,
+                                const struct usb_device_id *usb_id)
+{
+       const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
+       int i, err;
+       struct snd_usb_audio *chip;
+       struct usb_host_interface *alts;
+       int ifnum;
+       u32 id;
+
+       alts = &intf->altsetting[0];
+       ifnum = get_iface_desc(alts)->bInterfaceNumber;
+       id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                   le16_to_cpu(dev->descriptor.idProduct));
+       if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
+               goto __err_val;
+
+       if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0)
+               goto __err_val;
+
+       /*
+        * found a config.  now register to ALSA
+        */
+
+       /* check whether it's already registered */
+       chip = NULL;
+       mutex_lock(&register_mutex);
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               if (usb_chip[i] && usb_chip[i]->dev == dev) {
+                       if (usb_chip[i]->shutdown) {
+                               snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n");
+                               goto __error;
+                       }
+                       chip = usb_chip[i];
+                       break;
+               }
+       }
+       if (! chip) {
+               /* it's a fresh one.
+                * now look for an empty slot and create a new card instance
+                */
+               for (i = 0; i < SNDRV_CARDS; i++)
+                       if (enable[i] && ! usb_chip[i] &&
+                           (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
+                           (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
+                               if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
+                                       goto __error;
+                               }
+                               snd_card_set_dev(chip->card, &intf->dev);
+                               break;
+                       }
+               if (!chip) {
+                       printk(KERN_ERR "no available usb audio device\n");
+                       goto __error;
+               }
+       }
+
+       chip->txfr_quirk = 0;
+       err = 1; /* continue */
+       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
+               /* need some special handlings */
+               if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0)
+                       goto __error;
+       }
+
+       if (err > 0) {
+               /* create normal USB audio interfaces */
+               if (snd_usb_create_streams(chip, ifnum) < 0 ||
+                   snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
+                       goto __error;
+               }
+       }
+
+       /* we are allowed to call snd_card_register() many times */
+       if (snd_card_register(chip->card) < 0) {
+               goto __error;
+       }
+
+       usb_chip[chip->index] = chip;
+       chip->num_interfaces++;
+       mutex_unlock(&register_mutex);
+       return chip;
+
+ __error:
+       if (chip && !chip->num_interfaces)
+               snd_card_free(chip->card);
+       mutex_unlock(&register_mutex);
+ __err_val:
+       return NULL;
+}
+
+/*
+ * we need to take care of counter, since disconnection can be called also
+ * many times as well as usb_audio_probe().
+ */
+static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
+{
+       struct snd_usb_audio *chip;
+       struct snd_card *card;
+       struct list_head *p;
+
+       if (ptr == (void *)-1L)
+               return;
+
+       chip = ptr;
+       card = chip->card;
+       mutex_lock(&register_mutex);
+       chip->shutdown = 1;
+       chip->num_interfaces--;
+       if (chip->num_interfaces <= 0) {
+               snd_card_disconnect(card);
+               /* release the pcm resources */
+               list_for_each(p, &chip->pcm_list) {
+                       snd_usb_stream_disconnect(p);
+               }
+               /* release the midi resources */
+               list_for_each(p, &chip->midi_list) {
+                       snd_usbmidi_disconnect(p);
+               }
+               /* release mixer resources */
+               list_for_each(p, &chip->mixer_list) {
+                       snd_usb_mixer_disconnect(p);
+               }
+               usb_chip[chip->index] = NULL;
+               mutex_unlock(&register_mutex);
+               snd_card_free_when_closed(card);
+       } else {
+               mutex_unlock(&register_mutex);
+       }
+}
+
+/*
+ * new 2.5 USB kernel API
+ */
+static int usb_audio_probe(struct usb_interface *intf,
+                          const struct usb_device_id *id)
+{
+       void *chip;
+       chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
+       if (chip) {
+               usb_set_intfdata(intf, chip);
+               return 0;
+       } else
+               return -EIO;
+}
+
+static void usb_audio_disconnect(struct usb_interface *intf)
+{
+       snd_usb_audio_disconnect(interface_to_usbdev(intf),
+                                usb_get_intfdata(intf));
+}
+
+#ifdef CONFIG_PM
+static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct snd_usb_audio *chip = usb_get_intfdata(intf);
+       struct list_head *p;
+       struct snd_usb_stream *as;
+
+       if (chip == (void *)-1L)
+               return 0;
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
+       if (!chip->num_suspended_intf++) {
+               list_for_each(p, &chip->pcm_list) {
+                       as = list_entry(p, struct snd_usb_stream, list);
+                       snd_pcm_suspend_all(as->pcm);
+               }
+       }
+
+       return 0;
+}
+
+static int usb_audio_resume(struct usb_interface *intf)
+{
+       struct snd_usb_audio *chip = usb_get_intfdata(intf);
+
+       if (chip == (void *)-1L)
+               return 0;
+       if (--chip->num_suspended_intf)
+               return 0;
+       /*
+        * ALSA leaves material resumption to user space
+        * we just notify
+        */
+
+       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+#else
+#define usb_audio_suspend      NULL
+#define usb_audio_resume       NULL
+#endif         /* CONFIG_PM */
+
+static struct usb_device_id usb_audio_ids [] = {
+#include "quirks-table.h"
+    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
+      .bInterfaceClass = USB_CLASS_AUDIO,
+      .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
+    { }                                                /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_audio_ids);
+
+/*
+ * entry point for linux usb interface
+ */
+
+static struct usb_driver usb_audio_driver = {
+       .name =         "snd-usb-audio",
+       .probe =        usb_audio_probe,
+       .disconnect =   usb_audio_disconnect,
+       .suspend =      usb_audio_suspend,
+       .resume =       usb_audio_resume,
+       .id_table =     usb_audio_ids,
+};
+
+static int __init snd_usb_audio_init(void)
+{
+       if (nrpacks < 1 || nrpacks > MAX_PACKS) {
+               printk(KERN_WARNING "invalid nrpacks value.\n");
+               return -EINVAL;
+       }
+       return usb_register(&usb_audio_driver);
+}
+
+static void __exit snd_usb_audio_cleanup(void)
+{
+       usb_deregister(&usb_audio_driver);
+}
+
+module_init(snd_usb_audio_init);
+module_exit(snd_usb_audio_cleanup);
diff --git a/sound/usb/card.h b/sound/usb/card.h
new file mode 100644 (file)
index 0000000..ed92420
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef __USBAUDIO_CARD_H
+#define __USBAUDIO_CARD_H
+
+#define MAX_PACKS      20
+#define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
+#define MAX_URBS       8
+#define SYNC_URBS      4       /* always four urbs for sync */
+#define MAX_QUEUE      24      /* try not to exceed this queue length, in ms */
+
+struct audioformat {
+       struct list_head list;
+       u64 formats;                    /* ALSA format bits */
+       unsigned int channels;          /* # channels */
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+       unsigned int frame_size;        /* samples per frame for non-audio */
+       int iface;                      /* interface number */
+       unsigned char altsetting;       /* corresponding alternate setting */
+       unsigned char altset_idx;       /* array index of altenate setting */
+       unsigned char attributes;       /* corresponding attributes of cs endpoint */
+       unsigned char endpoint;         /* endpoint */
+       unsigned char ep_attr;          /* endpoint attributes */
+       unsigned char datainterval;     /* log_2 of data packet interval */
+       unsigned int maxpacksize;       /* max. packet size */
+       unsigned int rates;             /* rate bitmasks */
+       unsigned int rate_min, rate_max;        /* min/max rates */
+       unsigned int nr_rates;          /* number of rate table entries */
+       unsigned int *rate_table;       /* rate table */
+};
+
+struct snd_usb_substream;
+
+struct snd_urb_ctx {
+       struct urb *urb;
+       unsigned int buffer_size;       /* size of data buffer, if data URB */
+       struct snd_usb_substream *subs;
+       int index;      /* index for urb array */
+       int packets;    /* number of packets per urb */
+};
+
+struct snd_urb_ops {
+       int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+       int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+};
+
+struct snd_usb_substream {
+       struct snd_usb_stream *stream;
+       struct usb_device *dev;
+       struct snd_pcm_substream *pcm_substream;
+       int direction;  /* playback or capture */
+       int interface;  /* current interface */
+       int endpoint;   /* assigned endpoint */
+       struct audioformat *cur_audiofmt;       /* current audioformat pointer (for hw_params callback) */
+       unsigned int cur_rate;          /* current rate (for hw_params callback) */
+       unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
+       unsigned int altset_idx;     /* USB data format: index of alternate setting */
+       unsigned int datapipe;   /* the data i/o pipe */
+       unsigned int syncpipe;   /* 1 - async out or adaptive in */
+       unsigned int datainterval;      /* log_2 of data packet interval */
+       unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
+       unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
+       unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
+       unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+       unsigned int phase;      /* phase accumulator */
+       unsigned int maxpacksize;       /* max packet size in bytes */
+       unsigned int maxframesize;      /* max packet size in frames */
+       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
+       unsigned int curframesize;      /* current packet size in frames (for capture) */
+       unsigned int fill_max: 1;       /* fill max packet size always */
+       unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+
+       unsigned int running: 1;        /* running status */
+
+       unsigned int hwptr_done;        /* processed byte position in the buffer */
+       unsigned int transfer_done;             /* processed frames since last period update */
+       unsigned long active_mask;      /* bitmask of active urbs */
+       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
+
+       unsigned int nurbs;                     /* # urbs */
+       struct snd_urb_ctx dataurb[MAX_URBS];   /* data urb table */
+       struct snd_urb_ctx syncurb[SYNC_URBS];  /* sync urb table */
+       char *syncbuf;                          /* sync buffer for all sync URBs */
+       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
+
+       u64 formats;                    /* format bitmasks (all or'ed) */
+       unsigned int num_formats;               /* number of supported audio formats (list) */
+       struct list_head fmt_list;      /* format list */
+       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
+       spinlock_t lock;
+
+       struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
+};
+
+struct snd_usb_stream {
+       struct snd_usb_audio *chip;
+       struct snd_pcm *pcm;
+       int pcm_index;
+       unsigned int fmt_type;          /* USB audio format type (1-3) */
+       struct snd_usb_substream substream[2];
+       struct list_head list;
+};
+
+#endif /* __USBAUDIO_CARD_H */
diff --git a/sound/usb/debug.h b/sound/usb/debug.h
new file mode 100644 (file)
index 0000000..343ec2d
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __USBAUDIO_DEBUG_H
+#define __USBAUDIO_DEBUG_H
+
+/*
+ * h/w constraints
+ */
+
+#ifdef HW_CONST_DEBUG
+#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
+#else
+#define hwc_debug(fmt, args...) /**/
+#endif
+
+#endif /* __USBAUDIO_DEBUG_H */
+
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
new file mode 100644 (file)
index 0000000..ef07a6d
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "proc.h"
+#include "quirks.h"
+#include "endpoint.h"
+#include "urb.h"
+#include "pcm.h"
+#include "helper.h"
+#include "format.h"
+
+/*
+ * free a substream
+ */
+static void free_substream(struct snd_usb_substream *subs)
+{
+       struct list_head *p, *n;
+
+       if (!subs->num_formats)
+               return; /* not initialized */
+       list_for_each_safe(p, n, &subs->fmt_list) {
+               struct audioformat *fp = list_entry(p, struct audioformat, list);
+               kfree(fp->rate_table);
+               kfree(fp);
+       }
+       kfree(subs->rate_list.list);
+}
+
+
+/*
+ * free a usb stream instance
+ */
+static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
+{
+       free_substream(&stream->substream[0]);
+       free_substream(&stream->substream[1]);
+       list_del(&stream->list);
+       kfree(stream);
+}
+
+static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_usb_stream *stream = pcm->private_data;
+       if (stream) {
+               stream->pcm = NULL;
+               snd_usb_audio_stream_free(stream);
+       }
+}
+
+
+/*
+ * add this endpoint to the chip instance.
+ * if a stream with the same endpoint already exists, append to it.
+ * if not, create a new pcm stream.
+ */
+int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp)
+{
+       struct list_head *p;
+       struct snd_usb_stream *as;
+       struct snd_usb_substream *subs;
+       struct snd_pcm *pcm;
+       int err;
+
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, struct snd_usb_stream, list);
+               if (as->fmt_type != fp->fmt_type)
+                       continue;
+               subs = &as->substream[stream];
+               if (!subs->endpoint)
+                       continue;
+               if (subs->endpoint == fp->endpoint) {
+                       list_add_tail(&fp->list, &subs->fmt_list);
+                       subs->num_formats++;
+                       subs->formats |= fp->formats;
+                       return 0;
+               }
+       }
+       /* look for an empty stream */
+       list_for_each(p, &chip->pcm_list) {
+               as = list_entry(p, struct snd_usb_stream, list);
+               if (as->fmt_type != fp->fmt_type)
+                       continue;
+               subs = &as->substream[stream];
+               if (subs->endpoint)
+                       continue;
+               err = snd_pcm_new_stream(as->pcm, stream, 1);
+               if (err < 0)
+                       return err;
+               snd_usb_init_substream(as, stream, fp);
+               return 0;
+       }
+
+       /* create a new pcm */
+       as = kzalloc(sizeof(*as), GFP_KERNEL);
+       if (!as)
+               return -ENOMEM;
+       as->pcm_index = chip->pcm_devs;
+       as->chip = chip;
+       as->fmt_type = fp->fmt_type;
+       err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
+                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
+                         &pcm);
+       if (err < 0) {
+               kfree(as);
+               return err;
+       }
+       as->pcm = pcm;
+       pcm->private_data = as;
+       pcm->private_free = snd_usb_audio_pcm_free;
+       pcm->info_flags = 0;
+       if (chip->pcm_devs > 0)
+               sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+       else
+               strcpy(pcm->name, "USB Audio");
+
+       snd_usb_init_substream(as, stream, fp);
+
+       list_add(&as->list, &chip->pcm_list);
+       chip->pcm_devs++;
+
+       snd_usb_proc_pcm_format_add(as);
+
+       return 0;
+}
+
+int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
+{
+       struct usb_device *dev;
+       struct usb_interface *iface;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       int i, altno, err, stream;
+       int format = 0, num_channels = 0;
+       struct audioformat *fp = NULL;
+       unsigned char *fmt, *csep;
+       int num, protocol;
+
+       dev = chip->dev;
+
+       /* parse the interface's altsettings */
+       iface = usb_ifnum_to_if(dev, iface_no);
+
+       num = iface->num_altsetting;
+
+       /*
+        * Dallas DS4201 workaround: It presents 5 altsettings, but the last
+        * one misses syncpipe, and does not produce any sound.
+        */
+       if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+               num = 4;
+
+       for (i = 0; i < num; i++) {
+               alts = &iface->altsetting[i];
+               altsd = get_iface_desc(alts);
+               protocol = altsd->bInterfaceProtocol;
+               /* skip invalid one */
+               if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
+                    altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+                   (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
+                    altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
+                   altsd->bNumEndpoints < 1 ||
+                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
+                       continue;
+               /* must be isochronous */
+               if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+                   USB_ENDPOINT_XFER_ISOC)
+                       continue;
+               /* check direction */
+               stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
+                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+               altno = altsd->bAlternateSetting;
+
+               if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
+                       continue;
+
+               /* get audio formats */
+               switch (protocol) {
+               case UAC_VERSION_1: {
+                       struct uac_as_header_descriptor_v1 *as =
+                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+
+                       if (!as) {
+                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       if (as->bLength < sizeof(*as)) {
+                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       format = le16_to_cpu(as->wFormatTag); /* remember the format value */
+                       break;
+               }
+
+               case UAC_VERSION_2: {
+                       struct uac_as_header_descriptor_v2 *as =
+                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
+
+                       if (!as) {
+                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       if (as->bLength < sizeof(*as)) {
+                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
+                                          dev->devnum, iface_no, altno);
+                               continue;
+                       }
+
+                       num_channels = as->bNrChannels;
+                       format = le32_to_cpu(as->bmFormats);
+
+                       break;
+               }
+
+               default:
+                       snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
+                                  dev->devnum, iface_no, altno, protocol);
+                       continue;
+               }
+
+               /* get format type */
+               fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
+               if (!fmt) {
+                       snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+               if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
+                   ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
+                       snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+                                  dev->devnum, iface_no, altno);
+                       continue;
+               }
+
+               /*
+                * Blue Microphones workaround: The last altsetting is identical
+                * with the previous one, except for a larger packet size, but
+                * is actually a mislabeled two-channel setting; ignore it.
+                */
+               if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
+                   fp && fp->altsetting == 1 && fp->channels == 1 &&
+                   fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
+                   protocol == UAC_VERSION_1 &&
+                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
+                                                       fp->maxpacksize * 2)
+                       continue;
+
+               csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
+               /* Creamware Noah has this descriptor after the 2nd endpoint */
+               if (!csep && altsd->bNumEndpoints >= 2)
+                       csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
+               if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) {
+                       snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
+                                  " class specific endpoint descriptor\n",
+                                  dev->devnum, iface_no, altno);
+                       csep = NULL;
+               }
+
+               fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+               if (! fp) {
+                       snd_printk(KERN_ERR "cannot malloc\n");
+                       return -ENOMEM;
+               }
+
+               fp->iface = iface_no;
+               fp->altsetting = altno;
+               fp->altset_idx = i;
+               fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+               fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+               fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+               /* num_channels is only set for v2 interfaces */
+               fp->channels = num_channels;
+               if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
+                       fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
+                                       * (fp->maxpacksize & 0x7ff);
+               fp->attributes = csep ? csep[3] : 0;
+
+               /* some quirks for attributes here */
+
+               switch (chip->usb_id) {
+               case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
+                       /* Optoplay sets the sample rate attribute although
+                        * it seems not supporting it in fact.
+                        */
+                       fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
+                       break;
+               case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+               case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8 */
+               case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+                       /* doesn't set the sample rate attribute, but supports it */
+                       fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
+                       break;
+               case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
+               case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
+                                               an older model 77d:223) */
+               /*
+                * plantronics headset and Griffin iMic have set adaptive-in
+                * although it's really not...
+                */
+                       fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
+                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
+                       else
+                               fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
+                       break;
+               }
+
+               /* ok, let's parse further... */
+               if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
+                       kfree(fp->rate_table);
+                       kfree(fp);
+                       continue;
+               }
+
+               snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
+               err = snd_usb_add_audio_endpoint(chip, stream, fp);
+               if (err < 0) {
+                       kfree(fp->rate_table);
+                       kfree(fp);
+                       return err;
+               }
+               /* try to set the interface... */
+               usb_set_interface(chip->dev, iface_no, altno);
+               snd_usb_init_pitch(chip, iface_no, alts, fp);
+               snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
+       }
+       return 0;
+}
+
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
new file mode 100644 (file)
index 0000000..64dd0db
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __USBAUDIO_ENDPOINT_H
+#define __USBAUDIO_ENDPOINT_H
+
+int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip,
+                                 int iface_no);
+
+int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip,
+                              int stream,
+                              struct audioformat *fp);
+
+#endif /* __USBAUDIO_ENDPOINT_H */
diff --git a/sound/usb/format.c b/sound/usb/format.c
new file mode 100644 (file)
index 0000000..b87cf87
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "quirks.h"
+#include "helper.h"
+#include "debug.h"
+
+/*
+ * parse the audio format type I descriptor
+ * and returns the corresponding pcm format
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @format: the format tag (wFormatTag)
+ * @fmt: the format type descriptor
+ */
+static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
+                                    struct audioformat *fp,
+                                    int format, void *_fmt,
+                                    int protocol)
+{
+       int sample_width, sample_bytes;
+       u64 pcm_formats;
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
+               sample_width = fmt->bBitResolution;
+               sample_bytes = fmt->bSubframeSize;
+               format = 1 << format;
+               break;
+       }
+
+       case UAC_VERSION_2: {
+               struct uac_format_type_i_ext_descriptor *fmt = _fmt;
+               sample_width = fmt->bBitResolution;
+               sample_bytes = fmt->bSubslotSize;
+               format <<= 1;
+               break;
+       }
+
+       default:
+               return -EINVAL;
+       }
+
+       pcm_formats = 0;
+
+       if (format == 0 || format == (1 << UAC_FORMAT_TYPE_I_UNDEFINED)) {
+               /* some devices don't define this correctly... */
+               snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
+                           chip->dev->devnum, fp->iface, fp->altsetting);
+               format = 1 << UAC_FORMAT_TYPE_I_PCM;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM)) {
+               if (sample_width > sample_bytes * 8) {
+                       snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting,
+                                  sample_width, sample_bytes);
+               }
+               /* check the format byte size */
+               switch (sample_bytes) {
+               case 1:
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
+                       break;
+               case 2:
+                       if (snd_usb_is_big_endian_format(chip, fp))
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_BE; /* grrr, big endian!! */
+                       else
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S16_LE;
+                       break;
+               case 3:
+                       if (snd_usb_is_big_endian_format(chip, fp))
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3BE; /* grrr, big endian!! */
+                       else
+                               pcm_formats |= SNDRV_PCM_FMTBIT_S24_3LE;
+                       break;
+               case 4:
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S32_LE;
+                       break;
+               default:
+                       snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting,
+                                  sample_width, sample_bytes);
+                       break;
+               }
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_PCM8)) {
+               /* Dallas DS4201 workaround: it advertises U8 format, but really
+                  supports S8. */
+               if (chip->usb_id == USB_ID(0x04fa, 0x4201))
+                       pcm_formats |= SNDRV_PCM_FMTBIT_S8;
+               else
+                       pcm_formats |= SNDRV_PCM_FMTBIT_U8;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_IEEE_FLOAT)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_ALAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_A_LAW;
+       }
+       if (format & (1 << UAC_FORMAT_TYPE_I_MULAW)) {
+               pcm_formats |= SNDRV_PCM_FMTBIT_MU_LAW;
+       }
+       if (format & ~0x3f) {
+               snd_printk(KERN_INFO "%d:%u:%d : unsupported format bits %#x\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
+       }
+       return pcm_formats;
+}
+
+
+/*
+ * parse the format descriptor and stores the possible sample rates
+ * on the audioformat table (audio class v1).
+ *
+ * @dev: usb device
+ * @fp: audioformat record
+ * @fmt: the format descriptor
+ * @offset: the start offset of descriptor pointing the rate type
+ *          (7 for type I and II, 8 for type II)
+ */
+static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
+                                      unsigned char *fmt, int offset)
+{
+       int nr_rates = fmt[offset];
+
+       if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
+               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
+                                  chip->dev->devnum, fp->iface, fp->altsetting);
+               return -1;
+       }
+
+       if (nr_rates) {
+               /*
+                * build the rate table and bitmap flags
+                */
+               int r, idx;
+
+               fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+               if (fp->rate_table == NULL) {
+                       snd_printk(KERN_ERR "cannot malloc\n");
+                       return -1;
+               }
+
+               fp->nr_rates = 0;
+               fp->rate_min = fp->rate_max = 0;
+               for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
+                       unsigned int rate = combine_triple(&fmt[idx]);
+                       if (!rate)
+                               continue;
+                       /* C-Media CM6501 mislabels its 96 kHz altsetting */
+                       if (rate == 48000 && nr_rates == 1 &&
+                           (chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
+                            chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
+                           fp->altsetting == 5 && fp->maxpacksize == 392)
+                               rate = 96000;
+                       /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
+                       if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
+                               rate = 8000;
+
+                       fp->rate_table[fp->nr_rates] = rate;
+                       if (!fp->rate_min || rate < fp->rate_min)
+                               fp->rate_min = rate;
+                       if (!fp->rate_max || rate > fp->rate_max)
+                               fp->rate_max = rate;
+                       fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+                       fp->nr_rates++;
+               }
+               if (!fp->nr_rates) {
+                       hwc_debug("All rates were zero. Skipping format!\n");
+                       return -1;
+               }
+       } else {
+               /* continuous rates */
+               fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+               fp->rate_min = combine_triple(&fmt[offset + 1]);
+               fp->rate_max = combine_triple(&fmt[offset + 4]);
+       }
+       return 0;
+}
+
+/*
+ * parse the format descriptor and stores the possible sample rates
+ * on the audioformat table (audio class v2).
+ */
+static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
+                                      struct audioformat *fp,
+                                      struct usb_host_interface *iface)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned char tmp[2], *data;
+       int i, nr_rates, data_size, ret = 0;
+
+       /* get the number of sample rates first by only fetching 2 bytes */
+       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
+                             USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                             UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                             tmp, sizeof(tmp), 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
+               goto err;
+       }
+
+       nr_rates = (tmp[1] << 8) | tmp[0];
+       data_size = 2 + 12 * nr_rates;
+       data = kzalloc(data_size, GFP_KERNEL);
+       if (!data) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       /* now get the full information */
+       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
+                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                              UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                              data, data_size, 1000);
+
+       if (ret < 0) {
+               snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
+               ret = -EINVAL;
+               goto err_free;
+       }
+
+       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+       if (!fp->rate_table) {
+               ret = -ENOMEM;
+               goto err_free;
+       }
+
+       fp->nr_rates = 0;
+       fp->rate_min = fp->rate_max = 0;
+
+       for (i = 0; i < nr_rates; i++) {
+               int rate = combine_quad(&data[2 + 12 * i]);
+
+               fp->rate_table[fp->nr_rates] = rate;
+               if (!fp->rate_min || rate < fp->rate_min)
+                       fp->rate_min = rate;
+               if (!fp->rate_max || rate > fp->rate_max)
+                       fp->rate_max = rate;
+               fp->rates |= snd_pcm_rate_to_rate_bit(rate);
+               fp->nr_rates++;
+       }
+
+err_free:
+       kfree(data);
+err:
+       return ret;
+}
+
+/*
+ * parse the format type I and III descriptors
+ */
+static int parse_audio_format_i(struct snd_usb_audio *chip,
+                               struct audioformat *fp,
+                               int format, void *_fmt,
+                               struct usb_host_interface *iface)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
+       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
+       int protocol = altsd->bInterfaceProtocol;
+       int pcm_format, ret;
+
+       if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
+               /* FIXME: the format type is really IECxxx
+                *        but we give normal PCM format to get the existing
+                *        apps working...
+                */
+               switch (chip->usb_id) {
+
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+                       if (chip->setup == 0x00 && 
+                           fp->altsetting == 6)
+                               pcm_format = SNDRV_PCM_FORMAT_S16_BE;
+                       else
+                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+                       break;
+               default:
+                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+               }
+               fp->formats = 1uLL << pcm_format;
+       } else {
+               fp->formats = parse_audio_format_i_type(chip, fp, format,
+                                                       fmt, protocol);
+               if (!fp->formats)
+                       return -1;
+       }
+
+       /* gather possible sample rates */
+       /* audio class v1 reports possible sample rates as part of the
+        * proprietary class specific descriptor.
+        * audio class v2 uses class specific EP0 range requests for that.
+        */
+       switch (protocol) {
+       case UAC_VERSION_1:
+               fp->channels = fmt->bNrChannels;
+               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
+               break;
+       case UAC_VERSION_2:
+               /* fp->channels is already set in this case */
+               ret = parse_audio_format_rates_v2(chip, fp, iface);
+               break;
+       }
+
+       if (fp->channels < 1) {
+               snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
+               return -1;
+       }
+
+       return ret;
+}
+
+/*
+ * parse the format type II descriptor
+ */
+static int parse_audio_format_ii(struct snd_usb_audio *chip,
+                                struct audioformat *fp,
+                                int format, void *_fmt,
+                                struct usb_host_interface *iface)
+{
+       int brate, framesize, ret;
+       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
+       int protocol = altsd->bInterfaceProtocol;
+
+       switch (format) {
+       case UAC_FORMAT_TYPE_II_AC3:
+               /* FIXME: there is no AC3 format defined yet */
+               // fp->formats = SNDRV_PCM_FMTBIT_AC3;
+               fp->formats = SNDRV_PCM_FMTBIT_U8; /* temporary hack to receive byte streams */
+               break;
+       case UAC_FORMAT_TYPE_II_MPEG:
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
+               break;
+       default:
+               snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
+               fp->formats = SNDRV_PCM_FMTBIT_MPEG;
+               break;
+       }
+
+       fp->channels = 1;
+
+       switch (protocol) {
+       case UAC_VERSION_1: {
+               struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
+               brate = le16_to_cpu(fmt->wMaxBitRate);
+               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
+               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+               fp->frame_size = framesize;
+               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
+               break;
+       }
+       case UAC_VERSION_2: {
+               struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
+               brate = le16_to_cpu(fmt->wMaxBitRate);
+               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
+               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
+               fp->frame_size = framesize;
+               ret = parse_audio_format_rates_v2(chip, fp, iface);
+               break;
+       }
+       }
+
+       return ret;
+}
+
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
+                      int format, unsigned char *fmt, int stream,
+                      struct usb_host_interface *iface)
+{
+       int err;
+
+       switch (fmt[3]) {
+       case UAC_FORMAT_TYPE_I:
+       case UAC_FORMAT_TYPE_III:
+               err = parse_audio_format_i(chip, fp, format, fmt, iface);
+               break;
+       case UAC_FORMAT_TYPE_II:
+               err = parse_audio_format_ii(chip, fp, format, fmt, iface);
+               break;
+       default:
+               snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
+                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
+               return -1;
+       }
+       fp->fmt_type = fmt[3];
+       if (err < 0)
+               return err;
+#if 1
+       /* FIXME: temporary hack for extigy/audigy 2 nx/zs */
+       /* extigy apparently supports sample rates other than 48k
+        * but not in ordinary way.  so we enable only 48k atm.
+        */
+       if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
+           chip->usb_id == USB_ID(0x041e, 0x3020) ||
+           chip->usb_id == USB_ID(0x041e, 0x3061)) {
+               if (fmt[3] == UAC_FORMAT_TYPE_I &&
+                   fp->rates != SNDRV_PCM_RATE_48000 &&
+                   fp->rates != SNDRV_PCM_RATE_96000)
+                       return -1;
+       }
+#endif
+       return 0;
+}
+
diff --git a/sound/usb/format.h b/sound/usb/format.h
new file mode 100644 (file)
index 0000000..8298c4e
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __USBAUDIO_FORMAT_H
+#define __USBAUDIO_FORMAT_H
+
+int snd_usb_parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
+                              int format, unsigned char *fmt, int stream,
+                              struct usb_host_interface *iface);
+
+#endif /*  __USBAUDIO_FORMAT_H */
diff --git a/sound/usb/helper.c b/sound/usb/helper.c
new file mode 100644 (file)
index 0000000..d48d6f8
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+
+/*
+ * combine bytes and get an integer value
+ */
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
+{
+       switch (size) {
+       case 1:  return *bytes;
+       case 2:  return combine_word(bytes);
+       case 3:  return combine_triple(bytes);
+       case 4:  return combine_quad(bytes);
+       default: return 0;
+       }
+}
+
+/*
+ * parse descriptor buffer and return the pointer starting the given
+ * descriptor type.
+ */
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
+{
+       u8 *p, *end, *next;
+
+       p = descstart;
+       end = p + desclen;
+       for (; p < end;) {
+               if (p[0] < 2)
+                       return NULL;
+               next = p + p[0];
+               if (next > end)
+                       return NULL;
+               if (p[1] == dtype && (!after || (void *)p > after)) {
+                       return p;
+               }
+               p = next;
+       }
+       return NULL;
+}
+
+/*
+ * find a class-specified interface descriptor with the given subtype.
+ */
+void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
+{
+       unsigned char *p = after;
+
+       while ((p = snd_usb_find_desc(buffer, buflen, p,
+                                     USB_DT_CS_INTERFACE)) != NULL) {
+               if (p[0] >= 3 && p[2] == dsubtype)
+                       return p;
+       }
+       return NULL;
+}
+
+/*
+ * Wrapper for usb_control_msg().
+ * Allocates a temp buffer to prevent dmaing from/to the stack.
+ */
+int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
+                   __u8 requesttype, __u16 value, __u16 index, void *data,
+                   __u16 size, int timeout)
+{
+       int err;
+       void *buf = NULL;
+
+       if (size > 0) {
+               buf = kmemdup(data, size, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+       }
+       err = usb_control_msg(dev, pipe, request, requesttype,
+                             value, index, buf, size, timeout);
+       if (size > 0) {
+               memcpy(data, buf, size);
+               kfree(buf);
+       }
+       return err;
+}
+
+unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
+                                        struct usb_host_interface *alts)
+{
+       if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
+           get_endpoint(alts, 0)->bInterval >= 1 &&
+           get_endpoint(alts, 0)->bInterval <= 4)
+               return get_endpoint(alts, 0)->bInterval - 1;
+       else
+               return 0;
+}
+
diff --git a/sound/usb/helper.h b/sound/usb/helper.h
new file mode 100644 (file)
index 0000000..a6b0e51
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef __USBAUDIO_HELPER_H
+#define __USBAUDIO_HELPER_H
+
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
+
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
+void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
+
+int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
+                   __u8 request, __u8 requesttype, __u16 value, __u16 index,
+                   void *data, __u16 size, int timeout);
+
+unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
+                                        struct usb_host_interface *alts);
+
+/*
+ * retrieve usb_interface descriptor from the host interface
+ * (conditional for compatibility with the older API)
+ */
+#ifndef get_iface_desc
+#define get_iface_desc(iface)  (&(iface)->desc)
+#define get_endpoint(alt,ep)   (&(alt)->endpoint[ep].desc)
+#define get_ep_desc(ep)                (&(ep)->desc)
+#define get_cfg_desc(cfg)      (&(cfg)->desc)
+#endif
+
+#ifndef snd_usb_get_speed
+#define snd_usb_get_speed(dev) ((dev)->speed)
+#endif
+
+
+#endif /* __USBAUDIO_HELPER_H */
similarity index 99%
rename from sound/usb/usbmidi.c
rename to sound/usb/midi.c
index 9e28b20..2c1558c 100644 (file)
@@ -53,7 +53,8 @@
 #include <sound/rawmidi.h>
 #include <sound/asequencer.h>
 #include "usbaudio.h"
-
+#include "midi.h"
+#include "helper.h"
 
 /*
  * define this to log all USB packets
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
new file mode 100644 (file)
index 0000000..2089ec9
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __USBMIDI_H
+#define __USBMIDI_H
+
+/* maximum number of endpoints per interface */
+#define MIDI_MAX_ENDPOINTS 2
+
+/* data for QUIRK_MIDI_FIXED_ENDPOINT */
+struct snd_usb_midi_endpoint_info {
+       int8_t   out_ep;        /* ep number, 0 autodetect */
+       uint8_t  out_interval;  /* interval for interrupt endpoints */
+       int8_t   in_ep;
+       uint8_t  in_interval;
+       uint16_t out_cables;    /* bitmask */
+       uint16_t in_cables;     /* bitmask */
+};
+
+/* for QUIRK_MIDI_YAMAHA, data is NULL */
+
+/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
+ * structure (out_cables and in_cables only) */
+
+/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
+ * structures, terminated with .ifnum = -1 */
+
+/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
+
+/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
+
+/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */
+
+/* for QUIRK_IGNORE_INTERFACE, data is NULL */
+
+/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
+
+/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
+ * structure (out_cables and in_cables only) */
+
+/* for QUIRK_MIDI_CME, data is NULL */
+
+int snd_usbmidi_create(struct snd_card *card,
+                      struct usb_interface *iface,
+                      struct list_head *midi_list,
+                      const struct snd_usb_audio_quirk *quirk);
+void snd_usbmidi_input_stop(struct list_head* p);
+void snd_usbmidi_input_start(struct list_head* p);
+void snd_usbmidi_disconnect(struct list_head *p);
+
+#endif /* __USBMIDI_H */
diff --git a/sound/usb/misc/Makefile b/sound/usb/misc/Makefile
new file mode 100644 (file)
index 0000000..ccefd81
--- /dev/null
@@ -0,0 +1,2 @@
+snd-ua101-objs := ua101.o
+obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o
similarity index 99%
rename from sound/usb/ua101.c
rename to sound/usb/misc/ua101.c
index 3d458d3..796d8b2 100644 (file)
@@ -23,7 +23,8 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include "usbaudio.h"
+#include "../usbaudio.h"
+#include "../midi.h"
 
 MODULE_DESCRIPTION("Edirol UA-101/1000 driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
similarity index 72%
rename from sound/usb/usbmixer.c
rename to sound/usb/mixer.c
index 8e8f871..1deef62 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/string.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
 
 #include "usbaudio.h"
-
-/*
- */
-
-/* ignore error from controls - for debugging */
-/* #define IGNORE_CTL_ERROR */
-
-/*
- * Sound Blaster remote control configuration
- *
- * format of remote control data:
- * Extigy:       xx 00
- * Audigy 2 NX:  06 80 xx 00 00 00
- * Live! 24-bit: 06 80 xx yy 22 83
- */
-static const struct rc_config {
-       u32 usb_id;
-       u8  offset;
-       u8  length;
-       u8  packet_length;
-       u8  min_packet_length; /* minimum accepted length of the URB result */
-       u8  mute_mixer_id;
-       u32 mute_code;
-} rc_configs[] = {
-       { USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
-       { USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
-       { USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
-       { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
-};
+#include "mixer.h"
+#include "helper.h"
+#include "mixer_quirks.h"
 
 #define MAX_ID_ELEMS   256
 
-struct usb_mixer_interface {
-       struct snd_usb_audio *chip;
-       unsigned int ctrlif;
-       struct list_head list;
-       unsigned int ignore_ctl_error;
-       struct urb *urb;
-       /* array[MAX_ID_ELEMS], indexed by unit id */
-       struct usb_mixer_elem_info **id_elems;
-
-       /* Sound Blaster remote control stuff */
-       const struct rc_config *rc_cfg;
-       u32 rc_code;
-       wait_queue_head_t rc_waitq;
-       struct urb *rc_urb;
-       struct usb_ctrlrequest *rc_setup_packet;
-       u8 rc_buffer[6];
-
-       u8 audigy2nx_leds[3];
-       u8 xonar_u1_status;
-};
-
-
 struct usb_audio_term {
        int id;
        int type;
@@ -116,39 +69,6 @@ struct mixer_build {
        const struct usbmix_selector_map *selector_map;
 };
 
-#define MAX_CHANNELS   10      /* max logical channels */
-
-struct usb_mixer_elem_info {
-       struct usb_mixer_interface *mixer;
-       struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
-       struct snd_ctl_elem_id *elem_id;
-       unsigned int id;
-       unsigned int control;   /* CS or ICN (high byte) */
-       unsigned int cmask; /* channel mask bitmap: 0 = master */
-       int channels;
-       int val_type;
-       int min, max, res;
-       int dBmin, dBmax;
-       int cached;
-       int cache_val[MAX_CHANNELS];
-       u8 initialized;
-};
-
-
-enum {
-       USB_FEATURE_NONE = 0,
-       USB_FEATURE_MUTE = 1,
-       USB_FEATURE_VOLUME,
-       USB_FEATURE_BASS,
-       USB_FEATURE_MID,
-       USB_FEATURE_TREBLE,
-       USB_FEATURE_GEQ,
-       USB_FEATURE_AGC,
-       USB_FEATURE_DELAY,
-       USB_FEATURE_BASSBOOST,
-       USB_FEATURE_LOUDNESS
-};
-
 enum {
        USB_MIXER_BOOLEAN,
        USB_MIXER_INV_BOOLEAN,
@@ -213,7 +133,7 @@ enum {
  * if the mixer topology is too complicated and the parsed names are
  * ambiguous, add the entries in usbmixer_maps.c.
  */
-#include "usbmixer_maps.c"
+#include "mixer_maps.c"
 
 static const struct usbmix_name_map *
 find_map(struct mixer_build *state, int unitid, int control)
@@ -278,6 +198,7 @@ static int check_mapped_selector_name(struct mixer_build *state, int unitid,
 
 /*
  * find an audio control unit with the given unit id
+ * this doesn't return any clock related units, so they need to be handled elsewhere
  */
 static void *find_audio_control_unit(struct mixer_build *state, unsigned char unit)
 {
@@ -286,7 +207,7 @@ static void *find_audio_control_unit(struct mixer_build *state, unsigned char un
        p = NULL;
        while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
                                      USB_DT_CS_INTERFACE)) != NULL) {
-               if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC_EXTENSION_UNIT_V1 && p[3] == unit)
+               if (p[0] >= 4 && p[2] >= UAC_INPUT_TERMINAL && p[2] <= UAC2_EXTENSION_UNIT_V2 && p[3] == unit)
                        return p;
        }
        return NULL;
@@ -383,7 +304,7 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val)
  * retrieve a mixer value
  */
 
-static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
 {
        unsigned char buf[2];
        int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
@@ -405,6 +326,58 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
        return -EINVAL;
 }
 
+static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+       unsigned char buf[14]; /* enough space for one range of 4 bytes */
+       unsigned char *val;
+       int ret;
+       __u8 bRequest;
+
+       bRequest = (request == UAC_GET_CUR) ?
+               UAC2_CS_CUR : UAC2_CS_RANGE;
+
+       ret = snd_usb_ctl_msg(cval->mixer->chip->dev,
+                             usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
+                             bRequest,
+                             USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+                             validx, cval->mixer->ctrlif | (cval->id << 8),
+                             buf, sizeof(buf), 1000);
+
+       if (ret < 0) {
+               snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+                           request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
+               return ret;
+       }
+
+       switch (request) {
+       case UAC_GET_CUR:
+               val = buf;
+               break;
+       case UAC_GET_MIN:
+               val = buf + sizeof(__u16);
+               break;
+       case UAC_GET_MAX:
+               val = buf + sizeof(__u16) * 2;
+               break;
+       case UAC_GET_RES:
+               val = buf + sizeof(__u16) * 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16)));
+
+       return 0;
+}
+
+static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret)
+{
+       return (cval->mixer->protocol == UAC_VERSION_1) ?
+               get_ctl_value_v1(cval, request, validx, value_ret) :
+               get_ctl_value_v2(cval, request, validx, value_ret);
+}
+
 static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *value)
 {
        return get_ctl_value(cval, UAC_GET_CUR, validx, value);
@@ -429,8 +402,7 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
        err = get_cur_mix_raw(cval, channel, value);
        if (err < 0) {
                if (!cval->mixer->ignore_ctl_error)
-                       snd_printd(KERN_ERR "cannot get current value for "
-                                  "control %d ch %d: err = %d\n",
+                       snd_printd(KERN_ERR "cannot get current value for control %d ch %d: err = %d\n",
                                   cval->control, channel, err);
                return err;
        }
@@ -444,11 +416,26 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval,
  * set a mixer value
  */
 
-static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set)
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+                               int request, int validx, int value_set)
 {
        unsigned char buf[2];
-       int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
-       int timeout = 10;
+       int val_len, timeout = 10;
+
+       if (cval->mixer->protocol == UAC_VERSION_1) {
+               val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
+       } else { /* UAC_VERSION_2 */
+               /* audio class v2 controls are always 2 bytes in size */
+               val_len = sizeof(__u16);
+
+               /* FIXME */
+               if (request != UAC_SET_CUR) {
+                       snd_printdd(KERN_WARNING "RANGE setting not yet supported\n");
+                       return -EINVAL;
+               }
+
+               request = UAC2_CS_CUR;
+       }
 
        value_set = convert_bytes_value(cval, value_set);
        buf[0] = value_set & 0xff;
@@ -468,14 +455,14 @@ static int set_ctl_value(struct usb_mixer_elem_info *cval, int request, int vali
 
 static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int value)
 {
-       return set_ctl_value(cval, UAC_SET_CUR, validx, value);
+       return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value);
 }
 
 static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
                             int index, int value)
 {
        int err;
-       err = set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
+       err = snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, (cval->control << 8) | channel,
                            value);
        if (err < 0)
                return err;
@@ -644,46 +631,65 @@ static int get_term_name(struct mixer_build *state, struct usb_audio_term *iterm
  */
 static int check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term)
 {
-       unsigned char *p1;
+       void *p1;
 
        memset(term, 0, sizeof(*term));
        while ((p1 = find_audio_control_unit(state, id)) != NULL) {
+               unsigned char *hdr = p1;
                term->id = id;
-               switch (p1[2]) {
+               switch (hdr[2]) {
                case UAC_INPUT_TERMINAL:
-                       term->type = combine_word(p1 + 4);
-                       term->channels = p1[7];
-                       term->chconfig = combine_word(p1 + 8);
-                       term->name = p1[11];
+                       if (state->mixer->protocol == UAC_VERSION_1) {
+                               struct uac_input_terminal_descriptor *d = p1;
+                               term->type = le16_to_cpu(d->wTerminalType);
+                               term->channels = d->bNrChannels;
+                               term->chconfig = le16_to_cpu(d->wChannelConfig);
+                               term->name = d->iTerminal;
+                       } else { /* UAC_VERSION_2 */
+                               struct uac2_input_terminal_descriptor *d = p1;
+                               term->type = le16_to_cpu(d->wTerminalType);
+                               term->channels = d->bNrChannels;
+                               term->chconfig = le32_to_cpu(d->bmChannelConfig);
+                               term->name = d->iTerminal;
+                       }
                        return 0;
-               case UAC_FEATURE_UNIT:
-                       id = p1[4];
+               case UAC_FEATURE_UNIT: {
+                       /* the header is the same for v1 and v2 */
+                       struct uac_feature_unit_descriptor *d = p1;
+                       id = d->bUnitID;
                        break; /* continue to parse */
-               case UAC_MIXER_UNIT:
-                       term->type = p1[2] << 16; /* virtual type */
-                       term->channels = p1[5 + p1[4]];
-                       term->chconfig = combine_word(p1 + 6 + p1[4]);
-                       term->name = p1[p1[0] - 1];
+               }
+               case UAC_MIXER_UNIT: {
+                       struct uac_mixer_unit_descriptor *d = p1;
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
+                       term->channels = uac_mixer_unit_bNrChannels(d);
+                       term->chconfig = uac_mixer_unit_wChannelConfig(d, state->mixer->protocol);
+                       term->name = uac_mixer_unit_iMixer(d);
                        return 0;
-               case UAC_SELECTOR_UNIT:
+               }
+               case UAC_SELECTOR_UNIT: {
+                       struct uac_selector_unit_descriptor *d = p1;
                        /* call recursively to retrieve the channel info */
-                       if (check_input_term(state, p1[5], term) < 0)
+                       if (check_input_term(state, d->baSourceID[0], term) < 0)
                                return -ENODEV;
-                       term->type = p1[2] << 16; /* virtual type */
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
                        term->id = id;
-                       term->name = p1[9 + p1[0] - 1];
+                       term->name = uac_selector_unit_iSelector(d);
                        return 0;
+               }
                case UAC_PROCESSING_UNIT_V1:
-               case UAC_EXTENSION_UNIT_V1:
-                       if (p1[6] == 1) {
-                               id = p1[7];
+               case UAC_EXTENSION_UNIT_V1: {
+                       struct uac_processing_unit_descriptor *d = p1;
+                       if (d->bNrInPins) {
+                               id = d->baSourceID[0];
                                break; /* continue to parse */
                        }
-                       term->type = p1[2] << 16; /* virtual type */
-                       term->channels = p1[7 + p1[6]];
-                       term->chconfig = combine_word(p1 + 8 + p1[6]);
-                       term->name = p1[12 + p1[6] + p1[11 + p1[6]]];
+                       term->type = d->bDescriptorSubtype << 16; /* virtual type */
+                       term->channels = uac_processing_unit_bNrChannels(d);
+                       term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol);
+                       term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol);
                        return 0;
+               }
                default:
                        return -ENODEV;
                }
@@ -764,7 +770,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
                        int last_valid_res = cval->res;
 
                        while (cval->res > 1) {
-                               if (set_ctl_value(cval, UAC_SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0)
+                               if (snd_usb_mixer_set_ctl_value(cval, UAC_SET_RES,
+                                                               (cval->control << 8) | minchn, cval->res / 2) < 0)
                                        break;
                                cval->res /= 2;
                        }
@@ -929,6 +936,15 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = {
        .put = mixer_ctl_feature_put,
 };
 
+/* the read-only variant */
+static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "", /* will be filled later manually */
+       .info = mixer_ctl_feature_info,
+       .get = mixer_ctl_feature_get,
+       .put = NULL,
+};
+
 
 /*
  * build a feature control
@@ -939,20 +955,22 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str)
        return strlcat(kctl->id.name, str, sizeof(kctl->id.name));
 }
 
-static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                              unsigned int ctl_mask, int control,
-                             struct usb_audio_term *iterm, int unitid)
+                             struct usb_audio_term *iterm, int unitid,
+                             int read_only)
 {
+       struct uac_feature_unit_descriptor *desc = raw_desc;
        unsigned int len = 0;
        int mapped_name = 0;
-       int nameid = desc[desc[0] - 1];
+       int nameid = uac_feature_unit_iFeature(desc);
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
        const struct usbmix_name_map *map;
 
        control++; /* change from zero-based to 1-based value */
 
-       if (control == USB_FEATURE_GEQ) {
+       if (control == UAC_GRAPHIC_EQUALIZER_CONTROL) {
                /* FIXME: not supported yet */
                return;
        }
@@ -984,7 +1002,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
        /* get min/max values */
        get_min_max(cval, 0);
 
-       kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+       if (read_only)
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl_ro, cval);
+       else
+               kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+
        if (! kctl) {
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                kfree(cval);
@@ -999,8 +1021,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                                kctl->id.name, sizeof(kctl->id.name));
 
        switch (control) {
-       case USB_FEATURE_MUTE:
-       case USB_FEATURE_VOLUME:
+       case UAC_MUTE_CONTROL:
+       case UAC_VOLUME_CONTROL:
                /* determine the control name.  the rule is:
                 * - if a name id is given in descriptor, use it.
                 * - if the connected input can be determined, then use the name
@@ -1027,9 +1049,9 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                                len = append_ctl_name(kctl, " Playback");
                        }
                }
-               append_ctl_name(kctl, control == USB_FEATURE_MUTE ?
+               append_ctl_name(kctl, control == UAC_MUTE_CONTROL ?
                                " Switch" : " Volume");
-               if (control == USB_FEATURE_VOLUME) {
+               if (control == UAC_VOLUME_CONTROL) {
                        kctl->tlv.c = mixer_vol_tlv;
                        kctl->vd[0].access |= 
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ |
@@ -1094,49 +1116,92 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
        struct usb_audio_term iterm;
        unsigned int master_bits, first_ch_bits;
        int err, csize;
-       struct uac_feature_unit_descriptor *ftr = _ftr;
+       struct uac_feature_unit_descriptor *hdr = _ftr;
+       __u8 *bmaControls;
+
+       if (state->mixer->protocol == UAC_VERSION_1) {
+               csize = hdr->bControlSize;
+               channels = (hdr->bLength - 7) / csize - 1;
+               bmaControls = hdr->bmaControls;
+       } else {
+               struct uac2_feature_unit_descriptor *ftr = _ftr;
+               csize = 4;
+               channels = (hdr->bLength - 6) / 4;
+               bmaControls = ftr->bmaControls;
+       }
 
-       if (ftr->bLength < 7 || ! (csize = ftr->bControlSize) || ftr->bLength < 7 + csize) {
+       if (hdr->bLength < 7 || !csize || hdr->bLength < 7 + csize) {
                snd_printk(KERN_ERR "usbaudio: unit %u: invalid UAC_FEATURE_UNIT descriptor\n", unitid);
                return -EINVAL;
        }
 
        /* parse the source unit */
-       if ((err = parse_audio_unit(state, ftr->bSourceID)) < 0)
+       if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0)
                return err;
 
        /* determine the input source type and name */
-       if (check_input_term(state, ftr->bSourceID, &iterm) < 0)
+       if (check_input_term(state, hdr->bSourceID, &iterm) < 0)
                return -EINVAL;
 
-       channels = (ftr->bLength - 7) / csize - 1;
-
-       master_bits = snd_usb_combine_bytes(ftr->controls, csize);
+       master_bits = snd_usb_combine_bytes(bmaControls, csize);
        /* master configuration quirks */
        switch (state->chip->usb_id) {
        case USB_ID(0x08bb, 0x2702):
                snd_printk(KERN_INFO
                           "usbmixer: master volume quirk for PCM2702 chip\n");
                /* disable non-functional volume control */
-               master_bits &= ~(1 << (USB_FEATURE_VOLUME - 1));
+               master_bits &= ~UAC_FU_VOLUME;
                break;
        }
        if (channels > 0)
-               first_ch_bits = snd_usb_combine_bytes(ftr->controls + csize, csize);
+               first_ch_bits = snd_usb_combine_bytes(bmaControls + csize, csize);
        else
                first_ch_bits = 0;
-       /* check all control types */
-       for (i = 0; i < 10; i++) {
-               unsigned int ch_bits = 0;
-               for (j = 0; j < channels; j++) {
-                       unsigned int mask = snd_usb_combine_bytes(ftr->controls + csize * (j+1), csize);
-                       if (mask & (1 << i))
-                               ch_bits |= (1 << j);
+
+       if (state->mixer->protocol == UAC_VERSION_1) {
+               /* check all control types */
+               for (i = 0; i < 10; i++) {
+                       unsigned int ch_bits = 0;
+                       for (j = 0; j < channels; j++) {
+                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               if (mask & (1 << i))
+                                       ch_bits |= (1 << j);
+                       }
+                       /* audio class v1 controls are never read-only */
+                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, 0);
+                       if (master_bits & (1 << i))
+                               build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
+               }
+       } else { /* UAC_VERSION_2 */
+               for (i = 0; i < 30/2; i++) {
+                       /* From the USB Audio spec v2.0:
+                          bmaControls() is a (ch+1)-element array of 4-byte bitmaps,
+                          each containing a set of bit pairs. If a Control is present,
+                          it must be Host readable. If a certain Control is not
+                          present then the bit pair must be set to 0b00.
+                          If a Control is present but read-only, the bit pair must be
+                          set to 0b01. If a Control is also Host programmable, the bit
+                          pair must be set to 0b11. The value 0b10 is not allowed. */
+                       unsigned int ch_bits = 0;
+                       unsigned int ch_read_only = 0;
+
+                       for (j = 0; j < channels; j++) {
+                               unsigned int mask = snd_usb_combine_bytes(bmaControls + csize * (j+1), csize);
+                               if (mask & (1 << (i * 2))) {
+                                       ch_bits |= (1 << j);
+                                       if (~mask & (1 << ((i * 2) + 1)))
+                                               ch_read_only |= (1 << j);
+                               }
+                       }
+
+                       /* FIXME: the whole unit is read-only if any of the channels is marked read-only */
+                       if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+                               build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid, !!ch_read_only);
+                       if (master_bits & (1 << i * 2))
+                               build_feature_ctl(state, _ftr, 0, i, &iterm, unitid,
+                                                 ~master_bits & (1 << ((i * 2) + 1)));
                }
-               if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
-                       build_feature_ctl(state, _ftr, ch_bits, i, &iterm, unitid);
-               if (master_bits & (1 << i))
-                       build_feature_ctl(state, _ftr, 0, i, &iterm, unitid);
        }
 
        return 0;
@@ -1154,13 +1219,13 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
  * input channel number (zero based) is given in control field instead.
  */
 
-static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
+static void build_mixer_unit_ctl(struct mixer_build *state,
+                                struct uac_mixer_unit_descriptor *desc,
                                 int in_pin, int in_ch, int unitid,
                                 struct usb_audio_term *iterm)
 {
        struct usb_mixer_elem_info *cval;
-       unsigned int input_pins = desc[4];
-       unsigned int num_outs = desc[5 + input_pins];
+       unsigned int num_outs = uac_mixer_unit_bNrChannels(desc);
        unsigned int i, len;
        struct snd_kcontrol *kctl;
        const struct usbmix_name_map *map;
@@ -1178,7 +1243,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
        cval->control = in_ch + 1; /* based on 1 */
        cval->val_type = USB_MIXER_S16;
        for (i = 0; i < num_outs; i++) {
-               if (check_matrix_bitmap(desc + 9 + input_pins, in_ch, i, num_outs)) {
+               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol), in_ch, i, num_outs)) {
                        cval->cmask |= (1 << i);
                        cval->channels++;
                }
@@ -1211,18 +1276,19 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
 /*
  * parse a mixer unit
  */
-static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
+       struct uac_mixer_unit_descriptor *desc = raw_desc;
        struct usb_audio_term iterm;
        int input_pins, num_ins, num_outs;
        int pin, ich, err;
 
-       if (desc[0] < 11 || ! (input_pins = desc[4]) || ! (num_outs = desc[5 + input_pins])) {
+       if (desc->bLength < 11 || ! (input_pins = desc->bNrInPins) || ! (num_outs = uac_mixer_unit_bNrChannels(desc))) {
                snd_printk(KERN_ERR "invalid MIXER UNIT descriptor %d\n", unitid);
                return -EINVAL;
        }
        /* no bmControls field (e.g. Maya44) -> ignore */
-       if (desc[0] <= 10 + input_pins) {
+       if (desc->bLength <= 10 + input_pins) {
                snd_printdd(KERN_INFO "MU %d has no bmControls field\n", unitid);
                return 0;
        }
@@ -1230,10 +1296,10 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
        num_ins = 0;
        ich = 0;
        for (pin = 0; pin < input_pins; pin++) {
-               err = parse_audio_unit(state, desc[5 + pin]);
+               err = parse_audio_unit(state, desc->baSourceID[pin]);
                if (err < 0)
                        return err;
-               err = check_input_term(state, desc[5 + pin], &iterm);
+               err = check_input_term(state, desc->baSourceID[pin], &iterm);
                if (err < 0)
                        return err;
                num_ins += iterm.channels;
@@ -1241,7 +1307,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, unsigne
                        int och, ich_has_controls = 0;
 
                        for (och = 0; och < num_outs; ++och) {
-                               if (check_matrix_bitmap(desc + 9 + input_pins,
+                               if (check_matrix_bitmap(uac_mixer_unit_bmControls(desc, state->mixer->protocol),
                                                        ich, och, num_outs)) {
                                        ich_has_controls = 1;
                                        break;
@@ -1402,9 +1468,10 @@ static struct procunit_info extunits[] = {
 /*
  * build a processing/extension unit
  */
-static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name)
+static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, char *name)
 {
-       int num_ins = dsc[6];
+       struct uac_processing_unit_descriptor *desc = raw_desc;
+       int num_ins = desc->bNrInPins;
        struct usb_mixer_elem_info *cval;
        struct snd_kcontrol *kctl;
        int i, err, nameid, type, len;
@@ -1419,17 +1486,18 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                0, NULL, default_value_info
        };
 
-       if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) {
+       if (desc->bLength < 13 || desc->bLength < 13 + num_ins ||
+           desc->bLength < num_ins + uac_processing_unit_bControlSize(desc, state->mixer->protocol)) {
                snd_printk(KERN_ERR "invalid %s descriptor (id %d)\n", name, unitid);
                return -EINVAL;
        }
 
        for (i = 0; i < num_ins; i++) {
-               if ((err = parse_audio_unit(state, dsc[7 + i])) < 0)
+               if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
                        return err;
        }
 
-       type = combine_word(&dsc[4]);
+       type = le16_to_cpu(desc->wProcessType);
        for (info = list; info && info->type; info++)
                if (info->type == type)
                        break;
@@ -1437,8 +1505,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                info = &default_info;
 
        for (valinfo = info->values; valinfo->control; valinfo++) {
-               /* FIXME: bitmap might be longer than 8bit */
-               if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
+               __u8 *controls = uac_processing_unit_bmControls(desc, state->mixer->protocol);
+
+               if (! (controls[valinfo->control / 8] & (1 << ((valinfo->control % 8) - 1))))
                        continue;
                map = find_map(state, unitid, valinfo->control);
                if (check_ignored_ctl(map))
@@ -1456,9 +1525,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 
                /* get min/max values */
                if (type == USB_PROC_UPDOWN && cval->control == USB_PROC_UPDOWN_MODE_SEL) {
+                       __u8 *control_spec = uac_processing_unit_specific(desc, state->mixer->protocol);
                        /* FIXME: hard-coded */
                        cval->min = 1;
-                       cval->max = dsc[15];
+                       cval->max = control_spec[0];
                        cval->res = 1;
                        cval->initialized = 1;
                } else {
@@ -1488,7 +1558,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                else if (info->name)
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
                else {
-                       nameid = dsc[12 + num_ins + dsc[11 + num_ins]];
+                       nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
                        len = 0;
                        if (nameid)
                                len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
@@ -1507,14 +1577,16 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
 }
 
 
-static int parse_audio_processing_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_processing_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit");
+       return build_audio_procunit(state, unitid, raw_desc, procunits, "Processing Unit");
 }
 
-static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_extension_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
+       /* Note that we parse extension units with processing unit descriptors.
+        * That's ok as the layout is the same */
+       return build_audio_procunit(state, unitid, raw_desc, extunits, "Extension Unit");
 }
 
 
@@ -1616,9 +1688,9 @@ static void usb_mixer_selector_elem_free(struct snd_kcontrol *kctl)
 /*
  * parse a selector unit
  */
-static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsigned char *desc)
+static int parse_audio_selector_unit(struct mixer_build *state, int unitid, void *raw_desc)
 {
-       unsigned int num_ins = desc[4];
+       struct uac_selector_unit_descriptor *desc = raw_desc;
        unsigned int i, nameid, len;
        int err;
        struct usb_mixer_elem_info *cval;
@@ -1626,17 +1698,17 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        const struct usbmix_name_map *map;
        char **namelist;
 
-       if (! num_ins || desc[0] < 5 + num_ins) {
+       if (!desc->bNrInPins || desc->bLength < 5 + desc->bNrInPins) {
                snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid);
                return -EINVAL;
        }
 
-       for (i = 0; i < num_ins; i++) {
-               if ((err = parse_audio_unit(state, desc[5 + i])) < 0)
+       for (i = 0; i < desc->bNrInPins; i++) {
+               if ((err = parse_audio_unit(state, desc->baSourceID[i])) < 0)
                        return err;
        }
 
-       if (num_ins == 1) /* only one ? nonsense! */
+       if (desc->bNrInPins == 1) /* only one ? nonsense! */
                return 0;
 
        map = find_map(state, unitid, 0);
@@ -1653,18 +1725,18 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        cval->val_type = USB_MIXER_U8;
        cval->channels = 1;
        cval->min = 1;
-       cval->max = num_ins;
+       cval->max = desc->bNrInPins;
        cval->res = 1;
        cval->initialized = 1;
 
-       namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL);
+       namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
        if (! namelist) {
                snd_printk(KERN_ERR "cannot malloc\n");
                kfree(cval);
                return -ENOMEM;
        }
 #define MAX_ITEM_NAME_LEN      64
-       for (i = 0; i < num_ins; i++) {
+       for (i = 0; i < desc->bNrInPins; i++) {
                struct usb_audio_term iterm;
                len = 0;
                namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
@@ -1678,7 +1750,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
                }
                len = check_mapped_selector_name(state, unitid, i, namelist[i],
                                                 MAX_ITEM_NAME_LEN);
-               if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0)
+               if (! len && check_input_term(state, desc->baSourceID[i], &iterm) >= 0)
                        len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
                if (! len)
                        sprintf(namelist[i], "Input %d", i);
@@ -1694,7 +1766,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        kctl->private_value = (unsigned long)namelist;
        kctl->private_free = usb_mixer_selector_elem_free;
 
-       nameid = desc[desc[0] - 1];
+       nameid = uac_selector_unit_iSelector(desc);
        len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (len)
                ;
@@ -1713,7 +1785,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        }
 
        snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
-                   cval->id, kctl->id.name, num_ins);
+                   cval->id, kctl->id.name, desc->bNrInPins);
        if ((err = add_control_to_empty(state, kctl)) < 0)
                return err;
 
@@ -1748,9 +1820,17 @@ static int parse_audio_unit(struct mixer_build *state, int unitid)
        case UAC_FEATURE_UNIT:
                return parse_audio_feature_unit(state, unitid, p1);
        case UAC_PROCESSING_UNIT_V1:
-               return parse_audio_processing_unit(state, unitid, p1);
+       /*   UAC2_EFFECT_UNIT has the same value */
+               if (state->mixer->protocol == UAC_VERSION_1)
+                       return parse_audio_processing_unit(state, unitid, p1);
+               else
+                       return 0; /* FIXME - effect units not implemented yet */
        case UAC_EXTENSION_UNIT_V1:
-               return parse_audio_extension_unit(state, unitid, p1);
+       /*   UAC2_PROCESSING_UNIT_V2 has the same value */
+               if (state->mixer->protocol == UAC_VERSION_1)
+                       return parse_audio_extension_unit(state, unitid, p1);
+               else /* UAC_VERSION_2 */
+                       return parse_audio_processing_unit(state, unitid, p1);
        default:
                snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
                return -EINVAL;
@@ -1783,11 +1863,11 @@ static int snd_usb_mixer_dev_free(struct snd_device *device)
  */
 static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 {
-       struct uac_output_terminal_descriptor_v1 *desc;
        struct mixer_build state;
        int err;
        const struct usbmix_ctl_map *map;
        struct usb_host_interface *hostif;
+       void *p;
 
        hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
        memset(&state, 0, sizeof(state));
@@ -1806,23 +1886,39 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
                }
        }
 
-       desc = NULL;
-       while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, UAC_OUTPUT_TERMINAL)) != NULL) {
-               if (desc->bLength < 9)
-                       continue; /* invalid descriptor? */
-               set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
-               state.oterm.id = desc->bTerminalID;
-               state.oterm.type = le16_to_cpu(desc->wTerminalType);
-               state.oterm.name = desc->iTerminal;
-               err = parse_audio_unit(&state, desc->bSourceID);
-               if (err < 0)
-                       return err;
+       p = NULL;
+       while ((p = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, p, UAC_OUTPUT_TERMINAL)) != NULL) {
+               if (mixer->protocol == UAC_VERSION_1) {
+                       struct uac_output_terminal_descriptor_v1 *desc = p;
+
+                       if (desc->bLength < sizeof(*desc))
+                               continue; /* invalid descriptor? */
+                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       state.oterm.id = desc->bTerminalID;
+                       state.oterm.type = le16_to_cpu(desc->wTerminalType);
+                       state.oterm.name = desc->iTerminal;
+                       err = parse_audio_unit(&state, desc->bSourceID);
+                       if (err < 0)
+                               return err;
+               } else { /* UAC_VERSION_2 */
+                       struct uac2_output_terminal_descriptor *desc = p;
+
+                       if (desc->bLength < sizeof(*desc))
+                               continue; /* invalid descriptor? */
+                       set_bit(desc->bTerminalID, state.unitbitmap);  /* mark terminal ID as visited */
+                       state.oterm.id = desc->bTerminalID;
+                       state.oterm.type = le16_to_cpu(desc->wTerminalType);
+                       state.oterm.name = desc->iTerminal;
+                       err = parse_audio_unit(&state, desc->bSourceID);
+                       if (err < 0)
+                               return err;
+               }
        }
+
        return 0;
 }
 
-static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
-                                   int unitid)
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid)
 {
        struct usb_mixer_elem_info *info;
 
@@ -1871,34 +1967,6 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
        }
 }
 
-static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
-                                       int unitid)
-{
-       if (!mixer->rc_cfg)
-               return;
-       /* unit ids specific to Extigy/Audigy 2 NX: */
-       switch (unitid) {
-       case 0: /* remote control */
-               mixer->rc_urb->dev = mixer->chip->dev;
-               usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
-               break;
-       case 4: /* digital in jack */
-       case 7: /* line in jacks */
-       case 19: /* speaker out jacks */
-       case 20: /* headphones out jack */
-               break;
-       /* live24ext: 4 = line-in jack */
-       case 3: /* hp-out jack (may actuate Mute) */
-               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                   mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-                       snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
-               break;
-       default:
-               snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
-               break;
-       }
-}
-
 static void snd_usb_mixer_status_complete(struct urb *urb)
 {
        struct usb_mixer_interface *mixer = urb->context;
@@ -1916,7 +1984,7 @@ static void snd_usb_mixer_status_complete(struct urb *urb)
                        if (!(buf[0] & 0x40))
                                snd_usb_mixer_notify_id(mixer, buf[1]);
                        else
-                               snd_usb_mixer_memory_change(mixer, buf[1]);
+                               snd_usb_mixer_rc_memory_change(mixer, buf[1]);
                }
        }
        if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
@@ -1960,296 +2028,6 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
        return 0;
 }
 
-static void snd_usb_soundblaster_remote_complete(struct urb *urb)
-{
-       struct usb_mixer_interface *mixer = urb->context;
-       const struct rc_config *rc = mixer->rc_cfg;
-       u32 code;
-
-       if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
-               return;
-
-       code = mixer->rc_buffer[rc->offset];
-       if (rc->length == 2)
-               code |= mixer->rc_buffer[rc->offset + 1] << 8;
-
-       /* the Mute button actually changes the mixer control */
-       if (code == rc->mute_code)
-               snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
-       mixer->rc_code = code;
-       wmb();
-       wake_up(&mixer->rc_waitq);
-}
-
-static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
-                                    long count, loff_t *offset)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-       int err;
-       u32 rc_code;
-
-       if (count != 1 && count != 4)
-               return -EINVAL;
-       err = wait_event_interruptible(mixer->rc_waitq,
-                                      (rc_code = xchg(&mixer->rc_code, 0)) != 0);
-       if (err == 0) {
-               if (count == 1)
-                       err = put_user(rc_code, buf);
-               else
-                       err = put_user(rc_code, (u32 __user *)buf);
-       }
-       return err < 0 ? err : count;
-}
-
-static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
-                                           poll_table *wait)
-{
-       struct usb_mixer_interface *mixer = hw->private_data;
-
-       poll_wait(file, &mixer->rc_waitq, wait);
-       return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
-}
-
-static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
-{
-       struct snd_hwdep *hwdep;
-       int err, len, i;
-
-       for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
-               if (rc_configs[i].usb_id == mixer->chip->usb_id)
-                       break;
-       if (i >= ARRAY_SIZE(rc_configs))
-               return 0;
-       mixer->rc_cfg = &rc_configs[i];
-
-       len = mixer->rc_cfg->packet_length;
-       
-       init_waitqueue_head(&mixer->rc_waitq);
-       err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
-       if (err < 0)
-               return err;
-       snprintf(hwdep->name, sizeof(hwdep->name),
-                "%s remote control", mixer->chip->card->shortname);
-       hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
-       hwdep->private_data = mixer;
-       hwdep->ops.read = snd_usb_sbrc_hwdep_read;
-       hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
-       hwdep->exclusive = 1;
-
-       mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!mixer->rc_urb)
-               return -ENOMEM;
-       mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
-       if (!mixer->rc_setup_packet) {
-               usb_free_urb(mixer->rc_urb);
-               mixer->rc_urb = NULL;
-               return -ENOMEM;
-       }
-       mixer->rc_setup_packet->bRequestType =
-               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
-       mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
-       mixer->rc_setup_packet->wValue = cpu_to_le16(0);
-       mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
-       mixer->rc_setup_packet->wLength = cpu_to_le16(len);
-       usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
-                            usb_rcvctrlpipe(mixer->chip->dev, 0),
-                            (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
-                            snd_usb_soundblaster_remote_complete, mixer);
-       return 0;
-}
-
-#define snd_audigy2nx_led_info         snd_ctl_boolean_mono_info
-
-static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-
-       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
-       return 0;
-}
-
-static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       int index = kcontrol->private_value;
-       int value = ucontrol->value.integer.value[0];
-       int err, changed;
-
-       if (value > 1)
-               return -EINVAL;
-       changed = value != mixer->audigy2nx_leds[index];
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                             value, index + 2, NULL, 0, 100);
-       if (err < 0)
-               return err;
-       mixer->audigy2nx_leds[index] = value;
-       return changed;
-}
-
-static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "CMSS LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 0,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Power LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 1,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Dolby Digital LED Switch",
-               .info = snd_audigy2nx_led_info,
-               .get = snd_audigy2nx_led_get,
-               .put = snd_audigy2nx_led_put,
-               .private_value = 2,
-       },
-};
-
-static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
-{
-       int i, err;
-
-       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
-               if (i > 1 && /* Live24ext has 2 LEDs only */
-                       (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                        mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
-                       break; 
-               err = snd_ctl_add(mixer->chip->card,
-                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
-               if (err < 0)
-                       return err;
-       }
-       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
-       return 0;
-}
-
-static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
-                                   struct snd_info_buffer *buffer)
-{
-       static const struct sb_jack {
-               int unitid;
-               const char *name;
-       }  jacks_audigy2nx[] = {
-               {4,  "dig in "},
-               {7,  "line in"},
-               {19, "spk out"},
-               {20, "hph out"},
-               {-1, NULL}
-       }, jacks_live24ext[] = {
-               {4,  "line in"}, /* &1=Line, &2=Mic*/
-               {3,  "hph out"}, /* headphones */
-               {0,  "RC     "}, /* last command, 6 bytes see rc_config above */
-               {-1, NULL}
-       };
-       const struct sb_jack *jacks;
-       struct usb_mixer_interface *mixer = entry->private_data;
-       int i, err;
-       u8 buf[3];
-
-       snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
-               jacks = jacks_audigy2nx;
-       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-                mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
-               jacks = jacks_live24ext;
-       else
-               return;
-
-       for (i = 0; jacks[i].name; ++i) {
-               snd_iprintf(buffer, "%s: ", jacks[i].name);
-               err = snd_usb_ctl_msg(mixer->chip->dev,
-                                     usb_rcvctrlpipe(mixer->chip->dev, 0),
-                                     UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
-                                     USB_RECIP_INTERFACE, 0,
-                                     jacks[i].unitid << 8, buf, 3, 100);
-               if (err == 3 && (buf[0] == 3 || buf[0] == 6))
-                       snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
-               else
-                       snd_iprintf(buffer, "?\n");
-       }
-}
-
-static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-
-       ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
-       return 0;
-}
-
-static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
-       u8 old_status, new_status;
-       int err, changed;
-
-       old_status = mixer->xonar_u1_status;
-       if (ucontrol->value.integer.value[0])
-               new_status = old_status | 0x02;
-       else
-               new_status = old_status & ~0x02;
-       changed = new_status != old_status;
-       err = snd_usb_ctl_msg(mixer->chip->dev,
-                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
-                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                             50, 0, &new_status, 1, 100);
-       if (err < 0)
-               return err;
-       mixer->xonar_u1_status = new_status;
-       return changed;
-}
-
-static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "Digital Playback Switch",
-       .info = snd_ctl_boolean_mono_info,
-       .get = snd_xonar_u1_switch_get,
-       .put = snd_xonar_u1_switch_put,
-};
-
-static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
-{
-       int err;
-
-       err = snd_ctl_add(mixer->chip->card,
-                         snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
-       if (err < 0)
-               return err;
-       mixer->xonar_u1_status = 0x05;
-       return 0;
-}
-
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-                              unsigned char samplerate_id)
-{
-       struct usb_mixer_interface *mixer;
-       struct usb_mixer_elem_info *cval;
-       int unitid = 12; /* SamleRate ExtensionUnit ID */
-
-       list_for_each_entry(mixer, &chip->mixer_list, list) {
-               cval = mixer->id_elems[unitid];
-               if (cval) {
-                       set_cur_ctl_value(cval, cval->control << 8,
-                                         samplerate_id);
-                       snd_usb_mixer_notify_id(mixer, unitid);
-               }
-               break;
-       }
-}
-
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                         int ignore_error)
 {
@@ -2259,7 +2037,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        struct usb_mixer_interface *mixer;
        struct snd_info_entry *entry;
        struct usb_host_interface *host_iface;
-       int err, protocol;
+       int err;
 
        strcpy(chip->card->mixername, "USB Mixer");
 
@@ -2277,38 +2055,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        }
 
        host_iface = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
-       protocol = host_iface->desc.bInterfaceProtocol;
-
-       /* FIXME! */
-       if (protocol != UAC_VERSION_1) {
-               snd_printk(KERN_WARNING "mixer interface protocol 0x%02x not yet supported\n",
-                                       protocol);
-               return 0;
-       }
+       mixer->protocol = host_iface->desc.bInterfaceProtocol;
 
        if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
            (err = snd_usb_mixer_status_create(mixer)) < 0)
                goto _error;
 
-       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
-               goto _error;
-
-       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
-           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
-               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
-                       goto _error;
-               if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
-                       snd_info_set_text_ops(entry, mixer,
-                                             snd_audigy2nx_proc_read);
-       }
-
-       if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
-           mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
-               err = snd_xonar_u1_controls_create(mixer);
-               if (err < 0)
-                       goto _error;
-       }
+       snd_usb_mixer_apply_create_quirk(mixer);
 
        err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
        if (err < 0)
@@ -2329,7 +2082,7 @@ _error:
 void snd_usb_mixer_disconnect(struct list_head *p)
 {
        struct usb_mixer_interface *mixer;
-       
+
        mixer = list_entry(p, struct usb_mixer_interface, list);
        usb_kill_urb(mixer->urb);
        usb_kill_urb(mixer->rc_urb);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
new file mode 100644 (file)
index 0000000..1301238
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef __USBMIXER_H
+#define __USBMIXER_H
+
+struct usb_mixer_interface {
+       struct snd_usb_audio *chip;
+       unsigned int ctrlif;
+       struct list_head list;
+       unsigned int ignore_ctl_error;
+       struct urb *urb;
+       /* array[MAX_ID_ELEMS], indexed by unit id */
+       struct usb_mixer_elem_info **id_elems;
+
+       /* the usb audio specification version this interface complies to */
+       int protocol;
+
+       /* Sound Blaster remote control stuff */
+       const struct rc_config *rc_cfg;
+       u32 rc_code;
+       wait_queue_head_t rc_waitq;
+       struct urb *rc_urb;
+       struct usb_ctrlrequest *rc_setup_packet;
+       u8 rc_buffer[6];
+
+       u8 audigy2nx_leds[3];
+       u8 xonar_u1_status;
+};
+
+#define MAX_CHANNELS   10      /* max logical channels */
+
+struct usb_mixer_elem_info {
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */
+       struct snd_ctl_elem_id *elem_id;
+       unsigned int id;
+       unsigned int control;   /* CS or ICN (high byte) */
+       unsigned int cmask; /* channel mask bitmap: 0 = master */
+       int channels;
+       int val_type;
+       int min, max, res;
+       int dBmin, dBmax;
+       int cached;
+       int cache_val[MAX_CHANNELS];
+       u8 initialized;
+};
+
+int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
+                        int ignore_error);
+void snd_usb_mixer_disconnect(struct list_head *p);
+
+void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid);
+
+int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
+                               int request, int validx, int value_set);
+
+#endif /* __USBMIXER_H */
similarity index 98%
rename from sound/usb/usbmixer_maps.c
rename to sound/usb/mixer_maps.c
index 79e903a..d93fc89 100644 (file)
@@ -85,8 +85,8 @@ static struct usbmix_name_map extigy_map[] = {
        /* 16: MU (w/o controls) */
        { 17, NULL, 1 }, /* DISABLED: PU-switch (any effect?) */
        { 17, "Channel Routing", 2 },   /* PU: mode select */
-       { 18, "Tone Control - Bass", USB_FEATURE_BASS }, /* FU */
-       { 18, "Tone Control - Treble", USB_FEATURE_TREBLE }, /* FU */
+       { 18, "Tone Control - Bass", UAC_BASS_CONTROL }, /* FU */
+       { 18, "Tone Control - Treble", UAC_TREBLE_CONTROL }, /* FU */
        { 18, "Master Playback" }, /* FU; others */
        /* 19: OT speaker */
        /* 20: OT headphone */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
new file mode 100644 (file)
index 0000000..e7df1e5
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ *   USB Audio Driver for ALSA
+ *
+ *   Quirks and vendor-specific extensions for mixer interfaces
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by
+ *         Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *         Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
+
+#include "usbaudio.h"
+#include "mixer.h"
+#include "mixer_quirks.h"
+#include "helper.h"
+
+/*
+ * Sound Blaster remote control configuration
+ *
+ * format of remote control data:
+ * Extigy:       xx 00
+ * Audigy 2 NX:  06 80 xx 00 00 00
+ * Live! 24-bit: 06 80 xx yy 22 83
+ */
+static const struct rc_config {
+       u32 usb_id;
+       u8  offset;
+       u8  length;
+       u8  packet_length;
+       u8  min_packet_length; /* minimum accepted length of the URB result */
+       u8  mute_mixer_id;
+       u32 mute_code;
+} rc_configs[] = {
+       { USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */
+       { USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */
+       { USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */
+       { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
+};
+
+static void snd_usb_soundblaster_remote_complete(struct urb *urb)
+{
+       struct usb_mixer_interface *mixer = urb->context;
+       const struct rc_config *rc = mixer->rc_cfg;
+       u32 code;
+
+       if (urb->status < 0 || urb->actual_length < rc->min_packet_length)
+               return;
+
+       code = mixer->rc_buffer[rc->offset];
+       if (rc->length == 2)
+               code |= mixer->rc_buffer[rc->offset + 1] << 8;
+
+       /* the Mute button actually changes the mixer control */
+       if (code == rc->mute_code)
+               snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id);
+       mixer->rc_code = code;
+       wmb();
+       wake_up(&mixer->rc_waitq);
+}
+
+static long snd_usb_sbrc_hwdep_read(struct snd_hwdep *hw, char __user *buf,
+                                    long count, loff_t *offset)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+       int err;
+       u32 rc_code;
+
+       if (count != 1 && count != 4)
+               return -EINVAL;
+       err = wait_event_interruptible(mixer->rc_waitq,
+                                      (rc_code = xchg(&mixer->rc_code, 0)) != 0);
+       if (err == 0) {
+               if (count == 1)
+                       err = put_user(rc_code, buf);
+               else
+                       err = put_user(rc_code, (u32 __user *)buf);
+       }
+       return err < 0 ? err : count;
+}
+
+static unsigned int snd_usb_sbrc_hwdep_poll(struct snd_hwdep *hw, struct file *file,
+                                           poll_table *wait)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+
+       poll_wait(file, &mixer->rc_waitq, wait);
+       return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
+}
+
+static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
+{
+       struct snd_hwdep *hwdep;
+       int err, len, i;
+
+       for (i = 0; i < ARRAY_SIZE(rc_configs); ++i)
+               if (rc_configs[i].usb_id == mixer->chip->usb_id)
+                       break;
+       if (i >= ARRAY_SIZE(rc_configs))
+               return 0;
+       mixer->rc_cfg = &rc_configs[i];
+
+       len = mixer->rc_cfg->packet_length;
+
+       init_waitqueue_head(&mixer->rc_waitq);
+       err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
+       if (err < 0)
+               return err;
+       snprintf(hwdep->name, sizeof(hwdep->name),
+                "%s remote control", mixer->chip->card->shortname);
+       hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
+       hwdep->private_data = mixer;
+       hwdep->ops.read = snd_usb_sbrc_hwdep_read;
+       hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+       hwdep->exclusive = 1;
+
+       mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!mixer->rc_urb)
+               return -ENOMEM;
+       mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
+       if (!mixer->rc_setup_packet) {
+               usb_free_urb(mixer->rc_urb);
+               mixer->rc_urb = NULL;
+               return -ENOMEM;
+       }
+       mixer->rc_setup_packet->bRequestType =
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+       mixer->rc_setup_packet->bRequest = UAC_GET_MEM;
+       mixer->rc_setup_packet->wValue = cpu_to_le16(0);
+       mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
+       mixer->rc_setup_packet->wLength = cpu_to_le16(len);
+       usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
+                            usb_rcvctrlpipe(mixer->chip->dev, 0),
+                            (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+                            snd_usb_soundblaster_remote_complete, mixer);
+       return 0;
+}
+
+#define snd_audigy2nx_led_info         snd_ctl_boolean_mono_info
+
+static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+       return 0;
+}
+
+static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+       int value = ucontrol->value.integer.value[0];
+       int err, changed;
+
+       if (value > 1)
+               return -EINVAL;
+       changed = value != mixer->audigy2nx_leds[index];
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             value, index + 2, NULL, 0, 100);
+       if (err < 0)
+               return err;
+       mixer->audigy2nx_leds[index] = value;
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_audigy2nx_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "CMSS LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 0,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Power LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Dolby Digital LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 2,
+       },
+};
+
+static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+{
+       int i, err;
+
+       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+               if (i > 1 && /* Live24ext has 2 LEDs only */
+                       (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                        mixer->chip->usb_id == USB_ID(0x041e, 0x3048)))
+                       break; 
+               err = snd_ctl_add(mixer->chip->card,
+                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+               if (err < 0)
+                       return err;
+       }
+       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
+       return 0;
+}
+
+static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
+                                   struct snd_info_buffer *buffer)
+{
+       static const struct sb_jack {
+               int unitid;
+               const char *name;
+       }  jacks_audigy2nx[] = {
+               {4,  "dig in "},
+               {7,  "line in"},
+               {19, "spk out"},
+               {20, "hph out"},
+               {-1, NULL}
+       }, jacks_live24ext[] = {
+               {4,  "line in"}, /* &1=Line, &2=Mic*/
+               {3,  "hph out"}, /* headphones */
+               {0,  "RC     "}, /* last command, 6 bytes see rc_config above */
+               {-1, NULL}
+       };
+       const struct sb_jack *jacks;
+       struct usb_mixer_interface *mixer = entry->private_data;
+       int i, err;
+       u8 buf[3];
+
+       snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020))
+               jacks = jacks_audigy2nx;
+       else if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+               jacks = jacks_live24ext;
+       else
+               return;
+
+       for (i = 0; jacks[i].name; ++i) {
+               snd_iprintf(buffer, "%s: ", jacks[i].name);
+               err = snd_usb_ctl_msg(mixer->chip->dev,
+                                     usb_rcvctrlpipe(mixer->chip->dev, 0),
+                                     UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
+                                     USB_RECIP_INTERFACE, 0,
+                                     jacks[i].unitid << 8, buf, 3, 100);
+               if (err == 3 && (buf[0] == 3 || buf[0] == 6))
+                       snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+               else
+                       snd_iprintf(buffer, "?\n");
+       }
+}
+
+static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02);
+       return 0;
+}
+
+static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       u8 old_status, new_status;
+       int err, changed;
+
+       old_status = mixer->xonar_u1_status;
+       if (ucontrol->value.integer.value[0])
+               new_status = old_status | 0x02;
+       else
+               new_status = old_status & ~0x02;
+       changed = new_status != old_status;
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             50, 0, &new_status, 1, 100);
+       if (err < 0)
+               return err;
+       mixer->xonar_u1_status = new_status;
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_xonar_u1_output_switch = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "Digital Playback Switch",
+       .info = snd_ctl_boolean_mono_info,
+       .get = snd_xonar_u1_switch_get,
+       .put = snd_xonar_u1_switch_put,
+};
+
+static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       err = snd_ctl_add(mixer->chip->card,
+                         snd_ctl_new1(&snd_xonar_u1_output_switch, mixer));
+       if (err < 0)
+               return err;
+       mixer->xonar_u1_status = 0x05;
+       return 0;
+}
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                              unsigned char samplerate_id)
+{
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *cval;
+       int unitid = 12; /* SamleRate ExtensionUnit ID */
+
+       list_for_each_entry(mixer, &chip->mixer_list, list) {
+               cval = mixer->id_elems[unitid];
+               if (cval) {
+                       snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR,
+                                                   cval->control << 8,
+                                                   samplerate_id);
+                       snd_usb_mixer_notify_id(mixer, unitid);
+               }
+               break;
+       }
+}
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
+{
+       int err;
+       struct snd_info_entry *entry;
+
+       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+               return err;
+
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+           mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
+               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+                       return err;
+               if (!snd_card_proc_new(mixer->chip->card, "audigy2nx", &entry))
+                       snd_info_set_text_ops(entry, mixer,
+                                             snd_audigy2nx_proc_read);
+       }
+
+       if (mixer->chip->usb_id == USB_ID(0x0b05, 0x1739) ||
+           mixer->chip->usb_id == USB_ID(0x0b05, 0x1743)) {
+               err = snd_xonar_u1_controls_create(mixer);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+                                   int unitid)
+{
+       if (!mixer->rc_cfg)
+               return;
+       /* unit ids specific to Extigy/Audigy 2 NX: */
+       switch (unitid) {
+       case 0: /* remote control */
+               mixer->rc_urb->dev = mixer->chip->dev;
+               usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
+               break;
+       case 4: /* digital in jack */
+       case 7: /* line in jacks */
+       case 19: /* speaker out jacks */
+       case 20: /* headphones out jack */
+               break;
+       /* live24ext: 4 = line-in jack */
+       case 3: /* hp-out jack (may actuate Mute) */
+               if (mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
+                   mixer->chip->usb_id == USB_ID(0x041e, 0x3048))
+                       snd_usb_mixer_notify_id(mixer, mixer->rc_cfg->mute_mixer_id);
+               break;
+       default:
+               snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+               break;
+       }
+}
+
diff --git a/sound/usb/mixer_quirks.h b/sound/usb/mixer_quirks.h
new file mode 100644 (file)
index 0000000..bdbfab0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef SND_USB_MIXER_QUIRKS_H
+#define SND_USB_MIXER_QUIRKS_H
+
+int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer);
+
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                              unsigned char samplerate_id);
+
+void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
+                                   int unitid);
+
+#endif /* SND_USB_MIXER_QUIRKS_H */
+
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
new file mode 100644 (file)
index 0000000..2bf0d77
--- /dev/null
@@ -0,0 +1,935 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/audio-v2.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "quirks.h"
+#include "debug.h"
+#include "urb.h"
+#include "helper.h"
+#include "pcm.h"
+
+/*
+ * return the current pcm pointer.  just based on the hwptr_done value.
+ */
+static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_usb_substream *subs;
+       unsigned int hwptr_done;
+
+       subs = (struct snd_usb_substream *)substream->runtime->private_data;
+       spin_lock(&subs->lock);
+       hwptr_done = subs->hwptr_done;
+       spin_unlock(&subs->lock);
+       return hwptr_done / (substream->runtime->frame_bits >> 3);
+}
+
+/*
+ * find a matching audio format
+ */
+static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
+                                      unsigned int rate, unsigned int channels)
+{
+       struct list_head *p;
+       struct audioformat *found = NULL;
+       int cur_attr = 0, attr;
+
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!(fp->formats & (1uLL << format)))
+                       continue;
+               if (fp->channels != channels)
+                       continue;
+               if (rate < fp->rate_min || rate > fp->rate_max)
+                       continue;
+               if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
+                       unsigned int i;
+                       for (i = 0; i < fp->nr_rates; i++)
+                               if (fp->rate_table[i] == rate)
+                                       break;
+                       if (i >= fp->nr_rates)
+                               continue;
+               }
+               attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
+               if (! found) {
+                       found = fp;
+                       cur_attr = attr;
+                       continue;
+               }
+               /* avoid async out and adaptive in if the other method
+                * supports the same format.
+                * this is a workaround for the case like
+                * M-audio audiophile USB.
+                */
+               if (attr != cur_attr) {
+                       if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
+                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
+                           (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
+                            subs->direction == SNDRV_PCM_STREAM_CAPTURE))
+                               continue;
+                       if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
+                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
+                           (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
+                            subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
+                               found = fp;
+                               cur_attr = attr;
+                               continue;
+                       }
+               }
+               /* find the format with the largest max. packet size */
+               if (fp->maxpacksize > found->maxpacksize) {
+                       found = fp;
+                       cur_attr = attr;
+               }
+       }
+       return found;
+}
+
+static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
+                        struct usb_host_interface *alts,
+                        struct audioformat *fmt)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned int ep;
+       unsigned char data[1];
+       int err;
+
+       ep = get_endpoint(alts, 0)->bEndpointAddress;
+
+       /* if endpoint doesn't have pitch control, bail out */
+       if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
+               return 0;
+
+       data[0] = 1;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+                                  UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
+                          dev->devnum, iface, ep);
+               return err;
+       }
+
+       return 0;
+}
+
+/*
+ * initialize the picth control and sample rate
+ */
+int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
+                      struct usb_host_interface *alts,
+                      struct audioformat *fmt)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+       switch (altsd->bInterfaceProtocol) {
+       case UAC_VERSION_1:
+               return init_pitch_v1(chip, iface, alts, fmt);
+
+       case UAC_VERSION_2:
+               /* not implemented yet */
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
+                             struct usb_host_interface *alts,
+                             struct audioformat *fmt, int rate)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned int ep;
+       unsigned char data[3];
+       int err, crate;
+
+       ep = get_endpoint(alts, 0)->bEndpointAddress;
+       /* if endpoint doesn't have sampling rate control, bail out */
+       if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
+               snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
+                                  dev->devnum, iface, fmt->altsetting);
+               return 0;
+       }
+
+       data[0] = rate;
+       data[1] = rate >> 8;
+       data[2] = rate >> 16;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
+                                  UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
+                          dev->devnum, iface, fmt->altsetting, rate, ep);
+               return err;
+       }
+       if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
+                                  USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
+                                  UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
+                          dev->devnum, iface, fmt->altsetting, ep);
+               return 0; /* some devices don't support reading */
+       }
+       crate = data[0] | (data[1] << 8) | (data[2] << 16);
+       if (crate != rate) {
+               snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+               // runtime->rate = crate;
+       }
+
+       return 0;
+}
+
+static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
+                             struct usb_host_interface *alts,
+                             struct audioformat *fmt, int rate)
+{
+       struct usb_device *dev = chip->dev;
+       unsigned char data[4];
+       int err, crate;
+
+       data[0] = rate;
+       data[1] = rate >> 8;
+       data[2] = rate >> 16;
+       data[3] = rate >> 24;
+       if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
+                                  USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+                                  UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
+                          dev->devnum, iface, fmt->altsetting, rate);
+               return err;
+       }
+       if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
+                                  USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+                                  UAC2_CS_CONTROL_SAM_FREQ << 8, chip->clock_id << 8,
+                                  data, sizeof(data), 1000)) < 0) {
+               snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
+                          dev->devnum, iface, fmt->altsetting);
+               return err;
+       }
+       crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+       if (crate != rate)
+               snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
+
+       return 0;
+}
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+                            struct usb_host_interface *alts,
+                            struct audioformat *fmt, int rate)
+{
+       struct usb_interface_descriptor *altsd = get_iface_desc(alts);
+
+       switch (altsd->bInterfaceProtocol) {
+       case UAC_VERSION_1:
+               return set_sample_rate_v1(chip, iface, alts, fmt, rate);
+
+       case UAC_VERSION_2:
+               return set_sample_rate_v2(chip, iface, alts, fmt, rate);
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * find a matching format and set up the interface
+ */
+static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
+{
+       struct usb_device *dev = subs->dev;
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct usb_interface *iface;
+       unsigned int ep, attr;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+       int err;
+
+       iface = usb_ifnum_to_if(dev, fmt->iface);
+       if (WARN_ON(!iface))
+               return -EINVAL;
+       alts = &iface->altsetting[fmt->altset_idx];
+       altsd = get_iface_desc(alts);
+       if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
+               return -EINVAL;
+
+       if (fmt == subs->cur_audiofmt)
+               return 0;
+
+       /* close the old interface */
+       if (subs->interface >= 0 && subs->interface != fmt->iface) {
+               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
+                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
+                               dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EIO;
+               }
+               subs->interface = -1;
+               subs->altset_idx = 0;
+       }
+
+       /* set interface */
+       if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
+               if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
+                       snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EIO;
+               }
+               snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
+               subs->interface = fmt->iface;
+               subs->altset_idx = fmt->altset_idx;
+       }
+
+       /* create a data pipe */
+       ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
+       if (is_playback)
+               subs->datapipe = usb_sndisocpipe(dev, ep);
+       else
+               subs->datapipe = usb_rcvisocpipe(dev, ep);
+       subs->datainterval = fmt->datainterval;
+       subs->syncpipe = subs->syncinterval = 0;
+       subs->maxpacksize = fmt->maxpacksize;
+       subs->fill_max = 0;
+
+       /* we need a sync pipe in async OUT or adaptive IN mode */
+       /* check the number of EP, since some devices have broken
+        * descriptors which fool us.  if it has only one EP,
+        * assume it as adaptive-out or sync-in.
+        */
+       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+       if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
+            (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+           altsd->bNumEndpoints >= 2) {
+               /* check sync-pipe endpoint */
+               /* ... and check descriptor size before accessing bSynchAddress
+                  because there is a version of the SB Audigy 2 NX firmware lacking
+                  the audio fields in the endpoint descriptors */
+               if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
+                   (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                    get_endpoint(alts, 1)->bSynchAddress != 0)) {
+                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EINVAL;
+               }
+               ep = get_endpoint(alts, 1)->bEndpointAddress;
+               if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
+                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+                                  dev->devnum, fmt->iface, fmt->altsetting);
+                       return -EINVAL;
+               }
+               ep &= USB_ENDPOINT_NUMBER_MASK;
+               if (is_playback)
+                       subs->syncpipe = usb_rcvisocpipe(dev, ep);
+               else
+                       subs->syncpipe = usb_sndisocpipe(dev, ep);
+               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   get_endpoint(alts, 1)->bRefresh >= 1 &&
+                   get_endpoint(alts, 1)->bRefresh <= 9)
+                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+                       subs->syncinterval = 1;
+               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+                        get_endpoint(alts, 1)->bInterval <= 16)
+                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+               else
+                       subs->syncinterval = 3;
+       }
+
+       /* always fill max packet size */
+       if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
+               subs->fill_max = 1;
+
+       if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
+               return err;
+
+       subs->cur_audiofmt = fmt;
+
+       snd_usb_set_format_quirk(subs, fmt);
+
+#if 0
+       printk(KERN_DEBUG
+              "setting done: format = %d, rate = %d..%d, channels = %d\n",
+              fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
+       printk(KERN_DEBUG
+              "  datapipe = 0x%0x, syncpipe = 0x%0x\n",
+              subs->datapipe, subs->syncpipe);
+#endif
+
+       return 0;
+}
+
+/*
+ * hw_params callback
+ *
+ * allocate a buffer and set the given audio format.
+ *
+ * so far we use a physically linear buffer although packetize transfer
+ * doesn't need a continuous area.
+ * if sg buffer is supported on the later version of alsa, we'll follow
+ * that.
+ */
+static int snd_usb_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+       struct audioformat *fmt;
+       unsigned int channels, rate, format;
+       int ret, changed;
+
+       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (ret < 0)
+               return ret;
+
+       format = params_format(hw_params);
+       rate = params_rate(hw_params);
+       channels = params_channels(hw_params);
+       fmt = find_format(subs, format, rate, channels);
+       if (!fmt) {
+               snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
+                          format, rate, channels);
+               return -EINVAL;
+       }
+
+       changed = subs->cur_audiofmt != fmt ||
+               subs->period_bytes != params_period_bytes(hw_params) ||
+               subs->cur_rate != rate;
+       if ((ret = set_format(subs, fmt)) < 0)
+               return ret;
+
+       if (subs->cur_rate != rate) {
+               struct usb_host_interface *alts;
+               struct usb_interface *iface;
+               iface = usb_ifnum_to_if(subs->dev, fmt->iface);
+               alts = &iface->altsetting[fmt->altset_idx];
+               ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate);
+               if (ret < 0)
+                       return ret;
+               subs->cur_rate = rate;
+       }
+
+       if (changed) {
+               /* format changed */
+               snd_usb_release_substream_urbs(subs, 0);
+               /* influenced: period_bytes, channels, rate, format, */
+               ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
+                                                 params_rate(hw_params),
+                                                 snd_pcm_format_physical_width(params_format(hw_params)) *
+                                                       params_channels(hw_params));
+       }
+
+       return ret;
+}
+
+/*
+ * hw_free callback
+ *
+ * reset the audio format and release the buffer
+ */
+static int snd_usb_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       subs->cur_audiofmt = NULL;
+       subs->cur_rate = 0;
+       subs->period_bytes = 0;
+       if (!subs->stream->chip->shutdown)
+               snd_usb_release_substream_urbs(subs, 0);
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+/*
+ * prepare callback
+ *
+ * only a few subtle things...
+ */
+static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_usb_substream *subs = runtime->private_data;
+
+       if (! subs->cur_audiofmt) {
+               snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
+               return -ENXIO;
+       }
+
+       /* some unit conversions in runtime */
+       subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
+       subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+
+       /* reset the pointer */
+       subs->hwptr_done = 0;
+       subs->transfer_done = 0;
+       subs->phase = 0;
+       runtime->delay = 0;
+
+       return snd_usb_substream_prepare(subs, runtime);
+}
+
+static struct snd_pcm_hardware snd_usb_hardware =
+{
+       .info =                 SNDRV_PCM_INFO_MMAP |
+                               SNDRV_PCM_INFO_MMAP_VALID |
+                               SNDRV_PCM_INFO_BATCH |
+                               SNDRV_PCM_INFO_INTERLEAVED |
+                               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                               SNDRV_PCM_INFO_PAUSE,
+       .buffer_bytes_max =     1024 * 1024,
+       .period_bytes_min =     64,
+       .period_bytes_max =     512 * 1024,
+       .periods_min =          2,
+       .periods_max =          1024,
+};
+
+static int hw_check_valid_format(struct snd_usb_substream *subs,
+                                struct snd_pcm_hw_params *params,
+                                struct audioformat *fp)
+{
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+       struct snd_mask check_fmts;
+       unsigned int ptime;
+
+       /* check the format */
+       snd_mask_none(&check_fmts);
+       check_fmts.bits[0] = (u32)fp->formats;
+       check_fmts.bits[1] = (u32)(fp->formats >> 32);
+       snd_mask_intersect(&check_fmts, fmts);
+       if (snd_mask_empty(&check_fmts)) {
+               hwc_debug("   > check: no supported format %d\n", fp->format);
+               return 0;
+       }
+       /* check the channels */
+       if (fp->channels < ct->min || fp->channels > ct->max) {
+               hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
+               return 0;
+       }
+       /* check the rate is within the range */
+       if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
+               hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);
+               return 0;
+       }
+       if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
+               hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);
+               return 0;
+       }
+       /* check whether the period time is >= the data packet interval */
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+               ptime = 125 * (1 << fp->datainterval);
+               if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
+                       hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       unsigned int rmin, rmax;
+       int changed;
+
+       hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
+       changed = 0;
+       rmin = rmax = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               if (changed++) {
+                       if (rmin > fp->rate_min)
+                               rmin = fp->rate_min;
+                       if (rmax < fp->rate_max)
+                               rmax = fp->rate_max;
+               } else {
+                       rmin = fp->rate_min;
+                       rmax = fp->rate_max;
+               }
+       }
+
+       if (!changed) {
+               hwc_debug("  --> get empty\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+
+       changed = 0;
+       if (it->min < rmin) {
+               it->min = rmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (it->max > rmax) {
+               it->max = rmax;
+               it->openmax = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+                           struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       unsigned int rmin, rmax;
+       int changed;
+
+       hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
+       changed = 0;
+       rmin = rmax = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               if (changed++) {
+                       if (rmin > fp->channels)
+                               rmin = fp->channels;
+                       if (rmax < fp->channels)
+                               rmax = fp->channels;
+               } else {
+                       rmin = fp->channels;
+                       rmax = fp->channels;
+               }
+       }
+
+       if (!changed) {
+               hwc_debug("  --> get empty\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+
+       changed = 0;
+       if (it->min < rmin) {
+               it->min = rmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (it->max > rmax) {
+               it->max = rmax;
+               it->openmax = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+static int hw_rule_format(struct snd_pcm_hw_params *params,
+                         struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct list_head *p;
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       u64 fbits;
+       u32 oldbits[2];
+       int changed;
+
+       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
+       fbits = 0;
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               fbits |= fp->formats;
+       }
+
+       oldbits[0] = fmt->bits[0];
+       oldbits[1] = fmt->bits[1];
+       fmt->bits[0] &= (u32)fbits;
+       fmt->bits[1] &= (u32)(fbits >> 32);
+       if (!fmt->bits[0] && !fmt->bits[1]) {
+               hwc_debug("  --> get empty\n");
+               return -EINVAL;
+       }
+       changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
+       hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
+       return changed;
+}
+
+static int hw_rule_period_time(struct snd_pcm_hw_params *params,
+                              struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       struct audioformat *fp;
+       struct snd_interval *it;
+       unsigned char min_datainterval;
+       unsigned int pmin;
+       int changed;
+
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+       hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
+       min_datainterval = 0xff;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               min_datainterval = min(min_datainterval, fp->datainterval);
+       }
+       if (min_datainterval == 0xff) {
+               hwc_debug("  --> get emtpy\n");
+               it->empty = 1;
+               return -EINVAL;
+       }
+       pmin = 125 * (1 << min_datainterval);
+       changed = 0;
+       if (it->min < pmin) {
+               it->min = pmin;
+               it->openmin = 0;
+               changed = 1;
+       }
+       if (snd_interval_checkempty(it)) {
+               it->empty = 1;
+               return -EINVAL;
+       }
+       hwc_debug("  --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
+       return changed;
+}
+
+/*
+ *  If the device supports unusual bit rates, does the request meet these?
+ */
+static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
+                                 struct snd_usb_substream *subs)
+{
+       struct audioformat *fp;
+       int count = 0, needs_knot = 0;
+       int err;
+
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+                       return 0;
+               count += fp->nr_rates;
+               if (fp->rates & SNDRV_PCM_RATE_KNOT)
+                       needs_knot = 1;
+       }
+       if (!needs_knot)
+               return 0;
+
+       subs->rate_list.count = count;
+       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
+       subs->rate_list.mask = 0;
+       count = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               int i;
+               for (i = 0; i < fp->nr_rates; i++)
+                       subs->rate_list.list[count++] = fp->rate_table[i];
+       }
+       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                        &subs->rate_list);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+
+/*
+ * set up the runtime hardware information.
+ */
+
+static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
+{
+       struct list_head *p;
+       unsigned int pt, ptmin;
+       int param_period_time_if_needed;
+       int err;
+
+       runtime->hw.formats = subs->formats;
+
+       runtime->hw.rate_min = 0x7fffffff;
+       runtime->hw.rate_max = 0;
+       runtime->hw.channels_min = 256;
+       runtime->hw.channels_max = 0;
+       runtime->hw.rates = 0;
+       ptmin = UINT_MAX;
+       /* check min/max rates and channels */
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               fp = list_entry(p, struct audioformat, list);
+               runtime->hw.rates |= fp->rates;
+               if (runtime->hw.rate_min > fp->rate_min)
+                       runtime->hw.rate_min = fp->rate_min;
+               if (runtime->hw.rate_max < fp->rate_max)
+                       runtime->hw.rate_max = fp->rate_max;
+               if (runtime->hw.channels_min > fp->channels)
+                       runtime->hw.channels_min = fp->channels;
+               if (runtime->hw.channels_max < fp->channels)
+                       runtime->hw.channels_max = fp->channels;
+               if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
+                       /* FIXME: there might be more than one audio formats... */
+                       runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
+                               fp->frame_size;
+               }
+               pt = 125 * (1 << fp->datainterval);
+               ptmin = min(ptmin, pt);
+       }
+
+       param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
+       if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+               /* full speed devices have fixed data packet interval */
+               ptmin = 1000;
+       if (ptmin == 1000)
+               /* if period time doesn't go below 1 ms, no rules needed */
+               param_period_time_if_needed = -1;
+       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                    ptmin, UINT_MAX);
+
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                      hw_rule_rate, subs,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_channels, subs,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      SNDRV_PCM_HW_PARAM_RATE,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_format, subs,
+                                      SNDRV_PCM_HW_PARAM_RATE,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      param_period_time_if_needed,
+                                      -1)) < 0)
+               return err;
+       if (param_period_time_if_needed >= 0) {
+               err = snd_pcm_hw_rule_add(runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                         hw_rule_period_time, subs,
+                                         SNDRV_PCM_HW_PARAM_FORMAT,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS,
+                                         SNDRV_PCM_HW_PARAM_RATE,
+                                         -1);
+               if (err < 0)
+                       return err;
+       }
+       if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
+               return err;
+       return 0;
+}
+
+static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
+{
+       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_usb_substream *subs = &as->substream[direction];
+
+       subs->interface = -1;
+       subs->altset_idx = 0;
+       runtime->hw = snd_usb_hardware;
+       runtime->private_data = subs;
+       subs->pcm_substream = substream;
+       return setup_hw_info(runtime, subs);
+}
+
+static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
+{
+       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
+       struct snd_usb_substream *subs = &as->substream[direction];
+
+       if (!as->chip->shutdown && subs->interface >= 0) {
+               usb_set_interface(subs->dev, subs->interface, 0);
+               subs->interface = -1;
+       }
+       subs->pcm_substream = NULL;
+       return 0;
+}
+
+static int snd_usb_playback_open(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_usb_playback_close(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_usb_capture_open(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static int snd_usb_capture_close(struct snd_pcm_substream *substream)
+{
+       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static struct snd_pcm_ops snd_usb_playback_ops = {
+       .open =         snd_usb_playback_open,
+       .close =        snd_usb_playback_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_usb_hw_params,
+       .hw_free =      snd_usb_hw_free,
+       .prepare =      snd_usb_pcm_prepare,
+       .trigger =      snd_usb_substream_playback_trigger,
+       .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
+};
+
+static struct snd_pcm_ops snd_usb_capture_ops = {
+       .open =         snd_usb_capture_open,
+       .close =        snd_usb_capture_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_usb_hw_params,
+       .hw_free =      snd_usb_hw_free,
+       .prepare =      snd_usb_pcm_prepare,
+       .trigger =      snd_usb_substream_capture_trigger,
+       .pointer =      snd_usb_pcm_pointer,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
+};
+
+void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
+{
+       snd_pcm_set_ops(pcm, stream,
+                       stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       &snd_usb_playback_ops : &snd_usb_capture_ops);
+}
diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h
new file mode 100644 (file)
index 0000000..1c931b6
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __USBAUDIO_PCM_H
+#define __USBAUDIO_PCM_H
+
+void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
+
+int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
+                      struct usb_host_interface *alts,
+                      struct audioformat *fmt);
+
+int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
+                            struct usb_host_interface *alts,
+                            struct audioformat *fmt, int rate);
+
+#endif /* __USBAUDIO_PCM_H */
diff --git a/sound/usb/proc.c b/sound/usb/proc.c
new file mode 100644 (file)
index 0000000..f5e3f35
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+#include "card.h"
+#include "proc.h"
+
+/* convert our full speed USB rate into sampling rate in Hz */
+static inline unsigned get_full_speed_hz(unsigned int usb_rate)
+{
+       return (usb_rate * 125 + (1 << 12)) >> 13;
+}
+
+/* convert our high speed USB rate into sampling rate in Hz */
+static inline unsigned get_high_speed_hz(unsigned int usb_rate)
+{
+       return (usb_rate * 125 + (1 << 9)) >> 10;
+}
+
+/*
+ * common proc files to show the usb device info
+ */
+static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_audio *chip = entry->private_data;
+       if (!chip->shutdown)
+               snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
+}
+
+static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_audio *chip = entry->private_data;
+       if (!chip->shutdown)
+               snd_iprintf(buffer, "%04x:%04x\n", 
+                           USB_ID_VENDOR(chip->usb_id),
+                           USB_ID_PRODUCT(chip->usb_id));
+}
+
+void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
+{
+       struct snd_info_entry *entry;
+       if (!snd_card_proc_new(chip->card, "usbbus", &entry))
+               snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
+       if (!snd_card_proc_new(chip->card, "usbid", &entry))
+               snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
+}
+
+/*
+ * proc interface for list the supported pcm formats
+ */
+static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
+{
+       struct list_head *p;
+       static char *sync_types[4] = {
+               "NONE", "ASYNC", "ADAPTIVE", "SYNC"
+       };
+
+       list_for_each(p, &subs->fmt_list) {
+               struct audioformat *fp;
+               snd_pcm_format_t fmt;
+               fp = list_entry(p, struct audioformat, list);
+               snd_iprintf(buffer, "  Interface %d\n", fp->iface);
+               snd_iprintf(buffer, "    Altset %d\n", fp->altsetting);
+               snd_iprintf(buffer, "    Format:");
+               for (fmt = 0; fmt <= SNDRV_PCM_FORMAT_LAST; ++fmt)
+                       if (fp->formats & (1uLL << fmt))
+                               snd_iprintf(buffer, " %s",
+                                           snd_pcm_format_name(fmt));
+               snd_iprintf(buffer, "\n");
+               snd_iprintf(buffer, "    Channels: %d\n", fp->channels);
+               snd_iprintf(buffer, "    Endpoint: %d %s (%s)\n",
+                           fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
+                           fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
+                           sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]);
+               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
+                       snd_iprintf(buffer, "    Rates: %d - %d (continuous)\n",
+                                   fp->rate_min, fp->rate_max);
+               } else {
+                       unsigned int i;
+                       snd_iprintf(buffer, "    Rates: ");
+                       for (i = 0; i < fp->nr_rates; i++) {
+                               if (i > 0)
+                                       snd_iprintf(buffer, ", ");
+                               snd_iprintf(buffer, "%d", fp->rate_table[i]);
+                       }
+                       snd_iprintf(buffer, "\n");
+               }
+               if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+                       snd_iprintf(buffer, "    Data packet interval: %d us\n",
+                                   125 * (1 << fp->datainterval));
+               // snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
+               // snd_iprintf(buffer, "    EP Attribute = %#x\n", fp->attributes);
+       }
+}
+
+static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
+{
+       if (subs->running) {
+               unsigned int i;
+               snd_iprintf(buffer, "  Status: Running\n");
+               snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
+               snd_iprintf(buffer, "    Altset = %d\n", subs->altset_idx);
+               snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
+               for (i = 0; i < subs->nurbs; i++)
+                       snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
+               snd_iprintf(buffer, "]\n");
+               snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
+               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
+                           snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
+                           ? get_full_speed_hz(subs->freqm)
+                           : get_high_speed_hz(subs->freqm),
+                           subs->freqm >> 16, subs->freqm & 0xffff);
+       } else {
+               snd_iprintf(buffer, "  Status: Stop\n");
+       }
+}
+
+static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+       struct snd_usb_stream *stream = entry->private_data;
+
+       snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
+
+       if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
+               snd_iprintf(buffer, "\nPlayback:\n");
+               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+       }
+       if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
+               snd_iprintf(buffer, "\nCapture:\n");
+               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+       }
+}
+
+void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream)
+{
+       struct snd_info_entry *entry;
+       char name[32];
+       struct snd_card *card = stream->chip->card;
+
+       sprintf(name, "stream%d", stream->pcm_index);
+       if (!snd_card_proc_new(card, name, &entry))
+               snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
+}
+
diff --git a/sound/usb/proc.h b/sound/usb/proc.h
new file mode 100644 (file)
index 0000000..a45b765
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __USBAUDIO_PROC_H
+#define __USBAUDIO_PROC_H
+
+void snd_usb_audio_create_proc(struct snd_usb_audio *chip);
+void snd_usb_proc_pcm_format_add(struct snd_usb_stream *stream);
+
+#endif /* __USBAUDIO_PROC_H */
+
similarity index 97%
rename from sound/usb/usbquirks.h
rename to sound/usb/quirks-table.h
index 2b426c1..91ddef3 100644 (file)
@@ -279,7 +279,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 0,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S16_LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                        .channels = 4,
                                        .iface = 0,
                                        .altsetting = 1,
@@ -296,7 +296,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S16_LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -580,7 +580,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 0,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 0,
                                        .altsetting = 1,
@@ -597,7 +597,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -793,7 +793,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
@@ -810,7 +810,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 2,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = & (const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3LE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
                                        .channels = 2,
                                        .iface = 2,
                                        .altsetting = 1,
@@ -1826,6 +1826,60 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
+{
+       USB_DEVICE(0x0763, 0x2080),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               /* .vendor_name = "M-Audio", */
+               /* .product_name = "Fast Track Ultra 8", */
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = & (const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       /* interface 3 (MIDI) is standard compliant */
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
+{
+       USB_DEVICE(0x0763, 0x2081),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               /* .vendor_name = "M-Audio", */
+               /* .product_name = "Fast Track Ultra 8R", */
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = & (const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       /* interface 3 (MIDI) is standard compliant */
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
 
 /* Casio devices */
 {
@@ -2203,7 +2257,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                                .ifnum = 1,
                                .type = QUIRK_AUDIO_FIXED_ENDPOINT,
                                .data = &(const struct audioformat) {
-                                       .format = SNDRV_PCM_FORMAT_S24_3BE,
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3BE,
                                        .channels = 2,
                                        .iface = 1,
                                        .altsetting = 1,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
new file mode 100644 (file)
index 0000000..136e5b4
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "mixer.h"
+#include "mixer_quirks.h"
+#include "midi.h"
+#include "quirks.h"
+#include "helper.h"
+#include "endpoint.h"
+#include "pcm.h"
+
+/*
+ * handle the quirks for the contained interfaces
+ */
+static int create_composite_quirk(struct snd_usb_audio *chip,
+                                 struct usb_interface *iface,
+                                 struct usb_driver *driver,
+                                 const struct snd_usb_audio_quirk *quirk)
+{
+       int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
+       int err;
+
+       for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) {
+               iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
+               if (!iface)
+                       continue;
+               if (quirk->ifnum != probed_ifnum &&
+                   usb_interface_claimed(iface))
+                       continue;
+               err = snd_usb_create_quirk(chip, iface, driver, quirk);
+               if (err < 0)
+                       return err;
+               if (quirk->ifnum != probed_ifnum)
+                       usb_driver_claim_interface(driver, iface, (void *)-1L);
+       }
+       return 0;
+}
+
+static int ignore_interface_quirk(struct snd_usb_audio *chip,
+                                 struct usb_interface *iface,
+                                 struct usb_driver *driver,
+                                 const struct snd_usb_audio_quirk *quirk)
+{
+       return 0;
+}
+
+
+/*
+ * Allow alignment on audio sub-slot (channel samples) rather than
+ * on audio slots (audio frames)
+ */
+static int create_align_transfer_quirk(struct snd_usb_audio *chip,
+                                      struct usb_interface *iface,
+                                      struct usb_driver *driver,
+                                      const struct snd_usb_audio_quirk *quirk)
+{
+       chip->txfr_quirk = 1;
+       return 1;       /* Continue with creating streams and mixer */
+}
+
+static int create_any_midi_quirk(struct snd_usb_audio *chip,
+                                struct usb_interface *intf,
+                                struct usb_driver *driver,
+                                const struct snd_usb_audio_quirk *quirk)
+{
+       return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
+}
+
+/*
+ * create a stream for an interface with proper descriptors
+ */
+static int create_standard_audio_quirk(struct snd_usb_audio *chip,
+                                      struct usb_interface *iface,
+                                      struct usb_driver *driver,
+                                      const struct snd_usb_audio_quirk *quirk)
+{
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       int err;
+
+       alts = &iface->altsetting[0];
+       altsd = get_iface_desc(alts);
+       err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber);
+       if (err < 0) {
+               snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
+                          altsd->bInterfaceNumber, err);
+               return err;
+       }
+       /* reset the current interface */
+       usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
+       return 0;
+}
+
+/*
+ * create a stream for an endpoint/altsetting without proper descriptors
+ */
+static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
+                                    struct usb_interface *iface,
+                                    struct usb_driver *driver,
+                                    const struct snd_usb_audio_quirk *quirk)
+{
+       struct audioformat *fp;
+       struct usb_host_interface *alts;
+       int stream, err;
+       unsigned *rate_table = NULL;
+
+       fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
+       if (! fp) {
+               snd_printk(KERN_ERR "cannot memdup\n");
+               return -ENOMEM;
+       }
+       if (fp->nr_rates > 0) {
+               rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
+               if (!rate_table) {
+                       kfree(fp);
+                       return -ENOMEM;
+               }
+               memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
+               fp->rate_table = rate_table;
+       }
+
+       stream = (fp->endpoint & USB_DIR_IN)
+               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       err = snd_usb_add_audio_endpoint(chip, stream, fp);
+       if (err < 0) {
+               kfree(fp);
+               kfree(rate_table);
+               return err;
+       }
+       if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
+           fp->altset_idx >= iface->num_altsetting) {
+               kfree(fp);
+               kfree(rate_table);
+               return -EINVAL;
+       }
+       alts = &iface->altsetting[fp->altset_idx];
+       fp->datainterval = snd_usb_parse_datainterval(chip, alts);
+       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+       usb_set_interface(chip->dev, fp->iface, 0);
+       snd_usb_init_pitch(chip, fp->iface, alts, fp);
+       snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
+       return 0;
+}
+
+/*
+ * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
+ * The only way to detect the sample rate is by looking at wMaxPacketSize.
+ */
+static int create_uaxx_quirk(struct snd_usb_audio *chip,
+                            struct usb_interface *iface,
+                            struct usb_driver *driver,
+                            const struct snd_usb_audio_quirk *quirk)
+{
+       static const struct audioformat ua_format = {
+               .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+               .channels = 2,
+               .fmt_type = UAC_FORMAT_TYPE_I,
+               .altsetting = 1,
+               .altset_idx = 1,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+       };
+       struct usb_host_interface *alts;
+       struct usb_interface_descriptor *altsd;
+       struct audioformat *fp;
+       int stream, err;
+
+       /* both PCM and MIDI interfaces have 2 or more altsettings */
+       if (iface->num_altsetting < 2)
+               return -ENXIO;
+       alts = &iface->altsetting[1];
+       altsd = get_iface_desc(alts);
+
+       if (altsd->bNumEndpoints == 2) {
+               static const struct snd_usb_midi_endpoint_info ua700_ep = {
+                       .out_cables = 0x0003,
+                       .in_cables  = 0x0003
+               };
+               static const struct snd_usb_audio_quirk ua700_quirk = {
+                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                       .data = &ua700_ep
+               };
+               static const struct snd_usb_midi_endpoint_info uaxx_ep = {
+                       .out_cables = 0x0001,
+                       .in_cables  = 0x0001
+               };
+               static const struct snd_usb_audio_quirk uaxx_quirk = {
+                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                       .data = &uaxx_ep
+               };
+               const struct snd_usb_audio_quirk *quirk =
+                       chip->usb_id == USB_ID(0x0582, 0x002b)
+                       ? &ua700_quirk : &uaxx_quirk;
+               return snd_usbmidi_create(chip->card, iface,
+                                         &chip->midi_list, quirk);
+       }
+
+       if (altsd->bNumEndpoints != 1)
+               return -ENXIO;
+
+       fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+       if (!fp)
+               return -ENOMEM;
+       memcpy(fp, &ua_format, sizeof(*fp));
+
+       fp->iface = altsd->bInterfaceNumber;
+       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
+       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
+       fp->datainterval = 0;
+       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+
+       switch (fp->maxpacksize) {
+       case 0x120:
+               fp->rate_max = fp->rate_min = 44100;
+               break;
+       case 0x138:
+       case 0x140:
+               fp->rate_max = fp->rate_min = 48000;
+               break;
+       case 0x258:
+       case 0x260:
+               fp->rate_max = fp->rate_min = 96000;
+               break;
+       default:
+               snd_printk(KERN_ERR "unknown sample rate\n");
+               kfree(fp);
+               return -ENXIO;
+       }
+
+       stream = (fp->endpoint & USB_DIR_IN)
+               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+       err = snd_usb_add_audio_endpoint(chip, stream, fp);
+       if (err < 0) {
+               kfree(fp);
+               return err;
+       }
+       usb_set_interface(chip->dev, fp->iface, 0);
+       return 0;
+}
+
+/*
+ * audio-interface quirks
+ *
+ * returns zero if no standard audio/MIDI parsing is needed.
+ * returns a postive value if standard audio/midi interfaces are parsed
+ * after this.
+ * returns a negative value at error.
+ */
+int snd_usb_create_quirk(struct snd_usb_audio *chip,
+                        struct usb_interface *iface,
+                        struct usb_driver *driver,
+                        const struct snd_usb_audio_quirk *quirk)
+{
+       typedef int (*quirk_func_t)(struct snd_usb_audio *,
+                                   struct usb_interface *,
+                                   struct usb_driver *,
+                                   const struct snd_usb_audio_quirk *);
+       static const quirk_func_t quirk_funcs[] = {
+               [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+               [QUIRK_COMPOSITE] = create_composite_quirk,
+               [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
+               [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
+               [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
+               [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
+               [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
+               [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
+               [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
+               [QUIRK_MIDI_CME] = create_any_midi_quirk,
+               [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
+               [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
+               [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
+       };
+
+       if (quirk->type < QUIRK_TYPE_COUNT) {
+               return quirk_funcs[quirk->type](chip, iface, driver, quirk);
+       } else {
+               snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
+               return -ENXIO;
+       }
+}
+
+/*
+ * boot quirks
+ */
+
+#define EXTIGY_FIRMWARE_SIZE_OLD 794
+#define EXTIGY_FIRMWARE_SIZE_NEW 483
+
+static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
+{
+       struct usb_host_config *config = dev->actconfig;
+       int err;
+
+       if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
+           le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
+               snd_printdd("sending Extigy boot sequence...\n");
+               /* Send message to force it to reconnect with full interface. */
+               err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
+                                     0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
+               if (err < 0) snd_printdd("error sending boot message: %d\n", err);
+               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
+                               &dev->descriptor, sizeof(dev->descriptor));
+               config = dev->actconfig;
+               if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
+               err = usb_reset_configuration(dev);
+               if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
+               snd_printdd("extigy_boot: new boot length = %d\n",
+                           le16_to_cpu(get_cfg_desc(config)->wTotalLength));
+               return -ENODEV; /* quit this anyway */
+       }
+       return 0;
+}
+
+static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
+{
+       u8 buf = 1;
+
+       snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                       0, 0, &buf, 1, 1000);
+       if (buf == 0) {
+               snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                               1, 2000, NULL, 0, 1000);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/*
+ * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
+ * documented in the device's data sheet.
+ */
+static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
+{
+       u8 buf[4];
+       buf[0] = 0x20;
+       buf[1] = value & 0xff;
+       buf[2] = (value >> 8) & 0xff;
+       buf[3] = reg;
+       return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
+                              USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+                              0, 0, &buf, 4, 1000);
+}
+
+static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
+{
+       /*
+        * Enable line-out driver mode, set headphone source to front
+        * channels, enable stereo mic.
+        */
+       return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
+}
+
+/*
+ * C-Media CM6206 is based on CM106 with two additional
+ * registers that are not documented in the data sheet.
+ * Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
+{
+       int err, reg;
+       int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
+
+       for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
+               err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
+               if (err < 0)
+                       return err;
+       }
+
+       return err;
+}
+
+/*
+ * This call will put the synth in "USB send" mode, i.e it will send MIDI
+ * messages through USB (this is disabled at startup). The synth will
+ * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
+ * sign on its LCD. Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
+{
+       int err, actual_length;
+
+       /* "midi send" enable */
+       static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
+
+       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
+                       ARRAY_SIZE(seq), &actual_length, 1000);
+       kfree(buf);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+/*
+ * Setup quirks
+ */
+#define AUDIOPHILE_SET                 0x01 /* if set, parse device_setup */
+#define AUDIOPHILE_SET_DTS              0x02 /* if set, enable DTS Digital Output */
+#define AUDIOPHILE_SET_96K              0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
+#define AUDIOPHILE_SET_24B             0x08 /* 24bits sample if set, 16bits otherwise */
+#define AUDIOPHILE_SET_DI              0x10 /* if set, enable Digital Input */
+#define AUDIOPHILE_SET_MASK            0x1F /* bit mask for setup value */
+#define AUDIOPHILE_SET_24B_48K_DI      0x19 /* value for 24bits+48KHz+Digital Input */
+#define AUDIOPHILE_SET_24B_48K_NOTDI   0x09 /* value for 24bits+48KHz+No Digital Input */
+#define AUDIOPHILE_SET_16B_48K_DI      0x11 /* value for 16bits+48KHz+Digital Input */
+#define AUDIOPHILE_SET_16B_48K_NOTDI   0x01 /* value for 16bits+48KHz+No Digital Input */
+
+static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
+                                        int iface,
+                                        int altno)
+{
+       /* Reset ALL ifaces to 0 altsetting.
+        * Call it for every possible altsetting of every interface.
+        */
+       usb_set_interface(chip->dev, iface, 0);
+
+       if (chip->setup & AUDIOPHILE_SET) {
+               if ((chip->setup & AUDIOPHILE_SET_DTS)
+                   && altno != 6)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_96K)
+                   && altno != 1)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_24B_48K_DI && altno != 2)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_16B_48K_DI && altno != 4)
+                       return 1; /* skip this altsetting */
+               if ((chip->setup & AUDIOPHILE_SET_MASK) ==
+                   AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
+                       return 1; /* skip this altsetting */
+       }
+
+       return 0; /* keep this altsetting */
+}
+
+int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
+                                 int iface,
+                                 int altno)
+{
+       /* audiophile usb: skip altsets incompatible with device_setup */
+       if (chip->usb_id == USB_ID(0x0763, 0x2003))
+               return audiophile_skip_setting_quirk(chip, iface, altno);
+
+       return 0;
+}
+
+int snd_usb_apply_boot_quirk(struct usb_device *dev,
+                            struct usb_interface *intf,
+                            const struct snd_usb_audio_quirk *quirk)
+{
+       u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                       le16_to_cpu(dev->descriptor.idProduct));
+
+       /* SB Extigy needs special boot-up sequence */
+       /* if more models come, this will go to the quirk list. */
+       if (id == USB_ID(0x041e, 0x3000))
+               return snd_usb_extigy_boot_quirk(dev, intf);
+
+       /* SB Audigy 2 NX needs its own boot-up magic, too */
+       if (id == USB_ID(0x041e, 0x3020))
+               return snd_usb_audigy2nx_boot_quirk(dev);
+
+       /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
+       if (id == USB_ID(0x10f5, 0x0200))
+               return snd_usb_cm106_boot_quirk(dev);
+
+       /* C-Media CM6206 / CM106-Like Sound Device */
+       if (id == USB_ID(0x0d8c, 0x0102))
+               return snd_usb_cm6206_boot_quirk(dev);
+
+       /* Access Music VirusTI Desktop */
+       if (id == USB_ID(0x133e, 0x0815))
+               return snd_usb_accessmusic_boot_quirk(dev);
+
+       return 0;
+}
+
+/*
+ * check if the device uses big-endian samples
+ */
+int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
+{
+       switch (chip->usb_id) {
+       case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
+               if (fp->endpoint & USB_DIR_IN)
+                       return 1;
+               break;
+       case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+               if (chip->setup == 0x00 ||
+                   fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
+ * not for interface.
+ */
+
+enum {
+       EMU_QUIRK_SR_44100HZ = 0,
+       EMU_QUIRK_SR_48000HZ,
+       EMU_QUIRK_SR_88200HZ,
+       EMU_QUIRK_SR_96000HZ,
+       EMU_QUIRK_SR_176400HZ,
+       EMU_QUIRK_SR_192000HZ
+};
+
+static void set_format_emu_quirk(struct snd_usb_substream *subs,
+                                struct audioformat *fmt)
+{
+       unsigned char emu_samplerate_id = 0;
+
+       /* When capture is active
+        * sample rate shouldn't be changed
+        * by playback substream
+        */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
+                       return;
+       }
+
+       switch (fmt->rate_min) {
+       case 48000:
+               emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
+               break;
+       case 88200:
+               emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
+               break;
+       case 96000:
+               emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
+               break;
+       case 176400:
+               emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
+               break;
+       case 192000:
+               emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
+               break;
+       default:
+               emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
+               break;
+       }
+       snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
+}
+
+void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
+                             struct audioformat *fmt)
+{
+       switch (subs->stream->chip->usb_id) {
+       case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+       case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+       case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+               set_format_emu_quirk(subs, fmt);
+               break;
+       }
+}
+
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
new file mode 100644 (file)
index 0000000..03e5e94
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __USBAUDIO_QUIRKS_H
+#define __USBAUDIO_QUIRKS_H
+
+int snd_usb_create_quirk(struct snd_usb_audio *chip,
+                        struct usb_interface *iface,
+                        struct usb_driver *driver,
+                        const struct snd_usb_audio_quirk *quirk);
+
+int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
+                                 int iface,
+                                 int altno);
+
+int snd_usb_apply_boot_quirk(struct usb_device *dev,
+                            struct usb_interface *intf,
+                            const struct snd_usb_audio_quirk *quirk);
+
+void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
+                             struct audioformat *fmt);
+
+int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
+                                struct audioformat *fp);
+
+#endif /* __USBAUDIO_QUIRKS_H */
diff --git a/sound/usb/urb.c b/sound/usb/urb.c
new file mode 100644 (file)
index 0000000..5570a2b
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/gfp.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "usbaudio.h"
+#include "helper.h"
+#include "card.h"
+#include "urb.h"
+#include "pcm.h"
+
+/*
+ * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
+ * this will overflow at approx 524 kHz
+ */
+static inline unsigned get_usb_full_speed_rate(unsigned int rate)
+{
+       return ((rate << 13) + 62) / 125;
+}
+
+/*
+ * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
+ * this will overflow at approx 4 MHz
+ */
+static inline unsigned get_usb_high_speed_rate(unsigned int rate)
+{
+       return ((rate << 10) + 62) / 125;
+}
+
+/*
+ * unlink active urbs.
+ */
+static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+{
+       struct snd_usb_audio *chip = subs->stream->chip;
+       unsigned int i;
+       int async;
+
+       subs->running = 0;
+
+       if (!force && subs->stream->chip->shutdown) /* to be sure... */
+               return -EBADFD;
+
+       async = !can_sleep && chip->async_unlink;
+
+       if (!async && in_interrupt())
+               return 0;
+
+       for (i = 0; i < subs->nurbs; i++) {
+               if (test_bit(i, &subs->active_mask)) {
+                       if (!test_and_set_bit(i, &subs->unlink_mask)) {
+                               struct urb *u = subs->dataurb[i].urb;
+                               if (async)
+                                       usb_unlink_urb(u);
+                               else
+                                       usb_kill_urb(u);
+                       }
+               }
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       if (test_bit(i+16, &subs->active_mask)) {
+                               if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
+                                       struct urb *u = subs->syncurb[i].urb;
+                                       if (async)
+                                               usb_unlink_urb(u);
+                                       else
+                                               usb_kill_urb(u);
+                               }
+                       }
+               }
+       }
+       return 0;
+}
+
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(struct snd_urb_ctx *u)
+{
+       if (u->urb) {
+               if (u->buffer_size)
+                       usb_buffer_free(u->subs->dev, u->buffer_size,
+                                       u->urb->transfer_buffer,
+                                       u->urb->transfer_dma);
+               usb_free_urb(u->urb);
+               u->urb = NULL;
+       }
+}
+
+/*
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(struct snd_usb_substream *subs)
+{
+       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
+       unsigned int i;
+       int alive;
+
+       do {
+               alive = 0;
+               for (i = 0; i < subs->nurbs; i++) {
+                       if (test_bit(i, &subs->active_mask))
+                               alive++;
+               }
+               if (subs->syncpipe) {
+                       for (i = 0; i < SYNC_URBS; i++) {
+                               if (test_bit(i + 16, &subs->active_mask))
+                                       alive++;
+                       }
+               }
+               if (! alive)
+                       break;
+               schedule_timeout_uninterruptible(1);
+       } while (time_before(jiffies, end_time));
+       if (alive)
+               snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+       return 0;
+}
+
+/*
+ * release a substream
+ */
+void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
+{
+       int i;
+
+       /* stop urbs (to be sure) */
+       deactivate_urbs(subs, force, 1);
+       wait_clear_urbs(subs);
+
+       for (i = 0; i < MAX_URBS; i++)
+               release_urb_ctx(&subs->dataurb[i]);
+       for (i = 0; i < SYNC_URBS; i++)
+               release_urb_ctx(&subs->syncurb[i]);
+       usb_buffer_free(subs->dev, SYNC_URBS * 4,
+                       subs->syncbuf, subs->sync_dma);
+       subs->syncbuf = NULL;
+       subs->nurbs = 0;
+}
+
+/*
+ * complete callback from data urb
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_substream *subs = ctx->subs;
+       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+       int err = 0;
+
+       if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
+           !subs->running || /* can be stopped during retire callback */
+           (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
+           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               clear_bit(ctx->index, &subs->active_mask);
+               if (err < 0) {
+                       snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+               }
+       }
+}
+
+
+/*
+ * complete callback from sync urb
+ */
+static void snd_complete_sync_urb(struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_substream *subs = ctx->subs;
+       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
+       int err = 0;
+
+       if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
+           !subs->running || /* can be stopped during retire callback */
+           (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
+           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               clear_bit(ctx->index + 16, &subs->active_mask);
+               if (err < 0) {
+                       snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
+                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+               }
+       }
+}
+
+
+/*
+ * initialize a substream for plaback/capture
+ */
+int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
+                               unsigned int period_bytes,
+                               unsigned int rate,
+                               unsigned int frame_bits)
+{
+       unsigned int maxsize, i;
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+       unsigned int urb_packs, total_packs, packs_per_ms;
+       struct snd_usb_audio *chip = subs->stream->chip;
+
+       /* calculate the frequency in 16.16 format */
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+               subs->freqn = get_usb_full_speed_rate(rate);
+       else
+               subs->freqn = get_usb_high_speed_rate(rate);
+       subs->freqm = subs->freqn;
+       /* calculate max. frequency */
+       if (subs->maxpacksize) {
+               /* whatever fits into a max. size packet */
+               maxsize = subs->maxpacksize;
+               subs->freqmax = (maxsize / (frame_bits >> 3))
+                               << (16 - subs->datainterval);
+       } else {
+               /* no max. packet size: just take 25% higher than nominal */
+               subs->freqmax = subs->freqn + (subs->freqn >> 2);
+               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
+                               >> (16 - subs->datainterval);
+       }
+       subs->phase = 0;
+
+       if (subs->fill_max)
+               subs->curpacksize = subs->maxpacksize;
+       else
+               subs->curpacksize = maxsize;
+
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+               packs_per_ms = 8 >> subs->datainterval;
+       else
+               packs_per_ms = 1;
+
+       if (is_playback) {
+               urb_packs = max(chip->nrpacks, 1);
+               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
+       } else
+               urb_packs = 1;
+       urb_packs *= packs_per_ms;
+       if (subs->syncpipe)
+               urb_packs = min(urb_packs, 1U << subs->syncinterval);
+
+       /* decide how many packets to be used */
+       if (is_playback) {
+               unsigned int minsize, maxpacks;
+               /* determine how small a packet can be */
+               minsize = (subs->freqn >> (16 - subs->datainterval))
+                         * (frame_bits >> 3);
+               /* with sync from device, assume it can be 12% lower */
+               if (subs->syncpipe)
+                       minsize -= minsize >> 3;
+               minsize = max(minsize, 1u);
+               total_packs = (period_bytes + minsize - 1) / minsize;
+               /* we need at least two URBs for queueing */
+               if (total_packs < 2) {
+                       total_packs = 2;
+               } else {
+                       /* and we don't want too long a queue either */
+                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
+                       total_packs = min(total_packs, maxpacks);
+               }
+       } else {
+               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
+                       urb_packs >>= 1;
+               total_packs = MAX_URBS * urb_packs;
+       }
+       subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+       if (subs->nurbs > MAX_URBS) {
+               /* too much... */
+               subs->nurbs = MAX_URBS;
+               total_packs = MAX_URBS * urb_packs;
+       } else if (subs->nurbs < 2) {
+               /* too little - we need at least two packets
+                * to ensure contiguous playback/capture
+                */
+               subs->nurbs = 2;
+       }
+
+       /* allocate and initialize data urbs */
+       for (i = 0; i < subs->nurbs; i++) {
+               struct snd_urb_ctx *u = &subs->dataurb[i];
+               u->index = i;
+               u->subs = subs;
+               u->packets = (i + 1) * total_packs / subs->nurbs
+                       - i * total_packs / subs->nurbs;
+               u->buffer_size = maxsize * u->packets;
+               if (subs->fmt_type == UAC_FORMAT_TYPE_II)
+                       u->packets++; /* for transfer delimiter */
+               u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+               if (!u->urb)
+                       goto out_of_memory;
+               u->urb->transfer_buffer =
+                       usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
+                                        &u->urb->transfer_dma);
+               if (!u->urb->transfer_buffer)
+                       goto out_of_memory;
+               u->urb->pipe = subs->datapipe;
+               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+               u->urb->interval = 1 << subs->datainterval;
+               u->urb->context = u;
+               u->urb->complete = snd_complete_urb;
+       }
+
+       if (subs->syncpipe) {
+               /* allocate and initialize sync urbs */
+               subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
+                                                GFP_KERNEL, &subs->sync_dma);
+               if (!subs->syncbuf)
+                       goto out_of_memory;
+               for (i = 0; i < SYNC_URBS; i++) {
+                       struct snd_urb_ctx *u = &subs->syncurb[i];
+                       u->index = i;
+                       u->subs = subs;
+                       u->packets = 1;
+                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
+                       if (!u->urb)
+                               goto out_of_memory;
+                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
+                       u->urb->transfer_dma = subs->sync_dma + i * 4;
+                       u->urb->transfer_buffer_length = 4;
+                       u->urb->pipe = subs->syncpipe;
+                       u->urb->transfer_flags = URB_ISO_ASAP |
+                                                URB_NO_TRANSFER_DMA_MAP;
+                       u->urb->number_of_packets = 1;
+                       u->urb->interval = 1 << subs->syncinterval;
+                       u->urb->context = u;
+                       u->urb->complete = snd_complete_sync_urb;
+               }
+       }
+       return 0;
+
+out_of_memory:
+       snd_usb_release_substream_urbs(subs, 0);
+       return -ENOMEM;
+}
+
+/*
+ * prepare urb for full speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
+                                   struct snd_pcm_runtime *runtime,
+                                   struct urb *urb)
+{
+       unsigned char *cp = urb->transfer_buffer;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn >> 2;
+       cp[1] = subs->freqn >> 10;
+       cp[2] = subs->freqn >> 18;
+       return 0;
+}
+
+/*
+ * prepare urb for high speed capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ */
+static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned char *cp = urb->transfer_buffer;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn;
+       cp[1] = subs->freqn >> 8;
+       cp[2] = subs->freqn >> 16;
+       cp[3] = subs->freqn >> 24;
+       return 0;
+}
+
+/*
+ * process after capture sync complete
+ * - nothing to do
+ */
+static int retire_capture_sync_urb(struct snd_usb_substream *subs,
+                                  struct snd_pcm_runtime *runtime,
+                                  struct urb *urb)
+{
+       return 0;
+}
+
+/*
+ * prepare urb for capture data pipe
+ *
+ * fill the offset and length of each descriptor.
+ *
+ * we use a temporary buffer to write the captured data.
+ * since the length of written data is determined by host, we cannot
+ * write onto the pcm buffer directly...  the data is thus copied
+ * later at complete callback to the global buffer.
+ */
+static int prepare_capture_urb(struct snd_usb_substream *subs,
+                              struct snd_pcm_runtime *runtime,
+                              struct urb *urb)
+{
+       int i, offs;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       offs = 0;
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       for (i = 0; i < ctx->packets; i++) {
+               urb->iso_frame_desc[i].offset = offs;
+               urb->iso_frame_desc[i].length = subs->curpacksize;
+               offs += subs->curpacksize;
+       }
+       urb->transfer_buffer_length = offs;
+       urb->number_of_packets = ctx->packets;
+       return 0;
+}
+
+/*
+ * process after capture complete
+ *
+ * copy the data from each desctiptor to the pcm buffer, and
+ * update the current position.
+ */
+static int retire_capture_urb(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime,
+                             struct urb *urb)
+{
+       unsigned long flags;
+       unsigned char *cp;
+       int i;
+       unsigned int stride, frames, bytes, oldptr;
+       int period_elapsed = 0;
+
+       stride = runtime->frame_bits >> 3;
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               if (urb->iso_frame_desc[i].status) {
+                       snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+                       // continue;
+               }
+               bytes = urb->iso_frame_desc[i].actual_length;
+               frames = bytes / stride;
+               if (!subs->txfr_quirk)
+                       bytes = frames * stride;
+               if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       int oldbytes = bytes;
+#endif
+                       bytes = frames * stride;
+                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+                                                       oldbytes, bytes);
+               }
+               /* update the current pointer */
+               spin_lock_irqsave(&subs->lock, flags);
+               oldptr = subs->hwptr_done;
+               subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
+               frames = (bytes + (oldptr % stride)) / stride;
+               subs->transfer_done += frames;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+               }
+               spin_unlock_irqrestore(&subs->lock, flags);
+               /* copy a data chunk */
+               if (oldptr + bytes > runtime->buffer_size * stride) {
+                       unsigned int bytes1 =
+                                       runtime->buffer_size * stride - oldptr;
+                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
+                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+               } else {
+                       memcpy(runtime->dma_area + oldptr, cp, bytes);
+               }
+       }
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+       return 0;
+}
+
+/*
+ * Process after capture complete when paused.  Nothing to do.
+ */
+static int retire_paused_capture_urb(struct snd_usb_substream *subs,
+                                    struct snd_pcm_runtime *runtime,
+                                    struct urb *urb)
+{
+       return 0;
+}
+
+
+/*
+ * prepare urb for full speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
+                                    struct snd_pcm_runtime *runtime,
+                                    struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
+       return 0;
+}
+
+/*
+ * prepare urb for high speed playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
+                                       struct snd_pcm_runtime *runtime,
+                                       struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
+       return 0;
+}
+
+/*
+ * process after full speed playback sync complete
+ *
+ * retrieve the current 10.14 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb(struct snd_usb_substream *subs,
+                                   struct snd_pcm_runtime *runtime,
+                                   struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 3) {
+               f = combine_triple((u8*)urb->transfer_buffer) << 2;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * process after high speed playback sync complete
+ *
+ * retrieve the current 12.13 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 4) {
+               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
+ *
+ * These devices return the number of samples per packet instead of the number
+ * of samples per microframe.
+ */
+static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
+                                          struct snd_pcm_runtime *runtime,
+                                          struct urb *urb)
+{
+       unsigned int f;
+       unsigned long flags;
+
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 4) {
+               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+               f >>= subs->datainterval;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
+       }
+
+       return 0;
+}
+
+/* determine the number of frames in the next packet */
+static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
+{
+       if (subs->fill_max)
+               return subs->maxframesize;
+       else {
+               subs->phase = (subs->phase & 0xffff)
+                       + (subs->freqm << subs->datainterval);
+               return min(subs->phase >> 16, subs->maxframesize);
+       }
+}
+
+/*
+ * Prepare urb for streaming before playback starts or when paused.
+ *
+ * We don't have any data, so we send silence.
+ */
+static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
+                                      struct snd_pcm_runtime *runtime,
+                                      struct urb *urb)
+{
+       unsigned int i, offs, counts;
+       struct snd_urb_ctx *ctx = urb->context;
+       int stride = runtime->frame_bits >> 3;
+
+       offs = 0;
+       urb->dev = ctx->subs->dev;
+       for (i = 0; i < ctx->packets; ++i) {
+               counts = snd_usb_audio_next_packet_size(subs);
+               urb->iso_frame_desc[i].offset = offs * stride;
+               urb->iso_frame_desc[i].length = counts * stride;
+               offs += counts;
+       }
+       urb->number_of_packets = ctx->packets;
+       urb->transfer_buffer_length = offs * stride;
+       memset(urb->transfer_buffer,
+              runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
+              offs * stride);
+       return 0;
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static int prepare_playback_urb(struct snd_usb_substream *subs,
+                               struct snd_pcm_runtime *runtime,
+                               struct urb *urb)
+{
+       int i, stride;
+       unsigned int counts, frames, bytes;
+       unsigned long flags;
+       int period_elapsed = 0;
+       struct snd_urb_ctx *ctx = urb->context;
+
+       stride = runtime->frame_bits >> 3;
+
+       frames = 0;
+       urb->dev = ctx->subs->dev; /* we need to set this at each time */
+       urb->number_of_packets = 0;
+       spin_lock_irqsave(&subs->lock, flags);
+       for (i = 0; i < ctx->packets; i++) {
+               counts = snd_usb_audio_next_packet_size(subs);
+               /* set up descriptor */
+               urb->iso_frame_desc[i].offset = frames * stride;
+               urb->iso_frame_desc[i].length = counts * stride;
+               frames += counts;
+               urb->number_of_packets++;
+               subs->transfer_done += counts;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+                               if (subs->transfer_done > 0) {
+                                       /* FIXME: fill-max mode is not
+                                        * supported yet */
+                                       frames -= subs->transfer_done;
+                                       counts -= subs->transfer_done;
+                                       urb->iso_frame_desc[i].length =
+                                               counts * stride;
+                                       subs->transfer_done = 0;
+                               }
+                               i++;
+                               if (i < ctx->packets) {
+                                       /* add a transfer delimiter */
+                                       urb->iso_frame_desc[i].offset =
+                                               frames * stride;
+                                       urb->iso_frame_desc[i].length = 0;
+                                       urb->number_of_packets++;
+                               }
+                               break;
+                       }
+               }
+               if (period_elapsed) /* finish at the period boundary */
+                       break;
+       }
+       bytes = frames * stride;
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + bytes1,
+                      runtime->dma_area, bytes - bytes1);
+       } else {
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes);
+       }
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+       runtime->delay += frames;
+       spin_unlock_irqrestore(&subs->lock, flags);
+       urb->transfer_buffer_length = bytes;
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+       return 0;
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static int retire_playback_urb(struct snd_usb_substream *subs,
+                              struct snd_pcm_runtime *runtime,
+                              struct urb *urb)
+{
+       unsigned long flags;
+       int stride = runtime->frame_bits >> 3;
+       int processed = urb->transfer_buffer_length / stride;
+
+       spin_lock_irqsave(&subs->lock, flags);
+       if (processed > runtime->delay)
+               runtime->delay = 0;
+       else
+               runtime->delay -= processed;
+       spin_unlock_irqrestore(&subs->lock, flags);
+       return 0;
+}
+
+static const char *usb_error_string(int err)
+{
+       switch (err) {
+       case -ENODEV:
+               return "no device";
+       case -ENOENT:
+               return "endpoint not enabled";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -ENOSPC:
+               return "not enough bandwidth";
+       case -ESHUTDOWN:
+               return "device disabled";
+       case -EHOSTUNREACH:
+               return "device suspended";
+       case -EINVAL:
+       case -EAGAIN:
+       case -EFBIG:
+       case -EMSGSIZE:
+               return "internal error";
+       default:
+               return "unknown error";
+       }
+}
+
+/*
+ * set up and start data/sync urbs
+ */
+static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
+{
+       unsigned int i;
+       int err;
+
+       if (subs->stream->chip->shutdown)
+               return -EBADFD;
+
+       for (i = 0; i < subs->nurbs; i++) {
+               if (snd_BUG_ON(!subs->dataurb[i].urb))
+                       return -EINVAL;
+               if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
+                       snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
+                       goto __error;
+               }
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       if (snd_BUG_ON(!subs->syncurb[i].urb))
+                               return -EINVAL;
+                       if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
+                               snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
+                               goto __error;
+                       }
+               }
+       }
+
+       subs->active_mask = 0;
+       subs->unlink_mask = 0;
+       subs->running = 1;
+       for (i = 0; i < subs->nurbs; i++) {
+               err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
+               if (err < 0) {
+                       snd_printk(KERN_ERR "cannot submit datapipe "
+                                  "for urb %d, error %d: %s\n",
+                                  i, err, usb_error_string(err));
+                       goto __error;
+               }
+               set_bit(i, &subs->active_mask);
+       }
+       if (subs->syncpipe) {
+               for (i = 0; i < SYNC_URBS; i++) {
+                       err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
+                       if (err < 0) {
+                               snd_printk(KERN_ERR "cannot submit syncpipe "
+                                          "for urb %d, error %d: %s\n",
+                                          i, err, usb_error_string(err));
+                               goto __error;
+                       }
+                       set_bit(i + 16, &subs->active_mask);
+               }
+       }
+       return 0;
+
+ __error:
+       // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
+       deactivate_urbs(subs, 0, 0);
+       return -EPIPE;
+}
+
+
+/*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+       {
+               .prepare =      prepare_nodata_playback_urb,
+               .retire =       retire_playback_urb,
+               .prepare_sync = prepare_playback_sync_urb,
+               .retire_sync =  retire_playback_sync_urb,
+       },
+       {
+               .prepare =      prepare_capture_urb,
+               .retire =       retire_capture_urb,
+               .prepare_sync = prepare_capture_sync_urb,
+               .retire_sync =  retire_capture_sync_urb,
+       },
+};
+
+static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
+       {
+               .prepare =      prepare_nodata_playback_urb,
+               .retire =       retire_playback_urb,
+               .prepare_sync = prepare_playback_sync_urb_hs,
+               .retire_sync =  retire_playback_sync_urb_hs,
+       },
+       {
+               .prepare =      prepare_capture_urb,
+               .retire =       retire_capture_urb,
+               .prepare_sync = prepare_capture_sync_urb_hs,
+               .retire_sync =  retire_capture_sync_urb,
+       },
+};
+
+/*
+ * initialize the substream instance.
+ */
+
+void snd_usb_init_substream(struct snd_usb_stream *as,
+                           int stream, struct audioformat *fp)
+{
+       struct snd_usb_substream *subs = &as->substream[stream];
+
+       INIT_LIST_HEAD(&subs->fmt_list);
+       spin_lock_init(&subs->lock);
+
+       subs->stream = as;
+       subs->direction = stream;
+       subs->dev = as->chip->dev;
+       subs->txfr_quirk = as->chip->txfr_quirk;
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
+               subs->ops = audio_urb_ops[stream];
+       } else {
+               subs->ops = audio_urb_ops_high_speed[stream];
+               switch (as->chip->usb_id) {
+               case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+               case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+               case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+                       subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
+                       break;
+               case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra 8  */
+               case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+                       subs->ops.prepare_sync = prepare_playback_sync_urb;
+                       subs->ops.retire_sync = retire_playback_sync_urb;
+                       break;
+               }
+       }
+
+       snd_usb_set_pcm_ops(as->pcm, stream);
+
+       list_add_tail(&fp->list, &subs->fmt_list);
+       subs->formats |= fp->formats;
+       subs->endpoint = fp->endpoint;
+       subs->num_formats++;
+       subs->fmt_type = fp->fmt_type;
+}
+
+int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->ops.prepare = prepare_playback_urb;
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.prepare = prepare_nodata_playback_urb;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               subs->ops.retire = retire_capture_urb;
+               return start_urbs(subs, substream->runtime);
+       case SNDRV_PCM_TRIGGER_STOP:
+               return deactivate_urbs(subs, 0, 0);
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->ops.retire = retire_paused_capture_urb;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->ops.retire = retire_capture_urb;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int snd_usb_substream_prepare(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime)
+{
+       /* clear urbs (to be sure) */
+       deactivate_urbs(subs, 0, 1);
+       wait_clear_urbs(subs);
+
+       /* for playback, submit the URBs now; otherwise, the first hwptr_done
+        * updates for all URBs would happen at the same time when starting */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               subs->ops.prepare = prepare_nodata_playback_urb;
+               return start_urbs(subs, runtime);
+       }
+
+       return 0;
+}
+
diff --git a/sound/usb/urb.h b/sound/usb/urb.h
new file mode 100644 (file)
index 0000000..888da38
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef __USBAUDIO_URB_H
+#define __USBAUDIO_URB_H
+
+void snd_usb_init_substream(struct snd_usb_stream *as,
+                           int stream,
+                           struct audioformat *fp);
+
+int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
+                               unsigned int period_bytes,
+                               unsigned int rate,
+                               unsigned int frame_bits);
+
+void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
+
+int snd_usb_substream_prepare(struct snd_usb_substream *subs,
+                             struct snd_pcm_runtime *runtime);
+
+int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+
+#endif /* __USBAUDIO_URB_H */
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
deleted file mode 100644 (file)
index 11b0826..0000000
+++ /dev/null
@@ -1,4050 +0,0 @@
-/*
- *   (Tentative) USB Audio Driver for ALSA
- *
- *   Main and PCM part
- *
- *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
- *
- *   Many codes borrowed from audio.c by
- *         Alan Cox (alan@lxorguk.ukuu.org.uk)
- *         Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- *
- *  NOTES:
- *
- *   - async unlink should be used for avoiding the sleep inside lock.
- *     2.4.22 usb-uhci seems buggy for async unlinking and results in
- *     oops.  in such a cse, pass async_unlink=0 option.
- *   - the linked URBs would be preferred but not used so far because of
- *     the instability of unlinking.
- *   - type II is not supported properly.  there is no device which supports
- *     this type *correctly*.  SB extigy looks as if it supports, but it's
- *     indeed an AC3 stream packed in SPDIF frames (i.e. no real AC3 stream).
- */
-
-
-#include <linux/bitops.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/usb.h>
-#include <linux/moduleparam.h>
-#include <linux/mutex.h>
-#include <linux/usb/audio.h>
-#include <linux/usb/ch9.h>
-
-#include <sound/core.h>
-#include <sound/info.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-
-#include "usbaudio.h"
-
-
-MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
-MODULE_DESCRIPTION("USB Audio");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}");
-
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
-/* Vendor/product IDs for this card */
-static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
-static int nrpacks = 8;                /* max. number of packets per urb */
-static int async_unlink = 1;
-static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/
-static int ignore_ctl_error;
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for the USB audio adapter.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable USB audio adapter.");
-module_param_array(vid, int, NULL, 0444);
-MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device.");
-module_param_array(pid, int, NULL, 0444);
-MODULE_PARM_DESC(pid, "Product ID for the USB audio device.");
-module_param(nrpacks, int, 0644);
-MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB.");
-module_param(async_unlink, bool, 0444);
-MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
-module_param_array(device_setup, int, NULL, 0444);
-MODULE_PARM_DESC(device_setup, "Specific device setup (if needed).");
-module_param(ignore_ctl_error, bool, 0444);
-MODULE_PARM_DESC(ignore_ctl_error,
-                "Ignore errors from USB controller for mixer interfaces.");
-
-/*
- * debug the h/w constraints
- */
-/* #define HW_CONST_DEBUG */
-
-
-/*
- *
- */
-
-#define MAX_PACKS      20
-#define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
-#define MAX_URBS       8
-#define SYNC_URBS      4       /* always four urbs for sync */
-#define MAX_QUEUE      24      /* try not to exceed this queue length, in ms */
-
-struct audioformat {
-       struct list_head list;
-       snd_pcm_format_t format;        /* format type */
-       unsigned int channels;          /* # channels */
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-       unsigned int frame_size;        /* samples per frame for non-audio */
-       int iface;                      /* interface number */
-       unsigned char altsetting;       /* corresponding alternate setting */
-       unsigned char altset_idx;       /* array index of altenate setting */
-       unsigned char attributes;       /* corresponding attributes of cs endpoint */
-       unsigned char endpoint;         /* endpoint */
-       unsigned char ep_attr;          /* endpoint attributes */
-       unsigned char datainterval;     /* log_2 of data packet interval */
-       unsigned int maxpacksize;       /* max. packet size */
-       unsigned int rates;             /* rate bitmasks */
-       unsigned int rate_min, rate_max;        /* min/max rates */
-       unsigned int nr_rates;          /* number of rate table entries */
-       unsigned int *rate_table;       /* rate table */
-};
-
-struct snd_usb_substream;
-
-struct snd_urb_ctx {
-       struct urb *urb;
-       unsigned int buffer_size;       /* size of data buffer, if data URB */
-       struct snd_usb_substream *subs;
-       int index;      /* index for urb array */
-       int packets;    /* number of packets per urb */
-};
-
-struct snd_urb_ops {
-       int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-};
-
-struct snd_usb_substream {
-       struct snd_usb_stream *stream;
-       struct usb_device *dev;
-       struct snd_pcm_substream *pcm_substream;
-       int direction;  /* playback or capture */
-       int interface;  /* current interface */
-       int endpoint;   /* assigned endpoint */
-       struct audioformat *cur_audiofmt;       /* current audioformat pointer (for hw_params callback) */
-       unsigned int cur_rate;          /* current rate (for hw_params callback) */
-       unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
-       unsigned int format;     /* USB data format */
-       unsigned int datapipe;   /* the data i/o pipe */
-       unsigned int syncpipe;   /* 1 - async out or adaptive in */
-       unsigned int datainterval;      /* log_2 of data packet interval */
-       unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
-       unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
-       unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
-       unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
-       unsigned int phase;      /* phase accumulator */
-       unsigned int maxpacksize;       /* max packet size in bytes */
-       unsigned int maxframesize;      /* max packet size in frames */
-       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
-       unsigned int curframesize;      /* current packet size in frames (for capture) */
-       unsigned int fill_max: 1;       /* fill max packet size always */
-       unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-
-       unsigned int running: 1;        /* running status */
-
-       unsigned int hwptr_done;        /* processed byte position in the buffer */
-       unsigned int transfer_done;             /* processed frames since last period update */
-       unsigned long active_mask;      /* bitmask of active urbs */
-       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
-
-       unsigned int nurbs;                     /* # urbs */
-       struct snd_urb_ctx dataurb[MAX_URBS];   /* data urb table */
-       struct snd_urb_ctx syncurb[SYNC_URBS];  /* sync urb table */
-       char *syncbuf;                          /* sync buffer for all sync URBs */
-       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
-
-       u64 formats;                    /* format bitmasks (all or'ed) */
-       unsigned int num_formats;               /* number of supported audio formats (list) */
-       struct list_head fmt_list;      /* format list */
-       struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
-       spinlock_t lock;
-
-       struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
-};
-
-
-struct snd_usb_stream {
-       struct snd_usb_audio *chip;
-       struct snd_pcm *pcm;
-       int pcm_index;
-       unsigned int fmt_type;          /* USB audio format type (1-3) */
-       struct snd_usb_substream substream[2];
-       struct list_head list;
-};
-
-
-/*
- * we keep the snd_usb_audio_t instances by ourselves for merging
- * the all interfaces on the same card as one sound device.
- */
-
-static DEFINE_MUTEX(register_mutex);
-static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
-
-
-/*
- * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
- * this will overflow at approx 524 kHz
- */
-static inline unsigned get_usb_full_speed_rate(unsigned int rate)
-{
-       return ((rate << 13) + 62) / 125;
-}
-
-/*
- * convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
- * this will overflow at approx 4 MHz
- */
-static inline unsigned get_usb_high_speed_rate(unsigned int rate)
-{
-       return ((rate << 10) + 62) / 125;
-}
-
-/* convert our full speed USB rate into sampling rate in Hz */
-static inline unsigned get_full_speed_hz(unsigned int usb_rate)
-{
-       return (usb_rate * 125 + (1 << 12)) >> 13;
-}
-
-/* convert our high speed USB rate into sampling rate in Hz */
-static inline unsigned get_high_speed_hz(unsigned int usb_rate)
-{
-       return (usb_rate * 125 + (1 << 9)) >> 10;
-}
-
-
-/*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
- */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
-{
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 3;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn >> 2;
-       cp[1] = subs->freqn >> 10;
-       cp[2] = subs->freqn >> 18;
-       return 0;
-}
-
-/*
- * prepare urb for high speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
- */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 4;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn;
-       cp[1] = subs->freqn >> 8;
-       cp[2] = subs->freqn >> 16;
-       cp[3] = subs->freqn >> 24;
-       return 0;
-}
-
-/*
- * process after capture sync complete
- * - nothing to do
- */
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
-                                  struct snd_pcm_runtime *runtime,
-                                  struct urb *urb)
-{
-       return 0;
-}
-
-/*
- * prepare urb for capture data pipe
- *
- * fill the offset and length of each descriptor.
- *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly...  the data is thus copied
- * later at complete callback to the global buffer.
- */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
-{
-       int i, offs;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       offs = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = 0; i < ctx->packets; i++) {
-               urb->iso_frame_desc[i].offset = offs;
-               urb->iso_frame_desc[i].length = subs->curpacksize;
-               offs += subs->curpacksize;
-       }
-       urb->transfer_buffer_length = offs;
-       urb->number_of_packets = ctx->packets;
-       return 0;
-}
-
-/*
- * process after capture complete
- *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
- */
-static int retire_capture_urb(struct snd_usb_substream *subs,
-                             struct snd_pcm_runtime *runtime,
-                             struct urb *urb)
-{
-       unsigned long flags;
-       unsigned char *cp;
-       int i;
-       unsigned int stride, frames, bytes, oldptr;
-       int period_elapsed = 0;
-
-       stride = runtime->frame_bits >> 3;
-
-       for (i = 0; i < urb->number_of_packets; i++) {
-               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-               if (urb->iso_frame_desc[i].status) {
-                       snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
-                       // continue;
-               }
-               bytes = urb->iso_frame_desc[i].actual_length;
-               frames = bytes / stride;
-               if (!subs->txfr_quirk)
-                       bytes = frames * stride;
-               if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       int oldbytes = bytes;
-#endif
-                       bytes = frames * stride;
-                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
-                                                       oldbytes, bytes);
-               }
-               /* update the current pointer */
-               spin_lock_irqsave(&subs->lock, flags);
-               oldptr = subs->hwptr_done;
-               subs->hwptr_done += bytes;
-               if (subs->hwptr_done >= runtime->buffer_size * stride)
-                       subs->hwptr_done -= runtime->buffer_size * stride;
-               frames = (bytes + (oldptr % stride)) / stride;
-               subs->transfer_done += frames;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-               }
-               spin_unlock_irqrestore(&subs->lock, flags);
-               /* copy a data chunk */
-               if (oldptr + bytes > runtime->buffer_size * stride) {
-                       unsigned int bytes1 =
-                                       runtime->buffer_size * stride - oldptr;
-                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
-                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
-               } else {
-                       memcpy(runtime->dma_area + oldptr, cp, bytes);
-               }
-       }
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
-}
-
-/*
- * Process after capture complete when paused.  Nothing to do.
- */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
-{
-       return 0;
-}
-
-
-/*
- * prepare urb for full speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 3;
-       urb->iso_frame_desc[0].offset = 0;
-       return 0;
-}
-
-/*
- * prepare urb for high speed playback sync pipe
- *
- * set up the offset and length to receive the current frequency.
- */
-
-static int prepare_playback_sync_urb_hs(struct snd_usb_substream *subs,
-                                       struct snd_pcm_runtime *runtime,
-                                       struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 4;
-       urb->iso_frame_desc[0].offset = 0;
-       return 0;
-}
-
-/*
- * process after full speed playback sync complete
- *
- * retrieve the current 10.14 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 3) {
-               f = combine_triple((u8*)urb->transfer_buffer) << 2;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * process after high speed playback sync complete
- *
- * retrieve the current 12.13 frequency from pipe, and set it.
- * the value is referred in prepare_playback_urb().
- */
-static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 4) {
-               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/*
- * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete
- *
- * These devices return the number of samples per packet instead of the number
- * of samples per microframe.
- */
-static int retire_playback_sync_urb_hs_emu(struct snd_usb_substream *subs,
-                                          struct snd_pcm_runtime *runtime,
-                                          struct urb *urb)
-{
-       unsigned int f;
-       unsigned long flags;
-
-       if (urb->iso_frame_desc[0].status == 0 &&
-           urb->iso_frame_desc[0].actual_length == 4) {
-               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
-               f >>= subs->datainterval;
-               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
-                       spin_lock_irqsave(&subs->lock, flags);
-                       subs->freqm = f;
-                       spin_unlock_irqrestore(&subs->lock, flags);
-               }
-       }
-
-       return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
-       if (subs->fill_max)
-               return subs->maxframesize;
-       else {
-               subs->phase = (subs->phase & 0xffff)
-                       + (subs->freqm << subs->datainterval);
-               return min(subs->phase >> 16, subs->maxframesize);
-       }
-}
-
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned int i, offs, counts;
-       struct snd_urb_ctx *ctx = urb->context;
-       int stride = runtime->frame_bits >> 3;
-
-       offs = 0;
-       urb->dev = ctx->subs->dev;
-       for (i = 0; i < ctx->packets; ++i) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               urb->iso_frame_desc[i].offset = offs * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               offs += counts;
-       }
-       urb->number_of_packets = ctx->packets;
-       urb->transfer_buffer_length = offs * stride;
-       memset(urb->transfer_buffer,
-              subs->cur_audiofmt->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
-              offs * stride);
-       return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
-                               struct snd_pcm_runtime *runtime,
-                               struct urb *urb)
-{
-       int i, stride;
-       unsigned int counts, frames, bytes;
-       unsigned long flags;
-       int period_elapsed = 0;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       stride = runtime->frame_bits >> 3;
-
-       frames = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->number_of_packets = 0;
-       spin_lock_irqsave(&subs->lock, flags);
-       for (i = 0; i < ctx->packets; i++) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               /* set up descriptor */
-               urb->iso_frame_desc[i].offset = frames * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               frames += counts;
-               urb->number_of_packets++;
-               subs->transfer_done += counts;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-                               if (subs->transfer_done > 0) {
-                                       /* FIXME: fill-max mode is not
-                                        * supported yet */
-                                       frames -= subs->transfer_done;
-                                       counts -= subs->transfer_done;
-                                       urb->iso_frame_desc[i].length =
-                                               counts * stride;
-                                       subs->transfer_done = 0;
-                               }
-                               i++;
-                               if (i < ctx->packets) {
-                                       /* add a transfer delimiter */
-                                       urb->iso_frame_desc[i].offset =
-                                               frames * stride;
-                                       urb->iso_frame_desc[i].length = 0;
-                                       urb->number_of_packets++;
-                               }
-                               break;
-                       }
-               }
-               if (period_elapsed) /* finish at the period boundary */
-                       break;
-       }
-       bytes = frames * stride;
-       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-               /* err, the transferred area goes over buffer boundary. */
-               unsigned int bytes1 =
-                       runtime->buffer_size * stride - subs->hwptr_done;
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes1);
-               memcpy(urb->transfer_buffer + bytes1,
-                      runtime->dma_area, bytes - bytes1);
-       } else {
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes);
-       }
-       subs->hwptr_done += bytes;
-       if (subs->hwptr_done >= runtime->buffer_size * stride)
-               subs->hwptr_done -= runtime->buffer_size * stride;
-       runtime->delay += frames;
-       spin_unlock_irqrestore(&subs->lock, flags);
-       urb->transfer_buffer_length = bytes;
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
-{
-       unsigned long flags;
-       int stride = runtime->frame_bits >> 3;
-       int processed = urb->transfer_buffer_length / stride;
-
-       spin_lock_irqsave(&subs->lock, flags);
-       if (processed > runtime->delay)
-               runtime->delay = 0;
-       else
-               runtime->delay -= processed;
-       spin_unlock_irqrestore(&subs->lock, flags);
-       return 0;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
-       {
-               .prepare =      prepare_nodata_playback_urb,
-               .retire =       retire_playback_urb,
-               .prepare_sync = prepare_playback_sync_urb,
-               .retire_sync =  retire_playback_sync_urb,
-       },
-       {
-               .prepare =      prepare_capture_urb,
-               .retire =       retire_capture_urb,
-               .prepare_sync = prepare_capture_sync_urb,
-               .retire_sync =  retire_capture_sync_urb,
-       },
-};
-
-static struct snd_urb_ops audio_urb_ops_high_speed[2] = {
-       {
-               .prepare =      prepare_nodata_playback_urb,
-               .retire =       retire_playback_urb,
-               .prepare_sync = prepare_playback_sync_urb_hs,
-               .retire_sync =  retire_playback_sync_urb_hs,
-       },
-       {
-               .prepare =      prepare_capture_urb,
-               .retire =       retire_capture_urb,
-               .prepare_sync = prepare_capture_sync_urb_hs,
-               .retire_sync =  retire_capture_sync_urb,
-       },
-};
-
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-               }
-       }
-}
-
-
-/*
- * complete callback from sync urb
- */
-static void snd_complete_sync_urb(struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index + 16, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-               }
-       }
-}
-
-
-/*
- * unlink active urbs.
- */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
-{
-       unsigned int i;
-       int async;
-
-       subs->running = 0;
-
-       if (!force && subs->stream->chip->shutdown) /* to be sure... */
-               return -EBADFD;
-
-       async = !can_sleep && async_unlink;
-
-       if (!async && in_interrupt())
-               return 0;
-
-       for (i = 0; i < subs->nurbs; i++) {
-               if (test_bit(i, &subs->active_mask)) {
-                       if (!test_and_set_bit(i, &subs->unlink_mask)) {
-                               struct urb *u = subs->dataurb[i].urb;
-                               if (async)
-                                       usb_unlink_urb(u);
-                               else
-                                       usb_kill_urb(u);
-                       }
-               }
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (test_bit(i+16, &subs->active_mask)) {
-                               if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
-                                       struct urb *u = subs->syncurb[i].urb;
-                                       if (async)
-                                               usb_unlink_urb(u);
-                                       else
-                                               usb_kill_urb(u);
-                               }
-                       }
-               }
-       }
-       return 0;
-}
-
-
-static const char *usb_error_string(int err)
-{
-       switch (err) {
-       case -ENODEV:
-               return "no device";
-       case -ENOENT:
-               return "endpoint not enabled";
-       case -EPIPE:
-               return "endpoint stalled";
-       case -ENOSPC:
-               return "not enough bandwidth";
-       case -ESHUTDOWN:
-               return "device disabled";
-       case -EHOSTUNREACH:
-               return "device suspended";
-       case -EINVAL:
-       case -EAGAIN:
-       case -EFBIG:
-       case -EMSGSIZE:
-               return "internal error";
-       default:
-               return "unknown error";
-       }
-}
-
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
-       unsigned int i;
-       int err;
-
-       if (subs->stream->chip->shutdown)
-               return -EBADFD;
-
-       for (i = 0; i < subs->nurbs; i++) {
-               if (snd_BUG_ON(!subs->dataurb[i].urb))
-                       return -EINVAL;
-               if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
-                       snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
-                       goto __error;
-               }
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (snd_BUG_ON(!subs->syncurb[i].urb))
-                               return -EINVAL;
-                       if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
-                               snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
-                               goto __error;
-                       }
-               }
-       }
-
-       subs->active_mask = 0;
-       subs->unlink_mask = 0;
-       subs->running = 1;
-       for (i = 0; i < subs->nurbs; i++) {
-               err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
-               if (err < 0) {
-                       snd_printk(KERN_ERR "cannot submit datapipe "
-                                  "for urb %d, error %d: %s\n",
-                                  i, err, usb_error_string(err));
-                       goto __error;
-               }
-               set_bit(i, &subs->active_mask);
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
-                       if (err < 0) {
-                               snd_printk(KERN_ERR "cannot submit syncpipe "
-                                          "for urb %d, error %d: %s\n",
-                                          i, err, usb_error_string(err));
-                               goto __error;
-                       }
-                       set_bit(i + 16, &subs->active_mask);
-               }
-       }
-       return 0;
-
- __error:
-       // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-       deactivate_urbs(subs, 0, 0);
-       return -EPIPE;
-}
-
-
-/*
- *  wait until all urbs are processed.
- */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
-{
-       unsigned long end_time = jiffies + msecs_to_jiffies(1000);
-       unsigned int i;
-       int alive;
-
-       do {
-               alive = 0;
-               for (i = 0; i < subs->nurbs; i++) {
-                       if (test_bit(i, &subs->active_mask))
-                               alive++;
-               }
-               if (subs->syncpipe) {
-                       for (i = 0; i < SYNC_URBS; i++) {
-                               if (test_bit(i + 16, &subs->active_mask))
-                                       alive++;
-                       }
-               }
-               if (! alive)
-                       break;
-               schedule_timeout_uninterruptible(1);
-       } while (time_before(jiffies, end_time));
-       if (alive)
-               snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-       return 0;
-}
-
-
-/*
- * return the current pcm pointer.  just based on the hwptr_done value.
- */
-static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_usb_substream *subs;
-       unsigned int hwptr_done;
-       
-       subs = (struct snd_usb_substream *)substream->runtime->private_data;
-       spin_lock(&subs->lock);
-       hwptr_done = subs->hwptr_done;
-       spin_unlock(&subs->lock);
-       return hwptr_done / (substream->runtime->frame_bits >> 3);
-}
-
-
-/*
- * start/stop playback substream
- */
-static int snd_usb_pcm_playback_trigger(struct snd_pcm_substream *substream,
-                                       int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.prepare = prepare_playback_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-/*
- * start/stop capture substream
- */
-static int snd_usb_pcm_capture_trigger(struct snd_pcm_substream *substream,
-                                      int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               subs->ops.retire = retire_capture_urb;
-               return start_urbs(subs, substream->runtime);
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.retire = retire_paused_capture_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.retire = retire_capture_urb;
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-
-/*
- * release a urb data
- */
-static void release_urb_ctx(struct snd_urb_ctx *u)
-{
-       if (u->urb) {
-               if (u->buffer_size)
-                       usb_buffer_free(u->subs->dev, u->buffer_size,
-                                       u->urb->transfer_buffer,
-                                       u->urb->transfer_dma);
-               usb_free_urb(u->urb);
-               u->urb = NULL;
-       }
-}
-
-/*
- * release a substream
- */
-static void release_substream_urbs(struct snd_usb_substream *subs, int force)
-{
-       int i;
-
-       /* stop urbs (to be sure) */
-       deactivate_urbs(subs, force, 1);
-       wait_clear_urbs(subs);
-
-       for (i = 0; i < MAX_URBS; i++)
-               release_urb_ctx(&subs->dataurb[i]);
-       for (i = 0; i < SYNC_URBS; i++)
-               release_urb_ctx(&subs->syncurb[i]);
-       usb_buffer_free(subs->dev, SYNC_URBS * 4,
-                       subs->syncbuf, subs->sync_dma);
-       subs->syncbuf = NULL;
-       subs->nurbs = 0;
-}
-
-/*
- * initialize a substream for plaback/capture
- */
-static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int period_bytes,
-                              unsigned int rate, unsigned int frame_bits)
-{
-       unsigned int maxsize, i;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       unsigned int urb_packs, total_packs, packs_per_ms;
-
-       /* calculate the frequency in 16.16 format */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-               subs->freqn = get_usb_full_speed_rate(rate);
-       else
-               subs->freqn = get_usb_high_speed_rate(rate);
-       subs->freqm = subs->freqn;
-       /* calculate max. frequency */
-       if (subs->maxpacksize) {
-               /* whatever fits into a max. size packet */
-               maxsize = subs->maxpacksize;
-               subs->freqmax = (maxsize / (frame_bits >> 3))
-                               << (16 - subs->datainterval);
-       } else {
-               /* no max. packet size: just take 25% higher than nominal */
-               subs->freqmax = subs->freqn + (subs->freqn >> 2);
-               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
-                               >> (16 - subs->datainterval);
-       }
-       subs->phase = 0;
-
-       if (subs->fill_max)
-               subs->curpacksize = subs->maxpacksize;
-       else
-               subs->curpacksize = maxsize;
-
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-               packs_per_ms = 8 >> subs->datainterval;
-       else
-               packs_per_ms = 1;
-
-       if (is_playback) {
-               urb_packs = max(nrpacks, 1);
-               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
-       } else
-               urb_packs = 1;
-       urb_packs *= packs_per_ms;
-       if (subs->syncpipe)
-               urb_packs = min(urb_packs, 1U << subs->syncinterval);
-
-       /* decide how many packets to be used */
-       if (is_playback) {
-               unsigned int minsize, maxpacks;
-               /* determine how small a packet can be */
-               minsize = (subs->freqn >> (16 - subs->datainterval))
-                         * (frame_bits >> 3);
-               /* with sync from device, assume it can be 12% lower */
-               if (subs->syncpipe)
-                       minsize -= minsize >> 3;
-               minsize = max(minsize, 1u);
-               total_packs = (period_bytes + minsize - 1) / minsize;
-               /* we need at least two URBs for queueing */
-               if (total_packs < 2) {
-                       total_packs = 2;
-               } else {
-                       /* and we don't want too long a queue either */
-                       maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
-                       total_packs = min(total_packs, maxpacks);
-               }
-       } else {
-               while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
-                       urb_packs >>= 1;
-               total_packs = MAX_URBS * urb_packs;
-       }
-       subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-       if (subs->nurbs > MAX_URBS) {
-               /* too much... */
-               subs->nurbs = MAX_URBS;
-               total_packs = MAX_URBS * urb_packs;
-       } else if (subs->nurbs < 2) {
-               /* too little - we need at least two packets
-                * to ensure contiguous playback/capture
-                */
-               subs->nurbs = 2;
-       }
-
-       /* allocate and initialize data urbs */
-       for (i = 0; i < subs->nurbs; i++) {
-               struct snd_urb_ctx *u = &subs->dataurb[i];
-               u->index = i;
-               u->subs = subs;
-               u->packets = (i + 1) * total_packs / subs->nurbs
-                       - i * total_packs / subs->nurbs;
-               u->buffer_size = maxsize * u->packets;
-               if (subs->fmt_type == UAC_FORMAT_TYPE_II)
-                       u->packets++; /* for transfer delimiter */
-               u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
-               if (!u->urb)
-                       goto out_of_memory;
-               u->urb->transfer_buffer =
-                       usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL,
-                                        &u->urb->transfer_dma);
-               if (!u->urb->transfer_buffer)
-                       goto out_of_memory;
-               u->urb->pipe = subs->datapipe;
-               u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-               u->urb->interval = 1 << subs->datainterval;
-               u->urb->context = u;
-               u->urb->complete = snd_complete_urb;
-       }
-
-       if (subs->syncpipe) {
-               /* allocate and initialize sync urbs */
-               subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4,
-                                                GFP_KERNEL, &subs->sync_dma);
-               if (!subs->syncbuf)
-                       goto out_of_memory;
-               for (i = 0; i < SYNC_URBS; i++) {
-                       struct snd_urb_ctx *u = &subs->syncurb[i];
-                       u->index = i;
-                       u->subs = subs;
-                       u->packets = 1;
-                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
-                       if (!u->urb)
-                               goto out_of_memory;
-                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
-                       u->urb->transfer_dma = subs->sync_dma + i * 4;
-                       u->urb->transfer_buffer_length = 4;
-                       u->urb->pipe = subs->syncpipe;
-                       u->urb->transfer_flags = URB_ISO_ASAP |
-                                                URB_NO_TRANSFER_DMA_MAP;
-                       u->urb->number_of_packets = 1;
-                       u->urb->interval = 1 << subs->syncinterval;
-                       u->urb->context = u;
-                       u->urb->complete = snd_complete_sync_urb;
-               }
-       }
-       return 0;
-
-out_of_memory:
-       release_substream_urbs(subs, 0);
-       return -ENOMEM;
-}
-
-
-/*
- * find a matching audio format
- */
-static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned int format,
-                                      unsigned int rate, unsigned int channels)
-{
-       struct list_head *p;
-       struct audioformat *found = NULL;
-       int cur_attr = 0, attr;
-
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (fp->format != format || fp->channels != channels)
-                       continue;
-               if (rate < fp->rate_min || rate > fp->rate_max)
-                       continue;
-               if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
-                       unsigned int i;
-                       for (i = 0; i < fp->nr_rates; i++)
-                               if (fp->rate_table[i] == rate)
-                                       break;
-                       if (i >= fp->nr_rates)
-                               continue;
-               }
-               attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
-               if (! found) {
-                       found = fp;
-                       cur_attr = attr;
-                       continue;
-               }
-               /* avoid async out and adaptive in if the other method
-                * supports the same format.
-                * this is a workaround for the case like
-                * M-audio audiophile USB.
-                */
-               if (attr != cur_attr) {
-                       if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
-                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
-                           (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
-                            subs->direction == SNDRV_PCM_STREAM_CAPTURE))
-                               continue;
-                       if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
-                            subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
-                           (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
-                            subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
-                               found = fp;
-                               cur_attr = attr;
-                               continue;
-                       }
-               }
-               /* find the format with the largest max. packet size */
-               if (fp->maxpacksize > found->maxpacksize) {
-                       found = fp;
-                       cur_attr = attr;
-               }
-       }
-       return found;
-}
-
-
-/*
- * initialize the picth control and sample rate
- */
-static int init_usb_pitch(struct usb_device *dev, int iface,
-                         struct usb_host_interface *alts,
-                         struct audioformat *fmt)
-{
-       unsigned int ep;
-       unsigned char data[1];
-       int err;
-
-       ep = get_endpoint(alts, 0)->bEndpointAddress;
-       /* if endpoint has pitch control, enable it */
-       if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) {
-               data[0] = 1;
-               if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-                                          UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
-                                  dev->devnum, iface, ep);
-                       return err;
-               }
-       }
-       return 0;
-}
-
-static int init_usb_sample_rate(struct usb_device *dev, int iface,
-                               struct usb_host_interface *alts,
-                               struct audioformat *fmt, int rate)
-{
-       unsigned int ep;
-       unsigned char data[3];
-       int err;
-
-       ep = get_endpoint(alts, 0)->bEndpointAddress;
-       /* if endpoint has sampling rate control, set it */
-       if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) {
-               int crate;
-               data[0] = rate;
-               data[1] = rate >> 8;
-               data[2] = rate >> 16;
-               if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
-                                          UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
-                                  dev->devnum, iface, fmt->altsetting, rate, ep);
-                       return err;
-               }
-               if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
-                                          USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
-                                          UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep, data, 3, 1000)) < 0) {
-                       snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
-                                  dev->devnum, iface, fmt->altsetting, ep);
-                       return 0; /* some devices don't support reading */
-               }
-               crate = data[0] | (data[1] << 8) | (data[2] << 16);
-               if (crate != rate) {
-                       snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
-                       // runtime->rate = crate;
-               }
-       }
-       return 0;
-}
-
-/*
- * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
- * not for interface.
- */
-static void set_format_emu_quirk(struct snd_usb_substream *subs,
-                                struct audioformat *fmt)
-{
-       unsigned char emu_samplerate_id = 0;
-
-       /* When capture is active
-        * sample rate shouldn't be changed
-        * by playback substream
-        */
-       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
-                       return;
-       }
-
-       switch (fmt->rate_min) {
-       case 48000:
-               emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
-               break;
-       case 88200:
-               emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
-               break;
-       case 96000:
-               emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
-               break;
-       case 176400:
-               emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
-               break;
-       case 192000:
-               emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
-               break;
-       default:
-               emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
-               break;
-       }
-       snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
-}
-
-/*
- * find a matching format and set up the interface
- */
-static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
-{
-       struct usb_device *dev = subs->dev;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct usb_interface *iface;
-       unsigned int ep, attr;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       int err;
-
-       iface = usb_ifnum_to_if(dev, fmt->iface);
-       if (WARN_ON(!iface))
-               return -EINVAL;
-       alts = &iface->altsetting[fmt->altset_idx];
-       altsd = get_iface_desc(alts);
-       if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting))
-               return -EINVAL;
-
-       if (fmt == subs->cur_audiofmt)
-               return 0;
-
-       /* close the old interface */
-       if (subs->interface >= 0 && subs->interface != fmt->iface) {
-               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
-                               dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               subs->interface = -1;
-               subs->format = 0;
-       }
-
-       /* set interface */
-       if (subs->interface != fmt->iface || subs->format != fmt->altset_idx) {
-               if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
-               subs->interface = fmt->iface;
-               subs->format = fmt->altset_idx;
-       }
-
-       /* create a data pipe */
-       ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
-       if (is_playback)
-               subs->datapipe = usb_sndisocpipe(dev, ep);
-       else
-               subs->datapipe = usb_rcvisocpipe(dev, ep);
-       subs->datainterval = fmt->datainterval;
-       subs->syncpipe = subs->syncinterval = 0;
-       subs->maxpacksize = fmt->maxpacksize;
-       subs->fill_max = 0;
-
-       /* we need a sync pipe in async OUT or adaptive IN mode */
-       /* check the number of EP, since some devices have broken
-        * descriptors which fool us.  if it has only one EP,
-        * assume it as adaptive-out or sync-in.
-        */
-       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
-       if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-            (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
-           altsd->bNumEndpoints >= 2) {
-               /* check sync-pipe endpoint */
-               /* ... and check descriptor size before accessing bSynchAddress
-                  because there is a version of the SB Audigy 2 NX firmware lacking
-                  the audio fields in the endpoint descriptors */
-               if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
-                   (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                    get_endpoint(alts, 1)->bSynchAddress != 0)) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EINVAL;
-               }
-               ep = get_endpoint(alts, 1)->bEndpointAddress;
-               if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EINVAL;
-               }
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               if (is_playback)
-                       subs->syncpipe = usb_rcvisocpipe(dev, ep);
-               else
-                       subs->syncpipe = usb_sndisocpipe(dev, ep);
-               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   get_endpoint(alts, 1)->bRefresh >= 1 &&
-                   get_endpoint(alts, 1)->bRefresh <= 9)
-                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
-               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-                       subs->syncinterval = 1;
-               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
-                        get_endpoint(alts, 1)->bInterval <= 16)
-                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
-               else
-                       subs->syncinterval = 3;
-       }
-
-       /* always fill max packet size */
-       if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
-               subs->fill_max = 1;
-
-       if ((err = init_usb_pitch(dev, subs->interface, alts, fmt)) < 0)
-               return err;
-
-       subs->cur_audiofmt = fmt;
-
-       switch (subs->stream->chip->usb_id) {
-       case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
-       case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
-       case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
-               set_format_emu_quirk(subs, fmt);
-               break;
-       }
-
-#if 0
-       printk(KERN_DEBUG
-              "setting done: format = %d, rate = %d..%d, channels = %d\n",
-              fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
-       printk(KERN_DEBUG
-              "  datapipe = 0x%0x, syncpipe = 0x%0x\n",
-              subs->datapipe, subs->syncpipe);
-#endif
-
-       return 0;
-}
-
-/*
- * hw_params callback
- *
- * allocate a buffer and set the given audio format.
- *
- * so far we use a physically linear buffer although packetize transfer
- * doesn't need a continuous area.
- * if sg buffer is supported on the later version of alsa, we'll follow
- * that.
- */
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-       struct audioformat *fmt;
-       unsigned int channels, rate, format;
-       int ret, changed;
-
-       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (ret < 0)
-               return ret;
-
-       format = params_format(hw_params);
-       rate = params_rate(hw_params);
-       channels = params_channels(hw_params);
-       fmt = find_format(subs, format, rate, channels);
-       if (!fmt) {
-               snd_printd(KERN_DEBUG "cannot set format: format = %#x, rate = %d, channels = %d\n",
-                          format, rate, channels);
-               return -EINVAL;
-       }
-
-       changed = subs->cur_audiofmt != fmt ||
-               subs->period_bytes != params_period_bytes(hw_params) ||
-               subs->cur_rate != rate;
-       if ((ret = set_format(subs, fmt)) < 0)
-               return ret;
-
-       if (subs->cur_rate != rate) {
-               struct usb_host_interface *alts;
-               struct usb_interface *iface;
-               iface = usb_ifnum_to_if(subs->dev, fmt->iface);
-               alts = &iface->altsetting[fmt->altset_idx];
-               ret = init_usb_sample_rate(subs->dev, subs->interface, alts, fmt, rate);
-               if (ret < 0)
-                       return ret;
-               subs->cur_rate = rate;
-       }
-
-       if (changed) {
-               /* format changed */
-               release_substream_urbs(subs, 0);
-               /* influenced: period_bytes, channels, rate, format, */
-               ret = init_substream_urbs(subs, params_period_bytes(hw_params),
-                                         params_rate(hw_params),
-                                         snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params));
-       }
-
-       return ret;
-}
-
-/*
- * hw_free callback
- *
- * reset the audio format and release the buffer
- */
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       subs->cur_audiofmt = NULL;
-       subs->cur_rate = 0;
-       subs->period_bytes = 0;
-       if (!subs->stream->chip->shutdown)
-               release_substream_urbs(subs, 0);
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-/*
- * prepare callback
- *
- * only a few subtle things...
- */
-static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_usb_substream *subs = runtime->private_data;
-
-       if (! subs->cur_audiofmt) {
-               snd_printk(KERN_ERR "usbaudio: no format is specified!\n");
-               return -ENXIO;
-       }
-
-       /* some unit conversions in runtime */
-       subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
-       subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
-
-       /* reset the pointer */
-       subs->hwptr_done = 0;
-       subs->transfer_done = 0;
-       subs->phase = 0;
-       runtime->delay = 0;
-
-       /* clear urbs (to be sure) */
-       deactivate_urbs(subs, 0, 1);
-       wait_clear_urbs(subs);
-
-       /* for playback, submit the URBs now; otherwise, the first hwptr_done
-        * updates for all URBs would happen at the same time when starting */
-       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return start_urbs(subs, runtime);
-       } else
-               return 0;
-}
-
-static struct snd_pcm_hardware snd_usb_hardware =
-{
-       .info =                 SNDRV_PCM_INFO_MMAP |
-                               SNDRV_PCM_INFO_MMAP_VALID |
-                               SNDRV_PCM_INFO_BATCH |
-                               SNDRV_PCM_INFO_INTERLEAVED |
-                               SNDRV_PCM_INFO_BLOCK_TRANSFER |
-                               SNDRV_PCM_INFO_PAUSE,
-       .buffer_bytes_max =     1024 * 1024,
-       .period_bytes_min =     64,
-       .period_bytes_max =     512 * 1024,
-       .periods_min =          2,
-       .periods_max =          1024,
-};
-
-/*
- * h/w constraints
- */
-
-#ifdef HW_CONST_DEBUG
-#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args)
-#else
-#define hwc_debug(fmt, args...) /**/
-#endif
-
-static int hw_check_valid_format(struct snd_usb_substream *subs,
-                                struct snd_pcm_hw_params *params,
-                                struct audioformat *fp)
-{
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_mask *fmts = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-       struct snd_interval *pt = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
-       unsigned int ptime;
-
-       /* check the format */
-       if (!snd_mask_test(fmts, fp->format)) {
-               hwc_debug("   > check: no supported format %d\n", fp->format);
-               return 0;
-       }
-       /* check the channels */
-       if (fp->channels < ct->min || fp->channels > ct->max) {
-               hwc_debug("   > check: no valid channels %d (%d/%d)\n", fp->channels, ct->min, ct->max);
-               return 0;
-       }
-       /* check the rate is within the range */
-       if (fp->rate_min > it->max || (fp->rate_min == it->max && it->openmax)) {
-               hwc_debug("   > check: rate_min %d > max %d\n", fp->rate_min, it->max);
-               return 0;
-       }
-       if (fp->rate_max < it->min || (fp->rate_max == it->min && it->openmin)) {
-               hwc_debug("   > check: rate_max %d < min %d\n", fp->rate_max, it->min);
-               return 0;
-       }
-       /* check whether the period time is >= the data packet interval */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
-               ptime = 125 * (1 << fp->datainterval);
-               if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
-                       hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-static int hw_rule_rate(struct snd_pcm_hw_params *params,
-                       struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       unsigned int rmin, rmax;
-       int changed;
-
-       hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max);
-       changed = 0;
-       rmin = rmax = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               if (changed++) {
-                       if (rmin > fp->rate_min)
-                               rmin = fp->rate_min;
-                       if (rmax < fp->rate_max)
-                               rmax = fp->rate_max;
-               } else {
-                       rmin = fp->rate_min;
-                       rmax = fp->rate_max;
-               }
-       }
-
-       if (!changed) {
-               hwc_debug("  --> get empty\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-
-       changed = 0;
-       if (it->min < rmin) {
-               it->min = rmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (it->max > rmax) {
-               it->max = rmax;
-               it->openmax = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-
-static int hw_rule_channels(struct snd_pcm_hw_params *params,
-                           struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       unsigned int rmin, rmax;
-       int changed;
-
-       hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max);
-       changed = 0;
-       rmin = rmax = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               if (changed++) {
-                       if (rmin > fp->channels)
-                               rmin = fp->channels;
-                       if (rmax < fp->channels)
-                               rmax = fp->channels;
-               } else {
-                       rmin = fp->channels;
-                       rmax = fp->channels;
-               }
-       }
-
-       if (!changed) {
-               hwc_debug("  --> get empty\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-
-       changed = 0;
-       if (it->min < rmin) {
-               it->min = rmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (it->max > rmax) {
-               it->max = rmax;
-               it->openmax = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%d, %d) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-static int hw_rule_format(struct snd_pcm_hw_params *params,
-                         struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct list_head *p;
-       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-       u64 fbits;
-       u32 oldbits[2];
-       int changed;
-
-       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
-       fbits = 0;
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               fbits |= (1ULL << fp->format);
-       }
-
-       oldbits[0] = fmt->bits[0];
-       oldbits[1] = fmt->bits[1];
-       fmt->bits[0] &= (u32)fbits;
-       fmt->bits[1] &= (u32)(fbits >> 32);
-       if (!fmt->bits[0] && !fmt->bits[1]) {
-               hwc_debug("  --> get empty\n");
-               return -EINVAL;
-       }
-       changed = (oldbits[0] != fmt->bits[0] || oldbits[1] != fmt->bits[1]);
-       hwc_debug("  --> %x:%x (changed = %d)\n", fmt->bits[0], fmt->bits[1], changed);
-       return changed;
-}
-
-static int hw_rule_period_time(struct snd_pcm_hw_params *params,
-                              struct snd_pcm_hw_rule *rule)
-{
-       struct snd_usb_substream *subs = rule->private;
-       struct audioformat *fp;
-       struct snd_interval *it;
-       unsigned char min_datainterval;
-       unsigned int pmin;
-       int changed;
-
-       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
-       hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max);
-       min_datainterval = 0xff;
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               min_datainterval = min(min_datainterval, fp->datainterval);
-       }
-       if (min_datainterval == 0xff) {
-               hwc_debug("  --> get emtpy\n");
-               it->empty = 1;
-               return -EINVAL;
-       }
-       pmin = 125 * (1 << min_datainterval);
-       changed = 0;
-       if (it->min < pmin) {
-               it->min = pmin;
-               it->openmin = 0;
-               changed = 1;
-       }
-       if (snd_interval_checkempty(it)) {
-               it->empty = 1;
-               return -EINVAL;
-       }
-       hwc_debug("  --> (%u,%u) (changed = %d)\n", it->min, it->max, changed);
-       return changed;
-}
-
-/*
- *  If the device supports unusual bit rates, does the request meet these?
- */
-static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime,
-                                 struct snd_usb_substream *subs)
-{
-       struct audioformat *fp;
-       int count = 0, needs_knot = 0;
-       int err;
-
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
-                       return 0;
-               count += fp->nr_rates;
-               if (fp->rates & SNDRV_PCM_RATE_KNOT)
-                       needs_knot = 1;
-       }
-       if (!needs_knot)
-               return 0;
-
-       subs->rate_list.count = count;
-       subs->rate_list.list = kmalloc(sizeof(int) * count, GFP_KERNEL);
-       subs->rate_list.mask = 0;
-       count = 0;
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               int i;
-               for (i = 0; i < fp->nr_rates; i++)
-                       subs->rate_list.list[count++] = fp->rate_table[i];
-       }
-       err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                        &subs->rate_list);
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-
-/*
- * set up the runtime hardware information.
- */
-
-static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
-{
-       struct list_head *p;
-       unsigned int pt, ptmin;
-       int param_period_time_if_needed;
-       int err;
-
-       runtime->hw.formats = subs->formats;
-
-       runtime->hw.rate_min = 0x7fffffff;
-       runtime->hw.rate_max = 0;
-       runtime->hw.channels_min = 256;
-       runtime->hw.channels_max = 0;
-       runtime->hw.rates = 0;
-       ptmin = UINT_MAX;
-       /* check min/max rates and channels */
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               runtime->hw.rates |= fp->rates;
-               if (runtime->hw.rate_min > fp->rate_min)
-                       runtime->hw.rate_min = fp->rate_min;
-               if (runtime->hw.rate_max < fp->rate_max)
-                       runtime->hw.rate_max = fp->rate_max;
-               if (runtime->hw.channels_min > fp->channels)
-                       runtime->hw.channels_min = fp->channels;
-               if (runtime->hw.channels_max < fp->channels)
-                       runtime->hw.channels_max = fp->channels;
-               if (fp->fmt_type == UAC_FORMAT_TYPE_II && fp->frame_size > 0) {
-                       /* FIXME: there might be more than one audio formats... */
-                       runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
-                               fp->frame_size;
-               }
-               pt = 125 * (1 << fp->datainterval);
-               ptmin = min(ptmin, pt);
-       }
-
-       param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
-       if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
-               /* full speed devices have fixed data packet interval */
-               ptmin = 1000;
-       if (ptmin == 1000)
-               /* if period time doesn't go below 1 ms, no rules needed */
-               param_period_time_if_needed = -1;
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                    ptmin, UINT_MAX);
-
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                      hw_rule_rate, subs,
-                                      SNDRV_PCM_HW_PARAM_FORMAT,
-                                      SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      hw_rule_channels, subs,
-                                      SNDRV_PCM_HW_PARAM_FORMAT,
-                                      SNDRV_PCM_HW_PARAM_RATE,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
-                                      hw_rule_format, subs,
-                                      SNDRV_PCM_HW_PARAM_RATE,
-                                      SNDRV_PCM_HW_PARAM_CHANNELS,
-                                      param_period_time_if_needed,
-                                      -1)) < 0)
-               return err;
-       if (param_period_time_if_needed >= 0) {
-               err = snd_pcm_hw_rule_add(runtime, 0,
-                                         SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                         hw_rule_period_time, subs,
-                                         SNDRV_PCM_HW_PARAM_FORMAT,
-                                         SNDRV_PCM_HW_PARAM_CHANNELS,
-                                         SNDRV_PCM_HW_PARAM_RATE,
-                                         -1);
-               if (err < 0)
-                       return err;
-       }
-       if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0)
-               return err;
-       return 0;
-}
-
-static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
-{
-       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_usb_substream *subs = &as->substream[direction];
-
-       subs->interface = -1;
-       subs->format = 0;
-       runtime->hw = snd_usb_hardware;
-       runtime->private_data = subs;
-       subs->pcm_substream = substream;
-       return setup_hw_info(runtime, subs);
-}
-
-static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
-{
-       struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
-       struct snd_usb_substream *subs = &as->substream[direction];
-
-       if (!as->chip->shutdown && subs->interface >= 0) {
-               usb_set_interface(subs->dev, subs->interface, 0);
-               subs->interface = -1;
-       }
-       subs->pcm_substream = NULL;
-       return 0;
-}
-
-static int snd_usb_playback_open(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK);
-}
-
-static int snd_usb_playback_close(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
-}
-
-static int snd_usb_capture_open(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE);
-}
-
-static int snd_usb_capture_close(struct snd_pcm_substream *substream)
-{
-       return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
-}
-
-static struct snd_pcm_ops snd_usb_playback_ops = {
-       .open =         snd_usb_playback_open,
-       .close =        snd_usb_playback_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_usb_hw_params,
-       .hw_free =      snd_usb_hw_free,
-       .prepare =      snd_usb_pcm_prepare,
-       .trigger =      snd_usb_pcm_playback_trigger,
-       .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_lib_get_vmalloc_page,
-       .mmap =         snd_pcm_lib_mmap_vmalloc,
-};
-
-static struct snd_pcm_ops snd_usb_capture_ops = {
-       .open =         snd_usb_capture_open,
-       .close =        snd_usb_capture_close,
-       .ioctl =        snd_pcm_lib_ioctl,
-       .hw_params =    snd_usb_hw_params,
-       .hw_free =      snd_usb_hw_free,
-       .prepare =      snd_usb_pcm_prepare,
-       .trigger =      snd_usb_pcm_capture_trigger,
-       .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_lib_get_vmalloc_page,
-       .mmap =         snd_pcm_lib_mmap_vmalloc,
-};
-
-
-
-/*
- * helper functions
- */
-
-/*
- * combine bytes and get an integer value
- */
-unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
-{
-       switch (size) {
-       case 1:  return *bytes;
-       case 2:  return combine_word(bytes);
-       case 3:  return combine_triple(bytes);
-       case 4:  return combine_quad(bytes);
-       default: return 0;
-       }
-}
-
-/*
- * parse descriptor buffer and return the pointer starting the given
- * descriptor type.
- */
-void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype)
-{
-       u8 *p, *end, *next;
-
-       p = descstart;
-       end = p + desclen;
-       for (; p < end;) {
-               if (p[0] < 2)
-                       return NULL;
-               next = p + p[0];
-               if (next > end)
-                       return NULL;
-               if (p[1] == dtype && (!after || (void *)p > after)) {
-                       return p;
-               }
-               p = next;
-       }
-       return NULL;
-}
-
-/*
- * find a class-specified interface descriptor with the given subtype.
- */
-void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype)
-{
-       unsigned char *p = after;
-
-       while ((p = snd_usb_find_desc(buffer, buflen, p,
-                                     USB_DT_CS_INTERFACE)) != NULL) {
-               if (p[0] >= 3 && p[2] == dsubtype)
-                       return p;
-       }
-       return NULL;
-}
-
-/*
- * Wrapper for usb_control_msg().
- * Allocates a temp buffer to prevent dmaing from/to the stack.
- */
-int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
-                   __u8 requesttype, __u16 value, __u16 index, void *data,
-                   __u16 size, int timeout)
-{
-       int err;
-       void *buf = NULL;
-
-       if (size > 0) {
-               buf = kmemdup(data, size, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-       }
-       err = usb_control_msg(dev, pipe, request, requesttype,
-                             value, index, buf, size, timeout);
-       if (size > 0) {
-               memcpy(data, buf, size);
-               kfree(buf);
-       }
-       return err;
-}
-
-
-/*
- * entry point for linux usb interface
- */
-
-static int usb_audio_probe(struct usb_interface *intf,
-                          const struct usb_device_id *id);
-static void usb_audio_disconnect(struct usb_interface *intf);
-
-#ifdef CONFIG_PM
-static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message);
-static int usb_audio_resume(struct usb_interface *intf);
-#else
-#define usb_audio_suspend NULL
-#define usb_audio_resume NULL
-#endif
-
-static struct usb_device_id usb_audio_ids [] = {
-#include "usbquirks.h"
-    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
-      .bInterfaceClass = USB_CLASS_AUDIO,
-      .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL },
-    { }                                                /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, usb_audio_ids);
-
-static struct usb_driver usb_audio_driver = {
-       .name =         "snd-usb-audio",
-       .probe =        usb_audio_probe,
-       .disconnect =   usb_audio_disconnect,
-       .suspend =      usb_audio_suspend,
-       .resume =       usb_audio_resume,
-       .id_table =     usb_audio_ids,
-};
-
-
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_SND_VERBOSE_PROCFS)
-
-/*
- * proc interface for list the supported pcm formats
- */
-static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
-{
-       struct list_head *p;
-       static char *sync_types[4] = {
-               "NONE", "ASYNC", "ADAPTIVE", "SYNC"
-       };
-
-       list_for_each(p, &subs->fmt_list) {
-               struct audioformat *fp;
-               fp = list_entry(p, struct audioformat, list);
-               snd_iprintf(buffer, "  Interface %d\n", fp->iface);
-               snd_iprintf(buffer, "    Altset %d\n", fp->altsetting);
-               snd_iprintf(buffer, "    Format: %s\n",
-                           snd_pcm_format_name(fp->format));
-               snd_iprintf(buffer, "    Channels: %d\n", fp->channels);
-               snd_iprintf(buffer, "    Endpoint: %d %s (%s)\n",
-                           fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
-                           fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
-                           sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]);
-               if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
-                       snd_iprintf(buffer, "    Rates: %d - %d (continuous)\n",
-                                   fp->rate_min, fp->rate_max);
-               } else {
-                       unsigned int i;
-                       snd_iprintf(buffer, "    Rates: ");
-                       for (i = 0; i < fp->nr_rates; i++) {
-                               if (i > 0)
-                                       snd_iprintf(buffer, ", ");
-                               snd_iprintf(buffer, "%d", fp->rate_table[i]);
-                       }
-                       snd_iprintf(buffer, "\n");
-               }
-               if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-                       snd_iprintf(buffer, "    Data packet interval: %d us\n",
-                                   125 * (1 << fp->datainterval));
-               // snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
-               // snd_iprintf(buffer, "    EP Attribute = %#x\n", fp->attributes);
-       }
-}
-
-static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
-{
-       if (subs->running) {
-               unsigned int i;
-               snd_iprintf(buffer, "  Status: Running\n");
-               snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
-               snd_iprintf(buffer, "    Altset = %d\n", subs->format);
-               snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
-               for (i = 0; i < subs->nurbs; i++)
-                       snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
-               snd_iprintf(buffer, "]\n");
-               snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
-                           snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
-                           ? get_full_speed_hz(subs->freqm)
-                           : get_high_speed_hz(subs->freqm),
-                           subs->freqm >> 16, subs->freqm & 0xffff);
-       } else {
-               snd_iprintf(buffer, "  Status: Stop\n");
-       }
-}
-
-static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_stream *stream = entry->private_data;
-
-       snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
-
-       if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
-               snd_iprintf(buffer, "\nPlayback:\n");
-               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
-               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
-       }
-       if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
-               snd_iprintf(buffer, "\nCapture:\n");
-               proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
-               proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
-       }
-}
-
-static void proc_pcm_format_add(struct snd_usb_stream *stream)
-{
-       struct snd_info_entry *entry;
-       char name[32];
-       struct snd_card *card = stream->chip->card;
-
-       sprintf(name, "stream%d", stream->pcm_index);
-       if (!snd_card_proc_new(card, name, &entry))
-               snd_info_set_text_ops(entry, stream, proc_pcm_format_read);
-}
-
-#else
-
-static inline void proc_pcm_format_add(struct snd_usb_stream *stream)
-{
-}
-
-#endif
-
-/*
- * initialize the substream instance.
- */
-
-static void init_substream(struct snd_usb_stream *as, int stream, struct audioformat *fp)
-{
-       struct snd_usb_substream *subs = &as->substream[stream];
-
-       INIT_LIST_HEAD(&subs->fmt_list);
-       spin_lock_init(&subs->lock);
-
-       subs->stream = as;
-       subs->direction = stream;
-       subs->dev = as->chip->dev;
-       subs->txfr_quirk = as->chip->txfr_quirk;
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
-               subs->ops = audio_urb_ops[stream];
-       } else {
-               subs->ops = audio_urb_ops_high_speed[stream];
-               switch (as->chip->usb_id) {
-               case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
-               case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
-               case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
-                       subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;
-                       break;
-               }
-       }
-       snd_pcm_set_ops(as->pcm, stream,
-                       stream == SNDRV_PCM_STREAM_PLAYBACK ?
-                       &snd_usb_playback_ops : &snd_usb_capture_ops);
-
-       list_add_tail(&fp->list, &subs->fmt_list);
-       subs->formats |= 1ULL << fp->format;
-       subs->endpoint = fp->endpoint;
-       subs->num_formats++;
-       subs->fmt_type = fp->fmt_type;
-}
-
-
-/*
- * free a substream
- */
-static void free_substream(struct snd_usb_substream *subs)
-{
-       struct list_head *p, *n;
-
-       if (!subs->num_formats)
-               return; /* not initialized */
-       list_for_each_safe(p, n, &subs->fmt_list) {
-               struct audioformat *fp = list_entry(p, struct audioformat, list);
-               kfree(fp->rate_table);
-               kfree(fp);
-       }
-       kfree(subs->rate_list.list);
-}
-
-
-/*
- * free a usb stream instance
- */
-static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
-{
-       free_substream(&stream->substream[0]);
-       free_substream(&stream->substream[1]);
-       list_del(&stream->list);
-       kfree(stream);
-}
-
-static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
-{
-       struct snd_usb_stream *stream = pcm->private_data;
-       if (stream) {
-               stream->pcm = NULL;
-               snd_usb_audio_stream_free(stream);
-       }
-}
-
-
-/*
- * add this endpoint to the chip instance.
- * if a stream with the same endpoint already exists, append to it.
- * if not, create a new pcm stream.
- */
-static int add_audio_endpoint(struct snd_usb_audio *chip, int stream, struct audioformat *fp)
-{
-       struct list_head *p;
-       struct snd_usb_stream *as;
-       struct snd_usb_substream *subs;
-       struct snd_pcm *pcm;
-       int err;
-
-       list_for_each(p, &chip->pcm_list) {
-               as = list_entry(p, struct snd_usb_stream, list);
-               if (as->fmt_type != fp->fmt_type)
-                       continue;
-               subs = &as->substream[stream];
-               if (!subs->endpoint)
-                       continue;
-               if (subs->endpoint == fp->endpoint) {
-                       list_add_tail(&fp->list, &subs->fmt_list);
-                       subs->num_formats++;
-                       subs->formats |= 1ULL << fp->format;
-                       return 0;
-               }
-       }
-       /* look for an empty stream */
-       list_for_each(p, &chip->pcm_list) {
-               as = list_entry(p, struct snd_usb_stream, list);
-               if (as->fmt_type != fp->fmt_type)
-                       continue;
-               subs = &as->substream[stream];
-               if (subs->endpoint)
-                       continue;
-               err = snd_pcm_new_stream(as->pcm, stream, 1);
-               if (err < 0)
-                       return err;
-               init_substream(as, stream, fp);
-               return 0;
-       }
-
-       /* create a new pcm */
-       as = kzalloc(sizeof(*as), GFP_KERNEL);
-       if (!as)
-               return -ENOMEM;
-       as->pcm_index = chip->pcm_devs;
-       as->chip = chip;
-       as->fmt_type = fp->fmt_type;
-       err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
-                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
-                         stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
-                         &pcm);
-       if (err < 0) {
-               kfree(as);
-               return err;
-       }
-       as->pcm = pcm;
-       pcm->private_data = as;
-       pcm->private_free = snd_usb_audio_pcm_free;
-       pcm->info_flags = 0;
-       if (chip->pcm_devs > 0)
-               sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
-       else
-               strcpy(pcm->name, "USB Audio");
-
-       init_substream(as, stream, fp);
-
-       list_add(&as->list, &chip->pcm_list);
-       chip->pcm_devs++;
-
-       proc_pcm_format_add(as);
-
-       return 0;
-}
-
-
-/*
- * check if the device uses big-endian samples
- */
-static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp)
-{
-       switch (chip->usb_id) {
-       case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
-               if (fp->endpoint & USB_DIR_IN)
-                       return 1;
-               break;
-       case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-               if (device_setup[chip->index] == 0x00 ||
-                   fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
-                       return 1;
-       }
-       return 0;
-}
-
-/*
- * parse the audio format type I descriptor
- * and returns the corresponding pcm format
- *
- * @dev: usb device
- * @fp: audioformat record
- * @format: the format tag (wFormatTag)
- * @fmt: the format type descriptor
- */
-static int parse_audio_format_i_type(struct snd_usb_audio *chip,
-                                    struct audioformat *fp,
-                                    int format, void *_fmt,
-                                    int protocol)
-{
-       int pcm_format, i;
-       int sample_width, sample_bytes;
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
-               sample_width = fmt->bBitResolution;
-               sample_bytes = fmt->bSubframeSize;
-               break;
-       }
-
-       case UAC_VERSION_2: {
-               struct uac_format_type_i_ext_descriptor *fmt = _fmt;
-               sample_width = fmt->bBitResolution;
-               sample_bytes = fmt->bSubslotSize;
-
-               /*
-                * FIXME
-                * USB audio class v2 devices specify a bitmap of possible
-                * audio formats rather than one fix value. For now, we just
-                * pick one of them and report that as the only possible
-                * value for this setting.
-                * The bit allocation map is in fact compatible to the
-                * wFormatTag of the v1 AS streaming descriptors, which is why
-                * we can simply map the matrix.
-                */
-
-               for (i = 0; i < 5; i++)
-                       if (format & (1UL << i)) {
-                               format = i + 1;
-                               break;
-                       }
-
-               break;
-       }
-
-       default:
-               return -EINVAL;
-       }
-
-       /* FIXME: correct endianess and sign? */
-       pcm_format = -1;
-
-       switch (format) {
-       case UAC_FORMAT_TYPE_I_UNDEFINED: /* some devices don't define this correctly... */
-               snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
-                           chip->dev->devnum, fp->iface, fp->altsetting);
-               /* fall-through */
-       case UAC_FORMAT_TYPE_I_PCM:
-               if (sample_width > sample_bytes * 8) {
-                       snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting,
-                                  sample_width, sample_bytes);
-               }
-               /* check the format byte size */
-               switch (sample_bytes) {
-               case 1:
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
-                       break;
-               case 2:
-                       if (is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-                       break;
-               case 3:
-                       if (is_big_endian_format(chip, fp))
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
-                       break;
-               case 4:
-                       pcm_format = SNDRV_PCM_FORMAT_S32_LE;
-                       break;
-               default:
-                       snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting,
-                                  sample_width, sample_bytes);
-                       break;
-               }
-               break;
-       case UAC_FORMAT_TYPE_I_PCM8:
-               pcm_format = SNDRV_PCM_FORMAT_U8;
-
-               /* Dallas DS4201 workaround: it advertises U8 format, but really
-                  supports S8. */
-               if (chip->usb_id == USB_ID(0x04fa, 0x4201))
-                       pcm_format = SNDRV_PCM_FORMAT_S8;
-               break;
-       case UAC_FORMAT_TYPE_I_IEEE_FLOAT:
-               pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
-               break;
-       case UAC_FORMAT_TYPE_I_ALAW:
-               pcm_format = SNDRV_PCM_FORMAT_A_LAW;
-               break;
-       case UAC_FORMAT_TYPE_I_MULAW:
-               pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
-               break;
-       default:
-               snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, format);
-               break;
-       }
-       return pcm_format;
-}
-
-
-/*
- * parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v1).
- *
- * @dev: usb device
- * @fp: audioformat record
- * @fmt: the format descriptor
- * @offset: the start offset of descriptor pointing the rate type
- *          (7 for type I and II, 8 for type II)
- */
-static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audioformat *fp,
-                                      unsigned char *fmt, int offset)
-{
-       int nr_rates = fmt[offset];
-
-       if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
-               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
-                                  chip->dev->devnum, fp->iface, fp->altsetting);
-               return -1;
-       }
-
-       if (nr_rates) {
-               /*
-                * build the rate table and bitmap flags
-                */
-               int r, idx;
-
-               fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-               if (fp->rate_table == NULL) {
-                       snd_printk(KERN_ERR "cannot malloc\n");
-                       return -1;
-               }
-
-               fp->nr_rates = 0;
-               fp->rate_min = fp->rate_max = 0;
-               for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) {
-                       unsigned int rate = combine_triple(&fmt[idx]);
-                       if (!rate)
-                               continue;
-                       /* C-Media CM6501 mislabels its 96 kHz altsetting */
-                       if (rate == 48000 && nr_rates == 1 &&
-                           (chip->usb_id == USB_ID(0x0d8c, 0x0201) ||
-                            chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
-                           fp->altsetting == 5 && fp->maxpacksize == 392)
-                               rate = 96000;
-                       /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
-                       if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
-                               rate = 8000;
-                       fp->rate_table[fp->nr_rates] = rate;
-                       if (!fp->rate_min || rate < fp->rate_min)
-                               fp->rate_min = rate;
-                       if (!fp->rate_max || rate > fp->rate_max)
-                               fp->rate_max = rate;
-                       fp->rates |= snd_pcm_rate_to_rate_bit(rate);
-                       fp->nr_rates++;
-               }
-               if (!fp->nr_rates) {
-                       hwc_debug("All rates were zero. Skipping format!\n");
-                       return -1;
-               }
-       } else {
-               /* continuous rates */
-               fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
-               fp->rate_min = combine_triple(&fmt[offset + 1]);
-               fp->rate_max = combine_triple(&fmt[offset + 4]);
-       }
-       return 0;
-}
-
-/*
- * parse the format descriptor and stores the possible sample rates
- * on the audioformat table (audio class v2).
- */
-static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
-                                      struct audioformat *fp,
-                                      struct usb_host_interface *iface)
-{
-       struct usb_device *dev = chip->dev;
-       unsigned char tmp[2], *data;
-       int i, nr_rates, data_size, ret = 0;
-
-       /* get the number of sample rates first by only fetching 2 bytes */
-       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
-                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                              0x0100, chip->clock_id << 8, tmp, sizeof(tmp), 1000);
-
-       if (ret < 0) {
-               snd_printk(KERN_ERR "unable to retrieve number of sample rates\n");
-               goto err;
-       }
-
-       nr_rates = (tmp[1] << 8) | tmp[0];
-       data_size = 2 + 12 * nr_rates;
-       data = kzalloc(data_size, GFP_KERNEL);
-       if (!data) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       /* now get the full information */
-       ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_RANGE,
-                              USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
-                              0x0100, chip->clock_id << 8, data, data_size, 1000);
-
-       if (ret < 0) {
-               snd_printk(KERN_ERR "unable to retrieve sample rate range\n");
-               ret = -EINVAL;
-               goto err_free;
-       }
-
-       fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
-       if (!fp->rate_table) {
-               ret = -ENOMEM;
-               goto err_free;
-       }
-
-       fp->nr_rates = 0;
-       fp->rate_min = fp->rate_max = 0;
-
-       for (i = 0; i < nr_rates; i++) {
-               int rate = combine_quad(&data[2 + 12 * i]);
-
-               fp->rate_table[fp->nr_rates] = rate;
-               if (!fp->rate_min || rate < fp->rate_min)
-                       fp->rate_min = rate;
-               if (!fp->rate_max || rate > fp->rate_max)
-                       fp->rate_max = rate;
-               fp->rates |= snd_pcm_rate_to_rate_bit(rate);
-               fp->nr_rates++;
-       }
-
-err_free:
-       kfree(data);
-err:
-       return ret;
-}
-
-/*
- * parse the format type I and III descriptors
- */
-static int parse_audio_format_i(struct snd_usb_audio *chip,
-                               struct audioformat *fp,
-                               int format, void *_fmt,
-                               struct usb_host_interface *iface)
-{
-       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       struct uac_format_type_i_discrete_descriptor *fmt = _fmt;
-       int protocol = altsd->bInterfaceProtocol;
-       int pcm_format, ret;
-
-       if (fmt->bFormatType == UAC_FORMAT_TYPE_III) {
-               /* FIXME: the format type is really IECxxx
-                *        but we give normal PCM format to get the existing
-                *        apps working...
-                */
-               switch (chip->usb_id) {
-
-               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-                       if (device_setup[chip->index] == 0x00 && 
-                           fp->altsetting == 6)
-                               pcm_format = SNDRV_PCM_FORMAT_S16_BE;
-                       else
-                               pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-                       break;
-               default:
-                       pcm_format = SNDRV_PCM_FORMAT_S16_LE;
-               }
-       } else {
-               pcm_format = parse_audio_format_i_type(chip, fp, format, fmt, protocol);
-               if (pcm_format < 0)
-                       return -1;
-       }
-
-       fp->format = pcm_format;
-
-       /* gather possible sample rates */
-       /* audio class v1 reports possible sample rates as part of the
-        * proprietary class specific descriptor.
-        * audio class v2 uses class specific EP0 range requests for that.
-        */
-       switch (protocol) {
-       case UAC_VERSION_1:
-               fp->channels = fmt->bNrChannels;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 7);
-               break;
-       case UAC_VERSION_2:
-               /* fp->channels is already set in this case */
-               ret = parse_audio_format_rates_v2(chip, fp, iface);
-               break;
-       }
-
-       if (fp->channels < 1) {
-               snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
-               return -1;
-       }
-
-       return ret;
-}
-
-/*
- * parse the format type II descriptor
- */
-static int parse_audio_format_ii(struct snd_usb_audio *chip,
-                                struct audioformat *fp,
-                                int format, void *_fmt,
-                                struct usb_host_interface *iface)
-{
-       int brate, framesize, ret;
-       struct usb_interface_descriptor *altsd = get_iface_desc(iface);
-       int protocol = altsd->bInterfaceProtocol;
-
-       switch (format) {
-       case UAC_FORMAT_TYPE_II_AC3:
-               /* FIXME: there is no AC3 format defined yet */
-               // fp->format = SNDRV_PCM_FORMAT_AC3;
-               fp->format = SNDRV_PCM_FORMAT_U8; /* temporarily hack to receive byte streams */
-               break;
-       case UAC_FORMAT_TYPE_II_MPEG:
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
-               break;
-       default:
-               snd_printd(KERN_INFO "%d:%u:%d : unknown format tag %#x is detected.  processed as MPEG.\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, format);
-               fp->format = SNDRV_PCM_FORMAT_MPEG;
-               break;
-       }
-
-       fp->channels = 1;
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_format_type_ii_discrete_descriptor *fmt = _fmt;
-               brate = le16_to_cpu(fmt->wMaxBitRate);
-               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
-               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
-               fp->frame_size = framesize;
-               ret = parse_audio_format_rates_v1(chip, fp, _fmt, 8); /* fmt[8..] sample rates */
-               break;
-       }
-       case UAC_VERSION_2: {
-               struct uac_format_type_ii_ext_descriptor *fmt = _fmt;
-               brate = le16_to_cpu(fmt->wMaxBitRate);
-               framesize = le16_to_cpu(fmt->wSamplesPerFrame);
-               snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
-               fp->frame_size = framesize;
-               ret = parse_audio_format_rates_v2(chip, fp, iface);
-               break;
-       }
-       }
-
-       return ret;
-}
-
-static int parse_audio_format(struct snd_usb_audio *chip, struct audioformat *fp,
-                             int format, unsigned char *fmt, int stream,
-                             struct usb_host_interface *iface)
-{
-       int err;
-
-       switch (fmt[3]) {
-       case UAC_FORMAT_TYPE_I:
-       case UAC_FORMAT_TYPE_III:
-               err = parse_audio_format_i(chip, fp, format, fmt, iface);
-               break;
-       case UAC_FORMAT_TYPE_II:
-               err = parse_audio_format_ii(chip, fp, format, fmt, iface);
-               break;
-       default:
-               snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
-               return -1;
-       }
-       fp->fmt_type = fmt[3];
-       if (err < 0)
-               return err;
-#if 1
-       /* FIXME: temporary hack for extigy/audigy 2 nx/zs */
-       /* extigy apparently supports sample rates other than 48k
-        * but not in ordinary way.  so we enable only 48k atm.
-        */
-       if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
-           chip->usb_id == USB_ID(0x041e, 0x3020) ||
-           chip->usb_id == USB_ID(0x041e, 0x3061)) {
-               if (fmt[3] == UAC_FORMAT_TYPE_I &&
-                   fp->rates != SNDRV_PCM_RATE_48000 &&
-                   fp->rates != SNDRV_PCM_RATE_96000)
-                       return -1;
-       }
-#endif
-       return 0;
-}
-
-static unsigned char parse_datainterval(struct snd_usb_audio *chip,
-                                       struct usb_host_interface *alts)
-{
-       if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
-           get_endpoint(alts, 0)->bInterval >= 1 &&
-           get_endpoint(alts, 0)->bInterval <= 4)
-               return get_endpoint(alts, 0)->bInterval - 1;
-       else
-               return 0;
-}
-
-static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
-                                        int iface, int altno);
-static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
-{
-       struct usb_device *dev;
-       struct usb_interface *iface;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       int i, altno, err, stream;
-       int format = 0, num_channels = 0;
-       struct audioformat *fp = NULL;
-       unsigned char *fmt, *csep;
-       int num, protocol;
-
-       dev = chip->dev;
-
-       /* parse the interface's altsettings */
-       iface = usb_ifnum_to_if(dev, iface_no);
-
-       num = iface->num_altsetting;
-
-       /*
-        * Dallas DS4201 workaround: It presents 5 altsettings, but the last
-        * one misses syncpipe, and does not produce any sound.
-        */
-       if (chip->usb_id == USB_ID(0x04fa, 0x4201))
-               num = 4;
-
-       for (i = 0; i < num; i++) {
-               alts = &iface->altsetting[i];
-               altsd = get_iface_desc(alts);
-               protocol = altsd->bInterfaceProtocol;
-               /* skip invalid one */
-               if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
-                    altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
-                   (altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
-                    altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
-                   altsd->bNumEndpoints < 1 ||
-                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
-                       continue;
-               /* must be isochronous */
-               if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
-                   USB_ENDPOINT_XFER_ISOC)
-                       continue;
-               /* check direction */
-               stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
-                       SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-               altno = altsd->bAlternateSetting;
-       
-               /* audiophile usb: skip altsets incompatible with device_setup
-                */
-               if (chip->usb_id == USB_ID(0x0763, 0x2003) && 
-                   audiophile_skip_setting_quirk(chip, iface_no, altno))
-                       continue;
-
-               /* get audio formats */
-               switch (protocol) {
-               case UAC_VERSION_1: {
-                       struct uac_as_header_descriptor_v1 *as =
-                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
-                       if (!as) {
-                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       if (as->bLength < sizeof(*as)) {
-                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       format = le16_to_cpu(as->wFormatTag); /* remember the format value */
-                       break;
-               }
-
-               case UAC_VERSION_2: {
-                       struct uac_as_header_descriptor_v2 *as =
-                               snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
-
-                       if (!as) {
-                               snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       if (as->bLength < sizeof(*as)) {
-                               snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
-                                          dev->devnum, iface_no, altno);
-                               continue;
-                       }
-
-                       num_channels = as->bNrChannels;
-                       format = le32_to_cpu(as->bmFormats);
-
-                       break;
-               }
-
-               default:
-                       snd_printk(KERN_ERR "%d:%u:%d : unknown interface protocol %04x\n",
-                                  dev->devnum, iface_no, altno, protocol);
-                       continue;
-               }
-
-               /* get format type */
-               fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
-               if (!fmt) {
-                       snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-               if (((protocol == UAC_VERSION_1) && (fmt[0] < 8)) ||
-                   ((protocol == UAC_VERSION_2) && (fmt[0] != 6))) {
-                       snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
-                                  dev->devnum, iface_no, altno);
-                       continue;
-               }
-
-               /*
-                * Blue Microphones workaround: The last altsetting is identical
-                * with the previous one, except for a larger packet size, but
-                * is actually a mislabeled two-channel setting; ignore it.
-                */
-               if (fmt[4] == 1 && fmt[5] == 2 && altno == 2 && num == 3 &&
-                   fp && fp->altsetting == 1 && fp->channels == 1 &&
-                   fp->format == SNDRV_PCM_FORMAT_S16_LE &&
-                   protocol == UAC_VERSION_1 &&
-                   le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
-                                                       fp->maxpacksize * 2)
-                       continue;
-
-               csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
-               /* Creamware Noah has this descriptor after the 2nd endpoint */
-               if (!csep && altsd->bNumEndpoints >= 2)
-                       csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
-               if (!csep || csep[0] < 7 || csep[2] != UAC_EP_GENERAL) {
-                       snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
-                                  " class specific endpoint descriptor\n",
-                                  dev->devnum, iface_no, altno);
-                       csep = NULL;
-               }
-
-               fp = kzalloc(sizeof(*fp), GFP_KERNEL);
-               if (! fp) {
-                       snd_printk(KERN_ERR "cannot malloc\n");
-                       return -ENOMEM;
-               }
-
-               fp->iface = iface_no;
-               fp->altsetting = altno;
-               fp->altset_idx = i;
-               fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-               fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-               fp->datainterval = parse_datainterval(chip, alts);
-               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-               /* num_channels is only set for v2 interfaces */
-               fp->channels = num_channels;
-               if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
-                       fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
-                                       * (fp->maxpacksize & 0x7ff);
-               fp->attributes = csep ? csep[3] : 0;
-
-               /* some quirks for attributes here */
-
-               switch (chip->usb_id) {
-               case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
-                       /* Optoplay sets the sample rate attribute although
-                        * it seems not supporting it in fact.
-                        */
-                       fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
-                       break;
-               case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
-               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-                       /* doesn't set the sample rate attribute, but supports it */
-                       fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
-                       break;
-               case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
-               case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
-                                               an older model 77d:223) */
-               /*
-                * plantronics headset and Griffin iMic have set adaptive-in
-                * although it's really not...
-                */
-                       fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
-                       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-                               fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
-                       else
-                               fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
-                       break;
-               }
-
-               /* ok, let's parse further... */
-               if (parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
-                       kfree(fp->rate_table);
-                       kfree(fp);
-                       continue;
-               }
-
-               snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
-               err = add_audio_endpoint(chip, stream, fp);
-               if (err < 0) {
-                       kfree(fp->rate_table);
-                       kfree(fp);
-                       return err;
-               }
-               /* try to set the interface... */
-               usb_set_interface(chip->dev, iface_no, altno);
-               init_usb_pitch(chip->dev, iface_no, alts, fp);
-               init_usb_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max);
-       }
-       return 0;
-}
-
-
-/*
- * disconnect streams
- * called from snd_usb_audio_disconnect()
- */
-static void snd_usb_stream_disconnect(struct list_head *head)
-{
-       int idx;
-       struct snd_usb_stream *as;
-       struct snd_usb_substream *subs;
-
-       as = list_entry(head, struct snd_usb_stream, list);
-       for (idx = 0; idx < 2; idx++) {
-               subs = &as->substream[idx];
-               if (!subs->num_formats)
-                       return;
-               release_substream_urbs(subs, 1);
-               subs->interface = -1;
-       }
-}
-
-static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int interface)
-{
-       struct usb_device *dev = chip->dev;
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct usb_interface *iface = usb_ifnum_to_if(dev, interface);
-
-       if (!iface) {
-               snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
-                          dev->devnum, ctrlif, interface);
-               return -EINVAL;
-       }
-
-       if (usb_interface_claimed(iface)) {
-               snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n",
-                                               dev->devnum, ctrlif, interface);
-               return -EINVAL;
-       }
-
-       alts = &iface->altsetting[0];
-       altsd = get_iface_desc(alts);
-       if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
-            altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
-           altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
-               int err = snd_usbmidi_create(chip->card, iface,
-                                            &chip->midi_list, NULL);
-               if (err < 0) {
-                       snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n",
-                                               dev->devnum, ctrlif, interface);
-                       return -EINVAL;
-               }
-               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-
-               return 0;
-       }
-
-       if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
-            altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
-           altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING) {
-               snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n",
-                                       dev->devnum, ctrlif, interface, altsd->bInterfaceClass);
-               /* skip non-supported classes */
-               return -EINVAL;
-       }
-
-       if (snd_usb_get_speed(dev) == USB_SPEED_LOW) {
-               snd_printk(KERN_ERR "low speed audio streaming not supported\n");
-               return -EINVAL;
-       }
-
-       if (! parse_audio_endpoints(chip, interface)) {
-               usb_set_interface(dev, interface, 0); /* reset the current interface */
-               usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * parse audio control descriptor and create pcm/midi streams
- */
-static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
-{
-       struct usb_device *dev = chip->dev;
-       struct usb_host_interface *host_iface;
-       struct usb_interface_descriptor *altsd;
-       void *control_header;
-       int i, protocol;
-
-       /* find audiocontrol interface */
-       host_iface = &usb_ifnum_to_if(dev, ctrlif)->altsetting[0];
-       control_header = snd_usb_find_csint_desc(host_iface->extra,
-                                                host_iface->extralen,
-                                                NULL, UAC_HEADER);
-       altsd = get_iface_desc(host_iface);
-       protocol = altsd->bInterfaceProtocol;
-
-       if (!control_header) {
-               snd_printk(KERN_ERR "cannot find UAC_HEADER\n");
-               return -EINVAL;
-       }
-
-       switch (protocol) {
-       case UAC_VERSION_1: {
-               struct uac_ac_header_descriptor_v1 *h1 = control_header;
-
-               if (!h1->bInCollection) {
-                       snd_printk(KERN_INFO "skipping empty audio interface (v1)\n");
-                       return -EINVAL;
-               }
-
-               if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
-                       snd_printk(KERN_ERR "invalid UAC_HEADER (v1)\n");
-                       return -EINVAL;
-               }
-
-               for (i = 0; i < h1->bInCollection; i++)
-                       snd_usb_create_stream(chip, ctrlif, h1->baInterfaceNr[i]);
-
-               break;
-       }
-
-       case UAC_VERSION_2: {
-               struct uac_clock_source_descriptor *cs;
-               struct usb_interface_assoc_descriptor *assoc =
-                       usb_ifnum_to_if(dev, ctrlif)->intf_assoc;
-
-               if (!assoc) {
-                       snd_printk(KERN_ERR "Audio class v2 interfaces need an interface association\n");
-                       return -EINVAL;
-               }
-
-               /* FIXME: for now, we expect there is at least one clock source
-                * descriptor and we always take the first one.
-                * We should properly support devices with multiple clock sources,
-                * clock selectors and sample rate conversion units. */
-
-               cs = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen,
-                                               NULL, UAC_CLOCK_SOURCE);
-
-               if (!cs) {
-                       snd_printk(KERN_ERR "CLOCK_SOURCE descriptor not found\n");
-                       return -EINVAL;
-               }
-
-               chip->clock_id = cs->bClockID;
-
-               for (i = 0; i < assoc->bInterfaceCount; i++) {
-                       int intf = assoc->bFirstInterface + i;
-
-                       if (intf != ctrlif)
-                               snd_usb_create_stream(chip, ctrlif, intf);
-               }
-
-               break;
-       }
-
-       default:
-               snd_printk(KERN_ERR "unknown protocol version 0x%02x\n", protocol);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/*
- * create a stream for an endpoint/altsetting without proper descriptors
- */
-static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
-                                    struct usb_interface *iface,
-                                    const struct snd_usb_audio_quirk *quirk)
-{
-       struct audioformat *fp;
-       struct usb_host_interface *alts;
-       int stream, err;
-       unsigned *rate_table = NULL;
-
-       fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
-       if (! fp) {
-               snd_printk(KERN_ERR "cannot memdup\n");
-               return -ENOMEM;
-       }
-       if (fp->nr_rates > 0) {
-               rate_table = kmalloc(sizeof(int) * fp->nr_rates, GFP_KERNEL);
-               if (!rate_table) {
-                       kfree(fp);
-                       return -ENOMEM;
-               }
-               memcpy(rate_table, fp->rate_table, sizeof(int) * fp->nr_rates);
-               fp->rate_table = rate_table;
-       }
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               kfree(rate_table);
-               return err;
-       }
-       if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
-           fp->altset_idx >= iface->num_altsetting) {
-               kfree(fp);
-               kfree(rate_table);
-               return -EINVAL;
-       }
-       alts = &iface->altsetting[fp->altset_idx];
-       fp->datainterval = parse_datainterval(chip, alts);
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-       usb_set_interface(chip->dev, fp->iface, 0);
-       init_usb_pitch(chip->dev, fp->iface, alts, fp);
-       init_usb_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max);
-       return 0;
-}
-
-/*
- * create a stream for an interface with proper descriptors
- */
-static int create_standard_audio_quirk(struct snd_usb_audio *chip,
-                                      struct usb_interface *iface,
-                                      const struct snd_usb_audio_quirk *quirk)
-{
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       int err;
-
-       alts = &iface->altsetting[0];
-       altsd = get_iface_desc(alts);
-       err = parse_audio_endpoints(chip, altsd->bInterfaceNumber);
-       if (err < 0) {
-               snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
-                          altsd->bInterfaceNumber, err);
-               return err;
-       }
-       /* reset the current interface */
-       usb_set_interface(chip->dev, altsd->bInterfaceNumber, 0);
-       return 0;
-}
-
-/*
- * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
- * The only way to detect the sample rate is by looking at wMaxPacketSize.
- */
-static int create_uaxx_quirk(struct snd_usb_audio *chip,
-                             struct usb_interface *iface,
-                             const struct snd_usb_audio_quirk *quirk)
-{
-       static const struct audioformat ua_format = {
-               .format = SNDRV_PCM_FORMAT_S24_3LE,
-               .channels = 2,
-               .fmt_type = UAC_FORMAT_TYPE_I,
-               .altsetting = 1,
-               .altset_idx = 1,
-               .rates = SNDRV_PCM_RATE_CONTINUOUS,
-       };
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct audioformat *fp;
-       int stream, err;
-
-       /* both PCM and MIDI interfaces have 2 or more altsettings */
-       if (iface->num_altsetting < 2)
-               return -ENXIO;
-       alts = &iface->altsetting[1];
-       altsd = get_iface_desc(alts);
-
-       if (altsd->bNumEndpoints == 2) {
-               static const struct snd_usb_midi_endpoint_info ua700_ep = {
-                       .out_cables = 0x0003,
-                       .in_cables  = 0x0003
-               };
-               static const struct snd_usb_audio_quirk ua700_quirk = {
-                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                       .data = &ua700_ep
-               };
-               static const struct snd_usb_midi_endpoint_info uaxx_ep = {
-                       .out_cables = 0x0001,
-                       .in_cables  = 0x0001
-               };
-               static const struct snd_usb_audio_quirk uaxx_quirk = {
-                       .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                       .data = &uaxx_ep
-               };
-               const struct snd_usb_audio_quirk *quirk =
-                       chip->usb_id == USB_ID(0x0582, 0x002b)
-                       ? &ua700_quirk : &uaxx_quirk;
-               return snd_usbmidi_create(chip->card, iface,
-                                         &chip->midi_list, quirk);
-       }
-
-       if (altsd->bNumEndpoints != 1)
-               return -ENXIO;
-
-       fp = kmalloc(sizeof(*fp), GFP_KERNEL);
-       if (!fp)
-               return -ENOMEM;
-       memcpy(fp, &ua_format, sizeof(*fp));
-
-       fp->iface = altsd->bInterfaceNumber;
-       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-       fp->datainterval = 0;
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-
-       switch (fp->maxpacksize) {
-       case 0x120:
-               fp->rate_max = fp->rate_min = 44100;
-               break;
-       case 0x138:
-       case 0x140:
-               fp->rate_max = fp->rate_min = 48000;
-               break;
-       case 0x258:
-       case 0x260:
-               fp->rate_max = fp->rate_min = 96000;
-               break;
-       default:
-               snd_printk(KERN_ERR "unknown sample rate\n");
-               kfree(fp);
-               return -ENXIO;
-       }
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               return err;
-       }
-       usb_set_interface(chip->dev, fp->iface, 0);
-       return 0;
-}
-
-static int snd_usb_create_quirk(struct snd_usb_audio *chip,
-                               struct usb_interface *iface,
-                               const struct snd_usb_audio_quirk *quirk);
-
-/*
- * handle the quirks for the contained interfaces
- */
-static int create_composite_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
-       int err;
-
-       for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) {
-               iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
-               if (!iface)
-                       continue;
-               if (quirk->ifnum != probed_ifnum &&
-                   usb_interface_claimed(iface))
-                       continue;
-               err = snd_usb_create_quirk(chip, iface, quirk);
-               if (err < 0)
-                       return err;
-               if (quirk->ifnum != probed_ifnum)
-                       usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
-       }
-       return 0;
-}
-
-static int ignore_interface_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       return 0;
-}
-
-/*
- * Allow alignment on audio sub-slot (channel samples) rather than
- * on audio slots (audio frames)
- */
-static int create_align_transfer_quirk(struct snd_usb_audio *chip,
-                                 struct usb_interface *iface,
-                                 const struct snd_usb_audio_quirk *quirk)
-{
-       chip->txfr_quirk = 1;
-       return 1;       /* Continue with creating streams and mixer */
-}
-
-
-/*
- * boot quirks
- */
-
-#define EXTIGY_FIRMWARE_SIZE_OLD 794
-#define EXTIGY_FIRMWARE_SIZE_NEW 483
-
-static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interface *intf)
-{
-       struct usb_host_config *config = dev->actconfig;
-       int err;
-
-       if (le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_OLD ||
-           le16_to_cpu(get_cfg_desc(config)->wTotalLength) == EXTIGY_FIRMWARE_SIZE_NEW) {
-               snd_printdd("sending Extigy boot sequence...\n");
-               /* Send message to force it to reconnect with full interface. */
-               err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
-                                     0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
-               if (err < 0) snd_printdd("error sending boot message: %d\n", err);
-               err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
-                               &dev->descriptor, sizeof(dev->descriptor));
-               config = dev->actconfig;
-               if (err < 0) snd_printdd("error usb_get_descriptor: %d\n", err);
-               err = usb_reset_configuration(dev);
-               if (err < 0) snd_printdd("error usb_reset_configuration: %d\n", err);
-               snd_printdd("extigy_boot: new boot length = %d\n",
-                           le16_to_cpu(get_cfg_desc(config)->wTotalLength));
-               return -ENODEV; /* quit this anyway */
-       }
-       return 0;
-}
-
-static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
-{
-       u8 buf = 1;
-
-       snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
-                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                       0, 0, &buf, 1, 1000);
-       if (buf == 0) {
-               snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
-                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
-                               1, 2000, NULL, 0, 1000);
-               return -ENODEV;
-       }
-       return 0;
-}
-
-/*
- * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely
- * documented in the device's data sheet.
- */
-static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value)
-{
-       u8 buf[4];
-       buf[0] = 0x20;
-       buf[1] = value & 0xff;
-       buf[2] = (value >> 8) & 0xff;
-       buf[3] = reg;
-       return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
-                              USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
-                              0, 0, &buf, 4, 1000);
-}
-
-static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
-{
-       /*
-        * Enable line-out driver mode, set headphone source to front
-        * channels, enable stereo mic.
-        */
-       return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
-}
-
-/*
- * C-Media CM6206 is based on CM106 with two additional
- * registers that are not documented in the data sheet.
- * Values here are chosen based on sniffing USB traffic
- * under Windows.
- */
-static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
-{
-       int err, reg;
-       int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
-
-       for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
-               err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
-               if (err < 0)
-                       return err;
-       }
-
-       return err;
-}
-
-/*
- * This call will put the synth in "USB send" mode, i.e it will send MIDI
- * messages through USB (this is disabled at startup). The synth will
- * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
- * sign on its LCD. Values here are chosen based on sniffing USB traffic
- * under Windows.
- */
-static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
-{
-       int err, actual_length;
-
-       /* "midi send" enable */
-       static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
-
-       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-       err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
-                       ARRAY_SIZE(seq), &actual_length, 1000);
-       kfree(buf);
-       if (err < 0)
-               return err;
-
-       return 0;
-}
-
-/*
- * Setup quirks
- */
-#define AUDIOPHILE_SET                 0x01 /* if set, parse device_setup */
-#define AUDIOPHILE_SET_DTS              0x02 /* if set, enable DTS Digital Output */
-#define AUDIOPHILE_SET_96K              0x04 /* 48-96KHz rate if set, 8-48KHz otherwise */
-#define AUDIOPHILE_SET_24B             0x08 /* 24bits sample if set, 16bits otherwise */
-#define AUDIOPHILE_SET_DI              0x10 /* if set, enable Digital Input */
-#define AUDIOPHILE_SET_MASK            0x1F /* bit mask for setup value */
-#define AUDIOPHILE_SET_24B_48K_DI      0x19 /* value for 24bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_24B_48K_NOTDI   0x09 /* value for 24bits+48KHz+No Digital Input */
-#define AUDIOPHILE_SET_16B_48K_DI      0x11 /* value for 16bits+48KHz+Digital Input */
-#define AUDIOPHILE_SET_16B_48K_NOTDI   0x01 /* value for 16bits+48KHz+No Digital Input */
-
-static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
-                                        int iface, int altno)
-{
-       /* Reset ALL ifaces to 0 altsetting.
-        * Call it for every possible altsetting of every interface.
-        */
-       usb_set_interface(chip->dev, iface, 0);
-
-       if (device_setup[chip->index] & AUDIOPHILE_SET) {
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
-                   && altno != 6)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_96K)
-                   && altno != 1)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_24B_48K_DI && altno != 2)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_24B_48K_NOTDI && altno != 3)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_16B_48K_DI && altno != 4)
-                       return 1; /* skip this altsetting */
-               if ((device_setup[chip->index] & AUDIOPHILE_SET_MASK) ==
-                   AUDIOPHILE_SET_16B_48K_NOTDI && altno != 5)
-                       return 1; /* skip this altsetting */
-       }       
-       return 0; /* keep this altsetting */
-}
-
-static int create_any_midi_quirk(struct snd_usb_audio *chip,
-                                struct usb_interface *intf,
-                                const struct snd_usb_audio_quirk *quirk)
-{
-       return snd_usbmidi_create(chip->card, intf, &chip->midi_list, quirk);
-}
-
-/*
- * audio-interface quirks
- *
- * returns zero if no standard audio/MIDI parsing is needed.
- * returns a postive value if standard audio/midi interfaces are parsed
- * after this.
- * returns a negative value at error.
- */
-static int snd_usb_create_quirk(struct snd_usb_audio *chip,
-                               struct usb_interface *iface,
-                               const struct snd_usb_audio_quirk *quirk)
-{
-       typedef int (*quirk_func_t)(struct snd_usb_audio *, struct usb_interface *,
-                                   const struct snd_usb_audio_quirk *);
-       static const quirk_func_t quirk_funcs[] = {
-               [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
-               [QUIRK_COMPOSITE] = create_composite_quirk,
-               [QUIRK_MIDI_STANDARD_INTERFACE] = create_any_midi_quirk,
-               [QUIRK_MIDI_FIXED_ENDPOINT] = create_any_midi_quirk,
-               [QUIRK_MIDI_YAMAHA] = create_any_midi_quirk,
-               [QUIRK_MIDI_MIDIMAN] = create_any_midi_quirk,
-               [QUIRK_MIDI_NOVATION] = create_any_midi_quirk,
-               [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk,
-               [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
-               [QUIRK_MIDI_CME] = create_any_midi_quirk,
-               [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
-               [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
-               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
-               [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
-       };
-
-       if (quirk->type < QUIRK_TYPE_COUNT) {
-               return quirk_funcs[quirk->type](chip, iface, quirk);
-       } else {
-               snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
-               return -ENXIO;
-       }
-}
-
-
-/*
- * common proc files to show the usb device info
- */
-static void proc_audio_usbbus_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_audio *chip = entry->private_data;
-       if (!chip->shutdown)
-               snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum);
-}
-
-static void proc_audio_usbid_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
-{
-       struct snd_usb_audio *chip = entry->private_data;
-       if (!chip->shutdown)
-               snd_iprintf(buffer, "%04x:%04x\n", 
-                           USB_ID_VENDOR(chip->usb_id),
-                           USB_ID_PRODUCT(chip->usb_id));
-}
-
-static void snd_usb_audio_create_proc(struct snd_usb_audio *chip)
-{
-       struct snd_info_entry *entry;
-       if (!snd_card_proc_new(chip->card, "usbbus", &entry))
-               snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read);
-       if (!snd_card_proc_new(chip->card, "usbid", &entry))
-               snd_info_set_text_ops(entry, chip, proc_audio_usbid_read);
-}
-
-/*
- * free the chip instance
- *
- * here we have to do not much, since pcm and controls are already freed
- *
- */
-
-static int snd_usb_audio_free(struct snd_usb_audio *chip)
-{
-       kfree(chip);
-       return 0;
-}
-
-static int snd_usb_audio_dev_free(struct snd_device *device)
-{
-       struct snd_usb_audio *chip = device->device_data;
-       return snd_usb_audio_free(chip);
-}
-
-
-/*
- * create a chip instance and set its names.
- */
-static int snd_usb_audio_create(struct usb_device *dev, int idx,
-                               const struct snd_usb_audio_quirk *quirk,
-                               struct snd_usb_audio **rchip)
-{
-       struct snd_card *card;
-       struct snd_usb_audio *chip;
-       int err, len;
-       char component[14];
-       static struct snd_device_ops ops = {
-               .dev_free =     snd_usb_audio_dev_free,
-       };
-
-       *rchip = NULL;
-
-       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
-           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
-           snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
-               snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
-               return -ENXIO;
-       }
-
-       err = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card);
-       if (err < 0) {
-               snd_printk(KERN_ERR "cannot create card instance %d\n", idx);
-               return err;
-       }
-
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (! chip) {
-               snd_card_free(card);
-               return -ENOMEM;
-       }
-
-       chip->index = idx;
-       chip->dev = dev;
-       chip->card = card;
-       chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                             le16_to_cpu(dev->descriptor.idProduct));
-       INIT_LIST_HEAD(&chip->pcm_list);
-       INIT_LIST_HEAD(&chip->midi_list);
-       INIT_LIST_HEAD(&chip->mixer_list);
-
-       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-               snd_usb_audio_free(chip);
-               snd_card_free(card);
-               return err;
-       }
-
-       strcpy(card->driver, "USB-Audio");
-       sprintf(component, "USB%04x:%04x",
-               USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
-       snd_component_add(card, component);
-
-       /* retrieve the device string as shortname */
-       if (quirk && quirk->product_name) {
-               strlcpy(card->shortname, quirk->product_name, sizeof(card->shortname));
-       } else {
-               if (!dev->descriptor.iProduct ||
-                   usb_string(dev, dev->descriptor.iProduct,
-                              card->shortname, sizeof(card->shortname)) <= 0) {
-                       /* no name available from anywhere, so use ID */
-                       sprintf(card->shortname, "USB Device %#04x:%#04x",
-                               USB_ID_VENDOR(chip->usb_id),
-                               USB_ID_PRODUCT(chip->usb_id));
-               }
-       }
-
-       /* retrieve the vendor and device strings as longname */
-       if (quirk && quirk->vendor_name) {
-               len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname));
-       } else {
-               if (dev->descriptor.iManufacturer)
-                       len = usb_string(dev, dev->descriptor.iManufacturer,
-                                        card->longname, sizeof(card->longname));
-               else
-                       len = 0;
-               /* we don't really care if there isn't any vendor string */
-       }
-       if (len > 0)
-               strlcat(card->longname, " ", sizeof(card->longname));
-
-       strlcat(card->longname, card->shortname, sizeof(card->longname));
-
-       len = strlcat(card->longname, " at ", sizeof(card->longname));
-
-       if (len < sizeof(card->longname))
-               usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
-
-       strlcat(card->longname,
-               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
-               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
-               ", high speed",
-               sizeof(card->longname));
-
-       snd_usb_audio_create_proc(chip);
-
-       *rchip = chip;
-       return 0;
-}
-
-
-/*
- * probe the active usb device
- *
- * note that this can be called multiple times per a device, when it
- * includes multiple audio control interfaces.
- *
- * thus we check the usb device pointer and creates the card instance
- * only at the first time.  the successive calls of this function will
- * append the pcm interface to the corresponding card.
- */
-static void *snd_usb_audio_probe(struct usb_device *dev,
-                                struct usb_interface *intf,
-                                const struct usb_device_id *usb_id)
-{
-       const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info;
-       int i, err;
-       struct snd_usb_audio *chip;
-       struct usb_host_interface *alts;
-       int ifnum;
-       u32 id;
-
-       alts = &intf->altsetting[0];
-       ifnum = get_iface_desc(alts)->bInterfaceNumber;
-       id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                   le16_to_cpu(dev->descriptor.idProduct));
-       if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
-               goto __err_val;
-
-       /* SB Extigy needs special boot-up sequence */
-       /* if more models come, this will go to the quirk list. */
-       if (id == USB_ID(0x041e, 0x3000)) {
-               if (snd_usb_extigy_boot_quirk(dev, intf) < 0)
-                       goto __err_val;
-       }
-       /* SB Audigy 2 NX needs its own boot-up magic, too */
-       if (id == USB_ID(0x041e, 0x3020)) {
-               if (snd_usb_audigy2nx_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */
-       if (id == USB_ID(0x10f5, 0x0200)) {
-               if (snd_usb_cm106_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* C-Media CM6206 / CM106-Like Sound Device */
-       if (id == USB_ID(0x0d8c, 0x0102)) {
-               if (snd_usb_cm6206_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /* Access Music VirusTI Desktop */
-       if (id == USB_ID(0x133e, 0x0815)) {
-               if (snd_usb_accessmusic_boot_quirk(dev) < 0)
-                       goto __err_val;
-       }
-
-       /*
-        * found a config.  now register to ALSA
-        */
-
-       /* check whether it's already registered */
-       chip = NULL;
-       mutex_lock(&register_mutex);
-       for (i = 0; i < SNDRV_CARDS; i++) {
-               if (usb_chip[i] && usb_chip[i]->dev == dev) {
-                       if (usb_chip[i]->shutdown) {
-                               snd_printk(KERN_ERR "USB device is in the shutdown state, cannot create a card instance\n");
-                               goto __error;
-                       }
-                       chip = usb_chip[i];
-                       break;
-               }
-       }
-       if (! chip) {
-               /* it's a fresh one.
-                * now look for an empty slot and create a new card instance
-                */
-               for (i = 0; i < SNDRV_CARDS; i++)
-                       if (enable[i] && ! usb_chip[i] &&
-                           (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
-                           (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
-                               if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
-                                       goto __error;
-                               }
-                               snd_card_set_dev(chip->card, &intf->dev);
-                               break;
-                       }
-               if (!chip) {
-                       printk(KERN_ERR "no available usb audio device\n");
-                       goto __error;
-               }
-       }
-
-       chip->txfr_quirk = 0;
-       err = 1; /* continue */
-       if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
-               /* need some special handlings */
-               if ((err = snd_usb_create_quirk(chip, intf, quirk)) < 0)
-                       goto __error;
-       }
-
-       if (err > 0) {
-               /* create normal USB audio interfaces */
-               if (snd_usb_create_streams(chip, ifnum) < 0 ||
-                   snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {
-                       goto __error;
-               }
-       }
-
-       /* we are allowed to call snd_card_register() many times */
-       if (snd_card_register(chip->card) < 0) {
-               goto __error;
-       }
-
-       usb_chip[chip->index] = chip;
-       chip->num_interfaces++;
-       mutex_unlock(&register_mutex);
-       return chip;
-
- __error:
-       if (chip && !chip->num_interfaces)
-               snd_card_free(chip->card);
-       mutex_unlock(&register_mutex);
- __err_val:
-       return NULL;
-}
-
-/*
- * we need to take care of counter, since disconnection can be called also
- * many times as well as usb_audio_probe().
- */
-static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
-{
-       struct snd_usb_audio *chip;
-       struct snd_card *card;
-       struct list_head *p;
-
-       if (ptr == (void *)-1L)
-               return;
-
-       chip = ptr;
-       card = chip->card;
-       mutex_lock(&register_mutex);
-       chip->shutdown = 1;
-       chip->num_interfaces--;
-       if (chip->num_interfaces <= 0) {
-               snd_card_disconnect(card);
-               /* release the pcm resources */
-               list_for_each(p, &chip->pcm_list) {
-                       snd_usb_stream_disconnect(p);
-               }
-               /* release the midi resources */
-               list_for_each(p, &chip->midi_list) {
-                       snd_usbmidi_disconnect(p);
-               }
-               /* release mixer resources */
-               list_for_each(p, &chip->mixer_list) {
-                       snd_usb_mixer_disconnect(p);
-               }
-               usb_chip[chip->index] = NULL;
-               mutex_unlock(&register_mutex);
-               snd_card_free_when_closed(card);
-       } else {
-               mutex_unlock(&register_mutex);
-       }
-}
-
-/*
- * new 2.5 USB kernel API
- */
-static int usb_audio_probe(struct usb_interface *intf,
-                          const struct usb_device_id *id)
-{
-       void *chip;
-       chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
-       if (chip) {
-               usb_set_intfdata(intf, chip);
-               return 0;
-       } else
-               return -EIO;
-}
-
-static void usb_audio_disconnect(struct usb_interface *intf)
-{
-       snd_usb_audio_disconnect(interface_to_usbdev(intf),
-                                usb_get_intfdata(intf));
-}
-
-#ifdef CONFIG_PM
-static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
-{
-       struct snd_usb_audio *chip = usb_get_intfdata(intf);
-       struct list_head *p;
-       struct snd_usb_stream *as;
-
-       if (chip == (void *)-1L)
-               return 0;
-
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
-       if (!chip->num_suspended_intf++) {
-               list_for_each(p, &chip->pcm_list) {
-                       as = list_entry(p, struct snd_usb_stream, list);
-                       snd_pcm_suspend_all(as->pcm);
-               }
-       }
-
-       return 0;
-}
-
-static int usb_audio_resume(struct usb_interface *intf)
-{
-       struct snd_usb_audio *chip = usb_get_intfdata(intf);
-
-       if (chip == (void *)-1L)
-               return 0;
-       if (--chip->num_suspended_intf)
-               return 0;
-       /*
-        * ALSA leaves material resumption to user space
-        * we just notify
-        */
-
-       snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
-
-       return 0;
-}
-#endif         /* CONFIG_PM */
-
-static int __init snd_usb_audio_init(void)
-{
-       if (nrpacks < 1 || nrpacks > MAX_PACKS) {
-               printk(KERN_WARNING "invalid nrpacks value.\n");
-               return -EINVAL;
-       }
-       return usb_register(&usb_audio_driver);
-}
-
-
-static void __exit snd_usb_audio_cleanup(void)
-{
-       usb_deregister(&usb_audio_driver);
-}
-
-module_init(snd_usb_audio_init);
-module_exit(snd_usb_audio_cleanup);
index 42c299c..d679e72 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-/* maximum number of endpoints per interface */
-#define MIDI_MAX_ENDPOINTS 2
-
 /* handling of USB vendor/product ID pairs as 32-bit numbers */
 #define USB_ID(vendor, product) (((vendor) << 16) | (product))
 #define USB_ID_VENDOR(id) ((id) >> 16)
 #define USB_ID_PRODUCT(id) ((u16)(id))
 
 /*
+ *
  */
 
 struct snd_usb_audio {
@@ -51,6 +49,10 @@ struct snd_usb_audio {
        struct list_head midi_list;     /* list of midi interfaces */
 
        struct list_head mixer_list;    /* list of mixer interfaces */
+
+       int setup;                      /* from the 'device_setup' module param */
+       int nrpacks;                    /* from the 'nrpacks' module param */
+       int async_unlink;               /* from the 'async_unlink' module param */
 };
 
 /*
@@ -89,93 +91,8 @@ struct snd_usb_audio_quirk {
        const void *data;
 };
 
-/* data for QUIRK_MIDI_FIXED_ENDPOINT */
-struct snd_usb_midi_endpoint_info {
-       int8_t   out_ep;        /* ep number, 0 autodetect */
-       uint8_t  out_interval;  /* interval for interrupt endpoints */
-       int8_t   in_ep; 
-       uint8_t  in_interval;
-       uint16_t out_cables;    /* bitmask */
-       uint16_t in_cables;     /* bitmask */
-};
-
-/* for QUIRK_MIDI_YAMAHA, data is NULL */
-
-/* for QUIRK_MIDI_MIDIMAN, data points to a snd_usb_midi_endpoint_info
- * structure (out_cables and in_cables only) */
-
-/* for QUIRK_COMPOSITE, data points to an array of snd_usb_audio_quirk
- * structures, terminated with .ifnum = -1 */
-
-/* for QUIRK_AUDIO_FIXED_ENDPOINT, data points to an audioformat structure */
-
-/* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
-
-/* for QUIRK_AUDIO_EDIROL_UAXX, data is NULL */
-
-/* for QUIRK_IGNORE_INTERFACE, data is NULL */
-
-/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */
-
-/* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info
- * structure (out_cables and in_cables only) */
-
-/* for QUIRK_MIDI_CME, data is NULL */
-
-/*
- */
-
-/*E-mu USB samplerate control quirk*/
-enum {
-       EMU_QUIRK_SR_44100HZ = 0,
-       EMU_QUIRK_SR_48000HZ,
-       EMU_QUIRK_SR_88200HZ,
-       EMU_QUIRK_SR_96000HZ,
-       EMU_QUIRK_SR_176400HZ,
-       EMU_QUIRK_SR_192000HZ
-};
-
 #define combine_word(s)    ((*(s)) | ((unsigned int)(s)[1] << 8))
 #define combine_triple(s)  (combine_word(s) | ((unsigned int)(s)[2] << 16))
 #define combine_quad(s)    (combine_triple(s) | ((unsigned int)(s)[3] << 24))
 
-unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
-
-void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
-void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
-
-int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
-                   __u8 request, __u8 requesttype, __u16 value, __u16 index,
-                   void *data, __u16 size, int timeout);
-
-int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
-                        int ignore_error);
-void snd_usb_mixer_disconnect(struct list_head *p);
-
-int snd_usbmidi_create(struct snd_card *card,
-                      struct usb_interface *iface,
-                      struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk);
-void snd_usbmidi_input_stop(struct list_head* p);
-void snd_usbmidi_input_start(struct list_head* p);
-void snd_usbmidi_disconnect(struct list_head *p);
-
-void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
-                       unsigned char samplerate_id);
-
-/*
- * retrieve usb_interface descriptor from the host interface
- * (conditional for compatibility with the older API)
- */
-#ifndef get_iface_desc
-#define get_iface_desc(iface)  (&(iface)->desc)
-#define get_endpoint(alt,ep)   (&(alt)->endpoint[ep].desc)
-#define get_ep_desc(ep)                (&(ep)->desc)
-#define get_cfg_desc(cfg)      (&(cfg)->desc)
-#endif
-
-#ifndef snd_usb_get_speed
-#define snd_usb_get_speed(dev) ((dev)->speed)
-#endif
-
 #endif /* __USBAUDIO_H */
index 44deb21..5f7b942 100644 (file)
@@ -25,6 +25,7 @@
 #define MODNAME "US122L"
 #include "usb_stream.c"
 #include "../usbaudio.h"
+#include "../midi.h"
 #include "us122l.h"
 
 MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>");
index 1d174ce..e43c0a8 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef USBUSX2Y_H
 #define USBUSX2Y_H
 #include "../usbaudio.h"
+#include "../midi.h"
 #include "usbus428ctldefs.h" 
 
 #define NRURBS         2