diff options
-rw-r--r-- | Documentation/ABI/testing/configfs-usb-gadget-printer | 9 | ||||
-rw-r--r-- | Documentation/usb/gadget-testing.txt | 47 | ||||
-rw-r--r-- | drivers/usb/gadget/Kconfig | 13 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_printer.c | 130 | ||||
-rw-r--r-- | drivers/usb/gadget/function/u_printer.h | 7 |
5 files changed, 204 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-printer b/Documentation/ABI/testing/configfs-usb-gadget-printer new file mode 100644 index 000000000000..6b0714e3c605 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-printer | |||
@@ -0,0 +1,9 @@ | |||
1 | What: /config/usb-gadget/gadget/functions/printer.name | ||
2 | Date: Apr 2015 | ||
3 | KernelVersion: 4.1 | ||
4 | Description: | ||
5 | The attributes: | ||
6 | |||
7 | pnp_string - Data to be passed to the host in pnp string | ||
8 | q_len - Number of requests per endpoint | ||
9 | |||
diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index 076ac7ba7f93..f45b2bf4b41d 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt | |||
@@ -19,6 +19,7 @@ provided by gadgets. | |||
19 | 16. UAC1 function | 19 | 16. UAC1 function |
20 | 17. UAC2 function | 20 | 17. UAC2 function |
21 | 18. UVC function | 21 | 18. UVC function |
22 | 19. PRINTER function | ||
22 | 23 | ||
23 | 24 | ||
24 | 1. ACM function | 25 | 1. ACM function |
@@ -726,3 +727,49 @@ with these patches: | |||
726 | http://www.spinics.net/lists/linux-usb/msg99220.html | 727 | http://www.spinics.net/lists/linux-usb/msg99220.html |
727 | 728 | ||
728 | host: luvcview -f yuv | 729 | host: luvcview -f yuv |
730 | |||
731 | 19. PRINTER function | ||
732 | ==================== | ||
733 | |||
734 | The function is provided by usb_f_printer.ko module. | ||
735 | |||
736 | Function-specific configfs interface | ||
737 | ------------------------------------ | ||
738 | |||
739 | The function name to use when creating the function directory is "printer". | ||
740 | The printer function provides these attributes in its function directory: | ||
741 | |||
742 | pnp_string - Data to be passed to the host in pnp string | ||
743 | q_len - Number of requests per endpoint | ||
744 | |||
745 | Testing the PRINTER function | ||
746 | ---------------------------- | ||
747 | |||
748 | The most basic testing: | ||
749 | |||
750 | device: run the gadget | ||
751 | # ls -l /devices/virtual/usb_printer_gadget/ | ||
752 | |||
753 | should show g_printer<number>. | ||
754 | |||
755 | If udev is active, then /dev/g_printer<number> should appear automatically. | ||
756 | |||
757 | host: | ||
758 | |||
759 | If udev is active, then e.g. /dev/usb/lp0 should appear. | ||
760 | |||
761 | host->device transmission: | ||
762 | |||
763 | device: | ||
764 | # cat /dev/g_printer<number> | ||
765 | host: | ||
766 | # cat > /dev/usb/lp0 | ||
767 | |||
768 | device->host transmission: | ||
769 | |||
770 | # cat > /dev/g_printer<number> | ||
771 | host: | ||
772 | # cat /dev/usb/lp0 | ||
773 | |||
774 | More advanced testing can be done with the prn_example | ||
775 | described in Documentation/usb/gadget-printer.txt. | ||
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 9d507cf98f94..3bb0e67fded2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
@@ -437,6 +437,19 @@ config USB_CONFIGFS_F_UVC | |||
437 | device. It provides a userspace API to process UVC control requests | 437 | device. It provides a userspace API to process UVC control requests |
438 | and stream video data to the host. | 438 | and stream video data to the host. |
439 | 439 | ||
440 | config USB_CONFIGFS_F_PRINTER | ||
441 | bool "Printer function" | ||
442 | select USB_F_PRINTER | ||
443 | help | ||
444 | The Printer function channels data between the USB host and a | ||
445 | userspace program driving the print engine. The user space | ||
446 | program reads and writes the device file /dev/g_printer<X> to | ||
447 | receive or send printer data. It can use ioctl calls to | ||
448 | the device file to get or set printer status. | ||
449 | |||
450 | For more information, see Documentation/usb/gadget_printer.txt | ||
451 | which includes sample code for accessing the device file. | ||
452 | |||
440 | source "drivers/usb/gadget/legacy/Kconfig" | 453 | source "drivers/usb/gadget/legacy/Kconfig" |
441 | 454 | ||
442 | endchoice | 455 | endchoice |
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 7afe17d76f17..757fcf070013 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c | |||
@@ -1140,6 +1140,117 @@ static void printer_func_disable(struct usb_function *f) | |||
1140 | spin_unlock_irqrestore(&dev->lock, flags); | 1140 | spin_unlock_irqrestore(&dev->lock, flags); |
1141 | } | 1141 | } |
1142 | 1142 | ||
1143 | static inline struct f_printer_opts | ||
1144 | *to_f_printer_opts(struct config_item *item) | ||
1145 | { | ||
1146 | return container_of(to_config_group(item), struct f_printer_opts, | ||
1147 | func_inst.group); | ||
1148 | } | ||
1149 | |||
1150 | CONFIGFS_ATTR_STRUCT(f_printer_opts); | ||
1151 | CONFIGFS_ATTR_OPS(f_printer_opts); | ||
1152 | |||
1153 | static void printer_attr_release(struct config_item *item) | ||
1154 | { | ||
1155 | struct f_printer_opts *opts = to_f_printer_opts(item); | ||
1156 | |||
1157 | usb_put_function_instance(&opts->func_inst); | ||
1158 | } | ||
1159 | |||
1160 | static struct configfs_item_operations printer_item_ops = { | ||
1161 | .release = printer_attr_release, | ||
1162 | .show_attribute = f_printer_opts_attr_show, | ||
1163 | .store_attribute = f_printer_opts_attr_store, | ||
1164 | }; | ||
1165 | |||
1166 | static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts, | ||
1167 | char *page) | ||
1168 | { | ||
1169 | int result; | ||
1170 | |||
1171 | mutex_lock(&opts->lock); | ||
1172 | result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2); | ||
1173 | mutex_unlock(&opts->lock); | ||
1174 | |||
1175 | return result; | ||
1176 | } | ||
1177 | |||
1178 | static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts, | ||
1179 | const char *page, size_t len) | ||
1180 | { | ||
1181 | int result, l; | ||
1182 | |||
1183 | mutex_lock(&opts->lock); | ||
1184 | result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2); | ||
1185 | l = strlen(opts->pnp_string + 2) + 2; | ||
1186 | opts->pnp_string[0] = (l >> 8) & 0xFF; | ||
1187 | opts->pnp_string[1] = l & 0xFF; | ||
1188 | mutex_unlock(&opts->lock); | ||
1189 | |||
1190 | return result; | ||
1191 | } | ||
1192 | |||
1193 | static struct f_printer_opts_attribute f_printer_opts_pnp_string = | ||
1194 | __CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR, | ||
1195 | f_printer_opts_pnp_string_show, | ||
1196 | f_printer_opts_pnp_string_store); | ||
1197 | |||
1198 | static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts, | ||
1199 | char *page) | ||
1200 | { | ||
1201 | int result; | ||
1202 | |||
1203 | mutex_lock(&opts->lock); | ||
1204 | result = sprintf(page, "%d\n", opts->q_len); | ||
1205 | mutex_unlock(&opts->lock); | ||
1206 | |||
1207 | return result; | ||
1208 | } | ||
1209 | |||
1210 | static ssize_t f_printer_opts_q_len_store(struct f_printer_opts *opts, | ||
1211 | const char *page, size_t len) | ||
1212 | { | ||
1213 | int ret; | ||
1214 | u16 num; | ||
1215 | |||
1216 | mutex_lock(&opts->lock); | ||
1217 | if (opts->refcnt) { | ||
1218 | ret = -EBUSY; | ||
1219 | goto end; | ||
1220 | } | ||
1221 | |||
1222 | ret = kstrtou16(page, 0, &num); | ||
1223 | if (ret) | ||
1224 | goto end; | ||
1225 | |||
1226 | if (num > 65535) { | ||
1227 | ret = -EINVAL; | ||
1228 | goto end; | ||
1229 | } | ||
1230 | |||
1231 | opts->q_len = (unsigned)num; | ||
1232 | ret = len; | ||
1233 | end: | ||
1234 | mutex_unlock(&opts->lock); | ||
1235 | return ret; | ||
1236 | } | ||
1237 | |||
1238 | static struct f_printer_opts_attribute f_printer_opts_q_len = | ||
1239 | __CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show, | ||
1240 | f_printer_opts_q_len_store); | ||
1241 | |||
1242 | static struct configfs_attribute *printer_attrs[] = { | ||
1243 | &f_printer_opts_pnp_string.attr, | ||
1244 | &f_printer_opts_q_len.attr, | ||
1245 | NULL, | ||
1246 | }; | ||
1247 | |||
1248 | static struct config_item_type printer_func_type = { | ||
1249 | .ct_item_ops = &printer_item_ops, | ||
1250 | .ct_attrs = printer_attrs, | ||
1251 | .ct_owner = THIS_MODULE, | ||
1252 | }; | ||
1253 | |||
1143 | static inline int gprinter_get_minor(void) | 1254 | static inline int gprinter_get_minor(void) |
1144 | { | 1255 | { |
1145 | return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); | 1256 | return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL); |
@@ -1180,6 +1291,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void) | |||
1180 | if (!opts) | 1291 | if (!opts) |
1181 | return ERR_PTR(-ENOMEM); | 1292 | return ERR_PTR(-ENOMEM); |
1182 | 1293 | ||
1294 | mutex_init(&opts->lock); | ||
1183 | opts->func_inst.free_func_inst = gprinter_free_inst; | 1295 | opts->func_inst.free_func_inst = gprinter_free_inst; |
1184 | ret = &opts->func_inst; | 1296 | ret = &opts->func_inst; |
1185 | 1297 | ||
@@ -1201,6 +1313,8 @@ static struct usb_function_instance *gprinter_alloc_inst(void) | |||
1201 | if (idr_is_empty(&printer_ida.idr)) | 1313 | if (idr_is_empty(&printer_ida.idr)) |
1202 | gprinter_cleanup(); | 1314 | gprinter_cleanup(); |
1203 | } | 1315 | } |
1316 | config_group_init_type_name(&opts->func_inst.group, "", | ||
1317 | &printer_func_type); | ||
1204 | 1318 | ||
1205 | unlock: | 1319 | unlock: |
1206 | mutex_unlock(&printer_ida_lock); | 1320 | mutex_unlock(&printer_ida_lock); |
@@ -1210,8 +1324,13 @@ unlock: | |||
1210 | static void gprinter_free(struct usb_function *f) | 1324 | static void gprinter_free(struct usb_function *f) |
1211 | { | 1325 | { |
1212 | struct printer_dev *dev = func_to_printer(f); | 1326 | struct printer_dev *dev = func_to_printer(f); |
1327 | struct f_printer_opts *opts; | ||
1213 | 1328 | ||
1329 | opts = container_of(f->fi, struct f_printer_opts, func_inst); | ||
1214 | kfree(dev); | 1330 | kfree(dev); |
1331 | mutex_lock(&opts->lock); | ||
1332 | --opts->refcnt; | ||
1333 | mutex_unlock(&opts->lock); | ||
1215 | } | 1334 | } |
1216 | 1335 | ||
1217 | static void printer_func_unbind(struct usb_configuration *c, | 1336 | 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 | 1384 | ||
1266 | opts = container_of(fi, struct f_printer_opts, func_inst); | 1385 | opts = container_of(fi, struct f_printer_opts, func_inst); |
1267 | 1386 | ||
1268 | if (opts->minor >= minors) | 1387 | mutex_lock(&opts->lock); |
1388 | if (opts->minor >= minors) { | ||
1389 | mutex_unlock(&opts->lock); | ||
1269 | return ERR_PTR(-ENOENT); | 1390 | return ERR_PTR(-ENOENT); |
1391 | } | ||
1270 | 1392 | ||
1271 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | 1393 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
1272 | if (!dev) | 1394 | if (!dev) { |
1395 | mutex_unlock(&opts->lock); | ||
1273 | return ERR_PTR(-ENOMEM); | 1396 | return ERR_PTR(-ENOMEM); |
1397 | } | ||
1274 | 1398 | ||
1399 | ++opts->refcnt; | ||
1275 | dev->minor = opts->minor; | 1400 | dev->minor = opts->minor; |
1276 | dev->pnp_string = opts->pnp_string; | 1401 | dev->pnp_string = opts->pnp_string; |
1277 | dev->q_len = opts->q_len; | 1402 | dev->q_len = opts->q_len; |
1403 | mutex_unlock(&opts->lock); | ||
1278 | 1404 | ||
1279 | dev->function.name = "printer"; | 1405 | dev->function.name = "printer"; |
1280 | dev->function.bind = printer_func_bind; | 1406 | dev->function.bind = printer_func_bind; |
diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h index b2338cacdfe4..0e2c49d4274e 100644 --- a/drivers/usb/gadget/function/u_printer.h +++ b/drivers/usb/gadget/function/u_printer.h | |||
@@ -25,6 +25,13 @@ struct f_printer_opts { | |||
25 | int minor; | 25 | int minor; |
26 | char pnp_string[PNP_STRING_LEN]; | 26 | char pnp_string[PNP_STRING_LEN]; |
27 | unsigned q_len; | 27 | unsigned q_len; |
28 | |||
29 | /* | ||
30 | * Protect the data from concurrent access by read/write | ||
31 | * and create symlink/remove symlink | ||
32 | */ | ||
33 | struct mutex lock; | ||
34 | int refcnt; | ||
28 | }; | 35 | }; |
29 | 36 | ||
30 | #endif /* U_PRINTER_H */ | 37 | #endif /* U_PRINTER_H */ |