WinUsbCDC 1.7
Python package for communicating with USB / CDC devices on windows via the WinUsb driver
License: MIT

WinUsbCDC
=========

WinUsbCDC is a python package for communicating with USB / CDC devices
on windows via the built-in
`WinUsb <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb-installation>`__
driver.

It's intended for communicating with USB CDC (virtual com port) devices
on windows without using the built in usbserial.sys driver as it is
rather unstable and has data loss issues on Win 10 -
https://wiki.segger.com/CDC#CDC-ACM\_issues\_on\_Windows\_10 -
https://community.nxp.com/thread/458640 -
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/671638cd-0aed-4cae-80f2-860c1c551347/usb-cdc-acm-data-lost?forum=wdk

The standard means of usage is via the
``winusbcdc.ComPort(name, vid, pid)`` class which roughly matches the
``pyserial.Serial`` class api.

This module is based directly on
`WinUsbPy <https://github.com/felHR85/WinUsbPy>`__ and as such also
exposes the same WinUSB api: - A 1:1 wrapper over WinUsb which allows
calling C++ functions directly from involved dlls. - A high level api
which simplifies a lot of C++/windll/ctypes messy interactions offering
just a bunch of easy methods.

Install WinUsbCDC
=================

::

    pip install winusbcdc

::

    python setup.py install

USB CDC Device
==============

Ensure your device has the WinUSB driver configured. This can be done
with `Zadig <https://zadig.akeo.ie/>`__, `libusbk
InfWizard <https://osdn.net/projects/sfnet_libusb-win32/downloads/libusb-win32-releases/libusbK-inf-wizard.exe/>`__
or similar.

.. code:: python

    from winusbcdc import ComPort
    p = ComPort("My USB Device")  # friendly name as shown in device manager 
    # or 
    p = ComPort(vid=0xF055, pid=0x9800)

    p.open()
    p.write(b'foo')
    print(p.read())
    p.close()

Low Level WinUsbPy Api
======================

Low level api offers three methods for invoking functions from three
different dlls.

.. code:: python

    #args: arguments of the C++ function called
    def exec_function_winusb(self, function_name, *args):
    def exec_function_kernel32(self, function_name, *args):
    def exec_function_setupapi(self, function_name, *args):

if we need to call
`SetupDiGetClassDevs <http://msdn.microsoft.com/en-us/library/windows/hardware/ff551069%28v=vs.85%29.aspx>`__
which presents this prototype:

``c++ HDEVINFO SetupDiGetClassDevs(_In_opt_ const GUID *ClassGuid,_In_opt_ PCTSTR Enumerator,_In_opt_ HWND hwndParent,_In_ DWORD Flags);``

.. code:: python

    from winusbpy import *
    from ctypes import *
    from ctypes.wintypes import *
    from winusbclasses import DIGCF_DEVICE_INTERFACE, DIGCF_PRESENT

    api = WinUSBApi()
    byte_array = c_byte * 8
    guid = GUID(0xA5DCBF10L, 0x6530, 0x11D2, byte_array(0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED))
    flags = DWORD(DIGCF_DEVICE_INTERFACE | DIGCF_PRESENT)

    hdev_info = api.exec_function_setupapi("SetupDiGetClassDevs", byref(guid), None, None, flags)

`Good resources of WinUsb if you develop using this low level
layer <http://msdn.microsoft.com/en-us/library/windows/hardware/ff540174(v=vs.85).aspx>`__

High Level WinUsbPy Api
=======================

Built on top of the low level wrapper is a more usable api to perform
common USB operations. Here it is list of defined functions:

.. code:: python

    # Possible keyword arguments: default, present, allclasses, profile, deviceinterface (Boolean), Usually called as follows list_usb_devices(deviceinterface=True, present=True)
    def list_usb_devices(self, **kwargs):

    # vid and pid must be str, returns True if device was correctly initialized and False otherwise
    def init_winusb_device(self, vid, pid): 

    # path must be str, returns True if device was correctly initialized and False otherwise
    # example init_winusb_device_with_path("\\\\?\\usb#vid_9999&pid_0102#3555303335351909000b0#{a5dcbf10-6530-11d2-901f-00c04fb951ed}")
    def init_winusb_device_with_path(self, path): 

    # Returns True if device was correctly closed and False otherwise.
    def close_winusb_device(self):

    # Returns last error code. See http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382%28v=vs.85%29.aspx
    def get_last_error_code(self):

    # Returns information for a open device (0x03:High Speed, 0x01:full-speed or lower), query=1 in order to get USB speed.
    def query_device_info(self, query=1):

    # Returns a UsbInterfaceDescriptor object with information about a specified interface
    def query_interface_settings(self, index):

    # Change current interface, Winusb opens first interface (0 index) when a device is initialized
    def change_interface(self, index):

    # Returns a PipeInfo object with information of a specified pipe within current interface
    def query_pipe(self, pipe_index):

    # Send a control requesto to open device, setup_packet is a UsbSetupPacket object.
    # buff = None implies no data is going to be transferred besides setup packet
    # buff = [0] create a buffer of length 1. Buffer could be IN or OUT, direction is defined in setup packet
    # it returns a dict with the response and with the buffer under the keywords 'result' and 'buffer'
    def control_transfer(self, setup_packet, buff=None):

    #Send Bulk data to the Usb device, write_buffer must be a str buffer
    def write(self, pipe_id, write_buffer):

    #Read Bulk data from the Usb device, Returns of a buffer not greater than length_buffer length
    def read(self, pipe_id, length_buffer):

Let's say hello to our device:

.. code:: python

    from winusbpy import *
    vid = "vid_device" # for example: VID:067b PID:2303
    pid = "pid_device"

    api = WinUsbPy()
    result = api.list_usb_devices(deviceinterface=True, present=True)
    if result:
      if api.init_winusb_device(pl2303_vid, pl2303_pid):
        api.write(0x02, "hello")

Real examples
=============

If you run winusbcdc as a module it should act like a basic serial
terminal, see ``__main__.py`` for the code used.

::

    python -m winusbcdc --name "My USB Device"


In "Examples" folder there are two WinUsbPy examples configuring a PL2303
serial usb device, listing characteristics and sending data.

`Using WinUsbPy Low Level
Api <https://github.com/felHR85/WinUsbPy/blob/master/winusbpy/examples/winusbtest.py>`__

`Using WinUsbPy High Level
Api <https://github.com/felHR85/WinUsbPy/blob/master/winusbpy/examples/winusbtest2.py>`__



version: 1.7