xref: /openbmc/linux/drivers/usb/gadget/function/storage_common.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz  * storage_common.c -- Common definitions for mass storage functionality
400a2430fSAndrzej Pietrasiewicz  *
500a2430fSAndrzej Pietrasiewicz  * Copyright (C) 2003-2008 Alan Stern
600a2430fSAndrzej Pietrasiewicz  * Copyeight (C) 2009 Samsung Electronics
700a2430fSAndrzej Pietrasiewicz  * Author: Michal Nazarewicz (mina86@mina86.com)
800a2430fSAndrzej Pietrasiewicz  */
900a2430fSAndrzej Pietrasiewicz 
1000a2430fSAndrzej Pietrasiewicz /*
1100a2430fSAndrzej Pietrasiewicz  * This file requires the following identifiers used in USB strings to
1200a2430fSAndrzej Pietrasiewicz  * be defined (each of type pointer to char):
1300a2430fSAndrzej Pietrasiewicz  *  - fsg_string_interface    -- name of the interface
1400a2430fSAndrzej Pietrasiewicz  */
1500a2430fSAndrzej Pietrasiewicz 
1600a2430fSAndrzej Pietrasiewicz /*
1700a2430fSAndrzej Pietrasiewicz  * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
1800a2430fSAndrzej Pietrasiewicz  * sets the number of pipeline buffers (length of the fsg_buffhd array).
1900a2430fSAndrzej Pietrasiewicz  * The valid range of num_buffers is: num >= 2 && num <= 4.
2000a2430fSAndrzej Pietrasiewicz  */
2100a2430fSAndrzej Pietrasiewicz 
2200a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
2300a2430fSAndrzej Pietrasiewicz #include <linux/blkdev.h>
2400a2430fSAndrzej Pietrasiewicz #include <linux/file.h>
2500a2430fSAndrzej Pietrasiewicz #include <linux/fs.h>
26*a8bc8cc1SChristophe JAILLET #include <linux/kstrtox.h>
2700a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h>
2800a2430fSAndrzej Pietrasiewicz 
2900a2430fSAndrzej Pietrasiewicz #include "storage_common.h"
3000a2430fSAndrzej Pietrasiewicz 
3100a2430fSAndrzej Pietrasiewicz /* There is only one interface. */
3200a2430fSAndrzej Pietrasiewicz 
3300a2430fSAndrzej Pietrasiewicz struct usb_interface_descriptor fsg_intf_desc = {
3400a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof fsg_intf_desc,
3500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
3600a2430fSAndrzej Pietrasiewicz 
3700a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */
3800a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
3900a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */
4000a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */
4100a2430fSAndrzej Pietrasiewicz 	.iInterface =		FSG_STRING_INTERFACE,
4200a2430fSAndrzej Pietrasiewicz };
4300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_intf_desc);
4400a2430fSAndrzej Pietrasiewicz 
4500a2430fSAndrzej Pietrasiewicz /*
4600a2430fSAndrzej Pietrasiewicz  * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
4700a2430fSAndrzej Pietrasiewicz  * interrupt-in.
4800a2430fSAndrzej Pietrasiewicz  */
4900a2430fSAndrzej Pietrasiewicz 
5000a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
5100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
5200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
5300a2430fSAndrzej Pietrasiewicz 
5400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
5500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
5600a2430fSAndrzej Pietrasiewicz 	/* wMaxPacketSize set by autoconfiguration */
5700a2430fSAndrzej Pietrasiewicz };
5800a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
5900a2430fSAndrzej Pietrasiewicz 
6000a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
6100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
6200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
6300a2430fSAndrzej Pietrasiewicz 
6400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
6500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
6600a2430fSAndrzej Pietrasiewicz 	/* wMaxPacketSize set by autoconfiguration */
6700a2430fSAndrzej Pietrasiewicz };
6800a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
6900a2430fSAndrzej Pietrasiewicz 
7000a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *fsg_fs_function[] = {
7100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_intf_desc,
7200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
7300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
7400a2430fSAndrzej Pietrasiewicz 	NULL,
7500a2430fSAndrzej Pietrasiewicz };
7600a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_fs_function);
7700a2430fSAndrzej Pietrasiewicz 
7800a2430fSAndrzej Pietrasiewicz 
7900a2430fSAndrzej Pietrasiewicz /*
8000a2430fSAndrzej Pietrasiewicz  * USB 2.0 devices need to expose both high speed and full speed
8100a2430fSAndrzej Pietrasiewicz  * descriptors, unless they only run at full speed.
8200a2430fSAndrzej Pietrasiewicz  *
83cc50dc28SKrzysztof Opasiak  * That means alternate endpoint descriptors (bigger packets).
8400a2430fSAndrzej Pietrasiewicz  */
8500a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
8600a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
8700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
8800a2430fSAndrzej Pietrasiewicz 
8900a2430fSAndrzej Pietrasiewicz 	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
9000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
9100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
9200a2430fSAndrzej Pietrasiewicz };
9300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
9400a2430fSAndrzej Pietrasiewicz 
9500a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
9600a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
9700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
9800a2430fSAndrzej Pietrasiewicz 
9900a2430fSAndrzej Pietrasiewicz 	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
10000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
10100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
10200a2430fSAndrzej Pietrasiewicz 	.bInterval =		1,	/* NAK every 1 uframe */
10300a2430fSAndrzej Pietrasiewicz };
10400a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
10500a2430fSAndrzej Pietrasiewicz 
10600a2430fSAndrzej Pietrasiewicz 
10700a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *fsg_hs_function[] = {
10800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_intf_desc,
10900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
11000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
11100a2430fSAndrzej Pietrasiewicz 	NULL,
11200a2430fSAndrzej Pietrasiewicz };
11300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_hs_function);
11400a2430fSAndrzej Pietrasiewicz 
11500a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
11600a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
11700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
11800a2430fSAndrzej Pietrasiewicz 
11900a2430fSAndrzej Pietrasiewicz 	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
12000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
12100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
12200a2430fSAndrzej Pietrasiewicz };
12300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
12400a2430fSAndrzej Pietrasiewicz 
12500a2430fSAndrzej Pietrasiewicz struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
12600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
12700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
12800a2430fSAndrzej Pietrasiewicz 
12900a2430fSAndrzej Pietrasiewicz 	/*.bMaxBurst =		DYNAMIC, */
13000a2430fSAndrzej Pietrasiewicz };
13100a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
13200a2430fSAndrzej Pietrasiewicz 
13300a2430fSAndrzej Pietrasiewicz struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
13400a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
13500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
13600a2430fSAndrzej Pietrasiewicz 
13700a2430fSAndrzej Pietrasiewicz 	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
13800a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
13900a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
14000a2430fSAndrzej Pietrasiewicz };
14100a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
14200a2430fSAndrzej Pietrasiewicz 
14300a2430fSAndrzej Pietrasiewicz struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
14400a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
14500a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
14600a2430fSAndrzej Pietrasiewicz 
14700a2430fSAndrzej Pietrasiewicz 	/*.bMaxBurst =		DYNAMIC, */
14800a2430fSAndrzej Pietrasiewicz };
14900a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
15000a2430fSAndrzej Pietrasiewicz 
15100a2430fSAndrzej Pietrasiewicz struct usb_descriptor_header *fsg_ss_function[] = {
15200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_intf_desc,
15300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
15400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
15500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
15600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
15700a2430fSAndrzej Pietrasiewicz 	NULL,
15800a2430fSAndrzej Pietrasiewicz };
15900a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_ss_function);
16000a2430fSAndrzej Pietrasiewicz 
16100a2430fSAndrzej Pietrasiewicz 
16200a2430fSAndrzej Pietrasiewicz  /*-------------------------------------------------------------------------*/
16300a2430fSAndrzej Pietrasiewicz 
16400a2430fSAndrzej Pietrasiewicz /*
16500a2430fSAndrzej Pietrasiewicz  * If the next two routines are called while the gadget is registered,
16600a2430fSAndrzej Pietrasiewicz  * the caller must own fsg->filesem for writing.
16700a2430fSAndrzej Pietrasiewicz  */
16800a2430fSAndrzej Pietrasiewicz 
fsg_lun_close(struct fsg_lun * curlun)16900a2430fSAndrzej Pietrasiewicz void fsg_lun_close(struct fsg_lun *curlun)
17000a2430fSAndrzej Pietrasiewicz {
17100a2430fSAndrzej Pietrasiewicz 	if (curlun->filp) {
17200a2430fSAndrzej Pietrasiewicz 		LDBG(curlun, "close backing file\n");
17300a2430fSAndrzej Pietrasiewicz 		fput(curlun->filp);
17400a2430fSAndrzej Pietrasiewicz 		curlun->filp = NULL;
17500a2430fSAndrzej Pietrasiewicz 	}
17600a2430fSAndrzej Pietrasiewicz }
17700a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_lun_close);
17800a2430fSAndrzej Pietrasiewicz 
fsg_lun_open(struct fsg_lun * curlun,const char * filename)17900a2430fSAndrzej Pietrasiewicz int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
18000a2430fSAndrzej Pietrasiewicz {
18100a2430fSAndrzej Pietrasiewicz 	int				ro;
18200a2430fSAndrzej Pietrasiewicz 	struct file			*filp = NULL;
18300a2430fSAndrzej Pietrasiewicz 	int				rc = -EINVAL;
18400a2430fSAndrzej Pietrasiewicz 	struct inode			*inode = NULL;
18500a2430fSAndrzej Pietrasiewicz 	loff_t				size;
18600a2430fSAndrzej Pietrasiewicz 	loff_t				num_sectors;
18700a2430fSAndrzej Pietrasiewicz 	loff_t				min_sectors;
18800a2430fSAndrzej Pietrasiewicz 	unsigned int			blkbits;
18900a2430fSAndrzej Pietrasiewicz 	unsigned int			blksize;
19000a2430fSAndrzej Pietrasiewicz 
19100a2430fSAndrzej Pietrasiewicz 	/* R/W if we can, R/O if we must */
19200a2430fSAndrzej Pietrasiewicz 	ro = curlun->initially_ro;
19300a2430fSAndrzej Pietrasiewicz 	if (!ro) {
19400a2430fSAndrzej Pietrasiewicz 		filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
19500a2430fSAndrzej Pietrasiewicz 		if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
19600a2430fSAndrzej Pietrasiewicz 			ro = 1;
19700a2430fSAndrzej Pietrasiewicz 	}
19800a2430fSAndrzej Pietrasiewicz 	if (ro)
19900a2430fSAndrzej Pietrasiewicz 		filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
20000a2430fSAndrzej Pietrasiewicz 	if (IS_ERR(filp)) {
20100a2430fSAndrzej Pietrasiewicz 		LINFO(curlun, "unable to open backing file: %s\n", filename);
20200a2430fSAndrzej Pietrasiewicz 		return PTR_ERR(filp);
20300a2430fSAndrzej Pietrasiewicz 	}
20400a2430fSAndrzej Pietrasiewicz 
20500a2430fSAndrzej Pietrasiewicz 	if (!(filp->f_mode & FMODE_WRITE))
20600a2430fSAndrzej Pietrasiewicz 		ro = 1;
20700a2430fSAndrzej Pietrasiewicz 
2084e7b5671SChristoph Hellwig 	inode = filp->f_mapping->host;
20900a2430fSAndrzej Pietrasiewicz 	if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
21000a2430fSAndrzej Pietrasiewicz 		LINFO(curlun, "invalid file type: %s\n", filename);
21100a2430fSAndrzej Pietrasiewicz 		goto out;
21200a2430fSAndrzej Pietrasiewicz 	}
21300a2430fSAndrzej Pietrasiewicz 
21400a2430fSAndrzej Pietrasiewicz 	/*
21500a2430fSAndrzej Pietrasiewicz 	 * If we can't read the file, it's no good.
21600a2430fSAndrzej Pietrasiewicz 	 * If we can't write the file, use it read-only.
21700a2430fSAndrzej Pietrasiewicz 	 */
21800a2430fSAndrzej Pietrasiewicz 	if (!(filp->f_mode & FMODE_CAN_READ)) {
21900a2430fSAndrzej Pietrasiewicz 		LINFO(curlun, "file not readable: %s\n", filename);
22000a2430fSAndrzej Pietrasiewicz 		goto out;
22100a2430fSAndrzej Pietrasiewicz 	}
22200a2430fSAndrzej Pietrasiewicz 	if (!(filp->f_mode & FMODE_CAN_WRITE))
22300a2430fSAndrzej Pietrasiewicz 		ro = 1;
22400a2430fSAndrzej Pietrasiewicz 
2254e7b5671SChristoph Hellwig 	size = i_size_read(inode);
22600a2430fSAndrzej Pietrasiewicz 	if (size < 0) {
22700a2430fSAndrzej Pietrasiewicz 		LINFO(curlun, "unable to find file size: %s\n", filename);
22800a2430fSAndrzej Pietrasiewicz 		rc = (int) size;
22900a2430fSAndrzej Pietrasiewicz 		goto out;
23000a2430fSAndrzej Pietrasiewicz 	}
23100a2430fSAndrzej Pietrasiewicz 
23200a2430fSAndrzej Pietrasiewicz 	if (curlun->cdrom) {
23300a2430fSAndrzej Pietrasiewicz 		blksize = 2048;
23400a2430fSAndrzej Pietrasiewicz 		blkbits = 11;
2354e7b5671SChristoph Hellwig 	} else if (S_ISBLK(inode->i_mode)) {
2364e7b5671SChristoph Hellwig 		blksize = bdev_logical_block_size(I_BDEV(inode));
23700a2430fSAndrzej Pietrasiewicz 		blkbits = blksize_bits(blksize);
23800a2430fSAndrzej Pietrasiewicz 	} else {
23900a2430fSAndrzej Pietrasiewicz 		blksize = 512;
24000a2430fSAndrzej Pietrasiewicz 		blkbits = 9;
24100a2430fSAndrzej Pietrasiewicz 	}
24200a2430fSAndrzej Pietrasiewicz 
24300a2430fSAndrzej Pietrasiewicz 	num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
24400a2430fSAndrzej Pietrasiewicz 	min_sectors = 1;
24500a2430fSAndrzej Pietrasiewicz 	if (curlun->cdrom) {
24600a2430fSAndrzej Pietrasiewicz 		min_sectors = 300;	/* Smallest track is 300 frames */
24700a2430fSAndrzej Pietrasiewicz 		if (num_sectors >= 256*60*75) {
24800a2430fSAndrzej Pietrasiewicz 			num_sectors = 256*60*75 - 1;
24900a2430fSAndrzej Pietrasiewicz 			LINFO(curlun, "file too big: %s\n", filename);
25000a2430fSAndrzej Pietrasiewicz 			LINFO(curlun, "using only first %d blocks\n",
25100a2430fSAndrzej Pietrasiewicz 					(int) num_sectors);
25200a2430fSAndrzej Pietrasiewicz 		}
25300a2430fSAndrzej Pietrasiewicz 	}
25400a2430fSAndrzej Pietrasiewicz 	if (num_sectors < min_sectors) {
25500a2430fSAndrzej Pietrasiewicz 		LINFO(curlun, "file too small: %s\n", filename);
25600a2430fSAndrzej Pietrasiewicz 		rc = -ETOOSMALL;
25700a2430fSAndrzej Pietrasiewicz 		goto out;
25800a2430fSAndrzej Pietrasiewicz 	}
25900a2430fSAndrzej Pietrasiewicz 
26000a2430fSAndrzej Pietrasiewicz 	if (fsg_lun_is_open(curlun))
26100a2430fSAndrzej Pietrasiewicz 		fsg_lun_close(curlun);
26200a2430fSAndrzej Pietrasiewicz 
26300a2430fSAndrzej Pietrasiewicz 	curlun->blksize = blksize;
26400a2430fSAndrzej Pietrasiewicz 	curlun->blkbits = blkbits;
26500a2430fSAndrzej Pietrasiewicz 	curlun->ro = ro;
26600a2430fSAndrzej Pietrasiewicz 	curlun->filp = filp;
26700a2430fSAndrzej Pietrasiewicz 	curlun->file_length = size;
26800a2430fSAndrzej Pietrasiewicz 	curlun->num_sectors = num_sectors;
26900a2430fSAndrzej Pietrasiewicz 	LDBG(curlun, "open backing file: %s\n", filename);
27000a2430fSAndrzej Pietrasiewicz 	return 0;
27100a2430fSAndrzej Pietrasiewicz 
27200a2430fSAndrzej Pietrasiewicz out:
27300a2430fSAndrzej Pietrasiewicz 	fput(filp);
27400a2430fSAndrzej Pietrasiewicz 	return rc;
27500a2430fSAndrzej Pietrasiewicz }
27600a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_lun_open);
27700a2430fSAndrzej Pietrasiewicz 
27800a2430fSAndrzej Pietrasiewicz 
27900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
28000a2430fSAndrzej Pietrasiewicz 
28100a2430fSAndrzej Pietrasiewicz /*
28200a2430fSAndrzej Pietrasiewicz  * Sync the file data, don't bother with the metadata.
28300a2430fSAndrzej Pietrasiewicz  * This code was copied from fs/buffer.c:sys_fdatasync().
28400a2430fSAndrzej Pietrasiewicz  */
fsg_lun_fsync_sub(struct fsg_lun * curlun)28500a2430fSAndrzej Pietrasiewicz int fsg_lun_fsync_sub(struct fsg_lun *curlun)
28600a2430fSAndrzej Pietrasiewicz {
28700a2430fSAndrzej Pietrasiewicz 	struct file	*filp = curlun->filp;
28800a2430fSAndrzej Pietrasiewicz 
28900a2430fSAndrzej Pietrasiewicz 	if (curlun->ro || !filp)
29000a2430fSAndrzej Pietrasiewicz 		return 0;
29100a2430fSAndrzej Pietrasiewicz 	return vfs_fsync(filp, 1);
29200a2430fSAndrzej Pietrasiewicz }
29300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
29400a2430fSAndrzej Pietrasiewicz 
store_cdrom_address(u8 * dest,int msf,u32 addr)29500a2430fSAndrzej Pietrasiewicz void store_cdrom_address(u8 *dest, int msf, u32 addr)
29600a2430fSAndrzej Pietrasiewicz {
29700a2430fSAndrzej Pietrasiewicz 	if (msf) {
2989d4dc16eSKrishna Kurapati 		/*
2999d4dc16eSKrishna Kurapati 		 * Convert to Minutes-Seconds-Frames.
3009d4dc16eSKrishna Kurapati 		 * Sector size is already set to 2048 bytes.
3019d4dc16eSKrishna Kurapati 		 */
30200a2430fSAndrzej Pietrasiewicz 		addr += 2*75;		/* Lead-in occupies 2 seconds */
30300a2430fSAndrzej Pietrasiewicz 		dest[3] = addr % 75;	/* Frames */
30400a2430fSAndrzej Pietrasiewicz 		addr /= 75;
30500a2430fSAndrzej Pietrasiewicz 		dest[2] = addr % 60;	/* Seconds */
30600a2430fSAndrzej Pietrasiewicz 		addr /= 60;
30700a2430fSAndrzej Pietrasiewicz 		dest[1] = addr;		/* Minutes */
30800a2430fSAndrzej Pietrasiewicz 		dest[0] = 0;		/* Reserved */
30900a2430fSAndrzej Pietrasiewicz 	} else {
31000a2430fSAndrzej Pietrasiewicz 		/* Absolute sector */
31100a2430fSAndrzej Pietrasiewicz 		put_unaligned_be32(addr, dest);
31200a2430fSAndrzej Pietrasiewicz 	}
31300a2430fSAndrzej Pietrasiewicz }
31400a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(store_cdrom_address);
31500a2430fSAndrzej Pietrasiewicz 
31600a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
31700a2430fSAndrzej Pietrasiewicz 
31800a2430fSAndrzej Pietrasiewicz 
fsg_show_ro(struct fsg_lun * curlun,char * buf)31900a2430fSAndrzej Pietrasiewicz ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
32000a2430fSAndrzej Pietrasiewicz {
32100a2430fSAndrzej Pietrasiewicz 	return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
32200a2430fSAndrzej Pietrasiewicz 				  ? curlun->ro
32300a2430fSAndrzej Pietrasiewicz 				  : curlun->initially_ro);
32400a2430fSAndrzej Pietrasiewicz }
32500a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_show_ro);
32600a2430fSAndrzej Pietrasiewicz 
fsg_show_nofua(struct fsg_lun * curlun,char * buf)32700a2430fSAndrzej Pietrasiewicz ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
32800a2430fSAndrzej Pietrasiewicz {
32900a2430fSAndrzej Pietrasiewicz 	return sprintf(buf, "%u\n", curlun->nofua);
33000a2430fSAndrzej Pietrasiewicz }
33100a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_show_nofua);
33200a2430fSAndrzej Pietrasiewicz 
fsg_show_file(struct fsg_lun * curlun,struct rw_semaphore * filesem,char * buf)33300a2430fSAndrzej Pietrasiewicz ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
33400a2430fSAndrzej Pietrasiewicz 		      char *buf)
33500a2430fSAndrzej Pietrasiewicz {
33600a2430fSAndrzej Pietrasiewicz 	char		*p;
33700a2430fSAndrzej Pietrasiewicz 	ssize_t		rc;
33800a2430fSAndrzej Pietrasiewicz 
33900a2430fSAndrzej Pietrasiewicz 	down_read(filesem);
34000a2430fSAndrzej Pietrasiewicz 	if (fsg_lun_is_open(curlun)) {	/* Get the complete pathname */
3419bf39ab2SMiklos Szeredi 		p = file_path(curlun->filp, buf, PAGE_SIZE - 1);
34200a2430fSAndrzej Pietrasiewicz 		if (IS_ERR(p))
34300a2430fSAndrzej Pietrasiewicz 			rc = PTR_ERR(p);
34400a2430fSAndrzej Pietrasiewicz 		else {
34500a2430fSAndrzej Pietrasiewicz 			rc = strlen(p);
34600a2430fSAndrzej Pietrasiewicz 			memmove(buf, p, rc);
34700a2430fSAndrzej Pietrasiewicz 			buf[rc] = '\n';		/* Add a newline */
34800a2430fSAndrzej Pietrasiewicz 			buf[++rc] = 0;
34900a2430fSAndrzej Pietrasiewicz 		}
35000a2430fSAndrzej Pietrasiewicz 	} else {				/* No file, return 0 bytes */
35100a2430fSAndrzej Pietrasiewicz 		*buf = 0;
35200a2430fSAndrzej Pietrasiewicz 		rc = 0;
35300a2430fSAndrzej Pietrasiewicz 	}
35400a2430fSAndrzej Pietrasiewicz 	up_read(filesem);
35500a2430fSAndrzej Pietrasiewicz 	return rc;
35600a2430fSAndrzej Pietrasiewicz }
35700a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_show_file);
35800a2430fSAndrzej Pietrasiewicz 
fsg_show_cdrom(struct fsg_lun * curlun,char * buf)35900a2430fSAndrzej Pietrasiewicz ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
36000a2430fSAndrzej Pietrasiewicz {
36100a2430fSAndrzej Pietrasiewicz 	return sprintf(buf, "%u\n", curlun->cdrom);
36200a2430fSAndrzej Pietrasiewicz }
36300a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_show_cdrom);
36400a2430fSAndrzej Pietrasiewicz 
fsg_show_removable(struct fsg_lun * curlun,char * buf)36500a2430fSAndrzej Pietrasiewicz ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
36600a2430fSAndrzej Pietrasiewicz {
36700a2430fSAndrzej Pietrasiewicz 	return sprintf(buf, "%u\n", curlun->removable);
36800a2430fSAndrzej Pietrasiewicz }
36900a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_show_removable);
37000a2430fSAndrzej Pietrasiewicz 
fsg_show_inquiry_string(struct fsg_lun * curlun,char * buf)3716ac47090SPhilipp Gesang ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf)
3726ac47090SPhilipp Gesang {
3736ac47090SPhilipp Gesang 	return sprintf(buf, "%s\n", curlun->inquiry_string);
3746ac47090SPhilipp Gesang }
3756ac47090SPhilipp Gesang EXPORT_SYMBOL_GPL(fsg_show_inquiry_string);
3766ac47090SPhilipp Gesang 
37700a2430fSAndrzej Pietrasiewicz /*
37800a2430fSAndrzej Pietrasiewicz  * The caller must hold fsg->filesem for reading when calling this function.
37900a2430fSAndrzej Pietrasiewicz  */
_fsg_store_ro(struct fsg_lun * curlun,bool ro)38000a2430fSAndrzej Pietrasiewicz static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
38100a2430fSAndrzej Pietrasiewicz {
38200a2430fSAndrzej Pietrasiewicz 	if (fsg_lun_is_open(curlun)) {
38300a2430fSAndrzej Pietrasiewicz 		LDBG(curlun, "read-only status change prevented\n");
38400a2430fSAndrzej Pietrasiewicz 		return -EBUSY;
38500a2430fSAndrzej Pietrasiewicz 	}
38600a2430fSAndrzej Pietrasiewicz 
38700a2430fSAndrzej Pietrasiewicz 	curlun->ro = ro;
38800a2430fSAndrzej Pietrasiewicz 	curlun->initially_ro = ro;
38900a2430fSAndrzej Pietrasiewicz 	LDBG(curlun, "read-only status set to %d\n", curlun->ro);
39000a2430fSAndrzej Pietrasiewicz 
39100a2430fSAndrzej Pietrasiewicz 	return 0;
39200a2430fSAndrzej Pietrasiewicz }
39300a2430fSAndrzej Pietrasiewicz 
fsg_store_ro(struct fsg_lun * curlun,struct rw_semaphore * filesem,const char * buf,size_t count)39400a2430fSAndrzej Pietrasiewicz ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
39500a2430fSAndrzej Pietrasiewicz 		     const char *buf, size_t count)
39600a2430fSAndrzej Pietrasiewicz {
39700a2430fSAndrzej Pietrasiewicz 	ssize_t		rc;
39800a2430fSAndrzej Pietrasiewicz 	bool		ro;
39900a2430fSAndrzej Pietrasiewicz 
400*a8bc8cc1SChristophe JAILLET 	rc = kstrtobool(buf, &ro);
40100a2430fSAndrzej Pietrasiewicz 	if (rc)
40200a2430fSAndrzej Pietrasiewicz 		return rc;
40300a2430fSAndrzej Pietrasiewicz 
40400a2430fSAndrzej Pietrasiewicz 	/*
40500a2430fSAndrzej Pietrasiewicz 	 * Allow the write-enable status to change only while the
40600a2430fSAndrzej Pietrasiewicz 	 * backing file is closed.
40700a2430fSAndrzej Pietrasiewicz 	 */
40800a2430fSAndrzej Pietrasiewicz 	down_read(filesem);
40900a2430fSAndrzej Pietrasiewicz 	rc = _fsg_store_ro(curlun, ro);
41000a2430fSAndrzej Pietrasiewicz 	if (!rc)
41100a2430fSAndrzej Pietrasiewicz 		rc = count;
41200a2430fSAndrzej Pietrasiewicz 	up_read(filesem);
41300a2430fSAndrzej Pietrasiewicz 
41400a2430fSAndrzej Pietrasiewicz 	return rc;
41500a2430fSAndrzej Pietrasiewicz }
41600a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_store_ro);
41700a2430fSAndrzej Pietrasiewicz 
fsg_store_nofua(struct fsg_lun * curlun,const char * buf,size_t count)41800a2430fSAndrzej Pietrasiewicz ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
41900a2430fSAndrzej Pietrasiewicz {
42000a2430fSAndrzej Pietrasiewicz 	bool		nofua;
42100a2430fSAndrzej Pietrasiewicz 	int		ret;
42200a2430fSAndrzej Pietrasiewicz 
423*a8bc8cc1SChristophe JAILLET 	ret = kstrtobool(buf, &nofua);
42400a2430fSAndrzej Pietrasiewicz 	if (ret)
42500a2430fSAndrzej Pietrasiewicz 		return ret;
42600a2430fSAndrzej Pietrasiewicz 
42700a2430fSAndrzej Pietrasiewicz 	/* Sync data when switching from async mode to sync */
42800a2430fSAndrzej Pietrasiewicz 	if (!nofua && curlun->nofua)
42900a2430fSAndrzej Pietrasiewicz 		fsg_lun_fsync_sub(curlun);
43000a2430fSAndrzej Pietrasiewicz 
43100a2430fSAndrzej Pietrasiewicz 	curlun->nofua = nofua;
43200a2430fSAndrzej Pietrasiewicz 
43300a2430fSAndrzej Pietrasiewicz 	return count;
43400a2430fSAndrzej Pietrasiewicz }
43500a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_store_nofua);
43600a2430fSAndrzej Pietrasiewicz 
fsg_store_file(struct fsg_lun * curlun,struct rw_semaphore * filesem,const char * buf,size_t count)43700a2430fSAndrzej Pietrasiewicz ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
43800a2430fSAndrzej Pietrasiewicz 		       const char *buf, size_t count)
43900a2430fSAndrzej Pietrasiewicz {
44000a2430fSAndrzej Pietrasiewicz 	int		rc = 0;
44100a2430fSAndrzej Pietrasiewicz 
44200a2430fSAndrzej Pietrasiewicz 	if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
44300a2430fSAndrzej Pietrasiewicz 		LDBG(curlun, "eject attempt prevented\n");
44400a2430fSAndrzej Pietrasiewicz 		return -EBUSY;				/* "Door is locked" */
44500a2430fSAndrzej Pietrasiewicz 	}
44600a2430fSAndrzej Pietrasiewicz 
44700a2430fSAndrzej Pietrasiewicz 	/* Remove a trailing newline */
44800a2430fSAndrzej Pietrasiewicz 	if (count > 0 && buf[count-1] == '\n')
44900a2430fSAndrzej Pietrasiewicz 		((char *) buf)[count-1] = 0;		/* Ugh! */
45000a2430fSAndrzej Pietrasiewicz 
45100a2430fSAndrzej Pietrasiewicz 	/* Load new medium */
45200a2430fSAndrzej Pietrasiewicz 	down_write(filesem);
45300a2430fSAndrzej Pietrasiewicz 	if (count > 0 && buf[0]) {
45400a2430fSAndrzej Pietrasiewicz 		/* fsg_lun_open() will close existing file if any. */
45500a2430fSAndrzej Pietrasiewicz 		rc = fsg_lun_open(curlun, buf);
45600a2430fSAndrzej Pietrasiewicz 		if (rc == 0)
45700a2430fSAndrzej Pietrasiewicz 			curlun->unit_attention_data =
45800a2430fSAndrzej Pietrasiewicz 					SS_NOT_READY_TO_READY_TRANSITION;
45900a2430fSAndrzej Pietrasiewicz 	} else if (fsg_lun_is_open(curlun)) {
46000a2430fSAndrzej Pietrasiewicz 		fsg_lun_close(curlun);
46100a2430fSAndrzej Pietrasiewicz 		curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
46200a2430fSAndrzej Pietrasiewicz 	}
46300a2430fSAndrzej Pietrasiewicz 	up_write(filesem);
46400a2430fSAndrzej Pietrasiewicz 	return (rc < 0 ? rc : count);
46500a2430fSAndrzej Pietrasiewicz }
46600a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_store_file);
46700a2430fSAndrzej Pietrasiewicz 
fsg_store_cdrom(struct fsg_lun * curlun,struct rw_semaphore * filesem,const char * buf,size_t count)46800a2430fSAndrzej Pietrasiewicz ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
46900a2430fSAndrzej Pietrasiewicz 			const char *buf, size_t count)
47000a2430fSAndrzej Pietrasiewicz {
47100a2430fSAndrzej Pietrasiewicz 	bool		cdrom;
47200a2430fSAndrzej Pietrasiewicz 	int		ret;
47300a2430fSAndrzej Pietrasiewicz 
474*a8bc8cc1SChristophe JAILLET 	ret = kstrtobool(buf, &cdrom);
47500a2430fSAndrzej Pietrasiewicz 	if (ret)
47600a2430fSAndrzej Pietrasiewicz 		return ret;
47700a2430fSAndrzej Pietrasiewicz 
47800a2430fSAndrzej Pietrasiewicz 	down_read(filesem);
47900a2430fSAndrzej Pietrasiewicz 	ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
48000a2430fSAndrzej Pietrasiewicz 
48100a2430fSAndrzej Pietrasiewicz 	if (!ret) {
48200a2430fSAndrzej Pietrasiewicz 		curlun->cdrom = cdrom;
48300a2430fSAndrzej Pietrasiewicz 		ret = count;
48400a2430fSAndrzej Pietrasiewicz 	}
48500a2430fSAndrzej Pietrasiewicz 	up_read(filesem);
48600a2430fSAndrzej Pietrasiewicz 
48700a2430fSAndrzej Pietrasiewicz 	return ret;
48800a2430fSAndrzej Pietrasiewicz }
48900a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_store_cdrom);
49000a2430fSAndrzej Pietrasiewicz 
fsg_store_removable(struct fsg_lun * curlun,const char * buf,size_t count)49100a2430fSAndrzej Pietrasiewicz ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
49200a2430fSAndrzej Pietrasiewicz 			    size_t count)
49300a2430fSAndrzej Pietrasiewicz {
49400a2430fSAndrzej Pietrasiewicz 	bool		removable;
49500a2430fSAndrzej Pietrasiewicz 	int		ret;
49600a2430fSAndrzej Pietrasiewicz 
497*a8bc8cc1SChristophe JAILLET 	ret = kstrtobool(buf, &removable);
49800a2430fSAndrzej Pietrasiewicz 	if (ret)
49900a2430fSAndrzej Pietrasiewicz 		return ret;
50000a2430fSAndrzej Pietrasiewicz 
50100a2430fSAndrzej Pietrasiewicz 	curlun->removable = removable;
50200a2430fSAndrzej Pietrasiewicz 
50300a2430fSAndrzej Pietrasiewicz 	return count;
50400a2430fSAndrzej Pietrasiewicz }
50500a2430fSAndrzej Pietrasiewicz EXPORT_SYMBOL_GPL(fsg_store_removable);
50600a2430fSAndrzej Pietrasiewicz 
fsg_store_inquiry_string(struct fsg_lun * curlun,const char * buf,size_t count)5076ac47090SPhilipp Gesang ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf,
5086ac47090SPhilipp Gesang 				 size_t count)
5096ac47090SPhilipp Gesang {
5106ac47090SPhilipp Gesang 	const size_t len = min(count, sizeof(curlun->inquiry_string));
5116ac47090SPhilipp Gesang 
5126ac47090SPhilipp Gesang 	if (len == 0 || buf[0] == '\n') {
5136ac47090SPhilipp Gesang 		curlun->inquiry_string[0] = 0;
5146ac47090SPhilipp Gesang 	} else {
5156ac47090SPhilipp Gesang 		snprintf(curlun->inquiry_string,
5166ac47090SPhilipp Gesang 			 sizeof(curlun->inquiry_string), "%-28s", buf);
5176ac47090SPhilipp Gesang 		if (curlun->inquiry_string[len-1] == '\n')
5186ac47090SPhilipp Gesang 			curlun->inquiry_string[len-1] = ' ';
5196ac47090SPhilipp Gesang 	}
5206ac47090SPhilipp Gesang 
5216ac47090SPhilipp Gesang 	return count;
5226ac47090SPhilipp Gesang }
5236ac47090SPhilipp Gesang EXPORT_SYMBOL_GPL(fsg_store_inquiry_string);
5246ac47090SPhilipp Gesang 
fsg_store_forced_eject(struct fsg_lun * curlun,struct rw_semaphore * filesem,const char * buf,size_t count)525421c8d9aSMaxim Devaev ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *filesem,
526421c8d9aSMaxim Devaev 			       const char *buf, size_t count)
527421c8d9aSMaxim Devaev {
528421c8d9aSMaxim Devaev 	int ret;
529421c8d9aSMaxim Devaev 
530421c8d9aSMaxim Devaev 	/*
531421c8d9aSMaxim Devaev 	 * Forcibly detach the backing file from the LUN
532421c8d9aSMaxim Devaev 	 * regardless of whether the host has allowed it.
533421c8d9aSMaxim Devaev 	 */
534421c8d9aSMaxim Devaev 	curlun->prevent_medium_removal = 0;
535421c8d9aSMaxim Devaev 	ret = fsg_store_file(curlun, filesem, "", 0);
536421c8d9aSMaxim Devaev 	return ret < 0 ? ret : count;
537421c8d9aSMaxim Devaev }
538421c8d9aSMaxim Devaev EXPORT_SYMBOL_GPL(fsg_store_forced_eject);
539421c8d9aSMaxim Devaev 
54000a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
541