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.

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 or USB\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:

../../../_images/win32-devinfosets.png

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 then CM_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 with CM_DRP_ADDRESS (eq. to SPDRP_ADDRESS or DEVPKEY_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 with CM_DRP_CLASSGUID (eq. to SPDRP_CLASSGUID or DEVPKEY_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 with CM_DRP_SERVICE (eq. to SPDRP_SERVICE or DEVPKEY_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 with CM_REGISTRY_SOFTWARE, then RegQueryValueExA with REGSTR_VAL_DRVDESC (eq. to DEVPKEY_Device_DriverDesc) and REGSTR_VAL_DRIVERVERSION (eq. to DEVPKEY_Device_DriverVersion), then closing the handle using RegCloseKey.

      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:

  1. 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.

  2. 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:

    1. Get the parent hub, by calling CM_Get_Parent until we obtain a device with a bus number.

    2. Get the parent hub device interface with class {f18a0e88-c30c-11d0-8815-00a0c906bed8} (DEVINTERFACE_USB_HUB), and open a handle to it.

    3. 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).

    4. 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.

    5. 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

1.0.0.0

06/12/2002 (June 12th, 2002)

FA-124 1.01

1.0.0.0

04/10/2007 (April 10th, 2007)

FA-124 2.04

1.0.2.0

01/29/2007 (January 29th, 2007)

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:

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:

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 from FILE_BEGIN, SetEndOfFile to set the file size explicitely, and finally, SetFilePointer again to seek to FILE_BEGIN.

  • On reading, we want to get the current file size.

    In order to do this, we call SetFilePointer to seek 0 bytes from FILE_END, which returns the current file size, then the same function to seek 0 bytes from FILE_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: