📄 cycx_x25.c
字号:
static int cycx_netdevice_stop(struct net_device *dev){ struct cycx_x25_channel *chan = dev->priv; netif_stop_queue(dev); if (chan->state == WAN_CONNECTED || chan->state == WAN_CONNECTING) cycx_x25_chan_disconnect(dev); return 0;}/* Build media header. * o encapsulate packet according to encapsulation type. * * The trick here is to put packet type (Ethertype) into 'protocol' field of * the socket buffer, so that we don't forget it. If encapsulation fails, * set skb->protocol to 0 and discard packet later. * * Return: media header length. */static int cycx_netdevice_hard_header(struct sk_buff *skb, struct net_device *dev, u16 type, void *daddr, void *saddr, unsigned len){ skb->protocol = type; return dev->hard_header_len;}/* * Re-build media header. * Return: 1 physical address resolved. * 0 physical address not resolved */static int cycx_netdevice_rebuild_header(struct sk_buff *skb){ return 1;}/* Send a packet on a network interface. * o set busy flag (marks start of the transmission). * o check link state. If link is not up, then drop the packet. * o check channel status. If it's down then initiate a call. * o pass a packet to corresponding WAN device. * o free socket buffer * * Return: 0 complete (socket buffer must be freed) * non-0 packet may be re-transmitted (tbusy must be set) * * Notes: * 1. This routine is called either by the protocol stack or by the "net * bottom half" (with interrupts enabled). * 2. Setting tbusy flag will inhibit further transmit requests from the * protocol stack and can be used for flow control with protocol layer. */static int cycx_netdevice_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct cycx_x25_channel *chan = dev->priv; struct cycx_device *card = chan->card; if (!chan->svc) chan->protocol = skb->protocol; if (card->wandev.state != WAN_CONNECTED) ++chan->ifstats.tx_dropped; else if (chan->svc && chan->protocol && chan->protocol != skb->protocol) { printk(KERN_INFO "%s: unsupported Ethertype 0x%04X on interface %s!\n", card->devname, skb->protocol, dev->name); ++chan->ifstats.tx_errors; } else if (chan->protocol == ETH_P_IP) { switch (chan->state) { case WAN_DISCONNECTED: if (cycx_x25_chan_connect(dev)) { netif_stop_queue(dev); return -EBUSY; } /* fall thru */ case WAN_CONNECTED: reset_timer(dev); dev->trans_start = jiffies; netif_stop_queue(dev); if (cycx_x25_chan_send(dev, skb)) return -EBUSY; break; default: ++chan->ifstats.tx_dropped; ++card->wandev.stats.tx_dropped; } } else { /* chan->protocol == ETH_P_X25 */ switch (skb->data[0]) { case 0: break; case 1: /* Connect request */ cycx_x25_chan_connect(dev); goto free_packet; case 2: /* Disconnect request */ cycx_x25_chan_disconnect(dev); goto free_packet; default: printk(KERN_INFO "%s: unknown %d x25-iface request on %s!\n", card->devname, skb->data[0], dev->name); ++chan->ifstats.tx_errors; goto free_packet; } skb_pull(skb, 1); /* Remove control byte */ reset_timer(dev); dev->trans_start = jiffies; netif_stop_queue(dev); if (cycx_x25_chan_send(dev, skb)) { /* prepare for future retransmissions */ skb_push(skb, 1); return -EBUSY; } }free_packet: dev_kfree_skb(skb); return 0;}/* Get Ethernet-style interface statistics. * Return a pointer to struct net_device_stats */static struct net_device_stats *cycx_netdevice_get_stats(struct net_device *dev){ struct cycx_x25_channel *chan = dev->priv; return chan ? &chan->ifstats : NULL;}/* Interrupt Handlers *//* X.25 Interrupt Service Routine. */static void cycx_x25_irq_handler(struct cycx_device *card){ struct cycx_x25_cmd cmd; u16 z = 0; card->in_isr = 1; card->buff_int_mode_unbusy = 0; cycx_peek(&card->hw, X25_RXMBOX_OFFS, &cmd, sizeof(cmd)); switch (cmd.command) { case X25_DATA_INDICATION: cycx_x25_irq_rx(card, &cmd); break; case X25_ACK_FROM_VC: cycx_x25_irq_tx(card, &cmd); break; case X25_LOG: cycx_x25_irq_log(card, &cmd); break; case X25_STATISTIC: cycx_x25_irq_stat(card, &cmd); break; case X25_CONNECT_CONFIRM: cycx_x25_irq_connect_confirm(card, &cmd); break; case X25_CONNECT_INDICATION: cycx_x25_irq_connect(card, &cmd); break; case X25_DISCONNECT_INDICATION: cycx_x25_irq_disconnect(card, &cmd); break; case X25_DISCONNECT_CONFIRM: cycx_x25_irq_disconnect_confirm(card, &cmd); break; case X25_LINE_ON: cycx_set_state(card, WAN_CONNECTED); break; case X25_LINE_OFF: cycx_set_state(card, WAN_DISCONNECTED); break; default: cycx_x25_irq_spurious(card, &cmd); break; } cycx_poke(&card->hw, 0, &z, sizeof(z)); cycx_poke(&card->hw, X25_RXMBOX_OFFS, &z, sizeof(z)); card->in_isr = 0;}/* Transmit interrupt handler. * o Release socket buffer * o Clear 'tbusy' flag */static void cycx_x25_irq_tx(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct net_device *dev; struct wan_device *wandev = &card->wandev; u8 lcn; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); /* unbusy device and then dev_tint(); */ dev = cycx_x25_get_dev_by_lcn(wandev, lcn); if (dev) { card->buff_int_mode_unbusy = 1; netif_wake_queue(dev); } else printk(KERN_ERR "%s:ackvc for inexistent lcn %d\n", card->devname, lcn);}/* Receive interrupt handler. * This routine handles fragmented IP packets using M-bit according to the * RFC1356. * o map logical channel number to network interface. * o allocate socket buffer or append received packet to the existing one. * o if M-bit is reset (i.e. it's the last packet in a sequence) then * decapsulate packet and pass socket buffer to the protocol stack. * * Notes: * 1. When allocating a socket buffer, if M-bit is set then more data is * coming and we have to allocate buffer for the maximum IP packet size * expected on this channel. * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no * socket buffers available) the whole packet sequence must be discarded. */static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct wan_device *wandev = &card->wandev; struct net_device *dev; struct cycx_x25_channel *chan; struct sk_buff *skb; u8 bitm, lcn; int pktlen = cmd->len - 5; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); cycx_peek(&card->hw, cmd->buf + 4, &bitm, sizeof(bitm)); bitm &= 0x10; dev = cycx_x25_get_dev_by_lcn(wandev, lcn); if (!dev) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n", card->devname, lcn); return; } chan = dev->priv; reset_timer(dev); if (chan->drop_sequence) { if (!bitm) chan->drop_sequence = 0; else return; } if ((skb = chan->rx_skb) == NULL) { /* Allocate new socket buffer */ int bufsize = bitm ? dev->mtu : pktlen; if ((skb = dev_alloc_skb((chan->protocol == ETH_P_X25 ? 1 : 0) + bufsize + dev->hard_header_len)) == NULL) { printk(KERN_INFO "%s: no socket buffers available!\n", card->devname); chan->drop_sequence = 1; ++chan->ifstats.rx_dropped; return; } if (chan->protocol == ETH_P_X25) /* X.25 socket layer control */ /* 0 = data packet (dev_alloc_skb zeroed skb->data) */ skb_put(skb, 1); skb->dev = dev; skb->protocol = htons(chan->protocol); chan->rx_skb = skb; } if (skb_tailroom(skb) < pktlen) { /* No room for the packet. Call off the whole thing! */ dev_kfree_skb_irq(skb); chan->rx_skb = NULL; if (bitm) chan->drop_sequence = 1; printk(KERN_INFO "%s: unexpectedly long packet sequence " "on interface %s!\n", card->devname, dev->name); ++chan->ifstats.rx_length_errors; return; } /* Append packet to the socket buffer */ cycx_peek(&card->hw, cmd->buf + 5, skb_put(skb, pktlen), pktlen); if (bitm) return; /* more data is coming */ chan->rx_skb = NULL; /* dequeue packet */ ++chan->ifstats.rx_packets; chan->ifstats.rx_bytes += pktlen; skb->mac.raw = skb->data; netif_rx(skb); dev->last_rx = jiffies; /* timestamp */}/* Connect interrupt handler. */static void cycx_x25_irq_connect(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct wan_device *wandev = &card->wandev; struct net_device *dev = NULL; struct cycx_x25_channel *chan; u8 d[32], loc[24], rem[24]; u8 lcn, sizeloc, sizerem; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); cycx_peek(&card->hw, cmd->buf + 5, &sizeloc, sizeof(sizeloc)); cycx_peek(&card->hw, cmd->buf + 6, d, cmd->len - 6); sizerem = sizeloc >> 4; sizeloc &= 0x0F; loc[0] = rem[0] = '\0'; if (sizeloc) nibble_to_byte(d, loc, sizeloc, 0); if (sizerem) nibble_to_byte(d + (sizeloc >> 1), rem, sizerem, sizeloc & 1); dprintk(1, KERN_INFO "%s:lcn=%d, local=%s, remote=%s\n", __FUNCTION__, lcn, loc, rem); dev = cycx_x25_get_dev_by_dte_addr(wandev, rem); if (!dev) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s: connect not expected: remote %s!\n", card->devname, rem); return; } chan = dev->priv; chan->lcn = lcn; cycx_x25_connect_response(card, chan); cycx_x25_set_chan_state(dev, WAN_CONNECTED);}/* Connect confirm interrupt handler. */static void cycx_x25_irq_connect_confirm(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct wan_device *wandev = &card->wandev; struct net_device *dev; struct cycx_x25_channel *chan; u8 lcn, key; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); cycx_peek(&card->hw, cmd->buf + 1, &key, sizeof(key)); dprintk(1, KERN_INFO "%s: %s:lcn=%d, key=%d\n", card->devname, __FUNCTION__, lcn, key); dev = cycx_x25_get_dev_by_lcn(wandev, -key); if (!dev) { /* Invalid channel, discard packet */ clear_bit(--key, (void*)&card->u.x.connection_keys); printk(KERN_INFO "%s: connect confirm not expected: lcn %d, " "key=%d!\n", card->devname, lcn, key); return; } clear_bit(--key, (void*)&card->u.x.connection_keys); chan = dev->priv; chan->lcn = lcn; cycx_x25_set_chan_state(dev, WAN_CONNECTED);}/* Disconnect confirm interrupt handler. */static void cycx_x25_irq_disconnect_confirm(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct wan_device *wandev = &card->wandev; struct net_device *dev; u8 lcn; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); dprintk(1, KERN_INFO "%s: %s:lcn=%d\n", card->devname, __FUNCTION__, lcn); dev = cycx_x25_get_dev_by_lcn(wandev, lcn); if (!dev) { /* Invalid channel, discard packet */ printk(KERN_INFO "%s:disconnect confirm not expected!:lcn %d\n", card->devname, lcn); return; } cycx_x25_set_chan_state(dev, WAN_DISCONNECTED);}/* disconnect interrupt handler. */static void cycx_x25_irq_disconnect(struct cycx_device *card, struct cycx_x25_cmd *cmd){ struct wan_device *wandev = &card->wandev; struct net_device *dev; u8 lcn; cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn)); dprintk(1, KERN_INFO "%s:lcn=%d\n", __FUNCTION__, lcn); dev = cycx_x25_get_dev_by_lcn(wandev, lcn); if (dev) { struct cycx_x25_channel *chan = dev->priv; cycx_x25_disconnect_response(card, chan->link, lcn); cycx_x25_set_chan_state(dev, WAN_DISCONNECTED); } else cycx_x25_disconnect_response(card, 0, lcn);}/* LOG interrupt handler. */static void cycx_x25_irq_log(struct cycx_device *card, struct cycx_x25_cmd *cmd){#if CYCLOMX_X25_DEBUG char bf[20]; u16 size, toread, link, msg_code; u8 code, routine; cycx_peek(&card->hw, cmd->buf, &msg_code, sizeof(msg_code)); cycx_peek(&card->hw, cmd->buf + 2, &link, sizeof(link)); cycx_peek(&card->hw, cmd->buf + 4, &size, sizeof(size)); /* at most 20 bytes are available... thanks to Daniela :) */ toread = size < 20 ? size : 20; cycx_peek(&card->hw, cmd->buf + 10, &bf, toread); cycx_peek(&card->hw, cmd->buf + 10 + toread, &code, 1); cycx_peek(&card->hw, cmd->buf + 10 + toread + 1, &routine, 1); printk(KERN_INFO "cycx_x25_irq_handler: X25_LOG (0x4500) indic.:\n"); printk(KERN_INFO "cmd->buf=0x%X\n", cmd->buf); printk(KERN_INFO "Log message code=0x%X\n", msg_code); printk(KERN_INFO "Link=%d\n", link); printk(KERN_INFO "log code=0x%X\n", code); printk(KERN_INFO "log routine=0x%X\n", routine); printk(KERN_INFO "Message size=%d\n", size); hex_dump("Message", bf, toread);#endif}/* STATISTIC interrupt handler. */static void cycx_x25_irq_stat(struct cycx_device *card, struct cycx_x25_cmd *cmd){ cycx_peek(&card->hw, cmd->buf, &card->u.x.stats, sizeof(card->u.x.stats)); hex_dump("cycx_x25_irq_stat", (unsigned char*)&card->u.x.stats, sizeof(card->u.x.stats)); cycx_x25_dump_stats(&card->u.x.stats); wake_up_interruptible(&card->wait_stats);}/* Spurious interrupt handler. * o print a warning * If number of spurious interrupts exceeded some limit, then ??? */static void cycx_x25_irq_spurious(struct cycx_device *card, struct cycx_x25_cmd *cmd){ printk(KERN_INFO "%s: spurious interrupt (0x%X)!\n", card->devname, cmd->command);}#ifdef CYCLOMX_X25_DEBUGstatic void hex_dump(char *msg, unsigned char *p, int len){ unsigned char hex[1024], * phex = hex; if (len >= (sizeof(hex) / 2)) len = (sizeof(hex) / 2) - 1; while (len--) { sprintf(phex, "%02x", *p++); phex += 2; } printk(KERN_INFO "%s: %s\n", msg, hex);}#endif/* Cyclom 2X Firmware-Specific Functions *//* Exec X.25 command. */static int x25_exec(struct cycx_device *card, int command, int link, void *d1, int len1, void *d2, int len2){ struct cycx_x25_cmd c; unsigned long flags; u32 addr = 0x1200 + 0x2E0 * link + 0x1E2; u8 retry = CYCX_X25_MAX_CMD_RETRY; int err = 0; c.command = command; c.link = link; c.len = len1 + len2; spin_lock_irqsave(&card->u.x.lock, flags); /* write command */ cycx_poke(&card->hw, X25_MBOX_OFFS, &c, sizeof(c) - sizeof(c.buf)); /* write X.25 data */ if (d1) { cycx_poke(&card->hw, addr, d1, len1); if (d2) { if (len2 > 254) { u32 addr1 = 0xA00 + 0x400 * link; cycx_poke(&card->hw, addr + len1, d2, 249); cycx_poke(&card->hw, addr1, ((u8*)d2) + 249, len2 - 249); } else cycx_poke(&card->hw, addr + len1, d2, len2); } } /* generate interruption, executing command */ cycx_intr(&card->hw); /* wait till card->mbox == 0 */ do { err = cycx_exec(card->mbox); } while (retry-- && err); spin_unlock_irqrestore(&card->u.x.lock, flags); return err;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -