[Linux1394-cvslog] rev 653 - trunk
Brought to you by:
aeb,
bencollins
From: SVN U. <dm...@li...> - 2002-10-29 02:07:40
|
Author: dmaas Date: 2002-10-28 21:07:36 -0500 (Mon, 28 Oct 2002) New Revision: 653 Added: trunk/iso.c trunk/iso.h Modified: trunk/Makefile trunk/hosts.h trunk/ohci1394.c trunk/pcilynx.c Log: rawiso merge part 2 - kernel-level rawiso API Added: trunk/iso.h ============================================================================== --- trunk/iso.h (original) +++ trunk/iso.h 2002-10-28 21:07:37.000000000 -0500 @@ -0,0 +1,143 @@ +/* + * IEEE 1394 for Linux + * + * kernel ISO transmission/reception + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#ifndef IEEE1394_ISO_H +#define IEEE1394_ISO_H + +#include "hosts.h" +#include "dma.h" + +/* high-level ISO interface */ + +/* 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 char tag; + unsigned char sy; +}; + +/* + * 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 + * + * 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 + */ + +enum hpsb_iso_type { HPSB_ISO_RECV = 0, HPSB_ISO_XMIT = 1 }; + +struct hpsb_iso { + enum hpsb_iso_type type; + + /* pointer to low-level driver and its private data */ + struct hpsb_host *host; + void *hostdata; + + /* pointer to a wait queue that will receive wakeups */ + wait_queue_head_t *waitq; + + int speed; /* SPEED_100, 200, or 400 */ + int channel; + + /* greatest # of packets between interrupts - controls + the maximum latency of the buffer */ + int irq_interval; + + /* the packet ringbuffer */ + struct dma_region buf; + + /* # of packets in the ringbuffer */ + unsigned int buf_packets; + + /* offset between successive packets - + you can assume that this is a power of 2, + and that packets will never cross a page boundary */ + 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; + + /* 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 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; + + /* flags to track initialization progress */ + int driver_init, driver_started; +}; + +/* functions available to high-level drivers (e.g. raw1394) */ + +/* allocate the buffer and DMA context */ + +struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, + unsigned int buf_packets, + unsigned int max_packet_size, + int channel, + int speed, + int irq_interval, + wait_queue_head_t *waitq); + +struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, + unsigned int buf_packets, + unsigned int max_packet_size, + int channel, + int irq_interval, + wait_queue_head_t *waitq); + +/* start/stop DMA */ +int hpsb_iso_start(struct hpsb_iso *iso, int start_on_cycle); +void hpsb_iso_stop(struct hpsb_iso *iso); + +/* deallocate buffer and DMA context */ +void hpsb_iso_shutdown(struct hpsb_iso *iso); + +/* N packets have been written to the buffer; queue them for transmission */ +int hpsb_iso_xmit_queue_packets(struct hpsb_iso *xmit, unsigned int n_packets); + +/* N packets have been read out of the buffer, re-use the buffer space */ +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_packets); + +/* 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); + +#endif /* IEEE1394_ISO_H */ Modified: trunk/ohci1394.c ============================================================================== --- trunk/ohci1394.c (original) +++ trunk/ohci1394.c 2002-10-28 21:07:37.000000000 -0500 @@ -1944,6 +1944,7 @@ .get_rom = ohci_get_rom, .transmit_packet = ohci_transmit, .devctl = ohci_devctl, + .isoctl = NULL, .hw_csr_reg = ohci_hw_csr_reg, }; Modified: trunk/Makefile ============================================================================== --- trunk/Makefile (original) +++ trunk/Makefile 2002-10-28 21:07:37.000000000 -0500 @@ -5,7 +5,7 @@ export-objs := ieee1394_core.o ohci1394.o cmp.o dma.o ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \ - highlevel.o csr.o nodemgr.o oui.o dma.o + highlevel.o csr.o nodemgr.o oui.o dma.o iso.o obj-$(CONFIG_IEEE1394) += ieee1394.o obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o Modified: trunk/hosts.h ============================================================================== --- trunk/hosts.h (original) +++ trunk/hosts.h 2002-10-28 21:07:37.000000000 -0500 @@ -18,6 +18,7 @@ #define CSR_CONFIG_ROM_SIZE 0x100 struct hpsb_packet; +struct hpsb_iso; struct hpsb_host { struct list_head host_list; @@ -104,6 +105,28 @@ ISO_UNLISTEN_CHANNEL }; +enum isoctl_cmd { + /* rawiso API - see iso.h for the meanings of these commands + * INIT = allocate resources + * START = begin transmission/reception (arg: cycle to start on) + * STOP = halt transmission/reception + * QUEUE/RELEASE = produce/consume packets (arg: # of packets) + * SHUTDOWN = deallocate resources + */ + + XMIT_INIT, + XMIT_START, + XMIT_STOP, + XMIT_QUEUE, + XMIT_SHUTDOWN, + + RECV_INIT, + RECV_START, + RECV_STOP, + RECV_RELEASE, + RECV_SHUTDOWN, +}; + enum reset_types { /* 166 microsecond reset -- only type of reset available on non-1394a capable IEEE 1394 controllers */ @@ -141,6 +164,11 @@ */ int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg); + /* 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 *iso, enum isoctl_cmd command, int arg); + /* This function is mainly to redirect local CSR reads/locks to the iso * management registers (bus manager id, bandwidth available, channels * available) to the hardware registers in OHCI. reg is 0,1,2,3 for bus Modified: trunk/pcilynx.c ============================================================================== --- trunk/pcilynx.c (original) +++ trunk/pcilynx.c 2002-10-28 21:07:37.000000000 -0500 @@ -1706,6 +1706,7 @@ .get_rom = get_lynx_rom, .transmit_packet = lynx_transmit, .devctl = lynx_devctl, + .isoctl = NULL, }; MODULE_AUTHOR("Andreas E. Bombe <and...@mu...>"); Added: trunk/iso.c ============================================================================== --- trunk/iso.c (original) +++ trunk/iso.c 2002-10-28 21:07:38.000000000 -0500 @@ -0,0 +1,224 @@ +/* + * IEEE 1394 for Linux + * + * kernel ISO transmission/reception + * + * Copyright (C) 2002 Maas Digital LLC + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. + */ + +#include "iso.h" + +void hpsb_iso_stop(struct hpsb_iso *iso) +{ + if(!iso->driver_started) + return; + + iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? XMIT_STOP : RECV_STOP, 0); + iso->driver_started = 0; +} + +void hpsb_iso_shutdown(struct hpsb_iso *iso) +{ + if(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; + } + + dma_region_free(&iso->buf); + kfree(iso); +} + +static struct hpsb_iso* hpsb_iso_common_init(struct hpsb_host *host, enum hpsb_iso_type type, + unsigned int buf_packets, + unsigned int max_packet_size, + int channel, + int irq_interval, + wait_queue_head_t *waitq) +{ + struct hpsb_iso *iso; + unsigned int packet_plus_info; + int dma_direction; + + /* make sure driver supports the ISO API */ + if(!host->driver->isoctl) + return NULL; + + /* sanitize parameters */ + + if(buf_packets < 2) + buf_packets = 2; + + if(irq_interval < 1 || irq_interval > buf_packets / 2) + irq_interval = buf_packets / 2; + + if(max_packet_size + sizeof(struct hpsb_iso_packet_info) > PAGE_SIZE) + return NULL; + + /* size of packet payload plus the per-packet info must be a power of 2 + and at most equal to the page size */ + + for(packet_plus_info = 256; packet_plus_info < PAGE_SIZE; packet_plus_info *= 2) { + if(packet_plus_info >= (max_packet_size + sizeof(struct hpsb_iso_packet_info))) { + break; + } + } + + /* allocate and write the struct hpsb_iso */ + + iso = kmalloc(sizeof(*iso), SLAB_KERNEL); + if(!iso) + return NULL; + + iso->type = type; + iso->host = host; + iso->hostdata = NULL; + iso->waitq = waitq; + iso->channel = channel; + iso->irq_interval = irq_interval; + dma_region_init(&iso->buf); + iso->buf_packets = buf_packets; + iso->buf_stride = packet_plus_info; + iso->max_packet_size = max_packet_size; + iso->packet_data_offset = 0; + iso->packet_info_offset = max_packet_size; + iso->first_packet = 0; + + if(iso->type == HPSB_ISO_XMIT) { + atomic_set(&iso->n_dma_packets, 0); + dma_direction = PCI_DMA_TODEVICE; + } else { + atomic_set(&iso->n_dma_packets, iso->buf_packets); + dma_direction = PCI_DMA_FROMDEVICE; + } + + atomic_set(&iso->overflows, 0); + iso->first_packet_cycle = 0; + iso->driver_init = 0; + iso->driver_started = 0; + + /* allocate the packet buffer */ + if(dma_region_alloc(&iso->buf, iso->buf_packets * iso->buf_stride, + host->pdev, dma_direction)) + goto err; + + return iso; + +err: + hpsb_iso_shutdown(iso); + return NULL; +} + +int hpsb_iso_n_ready(struct hpsb_iso* iso) +{ + return iso->buf_packets - atomic_read(&iso->n_dma_packets); +} + + +struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, + unsigned int buf_packets, + unsigned int max_packet_size, + int channel, + int speed, + int irq_interval, + wait_queue_head_t *waitq) +{ + struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_XMIT, + buf_packets, max_packet_size, + channel, irq_interval, waitq); + if(!iso) + return NULL; + + iso->speed = speed; + + /* tell the driver to start working */ + if(host->driver->isoctl(iso, XMIT_INIT, 0)) + goto err; + + iso->driver_init = 1; + return iso; + +err: + hpsb_iso_shutdown(iso); + return NULL; +} + +struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, + unsigned int buf_packets, + unsigned int max_packet_size, + int channel, + int irq_interval, + wait_queue_head_t *waitq) +{ + struct hpsb_iso *iso = hpsb_iso_common_init(host, HPSB_ISO_RECV, + buf_packets, max_packet_size, + channel, irq_interval, waitq); + if(!iso) + return NULL; + + /* tell the driver to start working */ + if(host->driver->isoctl(iso, RECV_INIT, 0)) + goto err; + + iso->driver_init = 1; + return iso; + +err: + hpsb_iso_shutdown(iso); + return NULL; +} + +int hpsb_iso_start(struct hpsb_iso *iso, int cycle) +{ + int retval = 0; + + if(iso->driver_started) + return 0; + + retval = iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ? XMIT_START : RECV_START, cycle); + if(retval) + return retval; + + iso->driver_started = 1; + return retval; +} + +int hpsb_iso_xmit_queue_packets(struct hpsb_iso *iso, unsigned int n_packets) +{ + int i; + int pkt = iso->first_packet; + + /* check packet sizes for sanity */ + for(i = 0; i < n_packets; i++) { + struct hpsb_iso_packet_info *info = hpsb_iso_packet_info(iso, pkt); + if(info->len > iso->max_packet_size) { + printk(KERN_ERR "hpsb_iso_xmit_queue_packets: packet too long (%u, max is %u)\n", + info->len, iso->max_packet_size); + return -EINVAL; + } + + pkt = (pkt+1) % iso->buf_packets; + } + + return iso->host->driver->isoctl(iso, XMIT_QUEUE, n_packets); +} + +int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) +{ + return iso->host->driver->isoctl(iso, RECV_RELEASE, n_packets); +} + +unsigned char* hpsb_iso_packet_data(struct hpsb_iso *iso, unsigned int pkt) +{ + return (iso->buf.kvirt + pkt * iso->buf_stride) + + iso->packet_data_offset; +} + +struct hpsb_iso_packet_info* hpsb_iso_packet_info(struct hpsb_iso *iso, unsigned int pkt) +{ + return (struct hpsb_iso_packet_info*) ((iso->buf.kvirt + pkt * iso->buf_stride) + + iso->packet_info_offset); +} |