15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+ 28443f2d2SAndrzej Pietrasiewicz /* 38443f2d2SAndrzej Pietrasiewicz * mass_storage.c -- Mass Storage USB Gadget 48443f2d2SAndrzej Pietrasiewicz * 58443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2003-2008 Alan Stern 68443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2009 Samsung Electronics 78443f2d2SAndrzej Pietrasiewicz * Author: Michal Nazarewicz <mina86@mina86.com> 88443f2d2SAndrzej Pietrasiewicz * All rights reserved. 98443f2d2SAndrzej Pietrasiewicz * 108443f2d2SAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 118443f2d2SAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 128443f2d2SAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 138443f2d2SAndrzej Pietrasiewicz * (at your option) any later version. 148443f2d2SAndrzej Pietrasiewicz */ 158443f2d2SAndrzej Pietrasiewicz 168443f2d2SAndrzej Pietrasiewicz 178443f2d2SAndrzej Pietrasiewicz /* 188443f2d2SAndrzej Pietrasiewicz * The Mass Storage Gadget acts as a USB Mass Storage device, 198443f2d2SAndrzej Pietrasiewicz * appearing to the host as a disk drive or as a CD-ROM drive. In 208443f2d2SAndrzej Pietrasiewicz * addition to providing an example of a genuinely useful gadget 218443f2d2SAndrzej Pietrasiewicz * driver for a USB device, it also illustrates a technique of 228443f2d2SAndrzej Pietrasiewicz * double-buffering for increased throughput. Last but not least, it 238443f2d2SAndrzej Pietrasiewicz * gives an easy way to probe the behavior of the Mass Storage drivers 248443f2d2SAndrzej Pietrasiewicz * in a USB host. 258443f2d2SAndrzej Pietrasiewicz * 268443f2d2SAndrzej Pietrasiewicz * Since this file serves only administrative purposes and all the 278443f2d2SAndrzej Pietrasiewicz * business logic is implemented in f_mass_storage.* file. Read 288443f2d2SAndrzej Pietrasiewicz * comments in this file for more detailed description. 298443f2d2SAndrzej Pietrasiewicz */ 308443f2d2SAndrzej Pietrasiewicz 318443f2d2SAndrzej Pietrasiewicz 328443f2d2SAndrzej Pietrasiewicz #include <linux/kernel.h> 338443f2d2SAndrzej Pietrasiewicz #include <linux/usb/ch9.h> 348443f2d2SAndrzej Pietrasiewicz #include <linux/module.h> 358443f2d2SAndrzej Pietrasiewicz 368443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 378443f2d2SAndrzej Pietrasiewicz 388443f2d2SAndrzej Pietrasiewicz #define DRIVER_DESC "Mass Storage Gadget" 398443f2d2SAndrzej Pietrasiewicz #define DRIVER_VERSION "2009/09/11" 408443f2d2SAndrzej Pietrasiewicz 418443f2d2SAndrzej Pietrasiewicz /* 428443f2d2SAndrzej Pietrasiewicz * Thanks to NetChip Technologies for donating this product ID. 438443f2d2SAndrzej Pietrasiewicz * 448443f2d2SAndrzej Pietrasiewicz * DO NOT REUSE THESE IDs with any other driver!! Ever!! 458443f2d2SAndrzej Pietrasiewicz * Instead: allocate your own, using normal USB-IF procedures. 468443f2d2SAndrzej Pietrasiewicz */ 478443f2d2SAndrzej Pietrasiewicz #define FSG_VENDOR_ID 0x0525 /* NetChip */ 488443f2d2SAndrzej Pietrasiewicz #define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ 498443f2d2SAndrzej Pietrasiewicz 508443f2d2SAndrzej Pietrasiewicz #include "f_mass_storage.h" 518443f2d2SAndrzej Pietrasiewicz 528443f2d2SAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/ 538443f2d2SAndrzej Pietrasiewicz USB_GADGET_COMPOSITE_OPTIONS(); 548443f2d2SAndrzej Pietrasiewicz 558443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor msg_device_desc = { 568443f2d2SAndrzej Pietrasiewicz .bLength = sizeof msg_device_desc, 578443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_DEVICE, 588443f2d2SAndrzej Pietrasiewicz 590aecfc1bSIgor Kotrasinski /* .bcdUSB = DYNAMIC */ 608443f2d2SAndrzej Pietrasiewicz .bDeviceClass = USB_CLASS_PER_INTERFACE, 618443f2d2SAndrzej Pietrasiewicz 628443f2d2SAndrzej Pietrasiewicz /* Vendor and product id can be overridden by module parameters. */ 638443f2d2SAndrzej Pietrasiewicz .idVendor = cpu_to_le16(FSG_VENDOR_ID), 648443f2d2SAndrzej Pietrasiewicz .idProduct = cpu_to_le16(FSG_PRODUCT_ID), 658443f2d2SAndrzej Pietrasiewicz .bNumConfigurations = 1, 668443f2d2SAndrzej Pietrasiewicz }; 678443f2d2SAndrzej Pietrasiewicz 68d8678897SLi Jun static const struct usb_descriptor_header *otg_desc[2]; 698443f2d2SAndrzej Pietrasiewicz 708443f2d2SAndrzej Pietrasiewicz static struct usb_string strings_dev[] = { 718443f2d2SAndrzej Pietrasiewicz [USB_GADGET_MANUFACTURER_IDX].s = "", 728443f2d2SAndrzej Pietrasiewicz [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, 738443f2d2SAndrzej Pietrasiewicz [USB_GADGET_SERIAL_IDX].s = "", 748443f2d2SAndrzej Pietrasiewicz { } /* end of list */ 758443f2d2SAndrzej Pietrasiewicz }; 768443f2d2SAndrzej Pietrasiewicz 778443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings stringtab_dev = { 788443f2d2SAndrzej Pietrasiewicz .language = 0x0409, /* en-us */ 798443f2d2SAndrzej Pietrasiewicz .strings = strings_dev, 808443f2d2SAndrzej Pietrasiewicz }; 818443f2d2SAndrzej Pietrasiewicz 828443f2d2SAndrzej Pietrasiewicz static struct usb_gadget_strings *dev_strings[] = { 838443f2d2SAndrzej Pietrasiewicz &stringtab_dev, 848443f2d2SAndrzej Pietrasiewicz NULL, 858443f2d2SAndrzej Pietrasiewicz }; 868443f2d2SAndrzej Pietrasiewicz 878443f2d2SAndrzej Pietrasiewicz static struct usb_function_instance *fi_msg; 888443f2d2SAndrzej Pietrasiewicz static struct usb_function *f_msg; 898443f2d2SAndrzej Pietrasiewicz 908443f2d2SAndrzej Pietrasiewicz /****************************** Configurations ******************************/ 918443f2d2SAndrzej Pietrasiewicz 928443f2d2SAndrzej Pietrasiewicz static struct fsg_module_parameters mod_data = { 938443f2d2SAndrzej Pietrasiewicz .stall = 1 948443f2d2SAndrzej Pietrasiewicz }; 958443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 968443f2d2SAndrzej Pietrasiewicz 978443f2d2SAndrzej Pietrasiewicz static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; 988443f2d2SAndrzej Pietrasiewicz 998443f2d2SAndrzej Pietrasiewicz #else 1008443f2d2SAndrzej Pietrasiewicz 1018443f2d2SAndrzej Pietrasiewicz /* 1028443f2d2SAndrzej Pietrasiewicz * Number of buffers we will use. 1038443f2d2SAndrzej Pietrasiewicz * 2 is usually enough for good buffering pipeline 1048443f2d2SAndrzej Pietrasiewicz */ 1058443f2d2SAndrzej Pietrasiewicz #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS 1068443f2d2SAndrzej Pietrasiewicz 1078443f2d2SAndrzej Pietrasiewicz #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ 1088443f2d2SAndrzej Pietrasiewicz 1098443f2d2SAndrzej Pietrasiewicz FSG_MODULE_PARAMETERS(/* no prefix */, mod_data); 1108443f2d2SAndrzej Pietrasiewicz 111c94e289fSArnd Bergmann static int msg_do_config(struct usb_configuration *c) 1128443f2d2SAndrzej Pietrasiewicz { 1138443f2d2SAndrzej Pietrasiewicz struct fsg_opts *opts; 1148443f2d2SAndrzej Pietrasiewicz int ret; 1158443f2d2SAndrzej Pietrasiewicz 1168443f2d2SAndrzej Pietrasiewicz if (gadget_is_otg(c->cdev->gadget)) { 1178443f2d2SAndrzej Pietrasiewicz c->descriptors = otg_desc; 1188443f2d2SAndrzej Pietrasiewicz c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; 1198443f2d2SAndrzej Pietrasiewicz } 1208443f2d2SAndrzej Pietrasiewicz 1218443f2d2SAndrzej Pietrasiewicz opts = fsg_opts_from_func_inst(fi_msg); 1228443f2d2SAndrzej Pietrasiewicz 1238443f2d2SAndrzej Pietrasiewicz f_msg = usb_get_function(fi_msg); 1248443f2d2SAndrzej Pietrasiewicz if (IS_ERR(f_msg)) 1258443f2d2SAndrzej Pietrasiewicz return PTR_ERR(f_msg); 1268443f2d2SAndrzej Pietrasiewicz 1278443f2d2SAndrzej Pietrasiewicz ret = usb_add_function(c, f_msg); 1288443f2d2SAndrzej Pietrasiewicz if (ret) 1298443f2d2SAndrzej Pietrasiewicz goto put_func; 1308443f2d2SAndrzej Pietrasiewicz 1318443f2d2SAndrzej Pietrasiewicz return 0; 1328443f2d2SAndrzej Pietrasiewicz 1338443f2d2SAndrzej Pietrasiewicz put_func: 1348443f2d2SAndrzej Pietrasiewicz usb_put_function(f_msg); 1358443f2d2SAndrzej Pietrasiewicz return ret; 1368443f2d2SAndrzej Pietrasiewicz } 1378443f2d2SAndrzej Pietrasiewicz 1388443f2d2SAndrzej Pietrasiewicz static struct usb_configuration msg_config_driver = { 1398443f2d2SAndrzej Pietrasiewicz .label = "Linux File-Backed Storage", 1408443f2d2SAndrzej Pietrasiewicz .bConfigurationValue = 1, 1418443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_CONFIG_ATT_SELFPOWER, 1428443f2d2SAndrzej Pietrasiewicz }; 1438443f2d2SAndrzej Pietrasiewicz 1448443f2d2SAndrzej Pietrasiewicz 1458443f2d2SAndrzej Pietrasiewicz /****************************** Gadget Bind ******************************/ 1468443f2d2SAndrzej Pietrasiewicz 147c94e289fSArnd Bergmann static int msg_bind(struct usb_composite_dev *cdev) 1488443f2d2SAndrzej Pietrasiewicz { 1498443f2d2SAndrzej Pietrasiewicz struct fsg_opts *opts; 1508443f2d2SAndrzej Pietrasiewicz struct fsg_config config; 1518443f2d2SAndrzej Pietrasiewicz int status; 1528443f2d2SAndrzej Pietrasiewicz 1538443f2d2SAndrzej Pietrasiewicz fi_msg = usb_get_function_instance("mass_storage"); 1548443f2d2SAndrzej Pietrasiewicz if (IS_ERR(fi_msg)) 1558443f2d2SAndrzej Pietrasiewicz return PTR_ERR(fi_msg); 1568443f2d2SAndrzej Pietrasiewicz 1578443f2d2SAndrzej Pietrasiewicz fsg_config_from_params(&config, &mod_data, fsg_num_buffers); 1588443f2d2SAndrzej Pietrasiewicz opts = fsg_opts_from_func_inst(fi_msg); 1598443f2d2SAndrzej Pietrasiewicz 1608443f2d2SAndrzej Pietrasiewicz opts->no_configfs = true; 1618443f2d2SAndrzej Pietrasiewicz status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers); 1628443f2d2SAndrzej Pietrasiewicz if (status) 1638443f2d2SAndrzej Pietrasiewicz goto fail; 1648443f2d2SAndrzej Pietrasiewicz 1658443f2d2SAndrzej Pietrasiewicz status = fsg_common_set_cdev(opts->common, cdev, config.can_stall); 1668443f2d2SAndrzej Pietrasiewicz if (status) 1678443f2d2SAndrzej Pietrasiewicz goto fail_set_cdev; 1688443f2d2SAndrzej Pietrasiewicz 1698443f2d2SAndrzej Pietrasiewicz fsg_common_set_sysfs(opts->common, true); 1708443f2d2SAndrzej Pietrasiewicz status = fsg_common_create_luns(opts->common, &config); 1718443f2d2SAndrzej Pietrasiewicz if (status) 1728443f2d2SAndrzej Pietrasiewicz goto fail_set_cdev; 1738443f2d2SAndrzej Pietrasiewicz 1748443f2d2SAndrzej Pietrasiewicz fsg_common_set_inquiry_string(opts->common, config.vendor_name, 1758443f2d2SAndrzej Pietrasiewicz config.product_name); 1768443f2d2SAndrzej Pietrasiewicz 1778443f2d2SAndrzej Pietrasiewicz status = usb_string_ids_tab(cdev, strings_dev); 1788443f2d2SAndrzej Pietrasiewicz if (status < 0) 1798443f2d2SAndrzej Pietrasiewicz goto fail_string_ids; 1808443f2d2SAndrzej Pietrasiewicz msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; 1818443f2d2SAndrzej Pietrasiewicz 182d8678897SLi Jun if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) { 183d8678897SLi Jun struct usb_descriptor_header *usb_desc; 184d8678897SLi Jun 185d8678897SLi Jun usb_desc = usb_otg_descriptor_alloc(cdev->gadget); 186d8678897SLi Jun if (!usb_desc) 187d8678897SLi Jun goto fail_string_ids; 188d8678897SLi Jun usb_otg_descriptor_init(cdev->gadget, usb_desc); 189d8678897SLi Jun otg_desc[0] = usb_desc; 190d8678897SLi Jun otg_desc[1] = NULL; 191d8678897SLi Jun } 192d8678897SLi Jun 1938443f2d2SAndrzej Pietrasiewicz status = usb_add_config(cdev, &msg_config_driver, msg_do_config); 1948443f2d2SAndrzej Pietrasiewicz if (status < 0) 195d8678897SLi Jun goto fail_otg_desc; 1968443f2d2SAndrzej Pietrasiewicz 1978443f2d2SAndrzej Pietrasiewicz usb_composite_overwrite_options(cdev, &coverwrite); 1988443f2d2SAndrzej Pietrasiewicz dev_info(&cdev->gadget->dev, 1998443f2d2SAndrzej Pietrasiewicz DRIVER_DESC ", version: " DRIVER_VERSION "\n"); 2008443f2d2SAndrzej Pietrasiewicz return 0; 2018443f2d2SAndrzej Pietrasiewicz 202d8678897SLi Jun fail_otg_desc: 203d8678897SLi Jun kfree(otg_desc[0]); 204d8678897SLi Jun otg_desc[0] = NULL; 2058443f2d2SAndrzej Pietrasiewicz fail_string_ids: 2068443f2d2SAndrzej Pietrasiewicz fsg_common_remove_luns(opts->common); 2078443f2d2SAndrzej Pietrasiewicz fail_set_cdev: 2088443f2d2SAndrzej Pietrasiewicz fsg_common_free_buffers(opts->common); 2098443f2d2SAndrzej Pietrasiewicz fail: 2108443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(fi_msg); 2118443f2d2SAndrzej Pietrasiewicz return status; 2128443f2d2SAndrzej Pietrasiewicz } 2138443f2d2SAndrzej Pietrasiewicz 2148443f2d2SAndrzej Pietrasiewicz static int msg_unbind(struct usb_composite_dev *cdev) 2158443f2d2SAndrzej Pietrasiewicz { 2168443f2d2SAndrzej Pietrasiewicz if (!IS_ERR(f_msg)) 2178443f2d2SAndrzej Pietrasiewicz usb_put_function(f_msg); 2188443f2d2SAndrzej Pietrasiewicz 2198443f2d2SAndrzej Pietrasiewicz if (!IS_ERR(fi_msg)) 2208443f2d2SAndrzej Pietrasiewicz usb_put_function_instance(fi_msg); 2218443f2d2SAndrzej Pietrasiewicz 222d8678897SLi Jun kfree(otg_desc[0]); 223d8678897SLi Jun otg_desc[0] = NULL; 224d8678897SLi Jun 2258443f2d2SAndrzej Pietrasiewicz return 0; 2268443f2d2SAndrzej Pietrasiewicz } 2278443f2d2SAndrzej Pietrasiewicz 2288443f2d2SAndrzej Pietrasiewicz /****************************** Some noise ******************************/ 2298443f2d2SAndrzej Pietrasiewicz 230c94e289fSArnd Bergmann static struct usb_composite_driver msg_driver = { 2318443f2d2SAndrzej Pietrasiewicz .name = "g_mass_storage", 2328443f2d2SAndrzej Pietrasiewicz .dev = &msg_device_desc, 2338443f2d2SAndrzej Pietrasiewicz .max_speed = USB_SPEED_SUPER, 2348443f2d2SAndrzej Pietrasiewicz .needs_serial = 1, 2358443f2d2SAndrzej Pietrasiewicz .strings = dev_strings, 2368443f2d2SAndrzej Pietrasiewicz .bind = msg_bind, 2378443f2d2SAndrzej Pietrasiewicz .unbind = msg_unbind, 2388443f2d2SAndrzej Pietrasiewicz }; 2398443f2d2SAndrzej Pietrasiewicz 2408443f2d2SAndrzej Pietrasiewicz MODULE_DESCRIPTION(DRIVER_DESC); 2418443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Michal Nazarewicz"); 2428443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 2438443f2d2SAndrzej Pietrasiewicz 2448443f2d2SAndrzej Pietrasiewicz static int __init msg_init(void) 2458443f2d2SAndrzej Pietrasiewicz { 2461fbbb78fSAlan Stern return usb_composite_probe(&msg_driver); 2478443f2d2SAndrzej Pietrasiewicz } 2488443f2d2SAndrzej Pietrasiewicz module_init(msg_init); 2498443f2d2SAndrzej Pietrasiewicz 2501fbbb78fSAlan Stern static void __exit msg_cleanup(void) 2518443f2d2SAndrzej Pietrasiewicz { 2528443f2d2SAndrzej Pietrasiewicz usb_composite_unregister(&msg_driver); 2538443f2d2SAndrzej Pietrasiewicz } 2548443f2d2SAndrzej Pietrasiewicz module_exit(msg_cleanup); 255