[patch 1/2] add support for capture (microphone) to g_audio

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch section contains (among other things) the modification to
composite.c/h to support routing of setup(endpoint) to the function.


Signed-off-by: Robin Callender <robin_callender@xxxxxxxxxxx>
---


diff -Naur linux-2.6.31.orig/drivers/usb/gadget/audio.c linux-2.6.31.new/drivers/usb/gadget/audio.c
--- linux-2.6.31.orig/drivers/usb/gadget/audio.c	2009-09-12 11:51:48.000000000 -0700
+++ linux-2.6.31.new/drivers/usb/gadget/audio.c	2009-09-18 10:11:00.000000000 -0700
@@ -4,6 +4,8 @@
  * Copyright (C) 2008 Bryan Wu <cooloney@xxxxxxxxxx>
  * Copyright (C) 2008 Analog Devices, Inc
  *
+ * Copyright (C) 2009 Robin Callender
+ *
  * Enter bugs at http://blackfin.uclinux.org/
  *
  * Licensed under the GPL-2 or later.
@@ -89,120 +91,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-/**
- * Handle USB audio endpoint set/get command in setup class request
- */
-
-static int audio_set_endpoint_req(struct usb_configuration *c,
-		const struct usb_ctrlrequest *ctrl)
-{
-	struct usb_composite_dev *cdev = c->cdev;
-	int			value = -EOPNOTSUPP;
-	u16			ep = le16_to_cpu(ctrl->wIndex);
-	u16			len = le16_to_cpu(ctrl->wLength);
-	u16			w_value = le16_to_cpu(ctrl->wValue);
-
-	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
-			ctrl->bRequest, w_value, len, ep);
-
-	switch (ctrl->bRequest) {
-	case UAC_SET_CUR:
-		value = 0;
-		break;
-
-	case UAC_SET_MIN:
-		break;
-
-	case UAC_SET_MAX:
-		break;
-
-	case UAC_SET_RES:
-		break;
-
-	case UAC_SET_MEM:
-		break;
-
-	default:
-		break;
-	}
-
-	return value;
-}
-
-static int audio_get_endpoint_req(struct usb_configuration *c,
-		const struct usb_ctrlrequest *ctrl)
-{
-	struct usb_composite_dev *cdev = c->cdev;
-	int value = -EOPNOTSUPP;
-	u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
-	u16 len = le16_to_cpu(ctrl->wLength);
-	u16 w_value = le16_to_cpu(ctrl->wValue);
-
-	DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
-			ctrl->bRequest, w_value, len, ep);
-
-	switch (ctrl->bRequest) {
-	case UAC_GET_CUR:
-	case UAC_GET_MIN:
-	case UAC_GET_MAX:
-	case UAC_GET_RES:
-		value = 3;
-		break;
-	case UAC_GET_MEM:
-		break;
-	default:
-		break;
-	}
-
-	return value;
-}
-
-static int
-audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
-{
-	struct usb_composite_dev *cdev = c->cdev;
-	struct usb_request *req = cdev->req;
-	int value = -EOPNOTSUPP;
-	u16 w_index = le16_to_cpu(ctrl->wIndex);
-	u16 w_value = le16_to_cpu(ctrl->wValue);
-	u16 w_length = le16_to_cpu(ctrl->wLength);
-
-	/* composite driver infrastructure handles everything except
-	 * Audio class messages; interface activation uses set_alt().
-	 */
-	switch (ctrl->bRequestType) {
-	case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
-		value = audio_set_endpoint_req(c, ctrl);
-		break;
-
-	case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
-		value = audio_get_endpoint_req(c, ctrl);
-		break;
-
-	default:
-		ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n",
-			ctrl->bRequestType, ctrl->bRequest,
-			w_value, w_index, w_length);
-	}
-
-	/* respond with data transfer or status phase? */
-	if (value >= 0) {
-		DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n",
-			ctrl->bRequestType, ctrl->bRequest,
-			w_value, w_index, w_length);
-		req->zero = 0;
-		req->length = value;
-		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
-		if (value < 0)
-			ERROR(cdev, "Audio response on err %d\n", value);
-	}
-
-	/* device either stalls (value < 0) or reports success */
-	return value;
-}
-
-/*-------------------------------------------------------------------------*/
-
 static int __init audio_do_config(struct usb_configuration *c)
 {
 	/* FIXME alloc iConfiguration string, set it in c->strings */
@@ -220,7 +108,6 @@
 static struct usb_configuration audio_config_driver = {
 	.label			= DRIVER_DESC,
 	.bind			= audio_do_config,
-	.setup			= audio_setup,
 	.bConfigurationValue	= 1,
 	/* .iConfiguration = DYNAMIC */
 	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER,
diff -Naur linux-2.6.31.orig/drivers/usb/gadget/composite.c linux-2.6.31.new/drivers/usb/gadget/composite.c
--- linux-2.6.31.orig/drivers/usb/gadget/composite.c	2009-09-12 11:51:48.000000000 -0700
+++ linux-2.6.31.new/drivers/usb/gadget/composite.c	2009-09-18 10:18:54.000000000 -0700
@@ -228,6 +228,27 @@
 	return -ENODEV;
 }
 
+/**
+ * usb_endpoint_id() - associate an bEndpointAddress with
+ * the given usb_function in the usb_configuration.
+ * @config: configuration associated with the endpoint
+ * @function: function handling the interface
+ * @endpointaddress: endpoint's address without USB_DIR_IN bit.
+ *
+ * usb_endpoint_id() is called from usb_function.bind()
+ * callbacks to record endpoint IDs.
+ */
+int __init usb_endpoint_id(struct usb_configuration *config,
+		struct usb_function *function,
+		u8 endpointaddress)
+{
+	if (endpointaddress < MAX_CONFIG_ENDPOINTS) {
+		config->endpoint[endpointaddress] = function;
+		return 0;
+	}
+	return -ENODEV;
+}
+
 static int config_buf(struct usb_configuration *config,
 		enum usb_device_speed speed, void *buf, u8 type)
 {
@@ -810,9 +831,29 @@
 		 */
 		if ((ctrl->bRequestType & USB_RECIP_MASK)
 				== USB_RECIP_INTERFACE) {
-			f = cdev->config->interface[intf];
-			if (f && f->setup)
-				value = f->setup(f, ctrl);
+			if (intf < MAX_CONFIG_INTERFACES) {
+				f = cdev->config->interface[intf];
+				if (f && f->setup) {
+					value = f->setup(f, ctrl);
+				}
+				else
+					f = NULL;
+			}
+			else
+				f = NULL;
+			
+		}
+		if ((ctrl->bRequestType & USB_RECIP_MASK)
+				== USB_RECIP_ENDPOINT) {
+			u8 epaddr = w_index & ~USB_DIR_IN;
+			if (epaddr < MAX_CONFIG_ENDPOINTS) {
+				f = cdev->config->endpoint[epaddr];
+				if (f && f->setup) {
+					value = f->setup(f, ctrl);
+				}
+				else
+					f = NULL;
+			}
 			else
 				f = NULL;
 		}
diff -Naur linux-2.6.31.orig/drivers/usb/gadget/u_audio.c linux-2.6.31.new/drivers/usb/gadget/u_audio.c
--- linux-2.6.31.orig/drivers/usb/gadget/u_audio.c	2009-09-18 10:43:41.000000000 -0700
+++ linux-2.6.31.new/drivers/usb/gadget/u_audio.c	2009-09-18 10:51:39.000000000 -0700
@@ -109,6 +109,71 @@
 /**
  * Set default hardware params
  */
+static int capture_default_hw_params(struct gaudio_snd_dev *snd)
+{
+	struct snd_pcm_substream *substream = snd->substream;
+	struct snd_pcm_runtime   *runtime = substream->runtime;
+	struct snd_pcm_hw_params *params;
+	snd_pcm_sframes_t result;
+
+	/*
+	 * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
+	 * SNDRV_PCM_FORMAT_S16_LE
+	 * CHANNELS: 1
+	 * RATE: 48000
+	 */
+	snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
+	snd->format = SNDRV_PCM_FORMAT_S16_LE;
+	snd->channels = 1;
+	snd->rate = 48000;
+
+	params = kzalloc(sizeof(*params), GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	_snd_pcm_hw_params_any(params);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
+			snd->access, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			snd->format, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
+			snd->channels, 0);
+	_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
+			snd->rate, 0);
+
+	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
+	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
+
+	result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE,
+					NULL);
+	if (result < 0) {
+		ERROR(snd->card,
+			"Preparing sound card failed: %d\n", (int)result);
+		kfree(params);
+		return result;
+	}
+
+	/* Store the hardware parameters */
+	snd->access = params_access(params);
+	snd->format = params_format(params);
+	snd->channels = params_channels(params);
+	snd->rate = params_rate(params);
+
+	runtime->frame_bits = snd_pcm_format_physical_width(runtime->format);
+
+	kfree(params);
+
+	INFO(snd->card,
+		"Capture hardware params: access %x, format %x, "
+		"channels %d, rate %d\n",
+		snd->access, snd->format, snd->channels, snd->rate);
+
+	return 0;
+}
+
+/**
+ * Set default hardware params
+ */
 static int playback_default_hw_params(struct gaudio_snd_dev *snd)
 {
 	struct snd_pcm_substream *substream = snd->substream;
@@ -160,13 +225,55 @@
 	kfree(params);
 
 	INFO(snd->card,
-		"Hardware params: access %x, format %x, channels %d, rate %d\n",
+		"Playback hardware params: access %x, format %x, "
+		"channels %d, rate %d\n",
 		snd->access, snd->format, snd->channels, snd->rate);
 
 	return 0;
 }
 
 /**
+ * Capture audio buffer data by ALSA PCM device
+ */
+static size_t u_audio_capture(struct gaudio *card, void *buf, size_t count)
+{
+	ssize_t result;
+	mm_segment_t old_fs;
+	snd_pcm_sframes_t frames;
+
+	struct gaudio_snd_dev	 *snd = &card->capture;
+	struct snd_pcm_substream *substream = snd->substream;
+	struct snd_pcm_runtime   *runtime = substream->runtime;
+
+try_again:
+	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+	    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
+		result = snd_pcm_kernel_ioctl(substream,
+				SNDRV_PCM_IOCTL_PREPARE, NULL);
+		if (result < 0) {
+			ERROR(card, "Preparing sound card failed: %d\n",
+					(int)result);
+			return result;
+		}
+	}
+	frames = bytes_to_frames(runtime, count);
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	result = snd_pcm_lib_read(substream, buf, frames);
+	if (result != frames) {
+		ERROR(card, "Capture error: %d\n", (int)result);
+		set_fs(old_fs);
+		goto try_again;
+	}
+
+	set_fs(old_fs);
+
+	return 0;
+}
+
+/**
  * Playback audio buffer data by ALSA PCM device
  */
 static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
@@ -214,6 +321,16 @@
 	return card->playback.rate;
 }
 
+static int u_audio_get_capture_channels(struct gaudio *card)
+{
+	return card->capture.channels;
+}
+
+static int u_audio_get_capture_rate(struct gaudio *card)
+{
+	return card->capture.rate;
+}
+
 /**
  * Open ALSA PCM and control device files
  * Initial the PCM or control device
@@ -233,7 +350,6 @@
 		int ret = PTR_ERR(snd->filp);
 		ERROR(card, "unable to open sound control device file: %s\n",
 				fn_cntl);
-		snd->filp = NULL;
 		return ret;
 	}
 	snd->card = card;
@@ -243,24 +359,23 @@
 	snd->filp = filp_open(fn_play, O_WRONLY, 0);
 	if (IS_ERR(snd->filp)) {
 		ERROR(card, "No such PCM playback device: %s\n", fn_play);
-		snd->filp = NULL;
+	} else {
+		pcm_file = snd->filp->private_data;
+		snd->substream = pcm_file->substream;
+		snd->card = card;
+		playback_default_hw_params(snd);
 	}
-	pcm_file = snd->filp->private_data;
-	snd->substream = pcm_file->substream;
-	snd->card = card;
-	playback_default_hw_params(snd);
 
 	/* Open PCM capture device and setup substream */
 	snd = &card->capture;
 	snd->filp = filp_open(fn_cap, O_RDONLY, 0);
 	if (IS_ERR(snd->filp)) {
 		ERROR(card, "No such PCM capture device: %s\n", fn_cap);
-		snd->substream = NULL;
-		snd->card = NULL;
 	} else {
 		pcm_file = snd->filp->private_data;
 		snd->substream = pcm_file->substream;
 		snd->card = card;
+		capture_default_hw_params(snd);
 	}
 
 	return 0;
diff -Naur linux-2.6.31.orig/include/linux/usb/audio.h linux-2.6.31.new/include/linux/usb/audio.h
--- linux-2.6.31.orig/include/linux/usb/audio.h	2009-09-12 11:51:48.000000000 -0700
+++ linux-2.6.31.new/include/linux/usb/audio.h	2009-09-16 10:55:02.000000000 -0700
@@ -43,6 +43,12 @@
 /* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */
 #define UAC_EP_GENERAL			0x01
 
+/* endpoint attributes */
+#define UAC_EP_ATTR_MASK		0x0c
+#define UAC_EP_ATTR_ASYNC		0x04
+#define UAC_EP_ATTR_ADAPTIVE		0x08
+#define UAC_EP_ATTR_SYNC		0x0c
+
 /* A.9 Audio Class-Specific Request Codes */
 #define UAC_SET_			0x00
 #define UAC_GET_			0x80
@@ -154,22 +160,48 @@
 #define UAC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER	0x306
 #define UAC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER	0x307
 
+#define UAC_CHANNEL_CONFIG_L		0x0001
+#define UAC_CHANNEL_CONFIG_R		0x0002
+#define UAC_CHANNEL_CONFIG_C		0x0004
+#define UAC_CHANNEL_CONFIG_LFE		0x0008
+#define UAC_CHANNEL_CONFIG_LS		0x0010
+#define UAC_CHANNEL_CONFIG_RS		0x0020
+#define UAC_CHANNEL_CONFIG_LC		0x0040
+#define UAC_CHANNEL_CONFIG_RC		0x0080
+#define UAC_CHANNEL_CONFIG_S		0x0100
+#define UAC_CHANNEL_CONFIG_SL		0x0200
+#define UAC_CHANNEL_CONFIG_SR		0x0400
+#define UAC_CHANNEL_CONFIG_T		0x0800
+
 /* Set bControlSize = 2 as default setting */
-#define UAC_DT_FEATURE_UNIT_SIZE(ch)		(7 + ((ch) + 1) * 2)
+#define UAC_FEATURE_UNIT_SIZE(ch,n)	(7+(((ch)+1)*n))
 
 /* As above, but more useful for defining your own descriptors: */
-#define DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(ch) 		\
-struct uac_feature_unit_descriptor_##ch {			\
+#define DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(suffix,ch) 		\
+struct uac_feature_unit_descriptor_##suffix {			\
 	__u8  bLength;						\
 	__u8  bDescriptorType;					\
 	__u8  bDescriptorSubtype;				\
 	__u8  bUnitID;						\
 	__u8  bSourceID;					\
 	__u8  bControlSize;					\
-	__le16 bmaControls[ch + 1];				\
+	__u8  bmaControls[ch + 1];				\
 	__u8  iFeature;						\
 } __attribute__ ((packed))
 
+#define UAC_SELECTOR_UNIT_SIZE(pins)	(6 + pins)
+
+#define DECLARE_UAC_SELECTOR_UNIT_DESCRIPTOR(suffix,pins)	\
+struct uac_selector_unit_descriptor_##suffix {			\
+	__u8  bLength;						\
+	__u8  bDescriptorType;					\
+	__u8  bDescriptorSubtype;				\
+	__u8  bUnitID;						\
+	__u8  bNrInPins;					\
+	__u8  baSourceID[pins];					\
+	__u8  iSelector;					\
+} __attribute__ ((packed))
+
 /* 4.5.2 Class-Specific AS Interface Descriptor */
 struct uac_as_header_descriptor {
 	__u8  bLength;			/* in bytes: 7 */
@@ -238,6 +270,10 @@
 #define UAC_FORMAT_TYPE_II		0x2
 #define UAC_FORMAT_TYPE_III		0x3
 
+#define UAS_ENDPOINT_ASYNC		(1 << 2)
+#define UAS_ENDPOINT_ADAPTIVE		(2 << 2)
+#define UAS_ENDPOINT_SYNC		(3 << 2)
+
 struct uac_iso_endpoint_descriptor {
 	__u8  bLength;			/* in bytes: 7 */
 	__u8  bDescriptorType;		/* USB_DT_CS_ENDPOINT */
@@ -245,13 +281,16 @@
 	__u8  bmAttributes;
 	__u8  bLockDelayUnits;
 	__le16 wLockDelay;
-};
+} __attribute__ ((packed));
 #define UAC_ISO_ENDPOINT_DESC_SIZE	7
 
 #define UAC_EP_CS_ATTR_SAMPLE_RATE	0x01
 #define UAC_EP_CS_ATTR_PITCH_CONTROL	0x02
 #define UAC_EP_CS_ATTR_FILL_MAX		0x80
 
+#define UAC_EP_SAMPLE_FREQ	(1 << (UAC_EP_CS_ATTR_SAMPLE_RATE - 1))
+#define UAC_EP_PITCH_CONTROL	(1 << (UAC_EP_CS_ATTR_PITCH_CONTROL - 1))
+
 /* A.10.2 Feature Unit Control Selectors */
 #define UAC_FU_CONTROL_UNDEFINED	0x00
 #define UAC_MUTE_CONTROL		0x01
diff -Naur linux-2.6.31.orig/include/linux/usb/composite.h linux-2.6.31.new/include/linux/usb/composite.h
--- linux-2.6.31.orig/include/linux/usb/composite.h	2009-09-09 15:13:59.000000000 -0700
+++ linux-2.6.31.new/include/linux/usb/composite.h	2009-09-16 10:53:42.000000000 -0700
@@ -135,6 +135,7 @@
 int usb_function_activate(struct usb_function *);
 
 int usb_interface_id(struct usb_configuration *, struct usb_function *);
+int usb_endpoint_id(struct usb_configuration *, struct usb_function *, u8);
 
 /**
  * ep_choose - select descriptor endpoint at current device speed
@@ -152,6 +153,7 @@
 }
 
 #define	MAX_CONFIG_INTERFACES		16	/* arbitrary; max 255 */
+#define	MAX_CONFIG_ENDPOINTS		16	/* arbitrary; max 255 */
 
 /**
  * struct usb_configuration - represents one gadget configuration
@@ -228,6 +230,7 @@
 	unsigned		highspeed:1;
 	unsigned		fullspeed:1;
 	struct usb_function	*interface[MAX_CONFIG_INTERFACES];
+	struct usb_function	*endpoint[MAX_CONFIG_ENDPOINTS];
 };
 
 int usb_add_config(struct usb_composite_dev *,


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux