drivers/net/can/slcan/slcan-core.c

Source file repositories/reference/linux-study-clean/drivers/net/can/slcan/slcan-core.c

File Facts

System
Linux kernel
Corpus path
drivers/net/can/slcan/slcan-core.c
Extension
.c
Size
24707 bytes
Lines
955
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: operation-table or driver-model contract
Status
pattern implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

static const struct net_device_ops slcan_netdev_ops = {
	.ndo_open               = slcan_netdev_open,
	.ndo_stop               = slcan_netdev_close,
	.ndo_start_xmit         = slcan_netdev_xmit,
};

/******************************************
 *  Routines looking at TTY side.
 ******************************************/

/* Handle the 'receiver data ready' interrupt.
 * This function is called by the 'tty_io' module in the kernel when
 * a block of SLCAN data has been received, which can now be decapsulated
 * and sent on to some IP layer for further processing. This will not
 * be re-entered while running but other ldisc functions may be called
 * in parallel
 */
static void slcan_receive_buf(struct tty_struct *tty, const u8 *cp,
			      const u8 *fp, size_t count)
{
	struct slcan *sl = tty->disc_data;

	if (!netif_running(sl->dev))
		return;

	/* 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.
 * This function is called by the TTY module when the
 * SLCAN line discipline is called for.
 *
 * Called in process context serialized from other ldisc calls.
 */
static int 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;

	sl = netdev_priv(dev);

	/* Configure TTY interface */
	tty->receive_room = 65536; /* We don't flow control */
	sl->rcount = 0;
	sl->xleft = 0;
	spin_lock_init(&sl->lock);
	INIT_WORK(&sl->tx_work, slcan_transmit);
	init_waitqueue_head(&sl->xcmd_wait);

	/* Configure CAN metadata */
	sl->can.bitrate_const = slcan_bitrate_const;
	sl->can.bitrate_const_cnt = ARRAY_SIZE(slcan_bitrate_const);
	sl->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY;

	/* Configure netdev interface */
	sl->dev	= dev;
	dev->netdev_ops = &slcan_netdev_ops;
	dev->ethtool_ops = &slcan_ethtool_ops;

	/* Mark ldisc channel as alive */
	sl->tty = tty;
	tty->disc_data = sl;

	err = register_candev(dev);
	if (err) {
		free_candev(dev);
		pr_err("can't register candev\n");
		return err;
	}

Annotation

Implementation Notes