Win32 specific implementation details¶
This document details implementation details specific to Cahute’s usage of Win32.
Serial device management¶
Serial devices drivers are uniform across serial and USB-serial drivers with Win32; see Serial Communications in Win32 for more information.
Serial device detection using the Win32 API¶
In order to list available serial devices using Win32, the
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
registry key
contents is read, therefore making the same operations as GetCommPorts
with better compatibility.
Serial link handling using the Win32 API¶
Cahute can open a serial link using the Win32 API with the
cahute_open_win32_serial_link()
function.
This function interprets the provided name or path as a path,
and attempts at opening the device using CreateFile
.
If it succeeds, it calls SetCommTimeouts
with ReadTimeoutInterval
set
to MAXDWORD
in order to only read what is directly available, and create
the event for the overlapped object using CreateEvent
.
If it succeeds, the link can then be opened, with a HANDLE
with
Overlapped I/O and an internal buffer.
The available operations use :
Closing uses
CloseHandle
and, optionally,CancelIo
;Receiving uses
ReadFile
,WaitForSingleObject
, andGetOverlappedResult
;Sending uses
WriteFile
andWaitForSingleObject
, and depending on whether the second function succeeded or not, eitherGetOverlappedResult
orCancelIo
, to ensure we don’t have any buffer reads post-freeing the link;Serial params setting uses
SetCommState
.
Note
If WaitForSingleObject
on receiving ends with WAIT_TIMEOUT
, i.e.
if a timeout has occurred, rather than cancelling the call, the
respective function lets the call continue in the background.
This is because the function is also used by the CESG driver usage implementation in Cahute, which may crash under some circumstances when trying to cancel an overlapped read call; see #17 for more information.
The same overlapped event is used between calls to the receive implementation, until it completes or the link is closed.
Rather than reading directly in the provided buffer, the implementation reads into an internal buffer first, then copies the contents read in the internal buffer in the provided buffer. This is because, while the provided buffer may change from one call to the other, the internal buffer doesn’t, and the operation can safely continue asynchronously in between calls.
Other device management¶
The device tree model used by Cahute is implemented starting from Windows 2000 (NT 5.0). Said model has concepts of devices and device interfaces.
Devices are structured in a tree, meaning a device can have a parent (unless it is at root), and can have children. A device:
Has an identifier, as a unique string (e.g.
ACPI\PNP0200\4&1d401fb5&0
orUSB\Vid_07cf&Pid_6101\5&18f54cb7&0&2
);Has a class, also known as setup classes, represented as a GUID (UUID);
Can have one or more interfaces which each have classes, represented as a GUID (UUID).
When making a device query, the result can be navigated as a device information set:

A diagram showing the internal structure of a device information set.¶
While Windows Vista and later versions of Windows support a Unified Device Property Model which allows an application to retrieve a device from a device interface, earlier versions up to and including Windows 2000 have a more basic model.
Cahute can query devices and device interfaces using either SetupApi and CfgMgr32; Microsoft recommends the latter, so Cahute uses it exclusively.
Device detection using CfgMgr32 (Win32)¶
Cahute can detect drivers using CfgMgr32 on Win32 with the following filters:
An optional device identifier for which to find bus relations.
An example use of this by Cahute is to find the disk drive associated with a USB device, by placing the identifier of the USB device in this filter.
An optional device identifier for which to find removal relations.
Some relations between devices are not simple bus relations, but “removal” relations, meaning removing the first device removes the second one as well. An example in Cahute is with USB Mass Storage devices, where volumes are not directly associated with the disk drive through bus relations, but through removal relations.
An optional device (setup) class GUID.
This filters on devices with that class only, in order to look for e.g. USB devices, disk drives or volumes only rather than all devices on the system.
With Windows 7 and later systems, this can be passed to Cfgmgr32 directly using
CM_GETIDLIST_FILTER_CLASS
. Otherwise, all devices are returned by CfgMgr32, and Cahute queries and filters on the device class directly.An optional interface class GUID.
If passed, Cahute ensures that there exists at least one device interface for that device with this class.
Note
Since for systems older than Windows Vista, it is not possible to
retrieve the device from a device interface, if that last filter is set,
Cahute iterates over the devices, and calls
CM_Get_Device_Interface_List_SizeA
for every one.
In order to do so, it does the following in
cahute_enumerate_win32_devices()
:
It calls
CM_Get_Device_ID_List_SizeA
thenCM_Get_Device_ID_ListA
with parameters computed from the filters and the current Windows version;For each device:
It obtains the device instance using
CM_Locate_DevNodeA
, in order to use it to query device properties;It obtains the device address using
CM_Get_DevNode_Registry_PropertyA
withCM_DRP_ADDRESS
(eq. toSPDRP_ADDRESS
orDEVPKEY_Device_Address
).This address may be useful. For example, with USB, it represents the index of the port on which the USB device is plugged, and allows for querying USB-specific information later on;
It obtains the device class using
CM_Get_DevNode_Registry_PropertyA
withCM_DRP_CLASSGUID
(eq. toSPDRP_CLASSGUID
orDEVPKEY_Device_ClassGuid
).If the device class filter has been set and has not been passed in the call to
CM_Get_Device_ID_ListA
, this is used to make that check.It obtains the service using
CM_Get_DevNode_Registry_PropertyA
withCM_DRP_SERVICE
(eq. toSPDRP_SERVICE
orDEVPKEY_Device_Service
).This is used later on, to find out which driver runs on a USB device;
It obtains the driver name and version using
CM_Open_DevNode_Key
withCM_REGISTRY_SOFTWARE
, thenRegQueryValueExA
withREGSTR_VAL_DRVDESC
(eq. toDEVPKEY_Device_DriverDesc
) andREGSTR_VAL_DRIVERVERSION
(eq. toDEVPKEY_Device_DriverVersion
), then closing the handle usingRegCloseKey
.This is used later on, to implement more conditions on the driver. See Accessing device driver properties for more information.
If the address or the device class could not be gathered, the device is skipped.
If the interface class filter was set,
CM_Get_Device_Interface_List_SizeA
is set with the device ID and the class as filters.If the obtained size is 1 (single character, being a NUL byte), the device is skipped.
The device is correct and matches the filters! We can yield it.
USB device detection and access using the Win32 API¶
USB devices represent a fraction of the device tree, where:
All devices bear the
{36fc9e60-c465-11cf-8056-444553540000}
(DEVCLASS_USB
) or{88bae032-5a81-49f0-bc3d-a4ff138216d6}
(DEVCLASS_USB_DEVICE
) class;Hub devices have a single interface with the
{f18a0e88-c30c-11d0-8815-00a0c906bed8}
(DEVINTERFACE_USB_HUB
) class;Other devices may have an interface with the
{a5dcbf10-6530-11d2-901f-00c04fb951ed}
(DEVINTERFACE_USB_DEVICE
) class.
Note
One of the complexities of bridging the Cahute interface to the Win32 API is that while Cahute represents USB buses as integers, Win32 represents hubs (the equivalent of Cahute USB buses) as devices with string identifiers. This means before exploring USB devices, we must browse available hubs and assign them a numerical identifier.
Also note that bus identifiers must be consistent at least between calls on the same context, so the hub identifier to bus number mapping is kept as a context pointer.
USB device enumeration is done by cahute_enumerate_win32_usb_devices()
,
using the following steps:
Enumerate USB hubs, by enumerating devices with the setup class
{36fc9e60-c465-11cf-8056-444553540000}
(DEVCLASS_USB
), with at least one interface of class{f18a0e88-c30c-11d0-8815-00a0c906bed8}
(DEVINTERFACE_USB_HUB
).For each USB hub, if a number is not already assigned to it, assign the next available one.
Enumerate USB devices, by enumerating devices with the setup class
{36fc9e60-c465-11cf-8056-444553540000}
(DEVCLASS_USB
) or{88bae032-5a81-49f0-bc3d-a4ff138216d6}
(DEVCLASS_USB_DEVICE
), with at least one interface of class{a5dcbf10-6530-11d2-901f-00c04fb951ed}
(DEVINTERFACE_USB_DEVICE
).For each device:
Get the parent hub, by calling
CM_Get_Parent
until we obtain a device with a bus number.Get the parent hub device interface with class
{f18a0e88-c30c-11d0-8815-00a0c906bed8}
(DEVINTERFACE_USB_HUB
), and open a handle to it.Make a
DeviceIoControl
(
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
)
call on the obtained hub handle, using the device address as the connection index, and validate the VID/PID as well as the number of configurations (1).Make a
DeviceIoControl
(
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
)
call on the obtained hub handle, again using the device address as the connection index. Based on the result:Validate the number of interfaces (1).
Get the interface descriptor, and validate the interface class, subclass and protocol.
Match them to a transport protocol / detection entry type; see USB detection for CASIO calculators for more information.
Finally, determine the driver from the obtained device data, using the following table:
Service
Driver name
Driver version
Driver
USBSTOR
UMS driver
WinUSB
WinUSB serial / UMS driver
PVUSB
CESG502 USB
1.0.0.0
CESG502 (with capped read buffer capacity)
PVUSB
CESG502 USB
CESG502
Device communication using the CESG502 driver on Win32¶
CESG502 abstracts Serial transport over USB bulk
behind a stream-oriented device interface, on which to use the fileapi
(CreateFile
, ReadFile
, WriteFile
, CloseHandle
).
Note
The following versions of the driver are known:
Driver version |
Driver date |
Distributed with |
---|---|---|
|
|
FA-124 1.01 |
|
|
FA-124 2.04 |
|
|
FA-124 2.04 |
Note that at least 1.0.0.0
from 2002 is known to return 0x00000057
(ERROR_INVALID_PARAMETER
) if ReadFile
is called with a buffer
too large (4096 bytes work, 32768 do not).
Devices using the CESG502 driver, once opened, behaves mostly like native serial devices; see Serial link handling using the Win32 API for more information. It however has the following differences:
Serial specific parameters are not available (as this is USB);
It does the device enabling control flow automatically when the device is connected;
On reading, the provided buffer must be large enough to get all of the available data at once.
If using Protocol 7.00, 4096 bytes is enough, however in other contexts such as Protocol 7.00 Screenstreaming, 32768 bytes is safer.
Device communication using the generic volume driver on Win32¶
In case the calculator presents itself over USB as an USB Mass Storage device, the USB device is not to be interacted directly by Cahute.
Instead, the USB device has bus relations with a virtual disk drive
created automatically, being a device of class {4d36e967-e325-11ce-bfc1-08002be10318}
(DEVCLASS_DISKDRIVE
),
and the disk drive has removal relations (or bus relations if the former
do not exist) with a volume, being a device of class
{71a27cdd-812a-11d0-bec7-08002be2092f}
(DEVCLASS_VOLUME
). Then, from the volume, an interface of class
{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}
(DEVINTERFACE_VOLUME
) can be found, which can be opened.
Once opened as a HANDLE
, UMS operations use the following mechanisms:
Closing uses
CloseHandle
;Requesting using SCSI uses
DeviceIoControl
withIOCTL_SCSI_PASS_THROUGH_DIRECT
.
Device communication using WinUSB (serial over bulk)¶
In case the calculator presents itself as a serial over USB bulk
device, the USB device
has a device interface of class {a5dcbf10-6530-11d2-901f-00c04fb951ed}
(DEVINTERFACE_USB_DEVICE
) which can be
opened using WinUsb_Initialize
.
After initialization, the incoming and outgoing bulk pipe (endpoint) numbers
are queried using WinUsb_QueryInterfaceSettings
and
WinUsb_QueryPipe
, and the device enabling control flow
is run using WinUsb_ControlTransfer
.
From here, the operations use the following functions:
Closing uses
WinUsb_AbortPipe
andWinUsb_Free
;Reading uses
WinUsb_ReadPipe
,WaitForSingleObject
andWinUsb_GetOverlappedResult
;Writing uses
WinUsb_WritePipe
,WaitForSingleObject
andWinUsb_GetOverlappedResult
.
Note
In case of timeout, the read operation continues in the background, and is resumed on next read.
File handling using the Win32 API¶
When creating or opening a file on Win32, CreateFile
is called with the
appropriate options. Then, depending on the situation:
On creation, we want to set the file size to the provided one.
In order to do this, we call
SetFilePointer
to seek the provided file size fromFILE_BEGIN
,SetEndOfFile
to set the file size explicitely, and finally,SetFilePointer
again to seek toFILE_BEGIN
.On reading, we want to get the current file size.
In order to do this, we call
SetFilePointer
to seek 0 bytes fromFILE_END
, which returns the current file size, then the same function to seek 0 bytes fromFILE_BEGIN
.
Note
Files are opened without exclusivity, meaning another program may modify the file while it is being read or written.
When opening standard output on Win32, we call GetStdHandle
with
STD_OUTPUT_HANDLE
.
Once a file or stdout is opened, we have a HANDLE
we can then use with
the following operations:
Closing uses
CloseHandle
(except for the standard output, which we must not close);Reading uses
ReadFile
;Writing uses
WriteFile
;Seeking uses
SetFilePointer
.