четверг, 28 декабря 2006 г.

Впечатление о Motorola Pebl U6

+ цена 120$
+ bluetooth
+ gprs / edge
+ маленький
+ приятный
+ оригинальный
+ miniusb
- интерфейс не логичен
- как отправлять с телефона фалы я не нашел
- эксплорера фс на самом телефоне тоже не нашел
- бедный набор bluetooth сервисов
- куда после пробывания новой симки и установки старой пррпали смс и телефонные номера набранные ранее - я не понимаю... и по этой причине вообще сижу без единого контакта...

Цену телефон свою оправдывает. Если удастся синхронизировать телефон с кпк - будет вообще здорово.

четверг, 14 декабря 2006 г.

Восстановление после чистки

Если вы обладатель КПК, то наверняка слышали про утилиты MemMaid или SKTools, а значит этот пост для вас. Если вы к тому-же не только слышали, но еще и пользуетесь, то это 100% для вас. Если вы не только пользуетесь одной из этих программ, но еще и по глупости полностью почистили Notification Queue и не знаете как заставить КПК работать - как раз об этом и пойдет речь.
1. Почему это проблема? Notification queue (очередь уведомления, NQ) говорит КПК какое действие необходимо выполнить когда какое-то событие произошло. Например, если вы включаете КПК, то это событие AppRunAtWakeup (или типа того). КПК не будет ничего делать по событиям, если вы почистили NQ. Самое главное, что вы не сможете даже приконнектится своим КПК к ПК с помощью обычных USB, IR, RS232. Это означает, что вы не сможете синхронизировать ваш КПК с ПК, а также не сможете его поэксплорить с вашего большого компьютера.

2. Как я могу это вылечить? Простой путь - это восстановить состояние КПК из резервной копии. Очень хорошо иметь такую свежую копию :)))).

3. У меня нет резервной копии... не совсем так... есть, но она древняя, что я могу сделать в этом случае? Вы можете сделать аппаратный сброс (Hard Reset, XР). Но все же лучше восстановится из бэкапа, даже если он у вас древний.

4. Я не хочу делать ХР или восстанавливаться из резервной копии! Да, я тоже. Тогда мы должны добавить события в NQ вручную.

5. Какие события я должен добавить? Чтобы узнать список событий вы можете: сделать бэкап, сделать ХР, установить SKTools и записать список событий из очереди уведомления, затем восстановится из бэкапа. Или вы можете найти список для вашего КПК в интернете. В моем случае, я посмотрел на КПК моей жены. У нее КПК почти такой же как у меня (у нее C550, у меня N560).

6. Как добавить события в NQ? SKTools - лучшее что я видел для этих целей.

7. Мне нужно добавить "\windows\repllog.exe AppRunAfterRndisFnDetected", но SKTools не имеет события "AppRunAfterRndisFnDetected"! Да! Это сейчас основная проблема.
Я потратил четыре дня, чтобы найти такую программу, которая бы позволяла мне добавить это событие. Но я не нашел. Не было такой программы. Поэтому я написал свою. Вот она: http://toril.ru/soft/akrndisadd.exe. Просто запустите ее на КПК. Она должна показать "True", если событие было успешно добавлено.

8. Как я могу поместить эту программу на КПК, если я не могу посмотреть содержимое моего КПК с ПК, пока это событие не добавлю? Вы можете переместить программу с помощью Bluetooth, WIFI, MMC/SD/CF карты, инфракрасного порта или RS232 :).

Исходники akrndisadd
#include "stdafx.h"
#include <windows.h>
#include <commctrl.h>
#include <notify.h>

int _tmain(int argc, _TCHAR* argv[])
{
TCHAR *r = TEXT ("False");

// This API is deprecated. I know.
if (CeRunAppAtEvent (TEXT("\\windows\\repllog.exe"), 14L)) {
r = TEXT ("True");
};

MessageBox (NULL, r, r, MB_OKCANCEL | MB_ICONEXCLAMATION);
return 0;
}

четверг, 19 октября 2006 г.

Устали ждать обновление Debian пакета?

Оказывается в debian'е пакеты можно не только собирать из исходников, а еще эти исходники можно обновлять до upstream версии с сайта автора, а потом собирать deb пакет.

akshaal@akshaal:/tmp% apt-get source libtorrent9
Чтение списков пакетов... Готово
Построение дерева зависимостей... Готово
Нужно загрузить 472kB архивов с исходными текстами.
Получено:1 http://debian.org.ua unstable/main libtorrent 0.10.2-1 (dsc) [634B]
Получено:2 http://debian.org.ua unstable/main libtorrent 0.10.2-1 (tar) [453kB]
Получено:3 http://debian.org.ua unstable/main libtorrent 0.10.2-1 (diff) [18,2kB]
Получено 472kB за 3s (143kB/c)
gpg: Подпись создана Чтв 21 Сен 2006 07:41:55 EEST ключом DSA с ID 1880283C
gpg: Не могу проверить подпись: открытый ключ не найден
dpkg-source: extracting libtorrent in libtorrent-0.10.2
dpkg-source: unpacking libtorrent_0.10.2.orig.tar.gz
dpkg-source: applying ./libtorrent_0.10.2-1.diff.gz

akshaal@akshaal:/tmp% cd libtorrent-0.10.2

akshaal@akshaal:/tmp/libtorrent-0.10.2% uscan
libtorrent: Newer version (0.10.3) available on remote site:
http://libtorrent.rakshasa.no/downloads/libtorrent-0.10.3.tar.gz
(local version is 0.10.2)
libtorrent: Successfully downloaded updated package libtorrent-0.10.3.tar.gz
and symlinked libtorrent_0.10.3.orig.tar.gz to it

akshaal@akshaal:/tmp/libtorrent-0.10.2% uupdate ../libtorrent-0.10.3.tar.gz
New Release will be 0.10.3-1.
-- Untarring the new sourcecode archive ../libtorrent-0.10.3.tar.gz
Success! The diffs from version 0.10.2-1 worked fine.
Remember: Your current directory is the OLD sourcearchive!
Do a "cd ../libtorrent-0.10.3" to see the new package

akshaal@akshaal:/tmp/libtorrent-0.10.2% cd ../libtorrent-0.10.3/

akshaal@akshaal:/tmp/libtorrent-0.10.3% dpkg-buildpackage -rfakeroot -us -uc

вторник, 17 октября 2006 г.

JCA

Допустим мы пишем JCA RA который поддерживает исходящие соединения к EIS. Все написали. Верифицировали. Продеплоили в GlassFish. Все OK. Теперь продеплоили какое-то приложение, чтобы протестировать соединение к EIS. Запускаем. А оно нам @#%!: "Exception in NamingManagerImpl copyMutableObject() ... java.io.NotSerializableException".
Покопавшись мы выясняем, что падение происходит при копировании нашей имплементации java.resource.cci.ConnectionFactory интерфейса. А имеено в нашем классе ConnectionFactoryImpl есть не Serealizable поле, например ManagedConnectionFactory (содержащая сокет например). Убив N часов времени на поиски в гугле, выясняем, что GlassFish в JNDI помещает копии объектов! Наматерившись и решив, что разработчикам JNDI реализации виднее, мы делаем неугодное в ConnectionFactoryImpl поле transient'ым. А вдруг этот объект будет использоваться, только чтобы getReference вызвать? Хрен там. Повозившись еще, оказывается, что dependency injection отдает нам именно копию из JNDI у которой наше transient поле установленно в NULL!

Даём GlassFish'у последний шанс. Читаем его исходники и оказывается, что не все объекты помещаются в JNDI в сереализованном виде. Так например Connector'ы должны помещаться не сереализованными! Хм, а как же оно определяет, что этот объект коннектор? А вот как:

private boolean isConnector(String logicalJndiName){
return (logicalJndiName.indexOf(EIS_STRING)!=-1);
}
где EIS_STRING = "/eis/".

Короче, чтобы не иметь лишний геморрой: ConnectionFactory экземпляры должны помещаться в субконтексте /eis/ ! Если теперь поискать в гугле по словам "eis subcontext" то находятся рекомендации, а не требования!.

Вообще, это частая ошибка, проявляется в виде NPE (null pointer exception) при вызове transient полей (activemq этим страдает). Мне кажется, в ConnectionFactoryImpl разумно вставить:

/**
* Ensure that managedConnectionFactory is not null.
* @throws ResourceException if managedConnectionFactory is null.
*/
protected void ensureManagedConnectionFactory () throws ResourceException {
if (this.managedConnectionFactory == null) {
throw new ResourceException (
"No reference to managed connection factory exists."
+ " Either it is a bug of the RA or your JNDI resource"
+ " is not in the /eis/ subcontext");
}

и вызывать этот метод перед использованием this.managedConnectionFactory внутри ConnectionFactoryImpl.

среда, 11 октября 2006 г.

Из спецификации по JNI

"An interface pointer is a pointer to a pointer. This pointer points to an array of pointers, each of which points to an interface function."

понедельник, 9 октября 2006 г.

Смысл javax.resource.cci.*

Я наконец-то понял зачем нужен CCI. Все время казалось, что из-за него в простых RA много ненужного кода. А теперь ощутил зачем этот CCI нужен.

Если реализовывать RA без CCI, то наш MCF возвращает CF который не CCI-compliant, а тот уже в свою очередь по запросу RA клиента делегирует обращение в RM и получает соединение к RA отдавая его клиенту. Все просто и хорошо, но:
- в этом случае мы не может использовать DI, оказывается @Resource работает только для набора предопределенных интерфейсов (типа тогоа же CCI CF), поэтому CF придется ручками из JNDI доставать.
- если RA не продеплоена как EAR, а задеплоена в виде одельного RAR файла и клиент RA задеплоен соответственно как отдельный JAR, то, чтобы клиент мог использовать наш собственный CF, у него должен быть на руках интерфейс этого CF. В случае CCI CF этого не требуется, так как CCI CF определен в java.resource.cci.
- наверное есть еще что-то, но мне хватило этого

Доздравствует CCI!

четверг, 5 октября 2006 г.

SUNWappserver uninstall

Если кто-то пытается переустановить Sun Application Server 9.0 Platform Edition и у него не получается это в течении часа (то already installed, то unexpected exception), то стоит грохнуть нахрен (или аккуратно почистить) /var/tmp/productregistry или /tmp/productregistry.

Навеяно вот этим советом в котором неправильно указан путь.

среда, 4 октября 2006 г.

Mastering EJB 4ed

Офигенный код в книге Mastering EJB 4ed!
Chapter 15, конец метода execute:
if (out != null) {
return true;
} else {
return false;
}
класс! :))
А еще в этой книге есть строки в раздели Best Practice:
Many organizations have tried—and failed—to truly reuse components. Because of this, it is a perfectly valid strategy to not attempt true reuse at all. Rather, you can shoot for a copy-and-paste reuse strategy, which means to make the source code for components available in a registry to other team members or other teams. They can take your components’ code and change it as necessary to fit their business problem. While this may not be true reuse, it still offers some benefits.
Эта запись писалась в течении 7 минут 37 секунд.

пятница, 29 сентября 2006 г.

Попробовал себя


Попробовал себя в роли "электронщика". Спаял хреновину для коммутации трех каналов постоянного напряжения (up to 24В ) и одного переменного (up to +220В) через LPT порт. Ну что я могу сказать? Нафик, нафик, я лучше буду программировать.

Что общего в электронике с программированием:
1. По отдельности куски работают, а стоит засунуть "в корпус" - работает 10%.
2. Постоянно что-то отваливается.
3. Хрен найдешь, что отвалилось - при визуальном осмотре все OK.
4. Надо пользоваться отладочными средствами.
5. Поиски ерундовой проблемы занимают больше всего времени.
6. В конце понимаешь, как надо было делать, чтобы избежать половины проблемм и запутанного устройства.
7. Но переделывать лень и поэтому делаем заплатки.
8. В конце концов как-то вся это хрень начинает работаеть. Ты молишься, чтобы от неловкого движения все не сломалось и не пришлось лезть внутрь.
9. Решение "выйти, зайти или перезапустить" часто помогает - почему не понятно.
10. Надо иметь крепкие нервы.
11. Чудес не бывает.

В чем отличие:
1. Программированием тяжело поранится, общечься, убится, спалить квартиру или порт принтера вместе с материнской платой.
2. После программирования в комнате не стоит запах канифоли и вообще оно никак не пахнет.
3. Программирование не оставляет за собой кучу мусора и ошметков.
4. В программировании один программный модуль не может уничтожить прочие модули, если их неправильно сцепить.
5. Программу можно забэкапить...
6. ...

среда, 27 сентября 2006 г.

Личный прогресс

Собрал схему с этой странички. И она даже работает. Собирал на куске картона вместо текстолита. Несмотря на то, что схема расчитана на 112В/220В, у меня заработала на AC 24V. Получилось коммутировать переменное напряжение с компьютера через LPT порт. Виииии! :) Зачем пока не знаю, но что-нибудь придумаю. Надо еще щось для постоянного собрать...

пятница, 4 августа 2006 г.

Патч для spcacat

spcaview умеет подстраивать яркость изображения захватываемого камерой автоматически. А вот spcacat - необходимая для захвата изображения без его просмотра (в batch режиме) почему-то этого не умеет (хотя автор тот же самый). Сделал патчик, который это исправляет (ключ -b). Также добавил ключик -M который указывает, что надо пропустить y кадров, прежде чем "захватить" -N кадров. Это нужно, чтобы дать время на подстройку яркости - она подстраевается либо на основе пропущенного кадра, либо на основе захваченного.

Из-за абсолютно дибильного кода (серьезно, я хуже open source'ного кода не видел никогда) на написание патча ушло 1.5 часа, хотя работы там реально минут на 20-30.

Собственно патч.

суббота, 10 июня 2006 г.

Joyemu v3. Пишим сложный патч для телефона на C. Информация должна быть свободной.

Еще одной записью в TODO сегодня стало меньше, после новой версии патча, выпустить которую у меня наконец-то дошли руки. Что изменилось:
1. Теперь при включении режима эмуляции джойстика светодиоды в верхней части телефона горят и горят до выхода из эмуляции. Благодаря этому всегда понятно в каком мы сейчас режиме.
2. Нажатие на софт кнопки ничего не эмулирует. Иногда их надо нажимать, не выходя из эмуляции.
3. Блокировка клавиатуры эмуляцию выключает.

Новая версия патча в формате vkp.

Вопреки сложившейся традиции с форума сайта http://allsiemens.com я выложу и исходники патча, и все функции прошивки Siemens CX65 sw50, которые мне удалось найти (более 400), расскажу как писать патчи на C. Я не придерживаюсь мнения, что каждый новичок должен найти все эти функции сам - этот подход мне кажется бесполезной тратой времени, которое человек мог бы направить на что-то полезное, а не изобретать велосипед. Я также не сторонник подхода, когда создается элитарный клуб, между людьми которого передается информация - мне это несколько противно. Информация должна быть свободной.

Тем не менее, эта информация не освобождает от необходимости знать Assembler ARM.

Пишем патч на C на примере Joyemu.
При написании сложнофункциональных патчей некоторая логика может быть написана на C.
Так, например, при эмуляции джойстика (я рассказывал в первом посте на эту тему) телефона мы имеем множество ветвлений в алгоритме, реализовать которые можно и на ассемблере, но проще и чище это сделать на C.

Первое, что делает joyemu - перехватывает нажатие на клавишу. Для этого мы ищем в прошивке тот участок кода, который обрабатывает это событие. Нашли, допустим, по адресу ORIGINAL_KEY_HANDLER. Теперь надо найти свободное место в прошивке под наш код, который будет обрабатывать это нажатие (ищем в прошивке длинную последовательность FF). Пускай наш обработчик должен быть по адресу OUR_KEY_HANDLER. Передать управление на наш обработчик при нажатии на клавишу можно двумя способами. Найти все места из которых происходит переход в ORIGINAL_KEY_HANDLER. И заменить на переходы на OUR_KEY_HANDLER. Так обычно никто никогда не делает, ибо мест таких может быть много. Мы же вставляем по адресу ORIGINAL_KEY_HANDLER инструкцию для загрузки в регистр PC адрес OUR_KEY_HANDLER. То, что мы затерли этими инструкциями запоминаем, оно нам пригодится, когда мы захотим вызвать оригинальный обработчик. А код, который находится за нашей инструкцией перехода на OUR_KEY_HANDLER, будет иметь адрес ORIGINAL_KEY_HANDLER_CHUNK.
Получается вот что:

ROM:A121D4BC ORIGINAL_KEY_HANDLER:
ROM:A121D4BC 04 F0 1F E5 LDR PC, =OUR_KEY_HANDLER
ROM:A121D4C0 10 F3 65 A1 DCD OUR_KEY_HANDLER
ROM:A121D4C4 ORIGINAL_KEY_HANDLER_CHUNK:


А так как в алгоритме эмуляции нам понадобится вызов оригинального обработчика нажатия, то для этого надо где-то разместить тот самый затертый код, после которого вставить переход к ORIGINAL_KEY_HANDLER_CHUNK. Разместим этот код сразу перед
OUR_KEY_HANDLER по адресу MOVED_ORIGINAL_KEY_HANDLER. Выглядит это вот так (сначала идут 8 оригинальных байт, потом 4 байта инструкции перехода, после 8 байт данных для инструкций):

ROM:A165F2FC MOVED_ORIGINAL_KEY_HANDLER:
ROM:A165F2FC 70 40 2D E9 STMFD SP!, {R4-R6,LR}
ROM:A165F300 04 10 9F E5 LDR R1, =off_A8663120
ROM:A165F304 04 F0 1F E5 LDR PC, =ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F308 C4 D4 21 A1 DCD ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F30C 20 31 66 A8 DCD off_A8663120
ROM:A165F310 OUR_KEY_HANDLER:


Сохраняем изменения как vkp patch. Для этого в интернете валяется IDC скрипт для IDA, а также инструкции.

Из всех адресов, дальше нам понадобятся два:
MOVED_ORIGINAL_KEY_HANDLER - это та точка входа, куда мы должны сделать переход, если хотим вызывать родной обработчик нажатия.
OUR_KEY_HANDLER - здесь будет наш обработчик.

Теперь осталось только наш обработчик написать. Работа с ассемблером на этом заканчивается, на сцену выходит C. Для компиляции я использовал пакет gcc-4.1-arm для Debian. Начну с описания опций необходимых для компиляции под телефон:
-c -- делает объектный файл.
-mcpu=arm926ej-s -- компилировать под указанный процессор. Именно эти процы стоят в Siemens x65.
-mthumb-interwork -- компилировать инструкции дальнего перехода с учетом бита отвечающего за ARM/THUMB режим работы процессора. Короче вместо B и BL будет BX и BLX.
-fomit-frame-pointer -- а нафик он нужен?
-fno-unit-at-a-time -- это чтобы откомпилированные функции в объектном файле были в том же порядке, в котором они были в исходниках.
-mlong-calls -- ну длинные переходы типа.
Прочие опции типа -Wall -Os или -O3 - это на усмотрение.

Допустим мы написали C файл. Скомпилировали его с вышеперечисленными опциями в объектный файл. Теперь его надо слинковать в исполняемый файл elf. Это маленькое извращение нужно для того, чтобы линкер привел все relocation'ы в норму и разместил все по тем адресам, которые мы ему скажем. Нет, конечно можно при компиляции указать -fPIC и получить оверхеадный код, но мы пойдем другим путем. Опции линкера:
-s -- удаляет всю символьную информацию. А нафик она?
-nostdlib -- Не прилинковывать никаких левых библиотек.
-Bstatic -- Эта опция говорит о том, что все релокейшены будут жестко забиты. Никаких динамически подгружаемых библиотек. Статическая компиляция.
-X -- Забыл уже зачем добавил. Но наверное надо. Пусть будет.
-entry 0 -- стартовый адрес. 0 - адрес первой функции первого объектного файла.
-R local.symbols -- Файл, откуда будут подгружаться определения локальных символов.
-R ../CX65sw50dev/firmware.symbols -- определение символов прошивки.
--section-start .text=A165F310 -- адрес, по которому размещать код. Как раз тот самый адрес OUR_KEY_HANDLER.
--section-start .data=A80C8FFC -- адрес, по которому размещать данные (переменные).

Среди опций мелькнули два файла. Первый файл local.symbols:
my_original_press_key = 0xA165F2FC;
-- тут сказано, что C'ная функция с именем 'my_original_press_key' расположена по адресу '0xA165F2FC', а это тот самый адрес MOVED_ORIGINAL_KEY_HANDLER. Тоесть, чтобы вызвать из C оригинальный обработчик, я должен воспользоваться функцией void my_original_press_key (int keycode);

Файл ../CX65sw50dev/firmware.symbols, аналогичен, сюда я внес немного функций и переменных, которые всегда есть в прошивке sw50 у cx65, например кусок:

[...]
switch_phone_off = 0xA1240EF0;
dynamic_light_mode = 0xA863E6E0;

set_dynamic_light_mode = 0xA0B1ECDD;
locked = 0xA8656060;
[...]

Замечу, что если функция прошивки написана THUMB инструкциями, то к адресу нужно прибавить единичку, то есть адрес должен быть нечетным (младший бит включен).
Также напомню, что если мы хотим использовать внешние функции, то их прототипы где-то надо описать. Локальную в рамках патча функцию my_original_press_key я описал прям в .c файле, а функции и переменные прошивки в файле cx65sw50.h:

[...]
void switch_phone_off ();
void set_dynamic_light_mode (unsigned mode);

extern unsigned dynamic_light_mode;
extern unsigned locked;
[...]


Теперь все вспомогательные вещи описаны. Можно приступать к написанию самого патча.
Нужно помнить, что первая функция в объектном файле будет той функцией, что ляжет по адресу OUR_KEY_HANDLER. Еще нужно указывать глобальным переменным секцию .data, чтобы они вдруг не оказались в секции кода .text (как любит делать последний gcc), нужно это из-за того, что в телефоне есть флэш память (под код), а есть RAM (под переменные). Если попытаться изменить с помощью STR что-то, что лежит во флэше, то телефон пискнет и выключится.

К нашим баранам. Первой функцией будет pressKeyChunk, которая и будет эмулировать нажатия на джойстик путем хранения состояния, вызова к оригинальному коду и т.д. Все, что нужно этой функции объявлено перед ней прототипами. Чтобы отслеживать изменение состояния светодиодов и реагировать на блокировку клавиатуры, при первом нажатии на клавишу, будет запущен таймер с частотой вызова обработчика ~0.4с. Как это все сделано можно увидеть скачав исходный код (5kb).
Для компиляции патча необходим мой Набор-Разработчика-Патчей-Для-CX65-SW50 (6.5kb). Этот архив включает в себя:
Makefile.inc в котором прописаны правила сборки,
cx65sw50.h прототипы некоторых функций прошивки,
firmware.symbols адреса некоторых функций прошивки,
fw50.map адреса всех функций, что мне удалось найти в прошивке, пока не надоело,
mk_patch.pl скрипт для создания vkp патча на основе elf файла, использует в свой работе программу elfsh.

Конечный vkp файл создается на основе elf файла скриптом mk_patch.pl и добавлением заголовка - файла, который мы сделали из IDA с помощью IDC скрипта.

Вот и все. Может быть все это выглядит запутанно и сложно, но на самом деле большая часть этих функций выполняется Makefile'ом и, чтобы создать патч, надо только написать make. Общие механизмы сборки вынесены в файл Makefile.inc, а в Makefile патча остались только настройки типа адреса данных, адреса кода, опций оптимизации и т.д.

Немного слов о функциях прошивки которые я нашел (из них более 250 nucleus os).
Как я уже говорил, адреса этих функций находятся в файле fw50.map архива (6.5kb). Если первой цифрой адреса не является 'A', то значит это смещение функции внутри секции и надо перед использованием к адресу прибавить A0000000. Какое-то количество этих функций удалось насобирать по форуму. Немного - из других патчей. Из туториалов. Что-то нашел сам. Небольшое число функций nucleus os удалось получить по сигнатурам, остальные - мой кропотливый труд.

Перед использованием какой-то функции ее желательно проверить. Не гарантирую, что идентифицировал правильно и правильно интерпретировал.

Для использования функций операционной системы Nucleus PLUS достаточно посмотреть на nucleus.h который есть в демоверсии этого продукта, а также можно поискать документацию по этим функциям. Самое полное, что удалось найти - это pdf под названием 'Nucleus Plus Internals'.

P.S. Спасибо авторам программ IDA, ArmDebugger и V_klay, а также другим, кто писал софт для работы с Siemens x65. Без этих программ телефон скорее всего пришлось бы править паяльником или покупать новый.
P.S. Все это является художественным вымыслом. Совпадение с реальностью случайны. Слышал, что обезьяны в состоянии случайно Войну и Мир написать, чем я хуже?

четверг, 8 июня 2006 г.

Labtec Webcam Pro. Driver bugfix. Пример фиксинга драйвера.

Купил я тут вебкамеру. Выбирал такую, чтобы под линухом работала, была дешевой и качество имела получше. В выборе помогла вот эта страничка, на которой представлен перечень вебкамер поддерживаемых драйвером spca5xx с указанием качества картинки. Купил. Принес домой. Покдлючил. Скачал драйвера. Откомпилировал. Запустил xawtv, ужаснулся изображению. Повел камеру всторону и заметил, что компьютер "замер" и ни на что больше не реагирует. Оказалось, линух вис при резкой смене освещения.

Поискал в интернете - может кто уже сталкивался? Множество пользователей вебкамер с драйвером spca5xx сталкивались с этой проблемой. Кто-то советует не дергать камеру. Кто-то винист усб мост, хост или еще что-то. Автор драйвера вообще заявил, что драйвер написал для работы на мощьных компьютерах и собственно все у нас ok. Ближе всех к истине оказался вот этот пост. Но предложенное им решение (править код ядра линуха) это скорее лечение симптомов, а не устранение причины.
В чем суть проблемы. Есть у драйвера spca5xx такслет - это такая задача, которая шедулится из обработчика прерывания на этап окончания работы прерывания, тоесть прерывание побыстрому отработало, и запускается тасклет который может что-то делать продолжительное время (при этом другие прерывания не задисаблены). У каждого тасклета есть флаг состояния TASKLET_STATE_SCHED - если этот флаг включен, то считается, что тасклет еще не выполнялся, а только планируется к выполнению. Проблема возникла в том коде ядра, который активирует выполнение зашедуленного тасклета. Перед выполнением, ядро проверяет состояние этого флага, и если флаг включен, то сбрасывает его и выполняет код задачи. А вот если вдруг этот флаг выключен, но почему-то этот тасклет в очереди, то ядро выполняет функцию BUG() - тоесть распечатывает содержимое стека и вешается. Собственно, автор того поста так и предложил сделать - убрать вызов ф-ии BUG(). Если задуматься, почему к моменту выполнения тасклет оказывает без флага TASKLET_STATE_SCHED, можно предположить:
1. Бага в ядре - маловероятно.
2. Кто-то помимо ядра этот флаг сбрасывает. Проверка показала, что в драйверах spca5xx строка TASKLET_STATE_SCHED вообще отсутствует.
3. Память под тасклет выделяется один раз. Во время работы тасклета возникает прерывание и оно реинциализирует структуру тасклета (решедулит тасклет). После прерывания происходит возврат к тасклету, он оканчивает работу и сбрасывает флаг. А так как тасклет зашедулен в очереди, то к моменту его выполнения флаг оказывается сброшенным. Проверка показала, что это не так. Да и флаг сбрасывается до работы такслета, а не после.
4. Надо смотреть код.

В драйверах spca5xx есть всего одно место где шедулится тасклет. И сразу перед вызовом функции tasklet_schedule стоит функция tasklet_init. Все это обернуто циклом по пакетами полученным от usb драйвера и условием при котором шедулится тасклет: если найден конец фрейма (оноже начало следующего). Получается, что если обнаружен фрейм, то структура тасклета инициализируется и шедулится. А если обнаружено два фрейма? Вот она и бага. Если обнаружено два фрейма, то в ядре в очереди тасклетов окажется две записи которые ссылаются на один и тот же участок памяти.
Как пофиксить:
1. Поставить проверку перед инитом. Если уже зашедулено, то игнорируем и не шедулим больше. Минусы - фрейм теряется. Плюсы - просто.
2. Выделять память под тасклет динамически, в тасклет параметром передавать указатель на структуру в которой указатель на структуру тасклета и фрейм для декодирования. После декодирования память такслета очищать. Минусы - потеря производительности, муторно вообще. Плюсы - фреймы не теряются, правильное решение.
3. Замутить какую-нибудь очередь или пулл.. лениво.
Вобщем выбрал я первый вариант. И так сойдет %). Написал письмо в рассылку с патчем и обьяснениями на ломоном английском. Кому надо сам доделает нормально (open source модель такая вот).

А люблю я все-таки причины ошибок искать. Докапываться до сути... Такой процесс интересный. Расследование.

пятница, 2 июня 2006 г.

Один из проектов о кончине которого я жалею

Сегодня вдруг накатила тоска по одному из моих проектов, который я забросил в связи увлечением Haskell'ом. Разочаровавшись в кафедральной модели разработки O'Caml (невозможность вносить изменения сторонник разработчиков) я решил написать свой ML.. лучше, открытие и гибче. И вот лежит это недописанное чудо на винчестере в архивах, и уже с трудом-то компилируется (в Ocaml'евом camlp4 теперь не loc, а _loc называется, интерфейсы поменяли и тд). Но собрать понастальгировать таки удалось. Оставлю-ка я себе на память процесс работы с компилятором.

Компиляция, как это выглядело

Исходный файл.

---=====================================================================------
-- DESC: Just a test.
-- RUN: %(tdir)s/nnl-%(ckind)s %(src)s -O0 -o %(src)s.exec; %(src)s.exec
-- EXIT: 13
-- RM: %(src)s.exec
---=====================================================================------

let pairer = \x -> 3, x
and add = \(x, y) -> (ffi_call "builtin:int:+" (x:int) (y:int) : int)
and apply = \(f, arg) -> f arg in
apply (add, pairer 10)

./tools/nnl-llvm --show-all --keep-all ./test/00100-Base/0058000-Test.nn -o a.exec

АСТ дерево.

impl_file:
8.0 - 11.26: VE_let:
B_simple:
8.4 - 8.10: VP_ident: pairer
8.13 - 8.23: VE_fun:
8.14 - 8.15: VP_ident: x
8.19 - 8.23: VE_tuple:
8.19 - 8.20: VE_int: 3
8.22 - 8.23: VE_long_ident: x
B_simple:
9.4 - 9.7: VP_ident: add
9.10 - 9.69: VE_fun:
9.11 - 9.17: VP_paren:
9.12 - 9.16: VP_tuple:
9.12 - 9.13: VP_ident: x
9.15 - 9.16: VP_ident: y
9.21 - 9.69: VE_type_constraint:
9.22 - 9.62: VE_ffi_call: "builtin:int:+"
9.47 - 9.54: VE_type_constraint:
9.48 - 9.49: VE_long_ident: x
9.50 - 9.53: TE_long_ident: int
9.55 - 9.62: VE_type_constraint:
9.56 - 9.57: VE_long_ident: y
9.58 - 9.61: TE_long_ident: int
9.65 - 9.68: TE_long_ident: int
B_simple:
10.4 - 10.9: VP_ident: apply
10.12 - 10.30: VE_fun:
10.13 - 10.21: VP_paren:
10.14 - 10.20: VP_tuple:
10.14 - 10.15: VP_ident: f
10.17 - 10.20: VP_ident: arg
10.25 - 10.30: VE_flat:
10.25 - 10.26: VE_long_ident: f
10.27 - 10.30: VE_op:
10.27 - 10.30: VE_long_ident: arg
in_expr:
11.4 - 11.26: VE_flat:
11.4 - 11.9: VE_long_ident: apply
11.10 - 11.26: VE_op:
11.10 - 11.26: VE_paren:
11.11 - 11.25: VE_tuple:
11.11 - 11.14: VE_long_ident: add
11.16 - 11.25: VE_flat:
11.16 - 11.22: VE_long_ident: pairer
11.23 - 11.25: VE_op:
11.23 - 11.25: VE_int: 10
Типизированное АСТ дерево.

impl_file:
8.0 - 11.26: 13>35>int: VE_let:
B_simple:
8.4 - 8.10: 14>(15>int -> 16>(17>int * 18>15>int)): VP_ident: pairer/290
8.13 - 8.23: 14>(15>int -> 16>(17>int * 18>15>int)): VE_fun:
8.14 - 8.15: 15>int: VP_ident: x/300
8.19 - 8.23: 16>(17>int * 18>15>int): VE_tuple:
8.19 - 8.20: 17>int: VE_int: 3
8.22 - 8.23: 18>15>int: VE_long_ident: x/300
B_simple:
9.4 - 9.7: 21>(22>(24>int * 25>int) -> 23>int): VP_ident: add/314
9.10 - 9.69: 21>(22>(24>int * 25>int) -> 23>int): VE_fun:
9.11 - 9.17: 22>(24>int * 25>int): VP_paren:
9.12 - 9.16: 22>(24>int * 25>int): VP_tuple:
9.12 - 9.13: 24>int: VP_ident: x/322
9.15 - 9.16: 25>int: VP_ident: y/325
9.21 - 9.69: 23>int: VE_type_constraint:
9.22 - 9.62: 27>int: VE_ffi_call: "builtin:int:+"
9.47 - 9.54: 28>int: VE_type_constraint:
9.48 - 9.49: 29>int: VE_long_ident: x/322
9.50 - 9.53: 29>int: TE_long_ident: int
9.55 - 9.62: 30>int: VE_type_constraint:
9.56 - 9.57: 31>int: VE_long_ident: y/325
9.58 - 9.61: 31>int: TE_long_ident: int
9.65 - 9.68: 27>int: TE_long_ident: int
B_simple:
10.4 - 10.9: 33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int): VP_ident: apply/384
10.12 - 10.30: 33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int): VE_fun:
10.13 - 10.21: 34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VP_paren:
10.14 - 10.20: 34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VP_tuple:
10.14 - 10.15: 36>(39>37>(24>int * 25>int) -> 35>int): VP_ident: f/394
10.17 - 10.20: 37>(24>int * 25>int): VP_ident: arg/397
10.25 - 10.30: 35>int: VE_apply:
10.25 - 10.26: 39>37>(24>int * 25>int) -> 35>int: VE_long_ident: f/394
10.27 - 10.30: 39>37>(24>int * 25>int): VE_long_ident: arg/397
in_expr:
11.4 - 11.26: 13>35>int: VE_apply:
11.4 - 11.9: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 13>35>int: VE_long_ident: apply/384
11.10 - 11.26: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VE_paren:
11.11 - 11.25: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VE_tuple:
11.11 - 11.14: 44>(22>(24>int * 25>int) -> 23>int): VE_long_ident: add/314
11.16 - 11.25: 45>(17>int * 18>15>int): VE_apply:
11.16 - 11.22: 46>15>int -> 45>(17>int * 18>15>int): VE_long_ident: pairer/290
11.23 - 11.25: 46>15>int: VE_int: 10

Лямбда выражение.

(let "13>35>int" pairer/290
(fun "14>(15>int -> 16>(17>int * 18>15>int))" arg.2/-1
(let "16>(17>int * 18>15>int)" x/300
(var "15>int" "arg.2/-1") in
(block "16>(17>int * 18>15>int)"
(int "17>int" 3)
(var "18>15>int" "x/300")))) in
(let "13>35>int" add/314
(fun "21>(22>(24>int * 25>int) -> 23>int)" arg.1/-1
(let "27>int" tuple.2/-1
(var "22>(24>int * 25>int)" "arg.1/-1") in
(let "27>int" y/325
(field "25>int" (var "22>(24>int * 25>int)" "tuple.2/-1") 1) in
(let "27>int" x/322
(field "24>int" (var "22>(24>int * 25>int)" "tuple.2/-1") 0) in
(ffi_call "27>int" "builtin:int:+"
(var "29>int" "x/322")
(var "31>int" "y/325")))))) in
(let "13>35>int" apply/384
(fun "33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int)" arg.0/-1
(let "35>int" tuple.1/-1
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "arg.0/-1") in
(let "35>int" arg/397
(field "37>(24>int * 25>int)"
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "tuple.1/-1")
1) in
(let "35>int" f/394
(field "36>(39>37>(24>int * 25>int) -> 35>int)"
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "tuple.1/-1")
0) in
(apply "35>int"
(var "39>37>(24>int * 25>int) -> 35>int" "f/394")
(var "39>37>(24>int * 25>int)" "arg/397")))))) in
(apply "13>35>int"
(var "42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 13>35>int" "apply/384")
(block "42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))"
(var "44>(22>(24>int * 25>int) -> 23>int)" "add/314")
(apply "45>(17>int * 18>15>int)"
(var "46>15>int -> 45>(17>int * 18>15>int)" "pairer/290")
(int "46>15>int" 10)))))))
Откомпилированный в LLVM код (llvm assember):

declare int "builtin:int:+" (int, int)

{int, int}* "fun:39" (int "var:arg.2/-1") {
"temp:34" = cast int "var:arg.2/-1" to int
"var:x/300" = cast int "temp:34" to int
"temp:37" = malloc {int, int}
"temp:35" = add int 0, 3
"temp:37_f0" = getelementptr {int, int}* "temp:37", int 0, uint 0
store int "temp:35", int* "temp:37_f0"
"temp:36" = cast int "var:x/300" to int
"temp:37_f1" = getelementptr {int, int}* "temp:37", int 0, uint 1
store int "temp:36", int* "temp:37_f1"
"temp:38" = cast {int, int}* "temp:37" to {int, int}*
ret {int, int}* "temp:38"
}

int "fun:32" ({int, int}* "var:arg.1/-1") {
"temp:21" = cast {int, int}* "var:arg.1/-1" to {int, int}*
"var:tuple.2/-1" = cast {int, int}* "temp:21" to {int, int}*
"temp:28" = cast {int, int}* "var:tuple.2/-1" to {int, int}*
"temp:29_addr" = getelementptr {int, int}* "temp:28", int 0, uint 1
"temp:29" = load int* "temp:29_addr"
"var:y/325" = cast int "temp:29" to int
"temp:25" = cast {int, int}* "var:tuple.2/-1" to {int, int}*
"temp:26_addr" = getelementptr {int, int}* "temp:25", int 0, uint 0
"temp:26" = load int* "temp:26_addr"
"var:x/322" = cast int "temp:26" to int
"temp:22" = cast int "var:x/322" to int
"temp:23" = cast int "var:y/325" to int
"temp:24" = call int "builtin:int:+" (int "temp:22", int "temp:23")
"temp:27" = cast int "temp:24" to int
"temp:30" = cast int "temp:27" to int
"temp:31" = cast int "temp:30" to int
ret int "temp:31"
}
int "fun:19" ({{int ({int, int}*)*}*, {int, int}*}* "var:arg.0/-1") {
"temp:8" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:arg.0/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"var:tuple.1/-1" = cast {{int ({int, int}*)*}*, {int, int}*}* "temp:8" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:15" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:tuple.1/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:16_addr" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:15", int 0, uint 1
"temp:16" = load {int, int}** "temp:16_addr"
"var:arg/397" = cast {int, int}* "temp:16" to {int, int}*
"temp:12" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:tuple.1/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:13_addr" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:12", int 0, uint 0
"temp:13" = load {int ({int, int}*)*}** "temp:13_addr"
"var:f/394" = cast {int ({int, int}*)*}* "temp:13" to {int ({int, int}*)*}*
"temp:10" = cast {int ({int, int}*)*}* "var:f/394" to {int ({int, int}*)*}*
"temp:9" = cast {int, int}* "var:arg/397" to {int, int}*
"temp:11_fptr" = getelementptr {int ({int, int}*)*}* "temp:10", int 0, uint 0
"temp:11_f" = load int ({int, int}*)** "temp:11_fptr"
"temp:11" = call int "temp:11_f" ({int, int}* "temp:9")
"temp:14" = cast int "temp:11" to int
"temp:17" = cast int "temp:14" to int
"temp:18" = cast int "temp:17" to int
ret int "temp:18"
}

int %main () {
"temp:39" = malloc {{int, int}* (int)*}
"temp:39_fptr" = getelementptr {{int, int}* (int)*}* "temp:39", int 0, uint 0
store {int, int}* (int)* "fun:39", {int, int}* (int)** "temp:39_fptr"
"var:pairer/290" = cast {{int, int}* (int)*}* "temp:39" to {{int, int}* (int)*}*
"temp:32" = malloc {int ({int, int}*)*}
"temp:32_fptr" = getelementptr {int ({int, int}*)*}* "temp:32", int 0, uint 0
store int ({int, int}*)* "fun:32", int ({int, int}*)** "temp:32_fptr"
"var:add/314" = cast {int ({int, int}*)*}* "temp:32" to {int ({int, int}*)*}*
"temp:19" = malloc {int ({{int ({int, int}*)*}*, {int, int}*}*)*}
"temp:19_fptr" = getelementptr {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:19", int 0, uint 0
store int ({{int ({int, int}*)*}*, {int, int}*}*)* "fun:19", int ({{int ({int, int}*)*}*, {int, in
t}*}*)** "temp:19_fptr"
"var:apply/384" = cast {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:19" to {int ({{int ({int, int}*)*}*, {int, int}*}*)*}*
"temp:6" = cast {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "var:apply/384" to {int ({{int ({int, int}*)*}*, {int, int}*}*)*}*
"temp:5" = malloc {{int ({int, int}*)*}*, {int, int}*}
"temp:1" = cast {int ({int, int}*)*}* "var:add/314" to {int ({int, int}*)*}*
"temp:5_f0" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:5", int 0, uint 0
store {int ({int, int}*)*}* "temp:1", {int ({int, int}*)*}** "temp:5_f0"
"temp:3" = cast {{int, int}* (int)*}* "var:pairer/290" to {{int, int}* (int)*}*
"temp:2" = add int 0, 10
"temp:4_fptr" = getelementptr {{int, int}* (int)*}* "temp:3", int 0, uint 0
"temp:4_f" = load {int, int}* (int)** "temp:4_fptr"
"temp:4" = call {int, int}* "temp:4_f" (int "temp:2")
"temp:5_f1" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:5", int 0, uint 1
store {int, int}* "temp:4", {int, int}** "temp:5_f1"
"temp:7_fptr" = getelementptr {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:6", int 0, uint 0
"temp:7_f" = load int ({{int ({int, int}*)*}*, {int, int}*}*)** "temp:7_fptr"
"temp:7" = call int "temp:7_f" ({{int ({int, int}*)*}*, {int, int}*}* "temp:5")
"temp:20" = cast int "temp:7" to int
"temp:33" = cast int "temp:20" to int
"temp:40" = cast int "temp:33" to int
ret int "temp:40"
}
Запуск LLVM оптимизации.

=====
Alias Analysis Evaluator Report =====
385 Total Alias Queries Performed
179 no alias responses (46.4%)
137 may alias responses (35.5%)
69 must alias responses (17.9%)
Alias Analysis Evaluator Pointer Alias Summary: 46%/35%/17%
68 Total ModRef Queries Performed
20 no mod/ref responses (29.4%)
0 mod responses (0.0%)
0 ref responses (0.0%)
48 mod & ref responses (70.5%)
Alias Analysis Evaluator Mod/Ref Summary: 29%/0%/0%/70%
*** Function unreachable from main: builtin:int:-
*** Function unreachable from main: builtin:int:/
*** Function unreachable from main: builtin:int:*
*** Function unreachable from main: builtin:int:%
*** Function unreachable from main: builtin:int:=
*** Function unreachable from main: builtin:int:<>
*** Function unreachable from main: builtin:int:<
*** Function unreachable from main: builtin:int:>
*** Function unreachable from main: builtin:int:<=
*** Function unreachable from main: builtin:int:>=
===== Alias Analysis Evaluator Report =====
48 Total Alias Queries Performed
17 no alias responses (35.4%)
25 may alias responses (52.0%)
6 must alias responses (12.5%)
Alias Analysis Evaluator Pointer Alias Summary: 35%/52%/12%
21 Total ModRef Queries Performed
0 no mod/ref responses (0.0%)
0 mod responses (0.0%)
0 ref responses (0.0%)
21 mod & ref responses (100.0%)
Alias Analysis Evaluator Mod/Ref Summary: 0%/0%/0%/100%
===== Alias Analysis Evaluator Report =====
6 Total Alias Queries Performed
2 no alias responses (33.3%)
2 may alias responses (33.3%)
2 must alias responses (33.3%)
Alias Analysis Evaluator Pointer Alias Summary: 33%/33%/33%
3 Total ModRef Queries Performed
0 no mod/ref responses (0.0%)
0 mod responses (0.0%)
0 ref responses (0.0%)
3 mod & ref responses (100.0%)
Alias Analysis Evaluator Mod/Ref Summary: 0%/0%/0%/100%
Результат оптимизации (llvm assember):

int %main() {
ret int 13
}
Целевой ассебмлер, AT&T нотация:

.text
.align 16
.globl main
.type main, @function
main:
subl $2, %esp
fnstcw (%esp)
movb $2, 1(%esp)
fldcw (%esp)
movl $13, %eax
addl $2, %esp
ret

Результат работы:
akshaal@akshaal:~/mnt/oldprojects/nnl-boot% ./a.exec
**** Exit code: 13

let f as (++++) = \_ -> \x -> (ffi_call "builtin:int:+" x 10 : int) in
f 6 (10 ++++ 3)

==================================================================
Исходный файл:

---=====================================================================------
-- DESC: Just a test.
-- RUN: %(tdir)s/nnl-%(ckind)s %(src)s -O0 -o %(src)s.exec; %(src)s.exec
-- EXIT: 23
-- RM: %(src)s.exec
---=====================================================================------

let f as (++++) = \_ -> \x -> (ffi_call "builtin:int:+" x 10 : int) in
f 6 (10 ++++ 3)

Результат:

int %main() {
ret int 23
}

....


make test

--- TEST RESULTS -------------------------------------------------------------

00100-Base.0001000-Empty.nn : PASS

00100-Base.0002000-GoodComment.nn : PASS

00100-Base.0003000-GoodDashComment.nn : PASS

00100-Base.0004000-GoodSolidComment.nn : PASS

00100-Base.0005000-GoodComment2.nn : PASS

00100-Base.0009000-HelpOption.nn : PASS

00100-Base.000A000-ShowTypedAstOption.nn : PASS

00100-Base.000B000-ShowAstOption.nn : PASS

00100-Base.000C000-StopAfterOption.nn : PASS

00100-Base.000D000-ImplSuffixOption.nn : PASS

00100-Base.000E000-ImplSuffix.nn : PASS

00100-Base.000F000-WithoutImplSuffix.nn : PASS

00100-Base.0010000-Struct.nn : PASS

00100-Base.0011000-GarbageAfterStruct.nn : PASS

00100-Base.0012000-StructWithoutEnd.nn : PASS

00100-Base.0013000-GraphOp.nn : PASS

00100-Base.0014000-IdentOp.nn : PASS

00100-Base.0015000-LongGraphOp.nn : PASS

00100-Base.0016000-LongIdentOp.nn : PASS

00100-Base.0016800-TypeConstraint.nn : PASS

00100-Base.0016880-Fun.nn : PASS

00100-Base.0017000-ShowIntAST.nn : PASS

00100-Base.0018000-ShowIdentAST.nn : PASS

00100-Base.0019000-ShowLongIdentAst.nn : PASS

00100-Base.001A000-ShowOpAst.nn : PASS

00100-Base.001B000-ShowLongOpAst.nn : PASS

00100-Base.001C000-ShowIdentOpAst.nn : PASS

00100-Base.001D000-ShowLongIdentOpAst.nn : PASS

00100-Base.001E000-ShowParenAst.nn : PASS

00100-Base.001F000-ShowFloatAst.nn : PASS

00100-Base.001F800-ShowStringAST.nn : PASS

00100-Base.0020000-ShowFlatAst.nn : PASS

00100-Base.0021000-ShowIfThenElseAst.nn : PASS

00100-Base.0022000-ShowLetAst.nn : PASS

00100-Base.0023000-ShowApplyAst.nn : PASS

00100-Base.0023800-ShowFFICallAst.nn : PASS

00100-Base.0023A00-ShowFFICall0Ast.nn : PASS

00100-Base.0024000-ShowStructAst.nn : PASS

00100-Base.0025000-ShowValueAst.nn : PASS

00100-Base.0026000-ShowImplAst.nn : PASS

00100-Base.0027000-ShowValuePatAst.nn : PASS

00100-Base.0028000-ShowOpValuePatAst.nn : PASS

00100-Base.0028800-ShowTypeConstraintAst.nn : PASS

00100-Base.0029000-ShowIntTypedAST.nn : PASS

00100-Base.002A000-ShowFloatTypedAST.nn : PASS

00100-Base.002B000-ShowParenIntTypeAST.nn : PASS

00100-Base.0030000-ShowNotTypedAST.nn : PASS

00100-Base.0032000-ShowPlusTypedAST.nn : PASS

00100-Base.0033000-ShowPlusUseTypedAST.nn : PASS

00100-Base.0034000-ShowPlusUseAsOpTypedAST.nn : PASS

00100-Base.0034800-ShowTypeConstraintTypedAst.nn: PASS

00100-Base.0034880-ShowFloatTypeConstraintTypedAst.nn: PASS

00100-Base.0035000-ShowLetInTypedAST.nn : PASS

00100-Base.0035800-ShowFFICallTypedAST.nn : PASS

00100-Base.0035900-ShowFFICall0TypedAST.nn : PASS

00100-Base.0037000-StopAfterLambda.nn : PASS

00100-Base.0038000-ShowLambdaOption.nn : PASS

00100-Base.0039000-ShowIntLambda.nn : PASS

00100-Base.003A000-ShowFloatLambda.nn : PASS

00100-Base.003B000-ShowNotLambda.nn : PASS

00100-Base.003C000-ShowApplyLambda.nn : PASS

00100-Base.003D000-ShowIfThenElseLambda.nn : PASS

00100-Base.003E000-ShowOpLambda.nn : PASS

00100-Base.003E800-ShowFFICallLambda.nn : PASS

00100-Base.003E900-ShowFFICall0Lambda.nn : PASS

00100-Base.003E980-ShowLetInLambda.nn : PASS

00100-Base.003F000-EqPrecedence.nn : PASS

00100-Base.0040000-StopAfterCodegen.nn : PASS

00100-Base.0041000-ShowCodeOption.nn : PASS

00100-Base.0042000-OOption.nn : PASS

00100-Base.0042800-O0Option.nn : PASS

00100-Base.0043000-ReturnInt.nn : PASS

00100-Base.0044000-Return123Int.nn : PASS

00100-Base.0045000-LetIn.nn : PASS

00100-Base.0046000-LetInWithRebinding.nn : PASS

00100-Base.0047000-FFIAndLet.nn : PASS

00100-Base.0048000-ShowAllOption.nn : PASS

00100-Base.0049000-KeepAllOption.nn : PASS

00100-Base.004A000-ShowFunAst.nn : PASS

00100-Base.004B000-ShowFunTypedAst.nn : PASS

00100-Base.004C000-ShowFunLambda.nn : PASS

00100-Base.004D000-FunApply.nn : PASS

00100-Base.004E000-ShowTupleAst.nn : PASS

00100-Base.004F000-ShowTupleTypedAst.nn : PASS

00100-Base.0050000-ShowTupleLambda.nn : PASS

00100-Base.0051000-ShowTuplePatternAst.nn : PASS

00100-Base.0052000-ShowTuplePatternTypedAst.nn: PASS

00100-Base.0053000-ShowTuplePatternLambda.nn : PASS

00100-Base.0054000-Tuple.nn : PASS

00100-Base.0055000-Tuple2.nn : PASS

00100-Base.0055800-ShowParenPatternAst.nn : PASS

00100-Base.0055900-ShowParenPatternTypedAst.nn: PASS

00100-Base.0056000-Tuple3.nn : PASS

00100-Base.0057000-CommFunPreced.nn : PASS

00100-Base.0058000-Test.nn : PASS

00100-Base.0059000-ShowAnyPatternAst.nn : PASS

00100-Base.005A000-ShowAnyPatternTypedAst.nn : PASS

00100-Base.005A800-ShowAnyPatternLambda.nn : PASS

00100-Base.005B000-AnyPattern.nn : PASS

00100-Base.005C000-Test.nn : PASS

00100-Base.005D000-Test.nn : PASS

00100-Base.005E000-Test.nn : PASS

00100-Base.005F000-Test.nn : PASS

00100-Base.0060000-ShowAsPatternAst.nn : PASS

00100-Base.0061000-ShowAsPatternTypedAst.nn : PASS

00100-Base.0062000-ShowAsPatternLambda.nn : PASS

00100-Base.0063000-AsPattern.nn : PASS

00100-Base.0064000-Test.nn : PASS

00100-Base.0065000-ShowTypeConstraintPatternAst.nn: PASS

00100-Base.0066000-ShowTypeConstraintPatternTypedAst.nn: PASS

00100-Base.0067000-Test.nn : PASS

00100-Base.0068000-ShowGraphLetBindingAst.nn : PASS

00100-Base.0068800-ShowGraphLetBindingAst2.nn : PASS

00100-Base.0069000-ShowGraphLetBindingTypedAst.nn: PASS

00100-Base.006A000-ShowGraphLetBindingLambda.nn: FAIL
Test doesn't exit with 0. Regexp '^.*\\(let "\\d+>\\(\\d+>int \\* \\d+>int\\)" y/\\d+.*$' not found in stdout.

TestCompiler.description:
Show lambda of graph operator binding in let statement.

TestCompiler.exit:
2

TestCompiler.run:
./tools/nnl-llvm --stop-after lambda --show-lambda
/home/akshaal/mnt/oldprojects/nnl-boot/test/00100-Base/006A000-ShowGraphLetBindingLambda.nn

TestCompiler.stderr:
Fatal error: exception
Match_failure("lib/NNL/Lambda/NNLLambdaTranslate.ml", 146, 7)

TestCompiler.stdout:

qmtest.end_time:
2006-07-02T11:23:48Z

qmtest.start_time:
2006-07-02T11:23:48Z

qmtest.target:
local

00200-Backend.00100-LLVM.00006000-KeepCodeOption.nn: PASS

00200-Backend.00100-LLVM.00007000-KeepBytecodeOption.nn: PASS

00200-Backend.00100-LLVM.00008000-KeepLinkedOption.nn: PASS

00200-Backend.00100-LLVM.00009000-KeepOptimizedOption.nn: PASS

00200-Backend.00100-LLVM.0000A000-KeepAsmOption.nn: PASS

00300-Runtime.00100-BuiltinIntEq.nn : PASS

00300-Runtime.00200-BuiltinIntEq2.nn : PASS

00300-Runtime.00300-BuiltinIntPlus.nn : PASS

00300-Runtime.00380-BuiltinIntPlus2.nn : PASS

00300-Runtime.00400-BuiltinIntMinus.nn : PASS

00300-Runtime.00480-BuiltinIntMinus2.nn : PASS

00300-Runtime.00500-BuiltinIntMult.nn : PASS

00300-Runtime.00580-BuiltinIntMult2.nn : PASS

00300-Runtime.00600-BuiltinIntDiv.nn : PASS

00300-Runtime.00680-BuiltinIntDiv2.nn : PASS

00300-Runtime.00700-BuiltinIntMod.nn : PASS

00300-Runtime.00780-BuiltinIntMod2.nn : PASS

00300-Runtime.00800-BuiltinIntNe.nn : PASS

00300-Runtime.00880-BuiltinIntNe2.nn : PASS

00300-Runtime.00900-BuiltinIntLt.nn : PASS

00300-Runtime.00980-BuiltinIntLt2.nn : PASS

00300-Runtime.00990-BuiltinIntLt3.nn : PASS

00300-Runtime.00A00-BuiltinIntGt.nn : PASS

00300-Runtime.00A80-BuiltinIntGt2.nn : PASS

00300-Runtime.00A90-BuiltinIntGt3.nn : PASS

00300-Runtime.00B00-BuiltinIntLe.nn : PASS

00300-Runtime.00B80-BuiltinIntLe2.nn : PASS

00300-Runtime.00B90-BuiltinIntLe3.nn : PASS

00300-Runtime.00C00-BuiltinIntGe.nn : PASS

00300-Runtime.00C80-BuiltinIntGe2.nn : PASS

00300-Runtime.00C90-BuiltinIntGe3.nn : PASS

--- TESTS THAT DID NOT PASS --------------------------------------------------

00100-Base.006A000-ShowGraphLetBindingLambda.nn: FAIL
Test doesn't exit with 0. Regexp '^.*\\(let "\\d+>\\(\\d+>int \\* \\d+>int\\)" y/\\d+.*$' not found in stdout.


--- STATISTICS ---------------------------------------------------------------

146 tests total
1 ( 1%) tests FAIL
145 ( 99%) tests PASS

воскресенье, 16 апреля 2006 г.

apt-get install emacs-snapshot

Поставил emacs-snapshot (emacs 22 из cvs). Впечатления.

Первое что заметил сразу:
* Copy'n'past между emacs'ом и прочими программами заработал на все 100%.
* Новый emacs без правки конфигов пустился в терминальном окне (в 21.4 у меня русские буквы не вводились, а разбираться было лень).
* ibuffer-and-update почему-то стало открывать буфер со списком буферов в отдельном окошке, да еще и не делая его активным. Проблема быстро решилась собственной ф-ией my-ibuffer-and-update, так что проблема не критичная. Теперь в ibuffer можно настраивать группы буферов, например для разных проектов, для gnus'а и тд. Настроил пока только gnus (для gnus'овских буферов), custom (для буферов с настройками), fundamental (для прочего
хлама).

Благодрая changelog'у отметил для себя следующие приятные вещи:
* Если редактируемая строка шириной ровно в ширину экрана то дополнительный перевод строки больше не ставится, а если курсор нужно поместить за последний символ такой строки, то он появляется прям на правом 'поле для заметок'. Мне понравилось.
* longlines-mode. Так называемый soft перенос слов в длинных строках. Это когда автоматически происходит виртуальный перенос слов на новую строку, при этом при сохранении в файл или копировании в буфер обмена или другой буфер строка не будет содержать переносов строк. Повесил longlines-mode на f3.

Но самое интересно - ljupdate перестал логинится к серверу lievjournal! Как я с этим (извините меня) натрахался. Часа два убил. Проверил в буфере scratch соответствие между работой функций lj-md5 на одинаковый исходные строки считает одинаковый md5. С помощью ngrep и tcpdump ПОБАЙТНО сверил процесс авторизации ljupdate запущенной в старом emacs и новом, даже на параметры TCP соединений смотрел - но все совпадало, кроме challenge и response. Вместо получения challenge с сервера пробивал константное значение, но ljupdate как и в старом так и в новом emacs'е считал одинаковый response! Что еще можно подумать, если протокол общения верный и совпадает побайтно, кроме значений auth_challenge и auth_response, а auth_response вычисляется соответственно auth_challenge? Все расставила на места документация по функции md5.... Пояснение проблемы на ломаном английском и патч кинул в ljupdate.

среда, 29 марта 2006 г.

JDeveloper

Наконец-то поставил Oracle JDeveloper 10g на ноутбук. Не могу нарадоваться. После подучих и тормозных eclipse'ов прям бальзам на "душу". Хоть месяца три ничего не java и не писал, но все равно приятно..

Себе на будущее, чтобы не искать потом, отмечу, что системный шрифт в нем настраивается через файл ${HOME}/jdevhome/system/oracle.jdeveloper.${JDEV_VERSION}/ide.properties

среда, 22 февраля 2006 г.

Как я правил прошивку своего сотового телефона

В общем решил я тут починить джойстик у своего Siemens CX65 (ношу телефон в кармане джинс вот выпирающая часть джойстика и отломилась еще месяц назад). Взял телефон, снял панель съемную, достал клей моментальный, взял хреновину одну, чтобы приклеить её к остаткам джойстика, намазал джойстик, приставил хреновину, подождал - хреновина прилипла к рукам, а не куда надо. Проделал еще пару попыток, поматерился своей лени на тему зачистить, обезжирить и тд, одел съемную панель обратно, включил телефон и заметил, что джойстик теперь совсем перестал работать, наверное капелька клея скатилась по остаткам джойстика к контактам и изолировала их. Если раньше остатки джойстика можно было как-то ногтем или вспомогательным предметом двигать, чтобы добиться нажатия в нужном направлении, то теперь это стало бесполезно.

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

Первым делом требовалось вытащить эту самую прошивку из телефона, а для этого необходим шнур и програмное обеспечение. За четыре дня до прошлого нового года я купил шнур "телефон<->usb" за 1000 рублей, после чего, буквально в тот же день улетел на родину в Омск (из Москвы) на неделю, а потом в Киев еще недели на две. Когда же я вернулся в Москву и попробовал тот шнур, что купил вскрылись две детали: для телефонов сименс существует множество шнуров различающихся как по моделям телефонов, так и по функциональности. Мой шнур тогда не завелся ни в винде (с родными и не родным дровами), ни в линухе. А гарантия на обмен (15 дней) истекла, пока я мотался Москва->Омск->Москва->Киев->Москва. Но теперь, начитавшись форумов, я твердо решил взять этот шнур и переделать его из нерабочего в рабочий. Подключив его для верности в linux 2.6.15, дабы убедится, что он таки не работает, но по dmesg я увидел обратное:
drivers/usb/serial/usb-serial.c: USB Serial support registered for pl2303
pl2303 3-1:1.0: pl2303 converter detected
usb 3-1: pl2303 converter now attached to ttyUSB0

Поискав драйвера под винду конкретно для pl-2303 я завел его и в винде без всяких передлок. Теперь нужно программное обеспечение... к счастью, с ним никаких проблем, его полно в интернете.

Когда телефон подружился с компьютером, настало время сделать backup этого телефона: рассчитать все коды (HASH, SKEY, IMEI, BKEY, ESN) и записав их в файлик, сделать дампы фулфлэша, буткора (на всякий), лэнгпака (на всякий), еелайта и еефула. Теперь душа спокойна, шансы окончатильно убить тело уменьшились. Для того, чтобы не патчить старую прошивку, я заодно поставил sw50 взамен стоявшей 12 версии (обновил фуллфлэш, обновил файловую систему и мэппинг). На всякий случай снова сделал бэкапы всего подрят.

Бэкапы сделаны, прошивка лежит на винте, самое время поставить IDA, изучить ассемблер ARM710, найти в прошивке нужный код и наконец сделать патч. Для верности и на всякий случай (вдруг пригодится), собрал и поставил на свой debian binutils-arm-linux и компилятор gcc-arm-linux. Изучать набор инструкций ARM было достаточно необычно, интересно и приятно, все-таки набор отличается от x86 в лучшую сторону.

А дальше началось изучение прошивки. Процесс надо сказать неоднозначный.. то ты начинаешь впадать в уныние не знаешь с какой стороны подойти и понять какой вызов был для чего сделан, а то вдруг неожиданно находишь интересные куски кода. Процесс изучения прошивки напоминает следующую ситуацию: вас помещают в произвольное место гиганского лабиринта из которого вам надо найти выход, у вас есть возможность телепортироваться в случайное место лабиринта по желанию, в лабиринте темно, а на полу начерчена замкнутая схема, которую можно использовать на ощупь. Сравнение может быть не очень корректное, но процесс перемещения по функциям немного напоминает.. Радостными моментами в таком деле стали: распознавание стандартных функций типа memset, strcpy, strchr, strlen, printf и т.д., обнаружении функции вызываемой в случае ошибки (не понятно правда куда она ошибку девает, толи в лог, толи еще куда, но это и не важно, а важно, что она вызывается с текстовым описанием ошибки), функции isKeyGood и наконец функции KeyPress!!! А я ведь все время был рядом с этой заветной функцией... всего-то надо было внимательнее смотреть на BFC, хотя наверное эта внимательность пришла как раз в процессе разгребания прочих участков.

Теперь появилось чувство определенности. Самое главное - это найти где править, а это я уже сделал. Дело за малым - найти свободные участки прошвки, озу и написать патч. К сожалению скачать keil arm не получается - траффик на интернет аккаунте кончился, а keil весит под 50м, поэтому решаю покомпилировать с помощью arm-linux-as.

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

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




1, left-up 2, up 3, right-up
4, left 5, click 6, right
7, left-down 8, down 9, right-down

Как нетрудно заметить из функционального описания, программа должна использовать состояние. Выделяем под него 32 бита и находим свободную память для переменной с помощью ArmDebugger'а.

Алгоритм при любом подходе получается немного сложноватый, чтобы тратить время на его реализацию на ассемблере, поэтому я набросал его на C и скомпилировал с помощью arm-linux-gcc в объектный файл. Но не все так просто, как хотелось бы. Замечательный GCC сгенерировал код использующий не хилую таблицу абсолютных адресов (для switch'а), а так как этот код будет располагаться в совершенно другом участке памяти, то надо либо править адреса руками, либо думать как объяснить gcc/as, что я хочу код для определенного участка памяти. Как объяснить GCC/AS я не нашел :(. Но зато разобрался с ld, которым слинковал объектный файл, указав, что хочу секцию .text расположить строго по такому-то адресу. Получилось! Для того, чтобы получить vkp патч на основе elf файла, пишу скрипт на perl'е, на выходе которого получаю заветный патч.

Почти все сделано, напряжение и нетерпение растет! Загружаю патч в IDA, правлю в патче адрес переменной и адрес оригинального обработчика на реальные. Добавляю в патч изменения связанные с изменениями самой прошивки и вот патч готов!

Немного волнуясь, загружаю v_klay, заливаю патч в телефон.

Нажимаю кнопку включения - черный экран и ничего не происходит, что-то внутри опустилось. Нажимаю кнопку включения еще раз! О! Телефон включается - уже хорошо. Робко набираю первую цифру pin кода.. телефон издает характерный звук нажатия и цифра появляется на экране - значит нормальный режим отрабатывает верно. Захожу в меню и пробую режим имитации - работает, я могу бегать по меню.. работает все в том числе диагональные перемещения!

Даже не ожидал, что патч получится с первой попытки! Какой я молодец! :)))

А вот и сам патч.