162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * storage_common.c -- Common definitions for mass storage functionality 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003-2008 Alan Stern 662306a36Sopenharmony_ci * Copyeight (C) 2009 Samsung Electronics 762306a36Sopenharmony_ci * Author: Michal Nazarewicz (mina86@mina86.com) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * This file requires the following identifiers used in USB strings to 1262306a36Sopenharmony_ci * be defined (each of type pointer to char): 1362306a36Sopenharmony_ci * - fsg_string_interface -- name of the interface 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers 1862306a36Sopenharmony_ci * sets the number of pipeline buffers (length of the fsg_buffhd array). 1962306a36Sopenharmony_ci * The valid range of num_buffers is: num >= 2 && num <= 4. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/blkdev.h> 2462306a36Sopenharmony_ci#include <linux/file.h> 2562306a36Sopenharmony_ci#include <linux/fs.h> 2662306a36Sopenharmony_ci#include <linux/kstrtox.h> 2762306a36Sopenharmony_ci#include <linux/usb/composite.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "storage_common.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* There is only one interface. */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct usb_interface_descriptor fsg_intf_desc = { 3462306a36Sopenharmony_ci .bLength = sizeof fsg_intf_desc, 3562306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ 3862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_MASS_STORAGE, 3962306a36Sopenharmony_ci .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ 4062306a36Sopenharmony_ci .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ 4162306a36Sopenharmony_ci .iInterface = FSG_STRING_INTERFACE, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_intf_desc); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Three full-speed endpoint descriptors: bulk-in, bulk-out, and 4762306a36Sopenharmony_ci * interrupt-in. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { 5162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 5262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 5562306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 5662306a36Sopenharmony_ci /* wMaxPacketSize set by autoconfiguration */ 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { 6162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 6262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 6562306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 6662306a36Sopenharmony_ci /* wMaxPacketSize set by autoconfiguration */ 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct usb_descriptor_header *fsg_fs_function[] = { 7162306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_intf_desc, 7262306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, 7362306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, 7462306a36Sopenharmony_ci NULL, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_fs_function); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* 8062306a36Sopenharmony_ci * USB 2.0 devices need to expose both high speed and full speed 8162306a36Sopenharmony_ci * descriptors, unless they only run at full speed. 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * That means alternate endpoint descriptors (bigger packets). 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { 8662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 8762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ 9062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 9162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { 9662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 9762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ 10062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 10162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 10262306a36Sopenharmony_ci .bInterval = 1, /* NAK every 1 uframe */ 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct usb_descriptor_header *fsg_hs_function[] = { 10862306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_intf_desc, 10962306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, 11062306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, 11162306a36Sopenharmony_ci NULL, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_hs_function); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { 11662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 11762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ 12062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 12162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistruct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { 12662306a36Sopenharmony_ci .bLength = sizeof(fsg_ss_bulk_in_comp_desc), 12762306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci /*.bMaxBurst = DYNAMIC, */ 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { 13462306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 13562306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ 13862306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 13962306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistruct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { 14462306a36Sopenharmony_ci .bLength = sizeof(fsg_ss_bulk_in_comp_desc), 14562306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /*.bMaxBurst = DYNAMIC, */ 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistruct usb_descriptor_header *fsg_ss_function[] = { 15262306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_intf_desc, 15362306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, 15462306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, 15562306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, 15662306a36Sopenharmony_ci (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, 15762306a36Sopenharmony_ci NULL, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_ss_function); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /*-------------------------------------------------------------------------*/ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* 16562306a36Sopenharmony_ci * If the next two routines are called while the gadget is registered, 16662306a36Sopenharmony_ci * the caller must own fsg->filesem for writing. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid fsg_lun_close(struct fsg_lun *curlun) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (curlun->filp) { 17262306a36Sopenharmony_ci LDBG(curlun, "close backing file\n"); 17362306a36Sopenharmony_ci fput(curlun->filp); 17462306a36Sopenharmony_ci curlun->filp = NULL; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_lun_close); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint fsg_lun_open(struct fsg_lun *curlun, const char *filename) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int ro; 18262306a36Sopenharmony_ci struct file *filp = NULL; 18362306a36Sopenharmony_ci int rc = -EINVAL; 18462306a36Sopenharmony_ci struct inode *inode = NULL; 18562306a36Sopenharmony_ci loff_t size; 18662306a36Sopenharmony_ci loff_t num_sectors; 18762306a36Sopenharmony_ci loff_t min_sectors; 18862306a36Sopenharmony_ci unsigned int blkbits; 18962306a36Sopenharmony_ci unsigned int blksize; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* R/W if we can, R/O if we must */ 19262306a36Sopenharmony_ci ro = curlun->initially_ro; 19362306a36Sopenharmony_ci if (!ro) { 19462306a36Sopenharmony_ci filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); 19562306a36Sopenharmony_ci if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES) 19662306a36Sopenharmony_ci ro = 1; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci if (ro) 19962306a36Sopenharmony_ci filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); 20062306a36Sopenharmony_ci if (IS_ERR(filp)) { 20162306a36Sopenharmony_ci LINFO(curlun, "unable to open backing file: %s\n", filename); 20262306a36Sopenharmony_ci return PTR_ERR(filp); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!(filp->f_mode & FMODE_WRITE)) 20662306a36Sopenharmony_ci ro = 1; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci inode = filp->f_mapping->host; 20962306a36Sopenharmony_ci if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { 21062306a36Sopenharmony_ci LINFO(curlun, "invalid file type: %s\n", filename); 21162306a36Sopenharmony_ci goto out; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * If we can't read the file, it's no good. 21662306a36Sopenharmony_ci * If we can't write the file, use it read-only. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci if (!(filp->f_mode & FMODE_CAN_READ)) { 21962306a36Sopenharmony_ci LINFO(curlun, "file not readable: %s\n", filename); 22062306a36Sopenharmony_ci goto out; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci if (!(filp->f_mode & FMODE_CAN_WRITE)) 22362306a36Sopenharmony_ci ro = 1; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci size = i_size_read(inode); 22662306a36Sopenharmony_ci if (size < 0) { 22762306a36Sopenharmony_ci LINFO(curlun, "unable to find file size: %s\n", filename); 22862306a36Sopenharmony_ci rc = (int) size; 22962306a36Sopenharmony_ci goto out; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (curlun->cdrom) { 23362306a36Sopenharmony_ci blksize = 2048; 23462306a36Sopenharmony_ci blkbits = 11; 23562306a36Sopenharmony_ci } else if (S_ISBLK(inode->i_mode)) { 23662306a36Sopenharmony_ci blksize = bdev_logical_block_size(I_BDEV(inode)); 23762306a36Sopenharmony_ci blkbits = blksize_bits(blksize); 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci blksize = 512; 24062306a36Sopenharmony_ci blkbits = 9; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci num_sectors = size >> blkbits; /* File size in logic-block-size blocks */ 24462306a36Sopenharmony_ci min_sectors = 1; 24562306a36Sopenharmony_ci if (curlun->cdrom) { 24662306a36Sopenharmony_ci min_sectors = 300; /* Smallest track is 300 frames */ 24762306a36Sopenharmony_ci if (num_sectors >= 256*60*75) { 24862306a36Sopenharmony_ci num_sectors = 256*60*75 - 1; 24962306a36Sopenharmony_ci LINFO(curlun, "file too big: %s\n", filename); 25062306a36Sopenharmony_ci LINFO(curlun, "using only first %d blocks\n", 25162306a36Sopenharmony_ci (int) num_sectors); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci if (num_sectors < min_sectors) { 25562306a36Sopenharmony_ci LINFO(curlun, "file too small: %s\n", filename); 25662306a36Sopenharmony_ci rc = -ETOOSMALL; 25762306a36Sopenharmony_ci goto out; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (fsg_lun_is_open(curlun)) 26162306a36Sopenharmony_ci fsg_lun_close(curlun); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci curlun->blksize = blksize; 26462306a36Sopenharmony_ci curlun->blkbits = blkbits; 26562306a36Sopenharmony_ci curlun->ro = ro; 26662306a36Sopenharmony_ci curlun->filp = filp; 26762306a36Sopenharmony_ci curlun->file_length = size; 26862306a36Sopenharmony_ci curlun->num_sectors = num_sectors; 26962306a36Sopenharmony_ci LDBG(curlun, "open backing file: %s\n", filename); 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciout: 27362306a36Sopenharmony_ci fput(filp); 27462306a36Sopenharmony_ci return rc; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_lun_open); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * Sync the file data, don't bother with the metadata. 28362306a36Sopenharmony_ci * This code was copied from fs/buffer.c:sys_fdatasync(). 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ciint fsg_lun_fsync_sub(struct fsg_lun *curlun) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct file *filp = curlun->filp; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (curlun->ro || !filp) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci return vfs_fsync(filp, 1); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_lun_fsync_sub); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_civoid store_cdrom_address(u8 *dest, int msf, u32 addr) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci if (msf) { 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * Convert to Minutes-Seconds-Frames. 30062306a36Sopenharmony_ci * Sector size is already set to 2048 bytes. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci addr += 2*75; /* Lead-in occupies 2 seconds */ 30362306a36Sopenharmony_ci dest[3] = addr % 75; /* Frames */ 30462306a36Sopenharmony_ci addr /= 75; 30562306a36Sopenharmony_ci dest[2] = addr % 60; /* Seconds */ 30662306a36Sopenharmony_ci addr /= 60; 30762306a36Sopenharmony_ci dest[1] = addr; /* Minutes */ 30862306a36Sopenharmony_ci dest[0] = 0; /* Reserved */ 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci /* Absolute sector */ 31162306a36Sopenharmony_ci put_unaligned_be32(addr, dest); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(store_cdrom_address); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cissize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) 32262306a36Sopenharmony_ci ? curlun->ro 32362306a36Sopenharmony_ci : curlun->initially_ro); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_ro); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cissize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return sprintf(buf, "%u\n", curlun->nofua); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_nofua); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cissize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, 33462306a36Sopenharmony_ci char *buf) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci char *p; 33762306a36Sopenharmony_ci ssize_t rc; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci down_read(filesem); 34062306a36Sopenharmony_ci if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ 34162306a36Sopenharmony_ci p = file_path(curlun->filp, buf, PAGE_SIZE - 1); 34262306a36Sopenharmony_ci if (IS_ERR(p)) 34362306a36Sopenharmony_ci rc = PTR_ERR(p); 34462306a36Sopenharmony_ci else { 34562306a36Sopenharmony_ci rc = strlen(p); 34662306a36Sopenharmony_ci memmove(buf, p, rc); 34762306a36Sopenharmony_ci buf[rc] = '\n'; /* Add a newline */ 34862306a36Sopenharmony_ci buf[++rc] = 0; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci } else { /* No file, return 0 bytes */ 35162306a36Sopenharmony_ci *buf = 0; 35262306a36Sopenharmony_ci rc = 0; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci up_read(filesem); 35562306a36Sopenharmony_ci return rc; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_file); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cissize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci return sprintf(buf, "%u\n", curlun->cdrom); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_cdrom); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cissize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci return sprintf(buf, "%u\n", curlun->removable); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_removable); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cissize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci return sprintf(buf, "%s\n", curlun->inquiry_string); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_show_inquiry_string); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/* 37862306a36Sopenharmony_ci * The caller must hold fsg->filesem for reading when calling this function. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci if (fsg_lun_is_open(curlun)) { 38362306a36Sopenharmony_ci LDBG(curlun, "read-only status change prevented\n"); 38462306a36Sopenharmony_ci return -EBUSY; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci curlun->ro = ro; 38862306a36Sopenharmony_ci curlun->initially_ro = ro; 38962306a36Sopenharmony_ci LDBG(curlun, "read-only status set to %d\n", curlun->ro); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cissize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, 39562306a36Sopenharmony_ci const char *buf, size_t count) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci ssize_t rc; 39862306a36Sopenharmony_ci bool ro; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci rc = kstrtobool(buf, &ro); 40162306a36Sopenharmony_ci if (rc) 40262306a36Sopenharmony_ci return rc; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* 40562306a36Sopenharmony_ci * Allow the write-enable status to change only while the 40662306a36Sopenharmony_ci * backing file is closed. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci down_read(filesem); 40962306a36Sopenharmony_ci rc = _fsg_store_ro(curlun, ro); 41062306a36Sopenharmony_ci if (!rc) 41162306a36Sopenharmony_ci rc = count; 41262306a36Sopenharmony_ci up_read(filesem); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return rc; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_ro); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cissize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci bool nofua; 42162306a36Sopenharmony_ci int ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = kstrtobool(buf, &nofua); 42462306a36Sopenharmony_ci if (ret) 42562306a36Sopenharmony_ci return ret; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Sync data when switching from async mode to sync */ 42862306a36Sopenharmony_ci if (!nofua && curlun->nofua) 42962306a36Sopenharmony_ci fsg_lun_fsync_sub(curlun); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci curlun->nofua = nofua; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return count; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_nofua); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cissize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, 43862306a36Sopenharmony_ci const char *buf, size_t count) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci int rc = 0; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { 44362306a36Sopenharmony_ci LDBG(curlun, "eject attempt prevented\n"); 44462306a36Sopenharmony_ci return -EBUSY; /* "Door is locked" */ 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Remove a trailing newline */ 44862306a36Sopenharmony_ci if (count > 0 && buf[count-1] == '\n') 44962306a36Sopenharmony_ci ((char *) buf)[count-1] = 0; /* Ugh! */ 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Load new medium */ 45262306a36Sopenharmony_ci down_write(filesem); 45362306a36Sopenharmony_ci if (count > 0 && buf[0]) { 45462306a36Sopenharmony_ci /* fsg_lun_open() will close existing file if any. */ 45562306a36Sopenharmony_ci rc = fsg_lun_open(curlun, buf); 45662306a36Sopenharmony_ci if (rc == 0) 45762306a36Sopenharmony_ci curlun->unit_attention_data = 45862306a36Sopenharmony_ci SS_NOT_READY_TO_READY_TRANSITION; 45962306a36Sopenharmony_ci } else if (fsg_lun_is_open(curlun)) { 46062306a36Sopenharmony_ci fsg_lun_close(curlun); 46162306a36Sopenharmony_ci curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci up_write(filesem); 46462306a36Sopenharmony_ci return (rc < 0 ? rc : count); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_file); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_cissize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, 46962306a36Sopenharmony_ci const char *buf, size_t count) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci bool cdrom; 47262306a36Sopenharmony_ci int ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = kstrtobool(buf, &cdrom); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci down_read(filesem); 47962306a36Sopenharmony_ci ret = cdrom ? _fsg_store_ro(curlun, true) : 0; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (!ret) { 48262306a36Sopenharmony_ci curlun->cdrom = cdrom; 48362306a36Sopenharmony_ci ret = count; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci up_read(filesem); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_cdrom); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cissize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, 49262306a36Sopenharmony_ci size_t count) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci bool removable; 49562306a36Sopenharmony_ci int ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = kstrtobool(buf, &removable); 49862306a36Sopenharmony_ci if (ret) 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci curlun->removable = removable; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return count; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_removable); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cissize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, 50862306a36Sopenharmony_ci size_t count) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci const size_t len = min(count, sizeof(curlun->inquiry_string)); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (len == 0 || buf[0] == '\n') { 51362306a36Sopenharmony_ci curlun->inquiry_string[0] = 0; 51462306a36Sopenharmony_ci } else { 51562306a36Sopenharmony_ci snprintf(curlun->inquiry_string, 51662306a36Sopenharmony_ci sizeof(curlun->inquiry_string), "%-28s", buf); 51762306a36Sopenharmony_ci if (curlun->inquiry_string[len-1] == '\n') 51862306a36Sopenharmony_ci curlun->inquiry_string[len-1] = ' '; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return count; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_inquiry_string); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cissize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *filesem, 52662306a36Sopenharmony_ci const char *buf, size_t count) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci int ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * Forcibly detach the backing file from the LUN 53262306a36Sopenharmony_ci * regardless of whether the host has allowed it. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_ci curlun->prevent_medium_removal = 0; 53562306a36Sopenharmony_ci ret = fsg_store_file(curlun, filesem, "", 0); 53662306a36Sopenharmony_ci return ret < 0 ? ret : count; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsg_store_forced_eject); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 541