/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_USB_DEVICE_OBJECT_H__ #define ANDROID_USB_DEVICE_OBJECT_H__ /** \file This file consists of declaration of class AndroidUsbDeviceObject that encapsulates an extension for KMDF device (FDO) object. */ #include "android_usb_wdf_object.h" // Forward declaration for file object extension class AndroidUsbFileObject; /** AndroidUsbDeviceObject class encapsulates an extension for KMDF FDO device object. Instances of this class must be allocated from NonPagedPool. */ class AndroidUsbDeviceObject : public AndroidUsbWdfObject { public: /** \brief Constructs the object. This method must be called at low IRQL. */ AndroidUsbDeviceObject(); /** \brief Destructs the object. This method can be called at any IRQL. */ ~AndroidUsbDeviceObject(); public: /** \brief Creates and initializes FDO device object extension This method is called from driver's OnAddDevice method in response to AddDevice call from the PnP manager @param device_init[in] A pointer to a framework-allocated WDFDEVICE_INIT structure. @return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values defined in ntstatus.h. */ NTSTATUS CreateFDODevice(PWDFDEVICE_INIT device_init); /** \brief Resets target device When executing this method instance of this class may be deleted! This method must be called at PASSIVE IRQL. @return STATUS_SUCCESS or an appropriate error code */ NTSTATUS ResetDevice(); private: /** \name Device event handlers and callbacks */ ///@{ /** \brief Handler for PnP prepare hardware event This method performs any operations that are needed to make a device accessible to the driver. The framework calls this callback after the PnP manager has assigned hardware resources to the device and after the device has entered its uninitialized D0 state. This callback is called before calling the driver's EvtDeviceD0Entry callback function. This method is called at PASSIVE IRQL. @param resources_raw[in] A handle to a framework resource-list object that identifies the raw hardware resources that the PnP manager has assigned to the device. @param resources_translated[in] A handle to a framework resource-list object that identifies the translated hardware resources that the PnP manager has assigned to the device. @return Successful status or an appropriate error code */ NTSTATUS OnEvtDevicePrepareHardware(WDFCMRESLIST resources_raw, WDFCMRESLIST resources_translated); /** \brief Handler for PnP release hardware event This method performs operations that that are needed when a device is no longer accessible. Framework calls the callback function if the device is being removed, or if the PnP manager is attempting to redistribute hardware resources. The framework calls the EvtDeviceReleaseHardware callback function after the driver's device has been shut off, the PnP manager has reclaimed the hardware resources that it assigned to the device, and the device is no longer accessible. (The PCI configuration state is still accessible.) Typically, a EvtDeviceReleaseHardware callback function unmaps memory that the driver's EvtDevicePrepareHardware callback function mapped. Usually, all other hardware shutdown operations should take place in the driver's EvtDeviceD0Exit callback function. This method is called at PASSIVE IRQL. @param wdf_device[in] A handle to a framework device object. @param resources_translated[in] A handle to a framework resource-list object that identifies the translated hardware resources that the PnP manager has assigned to the device. @return Successful status or an appropriate error code */ NTSTATUS OnEvtDeviceReleaseHardware(WDFCMRESLIST resources_translated); /** \brief Handler for create file event (request) This method performs operations that are needed when an application requests access to an item within this device path (including device itself). This method is called synchronously, in the context of the user thread that opens the item. This method is called at PASSIVE IRQL. @param request[in] A handle to a framework request object that represents a file creation request. @param wdf_fo[in] A handle to a framework file object that describes a file that is being created with this request. @return Successful status or an appropriate error code */ void OnEvtDeviceFileCreate(WDFREQUEST request, WDFFILEOBJECT wdf_fo); /** \brief Entry point for PnP prepare hardware event This callback performs any operations that are needed to make a device accessible to the driver. The framework calls this callback after the PnP manager has assigned hardware resources to the device and after the device has entered its uninitialized D0 state. This callback is called before calling the driver's EvtDeviceD0Entry callback function. This callback is called at PASSIVE IRQL. @param wdf_dev[in] A handle to a framework device object. @param resources_raw[in] A handle to a framework resource-list object that identifies the raw hardware resources that the PnP manager has assigned to the device. @param resources_translated[in] A handle to a framework resource-list object that identifies the translated hardware resources that the PnP manager has assigned to the device. @return Successful status or an appropriate error code */ static NTSTATUS EvtDevicePrepareHardwareEntry(WDFDEVICE wdf_dev, WDFCMRESLIST resources_raw, WDFCMRESLIST resources_translated); /** \brief Entry point for PnP release hardware event This callback performs operations that that are needed when a device is no longer accessible. Framework calls the callback function if the device is being removed, or if the PnP manager is attempting to redistribute hardware resources. The framework calls the EvtDeviceReleaseHardware callback function after the driver's device has been shut off, the PnP manager has reclaimed the hardware resources that it assigned to the device, and the device is no longer accessible. (The PCI configuration state is still accessible.) Typically, a EvtDeviceReleaseHardware callback function unmaps memory that the driver's EvtDevicePrepareHardware callback function mapped. Usually, all other hardware shutdown operations should take place in the driver's EvtDeviceD0Exit callback function. This callback is called at PASSIVE IRQL. @param wdf_dev[in] A handle to a framework device object. @param resources_translated[in] A handle to a framework resource-list object that identifies the translated hardware resources that the PnP manager has assigned to the device. @return Successful status or an appropriate error code */ static NTSTATUS EvtDeviceReleaseHardwareEntry(WDFDEVICE wdf_dev, WDFCMRESLIST resources_translated); /** \brief Entry point for create file event (request) This callback performs operations that that are needed when an application requests access to a device. The framework calls a driver's EvtDeviceFileCreate callback function when a user application or another driver opens the device (or file on this device) to perform an I/O operation, such as reading or writing a file. This callback function is called synchronously, in the context of the user thread that opens the device. This callback is called at PASSIVE IRQL. @param wdf_dev[in] A handle to a framework device object. @param request[in] A handle to a framework request object that represents a file creation request. @param wdf_fo[in] A handle to a framework file object that describes a file that is being created with this request. @return Successful status or an appropriate error code */ static void EvtDeviceFileCreateEntry(WDFDEVICE wdf_dev, WDFREQUEST request, WDFFILEOBJECT wdf_fo); ///@} private: /** \name I/O request event handlers and callbacks */ ///@{ /** \brief Read event handler This method is called when a read request comes to a file object opened on this device. This method can be called IRQL <= DISPATCH_LEVEL. @param request[in] A handle to a framework request object. @param length[in] The number of bytes to be read. */ void OnEvtIoRead(WDFREQUEST request, size_t length); /** \brief Write event handler This method is called when a write request comes to a file object opened on this device. This method can be called IRQL <= DISPATCH_LEVEL. @param request[in] A handle to a framework request object. @param length[in] The number of bytes to be written. */ void OnEvtIoWrite(WDFREQUEST request, size_t length); /** \brief IOCTL event handler This method is called when a device control request comes to a file object opened on this device. This method can be called IRQL <= DISPATCH_LEVEL. @param request[in] A handle to a framework request object. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. @param input_buf_len[in] The length, in bytes, of the request's input buffer, if an input buffer is available. @param ioctl_code[in] The driver-defined or system-defined I/O control code that is associated with the request. */ void OnEvtIoDeviceControl(WDFREQUEST request, size_t output_buf_len, size_t input_buf_len, ULONG ioctl_code); /** \brief Entry point for read event This callback is called when a read request comes to a file object opened on this device. This callback can be called IRQL <= DISPATCH_LEVEL. @param queue[in] A handle to the framework queue object that is associated with the I/O request. @param request[in] A handle to a framework request object. @param length[in] The number of bytes to be read. */ static void EvtIoReadEntry(WDFQUEUE queue, WDFREQUEST request, size_t length); /** \brief Entry point for write event This callback is called when a write request comes to a file object opened on this device. This callback can be called IRQL <= DISPATCH_LEVEL. @param queue[in] A handle to the framework queue object that is associated with the I/O request. @param request[in] A handle to a framework request object. @param length[in] The number of bytes to be written. */ static void EvtIoWriteEntry(WDFQUEUE queue, WDFREQUEST request, size_t length); /** \brief Entry point for device IOCTL event This callback is called when a device control request comes to a file object opened on this device. This callback can be called IRQL <= DISPATCH_LEVEL. @param queue[in] A handle to the framework queue object that is associated with the I/O request. @param request[in] A handle to a framework request object. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. @param input_buf_len[in] The length, in bytes, of the request's input buffer, if an input buffer is available. @param ioctl_code[in] The driver-defined or system-defined I/O control code that is associated with the request. */ static void EvtIoDeviceControlEntry(WDFQUEUE queue, WDFREQUEST request, size_t output_buf_len, size_t input_buf_len, ULONG ioctl_code); ///@} public: /** \name Device level I/O request handlers */ ///@{ /** \brief Gets USB device descriptor This method can be called at IRQL <= DISPATCH_LEVEL @param request[in] A handle to a framework request object for this IOCTL. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. */ void OnGetUsbDeviceDescriptorCtl(WDFREQUEST request, size_t output_buf_len); /** \brief Gets USB configuration descriptor for the selected configuration. This method can be called at IRQL <= DISPATCH_LEVEL @param request[in] A handle to a framework request object for this IOCTL. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. */ void OnGetUsbConfigDescriptorCtl(WDFREQUEST request, size_t output_buf_len); /** \brief Gets USB configuration descriptor for the selected interface. This method can be called at IRQL <= DISPATCH_LEVEL @param request[in] A handle to a framework request object for this IOCTL. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. */ void OnGetUsbInterfaceDescriptorCtl(WDFREQUEST request, size_t output_buf_len); /** \brief Gets information about an endpoint. This method can be called at IRQL <= DISPATCH_LEVEL @param request[in] A handle to a framework request object for this IOCTL. @param input_buf_len[in] The length, in bytes, of the request's input buffer, if an input buffer is available. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. */ void OnGetEndpointInformationCtl(WDFREQUEST request, size_t input_buf_len, size_t output_buf_len); /** \brief Gets device serial number. Serial number is returned in form of zero-terminated string that in the output buffer. This method must be called at low IRQL. @param request[in] A handle to a framework request object for this IOCTL. @param output_buf_len[in] The length, in bytes, of the request's output buffer, if an output buffer is available. */ void OnGetSerialNumberCtl(WDFREQUEST request, size_t output_buf_len); ///@} private: /** \name Internal methods */ ///@{ /** \brief Creates default request queue for this device. In KMDF all I/O requests are coming through the queue object. So, in order to enable our device to receive I/O requests we must create a queue for it. This method is called at PASSIVE IRQL. @return STATUS_SUCCESS or an appropriate error code. */ NTSTATUS CreateDefaultQueue(); /** \brief Configures our device. This method is called from the prepare hardware handler after underlying FDO device has been created. This method is called at PASSSIVE IRQL. @return STATUS_SUCCESS or an appropriate error code. */ NTSTATUS ConfigureDevice(); /** \brief Selects interfaces on our device. This method is called from the prepare hardware handler after underlying FDO device has been created and configured. This method is called at PASSSIVE IRQL. @return STATUS_SUCCESS or an appropriate error code. */ NTSTATUS SelectInterfaces(); /** \brief Gets pipe index from a file name This method is called from OnEvtDeviceFileCreate to determine index of the pipe this file is addressing. This method is called at PASSIVE IRQL. @param file_path[in] Path to the file that being opened. @return Pipe index or INVALID_UCHAR if index cannot be calculated. */ UCHAR GetPipeIndexFromFileName(PUNICODE_STRING file_path); /** \brief Creates file object extension for a pipe This method is called from OnEvtDeviceFileCreate to create an appropriate file object extension for a particular pipe type. This method is called at PASSIVE IRQL. @param wdf_fo[in] KMDF file to extend. @param wdf_pipe_obj[in] KMDF pipe for this extension @param pipe_info[in] Pipe information @param wdf_file_ext[out] Upon successfull completion will receive instance of the extension. @return STATUS_SUCCESS or an appropriate error code */ NTSTATUS CreatePipeFileObjectExt(WDFFILEOBJECT wdf_fo, WDFUSBPIPE wdf_pipe_obj, const WDF_USB_PIPE_INFORMATION* pipe_info, AndroidUsbFileObject** wdf_file_ext); ///@} private: /** \name Debugging support */ ///@{ #if DBG /// Prints USB_DEVICE_DESCRIPTOR to debug output void PrintUsbDeviceDescriptor(const USB_DEVICE_DESCRIPTOR* desc); /// Prints WDF_USB_DEVICE_INFORMATION to debug output void PrintUsbTargedDeviceInformation(const WDF_USB_DEVICE_INFORMATION* info); /// Prints USB_CONFIGURATION_DESCRIPTOR to debug output void PrintConfigDescriptor(const USB_CONFIGURATION_DESCRIPTOR* desc, ULONG size); /// Prints WDF_USB_DEVICE_SELECT_CONFIG_PARAMS to debug output void PrintSelectedConfig(const WDF_USB_DEVICE_SELECT_CONFIG_PARAMS* config); /// Prints USB_INTERFACE_DESCRIPTOR to debug output void PrintInterfaceDescriptor(const USB_INTERFACE_DESCRIPTOR* desc); /// Prints WDF_USB_PIPE_INFORMATION to debug output void PrintPipeInformation(const WDF_USB_PIPE_INFORMATION* info, UCHAR pipe_index); #endif // DBG ///@} public: /// Gets WDF device handle for this device __forceinline WDFDEVICE wdf_device() const { return reinterpret_cast<WDFDEVICE>(wdf_object()); } /// Gets target USB device descriptor __forceinline const USB_DEVICE_DESCRIPTOR* usb_device_descriptor() const { return &usb_device_descriptor_; } /// Gets target USB device information __forceinline const WDF_USB_DEVICE_INFORMATION* usb_device_info() const { return &usb_device_info_; } /// Gets selected interface descriptor __forceinline const USB_INTERFACE_DESCRIPTOR* interface_descriptor() const { return &interface_descriptor_; } /// Gets target (PDO) device handle __forceinline WDFUSBDEVICE wdf_target_device() const { return wdf_target_device_; } /// Checks if target device has been created __forceinline bool IsTaretDeviceCreated() const { return (NULL != wdf_target_device()); } /// Gets USB configuration descriptor __forceinline const USB_CONFIGURATION_DESCRIPTOR* configuration_descriptor() const { return configuration_descriptor_; } /// Checks if device has been configured __forceinline bool IsDeviceConfigured() const { return (NULL != configuration_descriptor()); } /// Gets number of interfaces for this device __forceinline UCHAR GetInterfaceCount() const { ASSERT(IsDeviceConfigured()); return IsDeviceConfigured() ? configuration_descriptor()->bNumInterfaces : 0; } /// Checks if this is "single interface" device __forceinline bool IsSingleInterfaceDevice() const { return (1 == GetInterfaceCount()); } /// Gets USB interface selected on this device __forceinline WDFUSBINTERFACE wdf_usb_interface() const { return wdf_usb_interface_; } /// Checks if an interface has been selected on this device __forceinline bool IsInterfaceSelected() const { return (NULL != wdf_usb_interface()); } /// Gets number of pipes configured on this device __forceinline UCHAR configured_pipes_num() const { return configured_pipes_num_; } /// Gets index of the bulk read pipe __forceinline UCHAR bulk_read_pipe_index() const { return bulk_read_pipe_index_; } /// Gets index of the bulk write pipe __forceinline UCHAR bulk_write_pipe_index() const { return bulk_write_pipe_index_; } /// Checks if this is a high speed device __forceinline bool IsHighSpeed() const { return (0 != (usb_device_info()->Traits & WDF_USB_DEVICE_TRAIT_AT_HIGH_SPEED)); } /// Checks if bulk read pipe index is known __forceinline bool IsBulkReadPipeKnown() const { return (INVALID_UCHAR != bulk_read_pipe_index()); } /// Checks if bulk write pipe index is known __forceinline bool IsBulkWritePipeKnown() const { return (INVALID_UCHAR != bulk_write_pipe_index()); } /// Gets device serial number string. Note that string may be /// not zero-terminated. Use serial_number_len() to get actual /// length of this string. __forceinline const WCHAR* serial_number() const { ASSERT(NULL != serial_number_handle_); return (NULL != serial_number_handle_) ? reinterpret_cast<const WCHAR*> (WdfMemoryGetBuffer(serial_number_handle_, NULL)) : NULL; } /// Gets length (in bytes) of device serial number string __forceinline USHORT serial_number_char_len() const { return serial_number_char_len_; } /// Gets length (in bytes) of device serial number string __forceinline USHORT serial_number_byte_len() const { return serial_number_char_len() * sizeof(WCHAR); } protected: /// Target USB device descriptor USB_DEVICE_DESCRIPTOR usb_device_descriptor_; /// Target USB device information WDF_USB_DEVICE_INFORMATION usb_device_info_; /// Selected interface descriptor USB_INTERFACE_DESCRIPTOR interface_descriptor_; /// USB configuration descriptor PUSB_CONFIGURATION_DESCRIPTOR configuration_descriptor_; /// Target (PDO?) device handle WDFUSBDEVICE wdf_target_device_; /// USB interface selected on this device WDFUSBINTERFACE wdf_usb_interface_; /// Device serial number WDFMEMORY serial_number_handle_; /// Device serial number string length USHORT serial_number_char_len_; /// Number of pipes configured on this device UCHAR configured_pipes_num_; /// Index of the bulk read pipe UCHAR bulk_read_pipe_index_; /// Index of the bulk write pipe UCHAR bulk_write_pipe_index_; }; /** \brief Gets device KMDF object extension for the given KMDF object @param wdf_dev[in] KMDF handle describing device object @return Instance of AndroidUsbDeviceObject associated with KMDF object or NULL if association is not found. */ __forceinline AndroidUsbDeviceObject* GetAndroidUsbDeviceObjectFromHandle( WDFDEVICE wdf_dev) { AndroidUsbWdfObject* wdf_object_ext = GetAndroidUsbWdfObjectFromHandle(wdf_dev); ASSERT((NULL != wdf_object_ext) && wdf_object_ext->Is(AndroidUsbWdfObjectTypeDevice)); if ((NULL != wdf_object_ext) && wdf_object_ext->Is(AndroidUsbWdfObjectTypeDevice)) { return reinterpret_cast<AndroidUsbDeviceObject*>(wdf_object_ext); } return NULL; } #endif // ANDROID_USB_DEVICE_OBJECT_H__