USB driver using WriteFile and ReadFile not working in WIndows - windows

Background: I am a Linux expert and have very little experience in Windows. I am working on a windows driver for a USB device with 2 interfaces. My driver should open the second interface which is shown below and communicate with the interrupt OUT and IN endpoints.
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0 No Subclass
bInterfaceProtocol 0 None
iInterface 3
HID Device Descriptor:
bLength 9
bDescriptorType 33
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 1
I am able to track this interface using the String descriptor of this interface. And I followed the following methods to do the communication
I open the a Write and a Read handler
// Get a handle for writing Output reports.
WriteHandle=CreateFile(detailData->DevicePath,
GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
(LPSECURITY_ATTRIBUTES)NULL,OPEN_EXISTING,0,NULL);
//Get a handle to the device for the overlapped ReadFiles.
ReadHandle=CreateFile(detailData->DevicePath,
GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,(LPSECURITY_ATTRIBUTES)NULL,
OPEN_EXISTING,FILE_FLAG_OVERLAPPED,NULL);
And I try to push data like this
if(WriteFile(WriteHandle,buf,n,&BytesWritten,NULL)==FALSE) printf("Error sending output report\n");
if(ReadFile(ReadHandle,bufI,n,&NumberOfBytesRead,(LPOVERLAPPED) &HIDOverlapped)==FALSE) printf("Error receiving input report\n");
Question: This is not working. The problem I see is if the data in buf array used as WriteFile function argument is all zeros I see the data transmission. I am surprised as if I initialise the buf as
buf[0] = 0x80;
buf[1] = 0x00;
buf[2] = 0xDB;
Or any other data other than zeros then there is no transmission and the error message is printed. Also note n=65 and BytesWritten=0 when the function is invoked. I have confirmed the transmission of zeros using a hardware USB sniffer.
Could anybody point me out where have i gone wrong? Is there a better method to communicate to interrupt endpoints in Windows than using file read and write?

Related

PIC SPI Beginner Problems (XC8 MCC)

I've recently got I2C working in no time with some MCC generated functions which are well documented in the .h file, however SPI gives me nothing and is causing me frustraions, not helped by the fact I'm new to these serial protocols.
I'm simply trying to read a register on a MCP23S17, then write to it, then read it back again to verify it's changed.
I'm not even sure I'm going about this the right way but I've included my code below with comments. For some reason I seem to need to add some dummy writes to get the first read to work, but only happens on the 2nd loop though.
#include "mcc_generated_files/mcc.h"
uint8_t receiveData; /* Data that will be received */
uint8_t OpCodeW = 0x40;
uint8_t OpCodeR = 0x41;
void main(void)
{
SYSTEM_Initialize();
INTERRUPT_GlobalInterruptEnable();
INTERRUPT_PeripheralInterruptEnable();
Reset1_GPIO_SetLow();
__delay_ms(200);
Reset1_GPIO_SetHigh();
__delay_ms(200);
printf("Initalised \r\n");
while (1)
{
SPI1_Open(SPI1_DEFAULT);
CS1_GPIO_SetLow();
// Read IODIRA Register 0x00
SPI1_ExchangeByte(OpCodeR); // Address + Read
SPI1_ExchangeByte(0x00); // ??? -- When I add this in it works 2nd loop -- ???
receiveData = SPI1_ExchangeByte(0x00); // Returns 0x00 1st loop, then 0xFF after ...
// ... but only when duplicate sending of byte above ???
printf("Read IODIRA: 0x%02x \r\n", receiveData);
// Try writing to IODIRA Register
// Not sure what SPI1_WriteByte actually does!
// I thought it might be the same as ExchangeByte but without anything returned
// No idea!
SPI1_WriteByte(OpCodeW); // Address + Write
SPI1_WriteByte(0x00); // Register Addres IODIRA (Port A)
SPI1_WriteByte(0xF0); // Data to be written
// Read back changed IODIRA Register again - Same routine as above
SPI1_ExchangeByte(OpCodeR); // Address + Read
SPI1_ExchangeByte(0x00); // Same routine as above ...
// ... but always prints 0x00
receiveData = SPI1_ExchangeByte(0x00); // Register Address, IODIRA (Port A)
printf("Wrote to IODIRA and read back: 0x%02x \r\n", receiveData);
printf(" ----- \r\n\n");
CS1_GPIO_SetHigh();
SPI1_Close();
__delay_ms(5000);
}
}
The actual printed output looks like this:
Initalised
Read IODIRA: 0x00 // Should be 0xFF
Wrote to IODIRA and read back: 0x00 // Should be 0xF0
-----
Read IODIRA: 0xff // This is right now!
Wrote to IODIRA and read back: 0x00 // but this hasn't changed
-----
Read IODIRA: 0xff
Wrote to IODIRA and read back: 0x00
Read IODIRA: 0xff
Wrote to IODIRA and read back: 0x00
My main questions are am I approaching this the right way?
Why do I need some dummy writes to get it working on the 2nd loops? - clearly wrong
What's the difference between SPI1_ExchangeByte and SPI1_WriteByte.
Is there some documentation or guide to using these functions I'm missing?!?!?
Any help greatly appreciated.
Turns out the answer was the Chip Select need to be set (low) with each SPI write to tell the device when a message has started/completed. I was using this more like an enable (keeping the device CS low) all the time as I only wanted to talk to one device, but that was incorrect.

how to read a register in device driver?

in a linux device driver, in the init function for the device, I tried reading an address (which is SMMUv3 device for arm64) like below.
uint8_t *addr1;
addr1 = ioremap(0x09050000, 0x20000);
printk("SMMU_AIDR : 0x%X\n", *(addr1 + 0x1c));
but I get Internal error: synchronous external abort: 96000010 [#1] SMP error.
Is it not permitted to map an address to virtual address using ioremap and just reading that address?
I gave a fixed value 0x78789a9a to SMMU IDR[2] register. (at offset 0x8, 32 bit register. This is possible because it's qemu.)
SMMU starts at 0x09050000 and it has address space 0x20000.
__iomem uint32_t *addr1 = NULL;
static int __init my_driver_init(void)
{
...
addr1 = ioremap(0x09050000, 0x20000); // smmuv3
printk("SMMU_IDR[2] : 0x%X\n", readl(addr1 +0x08/4));
..}
This is the output when the driver is initialized.(The value is read ok)
[ 453.207261] SMMU_IDR[2] : 0x78789A9A
The first problem was that the access width was wrong for that address. Before, it was defined as uint8_t *addr1; and I used printk("SMMU_AIDR : 0x%X\n", *(addr1 + 0x1c)) so it was reading byte when it was not allowed by the SMMU model.
Second problem (I think this didn't cause the trap because arm64 provides memory mapped io) was that I used memory access(pointer dereferencing) for memory mapped IO registers. As people commented, I should have used readl function. (Mainly because to make the code portable. readl works also for iomap platforms like x86_64. using the mmio adderss as pointer will not work on such platforms. I later found that readl function takes care of the memory barrier problem too).
ADD : I fixed volatile to __iomem for variable addr1.(thanks #0andriy)

Access Raspberry Pi 4 system timer

I'm having trouble reading the Raspberry Pi 4 system timer.
My understanding is that the LO 32 bits should be at address 0x7e003004.
My reads always return -1.
Here's how I am trying:
int fd;
unsigned char* start;
uint32_t* t4lo;
fd = open("/dev/mem", O_RDONLY);
if (fd == -1)
{
perror("open /dev/mem");
exit(1);
}
start = (unsigned char*)mmap(0, getpagesize(), PROT_READ, MAP_SHARED,
fd, 0x7e003000);
t4lo = (unsigned int *)(start + 0x04);
...
uint32_t Rpi::readTimer(void)
{
return *t4lo;
}
I should be checking the value of start, but gdb tells me it's reasonable so I don't think that's the problem.
(gdb) p t4lo
$4 = (uint32_t *) 0xb6f3a004
and gdb won't let me access *t4lo. Any ideas?
Edit: clock_gettime() is fulfilling my needs, but I'm still curious.
A closer look at https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
figure 1 on page 5 shows that addresses vary depending upon who's looking at things. If you start with 0x7c00_0000 on the left side and follow it over to the right, it's apparent that it shows up at 0xfc00_0000 to the processor. So changing the timer base address to 0xfe00_3000 fixed the problem.
The secret is hidden in section 1.2.4:
So a peripheral described in this document as being at legacy address 0x7Enn_nnnn
is available in the 35-bit address space at 0x4_7Enn_nnnn, and visible to the ARM
at 0x0_FEnn_nnnn if Low Peripheral mode is enabled.
The address of the BCM2711 ARM Peripherals is the bus address which is not the same as the physical address in most systems. The bus address is easily used by DMA(Direct Memory Access) controller. mmap creates a new mapping from physical address to virtual address not bus address. So you can't use mmap funtion with parameter 0x7e003000. The rich answer is right.
So changing the timer base address to 0xfe00_3000 fixed the problem.
In addtion, your program run in the User space, only virtual address can you directly use.

Getting DTR and RTS pin of serial port in C on Windows platform

How to get DTR and RTS status of serial port on a windows platform? I want to read the current state (ON or OFF) of these two pins.
I can set pins with :
EscapeCommFunction(hSerial,SETRTS);
But I don't know how to read the pin status.
Since on Linux, it can be done with the following code, I assume it is technicaly feasable:
int status=0;
ioctl(fd, TIOCMGET, &status);
return status & TIOCM_RTS;
Using inc\api\ntddser.h API and winioctl.h, you can access DTR and RTS status. Call DeviceIoControl, set the second parameter to IOCTL_SERIAL_GET_DTRRTS:
Call:
DeviceIoControl(
handle, // handle returned by CreateFile
IOCTL_SERIAL_GET_DTRRTS,
NULL,
0,
&Status, // pointer to a DWORD variable 1
sizeof(Status),
&unused, // pointer to a DWORD variable
pOverlapped // optional pointer to overlapped buffer (may be NULL)
);
Documentation about DeviceIoControl here.
Unless you are actively changing the signal line, is the value set in DCB used?
Other than that, you control the signal line yourself, so you should remember it each time you change it.
As long as you have the serial port open, you have all control and nothing else will change.
Isn't there anybody who uses handshake or toggle mode now?
SetDefaultCommConfigW function
BOOL SetDefaultCommConfigW(
LPCWSTR lpszName,
LPCOMMCONFIG lpCC,
DWORD dwSize
);
SetCommConfig function
BOOL SetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG lpCC,
DWORD dwSize
);
GetCommConfig function
BOOL GetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG lpCC,
LPDWORD lpdwSize
);
COMMCONFIG structure
typedef struct _COMMCONFIG {
...
DCB dcb;
...
} COMMCONFIG, *LPCOMMCONFIG;
DCB structure
typedef struct _DCB {
DWORD DCBlength;
...
DWORD fDtrControl : 2;
...
DWORD fRtsControl : 2;
...
} DCB, *LPDCB;
DTR_CONTROL_DISABLE 0x00
DTR_CONTROL_ENABLE 0x01
DTR_CONTROL_HANDSHAKE 0x02
RTS_CONTROL_DISABLE 0x00
RTS_CONTROL_ENABLE 0x01
RTS_CONTROL_HANDSHAKE 0x02
RTS_CONTROL_TOGGLE 0x03
If you still want to do so, use DeviceIoControl() commented by #Hans Passant.
However, there is no guarantee that it is properly supported, since most people will not use it.
Device Input and Output Control (IOCTL)
DeviceIoControl function
The following is a sample DeviceIoControl call for a DISK drive, but you can call it by changing each of these parameters to those related to IOCTL_SERIAL_GET_DTRRTS for the serial port.
Calling DeviceIoControl
Serial Device Control Requests
IOCTL_SERIAL_GET_DTRRTS IOCTL

PN532 Tag Emulation - Understanding initial response and APDU data field

I have a pn532 which I'd like to present to a reader as a passive NFC tag with an NDEF record URL on it.
Following the PN532's User Manual I can put the device into target mode, and read the first ADPU command from the phone. I can use the iso-14443-4 pdf from 2005 to see that the command is A4, Read File, and that the two parameter bytes indicate "Select by DF name, first occurrence". Past that though, I don't know how to interpret the Data Field, which should the the name of a file being requested for reading.
Furthermore, I don't know how to interpret the initial command [E080] from the phone.
The program's output: (comments with #'s)
usart init.
i2c init.
pn532 init.
Firmware: 1.6
SAM config done.
Gen Status:
Err code: 0x00
Field: 0x00
Number of tags: 0x00
Initiating as target. # Here the pn532 waits for a reader.
tgInitAsTarget. Length: 5 # Callback function once the pn532 detects a reader and retrieves the first command from it.
Mode:
Baud: 106kbps, 14443-4: yes, DEP: no, Framing type: Mifare.
Initiator command:
0xE0
0x80
Entering tg loop.
callback: tgGetData. # The tgGetData command is sent without doing anything with the initial instructions.
status: 0x00
0x00 0xA4 0x04 0x00 0x07 0xD2 0x76 0x00 0x00 0x85 0x01 0x01 0x00
CLA: 0x00 # Single command, No SM, channel 0.
INS: 0xA4 # Select.
P1: 0x04 # Select by DF name.
P2: 0x00 # First/Only occurrence, return FCI template.
Lc: 0x07 # 7 data bytes.
Data: 0xD2 0x76 0x00 0x00 0x85 0x01 0x01 #???
Le: 0x00 # Any length response.
Furthermore, I don't know how to interpret the initial command [E080] from the phone.
I can point you at some starting points. E080 is part of RATS (Request for Answer To Select). Secion 5.9.2 (Rats command and ATS response) of this pdf (https://www.st.com/resource/en/datasheet/st25ta64k.pdf) breaks down the 0xE0 as the INS and 0x80 as the Param, which is a bit vector.

Resources