Доступ к устройствам модели архитектуры SCSI
Для устройств, поддерживаемых драйверами семьи Model архитектуры SCSI, семья Model архитектуры SCSI обеспечивает два типа интерфейса устройства, один для эксклюзивного доступа к устройству и один для неэксклюзивного доступа к осваивающим носители устройствам только. Эта глава фокусируется о том, как использовать эти интерфейсы устройства для передачи с устройствами, поддерживаемыми драйверами семьи Model архитектуры SCSI, и включает пример кода, иллюстрирующий, как получить доступ к диску CD-R/W.
Если необходимо получить доступ к устройству Параллели SCSI, и приложение должно работать в версиях OS X до v10.2, видеть Доступ Устройства Параллели SCSI для получения информации о том, как использовать API и семьи Model архитектуры SCSI и осуждаемой семьи SCSI для нахождения устройства. (Обратите внимание на то, что APIs осуждаемой семьи SCSI не будет работать в основанном на Intel Macintosh.)
Несмотря на то, что пример кода в этом документе был скомпилирован и протестирован, это не предназначается для удовлетворения потребностей коммерческого применения. Например, обработка ошибок минимальна и просто упрощает отладку этого кода — необходимо разработать собственные методы для обнаружения и ошибок из-за неправильного обращения. Кроме того, необходимо использовать собственный метод дискового арбитража. Поэтому Apple не рекомендует непосредственно включить весь пример программы в коммерческое применение.
Поддержка устройства семьи модели архитектуры SCSI
Семья Model архитектуры SCSI поддерживает периферийные устройства, соответствующие и SCSI Основная спецификация Команд или SPC, и одному из следующих протоколов автобусного транспорта:
ATA/ATAPI-5 спецификация (http://t13 .org)
Спецификация FireWire SBP-2 (http://t10 .org)
Спецификация Класса Массового хранения USB (http://www .usb.org)
Спецификация Параллели SCSI (SPI 4)
Семья Model архитектуры SCSI обеспечивает драйверы логической единицы в ядре, переводящие универсальные запросы I/O в специфичные для устройства команды для устройств на этих шинах, объявляющих один из следующих четырех типов периферийного устройства:
00$ для устройств блочной системы хранения, соответствующих спецификации команд блока SCSI
05$ для мультимедийных устройств, соответствующих спецификации команд мультимедиа SCSI
07$ для магнитооптических устройств, соответствующих спецификации команд блока SCSI
0$ E для уменьшенных блочных устройств команды, соответствующих SCSI, сократили блочную спецификацию команд
Для устройств, объявляющих другие типы периферийного устройства, такие как сканеры, лентопротяжные устройства и средние преобразователи, семья Model архитектуры SCSI обеспечивает интерфейс устройства, названный SCSITaskDeviceInterface, позволяющим приложению быть драйвером логической единицы для того устройства.
Семья Model архитектуры SCSI также обеспечивает доступ к авторским устройствам, поддерживающимся драйверами логической единицы в ядре. Интерфейс устройства для мультимедийных команд, названных MMCDeviceInterface, позволяет приложению получать неэксклюзивный доступ к авторскому устройству. Это позволяет приложению получать информацию об устройстве до получения эксклюзивного доступа к нему с SCSITaskDeviceInterface.
Семья Model архитектуры SCSI обеспечивает третий интерфейс, названный SCSITaskInterface, позволяющим приложению, действующему как драйвер логической единицы управлять объектом SCSITask в ядре. Объект SCSITask содержит команду, отправляемую в устройство, вместе с информацией, такой как состояние задачи и указатели функции обратного вызова.
Отправка SCSI или команд ATA к устройству хранения
Проектом OS X не позволяет приложениям отправлять команды SCSI или ATA в устройства хранения, если разработчик приложений также не обеспечивает драйвер устройства в ядре, поддерживающий команды. Семья Model архитектуры SCSI позволяет только одному драйверу логической единицы управлять устройством за один раз и обеспечивает драйверы логической единицы в ядре для устройств хранения (как перечислено в Поддержке Устройства семьи Модели архитектуры SCSI). Точно так же семья ATA не позволяет приложениям отправлять команды ATA непосредственно в ATA или SATA (Serial ATA) устройства. Этот проект сохраняет целостность операционной системы и улучшает безопасность данных пользователя двумя способами:
Произвольное приложение не может непосредственно изменить состояние устройства без явного сотрудничества пользовательского драйвера логической единицы разработчика в ядре.
Приложение не может обойти полномочия файловой системы, установленные пользователем путем отправки команд I/O непосредственно в устройство хранения.
И семья Model архитектуры SCSI и семья ATA обеспечивают интерфейсы устройства, позволяющие различные типы приложений доступа к устройствам. Остающиеся разделы в этой главе описывают интерфейсы устройства, которые семья Model архитектуры SCSI обеспечивает, чтобы позволить приложению отправлять команды в определенные типы устройства. Семья ATA обеспечивает интерфейс устройства, позволяющий приложениям получать SMART (самоконтроль, анализ и технология создания отчетов) данные от ATA и устройств SATA, реализующих набор функций SMART. Для получения дополнительной информации об этом интерфейсе устройства см. справочную документацию API для ATASMARTLib.h
в Ссылке Платформы Набора I/O.
Если для Вашего приложения нужна информация, доступная от SCSI INQUIRY
или ATA IDENTIFY DEVICE
команда, однако, Вы не должны отправлять команды в устройство или использовать интерфейс устройства. Большая часть информации, предоставленной INQUIRY
и IDENTIFY DEVICE
команды доступны в Реестре I/O, который можно просмотреть с приложением Проводника Реестра I/O (в /Developer/Applications)
или инструмент командной строки ioreg
.
Если Ваше приложение должно выполнить операции I/O блочного уровня на устройстве хранения, можно получить доступ к узлу устройства неструктурированного диска BSD в /dev
. Для получения дополнительной информации о том, как сделать это, посмотрите Руководство по Доступу Файла устройств для Устройств хранения.
Если Вы решите, что Ваше приложение должно отправить команды SCSI или ATA в устройства, не поддерживаемые интерфейсами устройства, предоставленными семьями Model и ATA архитектуры SCSI, то необходимо будет записать пользовательский драйвер логической единицы в ядре. Если Вы действительно создаете свой собственный драйвер логической единицы в ядре, уверены, что Вы не отправляете READ
или WRITE
команды от драйвера. При отправке этих команд Вы создаете вредоносный код дыры в системе безопасности, может использовать в своих интересах при помощи Вашего драйвера, чтобы считать или уничтожить данные по устройству, которое пользователь защитил путем установки прав доступа. Для получения дополнительной информации о том, как записать пользовательский драйвер логической единицы в ядре, см. Руководство по программированию Драйвера Устройства массового хранения и пример кода VendorSpecificType00.
Устройства модели архитектуры SCSI на OS X
Когда устройство обнаружено на OS X, Набор I/O находит и загружает драйверы, требуемые поддерживать его. В случае периферийного устройства Модели архитектуры SCSI, объявляющего тип периферийного устройства 00$, 05$, 07$ или 0$ E, Набор I/O загружает несколько уровней драйверов. Вместе, эти уровни известны как штабель драйвера массового хранения (для большей всесторонней информации о штабеле драйвера массового хранения, посмотрите Введение в Руководство по программированию Драйвера Устройства массового хранения).
Штабель драйвера массового хранения разделен на три основных уровня.
Физический взаимосвязанный уровень включает физические взаимосвязанные драйверы, поддерживающие физическое соединение устройства к шине.
Транспортный уровень драйвера включает драйвер логической единицы и драйвер служб протокола, переводящие универсальные запросы I/O в специфичные для устройства команды, подходящие для транспорта через определенную шину.
Уровень услуг устройства включает драйвер блочной системы хранения, поддерживающий универсальные системные запросы и дополнительные драйверы фильтра, которые могут реализовать схемы проверки или шифрование.
Рисунок 2-1 показывает штабель драйвера массового хранения, поддерживающий устройство массового хранения, объявляющее тип периферийного устройства 00$, 05$, 07$ или 0$ E.
Семья Model архитектуры SCSI поддерживает транспортный уровень драйвера. Это обеспечивает драйвер логической единицы, переводящий универсальные системные запросы в команды, определенные для набора команд SCSI, устройство совместимо с и драйвер служб протокола, упаковывающий команды от драйвера логической единицы в формат, который ожидает шина.
Семья Model архитектуры SCSI осуществляет политику эксклюзивного доступа для драйверов логической единицы. Это означает, что может быть только один драйвер логической единицы для каждой логической единицы устройства. Это применяется к и и основанным на приложении драйверам логической единицы в ядре.
Когда устройство, объявляющее тип периферийного устройства 00$, 05$, 07$ или 0$ E, обнаружено на одной из поддерживаемых шин, Набор I/O сначала находит и загружает надлежащие физические взаимосвязанные драйверы и драйвер служб протокола. Затем объект куска Набора I/O, названный куском периферийного устройства, запрашивает устройство и публикует его тип периферийного устройства в Реестре I/O. Набор I/O тогда находит и загружает драйвер логической единицы, соответствующий опубликованный тип периферийного устройства.
Другой объект куска Набора I/O, названный объектом связи услуг устройства, затем публикует логическую единицу водительский тип в Реестре I/O. Если устройство способно к авторской разработке, семья Model архитектуры SCSI публикует SCSITaskDeviceCategory
ключ в этом объекте куска. Этот ключ, со значением SCSITaskAuthoringDevice
, указывает, что MMCDeviceInterface доступен для устройства. Семья Model архитектуры SCSI также добавляет глобально уникальное идентификационное значение (или GUID) к куску, который приложение может использовать для идентификации устройства.
Затем, Набор I/O находит и загружает драйвер блочной системы хранения, и, если устройством является CDROM или DVD-ROM с настоящим носителей, это загружает схема выделения разделов DVD или CD. Устройство тогда готово обработать запросы I/O от хост-системы.
SCSITaskDeviceInterface
Когда устройство, объявляющее тип периферийного устройства кроме 00$, 05$, 07$ или 0$ E, обнаружено на поддерживаемой шине, Набор I/O начинает соответствие и процесс загрузки, описанный в Устройствах модели архитектуры SCSI на OS X.
Как прежде, кусок периферийного устройства публикует тип периферийного устройства, о котором сообщает устройство. Поскольку тип периферийного устройства не составляет 00$, 05$, 07$ или 0$ E, семья Model архитектуры SCSI добавляет GUID и SCSITaskDeviceCategory
ключ со значением SCSITaskUserClientDevice
к куску периферийного устройства. Эта пара ключ/значение указывает, что приложение может использовать SCSITaskDeviceInterface для управления устройством.
Поскольку нет никаких драйверов логической единицы в ядре, соответствующих на типе периферийного устройства кроме 00$, 05$, 07$ или 0$ E, здании остановов штабеля драйвера после того, как публикуется кусок периферийного устройства. Рисунок 2-2 показывает штабель драйвера массового хранения, когда основанный на приложении драйвер является драйвером логической единицы для устройства, объявляющего тип периферийного устройства 01$.
Когда основанный на приложении драйвер для устройства без драйвера логической единицы в ядре запускается, это ищет Реестр I/O тип устройства или тип устройства, которым это может управлять. Когда это находит устройство, это приобретает SCSITaskDeviceInterface, и приложение становится драйвером логической единицы для устройства.
Поскольку приложение имеет эксклюзивный доступ к устройству, устройство не имеет никаких других клиентов, и уровень услуг устройства штабеля драйвера массового хранения является ненужным.
MMCDeviceInterface
Когда авторская разработка или осваивающее носители устройство, такое как CD-R/W или DVD-R/W, обнаружены на поддерживаемой шине, Набор I/O выполняет соответствие и загрузку драйверов, требуемых создавать весь штабель драйвера массового хранения (как показано на рисунке 2-1). Поскольку семья Model архитектуры SCSI осуществляет политику эксклюзивного доступа, однако, исходное приложение должно так или иначе заменить драйвер логической единицы в ядре для получения эксклюзивного доступа к устройству. Когда исходное приложение является драйвером логической единицы для устройства CD-R/W, рисунок 2-3 показывает штабель драйвера массового хранения.
Исходное приложение заменяет драйвер логической единицы, с помощью семьи Model архитектуры SCSI и Набора I/O, в двух фазах:
Исходное приложение получает MMCDeviceInterface, обеспечивающий неэксклюзивный доступ к устройству, в то время как драйвер логической единицы в ядре продолжает управлять им. MMCDeviceInterface позволяет приложению безопасно запрашивать устройство и определять, может ли это, фактически, поддерживать его.
Приложение может также использовать MMCDeviceInterface, чтобы определить, присутствуют ли носители в устройстве и, если так, для получения информации о носителях, таких как ее тип и размер.
На основе информации, полученной из ее запроса, приложение резервирует носители, получает SCSITaskDeviceInterface и запрашивает эксклюзивный доступ к устройству.
Когда запрос на эксклюзивный доступ предоставляют, управление доходами драйвера логической единицы в ядре к исходному приложению. Драйвер блочной системы хранения в уровне услуг устройства также оставляет управление, и Семейство систем хранения разъединяет любые присутствовавшие схемы выделения разделов. В то время как исходное приложение имеет эксклюзивный доступ к устройству, это может выполнить авторские функции.
Когда исходное приложение завершается, драйвер логической единицы в ядре и драйвер блочной системы хранения восстанавливают управление устройством, и Семейство систем хранения восстанавливает схему выделения разделов в случае необходимости.
SCSITaskInterface
Драйверы логической единицы, или в ядре или основанный на приложении, используют объекты SCSITask связаться с устройствами. Объект SCSITask инкапсулирует всю требуемую информацию во время продолжительности жизни единственной транзакции I/O. Эта информация включает блок дескриптора команды (или CDB) надлежащий спецификации набора команд SCSI, которой устройство соответствует, состояние повторной попытки задачи и указатели функции обратного вызова.
Основанный на приложении драйвер логической единицы должен иметь эксклюзивный доступ к устройству, прежде чем это сможет создать и использовать объекты SCSITask. Поскольку основанный на приложении драйвер не может управлять объектом SCSITask в ядре непосредственно, это должно получить SCSITaskInterface. SCSITaskInterface обеспечивает доступ к объекту SCSITask в ядре — каждый объект SCSITaskInterface соответствует точно одному объекту SCSITask.
Доступ к устройствам SCSI в основанном на Intel Macintosh
Этот раздел обеспечивает обзор некоторых проблем, связанных с разработкой универсальной версии двоичных файлов приложения, получающего доступ к Модели архитектуры SCSI (или Параллель SCSI) устройство. Перед чтением этого раздела, убедиться считать Универсальные Двоичные Инструкции по Программированию. Тот документ касается архитектурных различий, и порядок байтов форматирует и обеспечивает всесторонние инструкции для модификации кода и создания универсальных двоичных файлов. Инструкции в том документе применяются ко всем типам приложений, включая тех который аппаратные средства доступа.
Прежде чем Вы создадите свое приложение как универсальный двоичный файл, удостоверьтесь что:
Вы портируете свой проект на GCC 4 (XCode использует GCC 4 для предназначения для основанных на Intel компьютеров Macintosh),
Вы устанавливаете OS X v10.4 универсальный SDK
Вы разрабатываете свой проект в Xcode 2.1 или позже
Устройства, соответствующие стандартам архитектуры SCSI и набора команд, возвращают данные в формате с обратным порядком байтов, независимо от собственного формата порядка байтов компьютера, в котором работает Ваше приложение. Поскольку основанный на PowerPC Macintosh является также обратным порядком байтов, необходимо исследовать приложение PowerPC на места, где Вы могли бы предположить, что никогда не должны подкачиваться многобайтовые данные.
Необходимо также искать трудно кодированный байт, загружает приложение (такое как код, всегда подкачивающий многобайтовое значение от одного формата порядка байтов до другого). Если Ваш код будет содержать такие подкачки (или он предполагает, что подкачки байта никогда не будут необходимы), то он не будет работать правильно в основанном на Intel Macintosh. Замените все трудно кодированные подкачки надлежащими условными подкачивающими байт макросами, определенными в платформе Ядра в libkern/OSByteOrder.h
. Они макросы доступны для использования в любом проекте, включая высокоуровневые приложения. Например, Авторский проект Испытательного образца Модуля (в /Developer/Examples/IOKit/scsi/SCSITaskLib/Authoring/Cocoa
) использование OSReadBigInt16
функция для обеспечения данных, возвращенных из диска, находится в порядке байтов порядка байтов узла, как показано в Перечислении 2-1:
Перечисление 2-1 , Гарантирующее корректный формат порядка байтов данных, читало из диска
// Here, "interface" is an instance of an MMCDeviceInterface. |
- (void) testReadDiscInfo { |
UInt8 discInfoBuffer[4096]; |
IOReturn err; |
SCSITaskStatus taskStatus; |
SCSI_Sense_Data senseData; |
UInt16 discInfoSize; |
... |
// Issue a READ_DISC_INFORMATION command to the device. Ask for |
// 4 bytes to get the length field. |
err = (*interface)->ReadDiscInformation (interface, discInfoBuffer, 4, &taskStatus, &senseData); |
// ...If the command completed successfully: |
// The first word contains the size. Add 2 bytes for the first word since it isn't counted |
discInfoSize = OSReadBigInt16 ( discInfoBuffer, 0 ) + sizeof ( UInt16 ); |
... |
} |
К счастью, спецификация модели команды SCSI определяет CDB (блок дескриптора команды) как массив байтов. Это означает, что эти байты сохранены в определенном порядке независимо от собственного формата порядка байтов компьютера, в котором работает Ваше приложение. Поскольку Вы создаете команды CDB с помощью объекта SCSITaskInterface, Вы не должны волноваться о формате порядка байтов значений, которые Вы используете.
Вы, возможно, должны выполнить свопинг байта на значениях Ваши чтения приложения от SCSI Устройство модели Параллельной или архитектуры SCSI. Необходимо ли выполнить свопинг байта на значениях, которые Вы отправляете и получаете в параметрах к функциям интерфейса устройства, зависит от типа значения:
Буферное, содержащее многобайтовые поля, который передается в параметре, должно быть в формате с обратным порядком байтов. Например, если Вы используете функцию MMCDeviceInterface
SetWriteParametersModePage
выпустить aMODE_SELECT
команда, буфер, на который указываютbuffer
параметр должен быть в формате с обратным порядком байтов.Другие значения параметров не должны быть подкачаны байтом. Необходимо передать эти значения параметров в формате узла компьютера и значениях, которые Вы получаете в функциях интерфейса устройства, будет в формате узла компьютера.
Используя интерфейсы устройства семьи модели архитектуры SCSI
Устройство взаимодействует через интерфейс, семья Model архитектуры SCSI обеспечивает, плагины, указывающие функции, которые Ваше приложение может вызвать для передачи с устройством. Эти интерфейсы определяются в /System/Library/Frameworks/IOKit.framework/Headers/scsi/SCSITaskLib.h
Прежде чем можно будет использовать эти интерфейсы, однако, необходимо найти устройство, которым Вы интересуетесь. Устройство, соответствующее, является процессом использования функций Набора I/O для поиска Реестра I/O определенные устройства или типы устройства. Этот процесс варьируется в зависимости от того, разрабатываете ли Вы исходное приложение или основанный на приложении драйвер для устройства, не имеющего никакого драйвера логической единицы в ядре. Следующие два раздела описывают, как выполнить устройство, соответствующее для обоих случаев. Затем Доступ к Устройству обрисовывает в общих чертах остающиеся шаги в процессе получения и использования интерфейсов устройства семьи Model архитектуры SCSI.
Устройство, соответствующее для способных к авторской разработке устройств
При разработке исходного приложения Вы сначала создаете соответствующий словарь, соответствующий на способных к авторской разработке Устройствах модели архитектуры SCSI. Для создания словаря Вы используете Базовые функции Основы в качестве в этом примере:
CFMutableDictionaryRef matchingDictionary; |
matchingDictionary = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
Теперь необходимо поместить пару ключ/значение в соответствующий словарь. Ключ IOPropertyMatch
ключ и значение являются подсловарем, который Вы создаете. Подсловарь содержит SCSITaskDeviceCategory
ключ со значением SCSITaskAuthoringDevice
. Перечисление 2-2 показывает, как создать подсловарь.
Перечисление 2-2 , Создающее подсловарь для SCSITaskDeviceCategory
CFMutableDictionaryRef subDictionary; |
subDictionary = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
CFDictionarySetValue ( subDictionary, CFSTR ( kIOPropertySCSITaskDeviceCategory ), CFSTR ( kIOPropertySCSITaskAuthoringDevice ) ); |
Если Вы не должны выполнять более определенный поиск, Вы добавляете IOPropertyMatch
ключ и этот подсловарь к соответствующему словарю Вы создали ранее, как в этом примере:
CFDictionarySetValue ( matchingDictionary, CFSTR ( kIOPropertyMatchKey ), subDictionary );
Можно совершенствовать этот поиск путем добавления других свойств для соответствия на. Например, можно определить соответствие на GUID устройства. Вы добавляете пару ключ/значение для GUID к подсловарю, который Вы создали выше, как в этом примере:
CFDictionarySetValue ( subDictionary, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ), myGUID ); |
Можно также создать другие подсловари, содержащие физический протокол обмена или характеристики устройства, описывающие устройство. Вы тогда добавляете эти подсловари к SCSITaskDeviceCategory
подсловарь Вы создали.
Подсловарь Характеристик Протокола может содержать или или оба из следующих ключей:
Physical Interconnect
Physical Interconnect Location
Подсловарь Характеристик устройства может содержать любой из следующих ключей:
Vendor Name
Product Name
Product Revision Level
Если, например, необходимо соответствовать на определенном поставщике и названии продукта, можно создать подсловарь, содержащий те пары ключ/значение, и добавьте его к SCSITaskDeviceCategory
подсловарь Вы создали ранее. Перечисление 2-3 показывает, как сделать это.
Перечисление 2-3 , Создающее подсловарь характеристик устройства
#define kMyVendorNameString “MyVendorName” |
#define kMyProductNameString “MyProductName” |
CFMutableDictionaryRef deviceCharacteristicsDict; |
deviceCharacteristicsDict = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
CFDictionarySetValue ( deviceCharacteristicsDict, CFSTR ( kIOPropertyVendorNameKey ), CFSTR ( kMyVendorNameString ) ); |
CFDictionarySetValue ( deviceCharacteristicsDict, CFSTR ( kIOPropertyProductNameKey ), CFSTR ( kMyProductNameString ) ); |
CFDictionarySetValue ( subDictionary, CFSTR ( kIOPropertyDeviceCharacteristicsKey ), deviceCharacteristicsDict ); |
Устройство, соответствующее для способных к неавторской разработке устройств
Для приложения, управляющего устройством без драйвера логической единицы в ядре, Вы создаете соответствующий словарь, содержащий IOPropertyMatch
ключ. Значение этого ключа является подсловарем, который Вы создаете, который содержит SCSITaskDeviceCategory
ключ и значение SCSITaskUserClientDevice
. Перечисление 2-4 показывает, как сделать это.
Перечисление 2-4 , Создающее соответствующий словарь для неавторского устройства
CFMutableDictionaryRef matchingDictionary; |
CFMutableDictionaryRef subDictionary; |
matchingDictionary = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
subDictionary = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
CFDictionarySetValue ( subDictionary, CFSTR ( kIOPropertySCSITaskDeviceCategory ), CFSTR ( kIOPropertySCSITaskUserClientDevice ) ); |
CFDictionarySetValue ( matchingDictionary, CFSTR ( kIOPropertyMatchKey ), subDictionary ); |
Можно совершенствовать этот поиск путем соответствия на GUID устройства. Перед добавлением подсловаря к соответствующему словарю Вы помещаете пару ключ/значение, представляющую GUID в подсловаре, как в этом примере:
CFDictionarySetValue ( subDictionary, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ), myGUID ); |
Доступ к устройству
Шаги, которые Вы предпринимаете для доступа к устройству, разделены на два набора. Первый набор применяется только к исходным приложениям. Второй набор применяется к и основанным на приложении драйверам устройств без драйверов логической единицы в ядре и исходным приложениям, уже завершившим первый набор шагов. Пример кода в Доступе к Устройству модели архитектуры SCSI получает доступ к устройству CD-R/W и следует за обоими наборами шагов.
Используйте эти шаги, только если Вы разрабатываете исходное приложение. Как только Вы завершили эти шаги, продолжите к второму набору шагов. Если Вы разрабатываете приложение, управляющее устройством без в драйвере ядра, пропустите эти шаги и следуйте за вторым набором шагов.
Получите интерфейс MMCDeviceInterface устройства. Этот интерфейс устройства обеспечивает функции, дающие Вам информацию о носителях в устройстве, таких как его размер и является ли это пробелом.
Используйте функции MMCDeviceInterface для запросов устройства, при необходимости.
Если диск в настоящее время находится в диске, зарезервируйте носители.
Используйте эти шаги, если Вы разрабатываете основанный на приложении драйвер для устройства без драйвера логической единицы в ядре, или Вы разрабатываете исходное приложение, и Вы уже следовали за первым набором шагов.
Получите интерфейс SCSITaskDeviceInterface устройства. Этот интерфейс устройства обеспечивает функции, чтобы помочь Вам подготовить отправлять объекты SCSITask в устройство. Это также осуществляет политику эксклюзивного доступа и обеспечивает функции для добавления асинхронных механизмов обратного вызова к циклу выполнения.
Запросите эксклюзивный доступ к устройству. Чтобы сделать это, Вы используете функцию SCSITaskDeviceInterface
ObtainExclusiveAccess
.Создайте объект SCSITask связаться с устройством. Чтобы сделать это, Вы используете функцию SCSITaskDeviceInterface
CreateSCSITask
.
Доступ к устройству модели архитектуры SCSI
Этот раздел обеспечивает поэтапные инструкции, включая пример кода, для доступа к диску CD-R/W. Пример кода находит устройство в Реестре I/O путем установки соответствующего словаря как тот в Устройстве, Соответствующем для Способных к авторской разработке Устройств, и затем следует за первым набором шагов в Доступе к Устройству для получения MMCDeviceInterface. Затем это следует за вторым набором шагов, чтобы получить SCSITaskDeviceInterface и запросить эксклюзивный доступ.
Когда пример кода имеет эксклюзивный доступ к диску CD-R/W, это получает SCSITaskInterface и отправляет объекты SCSITask протестировать устройство. Пример кода также показывает, как установить уведомления для динамического дополнения и демонтажа устройства.
Установка Вашего проекта
Пример кода в этой главе из проекта XCode, создающего инструмент Core Foundation. При необходимости в подробной документации относительно использования XCode, можно найти его в Руководствах> Инструменты> XCode.
Для установки XCode для создания примера кода сделайте следующее:
Выберите «Core Foundation Tool» при создании проекта. Это создает скелетное
main.c
файл и включаетCoreFoundation.h
в проекте.Использование Добавляет Платформы... из меню Projects для добавления
IOKit.framework
иSystem.framework
к Вашему проекту.Замените
main.c
файл с Перечислением 2-5 через Перечисление 2-14, включительно.Создайте свой код в Мужественном формате исполняемых файлов (формат XCode по умолчанию).
Рекомендуется запустить путем создания с отладки включенного. Чтобы сделать это в XCode, откройте целевой список путем щелчка по треугольнику раскрытия. Дважды щелкните по единственной перечисленной цели. В инспекторе, появляющемся, щелкните по вкладке сборки, затем щелкните по флажку рядом с, 'Генерируют Отладочные символы'.
Перечисление 2-5 показывает заголовочные файлы, которые необходимо будет включать для примера кода в эту главу.
Заголовочные файлы перечисления 2-5
#include <IOKit/IOKitLib.h> |
#include <IOKit/scsi-commands/SCSITaskLib.h> |
Пример кода в этой главе использует глобальные переменные в Перечислении 2-6 для установки асинхронных уведомлений для дополнения устройства и удаления.
Глобальные переменные перечисления 2-6
static IONotificationPortRef gNotifyPort; |
static io_iterator_t gAppearedIter; |
static io_iterator_t fDisappearedIter; |
Создание основной функции
Пример кода для работы с диском CD-R/W, поддерживаемым драйвером семьи Model архитектуры SCSI, начинается main
функция, показанная в Перечислении 2-7. Эта функция выполняет следующие задачи.
Это устанавливает сигнальный обработчик (показанный в Перечислении 2-8), чтобы заботиться о прерывании командной строки, используемом для выхода из цикла выполнения.
Это устанавливает связь с Набором I/O и устанавливает соответствующий словарь для нахождения авторских устройств.
Это устанавливает асинхронное уведомление, которое вызывают, когда устройство сначала присоединено к Реестру I/O и другому, которого вызывают, когда демонтировано устройство.
Это запускает цикл выполнения, таким образом, будут получены уведомления.
main
функционируйте использует функции Набора I/O, чтобы установить и изменить соответствующий словарь и установить уведомления и Базовые функции Основы для установки цикла выполнения для получения уведомлений. Это вызывает следующие функции для получения доступа к устройству.
DeviceAppeared
(показанный в Перечислении 2-9), выполняет итерации по набору согласующих устройств и вызововTestDevice
(показанный в Перечислении 2-11) для каждого.DeviceDisappeared
(показанный в Перечислении 2-10), выполняет итерации по набору согласующих устройств и выпускает каждого поочередно.
Перечисление 2-7 , Настраивающее основную функцию для доступа к устройству CD-R/W
int main (int argc, const char *argv[]) |
{ |
mach_port_t masterPort; |
CFMutableDictionaryRef matchingDict; |
CFMutableDictionaryRef subDict; |
CFRunLoopSourceRef runLoopSource; |
kern_return_t kr; |
sig_t oldHandler; |
// Set up a signal handler so we can clean up when we're interrupted from the command line |
// Otherwise we stay in our run loop forever. |
oldHandler = signal(SIGINT, SignalHandler); |
if (oldHandler == SIG_ERR) |
printf("Could not establish new signal handler"); |
// first create a master_port for my task |
kr = IOMasterPort(MACH_PORT_NULL, &masterPort); |
if (kr || !masterPort) |
{ |
printf("ERR: Couldn't create a master IOKit Port(%08x)\n", kr); |
return -1; |
} |
// Create the dictionaries |
matchingDict = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
subDict = CFDictionaryCreateMutable ( kCFAllocatorDefault, 0, NULL, NULL ); |
// Create a dictionary with the "SCSITaskDeviceCategory" key = "SCSITaskAuthoringDevice" |
CFDictionarySetValue ( subDict, |
CFSTR ( kIOPropertySCSITaskDeviceCategory ), |
CFSTR ( kIOPropertySCSITaskAuthoringDevice ) ); |
// Add the dictionary to the main dictionary with the key "IOPropertyMatch" to |
// narrow the search to the above dictionary. |
CFDictionarySetValue ( matchingDict, |
CFSTR ( kIOPropertyMatchKey ), |
subDict ); |
// Create a notification port and add its run loop event source to our run loop |
// This is how async notifications get set up. |
gNotifyPort = IONotificationPortCreate(masterPort); |
runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); |
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode); |
// Retain a reference since we arm both the appearance and disappearance notifications |
// and the call to IOServiceAddMatchingNotification() consumes a reference each time. |
matchingDict = ( CFMutableDictionaryRef ) CFRetain ( matchingDict ); |
// Now set up two notifications, one to be called when a raw device is first matched by I/O Kit, and the other to be |
// called when the device is terminated. |
kr = IOServiceAddMatchingNotification( gNotifyPort, |
kIOFirstMatchNotification, |
matchingDict, |
DeviceAppeared, |
NULL, |
&gAppearedIter ); |
DeviceAppeared(NULL, gAppearedIter); // Iterate once to get already-present devices and |
// arm the notification |
kr = IOServiceAddMatchingNotification( gNotifyPort, |
kIOTerminatedNotification, |
matchingDict, |
DeviceDisappeared, |
NULL, |
&gDisappearedIter ); |
DeviceDisappeared(NULL, gDisappearedIter); // Iterate once to arm the notification |
// Now done with the master_port |
mach_port_deallocate(mach_task_self(), masterPort); |
masterPort = 0; |
// Start the run loop. Now we'll receive notifications. |
CFRunLoopRun(); |
// We should never get here |
return 0; |
} |
При нажатии Control-C, чтобы остановить цикл выполнения и выйти из программы, некоторые объекты могут все еще использоваться. SignalHandler
функция, показанная в Перечислении 2-8, выпускает эти объекты и выходы.
Перечисление 2-8 , Обрабатывающее прерывания командной строки
void SignalHandler(int sigraised) |
{ |
printf("\nInterrupted\n"); |
// Clean up here |
IONotificationPortDestroy(gNotifyPort); |
if (gAppearedIter) |
{ |
IOObjectRelease(gAppearedIter); |
gAppearedIter = 0; |
} |
if (gDisappearedIter) |
{ |
IOObjectRelease(gDisappearedIter); |
gDisappearedIter = 0; |
} |
exit(0); |
} |
Теперь, когда Вы получили итератор для ряда согласующих устройств, можно использовать его для получения доступа к каждому устройству. Функция DeviceAppeared
(показанный в Перечислении 2-9), использует функцию Набора I/O IOIteratorNext
для доступа к каждому устройству возражают, и затем вызывает функцию TestDevice
(показанный в Перечислении 2-11), чтобы создать интерфейсы устройства для устройства и протестировать его.
Перечисление 2-9 , Получающее доступ к набору согласующих устройств
void DeviceAppeared(void *refCon, io_iterator_t iterator) |
{ |
kern_return_t kr; |
io_service_t obj; |
while (obj = IOIteratorNext(iterator)) |
{ |
printf("Device appeared.\n"); |
TestDevice(obj); |
kr = IOObjectRelease(obj); |
} |
} |
Функция DeviceDisappeared
(показанный в Перечислении 2-10), просто использует итератор, полученный из main
функция (показанный в Перечислении 2-7) для выпуска каждого объекта устройства. Это также имеет эффект вооружения уведомления завершения устройства, таким образом, это уведомит программу будущих удалений устройства.
Перечисление 2-10 , Выпускающее объекты устройства
void DeviceDisappeared(void *refCon, io_iterator_t iterator) |
{ |
kern_return_t kr; |
io_service_t obj; |
while (obj = IOIteratorNext(iterator)) |
{ |
printf("Device disappeared.\n"); |
kr = IOObjectRelease(obj); |
} |
} |
Тестирование устройства
Функция TestDevice
(показанный в Перечислении 2-11), создает промежуточный сменный интерфейс для устройства и затем запрашивает этот интерфейс для получения MMCDeviceInterface. Пример кода не использует MMCDeviceInterface для запросов устройства несмотря на то, что функции такой как GetPerformance
и GetTrayState
доступны.
Функция TestDevice
использование функции Набора I/O для создания MMCDeviceInterface и затем вызывает функцию MMCDeviceInterface GetSCSITaskDeviceInterface
создать SCSITaskDeviceInterface. Как только это получает SCSITaskDeviceInterface, это вызывает свою функцию ObtainExclusiveAccess
запросить эксклюзивный доступ к устройству. После получения эксклюзивного доступа, TestDevice
вызывает следующие функции, чтобы создать объекты SCSITask и отправить их в устройство.
Inquiry
, (показанный в Перечислении 2-12), создает объект SCSITask отправить команду INQUIRY в устройство и распечатывает данные запроса, которые это получает.TestUnitReady
, (показанный в Перечислении 2-13), создает объект SCSITask отправить команду TEST_UNIT_READY в устройство и вызовыPrintSenseString
(показанный в Перечислении 2-14) для печати смысла представляют возвраты устройства в виде строки.
TestDevice
тогда оставляет эксклюзивный доступ к устройству и выпускает SCSITaskDeviceInterface и MMCDeviceInterface.
Перечисление 2-11 , Получающее MMCDeviceInterface и получающее эксклюзивный доступ
void TestDevice(io_service_t service) |
{ |
SInt32 score; |
HRESULT herr; |
kern_return_t err; |
IOCFPlugInInterface **plugInInterface = NULL; |
MMCDeviceInterface **mmcInterface = NULL; |
SCSITaskDeviceInterface **interface = NULL; |
// Create the IOCFPlugIn interface so we can query it. |
err = IOCreatePlugInInterfaceForService ( service, |
kIOMMCDeviceUserClientTypeID, |
kIOCFPlugInInterfaceID, |
&plugInInterface, |
&score ); |
if ( err != noErr ) |
{ |
printf("IOCreatePlugInInterfaceForService returned %d\n", err); |
return; |
} |
// Query the interface for the MMCDeviceInterface. |
herr = ( *plugInInterface )->QueryInterface ( plugInInterface, |
CFUUIDGetUUIDBytes ( kIOMMCDeviceInterfaceID ), |
( LPVOID *) &mmcInterface ); |
if ( herr != S_OK ) |
{ |
printf("QueryInterface returned %ld\n", herr); |
return; |
} |
interface = ( *mmcInterface )->GetSCSITaskDeviceInterface ( mmcInterface ); |
if ( interface == NULL ) |
{ |
printf("GetSCSITaskDeviceInterface returned NULL\n"); |
return; |
} |
err = ( *interface )->ObtainExclusiveAccess ( interface ); |
if ( err != noErr ) |
{ |
printf("ObtainExclusiveAccess returned %d\n", err); |
return; |
} |
Inquiry(interface); |
TestUnitReady(interface); |
( *interface )->ReleaseExclusiveAccess ( interface ); |
// Release the SCSITaskDeviceInterface. |
( *interface )->Release ( interface ); |
( *mmcInterface )->Release ( mmcInterface ); |
IODestroyPlugInInterface ( plugInInterface ); |
} |
Теперь, когда Вы получили эксклюзивный доступ, можно создать объекты SCSITask и отправить их в устройство. Функция Inquiry
(показанный в Перечислении 2-12), использует функцию SCSITaskDeviceInterface CreateSCSITask
создать объект SCSITaskInterface. Это тогда использует функции SCSITaskInterface для подготовки объекта отправить команду INQUIRY. Если команда выполняется успешно, Inquiry
распечатывает результаты. Наконец, Inquiry
выпускает объект SCSITaskInterface и возвраты.
Перечисление 2-12 , Отправляющее команду INQUIRY в устройство
void Inquiry(SCSITaskDeviceInterface **interface) |
{ |
SCSICmd_INQUIRY_StandardData inqBuffer; |
SCSITaskStatus taskStatus; |
SCSI_Sense_Data senseData; |
SCSICommandDescriptorBlock cdb; |
SCSITaskInterface ** task = NULL; |
UInt8 * bufPtr = ( UInt8 * ) &inqBuffer; |
char vendorID[9]; |
char productID[17]; |
char firmwareRevLevel[5]; |
IOReturn err = 0; |
UInt32 index = 0; |
IOVirtualRange * range = NULL; |
UInt64 transferCount = 0; |
UInt32 transferCountHi = 0; |
UInt32 transferCountLo = 0; |
// Create a task now that we have exclusive access |
task = ( *interface )->CreateSCSITask ( interface ); |
if ( task != NULL ) |
{ |
// Zero the buffer. |
memset ( bufPtr, 0, sizeof ( SCSICmd_INQUIRY_StandardData ) ); |
// Allocate a virtual range for the buffer. If we had more than 1 scatter-gather entry, |
// we would allocate more than 1 IOVirtualRange. |
range = ( IOVirtualRange * ) malloc ( sizeof ( IOVirtualRange ) ); |
if ( range == NULL ) |
{ |
printf("*********** ERROR Malloc'ing IOVirtualRange ***********\n\n"); |
} |
// zero the senseData and CDB |
memset ( &senseData, 0, sizeof ( senseData ) ); |
memset ( cdb, 0, sizeof ( cdb ) ); |
// Set up the range. The address is just the buffer's address. The length is our request size. |
range->address = ( IOVirtualAddress ) bufPtr; |
range->length = sizeof ( SCSICmd_INQUIRY_StandardData ); |
// We're going to execute an INQUIRY to the device as a |
// test of exclusive commands. |
cdb[0] = 0x12 /* inquiry */; |
cdb[4] = sizeof ( SCSICmd_INQUIRY_StandardData ); |
// Set the actual CDB in the task |
err = ( *task )->SetCommandDescriptorBlock ( task, cdb, kSCSICDBSize_6Byte ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Setting CDB ***********\n\n"); |
} |
// Set the scatter-gather entry in the task |
err = ( *task )->SetScatterGatherEntries ( task, range, 1, sizeof ( SCSICmd_INQUIRY_StandardData ), |
kSCSIDataTransfer_FromTargetToInitiator ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Setting SG Entries ***********\n\n"); |
} |
// Set the timeout in the task |
err = ( *task )->SetTimeoutDuration ( task, 10000 ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Setting Timeout ***********\n\n"); |
} |
printf("*********** Requesting Inquiry Data ***********\n\n"); |
// Send it! |
err = ( *task )->ExecuteTaskSync ( task, &senseData, &taskStatus, &transferCount ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Executing Task ***********\n\n"); |
} |
// Get the transfer counts |
transferCountHi = ( ( transferCount >> 32 ) & 0xFFFFFFFF ); |
transferCountLo = ( transferCount & 0xFFFFFFFF ); |
printf("taskStatus = %d, transferCountHi = 0x%08lx, transferCountLo = 0x%08lx\n", taskStatus, transferCountHi, transferCountLo); |
// Task status is not GOOD, print any sense string if they apply. |
if ( taskStatus == kSCSITaskStatus_GOOD ) |
{ |
printf("*********** INQUIRY DATA ***********\n\n"); |
printf("Peripheral Device Type = %d\n", bufPtr[0] & 0x1F); |
printf("Removable Media Bit = %d\n", ( bufPtr[1] & 0x80 ) == 0x80); |
for ( index = 8; index < 16; index++ ) |
{ |
if ( bufPtr[index] == 0 ) |
break; |
vendorID[index-8] = bufPtr[index]; |
} |
vendorID[index-8] = 0; |
for ( index = 16; index < 32; index++ ) |
{ |
if ( bufPtr[index] == 0 ) |
break; |
productID[index-16] = bufPtr[index]; |
} |
productID[index-16] = 0; |
for ( index = 32; index < 36; index++ ) |
{ |
if ( bufPtr[index] == 0 ) |
break; |
firmwareRevLevel[index-32] = bufPtr[index]; |
} |
firmwareRevLevel[index-32] = 0; |
printf("Vendor Identification = %s\n", ( char * ) vendorID); |
printf("Product Identification = %s\n", ( char * ) productID); |
printf("Product Revision Level = %s\n", ( char * ) firmwareRevLevel); |
printf("\n"); |
} |
// Be a good citizen and cleanup |
free ( range ); |
// Release the task interface |
( *task )->Release ( task ); |
} |
} |
Функция TestUnitReady
(показанный в Перечислении 2-13), отправляет команду TEST_UNIT_READY в устройство. Во-первых, это создает объект SCSITaskInterface, и затем это создает CDB, содержащий команду. После отправки команды, TestUnitReady
проверяет состояние задачи и, в зависимости от значения состояния, вызывает функцию PrintSenseString
(показанный в Перечислении 2-14) для печати данных чувственного опыта. Перед возвратом, TestUnitReady
выпускает объект SCSITaskInterface.
Перечисление 2-13 , Отправляющее команду TEST_UNIT_READY в устройство
void TestUnitReady(SCSITaskDeviceInterface **interface) |
{ |
SCSITaskStatus taskStatus; |
SCSI_Sense_Data senseData; |
SCSICommandDescriptorBlock cdb; |
SCSITaskInterface ** task = NULL; |
IOReturn err = 0; |
UInt64 transferCount = 0; |
// Create a task now that we have exclusive access |
task = ( *interface )->CreateSCSITask ( interface ); |
if ( task != NULL ) |
{ |
// zero the senseData and CDB |
memset ( &senseData, 0, sizeof ( senseData ) ); |
memset ( cdb, 0, sizeof ( cdb ) ); |
// The TEST_UNIT_READY code consists of all zeroes so it is |
// not necessary to set any additional values in the CDB |
// Set the actual CDB in the task |
err = ( *task )->SetCommandDescriptorBlock ( task, cdb, kSCSICDBSize_6Byte ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Setting CDB ***********\n\n"); |
} |
// Set the timeout in the task |
err = ( *task )->SetTimeoutDuration ( task, 5000 ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Setting Timeout ***********\n\n"); |
} |
// Send it! |
err = ( *task )->ExecuteTaskSync ( task, &senseData, &taskStatus, &transferCount ); |
if ( err != kIOReturnSuccess ) |
{ |
printf("*********** ERROR Executing Task ***********\n\n"); |
} |
printf("taskStatus = %d\n", taskStatus); |
// Task status is not GOOD, print any sense string if they apply. |
if ( taskStatus == kSCSITaskStatus_GOOD ) |
{ |
printf("Good Status\n"); |
} |
else if ( taskStatus == kSCSITaskStatus_CHECK_CONDITION ) |
{ |
// Something happened. Print the sense string |
PrintSenseString(&senseData); |
} |
else |
{ |
printf("taskStatus = 0x%08x\n", taskStatus); |
} |
printf("\n"); |
// Release the task interface |
( *task )->Release ( task ); |
} |
} |
Функция PrintSenseString
(показанный в Перечислении 2-14), распечатывает данные чувственного опыта возвраты устройства после выполнения представленной команды TEST_UNIT_READY TestUnitReady
(показанный в Перечислении 2-13).
Перечисление 2-14 , Распечатывающее данные чувственного опыта
void |
PrintSenseString ( SCSI_Sense_Data * sense ) |
{ |
char str[256]; |
UInt8 key, ASC, ASCQ; |
key = sense->SENSE_KEY & 0x0F; |
ASC = sense->ADDITIONAL_SENSE_CODE; |
ASCQ = sense->ADDITIONAL_SENSE_CODE_QUALIFIER; |
// Print the sense string information. |
sprintf ( str, "Key: $%02lx, ASC: $%02lx, ASCQ: $%02lx ", key, ASC, ASCQ ); |
} |