📄 hdlc.c
字号:
else if (mode_is(hdlc, MODE_X25)) { struct lapb_register_struct cb; cb.connect_confirmation = x25_connected; cb.connect_indication = x25_connected; cb.disconnect_confirmation = x25_disconnected; cb.disconnect_indication = x25_disconnected; cb.data_indication = x25_data_indication; cb.data_transmit = x25_data_transmit; result = lapb_register(hdlc, &cb); if (result != LAPB_OK) return result; }#endif result = hdlc->open(hdlc); if (result) { if (mode_is(hdlc, MODE_FR | MODE_SOFT) || mode_is(hdlc, MODE_CISCO | MODE_SOFT)) fr_cisco_close(hdlc);#ifdef CONFIG_HDLC_PPP else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { sppp_close(dev); sppp_detach(dev); dev->rebuild_header = NULL; dev->change_mtu = hdlc_change_mtu; dev->mtu = HDLC_MAX_MTU; dev->hard_header_len = 16; }#endif#ifdef CONFIG_HDLC_X25 else if (mode_is(hdlc, MODE_X25)) lapb_unregister(hdlc);#endif } return result;}static int hdlc_close(struct net_device *dev){ hdlc_device *hdlc = dev_to_hdlc(dev); hdlc->close(hdlc); if (mode_is(hdlc, MODE_FR | MODE_SOFT) || mode_is(hdlc, MODE_CISCO | MODE_SOFT)) fr_cisco_close(hdlc);#ifdef CONFIG_HDLC_PPP else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { sppp_close(dev); sppp_detach(dev); dev->rebuild_header = NULL; dev->change_mtu = hdlc_change_mtu; dev->mtu = HDLC_MAX_MTU; dev->hard_header_len = 16; }#endif#ifdef CONFIG_HDLC_X25 else if (mode_is(hdlc, MODE_X25)) lapb_unregister(hdlc);#endif return 0;}static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev){ hdlc_device *hdlc = dev_to_hdlc(dev);#ifdef CONFIG_HDLC_X25 if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) { int result; /* X.25 to LAPB */ switch (skb->data[0]) { case 0: /* Data to be transmitted */ skb_pull(skb, 1); if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK) dev_kfree_skb(skb); return 0; case 1: if ((result = lapb_connect_request(hdlc))!= LAPB_OK) { if (result == LAPB_CONNECTED) { /* Send connect confirm. msg to level 3 */ x25_connected(hdlc, 0); } else { printk(KERN_ERR "%s: LAPB connect " "request failed, error code = " "%i\n", hdlc_to_name(hdlc), result); } } break; case 2: if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) { if (result == LAPB_NOTCONNECTED) { /* Send disconnect confirm. msg to level 3 */ x25_disconnected(hdlc, 0); } else { printk(KERN_ERR "%s: LAPB disconnect " "request failed, error code = " "%i\n", hdlc_to_name(hdlc), result); } } break; default: /* to be defined */ break; } dev_kfree_skb(skb); return 0; } /* MODE_X25 */#endif /* CONFIG_HDLC_X25 */ return hdlc->xmit(hdlc, skb);}void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb){/* skb contains raw HDLC frame, in both hard- and software modes */ skb->mac.raw = skb->data; switch(hdlc->mode & MODE_MASK) { case MODE_HDLC: skb->protocol = htons(ETH_P_IP); skb->dev = hdlc_to_dev(hdlc); netif_rx(skb); return; case MODE_FR: fr_netif(hdlc, skb); return; case MODE_CISCO: cisco_netif(hdlc, skb); return;#ifdef CONFIG_HDLC_PPP case MODE_PPP:#if 0 sppp_input(hdlc_to_dev(hdlc), skb);#else skb->protocol = htons(ETH_P_WAN_PPP); skb->dev = hdlc_to_dev(hdlc); netif_rx(skb);#endif return;#endif#ifdef CONFIG_HDLC_X25 case MODE_X25: skb->dev = hdlc_to_dev(hdlc); if (lapb_data_received(hdlc, skb) == LAPB_OK) return; break;#endif } hdlc->stats.rx_errors++; dev_kfree_skb_any(skb);}static struct net_device_stats *hdlc_get_stats(struct net_device *dev){ return &dev_to_hdlc(dev)->stats;}static int hdlc_set_mode(hdlc_device *hdlc, int mode){ int result = -1; /* Default to soft modes */ struct net_device *dev = hdlc_to_dev(hdlc); if(!capable(CAP_NET_ADMIN)) return -EPERM; if(dev->flags & IFF_UP) return -EBUSY; dev->addr_len = 0; dev->hard_header = NULL; hdlc->mode = MODE_NONE; if (!(mode & MODE_SOFT)) switch(mode & MODE_MASK) { case MODE_HDLC: result = hdlc->set_mode ? hdlc->set_mode(hdlc, MODE_HDLC) : 0; break; case MODE_CISCO: /* By card */#ifdef CONFIG_HDLC_PPP case MODE_PPP:#endif#ifdef CONFIG_HDLC_X25 case MODE_X25:#endif case MODE_FR: result = hdlc->set_mode ? hdlc->set_mode(hdlc, mode) : -ENOSYS; break; default: return -EINVAL; } if (result) { mode |= MODE_SOFT; /* Try "host software" protocol */ switch(mode & MODE_MASK) { case MODE_CISCO: dev->hard_header = cisco_hard_header; break;#ifdef CONFIG_HDLC_PPP case MODE_PPP: break;#endif#ifdef CONFIG_HDLC_X25 case MODE_X25: break;#endif case MODE_FR: dev->hard_header = fr_hard_header; dev->addr_len = 2; *(u16*)dev->dev_addr = htons(LMI_DLCI); dlci_to_q922(dev->broadcast, LMI_DLCI); break; default: return -EINVAL; } result = hdlc->set_mode ? hdlc->set_mode(hdlc, MODE_HDLC) : 0; } if (result) return result; hdlc->mode = mode; switch(mode & MODE_MASK) {#ifdef CONFIG_HDLC_PPP case MODE_PPP: dev->type = ARPHRD_PPP; break;#endif#ifdef CONFIG_HDLC_X25 case MODE_X25: dev->type = ARPHRD_X25; break;#endif case MODE_FR: dev->type = ARPHRD_FRAD; break; case MODE_CISCO: dev->type = ARPHRD_CISCO; break; default: dev->type = ARPHRD_RAWHDLC; } memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); destroy_pvc_list(hdlc); return 0;}static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci){ pvc_device **pvc_p = &hdlc->first_pvc; pvc_device *pvc; int result, create = 1; /* Create or delete PVC */ if(!capable(CAP_NET_ADMIN)) return -EPERM; if(dlci<0) { dlci = -dlci; create = 0; } if(dlci <= 0 || dlci >= 1024) return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */ if(!mode_is(hdlc, MODE_FR)) return -EINVAL; /* Only meaningfull on FR */ while(*pvc_p) { if (netdev_dlci(&(*pvc_p)->netdev) == dlci) break; pvc_p = &(*pvc_p)->next; } if (create) { /* Create PVC */ if (*pvc_p != NULL) return -EEXIST; pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); if (!pvc) { printk(KERN_WARNING "%s: Memory squeeze on " "hdlc_fr_pvc()\n", hdlc_to_name(hdlc)); return -ENOBUFS; } memset(pvc, 0, sizeof(pvc_device)); pvc->netdev.hard_start_xmit = pvc_xmit; pvc->netdev.get_stats = pvc_get_stats; pvc->netdev.open = pvc_open; pvc->netdev.stop = pvc_close; pvc->netdev.change_mtu = pvc_change_mtu; pvc->netdev.mtu = HDLC_MAX_MTU; pvc->netdev.type = ARPHRD_DLCI; pvc->netdev.hard_header_len = 16; pvc->netdev.hard_header = fr_hard_header; pvc->netdev.tx_queue_len = 0; pvc->netdev.flags = IFF_POINTOPOINT; pvc->master = hdlc; *(u16*)pvc->netdev.dev_addr = htons(dlci); dlci_to_q922(pvc->netdev.broadcast, dlci); pvc->netdev.addr_len = 2; pvc->netdev.irq = hdlc_to_dev(hdlc)->irq; result = dev_alloc_name(&pvc->netdev, "pvc%d"); if (result < 0) { kfree(pvc); *pvc_p = NULL; return result; } if (register_netdevice(&pvc->netdev) != 0) { kfree(pvc); *pvc_p = NULL; return -EIO; } if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) { result = hdlc->create_pvc(pvc); if (result) { unregister_netdevice(&pvc->netdev); kfree(pvc); *pvc_p = NULL; return result; } } hdlc->lmi.state |= LINK_STATE_CHANGED; hdlc->pvc_count++; return 0; } if (*pvc_p == NULL) /* Delete PVC */ return -ENOENT; pvc = *pvc_p; if (pvc->netdev.flags & IFF_UP) return -EBUSY; /* PVC in use */ if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc) hdlc->destroy_pvc(pvc); hdlc->lmi.state |= LINK_STATE_CHANGED; hdlc->pvc_count--; *pvc_p = pvc->next; unregister_netdevice(&pvc->netdev); kfree(pvc); return 0;}static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ hdlc_device *hdlc = dev_to_hdlc(dev); switch(cmd) { case HDLCGMODE: ifr->ifr_ifru.ifru_ivalue = hdlc->mode; return 0; case HDLCSMODE: return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue); case HDLCPVC: return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue); default: if (hdlc->ioctl != NULL) return hdlc->ioctl(hdlc, ifr, cmd); } return -EINVAL;}static int hdlc_init(struct net_device *dev){ hdlc_device *hdlc = dev_to_hdlc(dev); memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); dev->get_stats = hdlc_get_stats; dev->open = hdlc_open; dev->stop = hdlc_close; dev->hard_start_xmit = hdlc_xmit; dev->do_ioctl = hdlc_ioctl; dev->change_mtu = hdlc_change_mtu; dev->mtu = HDLC_MAX_MTU; dev->type = ARPHRD_RAWHDLC; dev->hard_header_len = 16; dev->flags = IFF_POINTOPOINT | IFF_NOARP; return 0;}int register_hdlc_device(hdlc_device *hdlc){ int result; struct net_device *dev = hdlc_to_dev(hdlc); dev->init = hdlc_init; dev->priv = &hdlc->syncppp_ptr; hdlc->syncppp_ptr = &hdlc->pppdev; hdlc->pppdev.dev = dev; hdlc->mode = MODE_NONE; hdlc->lmi.T391 = 10; /* polling verification timer */ hdlc->lmi.T392 = 15; /* link integrity verification polling timer */ hdlc->lmi.N391 = 6; /* full status polling counter */ hdlc->lmi.N392 = 3; /* error threshold */ hdlc->lmi.N393 = 4; /* monitored events count */ result = dev_alloc_name(dev, "hdlc%d"); if (result<0) return result; result = register_netdev(dev); if (result != 0) return -EIO; MOD_INC_USE_COUNT; return 0;}void unregister_hdlc_device(hdlc_device *hdlc){ destroy_pvc_list(hdlc); unregister_netdev(hdlc_to_dev(hdlc)); MOD_DEC_USE_COUNT;}MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");MODULE_DESCRIPTION("HDLC support module");MODULE_LICENSE("GPL");EXPORT_SYMBOL(hdlc_netif_rx);EXPORT_SYMBOL(register_hdlc_device);EXPORT_SYMBOL(unregister_hdlc_device);static int __init hdlc_module_init(void){ printk(KERN_INFO "%s\n", version); return 0;}module_init(hdlc_module_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -