Powered By Blogger

Thursday, November 29, 2007

Toshiba Satellite A100-811: ещё немного о Bluetooth. Удалённый доступ.

Итак, мы настроили устройство Bluetooth-адаптера. Что дальше? Ну, для начала, если Ваш телефон умеет GPRS и у Вас есть подписка, можно настроить удалённый доступ через GPRS-модем, в роли которого будет выступать Ваш телефон, оснащённый Bluetooth. В принципе, всё, о чём пойдёт речь ниже - вполне применимо и к настройке доступа с помощью GPRS + IrDA. Разве что во втором случае в качестве модема будет использоваться устройство ircomm, а не знакомый нам уже rfcomm.

Самый простой способ ИМХО, это настроить соединение в какой-нибудь звонилке, а ля kppp. Хотя, можно сделать это и с помощью скриптов ppp. Описание второго способа попадалось мне довольно часто, поэтому я думаю, у желающих и без меня будет масса возможностей настроить доступ с помощью скриптов. Я приведу пример, но подробно останавливаться на деталях не буду. С kppp и того проще. Моя цель лишь указать на некоторые вещи, которые мешают установить работоспособное соединение и которые не всегда очевидны для начинающих пользователей.

Будем исходить их того, что Bluetooth у Вас уже настроен, как рассказывалось ранее и есть конфиг для устройства, эмулирующего последовательную линию - rfcomm (для IrDA это будет устройство ircomm). На настройках телефона останавливаться не будем. Они в любом случае не универсальны и будут зависеть от Вашего оператора, которого и надо будет терроризировать на предмет информации по этому поводу :)

Первое, что мы сделаем, это убедимся, что телефон предоставляет услугу удалённого доступа (для Bluetooth это профиль DUN, для IrDA всё немного проще, ибо у IrDA устройств нет профилей, как это имеет место с Bluetooth). При включенном Bluetooth-адаптере запускаем:

root@devel0:/home/f0x# hcitool scan
Scanning ...
        00:0E:07:DC:F8:3E       Red Quick Fox
root@devel0:/home/f0x# hcitool inq
Inquiring ...
        00:0E:07:DC:F8:3E       clock offset: 0x71ce    class: 0x520204
root@devel0:/home/f0x# sdptool browse
Inquiring ...
Browsing 00:0E:07:DC:F8:3E ...
Service Name: Dial-up Networking
Service RecHandle: 0x10000
Service Class ID List:
  "Dialup Networking" (0x1103)
  "Generic Networking" (0x1201)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 1
Profile Descriptor List:
  "Dialup Networking" (0x1103)
    Version: 0x0100

Service Name: Voice gateway
Service RecHandle: 0x10002
Service Class ID List:
  "Headset Audio Gateway" (0x1112)
  "Generic Audio" (0x1203)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 3
Profile Descriptor List:
  "Headset" (0x1108)
    Version: 0x0100

Service Name: Serial Port 1
Service RecHandle: 0x10003
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 4

Service Name: Serial Port 2
Service RecHandle: 0x10004
Service Class ID List:
  "Serial Port" (0x1101)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 5

Service Name: OBEX Object Push
Service RecHandle: 0x10005
Service Class ID List:
  "OBEX Object Push" (0x1105)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 10
  "OBEX" (0x0008)
Profile Descriptor List:
  "OBEX Object Push" (0x1105)
    Version: 0x0100

Service Name: IrMC Synchronization
Service RecHandle: 0x10006
Service Class ID List:
  "IrMC Sync" (0x1104)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 11
  "OBEX" (0x0008)
Profile Descriptor List:
  "IrMC Sync" (0x1104)
    Version: 0x0100

Service Name: HF Voice gateway
Service RecHandle: 0x10007
Service Class ID List:
  "Handfree Audio Gateway" (0x111f)
  "Generic Audio" (0x1203)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 6
Profile Descriptor List:
  "Handsfree" (0x111e)
    Version: 0x0100

Service Name: OBEX Basic Imaging
Service RecHandle: 0x1000b
Service Class ID List:
  "Imaging Responder" (0x111b)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 15
  "OBEX" (0x0008)
Profile Descriptor List:
  "Imaging" (0x111a)
    Version: 0x0100

Service Name: OBEX File Transfer
Service RecHandle: 0x1000f
Service Class ID List:
  "OBEX File Transfer" (0x1106)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 7
  "OBEX" (0x0008)
Profile Descriptor List:
  "OBEX File Transfer" (0x1106)
    Version: 0x0100

Первой командой мы просто сканируем эфир в поисках другого Bluetooth-устройства и находим его. Второй командной смотрим, что это за устройство - класс. Хотя второе - это скорее чисто эстетический манёвр для маньяков :) И наконец с помощью третьей команды мы узнаём, какие сервисы нам предоставляет удалённое устройство. Первый блок информации и есть то, что нам нужно:

Service Name: Dial-up Networking
Service RecHandle: 0x10000
Service Class ID List:
  "Dialup Networking" (0x1103)
  "Generic Networking" (0x1201)
Protocol Descriptor List:
  "L2CAP" (0x0100)
  "RFCOMM" (0x0003)
    Channel: 1
Profile Descriptor List:
  "Dialup Networking" (0x1103)
    Version: 0x0100
Это наш профиль DUN (Dial-Up Networking).

Чобы создать соединение РРР, в /etc/ppp/peers создайте файл с именем новой точки. В этот файл мы свалим параметры для нового соединения. Скорее всего (во всяком случае, так обстоит дело с моим оператором), нам придётся отключить всякое сжатие. Должно получиться что-то вроде этого:

/dev/rfcomm0 # [ устройство, эмулирующее последовательною линию RS-232 по Bluetooth ]
 115200
 connect "/usr/sbin/chat /etc/chatscripts/gprs"

 noauth 
 defaultroute 
 lock 
 debug   # [ вставляем на всякий случай, пока всё не будет обкатано ]
 novjccomp
 nopcomp
 noaccomp
 nodeflate
 novj
 noccp
 nobsdcomp
 default-asyncmap
 ipcp-accept-local
 ipcp-accept-remote
 lcp-echo-failure 100
 lcp-echo-interval 30
 usepeerdns
 user <user_name>
Здесь user_name - имя пользователя, под которым Вы будете стучаться на РРР-сервер Вашего оператора. Как показывает практика, у некототорых операторов есть определённые ограничения на работу с PPP при доступе по GPRS. Это касается прежде всего поддержки сжатия (специально наматывают траф? :( ). Отключаем, всё что можно:
  1. novjccomp, novj - отключить сжатие TCP/IP по Ван Якобсону.
  2. nobsdcomp - отключить сжатие пакетов по схеме BSD.
  3. nodeflate - отключить сжатие Deflation.
  4. noaccomp - отключить сжатие адресов/управляющих пакетов.
  5. nopcomp - отключить согласование сжатия между удалённой точкой и нашей машиной.
  6. noccp - отключить протокол управления сжатием.
Далее, часто встречается ситуация, когда удалённая точка по каким-либо причинам не в состоянии корректно обеспечить управление линией (на уровне LCP - Link Control Protocol - протокол управления линией). Как правило, это выражается в том, что клиентская машина посылает LCP-запросы, чтобы выяснить состояние линии, но не получает отклика от сервера. В подобном случае содинение разрывается по инициативе клиента, т.к. pppd исходит из того, что линия неработоспособна. Насколько это хорошо, сказать сложно. Но изменить поведение демона, отключив ожидание откликов не представляется возможным. Я более склонен возложить вину на оператора, нежели на демон pppd, ибо ИМХО это вполне в силах оператора обеспечить корректную работу ppp-сервера.

Работа LCP в частности управляется параметрами lcp-echo-failure и lcp-echo-interval (на самом деле, параметров LCP, конечно, немного больше). Первый (lcp-echo-failure) задаёт максимальное количество попыток, по истечении которого считается, что произошёл сбой линии. Второй параметр (lcp-echo-interval) определяет временной интервал между посылками LCP-запросов, на которые удалённый сервер должен прислать своё эхо. Время устанавливается в секундах. В нашем случае параметры имеют значения 100 и 30 соответственно, т.е. если ни на один из посланных 100 запросов не был получен отклик, при том, что запросы отправляются с периодичностью в 30 с, то линия считается мёртвой и демон разрывает соединение. При этом в лог сыплется что-то вроде этого (при включении debug):

Nov  3 23:55:57 devel0 pppd[19993]: pppd 2.4.4 started by f0x, uid 1000
Nov  3 23:55:57 devel0 pppd[19993]: Using interface ppp0
Nov  3 23:55:57 devel0 pppd[19993]: Connect: ppp0 <--> /dev/rfcomm0
Nov  3 23:55:57 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 0
Nov  3 23:55:57 devel0 pppd[19993]: PAP authentication succeeded
Nov  3 23:55:58 devel0 pppd[19993]: local  IP address 10.20.85.219
Nov  3 23:55:58 devel0 pppd[19993]: remote IP address 212.98.170.50
Nov  3 23:55:58 devel0 pppd[19993]: primary   DNS address 77.74.32.66
Nov  3 23:55:58 devel0 pppd[19993]: secondary DNS address 77.74.32.11
Nov  3 23:56:27 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 1
Nov  3 23:56:57 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 2
Nov  3 23:57:27 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 3
...
Nov  4 00:43:29 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 95
Nov  4 00:43:59 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 96
Nov  4 00:44:29 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 97
Nov  4 00:44:59 devel0 pppd[19993]: LCP: Rcvd Code-Reject for code 9, id 98
Nov  4 00:45:59 devel0 pppd[19993]: No response to 100 echo-requests

lock - традиционный параметр, предписывающий pppd создавать файл блокировки и т.о. обеспечивает исключительный доступ к соединению. defaultroute - установить новый маршрут к PPP-шлюзу, полученный при установке соединения, маршрутом по умолчанию. usepeerdns - использовать DNS удалённой точки, позволяет не заботиться о поиске собственного DNS :) noauth - не требовать от удалённой точки авторизации. ipcp-accept-local и ipcp-accept-remote - параметры, определяющие, будут ли приняты адреса удалённой точки и нашей машины по инициативе удалённого сервера, даже если локально адреса уже были заданы. Так в общих чертах обстоит дело с конфигурированием соединения с удалённой точкой - РРР-сервером.

Далее, в случае с настройкой соединения с помощью скриптов идёт скрипт чата, определяющий реакцию на состояния линии ("занято", "звонок", "удалённый модем положил трубку" и т.п.). Здесь же прописывается номер, который набирает модем. В общих чертах скрипт чата выглядит так:

ECHO ON 
 ABORT '\nBUSY\r' 
 ABORT '\nERROR\r' 
 ABORT '\nNO ANSWER\r' 
 ABORT '\nNO CARRIER\r' 
 ABORT '\nNO DIALTONE\r' 
 ABORT '\nRINGING\r\n\r\nRINGING\r' 
 '' \rAT 
 TIMEOUT 12 
 OK ATH 
 OK ATE1 
 OK AT+CGDCONT=1,"IP","internet-address" 
 OK ATD*99*1# 
 TIMEOUT 22 
 CONNECT
Кладём этот файл в /etc/chatscripts/ под именем, ну, хотя бы gprs (за более подробными сведениями обращайтесь к man-страницам). Далее, в файле /etc/ppp/pap-secrets прописываем имя пользователя и пароль:
user_name * password
где user_name и password - соответственно имя пользователя и пароль, под которыми Вы ходите на сервер оператора. В общих чертах, всё. Теперь можно устанавливать соединение командой pppd call peer-name, где peer-name - имя точки доступа в /etc/ppp/peers.

Настройка доступа с помощью kppp отличается лишь тем, что параметры конфигурации для pppd и прочее нужно прописывать не в файлах ppp, а в диалогах kppp. В качестве устройства модема при конфигурировании устройства в kppp указывайте /dev/ircomm0 (или другое число вместо нуля в зависимости от того, как у вас настроена подсистема ircomm).

Sunday, November 11, 2007

Toshiba Satellite A100-811: Core 2 Duo и частота ядер после возврата из suspend-to-RAM (solved).

Довольно долгое время у меня на ноуте под управлением Ubuntu 7.04 наблюдался такой странный глюк. Во время работы от аккумулятора при засыпании ноута в память (suspend-to-RAM) и по возвращении из сна одно ядро процессора работало на пониженной частоте, как и положено в режиме экономии, а другое - почему-то начинало работать на полных оборотах. На просторах интернета я нашёл следующее решение: накатать скрипт следующего содержания:

#!/bin/sh

SYS_DIR=/sys/devices/system/cpu
POLICY=powersave

# ondemand

for CPU in `ls $SYS_DIR`
do
    echo -n "$POLICY" > $SYS_DIR/$CPU/cpufreq/scaling_governor
done
и положить его под именем <priority>-<name> в папку /etc/acpi/resume.d. Здесь priority - это число, определяющее порядок выполнения скрипта. Как и в директориях rc?.d файлы обрабатываются демоном acpid в алфавитном порядке и чтобы задать приоритет, необходимо приписать соответствующий префикс. Пусть в нашем случае это будет 10-core2duo-freq-fix.sh

Не понадобилось много времени, чтобы заметить, что данный способ не работает. Долго и упорно я пытался найти решение, копаясь в спецификации ACPI, в частности, производя поиски на предмет генерируемых событий. Много чего нового узнал, но того, что надо, так и не нашёл. Я не мог понять, почему в моей системе не генерируется событие по выходу из сна - resume... И не мог понять, отдаётся ли такое событие в принципе и предусмотрено ли оно самим ACPI. И хотя ответ на этот вопрос сразу найден не был, решение задачи оказалось гораздо проще и лежало оно в немного другой области. Собственно, решение мне подсказали на форуме и было даже немного обидно, до того всё оказалось очевидным :)
Чтобы понять все "что, как и почему" и вникнуть в суть, давайте немного пройдёмся по матчасти.

ACPI
Во времена оные всё было иначе - солнышко светило ярче, трава была зеленее и забористее, деревья большие, а для управления питанием использовался APM - интерфейс Advanced Power Management. APM - не что иное, как "расширение" BIOS'а по управлению питанием. Или говоря иначе, в системах с APM было устройство /dev/apm, на котором можно было сделать ioctl(), а остальное уже выполнялось железом. APM работал не идеально, но работал. Кто-то пришёл к выводу, что это не есть хорошо и так появился ACPI - Advanced Configuration and Power Interface. В отличие от APM, ACPI возлагает часть работы на операционную систему. Поэтому, строго говоря, если с APM всё было достаточно просто, т.к. со стороны ОС требовалась лишь поддержка интерфейса APM и вся работа выполнялась уже вне ядра ОС аппаратным обеспечением, то для нормальной работы энергосбережения через ACPI необходима корректная реализация этого самого ACPI в ОС, что несколько усложняет вещи. Но с другой стороны у ACPI есть преимущество перед APM. Если APM - это функция железа, то ACPI - это функция во многом ОС. Что легче заменить в случае неправильной работы? Железо или ядро ОС? Не будем углубляться в тонкости работы обоих интерфейсов. Сосредоточимся на ACPI. Как было упомянуто, тут ядро выполняет гораздо больше работы, чем в случае с APM. В частности, это позволяет переопределить поведение системы в некоторых случаях, где это было сложно или невозможно с APM. Работа ACPI управляется во многом событиями. Допустим такой пример: пользователь нажимает кнопку "power", это генерирует прерывание, которое перехватывается встроенным контроллером. Реакцией на прерывание становится выставление флагов. Таким образом, ОС уведомляется о том, что что-то произошло. Далее, ОС смотрит, что должно произойти дальше согласно DSDT и передаёт сообщение пользовательскому процессу в виде события. В роли процесса обычно выступает демон acpid, который читает файл /proc/acpi/event. Далее, acpid выполняет какие-то предопределённые конфигурацией действия. В общих чертах схема такова.

Suspend, как он есть.
Спецификация ACPI определяет набор так называемых состояний (глобальных - Gx и сна - Sx). suspend-2-ram - одно из S-состояний системы. В случае с переходом в suspend дела могут обстоять несколько иначе, чем было описано выше. В частности, если пользователь нажимает какую-то кнопку в диалоге выхода из системы, то процесс идёт в обратном направлении. Сам по себе процесс засыпания на этот раз инициируется пользовательским процессом - всё тем же acpid, допустим. Он выполняет некие действия, предусмотренные конфигурацией. Пусть была нажата кнопка перехода в состояние suspend-2-ram, происходит приблизительно следующее:

  1. acpid поучает сообщение через unix-сокет. Как гласит файл /etc/acpi/events/sleepbtn, обработчиком события является скрипт /etc/acpi/sleep.sh:
    # /etc/acpi/events/sleepbtn
    # Called when the user presses the sleep button
    # [ Здесь-то мы и определяем обработчик события ]
    
    event=button[ /]sleep
    action=/etc/acpi/sleep.sh
  2. Далее управление передаётся скрипту /etc/acpi/sleep.sh. Мы не будем рассматривать его здесь очень детально, скажем лишь, что этот скрипт вполне может умыть руки и передать управление gnome-power-manager'у или klaptopdaemon'у например, если кто-то из них запущен и не определено форсирование выполнения обработчика ACPI - /etc/acpi/sleep.sh. В противном случае, если демоны Gnome или KDE не работают или необходимо форсировать выполнение /etc/acpi/sleep.sh, далее по очереди вызывается скрипт prepare.sh. Он подготавливает пользовательское окружение к переходу в S-состояние. Всё, что делает этот скрипт - выполняет скрипты, сложенные в директории /etc/acpi/suspend.d:
    #!/bin/bash
    
    for SCRIPT in /etc/acpi/suspend.d/*.sh; do
      . $SCRIPT
    done
  3. Далее идёт кое-какая рутина. И, наконец, самое интересное, в конце... То, чего я не заметил и то, что оказалось так очевидно. Вызывается скрипт /etc/acpi/resume.sh, который тянет в свою очередь всё, что лежит в директории /etc/acpi/resume.d. Знакомое имя? :)
  4. acpid просит ядро заснуть. И наконец, очередь доходит до ядра, которое выполняет всю чёрную работу - останавливает и замораживает процессы и переводит железо в S-состояние. Теперь мы спим.

Хотя, на самом деле, есть событие ACPI, которое генерируется при пробуждении (Wakeup) - WAK, но как мы увидели, обработчика такого события у acpid нет. Всё сделано гораздо проще. Выполнение скрипта приостанавливается как раз в той точке, где в специальный файл записывается значение, определяющее тип сна - echo -n $ACPI_SLEEP_MODE >/sys/power/state. При пробуждении нам не нужны никакие обработчики, потому что скрипт продолжает выполняться со следующей строки и вполне логично доходит до вызова скриптов, что находятся в директории /etc/acpi/resume.d. Довольно красиво и просто. Итак, мы ответили на два вопроса - "что?" и "как?". Остался ещё один - почему не работает наш скрипт, помещённый в /etc/acpi/resume.d? Нет, конечно же он работает, если запускать его вручную. Но это не наш метод. Ответ на самом деле уже был дан. Осталось придать ему более чёткую формулировку.

KDE, Gnome и HAL
HAL - Hardware Abstraction Layer - предоставляет ещё один метод абстракции от железа и управления питанием. В десктоп-менеджерах, Gnome и KDE часто работают свои менеджеры питания. У меня, в частности, работает утилитка из kde-guidance-powermanager. Она не пользуется услугами acpid, используя вместо него HAL. HAL использует и gnome-power-manager. Дальше всё просто. Можно считать, решение задачи найдено. Мы уже знаем, где находятся скрипты HAL: /usr/lib/hal/scripts/linux. Здесь нам нужен скрипт hal-system-power-suspend-linux. Выполняется он по такому же принципу, как и скрипт-обработчик ACPI - т.е., доходит до определённой точки, усыпляет систему и продолжает выполнение далее по пробуждении. Вот этим-то мы и воспользуемся, добавив в конец скрипта такие строки:

for x in /proc/acpi/ac_adapter/*; do
    grep -q off-line $x/state

    if [ $? = 0 ] && [ x$1 != xstop ]; then
        /etc/acpi/resume.d/10-dualcore-freq.sh
    fi
done
Здесь мы проверяем, работает ли ноутбук от аккумулятора. Если мы работаем от сети, то ничего делать не надо. Профиль производительности cpufreq как был performance, так и остаётся. В противном случае, если мы работаем на аккумуляторе, сбиваем частоту ядер записью профиля powersave в /sys/devices/system/cpu/*/cpufreq/scaling_governor. Выравниваем частоту обоих ядер, чтобы не ошибиться :), так как ничего страшного в этом нет и искать ядро-спринтер нет нужды. Теперь всё должно работать. Мысленно поздравьте себя ;)


При написании частично использовалась статья http://www.advogato.org/. Большое спасибо аффтару.

udev в GNU/Linux

Содержание

* 1 udev - реализация devfs на уровне пользователя
* 2 Вступление
* 3 /dev
* 4 Проблемы. Какому файлу в /dev соответствует какое устройство?
* 5 Недостаточно номеров
* 6 объём статического /dev слишком велик
* 7 devfs
* 8 Цели, стоящие перед udev
* 9 namedev
* 10 libsysfs
* 11 udev

udev - реализация devfs на уровне пользователя

Вступление

Начиная с ядра версии 2.5 все физические и виртуальные устройства в системе видны пользователю через файловую систему sysfs, которая представляет все устройства ввиде иерархической древовидной структуры. Когда в системе появляется новое устройство или удаляется старое, hotplug уведомляет об этом окружение пользователя. Используя два этих момента, можно создать пользовательскую реализацию динамической файловой системы в /dev, которая предоставляла бы более гибкие правила именования устройств, нежели это было в devfs. В этом документе обсуждается udev - программа, которая заменяет devfs без потерь для функциональности системы. Более того, udev создаёт в /dev файлы только для тех устройств, которые присутствуют на данный момент в системе. udev предоставляет и новые возможности, которых не было в devfs:

1. постоянно закреплённые за устройствами имена, которые не зависят от того, какое положение они занимают в дереве устройств.
2. уведомление внешних по отношению к ядру программ, если устройство было заменено.
3. гибкие правила именования устройств.
4. динамическое использование нижних и верхних номеров устройств.
5. переход правил именования устройств из ядра в пространство пользователя.

Этот документ объясняет, почему программа, выполняемая в режиме пользователя предпочтительнее чем devfs, которая работает в режиме ядра. Далее также будет объяснено, как работает udev, как создавать для неё расширения (разные схемы именования устройств).

/dev

На каждом Linux-box'е /dev - это директория, в которой находятся файлы устройств, известных системе. Файл устройства - это интерфейс между пользовательскими приложениями и аппаратурой, механизм, предоставляемый ядром для доступа к физическим устройствам системы. Например устройство /dev/hda, как правило, представляет первый (master) IDE-накопитель на первичном канале. Имени hda соответствуют два числа - верхний и нижний номер устройства. Эти номера используются ядром для того, чтобы узнать, к какому физическому устройству необходимо обратиться (да, имена устройств существуют лишь для отвода глаз, реально же используются номера). Обычно каждому типу устройств отводится свой диапазон номеров. На данный момент есть ряд устройств одного типа с разными именами, но одинаковыми номерами (правда, их не так уж и много). Присвоение имён и номеров разным устройствам было проведено LANANA - The Linux Assigned Names And Numbers Authority. Список всех устройств можно найти в http://www.lanana.org/docs/device-list/devices.txt или в директории с исходными кодами ядра в Documentation/devices.txt. По мере того как в ядре появляется поддержка новых устройств, им необходимо выделать новые диапазоны номеров, с тем чтобы к ним можно было получить доступ через /dev. Одна из возможностей преодолеть дефицит номеров в статической /dev - организовать её как файловую систему (devfs). В ядрах серии 2.4 и более ранних диапазоны доступных номеров были 1-255 для нижней и 1-255 для верхней части.

Проблемы. Какому файлу в /dev соответствует какое устройство?

Когда ядро обнаруживает устройство, оно присваивает ему нижний/верхний номер, соответствующий типу устройства. Допустим, при загрузке ядро обнаружило USB-принтер. Оно присвоило этому устройству номера major:180 minor:0, в /dev этому устройству будет соответствовать файл /dev/usb/lp0. Второй USB-принтер получает номера 180:1 и к нему можно обращаться как к /dev/usb/lp1. Но если пользователь изменит топологию USB-устройств, скажем добавив USB-хаб? Тогда при следующей загрузке порядок обнаружения ядром принтеров может измениться. Принтер, на который раньше ссылались как на /dev/usb/lp1 превратиться в /dev/usb/lp0 и наоборот. Это справедливо и для устройств, которые могут быть подключены или удалены во время работы компьютера. Сказанное касается шин PCI hotplug, IEEE 394, USB, CardBus. Появление sysfs в ядрах 2.5 позволяет узнать какое именно устройство данного класса "скрывается" за данным minor-номером. Для системы с двумя подключенными USB-принтерами sysfs sys/class/usb может выглядеть следующим образом

/sys/class/usb

|--  lp0
|      |--dev
|      |--device -> ../../../devices/pci0/00:09.0/usb1/1-1/1-1:0
|      +--driver -> ../../../bus/usb/drivers/usblp
+--  lp1
       |--dev
       |--device -> ../../../devices/pci0/00:0d.0/usb3/3-1/3-1:0
       +--driver -> ../../../bus/usb/drivers/usblp


$ cat /sys/class/usb/lp0/device/serial HXOLL0012202323480 $cat /sys/class/usb/lp1/device/serial W09090207101241330

Симлинки lp0/device и lp1/device ссылаются на директории, содержащие дополнительную информацию об устройствах. Таким образом можно выяснить производителя устройства и его (устройства) уникальный (надеемся) серийный номер. Как мы выяснили, принтеру lp0 соответствует S/N HXOLL0012202323480, а lp1 - W09090207101241330. Если поменять порядок подключения принтеров или кинуть их через хаб, поменяются и их имена (но не серийные номера, конечно!). Вот что у нас получится:

/sys/class/usb

|--  lp0
|      |--dev
|      |--device -> ../../../devices/pci0/00:09.0/usb1/1-1/1-1.1/1-1.1:0
|      +--driver -> ../../../bus/usb/drivers/usblp
+--  lp1
       |--dev
       |--device -> ../../../devices/pci0/00:09.0/usb1/1-1/1-1.4/1-1.4:0
       +--driver -> ../../../bus/usb/drivers/usblp


$ cat /sys/class/usb/lp0/device/serial W09090207101241330 $cat /sys/class/usb/lp1/device/serial HXOLL0012202323480

Теперь принтеру lp0 соответствует S/N W09090207101241330, а lp1 - HXOLL0012202323480. sysfs позволяет с лёгкостью узнать, какому именно устройству соответствует какой файл. Это достаточно мощный механизм сопоставления физическим устройствам файлов устройств в /dev. Однако пользователя не интересует то, что lp0 и lp1 поменялись местами и что не мешало бы внести изменения в некоторые конфигурационные файлы. Пользователь просто хочет печатать на том принтере, на котором ему нужно, не задумываясь, к какому порту подключено USB-устройство и не подключено ил оно вообще через хаб.

Недостаточно номеров

Major- и minor-части номера устройства занимают по 8 бит (т.е., как было уже упомянуто 1-255 для верхней и столько же для нижней части). На данный момент для символьных и блочных устройств осталось не так уж и много major-номеров. Символьные и блочные устройства могут использовать один и тот же major-номер, ядро относит их к разным типам, поэтому конфликтов не возникает. Казалось бы, ещё море свободных номеров. Но на некоторых системах может использоваться, скажем, 4000 дисков, а 8 бит нижней части номера для этого уже явно не достаточно. Для каждого диска ядро резервирует 16 младших номеров, т.к. на диске может быть до 16 разделов. 4000 дисков требуют 64000 (!) файлов-устройств (не хило, не так ли, господа?). Но для того чтобы получить к ним доступ, есть лищь 255 номеров. Конечно и 8 битовая схема сработала бы, если распределить все диски по всему диапазону от 1 до 65535, но ведь большинство старших номеров уже зарезервировано. Дисковая подсистема не может просто оттяпать кусок из диапазона старших номеров. Если же это произошло, необходимо уведомить окружение пользователя, что отныне часть старших номеров переназначена под использование подсистемой SCSI. Из-за этого ограничения многие настаивают на увеличении диапазонов старших и младших номеров. Не стоит забывать и о том, что 4000 дисков это ещё не предел, на некоторых системах есть необходимость подключить одновременно 10000 накопителей. Похоже, что увеличение разрядности номера устройства могло бы произойти в ядрах серии 2.6, но по прежнему проблемой остаётся уведомление пользовательского окружения, какие major- и minor-номера заняты, а какие нет. Даже если диапазон верхних половин номеров будет увеличен, необходимо определить, какому устройству соответствует какой участок этого диапазона. Это требует наличия некоего органа, который будет заниматься распределением блоков номеров и стандартизированием имён файлов-устройств. Кроме того, неисключено, что в будущем и этот ресурс будет исчерпан. Если бы ядро автоматически распределяло диапазоны старших и младших номеров между устройствами, которые представлены в данный момент в системе, необходимость в таком органе отпала бы и верхний предел номеров, вероятно, не будет превышен. Идея с динамическим распределением номеров наталкивается на одно "но": окружение пользователя по прежнему не имеет понятия, какое оборудование подключено к машине и какие номера соответствуют каким устройствам. Пока ещё лишь немногие подсистемы ядра умеют динамически распределять номера устройств. Так например с успехом поступает подсистема USB2Serial (ещё со времён ядер серии 2.2). И вновь старая проблема, неведение пользователя. Было бы бесчеловечно заставлять его просматривать логи ядра, чтобы выяснить, какому устройству соответствуют определённые номера... sysfs, появившаяся в ветке 2.5, позволяет легко выяснить то, что нам так нужно.

Объём статического /dev слишком велик

Далеко не все устройства, для которых есть файлы в /dev рально присутствуют в системе. Некоторые могут быть просто не подключены на данный момент, а некоторых в ней никогда и не было. /dev как правило генерируется при установке ОС на жёстский диск и содержит файлы для всех устройств, известных на данный момент. На машине с установленным Red Hat 9 около 18000 файлов устройств. Иногда это сбивает с толку пользователей, которые пытаются определить, какие устройства реально присутствуют в системе. Поэтому в некоторых Unix-like ОС и дистрибутивах Linux управление /dev отдаётся на откуп ядру, которое знает, какие устройства подключены и которое создаёт в памяти devfs-систему. Такая схема стала достаточно популярной.

devfs

devfs используется в некоторых Unix-like ОС. Её реализация есть и в Linux. Однако в Linux devfs по-прежнему не решает многих проблем. devfs содержит лишь те устройства, которые представлены в системе и таким образом решает проблему с диким объёмом /dev. Но имена файлов в devfs не соответствуют именам, утверждённым LANANA. По этой причине полный переход со статического /dev на динамическую devfs проблематичен. Необходимы кардинальные изменения в файлах конфигурации. Создатели devfs попытались решить эту проблему, введя дополнительный слой совместимости (devfsd-демон), чтобы эмулировать /dev. Однако при том, что совместимость достигнута, правила именования устройств находятся в ведении ядра. Первый IDE-накопитель на первичном канале будет называться /dev/hda или /dev/ide/hd/c0b0t0u0 (лично у меня - /dev/ide/host0/bus0/target0/lun0/disc) и тут ничего не сделаешь. Вообще разработчики ядра не любят выносить правила именования устройств за пределы ядра. Но правила присвоения имён должны быть убраны из ядра. Это позволит разработчикам драйверов не заострять внимание на схемах именования. Короче, ядро не должно волновать то, как пользователь называет устройство. В случае с devfs это невозможно. devfs не позволяет распределять номера устройств динамически, в ней используются номера определённые LANANA. Хотя devfs можно модифицировать, чтобы она позволяла использовать динамические номера, никто до сих пор этого не сделал. Все данные (база данных, имена) devfs хранит в памяти ядра, которая не выгружается на диск. При больших количествах подключенных устройств (те же 4000 дисков) затраты памяти становятся весьма существенными.

Цели, стоящие перед udev

В свете упомянутых проблем началась работа на проектом udev. Цели этого проекта следующие:

1 переход в пространство пользователя
2 создание динамического /dev
3 предоставление пользователю возможности, самому формировать и назначать имена файлов-устройств
4 создание пользовательского API для доступа к информации об устройствах, подключённых к системе

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

1 namedev - работа с именами устройств
2 libsysfs - библиотека доступа к информации в sysfs
3 udev - динамический аналог /dev

namedev

Ввиду необходимости разных схем именования, эта часть проекта была выделена в отдельную подсистему. Это было сделано чтобы отграничить, собственно udev от правил назначения имён и обеспечить поддержку подключаемых схем другими группами разработчиков. Подсистема именования, namedev, - это стандартный интерфейс, который udev использует, чтобы присвоить имя данному устройству. В первых релизах проекта namedev пока компонуется в один исполняемый файл с udev. Вместе с этими релизами идёт и простая схема именования, основанная на спецификации LANANA. Она скорее всего подойдёт большинству пользователей Linux. namedev позволяет присвоить устройству имя исходя из пяти признаков:

1 метка или серийный номер
2 номер шины
3 номер устройства на шине
4 другое (подстановочное) имя
5 имя устройства в ядре

Первый признак - метка или серийный номер, проверяется при подключении устройства. Этот признак интерпретируется в зависимости от типа устройства. В случае SCSI - это UUID, для USB - серийный номер, для прочих блочных устройств - метка файловой системы. Второй признак - номер шины. Номер шины на большинстве систем меняется редко. Это может произойти при апгрейде BIOS или при подключении hotplug-контроллера. В кажды момент времени номер шины постоянен и уникален. Трерий признак - топология шины. Это может оказаться полезным в случае с USB-шиной. Подстановочное имя используется вместо имени устройства в ядре. И, наконец, если ни один из 4 предыдущих признаков не прошёл, то используется имя устройства в ядре. Это имя совпадает с именем, которое использовалось бы на системе без devfs или udev со статическим /dev. Вот пример конфига, который позволяет назначить устройствам имена, отличные от тех, которые использует ядро:

   1. USB Epson printer to be called lp_epson

LABEL, BUS="usb", serial="HXOLL0012202323480", NAME="lp_epson"

   1. USB HP printer to be called lp_hp

LABEL, BUS="usb", serial=@W09090207101241330", NAME="lp_hp"

   1. sound card with PCI bus id 00:0b.0 to be the first

NUMBER, BUS="pci", id="00:0b.0", NAME="dsp"

   1. sound card with PCI bus id 00:07.1 to be the second

NUMBER, BUS="pci", id="00:0b.0", NAME="dsp1"
libsysfs

libsysfs предоставляет интерфейс для доступа к данным на sysfs. Собственно libsysfs может быть использована не только в проекте udev. Подсистема присвоения имён udev должна запрашивать из sysfs довольно много информации о подключённом устройстве. Было бы глупо дублировать код доступа к sysfs в каждом проекте, который её использует. Поэтому libsysfs была выделена в отдельную подсистему.

udev

Чтобы приводить в действие правила именования устройств udev пользутся услугами namedev и libsysfs. udev вызывается каждый раз, когда ядро обращается к hotplug. Это происходит благодаря ссылке на udev в /etc/hotplug.d/default. Ядро сообщает hotplug массу интересной информации: что за устройство было добавлено (USB, PCI или ещё что-нибудь), а может оно было удалено, а не добавлено, где в sysfs находятся данные об устройстве, вызвавшем событие. Всё это передаётся в udev, который обращается в свою очередь к namedev, чтобы выяснить, какое имя присвоить устройству или просто удалить имя отключенного устройства (если action=remove). Т.о. содержимое /dev динамически изменяется на протяжении работы системы.

По материалам Linux Symposium Версия 2. Публикуется с небольшими изменениями.

Перевод (c) by Ваш покорный слуга, red f0x

Sunday, October 28, 2007

Toshiba Satellite A100-811: Bluetooth (solved)

Привет, мир! Я долго и упорно молчал, как румынский партизан и вот, наконец нашёл о чём написать :) Долго мне не удавалось заставить полноценно работать встроенный в ноутбук Bluetooth-адаптер. В конце-концов, задача была решена. Не скажу, что решение оказалось экстраординарным, но как это часто бывает, правильные мысли появляются пост-фактум.

Как писалось ранее, в некоторых моделях Toshiba Bluetooth включается через интерфейс HCI, в некоторых - через ACPI. Но стандартные драйвера toshiba и toshiba_acpi, входящие в ядро, для меня так и не заработали. Таким образом, первая проблема, с которой я здесь столкнулся, оказалось отсутствие драйвера. После некоторых поисков я наткнулся на проект omnibook на sourceforge.net. Предполагается, что все необходимые дайверы были скомпилированы статически или в виде модулей. Конфигурация приблизительно такая:

Configuration for 2.6 kernels
  Networking --->

<*> Bluetooth subsystem support --->
--- Bluetooth subsystem support <M> L2CAP protocol support <M> SCO links support <M> RFCOMM protocol support [*] RFCOMM TTY support <M> BNEP protocol support [*] Multicast filter support [*] Protocol filter support <M> HIDP protocol support
Bluetooth device drivers ---> <M> HCI USB driver [*] SCO (voice) support <M> HCI UART driver [*] UART (H4) protocol support [*] BCSP protocol support [*] Transmit CRC with every BCSP packet <M> HCI BCM203x USB driver <M> HCI BPA10x USB driver <M> HCI BlueFRITZ! USB driver (The four drivers below are for PCMCIA Bluetooth devices and will only show up if you have also selected PCMCIA support in your kernel.) <M> HCI DTL1 (PC Card) driver <M> HCI BT3C (PC Card) driver <M> HCI BlueCard (PC Card) driver <M> HCI UART (PC Card) device driver (The driver below is intended for HCI Emulation software.) <M> HCI VHCI (Virtual HCI device) driver
(Move back three levels to Device Drives and then check if USB is enabled. This is required if you use a Bluetooth dongle, which are mostly USB based.) USB support --->
<*> Support for Host-side USB --- USB Host Controller Drivers <M> EHCI HCD (USB 2.0) support [ ] Full speed ISO transactions (EXPERIMENTAL) [ ] Root Hub Transaction Translators (EXPERIMENTAL) <*> OHCI HCD support <*> UHCI HCD (most Intel and VIA) support < > SL811HS HCD support
Правда, PCMCIA я у себя отключил.

Итак, качаем последнюю версию модуля с sourceforge.net.

$ svn co https://omnibook.svn.sourceforge.net/svnroot/omnibook/trunk
...
$ mv trunk omnibook

Собираем модуль:

# cd omnibook
# make -C /lib/modules/$(uname -r)/build modules SUBDIRS=$(pwd)
или просто
# make; make install
:-) Теперь загружаем модуль с помощью modprobe omnibook.

В моём случае, правда, модуль не загрузился, т.к. моя модель не определилась как поддерживаемая. Модель определяется по данным из пула DMI (Desktop Management Interface - часть SMBIOS, хранящая данные о конфигурации платформы). Список официально поддерживаемых моделей можно найти здесь. Но если даже Ваша модель не поддерживается официально, можно выяснить, какая из официально поддерживаемых моделей наиболее близка Вашей и загрузить модуль принудительно с параметром ectype=x (ectype - embedded controller type - тип встроенного контроллера). В моём случае ectype равен 12 (Toshiba Satellite M70). Если всё прошло удачно, в директории /proc появится поддиректория omnibook. Каждый файл в этой директории соответствует одной функции контроллера, кроме файлов version и dmi (версия драйвера и информация, полученная из пула DMI соответственно):

f0x@devel0:~$ ls -la /proc/omnibook/
total 0
dr-xr-xr-x   2 root root 0 2007-10-23 23:50 .
dr-xr-xr-x 161 root root 0 2007-09-02 11:41 ..
-rw-r--r--   1 root root 0 2007-10-23 23:50 blank
-rw-r--r--   1 root root 0 2007-10-23 23:50 bluetooth
-rw-r--r--   1 root root 0 2007-10-23 23:50 display
-r--r--r--   1 root root 0 2007-10-23 23:50 dmi
-rw-r--r--   1 root root 0 2007-10-23 23:50 lcd
-r--r--r--   1 root root 0 2007-10-23 23:50 temperature
-rw-r--r--   1 root root 0 2007-10-23 23:50 throttling
-r--r--r--   1 root root 0 2007-10-23 23:50 version
-r--r--r--   1 root root 0 2007-10-23 23:50 wifi
За описанием функций контроллера Вы можете обратиться на http://omnibook.sourceforge.net/doku.php. Нас же здесь больше всего интересует файл bluetooth. Файл содержит фразу "Bluetooth adapter is present and disabled." Т.е., по умолчанию bluetooth-адаптер отключен. Включить его можно простой записью единицы в файл:
echo 1 > /proc/omnibook/bluetooth
Для выключения, аналогично, записываем в файл "0".

Далее предполагается, что все необходимые пакеты для работы с bluetooth (bluez-* или метапакет bluetooth, к-й потянет за собой по зависимостям всё, что нужно и что ненужно тоже) установлены. Таким образом, следующий шаг - настройка всего, что нужно для работы с bluetooth. Прежде всего следует сконфигурировать hcid - демон, предоставляющий интерфейс к HCI - интерфейсу хост-контроллера. Ниже приводится моя конфигурация с комментариями:

# /etc/bluetooth/hcid.conf
#
# HCI daemon configuration file.
#

# HCId options
options {
 # Automatically initialize new devices
 autoinit yes;

 # Security Manager mode
 #   none - Security manager disabled
 #   auto - Use local PIN for incoming connections
 #   user - Always ask user for a PIN
 #
 security auto;

 # Pairing mode
 #   none  - Pairing disabled
 #   multi - Allow pairing with already paired devices
 #   once  - Pair once and deny successive attempts
 pairing multi;

 # Default PIN code for incoming connections
 passkey "1234";
}

# Default settings for HCI devices
device {
 # Local device name
 #   %d - device id
 #   %h - host name
 name "%h";

 # Local device class
 class 0x3e0100;

 # Default packet type
 pkt_type DM1,DM3,DM5,DH1,DH3,DH5,HV1,HV2,HV3;

 # Inquiry and Page scan
 iscan enable;
 pscan enable;
 discovto 0;

 # Default link mode
 #   none   - no specific policy 
 #   accept - always accept incoming connections
 #   master - become master on incoming connections,
 #            deny role switch on outgoing connections
 lm accept;

 # Default link policy
 #   none    - no specific policy
 #   rswitch - allow role switch
 #   hold    - allow hold mode
 #   sniff   - allow sniff mode
 #   park    - allow park mode
 lp rswitch,hold,sniff,park;
}
Немного по опциям. Секция options - настройки демона:
  1. autoinit - автоматически инициализировать подсоединённые устройства (bluetooth-адаптеры)
  2. security - безопасность: none - не использовать PIN-код;
    auto - использовать для доступа к устройству, подсоединяемому через bluetooth локальный PIN по умолчанию, указанный в опции passkey;
    user - запрашивать PIN-код для установления соединения у пользователя.
  3. pairing - опции соединения: multi - разрешать соединения с устройствами, которые уже соединены;
    none - запретить соединения;
    once - допускать только одно соединение с помощью данного адаптера и игнорировать все последующие попытки установить новое соединение.
  4. passkey - PIN bluetooth-хоста по умолчанию. Он будет использоваться при добавлении Вашего компьютера другими bluetooth-устройствами в их списки устройств. Используйте любое число, которое Вам нравится.
Секция device - свойства контроллера:
  1. name - имя Вашего bluetooth-устройства (хоста). В моём случае bluetooth-хосту присваивается то же имя, что и имя Unix-хоста.
  2. class - класс устройства. Шестнадцатеричное число, определяющее класс bluetooth-устройства (хоста). С помощью битовой маски устанавливается тип хоста. За описанием битов обращайтесь к man hcid.conf или спецификации bluetooth. В моём случае средний байт 01 в 0x3e0100 обозначает, что мой хост - это компьютер, а не телефон, принтер или HID-устройство :-)
  3. pkt_type - типы пакетов. За разъяснениями также советую обращаться к спецификации Bluetooth или просто оставьте, как есть.
  4. iscan - разрешить другим bluetooth-устройствам обнаруживать Ваш хост при сканировании.
  5. pscan - разрешить другим устройствам устанавливать соединения с Вашим хостом, используя канал "page channel".
  6. discovto - время, на протяжении которого Ваш хост будет позволять обнаружить себя другому устройству. Устанавливается в секундах. 0 - устройство может быть обнаружено в любое время.
  7. lm - режим установки соединения: none - не определено;
    accept - всегда принимать входящие запросы на соединение;
    master - принимать входящие соединения в роли ведущего и запретить изменение этой роли при исходящих соединениях.
  8. lp - политика при установке соединения (касается пикосетей): none - не определено;
    rswitch - разрешить изменять роль (ведущий-ведомый);
    hold - эта политика необходима для синхронного обмена данными (нужно для беспроводной гарнитуры);
    sniff - позволить Вашему хосту пребывать в пикосети в определённые временные слоты. Когда устройство "отсутствует", оно может делать что-то другое, например, сканировать эфир, искать другие устройства;
    park - разрешить парковку хоста, т.е. позволять хосту переходить в энергосберегающий режим (сон). Устройство, входящее в пикосеть может "простаивать".
В принципе, то что здесь написано - это мой перевод информации из man hcid.conf, здесь я не буду разъяснять все принципы работы bluetooth-устройств и сетей, в которые такие устройства могут объединяться. Всем любопытным советую обратиться к первоисточнику - спецификации по Bluetooth.

Далее, нам необходимо отредактировать файл настройки подсистемы bluetooth - rfcomm для нашего хост-контроллера. rfcomm - Radio Frequency Communication - радиочастотная связь, это подсистема, которая эмулирует последовательную линию RS232 поверх протокола L2CAP. В принципе, настройка подсистемы rfcomm опциональна. Но для устройства, которое Вы хотите использовать, как модем, для сотового телефона, настроить её будет желательно, чтобы не делать этого каждый раз вручную. Ниже приведён пример конфигурационного файла:

# /etc/bluetooth/rfcomm.conf
#
# RFCOMM configuration file.
#

rfcomm0 {
# # Automatically bind the device at startup
 bind yes;
#
# # Bluetooth address of the device
 device 00:0A:0B:0C:0D:0E;
#
# # RFCOMM channel for the connection
# channel 1;
#
# # Description of the connection
 comment "SonyEricsson T630";
}
Пояснения:
  1. rfcomm0 - первый последовательный интерфейс. Вообще-то, Вы можете сконфигурировать их столько, сколько захотите. Конфигурация каждого последовательного интерфейса начинается с его имени (rfcomm0, rfcomm1, rfcomm2, ...). Сами параметры интерфейса указываются в блоке между двумя фигурными скобками { и };
  2. bind - автоматически привязывать сокет для нашего устройства при его обнаружении (yes или no, в случае, если Вы не хотите, чтобы сокет связывался автоматически;
  3. device - MAC-адрес bluetooth-устройства (сотового телефона, например);
  4. channel - канал bluetooth, который будет использоваться для связи с устройством;
  5. comment - опциональный комментарий к интерфейсу.
При указании МАС-адреса Вашего устройства, его сначала надо узнать. Для этого поднимем устройство hci и просканируем эфир:
# hciconfig hci0 up
# hciconfig piscan
# hcitool scan
Scanning ...
        00:0E:0F:10:11:12       Red Quick Fox
Здесь мы просто запускаем устройство hci0 с помощью утилиты hciconfig и сканируем эфир с помощью hcitool с параметром scan. 00:0E:0F:10:11:12 - это и есть нужный нам MAC-адрес. Утилита hciconfig предназначена для конфигурирования хост-контроллера hci. Если контроллеров несколько, то они имеют имена hci0, hci1 и т.д.. hcitool позволяет совершать некие вменяемые действия с помощью нашего хост-контроллера. Иными словами, она позволяет им управлять. Для списка агрументов обеих утилит см. man hciconfig и man hcitool. Используя утилиту l2ping и МАС-адрес в качестве её аргумента, можно послать пинга телефону :-)

Следующая стадия конфигурирования bluetooth - редактирование ещё одного файла. В debian-based дистрибутивах linux это /etc/default/bluetooth. По существу, этот файл нужен только стартовому скрипту bluetooth из /etc/init.d. Сложного там ничего нет. Всё, что от Вас требуется, указать, должны ли сервисы bluetooth стартовать при загрузке системы и если да, то какие. Приведём пример файла /etc/default/bluetooth:

# Defaults for bluez-utils

# This file supersedes /etc/default/bluez-pan.  If
# that exists on your system, you should use this
# file instead and remove the old one.  Until you
# do so, the contents of this file will be ignored.

# start bluetooth on boot?
# compatibility note: If this variable is not found bluetooth will
# start
BLUETOOTH_ENABLED=1

############ DUND
#
# Run dund -- this allows ppp logins. 1 for enabled, 0 for disabled.
DUND_ENABLED=1

# Arguments to dund: defaults to acting as a server
DUND_OPTIONS="--listen --persist"

# Run dund --help to see the full array of options.
# Here are some examples:
#
# Connect to any nearby host offering access
# DUND_OPTIONS="--search"
#
# Connect to host 00:11:22:33:44:55
# DUND_OPTIONS="--connect 00:11:22:33:44:55"
#
# Listen on channel 3
# DUND_OPTIONS="--listen --channel 3"

# Special consideration is needed for certain devices. Microsoft
# users see the --msdun option.  Ericsson P800 users will need to
# listen on channel 3 and also run 'sdptool add --channel=3 SP'

############ SDPTOOL
# this variable controls the options passed to sdptool on boot, useful if you
# need to setup sdpd on boot.
# options are ;-separated, i.e. for every ; an sdptool instance will be
# launched
#
# examples:
# SDPTOOL_OPTIONS="add --channel=3 SP" # ericsson P800 serial profile
# SDPTOOL_OPTIONS="add --channel=8 OPUSH ; add --channel=9 FTRN" # motorola
#                                             # object push and file transfer
SDPTOOL_OPTIONS=""

Здесь переменная BLUETOOTH_ENABLED=1 определяет, что при загрузке системы должны стартовать и сервисы bluetooth. Если переменная установлена в 0, поддержка bluetooth не включается. Если переменная не найдена, то по умолчанию сервисы загружаются. DUND_ENABLED=1 определяет, что должен быть запущен демон Dial-up-доступа. Ну, вот практически и всё. В переменной SDPTOOL_OPTIONS определяется, какие сервисы должен предоставлять наш компьютер другим bluetooth-устройствам. Список сервисов можно пополнить и вне этого файла с помощью sdptool (см. man sdptool). В общем и в целом Unix-box теперь готов к работе с bluetooth-устройствами. Можете по bluetooth зайти на сотовый телефон, настроить пикосеть, работающую через BNEP или ещё что-нибудь. Про HID я не писал, ибо не имею таковых девайсов. Про настройку пикосети на bluetooth Вы сможете узнать сами.

Что касаемо телефонов, было замечено одно занятное обстоятельство: PIN, указанный в конфигурационном файле hcid.conf внаглую игнорируется с новыми телефонами (т.е., с теми, с которыми мы раньше не работали). В качестве противоядия можно использовать следующее: создать маленький скрипт, который просто выводит с помощью echo PIN-код - echo "PIN:XXXX", где ХХХХ - Ваш PIN. Допустим, этот скрипт будет называться helper. Далее, можно запустить passkey-agent --default ./helper. По идее, агент должен запускаться автоматически для каждой пользовательской сессии, но как показывает опыт, он этого почему-то не делает. Можно самостоятельно запихать passkey-agent в какой-нибудь скрипт. Ну, вот, собственно, и всё. Чтобы использовать сотовый телефон для Dial-up доступа в интернет в качестве GPRS-модема, укажите в качестве устройства модема /dev/rfcomm0 в конфиге Вашей звонилки, настройте доступ согласно информации Вашего оператора. А дальше - всё как всегда.

Tuesday, August 28, 2007

Селим OpenSolaris рядом с Linux: загрузка одним bootloader'ом или история одной глупости

Уже довольно давно, может с месяца с 4, у меня валялся набор из 2 дисков OpenSolaris 10 Starter Kit. Так как солярой я интересовался и раньше и эпизодически ею пользовался (Sun Solaris 9 x86), то решил наконец попробовать и этот продукт. На диске есть 3 дистрибутива в редакции community: Nexenta, Schilix и Belenix. Опробовав последовательно все 3 образа (они загружаются с DVD-диска как Live без необходимоси установки), я остановил свой выбор на последнем, как на наиболее соответствующем моим требованиям дистре (gcc в комплекте и всё необходимое для разработки - библиотеки, плюс, конечно, zfs, Solaris zones, DTrace и прочее).

Кратко о самом дистре Belenix: довольно симпатичный дистр, в качестве рабочей графической среды предоставляется на выбор KDE 3.5 или Xfce, коим я и пользуюсь. По умолчанию графика не запускается. В смысле, она запускается на полуавтомате при загрузке LiveCD, но не в уже установленном солярисе, где по умолчанию пускать Xfce нужно явно, руками (хотя, никто не мешает Вам настроить этот момент под Ваши нужды). Порадовало, что без проблем определяются интегрированное аудио и eth-адаптер на nForce430, с дисковым контроллером проблем также никаких (у меня 2 SATA-II винчестера). В отличие от Nexenta здесь ничего не валится в коры буквально на ровном месте. Установка проходит без проблем и в принципе, процедура довольна тривиальна, поэтому на ней я останавливаься не буду. Единственное, что хотелось бы отметить, что инсталлятор как был во всех версиях Solaris, что мне попадались - текстовый, таковым остался и здесь. Однако, я не считаю это недостатком. Но вместе с тем, опций здесь лишь самый минимум - думаю, можно было бы сделать и побольше, хотя бы на предмет того, какой софт нужно ставить, а какой нет.

Итак, я выкроил свободное место на втором винчестере и поставил OS туда. Непредвиденные трудности начались, когда я попытался при перезагрузке получить мой свежеустановленный Belenix. Суть проблемы в следующем: до этого у меня уже был установлен Debian GNU/Linux 'Etch' и всё, что обреталось у меня на компьютере, грузилось его grub'ом. Честно говоря, мне не было особого резона использовать в качестве общесистемного загрузчика Solaris'овский grub, потому как всё же большую часть времени я провожу в Linux и такая схема была бы не очень удобна. Но Linux'овый grub понятия не имеет о zfs или ufs2 (хотя, последнее по идее должен уметь). В конце концов я уже готов был пойти на крайность и сделать общесистемным загрузчиком Solaris'овский grub. Перезагрузившись с Belenix LiveCD, вызвал installgrub со следующими параметрами:

# grubinstall -m /mnt/solaris0/boot/grub/stage1 /mnt/solaris0/boot/grub/stage2 /dev/rdsk/c2d0s0
В общем, здесь всё довольно self-explanatory: указываем файл для первой стадии загрузки grub (сектор, внедряемый в MBR), образ для второй стадии и слайс, на котором стоит Solaris (честно говоря, я до сих пор путаюсь в схемах BSD и Solaris, но если я не ошибаюсь, то это должен бы быть лейбл, а не слайс, ведь слайс - это раздел в терминологии DOS, однако по имени видно, что у нас контроллер 2, первый диск на 2-м контроллере и 1-й слайс на этом диске - так или иначе, но эта схема совершенно отлична от той, что используется в Linux и не совсем похожа на Free- или NetBSD'шную. ИМХО в FreeBSD имена устройств выглядят логичнее и нагляднее, чем в Solaris). Ключ -m здесь просто добавляет интерактивности. Если Вы не накачались пивом и прекрасно понимаете, что Вы собираетесь сделать, то им можно и пренебречь :) /mnt/solaris0 - подмонтированная ФС с уже установленной на диск солярой - мы будем брать необходимые файлы с этой ФС. Так вот, каково было моё разочарование, когда я обнаружил, что первый загрузочный сектор оказался не в MBR первого винчестера, как я ожидал, а в mboot'e второго. Указать installgrub'у явным образом, куда необходимо воткнуть загрузочный сектор не представлялось возможным.

В общем, ситуация вышла достаточно комичной. Linux'овый grub в упор не воспринимал Solaris'овскую ufs2 и о том, чтобы он мог грузить соляровое ядро непосредственно не могло быть и речи, а с другой стороны соляровый загрузчки упорно становился в mboot второго диска и, следовательно, пролетал при загрузке.

Решение было ясно как день и простое, как карандаш. Честно говоря, если бы не лень, можно было бы не морочить себе голову. Дело в том, что я пытался редактировать меню загрузки на лету, добавляя
rootnoverify (hd1)
chainloader +1
Но по не совсем понятной мне причине Linux'овый grub ругался кодом ошибки 27 и как резанный кричал, что не знает такой команды. Редактирование menu.lst для Linux'ового grub'а расставило всё по своим местам (хотя и странно, почему такого результата не удавалось добиться редактированием параметров загрузки на лету - ограничение внедряемого кода grub-шелла для экономии, чтобы по возможности сократить число секторов, которые необходимо внедрить?). Теперь и солярис и линукс грузятся одним загрузчиком - Linux'овым, с той поправкой, что солярис грузится не непосредственно, а через встроенный в цепь собственный загрузчик, который живёт в MBR второго диска.

Результат: в MBR 1-го диска установлен Linux grub, а в файл menu.lst для загрузки зацеплённого солярового grub'a, что установлен нами ранее в MBR 2-го винчестера installgrub'ом, были добавлены строки:

title  Belenix 32-bit
rootnoverify (hd1)  # Используем в качестве корня MBR 2-го диска (hd1), и не пытаемся ничего монтировать - не получится, т.к. там просто нет ФС и нечего монтировать :)
chainloader +1  # Встраиваем в цепь вторичный бутлоадер, на к-й ссылается MBR 2-го диска
Есть ещё одно обстоятельство. Файл menu.lst для солярового grub'a также нуждается в небольшой правке, которую лучше сделать именно в файле, чем каждый раз корректировать на лету команды загрузки. А именно, по непонятному недоразумению, которое уже описывалось ранее, соляровый инсталлятор grub'a, а вместе с ними вдогонку и сам grub почему-то считают, что они установлены на 1-м диске, а не на 2-м, как у меня обстоит дело в реальности. В результате, в /boot/grub/menu.lst для соляриса имеем такие сроки:
title  Belenix (32Bit, ACPI)
  root (hd0,3,a) # Ошибка: Корневая ФС, на которой находятся файлы образа ядра и загрузочный архив находится на втором диске, а не на первом (первое число должно быть 1, а не 0)!
  # Далее - всё в полном порядке :)
  kernel /platform/i86pc/multiboot kernel/unix
  module /platform/i86pc/boot_archive
Строка root нуждается в следующей правке: 0 необходимо заменить на 1 для второго диска, а в остальном всё правильно в моём случае - слайс 3, лейбл - а. С учётом сказанного, она должна выглядеть так: root (hd1,3,a). Хотя, если у Вас OS установлен на 1-м винчестере, то эта правка Вам не нужна.

Вместо Afterword: как и в Linux'e в OpenSolaris есть определённые ограничения для корневой ФС или, по крайней мере, /boot. Так, в Linux нет возможности внедрить секторы со второй стадией загрузки grub в раздел, который отформатирован в xfs (просто в xfs исторически не предусмотрена такая возможность, т.к. эта ФС была разработана для SGI IRIX и изначально к Linux никакого отношения не имела. Итог состоит в том, что в голове xfs-раздела нет необходимых 64 Кб для внедрения grub-секторов. Поэтому или всю корневую ФС или, по крайней мере, /boot нужно держать на ФС, более традиционной для Linux - ReiserFS, Ext2/3 и т.п.) Аналогично, grub на самом деле не имеет понятия о пулах zfs (на самом деле, если не ошибаюсь), но тот, что имеется в OS прекрасно работает по крайней мере с ufs2. Поэтому, выход здесь, как и в случае с Linux - держать корневую ФС на ufs2, а уже остальное, типа /home - в пуле на zfs.

PS: небольшая попроавка к примеру с xfs и grub. Честно говоря, я не уверен, что информация актуальна, но насколько можно судить сейчас по некоторым данным grub прекрасно работает и с корневой ФС на xfs. Но так как я не уверен, то текст оставил без исправлений. Если я всё-таки ошибся, то извините за неточность - мы все учимся, и не редко на ошибках и с их же помощью :)

Saturday, June 9, 2007

perl-proxy: Немного юмора

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

#!/usr/bin/perl -Tw

use strict;
$ENV{PATH} = join ":", qw(/usr/ucb /bin /usr/bin);
$|++;

my $VERSION_ID = q$Id: proxy,v 1.21 1998/xx/xx xx:xx:xx merlyn Exp $;
my $VERSION = (qw$Revision: 1.21 $ )[-1];

## Copyright (c) 1996, 1998 by Randal L. Schwartz
## This program is free software; you can redistribute it
## and/or modify it under the same terms as Perl itself.

### debug management
sub prefix {
   my $now = localtime;
   join "", map { "[$now] [${$}] $_\n" } split /\n/, join "", @_;
}

$SIG{__WARN__} = sub { warn prefix @_ };
$SIG{__DIE__} = sub { die prefix @_ };

&setup_signals();

### logging flags
my $LOG_PROC = 1;               # begin/end of processes
my $LOG_TRAN = 1;               # begin/end of each transaction
my $LOG_REQ_HEAD = 0;           # detailed header of each request
my $LOG_REQ_BODY = 0;           # header and body of each request
my $LOG_RES_HEAD = 0;           # detailed header of each response
my $LOG_RES_BODY = 0;           # header and body of each response

### configuration
my $HOST = 'WWW.XXX.YYY.ZZZ';
my $PORT = 3128;                   # pick next available user-port
my $SLAVE_COUNT = 8;            # how many slaves to fork
my $MAX_PER_SLAVE = 20;         # how many transactions per slave

### main
warn("running version ", $VERSION);

&main();
exit 0;

### subs
sub main {                      # return void
   use HTTP::Daemon;
   my %kids;

   my $master = HTTP::Daemon->new(LocalPort => $PORT, LocalAddr => $HOST) or die "Cannot create master: $!";
   warn("master is ", $master->url);
   ## fork the right number of children
   for (1..$SLAVE_COUNT) {
      $kids{&fork_a_slave($master)} = "slave";
   }
   {                             # forever:
      my $pid = wait;
      my $was = delete ($kids{$pid}) || "?unknown?";
      warn("child $pid ($was) terminated status $?") if $LOG_PROC;
      if ($was eq "slave") {      # oops, lost a slave
         sleep 1;                  # don't replace it right away
         #(avoid thrash)
         $kids{&fork_a_slave($master)} = "slave";
      }
   } continue { redo };          # semicolon for cperl-mode
}

sub setup_signals {             # return void
   setpgrp;                      # I *am* the leader
   $SIG{HUP} = $SIG{INT} = $SIG{TERM} = sub {
      my $sig = shift;
      $SIG{$sig} = 'IGNORE';
      kill $sig, 0;               # death to all-comers
      die "killed by $sig";
   };
}

sub fork_a_slave {              # return int (pid)
   my $master = shift;           # HTTP::Daemon

   my $pid;
   defined ($pid = fork) or die "Cannot fork: $!";
   &child_does($master) unless $pid;
   $pid;
}

sub child_does {                # return void
   my $master = shift;           # HTTP::Daemon
   my $did = 0;                  # processed count

   warn("child started") if $LOG_PROC;
   {
      flock($master, 2);          # LOCK_EX
      warn("child has lock") if $LOG_TRAN;
      my $slave = $master->accept or die "accept: $!";
      warn("child releasing lock") if $LOG_TRAN;
      flock($master, 8);          # LOCK_UN
      my @start_times = (times, time);
      $slave->autoflush(1);
      warn("connect from ", $slave->peerhost) if $LOG_TRAN;
      &handle_one_connection($slave); # closes $slave at right time
      if ($LOG_TRAN) {
         my @finish_times = (times, time);
         for (@finish_times) {
            $_ -= shift @start_times; # crude, but effective
         }
         warn(sprintf "times: %.2f %.2f %.2f %.2f %d\n", @finish_times);
      }
   } continue { redo if ++$did < $MAX_PER_SLAVE };
   warn("child terminating") if $LOG_PROC;
   exit 0;
}

sub handle_one_connection {     # return void
   use HTTP::Request;
   my $handle = shift;           # HTTP::Daemon::ClientConn

   my $request = $handle->get_request;
   print $request;
   defined($request) or die "bad request"; # XXX

   my $response = &fetch_request($request);
   warn("response: <<<\n", $response->headers_as_string, "\n>>>")
   if $LOG_RES_HEAD and not $LOG_RES_BODY;
   warn("response: <<<\n", $response->as_string, "\n>>>")
   if $LOG_RES_BODY;
   $handle->send_response($response);
   close $handle;
}

sub fetch_request {             # return HTTP::Response
   use HTTP::Response;
   use URI::URL;
   my $request = shift;          # HTTP::Request

   ## XXX
   print "Request was: " . $request->as_string;
   ## XXXX needs policy here
   my $url = URI::URL->new($request->url);

   if ($url->scheme !~ /^(https?|gopher|ftp)$/) {
      my $res = HTTP::Response->new(403, "Forbidden");
      $res->content("bad scheme: @{[$url->scheme]}\n");
      $res;
   } elsif (not $url->rel->netloc) {
      my $res = HTTP::Response->new(403, "Forbidden");
      $res->content("relative URL not permitted\n");
      $res;
   } else {
      ## validated request, get it!
      warn("processing url is $url") if $LOG_TRAN;
      &fetch_validated_request($request);
   }
}

BEGIN {                         # local static block
   my $agent;                    # LWP::UserAgent

sub fetch_validated_request {         # return HTTP::Response
   my $request = shift;         # HTTP::Request

   $agent ||= do {
      use LWP::UserAgent;
      my $agent = LWP::UserAgent->new;
      $agent->agent("proxy/$VERSION " . $agent->agent);
      $agent->env_proxy;
      $agent;
   };

   warn("fetch: <<<\n", $request->headers_as_string, "\n>>>")
   if $LOG_REQ_HEAD and not $LOG_REQ_BODY;
   warn("fetch: <<<\n", $request->as_string, "\n>>>")
   if $LOG_REQ_BODY;

   my $response = $agent->simple_request($request);

   if ($response->is_success and $response->content_type =~ /text\/(plain|html)/ and not ($response->content_encoding || "") =~ /\S/ and ($request->header("accept-encoding") || "") =~ /gzip/) {
      require Compress::Zlib;
      my $content = $response->content;
      my $new_content = Compress::Zlib::memGzip($content);
      if (defined $new_content) {
         $response->content($new_content);
         $response->content_length(length $new_content);
         $response->content_encoding("gzip");
         warn("gzipping content from " . (length $content) . " to " . (length $new_content)) if $LOG_TRAN;
      }
   }

   $response;
}
}

Немного разоблачений:

  1. Код не мой, как видно по копирайту - я лишь подправил кое-что в области URI::URL, чтоб оно работало и добавил print $request->as_string для отладки.
  2. Переменную $HOST устанавливаем в адрес хоста, на котором крутится прокси, а $PORT присваиваем номер порта, который слушает наш прокси.
  3. Настраиваем клиент на использование прокси - указываем адрес хоста и порт $HOST:$PORT
  4. Скрипт при запуске плодит pre-forked потомков, которые слушают порт $PORT
  5. При поступлении запроса на соединение на указанный порт с помощью манипуляций объектами HTTP::Request, HTTP::Response и LWP::UserAgent перебрасываем запрос на сервер-рецепиент.

Вот так вкратце. А впрочем, из кода довольно прозрачно всё видно. Всё гениальное просто :) Можно запустить эту игрушку и шарить т.о. одно dial-up соединение для доступа к web (чем я сейчас и занимаюсь), не трогая NAT.

Saturday, June 2, 2007

Toshiba Satellite A100-811: Ubuntu & power management

Странное дело, но после апгрейда до KUbuntu 7.04 Feisty Fawn на ноутбуке сломался hibernate suspend-to-disk. Правка /etc/hibernate/common.conf ничего не давала. Процесс сброса образа RAM на диск, кажется, шёл вполне нормально, но после возобновления (resume) X оказывался запорот. Система не висит - в неё даже можно залогиниться по ssh, но работа непосредственно на ноутбуке невозможна. Итак, немного подумав, я открыл для себя uswsusp и входящий сюда s2disk. Однако, и тут не всё было гладко. Одно время s2disk работал для меня вполне прекрасно, даже с некоторыми особенностями. Так, например, после слива образа на диск и закрытия крышки лаптопа при её отркывании система включалась сама, без нажатия кнопки power и linux возобновлялся. Но, позже появилась проблема. При открытии крышки ноутбук включался, появлялся сплэш и... всё. Сплэш замерзал на одном месте и система не возобновлялась. Немного подумав, я полез шерстить по конфигам. Итак, найдя файл /etc/uswsusp.conf, я сделал такую правку:

# /etc/uswsusp.conf(8) -- Configuration file for s2disk/s2both 
resume device = /dev/sda5
compress = y
early writeout = y
image size = 512000000
compute checksum = y
RSA key file = /etc/uswsusp.key
shutdown method = platform
splash = y
Поясню: до этого параметр splash в конфиге не был определён вообще, а image size был равен 236Мб с копейками. Как видно, я явно установил splash = y, для фидбэка в процессе слива образа RAM на диск и в процессе его считывания с диска, а image size в пол-гигабайта (объём RAM на моём ноутбуке - 512Мб). Как ни странно, но после этого всё заработало. Скажу честно, я исходил из ни на чём не обоснованного предположения, что всё дело именно в том, что на диск сливается не весь образ памяти. Это было довольно интуитивное решение. Экспериментировать со значением 0 я не стал, но в man uswsusp.conf написано, что при нулевом значении s2disk будет пытаться сделать образ как можно меньше, сохраняя только самый необходимый минимум.

Теперь s2disk работал. И работал прекрасно. Единственное, чего мне ещё не хватало, это чтобы он срабатывал автоматически при критическом уровне батареи (такая настройка у меня была и ранее - в целях предотвращения потерь данных. Вполне вероятна такая ситуация, когда Вы отойдёте, задержитесь где-то, а тем временем аккумулятор благополучно иссякнет и...). К сожалению, отрабатывало оно так же, как и усыпление через диалог KDE "Завершить сеанс" -> "Спящий режим", или точнее сказать, не работало вовсе. Linux просто гасил экран, блокировал сеанс KDE, а засыпать даже не думал. Я пользуюсь KLaptop. Так сложилось традиционно, да и просто, он субъективно нравится мне больше всяких там KPowersave. Там вполне можно было прописать, какую команду следует выполнить при критически малом уровне аккумулятора, но мне хотелось универсального решения, ведь для кнопки в KDE'шном завершении сеанса такой параметр не прописан. Механизм работы здесь однако был один. Это я знал по опыту - по одинаковому поведению при попытке усыпить ноутбук через KLaptop и "Завершение сеанса". Немного пошарив по google я нашёл решение. Оказалось, что hibernation здесь срабатывает через hal. Для того, чтобы исправить ситуацию, необходимо было заменить модуль hal, который отвечал за усыпление по требованию клиента. Этот модуль представляет собой нечто иное, как скрипт, лежащий в /usr/lib/halscripts/linux - hal-system-power-hibernate-linux. Моей целью было заставить этот скрипт отрабатывать через s2disk. Первоначально скрипт выглядёл следующим образом:

#!/bin/sh

POWERSAVED_SUSPEND2DISK="dbus-send --system --dest=com.novell.powersave \
                         --print-reply /com/novell/powersave \
                         com.novell.powersave.action.SuspendToDisk"

unsupported() {
 echo org.freedesktop.Hal.Device.SystemPowerManagement.NotSupported >&2
 echo No hibernate script found >&2
 exit 1
}

#SuSE and ALTLinux only support powersave
if [ -f /etc/altlinux-release ] || [ -f "/etc/SuSE-release" ] ; then
 if [ -x /usr/bin/powersave ] ; then
         $POWERSAVED_SUSPEND2DISK
  RET=$?
 else
  unsupported
 fi

#Mandriva support suspend-scripts 
elif [ -f /etc/mandriva-release ] ; then 
    if [ -x /usr/sbin/pmsuspend ] ; then 
 /usr/sbin/pmsuspend disk 
 RET=$? 
    else 
 unsupported 
    fi 

#RedHat/Fedora only support pm-utils
elif [ -f /etc/redhat-release ] || [ -f /etc/fedora-release ] ; then
 if [ -x /usr/sbin/pm-hibernate ] ; then
  /usr/sbin/pm-hibernate
  RET=$?
 else
  unsupported
 fi

#Other distros just need to have *any* tools installed
else
 if [ -x "/usr/bin/powersave" ] ; then
         $POWERSAVED_SUSPEND2DISK
  RET=$?
 elif [ -x "/usr/sbin/pmi" ] ; then
  /usr/sbin/pmi action hibernate force
  RET=$?
 elif [ -x "/usr/sbin/pm-hibernate" ] ; then
  /usr/sbin/pm-hibernate
  RET=$?
 elif [ -x "/usr/sbin/hibernate" ] ; then
  # Suspend2 tools installed
  /usr/sbin/hibernate --force
  RET=$?
 elif [ -x "/sbin/s2disk" ] ; then
  # uswsusp tools installed
  /sbin/s2disk
  RET=$?
 elif [ -x "/etc/acpi/hibernate.sh" ] ; then
  # acpi-support installed
  /etc/acpi/hibernate.sh force
  RET=$?
 elif [ -w "/sys/power/state" ] ; then
  # Use the raw kernel sysfs interface
  echo "disk" > /sys/power/state
  RET=$?
 else
  unsupported
  fi
 fi

#Refresh devices as a resume can do funny things
for type in button battery ac_adapter
do
 devices=`hal-find-by-capability --capability $type`
 for device in $devices
 do
  dbus-send --system --print-reply --dest=org.freedesktop.Hal \
     $device org.freedesktop.Hal.Device.Rescan
 done
done

exit $RET

Из приведённого листинга видно, что сперва скрипт пытается определить дистрибутив, а далее действует исходя из реалий данного дистрибутива. В моём случае hal пытался отработать через pmi - абстактный интерфейс управления энергосбережением. Мне же нужно было, чтобы первым и последним срабатывал s2disk. После исправления скрипт обрёл такой вид:

#!/bin/sh

[ SKIPPED ]

#Other distros just need to have *any* tools installed
else
 if [ -x "/sbin/s2disk" ] ; then
  # uswsusp tools installed
  /sbin/s2disk
  RET=$?
 elif [ -x "/usr/sbin/pmi" ] ; then
  /usr/sbin/pmi action hibernate force
  RET=$?
 elif [ -x "/usr/bin/powersave" ] ; then
         $POWERSAVED_SUSPEND2DISK
  RET=$?
 elif [ -x "/usr/sbin/pm-hibernate" ] ; then
  /usr/sbin/pm-hibernate
  RET=$?
 elif [ -x "/usr/sbin/hibernate" ] ; then
  # Suspend2 tools installed
  /usr/sbin/hibernate --force
  RET=$?
 elif [ -x "/etc/acpi/hibernate.sh" ] ; then
  # acpi-support installed
  /etc/acpi/hibernate.sh force
  RET=$?
 elif [ -w "/sys/power/state" ] ; then
  # Use the raw kernel sysfs interface
  echo "disk" > /sys/power/state
  RET=$?
 else
  unsupported
 fi
fi

[ SKIPPED ]
После этого и KLaptop и "Завершение сеанса" работают безупречно.

Кстати, режим suspend-to-ram у меня работал хорошо из коробки (после апгрейда до Feisty Fawn). s2ram напротив работать не хочет, ссылаясь на то, что модель ноута ему не знакома. Поэтому мои настройки энергосбережения закончились доведением до ума режима suspend-to-disk a.k.a. hibernation.

"Вот так мы победили сырость!" (с) Однако, есть ещё над чем поработать. Так, например, мне до сих пор не удалось заставить работать русский в консоли. Самое странное, что на десктопе с установленным Debian 4.0 Etch всё прекрасно работает (console-cyrillic не установлен). При такой же конфигурации на лаптопе оно не работает. Странность в том, что из коробки путём лишь небольших манипуляций после замены дистрибутивного ядра на собственное русский текст в UTF8 отображается в консоли без проблем, но нет возможности набирать русский текст. Вместо него не набирается вообще ничего. Одним словом, проблема всё ещё требует решения. В Edgy Eft в этом плане всё было отлично. Примечательно, что такие проблемы после перехода на Feisty Fawn наблюдаются не только у меня.

Вторая проблема более странная. С ней я пока не разбирался вплотную. Я наконец решил заставить работать встроенный модем. Поставил sl-modem-daemon, sl-modem-source для компиляции модулей под моё кастом-билт ядро. Но тут я столкнулся с тем, что sl-modem-source на уровне исходного кода не совместим с новыми ядрами. К примеру, в коде kernel-ver.c делается попытка подключить заголовок linux/version.h чтобы использовать макрос UTS_RELEASE, который в 2.6.20 (и не только в нём) уже определяется в другом файле - linux/utsrelease.h. Можно, конечно, и вручную подправить сырцы драйвера... Кстати, смешной момент, связанный с установкой драйвера. Если сказать apt-get install sl-modem-source, то в Вашем /usr/src появится тарбол sl-modem.tar.bz2 - это и есть сырцы драйвера. Далее, пытаемся ставить его с помощью module-assistant (хех, Debian-way): module-assistant prepare sl-modem; module-assistant install sl-modem и тут происходит ошибка. По вышеупомянутой причине. Правим сырцы, устраняем явные противоречия и снова пускаем m-a install sl-modem и... снова ошибка! В том же месте! :) Лезем в исходники и видим, наших изменений там нет! Так что, мораль в том, что если Вы собираетесь править сырцы sl-modem вручную, то Вам нужно будет из подправленных сырцов создать такой же тарбол с тем же именем, что и оригинал и заменить оригинал. Правда, я пошёл по другому пути - просто выкачал sl-modem-source из пула Debian Sid. Всё собралось без сучка и задоринки, но slmodemd всё равно не видит модули, хотя они и установлены в /lib/modules/`uname -r`/misc. Говорит: FATAL: Module slamr not found. Странно, короче. Можно было бы обойтись без модулей, используя alsa, но это, ИМХО - крайний случай.

Sunday, May 20, 2007

Toshiba Satellite A100-811: Ubuntu

Вот уже что-то около полтора месяца, как мой Toshiba Satellite A100-811 работает под Ubuntu Linux 6.10 (Edgy Eft). Всё началось с того, что мне надоело тянуть бэкпорты для Sarge и собирать софт, которого не было ни в репозитории, ни на бэкпортах вручную. Тем более, некоторые вещи, как например xorg собирать и ставить вручную было и вовсе неохота (заметный framedrop в mplayer'е, работающем через vesa, тоже порядком надоел). Нового Etch тогда под рукой у меня ещё не было, зато пришёл почтой заказанный на ubuntu.com Edgy Eft. Итак, решив, что в конце концов пролетариату нечего терять кроме своих цепей, я потёр систему, оставив снапшот /etc и домашний директорий. Надо сказать, что инсталлятор Ubuntu оказался очень симпатичным. Установка прошла без проблем. Т.к. версия для AMD64/EMT64 почему-то не хотела ставиться нормально, то я использовал обычный дистр для i386. Первое впечатление оказалось несколько неожиданным. С одной стороны, практически всё (за исключением злополучного bluetooth и модема, который я, правда, и не пытался использовать) работало из коробки. Даже Intel PRO/Wireless 3945ABG на драйвере ipw3945, о котором я здесь также уже упоминал. Но, Gnome в качестве десктоп-менеджера не сильно меня воодушевлял, а boot-сплэш вместо дампа ядра несколько дезориентировал :-). И с первым, и со вторым я справился. Gnome заменил на KDE, а к boot-сплэшу просто привык уже :-) Из коробки на штатном ядре (2.6.17) у меня работали даже suspend-to-ram и hibernate. Но, когда я налюбовавшись всеми этими прелестями начал окультуривать свежеустановленную систему, начали проявляться и побочные эффекты. 1-й и самый заментый - практически все пакеты надо тянуть из репозитория в сети, т.к. у меня был лишь установочный диск с базовыми компонентами (естественно, ядро, libc, xorg, Gnome, base-utils, net-utils и всё, по существу). Поэтому для людей, у которых нет толстого канала это может очень сильно омрачить первое впечатление. У меня толстого канала тоже нет, но зато есть работа, где, в свою очередь, есть приличный канал :-) Второй момент, вся система изначально заточена под Gnome в качестве десктоп-менеджера и это быстро проявляется не очень приятным образом. Так, например, после установки KDE у меня отказался нормально работать hibernate и suspend-to-ram. Система засыпала, но уже не просыпалась. Самое смешное, что происходило такое безобразие только при работе в KDE и никак не отражалось на gdm (я оставил его логин-менеджером - внешне он понравился мне больше, чем kdm). Немного порыскав по логам и конфигам я сделал такие правки в /etc/hibernate/common.conf:

### clock
SaveClock restore-only

### network
DownInterfaces eth0 eth1
UpInterfaces eth0 eth1

### vbetool
EnableVbetool yes
RestoreVbeStateFrom /var/lib/vbetool/vbestate
VbetoolPost yes
RestoreVCSAData yes

### xhacks
SwitchToTextMode yes
# UseDummyXServer yes
# DummyXServerConfig xorg-dummy.conf

### xstatus
## This can be set to gnome, kde or x:
XStatus kde
и такую в xorg.conf:
Section "Device"
 Identifier "Intel Corporation Mobile Integrated Graphics Controller"
 Driver  "i810"
 BusID  "PCI:0:2:0"
 Option  "VBERestore"  "true"
EndSection
(добавил опцию "VbeRestore" - для использования этого добра необходим пакет vbetool). Вот, собственно, и всё, что я сделал для восстановления работоспособности режимов hibernate и suspend-to-ram.

Ещё один неприятный момент выявился позже. Об этой ошибке уже писали на форуме ubuntu. При возврате из спящего режима, во время работы от аккумулятора, только одно ядро CPU работает на пониженной частоте, в то время, как другое процессорное ядро работает на полных оборотах, что нежелательно при работе от аккумулятора и вообще нарушает системную гармонию :-) Я быстро нашёл чьё-то решение: поместить следующий скрипт в /etc/acpi/resume.d

#!/bin/sh

SYS_DIR=/sys/devices/system/cpu
POLICY=ondemand

for CPU in `ls $SYS_DIR`
do
    echo -n "$POLICY" > $SYS_DIR/$CPU/cpufreq/scaling_governor
done
под именем, ну пусть хотя бы и 10-dualcore-freq (я обозвал его так, хотя у автора он назывался по-другому). Однако, это мне не помогло. Как было видно из лога /var/log/acpid, он почему-то не обрабатывал такие события, как suspend или resume. Зато он всё равно при пробуждении обрабатывал power, при котором, в свою очередь, выполнял скрипты из battery.d или ac.d, в зависимости от того, работает лаптоп от аккумулятора или сети. Поэтому сей скрипт я положил в /etc/acpi/battery.d, придя к выводу, что так даже лучше, потому что мне ни к чему экономить гигагерцы при работе от сетевого адаптера. Здесь, как не трудно догадаться по аналогии с boot-скриптами, префикс в имени файла "10-" - это приоритет. У меня, к примеру, этот скрипт имеет наивысший приоритет и выполняется самым первым.

Затем обнаружились проблемы с кард-ридером MMC/SD от Texas Instruments. Драйвер ядра просто не умел работать с некоторыми картами. Наконец я созрел для замены ядра! Я хотел заменить ядро в надежде, что новый драйвер, быть может, доработан в этом плане. Кроме того, я никогда не любил тайм-принты printk() :-) Итак, я решил поставить последнее ядро стабильной ветки, тарбол которого у меня было на тот момент - 2.6.20. Правда, я решился на замену ядра не сразу. Ведь драйвер для Intel PRO/Wireless 3945ABG тоже пришлось бы пересобирать. Но, в конце концов, я решился. Предвижу гневные возгласы, потому сразу скажу, там где мне проще работать по привычке, а не по-новому, я отдаю предпочтение привычке. У меня нет особого желания разучивать опции нового тула только потому, что его использование - это Debian-way. Поэтому, ядро я ставил вручную, как привык делать уже очень давно - без использования kernel-package:

cp /boot/config-<old-kernel-version> /usr/src/linux-2.6.20/.config
make oldconfig
make menuconfig
make -j 8 bzImage modules
make modules_install
update-initramfs -k 2.6.20 -c
cp ./System.map /boot/System.map-2.6.20
cp ./.config /boot/config-2.6.20
cp ./arch/i386/boot/bzImage /boot/vmlinuz-2.6.20
Правим /boot/grub/menu.lst соответственно - вот и вся премудрость. Затем, распаковав тарбол с модулем ipw3945 я попытался его собрать. Но... натолкнулся на глухое непонимание Makefile'а. Он кричал, что версия модулей подсистемы ieee80211 не соответствует карте символов ядра. Т.е., Makefile полагал, что я использую out-of-tree реализацию ieee80211, а не родную ядерную. Мне так и не удалось переубедить make. В сети я нашёл замечательное решение: пропатчить ядро на предмет ipw3945! Патч ipw3945-1.2.0_for_2.6.19.patch лёг на ядро без проблем. Правда, мне пришлось переконфигурировать ядро и пересобрать его, но зато - больше никаких проблем с компиляцией модуля вне дерева кода ядра. Ещё одна маленькая хитрость. Демон и микрокод для драйвера я не ставил - они уже были в системе. Но для того, чтобы всё заработало, надо сделать симлинк на демон ipw3945d-<old-kernel-version>, где бы вместо <old-kernel-version> стояла версия Вашего ядра (в моём случае, это 2.6.20, как не трудно догадаться :-) Кстати, в листинге ключ make -j определяет число параллельно выполняющихся экземпляров make при сборке. Если числовой параметр опущен, то make создаёт столько экземпляров самой себя, что в конце концов истощает все системные ресурсы. 8 - параллельных инстанций для моего Core 2 Duo было самое то. Если установить слишком большое число, система может перестать реагировать, будто она зависла, или начать сильно тормозить. Поэтому применяйте данный параметр разумно и без фанатизма. Кроме таймингов printk я убрал все ненужные модули, включил оптимизацию под Core 2 и включил в ядро драйвер консольного фрейм-буфера и vesa. Хотя, сперва я пытался заставить работать нативный драйвер intelfb. Драйвер имеет статус экспериментального и у меня так и не заработал нормально, поэтому я откатился к vesafb. Вот по сути и всё, что касается ядра.

Кстати, я долго пытался найти в KDE что-нибудь, что позволяло бы быстро переключаться между настройками сетевого интерфейса (домашняя сеть и сеть на работе), но так и не нашёл ничего работающего :-) В конечном счёте я решил воспользоваться способом, который видел когда-то в сети.

Суть этого нехитрого способа состоит в использовании разных схем в /etc/network/interfaces. Для его использования вносим изменения в сам файл /etc/network/interfaces:

auto lo
allow-hotplug eth1
iface lo inet loopback

mapping eth0
script /etc/network/map-scheme.sh
map home eth0-home
map work eth0-work

iface eth0-home inet static
 address xxx.xxx.xxx.xxx
 netmask xxx.xxx.xxx.xxx
 broadcast xxx.xxx.xxx.xxx
 network xxx.xx.xx.xxx
 gateway xxx.xxx.xxx.xxx

iface eth0-work inet static
 address yyy.yyy.yyy.yyy
 netmask yyy.yyy.yyy.yyy
 gateway yyy.yyy.yyy.yyy

auto eth0
Теперь мы имеем две схемы с разными настройками сетевого интерфейса: home - для домашней сети и work - для рабочей, соответственно. Там же, в /etc/network добавляем такой скрипт:
#!/bin/sh

iface="$1"
if [ "$iface" = "" ]; then iface="eth0"; fi
if [ -f /etc/network/scheme ]; then
 scheme=$(cat /etc/network/scheme)
 echo $iface-$scheme
else
 echo $iface
fi
exit 0
А в /usr/local/bin создаём такой скриптик:
#!/bin/sh

if [ $# -le 0 ]; then
 echo -n "Current scheme: "
 cat /etc/network/scheme
 exit 0
fi
echo "$1" > /etc/network/scheme
который у нас и будет переключать схемы и по совместительству показывать, какая схема активна в данный момент. Посмотреть её можно также в файле /etc/network/scheme, который используется нашим скриптом map-scheme.sh для получения имени активной схемы. Сама схема устанавливается скриптом netscheme. Опциональный параметр - имя схемы, на которую необходимо переключиться. Без параметра скрипт просто выдаёт имя активной схемы. После переключения схемы нужно ещё выполнить ifdown/ifup, чтобы изменение параметров вступили в силу.

Ну, что тут, в общем, скажешь, старый добрый shell script попрежнему даёт... эээ, фору разным там гуям ;-) Хотя, желающие вполне могут прикрутить к этому делу гуёвый фронт-энд, благо сделать это также не сложно.

В итоге, Edgy Eft мне понравилась. Я настроил под себя всё окружение. Свежий софт работает без нареканий. Хотя, кое-чего в репозитории не было и мне пришлось поставить недостающий софт из собственных запасов. Речь об Adobe Acrobat Reader и rar. Дистрибутивный KchmViewer меня приятно порадовал. Управление энергосбережением я настроил через KLaptop. К слову, влияние xfs на работу suspend установить больше не было возможности, так как я слегка реструктурировал разбиение диска. Теперь корневая ФС у меня на reiserfs, а /home - полностью на xfs. Я не вдавался в детальные исследования, но во всяком случае, ничто не мешает отмонтировать /home перед засыпанием и модуль xfs тут уже не может помешать, т.к. оказывается незадействованным. Действительно ли hibernate отмонтрирует /home, я не проверял - повторюсь ещё раз. Но, вполне вероятно, что проблему можно устранить, если использовать для root какую-нибудь другую ФС, кроме xfs. Драйвер i810 в xorg 7.1.1, как и ожидалось, прекрасно позволяет смотреть фильмы в полноэкранном режиме и играть в Quake III Arena. Правда, мне до сих пор не удалось заставить Q3A правильно масштабироваться. Но это уже мелочи :-)

Sunday, March 25, 2007

Toshiba Satellite A100-811: WiFi

В Toshiba Satellite A100-811 встроен WiFi-адаптер Intel PRO/Wireless 3945ABG. В принципе, поиск драйвера для этого устройства не представляет собой проблемы. Драйвер можно скачать с официального сайта intel. На данный момент доступны 2 разновидности драйвера: 1-я - это ipw3945. Строго говоря, драйвер состоит из трёх компонентов - собственно, модуль ядра ipw3945, бинарный образ микрокода (firmware) - ipw3945-ucode и демон, работающий в пространстве пользователя - ipw3945d (управляет драйвером).

2-я разновидность - экспериментальный драйвер iwlwifi: это собственно модуль ядра и образ firmware (iwlwifi-ucode). Управляющего демона здесь уже нет (к счастью). Этот драйвер также доступен для загрузки с сайта intel. Кстати, несмотря на предупреждение в документации, что драйвер имеет статус экспериментальный, у меня он работает без каких-либо нареканий.

Изначально, чтобы заставить работать адаптер, я использовал 1-й драйвер (ipw3945). Сложностей с его установкой в общем-то нет никаких (версия ядра, с которой согласен собираться модуль - 2.6.13+). Выкачал драйвер: ipw3945-1.2.0, демон ipw3945d-1.7.22 + firmware не-помню-какой-точно-версии (последняя). Собрал модуль (без проблем), положил firmware в /usr/lib/hotplug/firmware. Положил демон в куда-надо (/sbin), добавил в /etc/modprobe.d файлик, чтобы оно пускало демон при загрузке модуля (файл /etc/modprobe.d/ipw3945):

install ipw3945 /sbin/modprobe --ignore-install ipw3945 ; sleep 0.5 ; /sbin/ipw3945d-start 
remove ipw3945  /sbin/ipw3945d --kill ; /sbin/modprobe -r --ignore-remove ipw3945-stop
Скрипты взяты из тарбола с демоном - ipw3945d:
ipw3945-start
#!/usr/bin/env /bin/bash
# If no parameter is provided then set user to 'root'
[ "$1" ] && user=$1 || user=root

wait_for_cmd() {
 # First, wait for sysfs entry to show up, timing out after 10 seconds:
 count=0
 while [ ! -e /sys/bus/pci/drivers/ipw3945/*/cmd ]; do
  sleep 0.5
  count=$((count+1))
  ((count > 20)) && return 1
 done

 return 0
}

wait_for_cmd && {
 # Set ownership of sysfs entry:
 chown $user /sys/bus/pci/drivers/ipw3945/*/cmd
 chmod a-w,u+rw /sys/bus/pci/drivers/ipw3945/*/cmd

 # Verify/set up PID directory 
 [ ! -d /var/run/ipw3945d ] && {
  mkdir -m 0775 /var/run/ipw3945d
  chown $user /var/run/ipw3945d
 }

 # Launch the regulatory daemon:
 /sbin/ipw3945d --quiet --pid-file=/var/run/ipw3945d/ipw3945d.pid
}


ipw3945-stop
#!/usr/bin/env /bin/bash
PIDPATH=/var/run/ipw3945d
# Kill the regulatory daemon if it is running:
/sbin/ipw3945d --isrunning --pid-file=${PIDPATH}/ipw3945d.pid &&
 /sbin/ipw3945d --kill --pid-file=${PIDPATH}/ipw3945d.pid

# Remove the PID directory
[ -d ${PIDPATH} ] && rm -rf ${PIDPATH}

К слову, директорию /var/run/ipw3945d, где демон будет держать файл со своим PID, желательно создать вручную.

Сам модуль грузится нормально, тянет за собой ieee80211 (который должен быть включен в ядре - по идее, в документации требуется загрузить более новую версию ieee80211, но я не патчил ядро). Выделяется ирка (IRQ18). В общем, всё как-будто нормально. Но демон грузиться отказывается и вываливает сообщение: Could not find Intel PRO/Wireless connection. Модуль при загрузке рапортовар: Intel PRO/Wireless connection detected, но не показывал при этом Detected geography ... Как результат, нет второго eth-интерфейса и вообще, девайс неработает. Начал грешить на hotplug, а именно, что он не грузит firmware, но, как выяснилось позже, hotplug здесь совершенно ни при чём. В конечном счёте, виновным был признан демон. Согласно всё той же документации, демон сперва деинициализирует драйвер при загрузке, затем инициализирует по-новому и если его что-то не устраивает, просто отваливается. В противном случае, если всё нормально - производит калибровку.

Одним словом, я просто плюнул на всякие дальнейшие разбирательства. Тем более, что инстинктивно мне почему-то очень не нравилась такая модель драйвера, где присутствует управляющий user-space демон.

Итак, заменяем неблагонадёжный ipw3945 + ipw3945d на новый iwlwifi. В документах написано, что этот драйвер экспериментальный и может не работать. Но у меня он во всяком случае заработал. Для работы (и сборки?) понадобится подсистема mac80211 (по докам - это кучка фиксов для уже имеющейся в ванильном ядре подсистемы ieee80211 и "ставится" она без проблем на ядра 2.6.18+). Находим и сливаем по возможности последнюю версию, в моём случае это был тарбол mac80211-4.0.4. Распаковываем, говорим make. В ответ на это нам выплёвывают список изменений, которые будут предприняты в ядре. Затем, если всё удачно (этой приблуде может понадобиться rsync - мне пришлось доустанавливать его, т.к. до этого никакой потребности в нём я не испытывал и, соответственно, не ставил) говорим make patch_kernel. В каталоге, где распакован тарбол получаем несколько новых подкаталогов (некоторые были и до этого, но в них появилось новое содержимое): compatible, modified, origin, patches, stubs. В compatible лежат пропатченные куски кода. Копируем содержание compatible в /lib/modules/`uname -r`/build. Затем натравливаем на /lib/modules/`uname -r`/build скрипт ieee80211_ptr.sh из <unpacked_tarball>/scripts. Если этого не сделать, компилятор при пересборке ядра под конец начнёт вопеть как резанный, что structure has no member 'ieee80211_ptr' (На самом деле, странно, но в пропатченном коде просто не делается правильное приведение к типу). В любом случае, ядро придётся пересобирать полностью, естественно. Перед сборкой в конфиге должны быть определены символы CONFIG_CFG80211=m && CONFIG_MAC80211=m (Через menuconfig CONFIG_MAC80211 это Networking -> Generic IEEE 802.11 Networking Stack (dscape) где находится CONFIG_CFG80211, сейчас точно не вспомню, но при желании можно найти).

Если всё прошло успешно, ставим модули, ядро. Перезагружаемся. Тащим тарбол с интеловским firmware: iwlwifi-ucode-2.14.1 или выше. Кладём его в /usr/lib/hotplug/firmware (или /lib/firmware, в зависимости от того, где Ваш hotplug хотел бы всё это хозяйство видеть).

Тащим тарбол с самим драйвером: wlwifi-0.0.11 или выше. Распаковываем, говорим make, make install. После того, как модуль собран и установлен, проверяем:

 # ./load debug=0x43fff 
 
 <SKIPPED>

 выплёвывает кучу всякой полезной и не очень информации, главнй смысл в фразах, что соединение найдено, информация о каналах и тому подобное 
 </SKIPPED> 
 
 # iwconfig 
 wmaster0  IEEE 802.11a  Channel:0 
 
           RTS thr:off   Fragment thr=2346 B 
      
 wlan0     IEEE 802.11a  ESSID:"" 
    Mode:Managed  Channel:0  Access Point: 00:00:00:00:00:00 
    RTS thr:off   Fragment thr=2346 B 
    Encryption key:off 
    Link Quality:0  Signal level:0  Noise level:0 
    Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0 
    Tx excessive retries:0  Invalid misc:0   Missed beacon:0
Что и требовалось доказать. Демон здесь не нужен, поэтому, установка практически завершена.

PS: последовательность моих действий несколько отличается от той, что описана в документации. Например, мне не удалось запустить даже make menuconfig после пропатчивания ядра на предмет mac80211, т.к. make ругался на отсутствие конфига в net/mac80211 (самого каталога тоже не было). Поэтому я просто порывшись определил, где и что находится и скопировал содержимое compatible /lib/modules/`uname -r`/build. Про то, что в пропатченных сырцах компилятор начнёт жаловаться на неприведение к нужному типу, тоже сказано ничего не было (по поводу ieee80211_ptr.sh) Ещё один момент: утилиты выдают предупреждения по поводу стека wireless-расширений:

devel:/home/f0x/tmp# iwconfig wlan0
Warning: Driver for device wlan0 has been compiled with version 21
of Wireless Extension, while this program supports up to version 17.
Some things may be broken...
Но у меня пока проблем не наблюдалось никаких. Возможно, этот чисто внешний дефект устраняется апгрейдом пакета утилит, но меня эти сообщение особо не смущают, так что я ничего не апгрейдил.

ПОСЕТИТЕЛИ

free counters