Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

drivers/usb: Add Drivedroid Support #9

Open
wants to merge 1 commit into
base: eleven
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 235 additions & 3 deletions drivers/usb/gadget/function/f_mass_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@


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

#define PAGE_CACHE_SIZE PAGE_SIZE
#define FSG_DRIVER_DESC "Mass Storage Function"
#define FSG_DRIVER_VERSION "2009/09/11"

Expand Down Expand Up @@ -1234,19 +1234,238 @@ 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;
int msf = common->cmnd[1] & 0x02;
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 */
Expand Down Expand Up @@ -1993,12 +2212,24 @@ 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]);
Expand Down Expand Up @@ -3315,7 +3546,6 @@ static struct config_group *fsg_lun_make(struct config_group *group,
fsg_opts = to_fsg_opts(&group->cg_item);
if (num >= FSG_MAX_LUNS)
return ERR_PTR(-ERANGE);
num = array_index_nospec(num, FSG_MAX_LUNS);

mutex_lock(&fsg_opts->lock);
if (fsg_opts->refcnt || fsg_opts->common->luns[num]) {
Expand All @@ -3331,6 +3561,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);
Expand Down
1 change: 1 addition & 0 deletions include/scsi/scsi_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#define WRITE_SAME 0x41
#define UNMAP 0x42
#define READ_TOC 0x43
#define READ_CD 0xbe
#define READ_HEADER 0x44
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
#define LOG_SELECT 0x4c
Expand Down