📄 pcilynx.c
字号:
q += 2; size -= 8; } if (!lynx->phyic.reg_1394a && isroot && phyid != 0) { hpsb_selfid_received(host, lsid); } hpsb_selfid_complete(host, phyid, isroot); if (host->in_bus_reset) return; /* in bus reset again */ if (isroot) reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_RCV_CMP_VALID | LINK_CONTROL_TX_ASYNC_EN | LINK_CONTROL_RX_ASYNC_EN | LINK_CONTROL_CYCTIMEREN);}/* This must be called with the respective queue_lock held. */static void send_next(struct ti_lynx *lynx, int what){ struct ti_pcl pcl; struct lynx_send_data *d; struct hpsb_packet *packet; d = (what == hpsb_iso ? &lynx->iso_send : &lynx->async); packet = driver_packet(d->queue.next); d->header_dma = pci_map_single(lynx->dev, packet->header, packet->header_size, PCI_DMA_TODEVICE); if (packet->data_size) { d->data_dma = pci_map_single(lynx->dev, packet->data, packet->data_size, PCI_DMA_TODEVICE); } else { d->data_dma = 0; } pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID;#ifdef __BIG_ENDIAN pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size;#else pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size | PCL_BIGENDIAN;#endif pcl.buffer[0].pointer = d->header_dma; pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; pcl.buffer[1].pointer = d->data_dma; switch (packet->type) { case hpsb_async: pcl.buffer[0].control |= PCL_CMD_XMT; break; case hpsb_iso: pcl.buffer[0].control |= PCL_CMD_XMT | PCL_ISOMODE; break; case hpsb_raw: pcl.buffer[0].control |= PCL_CMD_UNFXMT; break; } if (!packet->data_be) { pcl.buffer[1].control |= PCL_BIGENDIAN; } put_pcl(lynx, d->pcl, &pcl); run_pcl(lynx, d->pcl_start, d->channel);}/* called from subsystem core */static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet){ struct ti_lynx *lynx = host->hostdata; struct lynx_send_data *d; unsigned long flags; if (packet->data_size >= 4096) { PRINT(KERN_ERR, lynx->id, "transmit packet data too big (%Zd)", packet->data_size); return 0; } switch (packet->type) { case hpsb_async: case hpsb_raw: d = &lynx->async; break; case hpsb_iso: d = &lynx->iso_send; break; default: PRINT(KERN_ERR, lynx->id, "invalid packet type %d", packet->type); return 0; } if (packet->tcode == TCODE_WRITEQ || packet->tcode == TCODE_READQ_RESPONSE) { cpu_to_be32s(&packet->header[3]); } spin_lock_irqsave(&d->queue_lock, flags); list_add_tail(&packet->driver_list, &d->queue); if (d->queue.next == &packet->driver_list) send_next(lynx, packet->type); spin_unlock_irqrestore(&d->queue_lock, flags); return 1;}/* called from subsystem core */static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg){ struct ti_lynx *lynx = host->hostdata; int retval = 0; struct hpsb_packet *packet; LIST_HEAD(packet_list); unsigned long flags; switch (cmd) { case RESET_BUS: if (reg_read(lynx, LINK_INT_STATUS) & LINK_INT_PHY_BUSRESET) { retval = 0; break; } if (arg) { arg = 3 << 6; } else { arg = 1 << 6; } retval = get_phy_reg(lynx, 1); arg |= (retval == -1 ? 63 : retval); retval = 0; PRINT(KERN_INFO, lynx->id, "resetting bus on request"); lynx->selfid_size = -1; lynx->phy_reg0 = -1; set_phy_reg(lynx, 1, arg); break; case GET_CYCLE_COUNTER: retval = reg_read(lynx, CYCLE_TIMER); break; case SET_CYCLE_COUNTER: reg_write(lynx, CYCLE_TIMER, arg); break; case SET_BUS_ID: reg_write(lynx, LINK_ID, (arg << 22) | (reg_read(lynx, LINK_ID) & 0x003f0000)); break; case ACT_CYCLE_MASTER: if (arg) { reg_set_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); } else { reg_clear_bits(lynx, LINK_CONTROL, LINK_CONTROL_CYCMASTER); } break; case CANCEL_REQUESTS: spin_lock_irqsave(&lynx->async.queue_lock, flags); reg_write(lynx, DMA_CHAN_CTRL(CHANNEL_ASYNC_SEND), 0); list_splice(&lynx->async.queue, &packet_list); INIT_LIST_HEAD(&lynx->async.queue); spin_unlock_irqrestore(&lynx->async.queue_lock, flags); while (!list_empty(&packet_list)) { packet = driver_packet(packet_list.next); list_del(&packet->driver_list); hpsb_packet_sent(host, packet, ACKX_ABORTED); } break; case MODIFY_USAGE: if (arg) { MOD_INC_USE_COUNT; } else { MOD_DEC_USE_COUNT; } retval = 1; break; case ISO_LISTEN_CHANNEL: spin_lock_irqsave(&lynx->iso_rcv.lock, flags); if (lynx->iso_rcv.chan_count++ == 0) { reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), DMA_WORD1_CMP_ENABLE_MASTER); } spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); break; case ISO_UNLISTEN_CHANNEL: spin_lock_irqsave(&lynx->iso_rcv.lock, flags); if (--lynx->iso_rcv.chan_count == 0) { reg_write(lynx, DMA_WORD1_CMP_ENABLE(CHANNEL_ISO_RCV), 0); } spin_unlock_irqrestore(&lynx->iso_rcv.lock, flags); break; default: PRINT(KERN_ERR, lynx->id, "unknown devctl command %d", cmd); retval = -1; } return retval;}/*************************************** * IEEE-1394 functionality section END * ***************************************/#ifdef CONFIG_IEEE1394_PCILYNX_PORTS/* VFS functions for local bus / aux device access. Access to those * is implemented as a character device instead of block devices * because buffers are not wanted for this. Therefore llseek (from * VFS) can be used for these char devices with obvious effects. */static int mem_open(struct inode*, struct file*);static int mem_release(struct inode*, struct file*);static unsigned int aux_poll(struct file*, struct poll_table_struct*);static loff_t mem_llseek(struct file*, loff_t, int);static ssize_t mem_read (struct file*, char*, size_t, loff_t*);static ssize_t mem_write(struct file*, const char*, size_t, loff_t*);static struct file_operations aux_ops = { .owner = THIS_MODULE, .read = mem_read, .write = mem_write, .poll = aux_poll, .llseek = mem_llseek, .open = mem_open, .release = mem_release,};static void aux_setup_pcls(struct ti_lynx *lynx){ struct ti_pcl pcl; pcl.next = PCL_NEXT_INVALID; pcl.user_data = pcl_bus(lynx, lynx->dmem_pcl); put_pcl(lynx, lynx->dmem_pcl, &pcl);}static int mem_open(struct inode *inode, struct file *file){ int cid = minor(inode->i_rdev); enum { t_rom, t_aux, t_ram } type; struct memdata *md; if (cid < PCILYNX_MINOR_AUX_START) { /* just for completeness */ return -ENXIO; } else if (cid < PCILYNX_MINOR_ROM_START) { cid -= PCILYNX_MINOR_AUX_START; if (cid >= num_of_cards || !cards[cid].aux_port) return -ENXIO; type = t_aux; } else if (cid < PCILYNX_MINOR_RAM_START) { cid -= PCILYNX_MINOR_ROM_START; if (cid >= num_of_cards || !cards[cid].local_rom) return -ENXIO; type = t_rom; } else { /* WARNING: Know what you are doing when opening RAM. * It is currently used inside the driver! */ cid -= PCILYNX_MINOR_RAM_START; if (cid >= num_of_cards || !cards[cid].local_ram) return -ENXIO; type = t_ram; } md = (struct memdata *)kmalloc(sizeof(struct memdata), SLAB_KERNEL); if (md == NULL) 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){ kfree(file->private_data); 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!"); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -