Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ce3db4e authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'topic/usb' into for-linus

parents 20406f9b e213e9cf
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -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
@@ -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
+17 −2
Original line number Original line Diff line number Diff line
@@ -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 */
@@ -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
@@ -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 {
+86 −14
Original line number Original line Diff line number Diff line
@@ -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: {
@@ -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);
@@ -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;
}
}