/* These are pointers to the malloc()ed frame buffers. */ unsignedchar rbuff[SLCAN_MTU]; /* receiver buffer */ int rcount; /* received chars counter */ unsignedchar xbuff[SLCAN_MTU]; /* transmitter buffer*/ unsignedchar *xhead; /* pointer to next XMIT byte */ int xleft; /* bytes left in XMIT queue */
unsignedlong flags; /* Flag values/ mode etc */ #define SLF_ERROR 0/* Parity, etc. error */ #define SLF_XCMD 1/* Command transmission */ unsignedlong cmd_flags; /* Command flags */ #define CF_ERR_RST 0/* Reset errors on open */
wait_queue_head_t xcmd_wait; /* Wait queue for commands */ /* transmission */
};
/* A CAN frame has a can_id (11 bit standard frame format OR 29 bit extended *frameformat)adatalengthcode(len)whichcanbefrom0to8 *andupto<len>databytesaspayload. *AdditionallyaCANframemaybecomearemotetransmissionframeifthe *RTR-bitisset.ThiscausesanotherECUtosendaCANframewiththe *givencan_id. * *TheSLCANASCIIrepresentationofthesedifferentframetypesis: *<type><id><dlc><data>* * *Extendedframes(29bit)aredefinedbycapitalcharactersinthetype. *RTRframesaredefinedas'r'types-normalframeshave't'type: *t=>11bitdataframe *r=>11bitRTRframe *T=>29bitdataframe *R=>29bitRTRframe * *The<id>is3(standard)or8(extended)bytesinASCIIHex(base64). *The<dlc>isaonebyteASCIInumber('0'-'8') *The<data>sectionhasatmuchASCIIHexbytesasdefinedbythe<dlc> * *Examples: * *t1230:can_id0x123,len0,nodata *t4563112233:can_id0x456,len3,data0x110x220x33 *T12ABCDEF2AA55:extendedcan_id0x12ABCDEF,len2,data0xAA0x55 *r1230:can_id0x123,len0,nodata,remotetransmissionrequest *
*/
/* Send one completely decapsulated can_frame to the network layer */ staticvoid slcan_bump_frame(struct slcan *sl)
{ struct sk_buff *skb; struct can_frame *cf; int i, tmp;
u32 tmpid; char *cmd = sl->rbuff;
if (sl->rcount < SLCAN_FRAME_MSG_LEN_MIN) return;
skb = alloc_can_skb(sl->dev, &cf); if (unlikely(!skb)) {
sl->dev->stats.rx_dropped++; return;
}
switch (*cmd) { case'r':
cf->can_id = CAN_RTR_FLAG;
fallthrough; case't': /* store dlc ASCII value and terminate SFF CAN ID string */
cf->len = sl->rbuff[SLCAN_CMD_LEN + SLCAN_SFF_ID_LEN];
sl->rbuff[SLCAN_CMD_LEN + SLCAN_SFF_ID_LEN] = 0; /* point to payload data behind the dlc */
cmd += SLCAN_CMD_LEN + SLCAN_SFF_ID_LEN + 1; break; case'R':
cf->can_id = CAN_RTR_FLAG;
fallthrough; case'T':
cf->can_id |= CAN_EFF_FLAG; /* store dlc ASCII value and terminate EFF CAN ID string */
cf->len = sl->rbuff[SLCAN_CMD_LEN + SLCAN_EFF_ID_LEN];
sl->rbuff[SLCAN_CMD_LEN + SLCAN_EFF_ID_LEN] = 0; /* point to payload data behind the dlc */
cmd += SLCAN_CMD_LEN + SLCAN_EFF_ID_LEN + 1; break; default: goto decode_failed;
}
if (kstrtou32(sl->rbuff + SLCAN_CMD_LEN, 16, &tmpid)) goto decode_failed;
cf->can_id |= tmpid;
/* get len from sanitized ASCII value */ if (cf->len >= '0' && cf->len < '9')
cf->len -= '0'; else goto decode_failed;
/* RTR frames may have a dlc > 0 but they never have any data bytes */ if (!(cf->can_id & CAN_RTR_FLAG)) { for (i = 0; i < cf->len; i++) {
tmp = hex_to_bin(*cmd++); if (tmp < 0) goto decode_failed;
/* Encapsulate one can_frame and stuff into a TTY queue. */ staticvoid slcan_encaps(struct slcan *sl, struct can_frame *cf)
{ int actual, i; unsignedchar *pos; unsignedchar *endpos;
canid_t id = cf->can_id;
pos = sl->xbuff;
if (cf->can_id & CAN_RTR_FLAG)
*pos = 'R'; /* becomes 'r' in standard frame format (SFF) */ else
*pos = 'T'; /* becomes 't' in standard frame format (SSF) */
/* determine number of chars for the CAN-identifier */ if (cf->can_id & CAN_EFF_FLAG) {
id &= CAN_EFF_MASK;
endpos = pos + SLCAN_EFF_ID_LEN;
} else {
*pos |= 0x20; /* convert R/T to lower case for SFF */
id &= CAN_SFF_MASK;
endpos = pos + SLCAN_SFF_ID_LEN;
}
/* build 3 (SFF) or 8 (EFF) digit CAN identifier */
pos++; while (endpos >= pos) {
*endpos-- = hex_asc_upper[id & 0xf];
id >>= 4;
}
/* RTR frames may have a dlc > 0 but they never have any data bytes */ if (!(cf->can_id & CAN_RTR_FLAG)) { for (i = 0; i < cf->len; i++)
pos = hex_byte_pack_upper(pos, cf->data[i]);
sl->dev->stats.tx_bytes += cf->len;
}
*pos++ = '\r';
/* Order of next two lines is *very* important. *Whenwearesendingalittleamountofdata, *thetransfermaybecompletedinsidetheops->write() *routine,becauseit'srunningwithinterruptsenabled. *Inthiscasewe*never*gotWRITE_WAKEUPevent, *ifwedidnotrequestitbeforewriteoperation. *14Oct1994DmitryGorodchanin.
*/
set_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
actual = sl->tty->ops->write(sl->tty, sl->xbuff, pos - sl->xbuff);
sl->xleft = (pos - sl->xbuff) - actual;
sl->xhead = sl->xbuff + actual;
}
/* Write out any remaining transmit buffer. Scheduled when tty is writable */ staticvoid slcan_transmit(struct work_struct *work)
{ struct slcan *sl = container_of(work, struct slcan, tx_work); int actual;
spin_lock_bh(&sl->lock); /* First make sure we're connected. */ if (unlikely(!netif_running(sl->dev)) &&
likely(!test_bit(SLF_XCMD, &sl->flags))) {
spin_unlock_bh(&sl->lock); return;
}
if (sl->xleft <= 0) { if (unlikely(test_bit(SLF_XCMD, &sl->flags))) {
clear_bit(SLF_XCMD, &sl->flags);
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
spin_unlock_bh(&sl->lock);
wake_up(&sl->xcmd_wait); return;
}
/* Now serial buffer is almost free & we can start *transmissionofanotherpacket
*/
sl->dev->stats.tx_packets++;
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
spin_unlock_bh(&sl->lock);
netif_wake_queue(sl->dev); return;
}
/* Called by the driver when there's room for more data. *Schedulethetransmit.
*/ staticvoid slcan_write_wakeup(struct tty_struct *tty)
{ struct slcan *sl = tty->disc_data;
schedule_work(&sl->tx_work);
}
/* Send a can_frame to a TTY queue. */ static netdev_tx_t slcan_netdev_xmit(struct sk_buff *skb, struct net_device *dev)
{ struct slcan *sl = netdev_priv(dev);
if (can_dev_dropped_skb(dev, skb)) return NETDEV_TX_OK;
spin_lock(&sl->lock); if (!netif_running(dev)) {
spin_unlock(&sl->lock);
netdev_warn(dev, "xmit: iface is down\n"); goto out;
} if (!sl->tty) {
spin_unlock(&sl->lock); goto out;
}
/* Netdevice DOWN -> UP routine */ staticint slcan_netdev_open(struct net_device *dev)
{ struct slcan *sl = netdev_priv(dev); unsignedchar cmd[SLCAN_MTU]; int err, s;
/* The baud rate is not set with the command *`iplinkset<iface>typecanbitrate<baud>'andtherefore *can.bittiming.bitrateisCAN_BITRATE_UNSET(0),causing *open_candev()tofail.Solet'ssettoafakevalue.
*/ if (sl->can.bittiming.bitrate == CAN_BITRATE_UNSET)
sl->can.bittiming.bitrate = CAN_BITRATE_UNKNOWN;
err = open_candev(dev); if (err) {
netdev_err(dev, "failed to open can device\n"); return err;
}
if (sl->can.bittiming.bitrate != CAN_BITRATE_UNKNOWN) { for (s = 0; s < ARRAY_SIZE(slcan_bitrate_const); s++) { if (sl->can.bittiming.bitrate == slcan_bitrate_const[s]) break;
}
/* The CAN framework has already validate the bitrate value, *sowecanavoidtocheckif`s'hasbeenproperlyset.
*/
snprintf(cmd, sizeof(cmd), "C\rS%d\r", s);
err = slcan_transmit_cmd(sl, cmd); if (err) {
netdev_err(dev, "failed to send bitrate command 'C\\rS%d\\r'\n",
s); goto cmd_transmit_failed;
}
if (test_bit(CF_ERR_RST, &sl->cmd_flags)) {
err = slcan_transmit_cmd(sl, "F\r"); if (err) {
netdev_err(dev, "failed to send error command 'F\\r'\n"); goto cmd_transmit_failed;
}
}
if (sl->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) {
err = slcan_transmit_cmd(sl, "L\r"); if (err) {
netdev_err(dev, "failed to send listen-only command 'L\\r'\n"); goto cmd_transmit_failed;
}
} else {
err = slcan_transmit_cmd(sl, "O\r"); if (err) {
netdev_err(dev, "failed to send open command 'O\\r'\n"); goto cmd_transmit_failed;
}
}
}
/* Read the characters out of the buffer */ while (count--) { if (fp && *fp++) { if (!test_and_set_bit(SLF_ERROR, &sl->flags))
sl->dev->stats.rx_errors++;
cp++; continue;
}
slcan_unesc(sl, *cp++);
}
}
/* Open the high-level part of the SLCAN channel. *ThisfunctioniscalledbytheTTYmodulewhenthe *SLCANlinedisciplineiscalledfor. * *Calledinprocesscontextserializedfromotherldisccalls.
*/ staticint slcan_open(struct tty_struct *tty)
{ struct net_device *dev; struct slcan *sl; int err;
if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!tty->ops->write) return -EOPNOTSUPP;
dev = alloc_candev(sizeof(*sl), 1); if (!dev) return -ENFILE;
/* Fill in our line protocol discipline, and register it */
status = tty_register_ldisc(&slcan_ldisc); if (status)
pr_err("can't register line discipline\n");
return status;
}
staticvoid __exit slcan_exit(void)
{ /* This will only be called when all channels have been closed by *userspace-tty_ldisc.ctakescareofthemodule'srefcount.
*/
tty_unregister_ldisc(&slcan_ldisc);
}
module_init(slcan_init);
module_exit(slcan_exit);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.