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

Commit ee1cd515 authored by Andrzej Pietrasiewicz's avatar Andrzej Pietrasiewicz Committed by Felipe Balbi
Browse files

usb: gadget: printer: add configfs support



Add support for configfs interface so that f_printer can be used as a
component of usb gadgets composed with it.

Signed-off-by: default avatarAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent a2a8e48a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
What:		/config/usb-gadget/gadget/functions/printer.name
Date:		Apr 2015
KernelVersion:	4.1
Description:
		The attributes:

		pnp_string	- Data to be passed to the host in pnp string
		q_len		- Number of requests per endpoint
+47 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ provided by gadgets.
16. UAC1 function
17. UAC2 function
18. UVC function
19. PRINTER function


1. ACM function
@@ -726,3 +727,49 @@ with these patches:
http://www.spinics.net/lists/linux-usb/msg99220.html

host: luvcview -f yuv

19. PRINTER function
====================

The function is provided by usb_f_printer.ko module.

Function-specific configfs interface
------------------------------------

The function name to use when creating the function directory is "printer".
The printer function provides these attributes in its function directory:

	pnp_string	- Data to be passed to the host in pnp string
	q_len		- Number of requests per endpoint

Testing the PRINTER function
----------------------------

The most basic testing:

device: run the gadget
# ls -l /devices/virtual/usb_printer_gadget/

should show g_printer<number>.

If udev is active, then /dev/g_printer<number> should appear automatically.

host:

If udev is active, then e.g. /dev/usb/lp0 should appear.

host->device transmission:

device:
# cat /dev/g_printer<number>
host:
# cat > /dev/usb/lp0

device->host transmission:

# cat > /dev/g_printer<number>
host:
# cat /dev/usb/lp0

More advanced testing can be done with the prn_example
described in Documentation/usb/gadget-printer.txt.
+13 −0
Original line number Diff line number Diff line
@@ -437,6 +437,19 @@ config USB_CONFIGFS_F_UVC
	  device. It provides a userspace API to process UVC control requests
	  and stream video data to the host.

config USB_CONFIGFS_F_PRINTER
	bool "Printer function"
	select USB_F_PRINTER
	help
	  The Printer function channels data between the USB host and a
	  userspace program driving the print engine. The user space
	  program reads and writes the device file /dev/g_printer<X> to
	  receive or send printer data. It can use ioctl calls to
	  the device file to get or set printer status.

	  For more information, see Documentation/usb/gadget_printer.txt
	  which includes sample code for accessing the device file.

source "drivers/usb/gadget/legacy/Kconfig"

endchoice
+128 −2
Original line number Diff line number Diff line
@@ -1140,6 +1140,117 @@ static void printer_func_disable(struct usb_function *f)
	spin_unlock_irqrestore(&dev->lock, flags);
}

static inline struct f_printer_opts
*to_f_printer_opts(struct config_item *item)
{
	return container_of(to_config_group(item), struct f_printer_opts,
			    func_inst.group);
}

CONFIGFS_ATTR_STRUCT(f_printer_opts);
CONFIGFS_ATTR_OPS(f_printer_opts);

static void printer_attr_release(struct config_item *item)
{
	struct f_printer_opts *opts = to_f_printer_opts(item);

	usb_put_function_instance(&opts->func_inst);
}

static struct configfs_item_operations printer_item_ops = {
	.release	= printer_attr_release,
	.show_attribute	= f_printer_opts_attr_show,
	.store_attribute = f_printer_opts_attr_store,
};

static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts,
					      char *page)
{
	int result;

	mutex_lock(&opts->lock);
	result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2);
	mutex_unlock(&opts->lock);

	return result;
}

static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts,
					       const char *page, size_t len)
{
	int result, l;

	mutex_lock(&opts->lock);
	result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2);
	l = strlen(opts->pnp_string + 2) + 2;
	opts->pnp_string[0] = (l >> 8) & 0xFF;
	opts->pnp_string[1] = l & 0xFF;
	mutex_unlock(&opts->lock);

	return result;
}

static struct f_printer_opts_attribute f_printer_opts_pnp_string =
	__CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR,
			f_printer_opts_pnp_string_show,
			f_printer_opts_pnp_string_store);

static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts,
					 char *page)
{
	int result;

	mutex_lock(&opts->lock);
	result = sprintf(page, "%d\n", opts->q_len);
	mutex_unlock(&opts->lock);

	return result;
}

static ssize_t f_printer_opts_q_len_store(struct f_printer_opts *opts,
					  const char *page, size_t len)
{
	int ret;
	u16 num;

	mutex_lock(&opts->lock);
	if (opts->refcnt) {
		ret = -EBUSY;
		goto end;
	}

	ret = kstrtou16(page, 0, &num);
	if (ret)
		goto end;

	if (num > 65535) {
		ret = -EINVAL;
		goto end;
	}

	opts->q_len = (unsigned)num;
	ret = len;
end:
	mutex_unlock(&opts->lock);
	return ret;
}

static struct f_printer_opts_attribute f_printer_opts_q_len =
	__CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show,
			f_printer_opts_q_len_store);

static struct configfs_attribute *printer_attrs[] = {
	&f_printer_opts_pnp_string.attr,
	&f_printer_opts_q_len.attr,
	NULL,
};

static struct config_item_type printer_func_type = {
	.ct_item_ops	= &printer_item_ops,
	.ct_attrs	= printer_attrs,
	.ct_owner	= THIS_MODULE,
};

static inline int gprinter_get_minor(void)
{
	return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
@@ -1180,6 +1291,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
	if (!opts)
		return ERR_PTR(-ENOMEM);

	mutex_init(&opts->lock);
	opts->func_inst.free_func_inst = gprinter_free_inst;
	ret = &opts->func_inst;

@@ -1201,6 +1313,8 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
		if (idr_is_empty(&printer_ida.idr))
			gprinter_cleanup();
	}
	config_group_init_type_name(&opts->func_inst.group, "",
				    &printer_func_type);

unlock:
	mutex_unlock(&printer_ida_lock);
@@ -1210,8 +1324,13 @@ unlock:
static void gprinter_free(struct usb_function *f)
{
	struct printer_dev *dev = func_to_printer(f);
	struct f_printer_opts *opts;

	opts = container_of(f->fi, struct f_printer_opts, func_inst);
	kfree(dev);
	mutex_lock(&opts->lock);
	--opts->refcnt;
	mutex_unlock(&opts->lock);
}

static void printer_func_unbind(struct usb_configuration *c,
@@ -1265,16 +1384,23 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)

	opts = container_of(fi, struct f_printer_opts, func_inst);

	if (opts->minor >= minors)
	mutex_lock(&opts->lock);
	if (opts->minor >= minors) {
		mutex_unlock(&opts->lock);
		return ERR_PTR(-ENOENT);
	}

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev)
	if (!dev) {
		mutex_unlock(&opts->lock);
		return ERR_PTR(-ENOMEM);
	}

	++opts->refcnt;
	dev->minor = opts->minor;
	dev->pnp_string = opts->pnp_string;
	dev->q_len = opts->q_len;
	mutex_unlock(&opts->lock);

	dev->function.name = "printer";
	dev->function.bind = printer_func_bind;
+7 −0
Original line number Diff line number Diff line
@@ -25,6 +25,13 @@ struct f_printer_opts {
	int				minor;
	char				pnp_string[PNP_STRING_LEN];
	unsigned			q_len;

	/*
	 * Protect the data from concurrent access by read/write
	 * and create symlink/remove symlink
	 */
	struct mutex			lock;
	int				refcnt;
};

#endif /* U_PRINTER_H */