genode/repos/os/src/drivers/usb_block/scsi.h

428 lines
9.8 KiB
C++

/*
* \brief SCSI Block Commands
* \author Josef Soentgen
* \date 2016-02-08
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _SCSI_H_
#define _SCSI_H_
#include <base/stdint.h>
#include <util/endian.h>
#include <util/mmio.h>
namespace Scsi {
using namespace Genode;
uint16_t be16(uint16_t val);
uint32_t be32(uint32_t val);
uint64_t be64(uint64_t val);
/******************
* SCSI commands **
******************/
enum Opcode {
TEST_UNIT_READY = 0x00,
REQUEST_SENSE = 0x03,
INQUIRY = 0x12,
START_STOP = 0x1B,
READ_CAPACITY_10 = 0x25,
READ_10 = 0x28,
WRITE_10 = 0x2a,
READ_16 = 0x88,
WRITE_16 = 0x8a,
READ_CAPACITY_16 = 0x9e,
};
struct Inquiry_response;
struct Request_sense_response;
struct Capacity_response_10;
struct Capacity_response_16;
struct Cmd_6;
struct Test_unit_ready;
struct Request_sense;
struct Inquiry;
struct Start_stop;
struct Cmd_10;
struct Read_capacity_10;
struct Io_10;
struct Read_10;
struct Write_10;
struct Cmd_16;
struct Read_capacity_16;
struct Io_16;
struct Read_16;
struct Write_16;
}
/*******************
** Endian helper **
*******************/
Genode::uint16_t Scsi::be16(Genode::uint16_t val)
{
Genode::uint8_t *p = reinterpret_cast<Genode::uint8_t*>(&val);
return (p[1]<<0)|(p[0]<<8);
}
Genode::uint32_t Scsi::be32(Genode::uint32_t val)
{
Genode::uint8_t *p = reinterpret_cast<Genode::uint8_t*>(&val);
return (p[3]<<0)|(p[2]<<8)|(p[1]<<16)|(p[0]<<24);
}
Genode::uint64_t Scsi::be64(Genode::uint64_t val)
{
Genode::uint8_t *p = reinterpret_cast<Genode::uint8_t*>(&val);
return ((((Genode::uint64_t)(p[3]<<0)|(p[2]<<8)|(p[1]<<16)|(p[0]<<24))<<32)|
(((Genode::uint32_t)(p[7]<<0)|(p[6]<<8)|(p[5]<<16)|(p[4]<<24))));
}
/***************************
* SCSI command responses **
***************************/
struct Scsi::Inquiry_response : Genode::Mmio
{
enum { LENGTH = 36 /* default */ + 20 /* drive serial number and vendor unique */};
struct Dt : Register<0x0, 8> { }; /* device type */
struct Rm : Register<0x1, 8> { struct Rmb : Bitfield<7, 1> { }; }; /* removable media */
struct Ver : Register<0x2, 8> { }; /* version */
struct Rdf : Register<0x3, 8> { }; /* response data format */
struct Al : Register<0x4, 8> { }; /* additional ength */
struct Flg : Register<0x7, 8> { }; /* flags */
struct Vid : Register_array<0x8, 8, 8, 8> { }; /* vendor identification */
struct Pid : Register_array<0x10, 8, 16, 8> { }; /* product identification */
struct Rev : Register_array<0x20, 8, 4, 8> { }; /* product revision level */
Inquiry_response(addr_t addr) : Mmio(addr) { }
bool sbc() const { return read<Dt>() == 0x00; }
bool removable() const { return read<Rm::Rmb>(); }
template <typename ID>
bool get_id(char *dst, size_t len)
{
if (len < ID::ITEMS+1) return false;
for (uint32_t i = 0; i < ID::ITEMS; i++) dst[i] = read<ID>(i);
dst[ID::ITEMS] = 0;
return true;
}
void dump()
{
PLOG("--- Dump INQUIRY data ---");
PLOG("Dt: 0x%02x", read<Dt>());
PLOG("Rm::Rmb: %u ", read<Rm::Rmb>());
PLOG("Ver: 0x%02x", read<Ver>());
PLOG("Rdf: 0x%02x", read<Rdf>());
PLOG("Al: %u", read<Al>());
PLOG("Flg: 0x%02x", read<Flg>());
}
};
struct Scsi::Request_sense_response : Genode::Mmio
{
enum { LENGTH = 18 };
struct Rc : Register<0x0, 8>
{
struct V : Bitfield<6, 1> { }; /* valid bit */
struct Ec : Bitfield<0, 7> { }; /* error code */
}; /* response code */
struct Flg : Register<0x2, 8>
{
struct Sk : Bitfield<0, 4> { }; /* sense key */
}; /* flags */
struct Inf : Register<0x3, 32> { }; /* information BE */
struct Asl : Register<0x7, 8> { }; /* additional sense length */
struct Csi : Register<0x8, 32> { }; /* command specific information BE */
struct Asc : Register<0xc, 8> { }; /* additional sense code */
struct Asq : Register<0xd, 8> { }; /* additional sense code qualifier */
struct Fru : Register<0xe, 8> { }; /* field replaceable unit code */
struct Sks : Register<0xf, 32> { }; /* sense key specific (3 byte) */
Request_sense_response(addr_t addr) : Mmio(addr) { }
void dump()
{
PLOG("--- Dump REQUEST_SENSE data ---");
PLOG("Rc::V: %u", read<Rc::V>());
PLOG("Rc::Ec: 0x%02x", read<Rc::Ec>());
PLOG("Flg::Sk: 0x%02x", read<Flg::Sk>());
PLOG("Asc: 0x%02x", read<Asc>());
PLOG("Asq: 0x%02x", read<Asq>());
}
};
struct Scsi::Capacity_response_10 : Genode::Mmio
{
enum { LENGTH = 8 };
struct Bc : Register<0x0, 32> { };
struct Bs : Register<0x4, 32> { };
Capacity_response_10(addr_t addr) : Mmio(addr) { }
uint32_t block_count() const { return be32(read<Bc>()); }
uint32_t block_size() const { return be32(read<Bs>()); }
void dump()
{
PLOG("--- Dump READ_CAPACITY_10 data ---");
PLOG("Bc: 0x%04x", block_count());
PLOG("Bs: 0x%04x", block_size());
}
};
struct Scsi::Capacity_response_16 : Genode::Mmio
{
enum { LENGTH = 32 };
struct Bc : Register<0x0, 64> { };
struct Bs : Register<0x8, 32> { };
Capacity_response_16(addr_t addr) : Mmio(addr) { }
uint64_t block_count() const { return be64(read<Bc>()); }
uint32_t block_size() const { return be32(read<Bs>()); }
void dump()
{
PLOG("--- Dump READ_CAPACITY_16 data ---");
PLOG("Bc: 0x%08llx", block_count());
PLOG("Bs: 0x%04x", block_size());
}
};
/*************************
** CBD 6 byte commands **
*************************/
struct Scsi::Cmd_6 : Genode::Mmio
{
enum { LENGTH = 6 };
struct Op : Register<0x0, 8> { }; /* SCSI command */
struct Lba : Register<0x2, 16> { }; /* logical block address */
struct Len : Register<0x4, 8> { }; /* transfer length */
struct Ctl : Register<0x5, 8> { }; /* controll */
Cmd_6(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
void dump()
{
PLOG("Op: 0x%02x", read<Op>());
PLOG("Lba: 0x%02x", be16(read<Lba>()));
PLOG("Len: %u", read<Len>());
PLOG("Ctl: 0x%02x", read<Ctl>());
}
};
struct Scsi::Test_unit_ready : Cmd_6
{
Test_unit_ready(addr_t addr) : Cmd_6(addr)
{
write<Cmd_6::Op>(Opcode::TEST_UNIT_READY);
}
};
struct Scsi::Request_sense : Cmd_6
{
Request_sense(addr_t addr) : Cmd_6(addr)
{
write<Cmd_6::Op>(Opcode::REQUEST_SENSE);
write<Cmd_6::Len>(Request_sense_response::LENGTH);
}
};
struct Scsi::Inquiry : Cmd_6
{
Inquiry(addr_t addr) : Cmd_6(addr)
{
write<Cmd_6::Op>(Opcode::INQUIRY);
write<Cmd_6::Len>(Inquiry_response::LENGTH);
}
};
/* not in use for now but might come in handy in the future */
struct Scsi::Start_stop : Genode::Mmio
{
enum { LENGTH = 6 };
struct Op : Register<0x0, 8> { }; /* SCSI command */
struct I : Register<0x1, 8> {
struct Immed : Bitfield<0, 1> { }; }; /* immediate */
struct Flg : Register<0x4, 8>
{
struct Pwc : Bitfield<4, 4> { }; /* power condition */
struct Loej : Bitfield<1, 1> { }; /* load eject */
struct St : Bitfield<0, 1> { }; /* start */
}; /* flags */
Start_stop(addr_t addr) : Mmio(addr)
{
memset((void*)addr, 0, LENGTH);
write<Op>(Opcode::START_STOP);
write<I::Immed>(1);
write<Flg::Pwc>(0);
write<Flg::Loej>(1);
write<Flg::St>(1);
}
void dump()
{
PLOG("Op: 0x%02x", read<Op>());
PLOG("I::Immed: %u", read<I::Immed>());
PLOG("Flg::Pwc: 0x%02x", read<Flg::Pwc>());
PLOG("Flg::Loej: %u", read<Flg::Loej>());
PLOG("Flg::St: %u", read<Flg::St>());
}
};
/**************************
** CBD 10 byte commands **
**************************/
struct Scsi::Cmd_10 : Genode::Mmio
{
enum { LENGTH = 10 };
struct Op : Register<0x0, 8> { }; /* SCSI command */
struct Lba : Register<0x2, 32> { }; /* logical block address */
struct Len : Register<0x7, 16> { }; /* transfer length */
struct Ctl : Register<0x9, 8> { }; /* controll */
Cmd_10(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
void dump()
{
PLOG("Op: 0x%0x", read<Op>());
PLOG("Lba: 0x%0x", be32(read<Lba>()));
PLOG("Len: %u", be16(read<Len>()));
PLOG("Ctl: 0x%0x", read<Ctl>());
}
};
struct Scsi::Read_capacity_10 : Cmd_10
{
Read_capacity_10(addr_t addr) : Cmd_10(addr)
{
write<Cmd_10::Op>(Opcode::READ_CAPACITY_10);
}
};
struct Scsi::Io_10 : Cmd_10
{
Io_10(addr_t addr, uint32_t lba, uint16_t len) : Cmd_10(addr)
{
write<Cmd_10::Lba>(be32(lba));
write<Cmd_10::Len>(be16(len));
}
};
struct Scsi::Read_10 : Io_10
{
Read_10(addr_t addr, uint32_t lba, uint16_t len) : Io_10(addr, lba, len)
{
write<Cmd_10::Op>(Opcode::READ_10);
}
};
struct Scsi::Write_10 : Io_10
{
Write_10(addr_t addr, uint32_t lba, uint16_t len) : Io_10(addr, lba, len)
{
write<Cmd_10::Op>(Opcode::WRITE_10);
}
};
/***********************************
** CBD 16 long LBA byte commands **
***********************************/
struct Scsi::Cmd_16 : Genode::Mmio
{
enum { LENGTH = 16 };
struct Op : Register<0x0, 8> { }; /* SCSI command */
struct Lba : Register<0x2, 64> { }; /* logical block address */
struct Len : Register<0xa, 32> { }; /* transfer length */
struct Ctl : Register<0xf, 8> { }; /* controll */
Cmd_16(addr_t addr) : Mmio(addr) { memset((void*)addr, 0, LENGTH); }
void dump()
{
PLOG("Op: 0x%0x", read<Op>());
PLOG("Lba: 0x%0llx", be64(read<Lba>()));
PLOG("Len: %u", be32(read<Len>()));
PLOG("Ctl: 0x%0x", read<Ctl>());
}
};
struct Scsi::Read_capacity_16 : Cmd_16
{
Read_capacity_16(addr_t addr) : Cmd_16(addr)
{
write<Cmd_16::Op>(Opcode::READ_CAPACITY_16);
}
};
struct Scsi::Io_16 : Cmd_16
{
Io_16(addr_t addr, uint64_t lba, uint32_t len) : Cmd_16(addr)
{
write<Cmd_16::Lba>(be64(lba));
write<Cmd_16::Len>(be32(len));
}
};
struct Scsi::Read_16 : Io_16
{
Read_16(addr_t addr, uint64_t lba, uint32_t len) : Io_16(addr, lba, len)
{
write<Cmd_16::Op>(Opcode::READ_16);
}
};
struct Scsi::Write_16 : Io_16
{
Write_16(addr_t addr, uint64_t lba, uint32_t len) : Io_16(addr, lba, len)
{
write<Cmd_16::Op>(Opcode::WRITE_16);
}
};
#endif /* _SCSI_H_ */