UPSTREAM: usb: gadget: f_uac1: disable IN/OUT ep if unused
User can configure f_uac1 function via p_chmask/c_chmask whether uac1 shall support playback and/or capture, but it has only effect on the created ALSA device, but not on the USB descriptor. This patch adds playback/capture descriptors dependent on that parameter. It is similar to the same conversion done earlier for f_uac2 Signed-off-by: Ruslan Bilovol <ruslan.bilovol@gmail.com> Link: https://lore.kernel.org/r/1614599375-8803-6-git-send-email-ruslan.bilovol@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Bug: 235196743 (cherry picked from commit 254cb1e0d78cfa2c189171cacb88fc85d915bc84) Change-Id: I02328b9c496645e487ad9eb4784e9a4ac80c85f9 Signed-off-by: Luiz Matheus <luizmmat@motorola.com>
This commit is contained in:

committed by
Luiz Matheus

parent
b389838308
commit
6719763187
@@ -22,6 +22,9 @@
|
|||||||
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
|
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
|
||||||
#define UAC1_CHANNEL_MASK 0x0FFF
|
#define UAC1_CHANNEL_MASK 0x0FFF
|
||||||
|
|
||||||
|
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
|
||||||
|
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
|
||||||
|
|
||||||
struct f_uac1 {
|
struct f_uac1 {
|
||||||
struct g_audio g_audio;
|
struct g_audio g_audio;
|
||||||
u8 ac_intf, as_in_intf, as_out_intf;
|
u8 ac_intf, as_in_intf, as_out_intf;
|
||||||
@@ -50,11 +53,6 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
|
|||||||
* USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
|
* USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture
|
||||||
* ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
|
* ALSA_Playback -> IT_3 -> OT_4 -> USB-IN
|
||||||
*/
|
*/
|
||||||
#define F_AUDIO_AC_INTERFACE 0
|
|
||||||
#define F_AUDIO_AS_OUT_INTERFACE 1
|
|
||||||
#define F_AUDIO_AS_IN_INTERFACE 2
|
|
||||||
/* Number of streaming interfaces */
|
|
||||||
#define F_AUDIO_NUM_INTERFACES 2
|
|
||||||
|
|
||||||
/* B.3.1 Standard AC Interface Descriptor */
|
/* B.3.1 Standard AC Interface Descriptor */
|
||||||
static struct usb_interface_descriptor ac_interface_desc = {
|
static struct usb_interface_descriptor ac_interface_desc = {
|
||||||
@@ -65,73 +63,47 @@ static struct usb_interface_descriptor ac_interface_desc = {
|
|||||||
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
|
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* The number of AudioStreaming and MIDIStreaming interfaces
|
|
||||||
* in the Audio Interface Collection
|
|
||||||
*/
|
|
||||||
DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
|
|
||||||
|
|
||||||
#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
|
|
||||||
/* 2 input terminals and 2 output terminals */
|
|
||||||
#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
|
|
||||||
+ 2*UAC_DT_INPUT_TERMINAL_SIZE + 2*UAC_DT_OUTPUT_TERMINAL_SIZE)
|
|
||||||
/* B.3.2 Class-Specific AC Interface Descriptor */
|
/* B.3.2 Class-Specific AC Interface Descriptor */
|
||||||
static struct uac1_ac_header_descriptor_2 ac_header_desc = {
|
static struct uac1_ac_header_descriptor *ac_header_desc;
|
||||||
.bLength = UAC_DT_AC_HEADER_LENGTH,
|
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
|
||||||
.bDescriptorSubtype = UAC_HEADER,
|
|
||||||
.bcdADC = cpu_to_le16(0x0100),
|
|
||||||
.wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH),
|
|
||||||
.bInCollection = F_AUDIO_NUM_INTERFACES,
|
|
||||||
.baInterfaceNr = {
|
|
||||||
/* Interface number of the AudioStream interfaces */
|
|
||||||
[0] = 1,
|
|
||||||
[1] = 2,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define USB_OUT_IT_ID 1
|
|
||||||
static struct uac_input_terminal_descriptor usb_out_it_desc = {
|
static struct uac_input_terminal_descriptor usb_out_it_desc = {
|
||||||
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
|
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
||||||
.bTerminalID = USB_OUT_IT_ID,
|
/* .bTerminalID = DYNAMIC */
|
||||||
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
||||||
.bAssocTerminal = 0,
|
.bAssocTerminal = 0,
|
||||||
.wChannelConfig = cpu_to_le16(0x3),
|
.wChannelConfig = cpu_to_le16(0x3),
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IO_OUT_OT_ID 2
|
|
||||||
static struct uac1_output_terminal_descriptor io_out_ot_desc = {
|
static struct uac1_output_terminal_descriptor io_out_ot_desc = {
|
||||||
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
|
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
||||||
.bTerminalID = IO_OUT_OT_ID,
|
/* .bTerminalID = DYNAMIC */
|
||||||
.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
|
.wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
|
||||||
.bAssocTerminal = 0,
|
.bAssocTerminal = 0,
|
||||||
.bSourceID = USB_OUT_IT_ID,
|
/* .bSourceID = DYNAMIC */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define IO_IN_IT_ID 3
|
|
||||||
static struct uac_input_terminal_descriptor io_in_it_desc = {
|
static struct uac_input_terminal_descriptor io_in_it_desc = {
|
||||||
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
|
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
|
||||||
.bTerminalID = IO_IN_IT_ID,
|
/* .bTerminalID = DYNAMIC */
|
||||||
.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
|
.wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
|
||||||
.bAssocTerminal = 0,
|
.bAssocTerminal = 0,
|
||||||
.wChannelConfig = cpu_to_le16(0x3),
|
.wChannelConfig = cpu_to_le16(0x3),
|
||||||
};
|
};
|
||||||
|
|
||||||
#define USB_IN_OT_ID 4
|
|
||||||
static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
|
static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
|
||||||
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
|
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
|
||||||
.bTerminalID = USB_IN_OT_ID,
|
/* .bTerminalID = DYNAMIC */
|
||||||
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
.wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
|
||||||
.bAssocTerminal = 0,
|
.bAssocTerminal = 0,
|
||||||
.bSourceID = IO_IN_IT_ID,
|
/* .bSourceID = DYNAMIC */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* B.4.1 Standard AS Interface Descriptor */
|
/* B.4.1 Standard AS Interface Descriptor */
|
||||||
@@ -176,7 +148,7 @@ static struct uac1_as_header_descriptor as_out_header_desc = {
|
|||||||
.bLength = UAC_DT_AS_HEADER_SIZE,
|
.bLength = UAC_DT_AS_HEADER_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_AS_GENERAL,
|
.bDescriptorSubtype = UAC_AS_GENERAL,
|
||||||
.bTerminalLink = USB_OUT_IT_ID,
|
/* .bTerminalLink = DYNAMIC */
|
||||||
.bDelay = 1,
|
.bDelay = 1,
|
||||||
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
|
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
|
||||||
};
|
};
|
||||||
@@ -185,7 +157,7 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
|
|||||||
.bLength = UAC_DT_AS_HEADER_SIZE,
|
.bLength = UAC_DT_AS_HEADER_SIZE,
|
||||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||||
.bDescriptorSubtype = UAC_AS_GENERAL,
|
.bDescriptorSubtype = UAC_AS_GENERAL,
|
||||||
.bTerminalLink = USB_IN_OT_ID,
|
/* .bTerminalLink = DYNAMIC */
|
||||||
.bDelay = 1,
|
.bDelay = 1,
|
||||||
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
|
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
|
||||||
};
|
};
|
||||||
@@ -513,6 +485,108 @@ static void f_audio_disable(struct usb_function *f)
|
|||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static struct
|
||||||
|
uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
|
||||||
|
{
|
||||||
|
struct uac1_ac_header_descriptor *ac_desc;
|
||||||
|
int ac_header_desc_size;
|
||||||
|
int num_ifaces = 0;
|
||||||
|
|
||||||
|
if (EPOUT_EN(opts))
|
||||||
|
num_ifaces++;
|
||||||
|
if (EPIN_EN(opts))
|
||||||
|
num_ifaces++;
|
||||||
|
|
||||||
|
ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces);
|
||||||
|
|
||||||
|
ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL);
|
||||||
|
if (!ac_desc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ac_desc->bLength = ac_header_desc_size;
|
||||||
|
ac_desc->bDescriptorType = USB_DT_CS_INTERFACE;
|
||||||
|
ac_desc->bDescriptorSubtype = UAC_HEADER;
|
||||||
|
ac_desc->bcdADC = cpu_to_le16(0x0100);
|
||||||
|
ac_desc->bInCollection = num_ifaces;
|
||||||
|
|
||||||
|
/* wTotalLength and baInterfaceNr will be defined later */
|
||||||
|
|
||||||
|
return ac_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use macro to overcome line length limitation */
|
||||||
|
#define USBDHDR(p) (struct usb_descriptor_header *)(p)
|
||||||
|
|
||||||
|
static void setup_descriptor(struct f_uac1_opts *opts)
|
||||||
|
{
|
||||||
|
/* patch descriptors */
|
||||||
|
int i = 1; /* ID's start with 1 */
|
||||||
|
|
||||||
|
if (EPOUT_EN(opts))
|
||||||
|
usb_out_it_desc.bTerminalID = i++;
|
||||||
|
if (EPIN_EN(opts))
|
||||||
|
io_in_it_desc.bTerminalID = i++;
|
||||||
|
if (EPOUT_EN(opts))
|
||||||
|
io_out_ot_desc.bTerminalID = i++;
|
||||||
|
if (EPIN_EN(opts))
|
||||||
|
usb_in_ot_desc.bTerminalID = i++;
|
||||||
|
|
||||||
|
usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
|
||||||
|
io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
|
||||||
|
|
||||||
|
as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
|
||||||
|
as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
|
||||||
|
|
||||||
|
ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength);
|
||||||
|
|
||||||
|
if (EPIN_EN(opts)) {
|
||||||
|
u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
|
||||||
|
|
||||||
|
len += sizeof(usb_in_ot_desc);
|
||||||
|
len += sizeof(io_in_it_desc);
|
||||||
|
ac_header_desc->wTotalLength = cpu_to_le16(len);
|
||||||
|
}
|
||||||
|
if (EPOUT_EN(opts)) {
|
||||||
|
u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
|
||||||
|
|
||||||
|
len += sizeof(usb_out_it_desc);
|
||||||
|
len += sizeof(io_out_ot_desc);
|
||||||
|
ac_header_desc->wTotalLength = cpu_to_le16(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
f_audio_desc[i++] = USBDHDR(&ac_interface_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(ac_header_desc);
|
||||||
|
|
||||||
|
if (EPOUT_EN(opts)) {
|
||||||
|
f_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EPIN_EN(opts)) {
|
||||||
|
f_audio_desc[i++] = USBDHDR(&io_in_it_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EPOUT_EN(opts)) {
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_out_header_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_out_type_i_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_out_ep_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
|
||||||
|
}
|
||||||
|
if (EPIN_EN(opts)) {
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_0_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_1_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_in_header_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_in_type_i_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_in_ep_desc);
|
||||||
|
f_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
|
||||||
|
}
|
||||||
|
f_audio_desc[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
|
static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
|
||||||
{
|
{
|
||||||
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
|
||||||
@@ -556,6 +630,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
struct usb_string *us;
|
struct usb_string *us;
|
||||||
u8 *sam_freq;
|
u8 *sam_freq;
|
||||||
int rate;
|
int rate;
|
||||||
|
int ba_iface_id;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = f_audio_validate_opts(audio, dev);
|
status = f_audio_validate_opts(audio, dev);
|
||||||
@@ -567,6 +642,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
|
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
|
||||||
if (IS_ERR(us))
|
if (IS_ERR(us))
|
||||||
return PTR_ERR(us);
|
return PTR_ERR(us);
|
||||||
|
|
||||||
|
ac_header_desc = build_ac_header_desc(audio_opts);
|
||||||
|
if (!ac_header_desc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
ac_interface_desc.iInterface = us[STR_AC_IF].id;
|
ac_interface_desc.iInterface = us[STR_AC_IF].id;
|
||||||
usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
|
usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
|
||||||
usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
|
usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
|
||||||
@@ -607,40 +687,52 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
uac1->ac_intf = status;
|
uac1->ac_intf = status;
|
||||||
uac1->ac_alt = 0;
|
uac1->ac_alt = 0;
|
||||||
|
|
||||||
|
ba_iface_id = 0;
|
||||||
|
|
||||||
|
if (EPOUT_EN(audio_opts)) {
|
||||||
status = usb_interface_id(c, f);
|
status = usb_interface_id(c, f);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
as_out_interface_alt_0_desc.bInterfaceNumber = status;
|
as_out_interface_alt_0_desc.bInterfaceNumber = status;
|
||||||
as_out_interface_alt_1_desc.bInterfaceNumber = status;
|
as_out_interface_alt_1_desc.bInterfaceNumber = status;
|
||||||
ac_header_desc.baInterfaceNr[0] = status;
|
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
|
||||||
uac1->as_out_intf = status;
|
uac1->as_out_intf = status;
|
||||||
uac1->as_out_alt = 0;
|
uac1->as_out_alt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EPIN_EN(audio_opts)) {
|
||||||
status = usb_interface_id(c, f);
|
status = usb_interface_id(c, f);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
as_in_interface_alt_0_desc.bInterfaceNumber = status;
|
as_in_interface_alt_0_desc.bInterfaceNumber = status;
|
||||||
as_in_interface_alt_1_desc.bInterfaceNumber = status;
|
as_in_interface_alt_1_desc.bInterfaceNumber = status;
|
||||||
ac_header_desc.baInterfaceNr[1] = status;
|
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
|
||||||
uac1->as_in_intf = status;
|
uac1->as_in_intf = status;
|
||||||
uac1->as_in_alt = 0;
|
uac1->as_in_alt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
audio->gadget = gadget;
|
audio->gadget = gadget;
|
||||||
|
|
||||||
status = -ENODEV;
|
status = -ENODEV;
|
||||||
|
|
||||||
/* allocate instance-specific endpoints */
|
/* allocate instance-specific endpoints */
|
||||||
|
if (EPOUT_EN(audio_opts)) {
|
||||||
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
|
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
|
||||||
if (!ep)
|
if (!ep)
|
||||||
goto fail;
|
goto fail;
|
||||||
audio->out_ep = ep;
|
audio->out_ep = ep;
|
||||||
audio->out_ep->desc = &as_out_ep_desc;
|
audio->out_ep->desc = &as_out_ep_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EPIN_EN(audio_opts)) {
|
||||||
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
|
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
|
||||||
if (!ep)
|
if (!ep)
|
||||||
goto fail;
|
goto fail;
|
||||||
audio->in_ep = ep;
|
audio->in_ep = ep;
|
||||||
audio->in_ep->desc = &as_in_ep_desc;
|
audio->in_ep->desc = &as_in_ep_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_descriptor(audio_opts);
|
||||||
|
|
||||||
/* copy descriptors, and track endpoint copies */
|
/* copy descriptors, and track endpoint copies */
|
||||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
||||||
@@ -667,6 +759,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
err_card_register:
|
err_card_register:
|
||||||
usb_free_all_descriptors(f);
|
usb_free_all_descriptors(f);
|
||||||
fail:
|
fail:
|
||||||
|
kfree(ac_header_desc);
|
||||||
|
ac_header_desc = NULL;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,6 +903,9 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
|
|||||||
g_audio_cleanup(audio);
|
g_audio_cleanup(audio);
|
||||||
usb_free_all_descriptors(f);
|
usb_free_all_descriptors(f);
|
||||||
|
|
||||||
|
kfree(ac_header_desc);
|
||||||
|
ac_header_desc = NULL;
|
||||||
|
|
||||||
audio->gadget = NULL;
|
audio->gadget = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user