📄 netiucv.c
字号:
static ssize_ttxtime_show (struct device *dev, struct device_attribute *attr, char *buf){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 5, __FUNCTION__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_time);}static ssize_ttxtime_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 4, __FUNCTION__); priv->conn->prof.tx_time = 0; return count;}static DEVICE_ATTR(max_tx_io_time, 0644, txtime_show, txtime_write);static ssize_ttxpend_show (struct device *dev, struct device_attribute *attr, char *buf){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 5, __FUNCTION__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_pending);}static ssize_ttxpend_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 4, __FUNCTION__); priv->conn->prof.tx_pending = 0; return count;}static DEVICE_ATTR(tx_pending, 0644, txpend_show, txpend_write);static ssize_ttxmpnd_show (struct device *dev, struct device_attribute *attr, char *buf){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 5, __FUNCTION__); return sprintf(buf, "%ld\n", priv->conn->prof.tx_max_pending);}static ssize_ttxmpnd_write (struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ struct netiucv_priv *priv = dev->driver_data; IUCV_DBF_TEXT(trace, 4, __FUNCTION__); priv->conn->prof.tx_max_pending = 0; return count;}static DEVICE_ATTR(tx_max_pending, 0644, txmpnd_show, txmpnd_write);static struct attribute *netiucv_attrs[] = { &dev_attr_buffer.attr, &dev_attr_user.attr, NULL,};static struct attribute_group netiucv_attr_group = { .attrs = netiucv_attrs,};static struct attribute *netiucv_stat_attrs[] = { &dev_attr_device_fsm_state.attr, &dev_attr_connection_fsm_state.attr, &dev_attr_max_tx_buffer_used.attr, &dev_attr_max_chained_skbs.attr, &dev_attr_tx_single_write_ops.attr, &dev_attr_tx_multi_write_ops.attr, &dev_attr_netto_bytes.attr, &dev_attr_max_tx_io_time.attr, &dev_attr_tx_pending.attr, &dev_attr_tx_max_pending.attr, NULL,};static struct attribute_group netiucv_stat_attr_group = { .name = "stats", .attrs = netiucv_stat_attrs,};static inline intnetiucv_add_files(struct device *dev){ int ret; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); ret = sysfs_create_group(&dev->kobj, &netiucv_attr_group); if (ret) return ret; ret = sysfs_create_group(&dev->kobj, &netiucv_stat_attr_group); if (ret) sysfs_remove_group(&dev->kobj, &netiucv_attr_group); return ret;}static inline voidnetiucv_remove_files(struct device *dev){ IUCV_DBF_TEXT(trace, 3, __FUNCTION__); sysfs_remove_group(&dev->kobj, &netiucv_stat_attr_group); sysfs_remove_group(&dev->kobj, &netiucv_attr_group);}static intnetiucv_register_device(struct net_device *ndev){ struct netiucv_priv *priv = ndev->priv; struct device *dev = kmalloc(sizeof(struct device), GFP_KERNEL); int ret; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (dev) { memset(dev, 0, sizeof(struct device)); snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name); dev->bus = &iucv_bus; dev->parent = iucv_root; /* * The release function could be called after the * module has been unloaded. It's _only_ task is to * free the struct. Therefore, we specify kfree() * directly here. (Probably a little bit obfuscating * but legitime ...). */ dev->release = (void (*)(struct device *))kfree; dev->driver = &netiucv_driver; } else return -ENOMEM; ret = device_register(dev); if (ret) return ret; ret = netiucv_add_files(dev); if (ret) goto out_unreg; priv->dev = dev; dev->driver_data = priv; return 0;out_unreg: device_unregister(dev); return ret;}static voidnetiucv_unregister_device(struct device *dev){ IUCV_DBF_TEXT(trace, 3, __FUNCTION__); netiucv_remove_files(dev); device_unregister(dev);}/** * Allocate and initialize a new connection structure. * Add it to the list of netiucv connections; */static struct iucv_connection *netiucv_new_connection(struct net_device *dev, char *username){ struct iucv_connection **clist = &iucv_connections; struct iucv_connection *conn = (struct iucv_connection *) kmalloc(sizeof(struct iucv_connection), GFP_KERNEL); if (conn) { memset(conn, 0, sizeof(struct iucv_connection)); skb_queue_head_init(&conn->collect_queue); skb_queue_head_init(&conn->commit_queue); conn->max_buffsize = NETIUCV_BUFSIZE_DEFAULT; conn->netdev = dev; conn->rx_buff = alloc_skb(NETIUCV_BUFSIZE_DEFAULT, GFP_KERNEL | GFP_DMA); if (!conn->rx_buff) { kfree(conn); return NULL; } conn->tx_buff = alloc_skb(NETIUCV_BUFSIZE_DEFAULT, GFP_KERNEL | GFP_DMA); if (!conn->tx_buff) { kfree_skb(conn->rx_buff); kfree(conn); return NULL; } conn->fsm = init_fsm("netiucvconn", conn_state_names, conn_event_names, NR_CONN_STATES, NR_CONN_EVENTS, conn_fsm, CONN_FSM_LEN, GFP_KERNEL); if (!conn->fsm) { kfree_skb(conn->tx_buff); kfree_skb(conn->rx_buff); kfree(conn); return NULL; } fsm_settimer(conn->fsm, &conn->timer); fsm_newstate(conn->fsm, CONN_STATE_INVALID); if (username) { memcpy(conn->userid, username, 9); fsm_newstate(conn->fsm, CONN_STATE_STOPPED); } conn->next = *clist; *clist = conn; } return conn;}/** * Release a connection structure and remove it from the * list of netiucv connections. */static voidnetiucv_remove_connection(struct iucv_connection *conn){ struct iucv_connection **clist = &iucv_connections; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (conn == NULL) return; while (*clist) { if (*clist == conn) { *clist = conn->next; if (conn->handle) { iucv_unregister_program(conn->handle); conn->handle = NULL; } fsm_deltimer(&conn->timer); kfree_fsm(conn->fsm); kfree_skb(conn->rx_buff); kfree_skb(conn->tx_buff); return; } clist = &((*clist)->next); }}/** * Release everything of a net device. */static voidnetiucv_free_netdevice(struct net_device *dev){ struct netiucv_priv *privptr; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (!dev) return; privptr = (struct netiucv_priv *)dev->priv; if (privptr) { if (privptr->conn) netiucv_remove_connection(privptr->conn); if (privptr->fsm) kfree_fsm(privptr->fsm); privptr->conn = NULL; privptr->fsm = NULL; /* privptr gets freed by free_netdev() */ } free_netdev(dev);}/** * Initialize a net device. (Called from kernel in alloc_netdev()) */static voidnetiucv_setup_netdevice(struct net_device *dev){ memset(dev->priv, 0, sizeof(struct netiucv_priv)); dev->mtu = NETIUCV_MTU_DEFAULT; dev->hard_start_xmit = netiucv_tx; dev->open = netiucv_open; dev->stop = netiucv_close; dev->get_stats = netiucv_stats; dev->change_mtu = netiucv_change_mtu; dev->destructor = netiucv_free_netdevice; dev->hard_header_len = NETIUCV_HDRLEN; dev->addr_len = 0; dev->type = ARPHRD_SLIP; dev->tx_queue_len = NETIUCV_QUEUELEN_DEFAULT; dev->flags = IFF_POINTOPOINT | IFF_NOARP; SET_MODULE_OWNER(dev);}/** * Allocate and initialize everything of a net device. */static struct net_device *netiucv_init_netdevice(char *username){ struct netiucv_priv *privptr; struct net_device *dev; dev = alloc_netdev(sizeof(struct netiucv_priv), "iucv%d", netiucv_setup_netdevice); if (!dev) return NULL; if (dev_alloc_name(dev, dev->name) < 0) { free_netdev(dev); return NULL; } privptr = (struct netiucv_priv *)dev->priv; privptr->fsm = init_fsm("netiucvdev", dev_state_names, dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS, dev_fsm, DEV_FSM_LEN, GFP_KERNEL); if (!privptr->fsm) { free_netdev(dev); return NULL; } privptr->conn = netiucv_new_connection(dev, username); if (!privptr->conn) { kfree_fsm(privptr->fsm); free_netdev(dev); IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_new_connection\n"); return NULL; } fsm_newstate(privptr->fsm, DEV_STATE_STOPPED); return dev;}static ssize_tconn_write(struct device_driver *drv, const char *buf, size_t count){ char *p; char username[10]; int i, ret; struct net_device *dev; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (count>9) { PRINT_WARN("netiucv: username too long (%d)!\n", (int)count); IUCV_DBF_TEXT(setup, 2, "conn_write: too long\n"); return -EINVAL; } for (i=0, p=(char *)buf; i<8 && *p; i++, p++) { if (isalnum(*p) || (*p == '$')) username[i]= *p; else if (*p == '\n') { /* trailing lf, grr */ break; } else { PRINT_WARN("netiucv: Invalid character in username!\n"); IUCV_DBF_TEXT_(setup, 2, "conn_write: invalid character %c\n", *p); return -EINVAL; } } while (i<9) username[i++] = ' '; username[9] = '\0'; dev = netiucv_init_netdevice(username); if (!dev) { PRINT_WARN( "netiucv: Could not allocate network device structure " "for user '%s'\n", netiucv_printname(username)); IUCV_DBF_TEXT(setup, 2, "NULL from netiucv_init_netdevice\n"); return -ENODEV; } if ((ret = netiucv_register_device(dev))) { IUCV_DBF_TEXT_(setup, 2, "ret %d from netiucv_register_device\n", ret); goto out_free_ndev; } /* sysfs magic */ SET_NETDEV_DEV(dev, (struct device*)((struct netiucv_priv*)dev->priv)->dev); if ((ret = register_netdev(dev))) { netiucv_unregister_device((struct device*) ((struct netiucv_priv*)dev->priv)->dev); goto out_free_ndev; } PRINT_INFO("%s: '%s'\n", dev->name, netiucv_printname(username)); return count;out_free_ndev: PRINT_WARN("netiucv: Could not register '%s'\n", dev->name); IUCV_DBF_TEXT(setup, 2, "conn_write: could not register\n"); netiucv_free_netdevice(dev); return ret;}DRIVER_ATTR(connection, 0200, NULL, conn_write);static ssize_tremove_write (struct device_driver *drv, const char *buf, size_t count){ struct iucv_connection **clist = &iucv_connections; struct net_device *ndev; struct netiucv_priv *priv; struct device *dev; char name[IFNAMSIZ]; char *p; int i; IUCV_DBF_TEXT(trace, 3, __FUNCTION__); if (count >= IFNAMSIZ) count = IFNAMSIZ-1; for (i=0, p=(char *)buf; i<count && *p; i++, p++) { if ((*p == '\n') | (*p == ' ')) { /* trailing lf, grr */ break; } else { name[i]=*p; } } name[i] = '\0'; while (*clist) { ndev = (*clist)->netdev; priv = (struct netiucv_priv*)ndev->priv; dev = priv->dev; if (strncmp(name, ndev->name, count)) { clist = &((*clist)->next); continue; } if (ndev->flags & (IFF_UP | IFF_RUNNING)) { PRINT_WARN( "netiucv: net device %s active with peer %s\n", ndev->name, priv->conn->userid); PRINT_WARN("netiucv: %s cannot be removed\n", ndev->name); IUCV_DBF_TEXT(data, 2, "remove_write: still active\n"); return -EBUSY; } unregister_netdev(ndev); netiucv_unregister_device(dev); return count; } PRINT_WARN("netiucv: net device %s unknown\n", name); IUCV_DBF_TEXT(data, 2, "remove_write: unknown device\n"); return -EINVAL;}DRIVER_ATTR(remove, 0200, NULL, remove_write);static voidnetiucv_banner(void){ char vbuf[] = "$Revision: 1.66 $"; char *version = vbuf; if ((version = strchr(version, ':'))) { char *p = strchr(version + 1, '$'); if (p) *p = '\0'; } else version = " ??? "; PRINT_INFO("NETIUCV driver Version%s initialized\n", version);}static void __exitnetiucv_exit(void){ IUCV_DBF_TEXT(trace, 3, __FUNCTION__); while (iucv_connections) { struct net_device *ndev = iucv_connections->netdev; struct netiucv_priv *priv = (struct netiucv_priv*)ndev->priv; struct device *dev = priv->dev; unregister_netdev(ndev); netiucv_unregister_device(dev); } driver_remove_file(&netiucv_driver, &driver_attr_connection); driver_remove_file(&netiucv_driver, &driver_attr_remove); driver_unregister(&netiucv_driver); iucv_unregister_dbf_views(); PRINT_INFO("NETIUCV driver unloaded\n"); return;}static int __initnetiucv_init(void){ int ret; ret = iucv_register_dbf_views(); if (ret) { PRINT_WARN("netiucv_init failed, " "iucv_register_dbf_views rc = %d\n", ret); return ret; } IUCV_DBF_TEXT(trace, 3, __FUNCTION__); ret = driver_register(&netiucv_driver); if (ret) { PRINT_ERR("NETIUCV: failed to register driver.\n"); IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_register\n", ret); iucv_unregister_dbf_views(); return ret; } /* Add entry for specifying connections. */ ret = driver_create_file(&netiucv_driver, &driver_attr_connection); if (!ret) { ret = driver_create_file(&netiucv_driver, &driver_attr_remove); netiucv_banner(); } else { PRINT_ERR("NETIUCV: failed to add driver attribute.\n"); IUCV_DBF_TEXT_(setup, 2, "ret %d from driver_create_file\n", ret); driver_unregister(&netiucv_driver); iucv_unregister_dbf_views(); } return ret;} module_init(netiucv_init);module_exit(netiucv_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -