When defining new IOCTLs, it is important to remember the following rules:
An I/O control code is a 32-bit value that consists of several fields. The following figure illustrates the layout of I/O control codes.

I/O Control Code Layout
Use the system-supplied CTL_CODE macro, which is defined in wdm.h and ntddk.h, to define new I/O control codes. The definition of a new IOCTL code, whether intended for use with IRP_MJ_DEVICE_CONTROL or IRP_MJ_INTERNAL_DEVICE_CONTROL requests, uses the following format:
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
Choose a descriptive constant name for the IOCTL, of the form IOCTL_Device_Function, where Device indicates the type of device and Function indicates the operation. An example constant name is IOCTL_VIDEO_ENABLE_CURSOR.
Supply the following parameters to the CTL_CODE macro:
Use one of the following system-defined constants:
For information about how the system specifies data buffers for METHOD_BUFFERED I/O control codes, see Buffer Descriptions for I/O Control Codes.
For more information about buffered I/O, see Using Buffered I/O.
Specify METHOD_IN_DIRECT if the caller of DeviceIoControl or IoBuildDeviceIoControlRequest will pass data to the driver.
Specify METHOD_OUT_DIRECT if the caller of DeviceIoControl or IoBuildDeviceIoControlRequest will receive data from the driver.
For information about how the system specifies data buffers for METHOD_IN_DIRECT and METHOD_OUT_DIRECT I/O control codes, see Buffer Descriptions for I/O Control Codes.
For more information about direct I/O, see Using Direct I/O.
For information about how the system specifies data buffers for METHOD_NEITHER I/O control codes, see Buffer Descriptions for I/O Control Codes.
This method can be used only if the driver can be guaranteed to be running in the context of the thread that originated the I/O control request. Only a highest-level kernel-mode driver is guaranteed to meet this condition, so METHOD_NEITHER is seldom used for the I/O control codes that are passed to low-level device drivers.
With this method, the highest-level driver must determine whether to set up buffered or direct access to user data on receipt of the request, possibly must lock down the user buffer, and must wrap its access to the user buffer in a structured exception handler (see Handling Exceptions). Otherwise, the originating user-mode caller might change the buffered data before the driver can use it, or the caller could be swapped out just as the driver is accessing the user buffer.
For more information, see Using Neither Buffered Nor Direct I/O.
FILE_READ_DATA and FILE_WRITE_DATA can be OR'ed together if the caller must have both read and write access rights.
Some system-defined I/O control codes have a RequiredAccess value of FILE_ANY_ACCESS. This is especially true for I/O control codes that are sent to drivers of exclusive devices, and for those that specify buffered I/O.
The system-defined I/O control codes for some types of devices require the caller to have read access rights, write access rights, or both. For example, the following definition of the public I/O control code IOCTL_DISK_SET_PARTITION_INFO shows that this I/O request can be sent to a driver only if the caller has both read and write access rights:
#define IOCTL_DISK_SET_PARTITION_INFO\
CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
FILE_READ_DATA | FILE_WRITE_DATA)
Before specifying FILE_ANY_ACCESS for a new IOCTL code, you must be absolutely certain that allowing unrestricted access to your device does not create a possible path for malicious users to compromise the system.
Drivers can use IoValidateDeviceIoControlAccess to perform stricter access checking than that provided by an IOCTL's RequiredAccess bits.