mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-25 07:19:28 -05:00
2fd3bf0136
* sd over usb works at least up to intel + big sur, not yet on m1 + ventura * style
287 lines
8.1 KiB
C
287 lines
8.1 KiB
C
/*
|
|
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
|
* Copyright (C) 2023 Bernd Herzog
|
|
*
|
|
* This file is part of PortaPack.
|
|
*
|
|
* 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, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "scsi.h"
|
|
#include "diskio.h"
|
|
|
|
volatile bool usb_bulk_block_done = false;
|
|
|
|
void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) {
|
|
usb_bulk_block_done = true;
|
|
|
|
(void)user_data;
|
|
(void)bytes_transferred;
|
|
}
|
|
|
|
void usb_send_bulk(void* const data, const uint32_t maximum_length) {
|
|
usb_bulk_block_done = false;
|
|
|
|
usb_transfer_schedule_block(
|
|
&usb_endpoint_bulk_in,
|
|
data,
|
|
maximum_length,
|
|
usb_bulk_block_cb,
|
|
NULL);
|
|
|
|
while (!usb_bulk_block_done)
|
|
;
|
|
}
|
|
|
|
void usb_receive_bulk(void* const data, const uint32_t maximum_length) {
|
|
usb_bulk_block_done = false;
|
|
|
|
usb_transfer_schedule_block(
|
|
&usb_endpoint_bulk_out,
|
|
data,
|
|
maximum_length,
|
|
usb_bulk_block_cb,
|
|
NULL);
|
|
|
|
while (!usb_bulk_block_done)
|
|
;
|
|
}
|
|
|
|
void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) {
|
|
msd_csw_t csw = {
|
|
.signature = MSD_CSW_SIGNATURE,
|
|
.tag = msd_cbw_data->tag,
|
|
.data_residue = 0,
|
|
.status = status};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &csw, sizeof(msd_csw_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(msd_csw_t));
|
|
}
|
|
|
|
uint8_t handle_inquiry(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
scsi_inquiry_response_t ret = {
|
|
0x00, /* direct access block device */
|
|
0x80, /* removable */
|
|
0x00, // 0x04, /* SPC-2 */
|
|
0x00, // 0x02, /* response data format */
|
|
0x20, /* response has 0x20 + 4 bytes */
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
"Mayhem",
|
|
"Portapack MSD",
|
|
{'v', '1', '.', '6'}};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_inquiry_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_inquiry_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t handle_inquiry_serial_number(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
scsi_unit_serial_number_inquiry_response_t ret = {
|
|
.peripheral = 0x00,
|
|
.page_code = 0x80,
|
|
.reserved = 0,
|
|
.page_length = 0x08,
|
|
.serialNumber = "Mayhem"};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_unit_serial_number_inquiry_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_unit_serial_number_inquiry_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t read_format_capacities(msd_cbw_t* msd_cbw_data) {
|
|
uint16_t len = msd_cbw_data->cmd_data[7] << 8 | msd_cbw_data->cmd_data[8];
|
|
|
|
if (len != 0) {
|
|
size_t num_blocks = get_capacity();
|
|
|
|
scsi_read_format_capacities_response_t ret = {
|
|
.header = {0, 0, 0, 1 * 8 /* num_entries * 8 */},
|
|
.blocknum = {((num_blocks) >> 24) & 0xff, ((num_blocks) >> 16) & 0xff, ((num_blocks) >> 8) & 0xff, num_blocks & 0xff},
|
|
.blocklen = {0b10 /* formated */, 0, (512) >> 8, 0},
|
|
};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_read_format_capacities_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_read_format_capacities_response_t));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t read_capacity10(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
size_t num_blocks = get_capacity();
|
|
|
|
scsi_read_capacity10_response_t ret = {
|
|
.last_block_addr = cpu_to_be32(num_blocks - 1),
|
|
.block_size = cpu_to_be32(512)};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_read_capacity10_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_read_capacity10_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t request_sense(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
scsi_sense_response_t ret = {
|
|
.byte = {0x70, 0, SCSI_SENSE_KEY_GOOD, 0,
|
|
0, 0, 0, 8,
|
|
0, 0, 0, 0,
|
|
SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER, 0, 0,
|
|
0, 0}};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_sense_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_sense_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t mode_sense6(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
scsi_mode_sense6_response_t ret = {
|
|
.byte = {
|
|
sizeof(scsi_mode_sense6_response_t) - 1,
|
|
0,
|
|
0,
|
|
0}};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_mode_sense6_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_mode_sense6_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t mode_sense10(msd_cbw_t* msd_cbw_data) {
|
|
(void)msd_cbw_data;
|
|
|
|
scsi_mode_sense10_response_t ret = {
|
|
.byte = {
|
|
cpu_to_be16(sizeof(scsi_mode_sense6_response_t) - 2),
|
|
0,
|
|
0,
|
|
0}};
|
|
|
|
memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_mode_sense10_response_t));
|
|
usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_mode_sense10_response_t));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static data_request_t decode_data_request(const uint8_t* cmd) {
|
|
data_request_t req;
|
|
uint32_t lba;
|
|
uint16_t blk;
|
|
|
|
memcpy(&lba, &cmd[2], sizeof(lba));
|
|
memcpy(&blk, &cmd[7], sizeof(blk));
|
|
|
|
req.first_lba = be32_to_cpu(lba);
|
|
req.blk_cnt = be16_to_cpu(blk);
|
|
|
|
return req;
|
|
}
|
|
|
|
uint8_t data_read10(msd_cbw_t* msd_cbw_data) {
|
|
data_request_t req = decode_data_request(msd_cbw_data->cmd_data);
|
|
|
|
for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) {
|
|
read_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */);
|
|
usb_send_bulk(&usb_bulk_buffer[0], 512);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint8_t data_write10(msd_cbw_t* msd_cbw_data) {
|
|
data_request_t req = decode_data_request(msd_cbw_data->cmd_data);
|
|
|
|
for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) {
|
|
usb_receive_bulk(&usb_bulk_buffer[0], 512);
|
|
write_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void scsi_command(msd_cbw_t* msd_cbw_data) {
|
|
uint8_t status = 1;
|
|
|
|
switch (msd_cbw_data->cmd_data[0]) {
|
|
case SCSI_CMD_INQUIRY:
|
|
if ((msd_cbw_data->cmd_data[1] & 0b1) && msd_cbw_data->cmd_data[2] == 0x80) {
|
|
status = handle_inquiry_serial_number(msd_cbw_data);
|
|
} else if ((msd_cbw_data->cmd_data[1] & 0b11) || msd_cbw_data->cmd_data[2] != 0) {
|
|
status = 1;
|
|
} else {
|
|
status = handle_inquiry(msd_cbw_data);
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_CMD_REQUEST_SENSE:
|
|
status = request_sense(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_READ_CAPACITY_10:
|
|
status = read_capacity10(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_READ_10:
|
|
status = data_read10(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_WRITE_10:
|
|
status = data_write10(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_TEST_UNIT_READY:
|
|
status = 0;
|
|
break;
|
|
|
|
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
|
status = 0;
|
|
break;
|
|
|
|
case SCSI_CMD_MODE_SENSE_6:
|
|
status = mode_sense6(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_MODE_SENSE_10:
|
|
status = mode_sense10(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_READ_FORMAT_CAPACITIES:
|
|
status = read_format_capacities(msd_cbw_data);
|
|
break;
|
|
|
|
case SCSI_CMD_VERIFY_10:
|
|
status = 0;
|
|
break;
|
|
}
|
|
|
|
usb_send_csw(msd_cbw_data, status);
|
|
} |