📄 lanai.c
字号:
skb->stamp = xtime; vcc_rx_memcpy(skb->data, lvcc, size); lvcc->rx.atmvcc->push(lvcc->rx.atmvcc, skb); atomic_inc(&lvcc->rx.atmvcc->stats->rx); out: lvcc->rx.buf.ptr = end; cardvcc_write(lvcc, endptr, vcc_rxreadptr);}static void vcc_rx_aal0(struct lanai_dev *lanai){ printk(KERN_INFO DEV_LABEL ": vcc_rx_aal0: not implemented\n"); /* Remember to get vcclist_read_lock while looking up VC */ /* Remember to increment lvcc->rx.atmvcc->stats->rx */}/* -------------------- MANAGING HOST-BASED VCC TABLE: *//* Decide whether to use vmalloc or get_free_page for VCC table */#if (NUM_VCI * BITS_PER_LONG) <= PAGE_SIZE#define VCCTABLE_GETFREEPAGE#else#include <linux/vmalloc.h>#endifstatic int __init vcc_table_allocate(struct lanai_dev *lanai){#ifdef VCCTABLE_GETFREEPAGE APRINTK((lanai->num_vci) * sizeof(struct lanai_vcc *) <= PAGE_SIZE, "vcc table > PAGE_SIZE!"); lanai->vccs = (struct lanai_vcc **) get_free_page(GFP_KERNEL); return (lanai->vccs == NULL) ? -ENOMEM : 0;#else int bytes = (lanai->num_vci) * sizeof(struct lanai_vcc *); lanai->vccs = (struct lanai_vcc **) vmalloc(bytes); if (lanai->vccs == NULL) return -ENOMEM; memset(lanai->vccs, 0, bytes); return 0;#endif}static inline void vcc_table_deallocate(const struct lanai_dev *lanai){#ifdef VCCTABLE_GETFREEPAGE free_page((unsigned long) lanai->vccs);#else vfree(lanai->vccs);#endif}/* Allocate a fresh lanai_vcc, with the appropriate things cleared */static inline struct lanai_vcc *new_lanai_vcc(void){ struct lanai_vcc *lvcc; lvcc = (struct lanai_vcc *) kmalloc(sizeof(*lvcc), GFP_KERNEL); if (lvcc != NULL) { lvcc->vbase = 0; lvcc->rx.atmvcc = lvcc->tx.atmvcc = NULL; lvcc->nref = 0; memset(&lvcc->stats, 0, sizeof lvcc->stats); lvcc->rx.buf.start = lvcc->tx.buf.start = NULL; skb_queue_head_init(&lvcc->tx.backlog); lvcc->tx.inprogress = NULL;#ifdef DEBUG lvcc->tx.unqueue = NULL; lvcc->vci = -1;#endif } return lvcc;}static int lanai_get_sized_buffer(int number, struct lanai_buffer *buf, int max_sdu, int multiplier, int min, const char *name){ int size; if (max_sdu < 1) max_sdu = 1; max_sdu = aal5_size(max_sdu); size = (max_sdu + 16) * multiplier + 16; lanai_buf_allocate(buf, size, min); if (buf->order < 0) return -ENOMEM; if (lanai_buf_size(buf) < size) printk(KERN_WARNING DEV_LABEL "(itf %d): wanted %d bytes " "for %s buffer, got only %d\n", number, size, name, lanai_buf_size(buf)); DPRINTK("Allocated %d byte %s buffer\n", lanai_buf_size(buf), name); return 0;}/* Setup a RX buffer for a currently unbound AAL5 vci */static inline int lanai_setup_rx_vci_aal5(int number, struct lanai_vcc *lvcc, const struct atm_qos *qos){ return lanai_get_sized_buffer(number, &lvcc->rx.buf, qos->rxtp.max_sdu, AAL5_RX_MULTIPLIER, qos->rxtp.max_sdu + 32, "RX");}/* Setup a TX buffer for a currently unbound AAL5 vci */static int lanai_setup_tx_vci(int number, struct lanai_vcc *lvcc, const struct atm_qos *qos){ int max_sdu, multiplier; if (qos->aal == ATM_AAL0) { lvcc->tx.unqueue = vcc_tx_unqueue_aal0; max_sdu = ATM_CELL_SIZE - 1; multiplier = AAL0_TX_MULTIPLIER; } else { lvcc->tx.unqueue = vcc_tx_unqueue_aal5; max_sdu = qos->txtp.max_sdu; multiplier = AAL5_TX_MULTIPLIER; } return lanai_get_sized_buffer(number, &lvcc->tx.buf, max_sdu, multiplier, 80, "TX");}static inline void host_vcc_bind(struct lanai_dev *lanai, struct lanai_vcc *lvcc, vci_t vci){ if (lvcc->vbase != 0) return; /* We already were bound in the other direction */ DPRINTK("Binding vci %d\n", vci);#ifdef USE_POWERDOWN if (lanai->nbound++ == 0) { DPRINTK("Coming out of powerdown\n"); lanai->conf1 &= ~CONFIG1_POWERDOWN; conf1_write(lanai); conf2_write(lanai); }#endif lvcc->vbase = cardvcc_addr(lanai, vci); lanai->vccs[lvcc->vci = vci] = lvcc;}static inline void host_vcc_unbind(struct lanai_dev *lanai, struct lanai_vcc *lvcc){ if (lvcc->vbase == 0) return; /* This vcc was never bound */ DPRINTK("Unbinding vci %d\n", lvcc->vci); lvcc->vbase = 0; lanai->vccs[lvcc->vci] = NULL;#ifdef USE_POWERDOWN if (--lanai->nbound == 0) { DPRINTK("Going into powerdown\n"); lanai->conf1 |= CONFIG1_POWERDOWN; conf1_write(lanai); }#endif}/* -------------------- RESET CARD: */static void lanai_reset(struct lanai_dev *lanai){ printk(KERN_CRIT DEV_LABEL "(itf %d): *NOT* reseting - not " "implemented\n", lanai->number); /* TODO */ /* The following is just a hack until we write the real * resetter - at least ack whatever interrupt sent us * here */ reg_write(lanai, INT_ALL, IntAck_Reg); lanai->stats.card_reset++;}/* -------------------- SERVICE LIST UTILITIES: *//* * Allocate service buffer and tell card about it */static int __init service_buffer_allocate(struct lanai_dev *lanai){ lanai_buf_allocate(&lanai->service, SERVICE_ENTRIES * 4, 0); if (lanai->service.order < 0) return -ENOMEM; DPRINTK("allocated service buffer at 0x%08lX, size %d(%d)\n", (unsigned long) lanai->service.start, lanai_buf_size(&lanai->service), lanai_buf_size_cardorder(&lanai->service)); /* Clear ServWrite register to be safe */ reg_write(lanai, 0, ServWrite_Reg); /* ServiceStuff register contains size and address of buffer */ reg_write(lanai, SSTUFF_SET_SIZE(lanai_buf_size_cardorder(&lanai->service)) | SSTUFF_SET_ADDR(lanai_buf_dmaaddr(&lanai->service)), ServiceStuff_Reg); return 0;}static inline void service_buffer_deallocate(struct lanai_dev *lanai){ lanai_buf_deallocate(&lanai->service);}/* Bitfields in service list */#define SERVICE_TX (0x80000000) /* Was from transmission */#define SERVICE_TRASH (0x40000000) /* RXed PDU was trashed */#define SERVICE_CRCERR (0x20000000) /* RXed PDU had CRC error */#define SERVICE_CI (0x10000000) /* RXed PDU had CI set */#define SERVICE_CLP (0x08000000) /* RXed PDU had CLP set */#define SERVICE_STREAM (0x04000000) /* RX Stream mode */#define SERVICE_GET_VCI(x) (((x)>>16)&0x3FF)#define SERVICE_GET_END(x) ((x)&0x1FFF)/* Handle one thing from the service list - returns true if it marked a * VCC ready for xmit */static int handle_service(struct lanai_dev *lanai, u32 s){ vci_t vci = SERVICE_GET_VCI(s); struct lanai_vcc *lvcc; vcclist_read_lock(); lvcc = lanai->vccs[vci]; if (lvcc == NULL) { vcclist_read_unlock(); DPRINTK("(itf %d) got service entry 0x%X for nonexistent " "vcc %d\n", lanai->number, s, vci); if (s & SERVICE_TX) lanai->stats.service_novcc_tx++; else lanai->stats.service_novcc_rx++; return 0; } if (s & SERVICE_TX) { /* segmentation interrupt */ if (lvcc->tx.atmvcc == NULL) { vcclist_read_unlock(); DPRINTK("(itf %d) got service entry 0x%X for non-TX " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_notx++; return 0; } vci_bitfield_set(&lanai->transmit_ready, vci); lvcc->tx.endptr = SERVICE_GET_END(s); vcclist_read_unlock(); return 1; } if (lvcc->rx.atmvcc == NULL) { vcclist_read_unlock(); DPRINTK("(itf %d) got service entry 0x%X for non-RX " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_norx++; return 0; } if (lvcc->rx.atmvcc->qos.aal != ATM_AAL5) { vcclist_read_unlock(); DPRINTK("(itf %d) got RX service entry 0x%X for non-AAL5 " "vcc %d\n", lanai->number, s, vci); lanai->stats.service_rxnotaal5++; atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); return 0; } if ((s & (SERVICE_TRASH | SERVICE_STREAM | SERVICE_CRCERR)) == 0) { vcc_rx_aal5(lvcc, SERVICE_GET_END(s)); vcclist_read_unlock(); return 0; } if (s & SERVICE_TRASH) { int bytes; vcclist_read_unlock(); DPRINTK("got trashed rx pdu on vci %d\n", vci); atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); lvcc->stats.x.aal5.service_trash++; bytes = (SERVICE_GET_END(s) * 16) - (((unsigned long) lvcc->rx.buf.ptr) - ((unsigned long) lvcc->rx.buf.start)) + 47; if (bytes < 0) bytes += lanai_buf_size(&lvcc->rx.buf); lanai->stats.ovfl_trash += (bytes / 48); return 0; } if (s & SERVICE_STREAM) { vcclist_read_unlock(); atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); lvcc->stats.x.aal5.service_stream++; printk(KERN_ERR DEV_LABEL "(itf %d): Got AAL5 stream " "PDU on VCI %d!\n", lanai->number, vci); lanai_reset(lanai); return 0; } DPRINTK("got rx crc error on vci %d\n", vci); atomic_inc(&lvcc->rx.atmvcc->stats->rx_err); lvcc->stats.x.aal5.service_rxcrc++; lvcc->rx.buf.ptr = &lvcc->rx.buf.start[SERVICE_GET_END(s) * 4]; cardvcc_write(lvcc, SERVICE_GET_END(s), vcc_rxreadptr); vcclist_read_unlock(); return 0;}/* Try transmitting on all VCIs that we marked ready to serve */static void iter_transmit(struct lanai_dev *lanai, vci_t vci){ struct lanai_vcc *lvcc = lanai->vccs[vci]; if (!vcc_is_backlogged(lvcc)) return; lvcc->tx.unqueue(lanai, lvcc, lvcc->tx.endptr);}/* Run service queue -- called from interrupt context or with * interrupts otherwise disabled and with the lanai->servicelock * lock held */static void run_service(struct lanai_dev *lanai){ int ntx = 0; u32 wreg = reg_read(lanai, ServWrite_Reg); const u32 *end = lanai->service.start + wreg; while (lanai->service.ptr != end) { ntx += handle_service(lanai, le32_to_cpup(lanai->service.ptr++)); if (lanai->service.ptr >= lanai->service.end) lanai->service.ptr = lanai->service.start; } reg_write(lanai, wreg, ServRead_Reg); if (ntx != 0) { spin_lock(&lanai->txlock); vcclist_read_lock(); vci_bitfield_iterate(lanai, &lanai->transmit_ready, iter_transmit); vci_bitfield_init(&lanai->transmit_ready); vcclist_read_unlock(); spin_unlock(&lanai->txlock); }}/* -------------------- GATHER STATISTICS: */static void get_statistics(struct lanai_dev *lanai){ u32 statreg = reg_read(lanai, Statistics_Reg); lanai->stats.atm_ovfl += STATS_GET_FIFO_OVFL(statreg); lanai->stats.hec_err += STATS_GET_HEC_ERR(statreg); lanai->stats.vci_trash += STATS_GET_BAD_VCI(statreg); lanai->stats.ovfl_trash += STATS_GET_BUF_OVFL(statreg);}/* -------------------- POLLING TIMER: */static void lanai_timed_poll(unsigned long arg){#ifndef DEBUG_RW struct lanai_dev *lanai = (struct lanai_dev *) arg; unsigned long flags;#ifdef USE_POWERDOWN if (lanai->conf1 & CONFIG1_POWERDOWN) return;#endif spin_lock_irqsave(&lanai->servicelock, flags); run_service(lanai); spin_unlock_irqrestore(&lanai->servicelock, flags); vcc_tx_dequeue_all(lanai); get_statistics(lanai); mod_timer(&lanai->timer, jiffies + LANAI_POLL_PERIOD);#endif /* DEBUG_RW */}static inline void lanai_timed_poll_start(struct lanai_dev *lanai){ init_timer(&lanai->timer); lanai->timer.expires = jiffies + LANAI_POLL_PERIOD; lanai->timer.data = (unsigned long) lanai; lanai->timer.function = lanai_timed_poll; add_timer(&lanai->timer);}static inline void lanai_timed_poll_stop(struct lanai_dev *lanai){ del_timer(&lanai->timer);}/* -------------------- INTERRUPT SERVICE: */static inline void lanai_int_1(struct lanai_dev *lanai, u32 reason){ u32 ack = 0; if (reason & INT_SERVICE) { ack = INT_SERVICE; spin_lock(&lanai->servicelock); run_service(lanai); spin_unlock(&lanai->servicelock); } if (reason & (INT_AAL0_STR | INT_AAL0)) { ack |= reason & (INT_AAL0_STR | INT_AAL0); vcc_rx_aal0(lanai); } if (reason & INT_STATS) { reason &= ~INT_STATS; /* No need to ack */ get_statistics(lanai); } if (reason & INT_STATUS) { ack |= reason & INT_STATUS; lanai_check_status(lanai); } if (reason & INT_DMASHUT) { printk(KERN_ERR DEV_LABEL "(itf %d): driver error - DMA "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -