123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /* Copyright (c) 2008-2009, 2012-2013, The Linux Foundation.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- *
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/cdev.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/uaccess.h>
- #include <linux/ratelimit.h>
- #include <linux/crc-ccitt.h>
- #include "diagchar_hdlc.h"
- #include "diagchar.h"
- MODULE_LICENSE("GPL v2");
- #define CRC_16_L_SEED 0xFFFF
- #define CRC_16_L_STEP(xx_crc, xx_c) \
- crc_ccitt_byte(xx_crc, xx_c)
- void diag_hdlc_encode(struct diag_send_desc_type *src_desc,
- struct diag_hdlc_dest_type *enc)
- {
- uint8_t *dest;
- uint8_t *dest_last;
- const uint8_t *src;
- const uint8_t *src_last;
- uint16_t crc;
- unsigned char src_byte = 0;
- enum diag_send_state_enum_type state;
- unsigned int used = 0;
- if (src_desc && enc) {
- /* Copy parts to local variables. */
- src = src_desc->pkt;
- src_last = src_desc->last;
- state = src_desc->state;
- dest = enc->dest;
- dest_last = enc->dest_last;
- if (state == DIAG_STATE_START) {
- crc = CRC_16_L_SEED;
- state++;
- } else {
- /* Get a local copy of the CRC */
- crc = enc->crc;
- }
- /* dest or dest_last may be NULL to trigger a
- state transition only */
- if (dest && dest_last) {
- /* This condition needs to include the possibility
- of 2 dest bytes for an escaped byte */
- while (src <= src_last && dest <= dest_last) {
- src_byte = *src++;
- if ((src_byte == CONTROL_CHAR) ||
- (src_byte == ESC_CHAR)) {
- /* If the escape character is not the
- last byte */
- if (dest != dest_last) {
- crc = CRC_16_L_STEP(crc,
- src_byte);
- *dest++ = ESC_CHAR;
- used++;
- *dest++ = src_byte
- ^ ESC_MASK;
- used++;
- } else {
- src--;
- break;
- }
- } else {
- crc = CRC_16_L_STEP(crc, src_byte);
- *dest++ = src_byte;
- used++;
- }
- }
- if (src > src_last) {
- if (state == DIAG_STATE_BUSY) {
- if (src_desc->terminate) {
- crc = ~crc;
- state++;
- } else {
- /* Done with fragment */
- state = DIAG_STATE_COMPLETE;
- }
- }
- while (dest <= dest_last &&
- state >= DIAG_STATE_CRC1 &&
- state < DIAG_STATE_TERM) {
- /* Encode a byte of the CRC next */
- src_byte = crc & 0xFF;
- if ((src_byte == CONTROL_CHAR)
- || (src_byte == ESC_CHAR)) {
- if (dest != dest_last) {
- *dest++ = ESC_CHAR;
- used++;
- *dest++ = src_byte ^
- ESC_MASK;
- used++;
- crc >>= 8;
- } else {
- break;
- }
- } else {
- crc >>= 8;
- *dest++ = src_byte;
- used++;
- }
- state++;
- }
- if (state == DIAG_STATE_TERM) {
- if (dest_last >= dest) {
- *dest++ = CONTROL_CHAR;
- used++;
- state++; /* Complete */
- }
- }
- }
- }
- /* Copy local variables back into the encode structure. */
- enc->dest = dest;
- enc->dest_last = dest_last;
- enc->crc = crc;
- src_desc->pkt = src;
- src_desc->last = src_last;
- src_desc->state = state;
- }
- return;
- }
- int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc)
- {
- uint8_t *src_ptr = NULL, *dest_ptr = NULL;
- unsigned int src_length = 0, dest_length = 0;
- unsigned int len = 0;
- unsigned int i;
- uint8_t src_byte;
- int pkt_bnd = 0;
- int msg_start;
- if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
- (hdlc->src_size > hdlc->src_idx) &&
- (hdlc->dest_size > hdlc->dest_idx)) {
- msg_start = (hdlc->src_idx == 0) ? 1 : 0;
- src_ptr = hdlc->src_ptr;
- src_ptr = &src_ptr[hdlc->src_idx];
- src_length = hdlc->src_size - hdlc->src_idx;
- dest_ptr = hdlc->dest_ptr;
- dest_ptr = &dest_ptr[hdlc->dest_idx];
- dest_length = hdlc->dest_size - hdlc->dest_idx;
- for (i = 0; i < src_length; i++) {
- src_byte = src_ptr[i];
- if (hdlc->escaping) {
- dest_ptr[len++] = src_byte ^ ESC_MASK;
- hdlc->escaping = 0;
- } else if (src_byte == ESC_CHAR) {
- if (i == (src_length - 1)) {
- hdlc->escaping = 1;
- i++;
- break;
- } else {
- dest_ptr[len++] = src_ptr[++i]
- ^ ESC_MASK;
- }
- } else if (src_byte == CONTROL_CHAR) {
- if (msg_start && i == 0 && src_length > 1)
- continue;
- /* Byte 0x7E will be considered
- as end of packet */
- dest_ptr[len++] = src_byte;
- i++;
- pkt_bnd = 1;
- break;
- } else {
- dest_ptr[len++] = src_byte;
- }
- if (len >= dest_length) {
- i++;
- break;
- }
- }
- hdlc->src_idx += i;
- hdlc->dest_idx += len;
- }
- return pkt_bnd;
- }
- int crc_check(uint8_t *buf, uint16_t len)
- {
- uint16_t crc = CRC_16_L_SEED;
- uint8_t sent_crc[2] = {0, 0};
- /*
- * The minimum length of a valid incoming packet is 4. 1 byte
- * of data and 3 bytes for CRC
- */
- if (!buf || len < 4) {
- pr_err_ratelimited("diag: In %s, invalid packet or length, buf: 0x%x, len: %d",
- __func__, (int)buf, len);
- return -EIO;
- }
- /*
- * Run CRC check for the original input. Skip the last 3 CRC
- * bytes
- */
- crc = crc_ccitt(crc, buf, len-3);
- crc ^= CRC_16_L_SEED;
- /* Check the computed CRC against the original CRC bytes. */
- sent_crc[0] = buf[len-3];
- sent_crc[1] = buf[len-2];
- if (crc != *((uint16_t *)sent_crc)) {
- pr_debug("diag: In %s, crc mismatch. expected: %x, sent %x.\n",
- __func__, crc, *((uint16_t *)sent_crc));
- return -EIO;
- }
- return 0;
- }
|