mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-03 06:27:47 +00:00
586 lines
21 KiB
Go
586 lines
21 KiB
Go
package sd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// For reference of CID/CSD structs see:
|
|
// See https://github.com/arduino-libraries/SD/blob/1c56f58252553c7537f7baf62798cacc625aa543/src/utility/SdInfo.h#L110
|
|
|
|
// CardKind classifies an SD card by its capacity class and specification
|
|
// version, as discovered during card initialization.
|
|
type CardKind uint8
|
|
|
|
// isTimeout reports whether err is one of the package's timeout errors.
|
|
func isTimeout(err error) bool {
|
|
return err == errReadTimeout || err == errWriteTimeout || err == errBusyTimeout
|
|
}
|
|
|
|
const (
|
|
// card types
|
|
TypeSD1 CardKind = 1 // Standard capacity V1 SD card
|
|
TypeSD2 CardKind = 2 // Standard capacity V2 SD card
|
|
TypeSDHC CardKind = 3 // High Capacity SD card
|
|
)
|
|
|
|
// CID is the Card Identification register, a 128-bit (16-byte) read-only
|
|
// register holding the card's identification information: manufacturer,
|
|
// product name, serial number and manufacturing date, among other data.
|
|
// It is programmed during card manufacture and cannot be changed.
|
|
type CID struct {
|
|
data [16]byte
|
|
}
|
|
|
|
// DecodeCID decodes a CID from the first 16 bytes of b. It returns an error
|
|
// if b is too short or if the CRC7/always-1 fields are invalid.
|
|
func DecodeCID(b []byte) (cid CID, _ error) {
|
|
if len(b) < 16 {
|
|
return CID{}, io.ErrShortBuffer
|
|
}
|
|
copy(cid.data[:], b)
|
|
if !cid.IsValid() {
|
|
return cid, errBadCSDCID
|
|
}
|
|
return cid, nil
|
|
}
|
|
|
|
// RawCopy returns a copy of the raw CID data.
|
|
func (c *CID) RawCopy() [16]byte { return c.data }
|
|
|
|
// ManufacturerID is an 8-bit binary number that identifies the card manufacturer. The MID number is controlled, defined, and allocated to a SD Memory Card manufacturer by the SD-3C, LLC.
|
|
func (c *CID) ManufacturerID() uint8 { return c.data[0] }
|
|
|
|
// OEMApplicationID A 2-character ASCII string that identifies the card OEM and/or the card contents (when used as a
|
|
// distribution media either on ROM or FLASH cards). The OID number is controlled, defined, and allocated
|
|
// to a SD Memory Card manufacturer by the SD-3C, LLC
|
|
func (c *CID) OEMApplicationID() uint16 {
|
|
return binary.BigEndian.Uint16(c.data[1:3])
|
|
}
|
|
|
|
// ProductName returns the product name, an ASCII string of up to 5 characters.
|
|
func (c *CID) ProductName() string {
|
|
return string(upToNull(c.data[3:8]))
|
|
}
|
|
|
|
// ProductRevision is composed of two Binary Coded Decimal (BCD) digits, four bits each, representing
|
|
// an "n.m" revision number. The "n" is the most significant nibble and "m" is the least significant nibble.
|
|
// As an example, the PRV binary value field for product revision "6.2" will be: 0110 0010b
|
|
func (c *CID) ProductRevision() (n, m uint8) {
|
|
rev := c.data[8]
|
|
return rev >> 4, rev & 0x0F
|
|
}
|
|
|
|
// ProductSerialNumber returns the product serial number, a 32-bit binary number.
|
|
func (c *CID) ProductSerialNumber() uint32 {
|
|
return binary.BigEndian.Uint32(c.data[9:13])
|
|
}
|
|
|
|
// ManufacturingDate returns the manufacturing date of the card,
|
|
// e.g. year=2023, month=4 for April 2023.
|
|
func (c *CID) ManufacturingDate() (year uint16, month uint8) {
|
|
date := binary.BigEndian.Uint16(c.data[13:15])
|
|
return (date >> 4) + 2000, uint8(date & 0x0F)
|
|
}
|
|
|
|
// CRC7 returns the CRC7 checksum for this CID. May be invalid. Use [IsValid] to check validity of CRC7+Always1 fields.
|
|
func (c *CID) CRC7() uint8 { return c.data[15] >> 1 }
|
|
|
|
// Always1 checks the presence of the Always 1 bit. Should return true for valid CIDs.
|
|
func (c *CID) Always1() bool { return c.data[15]&1 != 0 }
|
|
|
|
// IsValid checks if the CRC and always1 fields are expected values.
|
|
func (c *CID) IsValid() bool { return c.Always1() && CRC7(c.data[:15]) == c.CRC7() }
|
|
|
|
// CSD is the Card Specific Data register, a 128-bit (16-byte) register that defines how
|
|
// the SD card standard communicates with the memory field or register. This type is
|
|
// shared among V1 and V2 type devices.
|
|
type CSD struct {
|
|
data [16]byte
|
|
}
|
|
|
|
// CSDv1 is the Card Specific Data register for V1 devices. See [CSD] for more info.
|
|
type CSDv1 struct {
|
|
CSD
|
|
}
|
|
|
|
// CSDv2 is the Card Specific Data register for V2 devices. See [CSD] for more info.
|
|
type CSDv2 struct {
|
|
CSD
|
|
}
|
|
|
|
// DecodeCSD decodes the CSD from a 16-byte slice.
|
|
func DecodeCSD(b []byte) (CSD, error) {
|
|
if len(b) < 16 {
|
|
return CSD{}, io.ErrShortBuffer
|
|
}
|
|
csd := CSD{}
|
|
copy(csd.data[:], b)
|
|
if !csd.IsValid() {
|
|
return csd, errBadCSDCID
|
|
}
|
|
return csd, nil
|
|
}
|
|
|
|
// csdStructure returns the version of the CSD structure.
|
|
func (c *CSD) csdStructure() uint8 { return c.data[0] >> 6 }
|
|
|
|
// Version returns the version of the CSD structure. Effectively returns 1+CSDStructure.
|
|
func (c *CSD) Version() uint8 { return 1 + c.csdStructure() }
|
|
|
|
// MustV1 returns the CSD as a CSDv1. Panics if the CSD is not version 1.0.
|
|
func (c CSD) MustV1() CSDv1 {
|
|
if c.csdStructure() != 0 {
|
|
panic("CSD is not version 1.0")
|
|
}
|
|
return CSDv1{CSD: c}
|
|
}
|
|
|
|
// MustV2 returns the CSD as a CSDv2. Panics if the CSD is not version 2.0.
|
|
func (c CSD) MustV2() CSDv2 {
|
|
if c.csdStructure() != 1 {
|
|
panic("CSD is not version 2.0")
|
|
}
|
|
return CSDv2{CSD: c}
|
|
}
|
|
|
|
// RawCopy returns a copy of the raw CSD data.
|
|
func (c *CSD) RawCopy() [16]byte { return c.data }
|
|
|
|
// TAAC returns the Time Access Attribute Class (data read access-time-1).
|
|
func (c *CSD) TAAC() TAAC { return TAAC(c.data[1]) }
|
|
|
|
// NSAC returns the Data Read Access-time 2 in CLK cycles (NSAC*100).
|
|
func (c *CSD) NSAC() NSAC { return NSAC(c.data[2]) }
|
|
|
|
// TransferSpeed returns the Max Data Transfer Rate. Either 0x32 or 0x5A.
|
|
func (c *CSD) TransferSpeed() TransferSpeed { return TransferSpeed(c.data[3]) }
|
|
|
|
// CommandClasses returns the supported Card Command Classes as a bitfield;
|
|
// bit position i set means command class i is supported by the card.
|
|
func (c *CSD) CommandClasses() CommandClasses {
|
|
return CommandClasses(uint16(c.data[4])<<4 | uint16(c.data[5]&0xf0)>>4)
|
|
}
|
|
|
|
// ReadBlockLen returns the Max Read Data Block Length in bytes.
|
|
func (c *CSD) ReadBlockLen() int { return 1 << c.ReadBlockLenShift() }
|
|
|
|
// ReadBlockLenShift returns the base-2 logarithm of [CSD.ReadBlockLen] (READ_BL_LEN field).
|
|
func (c *CSD) ReadBlockLenShift() uint8 { return c.data[5] & 0x0F }
|
|
|
|
// AllowsReadBlockPartial indicates that partial block reads (down to a
|
|
// single byte) are allowed. Always true for SD cards.
|
|
func (c *CSD) AllowsReadBlockPartial() bool { return c.data[6]&(1<<7) != 0 }
|
|
|
|
// AllowsWriteBlockMisalignment defines if the data block to be written by one command
|
|
// can be spread over more than one physical block of the memory device.
|
|
func (c *CSD) AllowsWriteBlockMisalignment() bool { return c.data[6]&(1<<6) != 0 }
|
|
|
|
// AllowsReadBlockMisalignment defines if the data block to be read by one command
|
|
// can be spread over more than one physical block of the memory device.
|
|
func (c *CSD) AllowsReadBlockMisalignment() bool { return c.data[6]&(1<<5) != 0 }
|
|
|
|
// CRC7 returns the CRC read for this CSD. May be invalid. Use [IsValid] to check validity of CRC7+Always1 fields.
|
|
func (c *CSD) CRC7() uint8 { return c.data[15] >> 1 }
|
|
|
|
// Always1 checks the Always 1 bit. Should always evaluate to true for valid CSDs.
|
|
func (c *CSD) Always1() bool { return c.data[15]&1 != 0 }
|
|
|
|
// IsValid checks if the CRC and always1 fields are expected values.
|
|
func (c *CSD) IsValid() bool {
|
|
// Compare last byte with CRC and also the always1 bit.
|
|
return c.Always1() && CRC7(c.data[:15]) == c.CRC7()
|
|
}
|
|
|
|
// ImplementsDSR defines if the configurable driver stage is integrated on the card.
|
|
func (c *CSD) ImplementsDSR() bool { return c.data[6]&(1<<4) != 0 }
|
|
|
|
// EraseSectorSizeInBytes returns how much memory is erased by a single
|
|
// erase command, in bytes (SectorSize multiplied by the write block length).
|
|
func (c *CSDv1) EraseSectorSizeInBytes() int64 {
|
|
blklen := c.WriteBlockLen()
|
|
numblocks := c.SectorSize()
|
|
return int64(numblocks) * blklen
|
|
}
|
|
|
|
// SectorSize returns the size of an erasable sector in units of write blocks
|
|
// (SECTOR_SIZE field, range 1..128). Its meaning varies with the CSD version:
|
|
// for V1 it is the erase unit when [CSD.EraseBlockEnabled] is false; for V2
|
|
// it is fixed to 64KiB and does not reflect the real erase unit.
|
|
func (c *CSD) SectorSize() uint8 {
|
|
return 1 + ((c.data[10]&0b11_1111)<<1 | (c.data[11] >> 7))
|
|
}
|
|
|
|
// EraseBlockEnabled defines granularity of unit size of data to be erased.
|
|
// If enabled the erase operation can erase either one or multiple units of 512 bytes.
|
|
func (c *CSD) EraseBlockEnabled() bool { return (c.data[10]>>6)&1 != 0 }
|
|
|
|
// ReadToWriteFactor returns the typical write time as a power-of-2 multiple of
|
|
// the read access time (R2W_FACTOR field), i.e. writeTime = readTime << factor.
|
|
func (c *CSD) ReadToWriteFactor() uint8 { return (c.data[12] >> 2) & 0b111 }
|
|
|
|
// WriteProtectGroupSizeInSectors indicates the size of a write protected
|
|
// group in multiple of erasable sectors.
|
|
func (c *CSD) WriteProtectGroupSizeInSectors() uint8 {
|
|
return 1 + (c.data[11] & 0b111_1111)
|
|
}
|
|
|
|
// WriteBlockLen represents maximum write data block length in bytes.
|
|
func (c *CSD) WriteBlockLen() int64 {
|
|
return 1 << ((c.data[12]&0b11)<<2 | (c.data[13] >> 6))
|
|
}
|
|
|
|
// WriteGroupEnabled indicates if write group protection is available.
|
|
func (c *CSD) WriteGroupEnabled() bool { return c.data[12]&(1<<7) != 0 }
|
|
|
|
// AllowsWritePartial Defines whether partial block sizes can be used in write block sizes.
|
|
func (c *CSD) AllowsWritePartial() bool { return c.data[13]&(1<<5) != 0 }
|
|
|
|
// FileFormat returns the file format on the card. This field is read-only for ROM.
|
|
func (c *CSD) FileFormat() FileFormat { return FileFormat(c.data[14]>>2) & 0b11 }
|
|
|
|
// TmpWriteProtected indicates temporary protection over the entire card content from being overwritten or erased.
|
|
func (c *CSD) TmpWriteProtected() bool { return c.data[14]&(1<<4) != 0 }
|
|
|
|
// PermWriteProtected indicates permanent protecttion of entire card content against overwriting or erasing (write+erase permanently disabled).
|
|
func (c *CSD) PermWriteProtected() bool { return c.data[14]&(1<<5) != 0 }
|
|
|
|
// IsCopy whether contents are original or have been copied.
|
|
func (c *CSD) IsCopy() bool { return c.data[14]&(1<<6) != 0 }
|
|
|
|
// FileFormatGroup returns the file format group bit, which selects between
|
|
// the two [FileFormat] tables. Interpret together with [CSD.FileFormat].
|
|
func (c *CSD) FileFormatGroup() bool { return c.data[14]&(1<<7) != 0 }
|
|
|
|
// DeviceCapacity returns the total device capacity in bytes, dispatching on
|
|
// the CSD version. Returns 0 for unknown CSD versions.
|
|
func (c *CSD) DeviceCapacity() (size int64) {
|
|
switch c.csdStructure() {
|
|
case 0:
|
|
v1 := c.MustV1()
|
|
size = int64(v1.DeviceCapacity())
|
|
case 1:
|
|
v2 := c.MustV2()
|
|
size = v2.DeviceCapacity()
|
|
}
|
|
return size
|
|
}
|
|
|
|
// NumberOfBlocks returns amount of readable blocks in the device given by Capacity/ReadBlockLength.
|
|
func (c *CSD) NumberOfBlocks() (numBlocks int64) {
|
|
rblocks := c.ReadBlockLen()
|
|
if rblocks == 0 {
|
|
return 0
|
|
}
|
|
return c.DeviceCapacity() / int64(rblocks)
|
|
}
|
|
|
|
// After byte 5 CSDv1 and CSDv2 differ in structure at some fields.
|
|
|
|
// DeviceCapacity returns the device capacity in bytes:
|
|
// (C_SIZE+1) * 512KiB, as per section 5.3.3 of the SD Simplified Specification.
|
|
func (c *CSDv2) DeviceCapacity() int64 {
|
|
csize := c.csize()
|
|
return (int64(csize) + 1) * (512 * 1024)
|
|
}
|
|
|
|
// csize returns the 22-bit C_SIZE field (CSD bits 69:48).
|
|
func (c *CSDv2) csize() uint32 {
|
|
return uint32(c.data[7]&0x3F)<<16 | uint32(c.data[8])<<8 | uint32(c.data[9])
|
|
}
|
|
|
|
// DeviceCapacity returns the total memory capacity of the SDCard in bytes. Max is 2GB for V1.
|
|
func (c *CSDv1) DeviceCapacity() uint32 {
|
|
mult := c.mult()
|
|
csize := c.csize()
|
|
blklen := c.ReadBlockLen()
|
|
blockNR := uint32(csize+1) * uint32(mult)
|
|
return blockNR * uint32(blklen)
|
|
}
|
|
|
|
func (c *CSDv1) csize() uint16 {
|
|
// Jesus, why did SD make this so complicated?
|
|
return uint16(c.data[8]>>6) | uint16(c.data[7])<<2 | uint16(c.data[6]&0b11)<<10
|
|
}
|
|
|
|
// mult is a factor for computing total device size with csize and csizemult.
|
|
func (c *CSDv1) mult() uint16 { return 1 << (2 + c.csizemult()) }
|
|
|
|
func (c *CSDv1) csizemult() uint8 {
|
|
return (c.data[9]&0b11)<<1 | (c.data[10] >> 7)
|
|
}
|
|
|
|
// VddReadCurrent indicates min and max values for read power supply currents.
|
|
// - values min: 0=0.5mA; 1=1mA; 2=5mA; 3=10mA; 4=25mA; 5=35mA; 6=60mA; 7=100mA
|
|
// - values max: 0=1mA; 1=5mA; 2=10mA; 3=25mA; 4=35mA; 5=45mA; 6=80mA; 7=200mA
|
|
func (c *CSDv1) VddReadCurrent() (min, max uint8) {
|
|
return (c.data[8] >> 3) & 0b111, c.data[8] & 0b111
|
|
}
|
|
|
|
// VddWriteCurrent indicates min and max values for write power supply currents.
|
|
// - values min: 0=0.5mA; 1=1mA; 2=5mA; 3=10mA; 4=25mA; 5=35mA; 6=60mA; 7=100mA
|
|
// - values max: 0=1mA; 1=5mA; 2=10mA; 3=25mA; 4=35mA; 5=45mA; 6=80mA; 7=200mA
|
|
func (c *CSDv1) VddWriteCurrent() (min, max uint8) {
|
|
return c.data[9] >> 5, (c.data[9] >> 3) & 0b111
|
|
}
|
|
|
|
// String returns a human-readable multi-line summary of the CSD fields.
|
|
func (c *CSD) String() string {
|
|
version := c.csdStructure() + 1
|
|
if version > 2 {
|
|
return "<unsupported CSD version>"
|
|
}
|
|
const delim = '\n'
|
|
buf := make([]byte, 0, 64)
|
|
buf = c.appendf(buf, delim)
|
|
return string(buf)
|
|
}
|
|
|
|
func (c *CSDv1) String() string { return c.CSD.String() }
|
|
|
|
func (c *CSDv2) String() string { return c.CSD.String() }
|
|
|
|
func (c *CSD) appendf(b []byte, delim byte) []byte {
|
|
b = appendnum(b, "Version", uint64(c.Version()), delim)
|
|
b = appendnum(b, "Capacity(bytes)", uint64(c.DeviceCapacity()), delim)
|
|
b = appendnum(b, "TimeAccess_ns", uint64(c.TAAC().AccessTime()), delim)
|
|
b = appendnum(b, "NSAC", uint64(c.NSAC()), delim)
|
|
b = appendnum(b, "Tx_kb/s", uint64(c.TransferSpeed().RateKilobits()), delim)
|
|
b = appendnum(b, "CCC", uint64(c.CommandClasses()), delim)
|
|
b = appendnum(b, "ReadBlockLen", uint64(c.ReadBlockLen()), delim)
|
|
b = appendbit(b, "ReadBlockPartial", c.AllowsReadBlockPartial(), delim)
|
|
b = appendbit(b, "AllowWriteBlockMisalignment", c.AllowsWriteBlockMisalignment(), delim)
|
|
b = appendbit(b, "AllowReadBlockMisalignment", c.AllowsReadBlockMisalignment(), delim)
|
|
b = appendbit(b, "ImplementsDSR", c.ImplementsDSR(), delim)
|
|
b = appendnum(b, "WProtectNumSectors", uint64(c.WriteProtectGroupSizeInSectors()), delim)
|
|
b = appendnum(b, "WriteBlockLen", uint64(c.WriteBlockLen()), delim)
|
|
b = appendbit(b, "WGrpEnable", c.WriteGroupEnabled(), delim)
|
|
b = appendbit(b, "WPartialAllow", c.AllowsWritePartial(), delim)
|
|
b = append(b, "FileFmt:"...)
|
|
b = append(b, c.FileFormat().String()...)
|
|
b = append(b, delim)
|
|
b = appendbit(b, "TmpWriteProtect", c.TmpWriteProtected(), delim)
|
|
b = appendbit(b, "PermWriteProtect", c.PermWriteProtected(), delim)
|
|
b = appendbit(b, "IsCopy", c.IsCopy(), delim)
|
|
b = appendbit(b, "FileFormatGrp", c.FileFormatGroup(), delim)
|
|
return b
|
|
}
|
|
|
|
func appendnum(b []byte, label string, n uint64, delim byte) []byte {
|
|
b = append(b, label...)
|
|
b = append(b, ':')
|
|
b = strconv.AppendUint(b, n, 10)
|
|
b = append(b, delim)
|
|
return b
|
|
}
|
|
|
|
func appendbit(b []byte, label string, n bool, delim byte) []byte {
|
|
b = append(b, label...)
|
|
b = append(b, ':')
|
|
b = append(b, '0'+b2u8(n))
|
|
b = append(b, delim)
|
|
return b
|
|
}
|
|
|
|
func upToNull(buf []byte) []byte {
|
|
nullIdx := bytes.IndexByte(buf, 0)
|
|
if nullIdx < 0 {
|
|
return buf
|
|
}
|
|
return buf[:nullIdx]
|
|
}
|
|
|
|
type (
|
|
command byte
|
|
appcommand byte
|
|
)
|
|
|
|
// SD commands and application commands.
|
|
const (
|
|
cmdGoIdleState command = 0
|
|
cmdSendOpCnd command = 1
|
|
cmdAllSendCID command = 2
|
|
cmdSendRelativeAddr command = 3
|
|
cmdSetDSR command = 4
|
|
cmdSwitchFunc command = 6
|
|
cmdSelectDeselectCard command = 7
|
|
cmdSendIfCond command = 8
|
|
cmdSendCSD command = 9
|
|
cmdSendCID command = 10
|
|
cmdStopTransmission command = 12
|
|
cmdSendStatus command = 13
|
|
cmdGoInactiveState command = 15
|
|
cmdSetBlocklen command = 16
|
|
cmdReadSingleBlock command = 17
|
|
cmdReadMultipleBlock command = 18
|
|
cmdWriteBlock command = 24
|
|
cmdWriteMultipleBlock command = 25
|
|
cmdProgramCSD command = 27
|
|
cmdSetWriteProt command = 28
|
|
cmdClrWriteProt command = 29
|
|
cmdSendWriteProt command = 30
|
|
cmdEraseWrBlkStartAddr command = 32
|
|
cmdEraseWrBlkEndAddr command = 33
|
|
cmdErase command = 38
|
|
cmdLockUnlock command = 42
|
|
cmdAppCmd command = 55
|
|
cmdGenCmd command = 56
|
|
cmdReadOCR command = 58
|
|
cmdCRCOnOff command = 59
|
|
|
|
acmdSET_BUS_WIDTH appcommand = 6
|
|
acmdSD_STATUS appcommand = 13
|
|
acmdSEND_NUM_WR_BLOCKS appcommand = 22
|
|
acmdSET_WR_BLK_ERASE_COUNT appcommand = 23
|
|
acmdSD_APP_OP_COND appcommand = 41
|
|
acmdSET_CLR_CARD_DETECT appcommand = 42
|
|
acmdSEND_SCR appcommand = 51
|
|
acmdSECURE_READ_MULTI_BLOCK appcommand = 18
|
|
acmdSECURE_WRITE_MULTI_BLOCK appcommand = 25
|
|
acmdSECURE_WRITE_MKB appcommand = 26
|
|
acmdSECURE_ERASE appcommand = 38
|
|
acmdGET_MKB appcommand = 43
|
|
acmdGET_MID appcommand = 44
|
|
acmdSET_CER_RN1 appcommand = 45
|
|
acmdSET_CER_RN2 appcommand = 46
|
|
acmdSET_CER_RES2 appcommand = 47
|
|
acmdSET_CER_RES1 appcommand = 48
|
|
acmdCHANGE_SECURE_AREA appcommand = 49
|
|
)
|
|
|
|
// CSD field types.
|
|
type (
|
|
// TransferSpeed is the TRAN_SPEED CSD field: the maximum data transfer
|
|
// rate encoded as a rate unit (lower 3 bits) and time value multiplier.
|
|
TransferSpeed uint8
|
|
// TAAC is the data read access time CSD field, encoded as a time unit
|
|
// (lower 3 bits) and time value multiplier.
|
|
TAAC uint8
|
|
// FileFormat is the format of the data stored on the card. See the
|
|
// FileFmt* constants for possible values.
|
|
FileFormat uint8
|
|
// CommandClasses is the CCC CSD field, a bitfield where bit position i
|
|
// set means command class i is supported.
|
|
CommandClasses uint16
|
|
// NSAC is the data read access time 2 CSD field, given in units of
|
|
// 100 clock cycles.
|
|
NSAC uint8
|
|
)
|
|
|
|
const (
|
|
FileFmtPartition FileFormat = iota // Hard disk like file system with partition table.
|
|
FileFmtDOSFAT // DOS FAT (floppy like)
|
|
FileFmtUFF // Universal File Format
|
|
FileFmtUnknown
|
|
)
|
|
|
|
// String returns a human-readable name for the file format.
|
|
func (ff FileFormat) String() (s string) {
|
|
switch ff {
|
|
case FileFmtPartition:
|
|
s = "partition"
|
|
case FileFmtDOSFAT:
|
|
s = "DOS/FAT"
|
|
case FileFmtUFF:
|
|
s = "UFF"
|
|
case FileFmtUnknown:
|
|
s = "unknown"
|
|
default:
|
|
s = "<invalid format>"
|
|
}
|
|
return s
|
|
}
|
|
|
|
var log10table = [...]int64{
|
|
1,
|
|
10,
|
|
100,
|
|
1000,
|
|
10000,
|
|
100000,
|
|
1000000,
|
|
}
|
|
|
|
// RateKilobits returns the transfer rate in kilobits per second.
|
|
func (t TransferSpeed) RateKilobits() int64 {
|
|
return 100 * log10table[t&0b111]
|
|
}
|
|
|
|
// AccessTime returns the asynchronous part of the data access time.
|
|
func (t TAAC) AccessTime() (d time.Duration) {
|
|
return time.Duration(log10table[t&0b111]) * time.Nanosecond
|
|
}
|
|
|
|
func b2u8(b bool) uint8 {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// CRC16 computes the CRC16 checksum for a given payload using the CRC-16-CCITT polynomial.
|
|
func CRC16(buf []byte) (crc uint16) {
|
|
const poly uint16 = 0x1021 // Generator polynomial G(x) = x^16 + x^12 + x^5 + 1
|
|
for _, b := range buf {
|
|
crc ^= (uint16(b) << 8) // Shift byte into MSB of crc
|
|
for i := 0; i < 8; i++ { // Process each bit
|
|
if crc&0x8000 != 0 {
|
|
crc = (crc << 1) ^ poly
|
|
} else {
|
|
crc <<= 1
|
|
}
|
|
}
|
|
}
|
|
return crc
|
|
}
|
|
|
|
// CRC7 computes the CRC7 checksum for a given payload using the polynomial x^7 + x^3 + 1.
|
|
func CRC7(data []byte) (crc uint8) {
|
|
return crc7noshift(data) >> 1
|
|
}
|
|
|
|
func crc7noshift(data []byte) (crc uint8) {
|
|
for _, b := range data {
|
|
crc = crc7_table[crc^b]
|
|
}
|
|
return crc
|
|
}
|
|
|
|
var crc7_table = [256]byte{
|
|
0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e,
|
|
0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
|
|
0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c,
|
|
0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
|
|
0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a,
|
|
0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
|
|
0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28,
|
|
0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
|
|
0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6,
|
|
0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
|
|
0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84,
|
|
0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
|
|
0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2,
|
|
0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
|
|
0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0,
|
|
0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
|
|
0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc,
|
|
0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
|
|
0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce,
|
|
0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
|
|
0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98,
|
|
0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
|
|
0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa,
|
|
0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
|
|
0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34,
|
|
0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
|
|
0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06,
|
|
0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
|
|
0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50,
|
|
0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
|
|
0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62,
|
|
0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2,
|
|
}
|