Browse Source

usb: gadget: f_midi: add configfs support

Make the midi function available for gadgets composed with configfs.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Andrzej Pietrasiewicz 11 years ago
parent
commit
6f1de34455

+ 12 - 0
Documentation/ABI/testing/configfs-usb-gadget-midi

@@ -0,0 +1,12 @@
+What:		/config/usb-gadget/gadget/functions/midi.name
+Date:		Nov 2014
+KernelVersion:	3.19
+Description:
+		The attributes:
+
+		index		- index value for the USB MIDI adapter
+		id		- ID string for the USB MIDI adapter
+		buflen		- MIDI buffer length
+		qlen		- USB read request queue length
+		in_ports	- number of MIDI input ports
+		out_ports	- number of MIDI output ports

+ 14 - 0
drivers/usb/gadget/Kconfig

@@ -396,6 +396,20 @@ config USB_CONFIGFS_F_UAC2
 	  received from the USB Host and choose to provide whatever it
 	  received from the USB Host and choose to provide whatever it
 	  wants as audio data to the USB Host.
 	  wants as audio data to the USB Host.
 
 
+config USB_CONFIGFS_F_MIDI
+	boolean "MIDI function"
+	depends on USB_CONFIGFS
+	depends on SND
+	select USB_LIBCOMPOSITE
+	select SND_RAWMIDI
+	select USB_F_MIDI
+	help
+	  The MIDI Function acts as a USB Audio device, with one MIDI
+	  input and one MIDI output. These MIDI jacks appear as
+	  a sound "card" in the ALSA sound system. Other MIDI
+	  connections can then be made on the gadget system, using
+	  ALSA's aconnect utility etc.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 source "drivers/usb/gadget/legacy/Kconfig"
 
 
 endchoice
 endchoice

+ 159 - 2
drivers/usb/gadget/function/f_midi.c

@@ -896,12 +896,145 @@ fail_register:
 	return status;
 	return status;
 }
 }
 
 
+static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_midi_opts,
+			    func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_midi_opts);
+CONFIGFS_ATTR_OPS(f_midi_opts);
+
+static void midi_attr_release(struct config_item *item)
+{
+	struct f_midi_opts *opts = to_f_midi_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations midi_item_ops = {
+	.release	= midi_attr_release,
+	.show_attribute	= f_midi_opts_attr_show,
+	.store_attribute = f_midi_opts_attr_store,
+};
+
+#define F_MIDI_OPT(name, test_limit, limit)				\
+static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \
+{									\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", opts->name);			\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts,	\
+					 const char *page, size_t len)	\
+{									\
+	int ret;							\
+	u32 num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou32(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (test_limit && num > limit) {				\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	opts->name = num;						\
+	ret = len;							\
+									\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+static struct f_midi_opts_attribute f_midi_opts_##name =		\
+	__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \
+			f_midi_opts_##name##_store)
+
+F_MIDI_OPT(index, true, SNDRV_CARDS);
+F_MIDI_OPT(buflen, false, 0);
+F_MIDI_OPT(qlen, false, 0);
+F_MIDI_OPT(in_ports, true, MAX_PORTS);
+F_MIDI_OPT(out_ports, true, MAX_PORTS);
+
+static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = strlcpy(page, opts->id, PAGE_SIZE);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts,
+				    const char *page, size_t len)
+{
+	int ret;
+	char *c;
+
+	mutex_lock(&opts->lock);
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	c = kstrndup(page, len, GFP_KERNEL);
+	if (!c) {
+		ret = -ENOMEM;
+		goto end;
+	}
+	if (opts->id_allocated)
+		kfree(opts->id);
+	opts->id = c;
+	opts->id_allocated = true;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_midi_opts_attribute f_midi_opts_id =
+	__CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show,
+			f_midi_opts_id_store);
+
+static struct configfs_attribute *midi_attrs[] = {
+	&f_midi_opts_index.attr,
+	&f_midi_opts_buflen.attr,
+	&f_midi_opts_qlen.attr,
+	&f_midi_opts_in_ports.attr,
+	&f_midi_opts_out_ports.attr,
+	&f_midi_opts_id.attr,
+	NULL,
+};
+
+static struct config_item_type midi_func_type = {
+	.ct_item_ops	= &midi_item_ops,
+	.ct_attrs	= midi_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
 static void f_midi_free_inst(struct usb_function_instance *f)
 static void f_midi_free_inst(struct usb_function_instance *f)
 {
 {
 	struct f_midi_opts *opts;
 	struct f_midi_opts *opts;
 
 
 	opts = container_of(f, struct f_midi_opts, func_inst);
 	opts = container_of(f, struct f_midi_opts, func_inst);
 
 
+	if (opts->id_allocated)
+		kfree(opts->id);
+
 	kfree(opts);
 	kfree(opts);
 }
 }
 
 
@@ -912,7 +1045,18 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
 	if (!opts)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
 	opts->func_inst.free_func_inst = f_midi_free_inst;
 	opts->func_inst.free_func_inst = f_midi_free_inst;
+	opts->index = SNDRV_DEFAULT_IDX1;
+	opts->id = SNDRV_DEFAULT_STR1;
+	opts->buflen = 256;
+	opts->qlen = 32;
+	opts->in_ports = 1;
+	opts->out_ports = 1;
+
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &midi_func_type);
 
 
 	return &opts->func_inst;
 	return &opts->func_inst;
 }
 }
@@ -926,9 +1070,12 @@ static void f_midi_free(struct usb_function *f)
 	midi = func_to_midi(f);
 	midi = func_to_midi(f);
 	opts = container_of(f->fi, struct f_midi_opts, func_inst);
 	opts = container_of(f->fi, struct f_midi_opts, func_inst);
 	kfree(midi->id);
 	kfree(midi->id);
+	mutex_lock(&opts->lock);
 	for (i = opts->in_ports - 1; i >= 0; --i)
 	for (i = opts->in_ports - 1; i >= 0; --i)
 		kfree(midi->in_port[i]);
 		kfree(midi->in_port[i]);
 	kfree(midi);
 	kfree(midi);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
 }
 }
 
 
 static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
 static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -957,20 +1104,27 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
 	int status, i;
 	int status, i;
 
 
 	opts = container_of(fi, struct f_midi_opts, func_inst);
 	opts = container_of(fi, struct f_midi_opts, func_inst);
+
+	mutex_lock(&opts->lock);
 	/* sanity check */
 	/* sanity check */
-	if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS)
+	if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
+		mutex_unlock(&opts->lock);
 		return ERR_PTR(-EINVAL);
 		return ERR_PTR(-EINVAL);
+	}
 
 
 	/* allocate and initialize one new instance */
 	/* allocate and initialize one new instance */
 	midi = kzalloc(sizeof(*midi), GFP_KERNEL);
 	midi = kzalloc(sizeof(*midi), GFP_KERNEL);
-	if (!midi)
+	if (!midi) {
+		mutex_unlock(&opts->lock);
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
+	}
 
 
 	for (i = 0; i < opts->in_ports; i++) {
 	for (i = 0; i < opts->in_ports; i++) {
 		struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
 		struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
 
 
 		if (!port) {
 		if (!port) {
 			status = -ENOMEM;
 			status = -ENOMEM;
+			mutex_unlock(&opts->lock);
 			goto setup_fail;
 			goto setup_fail;
 		}
 		}
 
 
@@ -984,6 +1138,7 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
 	midi->id = kstrdup(opts->id, GFP_KERNEL);
 	midi->id = kstrdup(opts->id, GFP_KERNEL);
 	if (opts->id && !midi->id) {
 	if (opts->id && !midi->id) {
 		status = -ENOMEM;
 		status = -ENOMEM;
+		mutex_unlock(&opts->lock);
 		goto kstrdup_fail;
 		goto kstrdup_fail;
 	}
 	}
 	midi->in_ports = opts->in_ports;
 	midi->in_ports = opts->in_ports;
@@ -991,6 +1146,8 @@ struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
 	midi->index = opts->index;
 	midi->index = opts->index;
 	midi->buflen = opts->buflen;
 	midi->buflen = opts->buflen;
 	midi->qlen = opts->qlen;
 	midi->qlen = opts->qlen;
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
 
 
 	midi->func.name		= "gmidi function";
 	midi->func.name		= "gmidi function";
 	midi->func.bind		= f_midi_bind;
 	midi->func.bind		= f_midi_bind;

+ 8 - 0
drivers/usb/gadget/function/u_midi.h

@@ -22,10 +22,18 @@ struct f_midi_opts {
 	struct usb_function_instance	func_inst;
 	struct usb_function_instance	func_inst;
 	int				index;
 	int				index;
 	char				*id;
 	char				*id;
+	bool				id_allocated;
 	unsigned int			in_ports;
 	unsigned int			in_ports;
 	unsigned int			out_ports;
 	unsigned int			out_ports;
 	unsigned int			buflen;
 	unsigned int			buflen;
 	unsigned int			qlen;
 	unsigned int			qlen;
+
+	/*
+	 * Protect the data form concurrent access by read/write
+	 * and create symlink/remove symlink.
+	 */
+	 struct mutex			lock;
+	 int				refcnt;
 };
 };
 
 
 #endif /* U_MIDI_H */
 #endif /* U_MIDI_H */