1 /* 2 * Copyright (C) ST-Ericsson AB 2010 3 * Author: Sjur Brendeland/sjur.brandeland@stericsson.com 4 * License terms: GNU General Public License (GPL) version 2 5 */ 6 7 #include <linux/stddef.h> 8 #include <linux/spinlock.h> 9 #include <linux/slab.h> 10 #include <net/caif/caif_layer.h> 11 #include <net/caif/cfpkt.h> 12 #include <net/caif/cfserl.h> 13 14 #define container_obj(layr) ((struct cfserl *) layr) 15 16 #define CFSERL_STX 0x02 17 #define SERIAL_MINIUM_PACKET_SIZE 4 18 #define SERIAL_MAX_FRAMESIZE 4096 19 struct cfserl { 20 struct cflayer layer; 21 struct cfpkt *incomplete_frm; 22 /* Protects parallel processing of incoming packets */ 23 spinlock_t sync; 24 bool usestx; 25 }; 26 #define STXLEN(layr) (layr->usestx ? 1 : 0) 27 28 static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt); 29 static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt); 30 static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 31 int phyid); 32 33 struct cflayer *cfserl_create(int type, int instance, bool use_stx) 34 { 35 struct cfserl *this = kmalloc(sizeof(struct cfserl), GFP_ATOMIC); 36 if (!this) { 37 pr_warning("CAIF: %s(): Out of memory\n", __func__); 38 return NULL; 39 } 40 caif_assert(offsetof(struct cfserl, layer) == 0); 41 memset(this, 0, sizeof(struct cfserl)); 42 this->layer.receive = cfserl_receive; 43 this->layer.transmit = cfserl_transmit; 44 this->layer.ctrlcmd = cfserl_ctrlcmd; 45 this->layer.type = type; 46 this->usestx = use_stx; 47 spin_lock_init(&this->sync); 48 snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1"); 49 return &this->layer; 50 } 51 52 static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt) 53 { 54 struct cfserl *layr = container_obj(l); 55 u16 pkt_len; 56 struct cfpkt *pkt = NULL; 57 struct cfpkt *tail_pkt = NULL; 58 u8 tmp8; 59 u16 tmp; 60 u8 stx = CFSERL_STX; 61 int ret; 62 u16 expectlen = 0; 63 64 caif_assert(newpkt != NULL); 65 spin_lock(&layr->sync); 66 67 if (layr->incomplete_frm != NULL) { 68 layr->incomplete_frm = 69 cfpkt_append(layr->incomplete_frm, newpkt, expectlen); 70 pkt = layr->incomplete_frm; 71 if (pkt == NULL) { 72 spin_unlock(&layr->sync); 73 return -ENOMEM; 74 } 75 } else { 76 pkt = newpkt; 77 } 78 layr->incomplete_frm = NULL; 79 80 do { 81 /* Search for STX at start of pkt if STX is used */ 82 if (layr->usestx) { 83 cfpkt_extr_head(pkt, &tmp8, 1); 84 if (tmp8 != CFSERL_STX) { 85 while (cfpkt_more(pkt) 86 && tmp8 != CFSERL_STX) { 87 cfpkt_extr_head(pkt, &tmp8, 1); 88 } 89 if (!cfpkt_more(pkt)) { 90 cfpkt_destroy(pkt); 91 layr->incomplete_frm = NULL; 92 spin_unlock(&layr->sync); 93 return -EPROTO; 94 } 95 } 96 } 97 98 pkt_len = cfpkt_getlen(pkt); 99 100 /* 101 * pkt_len is the accumulated length of the packet data 102 * we have received so far. 103 * Exit if frame doesn't hold length. 104 */ 105 106 if (pkt_len < 2) { 107 if (layr->usestx) 108 cfpkt_add_head(pkt, &stx, 1); 109 layr->incomplete_frm = pkt; 110 spin_unlock(&layr->sync); 111 return 0; 112 } 113 114 /* 115 * Find length of frame. 116 * expectlen is the length we need for a full frame. 117 */ 118 cfpkt_peek_head(pkt, &tmp, 2); 119 expectlen = le16_to_cpu(tmp) + 2; 120 /* 121 * Frame error handling 122 */ 123 if (expectlen < SERIAL_MINIUM_PACKET_SIZE 124 || expectlen > SERIAL_MAX_FRAMESIZE) { 125 if (!layr->usestx) { 126 if (pkt != NULL) 127 cfpkt_destroy(pkt); 128 layr->incomplete_frm = NULL; 129 expectlen = 0; 130 spin_unlock(&layr->sync); 131 return -EPROTO; 132 } 133 continue; 134 } 135 136 if (pkt_len < expectlen) { 137 /* Too little received data */ 138 if (layr->usestx) 139 cfpkt_add_head(pkt, &stx, 1); 140 layr->incomplete_frm = pkt; 141 spin_unlock(&layr->sync); 142 return 0; 143 } 144 145 /* 146 * Enough data for at least one frame. 147 * Split the frame, if too long 148 */ 149 if (pkt_len > expectlen) 150 tail_pkt = cfpkt_split(pkt, expectlen); 151 else 152 tail_pkt = NULL; 153 154 /* Send the first part of packet upwards.*/ 155 spin_unlock(&layr->sync); 156 ret = layr->layer.up->receive(layr->layer.up, pkt); 157 spin_lock(&layr->sync); 158 if (ret == -EILSEQ) { 159 if (layr->usestx) { 160 if (tail_pkt != NULL) 161 pkt = cfpkt_append(pkt, tail_pkt, 0); 162 /* Start search for next STX if frame failed */ 163 continue; 164 } else { 165 cfpkt_destroy(pkt); 166 pkt = NULL; 167 } 168 } 169 170 pkt = tail_pkt; 171 172 } while (pkt != NULL); 173 174 spin_unlock(&layr->sync); 175 return 0; 176 } 177 178 static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt) 179 { 180 struct cfserl *layr = container_obj(layer); 181 int ret; 182 u8 tmp8 = CFSERL_STX; 183 if (layr->usestx) 184 cfpkt_add_head(newpkt, &tmp8, 1); 185 ret = layer->dn->transmit(layer->dn, newpkt); 186 if (ret < 0) 187 cfpkt_extr_head(newpkt, &tmp8, 1); 188 189 return ret; 190 } 191 192 static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 193 int phyid) 194 { 195 layr->up->ctrlcmd(layr->up, ctrl, phyid); 196 } 197