⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eth1394.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 4 页
字号:
 * arphdr) is the same format as the ip1394 header, so they overlap.  The rest
 * needs to be munged a bit.  The remainder of the arphdr is formatted based
 * on hwaddr len and ipaddr len.  We know what they'll be, so it's easy to
 * judge.
 *
 * Now that the EUI is used for the hardware address all we need to do to make
 * this work for 1394 is to insert 2 quadlets that contain max_rec size,
 * speed, and unicast FIFO address information between the sender_unique_id
 * and the IP addresses.
 */
static void ether1394_arp_to_1394arp(struct sk_buff *skb,
				     struct net_device *dev)
{
	struct eth1394_priv *priv = netdev_priv(dev);
	struct arphdr *arp = (struct arphdr *)skb->data;
	unsigned char *arp_ptr = (unsigned char *)(arp + 1);
	struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data;

	arp1394->hw_addr_len	= 16;
	arp1394->sip		= *(u32*)(arp_ptr + ETH1394_ALEN);
	arp1394->max_rec	= priv->host->csr.max_rec;
	arp1394->sspd		= priv->host->csr.lnk_spd;
	arp1394->fifo_hi	= htons(priv->local_fifo >> 32);
	arp1394->fifo_lo	= htonl(priv->local_fifo & ~0x0);
}

/* We need to encapsulate the standard header with our own. We use the
 * ethernet header's proto for our own. */
static unsigned int ether1394_encapsulate_prep(unsigned int max_payload,
					       __be16 proto,
					       union eth1394_hdr *hdr,
					       u16 dg_size, u16 dgl)
{
	unsigned int adj_max_payload =
				max_payload - hdr_type_len[ETH1394_HDR_LF_UF];

	/* Does it all fit in one packet? */
	if (dg_size <= adj_max_payload) {
		hdr->uf.lf = ETH1394_HDR_LF_UF;
		hdr->uf.ether_type = proto;
	} else {
		hdr->ff.lf = ETH1394_HDR_LF_FF;
		hdr->ff.ether_type = proto;
		hdr->ff.dg_size = dg_size - 1;
		hdr->ff.dgl = dgl;
		adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF];
	}
	return (dg_size + adj_max_payload - 1) / adj_max_payload;
}

static unsigned int ether1394_encapsulate(struct sk_buff *skb,
					  unsigned int max_payload,
					  union eth1394_hdr *hdr)
{
	union eth1394_hdr *bufhdr;
	int ftype = hdr->common.lf;
	int hdrsz = hdr_type_len[ftype];
	unsigned int adj_max_payload = max_payload - hdrsz;

	switch (ftype) {
	case ETH1394_HDR_LF_UF:
		bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
		bufhdr->words.word1 = htons(hdr->words.word1);
		bufhdr->words.word2 = hdr->words.word2;
		break;

	case ETH1394_HDR_LF_FF:
		bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz);
		bufhdr->words.word1 = htons(hdr->words.word1);
		bufhdr->words.word2 = hdr->words.word2;
		bufhdr->words.word3 = htons(hdr->words.word3);
		bufhdr->words.word4 = 0;

		/* Set frag type here for future interior fragments */
		hdr->common.lf = ETH1394_HDR_LF_IF;
		hdr->sf.fg_off = 0;
		break;

	default:
		hdr->sf.fg_off += adj_max_payload;
		bufhdr = (union eth1394_hdr *)skb_pull(skb, adj_max_payload);
		if (max_payload >= skb->len)
			hdr->common.lf = ETH1394_HDR_LF_LF;
		bufhdr->words.word1 = htons(hdr->words.word1);
		bufhdr->words.word2 = htons(hdr->words.word2);
		bufhdr->words.word3 = htons(hdr->words.word3);
		bufhdr->words.word4 = 0;
	}
	return min(max_payload, skb->len);
}

static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host)
{
	struct hpsb_packet *p;

	p = hpsb_alloc_packet(0);
	if (p) {
		p->host = host;
		p->generation = get_hpsb_generation(host);
		p->type = hpsb_async;
	}
	return p;
}

static int ether1394_prep_write_packet(struct hpsb_packet *p,
				       struct hpsb_host *host, nodeid_t node,
				       u64 addr, void *data, int tx_len)
{
	p->node_id = node;

	if (hpsb_get_tlabel(p))
		return -EAGAIN;

	p->tcode = TCODE_WRITEB;
	p->header_size = 16;
	p->expect_response = 1;
	p->header[0] =
		p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4;
	p->header[1] = host->node_id << 16 | addr >> 32;
	p->header[2] = addr & 0xffffffff;
	p->header[3] = tx_len << 16;
	p->data_size = (tx_len + 3) & ~3;
	p->data = data;

	return 0;
}

static void ether1394_prep_gasp_packet(struct hpsb_packet *p,
				       struct eth1394_priv *priv,
				       struct sk_buff *skb, int length)
{
	p->header_size = 4;
	p->tcode = TCODE_STREAM_DATA;

	p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 |
		       TCODE_STREAM_DATA << 4;
	p->data_size = length;
	p->data = (quadlet_t *)skb->data - 2;
	p->data[0] = cpu_to_be32(priv->host->node_id << 16 |
				 ETHER1394_GASP_SPECIFIER_ID_HI);
	p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 |
				 ETHER1394_GASP_VERSION);

	p->speed_code = priv->bc_sspd;

	/* prevent hpsb_send_packet() from overriding our speed code */
	p->node_id = LOCAL_BUS | ALL_NODES;
}

static void ether1394_free_packet(struct hpsb_packet *packet)
{
	if (packet->tcode != TCODE_STREAM_DATA)
		hpsb_free_tlabel(packet);
	hpsb_free_packet(packet);
}

static void ether1394_complete_cb(void *__ptask);

static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len)
{
	struct eth1394_priv *priv = ptask->priv;
	struct hpsb_packet *packet = NULL;

	packet = ether1394_alloc_common_packet(priv->host);
	if (!packet)
		return -ENOMEM;

	if (ptask->tx_type == ETH1394_GASP) {
		int length = tx_len + 2 * sizeof(quadlet_t);

		ether1394_prep_gasp_packet(packet, priv, ptask->skb, length);
	} else if (ether1394_prep_write_packet(packet, priv->host,
					       ptask->dest_node,
					       ptask->addr, ptask->skb->data,
					       tx_len)) {
		hpsb_free_packet(packet);
		return -EAGAIN;
	}

	ptask->packet = packet;
	hpsb_set_packet_complete_task(ptask->packet, ether1394_complete_cb,
				      ptask);

	if (hpsb_send_packet(packet) < 0) {
		ether1394_free_packet(packet);
		return -EIO;
	}

	return 0;
}

/* Task function to be run when a datagram transmission is completed */
static void ether1394_dg_complete(struct packet_task *ptask, int fail)
{
	struct sk_buff *skb = ptask->skb;
	struct eth1394_priv *priv = netdev_priv(skb->dev);
	unsigned long flags;

	/* Statistics */
	spin_lock_irqsave(&priv->lock, flags);
	if (fail) {
		priv->stats.tx_dropped++;
		priv->stats.tx_errors++;
	} else {
		priv->stats.tx_bytes += skb->len;
		priv->stats.tx_packets++;
	}
	spin_unlock_irqrestore(&priv->lock, flags);

	dev_kfree_skb_any(skb);
	kmem_cache_free(packet_task_cache, ptask);
}

/* Callback for when a packet has been sent and the status of that packet is
 * known */
static void ether1394_complete_cb(void *__ptask)
{
	struct packet_task *ptask = (struct packet_task *)__ptask;
	struct hpsb_packet *packet = ptask->packet;
	int fail = 0;

	if (packet->tcode != TCODE_STREAM_DATA)
		fail = hpsb_packet_success(packet);

	ether1394_free_packet(packet);

	ptask->outstanding_pkts--;
	if (ptask->outstanding_pkts > 0 && !fail) {
		int tx_len, err;

		/* Add the encapsulation header to the fragment */
		tx_len = ether1394_encapsulate(ptask->skb, ptask->max_payload,
					       &ptask->hdr);
		err = ether1394_send_packet(ptask, tx_len);
		if (err) {
			if (err == -EAGAIN)
				ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n");

			ether1394_dg_complete(ptask, 1);
		}
	} else {
		ether1394_dg_complete(ptask, fail);
	}
}

/* Transmit a packet (called by kernel) */
static int ether1394_tx(struct sk_buff *skb, struct net_device *dev)
{
	struct eth1394hdr hdr_buf;
	struct eth1394_priv *priv = netdev_priv(dev);
	__be16 proto;
	unsigned long flags;
	nodeid_t dest_node;
	eth1394_tx_type tx_type;
	unsigned int tx_len;
	unsigned int max_payload;
	u16 dg_size;
	u16 dgl;
	struct packet_task *ptask;
	struct eth1394_node_ref *node;
	struct eth1394_node_info *node_info = NULL;

	ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC);
	if (ptask == NULL)
		goto fail;

	/* XXX Ignore this for now. Noticed that when MacOSX is the IRM,
	 * it does not set our validity bit. We need to compensate for
	 * that somewhere else, but not in eth1394. */
#if 0
	if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000)
		goto fail;
#endif

	skb = skb_share_check(skb, GFP_ATOMIC);
	if (!skb)
		goto fail;

	/* Get rid of the fake eth1394 header, but first make a copy.
	 * We might need to rebuild the header on tx failure. */
	memcpy(&hdr_buf, skb->data, sizeof(hdr_buf));
	skb_pull(skb, ETH1394_HLEN);

	proto = hdr_buf.h_proto;
	dg_size = skb->len;

	/* Set the transmission type for the packet.  ARP packets and IP
	 * broadcast packets are sent via GASP. */
	if (memcmp(hdr_buf.h_dest, dev->broadcast, ETH1394_ALEN) == 0 ||
	    proto == htons(ETH_P_ARP) ||
	    (proto == htons(ETH_P_IP) &&
	     IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) {
		tx_type = ETH1394_GASP;
		dest_node = LOCAL_BUS | ALL_NODES;
		max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
		BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);
		dgl = priv->bc_dgl;
		if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
			priv->bc_dgl++;
	} else {
		__be64 guid = get_unaligned((u64 *)hdr_buf.h_dest);

		node = eth1394_find_node_guid(&priv->ip_node_list,
					      be64_to_cpu(guid));
		if (!node)
			goto fail;

		node_info =
		    (struct eth1394_node_info *)node->ud->device.driver_data;
		if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE)
			goto fail;

		dest_node = node->ud->ne->nodeid;
		max_payload = node_info->maxpayload;
		BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD);

		dgl = node_info->dgl;
		if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
			node_info->dgl++;
		tx_type = ETH1394_WRREQ;
	}

	/* If this is an ARP packet, convert it */
	if (proto == htons(ETH_P_ARP))
		ether1394_arp_to_1394arp(skb, dev);

	ptask->hdr.words.word1 = 0;
	ptask->hdr.words.word2 = 0;
	ptask->hdr.words.word3 = 0;
	ptask->hdr.words.word4 = 0;
	ptask->skb = skb;
	ptask->priv = priv;
	ptask->tx_type = tx_type;

	if (tx_type != ETH1394_GASP) {
		u64 addr;

		spin_lock_irqsave(&priv->lock, flags);
		addr = node_info->fifo;
		spin_unlock_irqrestore(&priv->lock, flags);

		ptask->addr = addr;
		ptask->dest_node = dest_node;
	}

	ptask->tx_type = tx_type;
	ptask->max_payload = max_payload;
	ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload,
					proto, &ptask->hdr, dg_size, dgl);

	/* Add the encapsulation header to the fragment */
	tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr);
	dev->trans_start = jiffies;
	if (ether1394_send_packet(ptask, tx_len)) {
		if (dest_node == (LOCAL_BUS | ALL_NODES))
			goto fail;

		/* At this point we want to restore the packet.  When we return
		 * here with NETDEV_TX_BUSY we will get another entrance in this
		 * routine with the same skb and we need it to look the same.
		 * So we pull 4 more bytes, then build the header again. */
		skb_pull(skb, 4);
		ether1394_header(skb, dev, ntohs(hdr_buf.h_proto),
				 hdr_buf.h_dest, NULL, 0);

		/* Most failures of ether1394_send_packet are recoverable. */
		netif_stop_queue(dev);
		priv->wake_node = dest_node;
		schedule_work(&priv->wake);
		kmem_cache_free(packet_task_cache, ptask);
		return NETDEV_TX_BUSY;
	}

	return NETDEV_TX_OK;
fail:
	if (ptask)
		kmem_cache_free(packet_task_cache, ptask);

	if (skb != NULL)
		dev_kfree_skb(skb);

	spin_lock_irqsave(&priv->lock, flags);
	priv->stats.tx_dropped++;
	priv->stats.tx_errors++;
	spin_unlock_irqrestore(&priv->lock, flags);

	/*
	 * FIXME: According to a patch from 2003-02-26, "returning non-zero
	 * causes serious problems" here, allegedly.  Before that patch,
	 * -ERRNO was returned which is not appropriate under Linux 2.6.
	 * Perhaps more needs to be done?  Stop the queue in serious
	 * conditions and restart it elsewhere?
	 */
	/* return NETDEV_TX_BUSY; */
	return NETDEV_TX_OK;
}

static void ether1394_get_drvinfo(struct net_device *dev,
				  struct ethtool_drvinfo *info)
{
	strcpy(info->driver, driver_name);
	strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */
}

static struct ethtool_ops ethtool_ops = {
	.get_drvinfo = ether1394_get_drvinfo
};

static int __init ether1394_init_module(void)
{
	int err;

	packet_task_cache = kmem_cache_create("packet_task",
					      sizeof(struct packet_task),
					      0, 0, NULL, NULL);
	if (!packet_task_cache)
		return -ENOMEM;

	hpsb_register_highlevel(&eth1394_highlevel);
	err = hpsb_register_protocol(&eth1394_proto_driver);
	if (err) {
		hpsb_unregister_highlevel(&eth1394_highlevel);
		kmem_cache_destroy(packet_task_cache);
	}
	return err;
}

static void __exit ether1394_exit_module(void)
{
	hpsb_unregister_protocol(&eth1394_proto_driver);
	hpsb_unregister_highlevel(&eth1394_highlevel);
	kmem_cache_destroy(packet_task_cache);
}

module_init(ether1394_init_module);
module_exit(ether1394_exit_module);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -