Hi, it took longer than expected, but here is a patch to add support for binary mode to slcan with error recovery! Regards,
Maximilian Patch against SVN Rev 1054: Index: kernel/2.6/drivers/net/can/slcan.c =================================================================== --- kernel/2.6/drivers/net/can/slcan.c (Revision 1054) +++ kernel/2.6/drivers/net/can/slcan.c (Arbeitskopie) @@ -11,6 +11,7 @@ * slip.c Authors : Laurence Culhane <[email protected]> * Fred N. van Kempen <[email protected]> * slcan.c Author : Oliver Hartkopp <[email protected]> + * Maximilian Falkenstein <[email protected]> * * Copyright (c) 2007-2009 Volkswagen Group Electronic Research * @@ -75,13 +76,15 @@ #include <socketcan/can.h> +#if 0 #include <socketcan/can/version.h> /* for RCSID. Removed by mkpatch script */ RCSID("$Id$"); +#endif -static __initdata const char banner[] = - KERN_INFO "slcan: serial line CAN interface driver\n"; +static __initdata const char banner[] = KERN_INFO +"slcan: serial line CAN interface driver\n"; -MODULE_ALIAS_LDISC(N_SLCAN); +MODULE_ALIAS_LDISC( N_SLCAN); MODULE_DESCRIPTION("serial line CAN interface"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Oliver Hartkopp <[email protected]>"); @@ -126,47 +129,73 @@ #define SLC_CHECK_TRANSMIT #define SLCAN_MAGIC 0x53CA -static int maxdev = 10; /* MAX number of SLCAN channels; - This can be overridden with - insmod slcan.ko maxdev=nnn */ +static int maxdev = 10; /* MAX number of SLCAN channels; + This can be overridden with + insmod slcan.ko maxdev=nnn */ module_param(maxdev, int, 0); MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces"); +/* MAX number of packets to put * + * into the device buffer, can * + * also be overridden */ +static int maxack = 2; +module_param(maxack, int, 0); +MODULE_PARM_DESC(maxack, "Maximum number of packets to put into the device buffer, be _VERY_ careful with this!"); -/* maximum rx buffer len: extended CAN frame with timestamp */ +/* maximum rx buffer len: extended CAN frame with timestamp in ASCII format */ #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1) struct slcan { - int magic; + int magic; + + /* We need some extra vars for binary mode! */ + int dnum; /* How many data bytes do we except to receive */ + int to_ack; /* How many packets have we put into the device buffer? */ + int rec_rcount; /* special rcount for recovery purposes */ + + /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - spinlock_t lock; + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* easy for intr handling */ + spinlock_t lock; + /* These are pointers to the malloc()ed frame buffers. */ - unsigned char rbuff[SLC_MTU]; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char xbuff[SLC_MTU]; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next XMIT byte */ - int xleft; /* bytes left in XMIT queue */ + unsigned char rbuff[SLC_MTU]; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char xbuff[SLC_MTU]; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next XMIT byte */ + int xleft; /* bytes left in XMIT queue */ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) /* SLCAN interface statistics. */ struct net_device_stats stats; #endif - unsigned long flags; /* Flag values/ mode etc */ + unsigned long flags; /* Flag values/ mode etc */ #define SLF_INUSE 0 /* Channel in use */ -#define SLF_ERROR 1 /* Parity, etc. error */ +#define SLF_ERROR 1 /* Parity, etc. error */ +#define SLF_BINENC 2 /* Do we use binary encapsulation? */ +#define SLF_EFRAME 3 /* Is the frame we are currently receiving extended */ +#define SLF_RESP 4 /* Is the next byte a response to a command */ - unsigned char leased; - dev_t line; - pid_t pid; + + /* true, if we've paused the transmission of packets because the hardware * + * hasn't answered (yet). */ +#define SLF_PAUSED 5 + + +#define SLF_LOST_SYNC 6 /* true, if we don't know what's going on... */ +#define SLF_FRAME_CANDIDAT 7 /* What we're receiving looks like a frame... */ + + unsigned char leased; + dev_t line; + pid_t pid; }; static struct net_device **slcan_devs; - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) /* Netdevice get statistics request */ static struct net_device_stats *slc_get_stats(struct net_device *dev) @@ -177,9 +206,9 @@ } #endif - /************************************************************************ - * SLCAN ENCAPSULATION FORMAT * - ************************************************************************/ +/************************************************************************ + * SLCAN ENCAPSULATION FORMAT * + ************************************************************************/ /* * A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended @@ -210,11 +239,19 @@ * T12ABCDEF2AA55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55 * r1230 : can_id 0x123, can_dlc 0, no data, remote transmission request * + * NEW: We also have a binary encapsulation. It's very similar to the ASCII + * one, except that we don't user \r \n and don't convert to ASCII. The return + * value is also different! As the lib on controller doesn't differ between + * RTR and normal frame with 0 databytes, we don't differ between 'R' and 'T', too. + * + * Examples(Binary): + * t 0x01 0x23 0x00 : like t1230, can_id 0x123, can_dlc 0, no data + * T 0x12 0xAB 0xCD 0xEF 0x03 0xAA 0x55 : extended can_id 0x12ABCDEF, can_dlc 2, data 0xAA 0x55 */ - /************************************************************************ - * STANDARD SLCAN DECAPSULATION * - ************************************************************************/ +/************************************************************************ + * STANDARD SLCAN DECAPSULATION * + ************************************************************************/ static int asc2nibble(char c) { @@ -245,50 +282,82 @@ unsigned long ultmp; char cmd = sl->rbuff[0]; - if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) - return; + if (!test_bit(SLF_BINENC, &sl->flags)) { + if ((cmd != 't') && (cmd != 'T') && (cmd != 'r') && (cmd != 'R')) + return; - if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */ - dlc_pos = 4; /* dlc position tiiid */ - else - dlc_pos = 9; /* dlc position Tiiiiiiiid */ + if (cmd & 0x20) /* tiny chars 'r' 't' => standard frame format */ + dlc_pos = 4; /* dlc position tiiid */ + else + dlc_pos = 9; /* dlc position Tiiiiiiiid */ - if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9'))) - return; + if (!((sl->rbuff[dlc_pos] >= '0') && (sl->rbuff[dlc_pos] < '9'))) + return; - cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */ + cf.can_dlc = sl->rbuff[dlc_pos] - '0'; /* get can_dlc from ASCII val */ - sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ + sl->rbuff[dlc_pos] = 0; /* terminate can_id string */ + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - ultmp = simple_strtoul(sl->rbuff+1, NULL, 16); + ultmp = simple_strtoul(sl->rbuff+1, NULL, 16); #else - if (strict_strtoul(sl->rbuff+1, 16, &ultmp)) - return; + if (strict_strtoul(sl->rbuff + 1, 16, &ultmp)) + return; #endif - cf.can_id = ultmp; + cf.can_id = ultmp; - if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */ - cf.can_id |= CAN_EFF_FLAG; + if (!(cmd & 0x20)) /* NO tiny chars => extended frame format */ + cf.can_id |= CAN_EFF_FLAG; - if ((cmd | 0x20) == 'r') /* RTR frame */ - cf.can_id |= CAN_RTR_FLAG; + if ((cmd | 0x20) == 'r') /* RTR frame */ + cf.can_id |= CAN_RTR_FLAG; - *(u64 *) (&cf.data) = 0; /* clear payload */ + *(u64 *) (&cf.data) = 0; /* clear payload */ - for (i = 0, dlc_pos++; i < cf.can_dlc; i++) { - - tmp = asc2nibble(sl->rbuff[dlc_pos++]); - if (tmp > 0x0F) + for (i = 0, dlc_pos++; i < cf.can_dlc; i++) { + tmp = asc2nibble(sl->rbuff[dlc_pos++]); + if (tmp > 0x0F) + return; + cf.data[i] = (tmp << 4); + tmp = asc2nibble(sl->rbuff[dlc_pos++]); + if (tmp > 0x0F) + return; + cf.data[i] |= tmp; + } + } else { + //Binary is so much easier ;-) + if (cmd == 't') { + //Normal can frame + cf.can_id = (sl->rbuff[1] << 8) | sl->rbuff[2]; + cf.can_dlc = sl->rbuff[3]; + if (cf.can_dlc > 8) { + printk(KERN_WARNING "slcan: Frame with lenght above 8, dropping!"); + stats->rx_dropped++; + stats->rx_errors++; + return; + } + for (i = 0; i < cf.can_dlc; i++) + cf.data[i] = sl->rbuff[4 + i]; + } else if (cmd == 'T') { + //Extended can frame + cf.can_id = (sl->rbuff[1] << 24) | (sl->rbuff[2] << 16) | (sl->rbuff[3] << 8) | sl->rbuff[4]; + cf.can_dlc = sl->rbuff[5]; + if (cf.can_dlc > 8) { + printk(KERN_WARNING "slcan: Frame with lenght above 8, dropping!"); + stats->rx_dropped++; + stats->rx_errors++; + return; + } + for (i = 0; i < cf.can_dlc; i++) + cf.data[i] = sl->rbuff[6 + i]; + } else { + printk(KERN_WARNING "slcan: Got unknown can frame from device!"); + stats->rx_errors++; return; - cf.data[i] = (tmp << 4); - tmp = asc2nibble(sl->rbuff[dlc_pos++]); - if (tmp > 0x0F) - return; - cf.data[i] |= tmp; + } } - skb = dev_alloc_skb(sizeof(struct can_frame)); if (!skb) return; @@ -297,8 +366,7 @@ skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->ip_summed = CHECKSUM_UNNECESSARY; - memcpy(skb_put(skb, sizeof(struct can_frame)), - &cf, sizeof(struct can_frame)); + memcpy(skb_put(skb, sizeof(struct can_frame)), &cf, sizeof(struct can_frame)); netif_rx(skb); sl->dev->last_rx = jiffies; @@ -314,29 +382,197 @@ #else struct net_device_stats *stats = &sl->dev->stats; #endif + int i; + unsigned char nrbuff[SLC_MTU]; - if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */ - if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && - (sl->rcount > 4)) { + if (!test_bit(SLF_BINENC, &sl->flags)) { + //ASCII + if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */ + if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 4)) { + slc_bump(sl); + } + sl->rcount = 0; + } else { + if (!test_bit(SLF_ERROR, &sl->flags)) { + if (sl->rcount < SLC_MTU) { + sl->rbuff[sl->rcount++] = s; + return; + } else { + stats->rx_over_errors++; + set_bit(SLF_ERROR, &sl->flags); + } + } + } + } else { + //BINARY + if (test_bit(SLF_LOST_SYNC, &sl->flags)) { + //OK, complicated things first(error recovery)... + sl->rbuff[sl->rec_rcount] = s; + sl->rec_rcount++; + + if (test_bit(SLF_FRAME_CANDIDAT, &sl->flags)) { + if (!test_bit(SLF_RESP, &sl->flags)) { + int offset = sl->rec_rcount - sl->rcount; + sl->rcount++; + if ((sl->rcount == 2 && !test_bit(SLF_EFRAME, &sl->flags)) | (sl->rcount == 4 && test_bit( + SLF_EFRAME, &sl->flags))) { + //We've recived the last bit of the CAN id + if (test_bit(SLF_EFRAME, &sl->flags) && (((sl->rbuff[offset + 1] << 24) + | (sl->rbuff[offset + 2] << 16) | (sl->rbuff[offset + 3] << 8) | sl->rbuff[offset + 4]) + > 0x1FFFFFFF) | !test_bit(SLF_EFRAME, &sl->flags) && ((sl->rbuff[offset + 1] << 8) + | sl->rbuff[offset + 2]) > 0x7FF) { + //Invalid ID + goto invalid; + } + } else if ((sl->rcount == 5 && test_bit(SLF_EFRAME, &sl->flags)) | ((sl->rcount == 3) && !test_bit( + SLF_EFRAME, &sl->flags))) { + if (s > 8) //Invalid dlc + goto invalid; + else if (s == 0) { + goto rec_out_decaps; + } else { + sl->dnum = s; + return; + } + } else if (sl->dnum > 0) { + sl->dnum--; + if (sl->dnum == 0) { + //Decaps + goto rec_out_decaps; + } + } + } else { + if (s & 0xf0) { + if (s != 0xf9) { + /* 0xf9 means OK, everything different means error */ + printk(KERN_WARNING "%s: Command failed, retval: %X\n", sl->dev->name, s); + stats->tx_errors++; + stats->tx_dropped++; + } + sl->to_ack--; + if (test_bit(SLF_PAUSED, &sl->flags) && (sl->to_ack < maxack)) { + /* The device buffer is large enough for at least one + * more packet, so let's wake the quere */ + clear_bit(SLF_PAUSED, &sl->flags); + netif_wake_queue(sl->dev); + } + clear_bit(SLF_RESP, &sl->flags); + sl->rcount = 0; + sl->rec_rcount = 0; + clear_bit(SLF_FRAME_CANDIDAT, &sl->flags); + clear_bit(SLF_LOST_SYNC, &sl->flags); + return; + } else + goto invalid; + } + } + if (sl->rec_rcount == SLC_MTU) { + printk( KERN_WARNING + "%s: Buffer full, but still havn't received a valid packet!\n", sl->dev->name); + sl->rcount = 0; + clear_bit(SLF_FRAME_CANDIDAT, &sl->flags); + } + if ((s == 't') | (s == 'T') | (s == 0xe0)) { + set_bit(SLF_FRAME_CANDIDAT, &sl->flags); + if (s == 't') + clear_bit(SLF_EFRAME, &sl->flags); + if (s == 'T') + set_bit(SLF_EFRAME, &sl->flags); + if (s == 0xe0) + set_bit(SLF_RESP, &sl->flags); + sl->rcount = 0; + } + return; + + invalid: clear_bit(SLF_FRAME_CANDIDAT, &sl->flags); + return; + + rec_out_decaps: + //Modify buffer so that we can call slc_bump + for (i = 0; i <= sl->rcount; i++) + nrbuff[i] = sl->rbuff[sl->rec_rcount - sl->rcount - 1 + i]; + memcpy(sl->rbuff, nrbuff, sl->rcount + 1); slc_bump(sl); + sl->rcount = 0; + sl->rec_rcount = 0; + clear_bit(SLF_FRAME_CANDIDAT, &sl->flags); + clear_bit(SLF_LOST_SYNC, &sl->flags); + return; } - sl->rcount = 0; - } else { - if (!test_bit(SLF_ERROR, &sl->flags)) { - if (sl->rcount < SLC_MTU) { - sl->rbuff[sl->rcount++] = s; + + if (test_bit(SLF_RESP, &sl->flags)) { + if (s != 0xf9) { + /* 0xf9 means OK, everything different means error */ + printk(KERN_WARNING "%s: Command failed, retval: %X\n", sl->dev->name, s); + stats->tx_errors++; + stats->tx_dropped++; + } + sl->to_ack--; + if (test_bit(SLF_PAUSED, &sl->flags) && (sl->to_ack < maxack)) { + /* The device buffer is large enough for at least one + * more packet, so let's wake the quere */ + clear_bit(SLF_PAUSED, &sl->flags); + netif_wake_queue(sl->dev); + } + clear_bit(SLF_RESP, &sl->flags); + sl->rcount = 0; + return; + } + + if (sl->rcount == 0) { + //What's coming next: a can frame, an extended can frame, or an ack/nack? + if (s == 't') { + clear_bit(SLF_EFRAME, &sl->flags); + goto out_write; + } else if (s == 'T') { + set_bit(SLF_EFRAME, &sl->flags); + goto out_write; + } else if (s == 0xe0) { + //ACK/NACK + set_bit(SLF_RESP, &sl->flags); + } else { + //Unknown packet, *may* happen during hot plugging! + printk(KERN_WARNING "slcan (dev: %s): Unknown packet(head: 0x%X), entering lost sync mode!\n", sl->dev->name, s); + stats->rx_errors++; + sl->rec_rcount = 0; + sl->rcount = 0; + sl->rec_rcount = 0; + set_bit(SLF_LOST_SYNC, &sl->flags); return; - } else { - stats->rx_over_errors++; - set_bit(SLF_ERROR, &sl->flags); } + } else if ((test_bit(SLF_EFRAME, &sl->flags) && sl->rcount == 5) | ((!test_bit(SLF_EFRAME, &sl->flags)) + && sl->rcount == 3)) { + //Now we're receiving the payload lenght + if (s == 0) { + //Oh, cool, a RTR; we can decapsulate it immediately + sl->rbuff[sl->rcount] = s; + goto out_decaps; + } + sl->dnum = s; + goto out_write; + } else { + //More data + sl->rbuff[sl->rcount] = s; + sl->rcount++; + sl->dnum--; + if (sl->dnum == 0) + goto out_decaps; } } + return; + + out_write: sl->rbuff[sl->rcount] = s; + sl->rcount++; + return; + + out_decaps: slc_bump(sl); + sl->rcount = 0; + return; } - /************************************************************************ - * STANDARD SLCAN ENCAPSULATION * - ************************************************************************/ +/************************************************************************ + * STANDARD SLCAN ENCAPSULATION * + ************************************************************************/ /* Encapsulate one can_frame and stuff into a TTY queue. */ static void slc_encaps(struct slcan *sl, struct can_frame *cf) @@ -349,41 +585,95 @@ int actual, idx, i; char cmd; - if (cf->can_id & CAN_RTR_FLAG) - cmd = 'R'; /* becomes 'r' in standard frame format */ - else - cmd = 'T'; /* becomes 't' in standard frame format */ + if (test_bit(SLF_BINENC, &sl->flags)) { + uint32_t id = cf->can_id & CAN_EFF_MASK; + uint8_t lof = 0; + if (cf->can_id & CAN_EFF_FLAG) { + sl->xbuff[0] = 'T'; //Send + sl->xbuff[1] = (uint8_t)(id >> 24); + sl->xbuff[2] = (uint8_t)(id >> 16); + sl->xbuff[3] = (uint8_t)(id >> 8); + sl->xbuff[4] = (uint8_t) id; + lof = 4; + } else { + sl->xbuff[0] = 't'; //Send + sl->xbuff[1] = (uint8_t)(id >> 8); + sl->xbuff[2] = (uint8_t) id; + lof = 2; + } + sl->xbuff[lof + 1] = cf->can_dlc; + /* Stringterm (Reordered for C90 compatibility) */ + sl->xbuff[lof + 2 + cf->can_dlc] = 0x00; - if (cf->can_id & CAN_EFF_FLAG) - sprintf(sl->xbuff, "%c%08X%d", cmd, - cf->can_id & CAN_EFF_MASK, cf->can_dlc); - else - sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20, - cf->can_id & CAN_SFF_MASK, cf->can_dlc); + for (i = 0; i < cf->can_dlc; i++) { + sl->xbuff[lof + 2 + i] = cf->data[i]; + } - idx = strlen(sl->xbuff); + /* We're going to send another packet into the device's buffer, + * let's tell the receiving side that we're sending something + * that's going to get a CMD_RESP. */ + sl->to_ack++; - for (i = 0; i < cf->can_dlc; i++) - sprintf(&sl->xbuff[idx + 2*i], "%02X", cf->data[i]); - DBG("ASCII frame = '%s'\n", sl->xbuff); + /* Flow control: The device may be busy, but able to receive + * data (interrupt driven, at least in my firmware), but it + * can't handle an infinite amount of data, so we have to + * stop and wait for response after some packets... */ + if (sl->to_ack >= maxack) + set_bit(SLF_PAUSED, &sl->flags); - strcat(sl->xbuff, "\r"); /* add terminating character */ - /* Order of next two lines is *very* important. - * When we are sending a little amount of data, - * the transfer may be completed inside driver.write() - * routine, because it's running with interrupts enabled. - * In this case we *never* got WRITE_WAKEUP event, - * if we did not request it before write operation. - * 14 Oct 1994 Dmitry Gorodchanin. - */ - sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + /* Order of next two lines is *very* important. + * When we are sending a little amount of data, + * the transfer may be completed inside driver.write() + * routine, because it's running with interrupts enabled. + * In this case we *never* got WRITE_WAKEUP event, + * if we did not request it before write operation. + * 14 Oct 1994 Dmitry Gorodchanin. + */ + sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) - actual = sl->tty->driver->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); + actual = sl->tty->driver->write(sl->tty, sl->xbuff, (cf->can_id & CAN_EFF_FLAG ? 6 : 4)+cf->can_dlc); #else - actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); + actual = sl->tty->ops->write(sl->tty, sl->xbuff, (cf->can_id & CAN_EFF_FLAG ? 6 : 4) + cf->can_dlc); #endif + + } else { + if (cf->can_id & CAN_RTR_FLAG) + cmd = 'R'; /* becomes 'r' in standard frame format */ + else + cmd = 'T'; /* becomes 't' in standard frame format */ + + if (cf->can_id & CAN_EFF_FLAG) + sprintf(sl->xbuff, "%c%08X%d", cmd, cf->can_id & CAN_EFF_MASK, cf->can_dlc); + else + sprintf(sl->xbuff, "%c%03X%d", cmd | 0x20, cf->can_id & CAN_SFF_MASK, cf->can_dlc); + + idx = strlen(sl->xbuff); + + for (i = 0; i < cf->can_dlc; i++) + sprintf(&sl->xbuff[idx + 2 * i], "%02X", cf->data[i]); + + DBG("ASCII frame = '%s'\n", sl->xbuff); + + strcat(sl->xbuff, "\r"); /* add terminating character */ + + + /* Order of next two lines is *very* important. + * When we are sending a little amount of data, + * the transfer may be completed inside driver.write() + * routine, because it's running with interrupts enabled. + * In this case we *never* got WRITE_WAKEUP event, + * if we did not request it before write operation. + * 14 Oct 1994 Dmitry Gorodchanin. + */ + sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) + actual = sl->tty->driver->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); +#else + actual = sl->tty->ops->write(sl->tty, sl->xbuff, strlen(sl->xbuff)); +#endif + } #ifdef SLC_CHECK_TRANSMIT sl->dev->trans_start = jiffies; #endif @@ -406,16 +696,22 @@ struct net_device_stats *stats = &sl->dev->stats; #endif + /* First make sure we're connected. */ if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) return; - if (sl->xleft <= 0) { + if (sl->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ stats->tx_packets++; tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - netif_wake_queue(sl->dev); + + + /* Maybe the device isn't ready, lets check before + * we try to send the next packet */ + if (!(test_bit(SLF_BINENC, &sl->flags) && test_bit(SLF_PAUSED, &sl->flags))) + netif_wake_queue(sl->dev); return; } @@ -442,27 +738,25 @@ * 14 Oct 1994 Dmitry Gorodchanin. */ #ifdef SLC_CHECK_TRANSMIT - if (time_before(jiffies, dev->trans_start + 20 * HZ)) { + if (time_before(jiffies, dev->trans_start + 20 * HZ)) { /* 20 sec timeout not reached */ goto out; - } - printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, + }printk + (KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) - (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) + (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) #else - (tty_chars_in_buffer(sl->tty) || sl->xleft) + (tty_chars_in_buffer(sl->tty) || sl->xleft) #endif - ? "bad line quality" : "driver error"); + ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); netif_wake_queue(sl->dev); #endif } -out: - spin_unlock(&sl->lock); + out: spin_unlock(&sl->lock); } - /****************************************** * Routines looking at netdevice side. ******************************************/ @@ -476,9 +770,10 @@ goto out; spin_lock(&sl->lock); - if (!netif_running(dev)) { + if (!netif_running(dev)) { spin_unlock(&sl->lock); - printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name); + printk + (KERN_WARNING "%s: xmit: iface is down\n", dev->name); goto out; } @@ -491,12 +786,10 @@ slc_encaps(sl, (struct can_frame *) skb->data); /* encaps & send */ spin_unlock(&sl->lock); -out: - kfree_skb(skb); + out: kfree_skb(skb); return 0; } - /* Netdevice UP -> DOWN routine */ static int slc_close(struct net_device *dev) { @@ -508,8 +801,8 @@ sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); } netif_stop_queue(dev); - sl->rcount = 0; - sl->xleft = 0; + sl->rcount = 0; + sl->xleft = 0; spin_unlock_bh(&sl->lock); return 0; @@ -523,18 +816,20 @@ if (sl->tty == NULL) return -ENODEV; - sl->flags &= (1 << SLF_INUSE); + /* ARGH! Bitmasks are not always clever... */ + //sl->flags &= (1 << SLF_INUSE); + sl->flags &= (1 << SLF_INUSE) | (1 << SLF_BINENC); netif_start_queue(dev); return 0; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) static const struct net_device_ops slc_netdev_ops = { - .ndo_open = slc_open, - .ndo_stop = slc_close, - .ndo_start_xmit = slc_xmit, + .ndo_open = slc_open, + .ndo_stop = slc_close, + .ndo_start_xmit = slc_xmit, #ifdef SLC_CHECK_TRANSMIT - .ndo_tx_timeout = slc_tx_timeout, + .ndo_tx_timeout = slc_tx_timeout, #endif }; #endif @@ -545,31 +840,32 @@ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,28) dev->netdev_ops = &slc_netdev_ops; #else - dev->open = slc_open; - dev->stop = slc_close; - dev->hard_start_xmit = slc_xmit; + dev->open = slc_open; + dev->stop = slc_close; + dev->hard_start_xmit = slc_xmit; #endif - dev->destructor = free_netdev; + dev->destructor = free_netdev; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) - dev->get_stats = slc_get_stats; + dev->get_stats = slc_get_stats; #endif - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->tx_queue_len = 10; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->tx_queue_len = 10; - dev->mtu = sizeof(struct can_frame); - dev->type = ARPHRD_CAN; + dev->mtu = sizeof(struct can_frame); + dev->type = ARPHRD_CAN; #ifdef SLC_CHECK_TRANSMIT #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) - dev->tx_timeout = slc_tx_timeout; + dev->tx_timeout = slc_tx_timeout; #endif - dev->watchdog_timeo = 20*HZ; + dev->watchdog_timeo = 20 * HZ; #endif + /* New-style flags. */ - dev->flags = IFF_NOARP; - dev->features = NETIF_F_NO_CSUM; + dev->flags = IFF_NOARP; + dev->features = NETIF_F_NO_CSUM; } /****************************************** @@ -579,7 +875,7 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) static int slcan_receive_room(struct tty_struct *tty) { - return 65536; /* We can handle an infinite amount of data. :-) */ + return 65536; /* We can handle an infinite amount of data. :-) */ } #endif @@ -591,9 +887,7 @@ * be re-entered while running but other ldisc functions may be called * in parallel */ - -static void slcan_receive_buf(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) +static void slcan_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { struct slcan *sl = (struct slcan *) tty->disc_data; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) @@ -602,10 +896,10 @@ struct net_device_stats *stats = &sl->dev->stats; #endif - if (!sl || sl->magic != SLCAN_MAGIC || - !netif_running(sl->dev)) + if (!sl || sl->magic != SLCAN_MAGIC || !netif_running(sl->dev)) return; + /* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { @@ -628,7 +922,7 @@ { int i; struct net_device *dev; - struct slcan *sl; + struct slcan *sl; for (i = 0; i < maxdev; i++) { dev = slcan_devs[i]; @@ -638,12 +932,11 @@ sl = netdev_priv(dev); if (sl->tty || sl->leased) continue; - if (dev->flags&IFF_UP) + if (dev->flags & IFF_UP) dev_close(dev); } } - /* Find a free SLCAN channel, and link in this `tty' line. */ static struct slcan *slc_alloc(dev_t line) { @@ -651,10 +944,10 @@ int sel = -1; int score = -1; struct net_device *dev = NULL; - struct slcan *sl; + struct slcan *sl; if (slcan_devs == NULL) - return NULL; /* Master array missing ! */ + return NULL; /* Master array missing ! */ for (i = 0; i < maxdev; i++) { dev = slcan_devs[i]; @@ -729,14 +1022,16 @@ dev = alloc_netdev(sizeof(*sl), name, slc_setup); if (!dev) return NULL; - dev->base_addr = i; + dev->base_addr = i; } sl = netdev_priv(dev); + /* Initialize channel control data */ - sl->magic = SLCAN_MAGIC; - sl->dev = dev; + sl->magic = SLCAN_MAGIC; + sl->dev = dev; + sl->to_ack = 0; spin_lock_init(&sl->lock); slcan_devs[i] = dev; @@ -752,7 +1047,6 @@ * * Called in process context serialized from other ldisc calls. */ - static int slcan_open(struct tty_struct *tty) { struct slcan *sl; @@ -766,12 +1060,14 @@ return -EOPNOTSUPP; #endif + /* RTnetlink lock is misused here to serialize concurrent - opens of slcan channels. There are better ways, but it is - the simplest one. + opens of slcan channels. There are better ways, but it is + the simplest one. */ rtnl_lock(); + /* Collect hanged up channels. */ slc_sync(); @@ -793,16 +1089,17 @@ sl->line = tty_devnum(tty); sl->pid = current->pid; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) /* FIXME: already done before we were called - seems this can go */ if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); + tty->driver->flush_buffer(tty); #endif if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLCAN initialization. */ - sl->rcount = 0; - sl->xleft = 0; + sl->rcount = 0; + sl->xleft = 0; set_bit(SLF_INUSE, &sl->flags); @@ -814,42 +1111,42 @@ /* Done. We have linked the TTY line to a channel. */ rtnl_unlock(); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - tty->receive_room = 65536; /* We don't flow control */ + tty->receive_room = 65536; /* We don't flow control */ #endif return sl->dev->base_addr; -err_free_chan: - sl->tty = NULL; + err_free_chan: sl->tty = NULL; tty->disc_data = NULL; clear_bit(SLF_INUSE, &sl->flags); -err_exit: - rtnl_unlock(); + err_exit: rtnl_unlock(); + /* Count references from TTY module */ return err; } /* - FIXME: 1,2 are fixed 3 was never true anyway. + FIXME: 1,2 are fixed 3 was never true anyway. - Let me to blame a bit. - 1. TTY module calls this funstion on soft interrupt. - 2. TTY module calls this function WITH MASKED INTERRUPTS! - 3. TTY module does not notify us about line discipline - shutdown, + Let me to blame a bit. + 1. TTY module calls this function on soft interrupt. + 2. TTY module calls this function WITH MASKED INTERRUPTS! + 3. TTY module does not notify us about line discipline + shutdown, - Seems, now it is clean. The solution is to consider netdevice and - line discipline sides as two independent threads. + Seems, now it is clean. The solution is to consider netdevice and + line discipline sides as two independent threads. - By-product (not desired): slc? does not feel hangups and remains open. - It is supposed, that user level program (dip, diald, slattach...) - will catch SIGHUP and make the rest of work. + By-product (not desired): slc? does not feel hangups and remains open. + It is supposed, that user level program (dip, diald, slattach...) + will catch SIGHUP and make the rest of work. - I see no way to make more with current tty code. --ANK + I see no way to make more with current tty code. --ANK */ /* @@ -861,6 +1158,7 @@ { struct slcan *sl = (struct slcan *) tty->disc_data; + /* First make sure we're connected. */ if (!sl || sl->magic != SLCAN_MAGIC || sl->tty != tty) return; @@ -870,62 +1168,71 @@ if (!sl->leased) sl->line = 0; + /* Count references from TTY module */ } /* Perform I/O control on an active SLCAN channel. */ -static int slcan_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) +static int slcan_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct slcan *sl = (struct slcan *) tty->disc_data; unsigned int tmp; + /* First make sure we're connected. */ if (!sl || sl->magic != SLCAN_MAGIC) return -EINVAL; switch (cmd) { - case SIOCGIFNAME: - tmp = strlen(sl->dev->name) + 1; - if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) - return -EFAULT; - return 0; + case SIOCGIFENCAP: + //Do we use binary encapsulation? + return sl->flags & (1 << SLF_BINENC); + case SIOCSIFENCAP: + //Let's modify our encapsulation! + if (arg > 0) { + sl->flags |= (1 << SLF_BINENC); + printk(KERN_INFO "slcan: Now using binary encapsulation on device %s!", sl->dev->name); + } else { + sl->flags &= ~(1 << SLF_BINENC); + printk(KERN_INFO "slcan: Now using ASCII encapsulation on device %s!", sl->dev->name); + } + return 0; + case SIOCGIFNAME: + tmp = strlen(sl->dev->name) + 1; + if (copy_to_user((void __user *)arg, sl->dev->name, tmp)) + return -EFAULT; + return 0; - case SIOCSIFHWADDR: - return -EINVAL; + case SIOCSIFHWADDR: + return -EINVAL; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) - /* Allow stty to read, but not set, the serial port */ - case TCGETS: - case TCGETA: - return n_tty_ioctl(tty, file, cmd, arg); + /* Allow stty to read, but not set, the serial port */ + case TCGETS: + case TCGETA: + return n_tty_ioctl(tty, file, cmd, arg); - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; #else - default: - return tty_mode_ioctl(tty, file, cmd, arg); + default: + return tty_mode_ioctl(tty, file, cmd, arg); #endif } } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27) -static struct tty_ldisc slc_ldisc = { +static struct tty_ldisc slc_ldisc = { #else static struct tty_ldisc_ops slc_ldisc = { #endif - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "slcan", - .open = slcan_open, - .close = slcan_close, - .ioctl = slcan_ioctl, - .receive_buf = slcan_receive_buf, + .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "slcan", .open = slcan_open, .close = slcan_close, + .ioctl = slcan_ioctl, .receive_buf = slcan_receive_buf, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) - .receive_room = slcan_receive_room, + .receive_room = slcan_receive_room, #endif - .write_wakeup = slcan_write_wakeup, -}; + .write_wakeup = slcan_write_wakeup, }; /************************************ * general slcan module init/exit @@ -936,8 +1243,11 @@ int status; if (maxdev < 4) - maxdev = 4; /* Sanity */ + maxdev = 4; /* Sanity */ + if (maxack < 1) + maxack = 1; /* Dito */ + printk(banner); printk(KERN_INFO "slcan: %d dynamic interface channels.\n", maxdev); @@ -952,7 +1262,7 @@ /* Fill in our line protocol discipline, and register it */ status = tty_register_ldisc(N_SLCAN, &slc_ldisc); - if (status != 0) { + if (status != 0) { printk(KERN_ERR "slcan: can't register line discipline\n"); kfree(slcan_devs); } @@ -968,19 +1278,19 @@ int busy = 0; if (slcan_devs == NULL) - return; + return; /* First of all: check for active disciplines and hangup them. */ do { if (busy) - msleep_interruptible(100); + msleep_interruptible(100); busy = 0; for (i = 0; i < maxdev; i++) { dev = slcan_devs[i]; if (!dev) - continue; + continue; sl = netdev_priv(dev); spin_lock_bh(&sl->lock); if (sl->tty) { @@ -989,19 +1299,18 @@ } spin_unlock_bh(&sl->lock); } - } while (busy && time_before(jiffies, timeout)); + }while (busy && time_before(jiffies, timeout)); - for (i = 0; i < maxdev; i++) { dev = slcan_devs[i]; if (!dev) - continue; + continue; slcan_devs[i] = NULL; sl = netdev_priv(dev); if (sl->tty) { printk(KERN_ERR "%s: tty discipline still running\n", - dev->name); + dev->name); /* Intentionally leak the control block. */ dev->destructor = NULL; } @@ -1014,8 +1323,8 @@ i = tty_unregister_ldisc(N_SLCAN); if (i) - printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i); + printk(KERN_ERR "slcan: can't unregister ldisc (err %d)\n", i); } -module_init(slcan_init); -module_exit(slcan_exit); +module_init( slcan_init); +module_exit( slcan_exit); Index: can-utils/slcan_attach.c =================================================================== --- can-utils/slcan_attach.c (Revision 1054) +++ can-utils/slcan_attach.c (Arbeitskopie) @@ -45,18 +45,22 @@ * */ -#include <stdio.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> -#include <fcntl.h> -#include <unistd.h> +#include <stdio.h> #include <getopt.h> #include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/file.h> +#define BUFFERSIZE 20 #define LDISC_N_SLCAN 17 /* default slcan line discipline since Kernel 2.6.25 */ -void print_usage(char *prg) -{ +void print_usage(char *prg) { fprintf(stderr, "\nUsage: %s [options] tty\n\n", prg); fprintf(stderr, "Options: -o (send open command 'O\\r')\n"); fprintf(stderr, " -c (send close command 'C\\r')\n"); @@ -64,6 +68,7 @@ fprintf(stderr, " -b <btr> (set bit time register value)\n"); fprintf(stderr, " -d (only detach line discipline)\n"); fprintf(stderr, " -w (attach - wait for keypess - detach)\n"); + fprintf(stderr, " -B (use binary mode)\n"); fprintf(stderr, "\nExamples:\n"); fprintf(stderr, "slcan_attach -w -o -s6 -c /dev/ttyS1\n"); fprintf(stderr, "slcan_attach /dev/ttyS1\n"); @@ -72,26 +77,30 @@ exit(1); } -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { int fd; int ldisc = LDISC_N_SLCAN; int detach = 0; int waitkey = 0; int send_open = 0; int send_close = 0; + int binary = 0; char *speed = NULL; char *btr = NULL; char buf[10]; char *tty; int opt; - while ((opt = getopt(argc, argv, "l:dwocs:b:?")) != -1) { + while ((opt = getopt(argc, argv, "l:dwBocs:b:?")) != -1) { switch (opt) { case 'l': fprintf(stderr, "Ignored option '-l'\n"); break; + case 'B': + binary = 1; + break; + case 'd': detach = 1; break; @@ -132,7 +141,7 @@ tty = argv[optind]; - if ((fd = open (tty, O_WRONLY | O_NOCTTY)) < 0) { + if ((fd = open(tty, O_WRONLY | O_NOCTTY)) < 0) { perror(tty); exit(1); } @@ -154,9 +163,13 @@ write(fd, buf, strlen(buf)); } - if (ioctl (fd, TIOCSETD, &ldisc) < 0) { + if (ioctl(fd, TIOCSETD, &ldisc) < 0) { perror("ioctl"); exit(1); + } else { + if (binary) + if(ioctl(fd, SIOCSIFENCAP, 1)!=0) + perror("ioctl2"); } } @@ -167,7 +180,7 @@ if (waitkey || detach) { ldisc = N_TTY; - if (ioctl (fd, TIOCSETD, &ldisc) < 0) { + if (ioctl(fd, TIOCSETD, &ldisc) < 0) { perror("ioctl"); exit(1); } _______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
