Thread: [Linux1394-cvslog] rev 729 - trunk
Brought to you by:
aeb,
bencollins
From: SVN U. <dm...@li...> - 2003-02-01 13:59:34
|
Author: dmaas Date: 2002-12-23 21:58:38 -0500 (Mon, 23 Dec 2002) New Revision: 729 Modified: trunk/dma.c trunk/hosts.h trunk/ieee1394_core.c trunk/iso.c trunk/iso.h trunk/ohci1394.c trunk/ohci1394.h trunk/raw1394.c trunk/raw1394.h Log: dmaas - large isochronous update: - switched ohci from packet-per-buffer to buffer-fill receive (necessary to enable multi-channel reception and contiguous batch reception) - added multichannel ops to iso.[ch] and raw1394 - changed iso packet buffer layout - packet descriptors are no longer embedded in buffer, instead they are passed to and from the kernel in raw1394 ioctls (XMIT/RECV_PACKETS) [ this breaks the kernel<->libraw interface, you will need to get the latest libraw code from SVN for iso to work ] - implement multi-channel reception in ohci. Note that you won't be able to use it yet, since the legacy ISO API claims the single multi-channel context for its exclusive use. (we will need to remove the legacy ISO API to make further progress - I'll bring this up on the list) Modified: trunk/iso.h ============================================================================== --- trunk/iso.h (original) +++ trunk/iso.h 2003-02-01 08:58:58.000000000 -0500 @@ -17,25 +17,37 @@ /* high-level ISO interface */ -/* per-packet data embedded in the ringbuffer */ +/* This API sends and receives isochronous packets on a large, + virtually-contiguous kernel memory buffer. The buffer may be mapped + into a user-space process for zero-copy transmission and reception. + + There are no explicit boundaries between packets in the buffer. A + packet may be transmitted or received at any location. However, + low-level drivers may impose certain restrictions on alignment or + size of packets. (e.g. in OHCI no packet may cross a page boundary, + and packets should be quadlet-aligned) +*/ + +/* Packet descriptor - the API maintains a ring buffer of these packet + descriptors in kernel memory (hpsb_iso.infos[]). */ + struct hpsb_iso_packet_info { - unsigned short len; - unsigned short cycle; - unsigned char channel; /* recv only */ - unsigned char tag; - unsigned char sy; -}; + /* offset of data payload relative to the first byte of the buffer */ + __u32 offset; + + /* length of the data payload, in bytes (not including the isochronous header) */ + __u16 len; -/* - * each packet in the ringbuffer consists of three things: - * 1. the packet's data payload (no isochronous header) - * 2. a struct hpsb_iso_packet_info - * 3. some empty space before the next packet - * - * packets are separated by hpsb_iso.buf_stride bytes - * an even number of packets fit on one page - * no packet can be larger than one page - */ + /* (recv only) the cycle number (mod 8000) on which the packet was received */ + __u16 cycle; + + /* (recv only) channel on which the packet was received */ + __u8 channel; + + /* 2-bit 'tag' and 4-bit 'sy' fields of the isochronous header */ + __u8 tag; + __u8 sy; +}; enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 }; @@ -45,48 +57,42 @@ /* pointer to low-level driver and its private data */ struct hpsb_host *host; void *hostdata; - - /* function to be called (from interrupt context) when the iso status changes */ + + /* a function to be called (from interrupt context) after + outgoing packets have been sent, or incoming packets have + arrived */ void (*callback)(struct hpsb_iso*); int speed; /* SPEED_100, 200, or 400 */ - int channel; + int channel; /* -1 if multichannel */ /* greatest # of packets between interrupts - controls the maximum latency of the buffer */ int irq_interval; - - /* the packet ringbuffer */ - struct dma_region buf; + + /* the buffer for packet data payloads */ + struct dma_region data_buf; + + /* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */ + unsigned int buf_size; + + /* ringbuffer of packet descriptors in regular kernel memory */ + struct hpsb_iso_packet_info *infos; /* # of packets in the ringbuffer */ unsigned int buf_packets; - /* offset between successive packets, in bytes - - you can assume that this is a power of 2, - and less than or equal to the page size */ - int buf_stride; - - /* largest possible packet size, in bytes */ - unsigned int max_packet_size; - - /* offset relative to (buf.kvirt + N*buf_stride) at which - the data payload begins for packet N */ - int packet_data_offset; - - /* offset relative to (buf.kvirt + N*buf_stride) at which the - struct hpsb_iso_packet_info is stored for packet N */ - int packet_info_offset; + /* protects packet cursors */ + spinlock_t lock; /* the index of the next packet that will be produced or consumed by the user */ int first_packet; - /* number of packets owned by the low-level driver and - queued for transmission or reception. - this is related to the number of packets available - to the user process: n_ready = buf_packets - n_dma_packets */ - atomic_t n_dma_packets; + /* how many packets, starting at first_packet: + (transmit) are ready to be filled with data + (receive) contain received data */ + int n_ready_packets; /* how many times the buffer has overflowed or underflowed */ atomic_t overflows; @@ -99,8 +105,12 @@ /* # of packets left to prebuffer (xmit only) */ int prebuffer; - /* starting cycle (xmit only) */ + /* starting cycle for DMA (xmit only) */ int start_cycle; + + /* cycle at which next packet will be transmitted, + -1 if not known */ + int xmit_cycle; }; /* functions available to high-level drivers (e.g. raw1394) */ @@ -108,20 +118,26 @@ /* allocate the buffer and DMA context */ struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, + unsigned int data_buf_size, unsigned int buf_packets, - unsigned int max_packet_size, int channel, int speed, int irq_interval, void (*callback)(struct hpsb_iso*)); +/* note: if channel = -1, multi-channel receive is enabled */ struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, + unsigned int data_buf_size, unsigned int buf_packets, - unsigned int max_packet_size, int channel, int irq_interval, void (*callback)(struct hpsb_iso*)); +/* multi-channel only */ +int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); +int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); +int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); + /* start/stop DMA */ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer); int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle); @@ -139,10 +155,10 @@ /* returns # of packets ready to send or receive */ int hpsb_iso_n_ready(struct hpsb_iso *iso); -/* returns a pointer to the payload of packet 'pkt' */ -unsigned char* hpsb_iso_packet_data(struct hpsb_iso *iso, unsigned int pkt); - -/* returns a pointer to the info struct of packet 'pkt' */ -struct hpsb_iso_packet_info* hpsb_iso_packet_info(struct hpsb_iso *iso, unsigned int pkt); +/* check to make sure the user has not supplied bogus offset/len values that would + cause the kernel to access memory outside the buffer. Writes the sanitized values + to *offset and *len. Returns 0 if OK, otherwise -EFAULT or -EINVAL. */ +int hpsb_iso_check_offset_len(struct hpsb_iso *iso, struct hpsb_iso_packet_info *info, + unsigned int *out_offset, unsigned short *out_len); #endif /* IEEE1394_ISO_H */ Modified: trunk/ieee1394_core.c ============================================================================== --- trunk/ieee1394_core.c (original) +++ trunk/ieee1394_core.c 2003-02-01 08:58:58.000000000 -0500 @@ -1000,7 +1000,7 @@ struct file_operations *file_ops) { int retval; - + if( (blocknum < 0) || (blocknum > 15) ) return -EINVAL; @@ -1016,7 +1016,7 @@ /* block already taken */ retval = -EBUSY; } - + write_unlock(&ieee1394_chardevs_lock); return retval; @@ -1027,14 +1027,14 @@ { if( (blocknum < 0) || (blocknum > 15) ) return; - + write_lock(&ieee1394_chardevs_lock); - + if(ieee1394_chardevs[blocknum].file_ops) { ieee1394_chardevs[blocknum].file_ops = NULL; ieee1394_chardevs[blocknum].module = NULL; } - + write_unlock(&ieee1394_chardevs_lock); } @@ -1072,7 +1072,7 @@ /* success! */ ret = 1; - + out: read_unlock(&ieee1394_chardevs_lock); return ret; @@ -1102,11 +1102,11 @@ reference count of whatever module file->f_op->owner points to, immediately after this function returns. */ - + /* shift away lower four bits of the minor to get the index of the ieee1394_driver we want */ - + blocknum = (minor(inode->i_rdev) >> 4) & 0xF; /* look up the driver */ @@ -1151,7 +1151,7 @@ if(module) __MOD_DEC_USE_COUNT(module); - + /* point the file's f_ops back to ieee1394. The VFS will then decrement ieee1394's reference count immediately after this function returns. */ @@ -1209,10 +1209,10 @@ kmem_cache_destroy(hpsb_packet_cache); unregister_chrdev(IEEE1394_MAJOR, "ieee1394"); - + /* it's ok to pass a NULL devfs_handle to devfs_unregister */ devfs_unregister(ieee1394_devfs_handle); - + remove_proc_entry("ieee1394", proc_bus); } @@ -1308,10 +1308,12 @@ EXPORT_SYMBOL(hpsb_iso_recv_init); EXPORT_SYMBOL(hpsb_iso_xmit_start); EXPORT_SYMBOL(hpsb_iso_recv_start); +EXPORT_SYMBOL(hpsb_iso_recv_listen_channel); +EXPORT_SYMBOL(hpsb_iso_recv_unlisten_channel); +EXPORT_SYMBOL(hpsb_iso_recv_set_channel_mask); EXPORT_SYMBOL(hpsb_iso_stop); EXPORT_SYMBOL(hpsb_iso_shutdown); EXPORT_SYMBOL(hpsb_iso_xmit_queue_packets); EXPORT_SYMBOL(hpsb_iso_recv_release_packets); EXPORT_SYMBOL(hpsb_iso_n_ready); -EXPORT_SYMBOL(hpsb_iso_packet_data); -EXPORT_SYMBOL(hpsb_iso_packet_info); +EXPORT_SYMBOL(hpsb_iso_check_offset_len); Modified: trunk/ohci1394.c ============================================================================== --- trunk/ohci1394.c (original) +++ trunk/ohci1394.c 2003-02-01 08:58:59.000000000 -0500 @@ -311,7 +311,7 @@ /* SelfID Ok, reset error counter. */ ohci->self_id_errors = 0; - + size = ((self_id_count & 0x00001FFC) >> 2) - 1; q++; @@ -373,12 +373,12 @@ "Running dma failed because Node ID == 63"); return -1; } - + /* Run the dma context */ reg_write(ohci, reg, 0x8000); - + if (msg) PRINT(KERN_DEBUG, ohci->id, "%s", msg); - + return 0; } @@ -450,7 +450,7 @@ reg_write(ohci, reg, 0xffffffff); tmp = reg_read(ohci, reg); - + DBGMSG(ohci->id,"Iso contexts reg: %08x implemented: %08x", reg, tmp); /* Count the number of contexts */ @@ -829,7 +829,7 @@ spin_lock_irqsave(&d->lock,flags); list_add_tail(&packet->driver_list, &d->pending_list); - + dma_trm_flush(ohci, d); spin_unlock_irqrestore(&d->lock,flags); @@ -898,11 +898,11 @@ case GET_CYCLE_COUNTER: retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer); break; - + case SET_CYCLE_COUNTER: reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg); break; - + case SET_BUS_ID: PRINT(KERN_ERR, ohci->id, "devctl command SET_BUS_ID err"); break; @@ -1024,22 +1024,65 @@ * rawiso ISO reception * ***********************************/ +/* + We use buffer-fill DMA mode. The DMA buffer is split into "blocks" + of one page each. It would be fairly easy to make blocks smaller + than a page, but I think the latency is good enough already. + + There is one little wrinkle with buffer-fill mode: a packet that + starts in the final block may wrap around into the first block. But + the user API expects all packets to be contiguous. Our solution is + to keep the very last page of the DMA buffer in reserve - if a + packet span the gap, we copy its tail into this page. +*/ + struct ohci_iso_recv { struct ti_ohci *ohci; - /* memory and PCI mapping for the DMA descriptors */ - struct dma_prog_region prog; - struct ohci1394_iso_tasklet task; int task_active; - /* index of next packet to arrive */ + /* memory and PCI mapping for the DMA descriptors */ + struct dma_prog_region prog; + struct dma_cmd *block; /* = (struct dma_cmd*) prog.virt */ + + /* how many INPUT_MORE blocks fit in the buffer */ + unsigned int nblocks; + + /* number of blocks to batch between interrupts */ + int block_irq_interval; + + /* block that the reader will release next */ + int block_reader; + + /* bytes of buffer the reader has released, less than one block */ + int released_bytes; + + /* cycle of last-received packet; used to detecting dropouts */ + int last_cycle; + + /* the following four fields are used exclusively by the interrupt + (no locking is required) */ + + /* index into the info array for next packet to appear */ int pkt_dma; - + + /* block that DMA will finish next */ + int block_dma; + + /* buffer offset at which the next packet will appear */ + int dma_offset; + + /* throttle the rate of buffer-space warnings to avoid + spamming the syslog */ + int warn_backoff; + + /* OHCI DMA context control registers */ u32 ContextControlSet; u32 ContextControlClear; u32 CommandPtr; u32 ContextMatch; + }; static void ohci_iso_recv_task(unsigned long data); @@ -1051,34 +1094,68 @@ static int ohci_iso_recv_init(struct hpsb_iso *iso) { + struct ti_ohci *ohci = iso->host->hostdata; struct ohci_iso_recv *recv; - unsigned int prog_size; + unsigned int nblocks; int ctx; int ret = -ENOMEM; - + + /* one block per page of data in the DMA buffer, minus the final guard page */ + nblocks = iso->buf_size/PAGE_SIZE - 1; + if(nblocks < 3) { + DBGMSG(ohci->id, "ohci_iso_recv_init: DMA buffer too small"); + return -EINVAL; + } + recv = kmalloc(sizeof(*recv), SLAB_KERNEL); - if(!recv) + if(!recv) { return -ENOMEM; + } iso->hostdata = recv; - recv->ohci = iso->host->hostdata; + recv->ohci = ohci; recv->task_active = 0; - recv->pkt_dma = iso->first_packet; - dma_prog_region_init(&recv->prog); + recv->block = NULL; + recv->nblocks = nblocks; + +#if 0 + /* DJM - disable for now; irq_interval > 1 results in many overflows... */ + /* iso->irq_interval is in packets - translate that to blocks */ + recv->block_irq_interval = (iso->irq_interval * iso->max_packet_size) / PAGE_SIZE; + if(recv->block_irq_interval < 1) + recv->block_irq_interval = 1; + else if(recv->block_irq_interval*4 > recv->nblocks) + recv->block_irq_interval = nblocks/4; +#else + recv->block_irq_interval = 1; +#endif - /* size of DMA program = one INPUT_LAST per packet in the buffer */ - prog_size = sizeof(struct dma_cmd) * iso->buf_packets; - - if(dma_prog_region_alloc(&recv->prog, prog_size, recv->ohci->dev)) + recv->block_reader = 0; + recv->released_bytes = 0; + recv->last_cycle = -1; + + recv->block_dma = 0; + recv->dma_offset = recv->block_dma * PAGE_SIZE; + recv->pkt_dma = 0; + recv->warn_backoff = 0; + + /* size of DMA program = one INPUT_MORE per block */ + if(dma_prog_region_alloc(&recv->prog, + sizeof(struct dma_cmd) * recv->nblocks, + recv->ohci->dev)) goto err; - ohci1394_init_iso_tasklet(&recv->task, OHCI_ISO_RECEIVE, + recv->block = (struct dma_cmd*) recv->prog.kvirt; + + ohci1394_init_iso_tasklet(&recv->task, + iso->channel == -1 ? OHCI_ISO_MULTICHANNEL_RECEIVE : + OHCI_ISO_RECEIVE, ohci_iso_recv_task, (unsigned long) iso); - + if(ohci1394_register_iso_tasklet(recv->ohci, &recv->task) < 0) goto err; - + recv->task_active = 1; /* recv context registers are spaced 32 bytes apart */ @@ -1088,12 +1165,15 @@ recv->CommandPtr = OHCI1394_IsoRcvCommandPtr + 32 * ctx; recv->ContextMatch = OHCI1394_IsoRcvContextMatch + 32 * ctx; - /* enable interrupts */ - reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskSet, 1 << ctx); - /* write the DMA program */ ohci_iso_recv_program(iso); - + + DBGMSG(ohci->id, "ohci_iso_recv_init: DMA buffer is %lu pages (%u info + %lu data)" + "(%u bytes), using %u blocks, block_irq_interval %d", + iso->buf_size/PAGE_SIZE, iso->info_pages, + iso->buf_size/PAGE_SIZE - iso->info_pages, + iso->buf_size, recv->nblocks, recv->block_irq_interval); + return 0; err: @@ -1107,7 +1187,7 @@ /* disable interrupts */ reg_write(recv->ohci, OHCI1394_IsoRecvIntMaskClear, 1 << recv->task.context); - + /* halt DMA */ ohci1394_stop_context(recv->ohci, recv->ContextControlClear, NULL); } @@ -1121,91 +1201,139 @@ ohci1394_unregister_iso_tasklet(recv->ohci, &recv->task); recv->task_active = 0; } - + dma_prog_region_free(&recv->prog); kfree(recv); iso->hostdata = NULL; } +/* set up a "gapped" ring buffer DMA program */ static void ohci_iso_recv_program(struct hpsb_iso *iso) { struct ohci_iso_recv *recv = iso->hostdata; - + /* address of 'branch' field in previous DMA descriptor */ u32 *prev_branch = NULL; - /* start at pkt_dma and go around the whole buffer */ - int pkt = recv->pkt_dma; + /* start at block_dma and go around the whole buffer */ + int blk = recv->block_dma; int i; - - for(i = 0; i < iso->buf_packets; i++) { - int want_interrupt; - unsigned int data_size; - - /* pointer to the DMA descriptor */ - struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + pkt; - /* offset of the DMA descriptor relative to the DMA prog buffer */ - unsigned long prog_offset = pkt * sizeof(struct dma_cmd); + for(i = 0; i < recv->nblocks; i++) { + /* the DMA descriptor */ + struct dma_cmd *im = &recv->block[blk]; - /* offset of this bus_cycle within the DMA buffer */ - unsigned long buf_offset = hpsb_iso_packet_data(iso, pkt) - iso->buf.kvirt; + /* offset of the DMA descriptor relative to the DMA prog buffer */ + unsigned long prog_offset = blk * sizeof(struct dma_cmd); - /* back up 8 bytes for the iso header */ - buf_offset -= 8; - data_size = iso->max_packet_size + 8; + /* offset of this packet's data within the DMA buffer */ + unsigned long buf_offset = blk*PAGE_SIZE; - /* ask for an interrupt every now and then, and - always interrupt on the final descriptor */ + unsigned short data_size = PAGE_SIZE; - if( ((i % iso->irq_interval) == 0) || - (i == (iso->buf_packets - 1)) ) { - want_interrupt = 1; - } else { - want_interrupt = 0; + im->control = 2 << 28; /* INPUT_MORE */ + im->control |= 8 << 24; /* s = 1, update xferStatus and resCount */ + + /* interrupt on last block, and at intervals */ + if(i == recv->nblocks-1 || (i % recv->block_irq_interval) == 0) { + im->control |= 3 << 20; /* want interrupt */ } - - /* write the DMA descriptor */ - - il->control = 3 << 28; /* INPUT_LAST */ - il->control |= 8 << 24; /* s = 1, update xferStatus and resCount */ - if(want_interrupt) - il->control |= 3 << 20; - il->control |= 0xC << 16; /* enable branch to address */ - il->control |= data_size; - il->address = dma_region_offset_to_bus(&iso->buf, buf_offset); - il->branchAddress = 0; /* filled in on next loop */ - il->status = data_size; + im->control |= 3 << 18; /* enable branch to address */ + im->control |= data_size; + + im->address = dma_region_offset_to_bus(&iso->data_buf, buf_offset); + im->branchAddress = 0; /* filled in on next loop */ + im->status = data_size; /* link the previous descriptor to this one */ if(prev_branch) { - *prev_branch = dma_prog_region_offset_to_bus(&recv->prog, prog_offset); + *prev_branch = dma_prog_region_offset_to_bus(&recv->prog, + prog_offset); *prev_branch |= 1; /* set Z=1 */ } - prev_branch = &il->branchAddress; + prev_branch = &im->branchAddress; - pkt = (pkt + 1) % iso->buf_packets; + blk = (blk + 1) % recv->nblocks; } /* the final descriptor's branch address and Z should be left at 0 */ } +/* listen or unlisten to a specific channel (multi-channel mode only) */ +static void ohci_iso_recv_change_channel(struct hpsb_iso *iso, unsigned char channel, int listen) +{ + struct ohci_iso_recv *recv = iso->hostdata; + int reg, i; + + if(channel < 32) { + reg = listen ? OHCI1394_IRMultiChanMaskLoSet : OHCI1394_IRMultiChanMaskLoClear; + i = channel; + } else { + reg = listen ? OHCI1394_IRMultiChanMaskHiSet : OHCI1394_IRMultiChanMaskHiClear; + i = channel - 32; + } + + reg_write(recv->ohci, reg, (1 << i)); + + /* issue a dummy read to force all PCI writes to be posted immediately */ + mb(); + reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); +} + +static void ohci_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) +{ + struct ohci_iso_recv *recv = iso->hostdata; + int i; + + for(i = 0; i < 64; i++) { + if(mask & (1ULL << i)) { + if(i < 32) + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoSet, (1 << i)); + else + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiSet, (1 << (i-32))); + } else { + if(i < 32) + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, (1 << i)); + else + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, (1 << (i-32))); + } + } + + /* issue a dummy read to force all PCI writes to be posted immediately */ + mb(); + reg_read(recv->ohci, OHCI1394_IsochronousCycleTimer); +} + static int ohci_iso_recv_start(struct hpsb_iso *iso, int cycle) { struct ohci_iso_recv *recv = iso->hostdata; u32 command, contextMatch; - + + recv->last_cycle = -1; + reg_write(recv->ohci, recv->ContextControlClear, 0xFFFFFFFF); wmb(); - /* use packet-per-buffer mode, and keep ISO headers */ - reg_write(recv->ohci, recv->ContextControlSet, (1 << 30)); + /* use buffer-fill mode, and keep ISO headers (required for multichannel) */ + reg_write(recv->ohci, recv->ContextControlSet, (1 << 31) | (1 << 30)); + + /* match on all tags */ + contextMatch = 0xF0000000; + + if(iso->channel == -1) { + /* enable multichannel reception */ + reg_write(recv->ohci, recv->ContextControlSet, (1 << 28)); + + /* clear channel selection mask */ + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskHiClear, 0xFFFFFFFF); + reg_write(recv->ohci, OHCI1394_IRMultiChanMaskLoClear, 0xFFFFFFFF); + } else { + /* listen on channel */ + contextMatch |= iso->channel; + } - /* match on all tags, listen on channel */ - contextMatch = 0xF0000000 | iso->channel; - if(cycle != -1) { u32 seconds; @@ -1230,7 +1358,8 @@ reg_write(recv->ohci, recv->ContextMatch, contextMatch); /* address of first descriptor block */ - command = dma_prog_region_offset_to_bus(&recv->prog, recv->pkt_dma * sizeof(struct dma_cmd)); + command = dma_prog_region_offset_to_bus(&recv->prog, + recv->block_dma * sizeof(struct dma_cmd)); command |= 1; /* Z=1 */ reg_write(recv->ohci, recv->CommandPtr, command); @@ -1250,7 +1379,8 @@ /* check RUN */ if(!(reg_read(recv->ohci, recv->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, recv->ohci->id, "Error starting IR DMA (ContextControl 0x%08x)\n", + PRINT(KERN_ERR, recv->ohci->id, + "Error starting IR DMA (ContextControl 0x%08x)\n", reg_read(recv->ohci, recv->ContextControlSet)); return -1; } @@ -1258,124 +1388,322 @@ return 0; } -static void ohci_iso_recv_release_one(struct hpsb_iso *iso) +static void ohci_iso_recv_check_block_overflow(struct ohci_iso_recv *recv) { - struct ohci_iso_recv *recv = iso->hostdata; + /* see how close we are to running out of blocks */ + int gap = 0; + int blk = recv->block_reader; + while(blk != recv->block_dma) { + blk = (blk+1) % recv->nblocks; + gap++; + } - /* re-use the DMA descriptor for first_packet */ + if(gap*2 > recv->nblocks) { + DBGMSG(recv->ohci->id, + "Running out of DMA blocks for ISO reception - " + "overrun likely (gap = %d)", gap); + } +} + +static void ohci_iso_recv_release_block(struct hpsb_iso *iso, struct ohci_iso_recv *recv) +{ + /* re-use the DMA descriptor for block_reader */ /* by linking the previous descriptor to it */ - int next_i = iso->first_packet; - int prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1); - - struct dma_cmd *next = dma_region_i(&recv->prog, struct dma_cmd, next_i); - struct dma_cmd *prev = dma_region_i(&recv->prog, struct dma_cmd, prev_i); - - /* 'next' becomes the new end of the DMA chain */ - next->control |= 3 << 20; /* enable interrupt */ - next->branchAddress = 0; /* disable branch */ + int next_i = recv->block_reader; + int prev_i = (next_i == 0) ? (recv->nblocks - 1) : (next_i - 1); + + struct dma_cmd *next = &recv->block[next_i]; + struct dma_cmd *prev = &recv->block[prev_i]; + ohci_iso_recv_check_block_overflow(recv); + + /* 'next' becomes the new end of the DMA chain, + so disable branch and enable interrupt */ + + next->branchAddress = 0; + next->control |= 3 << 20; + /* link prev to next */ - if(prev_i % iso->irq_interval) { - prev->control &= ~(3 << 20); /* no interrupt */ - } else { + prev->branchAddress = dma_prog_region_offset_to_bus(&recv->prog, + sizeof(struct dma_cmd) * next_i) + | 1; /* Z=1 */ + + /* disable interrupt on previous DMA descriptor, except at intervals */ + if((prev_i % recv->block_irq_interval) == 0) { prev->control |= 3 << 20; /* enable interrupt */ + } else { + prev->control &= ~(3<<20); /* disable interrupt */ } - prev->branchAddress = dma_prog_region_offset_to_bus(&recv->prog, - sizeof(struct dma_cmd) * next_i) | 1; wmb(); - + /* wake up DMA in case it fell asleep */ reg_write(recv->ohci, recv->ContextControlSet, (1 << 12)); + /* advance block cursor */ + recv->block_reader = (recv->block_reader + 1) % recv->nblocks; +} + +static void ohci_iso_recv_release_one_packet(struct hpsb_iso *iso, struct ohci_iso_recv *recv) +{ + int flags; + int len; + + /* release the memory where the packet was */ + /* note: we can't trust len, but the worst the user + can do is mess up his own received packets */ + + len = iso->infos[iso->first_packet].len; + + /* add the wasted space for padding to 4 bytes */ + if(len % 4) + len += 4 - (len % 4); + + /* add 8 bytes for the OHCI DMA data format overhead */ + len += 8; + + recv->released_bytes += len; + + /* have we released enough memory for one block? */ + while(recv->released_bytes > PAGE_SIZE) { + ohci_iso_recv_release_block(iso, recv); + recv->released_bytes -= PAGE_SIZE; + } + /* advance packet cursors */ iso->first_packet = (iso->first_packet+1) % iso->buf_packets; - atomic_inc(&iso->n_dma_packets); + + spin_lock_irqsave(&iso->lock, flags); + iso->n_ready_packets--; + spin_unlock_irqrestore(&iso->lock, flags); } static void ohci_iso_recv_release(struct hpsb_iso *iso, int n_packets) { int i; for(i = 0; i < n_packets; i++) - ohci_iso_recv_release_one(iso); + ohci_iso_recv_release_one_packet(iso, (struct ohci_iso_recv*) iso->hostdata); } -static void ohci_iso_recv_task(unsigned long data) +/* parse all packets from blocks that have been fully received */ +static void ohci_iso_recv_bytes(struct hpsb_iso *iso, struct ohci_iso_recv *recv) { - struct hpsb_iso *iso = (struct hpsb_iso*) data; - struct ohci_iso_recv *recv = iso->hostdata; - - int count; int wake = 0; - - /* loop over the entire buffer */ - for(count = 0; count < iso->buf_packets; count++) { - u32 packet_len = 0; + int runaway = 0; + + while(1) { + /* we expect the next parsable packet to begin at recv->dma_offset */ + /* note: packet layout is as shown in section 10.6.1.1 of the OHCI spec */ + + unsigned int offset; + unsigned short len, cycle; + unsigned char channel, tag, sy; - /* pointer to the DMA descriptor */ - struct dma_cmd *il = ((struct dma_cmd*) recv->prog.kvirt) + recv->pkt_dma; + unsigned long flags; + unsigned char *p = iso->data_buf.kvirt; - /* check the DMA descriptor for new writes to xferStatus */ - u16 xferstatus = il->status >> 16; - u16 rescount = il->status & 0xFFFF; + unsigned int this_block = recv->dma_offset/PAGE_SIZE; - unsigned char event = xferstatus & 0x1F; + /* don't loop indefinitely */ + if(runaway++ > 100000) { + atomic_inc(&iso->overflows); + PRINT(KERN_ERR, recv->ohci->id, + "IR DMA error - Runaway during buffer parsing!\n"); + break; + } - if(!event) { - /* this packet hasn't come in yet; we are done for now */ - goto out; + /* stop parsing once we arrive at block_dma (i.e. don't get ahead of DMA) */ + if(this_block == recv->block_dma) + break; + + wake = 1; + + /* parse data length, tag, channel, and sy */ + + /* note: we keep our own local copies of 'len' and 'offset' + so the user can't mess with them by poking in the mmap area */ + + len = p[recv->dma_offset+2] | (p[recv->dma_offset+3] << 8); + + if(len > PAGE_SIZE) { + PRINT(KERN_ERR, recv->ohci->id, + "IR DMA error - bogus 'len' value %u\n", len); } - if(event == 0x11) { - /* packet received successfully! */ - - /* rescount is the number of bytes *remaining* in the packet buffer, - after the packet was written */ - packet_len = iso->max_packet_size - rescount; - - } else if(event == 0x02) { - PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - packet too long for buffer\n"); - } else if(event) { - PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - OHCI error code 0x%02x\n", event); + channel = p[recv->dma_offset+1] & 0x3F; + tag = p[recv->dma_offset+1] >> 6; + sy = p[recv->dma_offset+0] & 0xF; + + /* advance to data payload */ + recv->dma_offset += 4; + + /* check for wrap-around */ + if(recv->dma_offset >= PAGE_SIZE*recv->nblocks) { + recv->dma_offset -= PAGE_SIZE*recv->nblocks; } - /* sync our view of the buffer */ - dma_region_sync(&iso->buf, recv->pkt_dma * iso->buf_stride, iso->buf_stride); + /* dma_offset now points to the first byte of the data payload */ + offset = recv->dma_offset; + + /* advance to xferStatus/timeStamp */ + recv->dma_offset += len; + + /* payload is padded to 4 bytes */ + if(len % 4) { + recv->dma_offset += 4 - (len%4); + } + + /* check for wrap-around */ + if(recv->dma_offset >= PAGE_SIZE*recv->nblocks) { + /* uh oh, the packet data wraps from the last + to the first DMA block - make the packet + contiguous by copying its "tail" into the + guard page */ + + int guard_off = recv->nblocks*PAGE_SIZE; + int tail_len = len - (guard_off - offset); + + if(tail_len > 0 && tail_len < PAGE_SIZE) { + memcpy(iso->data_buf.kvirt + guard_off, + iso->data_buf.kvirt, + tail_len); + } + + recv->dma_offset -= PAGE_SIZE*recv->nblocks; + } + + /* parse timestamp */ + cycle = p[recv->dma_offset+0] | (p[recv->dma_offset+1]<<8); + cycle &= 0x1FFF; + + /* check for cycle discontinuity */ + if(recv->last_cycle != -1) { + /* for multichannel, we have to allow cycle == last_cycle */ + if((cycle != (recv->last_cycle+1) % 8000) && + (cycle != recv->last_cycle)) { + atomic_inc(&iso->overflows); + DBGMSG(recv->ohci->id, "ISO reception overflow - " + "cycle discontinuity detected (cycle %4u last %4d)", + cycle, recv->last_cycle); + } + } + recv->last_cycle = cycle; + + /* advance to next packet */ + recv->dma_offset += 4; + + /* check for wrap-around */ + if(recv->dma_offset >= PAGE_SIZE*recv->nblocks) { + recv->dma_offset -= PAGE_SIZE*recv->nblocks; + } + + /* check if we have overflowed the ringbuffer of packet_infos */ + spin_lock_irqsave(&iso->lock, flags); + if(iso->n_ready_packets == iso->buf_packets) { + /* overflow! */ + atomic_inc(&iso->overflows); + + if(recv->warn_backoff == 0) { + DBGMSG(recv->ohci->id, + "ISO reception overflow - packet info buffer full"); + recv->warn_backoff = 10000; + } else { + recv->warn_backoff--; + } - /* record the per-packet info */ - { - /* iso header is 8 bytes ahead of the data payload */ - unsigned char *hdr = hpsb_iso_packet_data(iso, recv->pkt_dma) - 8; + } else { + /* good, no overflow; fill out the packet_info */ + struct hpsb_iso_packet_info *info = &iso->infos[recv->pkt_dma]; + info->offset = offset; + info->len = len; + info->cycle = cycle; + info->channel = channel; + info->tag = tag; + info->sy = sy; + wmb(); - struct hpsb_iso_packet_info *info = hpsb_iso_packet_info(iso, recv->pkt_dma); + /* one more packet for the user, one less for us */ + recv->pkt_dma = (recv->pkt_dma + 1) % iso->buf_packets; + iso->n_ready_packets++; - info->len = packet_len; - info->cycle = (hdr[0] | (hdr[1] << 8)) & 0x1FFF; - info->channel = hdr[5] & 0x3F; - info->tag = hdr[5] >> 6; - info->sy = hdr[4] & 0xF; + if(iso->n_ready_packets*2 > iso->buf_packets) { + if(recv->warn_backoff == 0) { + DBGMSG(recv->ohci->id, + "Running out of packet info buffer space " + "for ISO reception - overrun likely (%u/%u packets)", + iso->n_ready_packets, iso->buf_packets); + recv->warn_backoff = 10000; + } else { + recv->warn_backoff--; + } + } } + spin_unlock_irqrestore(&iso->lock, flags); + } + + if(wake && iso->callback) + iso->callback(iso); +} + +static void ohci_iso_recv_task(unsigned long data) +{ + struct hpsb_iso *iso = (struct hpsb_iso*) data; + struct ohci_iso_recv *recv = iso->hostdata; + + int loop; + + /* loop over all blocks */ + for(loop = 0; loop < recv->nblocks; loop++) { - /* at least one packet came in, so wake up the reader */ - wake = 1; + /* check block_dma to see if it's done */ + + struct dma_cmd *im = &recv->block[recv->block_dma]; + + /* check the DMA descriptor for new writes to xferStatus */ + u16 xferstatus = im->status >> 16; + + /* rescount is the number of bytes *remaining to be written* in the block */ + u16 rescount = im->status & 0xFFFF; - /* reset the DMA descriptor */ - il->status = iso->max_packet_size; + unsigned char event = xferstatus & 0x1F; + + if(!event) { + /* nothing has happened to this block yet */ + break; + } + + if(event != 0x11) { + atomic_inc(&iso->overflows); + PRINT(KERN_ERR, recv->ohci->id, + "IR DMA error - OHCI error code 0x%02x\n", event); + } - /* advance DMA packet cursor */ - recv->pkt_dma = (recv->pkt_dma + 1) % iso->buf_packets; + if(rescount != 0) { + /* the card is still writing to this block; + we can't touch it until it's done */ + break; + } + + /* OK, the block is finished... */ + + /* sync our view of the block */ + dma_region_sync(&iso->data_buf, recv->block_dma*PAGE_SIZE, PAGE_SIZE); + + /* reset the DMA descriptor */ + im->status = PAGE_SIZE; + + /* advance block_dma */ + recv->block_dma = (recv->block_dma + 1) % recv->nblocks; - /* one more packet for the user, one less for us */ - if(atomic_dec_and_test(&iso->n_dma_packets)) { - /* if n_dma_packets reaches zero, we have an overflow */ + if((recv->block_dma+1) % recv->nblocks == recv->block_reader) { atomic_inc(&iso->overflows); + DBGMSG(recv->ohci->id, "ISO reception overflow - " + "ran out of DMA blocks"); } } -out: - if(wake && iso->callback) { - iso->callback(iso); - } + /* parse any packets that have arrived */ + ohci_iso_recv_bytes(iso, recv); } @@ -1418,7 +1746,7 @@ unsigned int prog_size; int ctx; int ret = -ENOMEM; - + xmit = kmalloc(sizeof(*xmit), SLAB_KERNEL); if(!xmit) return -ENOMEM; @@ -1430,18 +1758,18 @@ xmit->last_cycle = -1; dma_prog_region_init(&xmit->prog); - + prog_size = sizeof(struct iso_xmit_cmd) * iso->buf_packets; - + if(dma_prog_region_alloc(&xmit->prog, prog_size, xmit->ohci->dev)) goto err; ohci1394_init_iso_tasklet(&xmit->task, OHCI_ISO_TRANSMIT, ohci_iso_xmit_task, (unsigned long) iso); - + if(ohci1394_register_iso_tasklet(xmit->ohci, &xmit->task) < 0) goto err; - + xmit->task_active = 1; /* xmit context registers are spaced 16 bytes apart */ @@ -1482,7 +1810,7 @@ ohci1394_unregister_iso_tasklet(xmit->ohci, &xmit->task); xmit->task_active = 0; } - + dma_prog_region_free(&xmit->prog); kfree(xmit); iso->hostdata = NULL; @@ -1492,9 +1820,12 @@ { struct hpsb_iso *iso = (struct hpsb_iso*) data; struct ohci_iso_xmit *xmit = iso->hostdata; + unsigned long flags; int wake = 0; int count; + spin_lock_irqsave(&iso->lock, flags); + /* check the whole buffer if necessary, starting at pkt_dma */ for(count = 0; count < iso->buf_packets; count++) { @@ -1507,7 +1838,7 @@ if(!event) { /* packet hasn't been sent yet; we are done for now */ - goto out; + break; } if(event != 0x11) { @@ -1517,10 +1848,9 @@ /* at least one packet went out, so wake up the writer */ wake = 1; - + /* parse cycle */ { - struct hpsb_iso_packet_info* info = hpsb_iso_packet_info(iso, xmit->pkt_dma); unsigned int cycle = cmd->output_last.status & 0x1FFF; /* check for discontinuity */ @@ -1529,14 +1859,14 @@ } xmit->last_cycle = cycle; - /* predict the timestamp pkt_dma will have - next time around the buffer */ - - cycle += iso->buf_packets; + /* predict the timestamp of the next packet to be queued */ + + /* jump ahead by the number of packets that are already buffered */ + cycle += iso->buf_packets - iso->n_ready_packets; while(cycle >= 8000) cycle -= 8000; - info->cycle = cycle; + iso->xmit_cycle = cycle; } /* reset the DMA descriptor for next time */ @@ -1544,15 +1874,16 @@ /* advance packet cursor */ xmit->pkt_dma = (xmit->pkt_dma + 1) % iso->buf_packets; - - /* one less packet for us */ - if(atomic_dec_and_test(&iso->n_dma_packets)) { + iso->n_ready_packets += 1; + + if(iso->n_ready_packets == iso->buf_packets) { /* underflow */ atomic_inc(&iso->overflows); } } -out: + spin_unlock_irqrestore(&iso->lock, flags); + if(wake && iso->callback) { iso->callback(iso); } @@ -1560,27 +1891,53 @@ static int ohci_iso_xmit_queue_one(struct hpsb_iso *iso) { + unsigned long flags; struct ohci_iso_xmit *xmit = iso->hostdata; - struct hpsb_iso_packet_info *info; + int next_i, prev_i; struct iso_xmit_cmd *next, *prev; - + + unsigned int offset; + unsigned short len; + unsigned char tag, sy; + + /* grab the packet offset, length, tag, and sy fields from the + buffer. NOTE: we cannot trust the user to supply a valid + offset and length! */ + { + struct hpsb_iso_packet_info *info = &iso->infos[iso->first_packet]; + + /* check for bogus offset/length */ + int ret = hpsb_iso_check_offset_len(iso, info, &offset, &len); + if(ret) + return ret; + + /* also check that the packet doesn't cross a page boundary + (we could allow this if we added OUTPUT_MORE descriptor support) */ + if(cross_bound(offset, len)) { + PRINT(KERN_ERR, xmit->ohci->id, + "rawiso xmit: packet %u crosses a page boundary", + iso->first_packet); + return -EINVAL; + } + + tag = info->tag; + sy = info->sy; + } + /* sync up the card's view of the buffer */ - dma_region_sync(&iso->buf, iso->first_packet * iso->buf_stride, iso->buf_stride); + dma_region_sync(&iso->data_buf, offset, len); /* append first_packet to the DMA chain */ /* by linking the previous descriptor to it */ /* (next will become the new end of the DMA chain) */ - + next_i = iso->first_packet; prev_i = (next_i == 0) ? (iso->buf_packets - 1) : (next_i - 1); - + next = dma_region_i(&xmit->prog, struct iso_xmit_cmd, next_i); prev = dma_region_i(&xmit->prog, struct iso_xmit_cmd, prev_i); - /* retrieve the packet info stashed in the buffer */ - info = hpsb_iso_packet_info(iso, iso->first_packet); - /* set up the OUTPUT_MORE_IMMEDIATE descriptor */ memset(next, 0, sizeof(struct iso_xmit_cmd)); next->output_more_immediate.control = 0x02000008; @@ -1588,29 +1945,28 @@ /* ISO packet header is embedded in the OUTPUT_MORE_IMMEDIATE */ /* tcode = 0xA, and sy */ - next->iso_hdr[0] = 0xA0 | (info->sy & 0xF); + next->iso_hdr[0] = 0xA0 | (sy & 0xF); /* tag and channel number */ - next->iso_hdr[1] = (info->tag << 6) | (iso->channel & 0x3F); + next->iso_hdr[1] = (tag << 6) | (iso->channel & 0x3F); /* transmission speed */ next->iso_hdr[2] = iso->speed & 0x7; /* payload size */ - next->iso_hdr[6] = info->len & 0xFF; - next->iso_hdr[7] = info->len >> 8; + next->iso_hdr[6] = len & 0xFF; + next->iso_hdr[7] = len >> 8; /* set up the OUTPUT_LAST */ next->output_last.control = 1 << 28; next->output_last.control |= 1 << 27; /* update timeStamp */ next->output_last.control |= 3 << 20; /* want interrupt */ next->output_last.control |= 3 << 18; /* enable branch */ - next->output_last.control |= info->len; + next->output_last.control |= len; /* payload bus address */ - next->output_last.address = dma_region_offset_to_bus(&iso->buf, - hpsb_iso_packet_data(iso, iso->first_packet) - iso->buf.kvirt); - + next->output_last.address = dma_region_offset_to_bus(&iso->data_buf, offset); + /* leave branchAddress at zero for now */ /* re-write the previous DMA descriptor to chain to this one */ @@ -1618,7 +1974,7 @@ /* set prev branch address to point to next (Z=3) */ prev->output_last.branchAddress = dma_prog_region_offset_to_bus(&xmit->prog, sizeof(struct iso_xmit_cmd) * next_i) | 3; - + /* disable interrupt, unless required by the IRQ interval */ if(prev_i % iso->irq_interval) { prev->output_last.control &= ~(3 << 20); /* no interrupt */ @@ -1637,8 +1993,11 @@ reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer); /* increment cursors */ + spin_lock_irqsave(&iso->lock, flags); iso->first_packet = (iso->first_packet+1) % iso->buf_packets; - atomic_inc(&iso->n_dma_packets); + iso->xmit_cycle = (iso->xmit_cycle+1) % 8000; + iso->n_ready_packets--; + spin_unlock_irqrestore(&irq->lock, flags); return 0; } @@ -1705,18 +2064,18 @@ reg_read(xmit->ohci, xmit->ContextControlSet)); return -1; } - + return 0; } -static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, int arg) +static int ohci_isoctl(struct hpsb_iso *iso, enum isoctl_cmd cmd, unsigned long arg) { - + switch(cmd) { case XMIT_INIT: return ohci_iso_xmit_init(iso); case XMIT_START: - return ohci_iso_xmit_start(iso, arg); + return ohci_iso_xmit_start(iso, arg < 8000 ? arg : -1); case XMIT_STOP: ohci_iso_xmit_stop(iso); return 0; @@ -1729,7 +2088,7 @@ case RECV_INIT: return ohci_iso_recv_init(iso); case RECV_START: - return ohci_iso_recv_start(iso, arg); + return ohci_iso_recv_start(iso, arg < 8000 ? arg : -1); case RECV_STOP: ohci_iso_recv_stop(iso); return 0; @@ -1739,7 +2098,16 @@ case RECV_SHUTDOWN: ohci_iso_recv_shutdown(iso); return 0; - + case RECV_LISTEN_CHANNEL: + ohci_iso_recv_change_channel(iso, arg, 1); + return 0; + case RECV_UNLISTEN_CHANNEL: + ohci_iso_recv_change_channel(iso, arg, 0); + return 0; + case RECV_SET_CHANNEL_MASK: + ohci_iso_recv_set_channel_mask(iso, *((u64*) arg)); + return 0; + default: PRINT_G(KERN_ERR, "ohci_isoctl cmd %d not implemented yet", cmd); @@ -2378,7 +2746,7 @@ return; DBGMSG(d->ohci->id, "Freeing dma_rcv_ctx %d", d->ctx); - + ohci1394_stop_context(d->ohci, d->ctrlClear, NULL); if (d->type == DMA_CTX_ISO) @@ -2499,7 +2867,7 @@ spin_lock_init(&d->lock); if (type == DMA_CTX_ISO) { - ohci1394_init_iso_tasklet(&ohci->ir_tasklet, OHCI_ISO_RECEIVE, + ohci1394_init_iso_tasklet(&ohci->ir_tasklet, OHCI_ISO_MULTICHANNEL_RECEIVE, dma_rcv_tasklet, (unsigned long) d); if (ohci1394_register_iso_tasklet(ohci, &ohci->ir_tasklet) < 0) { @@ -2810,7 +3178,7 @@ struct hpsb_host *host; struct ti_ohci *ohci; /* shortcut to currently handled device */ unsigned long ohci_base; - + if (version_printed++ == 0) PRINT_G(KERN_INFO, "%s", version); @@ -2886,7 +3254,7 @@ pci_alloc_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, &ohci->selfid_buf_bus); OHCI_DMA_ALLOC("consistent selfid_buf"); - + if (ohci->selfid_buf_cpu == NULL) FAIL(-ENOMEM, "Failed to allocate DMA buffer for self-id packets"); ohci->init_state = OHCI_INIT_HAVE_SELFID_BUFFER; @@ -2966,7 +3334,7 @@ OHCI1394_IsoRcvContextBase) < 0) FAIL(-ENOMEM, "Failed to allocate IR context"); - + /* IT DMA context allocation */ if (alloc_dma_trm_ctx(ohci, &ohci->it_context, DMA_CTX_ISO, 0, IT_NUM_DESC, @@ -3020,7 +3388,7 @@ /* Free IT dma */ free_dma_trm_ctx(&ohci->it_context); - + case OHCI_INIT_HAVE_SELFID_BUFFER: pci_free_consistent(ohci->dev, OHCI1394_SI_DMA_BUF_SIZE, ohci->selfid_buf_cpu, @@ -3162,7 +3530,7 @@ } spin_unlock_irqrestore(&ohci->iso_tasklet_list_lock, flags); - + return r; } Modified: trunk/ohci1394.h ============================================================================== --- trunk/ohci1394.h (original) +++ trunk/ohci1394.h 2003-02-01 08:59:00.000000000 -0500 @@ -171,7 +171,7 @@ /* dma buffer for self-id packets */ quadlet_t *selfid_buf_cpu; dma_addr_t selfid_buf_bus; - + /* buffer for csr config rom */ quadlet_t *csr_config_rom_cpu; dma_addr_t csr_config_rom_bus; @@ -194,13 +194,13 @@ int nb_iso_rcv_ctx; unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */ unsigned int ir_multichannel_used; /* ditto */ - + /* iso transmit */ struct dma_trm_ctx it_context; struct ohci1394_iso_tasklet it_tasklet; int nb_iso_xmit_ctx; unsigned long it_ctx_usage; /* use test_and_set_bit() for atomicity */ - + u64 ISO_channel_usage; /* IEEE-1394 part follows */ @@ -215,10 +215,10 @@ /* Tasklets for iso receive and transmit, used by video1394, * amdtp and dv1394 */ - + struct list_head iso_tasklet_list; spinlock_t iso_tasklet_list_lock; - + /* Swap the selfid buffer? */ unsigned int selfid_swap:1; /* Some Apple chipset seem to swap incoming headers for us */ Modified: trunk/raw1394.c ============================================================================== --- trunk/raw1394.c (original) +++ trunk/raw1394.c 2003-02-01 08:59:00.000000000 -0500 @@ -2004,7 +2004,7 @@ { struct list_head *lh; struct pending_request *req; - + list_for_each(lh, &fi->req_complete) { req = list_entry(lh, struct pending_request, list); if(req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) { @@ -2019,7 +2019,7 @@ static void queue_rawiso_event(struct file_info *fi) { unsigned long flags; - + spin_lock_irqsave(&fi->reqlists_lock, flags); /* only one ISO activity event may be in the queue */ @@ -2049,7 +2049,7 @@ spin_lock_irqsave(&host_info_lock, flags); hi = find_host_info(iso->host); - + if (hi != NULL) { list_for_each(lh, &hi->file_info_list) { struct file_info *fi = list_entry(lh, struct file_info, list); @@ -2057,24 +2057,21 @@ queue_rawiso_event(fi); } } - + spin_unlock_irqrestore(&host_info_lock, flags); } /* helper function - gather all the kernel iso status bits for returning to user-space */ static void raw1394_iso_fill_status(struct hpsb_iso *iso, struct raw1394_iso_status *stat) { + stat->config.data_buf_size = iso->buf_size; stat->config.buf_packets = iso->buf_packets; - stat->config.max_packet_size = iso->max_packet_size; stat->config.channel = iso->channel; stat->config.speed = iso->speed; stat->config.irq_interval = iso->irq_interval; - stat->buf_stride = iso->buf_stride; - stat->packet_data_offset = iso->packet_data_offset; - stat->packet_info_offset = iso->packet_info_offset; - stat->first_packet = iso->first_packet; stat->n_packets = hpsb_iso_n_ready(iso); stat->overflows = atomic_read(&iso->overflows); + stat->xmit_cycle = iso->xmit_cycle; } static int raw1394_iso_xmit_init(struct file_info *fi, void *uaddr) @@ -2083,10 +2080,10 @@ if(copy_from_user(&stat, uaddr, sizeof(stat))) return -EFAULT; - + fi->iso_handle = hpsb_iso_xmit_init(fi->host, + stat.config.data_buf_size, stat.config.buf_packets, - stat.config.max_packet_size, stat.config.channel, stat.config.speed, stat.config.irq_interval, @@ -2095,14 +2092,14 @@ return -ENOMEM; fi->iso_state = RAW1394_ISO_XMIT; - + raw1394_iso_fill_status(fi->iso_handle, &stat); if(copy_to_user(uaddr, &stat, sizeof(stat))) return -EFAULT; /* queue an event to get things started */ rawiso_activity_cb(fi->iso_handle); - + return 0; } @@ -2112,10 +2109,10 @@ if(copy_from_user(&stat, uaddr, sizeof(stat))) return -EFAULT; - + fi->iso_handle = hpsb_iso_recv_init(fi->host, + stat.config.data_buf_size, stat.config.buf_packets, - stat.config.max_packet_size, stat.config.channel, stat.config.irq_interval, rawiso_activity_cb); @@ -2123,7 +2120,7 @@ return -ENOMEM; fi->iso_state = RAW1394_ISO_RECV; - + raw1394_iso_fill_status(fi->iso_handle, &stat); if(copy_to_user(uaddr, &stat, sizeof(stat))) return -EFAULT; @@ -2141,15 +2138,77 @@ /* reset overflow counter */ atomic_set(&iso->overflows, 0); - + + return 0; +} + +/* copy N packet_infos out of the ringbuffer into user-supplied array */ +static int raw1394_iso_recv_packets(struct file_info *fi, void *uaddr) +{ + struct raw1394_iso_packets upackets; + unsigned int packet = fi->iso_handle->first_packet; + int i; + + if(copy_from_user(&upackets, uaddr, sizeof(upackets))) + return -EFAULT; + + if(upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) + return -EINVAL; + + /* ensure user-supplied buffer is accessible and big enough */ + if(verify_area(VERIFY_WRITE, upackets.infos, + upackets.n_packets * sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + /* copy the packet_infos out */ + for(i = 0; i < upackets.n_packets; i++) { + if(__copy_to_user(&upackets.infos[i], + &fi->iso_handle->infos[packet], + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + packet = (packet + 1) % fi->iso_handle->buf_packets; + } + return 0; } +/* copy N packet_infos from user to ringbuffer, and queue them for transmission */ +static int raw1394_iso_send_packets(struct file_info *fi, void *uaddr) +{ + struct raw1394_iso_packets upackets; + unsigned int packet = fi->iso_handle->first_packet; + int i; + + if(copy_from_user(&upackets, uaddr, sizeof(upackets))) + return -EFAULT; + + if(upackets.n_packets > hpsb_iso_n_ready(fi->iso_handle)) + return -EINVAL; + + /* ensure user-supplied buffer is accessible and big enough */ + if(verify_area(VERIFY_READ, upackets.infos, + upackets.n_packets * sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + /* copy the packet_infos in */ + for(i = 0; i < upackets.n_packets; i++) { + if(__copy_from_user(&fi->iso_handle->infos[packet], + &upackets.infos[i], + sizeof(struct raw1394_iso_packet_info))) + return -EFAULT; + + packet = (packet + 1) % fi->iso_handle->buf_packets; + } + + return hpsb_iso_xmit_queue_packets(fi->iso_handle, upackets.n_packets); +} + static void raw1394_iso_shutdown(struct file_info *fi) { if(fi->iso_handle) hpsb_iso_shutdown(fi->iso_handle); - + fi->iso_handle = NULL; fi->iso_state = RAW1394_ISO_INACTIVE; } @@ -2162,14 +2221,14 @@ if(fi->iso_state == RAW1394_ISO_INACTIVE) return -EINVAL; - return dma_region_mmap(&fi->iso_handle->buf, file, vma); + return dma_region_mmap(&fi->iso_handle->data_buf, file, vma); } /* ioctl is only used for rawiso operations */ static int raw1394_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct file_info *fi = file->private_data; - + switch(fi->iso_state) { case RAW1394_ISO_INACTIVE: switch(cmd) { @@ -2188,17 +2247,26 @@ case RAW1394_ISO_STOP: hpsb_iso_stop(fi->iso_handle); return 0; - + case RAW1394_ISO_RECV_LISTEN_CHANNEL: + return hpsb_iso_recv_listen_channel(fi->iso_handle, arg); + case RAW1394_ISO_RECV_UNLISTEN_CHANNEL: + return hpsb_iso_recv_unlisten_channel(fi->iso_handle, arg); + case RAW1394_ISO_RECV_SET_CHANNEL_MASK: { + /* copy the u64 from user-space */ + u64 mask; + if(copy_from_user(&mask, (void*) arg, sizeof(mask))) + return -EFAULT; + return hpsb_iso_recv_set_channel_mask(fi->iso_handle, mask); + } case RAW1394_ISO_GET_STATUS: return raw1394_iso_get_status(fi, (void*) arg); - - case RAW1394_ISO_PRODUCE_CONSUME: + case RAW1394_ISO_RECV_PACKETS: + return raw1394_iso_recv_packets(fi, (void*) arg); + case RAW1394_ISO_RECV_RELEASE_PACKETS: return hpsb_iso_recv_release_packets(fi->iso_handle, arg); - case RAW1394_ISO_SHUTDOWN: raw1394_iso_shutdown(fi); return 0; - case RAW1394_ISO_QUEUE_ACTIVITY: queue_rawiso_event(fi); return 0; @@ -2216,17 +2284,13 @@ case RAW1394_ISO_STOP: hpsb_iso_stop(fi->iso_handle); return 0; - case RAW1394_ISO_GET_STATUS: return raw1394_iso_get_status(fi, (void*) arg); - - case RAW1394_ISO_PRODUCE_CONSUME: - return hpsb_iso_xmit_queue_packets(fi->iso_handle, arg); - + case RAW1394_ISO_XMIT_PACKETS: + return raw1394_iso_send_packets(fi, (void*) arg); case RAW1394_ISO_SHUTDOWN: raw1394_iso_shutdown(fi); return 0; - case RAW1394_ISO_QUEUE_ACTIVITY: queue_rawiso_event(fi); return 0; @@ -2235,7 +2299,7 @@ default: break; } - + return -EINVAL; } @@ -2251,7 +2315,7 @@ mask |= POLLIN | POLLRDNORM; } spin_unlock_irq(&fi->reqlists_lock); - + return mask; } @@ -2301,7 +2365,7 @@ if(fi->iso_state != RAW1394_ISO_INACTIVE) raw1394_iso_shutdown(fi); - + for (i = 0; i < 64; i++) { if (fi->listen_channels & (1ULL << i)) { hpsb_unlisten_channel(hl_handle, fi->host, i); Modified: trunk/raw1394.h ============================================================================== --- trunk/raw1394.h (original) +++ trunk/raw1394.h 2003-02-01 08:59:00.000000000 -0500 @@ -118,31 +118,53 @@ /* rawiso API */ /* ioctls */ -#define RAW1394_ISO_XMIT_INIT 1 /* arg: raw1394_iso_status* */ -#define RAW1394_ISO_RECV_INIT 2 /* arg: raw1394_iso_status* */ -#define RAW1394_ISO_RECV_START 3 /* arg: int, starting cycle */ -#define RAW1394_ISO_XMIT_START 8 /* arg: int[2], { starting cycle, prebuffer } */ -#define RAW1394_ISO_STOP 4 -#define RAW1394_ISO_GET_STATUS 5 /* arg: raw1394_iso_status* */ -#define RAW1394_ISO_PRODUCE_CONSUME 6 /* arg: int, # of packets */ -#define RAW1394_ISO_SHUTDOWN 7 -#define RAW1394_ISO_QUEUE_ACTIVITY 9 - +#define RAW1394_ISO_XMIT_INIT 1 /* arg: raw1394_iso_status* */ +#define RAW1394_ISO_RECV_INIT 2 /* arg: raw1394_iso_status* */ +#define RAW1394_ISO_RECV_START 3 /* arg: int, starting cycle */ +#define RAW1394_ISO_XMIT_START 8 /* arg: int[2], { starting cycle, prebuffer } */ +#define RAW1394_ISO_STOP 4 +#define RAW1394_ISO_GET_STATUS 5 /* arg: raw1394_iso_status* */ +#define RAW1394_ISO_PRODUCE_CONSUME 6 /* no longer used */ +#define RAW1394_ISO_SHUTDOWN 7 +#define RAW1394_ISO_QUEUE_ACTIVITY 9 +#define RAW1394_ISO_RECV_LISTEN_CHANNEL 10 /* arg: channel number */ +#define RAW1394_ISO_RECV_UNLISTEN_CHANNEL 11 /* arg: channel number */ +#define RAW1394_ISO_RECV_SET_CHANNEL_MASK 12 /* arg: pointer to 64-bit mask */ +#define RAW1394_ISO_RECV_PACKETS 13 /* arg: struct raw1394_iso_packets* */ +#define RAW1394_ISO_RECV_RELEASE_PACKETS 14 /* arg: int n_packets */ +#define RAW1394_ISO_XMIT_PACKETS 15 /* arg: struct raw1394_iso_packets* */ + /* per-packet metadata embedded in the ringbuffer */ /* must be identical to hpsb_iso_packet_info in iso.h! */ struct raw1394_iso_packet_info { + __u32 offset; __u16 len; - __u16 cycle; + __u16 cycle; /* recv only */ __u8 channel; /* recv only */ __u8 tag; - __u8 sy; + __u8 sy; +}; + +/* argument for RAW1394_ISO_RECV/XMIT_PACKETS ioctls */ +struct raw1394_iso_packets { + __u32 n_packets; + struct raw1394_iso_packet_info *infos; }; struct raw1394_iso_config { + /* size of packet data buffer, in bytes (will be rounded up to PAGE_SIZE) */ + __u32 data_buf_size; + + /* # of packets to buffer */ __u32 buf_packets; - __u32 max_packet_size; - __u32 channel; - __u32 speed; /* xmit only */ + + /* iso channel (set to -1 for multi-channel recv) */ + __s32 channel; + + /* xmit only - iso transmission speed */ + __u8 speed; + + /* max. latency of buffer, in packets (-1 if you don't care) */ __s32 irq_interval; }; @@ -150,19 +172,6 @@ struct raw1394_iso_status { /* current settings */ struct raw1394_iso_config config; - - /* byte offset between successive packets in the buffer */ - __s32 buf_stride; - - /* byte offset of data payload within each packet */ - __s32 packet_data_offset; - - /* byte offset of struct iso_packet_info within each packet */ - __s32 packet_info_offset; - - /* index of next packet to fill with data (ISO transmission) - or next packet containing data recieved (ISO reception) */ - __u32 first_packet; /* number of packets waiting to be filled with data (ISO transmission) or containing data received (ISO reception) */ @@ -172,6 +181,10 @@ underflow of the packet buffer (a value of zero guarantees that no packets have been dropped) */ __u32 overflows; + + /* cycle number at which next packet will be transmitted; + -1 if not known */ + __s16 xmit_cycle; }; #endif /* IEEE1394_RAW1394_H */ Modified: trunk/dma.c ============================================================================== --- trunk/dma.c (original) +++ trunk/dma.c 2003-02-01 08:59:00.000000000 -0500 @@ -26,7 +26,7 @@ { /* round up to page size */ n_bytes = round_up_to_page(n_bytes); - + prog->n_pages = n_bytes / PAGE_SIZE; prog->kvirt = pci_alloc_consistent(dev, prog->n_pages * PAGE_SIZE, &prog->bus_addr); @@ -46,7 +46,7 @@ if(prog->kvirt) { pci_free_consistent(prog->dev, prog->n_pages * PAGE_SIZE, prog->kvirt, prog->bus_addr); } - + prog->kvirt = NULL; prog->dev = NULL; prog->n_pages = 0; @@ -70,9 +70,9 @@ /* round up to page size */ n_bytes = round_up_to_page(n_bytes); - + n_pages = n_bytes / PAGE_SIZE; - + dma->kvirt = vmalloc_32(n_pages * PAGE_SIZE); if(!dma->kvirt) { printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n"); @@ -80,7 +80,7 @@ } dma->n_pages = n_pages; - + /* Clear the ram out, no junk to the user */ memset(dma->kvirt, 0, n_pages * PAGE_SIZE); @@ -112,7 +112,7 @@ dma->dev = dev; dma->direction = direction; - + return 0; err: @@ -146,7 +146,7 @@ { int i; unsigned long off = offset; - + for(i = 0; i < dma->n_dma_pages; i++) { if(off < sg_dma_len(&dma->sglist[i])) { *rem = off; @@ -155,14 +155,14 @@ off -= sg_dma_len(&dma->sglist[i]); } - + panic("dma_region_find: offset %lu beyond end of DMA mapping\n", offset); } dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset) { unsigned long rem; - + struct scatterlist *sg = &dma->sglist[dma_region_find(dma, offset, &rem)]; return sg_dma_address(sg) + rem; } @@ -174,10 +174,10 @@ if(!len) len = 1; - + first = dma_region_find(dma, offset, &rem); last = dma_region_find(dma, offset + len - 1, &rem); - + pci_dma_sync_sg(dma->dev, &dma->sglist[first], last - first + 1, dma->direction); } @@ -214,7 +214,7 @@ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) { unsigned long size; - + if(!dma->kvirt) return -EINVAL; @@ -231,6 +231,6 @@ vma->vm_private_data = dma; vma->vm_file = file; vma->vm_flags |= VM_RESERVED; - + return 0; } Modified: trunk/hosts.h ============================================================================== --- trunk/hosts.h (original) +++ trunk/hosts.h 2003-02-01 08:59:00.000000000 -0500 @@ -113,14 +113,17 @@ * QUEUE/RELEASE = produce/consume packets (arg: # of packets) * SHUTDOWN = deallocate resources */ - + XMIT_INIT, XMIT_START, XMIT_STOP, XMIT_QUEUE, XMIT_SHUTDOWN, - + RECV_INIT, + RECV_LISTEN_CHANNEL, /* multi-channel only */ + RECV_UNLISTEN_CHANNEL, /* multi-channel only */ + RECV_SET_CHANNEL_MASK, /* multi-channel only; arg is a *u64 */ RECV_START, RECV_STOP, RECV_RELEASE, @@ -173,8 +176,8 @@ /* ISO transmission/reception functions. Return 0 on success, -1 on failure. * If the low-level driver does not support the new ISO API, set isoctl to NULL. */ - int (*isoctl) (struct hpsb_iso ... [truncated message content] |