Разработка классов-дескрипторов на c++/cli
Содержание:
- Properties¶
- 1.2. Использование семантики стека
- 2. Управляемые шаблоны
- Просмотр таблицы дескрипторов с помощью отладчика ядра.
- Системные вызовы обработки файлов
- 16.3. Индексные дескрипторы файлов
- Stdin, stdout, and stderr
- Operations on file descriptors
- Просмотр открытых дескрипторов.
- Usage
- CDC + MSC Composite Device
- Descriptor Example¶
- Descriptor Protocol¶
- Definition and Introduction¶
- Немного теории
- Ошибка номер 1400
- HTML
- CDC — виртуальный COM порт
Properties¶
Calling is a succinct way of building a data descriptor that
triggers function calls upon access to an attribute. Its signature is:
property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
The documentation shows a typical use to define a managed attribute :
class C(object): def getx(self): return self.__x def setx(self, value): self.__x = value def delx(self): del self.__x x = property(getx, setx, delx, "I'm the 'x' property.")
To see how is implemented in terms of the descriptor protocol,
here is a pure Python equivalent:
class Property(object): "Emulate PyProperty_Type() in Objects/descrobject.c" def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel if doc is None and fget is not None doc = fget.__doc__ self.__doc__ = doc def __get__(self, obj, objtype=None): if obj is None return self if self.fget is None raise AttributeError("unreadable attribute") return self.fget(obj) def __set__(self, obj, value): if self.fset is None raise AttributeError("can't set attribute") self.fset(obj, value) def __delete__(self, obj): if self.fdel is None raise AttributeError("can't delete attribute") self.fdel(obj) def getter(self, fget): return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): return type(self)(self.fget, self.fset, fdel, self.__doc__)
The builtin helps whenever a user interface has granted
attribute access and then subsequent changes require the intervention of a
method.
For instance, a spreadsheet class may grant access to a cell value through
. Subsequent improvements to the program require the cell
to be recalculated on every access; however, the programmer does not want to
affect existing client code accessing the attribute directly. The solution is
to wrap access to the value attribute in a property data descriptor:
1.2. Использование семантики стека
Шаблон Basic Dispose также реализуется компилятором, если в классе имеется член освобождаемого типа и он объявлен с использованием семантики стека. Это означает, что для объявления используется имя типа без крышки (»), а инициализация происходит в списке инициализации конструктора, а не с помощью . Семантика стека описана в .
Приведем пример:
Компилятор в этом случае делает следующее:
- Для класса реализует интерфейс .
- В обеспечивает вызов для .
Финализация определяется соответствующей функциональностью класса . Как и в предыдущем случае, наследование от можно указать явно, а самостоятельно определить нельзя. Естественно, класс может иметь еще другие члены, объявленные с использованием семантики стека, и для них также обеспечивается вызов их .
2. Управляемые шаблоны
И наконец, еще одна замечательная особенность C++/CLI позволяет максимально упростить создание классов-дескрипторов. Речь идет об управляемых шаблонах (managed templates). Это не обобщения (generics), а настоящие шаблоны, как в классическом C++, но шаблоны не родных, а управляемых классов. Инстанцирование таких шаблонов приводит к созданию управляемых классов, которые можно использовать в качестве базовых классов или членов других классов внутри сборки. Управляемые шаблоны описаны в .
Просмотр таблицы дескрипторов с помощью отладчика ядра.
В команде !handle отладчика ядра используются три аргумента: !handle <индекс дескриптора> <флаги> <идентификатор процесса>
Индекс дескриптора идентифицирует запись дескриптора в таблице дескрипторов. (Нуль означает «показать все дескрипторы».) Индекс первого дексритора имеет значение 4, второго — 8 и так далее. Например, после ввода команды !handle 4 будет показан первый дескриптор для текущего процесса.
Флаги можно указать в виде поразрядной маски, где разряд 0 означает «показать только информацию в записи дескриптора», разряд 1 означает «показать свободные (то есть неиспользуемые) дескрипторы, а разряд 2 означает «показать информацию об объекте, на который ссылается дескриптор». Следующая команда приводит к показу всех подробностей о таблице дескрипторов для процесса с идентификатором 0x62C:
lkd> !handle 0 7 62c
processor number 0, process 000000000000062c
Searching for Process with Cid == 62c
PROCESS fffffa80052a7060
SessionId: 1 Cid: 062c Peb: 7fffffdb000 ParentCid: 0558
DirBase: 7e401000 ObjectTable: fffff8a00381fc80 HandleCount: 111.
Image: windbg.exe
Handle table at fffff8a0038fa000 with 113 Entries in use
0000: free handle, Entry address fffff8a0038fa000, Next Entry 00000000fffffffe
0004: Object: fffff8a005022b70 GrantedAccess: 00000003 Entry: fffff8a0038fa010
Object: fffff8a005022b70 Type: (fffffa8002778f30) Directory
ObjectHeader: fffff8a005022b40fffff8a005022b40 (new version)
HandleCount: 25 PointerCount: 63
Directory Object: fffff8a000004980 Name: KnownDlls
0008: Object: fffffa8005226070 GrantedAccess: 00100020 Entry: fffff8a0038fa020
Object: fffffa8005226070 Type: (fffffa80027b3080) File
ObjectHeader: fffffa8005226040fffffa8005226040 (new version)
HandleCount: 1 PointerCount: 1
Directory Object: 00000000 Name: \Program Files\Debugging Tools for Windows (x64)
{HarddiskVolume2}
Первым флагом является бит блокировки, показывающий, что запись в настоящее время используется. Вторым флагом является указатель на возможность наследования, то есть он показывает, получат ли процессы, созданный данным процессом, копию этого дескриптора в свои таблицы дескрипторов. Как уже отмечалось, наследование дескрипторов может быть указано при создании дескриптора или после него с помощью функции SetHandleInformation.
Третий флаг показывает, должно ли закрытие объекта генерировать контрольное сообщение (флаг не показывается в Windows, диспетчер объектов использует его для внутренних нужд). Бит защиты от закрытия хранится в неиспользованной части маски доступа и показывает, разрешено ли вызывающей программе закрыть этот дескриптор (флаг может быть установлен с помощью системного вызова NtSetInformationObject).
Системным компонентам и драйверам устройств зачастую нужно открывать дескрипторы объектов, к которым не должны иметь доступ приложения пользовательского режима. Это делается путем создания дескрипторов в таблице дескрипторов ядра (внутренняя ссылка на которую осуществляется по имени ObpKernelHandleTable).
Дескрипторы в этой таблице доступны только из режима ядра и в контексте любого процесса. Это означает, что функция режима ядра может сослаться на дескриптор в контексте любого процесса, не оказывая отрицательного влияния на производительность системы. Диспетчер объектов распознает ссылки на дескрипторы из таблицы дескрипторов ядра, когда установлен старший бит дескриптора, то есть когда ссылки на дескрипторы из таблицы дескрипторов ядра имеют значение, больше чем 0x80000000.
Таблица дескрипторов ядра также служит в качестве таблицы дескрипторов для процесса System, и все дескрипторы, созданные процессом System (например, кодом, запущенным в системных потоках), автоматически помечаются как дескрипторы ядра, поскольку они размещаются в таблице дескрипторов ядра по определению.
Системные вызовы обработки файлов
В следующей таблице кратко описаны системные вызовы, связанные с обработкой файлов:
%eax | Имя системного вызова | %ebx | %ecx | %edx |
2 | sys_fork | struct pt_regs | — | — |
3 | sys_read | unsigned int | char * | size_t |
4 | sys_write | unsigned int | const char * | size_t |
5 | sys_open | const char * | int | int |
6 | sys_close | unsigned int | — | — |
8 | sys_creat | const char * | int | — |
19 | sys_lseek | unsigned int | off_t | unsigned int |
Необходимые шаги для использования системных вызовов:
Поместите номер системного вызова в регистр EAX.
Сохраните аргументы системного вызова в регистрах EBX, ECX и т.д.
Вызовите соответствующее прерывание ().
Результат обычно возвращается в регистр EAX.
16.3. Индексные дескрипторы файлов
Каждому файлу на диске соответствует один и только
один индексный дескриптор файла, который идентифицируется своим
порядковым номером — индексом файла. Это означает, что
число файлов, которые могут быть созданы в файловой системе,
ограничено числом индексных дескрипторов, которое либо явно задается
при создании файловой системы, либо вычисляется исходя из физического
объема дискового раздела.
Строение индексного дескриптора файла приведено в
табл. 16.4.
Таблица
16.4. Структура индексного дескриптора
Название поля |
Тип |
Описание |
i_mode |
USHORT |
Тип |
i_uid |
USHORT |
Идентификатор |
i_size |
ULONG |
Размер |
i_atime |
ULONG |
Время |
i_ctime |
ULONG |
Время |
i_mtime |
ULONG |
Время |
i_dtime |
ULONG |
Время |
i_gid |
USHORT |
Идентификатор |
i_links_count |
USHORT |
Счетчик |
i_blocks |
ULONG |
Число |
i_flags |
ULONG |
Флаги |
i_reserved1 |
ULONG |
Зарезервировано |
i_block |
ULONG |
Указатели |
i_version |
ULONG |
Версия |
i_file_acl |
ULONG |
ACL |
i_dir_acl |
ULONG |
ACL |
i_faddr |
ULONG |
Адрес |
i_frag |
UCHAR |
Номер |
i_fsize |
UCHAR |
Размер |
i_pad1 |
USHORT |
Заполнение |
i_reserved2 |
ULONG |
Зарезервировано |
Поле типа и прав доступа к файлу представляет
собой двухбайтовое слово, каждый бит которого служит флагом,
индицирующим отношение файла к определенному типу или установку
одного конкретного права на файл.
Таблица
16.5. Структура поля, задающего тип и права доступа
Идентификатор |
Значение |
Назначение флага (поля) |
S_IFMT |
F000 |
Маска |
S_IFSOCK |
A000 |
Доменное |
S_IFLNK |
C000 |
Символическая |
S_IFREG |
8000 |
Обычный |
S_IFBLK |
6000 |
Блок-ориентированное |
S_IFDIR |
4000 |
Каталог |
S_IFCHR |
2000 |
Байт-ориентированное |
S_IFIFO |
1000 |
Именованный |
S_ISUID |
0800 |
SUID — |
S_ISGID |
0400 |
SGID — |
S_ISVTX |
0200 |
Бит |
S_IRWXU |
01C0 |
Маска |
S_IRUSR |
0100 |
Право |
S_IWUSR |
0080 |
Право |
S_IXUSR |
0040 |
Право |
S_IRWXG |
0038 |
Маска |
S_IRGRP |
0020 |
Право |
S_IWGRP |
0010 |
Право |
S_IXGRP |
0008 |
Право |
S_IRWXO |
0007 |
Маска |
S_IROTH |
0004 |
Право |
S_IWOTH |
0002 |
Право |
S_IXOTH |
0001 |
Право |
Среди индексных дескрипторов имеется несколько
дескрипторов, которые зарезервированы для специальных целей и играют
особую роль в файловой системе (табл. 16.6).
Таблица
16.6. Особые индексные дескрипторы
Идентификатор |
Значение |
Описание |
EXT2_BAD_INO |
1 |
Индексный |
EXT2_ROOT_INO |
2 |
Индексный |
EXT2_ACL_IDX_INO |
3 |
ACL |
EXT2_ACL_DATA_INO |
4 |
ACL |
EXT2_BOOT_LOADER_INO |
5 |
Индексный |
EXT2_UNDEL_DIR_INO |
6 |
Инлексный |
EXT2_FIRST_INO |
11 |
Первый |
Самый важный дескриптор в этом списке —
дескриптор корневого каталога. Этот дескриптор указывает на корневой
каталог, который, подобно всем каталогам, представляет собой
связанный список, состоящий из записей переменной длины. Каждая
запись имеет следующую структуру (табл. 16.7):
Таблица
16.7. Структура дескриптора, описывающего корневой каталог
Название поля |
Тип |
Описание |
Inode |
ULONG |
Номер |
Rec_len |
USHORT |
Длина |
Name_len |
USHORT |
Длина |
Name |
CHAR |
Имя |
Использование
записей переменной длины позволяет использовать длинные имена файлов
без пустой траты дискового пространства. Отдельная запись в каталоге
не может пересекать границу блока (т. е. должна быть расположена
целиком внутри одного блока). Поэтому, если очередная запись не
помещается целиком в данном блоке, она переносится в следующий блок,
а предыдущая запись продолжается таким образом, чтобы она заполнила
блок до конца.
Следующий раздел |
Stdin, stdout, and stderr
On a Unix-like operating system, the first three file descriptors, by default, are STDIN (standard input), STDOUT (standard output), and STDERR (standard error).
Name | File descriptor | Description | Abbreviation |
---|---|---|---|
Standard input | The default data stream for input, for example in a command pipeline. In the terminal, this defaults to keyboard input from the user. | stdin | |
Standard output | 1 | The default data stream for output, for example when a command prints text. In the terminal, this defaults to the user’s screen. | stdout |
Standard error | 2 | The default data stream for output that relates to an error occurring. In the terminal, this defaults to the user’s screen. | stderr |
Operations on file descriptors
The following lists typical operations on file descriptors on modern Unix-like systems. Most of these functions are declared in the header, but some are in the header instead.
Creating file descriptors
- open()
- creat()
- socket()
- accept()
- socketpair()
- pipe()
- epoll_create() (Linux)
- signalfd() (Linux)
- eventfd() (Linux)
- timerfd_create() (Linux)
- memfd_create() (Linux)
- userfaultfd() (Linux)
- fanotify_init() (Linux)
- inotify_init() (Linux)
- clone() (with flag CLONE_PIDFD, Linux)
- pidfd_open() (Linux)
- open_by_handle_at() (Linux)
Operations on a single file descriptor
- read(), write()
- readv(), writev()
- pread(), pwrite()
- recv(), send()
- recvfrom(), sendto()
- recvmsg(), sendmsg() (also used for sending FDs to other processes over a Unix domain socket)
- recvmmsg(), sendmmsg()
- lseek(), llseek()
- fstat()
- fstatvfs()
- fchmod()
- fchown()
- ftruncate()
- fsync()
- fdatasync()
- fdopendir()
- fgetxattr(), fsetxattr() (Linux)
- flistxatrr(), fremovexattr() (Linux)
- statx (Linux)
- setns (Linux)
- vmsplice() (Linux)
- pidfd_send_signal() (Linux)
- waitid() (with P_PIDFD ID type, Linux)
- fdopen() (stdio function:converts file descriptor to FILE*)
- dprintf() (stdio function: prints to file descriptor)
Operations on multiple file descriptors
- select(), pselect()
- poll(), ppoll()
- epoll_wait(), epoll_pwait() (Linux, takes a single epoll filedescriptor to wait on many other file descriptors)
- epoll_ctl() (for Linux)
- kqueue() (for BSD-based systems).
- sendfile()
- splice(), tee() (for Linux)
- copy_file_range() (for Linux)
Operations on the file descriptor table
The fcntl() function is used to perform various operations on a file descriptor, depending on the command argument passed to it. There are commands to get and set attributes associated with a file descriptor, including F_GETFD, F_SETFD, F_GETFL and F_SETFL.
- close()
- closefrom() (BSD and Solaris only; deletes all file descriptors greater than or equal to specified number)
- dup() (duplicates an existing file descriptor guaranteeing to be the lowest number available file descriptor)
- dup2(), dup3() (Close fd1 if necessary, and make file descriptor fd1 point to the open file of fd2)
- fcntl (F_DUPFD)
Operations that modify process state
- fchdir() (sets the process’s current working directory based on a directory file descriptor)
- mmap() (maps ranges of a file into the process’s address space)
Sockets
- connect()
- bind()
- listen()
- accept() (creates a new file descriptor for an incoming connection)
- getsockname()
- getpeername()
- getsockopt()
- setsockopt()
- shutdown() (shuts down one or both halves of a full duplex connection)
Miscellaneous
ioctl() (a large collection of miscellaneous operations on a single file descriptor, often associated with a device)
Просмотр открытых дескрипторов.
Запустите Process Explorer и убедитесь, что нижняя панель включена и настроена на показ открытых дескрипторов. (View (Вид) — Lower Pane View (Просмотр нижней панели) — Handles (Дескрипторы)). После этого откройте окно командной строки и просмотрите таблицу дескрипторов для нового процесса Cmd.exe. Вы должны увидеть дескриптор открытого файла для текущего каталога. Например, предположим, что текущим является каталог C:\Users\Administrator, тогда Process Explorer покажет следующее.
Теперь поставьте Process Explorer на паузу, нажав клавишу Пробел или щелкнув на пунктах View (Вид) — Update Speed (Изменить скорость) — Pause (Пауза).
Затем измените текущий каталог с помощью команды cd и нажмите клавишу F5, чтобы обновить отображаемую информацию. Вы увидите в Process Explorer, что дескриптор предыдущего текущего каталога закрыт и открыт новый дескриптор для нового текущего каталога. Предыдущий дескриптор выделен красным цветом, а новый дескриптор выделен зеленым цветом.
Свойство выделения разным цветом, имеющееся в Process Explorer, делает заметнее изменения в таблице дескрипторов. Например, если процесс допускает утечку дескрипторов, просмотр таблицы дескрипторов с помощью Process Explorer может быстро показать, какой дескриптор или какие дескрипторы были открыты, но не были закрыты. (Обычно виден длинный список дескрипторов для одного и того же объекта.) Эта информация поможет программисту обнаружить утечку дескрипторов.
Монитор ресурсов также показывает открытые (именованные) дескрипторы для процессов, выбранных путем установки флажков напротив их имен. Вот как выглядят дескрипторы открытого окна командной строки.
Таблицу открытых дескрипторов можно также вывести, используя средство командной строки Handle из серии программных продуктов Sysinternals.
Посмотрите, к примеру, на следующий, частично показанный вывод, полученный с помощью средства Handle при изучении дескрипторов файловых объектов, находящихся в таблице дескрипторов для процесса Cmd.exe до и после изменения каталога. По умолчанию Handle отфильтровывает нефайловые дескрипторы, пока не будет использован ключ –a, который приводит к выводу всех дескрипторов в процессе, аналогично Process Explorer.
C:\>handle -p cmd.exe
Handle v3.46
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals — www.sysinternals.com
———————————————————————-
cmd.exe pid: 5124 Alex-Laptop\Alex Ionescu
3C: File (R-D) C:\Windows\System32\en-US\KernelBase.dll.mui
44: File (RW-) C:\
C:\>cd windows
C:\Windows>handle -p cmd.exe
Handle v3.46
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals — www.sysinternals.com
———————————————————————-
cmd.exe pid: 5124 Alex-Laptop\Alex Ionescu
3C: File (R-D) C:\Windows\System32\en-US\KernelBase.dll.mui
40: File (RW-) C:\Windows
Дескриптор объекта является индексом в таблице дескрипторов, относящейся к конкретному процессу. Этот индекс указывается исполнительным блоком процесса (EPROCESS). Первый индекс дескриптора имеет значение 4, второй — 8 и т. д. Таблица дескрипторов процесса содержит указатели на все объекты, которые процесс открыл для своей работы.
Таблицы дескрипторов реализованы по древовидной схеме, подобной той, которую реализует блок управления памятью x86 для перевода виртуальных адресов в физические, которая дает максимальное значение, превышающее 16 000 000 дескрипторов на процесс.
При создании процесса выделяется только таблица дескрипторов самого низкого уровня, другие уровни создаются по мере необходимости. Таблица дескрипторов нижнего уровня состоит из такого количества записей, которое может поместиться на странице минус одна запись, которая используется для контроля дескрипторов.
Например, для систем x86 страница составляет 4096 байт и поделена на записи таблицы дескрипторов размером 8 байт, которых получается 512 минус 1, то есть всего 511 записей в таблице дескрипторов самого низкого уровня. Таблица дескрипторов среднего уровня содержит полную страницу указателей на таблицы нижнего уровня, поэтому количество таблиц дескрипторов нижнего уровня зависит от размера страницы и размера указателя для платформы. Схема таблицы дескрипторов в системе Windows показана на следующем рисунке.
Usage
const isDescriptor = require('is-descriptor'); isDescriptor({ value: 'foo' }) //=> true isDescriptor({ get: function() {}, set: function() {} }) //=> true isDescriptor({ get: 'foo', set: function() {} }) //=> false
You may also check for a descriptor by passing an object as the first argument and property name () as the second argument.
const obj = {}; obj.foo = null; Object.defineProperty(obj, 'bar', { value: 'xyz' }); Reflect.defineProperty(obj, 'baz', { value: 'xyz' }); isDescriptor(obj, 'foo'); //=> true isDescriptor(obj, 'bar'); //=> true isDescriptor(obj, 'baz'); //=> true
CDC + MSC Composite Device
А теперь со всей этой фигней мы попробуем взлететь (С) анекдототсюда
- У нас будет всего одна конфигурация, а в ней 3 интерфейса
- Один интерфейс реализует MSC
- CDC реализуется двумя интерфейсами.
- Первый для управления. У него одна однонаправленная конечная точка для управления интерфейсом
- Второй интерфейс CDC для данных. У него двунаправленная конечная точка — для передачи и приема
- Еще одна конечная точка нужна для управления устройство в целом (реализуется ядром USB библиотеки)
Красивая картинка, которая описывает пример описания композитного устройства. Взято из спецификации IADспецификации этого дескриптора
Любую архитектурную проблему можно решить введением дополнительного абстрактного слоя… (С) еще один анекдот
отсюдаТутfrondersвыделятьКартинка из Reference Manual микроконтроллеров серии STM32F103бубном fronders
Descriptor Example¶
The following code creates a class whose objects are data descriptors which
print a message for each get or set. Overriding is
alternate approach that could do this for every attribute. However, this
descriptor is useful for monitoring just a few chosen attributes:
class RevealAccess(object): """A data descriptor that sets and returns values normally and prints a message logging their access. """ def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): print('Retrieving', self.name) return self.val def __set__(self, obj, val): print('Updating', self.name) self.val = val >>> class MyClass(object): ... x = RevealAccess(10, 'var "x"') ... y = 5 ... >>> m = MyClass() >>> m.x Retrieving var "x" 10 >>> m.x = 20 Updating var "x" >>> m.x Retrieving var "x" 20 >>> m.y 5
Descriptor Protocol¶
That is all there is to it. Define any of these methods and an object is
considered a descriptor and can override default behavior upon being looked up
as an attribute.
If an object defines or , it is considered
a data descriptor. Descriptors that only define are called
non-data descriptors (they are typically used for methods but other uses are
possible).
Data and non-data descriptors differ in how overrides are calculated with
respect to entries in an instance’s dictionary. If an instance’s dictionary
has an entry with the same name as a data descriptor, the data descriptor
takes precedence. If an instance’s dictionary has an entry with the same
name as a non-data descriptor, the dictionary entry takes precedence.
Definition and Introduction¶
In general, a descriptor is an object attribute with “binding behavior”, one
whose attribute access has been overridden by methods in the descriptor
protocol. Those methods are , , and
. If any of those methods are defined for an object, it is
said to be a descriptor.
The default behavior for attribute access is to get, set, or delete the
attribute from an object’s dictionary. For instance, has a lookup chain
starting with , then , and
continuing through the base classes of excluding metaclasses. If the
looked-up value is an object defining one of the descriptor methods, then Python
may override the default behavior and invoke the descriptor method instead.
Where this occurs in the precedence chain depends on which descriptor methods
were defined.
Немного теории
Usb in a nutshellпереводUSB Made Simpleспецификации для конкретных классов USB устройств
- Дескриптор устройства (Device Descriptor) — описывает устройство в целом, его название, производитель, серийный номер. Строковые данные описываются отдельными строковыми дескрипторами (String Descriptor)
- Дескриптор конфигурации (Configuration Descriptor) — устройство может иметь одну или несколько конфигураций. Каждая конфигурация определяет скорость общения с устройством, набор интерфейсов и параметры питания. Так, например, ноутбук, который работает от батареи, может попросить устройство (выбрать конфигурацию) использовать более низкую скорость обмена и переключиться на собственный источник питания (вместо ноутбучной батареи). Разумеется это работает только если устройство предоставляет такую конфигурацию.
- Дескриптор интерфейса (Interface descriptor) — описывает интерфейс общения с устройством. Интерфейсов может быть несколько. Например разные функции (MSC, CDC, HID) будут реализовывать свои интерфейсы. Некоторые функции (например CDC или DFU) реализуют сразу несколько интерфейсов для своей работы. В нашем случае композитного устройства нам потребуется реализовать сразу несколько интерфейсов от разных функций и заставить их ужиться друг с другом.
- Дескриптор конечной точки (Endpoint descriptor) — описывает канал связи в рамках конкретного интерфейса, задает размер пакета, описывает параметры прерываний. Используя конечные точки мы будем получать и принимать данные.
- Есть еще куча разных дескрипторов, которые описывают отдельные аспекты конкретных интерфейсов
Ошибка номер 1400
Данная проблема часто встречается в семействе операционных систем Windows. При её появлении вместе с ней может быть и краткое описание — недопустимый дескриптор окна. А может встречаться и такая формулировка — «Error_invalid_window_handle» или 0х578.
Как бороться и что значит неверный дескриптор? В зависимости от контекста объекта, нужно принимать разные меры.
В любом случае стоит сразу же пройтись по стандартным мерам, принимаемым ко всем проблемам с системой:
- перезагрузка компьютера;
- запуск и сканирование ошибки с помощью утилиты проверки целостности системных файлов;
- проверить компьютер на вирусы.
Если базовые шаги не помогли, то, возможно, стоит удалить и снова установить приложение, которое вызывает сбой.
Ещё один надёжный способ быстро восстановить работоспособность системы — сделать её откат с помощью стандартных инструментов. После использования «Восстановления системы», она вернётся к последней действующей резервной копии, при условии что она была ранее создана.
HTML
В языке разметки гипертекста, который применяется для создания статичных веб-страниц, дескрипторы — это не что иное, как тэги. В этой среде они выполняют роль меток, благодаря которым текст, размещённый между ними, отображается определенным способом. Таким образом и формируется дизайн и расположение элементов на странице.
Как правило, специфика языка определяет наличие открывающего дескриптора и закрывающего. Все, что между ними, подвержено изменению.
Дескриптор может иметь атрибуты, то есть определённые свойства. Их синтаксис выглядит так: имя атрибута = его значение.
Подчиняясь общей логике, дескрипторы в HTML представляют собой идентификационные метки объектов. С их помощью можно управлять внешним видом, взаимодействовать со страницей и присваивать ей особые значения.
CDC — виртуальный COM порт
переехал на STM32 CubeCube для контроллеров серии STM32F1графического конфигуратора CubeMXusb in a nutshellна русском
bDeviceClass, bDeviceSubClass и bDeviceProtocol — описывают хосту что же это у нас за устройство такое, что оно умеет и какие драйвера нужно грузить. В данном случае тут сказано, что устройство у нас реализует Communication Device Class, а значит хосту нужно сделать виртуальный COM порт и связать его с этим устройством.
PID (Product ID) и VID (Vendor ID) — по этим полям хост различает разные устройства, подключенные к системе. Устройства при этом реализовывать одинаковый класс
Говорят для устройств продаваемых на рынке очень важно иметь уникальные VID/PID, но я не узнавал кто и где выдает эти ID-шники. Для домашнего устройства в единственном экземпляре достаточно значений по умолчанию.
- wTotalLength — размер всего пакета дескрипторов для этой конфигурации — чтобы хост знал где заканчивается эта конфигурация и начинается следующая. Нам его нужно будет поправить, когда мы будем делать композитное устройство. Напомню, что все интерфейсы для этой конфигурации должны располагаться сплошным блоком, а значение wTotalLength определяет длину этого блока.
- bNumInterfaces: класс Communication Device реализуется с помощью двух интерфейсов. Один для управления, другой для собственно пересылаемых данных.
- bmAttributes и MaxPower указывает, что наше устройство имеет собственный источник питания, но при этом хочет потреблять до 100 мА от USB порта. С этими параметрами явно придется поиграться в будущем.
- Class Driver (в случае CDC это файлы usbd_cdc и usbd_cdc_if): реализуют логику конкретного класса устройств — CDC для виртуального COM порта, MSC для устройств хранения данных, HID для клавиатур/мышек и всяких специфических устройств с пользовательским интерфейсом.
- USB Core (usbd_core.c, usbd_ctlreq.c, usbd_ioreq.c): реализует общую логику работы всех классов USB устройств, умеет отдавать хосту запрашиваемые дескрипторы, обрабатывает запросы от хоста и настраивает USB устройство в целом. Также перенаправляет потоки данных из уровня драйвера класса в нижележащие уровни и наоборот.
- USB HW Driver (usbd_conf.c): Вышележащие слои платформенно независимые и работают одинаковым образом для нескольких серий микроконтроллеров. В коде нет низкоуровневых вызовов функций конкретного микроконтроллера. Файл usbd_conf.c реализует прослойку между USB Core и HAL — библиотеке низкоуровневых драйверов для выбранного микроконтроллера. В основном тут живут простые врапперы, которые перенаправляют вызовы сверху вниз и коллбеки снизу вверх.
- HAL (stm32f1xx_hal_pcd.c, stm32f1xx_ll_usb.c): занимаются общением с железом микроконтроллера, оперирует регистрами и отвечает на прерывания.
тут Virtual COM Port драйвер от STM