Definición de códigos de control de E/S
Al definir nuevas ICTLs, es importante recordar las reglas siguientes:
- Si un nuevo IOCTL estará disponible para los componentes de software en modo de usuario, el IOCTL debe usarse con IRP_MJ_DEVICE_CONTROL solicitudes. Los componentes en modo de usuario envían IRP_MJ_DEVICE_CONTROL solicitudes mediante una llamada a DeviceIoControl, que es una función Win32.
- Si un nuevo IOCTL solo estará disponible para los componentes del controlador en modo kernel, se debe usar el IOCTL con IRP_MJ_INTERNAL_DEVICE_CONTROL solicitudes. Los componentes del modo kernel crean IRP_MJ_INTERNAL_DEVICE_CONTROL solicitudes mediante una llamada a IoBuildDeviceIoControlRequest. Para obtener más información, vea Crear solicitudes IOCTL en controladores.
Un código de control de E/S es un valor de 32 bits que consta de varios campos. En la ilustración siguiente se muestra el diseño de los códigos de control de E/S.
Use la macro de CTL_CODE proporcionada por el sistema, que se define en Wdm.h y Ntddk.h, para definir nuevos códigos de control de E/S. La definición de un nuevo código IOCTL, ya sea diseñado para su uso con solicitudes de IRP_MJ_DEVICE_CONTROL o IRP_MJ_INTERNAL_DEVICE_CONTROL , usa el siguiente formato:
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
Elija un nombre de constante descriptivo para el IOCTL, con el formato IOCTL_Device_Function, donde Device indica el tipo de dispositivo y Function indica la operación. Se IOCTL_VIDEO_ENABLE_CURSOR un nombre constante de ejemplo.
Proporcione los parámetros siguientes a la macro CTL_CODE :
Identifica el tipo de dispositivo. Este valor debe coincidir con el valor establecido en el miembro DeviceType de la estructura DEVICE_OBJECT del controlador. (Consulte Especificación de tipos de dispositivo). Los valores de menos de 0x8000 están reservados para Microsoft. Los proveedores pueden usar los valores de 0x8000 y superiores. Tenga en cuenta que los valores asignados por el proveedor establecen el bit Común .
FunctionCode
Identifica la función que va a realizar el controlador. Los valores de menos de 0x800 están reservados para Microsoft. Los proveedores pueden usar los valores de 0x800 y superiores. Tenga en cuenta que los valores asignados por el proveedor establecen el bit Personalizado .
TransferType
Indica cómo pasará el sistema los datos entre el autor de la llamada de DeviceIoControl (o IoBuildDeviceIoControlRequest) y el controlador que controla el IRP.
Use una de las siguientes constantes definidas por el sistema:
METHOD_BUFFERED
Especifica el método de E/S almacenado en búfer , que normalmente se usa para transferir pequeñas cantidades de datos por solicitud. La mayoría de los códigos de control de E/S para los controladores intermedios y de dispositivo usan este valor TransferType .
Para obtener información sobre cómo el sistema especifica búferes de datos para METHOD_BUFFERED códigos de control de E/S, vea Búfer Descripciones de códigos de control de E/S.
Para obtener más información sobre la E/S almacenada en búfer, consulte Uso de E /S almacenado en búfer.
METHOD_IN_DIRECT o METHOD_OUT_DIRECT
Especifica el método de E/S directo , que normalmente se usa para leer o escribir grandes cantidades de datos, mediante DMA o PIO, que se deben transferir rápidamente.
Especifique METHOD_IN_DIRECT si el autor de la llamada de DeviceIoControl o IoBuildDeviceIoControlRequest pasará datos al controlador.
Especifique METHOD_OUT_DIRECT si el autor de la llamada de DeviceIoControl o IoBuildDeviceIoControlRequest recibirá datos del controlador.
Para obtener información sobre cómo el sistema especifica búferes de datos para METHOD_IN_DIRECT y METHOD_OUT_DIRECT códigos de control de E/S, vea Descripciones de búfer para códigos de control de E/S.
Para obtener más información sobre la E/S directa, consulte Uso de E /S directa.
METHOD_NEITHER
Especifica ni almacenado en búfer ni E/S directa. El administrador de E/S no proporciona ningún búfer del sistema ni MDL. IRP proporciona las direcciones virtuales en modo de usuario de los búferes de entrada y salida especificados en DeviceIoControl o IoBuildDeviceIoControlRequest, sin validarlos ni asignarlos.
Para obtener información sobre cómo el sistema especifica los búferes de datos para METHOD_NEITHER códigos de control de E/S, vea Descripciones de búferes para códigos de control de E/S.
Este método solo se puede usar si se puede garantizar que el controlador se esté ejecutando en el contexto del subproceso que originó la solicitud de control de E/S. Solo se garantiza que un controlador de modo kernel de nivel superior cumpla esta condición, por lo que METHOD_NEITHER rara vez se usa para los códigos de control de E/S que se pasan a controladores de dispositivo de bajo nivel.
Con este método, el controlador de nivel superior debe determinar si configurar el acceso almacenado en búfer o directo a los datos del usuario al recibir la solicitud, posiblemente debe bloquear el búfer de usuario y debe encapsular su acceso al búfer de usuario en un controlador de excepciones estructurado (consulte Control de excepciones). De lo contrario, el autor de la llamada del modo de usuario de origen podría cambiar los datos almacenados en búfer antes de que el controlador pueda usarlo, o el autor de la llamada podría intercambiarse igual que el controlador accede al búfer de usuario.
Para obtener más información, consulte Uso de E/S directa ni almacenada en búfer.
RequiredAccess
Indica el tipo de acceso que un autor de la llamada debe solicitar al abrir el objeto de archivo que representa el dispositivo (consulte IRP_MJ_CREATE). El administrador de E/S creará IRP y llamará al controlador con un código de control de E/S determinado solo si el autor de la llamada ha solicitado los derechos de acceso especificados.
RequiredAccess se especifica mediante las siguientes constantes definidas por el sistema:
FILE_ANY_ACCESS
El administrador de E/S envía el IRP para cualquier llamador que tenga un identificador al objeto de archivo que representa el objeto de dispositivo de destino.
FILE_READ_DATA
El administrador de E/S envía el IRP solo para un autor de la llamada con derechos de acceso de lectura, lo que permite al controlador de dispositivo subyacente transferir datos desde el dispositivo a la memoria del sistema.
FILE_WRITE_DATA
El administrador de E/S envía el IRP solo para un autor de la llamada con derechos de acceso de escritura, lo que permite al controlador de dispositivo subyacente transferir datos de la memoria del sistema a su dispositivo.
FILE_READ_DATA y FILE_WRITE_DATA pueden ser ORed juntos si el autor de la llamada debe tener derechos de acceso de lectura y escritura.
Algunos códigos de control de E/S definidos por el sistema tienen un valor RequiredAccess de FILE_ANY_ACCESS, que permite al autor de la llamada enviar el IOCTL concreto independientemente del acceso concedido al dispositivo. Algunos ejemplos son los códigos de control de E/S que se envían a los controladores de dispositivos exclusivos.
Otros códigos de control de E/S definidos por el sistema requieren que el autor de la llamada tenga derechos de acceso de lectura, derechos de acceso de escritura o ambos. Por ejemplo, la siguiente definición del código de control de E/S pública IOCTL_DISK_SET_PARTITION_INFO muestra que esta solicitud de E/S se puede enviar a un controlador solo si el autor de la llamada tiene derechos de acceso de lectura y escritura:
#define IOCTL_DISK_SET_PARTITION_INFO\
CTL_CODE(IOCTL_DISK_BASE, 0x008, METHOD_BUFFERED,\
FILE_READ_DATA | FILE_WRITE_DATA)
Nota:
Antes de especificar FILE_ANY_ACCESS para un nuevo código IOCTL, debe estar absolutamente seguro de que permitir el acceso sin restricciones al dispositivo no crea una ruta de acceso posible para que los usuarios malintencionados pongan en peligro el sistema.
Los controladores pueden usar IoValidateDeviceIoControlAccess para realizar comprobaciones de acceso más estrictas que las proporcionadas por los bits RequiredAccess de un IOCTL.
Otras macros útiles
Las macros siguientes son útiles para extraer los campos DeviceType de 16 bits y TransferType de 2 bits de un código IOCTL:
#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode) (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
#define METHOD_FROM_CTL_CODE(ctrlCode) ((ULONG)(ctrlCode & 3))
Estas macros se definen en Wdm.h y Ntddk.h.