Bluetooth Low Energy (BLE) Multiple Characteristics - embedded

I am trying to get a BLE module (datasheet) read/write to my laptop. I'm using python on my laptop and am able to see the services and characteristics as follows:
SERVICE 00001800-0000-1000-8000-00805f9b34fb (Handle: 1): Generic Access Profile
CHARACTERISTIC 00002a00-0000-1000-8000-00805f9b34fb (Handle: 2): Device Name ['read']
CHARACTERISTIC 00002a01-0000-1000-8000-00805f9b34fb (Handle: 4): Appearance ['read']
CHARACTERISTIC 00002a04-0000-1000-8000-00805f9b34fb (Handle: 6): Peripheral Preferred Connection Parameters ['read']
SERVICE 00001801-0000-1000-8000-00805f9b34fb (Handle: 8): Generic Attribute Profile
CHARACTERISTIC 00002a05-0000-1000-8000-00805f9b34fb (Handle: 9): Service Changed ['indicate']
DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 11): Client Characteristic Configuration
SERVICE 0000180a-0000-1000-8000-00805f9b34fb (Handle: 12): Device Information
CHARACTERISTIC 00002a23-0000-1000-8000-00805f9b34fb (Handle: 13): System ID ['read']
CHARACTERISTIC 00002a26-0000-1000-8000-00805f9b34fb (Handle: 15): Firmware Revision String ['read']
SERVICE 0000ffc0-0000-1000-8000-00805f9b34fb (Handle: 17): Vendor specific
CHARACTERISTIC 0000ffc1-0000-1000-8000-00805f9b34fb (Handle: 18): Vendor specific ['write-without-response', 'write']
CHARACTERISTIC 0000ffc2-0000-1000-8000-00805f9b34fb (Handle: 20): Vendor specific ['notify', 'indicate']
DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 22): Client Characteristic Configuration
SERVICE 0000ff90-0000-1000-8000-00805f9b34fb (Handle: 23): Vendor specific
CHARACTERISTIC 0000ff91-0000-1000-8000-00805f9b34fb (Handle: 24): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff92-0000-1000-8000-00805f9b34fb (Handle: 26): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff93-0000-1000-8000-00805f9b34fb (Handle: 28): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff94-0000-1000-8000-00805f9b34fb (Handle: 30): Vendor specific ['write-without-response', 'write']
CHARACTERISTIC 0000ff95-0000-1000-8000-00805f9b34fb (Handle: 32): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff96-0000-1000-8000-00805f9b34fb (Handle: 34): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff97-0000-1000-8000-00805f9b34fb (Handle: 36): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff98-0000-1000-8000-00805f9b34fb (Handle: 38): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff99-0000-1000-8000-00805f9b34fb (Handle: 40): Vendor specific ['read', 'write-without-response', 'write']
CHARACTERISTIC 0000ff9a-0000-1000-8000-00805f9b34fb (Handle: 42): Vendor specific ['read', 'write-without-response', 'write']
SERVICE 0000ffe0-0000-1000-8000-00805f9b34fb (Handle: 44): Vendor specific
CHARACTERISTIC 0000ffe9-0000-1000-8000-00805f9b34fb (Handle: 45): Vendor specific ['write-without-response', 'write']
CHARACTERISTIC 0000ffe4-0000-1000-8000-00805f9b34fb (Handle: 47): Vendor specific ['notify']
DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 49): Client Characteristic Configuration
SERVICE 5833ff01-9b8b-5191-6142-22a4536ef123 (Handle: 50): Unknown
CHARACTERISTIC 5833ff02-9b8b-5191-6142-22a4536ef123 (Handle: 51): Unknown ['write']
CHARACTERISTIC 5833ff03-9b8b-5191-6142-22a4536ef123 (Handle: 53): Unknown ['notify']
DESCRIPTOR 00002902-0000-1000-8000-00805f9b34fb (Handle: 55): Client Characteristic Configuration
I'm am getting that I am connected to the module but when I try to send data to module or receive from module (module is connected via UART to MCU) I don't get anything.
I'm new to BLE and tried a few characteristics above for the read and write but nothing seems to work. Why are there many multiples of characteristics and which one should I use?
P.S.
Also confused about notify vs read. From what I understand they are same thing.

Every BLE development, regardless if it is between two mobile phones or using a different BLE device, should start by scanning the target device using a generic BLE scanner such as nRF Connect or LightBlue. This way you can make sure that the BLE device is working properly before attempting to develop something on your own.
Regarding your device, the posted datasheet contains an interface description on page 27 where they explain that the Pass-through Data Channel uses the Service UUID 0xFFE0. THis is a 16-Bit short version and can be converted to 128 bit if needed.
The Service should contain two characteristics:
Characteristic UUID
Privilege
Bytes
Default Value
Remark
FFE9 (handle: 0x0013)
Write
20
None
APP Write data to module and output to UART TX.
FFE4 (handle: 0x000E)
notify
20
None
Notify the data from UART RX to BLE APP.
Try using one of the BLE scanner mentioned above to write to FFE9 and activate notifications on FFE4.
The notification privilege is not the same as a read privilege. You can't read from a characteristic that only offers notifications, but you can subscribe to them and, as the name suggests, get notified if new data is available. The data will be transmitted with the notification.

Related

Is it possible to create a Bluetooth (BLE) custom Service and Characteristic for the smartphone using react-native?

I have to create a custom Service and Characteristic on the smartphone so the peripheral device could write into these new smartphone characteristic. Is this possible using react-native-ble-plx? Is it possible to monitor the managers characteristics? Is there an example of this?
I've already monitored the peripheral devices characteristic successfully, but some developers told me that the communication is faster when you monitor your own characteristic.

Reading USB device Vendor ID and Device ID from PCI config space (EFI)

I want to get Vendor ID and Device ID for plugged USB device via EFI program. I can read whole PCI config space I find USB host controller to which My USB device is pugged I can also read whole memory addressed for this controller but I don't know what exactly I'am searching for in this memory to get these IDs. Can someone help me?
I will answer for xHCI. The USB protocols define a setup packet, which the xHCI Driver must construct and the xHCI controller hardware converts it to a USB transaction with the device – there are no registers that it accesses directly for this information like with PCIe.
After a hardware reset, all root hub ports will be in a disconnected state. The port will be powered on and waiting for a device connect. When the hardware detects a device attach, it sets the Current Connect Status and Connect Status Change flags in the PORTSC register to 1, and this action will cause the PSCEG signal to go high at the same time as it is a logical OR of the PORTSC register bits. This signal generates a Port Status Change Event in the controller, which causes the xHCI controller hardware to put a packet (known as a Transfer Request Block) on the Event Ring. The Event Ring segments and table were allocated from the non-paged pool and initialised during xHCI controller enumeration, likely during the StartDevice routine of the xHCI Driver, which is called when it is loaded; it also initialises the Event Ring Registers in the device MMIO space.
After enqueuing the event on the ring, the hardware triggers an interrupt at a particular offset into the MSI-X table (the MSI-X offset from a BAR and BAR no. are stored in the MSI-X capability in the PCIe configuration space of the xHCI controller; hence, the MSI-X table is in the MMIO space). The Primary Event Ring always receives all Port Status Change events. The Primary Event Ring is always mapped to the first MSI-X interrupt. The MSI-X interrupt will travel as a standard PCIe MSI to the LAPIC and it will queue an interrupt in the IRR for the vector specified in the MSI-X table store data. The xHCI Driver previously registered an ISR at the IDT entry corresponding to the vector using kernel and HAL APIs. The xHCI Driver's ISR realises the TRB packet is a Port Status Change event; it can evaluate the port ID to determine the root hub port that was the source of the change event (e.g. 5) and then examine the 5th PORTSC register to see what change has taken place, which it accesses at a certain offset from the operational base, which is an offset from the address in the BAR of the xHCI controller's PCIe configuration space that was allocated during PCIe enumeration at boot (MMIO base); the formula is Operational base + (400h + (10h*(n-1))), where n is the port number 1 through MaxPorts and Operational base is the value in the CAPLENGTH register + MMIO base.
The xHCI Driver notifies the Root Hub Driver that a device has arrived (I think by calling a Root Hub callback function, which it accesses through the PDO of the Root Hub maybe), and the Root Hub Driver creates a PDO for it and notifies the PnP manager that the set of child devices has changed for the Root Hub. Either the xHCI Driver automatically assigns a Slot ID and does the address device TRB procedure silently before calling the callback function and provides the slot ID on the spot, or the Hub Driver has to initiate this by sending a URB to the xHCI controller to request a slot ID be assigned and returned to it when it is informed that there is a port status change on a certain Port ID (I'm not sure. And I'm not sure which data structures are controlled by the hub driver instead of the xHCI driver, so this is a guess). When the xHCI Driver receives the URB, it will send an enable slot TRB on the command ring and get the slot ID from the command completion TRB on the event ring; it will allocate a Device Context structure (known as Output Device Context) and pointer to it in the Device Context Base Array, which the xHCI controller maintains in non-paged pool. In the URB response, the hub driver receives the slot ID; the hub driver then allocates an Input Device Context in memory. The Add Context flags for the slot context and endpoint 0 context in the Input Control Context structure in the Input Device Context are set to 1 to indicate they need to be added. The slot context in the input device context structure is then allocated with port number, root string and number of endpoints. The Endpoint 0 (aka. default control) context data structure in the Input Context must be configured with valid values for the TR Dequeue pointer (points to the transfer ring it allocates) , EP type, Error Count and Max Packet Size fields. The MaxPStreams, Max Burst Size and EP state values should be set to 0. The Input Context structure pointer is sent by the hub in an address device command addressed to the Slot ID of the new device, which is sent via a URB to the xHCI Driver and it places the address device TRB on the command ring and the Host Controller will copy the Input Context to the Output Context pointed to by the DCBA entry for the slot.
The Hub Driver then sends URBs to the xHCI Driver to get the device descriptor in the following in the form:
Status = SubmitRequestToRootHub(RootHubDeviceObject,
IOCTL_INTERNAL_USB_SUBMIT_URB,
Urb,
NULL);
All URBs sent after the Slot ID is returned to the hub will contain the slot ID. It will also link ChildDeviceObject->DeviceExtension->UsbDeviceHandle to Urb->UrbHeader.UsbdDeviceHandle, which makes the PDO the hub allocated for the new device accessible through the URB. RootHubDeviceObject is the PDO of the Hub Driver, which is owned by the xHCI Controller Driver (or usbxhci-usbport pair), which will be used in the call to IoCallDriver inside this routine. The URB will be of type GET_DESCRIPTOR. The IRP is then initialised with a major code of IRP_MJ_INTERNAL_DEVICE_CONTROL and the stack location is initialised with the URB as one of the parameters and StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;. It then calls IoCallDriver on the RootHubDeviceObject, which is owned by the xHCI Driver.
The xHCI driver will use Slot ID specified in the URB to index into the DCBA array and the doorbell array. It goes to the Control (Default, 0) Endpoint descriptor, which is at index 1 in the slot's device context array pointed to by DCBA[slotID] (slot context is at index 0) and it will write a Setup stage TD (Which consists of a single Setup TRB) to the Enqueue Pointer specified in the default (control) endpoint descriptor (which I presume is autoset to the same address as the dequeue pointer originally in the input device context when the xHCI controller handles the address device command), which is a physical address in RAM that the xHCI controller reads from using PCIe TLP transactions. In the TRB, it specifies the TRB type (Setup); Transfer Type (IN); Transfer Length (Device Descriptor size = 18); Immediate data = 0 (not sure what this is but only the setup stage appears to have it toggled to 1); Interrupt on completion (no); bmRequestType = 80h and bRequest = 6 together specify the GET_DESCRIPTOR request type; wValue is set to type: Device Descriptor, which is 0100h; and then wLength is 18 (length of the device descriptor). It then advances the Endpoint 0 Transfer ring Enqueue Pointer (adds the size of the previous TD to it). It then writes a Data Stage TD at the location of the new Enqueue Pointer that it wrote; but, indeed, it uses the virtual address the xHCI Driver defined over the MMIO space on xHCI enumeration (it used MmMapIoSpace on the CM_RESOURCE_LIST in Parameters.StartDevice.AllocatedResourcesTranslated in the start device routine) to write to the location in RAM because system software cannot use physical addresses unlike PCIe devices (the Host Controller). The Data Stage TD consists of one TRB with TRB type set to Data Stage TRB; Direction = 1; TRB transfer length = 18; Chain bit = 0; IOC = 0 (doesn't interrupt because only the Status Stage causes an interrupt i.e. when it is done); Immediate data = 0; Data buffer pointer (the 64 bit physical address that the xHCI controller will write the response to which is translated from the virtual address provided by the hub driver); and the Cycle bit (Current producer cycle state (toggled to 1 or 0 based on the enqueue pointer wrapping round the ring. The producer toggles the Cycle Bit from 0 to 1 if it encounters a link TRB (it reads before writing to a location to make sure there is not a Link TRB already there that points to the start of the ring)). It then advances the Enqueue pointer again. Finally it writes a Status Stage TD that consists of a single status TRB with TRB type = Status Stage TRB; Direction = '0'; TRB transfer length = 0; Chain bit = 0; Interrupt On Completion = 1; Immediate data = 0; Data buffer pointer = 0 (there isn't one as it's just a status stage); and Cycle bit = (Current producer cycle state).
The xHCI Driver then indexes into the Doorbell Array using the Slot ID and writes a sequence to the doorbell register at that index, which indicates that the Control EP 0 Enqueue Pointer was updated. The Host Controller then kicks into action and reads the TRBs, incrementing the Dequeue Pointer; and, when it is equal to the Enqueue Pointer, it stops. For each TRB, it sends the appropriate packet to the device. When it processes the Status stage TRB it will cause an interrupt on the Event Ring (I think ring 0), which causes an MSI-x interrupt, as stated before, to the LAPIC of the CPU to the specified vector, which will be picked up by the xHCI Contoller ISR and DPC. The ISR will deploy a DPC that will complete the IRP. The descriptor will be at the virtual address specified in the URB IRP by the Hub Driver.
The Hub Driver inserts the information it received in the URB IRPs into the PDO->DeviceExtension field, which is a pointer to a driver defined structure with which it can do what it wants, which means the information is essentially cached and no more URBs need to be sent to the xHCI Driver. The hub also sends GET_CONFIGURATION URBs to the xHCI Driver for each configuration number that the device reported in the Device Descriptor. The xHCI Driver would then pass that configuration value to the device in a GET_CONFIGURATION TRB with the correct configuration number, and the whole configuration hierarchy for that configuration number would be returned to the Hub Driver at the address specified in the URB.
The hub driver then alerts the PnP manager to the arrival of the device by calling IoInvalidateDeviceRelations() with Type parameter of BusRelations and a pointer to its PDO (Physical device object) that it was assigned by the xHCI Driver. The PnP manager queries the Device Stack of the PDO for the current list of devices on the bus using an IRP_MN_QUERY_DEVICE_RELATIONS request; to do so, it initialises an IRP structure (ideally reusing one from a lookaside list based on the stacksize hint in the device object; otherwise, it directly allocates memory from the non-paged pool for a new one). The IRP points to the stack (which is contiguous with the IRP) through the CurrentStackLocation member. It then initialises the first stack location for the call it wants to perform (In this case a Major function of IRP_MJ_PNP and a minor function of IRP_MN_QUERY_DEVICE_RELATIONS). It then calls the driver at the top of the Device Stack of the PDO that was sent, which could be an upper filter driver (which would just not implement that minor function and the function body would be code to pass it down – we will assume there isn't one for now). So, the top of the stack will be the FDO of the hub (which it reaches using IoGetAttachedDevice, which is the top of the stack). It calls it using IoCallDriver(*FDO, *IRP), a wrapper of IofCallDriver, which gets the next stack location by decrementing the CurrentStackLocation pointer, which causes it to point to the next stack location through the rules of C pointer arithmetic (which is the first stack location as the pointer was initialised one after it), and then it uses the major function number IRP_MJ_PNP indicated in the stack location to index into the MajorFunction array of the driver object of the FDO that was passed to IoCallDriver (Hub Driver) and calls the function at that position in the array.
The code for that call looks like this:
return FDO->DriverObject->MajorFunction[StackPtr->MajorFunction](FDO,
Irp);
You can see it passes the IRP. This allows the USB Hub Driver's function handler for IRP_MJ_PNP to check the minor function at the current stack location and then call the correct internal function. For each child device, the handler references the PDO in the DEVICE_RELATIONS structure, which is just an array of PDO pointers. It then sets Irp->IoStatus.Information to a pointer to the array and returns. The Plug and Play Manager then looks at the array of PDOs and compares the addresses to the address of the PDOs on the device tree that it has already enumerated. If there are new addresses it queries the Device and Instance IDs and the resource requirements by sending a bunch of IRPs like RP_MN_QUERY_ID, IRP_MN_QUERY_CAPABILITIES, IRP_MN_QUERY_DEVICE_TEXT, IRP_MN_QUERY_BUS_INFORMATION, IRP_MN_QUERY_RESOURCES and IRP_MN_QUERY_RESOURCE_REQUIREMENTS; and, if any of the PDOs have been marked inactive, it also sends an IRP_MN_SURPRISE_REMOVAL to those PDOs using the same IRP initialisation process as previously described (the FDOs of the devices will not implement the surprise removal function and pass it down to the Hub Driver) and the Hub Driver will disable the device and release hardware resources assigned to it.
To query the Device and Instance IDs, the PnP Manager sends a IRP_MN_QUERY_ID (one for Device ID and a separate one for Instance ID) to each PDO in the array whose pointer it received that is new (which will be the PDO for the new device that was created by the Root Hub Driver). In the IRP_MN_QUERY_ID, it initialises the Parameters.QueryId.IdType member of the stack location to BusQueryDeviceID. The hub driver receives the PDO queried by the PnP manager through the IRP_MJ_PNP handler it installed on DriverEntry, and in response to this Device ID query, the Hub Driver needs to query the device for the information required to build and concatenate the Device ID, but remember it already did this as soon as the PDO was created (it already got the device descriptor) so it can just use the DeviceExtension in which the information from the device descriptor was inserted -- the usDeviceId field of the device descriptor appears to be the Device ID. The Instance ID is a device identification string that distinguishes a device from other devices of the same type; it is either a number supplied by the bus usually unique to a slot/port (if UniqueID in DEVICE_CAPABILITIES structure acquired when the PnP manager queried the device capabilities is FALSE), otherwise it is an identifier unique to the device (supplied by the device) obtained from the iSerialNumber of the device descriptor. The InstanceID is queried by the PnP manager in a separate call after using Parameters.QueryId.IdType = BusQueryInstanceID in the IRP. The PnP manager then concatenates the device ID, say USB\VID_1C4F&PID_0002 to the instance ID 7&15cdfaa&0&3 to get the Device Instance ID (DIID) USB\VID_1C4F&PID_0002\7&15cdfaa&0&3. The obvious result is that there will be a single registry entry for each device with a serial number and multiple entries for a device without a serial number.
The PnP manager then uses the DIID to index into the registry at HKLM\SYSTEM\CurrentControlSet\Enum\Bus\DeviceID\InstanceID.
On my own system:
In it is a classguid value that leads to a class subkey under HKLM\SYSTEM\CurrentControlSet\Control\Class\<GUID>, which might be a keyboard class for instance. These values are filled in by driver .INF files.
The PnP manager checks the registry for the presence of a corresponding function driver, and when it doesn’t find one, it informs the user-mode PnP manager of the new device by its DIID. The user-mode PnP Manager first tries to perform an automatic install without user intervention. If the installation process involves the posting of dialog boxes that require user interaction and the currently logged-on user has administrator privileges, the user-mode PnP manager launches the Rundll32.exe application (the same application that hosts Control Panel utilities) to execute the Hardware Installation Wizard (%SystemRoot%\System32\Newdev.dll). If the currently logged-on user doesn’t have administrator privileges (or if no user is logged on) and the installation of the device requires user interaction, the user-mode PnP manager defers the installation until a privileged user logs on. The Hardware Installation Wizard uses Setupapi.dll and CfgMgr32.dll (configuration manager) API functions to locate INF files that correspond to drivers that are compatible with the detected device.
It selects the .INF file that most closely resembles by giving them a ranking by looking for Compatible IDs in an .INF file that hopefully match the Hardware / Compatible IDs generated from the DIID, which were inserted into the device extension. If it finds one then it installs the driver. Installation will happen with each new device that is plugged in and is essentially just the filling in of the registry with the correct information under that DIID.
Installation is performed in two steps. In the first, the third-party driver developer imports the driver package into the driver store, and in the second step, the system performs the actual installation, which is always done through the %SystemRoot%\System32\Drvinst.exe process.
Control passes back to the PnP Manager and it uses the registry keys to load drivers in the following order:
Any lower-level filter drivers specified in the LowerFilters value of the device’s enumeration key.
Any lower-level filter drivers specified in the LowerFilters value of the device’s class key.
The function driver specified by the Service value in the device’s enumeration key. This value is interpreted as the driver’s key under HKLM\SYSTEM\CurrentControlSet\Services.
Any upper-level filter drivers specified in the UpperFilters value of the device’s enumeration key.
Any upper-level filter drivers specified in the UpperFilters value of the device’s class key.
USB drivers will have an intermediate devnode – usbccgp if it is a composite device and usbstor if it is a Mass Storage Device, which can be seen here. When the Hub Driver sends the DIID, it is usbstor that gets loaded by the PnP Manager, as can be seen in the image above. (We need the intermediate USB storage node to translate generic disk.sys IRPs to URBs and handle USB specific drive configuration rather than cramming the functionality all in usbhub.sys).
It loads the drivers and calls the DriverEntry function of each and then the AddDevice routine if they aren't already running (for another USB device that uses the same driver); otherwise, it just calls the AddDevice routine. The AddDevice routine creates an FDO for the passed PDO. In its AddDevice routine, the Filter Driver creates a Filter Device Object (FiDO) and attaches it to the Device Stack (IoAttachDeviceToDeviceStack). Then, the PnP Manager creates a Device Node and associates it with the PDO. The PnP Manager already earlier acquired the Bus Device's (Hub Driver's) opinion on the resources of the device using IRP_MN_QUERY_RESOURCE_REQUIREMENTS at the same time as sending IRPs for the Device ID. The PnP Manager now sends a IRP_MN_FILTER_RESOURCE_REQUIREMENTS using IoCallDriver at the top of new the Device Node with the FDO specified. Only the FDO driver handles this and it will alter any requirements for the device object that it needs that the Hub Driver was unable to predict. A USB Mass Storage Device doesn't require an interrupt as it will just use the primary event ring. If it did, it would specify in the response to the IRP the number of MSI-x messages it requires), and once the PnP manager assigns resources to the device, it sends an IRP_MN_START_DEVICE IRP to the Device Stack. Although each USB device can have a separate interrupt and corresponding event ring, it doesn't really matter because it's always going to be the same lowest level driver that responds to interrupts: the xHCI driver; the USB devices do not have ISRs to register. Therefore all USB devices can share the single event ring and single interrupt.
In the usbstor routine that handles the Start Device IRP, the IRP gets passed down to the bus device (usbhub) after the routine sets an IoCompletionRoutine. The IoCompletionRoutine, when it is eventually called, will send a GET_CONFIGURATION URB which will passed down to its PDO (owned by usbhub) and usbhub will present the configuration that it cached in that PDO device extension earlier. Usbstor eventually decides on the configuration to use and sends a SET_CONFIGURATION URB. Usbhub will fill in an input context from the cached configuration with endpoints i.e. ISOCH IN, INTERRUPT IN and adds the input context pointer to the URB. Usbhub then adds more information like Slot ID and sends the URB to the xHCI Controller and it picks it up and inserts a TRB on the Default Endpoint 0 Transfer Ring for the device to which the Host Controller will populate the Output Device Context of the slot with the Input Device Context according to the Input Control Context and informs the device about the configuration selected.
Usbstor will then allocate a PDO and call IoInvalidateBusRelations. When it gets to it, usbstor will request the device information which was stored its PDO belonging to usbhub earlier and it will translate the DIID to a standard format for usbstor recognisable by disk.sys .inf drivers (format \Disk&Ven_xxx&Prod_xxx&Rev_x.xx) and append a USBSTOR\ prefix and this will allow the registry to load disk.sys and partmgr.sys as a filter stored in the disk.sys class subkey. Usbstor is now the bus device for the device.
Disk.sys FDO will check a driver maintained table to see how many disks (N) have been enumerated in the system and will name the FDO \Device\HarddiskN\DRN. Partmgr.sys creates a symbolic link \Device\HarddiskN\Partiton0 to \Device\HarddiskN\DRN. Partmgr will then call IoReadPartitionTableEx and create partition PDOs for each of the partitions, naming them \Device\HarddiskN\PartitonM and so on. For each partition, it sends an IOCTL_INTERNAL_VOLMGR_PARTITION_ARRIVED IRP to volmgr and provides the disk signature and offset to the partition. Volmgr creates a volume PDO for each volume and creates symlinks between \Device\HarddiskN\PartitonM and the volume PDO name \Device\HarddiskvolumeX X >= 1 it assigns to the volume PDO (actually because of this symbolic link, the partition PDO with that name can never be accessed, and the PDO itself lacks a devnode and is internally managed by partmgr) and sends MntMgr an IRP_MJ_DEVICE_CONTROL request, specifying IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION by calling IoBuildDeviceIoControlRequest for each volume PDO (Harddiskvolume1 is always the first volume on disk.sys's \Device\Harddisk0\DR0). The Mount Manager responds by querying volmgr for the volume’s nonpersistent device object name (by sending 3 IOCTLs (control IRPs)) located in the Device directory of the system object tree (for example: "\Device\HarddiskVolume1"), the Unique ID generated for the volume and a suggested persistent symbolic link name e.g. \DosDevices\D:.
The persistent drive letter and mount points are stored with identical data fields. The data of the values is called the unique ID which is provided by volmgr to mntmgr with IOCTL_MOUNTDEV_QUERY_UNIQUE_ID. The Unique ID for basic disks is the signature and offset to the partition. The mount manager uses the suggested name if the mount manager's database does not already contain a persistent drive letter name for the volume paired with the unique ID. Otherwise, it ignores the suggestion and uses the drive letter name in its persistent name database. It ties \DosDevices\D: to the unique ID and develops a GUID and ties the GUID in the form \??\Volume{GUID} to the unique ID in its namespace and then it creates object manager namespace symbolic links between them. Later mount points i.e. \DosDevices\D:\mymount will also be paired with the unique ID. In photo above, the MBR drive contains A: (system reserved) and C: partitions and it is clear they have the same MBR signature and a different offset. The D: drive is a GPT drive and has a GPT signature and offset. E: is a USB MBR drive with a USBSTOR specific signature and offset.
When a file on C: is opened for the first time, the file system will not be mounted, so when the file path string is parsed in IopParseDevice, which calls IopCheckVpbMounted on the volume (C: corresponds to device name \Device\HarddiskVolume2 due to the symbolic link created by the mount manager), it will call IopMountDevice because VPB->DeviceObject == NULL, which sends an IRP_MJ_FILE_SYSTEM_CONTROL/IRP_MN_MOUNT_VOLUME to each registered file system registered file system that registered itself using IoRegisterFileSystem. The called FSs handle IRP_MN_MOUNT_VOLUME and determine if their file system is on the media and, if it is, the file system creates the File System Volume Device Object (VDO) and puts it in the VPB. C: gives the device and \file gives the file object. \ is the root directory object. IoGetRelatedDeviceObject gets the top of the VDO stack from the file object (performs IoGetAttachedDevice on FileObject->Vpb->DeviceObject).
NTFS driver internal structures:
Windows USB Drive device tree
Overview of the MMIO space
How the xHCI controller is enumerated
At system boot, pci.sys will be loaded by apci.sys and one of things it does is call IoInvalidateDeviceRelations, which triggers the PnP manager to send an IRP_MN_QUERY_DEVICE_RELATIONS, to which it responds with a list of PDOs. It creates the list on the spot by using the MCFG ACPI table's Base address of enhanced configuration mechanism field to get the base of the PCIe configuration space (PCIEXBAR) and then iterating through it on 4096 byte boundaries, and for any Vendor/Device IDs it finds on the boundaries, it creates a PDO and pairs it with the configuration no. One of the devices will be the xHCI controller. It goes through exactly the same process as described above, eventually creating a DIID and checking the registry etc., which will cause the xHCI Driver to be loaded; and the PnP Manager also asks the bus for resources its child will require with a IRP_MN_QUERY_RESOURCE_REQUIREMENTS to the PDO half stack (which will also be handled by ACPI bus filter). After the xHCI Driver is loaded, it sends a IRP_MN_FILTER_RESOURCE_REQUIREMENTS to the xHCI Driver so that it can make modifications to the resource requirements. The Devnode of the xHCI controller PDO receives an IRP_MN_START_DEVICE, and the xHCI controller notices it is for its own PDO and sets an IoCompletionRoutine and passes it down to pci.sys, which will see that the passed PDO is a child, and it will allocate the MMIO physical range decided on by the PnP manager it received in the resource list in the start device IRP parameters into the BAR and also sets up an MSI-x interrupt decided upon in the IRP_MN_QUERY_RESOURCE_REQUIREMENTS and IRP_QUERY_FILTER_RESOURCE_REQUIREMENTS and calls IoCompleteRequest which calls the IoCompletionRoutine the xHCI Driver set, which will call MmMapIoSpace over the CmResourceTypeMemory descriptors in the CM_RESOURCE_LIST in Parameters.StartDevice.AllocatedResourcesTranslated. It will create the Event Ring, Command Ring and DBCA in the virtual address space received from MmMapIoSpace and set the registers in the MMIO space to point to them. It then associates the Event Ring the MSI-x vector received in IRP_MN_START_DEVICE. It will then set up the ISR using IoConnectInterrupt and register DPCs. I'm not sure how the USB Hub Driver is loaded, but potentially it is done in StartDevice of the xHCI driver; it could call IoInvalidateDeviceRelations and say it only has one child, the Hub. It provides a DIID affixed with IUSB3\.
PCI config space shows you PCI and PCI Express devices, not USB devices.
PCI config space will show you the Vendor and Device ID of the the USB controller, but not the attached devices. You would have to enumerate the USB bus by reading/writing USB registers for that.
Note that taking over the USB controller will break currently-running USB stack and kill your USB keyboard and boot devices.
If you are at the UEFI shell, perhaps you can find what you need in the output of devtree.
If you are writing your own UEFI DXE code, it would have to query the USB driver.
Despite the question already being answered and marked accepted, I would just like to wave a flag for using:
EFI_PCI_IO_PROTOCOL for PCI operations
EFI_USB_IO_PROTOCOL for interacting with USB devices regardless of what bus the host controller happens to be connected to.
This way your application ends up being portable between all compliant UEFI platforms.
User #fpmurphy who posts answers here occasionally has examples of both in their github area.

How does an O.S. or a high level abstraction layer gain knowledge of the hardware using the device driver?

When reading about hardware/device independence this statement from wikipedia (http://en.wikipedia.org/wiki/Device_independence#Desktop_computing) states that:
The application software does not need to know anything about the hardware on which it was to be used. Instead it discovers the capabilities of the hardware through the standardized abstraction layer, and then use abstracted commands to control the hardware.
I wanted to know about the lower level interaction between the BIOS routine/device driver/HAL/OS and the device controller about discovering the hardware capabilities.
Kindly help me out to understand the communication between these entities that takes place which helps in hardware independence.
Hardware devices, normally, connect to the main controller through a standard bus of some kind.
For example - PCI, PEX, USB.
Each connected device on the bus will be allocated with a device #, bus #, function #, etc, by the bus controller.
Modern bus contollers either provide the main controller with the ability to perform a scan, or send an event when a device is hot plugged into the bus.
Per each discovered device, it is possible, using the bus controller standard commands (such as read/write registers of a device, by device ID, bus number, etc), to interrogate the device for details such as:
Manufacturer ID
Device ID
Class (controller / networking device / Human interface / imaging device / and so on)
Per bus type, all these details must be available in the same way for every connected HW device, thus enabling the OS to use an abstraction layer.
Once a device has been discovered and identified, the OS will call all the specific bus registered device drivers' probe function, which use the details mentioned above to decide if can handle it.
When a device driver probe succeeds, an instance of the driver is allocated and can be used directly by the application that needs to access the HW.
For example:
USB PC CAM connects to USB port. An event is sent to the main CPU by the USB bus controller. The CPU will use the standard USB bus controller functions to learn the manufacturer & device ID/s, device class, functions, etc, and will call all the USB registered device drivers probe functions.
If an appropriate device driver is installed (registered), it will successfully create an instance of the device and a video application (such as skype) can use it directly, through DLLs supplied by the driver SW.
Hope this helps.

Writing a device driver for Platform Bus in Embedded Systems?

I have gone through some driver implementation in Linux Kernel Source and can see that these are the platform driver.
drivers/net/ethernet/smsc/smsc911x.c
static struct platform_driver smc911x_driver = {
.probe = smc911x_drv_probe,
.remove = smc911x_drv_remove,
.suspend = smc911x_drv_suspend,
.resume = smc911x_drv_resume,
.driver = {
.name = CARDNAME,
.owner = THIS_MODULE,
},
};
Above is a driver for platform device(smsc based Ethernet controller) and platform devices are devices which are not probed automatically during system boot-up unlike legacy devices sitting on the pci bus.
I guess this understanding of mine is OK here?
Now when I say it is the platform devices, is it mean these devices(Ethernet Controller) are sitting on Platform bus and on ARM architecture default platform bus is AMBA.
So when we solder the Ethernet controller on ARM based board it should be sit on or interfaced with AMBA bus?
How Do we decide that driver we're going to write is Platform driver or Normal driver?
From my limited experience in developing ARM platform drivers, AMBA devices typically have identification registers at the end of their memory mapped IO register interface.
Generally speaking, if you look at the reference manual for your ethernet controller and register summary specifies peripheral/component identification registers (usually at offsets 0xFE0-0xFEC and 0xFF0-0xFFC), you should write an AMBA bus driver. These drivers can be identified automatically by the bus driver.
Otherwise, if the register interface doesn't specify any ID registers at offsets 0xFE0-0xFEC and 0xFF0-0xFFC, you'll probably just want to write a platform driver. These devices cannot be automatically identified and you need to specifically attach a driver to the device.

Bluetooth low energy : Discovery modes and connection mode, Independent or dependent?

In GAP test spec (4.1.0) there is a test case (TP/DISC/NONM/BV-02-C [Non-discoverable Mode Undirected Connectable Mode]).
Basically i need to put IUT in non-discovarable mode and non-connectable mode.
Let's see what core4.1 spec has to say:
Non-Discovarable mode:
1)Shall not set LE GENERAL and LE LIMITED Flags in ADV data.
2)A Peripheral device in the non-connectable mode may send non-connectable
undirected advertising events or scannable undirected advertising events
or may not send advertising packets.
If the Peripheral device in the non-discoverable mode sends non-connectable
advertising events or scannable undirected advertising events then it is
recommended that the Host configures the Controller as follows:
• The Host should set the advertising filter policy to either ‘process scan and
connection requests only from devices in the White List’ or ‘process scan
and connection requests from all devices’.
Undirected-Connectable mode:
The Host shall configure the Controller to send undirected connectable advertising
events.
Type of advertisement is contradictory. So what should i do for this particular test case?
Just read a book on BLE. It seems Discovery mode has nothing to do with type of advertisement. Discovery mode only and only depends on the flag in advertisement data. Connection mode depends on type of advertisement.
I am not marking this as correct answer. Would like feedback from someone experienced in BLE dev/test.
Update:
Discoverable modes just define the flags in adv packet. They do not dictate any type of advertisement. Any type of advertisement which can carry advertisement data payload can be used in any discoverable mode.
Now when you advertise it has be in one of the connection mode. Connection mode defines type of advertisement and discover mode defines the flag in the advertisement data.
For example:
Peripheral = (No flag + connectable undirected mode ) and Central = (General or limited discovery procedure) then this device will not be seen by application on top of GAP central.