USB port programming. Programming simple USB devices in Delphi. We organize automatic connection of the device

  • Date of: 14.05.2021


fig.1 Illustration android work devices in USB Host and Accessory modes (picture from http://developer.android.com)

Note that using USB is not the only way to communicate with the same homemade device. Android allows you to use more, NFC, Wi-Fi P2P, SIP, as well as a standard network connection. So in the arsenal of the developer there are enough opportunities to implement their wildest ideas.

Another common option for communicating with various devices is still the use of a USB-COM adapter. There is material on the net on using the USB-COM adapter in Android - see, for example,. The popularity of such a connection is due to the presence of a large number of devices already developed using various microcontrollers, communication with which is carried out using a COM port (serial port), which 10 years ago was almost a standard way to transfer data from a computer to a home-made piece of hardware.

Compared to the COM port, the use of USB can significantly increase the speed of data transfer and make this process user-friendly. Transmission speed, which even in the case of low-speed devices (keyboards, mice, joysticks), is 10-1500 Kbps, simplicity and low cost of cabling and connections, self-identification of devices with automatic configuration, hiding electrical connection details from the end user (plus the ability to disconnecting the cable without turning off the devices), error control and their recovery at the protocol level - these are the undeniable advantages of this technology (see, p.12).

In general, speaking about the use of USB for data transfer, it would be useful to mention P. Agurov's book "USB Interface". It, although often criticized on the net and last released in 2006, has helped to find the right solution more than once when searching for information on various aspects of the application of this technology. The book covers questions: from choosing a microcircuit and circuitry for the controller to writing a microcontroller program and examples of programming data transfer via USB from a computer. It is impossible not to indicate the "original source" of data on this issue - the site of the non-profit organization USB IF (USB Implementers Forum), which develops the specifications of this interface -, although this material is English language. However, it is there that you will find comprehensive information about the USB interface device. There is a good translation of parts of the specification - . Those interested in software solutions from the microcontroller side can also see the link.

This article is addressed primarily to those who have some kind of electronic device (developed independently or by someone else), the data exchange protocol with which is well known (for example, there is already a program that works with this device in Windows / Linux) and would like to would have a program that works with it in android.

A little about USB device classes

It should be noted that the development of software for data exchange with a specific device is highly dependent on its implementation at the microcontroller level. For obvious reasons, it is impossible to give examples of communication programs for all types of USB devices within one article (initial information about programming various types of devices can be found in). However, we will limit ourselves to presenting the code that implements device search and access to its control points for information exchange. We will also analyze sending data using the example of one of the types of USB devices, namely, the HID device class (human interface device - a class of devices for interacting with a person). This class includes "slow" devices, such as a keyboard, mouse, joystick, and there are enough examples of its implementation using various microcontrollers on the network (there are, for example, in ).

Why is the HID class so fond of manufacturers of various homemade devices? To quote Wikipedia: “In addition to the detailed specifications of classical input devices (such as keyboards and mice), the HID standard defines a special class of devices without detailed specifications. This class is called USB HID Consumer Control and is essentially an ad hoc communication channel with the device. At the same time, the device uses the same drivers standard for the operating system as the mouse and keyboard. Thus, it is possible to create a USB device that does not require the creation and installation of special drivers in most common computer operating systems. It remains to add only that this specification also works in the Android OS (not excluding CyanogenMod firmware).

One of the options for data exchange with a HID device is interrupt transfer (interrupt transfer), which is used when it is necessary to transfer small data packets (the maximum packet size depends on the transfer rate and ranges from 64 to 1024 bytes) after a given time interval. The package for transmission is called a report (English - report, see p.71, 95). This report length is usually enough to exchange information with a homemade device, 64 bytes of information in one package, for example, is quite a lot for a controller, because 1 bit of information is enough to transfer the states of an LED or a simple sensor.

Required Tools

So, we need - a tablet or a phone with Android version 3.1 or higher. It should be noted here that the above USB Host API is not fully implemented on all mobile devices (this is also mentioned on the developer.android.com website, see link). Some tablets/phones only use the USB connector for charging and PC connection. Once again I will send the reader to the list of mobile devices suitable or unsuitable for our experiments (see).

You will also need some kind of USB device (for the first experiments, a regular USB flash drive will suffice), an OTG adapter (On-The-Go - see Fig. 2) and / or a USB cable to communicate with the device. Wikipedia about OTG says: “When connected via USB OTG, the rank of the device (master or slave) is determined by the presence or absence of a jumper between pins 4 and 5 in the connecting cable plug. In a USB OTG cable, such a jumper is installed in only one of the two connectors (see).» Accordingly, we need such a jumper from the side of the mobile device.


Fig. 2 Differences in the scheme of a conventional USB cable and an OTG cable (Figure from http://tech.firstpost.com)

You can also solder such an OTG cable for your device yourself. To do this, you need to buy a suitable connector in the radio store, plus the author, for example, used an old cable from a portable hard drive:

A good help in the work will also be the USB Device Info program installed from the storage Google Play market. The program is able to detect devices connected to the USB-connector of the tablet / phone both using the Java API and using the Linux kernel. That is, if your device is not detected using the Java USB Host API in USB Device Info, then, with a high probability, it will be in vain to use any (including your own) Android program written using Java for this mobile device. and USB Host API.

Sometimes, also, the information output by the lsusb command of the operating system is very useful. Linux systems. With the -v and -d options, lsusb displays everything, or almost everything, about a USB device that a software developer needs for devices of this class (see Figure 3).


Fig.3 Sample output of lsusb and lsusb -v -d commands

Next, you need a computer with installed Android SDK and the Eclipse Integrated Development Environment (IDE) with the ADT plugin (although you can get away with just the SDK). You can see how to create and install an Android application, for example, in, or on the Internet.

And, of course, it is necessary at least as well as the desire to achieve results, without it in any way! I note that it took the author weeks of painstaking search for information to clarify some of the technical issues of using USB in Android.

Java classes for working with USB in the Android API

So, as they say on the website of the USB Host API for Android developers (see) - "before you start, it is important to understand what classes you will use in your work." Table 1 describes the most important classes for working with the USB Host API (an attempt to translate information from http://developer.android.com).

Table 1. Description of classes for working with USB in Android

class name Description
UsbManager Allows you to enumerate and communicate with connected USB devices.
Allows you to detect a connected USB device and communicate with it.
UsbDevice Represents a connected USB device and contains methods to access its identifying information, interfaces, and endpoints.
Represents a connected USB device and contains methods for accessing its identity, interfaces, and endpoints.
usbinterface Represents an interface of a USB device, which defines a set of functionality for the device. A device can have one or more interfaces on which to communicate on.
Represents the "interface" of a USB device, which defines a set of features for this device. One device may have one or more interfaces for information exchange.
UsbEndpoint Represents an interface endpoint, which is a communication channel for this interface. An interface can have one or more endpoints, and usually has input and output endpoints for two-way communication with the device.
Represents the "endpoint" of an interface, which is the communication channel for that interface. An interface can have one or more endpoints, and usually has endpoints for receiving information and for transmitting it.
UsbDeviceConnection Represents a connection to the device, which transfers data on endpoints. This class allows you to send data back and forth sychronously or asynchronously.
Represents a "connection" to this device. Required to send data to the endpoint. This class allows data to be received or transmitted synchronously or asynchronously.
UsbRequest Represents an asynchronous request to communicate with a device through a UsbDeviceConnection.
Represents an asynchronous request to communicate with a device via a UsbDeviceConnection.
UsbConstants Defines USB constants that correspond to definitions in linux/usb/ch9.h of the Linux kernel..
Defines constants that match the definitions in linux/usb/ch9.h of the Linux kernel.

In almost all cases of using the USB Host API, the programmer uses these classes in his work. The algorithm for applying them looks something like this: we define devices (the goal is programmatic access to the UsbDevice class) connected to the host ( mobile device), using UsbManager. Once a device has been programmatically accessed, the appropriate UsbInterface and UsbEndpoint must be defined to communicate with it. Once you have the endpoint in your possession, open UsbDeviceConnection to talk to the USB device. If the endpoint is in asynchronous mode, use the UsbRequest class.

Let's try to figure it all out by creating a simple application that, using this API, will determine which is connected to the host with the OS Android device and display some information about it on the screen of your phone or tablet.

Create a project

In Eclipse, a project is created using the menu items File->New->Android Application Project. Note also that the code below is borrowed from the sample applications supplied with the Android SDK (the android sdk samples/android-N(API Level)/USB folder) we are talking about the Missile Launcher USB toy control program (see Fig. 4 ) Sample applications are downloaded through the Android SDK Manager (you need to check the item - Samples for SDK). In the listings below, the code examples are commented to explain what's going on.


Fig.4 Funny toy "Rocket launcher"

When creating a project, do not forget to mark the required API Level in the Minimum Requared SDK option (API Level 12, corresponding android versions 3.1 /Honeycomb/, or higher). The project will have a very simple user interface - the main window (Activity) and a TextView to display information. A similar project is discussed in detail in.

In the automatically generated class for the Activity of our project, you need to define the following class instances for working with USB:

private TextView lgView;
private UsbManager mUsbManager;
private UsbDevice mDevice;
private UsbDeviceConnection mConnection;
private UsbEndpoint mEndpointIntr;

LGView = (TextView) findViewById(R.id .logTextView ) ;

and get access to the UsbManager class

MUsbManager = (UsbManager) getSystemService(Context .USB_SERVICE ) ;

Let's create another event handler onResume(). Let's achieve the goal - that information about connected devices is updated when the window of our application is activated (see Listing 1).

Listing 1. The onResume() event handler

public void onResume()(
super .onResume();

//fill the container with a list of devices
hashmap< String , UsbDevice>deviceList = mUsbManager.getDeviceList();
Iterator< UsbDevice>deviceIterator = deviceList.values() .iterator() ;

lgView.setText ( "Devices Count:" + deviceList.size () ) ;

while (deviceIterator.hasNext()) (
UsbDevice device = (UsbDevice) deviceIterator.next() ;

//example of determining the ProductID of the device
\n"+ "Device ProductID: " + device.getProductId () ) ;
}
//define the intent described in the filter
// intent AndroidManifest.xml
intent intent = getIntent() ;
lgView.setText(lgView.getText() + " \n"+ "intent: " + intent) ;
String action = intent.getAction();

//if the device is connected, pass the link to
//to the setDevice() function
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE ) ;
if (UsbManager.ACTION_USB_DEVICE_ATTACHED .equals(action) ) (
setDevice(device) ;
lgView.setText(lgView.getText() + " \n" + "UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action) is TRUE") ;
) else if (UsbManager.ACTION_USB_DEVICE_DETACHED .equals(action) ) (
if (mDevice != null && mDevice.equals (device) ) (
setDevice(null) ;
lgView.setText(lgView.getText() + " \n" + "UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action) is TRUE") ;
}
}

Next, for the Activity, we will create the setDevice() function needed to work with our device (see Listing 2). In the onResume() handler and in the setDevice() function, we exactly followed the algorithm for using the USB Host API described in the previous section.

Listing 2. setDevice() function

private void setDevice(UsbDevice device) (
lgView.setText(lgView.getText() + " \n"+ "setDevice " + device) ;

//define available device interfaces
if (device.getInterfaceCount () != 1 ) (

lgView.setText(lgView.getText() + " \n"+ "could not find interface" ) ;
return;
}
UsbInterface intf = device.getInterface(0) ;

//define device endpoints
if (intf.getEndpointCount() == 0 ) (

lgView.setText(lgView.getText() + " \n"+ "could not find endpoint" ) ;
return;
) else (
lgView.setText(lgView.getText() + " \n"+ "Endpoints Count: " + intf.getEndpointCount () ) ;
}

UsbEndpoint epIN = null ;
UsbEndpoint epOUT = null ;

//look for endpoints to pass on interrupts
for (int i = 0 ; i< intf.getEndpointCount () ; i++ ) {
if (intf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_INT) (
if (intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)(
epIN = intf.getEndpoint (i) ;
lgView.setText(lgView.getText() + " \n"+ "IN endpoint: " + intf.getEndpoint (i) ) ;
}
else(
epOUT = intf.getEndpoint (i) ;
lgView.setText(lgView.getText() + " \n"+ "OUT endpoint: " + intf.getEndpoint (i) ) ;
}
) else ( lgView.setText(lgView.getText() + " \n" + "no endpoints for INTERRUPT_TRANSFER") ; }
}

MDevice = device;
mEndpointIntr = epOUT;

//open device for data transfer
if (device != null ) (
UsbDeviceConnection connection = mUsbManager.openDevice (device) ;
if (connection != null && connection.claimInterface (intf, true ) ) (

lgView.setText(lgView.getText() + " \n"+ "open device SUCCESS!" ) ;
mConnection = connection;

) else (

lgView.setText(lgView.getText() + " \n"+ "open device FAIL!" ) ;
mConnection = null ;
}
}
}
}

In addition to the above code, which, as the attentive reader must have already guessed, opens the device for receiving and transmitting data, all that remains is to use the data exchange protocol, which, I repeat, should be well known to the developer. Here is just, as promised, the code that will send some message data packet to the HID device using interrupt transmission, the UsbRequest class and the appropriate endpoint - see Listing 3.

Listing 3. Sample code for sending data to a device

//determining the size of the buffer to send
//based maximum size package
int bufferDataLength = mEndpointIntr.getMaxPacketSize();

lgView.setText(lgView.getText() + " \n"+ mEndpointIntr.getMaxPacketSize () ) ;

ByteBuffer buffer = ByteBuffer.allocate (bufferDataLength + 1 ) ;

UsbRequest request = new UsbRequest() ;

buffer put (message) ;

request.initialize(mConnection, mEndpointIntr) ;

request.queue (buffer, bufferDataLength) ;

if (request.equals (mConnection.requestWait () ) )

//send successful
//lgView.setText(lgView.getText() + "\n" + "sending CLEAR!!!");

catch (Exception ex)

//something is wrong...
//lgView.setText(lgView.getText() + "\n" + "sending not clear...");

Device filtering in AndroidManifest.xml

Although in our application there is no need to search specific device with known VID (Vendor-ID) and PID (Product-ID), Google engineers do not provide examples of applications without the intent-filter section in the manifest file, and the author was unable to get the program to work without device filtering in AndroidManifest.xml .

Let me remind you that Vendor-ID and Product-ID are unique identifiers for USB devices. That is, using filtering, you can create an application that interacts only with a certain device or some class of devices. Note that device manufacturers must agree these numbers with the USB IF organization.

An application whose manifest file is shown in Listing 4 and whose filter condition file is in Listing 5, for example, successfully recognizes USB flash drives connected to the mobile device, but does not recognize the author's keyboard and mice. This application, along with the source code, can be downloaded from the link.

Listing 4. AndroidManifest.xml file


" > http://schemas.android.com/apk/res/android"
> package="en.learn2prog.usbhostexample"
android:versionCode="1"
android:versionName="1.0" >


android:minSdkVersion="12"
android:targetSdkVersion="14" />


android:allowbackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >

android:name= "en.learn2prog.usbhostexample.MainActivity"
android:label="@string/app_name" >
>

"android.intent.category.DEFAULT" />

"android.intent.category.LAUNCHER" />

>

>

>
"android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
>
>

>

Listing 5. Device_filter.xml filter file (/res/xml directory)

>

>

The operations for building and installing our application are no different from the usual ones (see examples in , ). I want to pay attention to the actions of the intent filter - when the device connects to the host, the OS asks the user to launch our application.

Literature/Links: 11.
12.
13. http://developer.android.com/guide/topics/connectivity/usb/host.html - an overview of the classes required to work with USB in Android
14. link to application sources

USB Programming

Programming the SF-50 satellite tuner via the USB port differs from programming via the RS-232 port only in the way data is transferred from the instrument to a computer and from a computer to the instrument. When programming via USB, a portable USB stick(flash drive). This is useful when, for example, your computer or, more often, a laptop (netbook) does not have an RS-232 serial port on its chassis.
To program the device using a USB drive, you will need:
- USB drive (flash drive) formatted in FAT-32 file system;
- AliEditor editor program, located in the Database_editor_new folder of the OPENBOX SF-50 software section.

Before starting programming, it is necessary to transfer the database from the device to a computer. To do this, turn on the device, connect a USB flash drive to the USB port. Run MENU - System - Save to USB,

Exit the menu by pressing the MENU button until the current channel appears, or to the main menu picture if there are no channels yet and remove the flash drive. Insert flash drive into computer.
Run the Editor.exe program from the Database_editor_new folder and open the image on the flash drive in it.

When prompted to select a database, select User Data Base.

Press OK. Select All Services, a list of all scanned and saved TV, radio and service channels in the database will open.

Edit the channels as you wish, for example, leave only open channels.
On the computer keyboard, hold down the Shift button, press the "Down Arrow" and select the channels that you want to delete. Press Delete to delete the selection.

To edit satellite settings, select All Services - Satellite Information - EUTELSAT W4, W7, press the ENTER button.

If necessary, edit the values ​​in Antenna 1.
To delete a transponder, select an unnecessary one and press the Delete button on the computer keyboard, confirm the selected action.

To add a transponder, stand on the name of the satellite, press right button mouse and select Add Information (or select the satellite and press the Insert button on the keyboard).

Enter the data for the new transponder, taking them, for example, from the site lyngsat.com.

Press OK, make sure the transponder is registered.

To add a satellite, go to the line Satellite Information, press the Insert key on the keyboard and enter the parameters of the new satellite.

close the editor program, remove the drive.
Insert the drive into the OPENBOX SF-50 device, execute MENU - System - Update from USB in sequence, select the "SAT&TP List" mode.

Select Start. Confirm your intentions.

The device will update the database and reboot itself. After the reboot, in the settings, you will have to set the Russian menu language in a new way. Reset the device to factory settings.
Exit the settings menu by pressing the MENU button twice. Press the OK button on the current channel and make sure that the channel list is edited.

You can also make sure that the list of transponders and satellites has been edited.
Programming completed.

But it's not enough just to physically connect the device to the computer, you also need to establish data exchange between them. How to choose a port and organize a connection? Some years ago standard solution was using the COM port. By the way, various specialists still install 8, 16, or even 32 COM ports on industrial computers (there is a whole category of different PCI expansion cards for serial ports, controllers, etc.). Thus, if you need to connect several external devices with an RS-232 interface, you may need expensive adapters and exotic expansion boards, which, according to the old tradition, sail to Russia for weeks on steamboats. By the way, the name of an ordinary adapter "DB9m / DB25f adapter" can only cause irritation for a computer store manager.

What is a HID device

Now almost all devices are connected to a computer via a USB interface. Therefore, many new PCs do not have a COM port at all.

USB interface - a typical solution for pairing a new external device with a computer, more precisely, it is a HID interface based on the USB 1.1 protocol.

Although many people think that the HID (Human Interface Device) interface is exclusively for the keyboard, mouse and joystick, it is suitable for many solutions related to interfacing external devices and a computer.

If the user needs to perform low-speed data exchange (up to 64 kbps) and at the same time it is desirable to reduce the time on the tedious development of their own drivers, then HID is quite suitable for them. The output will be a simple and completely modern solution based on a standard USB software interface with guaranteed support on all common software platforms.

HID device properties

From the point of view of organizing software support for a HID device, everything looks quite attractive: for working under Windows, you can quickly create understandable compact code based on ready-made proven algorithms. In this case, the developer will have plenty of time to implement his own top-level data exchange protocol, since the necessary abstraction level is already organized by the HID protocol (see table). In addition, it is easy for a programmer to debug a written exchange protocol (of course, if there is a working HID device) - due to the relative rigidity of the protocol itself, it is enough to simply develop a program for supporting the device by a computer. Still would! A lot of work has already been done by the creator of the HID device.

Establishing data exchange between a HID device and a computer

To describe the interaction of a HID device with a computer, we will use the term "host". In this case, it refers to a control device in the general physical architecture of communication over the USB protocol. So, all ports in a computer are hosts. You can connect various USB devices to them (flash drives, mice, webcams, cameras, etc.) that do not have a host. The host provides discovery, connection, disconnection, device configuration, as well as statistics collection and power management.

The HID device itself can set the polling frequency, during which the presence of any new data is found out. This means that even at such a low level, the programmer can trust the system, since the polling rate and other communication parameters must be pre-set in the HID device controller program. In this, the HID protocol differs from the general description of USB 1.1 or USB 2.0, which does not have strict requirements for the organization of the protocol. However, for specific tasks that require a high level of security, it can be quite difficult to get rid of cyclic polls, when almost the same blocks of data are constantly transmitted.

Features of programming HID devices

HID devices have special descriptors. When the host determines that the device belongs to the HID class, it transfers control of it to the appropriate driver. It is assumed that further data exchange is carried out under his leadership.

On Windows, the HidServ system service is responsible for accessing HID devices. More details about the functions of requests to HID devices and other features of working with the HID driver are described in the work by P. V. Agurov “USB interface. The practice of using and programming” (St. Petersburg: BHV-Petersburg, 2005).

Programming HID devices at the "top level"

The hard life of "applied" programmers working on Pascal is facilitated by the proven HID module. PAS, the shell for hid. dll (Hid User Library - as specified in the file properties). The comments to the file say that it is based on the hidsdi.h and hidpi.h modules from Microsoft Corporation. And the HID file itself. PAS is part of the JEDI() package.

To work with a HID device in the Delphi for win32 environment, the TJvHidDeviceController component is used, which is a convenient global manager for accessing HID devices. And already on its basis, you can get an object instance for working with a specific device.

Main properties and events of the TJvHidDeviceController component

Let's consider the TJvHidDeviceController component in more detail. The OnArrival event fires when a HID device arrives (connects) to the system, access to the device is provided in the handler of this event through an instance of the TJvHidDevice class. The simple event OnDeviceChange reacts to a change in the state of the device, it only signals changes in the system. The OnDeviceData event fires when data arrives from one of the HID devices and passes the following to the handler: HidDev: TJvHidDevice; - the device from which the data was received;

The OnDeviceDataError event notifies of a data transfer error by passing HidDev parameters to the processing procedure: TJvHidDevice; - HID device and Error: DWORD; - error code. The OnDeviceUnplug event notifies you that a device has been removed from the list of devices installed on the system. The types of event handlers on Plug and Unplug are the same (in the source text: TJvHidUnplugEvent = TJvHidPlugEvent). An object of the TJvHidDevice class corresponding to the HID device is passed to the handler.

To sequentially enumerate the HID devices available in the system by calling the Enumerate method, the OnEnumerate event is intended, i.e., in the event handler, the found devices are sequentially transmitted as objects. This event is forcibly triggered by the Enumerate method, which is used to "pass" existing HID devices through the handler, for example, when auditing the state of HID devices initiated by the host (computer).

The OnRemoval event fires when the device is physically removed from the system and has the same TJvHidUnplugEvent handler type as for OnDeviceUnplug. The CountByProductName function returns the number of devices that match the product name specified in the argument, and CountByVendorName returns the number of vendor names specified in the argument.

Main properties and events of the TJvHidDevice class

The TJvHidDevice class is a virtual representation of a single HID device. A new object of this class can be obtained, as already mentioned, from the OnArrival or OnEnumerate event. The functionality of the TJvHidDeviceController and TJvHidDevice classes is partially duplicated, since the first of them integrates a common toolkit for working with a set of HID devices available in the system and a mechanism for accessing one of them. A device can be uniquely identified by its SerialNumber, ProductName, and VendorName properties. You can use the OnData event to get information about the arrival of data using such an object. Data is sent through the WriteFile method (in the strict sense, through a function). WriteFile is a wrapper around the WriteFile (kernel32) system function.

To control the fact that the device is removed, you should assign your own handler to the OnUnplug event. Before starting data exchange with a HID device, you need to make sure that such an exchange is possible using HasReadWriteAccess. This class even has a separate OnDataError event for the occurrence of a data exchange error.

And now let's look at code fragments from a "live" project that implements a test client application for organizing data exchange with a non-standard device - HID-based plastic chip cards. In the struggle for realism, the author took the liberty of not throwing out "extra" technological code bindings from the listings.

The ScanDevices method (Listing 1) is designed to initiate the process of searching the system for the required HID device. Most of the code, with the exception of the call to the Enumerate method, is optional and provides application flexibility, for example, so that you can add the ability to work on a non-HID interface to the same test program. The AddError method prints debugging information to the window while the program is running.

Listing 2 shows the OnEnumerate event handler for finding the required external device. For simplicity, we will assume that the program can work with only one device of the type it needs.

Before considering the further implementation of the project, we should talk a little about the accepted top-level data exchange format, that is, about the structure designed to be an intermediary between the methods of receiving and transmitting data and the specific applied task being solved. The fact is that here the developer is given the opportunity to realize their creative abilities. Or rather, developers, because the process of creating a new protocol is very often two-way, and the first violin is played by the one who finds it more difficult to implement the exchange algorithm. In general, whatever the exchange protocol, it is always nice to make each software entity as visual and self-sufficient as possible, even to the detriment of some generally accepted traditions. For the best solution is the one that will be implemented in a short time with minimal binding to the software environment and with great opportunities for further development. Based on these principles, a top-level exchange protocol was created, where the main concept is “command”. Listing 3 shows how much the author loves string data, which saved him more than once when debugging program modules. How wonderful that we even have a String type! All protocol commands are divided into categories (classes), within which there is a command code that uniquely characterizes its purpose. The edParam parameter is used to send data to the device, and the edAnswerData parameter contains the data received from the device. The string type of the described record members allows you to freely and visually manipulate data in the HEX string format. And what is most pleasant, the format of the described record is ideologically somewhere in the middle between its direct purpose and various forms of its presentation (INI, HEX, XML, etc.)

Command execution, i.e. sending data to the device, is implemented using sending data packets with a length of 8 bytes (Listing 4). This length is not the only decision, such a choice is dictated by the requirements of the upper layer protocol and may be different in each specific case. This is what is called a matter of taste. The weird IsUSBMode flag in the ExecuteCommand method (Listing 5 in PC Disk World) is left as a reminder that instead of working with USB, we may need to use a COM port or some other interface. At the beginning of the sent data group, a sync sequence of an arbitrarily chosen format (for example, 3E3E3E2B) is transmitted to the device, informing the device that it has completely legal data at its input. Let me remind you that in this case we are talking not so much about HID, but about a specific top-level protocol, ideologically divorced from hardware and designed to solve special applied problems.

In the GetDataExecutor handler of the data received from the device (package of 8 bytes), a specially created OnNewInputData event was used to transfer the initially processed data for further processing, indicating their old and new values ​​(Listing 6 on the "World of the PC disk"). Thus, the raw data arrival events and the indication of further processing are decoupled, allowing some specific warning algorithm to be added at an early stage for erroneous, repeated, or unnecessary inputs.

The examples of working with a HID device presented here illustrate the general idea of ​​the article - the relative simplicity of programming non-standard HID devices using Delphi tools.

Introduction
Who is this book for?
What will you find in the book
Software Requirements
Hardware Requirements
About the code
Brief description of the chapters
Notation
Thanks
Feedback
Part I. Understanding USB
Chapter 1 USB specification
1.1. What is USB and why is it needed
1.1.1. General USB architecture
1.1.2. USB physical and logical architecture
1.1.3. USB components
1.1.4. USB device properties
1.1.5. Communication principles
1.1.6. Interrupt mechanism
1.1.7. Communication modes
1.1.8. Logical levels of communication
1.1.8.1. Client software level
1.1.8.2. USB system driver level
1.1.8.3. Interface Host Controller Layer
1.1.8.4. USB peripheral bus level
1.1.8.5. USB logical device level
1.1.8.6. Functional level of the USB device
1.1.9. Data transfer by levels
1.1.10. Types of data transfers
1.1.11. Personnel
1.1.12. Endpoints
1.1.13. Channels
1.1.14. Packages
1.1.14.1. Format of IN, OUT, SETUP, and PING token packets
1.1.14.2. SOF packet format
1.1.14.3. Data Packet Format
1.1.14.4. Acknowledgment Packet Format
1.1.14.5. SPLIT Packet Format
1.1.15. Check sum
1.1.15.1. CRC Calculation Algorithm
1.1.15.2. Software CRC Calculation
1.1.16. Transactions
1.1.16.1. Transaction types
1.1.16.2. Transaction confirmation and flow control
1.1.16.3. Transaction protocols
1.2. Requests to USB devices
1.2.1. Configuration package
1.2.2. Standard requests to USB devices
1.2.2.1. Getting the GET_STATUS state
1.2.2.2. Resetting the CLEAR_FEATURE property
1.2.2.3. SET_FEATURE Property Resolution
1.2.2.4. Setting the address on the SET_ADDRESS bus
1.2.2.5. Getting a GET_DESCRIPTOR handle
1.2.2.6. Passing a SET_DESCRIPTOR Descriptor
1.2.2.7. Get configuration code GET_CONFIGURATION
1.2.2.8. Setting the SET_CONFIGURATION configuration code
1.2.2.9. Getting the interface configuration code GET_INTERFACE
1.2.2.10. Setting the SET_INTERFACE interface setup code
1.2.2.11. Setting the SYNC_FRAME sync frame number
1.2.2.12. Handling standard requests
1.2.3. Device handle
1.2.3.1. Device handle
1.2.3.2. Qualifying device descriptor
1.2.3.3. Configuration Descriptor
1.2.3.4. Interface descriptor
1.2.3.5. Endpoint handle
1.2.3.6. Line handle
1.2.3.7. Specific descriptors
1.2.3.8. Descriptor Obtaining Order
1.3. Plug and Play (PnP) system
1.3.1. Configuring USB Devices
1.3.2. USB device numbering
1.3.3. PnP USB IDs
1.3.4. Symbolic device names
1.4. WDM Model
Chapter 2 Microcontroller C Programming
2.1. General information about the C language for microcontrollers
2.2. Using Standard Libraries
2.3. Programming for AT89C5131
2.3.1. Initialization file
2.3.2. Descriptor Structures
2.3.3. Project structure
Chapter 3 Tools
3.1. programmers
3.1.1. Flip programmer
3.1.2. Programmer ER-Tronik
3.2. Driver Creation Tools
3.2.1. NuMega Driver Studio
3.2.2. Jungo WinDriver
3.2.3. Jungo KernelDriver
3.3. Microsoft Tools visual studio
3.3.1. Depends (Dependency Walker)
3.3.2. Error Lookup
3.3.3. GuidGen
3.4. Microsoft DDK Tools
3.4.1. device tree
3.4.2. devcon
3.4.2.1. classes key
3.4.2.2. driverfiles key
3.4.2.3. hwids key
3.4.2.4. key rescan
3.4.2.5. stack key
3.4.2.6. status key
3.4.3. Chklnf and Genlnf
3.5. CompuWare Corporation Tools
3.5.1. Monitor
3.5.2. SymLink
3.5.3. EzDriverInstaller
3.5.4. WdmSniff
3.6. SysInternals Tools
3.6.1. WinObj
3.7. USB Forum Tools
3.7.1. HID Descriptor Tool
3.8. USB Command Verifier
3.9. HDD Software Tools
3.10. Sourceforge Tools
3.11. Bus Hound monitoring software
Chapter 4 Understanding Win32 Functions in .NET
4.1. General information
4.2. Importing Win32 Functions
4.3. structures
4.3.1. StructLayout attribute
4.3.2. MarshalAs attribute
4.4. Direct access to data
4.5. Handling Windows Messages
4.6. General information about WMI
4.7. Online resources for this chapter
Part II. USB classes
Chapter 5
5.1. USB/RS-232 Interface Conversion Methods
5.2. General information about the RS-232 interface
5.2.1. Exchange lines
5.2.1.1. Transmitted data (BA/TxD/TD)
5.2.1.2. Received data (BB/RxD/RD)
5.2.1.3. Transfer Request (CA/RTS)
5.2.1.4. Transfer Ready (CB/CTS)
5.2.1.5. DCE Ready (CC/DSR)
5.2.1.6. DTE Ready (CD/DTR)
5.2.1.7. Call indicator (CE/RI)
5.2.1.8. Carrier Detect (CF/DCD)
5.2.1.9. Ready to Receive (CJ)
5.3. CDC specification
5.3.1. Standard descriptors
5.3.2. Function Descriptors
5.3.2.1. Header Function Descriptor
5.3.2.2. Command Mode Descriptor
5.3.2.3. Abstract Device Descriptor
5.3.2.4. Grouping descriptor
5.3.3. Special requests
5.3.3.1. SET_LINE_CODING request
5.3.3.2. GET_LINE_CODING request
5.3.3.3. SET_CONTROL_LINE_STATE query
5.3.3.4. SEND_BREAK request
5.3.4. Notifications
5.3.4.1. RING^DETECT notification
5.3.4.2. SERIAL_STATE notification
5.4. CDC support on Windows
5.4.1. Overview of Windows features for working with serial ports
5.4.1.1. Basic Port Operations
5.4.1.2. Port setting functions
5.4.1.3. Custom port setting
5.4.1.4. Getting the status of modem lines
5.4.1.5. Working with CDC on the platform. NET
5.4.2. Matching Windows features and USB requests
Chapter 6 HID class
6.1. HID device specification
6.2. How to communicate with a HID device
6.3. Installing the HID device driver
6.4. HID Device Identification
6.4.1. Identification of boot devices
6.4.2. HID device configuration descriptor
6.4.3. HID descriptor
6.4.4. Report descriptor
6.5. Report descriptor structure
6.5.1. Report elements
6.5.1.1. Short Type Elements
6.5.1.2. Long type elements
6.5.2. Report item types
6.5.2.1. Essential elements
6.5.2.2. Global elements
6.5.2.3. Local elements
6.5.3. Descriptor Examples
6.6. Requests to an HID device
6.6.1. GET_REPORT request
6.6.2. SET_REPORT request
6.6.3. GET_IDLE request
6.6.4. SET_IDLE request
6.6.5. GET_PROTOCOL request
6.6.6. SET_PROTOCOL request
6.7. Instruments
6.8. Drivers for HID Devices in Windows
Chapter 7 Other USB Classes
Part III. USB Programming Practice
Chapter 8. Creating a USB device based on AT89C5131
8.1. General information about AT89C5131
8.2. Structural diagram of AT89S5131
8.3. USB registers AT89C5131
8.3.1. USBCON register
8.3.2. Register USBADDR
8.3.3. USBINT register
8.3.4. USBIEN register
8.3.5. UEPNUM register
8.3.6. UEPCONX register
8.3.7. UEPSTAX register
8.3.8. Register UEPRST
8.3.9. UEPINT register
8.3.10. Register UEPIEN
8.3.11. UEPDATX register
8.3.12. Register UBYCTLX
8.3.13. UFNUML register
8.3.14. Register UFNUMH
8.4. Circuitry AT89S5131
8.5. Basic design for AT89C5131
8.5.1. The first version of the program for АТ89С5131
8.5.2. Adding string descriptors
8.5.3. Adding Endpoints
8.6. Program download
Chapter 9 Implementing the CDC Class
9.1. CDC Implementation
9.2. Device handles
9.2.1. Endpoint initialization
9.2.2. Processing CDC Requests
9.2.3. RS port and CDC line configuration
9.2.4. Receiving and transmitting data
9.3. Driver installation
9.4. Programming data exchange with a CDC device in Delphi
9.5. Programming an exchange with a CDC device in C#
9.5.1. Using the MSCOMM Component
9.5.2. Using Win32 Functions
9.6. CDC issues
Chapter 10 Implementing the HID Class
10.1. Implementation of HID on AT89C5131
10.2. Transferring multiple bytes
10.3. Feature reports
10.4. Sending data from the host (SET_REPORT)
10.5. HID device installation
10.6. Communication with HID device
10.6.1. Getting the name of a HID device
10.6.2. Getting device attributes and reading reports
10.6.3. Data transfer from host to HID device
10.7. HID Device Examples
10.7.1. Implementation of the mouse device
10.7.2. Implementation of the "keyboard" device
10.8. Using the HID Protocol
10.8.1. Data interpretation
10.8.2. Collections
10.8.3. Arrays and Buttons
10.9. HID device with multiple reports
Chapter 11 Special Windows Features
11.1. Setup API Functions
11.1.1. Enumerating USB Devices
11.1.2. Getting the status of a USB device
11.2. Enumerating USB Devices with WMI
11.3. Windows XP special features
11.3.1. HidD_GetInputReport - read HID reports
11.3.2. Getting Raw Input Data
11.4. DirectX Features
11.5. Dialog for adding new equipment
11.6. Working with symbolic device names
11.7. Safe ejection of flash drives
11.8. Device addition and removal detection
11.9. Internet resources
Chapter 12 Driver Development
12.1. WDM Driver Basic Procedures
12.1.1. Procedure DriverEntry
12.1.2. Procedure AddDevice
12.1.3. Procedure Unload
12.1.4. Driver Operating Procedures
12.1.4.1. Packet header
12.1.4.2. I/O Stack Cells
12.4.1.3. Driver Operating Procedures
12.1.5. Serving IOCTL Requests
12.2. Loading the Driver and Calling the Driver Procedures
12.2.1. Driver procedure
12.2.2. Driver registration
12.2.2.1. Registering with SCM
12.2.2.2. Driver settings in the registry
12.2.3. Referring to work procedures
12.2.4. Storing the driver inside the executable file
12.3. Creating a driver with Driver Studio
12.3.1. A few words about the Driver Studio library
12.3.1.1. KDriver class
12.3.1.2. KDevice class
12.3.1.3. Klrp class
12.3.1.4. KRegistryKey class
12.3.1.5. KLowerDevice class
12.3.1.6. USB classes
12.3.2. Other Driver Studio classes
12.3.3. Creating a Driver Template Using Driver Studio
12.3.3.1. Step 1. Set the name and path of the project
12.3.3.2. Step 2: Choosing a Driver Architecture
12.3.3.3. Step 3. Tire selection
12.3.3.4. Step 4 - Define a set of endpoints
12.3.3.5. Step 5. Specifying the class and file name
12.3.3.6. Step 6Choosing Driver Features
12.3.3.7. Step 7. Choosing how to process requests
12.3.3.8. Step 8: Create Persistent Driver Settings
12.3.3.9. Step 9 Driver Properties
12.3.3.10. Step 10. Specifying the IOCTL Codes
12.3.3.11. Step 11 Advanced Settings
12.3.4. Refinement of the driver template
12.3.5. Device Class Base Methods
12.3.6. Implementation of data reading
12.3.7. Driver installation
12.3.8. Data reader
12.3.9. Reading data from other types of endpoints
12.3.10. "Clean" USB device driver
Part IV. Directory
Chapter 13. INF File Format
13.1. INF file structure
13.1.1. Version section
13.1.2. Manufacturer section
13.1.3. DestinationDirs Section
13.1.3.1. DefaultDescDir key
13.1.3.2. file-list-section keys
13.1.3.3. dirid key
13.1.3.4. subdir key
13.1.4. Model description section
13.1.5. Section xxx. AddRegw xxx. DelReg
13.1.6. Section xxx. logconfig
13.1.7. Section xxx. CopyFiles
13.1.8. Section Strings
13.1.9. Section links
13.2. Creating and Testing INF Files
13.3. Installing Devices Using an INF File
13.4. Registry branches for USB
Chapter 14 Basic Windows Functions
14.1. CreateFile and CloseHandle Functions: Opening and Closing an Object
14.1.1. additional information
14.1.2. Return value
14.1.3. Call example
14.2. ReadFile function: reading data
14.2.1. additional information
14.2.2. Return value
14.2.3. Call example
14.3. Write File Function: Data Transfer
14.3.1. additional information
14.3.2. Return value
14.3.3. Call example
14.4. ReadFileEx function. APC data reading
14.4.1. Return value
14.4.2. additional information
14.4.3. Call example
14.5. WriteFiieEx Function: APC Data Transfer
14.5.1. Return value
14.5.2. Call example
14.6. WaitForSingieObject function waiting for the signaled state of an object
14.6.1. Return value
14.7. WaitForMultipleObjects function: waiting for the signaled state of objects
14.7.1. Return value
14.8. GetOverlapped Result function: the result of an asynchronous operation
14.8.1. Return value
14.9. DeviceloControl function: direct driver control
14.9.1. Return value
14.10. Cancel/o function: aborting an operation
14.10.1. Return value
14.11. Query Dos Device function, getting the device name from its DOS name
14.11.1. Return value
14.11.2. Call example
14.12. Define Dos Device function: operations with DOS device name
14.12.1. Return value
14.12.2. Call example
Chapter 15 Windows Structures and Functions for Serial Ports
15.1. COMMCONFIG port settings structure
15.2. Port Properties Structure COMMPROP
15.3. Structure of COMMTIMEOUTS timeouts
15.4. COMSTAT port status structure
15.5. DCB Structure
15.6. BuildCommDCB Function: Build a DCB Structure from a String
15.6.1. additional information
15.6.2. Return value
15.6.3. Call example
15.7. BuildCommDCBAndTimeouts Function: Build DCB Structure and Timeouts from String
15.8. SetCommBreak and ClearCommBreak Functions: Output Control
15.8.1. Return value
15.9. ClearCommError Function: Getting and Clearing Port Errors
15.9.1. Return value
15.10. EscapeCommFunction: port control
15.10.1. Return value
15.11. GetCommMask and SetCommMask functions: event call mask
15.11.1. Return value
15.12. WaitCommEvent function waiting for a COM port event
15.12.1. Return value
15.12.2. additional information
15.12.3. Call example
15.13. GetCommConfig and SetCommConfig Functions: Configuring Port Settings
15.13.1. Return value
15.13.2. Call example
15.14. CommConfigDialog Function: Port Configuration Dialog
15.14.1. Return value
15.14.2. additional information
15.14.3. Call example
15.15. GetCommProperties function: read port properties
15.15.1. Return value
15.15.2. Call example
15.16. GetCommState and SetCommState Functions: Port State
15.16.1. Return value
15.16.2. Call example
15.17. GetCommTimeouts and SetComniTimeouts Functions: Port Timeouts
15.17.1. Return value
15.17.2. Call example
15.18. PurgeComm Function: Flush Port Buffers
15.18.1. Return value
15.18.2. Call example
15.19. SetupComm function: configuring buffer sizes
15.19.1. Return value
15.20. GetDefaultCommConfig and SetDefaitltCommConfig Functions: Default Port Settings
15.20.1. Return value
15.21. TransmitCommChar function. passing special characters
15.21.1. Return value
15.22. GetCommModemStatus function: modem status
15.22.1. Return value
15.22.2. Call example
15.23. EnumPorts Function: Enumerating Ports
15.23.1. additional information
15.23.2. Return value
15.23.3. Call example
Chapter 16. Windows Setup API Structures and Functions
16.1. Setup function DiGetCiassDevs: device enumeration
16.1.1. Return value
16.2. SetupDiDestroyDevicelnfoList function freeing the device description block
16.2.1. Return value
16.3. SetupDiEnumDevicenterfaces Function: Device Information
16.3.1. Return value
16.4. SetupDiGetDevicelnterfaceDetaii function: detailed device information
16.5. SetupDiEnumDevicelInfo Function: Device Information
16.6. SetupDiGetDeviceRegistryProperty Function: Getting Plug and Play Device Properties
16.7. CM_Get_DevNode_Status function: device status
16.8. CM_Request_Device_Eject function safe device ejection
Chapter 17 Windows HID API Structures and Functions
17.1. HidD_Hello Function: Library Check
17.2. HidD_JetHidGuid Function: Getting a GUID
17.3. HidD_GetPreparsedData Function: Create Device Handle
17.4. HidD_EreePreparsedData Function: Release Device Handle
17.5. HidD_Get Feature function: getting a Feature report
17.6. HidD_SetFeature Function: Passing a Feature Report
17.7. Function HidD_GetNumlnputBuffers: getting the number of buffers
17.8. Function HidD_SetNumlnputBuffers: Set the number of buffers
17.9. HidD_GetAttributes Function: Getting Device Attributes
17.10. Function HidD_GetManufacturerString. get manufacturer string
17.11. HidD_GetProductString function getting a product string
17.12. Function HidD_GetSerialNumberString. getting a string serial number
17.13. HidD_GetIndexedString function. get row by index
17.14. Function HidD_Jetlnput Report receiving an Input report
17.15. Function HidD_SetOutputReport. sending an output report
17.16. HidP_GetCaps Function: Getting Device Properties
17.17. HidP_MaxDataListLength function: getting report sizes
17.18. HidD_FIushQueue Function: Flushing Buffers
17.19. HidP_GetLinkColiectionNodes Function: Collection Tree
17.20. Functions HidP_GetScaledUsageValue and HidP_SetScaledUsage Value: Get and set converted values
17.21. Function HidF_MaxUsageListLength: buffer size for keycodes
17.22. HidP_UsageListDifference Function: Difference Between Arrays
Applications
Appendix 1. Additional Features
Appendix 2. Compiling examples in other versions of Delphi
Appendix 3. Table of language identifiers (LangID)
Appendix 4. Table of vendor codes (Vendor ID, Device ID)
Appendix 5. How to Create a Device Manager Shortcut
Annex 6. Frequently Asked Questions
Appendix 7 CD Description
Literature
Subject index

As already mentioned, Windows operating systems provide software support for the operation of devices connected to the USB bus. The processing of USB device data streams at the operating system level is performed by a stack of standard drivers that perform the basic functions of managing all USB devices and exchanging data between them and the system.

If you need to write software for any USB device that would expand its data processing capabilities, then one of three possible paths can be chosen:

write your own device driver that provides all the necessary control and communication functions, and a program that interacts with this driver in user mode. In this case, you can completely do without standard system drivers;

write a filter driver that provides the required functionality, but sits in the driver stack above the system drivers. Thus, all standard processing functions would be performed by the USB drivers installed by the system, and additional functions would be provided by your filter driver, with which the user program would interact;

use freely distributed function and driver libraries

to access the USB device.

In most cases, you may need to programmatically access a USB device if the device has a very specific function. For example, USB-based "electronic oscilloscopes" or data acquisition systems have been developed that require access to the device itself. In most of these cases, you can use freely distributed function libraries that will work in almost all popular programming environments. For example, under the auspices of GNU, software known as LibUsb has been developed, which includes the necessary drivers and function libraries to run on Windows and Linux operating systems. These function libraries are very popular and allow you to quickly develop programs that interact with your device through a set of standard functions. This eliminates the need to write your own device driver, which saves a lot of time.

In addition, most users are not familiar with driver development techniques,

and this is a very complex area of ​​programming, so the availability of such free software will be of invaluable help to a wide range of users. Based on the LibUsb project, wrappers have been developed for working with Visual Basic .NET and C# .NET, the most popular of which is LibUsbDotNet, also developed under the auspices of free software. Despite the apparent complexity of programming USB devices, the listed software simplifies this task so much that even beginners can do it. Ras look at practical examples how to work with your USB devices mi, and start with the LibUsb software package. By the way, the above software can be downloaded for free from www.sourceforge.net or from numerous duplicate sites.

How to work with LibUsb USB function libraries? The library is built like this.

zom so that you can perform basic operations related to the USB device:

identification or, in another way, enumeration. This operation detects devices connected to the USB bus, which is done using the appropriate functions of the libusb library;

obtaining device parameters (device identifiers, data about the manufacturer and characteristics of the device), for which the library has a number of functions;

opening, closing, reading and writing data, sending commands. To a USB device, just like other objects file system, you can write to read, which is done using the corresponding functions of the library.

All of the features listed can be implemented by calling the appropriate libusb functions, but they won't be listed here because that would take up too much space; we will see how to use some of these functions

Rice. 6.10

Location of the libusb0.sys driver in the device driver stack

tions on practical examples. Readers can find a description of all functions in the corresponding documentation. Let me remind you that we are considering the use of libusb library functions in Windows operating systems.

When installing a distribution with libusb in operating system Windows installs the libusb0.sys filter driver on the system. This driver will be at the top of the system's driver stack, which is easy to see, for example, by looking at the driver information for any USB device (Figure 6.10).

In addition, to access the driver from user programs, the libusb0.dll library is installed into the system, using which you can develop user programs.

Rice. 6.17

View of the application window when uninstalling

USB devices from the system