Merge branch 'topic/usb' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Thu, 20 May 2010 09:59:43 +0000 (11:59 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 20 May 2010 09:59:43 +0000 (11:59 +0200)
include/linux/usb/audio-v2.h
include/linux/usb/audio.h
sound/usb/mixer.c

index 0952231..2389f93 100644 (file)
@@ -105,6 +105,17 @@ struct uac_as_header_descriptor_v2 {
        __u8 iChannelNames;
 } __attribute__((packed));
 
+/* 6.1 Interrupt Data Message */
+
+#define UAC2_INTERRUPT_DATA_MSG_VENDOR (1 << 0)
+#define UAC2_INTERRUPT_DATA_MSG_EP     (1 << 1)
+
+struct uac2_interrupt_data_msg {
+       __u8 bInfo;
+       __u8 bAttribute;
+       __le16 wValue;
+       __le16 wIndex;
+} __attribute__((packed));
 
 /* A.7 Audio Function Category Codes */
 #define UAC2_FUNCTION_SUBCLASS_UNDEFINED       0x00
@@ -153,6 +164,7 @@ struct uac_as_header_descriptor_v2 {
 /* A.14 Audio Class-Specific Request Codes */
 #define UAC2_CS_CUR                    0x01
 #define UAC2_CS_RANGE                  0x02
+#define UAC2_CS_MEM                    0x03
 
 /* A.15 Encoder Type Codes */
 #define UAC2_ENCODER_UNDEFINED         0x00
index 905a87c..c0ef18d 100644 (file)
@@ -244,7 +244,7 @@ struct uac_selector_unit_descriptor {
 static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc)
 {
        __u8 *raw = (__u8 *) desc;
-       return raw[desc->bLength - 1];
+       return raw[9 + desc->bLength - 1];
 }
 
 /* 4.3.2.5 Feature Unit Descriptor */
@@ -456,7 +456,7 @@ struct uac_iso_endpoint_descriptor {
        __u8  bmAttributes;
        __u8  bLockDelayUnits;
        __le16 wLockDelay;
-};
+} __attribute__((packed));
 #define UAC_ISO_ENDPOINT_DESC_SIZE     7
 
 #define UAC_EP_CS_ATTR_SAMPLE_RATE     0x01
@@ -488,6 +488,21 @@ struct uac_iso_endpoint_descriptor {
 #define UAC_FU_BASS_BOOST      (1 << (UAC_BASS_BOOST_CONTROL - 1))
 #define UAC_FU_LOUDNESS                (1 << (UAC_LOUDNESS_CONTROL - 1))
 
+/* status word format (3.7.1.1) */
+
+#define UAC1_STATUS_TYPE_ORIG_MASK             0x0f
+#define UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF 0x0
+#define UAC1_STATUS_TYPE_ORIG_AUDIO_STREAM_IF  0x1
+#define UAC1_STATUS_TYPE_ORIG_AUDIO_STREAM_EP  0x2
+
+#define UAC1_STATUS_TYPE_IRQ_PENDING           (1 << 7)
+#define UAC1_STATUS_TYPE_MEM_CHANGED           (1 << 6)
+
+struct uac1_status_word {
+       __u8 bStatusType;
+       __u8 bOriginator;
+} __attribute__((packed));
+
 #ifdef __KERNEL__
 
 struct usb_audio_control {
index 21613fe..97dd176 100644 (file)
@@ -656,7 +656,7 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_
                case UAC_FEATURE_UNIT: {
                        /* the header is the same for v1 and v2 */
                        struct uac_feature_unit_descriptor *d = p1;
-                       id = d->bUnitID;
+                       id = d->bSourceID;
                        break; /* continue to parse */
                }
                case UAC_MIXER_UNIT: {
@@ -1443,8 +1443,8 @@ static struct procunit_info procunits[] = {
  * predefined data for extension units
  */
 static struct procunit_value_info clock_rate_xu_info[] = {
-       { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 },
-       { 0 }
+       { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 },
+       { 0 }
 };
 static struct procunit_value_info clock_source_xu_info[] = {
        { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN },
@@ -1967,26 +1967,98 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
        }
 }
 
-static void snd_usb_mixer_status_complete(struct urb *urb)
+static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer,
+                                      int attribute, int value, int index)
+{
+       struct usb_mixer_elem_info *info;
+       __u8 unitid = (index >> 8) & 0xff;
+       __u8 control = (value >> 8) & 0xff;
+       __u8 channel = value & 0xff;
+
+       if (channel >= MAX_CHANNELS) {
+               snd_printk(KERN_DEBUG "%s(): bogus channel number %d\n",
+                               __func__, channel);
+               return;
+       }
+
+       for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) {
+               if (info->control != control)
+                       continue;
+
+               switch (attribute) {
+               case UAC2_CS_CUR:
+                       /* invalidate cache, so the value is read from the device */
+                       if (channel)
+                               info->cached &= ~(1 << channel);
+                       else /* master channel */
+                               info->cached = 0;
+
+                       snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                       info->elem_id);
+                       break;
+
+               case UAC2_CS_RANGE:
+                       /* TODO */
+                       break;
+
+               case UAC2_CS_MEM:
+                       /* TODO */
+                       break;
+
+               default:
+                       snd_printk(KERN_DEBUG "unknown attribute %d in interrupt\n",
+                                               attribute);
+                       break;
+               } /* switch */
+       }
+}
+
+static void snd_usb_mixer_interrupt(struct urb *urb)
 {
        struct usb_mixer_interface *mixer = urb->context;
+       int len = urb->actual_length;
+
+       if (urb->status != 0)
+               goto requeue;
 
-       if (urb->status == 0) {
-               u8 *buf = urb->transfer_buffer;
-               int i;
+       if (mixer->protocol == UAC_VERSION_1) {
+               struct uac1_status_word *status;
 
-               for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) {
+               for (status = urb->transfer_buffer;
+                    len >= sizeof(*status);
+                    len -= sizeof(*status), status++) {
                        snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n",
-                                  buf[0], buf[1]);
+                                               status->bStatusType,
+                                               status->bOriginator);
+
                        /* ignore any notifications not from the control interface */
-                       if ((buf[0] & 0x0f) != 0)
+                       if ((status->bStatusType & UAC1_STATUS_TYPE_ORIG_MASK) !=
+                               UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF)
                                continue;
-                       if (!(buf[0] & 0x40))
-                               snd_usb_mixer_notify_id(mixer, buf[1]);
+
+                       if (status->bStatusType & UAC1_STATUS_TYPE_MEM_CHANGED)
+                               snd_usb_mixer_rc_memory_change(mixer, status->bOriginator);
                        else
-                               snd_usb_mixer_rc_memory_change(mixer, buf[1]);
+                               snd_usb_mixer_notify_id(mixer, status->bOriginator);
+               }
+       } else { /* UAC_VERSION_2 */
+               struct uac2_interrupt_data_msg *msg;
+
+               for (msg = urb->transfer_buffer;
+                    len >= sizeof(*msg);
+                    len -= sizeof(*msg), msg++) {
+                       /* drop vendor specific and endpoint requests */
+                       if ((msg->bInfo & UAC2_INTERRUPT_DATA_MSG_VENDOR) ||
+                           (msg->bInfo & UAC2_INTERRUPT_DATA_MSG_EP))
+                               continue;
+
+                       snd_usb_mixer_interrupt_v2(mixer, msg->bAttribute,
+                                                  le16_to_cpu(msg->wValue),
+                                                  le16_to_cpu(msg->wIndex));
                }
        }
+
+requeue:
        if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
                urb->dev = mixer->chip->dev;
                usb_submit_urb(urb, GFP_ATOMIC);
@@ -2023,7 +2095,7 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
        usb_fill_int_urb(mixer->urb, mixer->chip->dev,
                         usb_rcvintpipe(mixer->chip->dev, epnum),
                         transfer_buffer, buffer_length,
-                        snd_usb_mixer_status_complete, mixer, ep->bInterval);
+                        snd_usb_mixer_interrupt, mixer, ep->bInterval);
        usb_submit_urb(mixer->urb, GFP_KERNEL);
        return 0;
 }