qeth_main.c

来自「linux 内核源代码」· C语言 代码 · 共 2,480 行 · 第 1/5 页

C
2,480
字号
static inline voidqeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,		     char prot_type){	memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);	memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data),&prot_type,1);	memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),	       &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);}static intqeth_osn_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,		      int data_len){	u16 s1, s2;	QETH_DBF_TEXT(trace,4,"osndipa");	qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);	s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);	s2 = (u16)data_len;	memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);	memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);	memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);	memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);	return qeth_osn_send_control_data(card, s1, iob);}static intqeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,		  int (*reply_cb)		  (struct qeth_card *,struct qeth_reply*, unsigned long),		  void *reply_param){	int rc;	char prot_type;	QETH_DBF_TEXT(trace,4,"sendipa");	if (card->options.layer2)		if (card->info.type == QETH_CARD_TYPE_OSN)			prot_type = QETH_PROT_OSN2;		else			prot_type = QETH_PROT_LAYER2;	else		prot_type = QETH_PROT_TCPIP;	qeth_prepare_ipa_cmd(card,iob,prot_type);	rc = qeth_send_control_data(card, IPA_CMD_LENGTH, iob,				    reply_cb, reply_param);	return rc;}static intqeth_cm_enable_cb(struct qeth_card *card, struct qeth_reply *reply,		  unsigned long data){	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup, 2, "cmenblcb");	iob = (struct qeth_cmd_buffer *) data;	memcpy(&card->token.cm_filter_r,	       QETH_CM_ENABLE_RESP_FILTER_TOKEN(iob->data),	       QETH_MPC_TOKEN_LENGTH);	QETH_DBF_TEXT_(setup, 2, "  rc%d", iob->rc);	return 0;}static intqeth_cm_enable(struct qeth_card *card){	int rc;	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup,2,"cmenable");	iob = qeth_wait_for_buffer(&card->write);	memcpy(iob->data, CM_ENABLE, CM_ENABLE_SIZE);	memcpy(QETH_CM_ENABLE_ISSUER_RM_TOKEN(iob->data),	       &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_CM_ENABLE_FILTER_TOKEN(iob->data),	       &card->token.cm_filter_w, QETH_MPC_TOKEN_LENGTH);	rc = qeth_send_control_data(card, CM_ENABLE_SIZE, iob,				    qeth_cm_enable_cb, NULL);	return rc;}static intqeth_cm_setup_cb(struct qeth_card *card, struct qeth_reply *reply,		 unsigned long data){	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup, 2, "cmsetpcb");	iob = (struct qeth_cmd_buffer *) data;	memcpy(&card->token.cm_connection_r,	       QETH_CM_SETUP_RESP_DEST_ADDR(iob->data),	       QETH_MPC_TOKEN_LENGTH);	QETH_DBF_TEXT_(setup, 2, "  rc%d", iob->rc);	return 0;}static intqeth_cm_setup(struct qeth_card *card){	int rc;	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup,2,"cmsetup");	iob = qeth_wait_for_buffer(&card->write);	memcpy(iob->data, CM_SETUP, CM_SETUP_SIZE);	memcpy(QETH_CM_SETUP_DEST_ADDR(iob->data),	       &card->token.issuer_rm_r, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_CM_SETUP_CONNECTION_TOKEN(iob->data),	       &card->token.cm_connection_w, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_CM_SETUP_FILTER_TOKEN(iob->data),	       &card->token.cm_filter_r, QETH_MPC_TOKEN_LENGTH);	rc = qeth_send_control_data(card, CM_SETUP_SIZE, iob,				    qeth_cm_setup_cb, NULL);	return rc;}static intqeth_ulp_enable_cb(struct qeth_card *card, struct qeth_reply *reply,		   unsigned long data){	__u16 mtu, framesize;	__u16 len;	__u8 link_type;	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup, 2, "ulpenacb");	iob = (struct qeth_cmd_buffer *) data;	memcpy(&card->token.ulp_filter_r,	       QETH_ULP_ENABLE_RESP_FILTER_TOKEN(iob->data),	       QETH_MPC_TOKEN_LENGTH);	if (qeth_get_mtu_out_of_mpc(card->info.type)) {		memcpy(&framesize, QETH_ULP_ENABLE_RESP_MAX_MTU(iob->data), 2);		mtu = qeth_get_mtu_outof_framesize(framesize);		if (!mtu) {			iob->rc = -EINVAL;			QETH_DBF_TEXT_(setup, 2, "  rc%d", iob->rc);			return 0;		}		card->info.max_mtu = mtu;		card->info.initial_mtu = mtu;		card->qdio.in_buf_size = mtu + 2 * PAGE_SIZE;	} else {		card->info.initial_mtu = qeth_get_initial_mtu_for_card(card);		card->info.max_mtu = qeth_get_max_mtu_for_card(card->info.type);		card->qdio.in_buf_size = QETH_IN_BUF_SIZE_DEFAULT;	}	memcpy(&len, QETH_ULP_ENABLE_RESP_DIFINFO_LEN(iob->data), 2);	if (len >= QETH_MPC_DIFINFO_LEN_INDICATES_LINK_TYPE) {		memcpy(&link_type,		       QETH_ULP_ENABLE_RESP_LINK_TYPE(iob->data), 1);		card->info.link_type = link_type;	} else		card->info.link_type = 0;	QETH_DBF_TEXT_(setup, 2, "  rc%d", iob->rc);	return 0;}static intqeth_ulp_enable(struct qeth_card *card){	int rc;	char prot_type;	struct qeth_cmd_buffer *iob;	/*FIXME: trace view callbacks*/	QETH_DBF_TEXT(setup,2,"ulpenabl");	iob = qeth_wait_for_buffer(&card->write);	memcpy(iob->data, ULP_ENABLE, ULP_ENABLE_SIZE);	*(QETH_ULP_ENABLE_LINKNUM(iob->data)) =		(__u8) card->info.portno;	if (card->options.layer2)		if (card->info.type == QETH_CARD_TYPE_OSN)			prot_type = QETH_PROT_OSN2;		else			prot_type = QETH_PROT_LAYER2;	else		prot_type = QETH_PROT_TCPIP;	memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data),&prot_type,1);	memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),	       &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),	       &card->token.ulp_filter_w, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_ULP_ENABLE_PORTNAME_AND_LL(iob->data),	       card->info.portname, 9);	rc = qeth_send_control_data(card, ULP_ENABLE_SIZE, iob,				    qeth_ulp_enable_cb, NULL);	return rc;}static intqeth_ulp_setup_cb(struct qeth_card *card, struct qeth_reply *reply,		  unsigned long data){	struct qeth_cmd_buffer *iob;	QETH_DBF_TEXT(setup, 2, "ulpstpcb");	iob = (struct qeth_cmd_buffer *) data;	memcpy(&card->token.ulp_connection_r,	       QETH_ULP_SETUP_RESP_CONNECTION_TOKEN(iob->data),	       QETH_MPC_TOKEN_LENGTH);	QETH_DBF_TEXT_(setup, 2, "  rc%d", iob->rc);	return 0;}static intqeth_ulp_setup(struct qeth_card *card){	int rc;	__u16 temp;	struct qeth_cmd_buffer *iob;	struct ccw_dev_id dev_id;	QETH_DBF_TEXT(setup,2,"ulpsetup");	iob = qeth_wait_for_buffer(&card->write);	memcpy(iob->data, ULP_SETUP, ULP_SETUP_SIZE);	memcpy(QETH_ULP_SETUP_DEST_ADDR(iob->data),	       &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_ULP_SETUP_CONNECTION_TOKEN(iob->data),	       &card->token.ulp_connection_w, QETH_MPC_TOKEN_LENGTH);	memcpy(QETH_ULP_SETUP_FILTER_TOKEN(iob->data),	       &card->token.ulp_filter_r, QETH_MPC_TOKEN_LENGTH);	ccw_device_get_id(CARD_DDEV(card), &dev_id);	memcpy(QETH_ULP_SETUP_CUA(iob->data), &dev_id.devno, 2);	temp = (card->info.cula << 8) + card->info.unit_addr2;	memcpy(QETH_ULP_SETUP_REAL_DEVADDR(iob->data), &temp, 2);	rc = qeth_send_control_data(card, ULP_SETUP_SIZE, iob,				    qeth_ulp_setup_cb, NULL);	return rc;}static inline intqeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,		       unsigned int siga_error, const char *dbftext){	if (qdio_error || siga_error) {		QETH_DBF_TEXT(trace, 2, dbftext);		QETH_DBF_TEXT(qerr, 2, dbftext);		QETH_DBF_TEXT_(qerr, 2, " F15=%02X",			       buf->element[15].flags & 0xff);		QETH_DBF_TEXT_(qerr, 2, " F14=%02X",			       buf->element[14].flags & 0xff);		QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error);		QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error);		return 1;	}	return 0;}static struct sk_buff *qeth_get_skb(unsigned int length, struct qeth_hdr *hdr){	struct sk_buff* skb;	int add_len;	add_len = 0;	if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN)		add_len = sizeof(struct qeth_hdr);#ifdef CONFIG_QETH_VLAN	else		add_len = VLAN_HLEN;#endif	skb = dev_alloc_skb(length + add_len);	if (skb && add_len)		skb_reserve(skb, add_len);	return skb;}static inline intqeth_create_skb_frag(struct qdio_buffer_element *element,		     struct sk_buff **pskb,		     int offset, int *pfrag, int data_len){	struct page *page = virt_to_page(element->addr);	if (*pfrag == 0) {		/* the upper protocol layers assume that there is data in the		 * skb itself. Copy a small amount (64 bytes) to make them		 * happy. */		*pskb = dev_alloc_skb(64 + QETH_FAKE_LL_LEN_ETH);		if (!(*pskb))			return -ENOMEM;		skb_reserve(*pskb, QETH_FAKE_LL_LEN_ETH);		if (data_len <= 64) {			memcpy(skb_put(*pskb, data_len), element->addr + offset,				data_len);		} else {			get_page(page);			memcpy(skb_put(*pskb, 64), element->addr + offset, 64);			skb_fill_page_desc(*pskb, *pfrag, page, offset + 64,				data_len - 64);			(*pskb)->data_len += data_len - 64;			(*pskb)->len	  += data_len - 64;			(*pskb)->truesize += data_len - 64;		}	} else {		get_page(page);		skb_fill_page_desc(*pskb, *pfrag, page, offset, data_len);		(*pskb)->data_len += data_len;		(*pskb)->len	  += data_len;		(*pskb)->truesize += data_len;	}	(*pfrag)++;	return 0;}static inline struct qeth_buffer_pool_entry *qeth_find_free_buffer_pool_entry(struct qeth_card *card){	struct list_head *plh;	struct qeth_buffer_pool_entry *entry;	int i, free;	struct page *page;	if (list_empty(&card->qdio.in_buf_pool.entry_list))		return NULL;	list_for_each(plh, &card->qdio.in_buf_pool.entry_list) {		entry = list_entry(plh, struct qeth_buffer_pool_entry, list);		free = 1;		for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {			if (page_count(virt_to_page(entry->elements[i])) > 1) {				free = 0;				break;			}		}		if (free) {			list_del_init(&entry->list);			return entry;		}	}	/* no free buffer in pool so take first one and swap pages */	entry = list_entry(card->qdio.in_buf_pool.entry_list.next,			struct qeth_buffer_pool_entry, list);	for (i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i) {		if (page_count(virt_to_page(entry->elements[i])) > 1) {			page = alloc_page(GFP_ATOMIC|GFP_DMA);			if (!page) {				return NULL;			} else {				free_page((unsigned long)entry->elements[i]);				entry->elements[i] = page_address(page);				if (card->options.performance_stats)					card->perf_stats.sg_alloc_page_rx++;			}		}	}	list_del_init(&entry->list);	return entry;}static struct sk_buff *qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,		  struct qdio_buffer_element **__element, int *__offset,		  struct qeth_hdr **hdr){	struct qdio_buffer_element *element = *__element;	int offset = *__offset;	struct sk_buff *skb = NULL;	int skb_len;	void *data_ptr;	int data_len;	int use_rx_sg = 0;	int frag = 0;	QETH_DBF_TEXT(trace,6,"nextskb");	/* qeth_hdr must not cross element boundaries */	if (element->length < offset + sizeof(struct qeth_hdr)){		if (qeth_is_last_sbale(element))			return NULL;		element++;		offset = 0;		if (element->length < sizeof(struct qeth_hdr))			return NULL;	}	*hdr = element->addr + offset;	offset += sizeof(struct qeth_hdr);	if (card->options.layer2)		if (card->info.type == QETH_CARD_TYPE_OSN)			skb_len = (*hdr)->hdr.osn.pdu_length;		else			skb_len = (*hdr)->hdr.l2.pkt_length;	else		skb_len = (*hdr)->hdr.l3.length;	if (!skb_len)		return NULL;	if ((skb_len >= card->options.rx_sg_cb) &&	    (!(card->info.type == QETH_CARD_TYPE_OSN)) &&	    (!atomic_read(&card->force_alloc_skb))) {		use_rx_sg = 1;	} else {		if (card->options.fake_ll) {			if (card->dev->type == ARPHRD_IEEE802_TR) {				if (!(skb = qeth_get_skb(skb_len +						QETH_FAKE_LL_LEN_TR, *hdr)))					goto no_mem;				skb_reserve(skb, QETH_FAKE_LL_LEN_TR);			} else {				if (!(skb = qeth_get_skb(skb_len +						QETH_FAKE_LL_LEN_ETH, *hdr)))					goto no_mem;				skb_reserve(skb, QETH_FAKE_LL_LEN_ETH);			}		} else {			skb = qeth_get_skb(skb_len, *hdr);			if (!skb)				goto no_mem;		}	}	data_ptr = element->addr + offset;	while (skb_len) {		data_len = min(skb_len, (int)(element->length - offset));		if (data_len) {			if (use_rx_sg) {				if (qeth_create_skb_frag(element, &skb, offset,				    &frag, data_len))					goto no_mem;			} else {				memcpy(skb_put(skb, data_len), data_ptr,					data_len);			}		}		skb_len -= data_len;		if (skb_len){			if (qeth_is_last_sbale(element)){				QETH_DBF_TEXT(trace,4,"unexeob");				QETH_DBF_TEXT_(trace,4,"%s",CARD_BUS_ID(card));				QETH_DBF_TEXT(qerr,2,"unexeob");				QETH_DBF_TEXT_(qerr,2,"%s",CARD_BUS_ID(card));				QETH_DBF_HEX(misc,4,buffer,sizeof(*buffer));				dev_kfree_skb_any(skb);				card->stats.rx_errors++;				return NULL;			}			element++;			offset = 0;			data_ptr = element->addr;		} else {			offset += data_len;		}	}	*__element = element;	*__offset = offset;	if (use_rx_sg && card->options.performance_stats) {		card->perf_stats.sg_skbs_rx++;		card->perf_stats.sg_frags_rx += skb_shinfo(skb)->nr_frags;	}	return skb;no_mem:	if (net_ratelimit()){		PRINT_WARN("No memory for packet received on %s.\n",			   QETH_CARD_IFNAME(card));		QETH_DBF_TEXT(trace,2,"noskbmem");		QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));	}	card->stats.rx_dropped++;	return NULL;}static __be16qeth_type_trans(struct sk_buff *skb, struct net_device *dev){	struct qeth_card *card;	struct ethhdr *eth;	QETH_DBF_TEXT(trace,6,"typtrans");	card = (struct qeth_card *)dev->priv;#ifdef CONFIG_TR	if ((card->in

⌨️ 快捷键说明

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