summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorrafa_99 <raroma09@gmail.com>2022-02-22 22:30:24 +0000
committerRafael Marçalo <raroma09@gmail.com>2024-02-27 03:55:34 +0000
commit2db887fc819fe8b95feae161a83ff5535e276ef0 (patch)
tree63f2ea5988418a64e3e3d124e1fa259ce99ce60a /drivers/usb
parent2e69f2ae550dcfbde1ffb6daa11492762b122191 (diff)
Drivedroid Patch
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c235
1 files changed, 233 insertions, 2 deletions
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 01e25ae0fb25..0cc12a343d60 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -226,7 +226,7 @@
/*------------------------------------------------------------------------*/
-
+#define PAGE_CACHE_SIZE PAGE_SIZE
#define FSG_DRIVER_DESC "Mass Storage Function"
#define FSG_DRIVER_VERSION "2009/09/11"
@@ -1233,6 +1233,219 @@ static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
return 8;
}
+static void _lba_to_msf(u8 *buf, int lba)
+{
+ lba += 150;
+ buf[0] = (lba / 75) / 60;
+ buf[1] = (lba / 75) % 60;
+ buf[2] = lba % 75;
+}
+static int _read_toc_raw(struct fsg_common *common,
+ struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+ u8 *q;
+ int len;
+ int msf = common->cmnd[1] & 0x02;
+ q = buf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa0; /* lead-in */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* first track */
+ *q++ = 0x00; /* disk type */
+ *q++ = 0x00;
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa1;
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ *q++ = 0;
+ *q++ = 1; /* last track */
+ *q++ = 0x00;
+ *q++ = 0x00;
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* data track */
+ *q++ = 0; /* track number */
+ *q++ = 0xa2; /* lead-out */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0; /* reserved */
+ _lba_to_msf(q, curlun->num_sectors);
+ q += 3;
+ } else {
+ put_unaligned_be32(curlun->num_sectors, q);
+ q += 4;
+ }
+ *q++ = 1; /* session number */
+ *q++ = 0x14; /* ADR, control */
+ *q++ = 0; /* track number */
+ *q++ = 1; /* point */
+ *q++ = 0; /* min */
+ *q++ = 0; /* sec */
+ *q++ = 0; /* frame */
+ if (msf) {
+ *q++ = 0;
+ _lba_to_msf(q, 0);
+ q += 3;
+ } else {
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ *q++ = 0;
+ }
+ len = q - buf;
+ put_unaligned_be16(len - 2, buf);
+ return len;
+}
+static void cd_data_to_raw(u8 *buf, int lba)
+{
+ /* sync bytes */
+ buf[0] = 0x00;
+ memset(buf + 1, 0xff, 10);
+ buf[11] = 0x00;
+ buf += 12;
+ /* MSF */
+ _lba_to_msf(buf, lba);
+ buf[3] = 0x01; /* mode 1 data */
+ buf += 4;
+ /* data */
+ buf += 2048;
+ /* XXX: ECC not computed */
+ memset(buf, 0, 288);
+}
+static int do_read_cd(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 lba;
+ u32 amount_left;
+ u32 nb_sectors, transfer_request;
+ loff_t file_offset, file_offset_tmp;
+ unsigned int amount;
+ unsigned int partial_page;
+ ssize_t nread;
+ nb_sectors = (common->cmnd[6] << 16) |
+ (common->cmnd[7] << 8) | common->cmnd[8];
+ lba = get_unaligned_be32(&common->cmnd[2]);
+ if (nb_sectors == 0)
+ return 0;
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ transfer_request = common->cmnd[9];
+ if ((transfer_request & 0xf8) == 0xf8) {
+ file_offset = ((loff_t) lba) << 11;
+ /* read all data - 2352 byte */
+ amount_left = 2352;
+ } else {
+ file_offset = ((loff_t) lba) << 9;
+ /* Carry out the file reads */
+ amount_left = common->data_size_from_cmnd;
+ }
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply */
+ for (;;) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ * Finally, if we're not at a page boundary, don't read past
+ * the next page.
+ * If this means reading 0 then we were asked to read past
+ * the end of file.
+ */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->file_length - file_offset);
+ partial_page = file_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount, (unsigned int) PAGE_CACHE_SIZE -
+ partial_page);
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common, true);
+ if (rc)
+ return rc;
+ }
+ /*
+ * If we were asked to read past the end of file,
+ * end with an empty buffer.
+ */
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ if ((transfer_request & 0xf8) == 0xf8) {
+ nread = vfs_read(curlun->filp,
+ ((char __user *)bh->buf)+16,
+ amount, &file_offset_tmp);
+ } else {
+ nread = vfs_read(curlun->filp,
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
+ }
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (signal_pending(current))
+ return -EINTR;
+ if (nread < 0) {
+ LDBG(curlun, "error in file read: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file read: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511); /* Round down to a block */
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ common->residue -= nread;
+ bh->inreq->length = nread;
+ bh->state = BUF_STATE_FULL;
+ /* If an error occurred, report it and its position */
+ if (nread < amount) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
+ break;
+ }
+ if (amount_left == 0)
+ break; /* No more left to read */
+ /* Send this buffer and go read some more */
+ if (!start_in_transfer(common, bh))
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ }
+ if ((transfer_request & 0xf8) == 0xf8)
+ cd_data_to_raw(bh->buf, lba);
+ return -EIO; /* No default reply */
+}
+
static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
{
struct fsg_lun *curlun = common->curlun;
@@ -1240,12 +1453,17 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
int start_track = common->cmnd[6];
u8 *buf = (u8 *)bh->buf;
+ int format = (common->cmnd[9] & 0xC0) >> 6;
+
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
+ if (format == 2)
+ return _read_toc_raw(common, bh);
+
memset(buf, 0, 20);
buf[1] = (20-2); /* TOC data length */
buf[2] = 1; /* First track number */
@@ -1992,12 +2210,23 @@ static int do_scsi_command(struct fsg_common *common)
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]);
reply = check_command(common, 10, DATA_DIR_TO_HOST,
- (7<<6) | (1<<1), 1,
+ (0xf<<6) | (1<<1), 1,
"READ TOC");
if (reply == 0)
reply = do_read_toc(common, bh);
break;
+ case READ_CD:
+ common->data_size_from_cmnd = ((common->cmnd[6] << 16)
+ | (common->cmnd[7] << 8)
+ | (common->cmnd[8])) << 9;
+ reply = check_command(common, 12, DATA_DIR_TO_HOST,
+ (0xf<<2) | (7<<7), 1,
+ "READ CD");
+ if (reply == 0)
+ reply = do_read_cd(common);
+ break;
+
case READ_FORMAT_CAPACITIES:
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]);
@@ -3313,6 +3542,8 @@ static struct config_group *fsg_lun_make(struct config_group *group,
memset(&config, 0, sizeof(config));
config.removable = true;
+ config.cdrom = true;
+ config.ro = true;
ret = fsg_common_create_lun(fsg_opts->common, &config, num, name,
(const char **)&group->cg_item.ci_name);