summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/storage_common.c
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-07 23:49:35 (GMT)
commit62b8c978ee6b8d135d9e7953221de58000dba986 (patch)
tree683b04b2e627f6710c22c151b23c8cc9a165315e /drivers/usb/gadget/storage_common.c
parent78fd82238d0e5716578c326404184a27ba67fd6e (diff)
downloadlinux-fsl-qoriq-62b8c978ee6b8d135d9e7953221de58000dba986.tar.xz
Rewind v3.13-rc3+ (78fd82238d0e5716) to v3.12
Diffstat (limited to 'drivers/usb/gadget/storage_common.c')
-rw-r--r--drivers/usb/gadget/storage_common.c430
1 files changed, 301 insertions, 129 deletions
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index ec20a1f..08a1a32 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -23,17 +23,242 @@
* 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"
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
+
+
+#ifdef DUMP_MSGS
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { \
+ if (length < 512) { \
+ DBG(fsg, "%s, length %u:\n", label, length); \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, length, 0); \
+ } \
+} while (0)
+
+# define dump_cdb(fsg) do { } while (0)
+
+#else
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+# ifdef VERBOSE_DEBUG
+
+# define dump_cdb(fsg) \
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
+ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
+
+# else
+
+# define dump_cdb(fsg) do { } while (0)
+
+# endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/*-------------------------------------------------------------------------*/
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE 16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_lun {
+ struct file *filp;
+ loff_t file_length;
+ loff_t num_sectors;
+
+ unsigned int initially_ro:1;
+ unsigned int ro:1;
+ unsigned int removable:1;
+ unsigned int cdrom:1;
+ unsigned int prevent_medium_removal:1;
+ unsigned int registered:1;
+ unsigned int info_valid:1;
+ unsigned int nofua:1;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+
+ unsigned int blkbits; /* Bits of logical block size of bound block device */
+ unsigned int blksize; /* logical block size of bound block device */
+ struct device dev;
+};
+
+static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
+{
+ return curlun->filp != NULL;
+}
+
+static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+ return container_of(dev, struct fsg_lun, dev);
+}
+
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE 256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
+MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(void)
+{
+ if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ return 0;
+ pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+ fsg_num_buffers, 2 ,4);
+ return -EINVAL;
+}
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+enum fsg_buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+ void *buf;
+ enum fsg_buffer_state state;
+ struct fsg_buffhd *next;
+
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
+ * better, if we don't submit any short bulk-out read requests.
+ * So we will record the intended request length here.
+ */
+ unsigned int bulk_out_intended_length;
+
+ struct usb_request *inreq;
+ int inreq_busy;
+ struct usb_request *outreq;
+ int outreq_busy;
+};
+
+enum fsg_state {
+ /* This one isn't used anywhere */
+ FSG_STATE_COMMAND_PHASE = -10,
+ FSG_STATE_DATA_PHASE,
+ FSG_STATE_STATUS_PHASE,
+
+ FSG_STATE_IDLE = 0,
+ FSG_STATE_ABORT_BULK_OUT,
+ FSG_STATE_RESET,
+ FSG_STATE_INTERFACE_CHANGE,
+ FSG_STATE_CONFIG_CHANGE,
+ FSG_STATE_DISCONNECT,
+ FSG_STATE_EXIT,
+ FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+enum {
+ FSG_STRING_INTERFACE
+};
+
/* There is only one interface. */
-struct usb_interface_descriptor fsg_intf_desc = {
+static struct usb_interface_descriptor
+fsg_intf_desc = {
.bLength = sizeof fsg_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
@@ -43,14 +268,14 @@ struct usb_interface_descriptor fsg_intf_desc = {
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
.iInterface = FSG_STRING_INTERFACE,
};
-EXPORT_SYMBOL(fsg_intf_desc);
/*
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
* interrupt-in.
*/
-struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -58,9 +283,9 @@ struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
-EXPORT_SYMBOL(fsg_fs_bulk_in_desc);
-struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -68,15 +293,13 @@ struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
-EXPORT_SYMBOL(fsg_fs_bulk_out_desc);
-struct usb_descriptor_header *fsg_fs_function[] = {
+static 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(fsg_fs_function);
/*
@@ -87,7 +310,8 @@ EXPORT_SYMBOL(fsg_fs_function);
* and a "device qualifier" ... plus more construction options
* for the configuration descriptor.
*/
-struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -95,9 +319,9 @@ struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
-EXPORT_SYMBOL(fsg_hs_bulk_in_desc);
-struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -106,18 +330,17 @@ struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 1, /* NAK every 1 uframe */
};
-EXPORT_SYMBOL(fsg_hs_bulk_out_desc);
-struct usb_descriptor_header *fsg_hs_function[] = {
+static 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(fsg_hs_function);
-struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -125,17 +348,16 @@ struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-EXPORT_SYMBOL(fsg_ss_bulk_in_desc);
-struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+static 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(fsg_ss_bulk_in_comp_desc);
-struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@@ -143,17 +365,15 @@ struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
-EXPORT_SYMBOL(fsg_ss_bulk_out_desc);
-struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+static 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(fsg_ss_bulk_out_comp_desc);
-struct usb_descriptor_header *fsg_ss_function[] = {
+static 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,
@@ -161,7 +381,17 @@ struct usb_descriptor_header *fsg_ss_function[] = {
(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
NULL,
};
-EXPORT_SYMBOL(fsg_ss_function);
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
/*-------------------------------------------------------------------------*/
@@ -171,7 +401,7 @@ EXPORT_SYMBOL(fsg_ss_function);
* the caller must own fsg->filesem for writing.
*/
-void fsg_lun_close(struct fsg_lun *curlun)
+static void fsg_lun_close(struct fsg_lun *curlun)
{
if (curlun->filp) {
LDBG(curlun, "close backing file\n");
@@ -179,9 +409,9 @@ void fsg_lun_close(struct fsg_lun *curlun)
curlun->filp = NULL;
}
}
-EXPORT_SYMBOL(fsg_lun_close);
-int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
int ro;
struct file *filp = NULL;
@@ -278,7 +508,6 @@ out:
fput(filp);
return rc;
}
-EXPORT_SYMBOL(fsg_lun_open);
/*-------------------------------------------------------------------------*/
@@ -287,7 +516,7 @@ EXPORT_SYMBOL(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)
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
@@ -295,9 +524,8 @@ int fsg_lun_fsync_sub(struct fsg_lun *curlun)
return 0;
return vfs_fsync(filp, 1);
}
-EXPORT_SYMBOL(fsg_lun_fsync_sub);
-void store_cdrom_address(u8 *dest, int msf, u32 addr)
+static void store_cdrom_address(u8 *dest, int msf, u32 addr)
{
if (msf) {
/* Convert to Minutes-Seconds-Frames */
@@ -314,28 +542,34 @@ void store_cdrom_address(u8 *dest, int msf, u32 addr)
put_unaligned_be32(addr, dest);
}
}
-EXPORT_SYMBOL(store_cdrom_address);
+
/*-------------------------------------------------------------------------*/
-ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
+static ssize_t ro_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
? curlun->ro
: curlun->initially_ro);
}
-EXPORT_SYMBOL(fsg_show_ro);
-ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
+static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
return sprintf(buf, "%u\n", curlun->nofua);
}
-EXPORT_SYMBOL(fsg_show_nofua);
-ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
- char *buf)
+static ssize_t file_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
char *p;
ssize_t rc;
@@ -357,44 +591,17 @@ ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
up_read(filesem);
return rc;
}
-EXPORT_SYMBOL(fsg_show_file);
-
-ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
-{
- return sprintf(buf, "%u\n", curlun->cdrom);
-}
-EXPORT_SYMBOL(fsg_show_cdrom);
-
-ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
-{
- return sprintf(buf, "%u\n", curlun->removable);
-}
-EXPORT_SYMBOL(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;
-}
-
-ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
- const char *buf, size_t count)
+static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
ssize_t rc;
- bool ro;
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ unsigned ro;
- rc = strtobool(buf, &ro);
+ rc = kstrtouint(buf, 2, &ro);
if (rc)
return rc;
@@ -403,21 +610,27 @@ ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
* backing file is closed.
*/
down_read(filesem);
- rc = _fsg_store_ro(curlun, ro);
- if (!rc)
+ if (fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "read-only status change prevented\n");
+ rc = -EBUSY;
+ } else {
+ curlun->ro = ro;
+ curlun->initially_ro = ro;
+ LDBG(curlun, "read-only status set to %d\n", curlun->ro);
rc = count;
+ }
up_read(filesem);
-
return rc;
}
-EXPORT_SYMBOL(fsg_store_ro);
-ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
+static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
- bool nofua;
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ unsigned nofua;
int ret;
- ret = strtobool(buf, &nofua);
+ ret = kstrtouint(buf, 2, &nofua);
if (ret)
return ret;
@@ -429,11 +642,12 @@ ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
return count;
}
-EXPORT_SYMBOL(fsg_store_nofua);
-ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
- const char *buf, size_t count)
+static ssize_t file_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
int rc = 0;
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
@@ -460,45 +674,3 @@ ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
up_write(filesem);
return (rc < 0 ? rc : count);
}
-EXPORT_SYMBOL(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;
-}
-EXPORT_SYMBOL(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;
-}
-EXPORT_SYMBOL(fsg_store_removable);
-
-MODULE_LICENSE("GPL");