18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IUCV network driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2001, 2009 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author(s): 88c2ecf20Sopenharmony_ci * Original netiucv driver: 98c2ecf20Sopenharmony_ci * Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com) 108c2ecf20Sopenharmony_ci * Sysfs integration and all bugs therein: 118c2ecf20Sopenharmony_ci * Cornelia Huck (cornelia.huck@de.ibm.com) 128c2ecf20Sopenharmony_ci * PM functions: 138c2ecf20Sopenharmony_ci * Ursula Braun (ursula.braun@de.ibm.com) 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Documentation used: 168c2ecf20Sopenharmony_ci * the source of the original IUCV driver by: 178c2ecf20Sopenharmony_ci * Stefan Hegewald <hegewald@de.ibm.com> 188c2ecf20Sopenharmony_ci * Hartmut Penner <hpenner@de.ibm.com> 198c2ecf20Sopenharmony_ci * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) 208c2ecf20Sopenharmony_ci * Martin Schwidefsky (schwidefsky@de.ibm.com) 218c2ecf20Sopenharmony_ci * Alan Altmark (Alan_Altmark@us.ibm.com) Sept. 2000 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "netiucv" 258c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#undef DEBUG 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/init.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/errno.h> 348c2ecf20Sopenharmony_ci#include <linux/types.h> 358c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 368c2ecf20Sopenharmony_ci#include <linux/timer.h> 378c2ecf20Sopenharmony_ci#include <linux/bitops.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <linux/signal.h> 408c2ecf20Sopenharmony_ci#include <linux/string.h> 418c2ecf20Sopenharmony_ci#include <linux/device.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#include <linux/ip.h> 448c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 458c2ecf20Sopenharmony_ci#include <linux/tcp.h> 468c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 478c2ecf20Sopenharmony_ci#include <linux/ctype.h> 488c2ecf20Sopenharmony_ci#include <net/dst.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include <asm/io.h> 518c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 528c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <net/iucv/iucv.h> 558c2ecf20Sopenharmony_ci#include "fsm.h" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciMODULE_AUTHOR 588c2ecf20Sopenharmony_ci ("(C) 2001 IBM Corporation by Fritz Elfert (felfert@millenux.com)"); 598c2ecf20Sopenharmony_ciMODULE_DESCRIPTION ("Linux for S/390 IUCV network driver"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * Debug Facility stuff 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci#define IUCV_DBF_SETUP_NAME "iucv_setup" 658c2ecf20Sopenharmony_ci#define IUCV_DBF_SETUP_LEN 64 668c2ecf20Sopenharmony_ci#define IUCV_DBF_SETUP_PAGES 2 678c2ecf20Sopenharmony_ci#define IUCV_DBF_SETUP_NR_AREAS 1 688c2ecf20Sopenharmony_ci#define IUCV_DBF_SETUP_LEVEL 3 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define IUCV_DBF_DATA_NAME "iucv_data" 718c2ecf20Sopenharmony_ci#define IUCV_DBF_DATA_LEN 128 728c2ecf20Sopenharmony_ci#define IUCV_DBF_DATA_PAGES 2 738c2ecf20Sopenharmony_ci#define IUCV_DBF_DATA_NR_AREAS 1 748c2ecf20Sopenharmony_ci#define IUCV_DBF_DATA_LEVEL 2 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define IUCV_DBF_TRACE_NAME "iucv_trace" 778c2ecf20Sopenharmony_ci#define IUCV_DBF_TRACE_LEN 16 788c2ecf20Sopenharmony_ci#define IUCV_DBF_TRACE_PAGES 4 798c2ecf20Sopenharmony_ci#define IUCV_DBF_TRACE_NR_AREAS 1 808c2ecf20Sopenharmony_ci#define IUCV_DBF_TRACE_LEVEL 3 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define IUCV_DBF_TEXT(name,level,text) \ 838c2ecf20Sopenharmony_ci do { \ 848c2ecf20Sopenharmony_ci debug_text_event(iucv_dbf_##name,level,text); \ 858c2ecf20Sopenharmony_ci } while (0) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define IUCV_DBF_HEX(name,level,addr,len) \ 888c2ecf20Sopenharmony_ci do { \ 898c2ecf20Sopenharmony_ci debug_event(iucv_dbf_##name,level,(void*)(addr),len); \ 908c2ecf20Sopenharmony_ci } while (0) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciDECLARE_PER_CPU(char[256], iucv_dbf_txt_buf); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define IUCV_DBF_TEXT_(name, level, text...) \ 958c2ecf20Sopenharmony_ci do { \ 968c2ecf20Sopenharmony_ci if (debug_level_enabled(iucv_dbf_##name, level)) { \ 978c2ecf20Sopenharmony_ci char* __buf = get_cpu_var(iucv_dbf_txt_buf); \ 988c2ecf20Sopenharmony_ci sprintf(__buf, text); \ 998c2ecf20Sopenharmony_ci debug_text_event(iucv_dbf_##name, level, __buf); \ 1008c2ecf20Sopenharmony_ci put_cpu_var(iucv_dbf_txt_buf); \ 1018c2ecf20Sopenharmony_ci } \ 1028c2ecf20Sopenharmony_ci } while (0) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define IUCV_DBF_SPRINTF(name,level,text...) \ 1058c2ecf20Sopenharmony_ci do { \ 1068c2ecf20Sopenharmony_ci debug_sprintf_event(iucv_dbf_trace, level, ##text ); \ 1078c2ecf20Sopenharmony_ci debug_sprintf_event(iucv_dbf_trace, level, text ); \ 1088c2ecf20Sopenharmony_ci } while (0) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * some more debug stuff 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci#define PRINTK_HEADER " iucv: " /* for debugging */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct device_driver netiucv_driver = { 1168c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1178c2ecf20Sopenharmony_ci .name = "netiucv", 1188c2ecf20Sopenharmony_ci .bus = &iucv_bus, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int netiucv_callback_connreq(struct iucv_path *, u8 *, u8 *); 1228c2ecf20Sopenharmony_cistatic void netiucv_callback_connack(struct iucv_path *, u8 *); 1238c2ecf20Sopenharmony_cistatic void netiucv_callback_connrej(struct iucv_path *, u8 *); 1248c2ecf20Sopenharmony_cistatic void netiucv_callback_connsusp(struct iucv_path *, u8 *); 1258c2ecf20Sopenharmony_cistatic void netiucv_callback_connres(struct iucv_path *, u8 *); 1268c2ecf20Sopenharmony_cistatic void netiucv_callback_rx(struct iucv_path *, struct iucv_message *); 1278c2ecf20Sopenharmony_cistatic void netiucv_callback_txdone(struct iucv_path *, struct iucv_message *); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct iucv_handler netiucv_handler = { 1308c2ecf20Sopenharmony_ci .path_pending = netiucv_callback_connreq, 1318c2ecf20Sopenharmony_ci .path_complete = netiucv_callback_connack, 1328c2ecf20Sopenharmony_ci .path_severed = netiucv_callback_connrej, 1338c2ecf20Sopenharmony_ci .path_quiesced = netiucv_callback_connsusp, 1348c2ecf20Sopenharmony_ci .path_resumed = netiucv_callback_connres, 1358c2ecf20Sopenharmony_ci .message_pending = netiucv_callback_rx, 1368c2ecf20Sopenharmony_ci .message_complete = netiucv_callback_txdone 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/** 1408c2ecf20Sopenharmony_ci * Per connection profiling data 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cistruct connection_profile { 1438c2ecf20Sopenharmony_ci unsigned long maxmulti; 1448c2ecf20Sopenharmony_ci unsigned long maxcqueue; 1458c2ecf20Sopenharmony_ci unsigned long doios_single; 1468c2ecf20Sopenharmony_ci unsigned long doios_multi; 1478c2ecf20Sopenharmony_ci unsigned long txlen; 1488c2ecf20Sopenharmony_ci unsigned long tx_time; 1498c2ecf20Sopenharmony_ci unsigned long send_stamp; 1508c2ecf20Sopenharmony_ci unsigned long tx_pending; 1518c2ecf20Sopenharmony_ci unsigned long tx_max_pending; 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * Representation of one iucv connection 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistruct iucv_connection { 1588c2ecf20Sopenharmony_ci struct list_head list; 1598c2ecf20Sopenharmony_ci struct iucv_path *path; 1608c2ecf20Sopenharmony_ci struct sk_buff *rx_buff; 1618c2ecf20Sopenharmony_ci struct sk_buff *tx_buff; 1628c2ecf20Sopenharmony_ci struct sk_buff_head collect_queue; 1638c2ecf20Sopenharmony_ci struct sk_buff_head commit_queue; 1648c2ecf20Sopenharmony_ci spinlock_t collect_lock; 1658c2ecf20Sopenharmony_ci int collect_len; 1668c2ecf20Sopenharmony_ci int max_buffsize; 1678c2ecf20Sopenharmony_ci fsm_timer timer; 1688c2ecf20Sopenharmony_ci fsm_instance *fsm; 1698c2ecf20Sopenharmony_ci struct net_device *netdev; 1708c2ecf20Sopenharmony_ci struct connection_profile prof; 1718c2ecf20Sopenharmony_ci char userid[9]; 1728c2ecf20Sopenharmony_ci char userdata[17]; 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * Linked list of all connection structs. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic LIST_HEAD(iucv_connection_list); 1798c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(iucv_connection_rwlock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/** 1828c2ecf20Sopenharmony_ci * Representation of event-data for the 1838c2ecf20Sopenharmony_ci * connection state machine. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_cistruct iucv_event { 1868c2ecf20Sopenharmony_ci struct iucv_connection *conn; 1878c2ecf20Sopenharmony_ci void *data; 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/** 1918c2ecf20Sopenharmony_ci * Private part of the network device structure 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_cistruct netiucv_priv { 1948c2ecf20Sopenharmony_ci struct net_device_stats stats; 1958c2ecf20Sopenharmony_ci unsigned long tbusy; 1968c2ecf20Sopenharmony_ci fsm_instance *fsm; 1978c2ecf20Sopenharmony_ci struct iucv_connection *conn; 1988c2ecf20Sopenharmony_ci struct device *dev; 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/** 2028c2ecf20Sopenharmony_ci * Link level header for a packet. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistruct ll_header { 2058c2ecf20Sopenharmony_ci u16 next; 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#define NETIUCV_HDRLEN (sizeof(struct ll_header)) 2098c2ecf20Sopenharmony_ci#define NETIUCV_BUFSIZE_MAX 65537 2108c2ecf20Sopenharmony_ci#define NETIUCV_BUFSIZE_DEFAULT NETIUCV_BUFSIZE_MAX 2118c2ecf20Sopenharmony_ci#define NETIUCV_MTU_MAX (NETIUCV_BUFSIZE_MAX - NETIUCV_HDRLEN) 2128c2ecf20Sopenharmony_ci#define NETIUCV_MTU_DEFAULT 9216 2138c2ecf20Sopenharmony_ci#define NETIUCV_QUEUELEN_DEFAULT 50 2148c2ecf20Sopenharmony_ci#define NETIUCV_TIMEOUT_5SEC 5000 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/** 2178c2ecf20Sopenharmony_ci * Compatibility macros for busy handling 2188c2ecf20Sopenharmony_ci * of network devices. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic void netiucv_clear_busy(struct net_device *dev) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(dev); 2238c2ecf20Sopenharmony_ci clear_bit(0, &priv->tbusy); 2248c2ecf20Sopenharmony_ci netif_wake_queue(dev); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int netiucv_test_and_set_busy(struct net_device *dev) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(dev); 2308c2ecf20Sopenharmony_ci netif_stop_queue(dev); 2318c2ecf20Sopenharmony_ci return test_and_set_bit(0, &priv->tbusy); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic u8 iucvMagic_ascii[16] = { 2358c2ecf20Sopenharmony_ci 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 2368c2ecf20Sopenharmony_ci 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic u8 iucvMagic_ebcdic[16] = { 2408c2ecf20Sopenharmony_ci 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 2418c2ecf20Sopenharmony_ci 0xF0, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/** 2458c2ecf20Sopenharmony_ci * Convert an iucv userId to its printable 2468c2ecf20Sopenharmony_ci * form (strip whitespace at end). 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * @param An iucv userId 2498c2ecf20Sopenharmony_ci * 2508c2ecf20Sopenharmony_ci * @returns The printable string (static data!!) 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic char *netiucv_printname(char *name, int len) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci static char tmp[17]; 2558c2ecf20Sopenharmony_ci char *p = tmp; 2568c2ecf20Sopenharmony_ci memcpy(tmp, name, len); 2578c2ecf20Sopenharmony_ci tmp[len] = '\0'; 2588c2ecf20Sopenharmony_ci while (*p && ((p - tmp) < len) && (!isspace(*p))) 2598c2ecf20Sopenharmony_ci p++; 2608c2ecf20Sopenharmony_ci *p = '\0'; 2618c2ecf20Sopenharmony_ci return tmp; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic char *netiucv_printuser(struct iucv_connection *conn) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci static char tmp_uid[9]; 2678c2ecf20Sopenharmony_ci static char tmp_udat[17]; 2688c2ecf20Sopenharmony_ci static char buf[100]; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (memcmp(conn->userdata, iucvMagic_ebcdic, 16)) { 2718c2ecf20Sopenharmony_ci tmp_uid[8] = '\0'; 2728c2ecf20Sopenharmony_ci tmp_udat[16] = '\0'; 2738c2ecf20Sopenharmony_ci memcpy(tmp_uid, netiucv_printname(conn->userid, 8), 8); 2748c2ecf20Sopenharmony_ci memcpy(tmp_udat, conn->userdata, 16); 2758c2ecf20Sopenharmony_ci EBCASC(tmp_udat, 16); 2768c2ecf20Sopenharmony_ci memcpy(tmp_udat, netiucv_printname(tmp_udat, 16), 16); 2778c2ecf20Sopenharmony_ci sprintf(buf, "%s.%s", tmp_uid, tmp_udat); 2788c2ecf20Sopenharmony_ci return buf; 2798c2ecf20Sopenharmony_ci } else 2808c2ecf20Sopenharmony_ci return netiucv_printname(conn->userid, 8); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/** 2848c2ecf20Sopenharmony_ci * States of the interface statemachine. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cienum dev_states { 2878c2ecf20Sopenharmony_ci DEV_STATE_STOPPED, 2888c2ecf20Sopenharmony_ci DEV_STATE_STARTWAIT, 2898c2ecf20Sopenharmony_ci DEV_STATE_STOPWAIT, 2908c2ecf20Sopenharmony_ci DEV_STATE_RUNNING, 2918c2ecf20Sopenharmony_ci /** 2928c2ecf20Sopenharmony_ci * MUST be always the last element!! 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci NR_DEV_STATES 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic const char *dev_state_names[] = { 2988c2ecf20Sopenharmony_ci "Stopped", 2998c2ecf20Sopenharmony_ci "StartWait", 3008c2ecf20Sopenharmony_ci "StopWait", 3018c2ecf20Sopenharmony_ci "Running", 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/** 3058c2ecf20Sopenharmony_ci * Events of the interface statemachine. 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cienum dev_events { 3088c2ecf20Sopenharmony_ci DEV_EVENT_START, 3098c2ecf20Sopenharmony_ci DEV_EVENT_STOP, 3108c2ecf20Sopenharmony_ci DEV_EVENT_CONUP, 3118c2ecf20Sopenharmony_ci DEV_EVENT_CONDOWN, 3128c2ecf20Sopenharmony_ci /** 3138c2ecf20Sopenharmony_ci * MUST be always the last element!! 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci NR_DEV_EVENTS 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const char *dev_event_names[] = { 3198c2ecf20Sopenharmony_ci "Start", 3208c2ecf20Sopenharmony_ci "Stop", 3218c2ecf20Sopenharmony_ci "Connection up", 3228c2ecf20Sopenharmony_ci "Connection down", 3238c2ecf20Sopenharmony_ci}; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/** 3268c2ecf20Sopenharmony_ci * Events of the connection statemachine 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cienum conn_events { 3298c2ecf20Sopenharmony_ci /** 3308c2ecf20Sopenharmony_ci * Events, representing callbacks from 3318c2ecf20Sopenharmony_ci * lowlevel iucv layer) 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci CONN_EVENT_CONN_REQ, 3348c2ecf20Sopenharmony_ci CONN_EVENT_CONN_ACK, 3358c2ecf20Sopenharmony_ci CONN_EVENT_CONN_REJ, 3368c2ecf20Sopenharmony_ci CONN_EVENT_CONN_SUS, 3378c2ecf20Sopenharmony_ci CONN_EVENT_CONN_RES, 3388c2ecf20Sopenharmony_ci CONN_EVENT_RX, 3398c2ecf20Sopenharmony_ci CONN_EVENT_TXDONE, 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /** 3428c2ecf20Sopenharmony_ci * Events, representing errors return codes from 3438c2ecf20Sopenharmony_ci * calls to lowlevel iucv layer 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /** 3478c2ecf20Sopenharmony_ci * Event, representing timer expiry. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci CONN_EVENT_TIMER, 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /** 3528c2ecf20Sopenharmony_ci * Events, representing commands from upper levels. 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci CONN_EVENT_START, 3558c2ecf20Sopenharmony_ci CONN_EVENT_STOP, 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /** 3588c2ecf20Sopenharmony_ci * MUST be always the last element!! 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci NR_CONN_EVENTS, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic const char *conn_event_names[] = { 3648c2ecf20Sopenharmony_ci "Remote connection request", 3658c2ecf20Sopenharmony_ci "Remote connection acknowledge", 3668c2ecf20Sopenharmony_ci "Remote connection reject", 3678c2ecf20Sopenharmony_ci "Connection suspended", 3688c2ecf20Sopenharmony_ci "Connection resumed", 3698c2ecf20Sopenharmony_ci "Data received", 3708c2ecf20Sopenharmony_ci "Data sent", 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci "Timer", 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci "Start", 3758c2ecf20Sopenharmony_ci "Stop", 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/** 3798c2ecf20Sopenharmony_ci * States of the connection statemachine. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cienum conn_states { 3828c2ecf20Sopenharmony_ci /** 3838c2ecf20Sopenharmony_ci * Connection not assigned to any device, 3848c2ecf20Sopenharmony_ci * initial state, invalid 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci CONN_STATE_INVALID, 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci /** 3898c2ecf20Sopenharmony_ci * Userid assigned but not operating 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci CONN_STATE_STOPPED, 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /** 3948c2ecf20Sopenharmony_ci * Connection registered, 3958c2ecf20Sopenharmony_ci * no connection request sent yet, 3968c2ecf20Sopenharmony_ci * no connection request received 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci CONN_STATE_STARTWAIT, 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /** 4018c2ecf20Sopenharmony_ci * Connection registered and connection request sent, 4028c2ecf20Sopenharmony_ci * no acknowledge and no connection request received yet. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci CONN_STATE_SETUPWAIT, 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /** 4078c2ecf20Sopenharmony_ci * Connection up and running idle 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci CONN_STATE_IDLE, 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /** 4128c2ecf20Sopenharmony_ci * Data sent, awaiting CONN_EVENT_TXDONE 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci CONN_STATE_TX, 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /** 4178c2ecf20Sopenharmony_ci * Error during registration. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci CONN_STATE_REGERR, 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /** 4228c2ecf20Sopenharmony_ci * Error during registration. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci CONN_STATE_CONNERR, 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /** 4278c2ecf20Sopenharmony_ci * MUST be always the last element!! 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci NR_CONN_STATES, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic const char *conn_state_names[] = { 4338c2ecf20Sopenharmony_ci "Invalid", 4348c2ecf20Sopenharmony_ci "Stopped", 4358c2ecf20Sopenharmony_ci "StartWait", 4368c2ecf20Sopenharmony_ci "SetupWait", 4378c2ecf20Sopenharmony_ci "Idle", 4388c2ecf20Sopenharmony_ci "TX", 4398c2ecf20Sopenharmony_ci "Terminating", 4408c2ecf20Sopenharmony_ci "Registration error", 4418c2ecf20Sopenharmony_ci "Connect error", 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/** 4468c2ecf20Sopenharmony_ci * Debug Facility Stuff 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_cistatic debug_info_t *iucv_dbf_setup = NULL; 4498c2ecf20Sopenharmony_cistatic debug_info_t *iucv_dbf_data = NULL; 4508c2ecf20Sopenharmony_cistatic debug_info_t *iucv_dbf_trace = NULL; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciDEFINE_PER_CPU(char[256], iucv_dbf_txt_buf); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void iucv_unregister_dbf_views(void) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci debug_unregister(iucv_dbf_setup); 4578c2ecf20Sopenharmony_ci debug_unregister(iucv_dbf_data); 4588c2ecf20Sopenharmony_ci debug_unregister(iucv_dbf_trace); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_cistatic int iucv_register_dbf_views(void) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci iucv_dbf_setup = debug_register(IUCV_DBF_SETUP_NAME, 4638c2ecf20Sopenharmony_ci IUCV_DBF_SETUP_PAGES, 4648c2ecf20Sopenharmony_ci IUCV_DBF_SETUP_NR_AREAS, 4658c2ecf20Sopenharmony_ci IUCV_DBF_SETUP_LEN); 4668c2ecf20Sopenharmony_ci iucv_dbf_data = debug_register(IUCV_DBF_DATA_NAME, 4678c2ecf20Sopenharmony_ci IUCV_DBF_DATA_PAGES, 4688c2ecf20Sopenharmony_ci IUCV_DBF_DATA_NR_AREAS, 4698c2ecf20Sopenharmony_ci IUCV_DBF_DATA_LEN); 4708c2ecf20Sopenharmony_ci iucv_dbf_trace = debug_register(IUCV_DBF_TRACE_NAME, 4718c2ecf20Sopenharmony_ci IUCV_DBF_TRACE_PAGES, 4728c2ecf20Sopenharmony_ci IUCV_DBF_TRACE_NR_AREAS, 4738c2ecf20Sopenharmony_ci IUCV_DBF_TRACE_LEN); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if ((iucv_dbf_setup == NULL) || (iucv_dbf_data == NULL) || 4768c2ecf20Sopenharmony_ci (iucv_dbf_trace == NULL)) { 4778c2ecf20Sopenharmony_ci iucv_unregister_dbf_views(); 4788c2ecf20Sopenharmony_ci return -ENOMEM; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci debug_register_view(iucv_dbf_setup, &debug_hex_ascii_view); 4818c2ecf20Sopenharmony_ci debug_set_level(iucv_dbf_setup, IUCV_DBF_SETUP_LEVEL); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci debug_register_view(iucv_dbf_data, &debug_hex_ascii_view); 4848c2ecf20Sopenharmony_ci debug_set_level(iucv_dbf_data, IUCV_DBF_DATA_LEVEL); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci debug_register_view(iucv_dbf_trace, &debug_hex_ascii_view); 4878c2ecf20Sopenharmony_ci debug_set_level(iucv_dbf_trace, IUCV_DBF_TRACE_LEVEL); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/* 4938c2ecf20Sopenharmony_ci * Callback-wrappers, called from lowlevel iucv layer. 4948c2ecf20Sopenharmony_ci */ 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic void netiucv_callback_rx(struct iucv_path *path, 4978c2ecf20Sopenharmony_ci struct iucv_message *msg) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5008c2ecf20Sopenharmony_ci struct iucv_event ev; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ev.conn = conn; 5038c2ecf20Sopenharmony_ci ev.data = msg; 5048c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_RX, &ev); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void netiucv_callback_txdone(struct iucv_path *path, 5088c2ecf20Sopenharmony_ci struct iucv_message *msg) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5118c2ecf20Sopenharmony_ci struct iucv_event ev; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ev.conn = conn; 5148c2ecf20Sopenharmony_ci ev.data = msg; 5158c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_TXDONE, &ev); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic void netiucv_callback_connack(struct iucv_path *path, u8 ipuser[16]) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_CONN_ACK, conn); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int netiucv_callback_connreq(struct iucv_path *path, u8 *ipvmid, 5268c2ecf20Sopenharmony_ci u8 *ipuser) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5298c2ecf20Sopenharmony_ci struct iucv_event ev; 5308c2ecf20Sopenharmony_ci static char tmp_user[9]; 5318c2ecf20Sopenharmony_ci static char tmp_udat[17]; 5328c2ecf20Sopenharmony_ci int rc; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci rc = -EINVAL; 5358c2ecf20Sopenharmony_ci memcpy(tmp_user, netiucv_printname(ipvmid, 8), 8); 5368c2ecf20Sopenharmony_ci memcpy(tmp_udat, ipuser, 16); 5378c2ecf20Sopenharmony_ci EBCASC(tmp_udat, 16); 5388c2ecf20Sopenharmony_ci read_lock_bh(&iucv_connection_rwlock); 5398c2ecf20Sopenharmony_ci list_for_each_entry(conn, &iucv_connection_list, list) { 5408c2ecf20Sopenharmony_ci if (strncmp(ipvmid, conn->userid, 8) || 5418c2ecf20Sopenharmony_ci strncmp(ipuser, conn->userdata, 16)) 5428c2ecf20Sopenharmony_ci continue; 5438c2ecf20Sopenharmony_ci /* Found a matching connection for this path. */ 5448c2ecf20Sopenharmony_ci conn->path = path; 5458c2ecf20Sopenharmony_ci ev.conn = conn; 5468c2ecf20Sopenharmony_ci ev.data = path; 5478c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_CONN_REQ, &ev); 5488c2ecf20Sopenharmony_ci rc = 0; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "Connection requested for %s.%s\n", 5518c2ecf20Sopenharmony_ci tmp_user, netiucv_printname(tmp_udat, 16)); 5528c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 5538c2ecf20Sopenharmony_ci return rc; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic void netiucv_callback_connrej(struct iucv_path *path, u8 *ipuser) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_CONN_REJ, conn); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic void netiucv_callback_connsusp(struct iucv_path *path, u8 *ipuser) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_CONN_SUS, conn); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void netiucv_callback_connres(struct iucv_path *path, u8 *ipuser) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct iucv_connection *conn = path->private; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci fsm_event(conn->fsm, CONN_EVENT_CONN_RES, conn); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/** 5788c2ecf20Sopenharmony_ci * NOP action for statemachines 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_cistatic void netiucv_action_nop(fsm_instance *fi, int event, void *arg) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* 5858c2ecf20Sopenharmony_ci * Actions of the connection statemachine 5868c2ecf20Sopenharmony_ci */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * netiucv_unpack_skb 5908c2ecf20Sopenharmony_ci * @conn: The connection where this skb has been received. 5918c2ecf20Sopenharmony_ci * @pskb: The received skb. 5928c2ecf20Sopenharmony_ci * 5938c2ecf20Sopenharmony_ci * Unpack a just received skb and hand it over to upper layers. 5948c2ecf20Sopenharmony_ci * Helper function for conn_action_rx. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_cistatic void netiucv_unpack_skb(struct iucv_connection *conn, 5978c2ecf20Sopenharmony_ci struct sk_buff *pskb) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci struct net_device *dev = conn->netdev; 6008c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 6018c2ecf20Sopenharmony_ci u16 offset = 0; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci skb_put(pskb, NETIUCV_HDRLEN); 6048c2ecf20Sopenharmony_ci pskb->dev = dev; 6058c2ecf20Sopenharmony_ci pskb->ip_summed = CHECKSUM_NONE; 6068c2ecf20Sopenharmony_ci pskb->protocol = cpu_to_be16(ETH_P_IP); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci while (1) { 6098c2ecf20Sopenharmony_ci struct sk_buff *skb; 6108c2ecf20Sopenharmony_ci struct ll_header *header = (struct ll_header *) pskb->data; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!header->next) 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci skb_pull(pskb, NETIUCV_HDRLEN); 6168c2ecf20Sopenharmony_ci header->next -= offset; 6178c2ecf20Sopenharmony_ci offset += header->next; 6188c2ecf20Sopenharmony_ci header->next -= NETIUCV_HDRLEN; 6198c2ecf20Sopenharmony_ci if (skb_tailroom(pskb) < header->next) { 6208c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "Illegal next field: %d > %d\n", 6218c2ecf20Sopenharmony_ci header->next, skb_tailroom(pskb)); 6228c2ecf20Sopenharmony_ci return; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci skb_put(pskb, header->next); 6258c2ecf20Sopenharmony_ci skb_reset_mac_header(pskb); 6268c2ecf20Sopenharmony_ci skb = dev_alloc_skb(pskb->len); 6278c2ecf20Sopenharmony_ci if (!skb) { 6288c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 6298c2ecf20Sopenharmony_ci "Out of memory in netiucv_unpack_skb\n"); 6308c2ecf20Sopenharmony_ci privptr->stats.rx_dropped++; 6318c2ecf20Sopenharmony_ci return; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), 6348c2ecf20Sopenharmony_ci pskb->len); 6358c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 6368c2ecf20Sopenharmony_ci skb->dev = pskb->dev; 6378c2ecf20Sopenharmony_ci skb->protocol = pskb->protocol; 6388c2ecf20Sopenharmony_ci pskb->ip_summed = CHECKSUM_UNNECESSARY; 6398c2ecf20Sopenharmony_ci privptr->stats.rx_packets++; 6408c2ecf20Sopenharmony_ci privptr->stats.rx_bytes += skb->len; 6418c2ecf20Sopenharmony_ci /* 6428c2ecf20Sopenharmony_ci * Since receiving is always initiated from a tasklet (in iucv.c), 6438c2ecf20Sopenharmony_ci * we must use netif_rx_ni() instead of netif_rx() 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_ci netif_rx_ni(skb); 6468c2ecf20Sopenharmony_ci skb_pull(pskb, header->next); 6478c2ecf20Sopenharmony_ci skb_put(pskb, NETIUCV_HDRLEN); 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic void conn_action_rx(fsm_instance *fi, int event, void *arg) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct iucv_event *ev = arg; 6548c2ecf20Sopenharmony_ci struct iucv_connection *conn = ev->conn; 6558c2ecf20Sopenharmony_ci struct iucv_message *msg = ev->data; 6568c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(conn->netdev); 6578c2ecf20Sopenharmony_ci int rc; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (!conn->netdev) { 6628c2ecf20Sopenharmony_ci iucv_message_reject(conn->path, msg); 6638c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 6648c2ecf20Sopenharmony_ci "Received data for unlinked connection\n"); 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci if (msg->length > conn->max_buffsize) { 6688c2ecf20Sopenharmony_ci iucv_message_reject(conn->path, msg); 6698c2ecf20Sopenharmony_ci privptr->stats.rx_dropped++; 6708c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "msglen %d > max_buffsize %d\n", 6718c2ecf20Sopenharmony_ci msg->length, conn->max_buffsize); 6728c2ecf20Sopenharmony_ci return; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci conn->rx_buff->data = conn->rx_buff->head; 6758c2ecf20Sopenharmony_ci skb_reset_tail_pointer(conn->rx_buff); 6768c2ecf20Sopenharmony_ci conn->rx_buff->len = 0; 6778c2ecf20Sopenharmony_ci rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data, 6788c2ecf20Sopenharmony_ci msg->length, NULL); 6798c2ecf20Sopenharmony_ci if (rc || msg->length < 5) { 6808c2ecf20Sopenharmony_ci privptr->stats.rx_errors++; 6818c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_receive\n", rc); 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci netiucv_unpack_skb(conn, conn->rx_buff); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic void conn_action_txdone(fsm_instance *fi, int event, void *arg) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci struct iucv_event *ev = arg; 6908c2ecf20Sopenharmony_ci struct iucv_connection *conn = ev->conn; 6918c2ecf20Sopenharmony_ci struct iucv_message *msg = ev->data; 6928c2ecf20Sopenharmony_ci struct iucv_message txmsg; 6938c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = NULL; 6948c2ecf20Sopenharmony_ci u32 single_flag = msg->tag; 6958c2ecf20Sopenharmony_ci u32 txbytes = 0; 6968c2ecf20Sopenharmony_ci u32 txpackets = 0; 6978c2ecf20Sopenharmony_ci u32 stat_maxcq = 0; 6988c2ecf20Sopenharmony_ci struct sk_buff *skb; 6998c2ecf20Sopenharmony_ci unsigned long saveflags; 7008c2ecf20Sopenharmony_ci struct ll_header header; 7018c2ecf20Sopenharmony_ci int rc; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (!conn || !conn->netdev) { 7068c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 7078c2ecf20Sopenharmony_ci "Send confirmation for unlinked connection\n"); 7088c2ecf20Sopenharmony_ci return; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci privptr = netdev_priv(conn->netdev); 7118c2ecf20Sopenharmony_ci conn->prof.tx_pending--; 7128c2ecf20Sopenharmony_ci if (single_flag) { 7138c2ecf20Sopenharmony_ci if ((skb = skb_dequeue(&conn->commit_queue))) { 7148c2ecf20Sopenharmony_ci refcount_dec(&skb->users); 7158c2ecf20Sopenharmony_ci if (privptr) { 7168c2ecf20Sopenharmony_ci privptr->stats.tx_packets++; 7178c2ecf20Sopenharmony_ci privptr->stats.tx_bytes += 7188c2ecf20Sopenharmony_ci (skb->len - NETIUCV_HDRLEN 7198c2ecf20Sopenharmony_ci - NETIUCV_HDRLEN); 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7228c2ecf20Sopenharmony_ci } 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci conn->tx_buff->data = conn->tx_buff->head; 7258c2ecf20Sopenharmony_ci skb_reset_tail_pointer(conn->tx_buff); 7268c2ecf20Sopenharmony_ci conn->tx_buff->len = 0; 7278c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->collect_lock, saveflags); 7288c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&conn->collect_queue))) { 7298c2ecf20Sopenharmony_ci header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN; 7308c2ecf20Sopenharmony_ci skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN); 7318c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, 7328c2ecf20Sopenharmony_ci skb_put(conn->tx_buff, skb->len), 7338c2ecf20Sopenharmony_ci skb->len); 7348c2ecf20Sopenharmony_ci txbytes += skb->len; 7358c2ecf20Sopenharmony_ci txpackets++; 7368c2ecf20Sopenharmony_ci stat_maxcq++; 7378c2ecf20Sopenharmony_ci refcount_dec(&skb->users); 7388c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci if (conn->collect_len > conn->prof.maxmulti) 7418c2ecf20Sopenharmony_ci conn->prof.maxmulti = conn->collect_len; 7428c2ecf20Sopenharmony_ci conn->collect_len = 0; 7438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->collect_lock, saveflags); 7448c2ecf20Sopenharmony_ci if (conn->tx_buff->len == 0) { 7458c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_IDLE); 7468c2ecf20Sopenharmony_ci return; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci header.next = 0; 7508c2ecf20Sopenharmony_ci skb_put_data(conn->tx_buff, &header, NETIUCV_HDRLEN); 7518c2ecf20Sopenharmony_ci conn->prof.send_stamp = jiffies; 7528c2ecf20Sopenharmony_ci txmsg.class = 0; 7538c2ecf20Sopenharmony_ci txmsg.tag = 0; 7548c2ecf20Sopenharmony_ci rc = iucv_message_send(conn->path, &txmsg, 0, 0, 7558c2ecf20Sopenharmony_ci conn->tx_buff->data, conn->tx_buff->len); 7568c2ecf20Sopenharmony_ci conn->prof.doios_multi++; 7578c2ecf20Sopenharmony_ci conn->prof.txlen += conn->tx_buff->len; 7588c2ecf20Sopenharmony_ci conn->prof.tx_pending++; 7598c2ecf20Sopenharmony_ci if (conn->prof.tx_pending > conn->prof.tx_max_pending) 7608c2ecf20Sopenharmony_ci conn->prof.tx_max_pending = conn->prof.tx_pending; 7618c2ecf20Sopenharmony_ci if (rc) { 7628c2ecf20Sopenharmony_ci conn->prof.tx_pending--; 7638c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_IDLE); 7648c2ecf20Sopenharmony_ci if (privptr) 7658c2ecf20Sopenharmony_ci privptr->stats.tx_errors += txpackets; 7668c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc); 7678c2ecf20Sopenharmony_ci } else { 7688c2ecf20Sopenharmony_ci if (privptr) { 7698c2ecf20Sopenharmony_ci privptr->stats.tx_packets += txpackets; 7708c2ecf20Sopenharmony_ci privptr->stats.tx_bytes += txbytes; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci if (stat_maxcq > conn->prof.maxcqueue) 7738c2ecf20Sopenharmony_ci conn->prof.maxcqueue = stat_maxcq; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic void conn_action_connaccept(fsm_instance *fi, int event, void *arg) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct iucv_event *ev = arg; 7808c2ecf20Sopenharmony_ci struct iucv_connection *conn = ev->conn; 7818c2ecf20Sopenharmony_ci struct iucv_path *path = ev->data; 7828c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 7838c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(netdev); 7848c2ecf20Sopenharmony_ci int rc; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci conn->path = path; 7898c2ecf20Sopenharmony_ci path->msglim = NETIUCV_QUEUELEN_DEFAULT; 7908c2ecf20Sopenharmony_ci path->flags = 0; 7918c2ecf20Sopenharmony_ci rc = iucv_path_accept(path, &netiucv_handler, conn->userdata , conn); 7928c2ecf20Sopenharmony_ci if (rc) { 7938c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "rc %d from iucv_accept", rc); 7948c2ecf20Sopenharmony_ci return; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_IDLE); 7978c2ecf20Sopenharmony_ci netdev->tx_queue_len = conn->path->msglim; 7988c2ecf20Sopenharmony_ci fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic void conn_action_connreject(fsm_instance *fi, int event, void *arg) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct iucv_event *ev = arg; 8048c2ecf20Sopenharmony_ci struct iucv_path *path = ev->data; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 8078c2ecf20Sopenharmony_ci iucv_path_sever(path, NULL); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic void conn_action_connack(fsm_instance *fi, int event, void *arg) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct iucv_connection *conn = arg; 8138c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 8148c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(netdev); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 8178c2ecf20Sopenharmony_ci fsm_deltimer(&conn->timer); 8188c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_IDLE); 8198c2ecf20Sopenharmony_ci netdev->tx_queue_len = conn->path->msglim; 8208c2ecf20Sopenharmony_ci fsm_event(privptr->fsm, DEV_EVENT_CONUP, netdev); 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void conn_action_conntimsev(fsm_instance *fi, int event, void *arg) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct iucv_connection *conn = arg; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 8288c2ecf20Sopenharmony_ci fsm_deltimer(&conn->timer); 8298c2ecf20Sopenharmony_ci iucv_path_sever(conn->path, conn->userdata); 8308c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STARTWAIT); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void conn_action_connsever(fsm_instance *fi, int event, void *arg) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci struct iucv_connection *conn = arg; 8368c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 8378c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(netdev); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci fsm_deltimer(&conn->timer); 8428c2ecf20Sopenharmony_ci iucv_path_sever(conn->path, conn->userdata); 8438c2ecf20Sopenharmony_ci dev_info(privptr->dev, "The peer z/VM guest %s has closed the " 8448c2ecf20Sopenharmony_ci "connection\n", netiucv_printuser(conn)); 8458c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 8468c2ecf20Sopenharmony_ci "conn_action_connsever: Remote dropped connection\n"); 8478c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STARTWAIT); 8488c2ecf20Sopenharmony_ci fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev); 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic void conn_action_start(fsm_instance *fi, int event, void *arg) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct iucv_connection *conn = arg; 8548c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 8558c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(netdev); 8568c2ecf20Sopenharmony_ci int rc; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STARTWAIT); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * We must set the state before calling iucv_connect because the 8648c2ecf20Sopenharmony_ci * callback handler could be called at any point after the connection 8658c2ecf20Sopenharmony_ci * request is sent 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_SETUPWAIT); 8698c2ecf20Sopenharmony_ci conn->path = iucv_path_alloc(NETIUCV_QUEUELEN_DEFAULT, 0, GFP_KERNEL); 8708c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "%s: connecting to %s ...\n", 8718c2ecf20Sopenharmony_ci netdev->name, netiucv_printuser(conn)); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci rc = iucv_path_connect(conn->path, &netiucv_handler, conn->userid, 8748c2ecf20Sopenharmony_ci NULL, conn->userdata, conn); 8758c2ecf20Sopenharmony_ci switch (rc) { 8768c2ecf20Sopenharmony_ci case 0: 8778c2ecf20Sopenharmony_ci netdev->tx_queue_len = conn->path->msglim; 8788c2ecf20Sopenharmony_ci fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC, 8798c2ecf20Sopenharmony_ci CONN_EVENT_TIMER, conn); 8808c2ecf20Sopenharmony_ci return; 8818c2ecf20Sopenharmony_ci case 11: 8828c2ecf20Sopenharmony_ci dev_warn(privptr->dev, 8838c2ecf20Sopenharmony_ci "The IUCV device failed to connect to z/VM guest %s\n", 8848c2ecf20Sopenharmony_ci netiucv_printname(conn->userid, 8)); 8858c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STARTWAIT); 8868c2ecf20Sopenharmony_ci break; 8878c2ecf20Sopenharmony_ci case 12: 8888c2ecf20Sopenharmony_ci dev_warn(privptr->dev, 8898c2ecf20Sopenharmony_ci "The IUCV device failed to connect to the peer on z/VM" 8908c2ecf20Sopenharmony_ci " guest %s\n", netiucv_printname(conn->userid, 8)); 8918c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STARTWAIT); 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case 13: 8948c2ecf20Sopenharmony_ci dev_err(privptr->dev, 8958c2ecf20Sopenharmony_ci "Connecting the IUCV device would exceed the maximum" 8968c2ecf20Sopenharmony_ci " number of IUCV connections\n"); 8978c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_CONNERR); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci case 14: 9008c2ecf20Sopenharmony_ci dev_err(privptr->dev, 9018c2ecf20Sopenharmony_ci "z/VM guest %s has too many IUCV connections" 9028c2ecf20Sopenharmony_ci " to connect with the IUCV device\n", 9038c2ecf20Sopenharmony_ci netiucv_printname(conn->userid, 8)); 9048c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_CONNERR); 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci case 15: 9078c2ecf20Sopenharmony_ci dev_err(privptr->dev, 9088c2ecf20Sopenharmony_ci "The IUCV device cannot connect to a z/VM guest with no" 9098c2ecf20Sopenharmony_ci " IUCV authorization\n"); 9108c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_CONNERR); 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci default: 9138c2ecf20Sopenharmony_ci dev_err(privptr->dev, 9148c2ecf20Sopenharmony_ci "Connecting the IUCV device failed with error %d\n", 9158c2ecf20Sopenharmony_ci rc); 9168c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_CONNERR); 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 5, "iucv_connect rc is %d\n", rc); 9208c2ecf20Sopenharmony_ci kfree(conn->path); 9218c2ecf20Sopenharmony_ci conn->path = NULL; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic void netiucv_purge_skb_queue(struct sk_buff_head *q) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct sk_buff *skb; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(q))) { 9298c2ecf20Sopenharmony_ci refcount_dec(&skb->users); 9308c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic void conn_action_stop(fsm_instance *fi, int event, void *arg) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct iucv_event *ev = arg; 9378c2ecf20Sopenharmony_ci struct iucv_connection *conn = ev->conn; 9388c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 9398c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(netdev); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci fsm_deltimer(&conn->timer); 9448c2ecf20Sopenharmony_ci fsm_newstate(fi, CONN_STATE_STOPPED); 9458c2ecf20Sopenharmony_ci netiucv_purge_skb_queue(&conn->collect_queue); 9468c2ecf20Sopenharmony_ci if (conn->path) { 9478c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, "calling iucv_path_sever\n"); 9488c2ecf20Sopenharmony_ci iucv_path_sever(conn->path, conn->userdata); 9498c2ecf20Sopenharmony_ci kfree(conn->path); 9508c2ecf20Sopenharmony_ci conn->path = NULL; 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci netiucv_purge_skb_queue(&conn->commit_queue); 9538c2ecf20Sopenharmony_ci fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic void conn_action_inval(fsm_instance *fi, int event, void *arg) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct iucv_connection *conn = arg; 9598c2ecf20Sopenharmony_ci struct net_device *netdev = conn->netdev; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "%s('%s'): conn_action_inval called\n", 9628c2ecf20Sopenharmony_ci netdev->name, conn->userid); 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic const fsm_node conn_fsm[] = { 9668c2ecf20Sopenharmony_ci { CONN_STATE_INVALID, CONN_EVENT_START, conn_action_inval }, 9678c2ecf20Sopenharmony_ci { CONN_STATE_STOPPED, CONN_EVENT_START, conn_action_start }, 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci { CONN_STATE_STOPPED, CONN_EVENT_STOP, conn_action_stop }, 9708c2ecf20Sopenharmony_ci { CONN_STATE_STARTWAIT, CONN_EVENT_STOP, conn_action_stop }, 9718c2ecf20Sopenharmony_ci { CONN_STATE_SETUPWAIT, CONN_EVENT_STOP, conn_action_stop }, 9728c2ecf20Sopenharmony_ci { CONN_STATE_IDLE, CONN_EVENT_STOP, conn_action_stop }, 9738c2ecf20Sopenharmony_ci { CONN_STATE_TX, CONN_EVENT_STOP, conn_action_stop }, 9748c2ecf20Sopenharmony_ci { CONN_STATE_REGERR, CONN_EVENT_STOP, conn_action_stop }, 9758c2ecf20Sopenharmony_ci { CONN_STATE_CONNERR, CONN_EVENT_STOP, conn_action_stop }, 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci { CONN_STATE_STOPPED, CONN_EVENT_CONN_REQ, conn_action_connreject }, 9788c2ecf20Sopenharmony_ci { CONN_STATE_STARTWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept }, 9798c2ecf20Sopenharmony_ci { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REQ, conn_action_connaccept }, 9808c2ecf20Sopenharmony_ci { CONN_STATE_IDLE, CONN_EVENT_CONN_REQ, conn_action_connreject }, 9818c2ecf20Sopenharmony_ci { CONN_STATE_TX, CONN_EVENT_CONN_REQ, conn_action_connreject }, 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_ACK, conn_action_connack }, 9848c2ecf20Sopenharmony_ci { CONN_STATE_SETUPWAIT, CONN_EVENT_TIMER, conn_action_conntimsev }, 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci { CONN_STATE_SETUPWAIT, CONN_EVENT_CONN_REJ, conn_action_connsever }, 9878c2ecf20Sopenharmony_ci { CONN_STATE_IDLE, CONN_EVENT_CONN_REJ, conn_action_connsever }, 9888c2ecf20Sopenharmony_ci { CONN_STATE_TX, CONN_EVENT_CONN_REJ, conn_action_connsever }, 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci { CONN_STATE_IDLE, CONN_EVENT_RX, conn_action_rx }, 9918c2ecf20Sopenharmony_ci { CONN_STATE_TX, CONN_EVENT_RX, conn_action_rx }, 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci { CONN_STATE_TX, CONN_EVENT_TXDONE, conn_action_txdone }, 9948c2ecf20Sopenharmony_ci { CONN_STATE_IDLE, CONN_EVENT_TXDONE, conn_action_txdone }, 9958c2ecf20Sopenharmony_ci}; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic const int CONN_FSM_LEN = sizeof(conn_fsm) / sizeof(fsm_node); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci/* 10018c2ecf20Sopenharmony_ci * Actions for interface - statemachine. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci/** 10058c2ecf20Sopenharmony_ci * dev_action_start 10068c2ecf20Sopenharmony_ci * @fi: An instance of an interface statemachine. 10078c2ecf20Sopenharmony_ci * @event: The event, just happened. 10088c2ecf20Sopenharmony_ci * @arg: Generic pointer, casted from struct net_device * upon call. 10098c2ecf20Sopenharmony_ci * 10108c2ecf20Sopenharmony_ci * Startup connection by sending CONN_EVENT_START to it. 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_cistatic void dev_action_start(fsm_instance *fi, int event, void *arg) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct net_device *dev = arg; 10158c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci fsm_newstate(fi, DEV_STATE_STARTWAIT); 10208c2ecf20Sopenharmony_ci fsm_event(privptr->conn->fsm, CONN_EVENT_START, privptr->conn); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci/** 10248c2ecf20Sopenharmony_ci * Shutdown connection by sending CONN_EVENT_STOP to it. 10258c2ecf20Sopenharmony_ci * 10268c2ecf20Sopenharmony_ci * @param fi An instance of an interface statemachine. 10278c2ecf20Sopenharmony_ci * @param event The event, just happened. 10288c2ecf20Sopenharmony_ci * @param arg Generic pointer, casted from struct net_device * upon call. 10298c2ecf20Sopenharmony_ci */ 10308c2ecf20Sopenharmony_cistatic void 10318c2ecf20Sopenharmony_cidev_action_stop(fsm_instance *fi, int event, void *arg) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct net_device *dev = arg; 10348c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 10358c2ecf20Sopenharmony_ci struct iucv_event ev; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci ev.conn = privptr->conn; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci fsm_newstate(fi, DEV_STATE_STOPWAIT); 10428c2ecf20Sopenharmony_ci fsm_event(privptr->conn->fsm, CONN_EVENT_STOP, &ev); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci/** 10468c2ecf20Sopenharmony_ci * Called from connection statemachine 10478c2ecf20Sopenharmony_ci * when a connection is up and running. 10488c2ecf20Sopenharmony_ci * 10498c2ecf20Sopenharmony_ci * @param fi An instance of an interface statemachine. 10508c2ecf20Sopenharmony_ci * @param event The event, just happened. 10518c2ecf20Sopenharmony_ci * @param arg Generic pointer, casted from struct net_device * upon call. 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_cistatic void 10548c2ecf20Sopenharmony_cidev_action_connup(fsm_instance *fi, int event, void *arg) 10558c2ecf20Sopenharmony_ci{ 10568c2ecf20Sopenharmony_ci struct net_device *dev = arg; 10578c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci switch (fsm_getstate(fi)) { 10628c2ecf20Sopenharmony_ci case DEV_STATE_STARTWAIT: 10638c2ecf20Sopenharmony_ci fsm_newstate(fi, DEV_STATE_RUNNING); 10648c2ecf20Sopenharmony_ci dev_info(privptr->dev, 10658c2ecf20Sopenharmony_ci "The IUCV device has been connected" 10668c2ecf20Sopenharmony_ci " successfully to %s\n", 10678c2ecf20Sopenharmony_ci netiucv_printuser(privptr->conn)); 10688c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 3, 10698c2ecf20Sopenharmony_ci "connection is up and running\n"); 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci case DEV_STATE_STOPWAIT: 10728c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 10738c2ecf20Sopenharmony_ci "dev_action_connup: in DEV_STATE_STOPWAIT\n"); 10748c2ecf20Sopenharmony_ci break; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/** 10798c2ecf20Sopenharmony_ci * Called from connection statemachine 10808c2ecf20Sopenharmony_ci * when a connection has been shutdown. 10818c2ecf20Sopenharmony_ci * 10828c2ecf20Sopenharmony_ci * @param fi An instance of an interface statemachine. 10838c2ecf20Sopenharmony_ci * @param event The event, just happened. 10848c2ecf20Sopenharmony_ci * @param arg Generic pointer, casted from struct net_device * upon call. 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_cistatic void 10878c2ecf20Sopenharmony_cidev_action_conndown(fsm_instance *fi, int event, void *arg) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci switch (fsm_getstate(fi)) { 10928c2ecf20Sopenharmony_ci case DEV_STATE_RUNNING: 10938c2ecf20Sopenharmony_ci fsm_newstate(fi, DEV_STATE_STARTWAIT); 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci case DEV_STATE_STOPWAIT: 10968c2ecf20Sopenharmony_ci fsm_newstate(fi, DEV_STATE_STOPPED); 10978c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 3, "connection is down\n"); 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic const fsm_node dev_fsm[] = { 11038c2ecf20Sopenharmony_ci { DEV_STATE_STOPPED, DEV_EVENT_START, dev_action_start }, 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci { DEV_STATE_STOPWAIT, DEV_EVENT_START, dev_action_start }, 11068c2ecf20Sopenharmony_ci { DEV_STATE_STOPWAIT, DEV_EVENT_CONDOWN, dev_action_conndown }, 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci { DEV_STATE_STARTWAIT, DEV_EVENT_STOP, dev_action_stop }, 11098c2ecf20Sopenharmony_ci { DEV_STATE_STARTWAIT, DEV_EVENT_CONUP, dev_action_connup }, 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci { DEV_STATE_RUNNING, DEV_EVENT_STOP, dev_action_stop }, 11128c2ecf20Sopenharmony_ci { DEV_STATE_RUNNING, DEV_EVENT_CONDOWN, dev_action_conndown }, 11138c2ecf20Sopenharmony_ci { DEV_STATE_RUNNING, DEV_EVENT_CONUP, netiucv_action_nop }, 11148c2ecf20Sopenharmony_ci}; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic const int DEV_FSM_LEN = sizeof(dev_fsm) / sizeof(fsm_node); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci/** 11198c2ecf20Sopenharmony_ci * Transmit a packet. 11208c2ecf20Sopenharmony_ci * This is a helper function for netiucv_tx(). 11218c2ecf20Sopenharmony_ci * 11228c2ecf20Sopenharmony_ci * @param conn Connection to be used for sending. 11238c2ecf20Sopenharmony_ci * @param skb Pointer to struct sk_buff of packet to send. 11248c2ecf20Sopenharmony_ci * The linklevel header has already been set up 11258c2ecf20Sopenharmony_ci * by netiucv_tx(). 11268c2ecf20Sopenharmony_ci * 11278c2ecf20Sopenharmony_ci * @return 0 on success, -ERRNO on failure. (Never fails.) 11288c2ecf20Sopenharmony_ci */ 11298c2ecf20Sopenharmony_cistatic int netiucv_transmit_skb(struct iucv_connection *conn, 11308c2ecf20Sopenharmony_ci struct sk_buff *skb) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct iucv_message msg; 11338c2ecf20Sopenharmony_ci unsigned long saveflags; 11348c2ecf20Sopenharmony_ci struct ll_header header; 11358c2ecf20Sopenharmony_ci int rc; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (fsm_getstate(conn->fsm) != CONN_STATE_IDLE) { 11388c2ecf20Sopenharmony_ci int l = skb->len + NETIUCV_HDRLEN; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci spin_lock_irqsave(&conn->collect_lock, saveflags); 11418c2ecf20Sopenharmony_ci if (conn->collect_len + l > 11428c2ecf20Sopenharmony_ci (conn->max_buffsize - NETIUCV_HDRLEN)) { 11438c2ecf20Sopenharmony_ci rc = -EBUSY; 11448c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 11458c2ecf20Sopenharmony_ci "EBUSY from netiucv_transmit_skb\n"); 11468c2ecf20Sopenharmony_ci } else { 11478c2ecf20Sopenharmony_ci refcount_inc(&skb->users); 11488c2ecf20Sopenharmony_ci skb_queue_tail(&conn->collect_queue, skb); 11498c2ecf20Sopenharmony_ci conn->collect_len += l; 11508c2ecf20Sopenharmony_ci rc = 0; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&conn->collect_lock, saveflags); 11538c2ecf20Sopenharmony_ci } else { 11548c2ecf20Sopenharmony_ci struct sk_buff *nskb = skb; 11558c2ecf20Sopenharmony_ci /** 11568c2ecf20Sopenharmony_ci * Copy the skb to a new allocated skb in lowmem only if the 11578c2ecf20Sopenharmony_ci * data is located above 2G in memory or tailroom is < 2. 11588c2ecf20Sopenharmony_ci */ 11598c2ecf20Sopenharmony_ci unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) + 11608c2ecf20Sopenharmony_ci NETIUCV_HDRLEN)) >> 31; 11618c2ecf20Sopenharmony_ci int copied = 0; 11628c2ecf20Sopenharmony_ci if (hi || (skb_tailroom(skb) < 2)) { 11638c2ecf20Sopenharmony_ci nskb = alloc_skb(skb->len + NETIUCV_HDRLEN + 11648c2ecf20Sopenharmony_ci NETIUCV_HDRLEN, GFP_ATOMIC | GFP_DMA); 11658c2ecf20Sopenharmony_ci if (!nskb) { 11668c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, "alloc_skb failed\n"); 11678c2ecf20Sopenharmony_ci rc = -ENOMEM; 11688c2ecf20Sopenharmony_ci return rc; 11698c2ecf20Sopenharmony_ci } else { 11708c2ecf20Sopenharmony_ci skb_reserve(nskb, NETIUCV_HDRLEN); 11718c2ecf20Sopenharmony_ci skb_put_data(nskb, skb->data, skb->len); 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci copied = 1; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci /** 11768c2ecf20Sopenharmony_ci * skb now is below 2G and has enough room. Add headers. 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_ci header.next = nskb->len + NETIUCV_HDRLEN; 11798c2ecf20Sopenharmony_ci memcpy(skb_push(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); 11808c2ecf20Sopenharmony_ci header.next = 0; 11818c2ecf20Sopenharmony_ci skb_put_data(nskb, &header, NETIUCV_HDRLEN); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci fsm_newstate(conn->fsm, CONN_STATE_TX); 11848c2ecf20Sopenharmony_ci conn->prof.send_stamp = jiffies; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci msg.tag = 1; 11878c2ecf20Sopenharmony_ci msg.class = 0; 11888c2ecf20Sopenharmony_ci rc = iucv_message_send(conn->path, &msg, 0, 0, 11898c2ecf20Sopenharmony_ci nskb->data, nskb->len); 11908c2ecf20Sopenharmony_ci conn->prof.doios_single++; 11918c2ecf20Sopenharmony_ci conn->prof.txlen += skb->len; 11928c2ecf20Sopenharmony_ci conn->prof.tx_pending++; 11938c2ecf20Sopenharmony_ci if (conn->prof.tx_pending > conn->prof.tx_max_pending) 11948c2ecf20Sopenharmony_ci conn->prof.tx_max_pending = conn->prof.tx_pending; 11958c2ecf20Sopenharmony_ci if (rc) { 11968c2ecf20Sopenharmony_ci struct netiucv_priv *privptr; 11978c2ecf20Sopenharmony_ci fsm_newstate(conn->fsm, CONN_STATE_IDLE); 11988c2ecf20Sopenharmony_ci conn->prof.tx_pending--; 11998c2ecf20Sopenharmony_ci privptr = netdev_priv(conn->netdev); 12008c2ecf20Sopenharmony_ci if (privptr) 12018c2ecf20Sopenharmony_ci privptr->stats.tx_errors++; 12028c2ecf20Sopenharmony_ci if (copied) 12038c2ecf20Sopenharmony_ci dev_kfree_skb(nskb); 12048c2ecf20Sopenharmony_ci else { 12058c2ecf20Sopenharmony_ci /** 12068c2ecf20Sopenharmony_ci * Remove our headers. They get added 12078c2ecf20Sopenharmony_ci * again on retransmit. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci skb_pull(skb, NETIUCV_HDRLEN); 12108c2ecf20Sopenharmony_ci skb_trim(skb, skb->len - NETIUCV_HDRLEN); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(data, 2, "rc %d from iucv_send\n", rc); 12138c2ecf20Sopenharmony_ci } else { 12148c2ecf20Sopenharmony_ci if (copied) 12158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 12168c2ecf20Sopenharmony_ci refcount_inc(&nskb->users); 12178c2ecf20Sopenharmony_ci skb_queue_tail(&conn->commit_queue, nskb); 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci return rc; 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci/* 12258c2ecf20Sopenharmony_ci * Interface API for upper network layers 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/** 12298c2ecf20Sopenharmony_ci * Open an interface. 12308c2ecf20Sopenharmony_ci * Called from generic network layer when ifconfig up is run. 12318c2ecf20Sopenharmony_ci * 12328c2ecf20Sopenharmony_ci * @param dev Pointer to interface struct. 12338c2ecf20Sopenharmony_ci * 12348c2ecf20Sopenharmony_ci * @return 0 on success, -ERRNO on failure. (Never fails.) 12358c2ecf20Sopenharmony_ci */ 12368c2ecf20Sopenharmony_cistatic int netiucv_open(struct net_device *dev) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(dev); 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci fsm_event(priv->fsm, DEV_EVENT_START, dev); 12418c2ecf20Sopenharmony_ci return 0; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci/** 12458c2ecf20Sopenharmony_ci * Close an interface. 12468c2ecf20Sopenharmony_ci * Called from generic network layer when ifconfig down is run. 12478c2ecf20Sopenharmony_ci * 12488c2ecf20Sopenharmony_ci * @param dev Pointer to interface struct. 12498c2ecf20Sopenharmony_ci * 12508c2ecf20Sopenharmony_ci * @return 0 on success, -ERRNO on failure. (Never fails.) 12518c2ecf20Sopenharmony_ci */ 12528c2ecf20Sopenharmony_cistatic int netiucv_close(struct net_device *dev) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(dev); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci fsm_event(priv->fsm, DEV_EVENT_STOP, dev); 12578c2ecf20Sopenharmony_ci return 0; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci/** 12618c2ecf20Sopenharmony_ci * Start transmission of a packet. 12628c2ecf20Sopenharmony_ci * Called from generic network device layer. 12638c2ecf20Sopenharmony_ci */ 12648c2ecf20Sopenharmony_cistatic netdev_tx_t netiucv_tx(struct sk_buff *skb, struct net_device *dev) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 12678c2ecf20Sopenharmony_ci int rc; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 12708c2ecf20Sopenharmony_ci /** 12718c2ecf20Sopenharmony_ci * Some sanity checks ... 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ci if (skb == NULL) { 12748c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, "netiucv_tx: skb is NULL\n"); 12758c2ecf20Sopenharmony_ci privptr->stats.tx_dropped++; 12768c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci if (skb_headroom(skb) < NETIUCV_HDRLEN) { 12798c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, 12808c2ecf20Sopenharmony_ci "netiucv_tx: skb_headroom < NETIUCV_HDRLEN\n"); 12818c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 12828c2ecf20Sopenharmony_ci privptr->stats.tx_dropped++; 12838c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /** 12878c2ecf20Sopenharmony_ci * If connection is not running, try to restart it 12888c2ecf20Sopenharmony_ci * and throw away packet. 12898c2ecf20Sopenharmony_ci */ 12908c2ecf20Sopenharmony_ci if (fsm_getstate(privptr->fsm) != DEV_STATE_RUNNING) { 12918c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 12928c2ecf20Sopenharmony_ci privptr->stats.tx_dropped++; 12938c2ecf20Sopenharmony_ci privptr->stats.tx_errors++; 12948c2ecf20Sopenharmony_ci privptr->stats.tx_carrier_errors++; 12958c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (netiucv_test_and_set_busy(dev)) { 12998c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, "EBUSY from netiucv_tx\n"); 13008c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci netif_trans_update(dev); 13038c2ecf20Sopenharmony_ci rc = netiucv_transmit_skb(privptr->conn, skb); 13048c2ecf20Sopenharmony_ci netiucv_clear_busy(dev); 13058c2ecf20Sopenharmony_ci return rc ? NETDEV_TX_BUSY : NETDEV_TX_OK; 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci/** 13098c2ecf20Sopenharmony_ci * netiucv_stats 13108c2ecf20Sopenharmony_ci * @dev: Pointer to interface struct. 13118c2ecf20Sopenharmony_ci * 13128c2ecf20Sopenharmony_ci * Returns interface statistics of a device. 13138c2ecf20Sopenharmony_ci * 13148c2ecf20Sopenharmony_ci * Returns pointer to stats struct of this interface. 13158c2ecf20Sopenharmony_ci */ 13168c2ecf20Sopenharmony_cistatic struct net_device_stats *netiucv_stats (struct net_device * dev) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(dev); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 13218c2ecf20Sopenharmony_ci return &priv->stats; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci/* 13258c2ecf20Sopenharmony_ci * attributes in sysfs 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_cistatic ssize_t user_show(struct device *dev, struct device_attribute *attr, 13298c2ecf20Sopenharmony_ci char *buf) 13308c2ecf20Sopenharmony_ci{ 13318c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 13348c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", netiucv_printuser(priv->conn)); 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic int netiucv_check_user(const char *buf, size_t count, char *username, 13388c2ecf20Sopenharmony_ci char *userdata) 13398c2ecf20Sopenharmony_ci{ 13408c2ecf20Sopenharmony_ci const char *p; 13418c2ecf20Sopenharmony_ci int i; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci p = strchr(buf, '.'); 13448c2ecf20Sopenharmony_ci if ((p && ((count > 26) || 13458c2ecf20Sopenharmony_ci ((p - buf) > 8) || 13468c2ecf20Sopenharmony_ci (buf + count - p > 18))) || 13478c2ecf20Sopenharmony_ci (!p && (count > 9))) { 13488c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); 13498c2ecf20Sopenharmony_ci return -EINVAL; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci for (i = 0, p = buf; i < 8 && *p && *p != '.'; i++, p++) { 13538c2ecf20Sopenharmony_ci if (isalnum(*p) || *p == '$') { 13548c2ecf20Sopenharmony_ci username[i] = toupper(*p); 13558c2ecf20Sopenharmony_ci continue; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci if (*p == '\n') 13588c2ecf20Sopenharmony_ci /* trailing lf, grr */ 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, 13618c2ecf20Sopenharmony_ci "conn_write: invalid character %02x\n", *p); 13628c2ecf20Sopenharmony_ci return -EINVAL; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci while (i < 8) 13658c2ecf20Sopenharmony_ci username[i++] = ' '; 13668c2ecf20Sopenharmony_ci username[8] = '\0'; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (*p == '.') { 13698c2ecf20Sopenharmony_ci p++; 13708c2ecf20Sopenharmony_ci for (i = 0; i < 16 && *p; i++, p++) { 13718c2ecf20Sopenharmony_ci if (*p == '\n') 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci userdata[i] = toupper(*p); 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci while (i > 0 && i < 16) 13768c2ecf20Sopenharmony_ci userdata[i++] = ' '; 13778c2ecf20Sopenharmony_ci } else 13788c2ecf20Sopenharmony_ci memcpy(userdata, iucvMagic_ascii, 16); 13798c2ecf20Sopenharmony_ci userdata[16] = '\0'; 13808c2ecf20Sopenharmony_ci ASCEBC(userdata, 16); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci return 0; 13838c2ecf20Sopenharmony_ci} 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cistatic ssize_t user_write(struct device *dev, struct device_attribute *attr, 13868c2ecf20Sopenharmony_ci const char *buf, size_t count) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 13898c2ecf20Sopenharmony_ci struct net_device *ndev = priv->conn->netdev; 13908c2ecf20Sopenharmony_ci char username[9]; 13918c2ecf20Sopenharmony_ci char userdata[17]; 13928c2ecf20Sopenharmony_ci int rc; 13938c2ecf20Sopenharmony_ci struct iucv_connection *cp; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 13968c2ecf20Sopenharmony_ci rc = netiucv_check_user(buf, count, username, userdata); 13978c2ecf20Sopenharmony_ci if (rc) 13988c2ecf20Sopenharmony_ci return rc; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (memcmp(username, priv->conn->userid, 9) && 14018c2ecf20Sopenharmony_ci (ndev->flags & (IFF_UP | IFF_RUNNING))) { 14028c2ecf20Sopenharmony_ci /* username changed while the interface is active. */ 14038c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 2, "user_write: device active\n"); 14048c2ecf20Sopenharmony_ci return -EPERM; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci read_lock_bh(&iucv_connection_rwlock); 14078c2ecf20Sopenharmony_ci list_for_each_entry(cp, &iucv_connection_list, list) { 14088c2ecf20Sopenharmony_ci if (!strncmp(username, cp->userid, 9) && 14098c2ecf20Sopenharmony_ci !strncmp(userdata, cp->userdata, 17) && cp->netdev != ndev) { 14108c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 14118c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "user_write: Connection to %s " 14128c2ecf20Sopenharmony_ci "already exists\n", netiucv_printuser(cp)); 14138c2ecf20Sopenharmony_ci return -EEXIST; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 14178c2ecf20Sopenharmony_ci memcpy(priv->conn->userid, username, 9); 14188c2ecf20Sopenharmony_ci memcpy(priv->conn->userdata, userdata, 17); 14198c2ecf20Sopenharmony_ci return count; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(user, 0644, user_show, user_write); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cistatic ssize_t buffer_show (struct device *dev, struct device_attribute *attr, 14258c2ecf20Sopenharmony_ci char *buf) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 14308c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", priv->conn->max_buffsize); 14318c2ecf20Sopenharmony_ci} 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic ssize_t buffer_write (struct device *dev, struct device_attribute *attr, 14348c2ecf20Sopenharmony_ci const char *buf, size_t count) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 14378c2ecf20Sopenharmony_ci struct net_device *ndev = priv->conn->netdev; 14388c2ecf20Sopenharmony_ci unsigned int bs1; 14398c2ecf20Sopenharmony_ci int rc; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 14428c2ecf20Sopenharmony_ci if (count >= 39) 14438c2ecf20Sopenharmony_ci return -EINVAL; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci rc = kstrtouint(buf, 0, &bs1); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (rc == -EINVAL) { 14488c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "buffer_write: invalid char %s\n", 14498c2ecf20Sopenharmony_ci buf); 14508c2ecf20Sopenharmony_ci return -EINVAL; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci if ((rc == -ERANGE) || (bs1 > NETIUCV_BUFSIZE_MAX)) { 14538c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, 14548c2ecf20Sopenharmony_ci "buffer_write: buffer size %d too large\n", 14558c2ecf20Sopenharmony_ci bs1); 14568c2ecf20Sopenharmony_ci return -EINVAL; 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci if ((ndev->flags & IFF_RUNNING) && 14598c2ecf20Sopenharmony_ci (bs1 < (ndev->mtu + NETIUCV_HDRLEN + 2))) { 14608c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, 14618c2ecf20Sopenharmony_ci "buffer_write: buffer size %d too small\n", 14628c2ecf20Sopenharmony_ci bs1); 14638c2ecf20Sopenharmony_ci return -EINVAL; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci if (bs1 < (576 + NETIUCV_HDRLEN + NETIUCV_HDRLEN)) { 14668c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, 14678c2ecf20Sopenharmony_ci "buffer_write: buffer size %d too small\n", 14688c2ecf20Sopenharmony_ci bs1); 14698c2ecf20Sopenharmony_ci return -EINVAL; 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci priv->conn->max_buffsize = bs1; 14738c2ecf20Sopenharmony_ci if (!(ndev->flags & IFF_RUNNING)) 14748c2ecf20Sopenharmony_ci ndev->mtu = bs1 - NETIUCV_HDRLEN - NETIUCV_HDRLEN; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci return count; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic ssize_t dev_fsm_show (struct device *dev, struct device_attribute *attr, 14838c2ecf20Sopenharmony_ci char *buf) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 14888c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", fsm_getstate_str(priv->fsm)); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic DEVICE_ATTR(device_fsm_state, 0444, dev_fsm_show, NULL); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_cistatic ssize_t conn_fsm_show (struct device *dev, 14948c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 14958c2ecf20Sopenharmony_ci{ 14968c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 14998c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", fsm_getstate_str(priv->conn->fsm)); 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_cistatic DEVICE_ATTR(connection_fsm_state, 0444, conn_fsm_show, NULL); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic ssize_t maxmulti_show (struct device *dev, 15058c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15108c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.maxmulti); 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_cistatic ssize_t maxmulti_write (struct device *dev, 15148c2ecf20Sopenharmony_ci struct device_attribute *attr, 15158c2ecf20Sopenharmony_ci const char *buf, size_t count) 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 15208c2ecf20Sopenharmony_ci priv->conn->prof.maxmulti = 0; 15218c2ecf20Sopenharmony_ci return count; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic DEVICE_ATTR(max_tx_buffer_used, 0644, maxmulti_show, maxmulti_write); 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_cistatic ssize_t maxcq_show (struct device *dev, struct device_attribute *attr, 15278c2ecf20Sopenharmony_ci char *buf) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15328c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.maxcqueue); 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_cistatic ssize_t maxcq_write (struct device *dev, struct device_attribute *attr, 15368c2ecf20Sopenharmony_ci const char *buf, size_t count) 15378c2ecf20Sopenharmony_ci{ 15388c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 15418c2ecf20Sopenharmony_ci priv->conn->prof.maxcqueue = 0; 15428c2ecf20Sopenharmony_ci return count; 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cistatic DEVICE_ATTR(max_chained_skbs, 0644, maxcq_show, maxcq_write); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic ssize_t sdoio_show (struct device *dev, struct device_attribute *attr, 15488c2ecf20Sopenharmony_ci char *buf) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15538c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.doios_single); 15548c2ecf20Sopenharmony_ci} 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_cistatic ssize_t sdoio_write (struct device *dev, struct device_attribute *attr, 15578c2ecf20Sopenharmony_ci const char *buf, size_t count) 15588c2ecf20Sopenharmony_ci{ 15598c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 15628c2ecf20Sopenharmony_ci priv->conn->prof.doios_single = 0; 15638c2ecf20Sopenharmony_ci return count; 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_single_write_ops, 0644, sdoio_show, sdoio_write); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_cistatic ssize_t mdoio_show (struct device *dev, struct device_attribute *attr, 15698c2ecf20Sopenharmony_ci char *buf) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15748c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.doios_multi); 15758c2ecf20Sopenharmony_ci} 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_cistatic ssize_t mdoio_write (struct device *dev, struct device_attribute *attr, 15788c2ecf20Sopenharmony_ci const char *buf, size_t count) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15838c2ecf20Sopenharmony_ci priv->conn->prof.doios_multi = 0; 15848c2ecf20Sopenharmony_ci return count; 15858c2ecf20Sopenharmony_ci} 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_multi_write_ops, 0644, mdoio_show, mdoio_write); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic ssize_t txlen_show (struct device *dev, struct device_attribute *attr, 15908c2ecf20Sopenharmony_ci char *buf) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 15958c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.txlen); 15968c2ecf20Sopenharmony_ci} 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_cistatic ssize_t txlen_write (struct device *dev, struct device_attribute *attr, 15998c2ecf20Sopenharmony_ci const char *buf, size_t count) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 16048c2ecf20Sopenharmony_ci priv->conn->prof.txlen = 0; 16058c2ecf20Sopenharmony_ci return count; 16068c2ecf20Sopenharmony_ci} 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic DEVICE_ATTR(netto_bytes, 0644, txlen_show, txlen_write); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_cistatic ssize_t txtime_show (struct device *dev, struct device_attribute *attr, 16118c2ecf20Sopenharmony_ci char *buf) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 16168c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.tx_time); 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic ssize_t txtime_write (struct device *dev, struct device_attribute *attr, 16208c2ecf20Sopenharmony_ci const char *buf, size_t count) 16218c2ecf20Sopenharmony_ci{ 16228c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 16258c2ecf20Sopenharmony_ci priv->conn->prof.tx_time = 0; 16268c2ecf20Sopenharmony_ci return count; 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_cistatic DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_cistatic ssize_t txpend_show (struct device *dev, struct device_attribute *attr, 16328c2ecf20Sopenharmony_ci char *buf) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 16378c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending); 16388c2ecf20Sopenharmony_ci} 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_cistatic ssize_t txpend_write (struct device *dev, struct device_attribute *attr, 16418c2ecf20Sopenharmony_ci const char *buf, size_t count) 16428c2ecf20Sopenharmony_ci{ 16438c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 16468c2ecf20Sopenharmony_ci priv->conn->prof.tx_pending = 0; 16478c2ecf20Sopenharmony_ci return count; 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic ssize_t txmpnd_show (struct device *dev, struct device_attribute *attr, 16538c2ecf20Sopenharmony_ci char *buf) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 5, __func__); 16588c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending); 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic ssize_t txmpnd_write (struct device *dev, struct device_attribute *attr, 16628c2ecf20Sopenharmony_ci const char *buf, size_t count) 16638c2ecf20Sopenharmony_ci{ 16648c2ecf20Sopenharmony_ci struct netiucv_priv *priv = dev_get_drvdata(dev); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 4, __func__); 16678c2ecf20Sopenharmony_ci priv->conn->prof.tx_max_pending = 0; 16688c2ecf20Sopenharmony_ci return count; 16698c2ecf20Sopenharmony_ci} 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_cistatic DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic struct attribute *netiucv_attrs[] = { 16748c2ecf20Sopenharmony_ci &dev_attr_buffer.attr, 16758c2ecf20Sopenharmony_ci &dev_attr_user.attr, 16768c2ecf20Sopenharmony_ci NULL, 16778c2ecf20Sopenharmony_ci}; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic struct attribute_group netiucv_attr_group = { 16808c2ecf20Sopenharmony_ci .attrs = netiucv_attrs, 16818c2ecf20Sopenharmony_ci}; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic struct attribute *netiucv_stat_attrs[] = { 16848c2ecf20Sopenharmony_ci &dev_attr_device_fsm_state.attr, 16858c2ecf20Sopenharmony_ci &dev_attr_connection_fsm_state.attr, 16868c2ecf20Sopenharmony_ci &dev_attr_max_tx_buffer_used.attr, 16878c2ecf20Sopenharmony_ci &dev_attr_max_chained_skbs.attr, 16888c2ecf20Sopenharmony_ci &dev_attr_tx_single_write_ops.attr, 16898c2ecf20Sopenharmony_ci &dev_attr_tx_multi_write_ops.attr, 16908c2ecf20Sopenharmony_ci &dev_attr_netto_bytes.attr, 16918c2ecf20Sopenharmony_ci &dev_attr_max_tx_io_time.attr, 16928c2ecf20Sopenharmony_ci &dev_attr_tx_pending.attr, 16938c2ecf20Sopenharmony_ci &dev_attr_tx_max_pending.attr, 16948c2ecf20Sopenharmony_ci NULL, 16958c2ecf20Sopenharmony_ci}; 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cistatic struct attribute_group netiucv_stat_attr_group = { 16988c2ecf20Sopenharmony_ci .name = "stats", 16998c2ecf20Sopenharmony_ci .attrs = netiucv_stat_attrs, 17008c2ecf20Sopenharmony_ci}; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic const struct attribute_group *netiucv_attr_groups[] = { 17038c2ecf20Sopenharmony_ci &netiucv_stat_attr_group, 17048c2ecf20Sopenharmony_ci &netiucv_attr_group, 17058c2ecf20Sopenharmony_ci NULL, 17068c2ecf20Sopenharmony_ci}; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_cistatic int netiucv_register_device(struct net_device *ndev) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci struct netiucv_priv *priv = netdev_priv(ndev); 17118c2ecf20Sopenharmony_ci struct device *dev = kzalloc(sizeof(struct device), GFP_KERNEL); 17128c2ecf20Sopenharmony_ci int ret; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (dev) { 17178c2ecf20Sopenharmony_ci dev_set_name(dev, "net%s", ndev->name); 17188c2ecf20Sopenharmony_ci dev->bus = &iucv_bus; 17198c2ecf20Sopenharmony_ci dev->parent = iucv_root; 17208c2ecf20Sopenharmony_ci dev->groups = netiucv_attr_groups; 17218c2ecf20Sopenharmony_ci /* 17228c2ecf20Sopenharmony_ci * The release function could be called after the 17238c2ecf20Sopenharmony_ci * module has been unloaded. It's _only_ task is to 17248c2ecf20Sopenharmony_ci * free the struct. Therefore, we specify kfree() 17258c2ecf20Sopenharmony_ci * directly here. (Probably a little bit obfuscating 17268c2ecf20Sopenharmony_ci * but legitime ...). 17278c2ecf20Sopenharmony_ci */ 17288c2ecf20Sopenharmony_ci dev->release = (void (*)(struct device *))kfree; 17298c2ecf20Sopenharmony_ci dev->driver = &netiucv_driver; 17308c2ecf20Sopenharmony_ci } else 17318c2ecf20Sopenharmony_ci return -ENOMEM; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci ret = device_register(dev); 17348c2ecf20Sopenharmony_ci if (ret) { 17358c2ecf20Sopenharmony_ci put_device(dev); 17368c2ecf20Sopenharmony_ci return ret; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci priv->dev = dev; 17398c2ecf20Sopenharmony_ci dev_set_drvdata(dev, priv); 17408c2ecf20Sopenharmony_ci return 0; 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic void netiucv_unregister_device(struct device *dev) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 17468c2ecf20Sopenharmony_ci device_unregister(dev); 17478c2ecf20Sopenharmony_ci} 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci/** 17508c2ecf20Sopenharmony_ci * Allocate and initialize a new connection structure. 17518c2ecf20Sopenharmony_ci * Add it to the list of netiucv connections; 17528c2ecf20Sopenharmony_ci */ 17538c2ecf20Sopenharmony_cistatic struct iucv_connection *netiucv_new_connection(struct net_device *dev, 17548c2ecf20Sopenharmony_ci char *username, 17558c2ecf20Sopenharmony_ci char *userdata) 17568c2ecf20Sopenharmony_ci{ 17578c2ecf20Sopenharmony_ci struct iucv_connection *conn; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci conn = kzalloc(sizeof(*conn), GFP_KERNEL); 17608c2ecf20Sopenharmony_ci if (!conn) 17618c2ecf20Sopenharmony_ci goto out; 17628c2ecf20Sopenharmony_ci skb_queue_head_init(&conn->collect_queue); 17638c2ecf20Sopenharmony_ci skb_queue_head_init(&conn->commit_queue); 17648c2ecf20Sopenharmony_ci spin_lock_init(&conn->collect_lock); 17658c2ecf20Sopenharmony_ci conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; 17668c2ecf20Sopenharmony_ci conn->netdev = dev; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci conn->rx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA); 17698c2ecf20Sopenharmony_ci if (!conn->rx_buff) 17708c2ecf20Sopenharmony_ci goto out_conn; 17718c2ecf20Sopenharmony_ci conn->tx_buff = alloc_skb(conn->max_buffsize, GFP_KERNEL | GFP_DMA); 17728c2ecf20Sopenharmony_ci if (!conn->tx_buff) 17738c2ecf20Sopenharmony_ci goto out_rx; 17748c2ecf20Sopenharmony_ci conn->fsm = init_fsm("netiucvconn", conn_state_names, 17758c2ecf20Sopenharmony_ci conn_event_names, NR_CONN_STATES, 17768c2ecf20Sopenharmony_ci NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN, 17778c2ecf20Sopenharmony_ci GFP_KERNEL); 17788c2ecf20Sopenharmony_ci if (!conn->fsm) 17798c2ecf20Sopenharmony_ci goto out_tx; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci fsm_settimer(conn->fsm, &conn->timer); 17828c2ecf20Sopenharmony_ci fsm_newstate(conn->fsm, CONN_STATE_INVALID); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci if (userdata) 17858c2ecf20Sopenharmony_ci memcpy(conn->userdata, userdata, 17); 17868c2ecf20Sopenharmony_ci if (username) { 17878c2ecf20Sopenharmony_ci memcpy(conn->userid, username, 9); 17888c2ecf20Sopenharmony_ci fsm_newstate(conn->fsm, CONN_STATE_STOPPED); 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci write_lock_bh(&iucv_connection_rwlock); 17928c2ecf20Sopenharmony_ci list_add_tail(&conn->list, &iucv_connection_list); 17938c2ecf20Sopenharmony_ci write_unlock_bh(&iucv_connection_rwlock); 17948c2ecf20Sopenharmony_ci return conn; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ciout_tx: 17978c2ecf20Sopenharmony_ci kfree_skb(conn->tx_buff); 17988c2ecf20Sopenharmony_ciout_rx: 17998c2ecf20Sopenharmony_ci kfree_skb(conn->rx_buff); 18008c2ecf20Sopenharmony_ciout_conn: 18018c2ecf20Sopenharmony_ci kfree(conn); 18028c2ecf20Sopenharmony_ciout: 18038c2ecf20Sopenharmony_ci return NULL; 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci/** 18078c2ecf20Sopenharmony_ci * Release a connection structure and remove it from the 18088c2ecf20Sopenharmony_ci * list of netiucv connections. 18098c2ecf20Sopenharmony_ci */ 18108c2ecf20Sopenharmony_cistatic void netiucv_remove_connection(struct iucv_connection *conn) 18118c2ecf20Sopenharmony_ci{ 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 18148c2ecf20Sopenharmony_ci write_lock_bh(&iucv_connection_rwlock); 18158c2ecf20Sopenharmony_ci list_del_init(&conn->list); 18168c2ecf20Sopenharmony_ci write_unlock_bh(&iucv_connection_rwlock); 18178c2ecf20Sopenharmony_ci fsm_deltimer(&conn->timer); 18188c2ecf20Sopenharmony_ci netiucv_purge_skb_queue(&conn->collect_queue); 18198c2ecf20Sopenharmony_ci if (conn->path) { 18208c2ecf20Sopenharmony_ci iucv_path_sever(conn->path, conn->userdata); 18218c2ecf20Sopenharmony_ci kfree(conn->path); 18228c2ecf20Sopenharmony_ci conn->path = NULL; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci netiucv_purge_skb_queue(&conn->commit_queue); 18258c2ecf20Sopenharmony_ci kfree_fsm(conn->fsm); 18268c2ecf20Sopenharmony_ci kfree_skb(conn->rx_buff); 18278c2ecf20Sopenharmony_ci kfree_skb(conn->tx_buff); 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci/** 18318c2ecf20Sopenharmony_ci * Release everything of a net device. 18328c2ecf20Sopenharmony_ci */ 18338c2ecf20Sopenharmony_cistatic void netiucv_free_netdevice(struct net_device *dev) 18348c2ecf20Sopenharmony_ci{ 18358c2ecf20Sopenharmony_ci struct netiucv_priv *privptr = netdev_priv(dev); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (!dev) 18408c2ecf20Sopenharmony_ci return; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (privptr) { 18438c2ecf20Sopenharmony_ci if (privptr->conn) 18448c2ecf20Sopenharmony_ci netiucv_remove_connection(privptr->conn); 18458c2ecf20Sopenharmony_ci if (privptr->fsm) 18468c2ecf20Sopenharmony_ci kfree_fsm(privptr->fsm); 18478c2ecf20Sopenharmony_ci privptr->conn = NULL; privptr->fsm = NULL; 18488c2ecf20Sopenharmony_ci /* privptr gets freed by free_netdev() */ 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci} 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci/** 18538c2ecf20Sopenharmony_ci * Initialize a net device. (Called from kernel in alloc_netdev()) 18548c2ecf20Sopenharmony_ci */ 18558c2ecf20Sopenharmony_cistatic const struct net_device_ops netiucv_netdev_ops = { 18568c2ecf20Sopenharmony_ci .ndo_open = netiucv_open, 18578c2ecf20Sopenharmony_ci .ndo_stop = netiucv_close, 18588c2ecf20Sopenharmony_ci .ndo_get_stats = netiucv_stats, 18598c2ecf20Sopenharmony_ci .ndo_start_xmit = netiucv_tx, 18608c2ecf20Sopenharmony_ci}; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic void netiucv_setup_netdevice(struct net_device *dev) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci dev->mtu = NETIUCV_MTU_DEFAULT; 18658c2ecf20Sopenharmony_ci dev->min_mtu = 576; 18668c2ecf20Sopenharmony_ci dev->max_mtu = NETIUCV_MTU_MAX; 18678c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 18688c2ecf20Sopenharmony_ci dev->priv_destructor = netiucv_free_netdevice; 18698c2ecf20Sopenharmony_ci dev->hard_header_len = NETIUCV_HDRLEN; 18708c2ecf20Sopenharmony_ci dev->addr_len = 0; 18718c2ecf20Sopenharmony_ci dev->type = ARPHRD_SLIP; 18728c2ecf20Sopenharmony_ci dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT; 18738c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 18748c2ecf20Sopenharmony_ci dev->netdev_ops = &netiucv_netdev_ops; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci/** 18788c2ecf20Sopenharmony_ci * Allocate and initialize everything of a net device. 18798c2ecf20Sopenharmony_ci */ 18808c2ecf20Sopenharmony_cistatic struct net_device *netiucv_init_netdevice(char *username, char *userdata) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct netiucv_priv *privptr; 18838c2ecf20Sopenharmony_ci struct net_device *dev; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d", 18868c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, netiucv_setup_netdevice); 18878c2ecf20Sopenharmony_ci if (!dev) 18888c2ecf20Sopenharmony_ci return NULL; 18898c2ecf20Sopenharmony_ci rtnl_lock(); 18908c2ecf20Sopenharmony_ci if (dev_alloc_name(dev, dev->name) < 0) 18918c2ecf20Sopenharmony_ci goto out_netdev; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci privptr = netdev_priv(dev); 18948c2ecf20Sopenharmony_ci privptr->fsm = init_fsm("netiucvdev", dev_state_names, 18958c2ecf20Sopenharmony_ci dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS, 18968c2ecf20Sopenharmony_ci dev_fsm, DEV_FSM_LEN, GFP_KERNEL); 18978c2ecf20Sopenharmony_ci if (!privptr->fsm) 18988c2ecf20Sopenharmony_ci goto out_netdev; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci privptr->conn = netiucv_new_connection(dev, username, userdata); 19018c2ecf20Sopenharmony_ci if (!privptr->conn) { 19028c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n"); 19038c2ecf20Sopenharmony_ci goto out_fsm; 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); 19068c2ecf20Sopenharmony_ci return dev; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ciout_fsm: 19098c2ecf20Sopenharmony_ci kfree_fsm(privptr->fsm); 19108c2ecf20Sopenharmony_ciout_netdev: 19118c2ecf20Sopenharmony_ci rtnl_unlock(); 19128c2ecf20Sopenharmony_ci free_netdev(dev); 19138c2ecf20Sopenharmony_ci return NULL; 19148c2ecf20Sopenharmony_ci} 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_cistatic ssize_t connection_store(struct device_driver *drv, const char *buf, 19178c2ecf20Sopenharmony_ci size_t count) 19188c2ecf20Sopenharmony_ci{ 19198c2ecf20Sopenharmony_ci char username[9]; 19208c2ecf20Sopenharmony_ci char userdata[17]; 19218c2ecf20Sopenharmony_ci int rc; 19228c2ecf20Sopenharmony_ci struct net_device *dev; 19238c2ecf20Sopenharmony_ci struct netiucv_priv *priv; 19248c2ecf20Sopenharmony_ci struct iucv_connection *cp; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 19278c2ecf20Sopenharmony_ci rc = netiucv_check_user(buf, count, username, userdata); 19288c2ecf20Sopenharmony_ci if (rc) 19298c2ecf20Sopenharmony_ci return rc; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci read_lock_bh(&iucv_connection_rwlock); 19328c2ecf20Sopenharmony_ci list_for_each_entry(cp, &iucv_connection_list, list) { 19338c2ecf20Sopenharmony_ci if (!strncmp(username, cp->userid, 9) && 19348c2ecf20Sopenharmony_ci !strncmp(userdata, cp->userdata, 17)) { 19358c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 19368c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "conn_write: Connection to %s " 19378c2ecf20Sopenharmony_ci "already exists\n", netiucv_printuser(cp)); 19388c2ecf20Sopenharmony_ci return -EEXIST; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci dev = netiucv_init_netdevice(username, userdata); 19448c2ecf20Sopenharmony_ci if (!dev) { 19458c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); 19468c2ecf20Sopenharmony_ci return -ENODEV; 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci rc = netiucv_register_device(dev); 19508c2ecf20Sopenharmony_ci if (rc) { 19518c2ecf20Sopenharmony_ci rtnl_unlock(); 19528c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, 19538c2ecf20Sopenharmony_ci "ret %d from netiucv_register_device\n", rc); 19548c2ecf20Sopenharmony_ci goto out_free_ndev; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* sysfs magic */ 19588c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 19598c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, priv->dev); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci rc = register_netdevice(dev); 19628c2ecf20Sopenharmony_ci rtnl_unlock(); 19638c2ecf20Sopenharmony_ci if (rc) 19648c2ecf20Sopenharmony_ci goto out_unreg; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci dev_info(priv->dev, "The IUCV interface to %s has been established " 19678c2ecf20Sopenharmony_ci "successfully\n", 19688c2ecf20Sopenharmony_ci netiucv_printuser(priv->conn)); 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci return count; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ciout_unreg: 19738c2ecf20Sopenharmony_ci netiucv_unregister_device(priv->dev); 19748c2ecf20Sopenharmony_ciout_free_ndev: 19758c2ecf20Sopenharmony_ci netiucv_free_netdevice(dev); 19768c2ecf20Sopenharmony_ci return rc; 19778c2ecf20Sopenharmony_ci} 19788c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(connection); 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_cistatic ssize_t remove_store(struct device_driver *drv, const char *buf, 19818c2ecf20Sopenharmony_ci size_t count) 19828c2ecf20Sopenharmony_ci{ 19838c2ecf20Sopenharmony_ci struct iucv_connection *cp; 19848c2ecf20Sopenharmony_ci struct net_device *ndev; 19858c2ecf20Sopenharmony_ci struct netiucv_priv *priv; 19868c2ecf20Sopenharmony_ci struct device *dev; 19878c2ecf20Sopenharmony_ci char name[IFNAMSIZ]; 19888c2ecf20Sopenharmony_ci const char *p; 19898c2ecf20Sopenharmony_ci int i; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci if (count >= IFNAMSIZ) 19948c2ecf20Sopenharmony_ci count = IFNAMSIZ - 1; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci for (i = 0, p = buf; i < count && *p; i++, p++) { 19978c2ecf20Sopenharmony_ci if (*p == '\n' || *p == ' ') 19988c2ecf20Sopenharmony_ci /* trailing lf, grr */ 19998c2ecf20Sopenharmony_ci break; 20008c2ecf20Sopenharmony_ci name[i] = *p; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci name[i] = '\0'; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci read_lock_bh(&iucv_connection_rwlock); 20058c2ecf20Sopenharmony_ci list_for_each_entry(cp, &iucv_connection_list, list) { 20068c2ecf20Sopenharmony_ci ndev = cp->netdev; 20078c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 20088c2ecf20Sopenharmony_ci dev = priv->dev; 20098c2ecf20Sopenharmony_ci if (strncmp(name, ndev->name, count)) 20108c2ecf20Sopenharmony_ci continue; 20118c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 20128c2ecf20Sopenharmony_ci if (ndev->flags & (IFF_UP | IFF_RUNNING)) { 20138c2ecf20Sopenharmony_ci dev_warn(dev, "The IUCV device is connected" 20148c2ecf20Sopenharmony_ci " to %s and cannot be removed\n", 20158c2ecf20Sopenharmony_ci priv->conn->userid); 20168c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, "remove_write: still active\n"); 20178c2ecf20Sopenharmony_ci return -EPERM; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci unregister_netdev(ndev); 20208c2ecf20Sopenharmony_ci netiucv_unregister_device(dev); 20218c2ecf20Sopenharmony_ci return count; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci read_unlock_bh(&iucv_connection_rwlock); 20248c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); 20258c2ecf20Sopenharmony_ci return -EINVAL; 20268c2ecf20Sopenharmony_ci} 20278c2ecf20Sopenharmony_cistatic DRIVER_ATTR_WO(remove); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_cistatic struct attribute * netiucv_drv_attrs[] = { 20308c2ecf20Sopenharmony_ci &driver_attr_connection.attr, 20318c2ecf20Sopenharmony_ci &driver_attr_remove.attr, 20328c2ecf20Sopenharmony_ci NULL, 20338c2ecf20Sopenharmony_ci}; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_cistatic struct attribute_group netiucv_drv_attr_group = { 20368c2ecf20Sopenharmony_ci .attrs = netiucv_drv_attrs, 20378c2ecf20Sopenharmony_ci}; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_cistatic const struct attribute_group *netiucv_drv_attr_groups[] = { 20408c2ecf20Sopenharmony_ci &netiucv_drv_attr_group, 20418c2ecf20Sopenharmony_ci NULL, 20428c2ecf20Sopenharmony_ci}; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_cistatic void netiucv_banner(void) 20458c2ecf20Sopenharmony_ci{ 20468c2ecf20Sopenharmony_ci pr_info("driver initialized\n"); 20478c2ecf20Sopenharmony_ci} 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_cistatic void __exit netiucv_exit(void) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci struct iucv_connection *cp; 20528c2ecf20Sopenharmony_ci struct net_device *ndev; 20538c2ecf20Sopenharmony_ci struct netiucv_priv *priv; 20548c2ecf20Sopenharmony_ci struct device *dev; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 20578c2ecf20Sopenharmony_ci while (!list_empty(&iucv_connection_list)) { 20588c2ecf20Sopenharmony_ci cp = list_entry(iucv_connection_list.next, 20598c2ecf20Sopenharmony_ci struct iucv_connection, list); 20608c2ecf20Sopenharmony_ci ndev = cp->netdev; 20618c2ecf20Sopenharmony_ci priv = netdev_priv(ndev); 20628c2ecf20Sopenharmony_ci dev = priv->dev; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci unregister_netdev(ndev); 20658c2ecf20Sopenharmony_ci netiucv_unregister_device(dev); 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci driver_unregister(&netiucv_driver); 20698c2ecf20Sopenharmony_ci iucv_unregister(&netiucv_handler, 1); 20708c2ecf20Sopenharmony_ci iucv_unregister_dbf_views(); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci pr_info("driver unloaded\n"); 20738c2ecf20Sopenharmony_ci return; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_cistatic int __init netiucv_init(void) 20778c2ecf20Sopenharmony_ci{ 20788c2ecf20Sopenharmony_ci int rc; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci rc = iucv_register_dbf_views(); 20818c2ecf20Sopenharmony_ci if (rc) 20828c2ecf20Sopenharmony_ci goto out; 20838c2ecf20Sopenharmony_ci rc = iucv_register(&netiucv_handler, 1); 20848c2ecf20Sopenharmony_ci if (rc) 20858c2ecf20Sopenharmony_ci goto out_dbf; 20868c2ecf20Sopenharmony_ci IUCV_DBF_TEXT(trace, 3, __func__); 20878c2ecf20Sopenharmony_ci netiucv_driver.groups = netiucv_drv_attr_groups; 20888c2ecf20Sopenharmony_ci rc = driver_register(&netiucv_driver); 20898c2ecf20Sopenharmony_ci if (rc) { 20908c2ecf20Sopenharmony_ci IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", rc); 20918c2ecf20Sopenharmony_ci goto out_iucv; 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci netiucv_banner(); 20958c2ecf20Sopenharmony_ci return rc; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ciout_iucv: 20988c2ecf20Sopenharmony_ci iucv_unregister(&netiucv_handler, 1); 20998c2ecf20Sopenharmony_ciout_dbf: 21008c2ecf20Sopenharmony_ci iucv_unregister_dbf_views(); 21018c2ecf20Sopenharmony_ciout: 21028c2ecf20Sopenharmony_ci return rc; 21038c2ecf20Sopenharmony_ci} 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_cimodule_init(netiucv_init); 21068c2ecf20Sopenharmony_cimodule_exit(netiucv_exit); 21078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2108