/* * bfb/bfb.c: BFB transport encapsulation (used for Siemens mobile equipment) * * Copyright (c) 2002 Christian W. Zuckschwerdt * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* * v0.1: Die, 5 Feb 2002 22:46:19 +0100 * v0.4: Don, 25 Jul 2002 03:16:41 +0200 */ #include #include #include #ifndef S_SPLINT_S #include #endif /* htons */ #ifdef _WIN32 #include #else #include #endif #include "crc.h" #include "bfb.h" #include "common.h" /* Provide convenience macros for handling structure * fields through their offsets. */ #define STRUCT_OFFSET(struct_type, member) \ ((uint64_t) ((char*) &((struct_type*) 0)->member)) #define STRUCT_MEMBER_P(struct_p, struct_offset) \ ((void *) ((char*) (struct_p) + (uint64_t) (struct_offset))) #define STRUCT_MEMBER(member_type, struct_p, struct_offset) \ (*(member_type*) STRUCT_MEMBER_P ((struct_p), (struct_offset))) /* mobile need little-endianess always */ /* Solaris: _LITTLE_ENDIAN / _BIG_ENDIAN */ /* Linux: */ #if (BYTE_ORDER == LITTLE_ENDIAN) || defined (_LITTLE_ENDIAN) #define htoms(A) (A) #define htoml(A) (A) #define mtohs(A) (A) #define mtohl(A) (A) #elif (BYTE_ORDER == BIG_ENDIAN) || defined (_BIG_ENDIAN) #define htoms(A) ((((uint16_t)(A) & 0xff00) >> 8) | \ (((uint16_t)(A) & 0x00ff) << 8)) #define htoml(A) ((((uint32_t)(A) & 0xff000000) >> 24) | \ (((uint32_t)(A) & 0x00ff0000) >> 8) | \ (((uint32_t)(A) & 0x0000ff00) << 8) | \ (((uint32_t)(A) & 0x000000ff) << 24)) #define mtohs htoms #define mtohl htoml #else #error "BYTE_ORDER needs to be either BIG_ENDIAN or LITTLE_ENDIAN." #endif /* returns the whole buffer folded with xor. */ uint8_t bfb_checksum(uint8_t *data, int len) { int i; uint8_t chk = 0; for (i=0; i < len; i++) chk ^= data[i]; return chk; } /* stuff data frame into serial cable encapsulation */ /* buffer needs to be of at leaset len+7 size */ /* Type 0x01: "prepare" command. */ /* Type 0x02: first transmission in a row. */ /* Type 0x03: continued transmission. */ /* seq needs to be incremented afterwards. */ int bfb_stuff_data(uint8_t *buffer, uint8_t type, uint8_t *data, int len, int seq) { int i; union { uint16_t value; uint8_t bytes[2]; } fcs; /* special case: "attention" packet */ if (type == 1) { buffer[0] = 0x01; buffer[1] = ~buffer[0]; return 2; } /* error */ if (type != 2 && type != 3) { return 0; } buffer[0] = type; buffer[1] = ~buffer[0]; buffer[2] = seq; fcs.value = htons(len); buffer[3] = fcs.bytes[0]; buffer[4] = fcs.bytes[1]; /* copy data */ memcpy(&buffer[5], data, len); /* gen CRC */ fcs.value = INIT_FCS; for (i=2; i < len+5; i++) { fcs.value = irda_fcs(fcs.value, buffer[i]); } fcs.value = ~fcs.value; /* append CRC to packet */ /* fcs.value = htons(fcs.value); */ buffer[len+5] = fcs.bytes[0]; buffer[len+6] = fcs.bytes[1]; return len+7; } /* send a cmd, subcmd packet, add chk (no parameters) */ int bfb_write_subcmd(fd_t fd, uint8_t type, uint8_t subtype) { uint8_t buffer[2]; buffer[0] = subtype; buffer[1] = bfb_checksum(buffer, 1); return bfb_write_packets(fd, type, buffer, 2); } /* send a cmd, subcmd packet */ int bfb_write_subcmd0(fd_t fd, uint8_t type, uint8_t subtype) { return bfb_write_packets(fd, type, &subtype, 1); } /* send a cmd, subcmd, data packet */ int bfb_write_subcmd8(fd_t fd, uint8_t type, uint8_t subtype, uint8_t p1) { uint8_t buffer[2]; buffer[0] = subtype; buffer[1] = p1; return bfb_write_packets(fd, type, buffer, 2); } /* send a cmd, subcmd packet, add chk (one word parameter) */ int bfb_write_subcmd1(fd_t fd, uint8_t type, uint8_t subtype, uint16_t p1) { uint8_t buffer[4]; buffer[0] = subtype; p1 = htoms(p1); /* mobile need little-endianess always */ buffer[1] = STRUCT_MEMBER(uint8_t, &p1, 0); buffer[2] = STRUCT_MEMBER(uint8_t, &p1, 1); buffer[3] = bfb_checksum(buffer, 3); DEBUG(3, "buf: %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3]); return bfb_write_packets(fd, type, buffer, 4); } /* send a cmd, subcmd packet, add chk (two word parameter) */ int bfb_write_subcmd2(fd_t fd, uint8_t type, uint8_t subtype, uint16_t p1, uint16_t p2) { uint8_t buffer[6]; buffer[0] = subtype; p1 = htoms(p1); /* mobile need little-endianess always */ buffer[1] = STRUCT_MEMBER(uint8_t, &p1, 0); buffer[2] = STRUCT_MEMBER(uint8_t, &p1, 1); p2 = htoms(p2); /* mobile need little-endianess always */ buffer[3] = STRUCT_MEMBER(uint8_t, &p2, 0); buffer[4] = STRUCT_MEMBER(uint8_t, &p2, 1); buffer[5] = bfb_checksum(buffer, 5); DEBUG(3, "buf: %x %x %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); return bfb_write_packets(fd, type, buffer, 6); } /* send a cmd, subcmd packet, add chk (three word parameter) */ int bfb_write_subcmd3(fd_t fd, uint8_t type, uint8_t subtype, uint16_t p1, uint16_t p2, uint16_t p3) { uint8_t buffer[8]; buffer[0] = subtype; p1 = htoms(p1); /* mobile need little-endianess always */ buffer[1] = STRUCT_MEMBER(uint8_t, &p1, 0); buffer[2] = STRUCT_MEMBER(uint8_t, &p1, 1); p2 = htoms(p2); /* mobile need little-endianess always */ buffer[3] = STRUCT_MEMBER(uint8_t, &p2, 0); buffer[4] = STRUCT_MEMBER(uint8_t, &p2, 1); p3 = htoms(p3); /* mobile need little-endianess always */ buffer[5] = STRUCT_MEMBER(uint8_t, &p3, 0); buffer[6] = STRUCT_MEMBER(uint8_t, &p3, 1); buffer[7] = bfb_checksum(buffer, 7); DEBUG(3, "buf: %x %x %x %x %x %x %x %x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]); return bfb_write_packets(fd, type, buffer, 8); } /* send a cmd, subcmd packet, add long, word parameter */ int bfb_write_subcmd_lw(fd_t fd, uint8_t type, uint8_t subtype, uint32_t p1, uint16_t p2) { uint8_t buffer[8]; buffer[0] = subtype; p1 = htoml(p1); /* mobile need little-endianess always */ buffer[1] = STRUCT_MEMBER(uint8_t, &p1, 0); buffer[2] = STRUCT_MEMBER(uint8_t, &p1, 1); buffer[3] = STRUCT_MEMBER(uint8_t, &p1, 2); buffer[4] = STRUCT_MEMBER(uint8_t, &p1, 3); p2 = htoms(p2); /* mobile need little-endianess always */ buffer[5] = STRUCT_MEMBER(uint8_t, &p2, 0); buffer[6] = STRUCT_MEMBER(uint8_t, &p2, 1); buffer[7] = bfb_checksum(buffer, 7); DEBUG(3, "buf: %02x %02x %02x %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6]); return bfb_write_packets(fd, type, buffer, 7); /* no chk? */ } /* send actual packets */ int bfb_write_packets(fd_t fd, uint8_t type, uint8_t *buffer, int length) { bfb_frame_t *frame; int i; int l; #ifdef _WIN32 DWORD actual; #else int actual; #endif #ifdef _WIN32 return_val_if_fail (fd != INVALID_HANDLE_VALUE, FALSE); #else return_val_if_fail (fd > 0, FALSE); #endif /* alloc frame buffer */ frame = malloc((length > MAX_PACKET_DATA ? MAX_PACKET_DATA : length) + sizeof (bfb_frame_t)); if (frame == NULL) return -1; for(i=0; i MAX_PACKET_DATA) l = MAX_PACKET_DATA; frame->type = type; frame->len = l; frame->chk = frame->type ^ frame->len; memcpy(frame->payload, &buffer[i], l); /* actual = bfb_io_write(fd, frame, l + sizeof (bfb_frame_t)); */ #ifdef _WIN32 if(!WriteFile(fd, frame, l + sizeof (bfb_frame_t), &actual, NULL)) DEBUG(2, "%s() Write error: %ld\n", __func__, actual); DEBUG(3, "%s() Wrote %ld bytes (expected %d)\n", __func__, actual, l + sizeof (bfb_frame_t)); #else actual = write(fd, frame, l + sizeof (bfb_frame_t)); DEBUG(3, "%s() Wrote %d bytes (expected %d)\n", __func__, actual, l + sizeof (bfb_frame_t)); #endif if (actual < 0 || actual < l + sizeof (bfb_frame_t)) { DEBUG(1, "%s() Write failed\n", __func__); free(frame); return -1; } } free(frame); return i / MAX_PACKET_DATA; } int bfb_send_data(fd_t fd, uint8_t type, uint8_t *data, int length, int seq) { uint8_t *buffer; int actual; buffer = malloc(length + 7); if (buffer == NULL) return -1; actual = bfb_stuff_data(buffer, type, data, length, seq); DEBUG(3, "%s() Stuffed %d bytes\n", __func__, actual); actual = bfb_write_packets(fd, BFB_FRAME_DATA, buffer, actual); DEBUG(3, "%s() Wrote %d packets\n", __func__, actual); free(buffer); return actual; } /* retrieve actual packets */ /*@null@*/ bfb_frame_t *bfb_read_packets(uint8_t *buffer, int *length) { bfb_frame_t *frame; int l; DEBUG(3, "%s() \n", __func__); if (*length < 0) { DEBUG(1, "%s() Wrong length?\n", __func__); return NULL; } if (*length == 0) { DEBUG(1, "%s() No packet?\n", __func__); return NULL; } if (*length < sizeof(bfb_frame_t)) { DEBUG(1, "%s() Short packet?\n", __func__); return NULL; } /* temp frame */ frame = (bfb_frame_t *)buffer; if ((frame->type ^ frame->len) != frame->chk) { DEBUG(1, "%s() Header error?\n", __func__); DEBUGBUFFER(buffer, *length); return NULL; } if (*length < frame->len + sizeof(bfb_frame_t)) { DEBUG(2, "%s() Need more data?\n", __func__); return NULL; } /* copy frame from buffer */ l = sizeof(bfb_frame_t) + frame->len; frame = malloc(l); if (frame == NULL) return NULL; memcpy(frame, buffer, l); /* remove frame from buffer */ *length -= l; memmove(buffer, &buffer[l], *length); DEBUG(3, "%s() Packet 0x%02x (%d bytes)\n", __func__, frame->type, frame->len); return frame; } /*@null@*/ bfb_data_t *bfb_assemble_data(bfb_data_t **data, int *size, int *len, bfb_frame_t *frame) { bfb_data_t *tmp; int l; DEBUG(3, "%s() \n", __func__); if (frame->type != BFB_FRAME_DATA) { DEBUG(1, "%s() Wrong frame type (0x%02x)?\n", __func__, frame->type); return *data; } /* temp data */ tmp = (bfb_data_t *)frame->payload; if ((*len == 0) && (tmp->cmd == BFB_DATA_ACK)) { DEBUG(3, "%s() Skipping ack\n", __func__); return *data; } /* copy frame from buffer */ DEBUG(3, "%s() data: %d + frame: %d\n", __func__, *len, frame->len); l = *len + frame->len; if (l > *size) { DEBUG(1, "%s() data buffer to small, realloc'ing\n", __func__); *data = realloc(*data, l); *size = l; } /* memcpy(ret, *data, *len); */ memcpy(&((uint8_t *)*data)[*len], frame->payload, frame->len); /* free(*data); */ *len = l; return *data; } int bfb_check_data(bfb_data_t *data, int len) { int i; union { uint16_t value; uint8_t bytes[2]; } l; DEBUG(3, "%s() \n", __func__); if (data == NULL) return -1; if (len < sizeof(bfb_data_t)) return 0; if (data->cmd != (uint8_t)~data->chk) { DEBUG(1, "%s() Broken data? 0x%02x, 0x%02x\n", __func__, data->cmd, (uint8_t)~data->chk); DEBUGBUFFER(data, len); } DEBUG(3, "%s() cmd: 0x%02x, chk: 0x%02x, seq: %d\n", __func__, data->cmd, data->chk, data->seq); if ((data->cmd != BFB_DATA_FIRST) && (data->cmd != BFB_DATA_NEXT)) { DEBUG(1, "%s() Wrong data type (0x%02x)?\n", __func__, data->cmd); return -1; } l.bytes[0] = data->len0; l.bytes[1] = data->len1; l.value = htons(l.value); DEBUG(3, "%s() fragment size %d, payload %d of indicated %d\n", __func__, len, len-sizeof(bfb_data_t), l.value); if (len-(int)sizeof(bfb_data_t) < (int)(l.value) + /*crc*/ 2) return 0; /* check CRC */ l.value = INIT_FCS; for (i=2; i < len-2; i++) { l.value = irda_fcs(l.value, ((uint8_t *)data)[i]); } l.value = ~l.value; if ((((uint8_t *)data)[len-2] != l.bytes[0]) || (((uint8_t *)data)[len-1] != l.bytes[1])) { DEBUG(1, "%s() CRC-ERROR %02x %02x vs %02x %02x\n", __func__, ((uint8_t *)data)[len-2], ((uint8_t *)data)[len-1], l.bytes[0], l.bytes[1]); } DEBUG(2, "%s() data ready!\n", __func__); return 1; }