drivers/net/usb/qmi_wwan.c

Source file repositories/reference/linux-study-clean/drivers/net/usb/qmi_wwan.c

File Facts

System
Linux kernel
Corpus path
drivers/net/usb/qmi_wwan.c
Extension
.c
Size
58399 bytes
Lines
1623
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 qmimux_netdev_ops = {
	.ndo_open        = qmimux_open,
	.ndo_stop        = qmimux_stop,
	.ndo_start_xmit  = qmimux_start_xmit,
};

static void qmimux_setup(struct net_device *dev)
{
	dev->header_ops      = NULL;  /* No header */
	dev->type            = ARPHRD_NONE;
	dev->hard_header_len = 0;
	dev->addr_len        = 0;
	dev->flags           = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
	dev->netdev_ops      = &qmimux_netdev_ops;
	dev->mtu             = 1500;
	dev->pcpu_stat_type  = NETDEV_PCPU_STAT_TSTATS;
	dev->needs_free_netdev = true;
}

static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id)
{
	struct qmimux_priv *priv;
	struct list_head *iter;
	struct net_device *ldev;

	rcu_read_lock();
	netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) {
		priv = netdev_priv(ldev);
		if (priv->mux_id == mux_id) {
			rcu_read_unlock();
			return ldev;
		}
	}
	rcu_read_unlock();
	return NULL;
}

static bool qmimux_has_slaves(struct usbnet *dev)
{
	return !list_empty(&dev->net->adj_list.upper);
}

static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{
	unsigned int len, offset = 0, pad_len, pkt_len;
	struct qmimux_hdr *hdr;
	struct net_device *net;
	struct sk_buff *skbn;
	u8 qmimux_hdr_sz = sizeof(*hdr);

	while (offset + qmimux_hdr_sz < skb->len) {
		hdr = (struct qmimux_hdr *)(skb->data + offset);
		len = be16_to_cpu(hdr->pkt_len);

		/* drop the packet, bogus length */
		if (offset + len + qmimux_hdr_sz > skb->len)
			return 0;

		/* control packet, we do not know what to do */
		if (hdr->pad & 0x80)
			goto skip;

		/* extract padding length and check for valid length info */
		pad_len = hdr->pad & 0x3f;
		if (len == 0 || pad_len >= len)
			goto skip;
		pkt_len = len - pad_len;

		net = qmimux_find_dev(dev, hdr->mux_id);
		if (!net)
			goto skip;
		skbn = netdev_alloc_skb(net, pkt_len + LL_MAX_HEADER);
		if (!skbn)
			return 0;

	       /* Raw IP packets don't have a MAC header, but other subsystems
		* (like xfrm) may still access MAC header offsets, so they must
		* be initialized.
		*/
		skb_reset_mac_header(skbn);

		switch (skb->data[offset + qmimux_hdr_sz] & 0xf0) {
		case 0x40:
			skbn->protocol = htons(ETH_P_IP);
			break;
		case 0x60:
			skbn->protocol = htons(ETH_P_IPV6);
			break;
		default:
			/* not ip - do not know what to do */

Annotation

Implementation Notes