This question already has answers here:
spidev Linux driver on Intel Atom board
(3 answers)
Closed 3 years ago.
I am attempting to expose the SPI #2 interface from the Intel E3900 series (specifically the E3940) as a spidev interface to CentOS 8 (kernel version 4.18). As a fallback, any method to access the SPI controller through a C/C++ API would be acceptable.
I am trying to determine if the issue is something that must be corrected by the BIOS vendor or something I can fix with an ACPI patch. The vendor (Congatec) has claimed that the SPI interface is currently not supported as a userspace entity, but I am still waiting for my issue to be escalated to their engineering group to confirm that. The vendor also stated that the BIOS setting for the SPIs should be left at "Disabled", but I have also tried the "PCI" and "ACPI" options with no change.
I have attempted to merge together snippets from several references to create an ACPI patch, including:
spidev Linux driver on Intel Atom board
https://www.kernel.org/doc/Documentation/acpi/initrd_table_override.txt
https://www.kernel.org/doc/html/latest/firmware-guide/acpi/enumeration.html
I have recompiled the CentOS 8 kernel to include all the necessary options, and I am able to successfully rebuild the Linux initrd (initramfs in CentOS 8). I confirmed through dmesg logs that my modification is being loaded at boot; I don't see any error messages in the logs, so I am assuming that it is being applied successfully.
For reference, here are the kernel options I have ensured are compiled in (=y). I plan to eventually use kernel modules in concert with the stock kernel, but for now I thought this was the easier path.
CONFIG_MFD_INTEL_LPSS
CONFIG_MFD_INTEL_LPSS_ACPI
CONFIG_MFD_INTEL_LPSS_PCI
CONFIG_X86_INTEL_LPSS
CONFIG_SERIAL_8250_LPSS
CONFIG_PWM_LPSS
CONFIG_PWM_LPSS_PCI
CONFIG_PWM_LPSS_PLATFORM
CONFIG_SPI_PXA2XX
CONFIG_SPI_SPIDEV
CONFIG_SPI_BITBANG
When I dump the unmodified ACPI device tree with the following commands, I am able to see references to three different SPI buses, which correlate with their BIOS settings. As far as I know, the Intel chip only includes two SPI buses, which makes me think this really is something that will need to be fixed in their BIOS.
acpidump >acpidump
acpixtract -a acpidump
iasl -sa *.dat
grep -i spi *.dsl
I have tried several options to patch the device tree that include both references I found and reusing the device-tree configuration from SPI#1 (which I am assuming works), but none have seemed to work. Since the examples I have found are from the E3800 series, I'm hoping that I just have some register setting or pin identifier wrong and it needs to be updated for the E3900 series.
For reference, the SPI#1 bus is used to control other components on the SOM, so I want to avoid using that for general purpose as well.
Thanks in advance for any ideas/support.
DefinitionBlock ("spidev.aml", "SSDT", 2, "INTEL ", "SpiDev", 1)
{
External (_SB_.PCI0.SPI2, DeviceObj)
Scope (\_SB.PCI0.SPI2)
{
Device (FPNT)
{
Method (_HID, 0, NotSerialized) // _HID: Hardware ID
{
Return ("FPNT_DIS")
}
Method (_STA, 0, NotSerialized) // _STA: Status
{
Return (0x0F)
}
Method (_CRS, 0, Serialized) // _CRS: Current Resource Settings
{
Name (BBUF, ResourceTemplate ()
{
SpiSerialBusV2 (0x0000, PolarityLow, FourWireMode, 0x08,
ControllerInitiated, 0x002DC6C0, ClockPolarityLow,
ClockPhaseFirst, "\\_SB.PCI0.SPI2",
0x00, ResourceConsumer, , Exclusive,
)
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0043
}
GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullDefault, 0x0000,
"\\_SB.GPO0", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x000E
}
})
Return (BBUF) /* \_SB_.PCI0.SPI2.FPNT._CRS.BBUF */
}
}
}
}
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
External (_SB_.PCI0.SPI2, DeviceObj)
Scope (\_SB.PCI0.SPI2)
{
Device (TP0) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI test device connected to CS2")
Name (_DSD, Package() {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package (2) { "compatible", "spidev" },
}
})
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
2, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI2", // SPI host controller
0 // Must be 0
)
})
}
}
}
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
External (_SB_.PCI0.SPI2, DeviceObj)
Scope (\_SB.PCI0.SPI2)
{
Device (TP0) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI test device connected to CS2")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
2, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI2", // SPI host controller
0 // Must be 0
)
})
}
}
}
EDIT: Added dmesg output after applying above via initrd
[ 0.000000] BRK [0x63ef9000, 0x63ef9fff] PGTABLE
[ 0.000000] BRK [0x63efa000, 0x63efafff] PGTABLE
[ 0.000000] RAMDISK: [mem 0x3b4f6000-0x3ce5cfff]
[ 0.000000] ACPI: SSDT ACPI table found in initrd [kernel/firmware/acpi/spidev.aml][0xb7]
[ 0.000000] modified physical RAM map:
[ 0.000000] modified: [mem 0x0000000000000000-0x0000000000000fff] reserved
[ 0.000000] modified: [mem 0x0000000000001000-0x000000000003efff] usable
--
[ 0.000000] ACPI: UEFI 0x00000000798C8400 000042 (v01 ALASKA A M I 00000000 00000000)
[ 0.000000] ACPI: TPM2 0x00000000798C8450 000034 (v04 ALASKA A M I 00000001 AMI 00000000)
[ 0.000000] ACPI: WDAT 0x00000000798C8490 000104 (v01 00000000 00000000)
[ 0.000000] ACPI: Table Upgrade: install [SSDT- INTEL- SPIDEV]
[ 0.000000] ACPI: SSDT 0x00000000774A2000 0000B7 (v05 INTEL SPIDEV 00000001 INTL 20180629)
[ 0.000000] ACPI: Local APIC address 0xfee00000
[ 0.000000] No NUMA configuration found
[ 0.000000] Faking a node at [mem 0x0000000000000000-0x000000017fffffff]
--
[ 1.141553] dw-apb-uart.0: ttyS4 at MMIO 0x91326000 (irq = 4, base_baud = 115200) is a 16550A
[ 1.144263] dw-apb-uart.1: ttyS5 at MMIO 0x91324000 (irq = 5, base_baud = 115200) is a 16550A
[ 1.146886] dw-apb-uart.2: ttyS6 at MMIO 0x91322000 (irq = 6, base_baud = 115200) is a 16550A
[ 1.149799] pxa2xx-spi pxa2xx-spi.4: cs2 >= max 2
[ 1.151063] spi_master spi2: failed to add SPI device SPT0001:00 from ACPI
[ 1.153366] rdac: device handler registered
[ 1.154791] hp_sw: device handler registered
[ 1.156043] emc: device handler registered
[root#localhost ~]# ls /dev/
autofs fuse log nvram tty tty25 tty42 tty6 ttyS5 vcsa1
block gpiochip0 loop-control port tty0 tty26 tty43 tty60 ttyS6 vcsa2
bus gpiochip1 mapper ppp tty1 tty27 tty44 tty61 ttyS7 vcsa3
char gpiochip2 mcelog pps0 tty10 tty28 tty45 tty62 ttyS8 vcsa4
console gpiochip3 mei0 pps1 tty11 tty29 tty46 tty63 ttyS9 vcsa5
core hidraw0 mem ptmx tty12 tty3 tty47 tty7 uhid vcsa6
cpu hpet memory_bandwidth ptp0 tty13 tty30 tty48 tty8 uinput vfio
cpu_dma_latency hugepages mmcblk1 ptp1 tty14 tty31 tty49 tty9 urandom vga_arbiter
cs hwrng mmcblk1boot0 pts tty15 tty32 tty5 ttyS0 usbmon0 vhci
cuse i2c-0 mmcblk1boot1 random tty16 tty33 tty50 ttyS1 usbmon1 vhost-net
disk i2c-1 mmcblk1p1 raw tty17 tty34 tty51 ttyS10 usbmon2 vhost-vsock
dm-0 i2c-2 mmcblk1p2 rtc tty18 tty35 tty52 ttyS11 vcs zero
dm-1 i2c-3 mmcblk1p3 rtc0 tty19 tty36 tty53 ttyS12 vcs1
dri i2c-4 mmcblk1rpmb shm tty2 tty37 tty54 ttyS13 vcs2
drm_dp_aux0 i2c-5 mqueue snapshot tty20 tty38 tty55 ttyS14 vcs3
drm_dp_aux1 initctl net snd tty21 tty39 tty56 ttyS15 vcs4
fb0 input network_latency stderr tty22 tty4 tty57 ttyS2 vcs5
fd kmsg network_throughput stdin tty23 tty40 tty58 ttyS3 vcs6
full kvm null stdout tty24 tty41 tty59 ttyS4 vcsa
EDIT: Added requested tables.dat output
https://pastebin.com/TBj8LRVc
EDIT: Added requested status output
[root#localhost ~]# grep -H 15 /sys/bus/acpi/devices/*/status
/sys/bus/acpi/devices/device:19/status:15
/sys/bus/acpi/devices/device:1a/status:15
/sys/bus/acpi/devices/device:1d/status:15
/sys/bus/acpi/devices/device:3e/status:15
/sys/bus/acpi/devices/device:44/status:15
/sys/bus/acpi/devices/device:45/status:15
/sys/bus/acpi/devices/INT33A1:00/status:15
/sys/bus/acpi/devices/INT3452:00/status:15
/sys/bus/acpi/devices/INT3452:01/status:15
/sys/bus/acpi/devices/INT3452:02/status:15
/sys/bus/acpi/devices/INT3452:03/status:15
/sys/bus/acpi/devices/INT3511:00/status:15
/sys/bus/acpi/devices/INT3512:00/status:15
/sys/bus/acpi/devices/LNXPOWER:00/status:15
/sys/bus/acpi/devices/MSFT0101:00/status:15
/sys/bus/acpi/devices/PNP0103:00/status:15
/sys/bus/acpi/devices/PNP0C0D:00/status:15
/sys/bus/acpi/devices/PNP0C0E:00/status:15
EDIT: Added requested lspci output
[root#localhost ~]# lspci -nk -s 19
00:19.0 1180: 8086:5ac2 (rev 0b)
Subsystem: 8086:7270
Kernel driver in use: intel-lpss
00:19.1 1180: 8086:5ac4 (rev 0b)
Subsystem: 8086:7270
Kernel driver in use: intel-lpss
00:19.2 1180: 8086:5ac6 (rev 0b)
Subsystem: 8086:7270
Kernel driver in use: intel-lpss
Thanks to 0andriy! He got me past the roadblock and taught me a few new commands along the way. The root cause of my issue was two-fold as it turned out:
The board vendor had cautioned me against enabling SPI#1 in BIOS, as that bus is used to control items on the SoM itself (assuming via their Linux BSP/driver?). I had to enable all three SPI interfaces in ACPI mode to have them be loaded and show up in the lspci -nk -s 19 output.
The device-tree update file had an error, which I missed previously because the interface itself was not being loaded. The AML file needed to specify Chip Select 1, not 2.
The script below will make all of the initrd changes and expose all three SPI buses using SPIDEV. On the board I am testing with, the SPI bus is coming through as spidev1.
I still need to confirm the Maximum speed the E3900 can handle, but I think the other parameters are set correctly.
#!/bin/bash
#
# SCRIPT NAME: ENABLE SPIDEV ON INTEL ATOM E3900 SERIES SOC
# TARGET PLATFORM: CENTOS8_x86-64
# AUTHOR: ADAM ACKERMAN
# LICENSE: MIT
#
# REFERENCES:
# https://www.kernel.org/doc/Documentation/acpi/initrd_table_override.txt
# https://stackoverflow.com/questions/39118721/spidev-linux-driver-on-intel-atom-board
# https://www.kernel.org/doc/html/latest/firmware-guide/acpi/enumeration.html
#
# Pull current kernel version
KERNEL_VER=$(cat /proc/version | cut -d " " -f 3)
# Verify current kernel includes spidev support
# NOTE: If configured as module, must be actively loaded
if [[ ! -d /sys/class/spidev ]]; then
modprobe spidev
if [[ ! -d /sys/class/spidev ]]; then
echo "Kernel does not support SPIDEV. Please enable first."
exit 1
fi
fi
# Move the backup file back to active, if exists
if [[ -f /boot/initramfs-$KERNEL_VER.img.bak ]]; then
rm -f /boot/initramfs-$KERNEL_VER.img
mv /boot/initramfs-$KERNEL_VER.img.bak /boot/initramfs-$KERNEL_VER.img
fi
# Create new temp directory and change to it
ACPI_TMP=$(mktemp -d)
cd $ACPI_TMP
# Reference commands to pull current ACPI tree
#acpidump >acpidump
#acpixtract -a acpidump
#iasl -sa *.dat
#grep -i spi *.dsl
# Paste in ASL file to enable the SPIDEV interface
cat > spidev.asl <<'_EOF'
DefinitionBlock ("spidev.aml", "SSDT", 5, "INTEL", "SPIDEV", 1)
{
External (_SB_.PCI0.SPI1, DeviceObj)
Scope (\_SB.PCI0.SPI1)
{
Device (TP10) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI1-CS0")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
0, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI1", // SPI host controller
0 // Must be 0
)
})
}
Device (TP11) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI1-CS1")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
1, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI1", // SPI host controller
0 // Must be 0
)
})
}
}
External (_SB_.PCI0.SPI2, DeviceObj)
Scope (\_SB.PCI0.SPI2)
{
Device (TP20) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI2-CS0")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
0, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI2", // SPI host controller
0 // Must be 0
)
})
}
Device (TP21) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI2-CS1")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
1, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI2", // SPI host controller
0 // Must be 0
)
})
}
}
External (_SB_.PCI0.SPI3, DeviceObj)
Scope (\_SB.PCI0.SPI3)
{
Device (TP30) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI3-CS0")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
0, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI3", // SPI host controller
0 // Must be 0
)
})
}
Device (TP31) {
Name (_HID, "SPT0001")
Name (_DDN, "SPI3-CS1")
Name (_CRS, ResourceTemplate () {
SpiSerialBus (
1, // Chip select
PolarityLow, // Chip select is active low
FourWireMode, // Full duplex
8, // Bits per word is 8 (byte)
ControllerInitiated, // Don't care
1000000, // 1 MHz
ClockPolarityLow, // SPI mode 0
ClockPhaseFirst, // SPI mode 0
"\\_SB.PCI0.SPI3", // SPI host controller
0 // Must be 0
)
})
}
}
}
_EOF
# Convert the ASL file to AML
iasl spidev.asl
# Create new directory structure to match initrd format
mkdir -p kernel/firmware/acpi
# Copy in the AML file
cp spidev.aml kernel/firmware/acpi
# Load all files into a new initrd in /boot
find kernel | cpio -H newc --create > /boot/instrumented_initrd
# Move out of the temporary directory and remove
cd ~
rm -rf $ACPI_TMP
# Merge the current initrd to the end of the one just created
cat /boot/initramfs-$KERNEL_VER.img >>/boot/instrumented_initrd
# Move the working one to a backup location
mv /boot/initramfs-$KERNEL_VER.img /boot/initramfs-$KERNEL_VER.img.bak
# Move the new one into place
mv /boot/instrumented_initrd /boot/initramfs-$KERNEL_VER.img
# Script Finished
echo "Process Complete - reboot the system for the changes to take effect."
echo "After reboot, verify success with command 'dmesg | grep -i spi'"
The resulting device list is:
[root#localhost ~]# ls /dev/spi*
/dev/spidev1.0 /dev/spidev1.1 /dev/spidev2.0 /dev/spidev2.1 /dev/spidev3.0 /dev/spidev3.1
I have troubles to get my streaming over OTG-USB-FS configured as VCP. In my disposition I have nucleo-h743zi board that seems to doing a good job at sending me data, but on PC side I have a problem to receive that data.
for(;;) {
#define number_of_ccr 1024
unsigned int lpBuffer[number_of_ccr] = {0};
unsigned long nNumberOfBytesToRead = number_of_ccr*4;
unsigned long lpNumberOfBytesRead;
QueryPerformanceCounter(&startCounter);
ReadFile(
hSerial,
lpBuffer,
nNumberOfBytesToRead,
&lpNumberOfBytesRead,
NULL
);
if(!strcmp(lpBuffer, "end\r\n")) {
CloseHandle(FileHandle);
fprintf(stderr, "end flag was received\n");
break;
}
else if(lpNumberOfBytesRead > 0) {
// NOTE(): succeed
QueryPerformanceCounter(&endCounter);
time = Win32GetSecondsElapsed(startCounter, endCounter);
char *copyString = "copy";
WriteFile(hSerial, copyString , strlen(copyString), &bytes_written, NULL);
DWORD BytesWritten;
// write data to file
WriteFile(FileHandle, lpBuffer, nNumberOfBytesToRead, &BytesWritten, 0);
}
}
QPC shows that speed was 0.00733297970 - it's one time for one successful data block transfer (1024*4 bytes).
this is the Listener code, I bet that this is not how it should be done, so I here to seek advices. I was hopping that maybe full streaming without control sequences ("copy") will be possible, but in that case I can't receive adjacent data (within one transfer block it's OKAY, but two consecutive received blocks aren't adjacent.
Example:
block_1: 1 2 3 4 5 6
block_2: 13 14 15 16 17 18
Is there any way to speed up my receiving?
(I was trying O2 key without any success)
You need to configure buffer on PC side that will be 2 or 3 times the buffer you are transfer from your board, and use something like double buffer scheme for transferring the data. You transfer the first buffer while filing the second, then alternate.
Good thing to do is to activate caches, and place the buffers in fast memory for stm32h7 (it's 1 domain RAM).
But if your interface do not match the speed you needed, there will be no tricks to do this. Except maybe one, if your controller is fast enough -> you can implement and use lossless data compression on that data of yours and transfer compressed files. If you transmit low entropy data, this could give you a solid boost in speed.
I'm pretty new to coding. I'm trying to read a PT100 rtd via my Raspberry Pi 3. I read that I needed the Max31865 RTD amplifier to properly read the data because the resistances are so small. I am fairly certain I have it plugged in correctly.
I'm using this code, only slightly editted.
https://github.com/steve71/MAX31865
I'm getting two different outputs so far but it doesn't seem to correlate with anything I'm changing (The byte associated with the readTemp mostly) since I've run the same code twice and gotten both outputs. The outputs are as follows:
config register byte: ff
RTD ADC Code: 32767
PT100 Resistance: 429.986877 ohms
Straight Line Approx. Temp: 767.968750 degC
Callendar-Van Dusen Temp (degC > 0): 988.792111 degC
high fault threshold: 32767
low fault threshold: 32767
and
config register byte: 08
RTD ADC Code: 0
PT100 Resistance: 0.000000 ohms
Straight Line Approx. Temp: -256.000000 degC
Callendar-Van Dusen Temp (degC > 0): -246.861024 degC
high fault threshold: 0
low fault threshold: 0
Any help would be appreciated.
I'am dealing exactly with the same issue right now. Do you use your Pt100 with 3- or 4-wires?
I fixed the problem by setting the correct configuration status register in Line 78 of the original code (https://github.com/steve71/MAX31865) to 0xA2
self.writeRegister(0, 0xA2)
I am using 4-wires, so i had to change bit4 from 1 (3-wires) to 0 (2- or 4-wires)
0xb10100010
After this, i've got this as output
config register byte: 80
RTD ADC Code: 8333
PT100 Resistance: 101.721191 ohms
Straight Line Approx. Temp: 4.406250 degC
Callendar-Van Dusen Temp (degC > 0): 4.406808 degC
high fault threshold: 32767
low fault threshold: 0
Brrr... it's very cold in my room, isn't it? To fix this, i had to change the reference resistance in Line 170 to 430 Ohm
R_REF = 430.0 # Reference Resistor
It's curious, because i red a lot of times, there is a 400 Ohm resistance mounted on this devices as the reference. Indeed, on the SMD resistor is a 3-digit Code "431" which means 430 Ohm. Humm...
But now i have it nice and warm in here
Callendar-Van Dusen Temp (degC > 0): 25.091629 degC
Best regards
Did you get this resolved ? In case you didn't, the below python class method works for me. I remember that I had some trouble with wiring the force terminals, from memory for 2-wire you have to bridge both force terminals.
def _take_Resistance_Reading(self):
msg = '%s: taking resistance reading...' % self.Name
try:
self.Logger.debug(msg + 'entered method take_resistance_Reading()')
with self._RLock:
reg = self.spi.readbytes(9)
del reg[0] # delete 0th dummy data
self.Logger.debug("%s: register values: %s", self.Name, reg)
RTDdata = reg[1] << 8 | reg[2]
self.Logger.debug("%s: RTD data: %s", self.Name, hex(RTDdata))
ADCcode = RTDdata >> 1
self.Logger.debug("%s: ADC code: %s", self.Name, hex(ADCcode))
self.Vout = ADCcode
self._Resistance = round(ADCcode * self.Rref / 8192, 1)
self.Logger.debug(msg + "success, Vout: %s, resistance: %s Ohm" % (self.Vout, self._Resistance))
return True
except Exception as e:
I want to calculate the end offset of a parent locator in a VHD. Here is a part of the VHD header:
Cookie: cxsparse
Data offset: 0xffffffffffffffff
Table offset: 0x2000
Header version: 0x00010000
Max table entries: 10240
Block size: 0x200000
Checksum: 4294956454
Parent Unique Id: 0x9678bf077e719640b55e40826ce5d178
Parent time stamp: 525527478
Reserved: 0
Parent Unicode name:
Parent locator 1:
- platform code: 0x57326b75
- platform_data_space: 4096
- platform_data_length: 86
- reserved: 0
- platform_data_offset: 0x1000
Parent locator 2:
- platform code: 0x57327275
- platform_data_space: 65536
- platform_data_length: 34
- reserved: 0
- platform_data_offset: 0xc000
Some definitions from the Virtual Hard Disk Image Format Specification:
"Table Offset: This field stores the absolute byte offset of the Block Allocation Table (BAT) in the file.
Platform Data Space: This field stores the number of 512-byte sectors needed to store the parent hard disk locator.
Platform Data Offset: This field stores the absolute file offset in bytes where the platform specific file locator data is stored.
Platform Data Length. This field stores the actual length of the parent hard disk locator in bytes."
Based on this the end offset of the two parent locators should be:
data offset + 512 * data space:
0x1000 + 512 * 4096 = 0x201000
0xc000 + 512 * 65536 = 0x200c000
But if one uses only data offset + data space:
0x1000 + 4096 = 0x2000 //end of parent locator 1, begin of BAT
0xc000 + 65536 = 0x1c000
This latter calculation makes much more sense: the end of the first parent locator is the beginning of the BAT (see header data above); and since the first BAT entry is 0xe7 (sector offset), this corresponds to file offset 0x1ce00 (sector offset * 512), which is OK, if the second parent locator ends at 0x1c000.
But if one uses the formula data offset + 512 * data space, he ends up having other data written in the parent locator. (But, in this example there would be no data corruption, since Platform Data Length is very small)
So is this a mistake in the specification, and the sentence
"Platform Data Space: This field stores the number of 512-byte sectors needed to store the parent hard disk locator."
should be
"Platform Data Space: This field stores the number of bytes needed to store the parent hard disk locator."?
Apparently Microsoft does not care about correcting their mistake, this being already discovered by Virtualbox developers. VHD.cpp contains the following comment:
/*
* The VHD spec states that the DataSpace field holds the number of sectors
* required to store the parent locator path.
* As it turned out VPC and Hyper-V store the amount of bytes reserved for the
* path and not the number of sectors.
*/
How can I know if a TIFF image is in the format CCITT T.6(Group 4)?
You can use this (C#) code example.
It returns a value indicating the compression type:
1: no compression
2: CCITT Group 3
3: Facsimile-compatible CCITT Group 3
4: CCITT Group 4 (T.6)
5: LZW
public static int GetCompressionType(Image image)
{
int compressionTagIndex = Array.IndexOf(image.PropertyIdList, 0x103);
PropertyItem compressionTag = image.PropertyItems[compressionTagIndex];
return BitConverter.ToInt16(compressionTag.Value, 0);
}
You can check these links
The TIFF File Format
TIFF Tag Compression
TIFF File Format Summary
The tag 259 (hex 0x0103) store the info about the Compression method.
--- Compression
Tag = 259 (103)
Type = word
N = 1
Default = 1.
1 = No compression, but pack data into bytes as tightly as possible, with no
unused bits except at the end of a row. The bytes are stored as an array
of bytes, for BitsPerSample <= 8, word if BitsPerSample > 8 and <= 16, and
dword if BitsPerSample > 16 and <= 32. The byte ordering of data >8 bits
must be consistent with that specified in the TIFF file header (bytes 0
and 1). Rows are required to begin on byte boundaries.
2 = CCITT Group 3 1-Dimensional Modified Huffman run length encoding.
See ALGRTHMS.txt BitsPerSample must be 1, since this type of compression
is defined only for bilevel images (like FAX images...)
3 = Facsimile-compatible CCITT Group 3, exactly as specified in
"Standardization of Group 3 facsimile apparatus for document
transmission," Recommendation T.4, Volume VII, Fascicle VII.3,
Terminal Equipment and Protocols for Telematic Services, The
International Telegraph and Telephone Consultative Committee
(CCITT), Geneva, 1985, pages 16 through 31. Each strip must
begin on a byte boundary. (But recall that an image can be a
single strip.) Rows that are not the first row of a strip are
not required to begin on a byte boundary. The data is stored as
bytes, not words - byte-reversal is not allowed. See the
Group3Options field for Group 3 options such as 1D vs 2D coding.
4 = Facsimile-compatible CCITT Group 4, exactly as specified in
"Facsimile Coding Schemes and Coding Control Functions for Group
4 Facsimile Apparatus," Recommendation T.6, Volume VII, Fascicle
VII.3, Terminal Equipment and Protocols for Telematic Services,
The International Telegraph and Telephone Consultative Committee
(CCITT), Geneva, 1985, pages 40 through 48. Each strip must
begin on a byte boundary. Rows that are not the first row of a
strip are not required to begin on a byte boundary. The data is
stored as bytes, not words. See the Group4Options field for
Group 4 options.
5 = LZW Compression, for grayscale, mapped color, and full color images.
You can run identify -verbose from the ImageMagick suite on the image. Look for "Compression: Group4" in the output.
UPDATE:
SO, I downloaded the libtiff library from the link I mentioned before, and from what I've seen, you can do the following: (untested)
int isTIFF_T6(const char* filename)
{
TIFF* tif= TIFFOpen(filename,"r");
TIFFDirectory *td = &tif->tif_dir;
if(td->td_compression == COMPRESSION_CCITTFAX4) return 1;
return 0;
}
PREVIOUS:
This page has a lot of information about this format and links to some code in C:
Here's an excerpt:
The following paper covers T.4, T.6
and JBIG:
"Review of standards for electronic
imaging for facsimile systems" in
Journal of Electronic Imaging, Vol. 1,
No. 1, pp. 5-21, January 1992.
Source code can be obtained as part of
a TIFF toolkit - TIFF image
compression techniques for binary
images include CCITT T.4 and T.6:
ftp://ftp.sgi.com/graphics/tiff/tiff-v3.4beta035-tar.gz
Contact: sam#engr.sgi.com
Read more: http://www.faqs.org/faqs/compression-faq/part1/section-16.html#ixzz0TYLGKnHI