diff options
Diffstat (limited to 'drivers/net/can/slcan.c')
-rw-r--r-- | drivers/net/can/slcan.c | 139 |
1 files changed, 90 insertions, 49 deletions
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c index 874188ba06f7..25377e547f9b 100644 --- a/drivers/net/can/slcan.c +++ b/drivers/net/can/slcan.c | |||
@@ -76,6 +76,10 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces"); | |||
76 | /* maximum rx buffer len: extended CAN frame with timestamp */ | 76 | /* maximum rx buffer len: extended CAN frame with timestamp */ |
77 | #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1) | 77 | #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1) |
78 | 78 | ||
79 | #define SLC_CMD_LEN 1 | ||
80 | #define SLC_SFF_ID_LEN 3 | ||
81 | #define SLC_EFF_ID_LEN 8 | ||
82 | |||
79 | struct slcan { | 83 | struct slcan { |
80 | int magic; | 84 | int magic; |
81 | 85 | ||
@@ -142,47 +146,63 @@ static void slc_bump(struct slcan *sl) | |||
142 | { | 146 | { |
143 | struct sk_buff *skb; | 147 | struct sk_buff *skb; |
144 | struct can_frame cf; | 148 | struct can_frame cf; |
145 | int i, dlc_pos, tmp; | 149 | int i, tmp; |
146 | unsigned long ultmp; | 150 | u32 tmpid; |
147 | char cmd = sl->rbuff[0]; | 151 | char *cmd = sl->rbuff; |
148 | 152 | ||
149 | if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) | 153 | cf.can_id = 0; |
154 | |||
155 | switch (*cmd) { | ||
156 | case 'r': | ||
157 | cf.can_id = CAN_RTR_FLAG; | ||
158 | /* fallthrough */ | ||
159 | case 't': | ||
160 | /* store dlc ASCII value and terminate SFF CAN ID string */ | ||
161 | cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN]; | ||
162 | sl->rbuff[SLC_CMD_LEN + SLC_SFF_ID_LEN] = 0; | ||
163 | /* point to payload data behind the dlc */ | ||
164 | cmd += SLC_CMD_LEN + SLC_SFF_ID_LEN + 1; | ||
165 | break; | ||
166 | case 'R': | ||
167 | cf.can_id = CAN_RTR_FLAG; | ||
168 | /* fallthrough */ | ||
169 | case 'T': | ||
170 | cf.can_id |= CAN_EFF_FLAG; | ||
171 | /* store dlc ASCII value and terminate EFF CAN ID string */ | ||
172 | cf.can_dlc = sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN]; | ||
173 | sl->rbuff[SLC_CMD_LEN + SLC_EFF_ID_LEN] = 0; | ||
174 | /* point to payload data behind the dlc */ | ||
175 | cmd += SLC_CMD_LEN + SLC_EFF_ID_LEN + 1; | ||
176 | break; | ||
177 | default: | ||
150 | return; | 178 | return; |
179 | } | ||
151 | 180 | ||
152 | if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */ | 181 | if (kstrtou32(sl->rbuff + SLC_CMD_LEN, 16, &tmpid)) |
153 | dlc_pos = 4; /* dlc position tiiid */ | ||
154 | else | ||
155 | dlc_pos = 9; /* dlc position Tiiiiiiiid */ | ||
156 | |||
157 | if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9'))) | ||
158 | return; | 182 | return; |
159 | 183 | ||
160 | cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */ | 184 | cf.can_id |= tmpid; |
161 | 185 | ||
162 | sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ | 186 | /* get can_dlc from sanitized ASCII value */ |
163 | 187 | if (cf.can_dlc >= '0' && cf.can_dlc < '9') | |
164 | if (kstrtoul(sl->rbuff+1, 16, &ultmp)) | 188 | cf.can_dlc -= '0'; |
189 | else | ||
165 | return; | 190 | return; |
166 | 191 | ||
167 | cf.can_id = ultmp; | ||
168 | |||
169 | if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */ | ||
170 | cf.can_id |= CAN_EFF_FLAG; | ||
171 | |||
172 | if ((cmd | 0x20) == 'r') /* RTR frame */ | ||
173 | cf.can_id |= CAN_RTR_FLAG; | ||
174 | |||
175 | *(u64 *) (&cf.data) = 0; /* clear payload */ | 192 | *(u64 *) (&cf.data) = 0; /* clear payload */ |
176 | 193 | ||
177 | for (i = 0, dlc_pos++; i < cf.can_dlc; i++) { | 194 | /* RTR frames may have a dlc > 0 but they never have any data bytes */ |
178 | tmp = hex_to_bin(sl->rbuff[dlc_pos++]); | 195 | if (!(cf.can_id & CAN_RTR_FLAG)) { |
179 | if (tmp < 0) | 196 | for (i = 0; i < cf.can_dlc; i++) { |
180 | return; | 197 | tmp = hex_to_bin(*cmd++); |
181 | cf.data[i] = (tmp << 4); | 198 | if (tmp < 0) |
182 | tmp = hex_to_bin(sl->rbuff[dlc_pos++]); | 199 | return; |
183 | if (tmp < 0) | 200 | cf.data[i] = (tmp << 4); |
184 | return; | 201 | tmp = hex_to_bin(*cmd++); |
185 | cf.data[i] |= tmp; | 202 | if (tmp < 0) |
203 | return; | ||
204 | cf.data[i] |= tmp; | ||
205 | } | ||
186 | } | 206 | } |
187 | 207 | ||
188 | skb = dev_alloc_skb(sizeof(struct can_frame) + | 208 | skb = dev_alloc_skb(sizeof(struct can_frame) + |
@@ -209,7 +229,6 @@ static void slc_bump(struct slcan *sl) | |||
209 | /* parse tty input stream */ | 229 | /* parse tty input stream */ |
210 | static void slcan_unesc(struct slcan *sl, unsigned char s) | 230 | static void slcan_unesc(struct slcan *sl, unsigned char s) |
211 | { | 231 | { |
212 | |||
213 | if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */ | 232 | if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */ |
214 | if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && | 233 | if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && |
215 | (sl->rcount > 4)) { | 234 | (sl->rcount > 4)) { |
@@ -236,27 +255,46 @@ static void slcan_unesc(struct slcan *sl, unsigned char s) | |||
236 | /* Encapsulate one can_frame and stuff into a TTY queue. */ | 255 | /* Encapsulate one can_frame and stuff into a TTY queue. */ |
237 | static void slc_encaps(struct slcan *sl, struct can_frame *cf) | 256 | static void slc_encaps(struct slcan *sl, struct can_frame *cf) |
238 | { | 257 | { |
239 | int actual, idx, i; | 258 | int actual, i; |
240 | char cmd; | 259 | unsigned char *pos; |
260 | unsigned char *endpos; | ||
261 | canid_t id = cf->can_id; | ||
262 | |||
263 | pos = sl->xbuff; | ||
241 | 264 | ||
242 | if (cf->can_id & CAN_RTR_FLAG) | 265 | if (cf->can_id & CAN_RTR_FLAG) |
243 | cmd = 'R'; /* becomes 'r' in standard frame format */ | 266 | *pos = 'R'; /* becomes 'r' in standard frame format (SFF) */ |
244 | else | 267 | else |
245 | cmd = 'T'; /* becomes 't' in standard frame format */ | 268 | *pos = 'T'; /* becomes 't' in standard frame format (SSF) */ |
246 | 269 | ||
247 | if (cf->can_id & CAN_EFF_FLAG) | 270 | /* determine number of chars for the CAN-identifier */ |
248 | sprintf(sl->xbuff, "%c%08X%d", cmd, | 271 | if (cf->can_id & CAN_EFF_FLAG) { |
249 | cf->can_id & CAN_EFF_MASK, cf->can_dlc); | 272 | id &= CAN_EFF_MASK; |
250 | else | 273 | endpos = pos + SLC_EFF_ID_LEN; |
251 | sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20, | 274 | } else { |
252 | cf->can_id & CAN_SFF_MASK, cf->can_dlc); | 275 | *pos |= 0x20; /* convert R/T to lower case for SFF */ |
276 | id &= CAN_SFF_MASK; | ||
277 | endpos = pos + SLC_SFF_ID_LEN; | ||
278 | } | ||
253 | 279 | ||
254 | idx = strlen(sl->xbuff); | 280 | /* build 3 (SFF) or 8 (EFF) digit CAN identifier */ |
281 | pos++; | ||
282 | while (endpos >= pos) { | ||
283 | *endpos-- = hex_asc_upper[id & 0xf]; | ||
284 | id >>= 4; | ||
285 | } | ||
286 | |||
287 | pos += (cf->can_id & CAN_EFF_FLAG) ? SLC_EFF_ID_LEN : SLC_SFF_ID_LEN; | ||
255 | 288 | ||
256 | for (i = 0; i < cf->can_dlc; i++) | 289 | *pos++ = cf->can_dlc + '0'; |
257 | sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]); | 290 | |
291 | /* RTR frames may have a dlc > 0 but they never have any data bytes */ | ||
292 | if (!(cf->can_id & CAN_RTR_FLAG)) { | ||
293 | for (i = 0; i < cf->can_dlc; i++) | ||
294 | pos = hex_byte_pack_upper(pos, cf->data[i]); | ||
295 | } | ||
258 | 296 | ||
259 | strcat(sl->xbuff, "\r"); /* add terminating character */ | 297 | *pos++ = '\r'; |
260 | 298 | ||
261 | /* Order of next two lines is *very* important. | 299 | /* Order of next two lines is *very* important. |
262 | * When we are sending a little amount of data, | 300 | * When we are sending a little amount of data, |
@@ -267,8 +305,8 @@ static void slc_encaps(struct slcan *sl, struct can_frame *cf) | |||
267 | * 14 Oct 1994 Dmitry Gorodchanin. | 305 | * 14 Oct 1994 Dmitry Gorodchanin. |
268 | */ | 306 | */ |
269 | set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); | 307 | set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); |
270 | actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); | 308 | actual = sl->tty->ops->write(sl->tty, sl->xbuff, pos - sl->xbuff); |
271 | sl->xleft = strlen(sl->xbuff) - actual; | 309 | sl->xleft = (pos - sl->xbuff) - actual; |
272 | sl->xhead = sl->xbuff + actual; | 310 | sl->xhead = sl->xbuff + actual; |
273 | sl->dev->stats.tx_bytes += cf->can_dlc; | 311 | sl->dev->stats.tx_bytes += cf->can_dlc; |
274 | } | 312 | } |
@@ -286,11 +324,13 @@ static void slcan_write_wakeup(struct tty_struct *tty) | |||
286 | if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) | 324 | if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) |
287 | return; | 325 | return; |
288 | 326 | ||
327 | spin_lock(&sl->lock); | ||
289 | if (sl->xleft <= 0) { | 328 | if (sl->xleft <= 0) { |
290 | /* Now serial buffer is almost free & we can start | 329 | /* Now serial buffer is almost free & we can start |
291 | * transmission of another packet */ | 330 | * transmission of another packet */ |
292 | sl->dev->stats.tx_packets++; | 331 | sl->dev->stats.tx_packets++; |
293 | clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | 332 | clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); |
333 | spin_unlock(&sl->lock); | ||
294 | netif_wake_queue(sl->dev); | 334 | netif_wake_queue(sl->dev); |
295 | return; | 335 | return; |
296 | } | 336 | } |
@@ -298,6 +338,7 @@ static void slcan_write_wakeup(struct tty_struct *tty) | |||
298 | actual = tty->ops->write(tty, sl->xhead, sl->xleft); | 338 | actual = tty->ops->write(tty, sl->xhead, sl->xleft); |
299 | sl->xleft -= actual; | 339 | sl->xleft -= actual; |
300 | sl->xhead += actual; | 340 | sl->xhead += actual; |
341 | spin_unlock(&sl->lock); | ||
301 | } | 342 | } |
302 | 343 | ||
303 | /* Send a can_frame to a TTY queue. */ | 344 | /* Send a can_frame to a TTY queue. */ |