@@ -498,35 +498,6 @@ static struct usb_descriptor_header *f_audio_ss_desc[] = {
NULL,
};
-enum {
- STR_AC_IF,
- STR_USB_OUT_IT,
- STR_USB_OUT_IT_CH_NAMES,
- STR_IO_OUT_OT,
- STR_IO_IN_IT,
- STR_IO_IN_IT_CH_NAMES,
- STR_USB_IN_OT,
- STR_FU_IN,
- STR_FU_OUT,
- STR_AS_OUT_IF_ALT0,
- STR_AS_OUT_IF_ALT1,
- STR_AS_IN_IF_ALT0,
- STR_AS_IN_IF_ALT1,
- NUM_STR_DESCRIPTORS,
-};
-
-static struct usb_string strings_uac1[NUM_STR_DESCRIPTORS + 1] = {};
-
-static struct usb_gadget_strings str_uac1 = {
- .language = 0x0409, /* en-us */
- .strings = strings_uac1,
-};
-
-static struct usb_gadget_strings *uac1_strings[] = {
- &str_uac1,
- NULL,
-};
-
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
@@ -1163,6 +1134,93 @@ f_audio_suspend(struct usb_function *f)
/*-------------------------------------------------------------------------*/
+/*
+ * String handling
+ */
+
+#define MAX_STRINGS 256
+
+static int add_string(struct usb_string *strings, const char *s)
+{
+ int i;
+
+ if (!s || s[0] == '\0')
+ return 0;
+
+ for (i = 0; i < MAX_STRINGS; i++) {
+ if (!strings[i].s) {
+ strings[i].s = s;
+ return 0; /* IDs aren't allocated yet */
+ }
+
+ if (!strcmp(s, strings[i].s))
+ return strings[i].id;
+ }
+
+ return -1;
+}
+
+static void add_alt_strings(struct usb_string *strings, struct f_uac1_alt_opts *alt_opts, bool fu)
+{
+ add_string(strings, alt_opts->name);
+ add_string(strings, alt_opts->it_name);
+ add_string(strings, alt_opts->it_ch_name);
+ add_string(strings, alt_opts->ot_name);
+ if (fu)
+ add_string(strings, alt_opts->fu_vol_name);
+}
+
+static struct usb_string *attach_strings(struct usb_composite_dev *cdev,
+ struct f_uac1_opts *audio_opts)
+{
+ struct usb_string *strings = kzalloc(sizeof(struct usb_string) * MAX_STRINGS,
+ GFP_KERNEL);
+ struct f_uac1_alt_opts *alt_opts;
+ struct usb_string *us;
+ int strings_uac1_length;
+
+ struct usb_gadget_strings str_uac1 = {
+ .language = 0x0409, /* en-us */
+ .strings = strings
+ };
+
+ struct usb_gadget_strings *uac1_strings[] = {
+ &str_uac1,
+ NULL,
+ };
+
+ if (!strings)
+ return ERR_PTR(-ENOMEM);
+
+ /* Add all the strings from all the alt mode options */
+ add_string(strings, audio_opts->function_name);
+ add_string(strings, audio_opts->c_alt_0_opts.name);
+ add_string(strings, audio_opts->p_alt_0_opts.name);
+ add_alt_strings(strings, &audio_opts->c_alt_1_opts, FUOUT_EN(audio_opts));
+ add_alt_strings(strings, &audio_opts->p_alt_1_opts, FUIN_EN(audio_opts));
+ list_for_each_entry(alt_opts, &audio_opts->c_alt_opts, list) {
+ add_alt_strings(strings, alt_opts, FUOUT_EN(audio_opts));
+ }
+ list_for_each_entry(alt_opts, &audio_opts->p_alt_opts, list) {
+ add_alt_strings(strings, alt_opts, FUIN_EN(audio_opts));
+ }
+
+ for (strings_uac1_length = 0; strings[strings_uac1_length].s; strings_uac1_length++)
+ ;
+
+ /* Attach strings to the composite device and get string IDs assigned */
+ us = usb_gstrings_attach(cdev, uac1_strings, strings_uac1_length);
+
+ /* Strings are now copied to the composite device and we use the
+ * copy in "us" going forward, that has all the string IDs.
+ */
+ kfree(strings);
+
+ return us;
+}
+
+/*-------------------------------------------------------------------------*/
+
static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
@@ -1526,23 +1584,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
return status;
- strings_uac1[STR_AC_IF].s = audio_opts->function_name;
-
- strings_uac1[STR_USB_OUT_IT].s = audio_opts->c_it_name;
- strings_uac1[STR_USB_OUT_IT_CH_NAMES].s = audio_opts->c_it_ch_name;
- strings_uac1[STR_IO_OUT_OT].s = audio_opts->c_ot_name;
- strings_uac1[STR_FU_OUT].s = audio_opts->c_fu_vol_name;
- strings_uac1[STR_AS_OUT_IF_ALT0].s = "Playback Inactive";
- strings_uac1[STR_AS_OUT_IF_ALT1].s = "Playback Active";
-
- strings_uac1[STR_IO_IN_IT].s = audio_opts->p_it_name;
- strings_uac1[STR_IO_IN_IT_CH_NAMES].s = audio_opts->p_it_ch_name;
- strings_uac1[STR_USB_IN_OT].s = audio_opts->p_ot_name;
- strings_uac1[STR_FU_IN].s = audio_opts->p_fu_vol_name;
- strings_uac1[STR_AS_IN_IF_ALT0].s = "Capture Inactive";
- strings_uac1[STR_AS_IN_IF_ALT1].s = "Capture Active";
+ /* Past this point, all settings that apply to an alt mode should
+ * be used from their alt mode opts.
+ */
- us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
+ us = attach_strings(cdev, audio_opts);
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1565,31 +1611,37 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
}
- ac_interface_desc.iInterface = us[STR_AC_IF].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;
- io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id;
- as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id;
- as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id;
- io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id;
- io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id;
- usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id;
- as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
- as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
+ ac_interface_desc.iInterface = add_string(us, audio_opts->function_name);
+ usb_out_it_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.it_name);
+ usb_out_it_desc.iChannelNames = add_string(us, audio_opts->c_alt_1_opts.it_ch_name);
+ io_out_ot_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.ot_name);
+ as_out_interface_alt_0_desc.iInterface = add_string(us, audio_opts->c_alt_0_opts.name);
+ as_out_interface_alt_1_desc.iInterface = add_string(us, audio_opts->c_alt_1_opts.name);
+ io_in_it_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.it_name);
+ io_in_it_desc.iChannelNames = add_string(us, audio_opts->p_alt_1_opts.it_ch_name);
+ usb_in_ot_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.ot_name);
+ as_in_interface_alt_0_desc.iInterface = add_string(us, audio_opts->p_alt_0_opts.name);
+ as_in_interface_alt_1_desc.iInterface = add_string(us, audio_opts->p_alt_1_opts.name);
if (FUOUT_EN(audio_opts)) {
u8 *i_feature;
i_feature = (u8 *)out_feature_unit_desc +
out_feature_unit_desc->bLength - 1;
- *i_feature = us[STR_FU_OUT].id;
+ *i_feature = add_string(us, audio_opts->c_alt_1_opts.fu_vol_name);
}
if (FUIN_EN(audio_opts)) {
u8 *i_feature;
i_feature = (u8 *)in_feature_unit_desc +
in_feature_unit_desc->bLength - 1;
- *i_feature = us[STR_FU_IN].id;
+ *i_feature = add_string(us, audio_opts->p_alt_1_opts.fu_vol_name);
+ }
+
+ us = attach_strings(cdev, audio_opts);
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto fail;
}
/* Set channel numbers */