📄 pcilynx.c
字号:
return -ENOMEM; } md->lynx = &cards[cid]; md->cid = cid; switch (type) { case t_rom: md->type = rom; break; case t_ram: md->type = ram; break; case t_aux: atomic_set(&md->aux_intr_last_seen, atomic_read(&cards[cid].aux_intr_seen)); md->type = aux; break; } file->private_data = md; return 0;}static int mem_release(struct inode *inode, struct file *file){ struct memdata *md = (struct memdata *)file->private_data; kfree(md); V22_COMPAT_MOD_DEC_USE_COUNT; return 0;}static unsigned int aux_poll(struct file *file, poll_table *pt){ struct memdata *md = (struct memdata *)file->private_data; int cid = md->cid; unsigned int mask; /* reading and writing is always allowed */ mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; if (md->type == aux) { poll_wait(file, &cards[cid].aux_intr_wait, pt); if (atomic_read(&md->aux_intr_last_seen) != atomic_read(&cards[cid].aux_intr_seen)) { mask |= POLLPRI; atomic_inc(&md->aux_intr_last_seen); } } return mask;}loff_t mem_llseek(struct file *file, loff_t offs, int orig){ loff_t newoffs; switch (orig) { case 0: newoffs = offs; break; case 1: newoffs = offs + file->f_pos; break; case 2: newoffs = PCILYNX_MAX_MEMORY + 1 + offs; break; default: return -EINVAL; } if (newoffs < 0 || newoffs > PCILYNX_MAX_MEMORY + 1) return -EINVAL; file->f_pos = newoffs; return newoffs;}/* * do not DMA if count is too small because this will have a serious impact * on performance - the value 2400 was found by experiment and may not work * everywhere as good as here - use mem_mindma option for modules to change */short mem_mindma = 2400;MODULE_PARM(mem_mindma, "h");static ssize_t mem_dmaread(struct memdata *md, u32 physbuf, ssize_t count, int offset){ pcltmp_t pcltmp; struct ti_pcl *pcl; size_t retval; int i; DECLARE_WAITQUEUE(wait, current); count &= ~3; count = MIN(count, 53196); retval = count; if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) & DMA_CHAN_CTRL_BUSY) { PRINT(KERN_WARNING, md->lynx->id, "DMA ALREADY ACTIVE!"); } reg_write(md->lynx, LBUS_ADDR, md->type | offset); pcl = edit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); pcl->buffer[0].control = PCL_CMD_LBUS_TO_PCI | MIN(count, 4092); pcl->buffer[0].pointer = physbuf; count -= 4092; i = 0; while (count > 0) { i++; pcl->buffer[i].control = MIN(count, 4092); pcl->buffer[i].pointer = physbuf + i * 4092; count -= 4092; } pcl->buffer[i].control |= PCL_LAST_BUFF; commit_pcl(md->lynx, md->lynx->dmem_pcl, &pcltmp); set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); run_sub_pcl(md->lynx, md->lynx->dmem_pcl, 2, CHANNEL_LOCALBUS); schedule(); while (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) & DMA_CHAN_CTRL_BUSY) { if (signal_pending(current)) { retval = -EINTR; break; } schedule(); } reg_write(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS), 0); remove_wait_queue(&md->lynx->mem_dma_intr_wait, &wait); if (reg_read(md->lynx, DMA_CHAN_CTRL(CHANNEL_LOCALBUS)) & DMA_CHAN_CTRL_BUSY) { PRINT(KERN_ERR, md->lynx->id, "DMA STILL ACTIVE!"); } return retval;}static ssize_t mem_read(struct file *file, char *buffer, size_t count, loff_t *offset){ struct memdata *md = (struct memdata *)file->private_data; ssize_t bcount; size_t alignfix; int off = (int)*offset; /* avoid useless 64bit-arithmetic */ ssize_t retval; void *membase; if ((off + count) > PCILYNX_MAX_MEMORY + 1) { count = PCILYNX_MAX_MEMORY + 1 - off; } if (count == 0) { return 0; } switch (md->type) { case rom: membase = md->lynx->local_rom; break; case ram: membase = md->lynx->local_ram; break; case aux: membase = md->lynx->aux_port; break; default: panic("pcilynx%d: unsupported md->type %d in " __FUNCTION__, md->lynx->id, md->type); } down(&md->lynx->mem_dma_mutex); if (count < mem_mindma) { memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, count); goto out; } bcount = count; alignfix = 4 - (off % 4); if (alignfix != 4) { if (bcount < alignfix) { alignfix = bcount; } memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, alignfix); if (bcount == alignfix) { goto out; } bcount -= alignfix; off += alignfix; } while (bcount >= 4) { retval = mem_dmaread(md, md->lynx->mem_dma_buffer_dma + count - bcount, bcount, off); if (retval < 0) return retval; bcount -= retval; off += retval; } if (bcount) { memcpy_fromio(md->lynx->mem_dma_buffer + count - bcount, membase+off, bcount); } out: retval = copy_to_user(buffer, md->lynx->mem_dma_buffer, count); up(&md->lynx->mem_dma_mutex); if (retval < 0) return retval; *offset += count; return count;}static ssize_t mem_write(struct file *file, const char *buffer, size_t count, loff_t *offset){ struct memdata *md = (struct memdata *)file->private_data; if (((*offset) + count) > PCILYNX_MAX_MEMORY+1) { count = PCILYNX_MAX_MEMORY+1 - *offset; } if (count == 0 || *offset > PCILYNX_MAX_MEMORY) { return -ENOSPC; } /* FIXME: dereferencing pointers to PCI mem doesn't work everywhere */ switch (md->type) { case aux: copy_from_user(md->lynx->aux_port+(*offset), buffer, count); break; case ram: copy_from_user(md->lynx->local_ram+(*offset), buffer, count); break; case rom: /* the ROM may be writeable */ copy_from_user(md->lynx->local_rom+(*offset), buffer, count); break; } file->f_pos += count; return count;}#endif /* CONFIG_IEEE1394_PCILYNX_PORTS *//******************************************************** * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/static void lynx_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused){ struct ti_lynx *lynx = (struct ti_lynx *)dev_id; struct hpsb_host *host = lynx->host; u32 intmask; u32 linkint; linkint = reg_read(lynx, LINK_INT_STATUS); intmask = reg_read(lynx, PCI_INT_STATUS); PRINTD(KERN_DEBUG, lynx->id, "interrupt: 0x%08x / 0x%08x", intmask, linkint); if (!(intmask & PCI_INT_INT_PEND)) return; reg_write(lynx, LINK_INT_STATUS, linkint); reg_write(lynx, PCI_INT_STATUS, intmask);#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (intmask & PCI_INT_AUX_INT) { atomic_inc(&lynx->aux_intr_seen); wake_up_interruptible(&lynx->aux_intr_wait); } if (intmask & PCI_INT_DMA_HLT(CHANNEL_LOCALBUS)) { wake_up_interruptible(&lynx->mem_dma_intr_wait); }#endif if (intmask & PCI_INT_1394) { if (linkint & LINK_INT_PHY_TIMEOUT) { PRINT(KERN_INFO, lynx->id, "PHY timeout occurred"); } if (linkint & LINK_INT_PHY_BUSRESET) { PRINT(KERN_INFO, lynx->id, "bus reset interrupt"); lynx->selfid_size = -1; lynx->phy_reg0 = -1; if (!host->in_bus_reset) hpsb_bus_reset(host); } if (linkint & LINK_INT_PHY_REG_RCVD) { u32 reg; spin_lock(&lynx->phy_reg_lock); reg = reg_read(lynx, LINK_PHY); spin_unlock(&lynx->phy_reg_lock); if (!host->in_bus_reset) { PRINT(KERN_INFO, lynx->id, "phy reg received without reset"); } else if (reg & 0xf00) { PRINT(KERN_INFO, lynx->id, "unsolicited phy reg %d received", (reg >> 8) & 0xf); } else { lynx->phy_reg0 = reg & 0xff; handle_selfid(lynx, host); } } if (linkint & LINK_INT_ISO_STUCK) { PRINT(KERN_INFO, lynx->id, "isochronous transmitter stuck"); } if (linkint & LINK_INT_ASYNC_STUCK) { PRINT(KERN_INFO, lynx->id, "asynchronous transmitter stuck"); } if (linkint & LINK_INT_SENT_REJECT) { PRINT(KERN_INFO, lynx->id, "sent reject"); } if (linkint & LINK_INT_TX_INVALID_TC) { PRINT(KERN_INFO, lynx->id, "invalid transaction code"); } if (linkint & LINK_INT_GRF_OVERFLOW) { /* flush FIFO if overflow happens during reset */ if (host->in_bus_reset) reg_write(lynx, FIFO_CONTROL, FIFO_CONTROL_GRF_FLUSH); PRINT(KERN_INFO, lynx->id, "GRF overflow"); } if (linkint & LINK_INT_ITF_UNDERFLOW) { PRINT(KERN_INFO, lynx->id, "ITF underflow"); } if (linkint & LINK_INT_ATF_UNDERFLOW) { PRINT(KERN_INFO, lynx->id, "ATF underflow"); } } if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_RCV)) { PRINTD(KERN_DEBUG, lynx->id, "iso receive"); spin_lock(&lynx->iso_rcv.lock); lynx->iso_rcv.stat[lynx->iso_rcv.next] = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ISO_RCV)); lynx->iso_rcv.used++; lynx->iso_rcv.next = (lynx->iso_rcv.next + 1) % NUM_ISORCV_PCL; if ((lynx->iso_rcv.next == lynx->iso_rcv.last) || !lynx->iso_rcv.chan_count) { PRINTD(KERN_DEBUG, lynx->id, "stopped"); reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); } run_sub_pcl(lynx, lynx->iso_rcv.pcl_start, lynx->iso_rcv.next, CHANNEL_ISO_RCV); spin_unlock(&lynx->iso_rcv.lock); tasklet_schedule(&lynx->iso_rcv.tq); } if (intmask & PCI_INT_DMA_HLT(CHANNEL_ASYNC_SEND)) { u32 ack; struct hpsb_packet *packet; spin_lock(&lynx->async.queue_lock); ack = reg_read(lynx, DMA_CHAN_STAT(CHANNEL_ASYNC_SEND)); packet = lynx->async.queue; lynx->async.queue = packet->xnext; pci_unmap_single(lynx->dev, lynx->async.header_dma, packet->header_size, PCI_DMA_TODEVICE); if (packet->data_size) { pci_unmap_single(lynx->dev, lynx->async.data_dma, packet->data_size, PCI_DMA_TODEVICE); } if (lynx->async.queue != NULL) { send_next(lynx, hpsb_async); } spin_unlock(&lynx->async.queue_lock); if (ack & DMA_CHAN_STAT_SPECIALACK) { ack = (ack >> 15) & 0xf; PRINTD(KERN_INFO, lynx->id, "special ack %d", ack); ack = (ack == 1 ? ACKX_TIMEOUT : ACKX_SEND_ERROR); } else { ack = (ack >> 15) & 0xf; } hpsb_packet_sent(host, packet, ack); } if (intmask & PCI_INT_DMA_HLT(CHANNEL_ISO_SEND)) { struct hpsb_packet *packet; spin_lock(&lynx->iso_send.queue_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -