Программирование usb для windows

Программирование USB*устройств

Как уже упоминалось, операционные системы Windows обеспечивают программную под держку функционирования устройств, подключенных к шине USB. Обработку потоков данных устройств USB на уровне операционной системы выполняет стек стандартных драйверов, которые выполняют основные функции по управлению всеми устройствами USB и обмену данными между ними и системой.

Если вам необходимо написать программное обеспечение для какого либо устройства USB, которое расширяло бы его возможности по обработке данных, то можно избрать один из трех возможных путей:

написать собственный драйвер устройства, который бы обеспечивал все необходи мые функции управления и обмена данными, и программу, которая бы взаимодей ствовала с этим драйвером в пользовательском режиме. При этом можно полностью обойтись без стандартных драйверов системы;

написать драйвер фильтра, который бы обеспечивал требуемую функциональность, но располагался бы в стеке драйверов над системными драйверами. Таким образом, все стандартные функции обработки выполняли бы драйверы USB, установленные системой, а дополнительные функции обеспечивались бы вашим драйвером фильтра, с которым и взаимодействовала бы программа пользователя;

воспользоваться свободно распространяемыми библиотеками функций и драйвера

ми для доступа к USB устройству.

В большинстве случаев программный доступ к устройству USB может потребоваться, если данное устройство выполняет какую то очень специфичную функцию. Например, на базе USB разработаны «электронные осциллографы» или системы сбора данных, для работы с которы ми необходимо иметь доступ к самому устройству. В большинстве таких случаев можно вос пользоваться свободно распространяемыми библиотеками функций, которые будут работать практически во всех популярных средах программирования. Например, под эгидой GNU раз работано программное обеспечение, известное под названием LibUsb, включающее необхо димые драйверы и библиотеки функций для работы в операционных системах Windows и Linux. Эти библиотеки функций очень популярны и позволяют быстро разрабатывать про граммы, взаимодействующие с вашим устройством посредством набора стандартных функ ций. Это исключает необходимость написания собственного драйвера устройства, что суще ственно экономит время.

Кроме того, большинство пользователей не знакомо с методикой разработки драйверов,

а это очень сложная область программирования, поэтому наличие такого свободно распрос траняемого программного обеспечения окажет неоценимую помощь широкому кругу пользо вателей. На основе проекта LibUsb разработаны оболочки (wrappers) для работы с Visual Basic .NET и C# .NET, наиболее популярной из которых является LibUsbDotNet, также разра ботанная под эгидой свободно распространяемого программного обеспечения. Несмотря на кажущуюся сложность программирования USB устройств, перечисленное программное обес печение настолько упрощает эту задачу, что она становится под силу даже новичкам. Рас смотрим на практических примерах, как работать с вашими USB устройствами, и начнем с пакета программ LibUsb. Кстати, вышеперечисленное программное обеспечение можно бес платно загрузить с сайта www.sourceforge.net или из многочисленных дублирующих сайтов.

Как работать с библиотеками USB функций LibUsb? Библиотека построена таким обра

зом, чтобы можно было выполнять основные операции, связанные с USB устройством:

идентификацию или, по–другому, перечисление (enumeration). При выполнении этой операции происходит обнаружение устройств, подключенных к шине USB, что выпол няется с помощью соответствующих функций библиотеки libusb;

получение параметров устройства (идентификаторов устройства, данных о произво дителе и характеристиках устройства), для чего в библиотеке имеется целый ряд функций;

открытие, закрытие, чтение и запись данных, посылка команд. К устройству USB, так же, как и к другим объектам файловой системы, можно обращаться по записи чте нию, что и выполняется с помощью соответствующих функций библиотеки.

Все перечисленные возможности могут быть реализованы посредством вызова соответ ствующих функций библиотеки libusb, но здесь они не будут перечисляться, поскольку это заняло бы слишком много места; мы посмотрим, как использовать некоторые из этих функ

Расположение драйвера libusb0.sys в стеке драйверов устройства

ций на практических примерах. Описание всех функций читатели смогут найти в со ответствующей документации. Напомню, что мы рассматриваем применение функций биб лиотеки libusb в операционных системах Windows.

При инсталляции дистрибутива с libusb в операционной системе Windows в системе инсталлируется драйвер фильтра libusb0.sys. Этот драйвер будет находиться в вершине сте ка драйверов системы, что легко увидеть, на пример, посмотрев сведения о драйверах для любого USB устройства (рис. 6.10).

Кроме того, для обращения к драйверу из программ пользователя в систему инсталли руется библиотека libusb0.dll, используя кото рую можно разрабатывать пользовательские программы.

Вид окна приложения при удалении

USB устройства из системы

Источник: Магда Ю. С. Компьютер в домашней лаборатории. – М.: ДМК Пресс, 2008. – 200 с.: ил.

Visual C++: Работа с USB устройствами

Статья посвящена основам создания приложений в среде windows, взаимодействующих с usb устройствами. Кратко рассматривается архитектура usb шины, программная модель и отрывки кода реализации драйвера usb устройства, доступ к драйверу из приложения.

Краткий обзор шины

usb — universal serial bus — универсальная последовательная шина. Поскольку шина последовательная, то в каждый момент времени передается только один поток данных. Это следует держать в голове, поскольку когда мы будем рассматривать программную модель, может возникнуть ощущение, что данные передаются параллельно. Шина usb чем-то похожа на ethernet — данные передаются пакетами. Каждый пакет имеет заголовок отвечающий за транспортировку и маршрутизацию данных. Но есть и существенно отличие: в usb полоса (bandwidth) (этот термин попросту говоря можно понимать как «общая пропускная способность») шины делится между устройствами не по принципу «кто первый занял» (random access). В usb ресурсы распределяются централизовано — концентратором (hub) шины. У шины может быть только один корневой концентратор (root hub), управляющий работой всей шины (он то как раз и распределяет ресурсы шины между устройствами). Поэтому нельзя соединить два компьютер напрямую проводом (наподобие нуль-модема) через usb — получится конфигурация с двумя корневыми концентраторами. К корневому концентратору подключаются устройства — всего до 127. Устройство может быть в свою очередь концентратором, контролирующим нижележащий сегмент шины. Таким образом, usb шина может выглядеть как многоранговая звезда (дерево).

Спецификация usb 2.0 предусматривает три возможные скорости передачи:

high speed — 480Мб/c

full speed — 12Мб/c

low speed — 1.5Мб/c

Следует помнить, что максимальную скорость должен поддерживать корневой концентратор. К 12Мб/c концентратору можно подключить низкоскоростное устройство и нельзя — высокоскоростное (т.е. можно, но оно будет работать на скорости 12Мб/с, а не 480). Следует заметить, что современные чипсеты персональных компьютеров зачастую обеспечиваю скорость только 12Мб/c (они хотя и позиционируются как usb 2.0 совместимые, но по сути являются usb1.1), поэтому не стоит полагать, что usb обеспечит во всех случаях жизни высокоскоростной обмен.

Читайте также:  Windows 10 версия 2004 сборка 19041


Различают 4 различных типа передачи по usb:

Управляющий — используется для конфигурации устройства на шине, также может использоваться для специфичных для устройства целей;

Пакетный — передача больших пакетов информации, допускающая большие задержки в доставке данных;

«По прерыванию» — надежная, кратковременная передача (например, кода клавиши от клавиатуры);

Изохронный — для передачи данных в режиме реального времени ( с минимальной задержкой).

Каждое устройство предоставляет одну (или несколько) т.н. функцию. Некоторые устройства могут содержать также один (или несколько) концентратор, к которому подключаются подчиненные устройства.

Каждая функция может иметь несколько точек подключения ( end point). Каждая точка отвечает за определенный тип передачи данных. Каждое устройство имеет управляющую точку подключения (control end points).

2. Программная модель

Рассматривая программную модель usb для windows прежде всего стоит отметить следующие особенности:

1)Каждое usb устройство должно обслуживаться собственным драйвером. В отличие от, скажем, устройств, подключаемых к lpt, для которых наличие собственного драйвера в общем необязательно. На шину usb нельзя просто передать сырой поток данных.

2)Все usb контроллеры соответствуют спецификации acpi, т.е поддерживают функции pnp и управления питанием. Поэтому работать с устройством можно только если оно подключено к шине (как это ни удивительно j ).

3)Драйвера в windows образуют т.н стек, по которому и передаются данные вверх и вниз, не следует пытаться работать напрямую с портами контроллера.

Держа в голове эти три пункта (чего не нужно делать j ), посмотрим как же работать с этим хозяйством.

Для начала рассмотрим аппаратную реализацию на примере популярного (в недавнем прошлом) чипсета i815. Обслуживанием всех устройств ввода/вывода в этом чипсете занимается специализированный контроллер — ich (i/o controller hub) — 82801ba. Чтобы перечислить все его функции не хватит листа. Нас будет интересовать тот факт, что в состав этой микросхемы входит в том числе два независимых usb контроллера, каждый из которых имеет по два порта. Контроллеры поддерживают скорость передачи 12Мб/c (т.е usb 1.1). Каждый контроллер имеет в диапазоне ввода/вывода набор портов, через которые ими можно управлять.

Тут вспоминаем п.3 и отказываемся от мысли управлять usb контроллерами напрямую. Производитель контроллера постарался и написал для нас драйвер. Те, кто имел опыт взаимодействия с драйверами из режима приложения, возможно, уже сокрушается: «Что толку от этого драйвера! Нужно знать еще его интерфейс!». Абсолютно верно, но дело в том, что нам его интерфейс не понадобиться. В операционной системе есть уже универсальный драйвер для корневых концентраторов. Это соответствует концепции модели драйверов: «Логическое устройство — Физическое устройство». В данном случае драйвер корневого концентратора (usbhub.sys) создает логическое устройство позволяющие для вышележащих драйверов абстрагироваться от подробностей работы драйвера usb контроллера. Нетерпеливый читатель опять воскликнет: «И что толку!». Для организации интерфейса с клиентом (драйвером) существует третий драйвер — драйвер шины (usbd.sys). Его интерфейс документирован в ddk. Это набор управляющих кодов для диспетчера (просто процедура, обрабатывающая запросы) запросов типа irp_mj_internal_device_control. С помощью этих запросов можно получить разнообразную информацию о состоянии шины, количестве портов, их статусе и пр. Все эти коды имеют символические имена вида: ioctl_internal_usb_ХХХХ. Особое внимание следует обратить на запрос ioctl_internal_usb_submit_urb. Управление устройством, запись и чтение данных осуществляется именно через этот запрос. Из этого следует, чтобы передать данные на шину, нельзя вызвать writefile для драйвера шины или корневого концентратора — он может вообще не иметь диспетчера для irp_mj_write (. С другой стороны обратиться к диспетчеру irp_mj_internal_device_control из пользовательского приложения нельзя. Посему вспоминаем п.1 — каждое устройство обязано иметь свой драйвер, который разрешает эту дилемму — он имеет диспетчер для irp_mj_write (который вызывается диспетчером ввода-вывода когда приложение вызывает writefile). Все тоже самое относится к чтению. Для некоторых классов устройств существую стандартные драйвера: для hid устройств (устройства интерфейса — мыши, клавиатуры и.т.п.), usb audio — устройства воспроизведения звука (ЦАП располагается прямо в локальной аудиоустановке, а по шине передается цифровой поток). Для устройств, принадлежащих к этим классам, не нужно драйверов.

Поговорим немного о модели ввода/вывода. Для разработчиков пользовательских программ это, возможно, абсолютно незнакомая тема. С аппаратной точки зрения вывод/вывод бывает программный — использование инструкций in, out, mov а также (наиболее интересный случай!) использование строковых операций с префиксом повтора (rep movsb) или с использованием контроллера прямого доступа к памяти (dma). Однако, в данном случае речь идет о другом. Аппаратным вводом/выводом занимается как раз драйвер usb контроллера. А нас интересует, куда попадают принятые данные (и куда помещаются данные, предназначенные для передачи)? Существует два подхода: «буферизованный» и «прямой» ввод/вывод.

При буферизованном выводе происходит следующее:

1)Пользовательское приложение вызывает функцию writefile, передав указатель на буфер содержащий данные;

2)ОС вызывает диспетчер irp_mj_write драйвера и передает туда (через структуру irp) указатель на буфер данных

3)Драйвер копирует данные в свой внутренний буфер. После этого, возможно, сообщает, что данные переданы или откладывает это сообщение до момента актуальной передачи.

4)Актуальная передача осуществляется когда-то позже.

При буферизованном вводе все аналогично. Главное достоинство этого метода — принятые данные не могут «пропасть» (если только внутренний буфер не переполниться). Буферизованный ввод/вывод осуществляет, например, драйвер последовательного порта. Для медленных устройств ввода/вывода это рекомендованный способ.

Для быстрых устройств, особенно передающих данные большими пакетами, использование программных буферов имеет два основных недостатка — большие накладные расходы на копирование данных в(из) промежуточный буфер, нерационально используется системная память. Поэтому в данном случае используется прямой ввод/вывод — данные принимаются непосредственно в буфер (или передаются из этого буфера), зарезервированный пользовательской программой. Производительность такого метода заметно выше. Однако возникает некоторые сложности с приемом данных — у драйвера всегда должен быть «под рукой» буфер. И это должен обеспечить клиент (пользовательская программа или вышележащий драйвер).

3. Основы программирования драйвера usb устройства

Коль мы установили, что любое usb устройство (кроме hid и usb audio) должно иметь собственный драйвер, то рассмотрим далее как устроен такой драйвер. Любой драйвер, удовлетворяющий модели драйверов nt или wdm, начинается стандартной точки входа — driverentry. В этой процедуре должны быть заданы диспетчеры — процедуры, по средством которых драйвер взаимодействует с системой. Рассмотрим следующий листинг. Здесь и далее опущен вывод отладочной информации, проверка возвращаемых значений на ошибки, обработка исключений, короче все то, что делает код надежным, но ухудшает его читабельность, в коде реального драйвера, естественно, следует это все предусмотреть. Более того, опущены некоторые детали, которые не являются специфичными для usb драйверов. Тем не менее без них драйвер просто не будут работать. Разработчикам драйверов советую обратиться к примерам из ddk или driverstudio. Приведенный же код рассчитан в первую очередь на разработчиков приложений — чтоб знали что по чем. j

Читайте также:  Отменить печать mac os

ntstatus driverentry(in pdriver_object driverobject, in punicode_string registrypath )
<
driverobject->majorfunction[irp_mj_create] = usbdrv_create;
driverobject->majorfunction[irp_mj_close] = usbdrv_close;
driverobject->majorfunction[irp_mj_device_control] = usbdrv _processioctl;
driverobject->majorfunction[irp_mj_write] = usbdrv_write;
driverobject->majorfunction[irp_mj_read] = usbdrv_read;
driverobject->majorfunction[irp_mj_system_control] = usbdrv _processsyscontrolirp;
driverobject->majorfunction[irp_mj_pnp] = usbdrv_processpnpirp;
driverobject->majorfunction[irp_mj_power] = usbdrv_processpowerirp;

driverobject->driverextension->adddevice = usbdrv_adddevice;
driverobject->driverunload = usbdrv_unload;

usbdrv_create — извещает драйвер, что на нем был открыт файл, т.е была вызвана функция ntcreatefile (в пользовательском режиме приложения обычно вызывают эту функцию, экспортируемую ntdll.dll через вызов createfile из kernel32.dll; дайвера режима ядра вызывают zwcreatefile, экспортируемую ядром — ntoskrn.exe). Отметим следующее:

1. с помощью этого диспетчера можно и отказать в открытии файла — достаточно вернуть что-то отличное от status_success;

2. в диспетчер через имя файла может быть передана дополнительная текстовая информация — она просто «прицепляется» к имени сзади.

usbdrv_close — извещает драйвер о закрытии файла. Вызывается в ответ на вызов closehandle из пользовательского приложения.

usbdrv_processioctl — передает драйверу управляющий код. Пользовательское приложение для передачи такого кода использует deviceiocontrol

usbdrv_write — передает драйверу буфер для записи в устройство. Вызывается менеджером ввода/вывода в ответ на вызов writefile(ex).

usbdrv_read — передает драйверу буфер для чтения.

Вышеперечисленные диспетчеры предназначены для общения драйвера со своим клиентом. Следующие три диспетчера используются системой:

usbdrv _processsyscontrolirp — обработчик системных запросов. Как правило, драйвер передает их просто нижележащему драйверу.

usbdrv_processpnpirp — обработчик pnp запросов. usb устройство удовлетворяет спецификации acpi и его драйвер должен иметь этот диспетчер.

usbdrv_processpowerirp — обработчик запросов системы управления питанием.

usbdrv_adddevice — драйвер любого pnp устройства обязан иметь эту функцию. Она вызывается при обнаружении нового устройства, обслуживаемого драйвером.

usbdrv_unload — вызывается при выгрузке драйвера из памяти

Далее мы не будем рассматривать полностью код, рассмотрим только важные моменты, которые должны быть интересны также и прикладным программистам. Будем рассматривать код в «событийном» порядке.

После того, как система обнаруживает устройство, обслуживаемое нашим драйвером, она вызывает функцию adddevice. Для сопоставления драйвера обнаруженному устройству используется реестр. Те, кто интересуется подробностями этого процесса, может заглянуть в реестр hklmsystemcurrentcontrolsetusb. По pid (product id) и vid (v end or id) устанавливается имя сервиса. Если сервис еще не загружен — он загружается в память и вызывается driverentry. Затем вызывается та самая функция adddevice. Если драйвер не загружен в память он для начала туда загружается. Если же не найденa соответствующая запись в реестре — появляется знакомая надпись об обнаружении нового устройства.

Рассмотрим, что делает функция adddevice:

ntstatus usbdrv_pnpadddevice(in pdriver_object driverobject, in pdevice_object physicaldeviceobject )
<
ntstatus ntstatus = status_success;
pdevice_object deviceobject = null;
pdevice_extension deviceextension;
unicode_string devicelink;
ulong i;

ntstatus = ioregisterdeviceinterface(physicaldeviceobject, (lpguid)&guid_class_usb_drv, null, &devicelink);

ntstatus = iosetdeviceinterfacestate(&devicelink, true);
ntstatus = iocreatedevice (driverobject,
sizeof (device_extension),
null,
file_device_unknown,
file_autogenerated_device_name, false,
&deviceobject);
deviceextension = (deviceobject)->deviceextension;

rtlcopymemory(deviceextension ->devicelinknamebuffer, devicelink.buffer, devicelink.length);

deviceextension ->pdo = physicaldeviceobject;

deviceobject ->flags /= do_direct_io;

Первым делом, нужно зарегистрировать интерфейс устройства с помощью ioregisterdeviceinterface. Если устройство поддерживает несколько интерфейсов (например, контроллер может обслуживать мышь и клавиатуру) — это функция может вызываться несколько раз. Далее создается объект — устройство device_object. Если у вас установлен softice, можете попробовать набрать в режиме отладки device и найти свое устройство. После того, как интерфейс будет разрешен ( iosetdeviceinterfacestate ), автоматически будет создана соответствующая символическая ссылка в разделе dosdevices, с помощью которой пользовательские приложения смогут получить доступ к устройству. Созданный объект устройство следует подключить (ioattachdevicetodevicestack) к стеку устройств. После этого наш драйвер в контексте созданного устройства будет обрабатывать запросы pnp и системы управления питанием. Кроме того, наш драйвер сможет отправлять irp запросы вниз по стеку, к которому он приаттачился. Одним из первых будет получен pnp запрос irp_mn_start_device, с помощью которого драйвер получит всю необходимую информацию об устройстве. Приведем код соответствующего обработчика:

ntstatus usbdrv_onstartdevice(in pdevice_object deviceobject )
<
ntstatus ntstatus = status_success;
pdevice_extension pdevext = null;
purb purb = null;
pusb_device_de script or pdevdesc = null;
pusb_configuration_de script or pconfigdesc = null;

Первым делом, получим всякую полезную информацию об устройстве, которое мы собираемся обслуживать. Сразу заметим, что всю работу за нас будут делать нижележащие драйвера (usbd.sys). Нам остается только грамотно формировать запросы urb — usb request block. Для этого:

Для начала получаем т.н deviceextension — область памяти, выделенной при вызове iocreatedevice, которую мы можем использовать по собственному усмотрению — т.н контекст устройства:

Формируем urb запрос:

purb = exallocatepool(nonpagedpool, sizeof(struct _urb_control_de script or_request) ) ;

pdevdesc = exallocatepool(nonpagedpool, sizeof(usb_device_de script or) );

usbbuildgetde script orrequest(purb,(ushort)sizeof(struct _urb_control_de script or_request),
usb_device_de script or_type, 0, 0, pdevdesc, null, sizeof(usb_device_de script or), null);

Любое общение между драйверами происходит с помощью irp. В нашем случае мы через irp передаем наш urb запрос:

pirp = iobuilddeviceiocontrolrequest(ioctl_internal_usb_submit_urb,
pdevext ->topstackdevice, null, 0, null, 0, true, &event, &iostatus);

nextstack = iogetnextirpstacklocation(pirp);
nextstack->parameters.others.argument1 = urb;

И вот кульминация — передаем запрос нижележащему драйверу:
iocalldriver(pdevext ->topstackdevice, pirp);
При нормальном стечении обстоятельсив мы получим следующую информацию об устройстве:

kdprint((«usblink device de script or:n»));

kdprint((«blength %dn», pdevdesc->blength));

kdprint((«bde script ortype 0x%xn», pdevdesc->bde script ortype));

kdprint((«bcdusb 0x%xn», pdevdesc->bcdusb));

kdprint((«bdeviceclass 0x%xn», pdevdesc->bdeviceclass));

kdprint((«bdevicesubclass 0x%xn», pdevdesc->bdevicesubclass));

kdprint((«bdeviceprotocol 0x%xn», pdevdesc->bdeviceprotocol));

kdprint((«bmaxpacketsize0 0x%xn», pdevdesc->bmaxpacketsize0));

kdprint((«idv end or 0x%xn», pdevdesc->idv end or));

kdprint((«idproduct 0x%xn», pdevdesc->idproduct));

kdprint((«bcddevice 0x%xn», pdevdesc->bcddevice));

kdprint((«imanufacturer 0x%xn», pdevdesc->imanufacturer));

kdprint((«iproduct 0x%xn», pdevdesc->iproduct));

kdprint((«iserialnumber 0x%xn», pdevdesc->iserialnumber));

kdprint((«bnumconfigurations 0x%xn», pdevdesc->bnumconfigurations));

Запомним эту информацию в контексте устройства

pdevext ->usbdevicede script or = pdevdesc;

Теперь используем полученную информацию для инициализации устройства:
Построим еще один urb запрос. В данном случае мы хотим получить т.н. конфигурационный дескриптор. Но дело в том, что мы не знаем его длину и не можем выделить гарантированно достаточный буфер памяти. Поэтому мы для начала формируем запрос с недостаточным размером буфера — нам в ответ вернут необходимую длину буфера:

Читайте также:  Roverbook neo установка windows

pconfigdesc = exallocatepool(nonpagedpool, sizeof(usb_configuration_de script or) );

usbbuildgetde script orrequest(purb,

(ushort) sizeof (struct _urb_control_de script or_request), usb_configuration_de script or_type, 0, 0, pconfigdesc, null, sizeof(usb_configuration_de script or), null);

pdevext ->topstackdevice, null, 0, null, 0, true, &event, &iostatus);

iocalldriver(pdevext ->topstackdevice, pirp);

А вот теперь попробуем получить актуальную конфигурационную информацию:

pdevext ->usbconfigde script or = exallocatepool(nonpagedpool, pconfigdesc ->wtotallength);

usbbuildgetde script orrequest(purb, (ushort) sizeof (struct _urb_control_de script or_request),
usb_configuration_de script or_type, 0, 0, pdevext ->usbconfigde script or, null,
pconfigdesc ->wtotallength, null);

iocalldriver(pdevext ->topstackdevice, pirp);

kdprint((«usblink configuration de script or:n»));

kdprint((«bde script ortype 0x%xn», pdevext ->usbconfigde script or->bde script ortype));

kdprint((«wtotallength 0x%xn», pdevext ->usbconfigde script or-> wtotallength));

kdprint((«bnuminterfaces 0x%xn», pdevext ->usbconfigde script or->bnuminterfaces));

kdprint((«iconfiguration 0x%xn», pdevext ->usbconfigde script or->iconfiguration));

kdprint((«bmattributes 0x%xn», pdevext ->usbconfigde script or->bmattributes));

kdprint((«maxpower 0x%xn», pdevext ->usbconfigde script or->maxpower));
exfreepool(pconfigdesc);
exfreepool(purb);

Если читатель утомился рассмотрением кода — могу только посочувствовать — далее начинается самое главное.

Получив конфигурационный описатель (дескриптор) получим список интерфейсов, предоставляемых устройством. Этим занимается usbd.sys. А мы будем использовать функции, экспортируемые этим драйвером (драйвера, как любые pe файлы могут экспортировать функции, чего тут удивительного? j ).

pusbd_interface_list_entry pinterfaceslist;
pusb_interface_de script or pcurrentde script or;
ulong i;

pinterfaceslist = exallocatepool(nonpagedpool, sizeof(usbd_interface_list_entry)*
(pdevext ->usbconfigde script or -> bnuminterfaces + 1) );

for (i = 0; i usbconfigde script or ->bnuminterfaces; i++ )
<
pcurrentde script or = usbd_parseconfigurationde script orex(pdevext->usbconfigde script or,
pdevext->usbconfigde script or, i, 0, -1, -1, -1 );

pinterfaceslist[i].interfacede script or = pcurrentde script or;

pinterfaceslist[i].interfacede script or = null;
pinterfaceslist[i].interface = null;
purb = usbd_createconfigurationrequestex(pdevext ->usbconfigde script or, pinterfaceslist);

for (i = 0; i numberofpipes; ++i)
pinterfaceslist[0].interface -> pipes[i].maximumtransfersize = max_transfer_size;

usbbuild select configurationrequest(purb, sizeof(struct _urb_ select _configuration),
pdevext->usbconfigde script or);

pirp = iobuilddeviceiocontrolrequest(ioctl_internal_usb_submit_urb,
pdevext ->topstackdevice, null, 0, null, 0, true, &event, &iostatus);

nextstack = iogetnextirpstacklocation(pirp);
nextstack->parameters.others.argument1 = urb;

iocalldriver(pdevext ->topstackdevice, pirp);

Я честно говоря утомился комментировать каждую строчку кода, так что займитесь этим сами j. Вместо этого я лучше обращу ваше внимание на следующий факт: получив конфигурационный дескриптор мы знали число интерфейсов, предоставленных нам устройством (bnuminterfaces). А выделили памяти на один элемент списка больше. Зачем? Дело в том, что в функции нет параметра задающего длину списка usbd_createconfigurationrequestex. Вместо этого длина списка определяется на манер работы с нуль-терминированными строками — последним стоит элемент, у которого pinterfaceslist[i].interfacede script or = null;

После обработки запроса, сформированного с помощью usbd_createconfigurationrequestex для каждого элемента списка интерфейсов мы получим кое-что интересное: pinterfaceslist[num].interface ->pipes — список пайпов данного интерфейса (массив структур usbd_pipe_in form ation). Для нас в этой структуре самым важным будет поле usbd_pipe_handle pipehandle — когда мы захотим что-то заслать в пайпу, нужно будет создать соответствующий urb запрос urb_bulk_or_interrupt_transfer и указать этот самый дескриптор пайпа. Как раз и рассмотрим, как что-либо записать в пайп. Запись производится либо по инициативе драйвера, либо по инициативе клиента драйвера — при обработке запроса irp_mj_write.

ntstatus ntstatus = status_success;

pio_stack_location pirpstack, pnextstack;

ulong length = 0;

purb purb = null, pprevurb = null, pfirsturb = null;

pdevext = deviceobject -> deviceextension;

pirpstack = iogetcurrentirpstacklocation (irp);

if (irp->mdladdress) length = mmgetmdlbytecount(irp->mdladdress);

rtlzeromemory(purb, sizeof(struct _urb_bulk_or_interrupt_transfer));

purb ->urbbulkorinterrupttransfer.hdr.length = (ushort) sizeof(struct _urb_bulk_or_interrupt_transfer);

purb ->urbbulkorinterrupttransfer.hdr. function = urb_function_bulk_or_interrupt_transfer;

purb ->urbbulkorinterrupttransfer.pipehandle = pdevext ->writepipehandle;

purb ->urbbulkorinterrupttransfer.transferflags = usbd_transfer_direction_in ;

purb ->urbbulkorinterrupttransfer.transferflags /= usbd_short_transfer_ok;

purb ->urbbulkorinterrupttransfer.urblink = null;

purb ->urbbulkorinterrupttransfer.transferbuffermdl = irp->mdladdress;

pnextstack->major function = irp_mj_internal_device_control;

iocalldriver(pdevext ->topstackdevice, irp );

Если вы ознакомились внимательно с кодом, приведенным в этом разделе, то листинг функции, производящей запись в устройство не должен быть совсем уж непонятным. Обратим внимание на детали:

mdladdress — это указатель на структуру mdl (memory de script ion list), описывающую буфер, переданный через irp c кодом irp_mj_write. Как уже упоминалось выше, такое поведение характерно для драйверов с прямой моделью ввода-вывода. Повторюсь, но тем не менее замечу: при чтении данных это приводит к необходимости упреждающего вызова readfile — в противном случае, данные не будут буферизированы и просто будут утеряны.

На этом мы закончим с рассмотрением внутренностей usb драйвера. В следующем разделе рассмотрим взаимодействие пользовательского приложения с драйвером.

4. Взаимодействие пользовательского приложения с драйвером usb устройства

Пользовательское приложение взаимодействует с устройством опосредованно — через драйвер этого устройства. Драйвер, как мы помним из предыдущего раздела, регистрирует и разрешает интерфейс, после чего система сама создает соответствующее символическое имя, через которое можно обратиться к устройству, как к файлу. Перво-наперво, это символическое имя надо выяснить. Для этого нам понадобиться библиотека, доступная для приложений — setupapi.dll. Описания функций, экспортируемых этой библиотекой, есть в ddk (но нет в sdk!). Рассмотрим код, позволяющий получить доступ к драйверу устройства как к файлу. Сначала нужно получить описатель класса устройства:

Мы попросили вернуть нам описатель для устройств, предоставляющих интерфейс с guid = guid_class_usb_drv и присутствующих в данный момент в системе.

Далее получаем краткую информацию для интерфейсов (в данном случае, для первого интерфейса в списке с подходящим guid):

devinfodata ->cbsize = sizeof(sp_device_interface_data);

setupdienumdeviceinterfaces(hdevinfo, null, (guid*)&guid_class_usb_link, 0,

После того, можно попытаться узнать символическое имя для устройства с заданным интерфейсом. Но для этого сначала определим необходимую для хранения этого имени длину буфера:

setupdigetinterfacedevicedetail (hdevinfo, devinfodata, null, 0, &requiredlength, null);

А теперь можно наконец узнать имя, присвоенное устройству:

devinfodetail ->cbsize = sizeof(sp_device_interface_detail_data);

setupdigetinterfacedevicedetail (hdevinfo, devinfodata, devinfodetail,

После этого, открываем устройство как файл:

husbdevice = createfile ( devinfodetail->devicepath, generic_read / generic_write, file_share_read / file_share_write, null, open_existing, 0, null);

После этого, с устройством можно работать как с обычным файлом: используя функции readfile(ex), writefile(ex), deviceiocontrol.

Обратите внимание! Библиотека setupapi.dll требует, чтобы структуры, передаваемые в нее, имели однобайтовое вырвнивание. Проверьте опции компилятора (по умолчанию, скорее всего, выравнивание будет другим).

Ссылки по теме

Помощь
Задать вопрос
программы
обучение
экзамены
компьютеры
ICQ-консультанты
Skype-консультанты
Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

Оцените статью