1. Си / Говнокод #14142

    +139

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    // drivers/usb/serial/cp210x.c
    
    static void cp210x_close(struct usb_serial_port *port)
    {
            usb_serial_generic_close(port);
            cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE);
    }

    Дело было так.

    На прошлой неделе, по просьбе одного знакомого, я сел писать прошивку для attiny2313, которая должна принять по RS-232 строчку и исполнить соответствующую команду. Потратив несколько минут на раскур даташита, а именно главы, посвященной USART, я набросал тупейший код на сях, залил его на чип, и затестил через миником. Все работало нормально, ничто не предвещало беды...

    Но когда я попробовал послать команду через echo, меня ждал облом. Она не выполнилась. На 10 раз проверил код - все ок, в миникоме пашет идеально, через echo - куй. Под виндой та же ересь. Чем отличается echo от миникома? Правильно, тем, что оно отправляет символы подряд, без пауз. Контроллер тактировался от внутренного генератора, поэтому я подумал, что оный хреново откалиброван, и что надо бы его настроить... Вечер ушел на написание прошивки для калибровки... Оказалось, что частота действительно уплыла на 2% от расчетной, что некритично. Ну да пофиг, откалибровался до +-0.5%. Запустил echo - куй. Послал все нахрен, и пошел спать...

    (Продолжение в комменте ниже)

    Запостил: bormand, 26 Ноября 2013

    Комментарии (49) RSS

    • (Продолжение)

      На следующий вечер я залил на чип эхо-тест: кидаешь ему байтики, он их отправляет обратно. Тест, внезапно, работал. Идеально. И в миникоме и в echo, и в самопальной сишной проге... Уже договорился с другом, что на следующий день забегу к нему за логическим анализатором, ибо баттхерт становился все сильнее и сильнее... Но спать еще не хотелось, поэтому just for lulz написал для контроллера счетчик полученных байт, которые, для чистоты эксперимента, прошивка выводила на банальный семисегментник. В minicom'е все было нормально, а echo и моя прога, состоящая из open/write/close насчитывали только один байт (хотя write исправно докладывал, что записал столько байт, сколько просили, и даже close не возвращал ошибок)...

      В этот момент до меня начало доходить, что в миникоме и при эхо-тестировании я еще и читал из порта, а echo туда только пишет. Я добавил к прошивке подтверждение в виде "OK\n", и его чтение на стороне линухи... и, о чудо!, всё заработало. Убрал read - один байт. Вернул на место - все норм. Поменял read на sleep - все норм. Вот тут-то до меня и начало доходить, что кривые руки не у меня, а у разрабов дров...

      Покопавшись в исходниках ядра, я понял в чем было дело... Во время записи драйвер отправлял по usb весь мой пакет на конвертер, и сообщал что все зашибись. А во время закрытия дескриптора он сразу же посылал команду на отключение UART'а на конвертере. Т.к. порт работал медленно (9600), за это время на выход конвертера успевал попасть только тот самый первый байт...

      Вот такая вот история произошла из-за шестой строчки в приведенном выше коде ;)
      Ответить
      • А там есть команда "дождаться когда всё байты пройдут"?
        Ответить
        • Вот вроде
          http://govnokod.ru/14139#comment202648
          Ответить
        • В том и лол, что нету. По хорошему, имхо, дрова должны были высрать все байтики на провод, и только потом глушить UART.

          А с подтверждением от чипа ("OK\n") все стабильно работает. Да так и кошерней даже. Обидно только, что эхо не поюзать для отправки команд.
          Ответить
          • http://www.getchip.net/v-razrabotke/
            Ответить
          • хм, не соглашусь
            тебе надо закрыть девайс, и тебе пофиг, что у него осталось в буфере отправки и в каком состоянии сейчас передача
            иначе ты зашлёшь туда порноролик на 9600 бод/с и будешь ждать до скончания века
            Ответить
            • но вообще, открыть порт, кинуть одну команду, закрыть порт - это какая-то слишком ковбойская схема
              как минимум, надо дождаться хоть какого-нибудь вменяемого ACK/NAK - на последовательном порту легко проебутся байты, и эффект будет потрясающий
              и не забыть про засылку длины и подписи пакета (lrc, crc, да хоть черта лешего из xor байтов хотя бы)
              Ответить
              • > хоть какого-нибудь вменяемого ACK/NAK
                Так и сделал ;) Ну если, конечно, считать "OK\n" вменяемым.

                > и не забыть про засылку длины и подписи пакета (lrc, crc, да хоть черта лешего из xor байтов хотя бы)
                Блин, ну я же не контроллер для опускания стержней в ядерный реактор делал, и даже не прошивку для тормозной системы автомобиля... CRC, длина и Start-of-frame это конечно хорошо, но тогда теряется прелесть ручного управления через терминал :) В цисках вон нет контрольной суммы, и протокол текстовый, но вполне юзабельно.
                Ответить
                • не управлял цисками по шнурку, вот как-то не доводилось
                  а вот огребать проблемы от того, что на ком-порту включенный parity check не спасает (и, порой, даже сраный xor) - на старой работе очень часто
                  особенно было весело, когда в одном из проектов решили немного подзабить на эту особенность и управляющий справочник прилетал по ком-порту, мягко говоря, не заебись
                  Ответить
                  • [лирическое отступление]Блин, это ж какие помехи там гуляли в кабеле, чтобы пробить 12 вольт на линии (ну если не учитывать падения), мультисемплинг (даже сраная tiny2313 делает 3 замера с разбросом 1/8 бита от его центра), parity check, проверку длины и xor одновременно...[/лирическое отступление]

                    А как в итоге решили проблему? Побили справочник на более мелкие пакеты, каждый из которых был покрыт своей crc?
                    Ответить
                    • конкретно такой пиздец вспомнился в ебанистическом сетапе, включавшем COM-GPRS модем, у которого были явные проблемы с надежной доставкой пакета из сети к девайсу
                      как решили - при мне никак, т.к. сбоило лишь в 1% случаев, оставили решение на тот квартал, когда я уже успел уволиться :)
                      а в сетапах, где заранее учитывались помехи, всё разбито на мелкие пакеты, каждый из которых имеет контрольную сумму, которые затем собираются в нечто большое (например, справочник, или вообще дистрибутив софта для самообновления), для которого вцелом самостоятельно есть контрольная сумма
                      Ответить
                • Если ты про SSH то там хеши как раз есть.
                  Ответить
          • Так исправь же драйвер и пушреквест. Или хотя бы отправь багрпорт. И снизойдёт на тебя высшая кармическая благодать.
            Ответить
            • Там его если и править, то вместе с их микросхемой :) Судя по исходнику никаких сообщений, о том что все байты ушли, она не посылает. Поэтому драйвер никогда не узнает о том, ушли ли байты в порт. Ну и соответственно пофиксить его нельзя.
              Ответить
          • "В том и лол, что нету."

            Не гони. Она есть. На файл дескриптор ставишь флаг O_SYNC.

            Пару дров которые я писал в прошлом именно так это и поддерживали. И если я правильно помню, то я об этом не сам догадался - а где-то подсмотрел.

            И к слову. Именно с синком, close() тоже имеет право становится блокирующим что бы дождатся окончания всех pending операций и вернуть в rc их статус.

            Ну да твою проблему с echo это не решит, потому что там некому поставить O_SYNC на дескриптор. Надо кастом прогу писать для этой цели.
            Ответить
            • > На файл дескриптор ставишь флаг O_SYNC.
              Ну так это надо чтобы дрова поддерживали. Вроде бы я даже пробовал этот O_SYNC, но точно не помню... Завтра, если не будет лень, восстановлю макетку, попробую. fsync() тоже пробовал, думал подвесится, пока все не уйдет - фиг там.

              > Надо кастом прогу писать для этой цели.
              Ну да. А с кастомной прогой проблемы и нет, там подтверждение можно замутить, и труба раньше времени уже не закроется ;)
              Ответить
              • > Ну так это надо чтобы дрова поддерживали.

                > > http://lxr.free-electrons.com/source/drivers/usb/serial/generic.c#L103

                Ндя. В старые времена вроде бы как для char device'ов это было нормой O_SYNC поддерживать. Ну да хез какие соглашения для USB устройств.

                Ну да в добавок это как бы традиционные серийные устройства с которыми я никогда в глубину не работал. (*) Если я не ошибаюсь они тоже синка не поддерживали, а вместо этого буффер на 1 байт ставили. Это вроде tcsetattr()/друзья. Трэйсни minicom (strace на линухе) глять что он там с устройством делает.

                (*) Я как раз для серийных девайсов и делал сетевой интерфейс что бы можно было connect()/bind()/send()/recv() делать. Но там те же грабли были, потому что серийную линию надо на самом деле сбрасывать, потому что приложение ее может слегка подкофигурить, и без сброса, старые настройки будут действительны для ново-подключившихся приложений. Вообщем темный лес.
                Ответить
                • Надо будет вот эту штуку еще попробовать: tcdrain() waits until all output written to the object referred to by fd has been transmitted. Судя по ману - то что надо ;)

                  P.S. Судя по сырцам она кидает TCSBRK, который делает tty_wait_until_sent, который вызывает "метод" wait_until_set у tty драйвера, который реализован в generic usb serial. Т.е. может даже проканать :)
                  Ответить
                  • У tty_wait_until_sent() в комменте есть воодушевляющая фраза про "hit the wire" :)

                    Завтра вечерком восстановлю макетку, попробую tcdrain() перед close() сделать.
                    Ответить
                    • Блин, как я рад что мне ни разу это реализовывать не приходилось.

                      В какой-то степени теперь я понимаю почему в кернел это долго отказывались включать: потому что интерфейс дерьмо и его никто не фиксит.

                      У меня с сетевым интерфейсом было все жутко просто: bind() или connect() со спец PF_BLAHBLAH и настройками в спец sockaddr, а дальше send()/recv() со всеми примочками работли как обычно для DGRAM сокетов, включая SYNC и PEEK.
                      Ответить
                      • Ну а кстати, а почему бы просто не поднять slip или ppp поверх ком-порта? Должно же работать, и все протоколы при месте... Или на том конце девайс был не линуксовый, и писать ppp для него было бы оверкиллом?
                        Ответить
                        • "Должно же работать, и все протоколы при месте... "

                          Там было не все так просто. 32 порта на контроллер, 4ре контроллера на борде, и частичная хардварная поддержка MTP2/MTP3 (из SS7) + мои кернеловы приблуды для полноты MTP3 (если сокет в DGRAM режиме, иначе, в STREAM, просто сырая серийная линия).

                          http://en.wikipedia.org/wiki/E1-carrier#E1_frame_structure

                          "Или на том конце девайс был не линуксовый [...]?"

                          Если не ошибаюсь, на другом конце того что я писал, типично находлся либо HLR, либо ISDN клиент. :)
                          Ответить
      • прошел 4 раза мимо, на пятный вник в суть. Даа, прогресс налицо.
        В мое время логические элементы составляли сами - на транзисторах. Спаиваешь их по схеме, и готов либо усилитель НЧ либо радиоприемник. Сейчас же берут готовую микросхему, вбивают в нее код - и вуаля. Транзисторы нах никому не нужны.
        Ответить
        • >В мое время
          Седомудый в треде.
          Ответить
          • Не такой уж я и "седомудый", но транзисторы застал. Я этим занимался вплоть до 2000 года, когда вовсю уже свирепствовали микросхемы.
            Ответить
            • Я тоже застал, когда на радиокружок ходил, но мы там делали элементарщину.
              Ответить
              • > мы там делали элементарщину
                Мультивибраторы и приемники?
                Ответить
                • утюги и кассетные магнитафоны?
                  Ответить
                • Ты чо, приёмник это огого. Стробоскоп, еще какую-то хрень.
                  Ответить
                  • Опять скриптами минусуют?
                    Ответить
                    • Нет, серьезно, анонимб, это уже паранойя.
                      Ответить
                  • Это ж самое элементарное - собрать детекторный приемник или приемник хотя бы на 1 транзисторе. Помню, как то вместо мп39 я впаял в схему полевой транзистор - мощность была значительной.
                    Ответить
                    • Точно, детекторный был, я его не собирал ибо там была нужна антенна-растяжка.

                      Я вообще ничего не собрал, кроме мигалки на мультивибраторе по книжке. Зато от нехуй делать распаивал телевизоры на детали.
                      Ответить
                • гг, слыш, бивис, он сказал вибратор
                  Ответить
      • "А во время закрытия дескриптора [...]"

        Гыгы. Пока на эти грябли не наступишь - настоящим системщиком не станешь!
        Ответить
    • http://habrahabr.ru/post/203276/

      борманд, это про тебя
      Ответить

    Добавить комментарий