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 void fr_timer(unsigned long arg) { hdlc_device *hdlc=(hdlc_device*)arg; int i, cnt=0, reliable; u32 list; if (mode_is(hdlc, MODE_DCE)) reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ); else { hdlc->lmi.last_errors <<= 1; /* Shift the list */ if (hdlc->lmi.state & LINK_STATE_REQUEST) { printk(KERN_INFO "%s: No LMI status reply received\n", hdlc->name); hdlc->lmi.last_errors |= 1; } for (i=0, list=hdlc->lmi.last_errors; i<hdlc->lmi.N393; i++, list>>=1) cnt += (list & 1); /* errors count */ reliable = (cnt < hdlc->lmi.N392); } if ((hdlc->lmi.state & LINK_STATE_RELIABLE) != (reliable ? LINK_STATE_RELIABLE : 0)) { pvc_device *pvc=hdlc->first_pvc; while (pvc) {/* Deactivate all PVCs */ pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE); pvc=pvc->next; } hdlc->lmi.state ^= LINK_STATE_RELIABLE; printk(KERN_INFO "%s: Link %sreliable\n", hdlc->name, reliable ? "" : "un"); if (reliable) { hdlc->lmi.N391cnt=0; /* Request full status */ hdlc->lmi.state |= LINK_STATE_CHANGED; } } if (mode_is(hdlc, MODE_DCE)) hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ; else { if (hdlc->lmi.N391cnt) hdlc->lmi.N391cnt--; fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0); hdlc->lmi.state |= LINK_STATE_REQUEST; hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ; } hdlc->timer.function = fr_timer; hdlc->timer.data = arg; add_timer(&hdlc->timer); }
static void fr_cisco_open(hdlc_device *hdlc) { hdlc->lmi.state = LINK_STATE_CHANGED; hdlc->lmi.txseq = hdlc->lmi.rxseq = 0; hdlc->lmi.last_errors = 0xFFFFFFFF; hdlc->lmi.N391cnt = 0; if (mode_is(hdlc, MODE_CISCO)) hdlc_to_dev(hdlc)->hard_header=cisco_hard_header; else hdlc_to_dev(hdlc)->hard_header=fr_hard_header; init_timer(&hdlc->timer); hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */ hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer; hdlc->timer.data = (unsigned long)hdlc; add_timer(&hdlc->timer); }
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); }
static int pvc_close(struct device *dev) { pvc_device *pvc=dev_to_pvc(dev); pvc->state=0; if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc) pvc->master->close_pvc(pvc); pvc->master->lmi.state |= LINK_STATE_CHANGED; return 0; }
static int hdlc_close(struct 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); 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; } return 0; }
static int pvc_open(struct device *dev) { pvc_device *pvc=dev_to_pvc(dev); int result=0; if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) return -EIO; /* Master must be UP in order to activate PVC */ memset(&(pvc->stats), 0, sizeof(struct net_device_stats)); pvc->state=0; if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc) result=pvc->master->open_pvc(pvc); if (result) return result; pvc->master->lmi.state |= LINK_STATE_CHANGED; return 0; }
void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb, int dlci) { skb->mac.raw=skb->data; if (mode_is(hdlc, MODE_SOFT)) { if (mode_is(hdlc, MODE_FR)) { fr_netif(hdlc, skb); return; } else if (mode_is(hdlc, MODE_CISCO)) { cisco_netif(hdlc, skb); return; } else if (mode_is(hdlc, MODE_PPP)) { hdlc->stats.rx_bytes+=skb->len; hdlc->stats.rx_packets++; skb->protocol=htons(ETH_P_WAN_PPP); skb->dev=hdlc_to_dev(hdlc); netif_rx(skb); return; } } else { /* protocol support in hardware/firmware */ hdlc->stats.rx_bytes+=skb->len; hdlc->stats.rx_packets++; if (mode_is(hdlc, MODE_HDLC)) skb->protocol=htons(ETH_P_IP); /* otherwise protocol set by hw driver */ if (mode_is(hdlc, MODE_FR)) { pvc_device *pvc=find_pvc(hdlc, dlci); if (!pvc) { /* packet from nonexistent PVC */ hdlc->stats.rx_errors++; dev_kfree_skb(skb); } pvc->stats.rx_bytes+=skb->len; pvc->stats.rx_packets++; skb->dev=&pvc->netdev; } else skb->dev=hdlc_to_dev(hdlc); netif_rx(skb); return; } hdlc->stats.rx_errors++; /* unsupported mode */ dev_kfree_skb(skb); }
static int hdlc_open(struct device *dev) { hdlc_device *hdlc=dev_to_hdlc(dev); int result; if (hdlc->mode==MODE_NONE) return -ENOSYS; memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); if (mode_is(hdlc, MODE_FR | MODE_SOFT) || mode_is(hdlc, MODE_CISCO | MODE_SOFT)) fr_cisco_open(hdlc); else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { sppp_attach(&hdlc->pppdev); /* sppp_attach nukes them. We don't need syncppp's ioctl */ /* Anyway, I'm going to replace it with ppp_synctty.c */ dev->do_ioctl = hdlc_ioctl; hdlc->pppdev.sppp.pp_flags&=~PP_CISCO; dev->type=ARPHRD_PPP; result = sppp_open(dev); if (result) { sppp_detach(dev); return result; } } 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); 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; } } return result; }
static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) { int stat_len; pvc_device *pvc; int reptype=-1, error; u8 rxseq, txseq; int i; if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH)) { printk(KERN_INFO "%s: Short LMI frame\n", hdlc->name); return 1; } if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY)) { printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n", hdlc->name, skb->data[2], mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply"); return 1; } i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6; if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) { printk(KERN_INFO "%s: Not a report type=%x\n", hdlc->name, skb->data[i]); return 1; } i++; i++; /* Skip length field */ reptype=skb->data[i++]; if (skb->data[i]!= (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) { printk(KERN_INFO "%s: Unsupported status element=%x\n", hdlc->name, skb->data[i]); return 1; } i++; i++; /* Skip length field */ hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */ rxseq = skb->data[i++]; /* Should confirm our sequence */ txseq = hdlc->lmi.txseq; if (mode_is(hdlc, MODE_DCE)) { if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) { printk(KERN_INFO "%s: Unsupported report type=%x\n", hdlc->name, reptype); return 1; } } error=0; if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) error=1; if (rxseq == 0 || rxseq != txseq) { hdlc->lmi.N391cnt=0; /* Ask for full report next time */ error=1; } if (mode_is(hdlc, MODE_DCE)) { if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) { /* Stop sending full report - the last one has been confirmed by DTE */ hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT; pvc=hdlc->first_pvc; while (pvc) { if (pvc->state & PVC_STATE_NEW) { pvc->state &= ~PVC_STATE_NEW; pvc->state |= PVC_STATE_ACTIVE; fr_log_dlci_active(pvc); /* Tell DTE that new PVC is now active */ hdlc->lmi.state |= LINK_STATE_CHANGED; } pvc=pvc->next; } } if (hdlc->lmi.state & LINK_STATE_CHANGED) { reptype = LMI_FULLREP; hdlc->lmi.state |= LINK_STATE_FULLREP_SENT; hdlc->lmi.state &= ~LINK_STATE_CHANGED; } fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0); return 0; } /* DTE */ if (reptype != LMI_FULLREP || error) return 0; stat_len = 3; pvc=hdlc->first_pvc; while (pvc) { pvc->newstate = 0; pvc=pvc->next; } while (skb->len >= i + 2 + stat_len) { u16 dlci; u8 state=0; if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n", hdlc->name, skb->data[i]); return 1; } i++; if (skb->data[i] != stat_len) { printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n", hdlc->name, skb->data[i]); return 1; } i++; dlci=status_to_dlci(hdlc, skb->data+i, &state); pvc=find_pvc(hdlc, dlci); if (pvc) pvc->newstate = state; else if (state == PVC_STATE_NEW) printk(KERN_INFO "%s: new PVC available, DLCI=%u\n", hdlc->name, dlci); i+=stat_len; } pvc=hdlc->first_pvc; while (pvc) { if (pvc->newstate == PVC_STATE_NEW) pvc->newstate = PVC_STATE_ACTIVE; pvc->newstate |= (pvc->state & ~(PVC_STATE_NEW|PVC_STATE_ACTIVE)); if (pvc->state != pvc->newstate) { pvc->state=pvc->newstate; fr_log_dlci_active(pvc); } pvc=pvc->next; } /* Next full report after N391 polls */ hdlc->lmi.N391cnt = hdlc->lmi.N391; return 0; }
static void fr_lmi_send(hdlc_device *hdlc, int fullrep) { struct sk_buff *skb; pvc_device *pvc=hdlc->first_pvc; int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH; int stat_len = 3; u8 *data; int i=0; if (mode_is(hdlc, MODE_DCE) && fullrep) { len += hdlc->pvc_count * (2 + stat_len); if (len>HDLC_MAX_MTU) { printk(KERN_WARNING "%s: Too many PVCs while sending " "LMI full report\n", hdlc->name); return; } } skb=dev_alloc_skb(len); memset(skb->data, 0, len); skb_reserve(skb, 4); fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0); data=skb->tail; data[i++] = LMI_CALLREF; data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY; if (mode_is(hdlc, MODE_FR_ANSI)) data[i++] = LMI_ANSI_LOCKSHIFT; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE; data[i++] = LMI_REPT_LEN; data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY; data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE; data[i++] = LMI_INTEG_LEN; data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq); data[i++] = hdlc->lmi.rxseq; if (mode_is(hdlc, MODE_DCE) && fullrep) { while (pvc) { data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_PVCSTAT:LMI_PVCSTAT; data[i++] = stat_len; if ((hdlc->lmi.state & LINK_STATE_RELIABLE) && (pvc->netdev.flags & IFF_UP) && !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) { pvc->state |= PVC_STATE_NEW; fr_log_dlci_active(pvc); } dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), data+i, pvc->state); i+=stat_len; pvc=pvc->next; } } skb_put(skb, i); skb->priority=TC_PRIO_CONTROL; skb->dev = hdlc_to_dev(hdlc); dev_queue_xmit(skb); }
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_p=kmalloc(sizeof(pvc_device), GFP_KERNEL); pvc=*pvc_p; memset(pvc, 0, sizeof(pvc_device)); pvc->netdev.name=pvc->name; 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=PVC_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; dev_init_buffers(&pvc->netdev); pvc->master=hdlc; *(u16*)pvc->netdev.dev_addr=htons(dlci); dlci_to_q922(pvc->netdev.broadcast, dlci); pvc->netdev.addr_len=2; /* 16 bits is enough */ 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_set_mode(hdlc_device *hdlc, int mode) { int result=-1; /* Default to soft modes */ if(!capable(CAP_NET_ADMIN)) return -EPERM; if(hdlc_to_dev(hdlc)->flags & IFF_UP) return -EBUSY; hdlc_to_dev(hdlc)->addr_len=0; hdlc->mode=MODE_NONE; if (!(mode & MODE_SOFT)) switch(mode) { case MODE_HDLC: result = hdlc->set_mode ? hdlc->set_mode(hdlc, MODE_HDLC) : 0; break; case MODE_X25: /* By card */ case MODE_CISCO: case MODE_PPP: case MODE_FR_ANSI: case MODE_FR_CCITT: case MODE_FR_ANSI | MODE_DCE: case MODE_FR_CCITT | MODE_DCE: 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_SOFT) { case MODE_CISCO: case MODE_PPP: break; case MODE_FR_ANSI: case MODE_FR_CCITT: case MODE_FR_ANSI | MODE_DCE: case MODE_FR_CCITT | MODE_DCE: hdlc_to_dev(hdlc)->addr_len=2; *(u16*)hdlc_to_dev(hdlc)->dev_addr=htons(LMI_DLCI); dlci_to_q922(hdlc_to_dev(hdlc)->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; if (mode_is(hdlc, MODE_PPP)) hdlc_to_dev(hdlc)->type=ARPHRD_PPP; if (mode_is(hdlc, MODE_X25)) hdlc_to_dev(hdlc)->type=ARPHRD_X25; else if (mode_is(hdlc, MODE_FR)) hdlc_to_dev(hdlc)->type=ARPHRD_FRAD; else /* Conflict - raw HDLC and Cisco */ hdlc_to_dev(hdlc)->type=ARPHRD_HDLC; memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); destroy_pvc_list(hdlc); return 0; }
static int hdlc_open(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); int result; if (hdlc->mode == MODE_NONE) return -ENOSYS; memset(&(hdlc->stats), 0, sizeof(struct net_device_stats)); if (mode_is(hdlc, MODE_FR | MODE_SOFT) || mode_is(hdlc, MODE_CISCO | MODE_SOFT)) fr_cisco_open(hdlc); #ifdef CONFIG_HDLC_PPP else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) { sppp_attach(&hdlc->pppdev); /* sppp_attach nukes them. We don't need syncppp's ioctl */ dev->do_ioctl = hdlc_ioctl; hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO; dev->type = ARPHRD_PPP; result = sppp_open(dev); if (result) { sppp_detach(dev); return result; } } #endif #ifdef CONFIG_HDLC_X25 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; }