分享
 
 
 

(装载)Drivers for Serial-Attached Devices

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

(转载)Drivers for Serial-Attached Devices

March 15, 2003

Walter Oney

Copyright © 2003 by Walter Oney. All rights reserved

I'll explain in this article how to write a driver for a device that attaches to a serial port in a PC. With the advent of the Universal Serial Bus, the standard RS-232 serial port has become much less important as an attachment point for PC hardware. One still finds devices that attach this way, though, including SmartCard readers, bar code scanners, and one-off laboratory instruments.

The first section of the article describes the overall architecture of a driver for a serial-attached device and explains some of the factors you will need to consider in designing your hardware and in deciding which OS platforms to support. The second section contains sample code for inclusion in a WDM driver for a device that provides a Plug and Play identification string. The third section discusses how you would write an equivalent VxD driver for the Windows 9x/Me platforms. The fourth and final section of this article describes the alterations you would want to make to your driver package for a seriously stupid device that doesn't provide a PnP id.

Overview:

Let's suppose you need to write the driver for a widget that connects to a PC through a standard serial port, as shown in Figure One:

Figure One

A Serial-Attached Widget

Your driver fits into the system into the system between the serial port driver and other drivers, as suggested by Figure Two:

Figure Two

Driver Architecture for Serial-Attached Device

Figure Two doesn't, unfortunately, accurately describe the stack of drivers that you'll end up with in a real situation. Think of it as an architectural picture that shows the logical flow information. To see an accurate picture, we have to know which OS platform we're talking about and whether our widget has a PnP id.

Widgets with PnP identifiers, Windows 2000 and later systems:

If your device follows the standard described in the Plug and Play External COM Device Specification to provide a PnP id, the system SERENUM.SYS driver will find your widget whenever it enumerates the serial port to which the end user has attached it. In this situation, SERENUM will act as a bus driver and create a Physical Device Object (PDO) to represent the widget. Your driver will be a standard WDM function driver that the PnP Manager loads automatically. Using a tool such as the DevView utility that accompanies Programming the Microsoft Windows Driver Model Second Edition (Microsoft Press 2003), you could inspect the tree of device objects to generate a picture like Figure Three:

Figure Three

Driver Stack for Widget with PnP ID

In this figure, SERENUM appears twice: once in its role as an Upper Filter for a serial port, and once in its role as the bus driver that enumerates your widget.

In this architecture, SERENUM reads the PnP id from your widget. The id will be a pseudo-EISA identifer of the form XYZ1234, where "XYZ" is a 3-letter manufacturer prefix and "1234" is a 4-digit hex number you choose to differentiate your products, one from the other. TODO describe how to obtain the 3-letter prefix. The INF file in your driver package will have a model statement like this one:

"Acme Widget Model 101 (PNP)"=DriverInstall,SERENUM\XYZ1234

The PnP Manager matches the device id returned by SERENUM to this model statement in order to install your driver according to the directions in the DriverInstall section. It then loads your driver, calling DriverEntry and AddDevice in the normal way for WDM drivers. In your StartDevice function, you would normally send an IRP_MJ_CREATE to the lower driver. When SERENUM receives this IRP, it forwards it in the other driver stack down to SERIAL, which opens the port for your use. Thereafter, you send IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests down the stack to SERENUM, which forwards them to SERIAL. You use these requests to control the RS-232 signal lines and and to send and receive data from your widget.

If possible, design your device so that removing the device changes one of the RS-232 signal lines CTS or DSR. Your driver can detect the change and initiate a surprise removal sequence that mimics the way other hot-pluggable devices behave.

Widgets without PnP identifiers, Windows 2000 and later systems:

If your device does not provide a PnP id, you'll install your driver as legacy driver. You need to give the end user a way to specify the COM port to which the device is attached, since scanning ports in a driver is not a good thing to do. The best, though not the easiest, way to do this is by means of a co-installer DLL that stores the port name in the Device Parameters registry subkey for your device. You'll then instruct the end user to use the Add Hardware wizard to install an instance of your device, and your instructions will include advice about filling in the COM port name in a dialog presented by your co-installer. To remove your device, the end user should click the Remove choice in the Device Manager.

Your INF file will use legacy syntax in the model statement, as in this example:

"Acme Widget Model 102 (Legacy)"=DriverInstall,*XYZ1234

Here, XYZ1234 is a pseudo-EISA identifier as in the previous example, but it appears with no bus driver qualification.

Figure Four illustrates the driver stack topology for his legacy situation:

Figure Four

Driver Stack for Legacy Widget

The root enumerator acts as the bus driver for your device. The PnP Manager loads your driver and calls DriverEntry and AddDevice in the normal way for a WDM driver. In your StartDevice function, you would normally read the registry to get the name of the attachment port, and then you'd call IoGetDeviceObjectPointer to open a handle to SERIAL.SYS and to obtain a DEVICE_OBJECT pointer to which you can submit IRPs. Thereafter, you send IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests to SERIAL. You use these requests to control the RS-232 signal lines and and to send and receive data from your widget.

The major programming difference between a legacy device and a PnP device is this: with the legacy device, someone has to tell you which port to use, and you use IoGetDeviceObjectPointer to connect to the driver for that port. With the PnP device, the identity of the port is implicit in the driver topology established by the PnP Manager, and you simply send an IRP_MJ_CREATE to your bus driver to connect to the driver for the port.

Widgets with PnP identifiers, Windows 9x and Me systems:

In the 9x/Me world, you need to provide a VxD driver for your widget. Your driver calls services in the system VCOMM driver, which then calls functions in a port driver like SERIAL.VXD. A third driver named SERENUM.VXD comes into play when your widget provides a PnP id. SERENUM is the "enumerator" VxD for a standard serial port. When it reenumerates the port, it reads your PnP id and constructs a child DEVNODE below the serial port's DEVNODE. The Configuration Manager will eventually load your driver because of statements like these in your INF file:

"Acme Widget Model 101 (PNP)"=DriverInstall,SERENUM\XYZ1234

. . .

[DriverInstall]

AddReg=DriverAddReg

[DriverAddReg]

HKR,,DevLoader,,widget.vxd

That is, your driver (widget.vxd) is technically the "Device Driver Loader" for your device's DEVNODE. As such, it will receive a PNP_NEW_DEVNODE control message from the Configuration Manager, whereupon it will register a configuration function. Thereafter, the Configuration Manager will call the configuration function to get the device configured and running. Refer to chapters 11 and 12 in Systems Programming for Windows 95 (Microsoft Press 1996), if you have it, for all the gory details about configuration functions.

The driver topology you end up with in this situation looks like Figure Five:

Figure Five

DEVNODE and Driver topology for PnP Widget

In Configuration Manager terms, the Widget device node (DEVNODE) is a child of the port DEVNODE. SERENUM is the enumerator for the port DEVNODE, and VCOMM is both the DevLoader and the device driver. (In reality, VCOMM relies on a separate port driver VxD to actually talk to the hardware.) Your Widget driver is both the DevLoader and the device driver for the Widget DEVNODE.

In your driver's PNP_NEW_DEVNODE handler, you would interrogate the hardware registry key belonging to the parent DEVNODE to determine the port name. You would then call VCOMM_OpenComm to open the port. Thereafter, you call other VCOMM services to actually access your device.

Widgets without PnP identifiers, Windows 9x and Me systems:

In the 9x/Me world, you would handle a serial-attached device that doesn't have a PnP id in nearly the same way as one that does have a PnP id. The architectural picture is only slightly different than for the PnP case, as shown in Figure Six:

Figure Six

DEVNODE and Driver toplogy for Legacy Widget

Your INF file would contains statements like these:

"Acme Widget Model 102 (Legacy)"=DriverInstall,*XYZ1234

. . .

[DriverInstall]

AddReg=DriverAddReg

[DriverAddReg]

HKR,,DevLoader,,widget.vxd

The only difference between Figures Five and Six is that the legacy Widget doesn't rely on SERENUM. You will have some independent way (such as a control panel applet that writes to the registry) to determine the name of the port to which your device is attached. While handling PNP_NEW_DEVNODE, you would call VCOMM_OpenComm to open this port. Thereafter, you call other VCOMM services to actually access your device.

WDM Drivers for PnP-type Devices:

Now that I've explained the architectural overview of the four different types of driver you might need for a serial-attached widget, it's time to focus in on the most common situation you're likely to encounter: a device that provides a PnP id, for which you need a driver that works in Windows 2000 and later systems. For this kind of device, you'll write a WDM driver that the PnP Manager loads automatically after SERENUM detects your device. See Figure Three. To keep the discussion bounded, I'll suppose that you're building your driver according to the pattern in my books, perhaps by using the WDMWIZ that comes with the sample drivers. Following this pattern, you'll define these subroutines in your driver:

DriverEntry initializes the DRIVER_OBJECT by providing pointers to other functions in your driver.

AddDevice creates a DEVICE_OBJECT and links it into the PnP stack of device objects.

StartDevice performs device-specific processing to help GENERIC.SYS handle IRP_MN_START_DEVICE.

StopDevice performs device-specific processing to help GENERIC.SYS handle IRP_MN_STOP_DEVICE and other PnP requests.

RemoveDevice performs device-specific processing to help GENERIC.SYS handle IRP_MN_REMOVE_DEVICE.

DispatchXxx is a standard IRP dispatch routine for an IRP_MJ_XXX request. You'll have several such routines, and you'll replace "Xxx" with various names. E.g., you'll probably have a DispatchCreate routine to handle IRP_MJ_CREATE requests, etc.

Most of the work you do in these routines has nothing to do with the fact that your device is connected to a serial port, and I'm not going to discuss it further in this article. Refer to my books and to the DDK samples for all of those details. I'm going to concentrate here on the parts that will be different because your device is connected through a serial port managed by another driver.

AddDevice processing:

In a WDM driver's AddDevice function, you normally create and initialize a DEVICE_OBJECT, establish a name for your device, initialize a device extension structure,and call IoAttachDeviceToDeviceStack to attach your new device object to the PnP stack. In my drivers, I save the return value from this function in a field within my device extension:

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

. . .

pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);

With a PnP serial-attached device, the bus driver underneath you is SERENUM.SYS, and the LowerDeviceObject belongs to it or, possibly, to some lower filter driver. No matter who owns the LowerDeviceObject, it's the target for all the IRPs you're going to generate in order to talk to your device.

StartDevice processing:

In a regular WDM driver built according to the patterns in my book, StartDevice is where you do the device-dependent work associated with handling the IRP_MN_START_DEVICE request. Instead of handling standard I/O resources like ports or interrupts, you need to open a handle to the serial port through which your device is connected. You open the port by sending a synchronous IRP_MJ_CREATE down the stack to SERENUM, which will forward it to the actual port driver:

typedef struct _DEVICE_EXTENSION {

PDEVICE_OBJECT LowerDeviceObject;

BOOLEAN portopen;

. . .

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST junk1,

PCM_PARTIAL_RESOURCE junk2)

{

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

KEVENT event;

IO_STATUS_BLOCK iosb;

KeInitializeEvent(&event, NotificationEvent, FALSE);

PIRP Irp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, pdx->LowerDeviceObject,

NULL, 0, NULL, &event, &iosb);

if (!Irp)

return STATUS_INSUFFICIENT_RESOURCES;

PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);

stack->MajorFunction = IRP_MJ_CREATE;

NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);

if (status == STATUS_PENDING)

{

KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

status = iosb.Status;

}

if (!NT_SUCCESS(status))

return status;

pdx->portopen = TRUE;

. . .

return STATUS_SUCCESS;

}

StopDevice processing:

In my driver scheme, StopDevice is where you do the device-dependent work associated with releasing your I/O resources in response to IRP_MN_STOP_DEVICE and other PnP requests. This function essentially undoes whatever work your StartDevice function did. For a serial-attached device, you need to close the handle you previously opened to the port by sending the port driver a synchronous IRP_MJ_CLOSE:

NTSTATUS StartDevice(PDEVICE_OBJECT fdo, BOOLEAN oktouch)

{

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

if (pdx->portopen)

{

pdx->portopen = FALSE;

KEVENT event;

IO_STATUS_BLOCK iosb;

KeInitializeEvent(&event, NotificationEvent, FALSE);

PIRP Irp = IoBuildSynchronousFsdRequest(IRP_MJ_FLUSH_BUFFERS, pdx->LowerDeviceObject,

NULL, 0, NULL, &event, &iosb);

if (Irp)

{

PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);

stack->MajorFunction = IRP_MJ_CLOSE;

NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);

if (status == STATUS_PENDING)

KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

}

}

. . .

}

Talking to your device:

You talk to your device indirectly, by sending IRPs down the PnP stack to SERENUM, which forwards them to the serial port driver. In turn, the serial port driver controls the RS-232 signal lines DTR and RTS; monitors the RS-232 signal lines DCD, CTS, and DSR; and reads and writes data over the RS-232 Tx and Rx signal lines.

Your job in the function driver is to create the right IRPs to control your device. A standard serial port supports about 40 different IOCTL requests, many of which you're going to need to use, as well as IRP_MJ_READ and IRP_MJ_WRITE. Within a driver routine that happens to be running at PASSIVE_LEVEL, you can use IoBuildDeviceIoControlRequest and IoBuildSynchronousFsdRequest to build synchronous IRPs to send to the port driver. The port driver is able to accept IRPs at DISPATCH_LEVEL too, however. To create an IRP at DISPATCH_LEVEL, you must use IoBuildAsynchronousFsdRequest or IoAllocateIrp, and you must have a completion routine that will (at least) call IoFreeIrp. Furthermore, you can't block at DISPATCH_LEVEL in order to wait for an asynchronous operation to complete. I'm not going to describe the mechanics of using these routines here. Consult the DDK documentation and pp. 299-303 of my book for some examples.

Table One lists some of the IOCTL codes you're likely to use in a driver for a serial-attached device. The DDK documentation describes these requests in detail.

Table One

Common IOCTL Codes for Serial Ports

Control Code

Purpose

IOCTL_SERIAL_CLR_DTR

Clears the DTR signal line

IOCTL_SERIAL_CLR_RTS

Clears the RTS signal line

IOCTL_SERIAL_GET_MODEMSTATUS

Gets the current contents of the modem status register

IOCTL_SERIAL_IMMEDIATE_CHAR

Transmits a priority character

IOCTL_SERIAL_SET_BAUD_RATE

Sets the baud rate

IOCTL_SERIAL_SET_BREAK_ON

IOCTL_SERIAL_SET_BREAK_OFF

Used in pairs to transmit a break signal

IOCTL_SERIAL_SET_DTR

Sets the DTR signal line

IOCTL_SERIAL_SET_HANDFLOW

Sets flow control options (usually, you'd want to turn off all flow control when talking to a serial-attached device, I think).

IOCTL_SERIAL_SET_LINE_CONTROL

Sets line control parameters, including parity, word-size, and number of stop bits.

IOCTL_SERIAL_SET_RTS

Sets the RTS signal line

IOCTL_SERIAL_SET_TIMEOUTS

Sets timeout values. Consult the Platform SDK documentation for the COMMTIMEOUTS structure for an explanation of the complex way these timeouts affect processing.

IOCTL_SERIAL_SET_WAIT_MASK

Set the event mask for a later WAIT_ON_MASK.

IOCTL_SERIAL_WAIT_ON_MASK

Wait for an unmasked event to occur.

The WAIT_ON_MASK request is especially important in most drivers. You will normally have one of these outstanding as an asynchronous request at all times in order to detect communications events of interest to your driver. For example:

At StartDevice time, you might use SET_WAIT_MASK to unmask the EV_RXCHAR and EV_DSR events. The Platform SDK entry for SetCommMask explains the different event codes, by the way.

Also at StartDevice time, you'd create and send an asynchronous WAIT_ON_MASK request.

The port driver completes the WAIT_ON_MASK request whenever one of the unmasked events occurs, whereupon your completion routine gains control. In the completion routine, you would examine the output mask. For EV_RXCHAR, you might read some data that has become available in the port driver's input buffer. For EV_DSR, you might use GET_MODEMSTATUS to check whether the DSR line is still set or not: perhaps your device wires DSR in the "set" state, and you can interpret DSR dropping as a surprise removal signal. After processing the events, you'd recycle the same IRP by reinitializing the next stack location and sending it back down the stack to the port driver.

While you're at the prototyping stage, by the way, you can test some aspects of your device's operation by writing a user-mode application that uses the standard Win32 API functions for serial ports to talk to the device. If you compare these functions with the documented IOCTL codes, it should be pretty obvious how to translate your test application's API calls into IRPs to be used by your driver. Just as one example, if you need to raise the DTR signal line from user mode, you'd call EscapeCommFunction with the SETDTR code. In kernel mode, you'd send an IOCTL_SERIAL_SET_DTR. (In fact, that's what EscapeCommFunction does!)

VxD Drivers for PnP-type Devices:

In the world of Windows 9x/Me, VCOMM.VXD (a system component) is in overall charge of the serial ports. Figure Seven shows where your driver (WIDGET.VXD) fits into the picture:

Figure Seven

Overall Driver Architecture in Windows 9x/Me

SERENUM.VXD will read the PnP id from your device, and the Configuration Manager will then load your driver and send you a PNP_NEW_DEVNODE control message with a dwLoadType argument of DLVXD_LOAD_DEVLOADER. You'll ignore the literal meaning of the dwLoadType argument (Yo! Load the driver for this device!) and perform the following steps:

Determine the name of the COM port to which your device is attached.

Open your COM port.

Register a configuration function.

Configure the port and your device.

Now I'll explain these steps in detail.

Determine the name of your COM port:

The name of the COM port is in the hardware registry key for the port, whose DEVNODE is your own DEVNODE's parent. See Figure Five for an illustration of the relationship between DEVNODEs and drivers. You would use code like this to get the name of the port:

extern "C" CONFIGRET OnPnpNewDevnode(DEVNODE devnode, DWORD dwLoadType)

{

. . .

DEVNODE parent;

CM_Get_parent(&parent, devnode, 0);

char portname[64];

DWORD size = sizeof(portname);

CM_Read_Registry_Value(parent, NULL, "PortName", REG_SZ, portname, &size, CM_REGISTRY_HARDWARE);

. . .

return CR_SUCCESS;

}

Open your COM port:

You open the COM port by calling a VCOMM service:

HPORT hport = VCOMM_OpenComm(portname, 0xFFFFFFFF);

If your driver is initializing during the startup of Windows, a complication can occur at this point. SERENUM keeps its handle open through much of the DEVICE_INIT phase of startup, which is the time at which the Configuration Manager is trying to configure your device. Your call to VCOMM_OpenComm will return the error code IE_OPEN in this situation. To cope with this problem, you need to do something rather complex:

Use Hook_Device_Service to hook the VCOMM_CloseComm service entry.

Return CR_DEVLOADER_NOT_READY from your PNP_NEW_DEVNODE handler. This code causes the Configuration Manager to stop trying to initialize your driver.

When SERENUM gets around to closing its handle, it will call VCOMM_CloseComm. Your hook procedure will call CM_Register_DevLoader, whereupon the Configuration Manager will send you a new PNP_NEW_DEVNODE. You'll repeat all the same steps that I've been discussing. If you succeed in opening the port, you'll unhook from VCOMM_CloseComm.

Incidentally, the reason why we want to open the COM port before registering our configuration function may now be clear: we need to return the special value CR_DEVLOADER_NOT_READY if the port is busy, but that return code would be ineffective if we were to register the configuration function first.

Register a configuration function:

Once the port is open, register your configuration function in the normal way:

DWORD flags = CM_REGISTER_DEVICE_DRIVER_REMOVABLE | CM_REGISTER_DEVICE_DRIVER_DISABLEABLE | CM_REGISTER_DEVICE_DRIVER_ACPI_APM;

CM_Register_Device_Driver(devnode, (CMCONFIGHANDLER) ConfigurationFunction, (ULONG) context, flags);

Before this DDI call returns, the Configuration Manager will send your configuration function a CONFIG_START message, which you'll handle as described in the next section.

(Note: the context parameter in the preceding code fragment is an arbitrary value that the Configuration Manager will pass on as an argument to every configuration function call. I usually create a data structure, analogous to the DEVICE_EXTENSION in a WDM driver, and use its address here.)

Configure the port:

When the Configuration Manager sends your configuration function a CONFIG_START message, you'll want to finish configuring the port. You may need to take several steps, but one of them will almost certainly be to call VCOMM_SetCommState to initialize the communication parameters for the device. The exact values you pick depend on your device:

_DCB dcb;

memset(&dcb, 0, sizeof(dcb));

dcb.DCBLength = sizeof(dcb);

dcb.BaudRate = CBR_9600;

dcb.BitMask = fBinary | fRtsDisable | fDtrEnable;

dcb.ByteSize = 8;

dcb.Parity = 0; // no parity

dcb.StopBits = 0; // 1 stop bit

. . .

VCOMM_SetCommState(hport, &dcb, 0xFFFFFFFF);

Additionally, you may need to perform these additional initialization steps:

Call VCOMM_PurgeComm to purge the input and output queues.

Call VCOMM_EnableCommNotifications to establish a callback when unmasked events occur.

Call VCOMM_SetCommEventMask to unmask selected events, such as EV_RXCHAR and EV_DSR.

Talking to your device:

You talk to your device indirectly, by calling VCOMM services. VCOMM implements service calls by making further calls to a port drivers, such as SERIAL.VXD. In turn, the serial port driver controls the RS-232 signal lines DTR and RTS; monitors the RS-232 signal lines DCD, CTS, and DSR; and reads and writes data over the RS-232 Tx and Rx signal lines.

You can find documentation for VCOMM and other VxD services in the Windows 95 DDK file docs\ddpr.hlp. Systems Programming for Windows 95 (Microsoft Press 1996) contains useful information about VCOMM and serial port drivers but is, unfortunately, out of print. Here is a summary of the VCOMM services that you're likely to use in a driver for a serial-attached device:

Table Two

Common VCOMM Service Calls

Service Call

Purpose

_VCOMM_ClearComm

Retrieves and clears error flags

_VCOMM_CloseComm

Closes a port

_VCOMM_EnableCommNotification

Enables a callback when unmasked events occur

_VCOMM_EscapeCommFunction

Performs an "escape" operation, such as CLRDTR, CLRRTS, SETDTR, SETRTS, etc.

_VCOMM_GetCommEventMask

Retrieves and clears unmasked events

_VCOMM_GetCommState

Gets the current communications parameters (baud rate, parity, etc.)

_VCOMM_GetLastError

Gets the error code associated with the most recent VCOMM service call

_VCOMM_GetModemStatus

Retrieves the current modem status register

_VCOMM_GetSetCommTimeouts

Gets or sets the timeouts for a port

_VCOMM_OpenPort

Opens a port

_VCOMM_PurgeComm

Purges the input or output buffer

_VCOMM_ReadComm

Reads Rx data

_VCOMM_SetCommEventMask

Unmasks specified events

_VCOMM_SetState

Sets communication parameters (baud rate, parity, etc.)

_VCOMM_WriteComm

Writes Tx data

Drivers for Legacy-type Devices:

In the preceding sections of this article, I considered a device that provides a PnP id when interrogated by SERENUM. If your device doesn't provide a PnP id, you need to make some slight changes to your driver. First of all, you'll want to provide some way for the end user to indicate which serial port (s)he has connected your widget to. In Windows 2000 and later systems, I recommend providing a coinstaller DLL to obtain this information when the user installs your device. Since Windows 9x and Millennium don't support coinstallers, you would need to provide some other kind of applet that you can launch from a RunOnce registry entry populated by your INF file.

An overview of the installation and removal process for a legacy-type device would, then, be as follows:

User launches Add Hardware wizard and points the setup program to your INF file.

INF file either designates a device-specific coinstaller (2K/XP) or uses the RunOnce registry key to designate an applet (9x/Me). The 2K/XP coinstaller presents an additional wizard page to obtain a port name. The 9x/Me applet presents a dialog for the same purpose. Both components write the port name to a PortName value in the hardware registry key for the device.

Driver's AddDevice function or PNP_NEW_DEVNODE handler reads the registry to determine the attachment point for the widget. From this point on, the driver will work about the same way as does the driver for a PnP device.

When user wishes to unplug the device, (s)he first opens the Device Manager and performs a Remove step.

Summary:

In this article, I discussed the basics of writing a WDM or VxD driver for a device that attaches to a Windows system via a serial port. The following table summarizes the key architectural points you'll need to consider.

Table Three

Key Architectural Points about Serial-Attached Device Drivers

Windows 2000, XP, and later

Windows 9x and Millennium

Has Plug and Play ID

WDM driver, opens LowerDeviceObject, sends IRPs to LowerDeviceObject

VxD driver, opens PortName from parent devnode's hardware key, calls VCOMM services for resulting port handle

Doesn't have Plug and Play ID

WDM driver with coinstaller, calls IoGetDeviceObjectPointer for PortName from hardware key, sends IRPs to resulting DEVICE_OBJECT

VxD driver with RunOnce applet, opens PortName from hardware key, calls VCOMM services for resulting port handle

Another key point about the kind of driver considered here is that it talks only indirectly to the hardware, either by issuing IRPs that the serial port driver interprets (2K and later) or by calling VCOMM services (9x/Me).

About the author:

Walter Oney is a freelance driver programmer, seminar leader, and author based in Boston, Massachusetts. You can reach him by e-mail at waltoney@oneysoft.com. Information about the Walter Oney Software seminar series, and other services, is available online at http://www.oneysoft.com.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有