Статья посвящена основам создания приложений в среде windows, взаимодействующих с usb устройствами. Кратко рассматривается архитектура usb шины, программная модель и отрывки кода реализации драйвера usb устройства, доступ к драйверу из приложения.
- Краткий обзор шины
- Спецификация usb 2.0 предусматривает три возможные скорости передачи:
- Различают 4 различных типа передачи по usb:
- 2. Программная модель
- При буферизованном выводе происходит следующее:
- 3. Основы программирования драйвера usb устройства
- Рассмотрим, что делает функция adddevice:
- Ссылки по теме
- 🔥 Видео
Краткий обзор шины
usb — universal serial bus — универсальная последовательная шина. Поскольку шина последовательная, то в каждый момент времени передается только один поток данных. Это следует держать в голове, поскольку когда мы будем рассматривать программную модель, может возникнуть ощущение, что данные передаются параллельно. Шина usb чем-то похожа на ethernet — данные передаются пакетами. Каждый пакет имеет заголовок отвечающий за транспортировку и маршрутизацию данных. Но есть и существенно отличие: в usb полоса (bandwidth) (этот термин попросту говоря можно понимать как «общая пропускная способность») шины делится между устройствами не по принципу «кто первый занял» (random access). В usb ресурсы распределяются централизовано — концентратором (hub) шины. У шины может быть только один корневой концентратор (root hub), управляющий работой всей шины (он то как раз и распределяет ресурсы шины между устройствами). Поэтому нельзя соединить два компьютер напрямую проводом (наподобие нуль-модема) через usb — получится конфигурация с двумя корневыми концентраторами. К корневому концентратору подключаются устройства — всего до 127. Устройство может быть в свою очередь концентратором, контролирующим нижележащий сегмент шины. Таким образом, usb шина может выглядеть как многоранговая звезда (дерево).
Спецификация usb 2.0 предусматривает три возможные скорости передачи:
Следует помнить, что максимальную скорость должен поддерживать корневой концентратор. К 12Мб/c концентратору можно подключить низкоскоростное устройство и нельзя — высокоскоростное (т.е. можно, но оно будет работать на скорости 12Мб/с, а не 480). Следует заметить, что современные чипсеты персональных компьютеров зачастую обеспечиваю скорость только 12Мб/c (они хотя и позиционируются как usb 2.0 совместимые, но по сути являются usb1.1), поэтому не стоит полагать, что usb обеспечит во всех случаях жизни высокоскоростной обмен.
Различают 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 контроллера. А нас интересует, куда попадают принятые данные (и куда помещаются данные, предназначенные для передачи)? Существует два подхода: «буферизованный» и «прямой» ввод/вывод.
При буферизованном выводе происходит следующее:
Видео:MCP2515, контроллер CAN шины с интерфейсом SPIСкачать
1)Пользовательское приложение вызывает функцию writefile, передав указатель на буфер содержащий данные;
2)ОС вызывает диспетчер irp_mj_write драйвера и передает туда (через структуру irp) указатель на буфер данных
3)Драйвер копирует данные в свой внутренний буфер. После этого, возможно, сообщает, что данные переданы или откладывает это сообщение до момента актуальной передачи.
4)Актуальная передача осуществляется когда-то позже.
При буферизованном вводе все аналогично. Главное достоинство этого метода — принятые данные не могут «пропасть» (если только внутренний буфер не переполниться). Буферизованный ввод/вывод осуществляет, например, драйвер последовательного порта. Для медленных устройств ввода/вывода это рекомендованный способ.
Для быстрых устройств, особенно передающих данные большими пакетами, использование программных буферов имеет два основных недостатка — большие накладные расходы на копирование данных в(из) промежуточный буфер, нерационально используется системная память. Поэтому в данном случае используется прямой ввод/вывод — данные принимаются непосредственно в буфер (или передаются из этого буфера), зарезервированный пользовательской программой. Производительность такого метода заметно выше. Однако возникает некоторые сложности с приемом данных — у драйвера всегда должен быть «под рукой» буфер. И это должен обеспечить клиент (пользовательская программа или вышележащий драйвер).
3. Основы программирования драйвера usb устройства
Коль мы установили, что любое usb устройство (кроме hid и usb audio) должно иметь собственный драйвер, то рассмотрим далее как устроен такой драйвер. Любой драйвер, удовлетворяющий модели драйверов nt или wdm, начинается стандартной точки входа — driverentry. В этой процедуре должны быть заданы диспетчеры — процедуры, по средством которых драйвер взаимодействует с системой. Рассмотрим следующий листинг. Здесь и далее опущен вывод отладочной информации, проверка возвращаемых значений на ошибки, обработка исключений, короче все то, что делает код надежным, но ухудшает его читабельность, в коде реального драйвера, естественно, следует это все предусмотреть. Более того, опущены некоторые детали, которые не являются специфичными для usb драйверов. Тем не менее без них драйвер просто не будут работать. Разработчикам драйверов советую обратиться к примерам из ddk или driverstudio. Приведенный же код рассчитан в первую очередь на разработчиков приложений — чтоб знали что по чем. j
ntstatus driverentry(in pdriver_object driverobject, in punicode_string registrypath )
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 — вызывается при выгрузке драйвера из памяти
Видео:Лекция 310. Шина USB - функциональная схемаСкачать
Далее мы не будем рассматривать полностью код, рассмотрим только важные моменты, которые должны быть интересны также и прикладным программистам. Будем рассматривать код в «событийном» порядке.
После того, как система обнаруживает устройство, обслуживаемое нашим драйвером, она вызывает функцию 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 )
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 )
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));
Видео:Лекция19 USBСкачать
kdprint((«iserialnumber 0x%xn», pdevdesc->iserialnumber));
kdprint((«bnumconfigurations 0x%xn», pdevdesc->bnumconfigurations));
Запомним эту информацию в контексте устройства
pdevext ->usbdevicede script or = pdevdesc;
Теперь используем полученную информацию для инициализации устройства:
Построим еще один urb запрос. В данном случае мы хотим получить т.н. конфигурационный дескриптор. Но дело в том, что мы не знаем его длину и не можем выделить гарантированно достаточный буфер памяти. Поэтому мы для начала формируем запрос с недостаточным размером буфера — нам в ответ вернут необходимую длину буфера:
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 ).
Читайте также: Датчики шин для hyundai creta
Видео:Шина данных i2c - декодируем/синхронизируем с помощью осциллографа Lecroy!Скачать
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++ )
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;
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;
Видео:Как работает USB? Просто, доступно, с примерами.Скачать
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 требует, чтобы структуры, передаваемые в нее, имели однобайтовое вырвнивание. Проверьте опции компилятора (по умолчанию, скорее всего, выравнивание будет другим).
Ссылки по теме
Помощь |
На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.
Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.
- Свежие записи
- Нужно ли менять пружины при замене амортизаторов
- Скрипят амортизаторы на машине что делать
- Из чего состоит стойка амортизатора передняя
- Чем стянуть пружину амортизатора без стяжек
- Для чего нужны амортизаторы в автомобиле
Видео:CAN шина👏 Как это работаетСкачать
- Правообладателям
- Политика конфиденциальности
Автоподбор © 2023
Информация, опубликованная на сайте, носит исключительно ознакомительный характер🔥 Видео
Передача данных - шина SPIСкачать
лекция 313. Формирование пакетов на шине USBСкачать
лекция 314. Транзакции на шине USBСкачать
#387 САМОДЕЛКА Перехват протокола RS-232 а также RS-485, RS-422, USB, UsartСкачать
💬 Как передавать и принимать данные из Arduino в компьютер и обратно? Очень просто!Скачать
Универсальный CAN адаптер MFD207CAN-UN (часть 2)Скачать
Цифровые интерфейсы и протоколыСкачать
Виды USB флешек и скорость передачи данных.Скачать
Arduino CAN Monitor (простейший монитор шины CAN)Скачать
IT 81. Шины PCIe, USB 1-3. 239 стр.Скачать
Интерфейс USB. Виртуальный COM порт и схемотехника.Скачать
лекция 417 Чтение и запись данных на общую шинуСкачать
От RS232 до USBСкачать
Лекция-практикум "Аппаратный USB STM32"Скачать