📄 ohci1394.c
字号:
if (!(ohci->ISO_channel_usage & mask)) { PRINT(KERN_ERR, ohci->id, __FUNCTION__ "IS0_UNLISTEN_CHANNEL channel %d not used", arg); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); return -EFAULT; } ohci->ISO_channel_usage &= ~mask; if (arg>31) reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 1<<(arg-32)); else reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 1<<arg); spin_unlock_irqrestore(&ohci->IR_channel_lock, flags); DBGMSG(ohci->id, "listening disabled on channel %d", arg); break; } default: PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet", cmd); break; } return retval;}/*************************************** * IEEE-1394 functionality section END * ***************************************//******************************************************** * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/static void dma_trm_reset(struct dma_trm_ctx *d){ struct ti_ohci *ohci; unsigned long flags; struct hpsb_packet *nextpacket; if (d==NULL) { PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg"); return; } ohci = (struct ti_ohci *)(d->ohci); ohci1394_stop_context(ohci, d->ctrlClear, NULL); spin_lock_irqsave(&d->lock,flags); /* is there still any packet pending in the fifo ? */ while(d->fifo_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); nextpacket = d->fifo_first->xnext; hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED); d->fifo_first = nextpacket; } d->fifo_first = d->fifo_last = NULL; /* is there still any packet pending ? */ while(d->pending_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); nextpacket = d->pending_first->xnext; hpsb_packet_sent(ohci->host, d->pending_first, ACKX_ABORTED); d->pending_first = nextpacket; } d->pending_first = d->pending_last = NULL; d->branchAddrPtr=NULL; d->sent_ind = d->prg_ind; d->free_prgs = d->num_desc; spin_unlock_irqrestore(&d->lock,flags);}static void ohci_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused){ quadlet_t event,node_id; struct ti_ohci *ohci = (struct ti_ohci *)dev_id; struct hpsb_host *host = ohci->host; int phyid = -1, isroot = 0; int timeout = 255; do { /* read the interrupt event register */ event=reg_read(ohci, OHCI1394_IntEventClear); if (!event) return; DBGMSG(ohci->id, "IntEvent: %08x",event); /* clear the interrupt event register */ reg_write(ohci, OHCI1394_IntEventClear, event); if (event & OHCI1394_busReset) { if (!host->in_bus_reset) { PRINT(KERN_INFO, ohci->id, "Bus reset"); /* Wait for the AT fifo to be flushed */ dma_trm_reset(ohci->at_req_context); dma_trm_reset(ohci->at_resp_context); /* Subsystem call */ hpsb_bus_reset(ohci->host); ohci->NumBusResets++; } } /* * Problem: How can I ensure that the AT bottom half will be * executed before the AR bottom half (both events may have * occured within a single irq event) * Quick hack: just launch it within the IRQ handler */ if (event & OHCI1394_reqTxComplete) { struct dma_trm_ctx *d = ohci->at_req_context; DBGMSG(ohci->id, "Got reqTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "reqTxComplete"); else dma_trm_bh((void *)d); } if (event & OHCI1394_respTxComplete) { struct dma_trm_ctx *d = ohci->at_resp_context; DBGMSG(ohci->id, "Got respTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "respTxComplete"); else dma_trm_bh((void *)d); } if (event & OHCI1394_RQPkt) { struct dma_rcv_ctx *d = ohci->ar_req_context; DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); else {#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH);#else dma_rcv_bh((void *)d);#endif } } if (event & OHCI1394_RSPkt) { struct dma_rcv_ctx *d = ohci->ar_resp_context; DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); else {#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH);#else dma_rcv_bh((void *)d);#endif } } if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; struct dma_rcv_ctx *d = ohci->ir_context; isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, isoRecvIntEvent); DBGMSG(ohci->id, "Got isochRx interrupt " "status=0x%08X isoRecvIntEvent=%08x", reg_read(ohci, d->ctrlSet), isoRecvIntEvent); if (isoRecvIntEvent & 0x1) { if (reg_read(ohci, d->ctrlSet) & 0x800) ohci1394_stop_context(ohci, d->ctrlClear, "isochRx"); else {#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH);#else dma_rcv_bh((void *)d);#endif } } if (ohci->video_tmpl) ohci->video_tmpl->irq_handler(ohci->id, isoRecvIntEvent, 0); } if (event & OHCI1394_isochTx) { quadlet_t isoXmitIntEvent; isoXmitIntEvent = reg_read(ohci, OHCI1394_IsoXmitIntEventSet); reg_write(ohci, OHCI1394_IsoXmitIntEventClear, isoXmitIntEvent); DBGMSG(ohci->id, "Got isochTx interrupt"); if (ohci->video_tmpl) ohci->video_tmpl->irq_handler(ohci->id, 0, isoXmitIntEvent); } if (event & OHCI1394_selfIDComplete) { if (host->in_bus_reset) { /* * Begin Fix (JSG): Check to make sure our * node id is valid */ node_id = reg_read(ohci, OHCI1394_NodeID); if (!(node_id & 0x80000000)) { mdelay(1); /* phy is upset - * this happens once in * a while on hot-plugs... * give it a ms to recover */ } /* End Fix (JSG) */ node_id = reg_read(ohci, OHCI1394_NodeID); if (node_id & 0x80000000) { /* NodeID valid */ phyid = node_id & 0x0000003f; isroot = (node_id & 0x40000000) != 0; PRINT(KERN_INFO, ohci->id, "SelfID process finished " "(phyid %d, %s)", phyid, (isroot ? "root" : "not root")); handle_selfid(ohci, host, phyid, isroot); } else PRINT(KERN_ERR, ohci->id, "SelfID process finished but " "NodeID not valid: %08X", node_id); /* Accept Physical requests from all nodes. */ reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff); reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff); /* * Tip by James Goodwin <jamesg@Filanet.com> * Turn on phys dma reception. We should * probably manage the filtering somehow, * instead of blindly turning it on. */ reg_write(ohci,OHCI1394_PhyReqFilterHiSet, 0xffffffff); reg_write(ohci,OHCI1394_PhyReqFilterLoSet, 0xffffffff); reg_write(ohci,OHCI1394_PhyUpperBound, 0xffff0000); } else PRINT(KERN_ERR, ohci->id, "self-id received outside of bus reset" "sequence"); } if (event & OHCI1394_phyRegRcvd) {#if 1 if (host->in_bus_reset) { PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", reg_read(ohci, OHCI1394_PhyControl)); } else PRINT(KERN_ERR, ohci->id, "phy reg received outside of bus reset" "sequence");#endif } } while (--timeout); PRINT(KERN_ERR, ohci->id, "irq_handler timeout event=0x%08x", event);}/* Put the buffer back into the dma context */static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx){ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); DBGMSG(ohci->id, "Inserting dma buf ctx=%d idx=%d", d->ctx, idx); d->prg_cpu[idx]->status = d->buf_size; d->prg_cpu[idx]->branchAddress &= 0xfffffff0; idx = (idx + d->num_desc - 1 ) % d->num_desc; d->prg_cpu[idx]->branchAddress |= 0x1; /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { PRINT(KERN_INFO, ohci->id, "Waking dma cxt=%d ... processing is probably too slow", d->ctx); reg_write(ohci, d->ctrlSet, 0x1000); }} static int block_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr, int offset){ int length=0; /* Where is the data length ? */ if (offset+12>=d->buf_size) length = (d->buf_cpu[(idx+1)%d->num_desc] [3-(d->buf_size-offset)/4]>>16); else length = (buf_ptr[3]>>16); if (length % 4) length += 4 - (length % 4); return length;}const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0, -1, 0, -1, 0, -1, -1, 16, -1};/* * Determine the length of a packet in the buffer * Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca> */static int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr,int offset){ unsigned char tcode; int length = -1; /* Let's see what kind of packet is in there */ tcode = (buf_ptr[0] >> 4) & 0xf; if (d->ctx < 2) { /* Async Receive Response/Request */ length = TCODE_SIZE[tcode]; if (length == 0) length = block_length(d, idx, buf_ptr, offset) + 20; } else if (d->ctx==2) { /* Iso receive */ /* Assumption: buffer fill mode with header/trailer */ length = (buf_ptr[0]>>16); if (length % 4) length += 4 - (length % 4); length+=8; } return length;}/* Bottom half that processes dma receive buffers */static void dma_rcv_bh(void *data){ struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data; struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); unsigned int split_left, idx, offset, rescount; unsigned char tcode; int length, bytes_left, ack; quadlet_t *buf_ptr; char *split_ptr; char msg[256]; spin_lock(&d->lock); idx = d->buf_ind; offset = d->buf_offset; buf_ptr = d->buf_cpu[idx] + offset/4; rescount = d->prg_cpu[idx]->status&0xffff; bytes_left = d->buf_size - rescount - offset; while (bytes_left>0) { tcode = (buf_ptr[0]>>4)&0xf; length = packet_length(d, idx, buf_ptr, offset); if (length<4) { /* something is wrong */ sprintf(msg,"unexpected tcode 0x%X in AR ctx=%d", tcode, d->ctx); ohci1394_stop_context(ohci, d->ctrlClear, msg); spin_unlock(&d->lock); return; } if ((offset+length)>d->buf_size) { /* Split packet */ if (length>d->split_buf_size) { ohci1394_stop_context(ohci, d->ctrlClear, "split packet size exceeded"); d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); return; } if (d->prg_cpu[(idx+1)%d->num_desc]->status ==d->buf_size) { /* other part of packet not written yet */ /* this should never happen I think */ /* anyway we'll get it on the next call */ PRINT(KERN_INFO, ohci->id, "Got only half a packet !!!"); d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); return; } split_left = length; split_ptr = (char *)d->spb; memcpy(split_ptr,buf_ptr,d->buf_size-offset); split_left -= d->buf_size-offset; split_ptr += d->buf_size-offset; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; buf_ptr = d->buf_cpu[idx]; offset=0; while (split_left >= d->buf_size) { memcpy(split_ptr,buf_ptr,d->buf_size); split_ptr += d->buf_size; split_left -= d->buf_size; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; buf_ptr = d->buf_cpu[idx]; } if (split_left>0) { memcpy(split_ptr, buf_ptr, split_left); offset = split_left; buf_ptr += offset/4; } /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may * have changed. Just ignore it for the moment */ if (tcode != 0xE) { DBGMSG(ohci->id, "Split packet received from" " node %d ack=0x%02X spd=%d tcode=0x%X" " length=%d data=0x%08x ctx=%d", (d->spb[1]>>16)&0x3f, (d->spb[length/4-1]>>16)&0x1f, (d->spb[length/4-1]>>21)&0x3, tcode, length, d->spb[3], d->ctx); ack = (((d->spb[length/4-1]>>16)&0x1f) == 0x11) ? 1 : 0; hpsb_packet_received(ohci->host, d->spb, length, ack); } else PRINT(KERN_INFO, ohci->id, "Got phy packet ctx=%d ... discarded", d->ctx); } else { /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may * have changed. Just ignore it for the moment */ if (tcode != 0xE) { DBGMSG(ohci->id, "Packet received from node" " %d ack=0x%02X spd=%d tcode=0x%X" " length=%d data=0x%08x ctx=%d", (buf_ptr[1]>>16)&0x3f, (buf_ptr[length/4-1]>>16)&0x1f, (buf_ptr[length/4-1]>>21)&0x3, tcode, length, buf_ptr[3], d->ctx); ack = (((buf_ptr[length/4-1]>>16)&0x1f)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -