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

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
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
...@@ -19,6 +19,7 @@ provided by gadgets. ...@@ -19,6 +19,7 @@ provided by gadgets.
16. UAC1 function 16. UAC1 function
17. UAC2 function 17. UAC2 function
18. UVC function 18. UVC function
19. PRINTER function
1. ACM function 1. ACM function
...@@ -726,3 +727,49 @@ with these patches: ...@@ -726,3 +727,49 @@ with these patches:
http://www.spinics.net/lists/linux-usb/msg99220.html http://www.spinics.net/lists/linux-usb/msg99220.html
host: luvcview -f yuv 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.
...@@ -437,6 +437,19 @@ config USB_CONFIGFS_F_UVC ...@@ -437,6 +437,19 @@ config USB_CONFIGFS_F_UVC
device. It provides a userspace API to process UVC control requests device. It provides a userspace API to process UVC control requests
and stream video data to the host. 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" source "drivers/usb/gadget/legacy/Kconfig"
endchoice endchoice
......
...@@ -1140,6 +1140,117 @@ static void printer_func_disable(struct usb_function *f) ...@@ -1140,6 +1140,117 @@ static void printer_func_disable(struct usb_function *f)
spin_unlock_irqrestore(&dev->lock, flags); 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) static inline int gprinter_get_minor(void)
{ {
return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
...@@ -1180,6 +1291,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void) ...@@ -1180,6 +1291,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
if (!opts) if (!opts)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = gprinter_free_inst; opts->func_inst.free_func_inst = gprinter_free_inst;
ret = &opts->func_inst; ret = &opts->func_inst;
...@@ -1201,6 +1313,8 @@ static struct usb_function_instance *gprinter_alloc_inst(void) ...@@ -1201,6 +1313,8 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
if (idr_is_empty(&printer_ida.idr)) if (idr_is_empty(&printer_ida.idr))
gprinter_cleanup(); gprinter_cleanup();
} }
config_group_init_type_name(&opts->func_inst.group, "",
&printer_func_type);
unlock: unlock:
mutex_unlock(&printer_ida_lock); mutex_unlock(&printer_ida_lock);
...@@ -1210,8 +1324,13 @@ static struct usb_function_instance *gprinter_alloc_inst(void) ...@@ -1210,8 +1324,13 @@ static struct usb_function_instance *gprinter_alloc_inst(void)
static void gprinter_free(struct usb_function *f) static void gprinter_free(struct usb_function *f)
{ {
struct printer_dev *dev = func_to_printer(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); kfree(dev);
mutex_lock(&opts->lock);
--opts->refcnt;
mutex_unlock(&opts->lock);
} }
static void printer_func_unbind(struct usb_configuration *c, static void printer_func_unbind(struct usb_configuration *c,
...@@ -1265,16 +1384,23 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) ...@@ -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); 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); return ERR_PTR(-ENOENT);
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) if (!dev) {
mutex_unlock(&opts->lock);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
++opts->refcnt;
dev->minor = opts->minor; dev->minor = opts->minor;
dev->pnp_string = opts->pnp_string; dev->pnp_string = opts->pnp_string;
dev->q_len = opts->q_len; dev->q_len = opts->q_len;
mutex_unlock(&opts->lock);
dev->function.name = "printer"; dev->function.name = "printer";
dev->function.bind = printer_func_bind; dev->function.bind = printer_func_bind;
......
...@@ -25,6 +25,13 @@ struct f_printer_opts { ...@@ -25,6 +25,13 @@ struct f_printer_opts {
int minor; int minor;
char pnp_string[PNP_STRING_LEN]; char pnp_string[PNP_STRING_LEN];
unsigned q_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 */ #endif /* U_PRINTER_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment