Подаруночок від FTDI

Подаруночок від FTDI виявився дуже цікавим.

У повній відповідності до принципу «дивись рисунок 1» вони взяли і замінили типи даних при переході від бібліотек libftd2xx.so версій 0.4.x до libftd2xx.so.1.0.0. Подаруночок впливає лише на роботу з 64-бітними варіантами бібліотек, з 32-бітними проблем нема.

Бібліотека ftd2xx народжувалася в Windows та ще й містить в собі функції FT_W32_ххх() для роботи з мікросхемами FTDI в стилі функцій роботи з COM-портом у WinAPI. Тому в ній використовуются стандартні для WinAPI типи даних DWORD, BYTE тощо. Щоб при написанні Linux-версії бібліотеки не міняти файл ftd2xx.h та всі тексти, в проект було додано файл WinTypes.h з означеннями потрібних типів. Загалом, крок логічний, але…

Ось фрагмент файлу WinTypes.h від libftd2xx версії 0.4.16:

typedef unsigned long    DWORD;
typedef unsigned long    ULONG;
typedef long             LONG;

Тобто просто взято типи з файлу windef.h компіляторів для Windows.

А ось те ж саме місце з версії 1.0.0:

typedef unsigned int    DWORD;
typedef unsigned int    ULONG;
typedef int             LONG;

Для Windows, в тому числі 64-бітних, різниці між цими означеннями на практиці нема, бо в Windows-64 використовється так звана модель даних LLP64 (64-бітними є лише типи long long та pointer-types). При цьому sizeof(long) == sizeof(int), обидва ці типи є 32-бітними. Так само буде і в 32-бітному Linux. В цих операційних системах стара версія avreal працюваиме з новою бібліотекою.

А от у 64-біт Linux використовується модель LP64, тобто 64-бітним є також тип long.

Отже, для libftd2xx.so 0.4.x та 64-біт Linux типи DWORD, ULONG, LONG 64-біті, тобто займають вдвічі більше місця, ніж в 32-бітній версії. Але, оскільки і бібліотека, і програма компілюються з одним і тим же заголовочним файлом, все працює правильно.

Для libftd2xx 1.0.0 розміри типів в 32- та 64-бітних версіях ОС співпадають, вони скрізь 32-бітні. Теж нічого поганого, якщо бібліотека та програма компілюються з одними й тими ж типами даних.

Але якщо avreal-у, зібраному зі старим заголовочним файлом, підсунути нову бібліотеку, то працювати програма не буде. Розглянемо такий виклик:

    DWORD chip_type, chip_id, status;
    char serno[16];
    char description[64];

    status = FT_GetDeviceInfo(handle, &chip_type, &chip_id, serno, description, 0);

В старій програмі змінні chip_type, chip_id 64-бітні. Нова бібліотека запише 32-бітні дані в молодші половинки цих змінних. Якби я писав трохи акуратніше (параноїдальніше?) і ініціалізував ці змінні нулями, то стара програма навіть не помітила б, що нова бібліотека вважала ці змінні 32-бітними (подякуємо x86 за те, що він є low-endian процесором). Але користі з того було б мало, бо в бібліотеці є ще такий тип даних:

typedef struct _ft_device_list_info_node {
    ULONG Flags;
    ULONG Type;
    ULONG ID;
    DWORD LocId;
    char SerialNumber[16];
    char Description[64];
    FT_HANDLE ftHandle;
} FT_DEVICE_LIST_INFO_NODE;

Ця структура займає 120 байт при роботі старої версії бібліотеки та 104 байти при роботі нової версії. Функція побудови списку підключених пристроїв FT_GetDeviceInfoList() приймає вказівник на масив таких структур. Зрозуміло, що це місце не працюватиме в будь-якому випадку.

Гірше буде, якщо зібрати нову версію avreal з новим заголовочним файлом, але у користувача на комп’ютері буде стара бібліотека. В усі змінні бібліотека писатиме більше, ніж розмір змінних, затираючи при цьому вміст сусідніх змінних. В кращому випадку просто не працюватиме, в гіршому буде «segmentation fault».

Тому поки-що я не збиратиму avreal з новою бібліотекою і для 64-біт Linux пропоную брати стару версію libftd2xx 0.4.16 у мене на сторінці download, поруч з архівом avreal.

Я подумаю, що я маю робити з цим подарунком від FTDI. Можливо, починаючи з якоїсь версії avreal, перейти на новий варіант бібліотеки і всі, хто оновлює avreal повинні будуть замінити і бібліотеку. Можливо, я спробую зробити вибір одного з двох режимів роботи залежно від версії бібліотеки, яку поверне FT_GetLibraryVersion().

3 Responses to “Подаруночок від FTDI”

  1. goblin says:


    и для чего существуют uint8_t .. uint64_t ?

  2. Dekar says:

    А использовать открытую libftdi?

    • ReAl says:

      Так про неё smartly уже сказал по соседству.
      Можно, и в том направлении я буду двигаться малой скоростью.
      Но и с libfd2xx особых проблем нет — когда стало ясно, в чём дело.
      В любом случае это будет не «перейти», а «поддерживать и», так как совсем отказываться от libftd2xx я не собираюсь.

Leave a Reply

[flagcounter image]