📄 lanai.c
字号:
static inline void intr_enable(const struct lanai_dev *lanai, u32 i){ reg_write(lanai, i, IntControlEna_Reg);}static inline void intr_disable(const struct lanai_dev *lanai, u32 i){ reg_write(lanai, i, IntControlDis_Reg);}/* -------------------- CARD/PCI STATUS: */static void status_message(int itf, const char *name, int status){ static const char *onoff[2] = { "off to on", "on to off" }; printk(KERN_INFO DEV_LABEL "(itf %d): %s changed from %s\n", itf, name, onoff[!status]);}static void lanai_check_status(struct lanai_dev *lanai){ u32 new = reg_read(lanai, Status_Reg); u32 changes = new ^ lanai->status; lanai->status = new;#define e(flag, name) \ if (changes & flag) \ status_message(lanai->number, name, new & flag) e(STATUS_SOOL, "SOOL"); e(STATUS_LOCD, "LOCD"); e(STATUS_LED, "LED"); e(STATUS_GPIN, "GPIN");#undef e}static void pcistatus_got(int itf, const char *name){ printk(KERN_INFO DEV_LABEL "(itf %d): PCI got %s error\n", itf, name);}static void pcistatus_check(struct lanai_dev *lanai, int clearonly){ u16 s; int result; result = pci_read_config_word(lanai->pci, PCI_STATUS, &s); if (result != PCIBIOS_SUCCESSFUL) { printk(KERN_ERR DEV_LABEL "(itf %d): can't read PCI_STATUS: " "%d\n", lanai->number, result); return; } s &= PCI_STATUS_DETECTED_PARITY | PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_SIG_TARGET_ABORT | PCI_STATUS_PARITY; if (s == 0) return; result = pci_write_config_word(lanai->pci, PCI_STATUS, s); if (result != PCIBIOS_SUCCESSFUL) printk(KERN_ERR DEV_LABEL "(itf %d): can't write PCI_STATUS: " "%d\n", lanai->number, result); if (clearonly) return;#define e(flag, name, stat) \ if (s & flag) { \ pcistatus_got(lanai->number, name); \ ++lanai->stats.pcierr_##stat; \ } e(PCI_STATUS_DETECTED_PARITY, "parity", parity_detect); e(PCI_STATUS_SIG_SYSTEM_ERROR, "signalled system", serr_set); e(PCI_STATUS_REC_MASTER_ABORT, "master", master_abort); e(PCI_STATUS_REC_TARGET_ABORT, "master target", m_target_abort); e(PCI_STATUS_SIG_TARGET_ABORT, "slave", s_target_abort); e(PCI_STATUS_PARITY, "master parity", master_parity);#undef e}/* -------------------- VCC TX BUFFER UTILITIES: *//* space left in tx buffer in bytes */static inline int vcc_tx_space(const struct lanai_vcc *lvcc, int endptr){ int r; r = endptr * 16; r -= ((unsigned long) lvcc->tx.buf.ptr) - ((unsigned long) lvcc->tx.buf.start); r -= 16; /* Leave "bubble" - if start==end it looks empty */ if (r < 0) r += lanai_buf_size(&lvcc->tx.buf); return r;}/* Bit fields in the segmentation buffer descriptor */#define DESCRIPTOR_MAGIC (0xD0000000)#define DESCRIPTOR_AAL5 (0x00008000)#define DESCRIPTOR_AAL5_STREAM (0x00004000)#define DESCRIPTOR_CLP (0x00002000)/* Add 32-bit descriptor with it's padding */static inline void vcc_tx_add_aal5_descriptor(struct lanai_vcc *lvcc, u32 flags, int len){ int pos; APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 0, "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); lvcc->tx.buf.ptr += 4; /* Hope the values REALLY don't matter */ pos = ((unsigned char *) lvcc->tx.buf.ptr) - (unsigned char *) lvcc->tx.buf.start; APRINTK((pos & ~0x0001FFF0) == 0, "vcc_tx_add_aal5_descriptor: bad pos (%d) before, vci=%d, " "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); pos = (pos + len) & (lanai_buf_size(&lvcc->tx.buf) - 1); APRINTK((pos & ~0x0001FFF0) == 0, "vcc_tx_add_aal5_descriptor: bad pos (%d) after, vci=%d, " "start,ptr,end=%p,%p,%p\n", pos, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); lvcc->tx.buf.ptr[-1] = cpu_to_le32(DESCRIPTOR_MAGIC | DESCRIPTOR_AAL5 | ((lvcc->tx.atmvcc->atm_options & ATM_ATMOPT_CLP) ? DESCRIPTOR_CLP : 0) | flags | pos >> 4); if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) lvcc->tx.buf.ptr = lvcc->tx.buf.start;}/* Add 32-bit AAL5 trailer and leave room for its CRC */static inline void vcc_tx_add_aal5trailer(struct lanai_vcc *lvcc, int len, int cpi, int uu){ APRINTK((((unsigned long) lvcc->tx.buf.ptr) & 15) == 8, "vcc_tx_add_aal5_descriptor: bad ptr=%p\n", lvcc->tx.buf.ptr); lvcc->tx.buf.ptr += 2; lvcc->tx.buf.ptr[-2] = cpu_to_be32((uu << 24) | (cpi << 16) | len); if (lvcc->tx.buf.ptr >= lvcc->tx.buf.end) lvcc->tx.buf.ptr = lvcc->tx.buf.start;}static inline void vcc_tx_memcpy(struct lanai_vcc *lvcc, const unsigned char *src, int n){ unsigned char *e; int m; e = ((unsigned char *) lvcc->tx.buf.ptr) + n; m = e - (unsigned char *) lvcc->tx.buf.end; if (m < 0) m = 0; memcpy(lvcc->tx.buf.ptr, src, n - m); if (m != 0) { memcpy(lvcc->tx.buf.start, src + n - m, m); e = ((unsigned char *) lvcc->tx.buf.start) + m; } lvcc->tx.buf.ptr = (u32 *) e;}static inline void vcc_tx_memzero(struct lanai_vcc *lvcc, int n){ unsigned char *e; int m; if (n == 0) return; e = ((unsigned char *) lvcc->tx.buf.ptr) + n; m = e - (unsigned char *) lvcc->tx.buf.end; if (m < 0) m = 0; memset(lvcc->tx.buf.ptr, 0, n - m); if (m != 0) { memset(lvcc->tx.buf.start, 0, m); e = ((unsigned char *) lvcc->tx.buf.start) + m; } lvcc->tx.buf.ptr = (u32 *) e;}/* Update "butt" register to specify new WritePtr */static inline void lanai_endtx(const struct lanai_dev *lanai, const struct lanai_vcc *lvcc){ int i, ptr = ((unsigned char *) lvcc->tx.buf.ptr) - (unsigned char *) lvcc->tx.buf.start; APRINTK((ptr & ~0x0001FFF0) == 0, "lanai_endtx: bad ptr (%d), vci=%d, start,ptr,end=%p,%p,%p\n", ptr, lvcc->vci, lvcc->tx.buf.start, lvcc->tx.buf.ptr, lvcc->tx.buf.end); /* * We need to check if the "butt busy" bit is set before * updating the butt register. In theory this should * never happen because the ATM card is plenty fast at * updating the register. Still, we should make sure */ for (i = 0; reg_read(lanai, Status_Reg) & STATUS_BUTTBUSY; i++) { if (i > 50) { printk(KERN_ERR DEV_LABEL "(itf %d): butt register " "always busy!\n", lanai->number); break; } udelay(5); } reg_write(lanai, (ptr << 12) | lvcc->vci, Butt_Reg);}/* Try to fill the buffer - don't call unless there is backlog */static void vcc_tx_unqueue_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, int endptr){ int pad, n; struct sk_buff *skb; int space = vcc_tx_space(lvcc, endptr); APRINTK(vcc_is_backlogged(lvcc), "vcc_tx_unqueue() called with empty backlog (vci=%d)\n", lvcc->vci); if (space < 64) return; /* No space for even 1 cell+descriptor */ if (lvcc->tx.inprogress != NULL) { APRINTK((lvcc->tx.inprogleft % 48) == 0, "vcc_tx_unqueue_aal5: bad progleft=%d\n", lvcc->tx.inprogleft); if (lvcc->tx.inprogleft + 16 > space) { /* Can't send all? */ n = aal5_spacefor(space - 16); /* Bytes to send */ vcc_tx_add_aal5_descriptor(lvcc, DESCRIPTOR_AAL5_STREAM, n); pad = lvcc->tx.pptr + n - lvcc->tx.inprogress->tail; if (pad < 0) pad = 0; vcc_tx_memcpy(lvcc, lvcc->tx.pptr, n - pad); vcc_tx_memzero(lvcc, pad); lvcc->tx.pptr += n; lvcc->tx.inprogleft -= n; goto end; /* Buffer is now full */ } /* OK, there's at least space for all of "inprogress" skb */ vcc_tx_add_aal5_descriptor(lvcc, 0, lvcc->tx.inprogleft); pad = lvcc->tx.pptr + lvcc->tx.inprogleft - lvcc->tx.inprogress->tail; if (pad >= lvcc->tx.inprogleft) { /* Nothing but pad left */ APRINTK(lvcc->tx.inprogleft == 48, "vcc_tx_unqueue_aal5: bad pure-pad=%d\n", lvcc->tx.inprogleft); pad = 48; } else vcc_tx_memcpy(lvcc, lvcc->tx.pptr, lvcc->tx.inprogleft - pad); vcc_tx_memzero(lvcc, pad - 8); vcc_tx_add_aal5trailer(lvcc, lvcc->tx.inprogress->len, 0, 0); lanai_free_skb(lvcc->tx.atmvcc, lvcc->tx.inprogress); lvcc->tx.inprogress = NULL; space -= lvcc->tx.inprogleft + 16; atomic_inc(&lvcc->tx.atmvcc->stats->tx); } while (space >= 64) { if ((skb = skb_dequeue(&lvcc->tx.backlog)) == NULL) break; n = aal5_size(skb->len); if (n + 16 > space) { /* Can only send part */ int m = aal5_spacefor(space - 16); /* Bytes to send */ vcc_tx_add_aal5_descriptor(lvcc, DESCRIPTOR_AAL5_STREAM, m); lvcc->tx.pptr = skb->data + m; pad = lvcc->tx.pptr - skb->tail; if (pad < 0) pad = 0; vcc_tx_memcpy(lvcc, skb->data, m - pad); vcc_tx_memzero(lvcc, pad); lvcc->tx.inprogleft = n - m; lvcc->tx.inprogress = skb; goto end; } vcc_tx_add_aal5_descriptor(lvcc, 0, n); pad = n - skb->len - 8; vcc_tx_memcpy(lvcc, skb->data, skb->len); vcc_tx_memzero(lvcc, pad); lanai_free_skb(lvcc->tx.atmvcc, skb); vcc_tx_add_aal5trailer(lvcc, skb->len, 0, 0); space -= n + 16; atomic_inc(&lvcc->tx.atmvcc->stats->tx); } if (skb_queue_empty(&lvcc->tx.backlog)) vcc_unmark_backlogged(lanai, lvcc); end: lanai_endtx(lanai, lvcc);}/* Given an skb that we want to transmit either send it now or queue */static void vcc_tx_aal5(struct lanai_dev *lanai, struct lanai_vcc *lvcc, struct sk_buff *skb){ int space, n, pad; if (vcc_is_backlogged(lvcc)) /* Already backlogged */ goto queue_it; space = vcc_tx_space(lvcc, TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr))); if (space < 64) { vcc_mark_backlogged(lanai, lvcc); /* No space */ goto queue_it; } if (space >= 16 + (n = aal5_size(skb->len))) { /* We can send the whole thing now */ vcc_tx_add_aal5_descriptor(lvcc, 0, n); pad = n - skb->len; vcc_tx_memcpy(lvcc, skb->data, skb->len); vcc_tx_memzero(lvcc, pad - 8); vcc_tx_add_aal5trailer(lvcc, skb->len, 0, 0); lanai_free_skb(lvcc->tx.atmvcc, skb); atomic_inc(&lvcc->tx.atmvcc->stats->tx); } else { /* Space for only part of skb */ int bytes = aal5_spacefor(space - 16); /* Bytes to send */ vcc_tx_add_aal5_descriptor(lvcc, DESCRIPTOR_AAL5_STREAM, bytes); pad = bytes - skb->len; if (pad < 0) pad = 0; vcc_tx_memcpy(lvcc, skb->data, bytes - pad); vcc_tx_memzero(lvcc, pad); lvcc->tx.inprogress = skb; lvcc->tx.inprogleft = n - bytes; lvcc->tx.pptr = skb->data + bytes; vcc_mark_backlogged(lanai, lvcc); } lanai_endtx(lanai, lvcc); return; queue_it: skb_queue_tail(&lvcc->tx.backlog, skb);}static void vcc_tx_unqueue_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, int endptr){ printk(KERN_INFO DEV_LABEL ": vcc_tx_unqueue_aal0: not implemented\n");}static void vcc_tx_aal0(struct lanai_dev *lanai, struct lanai_vcc *lvcc, struct sk_buff *skb){ printk(KERN_INFO DEV_LABEL ": vcc_tx_aal0: not implemented\n"); /* Remember to increment lvcc->tx.atmvcc->stats->tx */ lanai_free_skb(lvcc->tx.atmvcc, skb);}/* Try to undequeue 1 backlogged vcc */static void iter_dequeue(struct lanai_dev *lanai, vci_t vci){ struct lanai_vcc *lvcc = lanai->vccs[vci]; int endptr; if (lvcc == NULL || !vcc_is_backlogged(lvcc)) { vci_bitfield_clear(&lanai->backlog_vccs, vci); return; } endptr = TXREADPTR_GET_PTR(cardvcc_read(lvcc, vcc_txreadptr)); lvcc->tx.unqueue(lanai, lvcc, endptr);}/* Try a dequeue on all backlogged connections */static inline void vcc_tx_dequeue_all(struct lanai_dev *lanai){ unsigned long flags; spin_lock_irqsave(&lanai->txlock, flags); vci_bitfield_iterate(lanai, &lanai->backlog_vccs, iter_dequeue); spin_unlock_irqrestore(&lanai->txlock, flags);}/* -------------------- VCC RX BUFFER UTILITIES: *//* unlike the _tx_ cousins, this doesn't update ptr */static inline void vcc_rx_memcpy(unsigned char *dest, const struct lanai_vcc *lvcc, int n){ int m = ((const unsigned char *) lvcc->rx.buf.ptr) + n - ((const unsigned char *) (lvcc->rx.buf.end)); if (m < 0) m = 0; memcpy(dest, lvcc->rx.buf.ptr, n - m); memcpy(dest + n - m, lvcc->rx.buf.start, m);}/* Receive AAL5 data on a VCC with a particular endptr */static void vcc_rx_aal5(struct lanai_vcc *lvcc, int endptr){ int size; struct sk_buff *skb; /*const*/ u32 *x, *end = &lvcc->rx.buf.start[endptr * 4]; int n = ((unsigned long) end) - ((unsigned long) lvcc->rx.buf.ptr); if (n < 0) n += lanai_buf_size(&lvcc->rx.buf); APRINTK(n >= 0 && n < lanai_buf_size(&lvcc->rx.buf) && !(n & 15), "vcc_rx_aal5: n out of range (%d/%d)\n", n, lanai_buf_size(&lvcc->rx.buf)); /* Recover the second-to-last word to get true pdu length */ if ((x = &end[-2]) < lvcc->rx.buf.start) x = &lvcc->rx.buf.end[-2]; size = be32_to_cpup(x) & 0xffff; if (n != aal5_size(size)) { /* Make sure size matches padding */ printk(KERN_INFO DEV_LABEL "(itf %d): Got bad AAL5 length " "on vci=%d - size=%d n=%d\n", lvcc->rx.atmvcc->dev->number, lvcc->vci, size, n); lvcc->stats.x.aal5.rx_badlen++; goto out; } skb = atm_alloc_charge(lvcc->rx.atmvcc, size, GFP_ATOMIC); if (skb == NULL) { lvcc->stats.rx_nomem++; goto out; } skb_put(skb, size); ATM_SKB(skb)->vcc = lvcc->rx.atmvcc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -