mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-15 18:04:36 +00:00
Merge branch 'topic/usb' into for-linus
This commit is contained in:
commit
9ce3db4e79
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
u8 *buf = urb->transfer_buffer;
|
||||
int i;
|
||||
if (urb->status != 0)
|
||||
goto requeue;
|
||||
|
||||
for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) {
|
||||
if (mixer->protocol == UAC_VERSION_1) {
|
||||
struct uac1_status_word *status;
|
||||
|
||||
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user