[Linux1394-cvslog] rev 659 - trunk
Brought to you by:
aeb,
bencollins
From: SVN U. <dm...@li...> - 2002-11-06 03:27:48
|
Author: dmaas Date: 2002-11-05 22:27:36 -0500 (Tue, 05 Nov 2002) New Revision: 659 Modified: trunk/dma.c trunk/dma.h trunk/ieee1394_core.c trunk/iso.c trunk/iso.h trunk/ohci1394.c trunk/raw1394.c trunk/raw1394.h Log: rawiso updates - make the PCI DMA wrapper a tiny bit more efficient - remove iso_status.first_packet_cycle; cycle tracking is now done in the packet_info struct only - make ISO xmit prebuffering the kernel's responsibility - change raw1394 ISO_XMIT_START ioctl to take prebuffer argument - clean up & improve ohci1394 implementation of rawiso - added timestamp prediction for xmit - recv side still needs a little more work Modified: trunk/iso.h ============================================================================== --- trunk/iso.h (original) +++ trunk/iso.h 2002-11-05 22:27:38.000000000 -0500 @@ -20,8 +20,8 @@ /* per-packet data embedded in the ringbuffer */ struct hpsb_iso_packet_info { unsigned short len; - unsigned short cycle; /* receive only */ - unsigned char channel; /* receive only */ + unsigned short cycle; + unsigned char channel; /* recv only */ unsigned char tag; unsigned char sy; }; @@ -29,7 +29,6 @@ /* * each packet in the ringbuffer consists of three things: * 1. the packet's data payload (no isochronous header) - * XXX ^^ this must change * 2. a struct hpsb_iso_packet_info * 3. some empty space before the next packet * @@ -63,9 +62,9 @@ /* # of packets in the ringbuffer */ unsigned int buf_packets; - /* offset between successive packets - + /* offset between successive packets, in bytes - you can assume that this is a power of 2, - and that packets will never cross a page boundary */ + and less than or equal to the page size */ int buf_stride; /* largest possible packet size, in bytes */ @@ -92,11 +91,16 @@ /* how many times the buffer has overflowed or underflowed */ atomic_t overflows; - /* transmit only - the cycle number at which first_packet will be sent */ - int first_packet_cycle; + /* private flags to track initialization progress */ +#define HPSB_ISO_DRIVER_INIT (1<<0) +#define HPSB_ISO_DRIVER_STARTED (1<<1) + unsigned int flags; - /* flags to track initialization progress */ - int driver_init, driver_started; + /* # of packets left to prebuffer (xmit only) */ + int prebuffer; + + /* starting cycle (xmit only) */ + int start_cycle; }; /* functions available to high-level drivers (e.g. raw1394) */ @@ -119,7 +123,8 @@ wait_queue_head_t *waitq); /* start/stop DMA */ -int hpsb_iso_start(struct hpsb_iso *iso, int start_on_cycle); +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); void hpsb_iso_stop(struct hpsb_iso *iso); /* deallocate buffer and DMA context */ Modified: trunk/ieee1394_core.c ============================================================================== --- trunk/ieee1394_core.c (original) +++ trunk/ieee1394_core.c 2002-11-05 22:27:38.000000000 -0500 @@ -1276,11 +1276,13 @@ EXPORT_SYMBOL(dma_region_free); EXPORT_SYMBOL(dma_region_sync); EXPORT_SYMBOL(dma_region_mmap); +EXPORT_SYMBOL(dma_region_offset_to_bus); /** iso.c **/ EXPORT_SYMBOL(hpsb_iso_xmit_init); EXPORT_SYMBOL(hpsb_iso_recv_init); -EXPORT_SYMBOL(hpsb_iso_start); +EXPORT_SYMBOL(hpsb_iso_xmit_start); +EXPORT_SYMBOL(hpsb_iso_recv_start); EXPORT_SYMBOL(hpsb_iso_stop); EXPORT_SYMBOL(hpsb_iso_shutdown); EXPORT_SYMBOL(hpsb_iso_xmit_queue_packets); Modified: trunk/ohci1394.c ============================================================================== --- trunk/ohci1394.c (original) +++ trunk/ohci1394.c 2002-11-05 22:27:38.000000000 -0500 @@ -1242,8 +1242,6 @@ int count; int wake = 0; - int good_packets = 0; - int bad_packets = 0; /* loop over the entire buffer */ for(count = 0; count < iso->buf_packets; count++) { @@ -1259,35 +1257,32 @@ unsigned char event = xferstatus & 0x1F; + if(!event) { + /* this packet hasn't come in yet; we are done for now */ + goto out; + } + if(event == 0x11) { /* packet received successfully! */ - good_packets++; /* 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, - "ISO DMA reception error - packet too long for buffer\n"); - bad_packets++; + PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - packet too long for buffer\n"); } else if(event) { - PRINT(KERN_ERR, recv->ohci->id, - "ISO DMA reception error - OHCI error code 0x%02x\n", event); - bad_packets++; - } - - if(!event) { - /* this packet hasn't come in yet; we are done for now */ - goto out; + PRINT(KERN_ERR, recv->ohci->id, "IR DMA error - OHCI error code 0x%02x\n", event); } /* sync our view of the buffer */ - dma_region_sync(&iso->buf); + dma_region_sync(&iso->buf, recv->pkt_dma * iso->buf_stride, iso->buf_stride); /* record the per-packet info */ info = hpsb_iso_packet_info(iso, recv->pkt_dma); info->len = packet_len; + + /* XXX record channel, sy, tag, cycle */ /* at least one packet came in, so wake up the reader */ wake = 1; @@ -1303,7 +1298,6 @@ /* if n_dma_packets reaches zero, we have an overflow */ atomic_inc(&iso->overflows); } - } out: @@ -1345,28 +1339,6 @@ static void ohci_iso_xmit_shutdown(struct hpsb_iso *iso); static void ohci_iso_xmit_task(unsigned long data); -static int ohci_iso_xmit_suggest_start_cycle(struct ti_ohci *ohci) -{ - u32 cycleTimer, sec, cyc, off; - - cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - sec = cycleTimer >> 25; - cyc = (cycleTimer >> 12) & 0x1FFF; - off = cycleTimer & 0xFFF; - - /* go ahead 500 cycles - if the user doesn't xmit_produce() a - packet within this time, we will end up waiting until the - 4-bit second counter wraps around to start transmission... - is there a better way? */ - - cyc += 500; - - sec += cyc/8000; - cyc %= 8000; - - return ((sec & 0x3) << 13) | cyc; -} - static int ohci_iso_xmit_init(struct hpsb_iso *iso) { struct ohci_iso_xmit *xmit; @@ -1409,9 +1381,6 @@ /* enable interrupts */ reg_write(xmit->ohci, OHCI1394_IsoXmitIntMaskSet, 1 << ctx); - /* suggest a starting cycle */ - iso->first_packet_cycle = ohci_iso_xmit_suggest_start_cycle(xmit->ohci); - return 0; err: @@ -1450,39 +1419,45 @@ struct hpsb_iso *iso = (struct hpsb_iso*) data; struct ohci_iso_xmit *xmit = iso->hostdata; int wake = 0; - unsigned int good_packets = 0; - unsigned int bad_packets = 0; int count; - /* check the whole buffer if necessary, starting at pkt_dma*/ + /* check the whole buffer if necessary, starting at pkt_dma */ for(count = 0; count < iso->buf_packets; count++) { - struct iso_xmit_cmd *cmd = - dma_region_i(&xmit->prog, struct iso_xmit_cmd, xmit->pkt_dma); - + + /* DMA descriptor */ + struct iso_xmit_cmd *cmd = dma_region_i(&xmit->prog, struct iso_xmit_cmd, xmit->pkt_dma); + /* check for new writes to xferStatus */ u16 xferstatus = cmd->output_last.status >> 16; - unsigned char event = xferstatus & 0x1F; - - if(event == 0x11) { - /* packet sent successfully */ - good_packets++; - } else if(event) { - PRINT(KERN_ERR, xmit->ohci->id, - "ISO DMA transmission error - OHCI error code 0x%02x\n", event); - bad_packets++; - } + u8 event = xferstatus & 0x1F; if(!event) { /* packet hasn't been sent yet; we are done for now */ goto out; } - /* at least one packet came in, so wake up the writer */ + if(event != 0x11) { + PRINT(KERN_ERR, xmit->ohci->id, "IT DMA error - OHCI error code 0x%02x\n", event); + } + + /* at least one packet went out, so wake up the writer */ wake = 1; - + + /* predict the timestamp pkt_dma will have next time around the buffer */ + { + struct hpsb_iso_packet_info* info = hpsb_iso_packet_info(iso, xmit->pkt_dma); + unsigned int cycle = cmd->output_last.status & 0x1FFF; + + cycle += iso->buf_packets; + while(cycle > 8000) + cycle -= 8000; + + info->cycle = cycle; + } + /* reset the DMA descriptor for next time */ cmd->output_last.status = 0; - + /* advance packet cursor */ xmit->pkt_dma = (xmit->pkt_dma + 1) % iso->buf_packets; @@ -1507,7 +1482,7 @@ struct iso_xmit_cmd *next, *prev; /* sync up the card's view of the buffer */ - dma_region_sync(&iso->buf); + dma_region_sync(&iso->buf, iso->first_packet * iso->buf_stride, iso->buf_stride); /* append first_packet to the DMA chain */ /* by linking the previous descriptor to it */ @@ -1522,7 +1497,7 @@ /* retrieve the packet info stashed in the buffer */ info = hpsb_iso_packet_info(iso, iso->first_packet); - /* set up the OUTPUT_MORE_IMMEDIATE */ + /* set up the OUTPUT_MORE_IMMEDIATE descriptor */ memset(next, 0, sizeof(struct iso_xmit_cmd)); next->output_more_immediate.control = 0x02000008; @@ -1579,8 +1554,6 @@ /* increment cursors */ iso->first_packet = (iso->first_packet+1) % iso->buf_packets; - iso->first_packet_cycle = (iso->first_packet_cycle+1) % iso->buf_packets; - atomic_inc(&iso->n_dma_packets); } @@ -1605,8 +1578,18 @@ /* cycle match */ if(cycle != -1) { - /* XXX pick a cycle and set iso->first_packet_cycle */ - reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (cycle << 16)); + u32 start = cycle & 0x1FFF; + + /* 'cycle' is only mod 8000, but we also need two 'seconds' bits - + just snarf them from the current time */ + u32 seconds = reg_read(xmit->ohci, OHCI1394_IsochronousCycleTimer) >> 25; + + /* advance one second to give some extra time for DMA to start */ + seconds += 1; + + start |= (seconds & 3) << 13; + + reg_write(xmit->ohci, xmit->ContextControlSet, 0x80000000 | (start << 16)); } /* run */ @@ -1618,7 +1601,7 @@ /* check the RUN bit */ if(!(reg_read(xmit->ohci, xmit->ContextControlSet) & 0x8000)) { - PRINT(KERN_ERR, xmit->ohci->id, "Error starting ISO DMA transmission (ContextControl 0x%08x)\n", + PRINT(KERN_ERR, xmit->ohci->id, "Error starting IT DMA (ContextControl 0x%08x)\n", reg_read(xmit->ohci, xmit->ContextControlSet)); return -1; } Modified: trunk/raw1394.c ============================================================================== --- trunk/raw1394.c (original) +++ trunk/raw1394.c 2002-11-05 22:27:38.000000000 -0500 @@ -2009,7 +2009,6 @@ stat->first_packet = iso->first_packet; stat->n_packets = hpsb_iso_n_ready(iso); stat->overflows = atomic_read(&iso->overflows); - stat->first_packet_cycle = iso->first_packet_cycle; } static int raw1394_iso_xmit_init(struct file_info *fi, void *uaddr) @@ -2114,8 +2113,8 @@ break; case RAW1394_ISO_RECV: switch(cmd) { - case RAW1394_ISO_START: - return hpsb_iso_start(fi->iso_handle, arg); + case RAW1394_ISO_RECV_START: + return hpsb_iso_recv_start(fi->iso_handle, arg); case RAW1394_ISO_STOP: hpsb_iso_stop(fi->iso_handle); return 0; @@ -2133,8 +2132,13 @@ break; case RAW1394_ISO_XMIT: switch(cmd) { - case RAW1394_ISO_START: - return hpsb_iso_start(fi->iso_handle, arg); + case RAW1394_ISO_XMIT_START: { + /* copy two ints from user-space */ + int args[2]; + if(copy_from_user(&args[0], (void*) arg, sizeof(args))) + return -EFAULT; + return hpsb_iso_xmit_start(fi->iso_handle, args[0], args[1]); + } case RAW1394_ISO_STOP: hpsb_iso_stop(fi->iso_handle); return 0; Modified: trunk/raw1394.h ============================================================================== --- trunk/raw1394.h (original) +++ trunk/raw1394.h 2002-11-05 22:27:39.000000000 -0500 @@ -118,12 +118,13 @@ /* rawiso API */ /* ioctls */ -#define RAW1394_ISO_XMIT_INIT 1 -#define RAW1394_ISO_RECV_INIT 2 -#define RAW1394_ISO_START 3 +#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 -#define RAW1394_ISO_PRODUCE_CONSUME 6 +#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 /* per-packet metadata embedded in the ringbuffer */ @@ -131,7 +132,7 @@ struct raw1394_iso_packet_info { unsigned short len; unsigned short cycle; - unsigned char channel; + unsigned char channel; /* recv only */ unsigned char tag; unsigned char sy; }; @@ -170,10 +171,6 @@ underflow of the packet buffer (a value of zero guarantees that no packets have been dropped) */ unsigned int overflows; - - /* (transmission only) the value the ISO CYCLE_COUNT register - will have when the first_packet is sent */ - int first_packet_cycle; }; #ifdef __KERNEL__ Modified: trunk/dma.c ============================================================================== --- trunk/dma.c (original) +++ trunk/dma.c 2002-11-05 22:27:39.000000000 -0500 @@ -140,10 +140,45 @@ } } -void dma_region_sync(struct dma_region *dma) +/* find the scatterlist index and remaining offset corresponding to a + given offset from the beginning of the buffer */ +static inline int dma_region_find(struct dma_region *dma, unsigned long offset, unsigned long *rem) { - /* XXX DJM - inefficient - add address/length argument */ - pci_dma_sync_sg(dma->dev, &dma->sglist[0], dma->n_dma_pages, dma->direction); + 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; + return i; + } + + 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; +} + +void dma_region_sync(struct dma_region *dma, unsigned long offset, unsigned long len) +{ + int first, last; + unsigned long rem; + + 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); } /* nopage() handler for mmap access */ Modified: trunk/dma.h ============================================================================== --- trunk/dma.h (original) +++ trunk/dma.h 2002-11-05 22:27:39.000000000 -0500 @@ -13,8 +13,11 @@ #include <linux/pci.h> #include <asm/scatterlist.h> -/* structure for bookkeeping of a small, physically-contiguous DMA buffer - with random-access, synchronous usage characteristics */ +/* struct dma_prog_region + + a small, physically-contiguous DMA buffer with random-access, + synchronous usage characteristics +*/ struct dma_prog_region { unsigned char *kvirt; /* kernel virtual address */ @@ -33,17 +36,18 @@ return prog->bus_addr + offset; } - - -/* structure for bookkeeping of a large, non-physically-contiguous DMA buffer - with streaming, asynchronous usage characteristics */ +/* struct dma_region + + a large, non-physically-contiguous DMA buffer with streaming, + asynchronous usage characteristics +*/ struct dma_region { unsigned char *kvirt; /* kernel virtual address */ struct pci_dev *dev; /* PCI device */ unsigned int n_pages; /* # of kernel pages */ unsigned int n_dma_pages; /* # of IOMMU pages */ - struct scatterlist *sglist; + struct scatterlist *sglist; /* IOMMU mapping */ int direction; /* PCI_DMA_TODEVICE, etc */ }; @@ -57,30 +61,16 @@ void dma_region_free(struct dma_region *dma); /* sync the IO bus' view of the buffer with the CPU's view */ -/* XXX DJM - add addr, len arguments */ -void dma_region_sync(struct dma_region *dma); +void dma_region_sync(struct dma_region *dma, unsigned long offset, unsigned long len); /* map the buffer into a user space process */ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); -/* macro to index into a DMA region */ +/* macro to index into a DMA region (or dma_prog_region) */ #define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) ) /* return the DMA bus address of the byte with the given offset relative to the beginning of the dma_region */ -/* XXX DJM take out of line and use binary search */ -static inline dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset) -{ - int i; - struct scatterlist *sg; - - for(i = 0, sg = &dma->sglist[0]; i < dma->n_dma_pages; i++, sg++) { - if(offset < sg_dma_len(sg)) { - return sg_dma_address(sg) + offset; - } - offset -= sg_dma_len(sg); - } - return 0; -} +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset); #endif /* IEEE1394_DMA_H */ Modified: trunk/iso.c ============================================================================== --- trunk/iso.c (original) +++ trunk/iso.c 2002-11-05 22:27:39.000000000 -0500 @@ -13,19 +13,19 @@ void hpsb_iso_stop(struct hpsb_iso *iso) { - if(!iso->driver_started) + if(!iso->flags & HPSB_ISO_DRIVER_STARTED) return; iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? XMIT_STOP : RECV_STOP, 0); - iso->driver_started = 0; + iso->flags &= ~HPSB_ISO_DRIVER_STARTED; } void hpsb_iso_shutdown(struct hpsb_iso *iso) { - if(iso->driver_init) { + if(iso->flags & HPSB_ISO_DRIVER_INIT) { hpsb_iso_stop(iso); iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? XMIT_SHUTDOWN : RECV_SHUTDOWN, 0); - iso->driver_init = 0; + iso->flags &= ~HPSB_ISO_DRIVER_INIT; } dma_region_free(&iso->buf); @@ -96,10 +96,9 @@ } atomic_set(&iso->overflows, 0); - iso->first_packet_cycle = 0; - iso->driver_init = 0; - iso->driver_started = 0; - + iso->flags = 0; + iso->prebuffer = 0; + /* allocate the packet buffer */ if(dma_region_alloc(&iso->buf, iso->buf_packets * iso->buf_stride, host->pdev, dma_direction)) @@ -137,8 +136,8 @@ /* tell the driver to start working */ if(host->driver->isoctl(iso, XMIT_INIT, 0)) goto err; - - iso->driver_init = 1; + + iso->flags |= HPSB_ISO_DRIVER_INIT; return iso; err: @@ -162,8 +161,8 @@ /* tell the driver to start working */ if(host->driver->isoctl(iso, RECV_INIT, 0)) goto err; - - iso->driver_init = 1; + + iso->flags |= HPSB_ISO_DRIVER_INIT; return iso; err: @@ -171,25 +170,81 @@ return NULL; } -int hpsb_iso_start(struct hpsb_iso *iso, int cycle) +static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle) +{ + int retval = iso->host->driver->isoctl(iso, XMIT_START, cycle); + if(retval) + return retval; + + iso->flags |= HPSB_ISO_DRIVER_STARTED; + return retval; +} + +int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) +{ + if(iso->type != HPSB_ISO_XMIT) + return -1; + + if(iso->flags & HPSB_ISO_DRIVER_STARTED) + return 0; + + if(prebuffer < 1) + prebuffer = 1; + + if(prebuffer > iso->buf_packets) + prebuffer = iso->buf_packets; + + iso->prebuffer = prebuffer; + + if(cycle != -1) { + /* pre-fill info->cycle */ + int pkt = iso->first_packet; + int c, i; + + cycle %= 8000; + + c = cycle; + for(i = 0; i < iso->buf_packets; i++) { + struct hpsb_iso_packet_info *info = hpsb_iso_packet_info(iso, pkt); + + info->cycle = c; + + c = (c+1) % 8000; + pkt = (pkt+1) % iso->buf_packets; + } + } + + /* remember the starting cycle; DMA will commence from xmit_queue_packets() */ + iso->start_cycle = cycle; + + return 0; +} + +int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle) { int retval = 0; + + if(iso->type != HPSB_ISO_RECV) + return -1; - if(iso->driver_started) + if(iso->flags & HPSB_ISO_DRIVER_STARTED) return 0; - retval = iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? XMIT_START : RECV_START, cycle); + retval = iso->host->driver->isoctl(iso, RECV_START, cycle); if(retval) return retval; - - iso->driver_started = 1; + + iso->flags |= HPSB_ISO_DRIVER_STARTED; return retval; } int hpsb_iso_xmit_queue_packets(struct hpsb_iso *iso, unsigned int n_packets) { - int i; + int i, retval; int pkt = iso->first_packet; + + if(iso->type != HPSB_ISO_XMIT) + return -1; /* check packet sizes for sanity */ for(i = 0; i < n_packets; i++) { @@ -202,12 +257,28 @@ pkt = (pkt+1) % iso->buf_packets; } - - return iso->host->driver->isoctl(iso, XMIT_QUEUE, n_packets); + + retval = iso->host->driver->isoctl(iso, XMIT_QUEUE, n_packets); + if(retval) + return retval; + + if(iso->prebuffer != 0) { + iso->prebuffer -= n_packets; + if(iso->prebuffer <= 0) { + iso->prebuffer = 0; + return do_iso_xmit_start(iso, + iso->start_cycle); + } + } + + return 0; } int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) { + if(iso->type != HPSB_ISO_RECV) + return -1; + return iso->host->driver->isoctl(iso, RECV_RELEASE, n_packets); } |