From e93268a8f9e303c3c0e8a127822b0c4de3bb416b Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 11 Dec 2021 09:30:05 +1000 Subject: [PATCH] MMC:READ_CD implementation and example Signed-off-by: Ronnie Sahlberg --- examples/read_cd.py | 140 +++++++++++++++++++++++++++++++ pyscsi/pyscsi/__init__.py | 1 + pyscsi/pyscsi/scsi.py | 26 ++++++ pyscsi/pyscsi/scsi_cdb_readcd.py | 66 +++++++++++++++ 4 files changed, 233 insertions(+) create mode 100755 examples/read_cd.py create mode 100644 pyscsi/pyscsi/scsi_cdb_readcd.py diff --git a/examples/read_cd.py b/examples/read_cd.py new file mode 100755 index 0000000..73f5f83 --- /dev/null +++ b/examples/read_cd.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: 2021 The python-scsi Authors +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +# coding: utf-8 + +import sys + +#from pyscsi.pyscsi import scsi_enum_readcd as READCD +from pyscsi.pyscsi.scsi import SCSI +from pyscsi.pyscsi.scsi_sense import SCSICheckCondition +from pyscsi.utils import init_device + + +def usage(): + print('Usage: read_cd.py ') + + +def main(): + i = 1 + lba = 0 + tl = 1 + est = 0 + dap = 0 + mcsb = 0x02 + c2ei = 0 + scsb = 0 + while i < len(sys.argv): + if sys.argv[i] == '--help': + return usage() + if sys.argv[i] == '-lba': + del sys.argv[i] + if sys.argv[i][:2] == '0x': + lba = int(sys.argv[i], 16) + else: + lba = int(sys.argv[i], 10) + del sys.argv[i] + continue + if sys.argv[i] == '-tl': + del sys.argv[i] + if sys.argv[i][:2] == '0x': + tl = int(sys.argv[i], 16) + else: + tl = int(sys.argv[i], 10) + del sys.argv[i] + continue + if sys.argv[i] == '-est': + del sys.argv[i] + est = int(sys.argv[i], 16) + del sys.argv[i] + continue + if sys.argv[i] == '-dap': + del sys.argv[i] + dap = int(sys.argv[i]) + del sys.argv[i] + continue + if sys.argv[i] == '-mcsb': + del sys.argv[i] + mcsb = int(sys.argv[i], 16) + del sys.argv[i] + continue + if sys.argv[i] == '-c2ei': + del sys.argv[i] + c2ei = int(sys.argv[i], 16) + del sys.argv[i] + continue + if sys.argv[i] == '-scsb': + del sys.argv[i] + scsb = int(sys.argv[i], 16) + del sys.argv[i] + continue + i += 1 + + if len(sys.argv) < 2: + return usage() + + device = init_device(sys.argv[1]) + + with SCSI(device) as s: + + try: + s.testunitready() + + cmd = s.readcd(lba, tl, est=est, dap=dap, mcsb=mcsb, c2ei=c2ei, scsb=scsb) + di = cmd.datain + for i in range(tl): + print('LBA 0x%08x' % (lba + i), '/', lba + i) + if mcsb & 0x10: + print('SYNC', di[:12].hex()) + di = di[12:] + if mcsb & 0x04: + print('SECTOR HEADER', di[:4].hex()) + di = di[4:] + if mcsb & 0x08: + if est != 4 and est != 5: + raise RuntimeError('Subheader data can only be requested from Mode 2 Form 1/2 sectors') + + print('SECTOR SUB-HEADER', di[:8].hex()) + di = di[8:] + if mcsb & 0x02: + ds = 0 + if est == 2: # Mode 1 + ds = 2048 + if est == 3: # Mode 2 formless + ds = 2336 + if est == 4: # Mode 2 form 1 + ds = 2048 + if est == 5: # Mode 2 form 2 + ds = 2324 + if ds == 0: + raise NotImplementedError('USER DATA requested but we can not determine the size of the data area') + + print('USER DATA', di[:ds].hex()) + di = di[ds:] + if mcsb & 0x01: + raise NotImplementedError('EDC&ECC') + + if c2ei == 1: # C2 Errors Codes + print('C2 ERROR CODES', di[:294].hex()) + di = di[294:] + if c2ei == 2: # C2 Errors Codes + print('C2 ERROR CODES', di[:296].hex()) + di = di[296:] + if scsb == 2: # Formatted Q sub-channel data + print('SUB-CHANNEL', di[:16].hex()) + di = di[16:] + if scsb == 4: # Corrected and de-interleaved R-W sub-channel data + print('SUB-CHANNEL', di[:96].hex()) + di = di[96:] + + except SCSICheckCondition as ex: + # if you want a print out of the sense data dict uncomment the next line + #ex.show_data = True + print(ex) + + +if __name__ == "__main__": + main() diff --git a/pyscsi/pyscsi/__init__.py b/pyscsi/pyscsi/__init__.py index 0e82a3e..6935c14 100644 --- a/pyscsi/pyscsi/__init__.py +++ b/pyscsi/pyscsi/__init__.py @@ -19,6 +19,7 @@ 'scsi_cdb_read16', 'scsi_cdb_readcapacity16', 'scsi_cdb_readcapacity10', + 'scsi_cdb_readcd', 'scsi_cdb_readelementstatus', 'scsi_cdb_report_luns', 'scsi_cdb_report_priority', diff --git a/pyscsi/pyscsi/scsi.py b/pyscsi/pyscsi/scsi.py index 9d9914c..2971c88 100644 --- a/pyscsi/pyscsi/scsi.py +++ b/pyscsi/pyscsi/scsi.py @@ -26,6 +26,7 @@ from pyscsi.pyscsi.scsi_cdb_read16 import Read16 from pyscsi.pyscsi.scsi_cdb_readcapacity10 import ReadCapacity10 from pyscsi.pyscsi.scsi_cdb_readcapacity16 import ReadCapacity16 +from pyscsi.pyscsi.scsi_cdb_readcd import ReadCd from pyscsi.pyscsi.scsi_cdb_readelementstatus import ReadElementStatus from pyscsi.pyscsi.scsi_cdb_report_luns import ReportLuns from pyscsi.pyscsi.scsi_cdb_report_priority import ReportPriority @@ -477,6 +478,31 @@ def readcapacity16(self, cmd.unmarshall() return cmd + def readcd(self, + lba, + tl, + **kwargs): + """ + Returns a ReadCd Instance + + :param lba: Logical Block Address + :param tl: Transfer Length + :param kwargs: a dict with key/value pairs + est=0: Expected Sector Type + dap=0: Digital Audio Play + mcsb=0: Main Channel Selection Bits + c2e1=0: C2 Error Information + scsb=0: Sub-Channel Selection Bits + :return: a ReadCd instance + """ + opcode = self.device.opcodes.READ_CD + cmd = ReadCd(opcode, + lba, + tl, + **kwargs) + self.execute(cmd) + return cmd + def readelementstatus(self, start, num, diff --git a/pyscsi/pyscsi/scsi_cdb_readcd.py b/pyscsi/pyscsi/scsi_cdb_readcd.py new file mode 100644 index 0000000..5dc4e98 --- /dev/null +++ b/pyscsi/pyscsi/scsi_cdb_readcd.py @@ -0,0 +1,66 @@ +# coding: utf-8 + +# Copyright (C) 2021 by Ronnie Sahlberg +# SPDX-FileCopyrightText: 2014 The python-scsi Authors +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +from pyscsi.pyscsi.scsi_command import SCSICommand +from pyscsi.utils.converter import decode_bits, encode_dict + +# +# SCSI ReadCd command and definitions +# + + +class ReadCd(SCSICommand): + """ + A class to hold information from a ReadCd command to a scsi device + """ + _cdb_bits = {'opcode': [0xff, 0], + 'est': [0x1c, 1], + 'dap': [0x02, 1], + 'lba': [0xffffffff, 2], + 'tl': [0xffff, 6], + 'mcsb': [0xf8, 9], + 'c2ei': [0x06, 9], + 'scsb': [0x07, 10], + } + + def __init__(self, + opcode, + lba, + tl, + est=0, + dap=0, + mcsb=0, + c2ei=0, + scsb=0): + """ + initialize a new instance + + :param opcode: a OpCode instance + :param lba: Logical Block Address + :param tl: transfer length + :param est=0: Expected Sector Type + :param dap=0: Digital Audio Play + :param mcsb=0: Main Channel Selection Bits + :param c2e1=0: C2 Error Information + :param scsb=0: Sub-Channel Selection Bits + """ + + # dont bother to compute the exact allocation length needed, just use + # 3kb per lba + SCSICommand.__init__(self, + opcode, + 0, + tl * 3072) + + self.cdb = self.build_cdb(opcode=self.opcode.value, + lba=lba, + tl=tl, + est=est, + dap=dap, + mcsb=mcsb, + c2ei=c2ei, + scsb=scsb)