При написании USB драйвера возникла проблема разбиения оного большого пакета на мелкие - по азмеру буфера девайса. Мелкомягкие в своем ddk\src\wdm\usb\bulkusb делают это следуюшим образом: послыют пакет максимального размера и ставят процедуру завершения. Внутри которой проверяю - а не последний ли это пакет. Если не последний, то из CompletionRoutine выполняют IoCallDriver к нижележфщему драйверу со следующими данными для девайса. При максимально разрешенном размере пакета таких итераций может быть 200 (у MS). Реализовал подобное, вознкла следующая проблема - при более 100 операций винда отправляется в ребут даже не посинев. Логи показали, что процедуры получают управление в следующем порядке start WriteDispatch before IoCallDriver in WriteDispatch IoCallDriver in IoCompletion 1 in IoCompletion 2 ... end of IoCompletion2 end of IoCompletion1 after IoCallDriver in WriteDispatch end of Write Dispatch Получается, что WriteDispatch возвращает STATUS_PENDING _после_ того, как IRP был завершен в процедуре iocompletion. Можно конечно реализовать по другому, но хотелось бы услышать мнение опытных людей
Из статьи Four-F Жизненный цикл IRP Правило: Завершать IRP с кодом STATUS_PENDING нельзя. ну а сама IoCallDriver работает примерно так: Код (Text): /** * @return status from driver's dispatch routine. */ NTSTATUS call_driver( DEVICE_OBJECT * device, ///< Pointer to device object to which the IRP should be passed. IRP * irp ///< Pointer to IRP for request. ) { irp->CurrentLocation--; if( irp->CurrentLocation <= 0 ) KeBugCheckEx(NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) irp, 0, 0, 0); IO_STACK_LOCATION * stack = IoGetNextIrpStackLocation(irp); irp->Tail.Overlay.CurrentStackLocation = stack; stack->DeviceObject = device; return device->DriverObject->MajorFunction[stack->MajorFunction](device, irp); }
Упс, забыл указать, что я создаю второй irp packet, который и отправляется гулять вниз по стеку, а iomarkirppending и статус status_pending возвращается для исходного -> так что завершается он _один_ раз, так как не попадает к нижележащим драйверам. Собственно, проблема состоит в том, что при вызове iocalldriver процедура обработки ниднего драйвера выполняется _до_ возвращения управления. Я не знаю, может это правильно, но как тогда должен работать пример мелкомягких?
Если нижестоящий драйвер синхронно завершает IRP, то теоретически порядок вызова процедур будет именно таким, как ты привел. Смотри: в WriteDispatch ты вызываешь IoCallDriver, нижестоящий драйвер заполняет буфер и завершает IRP и мы окажемся в IoCompletion 1. Она опять шлет IRP (не возвращая управления), нижестоящий драйвер заполняет буфер и опять завершает IRP, и мы окажемся в IoCompletion 2. И так до IoCompletion X, которая наконец вернет управление и всё отмотается назад и мы выйдем из самого первого IoCallDriver, вызванного в WriteDispatch. Если же нижестоящий драйвер ставит IRP в очередь, то порядок будет другим. Как должен работать пример мелкомягких я не знаю. Но похоже в твоём случае происходит банальное переполнение стека, ведь IoCompletion фактически косвенно вызывает сама себя рекурсивно. Ядреный стек маленький (~12Кб), так что очень похоже на то. Воткни IoGetRemainingStackSize перед вызовом IoCallDriver в процедуре завершения и рез-т выводи в лог. Возможно это прояснит ситуацию.
2 compnet: А Вы нас в заблуждение не вводите? Специально посмотрел пример bulkusb ( в свое время я на основе его написал свой работающий драйвер ). Там не так, как вы описываете. Есть процедура пакетного чтения/записи. Она вызвается, если клиент пожелал иницировать операцию ввода/вывода, больше, чем устройство может возратить за один раз. Дабы не заниматься буферизацией и при этом прочесть максимально возможно кол-во данных. Реализовано так: в процедуре в цикле содается N IRP, которые засылаются вниз (в этом же цикле). После чего поток переводится в ожидание события окончания всей транзакции. В процедур завершения никаких IoCallDriver нет - соответсвенно нет переполнения стека. При обработке полседнего IRP выставялется событие, разблокирующее поток. Так по крайней мере в DDK 2000
Фууу, разобрался - надо было просто выспаться. Большое спасибо всем, принявшим участие в обсуждении. To Four-F: Ты совершенно прав, именно это и проиcходило... To TarasCo: Честно говоря, не знаю, как реализовано в DDK2k, у меня xp, но там процедура read/write dispatch реализована именно таким образом. Если интересно, такой же кусок есть в драйвех cypress semiconductor, описан в книге Уолтера Они и т.д. Если кому инетересно, могу сказать в чем было дело: при создании irp'а вместо irp_mj_internal_device_control написал irp_mj_device_control и обнаружил это только вчера. Схема, предложенная МС, работает, если дайвер делает отложенную обработку (респект то Four-F), если же он все выполняет в dispatch_control и возващает success, то происходит банальное переполнение стека.