Loading include/linux/usb/audio-v2.h +12 −0 Original line number Original line Diff line number Diff line Loading @@ -105,6 +105,17 @@ struct uac_as_header_descriptor_v2 { __u8 iChannelNames; __u8 iChannelNames; } __attribute__((packed)); } __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 */ /* A.7 Audio Function Category Codes */ #define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00 #define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00 Loading Loading @@ -153,6 +164,7 @@ struct uac_as_header_descriptor_v2 { /* A.14 Audio Class-Specific Request Codes */ /* A.14 Audio Class-Specific Request Codes */ #define UAC2_CS_CUR 0x01 #define UAC2_CS_CUR 0x01 #define UAC2_CS_RANGE 0x02 #define UAC2_CS_RANGE 0x02 #define UAC2_CS_MEM 0x03 /* A.15 Encoder Type Codes */ /* A.15 Encoder Type Codes */ #define UAC2_ENCODER_UNDEFINED 0x00 #define UAC2_ENCODER_UNDEFINED 0x00 Loading include/linux/usb/audio.h +17 −2 Original line number Original line Diff line number Diff line Loading @@ -244,7 +244,7 @@ struct uac_selector_unit_descriptor { static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc) static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc) { { __u8 *raw = (__u8 *) desc; __u8 *raw = (__u8 *) desc; return raw[desc->bLength - 1]; return raw[9 + desc->bLength - 1]; } } /* 4.3.2.5 Feature Unit Descriptor */ /* 4.3.2.5 Feature Unit Descriptor */ Loading Loading @@ -456,7 +456,7 @@ struct uac_iso_endpoint_descriptor { __u8 bmAttributes; __u8 bmAttributes; __u8 bLockDelayUnits; __u8 bLockDelayUnits; __le16 wLockDelay; __le16 wLockDelay; }; } __attribute__((packed)); #define UAC_ISO_ENDPOINT_DESC_SIZE 7 #define UAC_ISO_ENDPOINT_DESC_SIZE 7 #define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01 #define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01 Loading Loading @@ -488,6 +488,21 @@ struct uac_iso_endpoint_descriptor { #define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) #define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) #define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_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__ #ifdef __KERNEL__ struct usb_audio_control { struct usb_audio_control { Loading sound/usb/mixer.c +86 −14 Original line number Original line Diff line number Diff line Loading @@ -656,7 +656,7 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_ case UAC_FEATURE_UNIT: { case UAC_FEATURE_UNIT: { /* the header is the same for v1 and v2 */ /* the header is the same for v1 and v2 */ struct uac_feature_unit_descriptor *d = p1; struct uac_feature_unit_descriptor *d = p1; id = d->bUnitID; id = d->bSourceID; break; /* continue to parse */ break; /* continue to parse */ } } case UAC_MIXER_UNIT: { case UAC_MIXER_UNIT: { Loading Loading @@ -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; struct usb_mixer_interface *mixer = urb->context; int len = urb->actual_length; if (urb->status == 0) { if (urb->status != 0) u8 *buf = urb->transfer_buffer; goto requeue; 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", 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 */ /* 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; 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 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) { if (urb->status != -ENOENT && urb->status != -ECONNRESET) { urb->dev = mixer->chip->dev; urb->dev = mixer->chip->dev; usb_submit_urb(urb, GFP_ATOMIC); usb_submit_urb(urb, GFP_ATOMIC); Loading Loading @@ -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_fill_int_urb(mixer->urb, mixer->chip->dev, usb_rcvintpipe(mixer->chip->dev, epnum), usb_rcvintpipe(mixer->chip->dev, epnum), transfer_buffer, buffer_length, 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); usb_submit_urb(mixer->urb, GFP_KERNEL); return 0; return 0; } } Loading Loading
include/linux/usb/audio-v2.h +12 −0 Original line number Original line Diff line number Diff line Loading @@ -105,6 +105,17 @@ struct uac_as_header_descriptor_v2 { __u8 iChannelNames; __u8 iChannelNames; } __attribute__((packed)); } __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 */ /* A.7 Audio Function Category Codes */ #define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00 #define UAC2_FUNCTION_SUBCLASS_UNDEFINED 0x00 Loading Loading @@ -153,6 +164,7 @@ struct uac_as_header_descriptor_v2 { /* A.14 Audio Class-Specific Request Codes */ /* A.14 Audio Class-Specific Request Codes */ #define UAC2_CS_CUR 0x01 #define UAC2_CS_CUR 0x01 #define UAC2_CS_RANGE 0x02 #define UAC2_CS_RANGE 0x02 #define UAC2_CS_MEM 0x03 /* A.15 Encoder Type Codes */ /* A.15 Encoder Type Codes */ #define UAC2_ENCODER_UNDEFINED 0x00 #define UAC2_ENCODER_UNDEFINED 0x00 Loading
include/linux/usb/audio.h +17 −2 Original line number Original line Diff line number Diff line Loading @@ -244,7 +244,7 @@ struct uac_selector_unit_descriptor { static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc) static inline __u8 uac_selector_unit_iSelector(struct uac_selector_unit_descriptor *desc) { { __u8 *raw = (__u8 *) desc; __u8 *raw = (__u8 *) desc; return raw[desc->bLength - 1]; return raw[9 + desc->bLength - 1]; } } /* 4.3.2.5 Feature Unit Descriptor */ /* 4.3.2.5 Feature Unit Descriptor */ Loading Loading @@ -456,7 +456,7 @@ struct uac_iso_endpoint_descriptor { __u8 bmAttributes; __u8 bmAttributes; __u8 bLockDelayUnits; __u8 bLockDelayUnits; __le16 wLockDelay; __le16 wLockDelay; }; } __attribute__((packed)); #define UAC_ISO_ENDPOINT_DESC_SIZE 7 #define UAC_ISO_ENDPOINT_DESC_SIZE 7 #define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01 #define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01 Loading Loading @@ -488,6 +488,21 @@ struct uac_iso_endpoint_descriptor { #define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) #define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1)) #define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_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__ #ifdef __KERNEL__ struct usb_audio_control { struct usb_audio_control { Loading
sound/usb/mixer.c +86 −14 Original line number Original line Diff line number Diff line Loading @@ -656,7 +656,7 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_ case UAC_FEATURE_UNIT: { case UAC_FEATURE_UNIT: { /* the header is the same for v1 and v2 */ /* the header is the same for v1 and v2 */ struct uac_feature_unit_descriptor *d = p1; struct uac_feature_unit_descriptor *d = p1; id = d->bUnitID; id = d->bSourceID; break; /* continue to parse */ break; /* continue to parse */ } } case UAC_MIXER_UNIT: { case UAC_MIXER_UNIT: { Loading Loading @@ -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; struct usb_mixer_interface *mixer = urb->context; int len = urb->actual_length; if (urb->status == 0) { if (urb->status != 0) u8 *buf = urb->transfer_buffer; goto requeue; 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", 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 */ /* 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; 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 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) { if (urb->status != -ENOENT && urb->status != -ECONNRESET) { urb->dev = mixer->chip->dev; urb->dev = mixer->chip->dev; usb_submit_urb(urb, GFP_ATOMIC); usb_submit_urb(urb, GFP_ATOMIC); Loading Loading @@ -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_fill_int_urb(mixer->urb, mixer->chip->dev, usb_rcvintpipe(mixer->chip->dev, epnum), usb_rcvintpipe(mixer->chip->dev, epnum), transfer_buffer, buffer_length, 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); usb_submit_urb(mixer->urb, GFP_KERNEL); return 0; return 0; } } Loading