drivers/net/wan/fsl_qmc_hdlc.c

Source file repositories/reference/linux-study-clean/drivers/net/wan/fsl_qmc_hdlc.c

File Facts

System
Linux kernel
Corpus path
drivers/net/wan/fsl_qmc_hdlc.c
Extension
.c
Size
19842 bytes
Lines
809
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 qmc_hdlc_netdev_ops = {
	.ndo_open       = qmc_hdlc_open,
	.ndo_stop       = qmc_hdlc_close,
	.ndo_start_xmit = hdlc_start_xmit,
	.ndo_siocwandev = qmc_hdlc_ioctl,
};

static int qmc_hdlc_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct qmc_chan_ts_info ts_info;
	struct qmc_hdlc *qmc_hdlc;
	struct qmc_chan_info info;
	hdlc_device *hdlc;
	int ret;

	qmc_hdlc = devm_kzalloc(dev, sizeof(*qmc_hdlc), GFP_KERNEL);
	if (!qmc_hdlc)
		return -ENOMEM;

	qmc_hdlc->dev = dev;
	spin_lock_init(&qmc_hdlc->tx_lock);
	mutex_init(&qmc_hdlc->carrier_lock);

	qmc_hdlc->qmc_chan = devm_qmc_chan_get_bychild(dev, dev->of_node);
	if (IS_ERR(qmc_hdlc->qmc_chan))
		return dev_err_probe(dev, PTR_ERR(qmc_hdlc->qmc_chan),
				     "get QMC channel failed\n");

	ret = qmc_chan_get_info(qmc_hdlc->qmc_chan, &info);
	if (ret)
		return dev_err_probe(dev, ret, "get QMC channel info failed\n");

	if (info.mode != QMC_HDLC)
		return dev_err_probe(dev, -EINVAL, "QMC chan mode %d is not QMC_HDLC\n",
				     info.mode);

	ret = qmc_chan_get_ts_info(qmc_hdlc->qmc_chan, &ts_info);
	if (ret)
		return dev_err_probe(dev, ret, "get QMC channel ts info failed\n");

	ret = qmc_hdlc_xlate_ts_info(qmc_hdlc, &ts_info, &qmc_hdlc->slot_map);
	if (ret)
		return ret;

	qmc_hdlc->framer = devm_framer_optional_get(dev, "fsl,framer");
	if (IS_ERR(qmc_hdlc->framer))
		return PTR_ERR(qmc_hdlc->framer);

	ret = qmc_hdlc_framer_init(qmc_hdlc);
	if (ret)
		return ret;

	qmc_hdlc->netdev = alloc_hdlcdev(qmc_hdlc);
	if (!qmc_hdlc->netdev) {
		ret = -ENOMEM;
		goto framer_exit;
	}

	hdlc = dev_to_hdlc(qmc_hdlc->netdev);
	hdlc->attach = qmc_hdlc_attach;
	hdlc->xmit = qmc_hdlc_xmit;
	SET_NETDEV_DEV(qmc_hdlc->netdev, dev);
	qmc_hdlc->netdev->tx_queue_len = ARRAY_SIZE(qmc_hdlc->tx_descs);
	qmc_hdlc->netdev->netdev_ops = &qmc_hdlc_netdev_ops;
	ret = register_hdlc_device(qmc_hdlc->netdev);
	if (ret) {
		dev_err_probe(dev, ret, "failed to register hdlc device\n");
		goto free_netdev;
	}

	platform_set_drvdata(pdev, qmc_hdlc);
	return 0;

free_netdev:
	free_netdev(qmc_hdlc->netdev);
framer_exit:
	qmc_hdlc_framer_exit(qmc_hdlc);
	return ret;
}

static void qmc_hdlc_remove(struct platform_device *pdev)
{
	struct qmc_hdlc *qmc_hdlc = platform_get_drvdata(pdev);

	unregister_hdlc_device(qmc_hdlc->netdev);
	free_netdev(qmc_hdlc->netdev);
	qmc_hdlc_framer_exit(qmc_hdlc);
}

Annotation

Implementation Notes