Ключевые слова:winmodem, modem, dialup, linux, kernel, fedora, udev, (найти похожие документы)
From: Арсений Чеботарев
Newsgroups:
Date: Mon, 25 Jun 2006 14:31:37 +0000 (UTC)
Subject: Настройка winmodem'а и udev в Linux
Оригинал: http://www.cpp.com.ua/?in=kpp_show_article&kpp_art_ID=580
Linux 2.6: откуда берется пыль и куда деваются линки
Как всегда, не претендуя на типичность случая, расскажу небольшую
историю о том, как ставил winmodem на Fedora Core 4. Мне помогло - не
исключено, что поможет и еще комуто. Педанты, как всегда, могут
поискать ошибки в тексте - тоже занятие.
С месяц назад получаю письмо: "Вот Вы писали про Fedor'у 4. Я ставлю
winmodem, и у меня сим-линк /dev/modem, который я создаю, куда-то
пропадает под этой системой". Тогда я просто съехал: да ты чё, какой
winmodem, это глупое занятие, возьми нормальный - и так далее (если
честно, не было сил разгибать мозги). Человек и пропал куда-то, запил,
наверное, с горя. И вот проходит месяц - и я оказываюсь в глубоком
лесу со своим ноутом, телефонным кабелем и этим, вы поняли.
winmodem'ом на борту. Награда нашла героя, пришлось все-таки поскрести
"чугунок" - давненько я под модемом никуда не ходил, а тем более под
winmodem'ом.
Ну, конечно, если начинать с конца, то ответ на главный вопрос прост и
всем известен. Конечно же, сим-линки, созданные в каталоге /dev, не
будут храниться, поскольку сама эта файловая система /dev в FC4
создается на лету подсистемой udev как следствие нескольких действий.
Для начала ядро делает учет устройств в виде файловой системы SYSFS,
которая располагается (монтируется) в каталоге /sys. Это, так сказать,
"честные" устройства, обнаруженные в системе. После этого запускается
udev - вы и сами можете это проделать с помощью /sys/udevstart.
Udev - это уровень "индерекции", то есть трансляции "физических"
устройств из /sys в логические в /dev. Для полноты изложения нужно
сказать, что тут задействована также система "горячего" подключения,
поскольку /sys может видоизменяться в процессе работы.
Сайт Linux Udev расположен по адресу
http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
Собственно, трансляция sys в dev может выполняться полностью
автоматически, без вашего вмешательства. Ядро, опрашивая драйверы,
дает устройствам вполне жизнеспособные имена и номера, например tty1.
Однако в некоторых случаях вы можете быть недовольны таким положением
вещей - это особенно верно для динамически подключаемых устройств, в
частности по USB. Например, вы подключаете USB-винчестер в различные
разъемы - и при этом он каждый раз будет называться по-разному. В
результате вы не можете написать удобную программу, поскольку не
знаете, где ваше устройство. Еще хуже, если однотипных устройств
много: все они будут называться одинаково, с разницей только в индексе
- и в следующий раз могут запросто поменяться местами. Это именно та
проблема, с которой так плохо справился DEVFS, отчего и пошел
подальше.
Для того чтобы Udev мог что-то намутить с создаваемыми именами
устройств, существуют правила udev, расположенные в каталоге
/etc/udev/rules.d. На самом деле это настраивается в /etc/udev, но
менять настройку не в пределах нашей компетенции. Подобно, например,
стартовым скриптам, правила хранятся не в одном файле, а во всех
документах с расширением .rules (остальные игнорируются),
располагаемых в каталоге /etc/udev/rules.d. При этом файлы
располагаются в алфавитном порядке, и все строки из них читаются в
один большой буфер; затем каждое устройство из /sys будет
"квалифицировано" согласно данным правилам.
Обратите внимание на логику udev: поиск происходит до первого
совпадения, после чего устройство считается "обслуженным". Таким
образом для одного устройства во всех файлах может быть только одно
правило - то, которое будет найдено первым. В этом отличие от
скриптов, которые всегда будут выполнены все - один за одним в
заданном порядке. Это меняет и нашу логику: если в случае со скриптами
мы, вероятно, хотим, чтобы система сначала выполнила все стандартные
настройки (например запустила сетевые интерфейсы), а уже потом вносим
свои изменения, например задаем другой сетевой адрес. Специально для
этих целей существует ряд файлов-"постинициаторы".
В случае с правилами Udev "потома не будет" - первое же попадание
устройства в правила будет и последним. Следовательно, нам нужно,
чтобы наши правила проверялись раньше, чем системные по умолчанию.
Файл настроек по умолчанию начинается с префикса "50" - так чтобы
можно было что-то добавить до и после. Умные люди добавили также файл
с префиксом "10" для каких-то "очень важных" устройств - ну что ж,
назовемся как-то вроде /etc/udev/rules.d/00-ltmodem.rules и поместим
туда следующую строку:
KERNEL='ttyLTM0", NAME='%k', SYMLINK='modem', MODE='0660", GROUP='uucp'
Эта строка содержит (незримо) две части: ключевую, или идентификатор,
и триггер - то есть часть, которая будет "выполнена" для устройства,
которое совпадет с нашим ключом. Каждое устройство, как было сказано
выше, единожды проходит через этот "строй" - процесс довольно нудный,
в чем вы можете легко убедиться, запустив /sbin/udevstart, Тем более
что вам все равно придется это сделать после внесения изменений, если
вы не хотите перезагружать компьютер.
В данном случае устройство детектируется по "к-имени", то есть так,
как оно известно ядру. А именно - ttyLTM0. Конечно, это не самый
гибкий ход. Но я просто знаю, что более одного винмодема у меня быть
не может. В общем случае можно было бы записать "ttyLTM?", или даже
"ttyLTM*": значение звездочки и вопросительного знака точно такие, как
и в командной строке.
Конечно, один винчестер от другого по к-имени не отличишь - для этого
существует еще ряд признаков. Самый общий - SYSFS{имя файла}, что
обозначает сравнить значение в данном файле SYSFS для данного
устройства с заданным значением. Вся SYSFS состоит из
классифицированных каталогов. В этих каталогах лежат файлы с
параметрами устройств (вы можете сами на это посмотреть, вас там никто
не укусит). Вот идентификатор SYSFS и сравнивает значение такого
"атомарного файла" с некоторым значением.
Udev сам может сварганить ключ для любого подключенного устройства.
Если вы знаете, где оно находится в иерархии /sys (благодаря линкам
там все встречается по три раза - ищите, как вам удобно, по типу или
по шине подключения), то можете даже "вывернуть" значения параметров с
помощью утилиты udevinfo. Собственно, она как раз и помогает искать
параметры в /sys, в частности по "готовому" /dev - устройству.
Например, узнаем, как в SYSFS называется наш модем, указав его "ноду"
в devfs. Выполняем:
# /usr/bin/udevinfo -q path -n /dev/ttyLTM0
и получаем:
/class/tty/ttyLTM0
где полученный путь "абсолютно относителен" в системе /sys. Теперь
запросим все параметры (атрибуты, в терминах Udev), связанные с этим
устройством:
# usr/bin/udevinfo -a -p /class/tty/LTM0
...бла-бла-бла
device '/sys/class/tty/ttyLTM0' has major:minor 62:64
looking at class device '/sys/class/tty/ttyLTM0':
SUBSYSTEM=="tty"
SYSFS{dev}=="62:64"
Параметры читаются так: q(uery), n(ode), p(ath), a(ttributes).
Конечно, вы легко можете скомбинировать два вызова в одной строке
через обратные кавычки или конструктив $(). Например, следующая строка
выдает всю известную системе информацию о моем флэш-накопителе:
#udevinfo -a -p `udevinfo -q path -n /dev/sda1`
Короче, мне достаточно к-имени - я и так доволен, тем более что
драйвер не предоставил никакой другой веской информации для
классификации винмодемов. Предполагается, что это PCI-устройства без
поддержки hotplug. Помимо SYSFS существуют и другие ключи, такие как
BUS или SUBSYSTEM,- но мне это уже совсем не надо. Учтите, если вы
задаете несколько ключей поиска, они должны совпасть все вместе - так
что указывайте так много ключей, сколько надо для "отсечения" вашего
устройства или группы устройств.
Когда устройство найдено, с помощью правил оно будет "причесано" перед
отправкой в /dev. NAME='%k' подставляет к-имя, говоря "называй так,
как хочет ядро", а точнее драйвер. Здесь же можно немного покрутить с
каталогами, написав, например, NAME='modems/%k', или с самим именем:
NAME='momed%n' (где %n обозначает индекс, в нашем случае - 0).
Если мы не хотим задавать новое имя - значит, наше правило хочет
поиграть с правами доступа или сим-линком. Право 0660 называется "себе
и группе читать и писать" - это просто для понтов написано, поскольку
такое право прописано по умолчанию в /etc/udev/udev.conf в параметре
default_mode. Владелец устройств по умолчанию всегда root. Группа uucp
занимается всякими коммуникациями, так что все нужные приложения,
вроде PPP-дайлера, смогут получить доступ к модему.
В оригинальной доке советуют что-то там настраивать в permissions.d -
но, как я понял, эта техника уже ушла в прошлое, такого каталога нет,
а параметры владельца и файловых прав доступа вполне доступны через
rules.d. При этом в скриптах есть интересный код "конвертация
пермишенов в новый формат". Правильно - ну его, всё в один файл, в
одну строку. Устройство ttyLTM0 в SYSFS создаст наш драйвер модема,
например, по modprobe ltserial, который мы пока даже не поставили. Да,
именно так - стартуем модуль ltserial, а модем там тоже окажется,
скоро увидим почему.
Мы проследили путь с конца: от сим-линка и имени устройства в /dev до
/sys и далее - до драйвера. Теперь закончим это расследование в
обратном направлении, чтобы вы могли пользоваться результатом
практически.
В последнее время драйвер любого "нештатного" устройства представляет
собой модуль, загружаемый сервисом modprobe. Несмотря на вялое
сопротивление сторонников монолитного ядра, идея динамического "уровня
0" вокруг мини-ядра - так, как это сделано в QNX (хотя это не совсем
точно - в QNX модули не располагаются в L0),- постепенно становится
стандартом де-факто. Впрочем, хорошо, конечно, когда есть выбор.
Кое-что действительно не имеет смысла загружать динамически.
Вообще-то, модуль загружает само ядро, и отчет об этом помещается в
файл /var/log/dmesg - для его просмотра даже существует специальная
утилита dmesg, так что можете просто запускать ее. Утилита modprobe
используется только для загрузки модулей в самом общем смысле: сначала
исследуются зависимости модуля, в том числе и "зависимости
зависимостей", после чего загружаются все модули, необходимые для
загрузки указанного.
В нашем случае для "создания" устройства в SYSFS достаточно в любой
момент (удачно) выполнить загрузку драйвера modprobe ltserial. Модули
расположены в каталоге "модули текущей сборки" - или, как это часто
пишут, /lib/modules/`uname -r`, где uname -r возвращает "релиз" ядра.
Например, в случае текущей сборки Fedora Core 4 это значение будет
'2.6.11-1.1369_FC4'. Вы можете выполнить эту команду и убедиться, что
на релиз легче ссылаться, как на uname -r :).
Поскольку модули могут зависеть друг от друга через внешние ссылки
(так называемые внешние символы, экспортируемые через декларации
EXPORT_SYMBOL), то для загрузки модуля система должна знать о таких
связях. В целях оптимизации (поиск всех внешних символов во всех
библиотеках - задача не скорая) данная информация кэшируется в файле
modules.dep, располагающийся в корневом каталоге модулей, путь к
которому мы только что обсуждали. Этот файл является текстовым, с
очень простым форматом, так что вы легко можете его исследовать.
Утилита modprobe ожидает, что modules.dep находится в актуальном
состоянии. Для этого после "заброски" новых модулей и перед запуском
(явным или неявным, при перезагрузке) modprobe нужно выполнить depmod
-a - это обновит modules.dep. Конечно, это надо проделать только один
раз.
Допустим, что у нас уже есть два нужных модуля (мы-то движемся с
конца): ltmodem.ko и ltserial.ko. Скопируем их в какое-нибудь
приметное место внутри каталога модулей, например в
/lib/modules/`uname -r`/extra. Выполним обновление зависимостей depmod
-a и убедимся, что в modules.dep появились строки:
/lib/modules/2.6.11-1.1369_FC4/extra/ltserial.ko:
/lib/modules/2.6.11-1.1369_FC4
/extra/ltmodem.ko
/lib/modules/2.6.11-1.1369_FC4/extra/ltmodem.ko:
Как видите, модуль ltserial зависит от ltmodem, так что мы можем
выдать только modprobe ltserial - и ltmodem будет загружен
автоматически (маленькая радость). Если мы один раз загрузим модуль,
он и впредь будет загружаться, даже после перезагрузки. Выполним
modprobe ltmodem и посмотрим dmesg, где помимо прочего будут и такие
строки:
ltmodem: module license 'Proprietary' taints kernel.
Loading Lucent Modem Controller driver version 8.26-alk
ACPI: PCI Interrupt 0000:00:09.1[A] -> Link [C185] -> GSI 11 (level, low) -> IRQ 11
Detected Parameters Irq=11 BaseAddress=0x3430 ComAddress=0x0
ttyLTM0 at I/O 0x3430 (irq = 11) is a Lucent/Agere Modem
Теперь, наконец, займемся самим драйвером, то есть двумя
упоминавшимися уже модулями *.ko (ko - от Kernel Object file).
Во-первых, обратите внимание на название драйвера - Ltmodem, в данном
случае это обозначает Lucent Technologies. В модеме используется
сигнальный процессор (DSP) этого известного производителя. Если вам не
так повезло, и у вас другая модель, то возможно, что проблем будет
больше, вплоть до неразрешимых - и действительно придется брать другой
модем. Это просто Lucent так любезна, что старается предоставлять
драйверы для своих юнитов, так же как и драйвера для беспроводных
карточек Orinoco и т.д. Под "и т.д." следует понимать компанию Agere и
ее продукцию, в которую это "практическое Bell-lab'ство" сейчас
превратилось.
Кстати, кроме "вынь"-модема на настоящем DSP (он "вынь" стал только
потому, что не на взаправдашнем СОМ-порту висит, а на PCI-шине),
существует еще и софт-модем Agere. И как с ним быть, наш драйвер не в
курсе. И даже спросить не у кого - производитель драйверы для Linux не
выпускает. Там и модема-то не видно, по сути, один жирный фирмварь,
который весь и надо бы портировать.
"Поддержка", однако, еще не значит "открытый код". Lucent только
предоставила базовый код виртуального COM-порта в бинарном виде, к
которому нужно линковать драйвер. В результате драйвер состоит из трех
частей: открытые, собираемые вами ltmodem.ko, ltserial.ko и закрытый
кусок кода - ltmdmobj.o (по факту - сим-линк на конкретную версию этой
библиотеки, на сегодня это 8.31). Загрузить отдельно либу с доками
можно на ltmodem.heby.de.
Текущая версия самого драйвера как целого - 2.6-alk-7. В данном случае
alk обозначает "форк", то есть ветку разработки, которую ведет Алексей
Кондратенко. Это как раз то, что нужно для ядра 2.6, с учетом как раз
SYSFS. Исходники можно получить по адресу
http://alk.at.tut.by/ltmodem-2.6-alk-7.tar.bz2 или на нашем диске.
Конечно, другой бы вам еще рассказал, как настроить дозвон модемом из
командной строки, но это уж в другой раз. Я же просто залез в KPPP,
проверил модем опросом ATIn, потыкался в мини-терминал и,
удовлетворенный богоугодной работой, отправился в Большую Сеть.
Единственное что в "командах модема" пришлось поменять тоновый набор
на импульсный, ATDT на ATDP - но уж где и как все это делается, думаю,
вы найдете.
Если winmodem Lucent вам действительно нужен, то на КП-диске лежат и
файлы, и документы, необходимые для установки: файлы универсальные,
исходники, а вот инструкции только к FC4 - думаю, c минимумом
очевидных вариаций подойдет к любому ядру 2.6.
Обратите внимание на логику udev: поиск происходит до первого совпадения, после чего устройство считается "обслуженным". Таким образом для одного устройства во всех файлах может быть только одно
правило - то, которое будет найдено первым.
Дополнение: в документации написано, что "One device can be matched by more than one rule. This has it's practical advantages, for example, we can write two rules which match the same device, where each one provides its own alternate name for the device. Both alternate names will be created, even if the rules are in separate files. It is important to understand that udev will not stop processing when it finds a matching rule, it will continue searching and attempt to apply every rule that it knows about."