суббота, 5 апреля 2014 г.

CubieTruck - недо-NAS и пере-Arduino. Часть вторая.

Это вторая часть заметок по установке и настройке Cubieboard.
Первую часть можно почитать здесь.

Устанавливаем на разные носители, собираем ядро и т.п.


Установка на NAND.

Казалось бы — всё описано в доке, запускаешь sudo cubian-nandinstall (возможно, предварительно его установив через sudo apt-get install cubian-nandinstall), перезагружаешься и ещё раз запускаешь. Неспешно пьёшь кофе и, вынув флешку с cubian, запускаешь Кубик...

А! Оп-паньки! Только RS232 консоль, да и в той ругань, что не найден /boot/script.bin и иже с ним.

Горестно запускаемся снова с флешки и изучаем что у нас с NAND-ом. А он разбит на 3 раздела, один из которых — пустое место. Гугление приводит нас к выводу, что на загрузочном разделе NAND-а вовсе не то, что там должно располагаться.
  • Восстанавливаем "убитый" NAND, как было описано в предыдущей части  (я использовал образ lubuntu-server-nand-vga.img версии 1.02). Запускаем и убеждаемся, что всё заработало.
  • Выполняем "магические команды", прямо из запущенного lubuntu-server:
    $ sudo nand-part -f a20 /dev/nand 32768 'boot 131072' 'rootfs 14778368'
    $ sudo reboot
    ...
    $ sudo e2fsck -f /dev/nandb
    $ sudo resize2fs /dev/nandb
    
    Первая команда переразбивает логические разделы NAND. Как ни странно, данные при этом не теряются. Вторая команда проверяет файловую систему на ошибки — её попросит выполнить третья. Третья же отдаёт второму разделу всё свободное место. Секретные цифры первой команды не что иное, как указание начала разделов и их размеры. Смещение и размер раздела 'boot' надо оставить как есть, а раздел 'rootfs' вычисляется из общего числа секторов на /dev/nand минус смещение и 'boot'. Говорят, его можно указать и сильно больше — урежется автоматически. Не пробовал.
  • Лезем в /etc/fstab и убеждаемся, что корневой раздел монтируется на /dev/nandb с флагами relatime,discard,errors=remount-ro. Если нет — правим.
  • Перезагружаемся и проверяем, что всё работает, а под раздел / выделено всё доступное место — порядка 7Гб:
    $ sudo fdisk -hBM
    Filesystem                  1M-blocks    Used Available Use% Mounted on
    ...
    /dev/nandb                      7152M   1470M     5356M  22% /mnt/nandb
    /dev/nanda                       128M     16M      113M  13% /mnt/nanda
    
  • Сохраняем куда-нибудь наш загрузочный раздел, чтобы можно было потом восстановить только его:
    $sudo dd if=/dev/nanda conv=sync,noerror bs=1024 count=65536 |gzip -c -9 >nanda.img.gz
    $sudo dd if=/dev/nand conv=sync,noerror bs=1024 count=16384 |gzip -c -9 > nand-boot.img.gz 
  • Распаковываем образ cubian-base-xxx на флешку с cubian (потребуется порядка 1Гб места)
  • Загружаемся с флешки, очищаем корневой раздел nandb, монтируем образ и копируем его на NAND:
    $ sudo mkfs.ext4 /dev/nandb
    $ mkdir tmp
    $ sudo mount -o loop,offset=1048576 -t ext4 cubian-base-r5-a20-ct.img tmp
    $ mkdir r
    $ sudo mount /dev/nandb r
    $ sudo rsync -HEAhav --numeric-ids tmp/* r
    $ sudo umount r
    $ sudo mount /dev/nanda r & cd r
    $ sudo for F in {script.bin,uEnv.txt,uImage}; do cp $F $F.old; done
    $ cd ..
    $ sudo cp tmp/{script.bin,uEnv.txt,uImage} r
    $ nano r/uEnv.txt
      -> edit/add: rootfstype=ext4 rootfsflags=discard
      -> (?) edit/del: строки с EDID
      -> edit: nand_root=/dev/nandb
      -> edit/add: mac_addr=c0:b0:01:02:03:04
  • Последняя строчка это один из способов задать фиксированный MAC адрес сетевой карты
  • Выключаем Cubietruck, вынимаем флешку и проверяем, что всё заработало

Разворачивание на HDD


Для тестов и сбережения времени и ресура флеша (на время тестов) установим систему на внешний SATA диск.

Действия аналогичны вышеописанному копированию на NAND, за исключением того, что в uEnv.txt надо написать nand_root=/dev/sda1

Замечена закономерность: если в выключенный cubietruck воткнуть uSD карточку, то при включении может пойти с SATA. Для запуска же с карточки надо перезагрузиться не выключая питания (или даже кнопкой reset)

До-настройка сетевой карты

Поскольку в Кубике используется дешёвая сетевая карта, не умеющая хранить свой MAC, это приводит к неудобству с DHCP серверами и прочим железом. Аналогично себя ведут большинство китайских андроидо-фонов, меняющих MAC после перезагрузки.

Способов лечения несколько:
  1. Правка config.fex с добавлением раздела (не заработало с гигабитной картой):
    [dynamic]
    MAC="010203040506"
  2. Вроде бы "кошерный" — передачей параметров ядру. В uEnv.txt добавить параметр вида mac_addr=01:02:03:04:05:06
  3. В cubian встроен сервис, устанавливающий MAC при загрузке. MAC хранится в /root/.cubian-emac, и создаётся со случайным адресом вида c0:b0:xx:xx:xx:xx, если его там не было. У меня как-то через раз срабатывает, судя по логам загрузки
  4. Вручную добавить настройки в /etc/network/interfaces примерно так:
    auto eth0
    iface eth0 inet dhcp
       hwaddress ether c0:b0:01:02:03:04
Оптимизация гигабитной сети (по мотивам интернетов, насколько полезно пока не понятно) Изначально запускалось скриптом из /etc/rc.local, но было перенесено в /etc/sysctl.conf:

## /etc/sysctl.conf
# -- TUNE Gigabit Ethernet --
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 0
net.core.wmem_max = 8388608
net.core.wmem_default = 131072
net.core.rmem_default = 131072
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_mem = 98304 131072 196608

# -- DISABLE IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

Дополнительно в /etc/rc.local добавляется строчка "ifconfig eth0 txqueuelen 5000"

UPD: По результатам простого теста эти "оптимизации" ухудшают картику: если тормозная samba с ними даёт 17.4Мб/с при копировании 1.5Гб файла, то без них получается 21.4 Мб/с. Отключаем опции в sysctl.conf и продолжаем тестирование...
UPD2: Если вы готовы мириться с зависаниями клиентов при отключении NFS-шары, то лучше обойтись без samba-ы: разница в скорости — вдвое: 21.4Мб/с у samba  против 41.6Мб/с у NFS4.

Копируем HDD на NAND.

Наигравшись и настроив всё, как хотелось, переносим готовую систему на NAND.

  • Загружаемся с SD карты. предполагается, что cubian-nandinstall установлен (он нужен только для файла исключений, содержимое которого приведено ниже и может быть просто создано где-нибудь)
  • Выполняем:
    $ sudo mount /dev/nandb /mnt/nandb
    $ sudo mount /dev/sda1 mnt/sda
    $ cd /usr/lib/cubian-nandinstall
    $ sudo rsync -HEAXahv --numeric-ids --exclude-from=exclude.txt --delete --delete-excluded /mnt/sda/* /mnt/nandb
    
    $ sudo mount /dev/nanda /mnt/nanda
    $ sudo nano /mnt/nanda/uEnv.txt
       -> edit nand_root=/dev/nandb
    
  • Содержимое exclude.txt:
    /home/cubie/*
    /boot/*
    /dev/*
    /proc/*
    /sys/*
    /media/*
    /mnt/*
    /run/*
    /tmp/*
    

Собираем своё ядро с блэкдж... дополнительными модулями

Поскольку одной из целей покупки сего девайса было использование его с самой разной периферией, первое что я попытался сделать — подключить радио модуль NRF24L01 дабы читать показания всяких температур и прочих влажностей с уже готового прототипа передатчика.

Тут меня ждал немалый облом — оказывается, ни в одном из готовых ядер нет поддержки шины SPI для Cubietruck.

Для начала было решено собрать своё ядро, включив этот модуль. Позже выяснилось, что просто так это работать не будет и надо внести некоторые изменения в конфиги чтобы получить желаемое (см. http://docs.cubieboard.org/tutorials/dvk521/documentations/a20/driver_porting_and_configuration).

Итоговая последовательность действий примерно такая:
  • Готовим среду кросс-компиляции на PC:
    $ sudo apt-get install arm-gcc-gnueabihf u-boot-tools
    $ sudo apt-get install libgtk2.0-dev libglib2.0-dev libglade2-dev
  • Качаем ядро (я брал 3.4.79 от patwood) и дополнительый файл с поддержкой SPI:
    $ git clone git@github.com:patrickhwood/linux-sunxi.git
    $ cd linux-sunxi/drivers/spi
    $ wget -nc http://dl.cubieboard.org/parteners/waveshare/Source_Code/a20-cubieboard-dvk/driver%20source/spi-sun7i.c
  • Правим drivers/spi/Makefile (добавить строку):
    + obj-$(CONFIG_SPI_SUN7I)         += spi-sun7i.o
  • Правим drivers/spi/Kconfig. Добавляем где-то после проеверки "IF SPI":
    
    config SPI_SUN7I
       tristate "SUN7I SPI Controller"
       depends on ARCH_SUN7I
       help
           Allwinner Soc SPI controller,present on SUN7I chips.
           
    config SUN7I_SPI_NDMA
        bool "SUN7I SPI Normal DMA mode select"
        depends on SPI_SUN7I
        help
           This selects SPI DMA mode with DMA transfer
           Y select NDMA mode and N select DDMA mode
  • Загружаемся в  Cubietruck и сохраняем на PC файл /proc/config.gz.
  • Распаковываем его в ядро и начинаем конфигурировать систему:
    $ gunzip -c config.gz > linux-sunxi/.config
    $ make ARCH=arm SUBARCH=mach-sun7i gconfig
  • Включаем поддержку SPI как модуль. Должка получиться примерно такая картинка (SUN7I включены как модули):
    Device Drivers  --->
    [*] SPI support  --->
        <*>   SUN7I SPI Controller
    [*]     SUN7I SPI Normal DMA mode select
    <*>   DesignWare SPI controller core support 
    <*>   User mode SPI device driver support 
  • Исправляем ещё, что захочется. Например, добавляем поддержку 1-wire или отключаем ненужные модули. Не стоит отключать поддержку CSI если будет подключён VGA дисплей. Желательно отключить отладочные символы в ядре (Compile the kernel with debug info (DEBUG_INFO)) это сэкономит порядка 300Мб места и, наверняка, несколько ускорит работу.
  • Говорят, следующая опция ощутимо ускоряет сеть. У меня не помогло. В gconfig:
    •  Device Drivers
      • Network Device Support
        • Ethernet Driver Support
          • Select DMA for TX/RX [...] modes
            • Ring Mode переключить на Chained Mode
  • Сохраняемся и выходим из конфигуратора
  • Собираем ядро, модули и заголовочные файлы:
    $ make ARCH=arm SUBARCH=mach-sun7i CROSS_COMPILE=arm-linux-gnueabihf -j4 uImage modules
    $ make ARCH=arm SUBARCH=mach-sun7i CROSS_COMPILE=arm-linux-gnueabihf INSTALL_MOD_PATH=output modules_install
    $ make ARCH=arm SUBARCH=mach-sun7i CROSS_COMPILE=arm-linux-gnueabihf INSTALL_HDR_PATH=output/usr headers_install
    $ cp arch/arm/boot/uImage output
  • Переносим содержимое output на Кубик. Папки модулей заменяем целиком, стирая предыдущее содержимое, заголовки перезаписываем измененное, uImage копируем на загрузочный раздел (/dev/nanda). Всё это можно проделывать прямо на загруженном cubietruck, главное — сделать изменения и в /lib и заменить uImage до перезагрузки чтобы версии ядра и модулей совпадали.
  • Не помешает установить правильного владельца на файлы модулей:
    $ sudo chown -R root:root /lib/{modules,firmware}
  • Перезагружаем Cubietruck. Если не накосячили с выбором модулей, всё будет работать. Если нет — смотреть информацию в терминале и анализировать.
  • Замечание по пересборке. После выполнения make clean или make mrproper для очистки перед пересборкой ядра будут удалены файлы firmware/ap6210, которые нужны для сборки wifi/bluetooth. Временное решение — скопировать их заранее куда-нибудь и восстанавливать перед сборкой или выполнить команды:
    $ cd firmware/ap6210
    $ git checkout -- *
    которая восстановит "всё как было". В противном случае сборка будет неудачна с руганью на broadcomm
  • Замечания по Hardware monitor. Если возникает ошибка/предупреждение про hwmon_axp — проверить чтобы оба параметра AXP_HWMON и HWMON были включены в ядро ("Y")

SPI

  • Правим /dev/nanda/script.fex для включения SPI:
    $ cd /mnt/nanda
    $ sudo bin2fex script.bin script.fex
    $ sudo nano script.fex
    [spi2_para]
    spi_used = 1
    # ...
    [spi_devices]
    spi_dev_num = 1
    
    [spi_board_0]
    modalias = "spidev"
    max_speed_hz = 8000000
    bus_num = 2
    chip_select = 0
    mode = 0
    full_duplex = 1
    manual_cs = 0
    
    $ fex2bin script.fex script.bin
  • Создаём правило для юзера в /udev/rules.d и перезагружаемся:
    KERNEL=="spidev*", MODE="0660", GROUP="gpio" 
  • Перезагружаемся
  • Качаем spidev_test.c, правим (по желанию) устройство по-умолчанию на /dev/spidev2.0, компилим (gcc spidev_test.c).
  • Замыкаем MISO и MOSI пины (6, 8 в данном случае)
  • запускаем ./a.out:
    root@Truck:~# ./a.out
    spi mode: 0
    bits per word: 8
    max speed: 500000 Hz (500 KHz)
    
    FF FF FF FF FF FF
    40 00 00 00 00 95
    FF FF FF FF FF FF
    FF FF FF FF FF FF
    FF FF FF FF FF FF
    DE AD BE EF BA AD
    F0 0D
    
  • Если тестовая программа test_spi.c не собирается или вылетает с внутренней ошибкой — смотреть содержимое /usr/include/linux/spi/spidev.h: в оригинальном ядре пропущен один из элементов структуры. Если устанавливали заголовочные файлы, то это уже должно быть поправлено, но проверить:
    struct spi_ioc_transfer {
        __u64      tx_buf;
        __u64      rx_buf;
    
        __u32      len;
        __u32      speed_hz;
    
        __u16      delay_usecs;
        __u16      interbyte_usecs; // !!! вот это пропущено в оригинале
    
        __u8       bits_per_word;
        __u8       cs_change;
        __u32      pad;
    }
    

 1-Wire 

В железе

Поскольку входы у Cubietruck трёхвольтовые, для тех сенсоров, что не могут работать от 3.3В придётся городить преобразователь уровней. Для традиционных датчиков температуры DS18B20 и расширителей входов/выходов DS2804 3.3В вполне достаточно, поэтому можно обойтись просто питанием 3.3В на датчики (желательно через самовосстанавливающийся предохранитель на, например, 0.1А) с подтяжкой к тем же 3.3В резистором порядка 4.7кОм.

В ядре

Включаем поддержку 1-wire (Device Drivers/Dallas's 1-wire support), там же выбираем все под-опции со словом sunxi, GPIO 1-w busmaster и 1-wire Slaves  для тех чипов, что будем использовать (DS18x20 - 1w_thermal).
В script.fex
  • добавляем в раздел [gpio_para] пин, через который пойдёт 1-wire
  • добавляем новый раздел [w1_para], где gpio — номер пина из предыдущего пункта:
    [w1_para]
    gpio = 12
  • перекомпилируем .fex (fex2bin)

В системе

Сначала проверяем через modprobe, если заработало — добавляем в /etc/modules:

gpio_sunxi
#...
w1_gpio
w1_therm

Результатом будет появление в /sys/bus/w1/devices наших датчиков (как код датчика и MAC адрес)

Прочитать значение можно, например, подобной командой (самый быстрый вариант по мотивам обсуждения в на Хабре и реальным тестам):

awk -F "=" '/t=/{printf("%0.1f", $2/1000);} /sys/bus/w1/devices/28-000001020304'

 NFS4 Server

На сервере

$ sudo apt-get install nfs-kernel-server
$ sudo mkdir /srv/{homes,data}

$ echo "/home  /srv/export/homes  none  bind  0  0" | sudo tee -a /etc/fstab
$ echo "/mnt/data  /srv/export/data  none  bind  0  0" | sudo tee -a /etc/fstab

$ sudo -i
cubie password:
# echo cat >>/etc/exports <<EOL
# -- Note: no spaces between address and options !!!
/srv/export  10.0.0.0/24(rw,fsid=0,no_subtree_check,sync)
/srv/export/homes 10.0.0.0/24(rw,nohide,insecure,no_subtree_check,sync)
/srv/export/data 10.0.0.0/24(rw,nohide,insecure,no_subtree_check,sync)

EOL

# exportfs -ar || service nfs-kernel-server restart

На клиентах

$ sudo apt-get install nfs-common
$ sudo mkdir /mnt/x
$ sudo mount -t nfs4 -o proto=tcp,port=2049 cubie-ip:/homes /mnt/x

Тюнинг

$ sudo apt-get install hdparm smartmontools
$ sudo hdparm -S 255 -k 1 /dev/sda (standby after 21min 15sec)
$ smartctl -i -n standby /dev/sda && do-somethign-if-hdd-not-sleep

Логи в память с синхронизацией при выключении или по расписанию.

 $ sudo dpkg -i ramlog_2.0.0.deb
Исправляем "INIT INFO" в /etc/init.d/ramlog. Добавляем записи X-Start-Before и X-Stop-After и запускаем sudo insserv -v ramlog для применения изменений:
### BEGIN INIT INFO
# Provides: ramlog
# Required-Start:
# Should-Start: $local_fs
# Required-Stop:
# X-Stop-After: rsyslog console-k
# X-Start-before: rsyslog console-k
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: moves /var/log into ramdisk
# Description: ramlog daemon moves /var/log to ramdisk on startup and copies it back to harddrive on shutdown or restart
### END INIT INFO  

Если /var/log лежит на HDD, можно добавить хитрое условие в /etc/cron.hourly:
  • Копируем /etc/cron.daily/ramlog в /etc/cron.hourly
  • Исправляем строку запуска:
    smartctl -i -n standby /dev/sda 2>&1 >/dev/null && /etc/init.d/ramlog restart
    
    Что не даст ежечасно будить диск, если он заснул.

Комментариев нет:

Отправить комментарий