cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/gadget/function/storage_common.c

/*
 * storage_common.c -- Common definitions for mass storage functionality
 *
 * Copyright (C) 2003-2008 Alan Stern
 * Copyeight (C) 2009 Samsung Electronics
 * Author: Michal Nazarewicz (mina86@mina86.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

/*
 * This file requires the following identifiers used in USB strings to
 * be defined (each of type pointer to char):
 *  - fsg_string_interface    -- name of the interface
 */

/*
 * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
 * sets the number of pipeline buffers (length of the fsg_buffhd array).
 * The valid range of num_buffers is: num >= 2 && num <= 4.
 */

#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/usb/composite.h>

#include "storage_common.h"

/* There is only one interface. */


struct usb_interface_descriptor fsg_intf_desc = {
	.bLength =		sizeof fsg_intf_desc,
	.bDescriptorType =	USB_DT_INTERFACE,

	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */
	.bInterfaceClass =	USB_CLASS_MASS_STORAGE,
	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */
	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */
	.iInterface =		FSG_STRING_INTERFACE,
};

EXPORT_SYMBOL_GPL(fsg_intf_desc);

/*
 * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
 * interrupt-in.
 */


struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_IN,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	/* wMaxPacketSize set by autoconfiguration */
};

EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);


struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	.bEndpointAddress =	USB_DIR_OUT,
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	/* wMaxPacketSize set by autoconfiguration */
};

EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);


struct usb_descriptor_header *fsg_fs_function[] = {
	(struct usb_descriptor_header *) &fsg_intf_desc,
	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
	NULL,
};

EXPORT_SYMBOL_GPL(fsg_fs_function);


/*
 * USB 2.0 devices need to expose both high speed and full speed
 * descriptors, unless they only run at full speed.
 *
 * That means alternate endpoint descriptors (bigger packets).
 */

struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
};

EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);


struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(512),
	.bInterval =		1,	/* NAK every 1 uframe */
};

EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);



struct usb_descriptor_header *fsg_hs_function[] = {
	(struct usb_descriptor_header *) &fsg_intf_desc,
	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
	NULL,
};

EXPORT_SYMBOL_GPL(fsg_hs_function);


struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(1024),
};

EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);


struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,

	/*.bMaxBurst =          DYNAMIC, */
};

EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);


struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
	.bLength =		USB_DT_ENDPOINT_SIZE,
	.bDescriptorType =	USB_DT_ENDPOINT,

	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize =	cpu_to_le16(1024),
};

EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);


struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
	.bLength =		sizeof(fsg_ss_bulk_in_comp_desc),
	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,

	/*.bMaxBurst =          DYNAMIC, */
};

EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);


struct usb_descriptor_header *fsg_ss_function[] = {
	(struct usb_descriptor_header *) &fsg_intf_desc,
	(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
	(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
	(struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
	(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
	NULL,
};

EXPORT_SYMBOL_GPL(fsg_ss_function);


 /*-------------------------------------------------------------------------*/

/*
 * If the next two routines are called while the gadget is registered,
 * the caller must own fsg->filesem for writing.
 */


void fsg_lun_close(struct fsg_lun *curlun) { if (curlun->filp) { LDBG(curlun, "close backing file\n"); fput(curlun->filp); curlun->filp = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz38100.00%1100.00%
Total38100.00%1100.00%

EXPORT_SYMBOL_GPL(fsg_lun_close);
int fsg_lun_open(struct fsg_lun *curlun, const char *filename) { int ro; struct file *filp = NULL; int rc = -EINVAL; struct inode *inode = NULL; loff_t size; loff_t num_sectors; loff_t min_sectors; unsigned int blkbits; unsigned int blksize; /* R/W if we can, R/O if we must */ ro = curlun->initially_ro; if (!ro) { filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES) ro = 1; } if (ro) filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); if (IS_ERR(filp)) { LINFO(curlun, "unable to open backing file: %s\n", filename); return PTR_ERR(filp); } if (!(filp->f_mode & FMODE_WRITE)) ro = 1; inode = file_inode(filp); if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { LINFO(curlun, "invalid file type: %s\n", filename); goto out; } /* * If we can't read the file, it's no good. * If we can't write the file, use it read-only. */ if (!(filp->f_mode & FMODE_CAN_READ)) { LINFO(curlun, "file not readable: %s\n", filename); goto out; } if (!(filp->f_mode & FMODE_CAN_WRITE)) ro = 1; size = i_size_read(inode->i_mapping->host); if (size < 0) { LINFO(curlun, "unable to find file size: %s\n", filename); rc = (int) size; goto out; } if (curlun->cdrom) { blksize = 2048; blkbits = 11; } else if (inode->i_bdev) { blksize = bdev_logical_block_size(inode->i_bdev); blkbits = blksize_bits(blksize); } else { blksize = 512; blkbits = 9; } num_sectors = size >> blkbits; /* File size in logic-block-size blocks */ min_sectors = 1; if (curlun->cdrom) { min_sectors = 300; /* Smallest track is 300 frames */ if (num_sectors >= 256*60*75) { num_sectors = 256*60*75 - 1; LINFO(curlun, "file too big: %s\n", filename); LINFO(curlun, "using only first %d blocks\n", (int) num_sectors); } } if (num_sectors < min_sectors) { LINFO(curlun, "file too small: %s\n", filename); rc = -ETOOSMALL; goto out; } if (fsg_lun_is_open(curlun)) fsg_lun_close(curlun); curlun->blksize = blksize; curlun->blkbits = blkbits; curlun->ro = ro; curlun->filp = filp; curlun->file_length = size; curlun->num_sectors = num_sectors; LDBG(curlun, "open backing file: %s\n", filename); return 0; out: fput(filp); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz41383.60%654.55%
peiyu lipeiyu li5410.93%19.09%
tejun heotejun heo153.04%19.09%
al viroal viro122.43%327.27%
Total494100.00%11100.00%

EXPORT_SYMBOL_GPL(fsg_lun_open); /*-------------------------------------------------------------------------*/ /* * Sync the file data, don't bother with the metadata. * This code was copied from fs/buffer.c:sys_fdatasync(). */
int fsg_lun_fsync_sub(struct fsg_lun *curlun) { struct file *filp = curlun->filp; if (curlun->ro || !filp) return 0; return vfs_fsync(filp, 1); }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz39100.00%2100.00%
Total39100.00%2100.00%

EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
void store_cdrom_address(u8 *dest, int msf, u32 addr) { if (msf) { /* Convert to Minutes-Seconds-Frames */ addr >>= 2; /* Convert to 2048-byte frames */ addr += 2*75; /* Lead-in occupies 2 seconds */ dest[3] = addr % 75; /* Frames */ addr /= 75; dest[2] = addr % 60; /* Seconds */ addr /= 60; dest[1] = addr; /* Minutes */ dest[0] = 0; /* Reserved */ } else { /* Absolute sector */ put_unaligned_be32(addr, dest); } }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz89100.00%1100.00%
Total89100.00%1100.00%

EXPORT_SYMBOL_GPL(store_cdrom_address); /*-------------------------------------------------------------------------*/
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) { return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) ? curlun->ro : curlun->initially_ro); }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz3291.43%133.33%
andrzej pietrasiewiczandrzej pietrasiewicz38.57%266.67%
Total35100.00%3100.00%

EXPORT_SYMBOL_GPL(fsg_show_ro);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) { return sprintf(buf, "%u\n", curlun->nofua); }

Contributors

PersonTokensPropCommitsCommitProp
andy shevchenkoandy shevchenko2388.46%133.33%
andrzej pietrasiewiczandrzej pietrasiewicz311.54%266.67%
Total26100.00%3100.00%

EXPORT_SYMBOL_GPL(fsg_show_nofua);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, char *buf) { char *p; ssize_t rc; down_read(filesem); if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ p = file_path(curlun->filp, buf, PAGE_SIZE - 1); if (IS_ERR(p)) rc = PTR_ERR(p); else { rc = strlen(p); memmove(buf, p, rc); buf[rc] = '\n'; /* Add a newline */ buf[++rc] = 0; } } else { /* No file, return 0 bytes */ *buf = 0; rc = 0; } up_read(filesem); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz12095.24%240.00%
andrzej pietrasiewiczandrzej pietrasiewicz53.97%240.00%
miklos szeredimiklos szeredi10.79%120.00%
Total126100.00%5100.00%

EXPORT_SYMBOL_GPL(fsg_show_file);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) { return sprintf(buf, "%u\n", curlun->cdrom); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(fsg_show_cdrom);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) { return sprintf(buf, "%u\n", curlun->removable); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz26100.00%1100.00%
Total26100.00%1100.00%

EXPORT_SYMBOL_GPL(fsg_show_removable); /* * The caller must hold fsg->filesem for reading when calling this function. */
static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) { if (fsg_lun_is_open(curlun)) { LDBG(curlun, "read-only status change prevented\n"); return -EBUSY; } curlun->ro = ro; curlun->initially_ro = ro; LDBG(curlun, "read-only status set to %d\n", curlun->ro); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz60100.00%1100.00%
Total60100.00%1100.00%


ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count) { ssize_t rc; bool ro; rc = strtobool(buf, &ro); if (rc) return rc; /* * Allow the write-enable status to change only while the * backing file is closed. */ down_read(filesem); rc = _fsg_store_ro(curlun, ro); if (!rc) rc = count; up_read(filesem); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz6380.77%444.44%
andrzej pietrasiewiczandrzej pietrasiewicz1316.67%444.44%
andy shevchenkoandy shevchenko22.56%111.11%
Total78100.00%9100.00%

EXPORT_SYMBOL_GPL(fsg_store_ro);
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) { bool nofua; int ret; ret = strtobool(buf, &nofua); if (ret) return ret; /* Sync data when switching from async mode to sync */ if (!nofua && curlun->nofua) fsg_lun_fsync_sub(curlun); curlun->nofua = nofua; return count; }

Contributors

PersonTokensPropCommitsCommitProp
andy shevchenkoandy shevchenko5076.92%120.00%
michal nazarewiczmichal nazarewicz1015.38%120.00%
andrzej pietrasiewiczandrzej pietrasiewicz57.69%360.00%
Total65100.00%5100.00%

EXPORT_SYMBOL_GPL(fsg_store_nofua);
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count) { int rc = 0; if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { LDBG(curlun, "eject attempt prevented\n"); return -EBUSY; /* "Door is locked" */ } /* Remove a trailing newline */ if (count > 0 && buf[count-1] == '\n') ((char *) buf)[count-1] = 0; /* Ugh! */ /* Load new medium */ down_write(filesem); if (count > 0 && buf[0]) { /* fsg_lun_open() will close existing file if any. */ rc = fsg_lun_open(curlun, buf); if (rc == 0) curlun->unit_attention_data = SS_NOT_READY_TO_READY_TRANSITION; } else if (fsg_lun_is_open(curlun)) { fsg_lun_close(curlun); curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; } up_write(filesem); return (rc < 0 ? rc : count); }

Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz15896.93%360.00%
andrzej pietrasiewiczandrzej pietrasiewicz53.07%240.00%
Total163100.00%5100.00%

EXPORT_SYMBOL_GPL(fsg_store_file);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, const char *buf, size_t count) { bool cdrom; int ret; ret = strtobool(buf, &cdrom); if (ret) return ret; down_read(filesem); ret = cdrom ? _fsg_store_ro(curlun, true) : 0; if (!ret) { curlun->cdrom = cdrom; ret = count; } up_read(filesem); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz89100.00%3100.00%
Total89100.00%3100.00%

EXPORT_SYMBOL_GPL(fsg_store_cdrom);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, size_t count) { bool removable; int ret; ret = strtobool(buf, &removable); if (ret) return ret; curlun->removable = removable; return count; }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz50100.00%2100.00%
Total50100.00%2100.00%

EXPORT_SYMBOL_GPL(fsg_store_removable); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
michal nazarewiczmichal nazarewicz121361.98%1343.33%
andrzej pietrasiewiczandrzej pietrasiewicz40720.80%516.67%
felipe balbifelipe balbi1789.10%26.67%
andy shevchenkoandy shevchenko753.83%26.67%
peiyu lipeiyu li542.76%13.33%
tejun heotejun heo150.77%13.33%
al viroal viro120.61%310.00%
krzysztof opasiakkrzysztof opasiak10.05%13.33%
miklos szeredimiklos szeredi10.05%13.33%
per forlinper forlin10.05%13.33%
Total1957100.00%30100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}