Переносимое преобразование данных с помощью API ядра Linux - PullRequest
0 голосов
/ 28 мая 2018

Как я могу улучшить следующий код, то есть сделать его более надежным в отношении безопасности типов и порядка байтов, используя функции и макросы в API ядра Linux?Например, в следующем примере src_data представляет собой массив из двух 16-разрядных целых чисел со знаком (обычно хранящихся в порядке с прямым порядком байтов) и должен быть отправлен через UART в порядке байтов с прямым порядком байтов.

s16 src_data[2] = {...}; /* note: this is signed data! */
u8 tx_data[4];

u8* src_data_u8 = (u8*)src_data;

tx_data[0] = src_data_u8[1];
tx_data[1] = src_data_u8[0];
tx_data[2] = src_data_u8[3];
tx_data[3] = src_data_u8[2];

Я думаю, что функции cpu_to_be16 и cpu_to_be16p должны играть роль в выполнении этого преобразования.Хотя я не уверен, как я могу использовать их так, чтобы это было безопасно и устойчиво к порядку байтов.

Ответы [ 3 ]

0 голосов
/ 28 мая 2018

Ваша проблема с безопасностью заключается в том, что функция / макрос htons(x) ожидает целое число без знака, но у вас есть целое число со знаком.Не проблема:

union {
    int16_t signed_repr;
    uint16_t unsigned_repr;
} data;

data.signed_repr = ...;

u16 unsigned_big_endian_data = htons(data.unsigned_repr);

memcpy(tx_data, &unsigned_big_endian_data,
       min(sizeof tx_data, sizeof unsigned_big_endian_data));

PS.Наказание по типу с помощью союзов идеально определено .

0 голосов
/ 06 июня 2018

Я считаю, что это один из лучших ответов на мой вопрос.Я использовал ссылки, предоставленные @ 0andriy, на существующие примеры в исходном коде ядра.

Преобразование 16-разрядного значения со знаком для передачи

s16 src = -5;
u8 dst[2];
__be16 tx_buf;
*(__be16*)dst = cpu_to_be16(src);

Преобразование 16-разрядных значений со знаком дляпередача

s16 src[2] = {-5,-2};
u8 dst[4];
s16* psrc = src;
u8* pdst = dst;
int len = sizeof(src);

for ( ; len > 1; len -= 2) {
    *(__be16 *)pdst = cpu_to_be16p(psrc++);
    pdst += 2;
}

Быстрый отказ от ответственности, мне все еще нужно проверить, корректен ли этот код / ​​компилируется.

В целом, я немного недоволен решением для копирования и преобразования порядка байтовнескольких значений, так как он подвержен опечаткам и может быть легко внедрен в макрос.

0 голосов
/ 28 мая 2018

Если машина Linux всегда будет с прямым порядком байтов, а протокол всегда будет с прямым порядком байтов, тогда код работает нормально, и вам не нужно ничего менять.

Если вам по какой-то причине необходимосделав код Linux независимым от байтов, тогда вы будете использовать:

tx_data[0] = ((unsigned int)src_data[0] >> 8) & 0xFF;
tx_data[1] = ((unsigned int)src_data[0] >> 0) & 0xFF;
tx_data[2] = ((unsigned int)src_data[1] >> 8) & 0xFF;
tx_data[3] = ((unsigned int)src_data[1] >> 0) & 0xFF;

Где приведение приведено, чтобы гарантировать, что сдвиги вправо не выполняются для подписанного типа, что вызовет непереносимую реализациюопределенное поведение.

Преимущество сдвигов битов по сравнению с любой другой версией заключается в том, что они работают на уровне абстракции выше аппаратного и порядкового номера, позволяя конкретному компилятору генерировать инструкции для базового доступа к памяти.Код, такой как u16 >> 8, всегда означает «дать мне младший байт» независимо от того, где этот байт хранится в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...