Commit 8e14bc0b authored by Ben Collins's avatar Ben Collins Committed by Jens Axboe

[PATCH] IEEE-1394 Updates

- Converts Video1394 to PCI dma.
- Converts ioctl's to standard interface.
- Various minor fixes
- Merges from 2.5.x tree
parent fe2701f1
......@@ -18,6 +18,8 @@ obj-$(CONFIG_IEEE1394_CMP) += cmp.o
clean-files := oui.c
ieee1394.o: $(ieee1394-objs)
$(LD) $(LDFLAGS) -r -o $@ $(ieee1394-objs)
ifeq ($(obj),)
obj = .
......
......@@ -598,6 +598,8 @@ static struct buffer *buffer_alloc(int size)
struct buffer *b;
b = kmalloc(sizeof *b + size, SLAB_KERNEL);
if (b == NULL)
return NULL;
b->head = 0;
b->tail = 0;
b->length = 0;
......
......@@ -3,8 +3,8 @@
#ifndef __AMDTP_H
#define __AMDTP_H
#include <asm/ioctl.h>
#include <asm/types.h>
#include "ieee1394-ioctl.h"
/* The userspace interface for the Audio & Music Data Transmission
* Protocol driver is really simple. First, open /dev/amdtp, use the
......@@ -57,13 +57,6 @@
*
*/
/* We use '#' for our ioctl magic number because it's cool. */
#define AMDTP_IOC_CHANNEL _IOW('#', 0, sizeof (struct amdtp_ioctl))
#define AMDTP_IOC_PLUG _IOW('#', 1, sizeof (struct amdtp_ioctl))
#define AMDTP_IOC_PING _IOW('#', 2, sizeof (struct amdtp_ioctl))
#define AMDTP_IOC_ZAP _IO('#', 3)
enum {
AMDTP_FORMAT_RAW,
AMDTP_FORMAT_IEC958_PCM,
......
......@@ -25,8 +25,7 @@ void dma_prog_region_init(struct dma_prog_region *prog)
int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev)
{
/* round up to page size */
if(n_bytes % PAGE_SIZE)
n_bytes += PAGE_SIZE - (n_bytes & PAGE_SIZE);
n_bytes = round_up_to_page(n_bytes);
prog->n_pages = n_bytes / PAGE_SIZE;
......@@ -70,8 +69,7 @@ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_d
unsigned int i, n_pages;
/* round up to page size */
if(n_bytes % PAGE_SIZE)
n_bytes += PAGE_SIZE - (n_bytes & PAGE_SIZE);
n_bytes = round_up_to_page(n_bytes);
n_pages = n_bytes / PAGE_SIZE;
......@@ -210,7 +208,7 @@ dma_region_pagefault(struct vm_area_struct *area, unsigned long address, int wri
}
static struct vm_operations_struct dma_region_vm_ops = {
nopage: dma_region_pagefault,
.nopage = dma_region_pagefault,
};
int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma)
......
......@@ -73,4 +73,12 @@ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_s
relative to the beginning of the dma_region */
dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset);
/* round up a number of bytes to be a multiple of the PAGE_SIZE */
static inline unsigned long round_up_to_page(unsigned long len)
{
if(len % PAGE_SIZE)
len += PAGE_SIZE - (len % PAGE_SIZE);
return len;
}
#endif /* IEEE1394_DMA_H */
......@@ -918,6 +918,7 @@ static int do_dv1394_init(struct video_card *video, struct dv1394_init *init)
u64 chan_mask;
int retval = -EINVAL;
debug_printk( "dv1394: initialising %d\n", video->id );
if(init->api_version != DV1394_API_VERSION)
goto err;
......@@ -1186,6 +1187,7 @@ static void stop_dma(struct video_card *video)
if( (reg_read(video->ohci, video->ohci_IsoXmitContextControlClear) & (1 << 10)) ||
(reg_read(video->ohci, video->ohci_IsoRcvContextControlClear) & (1 << 10)) ) {
/* still active */
debug_printk("dv1394: stop_dma: DMA not stopped yet\n" );
mb();
} else {
debug_printk("dv1394: stop_dma: DMA stopped safely after %d ms\n", i/10);
......@@ -1199,6 +1201,8 @@ static void stop_dma(struct video_card *video)
printk(KERN_ERR "dv1394: stop_dma: DMA still going after %d ms!\n", i/10);
}
}
else
debug_printk("dv1394: stop_dma: already stopped.\n");
spin_unlock_irqrestore(&video->spinlock, flags);
}
......@@ -1226,7 +1230,8 @@ static int do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
/* disable interrupts for IT context */
reg_write(video->ohci, OHCI1394_IsoXmitIntMaskClear, (1 << video->ohci_it_ctx));
clear_bit(video->ohci_it_ctx, &video->ohci->it_ctx_usage);
/* remove tasklet */
ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
debug_printk("dv1394: IT context %d released\n", video->ohci_it_ctx);
video->ohci_it_ctx = -1;
}
......@@ -1240,23 +1245,14 @@ static int do_dv1394_shutdown(struct video_card *video, int free_dv_buf)
/* disable interrupts for IR context */
reg_write(video->ohci, OHCI1394_IsoRecvIntMaskClear, (1 << video->ohci_ir_ctx));
clear_bit(video->ohci_ir_ctx, &video->ohci->ir_ctx_usage);
/* remove tasklet */
ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
debug_printk("dv1394: IR context %d released\n", video->ohci_ir_ctx);
video->ohci_ir_ctx = -1;
}
spin_unlock_irqrestore(&video->spinlock, flags);
/* remove tasklets */
if(video->ohci_it_ctx != -1) {
ohci1394_unregister_iso_tasklet(video->ohci, &video->it_tasklet);
video->ohci_it_ctx = -1;
}
if(video->ohci_ir_ctx != -1) {
ohci1394_unregister_iso_tasklet(video->ohci, &video->ir_tasklet);
video->ohci_ir_ctx = -1;
}
/* release the ISO channel */
if(video->channel != -1) {
u64 chan_mask;
......@@ -1612,7 +1608,7 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
case DV1394_SUBMIT_FRAMES: {
case DV1394_IOC_SUBMIT_FRAMES: {
unsigned int n_submit;
if( !video_card_initialized(video) ) {
......@@ -1666,7 +1662,7 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
break;
}
case DV1394_WAIT_FRAMES: {
case DV1394_IOC_WAIT_FRAMES: {
unsigned int n_wait;
if( !video_card_initialized(video) ) {
......@@ -1715,7 +1711,7 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
break;
}
case DV1394_RECEIVE_FRAMES: {
case DV1394_IOC_RECEIVE_FRAMES: {
unsigned int n_recv;
if( !video_card_initialized(video) ) {
......@@ -1748,7 +1744,7 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
break;
}
case DV1394_START_RECEIVE: {
case DV1394_IOC_START_RECEIVE: {
if( !video_card_initialized(video) ) {
ret = do_dv1394_init_default(video);
if(ret)
......@@ -1765,7 +1761,7 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
break;
}
case DV1394_INIT: {
case DV1394_IOC_INIT: {
struct dv1394_init init;
if(arg == (unsigned long) NULL) {
ret = do_dv1394_init_default(video);
......@@ -1779,12 +1775,12 @@ static int dv1394_ioctl(struct inode *inode, struct file *file,
break;
}
case DV1394_SHUTDOWN:
case DV1394_IOC_SHUTDOWN:
ret = do_dv1394_shutdown(video, 0);
break;
case DV1394_GET_STATUS: {
case DV1394_IOC_GET_STATUS: {
struct dv1394_status status;
if( !video_card_initialized(video) ) {
......@@ -2346,6 +2342,7 @@ static void ir_tasklet_func(unsigned long data)
dbc = (int) (p->cip_h1 >> 24);
if ( video->continuity_counter != -1 && dbc > ((video->continuity_counter + 1) % 256) )
{
printk(KERN_WARNING "dv1394: discontinuity detected, dropping all frames\n" );
video->dropped_frames += video->n_clear_frames + 1;
video->first_frame = 0;
video->n_clear_frames = 0;
......@@ -2364,9 +2361,8 @@ static void ir_tasklet_func(unsigned long data)
video->n_clear_frames++;
if (video->n_clear_frames > video->n_frames) {
video->dropped_frames++;
video->n_clear_frames--;
if (video->n_clear_frames < 0)
video->n_clear_frames = 0;
printk(KERN_WARNING "dv1394: dropped a frame during reception\n" );
video->n_clear_frames = video->n_frames-1;
video->first_clear_frame = (video->first_clear_frame + 1) % video->n_frames;
}
if (video->first_clear_frame == -1)
......@@ -2375,7 +2371,6 @@ static void ir_tasklet_func(unsigned long data)
/* get the next frame */
video->active_frame = (video->active_frame + 1) % video->n_frames;
f = video->frames[video->active_frame];
irq_printk(" frame received, active_frame = %d, n_clear_frames = %d, first_clear_frame = %d\n",
video->active_frame, video->n_clear_frames, video->first_clear_frame);
}
......
......@@ -200,48 +200,7 @@
/* ioctl() commands */
enum {
/* I don't like using 0 as a valid ioctl() */
DV1394_INVALID = 0,
/* get the driver ready to transmit video.
pass a struct dv1394_init* as the parameter (see below),
or NULL to get default parameters */
DV1394_INIT,
/* stop transmitting video and free the ringbuffer */
DV1394_SHUTDOWN,
/* submit N new frames to be transmitted, where
the index of the first new frame is first_clear_buffer,
and the index of the last new frame is
(first_clear_buffer + N) % n_frames */
DV1394_SUBMIT_FRAMES,
/* block until N buffers are clear (pass N as the parameter)
Because we re-transmit the last frame on underrun, there
will at most be n_frames - 1 clear frames at any time */
DV1394_WAIT_FRAMES,
/* capture new frames that have been received, where
the index of the first new frame is first_clear_buffer,
and the index of the last new frame is
(first_clear_buffer + N) % n_frames */
DV1394_RECEIVE_FRAMES,
DV1394_START_RECEIVE,
/* pass a struct dv1394_status* as the parameter (see below) */
DV1394_GET_STATUS,
};
#include "ieee1394-ioctl.h"
enum pal_or_ntsc {
......
......@@ -77,7 +77,7 @@
printk(KERN_ERR fmt, ## args)
static char version[] __devinitdata =
"$Rev: 641 $ Ben Collins <bcollins@debian.org>";
"$Rev: 770 $ Ben Collins <bcollins@debian.org>";
/* Our ieee1394 highlevel driver */
#define ETHER1394_DRIVER_NAME "ether1394"
......@@ -368,6 +368,7 @@ static void ether1394_add_host (struct hpsb_host *host)
if (register_netdev (dev)) {
ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n");
kfree (dev);
kfree (hi);
return;
}
......
......@@ -164,17 +164,19 @@ int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, u64 start)
return retval;
}
void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
unsigned int channel)
{
if (channel > 63) {
HPSB_ERR("%s called with invalid channel", __FUNCTION__);
return;
return -EINVAL;
}
if (host->iso_listen_count[channel]++ == 0) {
host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
return host->driver->devctl(host, ISO_LISTEN_CHANNEL, channel);
}
return 0;
}
void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
......
......@@ -150,7 +150,7 @@ int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, u64 start);
* Enable or disable receving a certain isochronous channel through the
* iso_receive op.
*/
void hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
unsigned int channel);
void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
unsigned int channel);
......
......@@ -38,9 +38,15 @@ static int dummy_devctl(struct hpsb_host *h, enum devctl_cmd c, int arg)
return -1;
}
static int dummy_isoctl(struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg)
{
return -1;
}
static struct hpsb_host_driver dummy_driver = {
.transmit_packet = dummy_transmit_packet,
.devctl = dummy_devctl
.devctl = dummy_devctl,
.isoctl = dummy_isoctl
};
/**
......@@ -63,9 +69,11 @@ int hpsb_ref_host(struct hpsb_host *host)
spin_lock_irqsave(&hosts_lock, flags);
list_for_each(lh, &hosts) {
if (host == list_entry(lh, struct hpsb_host, host_list)) {
if (host->driver->devctl(host, MODIFY_USAGE, 1)) {
host->driver->devctl(host, MODIFY_USAGE, 1);
host->refcount++;
retval = 1;
}
break;
}
}
......
......@@ -108,9 +108,9 @@ enum devctl_cmd {
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)
* START = begin transmission/reception
* STOP = halt transmission/reception
* QUEUE/RELEASE = produce/consume packets (arg: # of packets)
* QUEUE/RELEASE = produce/consume packets
* SHUTDOWN = deallocate resources
*/
......@@ -121,6 +121,9 @@ enum isoctl_cmd {
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,
......@@ -170,10 +173,11 @@ struct hpsb_host_driver {
*/
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.
/* ISO transmission/reception functions. Return 0 on success, -1
* (or -EXXX errno code) 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);
int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg);
/* This function is mainly to redirect local CSR reads/locks to the iso
* management registers (bus manager id, bandwidth available, channels
......
/* Base file for all ieee1394 ioctl's. Linux-1394 has allocated base '#'
* with a range of 0x00-0x3f. */
#ifndef __IEEE1394_IOCTL_H
#define __IEEE1394_IOCTL_H
#include <asm/ioctl.h>
#include <asm/types.h>
/* AMDTP Gets 6 */
#define AMDTP_IOC_CHANNEL _IOW('#', 0x00, struct amdtp_ioctl)
#define AMDTP_IOC_PLUG _IOW('#', 0x01, struct amdtp_ioctl)
#define AMDTP_IOC_PING _IOW('#', 0x02, struct amdtp_ioctl)
#define AMDTP_IOC_ZAP _IO ('#', 0x03)
/* DV1394 Gets 10 */
/* Get the driver ready to transmit video. pass a struct dv1394_init* as
* the parameter (see below), or NULL to get default parameters */
#define DV1394_IOC_INIT _IOW('#', 0x06, struct dv1394_init)
/* Stop transmitting video and free the ringbuffer */
#define DV1394_IOC_SHUTDOWN _IO ('#', 0x07)
/* Submit N new frames to be transmitted, where the index of the first new
* frame is first_clear_buffer, and the index of the last new frame is
* (first_clear_buffer + N) % n_frames */
#define DV1394_IOC_SUBMIT_FRAMES _IO ('#', 0x08)
/* Block until N buffers are clear (pass N as the parameter) Because we
* re-transmit the last frame on underrun, there will at most be n_frames
* - 1 clear frames at any time */
#define DV1394_IOC_WAIT_FRAMES _IO ('#', 0x09)
/* Capture new frames that have been received, where the index of the
* first new frame is first_clear_buffer, and the index of the last new
* frame is (first_clear_buffer + N) % n_frames */
#define DV1394_IOC_RECEIVE_FRAMES _IO ('#', 0x0a)
/* Tell card to start receiving DMA */
#define DV1394_IOC_START_RECEIVE _IO ('#', 0x0b)
/* Pass a struct dv1394_status* as the parameter */
#define DV1394_IOC_GET_STATUS _IOR('#', 0x0c, struct dv1394_status)
/* Video1394 Gets 10 */
#define VIDEO1394_IOC_LISTEN_CHANNEL \
_IOWR('#', 0x10, struct video1394_mmap)
#define VIDEO1394_IOC_UNLISTEN_CHANNEL \
_IOW ('#', 0x11, int)
#define VIDEO1394_IOC_LISTEN_QUEUE_BUFFER \
_IOW ('#', 0x12, struct video1394_wait)
#define VIDEO1394_IOC_LISTEN_WAIT_BUFFER \
_IOWR('#', 0x13, struct video1394_wait)
#define VIDEO1394_IOC_TALK_CHANNEL \
_IOWR('#', 0x14, struct video1394_mmap)
#define VIDEO1394_IOC_UNTALK_CHANNEL \
_IOW ('#', 0x15, int)
#define VIDEO1394_IOC_TALK_QUEUE_BUFFER \
_IOW ('#', 0x16, sizeof (struct video1394_wait) + \
sizeof (struct video1394_queue_variable))
#define VIDEO1394_IOC_TALK_WAIT_BUFFER \
_IOW ('#', 0x17, struct video1394_wait)
#define VIDEO1394_IOC_LISTEN_POLL_BUFFER \
_IOWR('#', 0x18, struct video1394_wait)
/* Raw1394's ISO interface */
#define RAW1394_IOC_ISO_XMIT_INIT \
_IOW ('#', 0x1a, struct raw1394_iso_status)
#define RAW1394_IOC_ISO_RECV_INIT \
_IOWR('#', 0x1b, struct raw1394_iso_status)
#define RAW1394_IOC_ISO_RECV_START \
_IOC (_IOC_WRITE, '#', 0x1c, sizeof(int) * 3)
#define RAW1394_IOC_ISO_XMIT_START \
_IOC (_IOC_WRITE, '#', 0x1d, sizeof(int) * 2)
#define RAW1394_IOC_ISO_XMIT_RECV_STOP \
_IO ('#', 0x1e)
#define RAW1394_IOC_ISO_GET_STATUS \
_IOR ('#', 0x1f, struct raw1394_iso_status)
#define RAW1394_IOC_ISO_SHUTDOWN \
_IO ('#', 0x20)
#define RAW1394_IOC_ISO_QUEUE_ACTIVITY \
_IO ('#', 0x21)
#define RAW1394_IOC_ISO_RECV_LISTEN_CHANNEL \
_IOW ('#', 0x22, unsigned char)
#define RAW1394_IOC_ISO_RECV_UNLISTEN_CHANNEL \
_IOW ('#', 0x23, unsigned char)
#define RAW1394_IOC_ISO_RECV_SET_CHANNEL_MASK \
_IOW ('#', 0x24, u64)
#define RAW1394_IOC_ISO_RECV_PACKETS \
_IOW ('#', 0x25, struct raw1394_iso_packets)
#define RAW1394_IOC_ISO_RECV_RELEASE_PACKETS \
_IOW ('#', 0x26, unsigned int)
#define RAW1394_IOC_ISO_XMIT_PACKETS \
_IOW ('#', 0x27, struct raw1394_iso_packets)
#define RAW1394_IOC_ISO_XMIT_SYNC \
_IO ('#', 0x28)
#endif /* __IEEE1394_IOCTL_H */
......@@ -1055,7 +1055,7 @@ static int ieee1394_get_chardev(int blocknum,
{
int ret = 0;
if( (blocknum < 0) || (blocknum > 15) )
if ((blocknum < 0) || (blocknum > 15))
return ret;
read_lock(&ieee1394_chardevs_lock);
......@@ -1063,10 +1063,10 @@ static int ieee1394_get_chardev(int blocknum,
*module = ieee1394_chardevs[blocknum].module;
*file_ops = ieee1394_chardevs[blocknum].file_ops;
if(*file_ops == NULL)
if (*file_ops == NULL)
goto out;
if(!try_module_get(*module))
if (!try_module_get(*module))
goto out;
/* success! */
......@@ -1126,14 +1126,14 @@ static int ieee1394_dispatch_open(struct inode *inode, struct file *file)
if(retval == 0) {
/* If the open() succeeded, then ieee1394 will be left
with an extra module reference, so we discard it here.
* with an extra module reference, so we discard it here.
*
* The task-specific driver still has the extra reference
* given to it by ieee1394_get_chardev(). This extra
* reference prevents the module from unloading while the
* file is open, and will be dropped by the VFS when the
* file is released. */
The task-specific driver still has the extra
reference given to it by ieee1394_get_chardev().
This extra reference prevents the module from
unloading while the file is open, and will be
dropped by the VFS when the file is released.
*/
module_put(THIS_MODULE);
} else {
/* point the file's f_ops back to ieee1394. The VFS will then
......@@ -1142,11 +1142,10 @@ static int ieee1394_dispatch_open(struct inode *inode, struct file *file)
file->f_op = &ieee1394_chardev_ops;
/* if the open() failed, then we need to drop the
extra reference we gave to the task-specific
driver */
module_put(module);
/* If the open() failed, then we need to drop the extra
* reference we gave to the task-specific driver. */
module_put(module);
}
return retval;
......@@ -1298,10 +1297,16 @@ EXPORT_SYMBOL(hpsb_iso_xmit_init);
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_xmit_queue_packet);
EXPORT_SYMBOL(hpsb_iso_xmit_sync);
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_packet_sent);
EXPORT_SYMBOL(hpsb_iso_packet_received);
EXPORT_SYMBOL(hpsb_iso_wake);
This diff is collapsed.
......@@ -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;
/*
* 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
*/
/* length of the data payload, in bytes (not including the isochronous header) */
__u16 len;
/* (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 };
......@@ -46,47 +58,48 @@ struct hpsb_iso {
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*);
/* wait for buffer space */
wait_queue_head_t waitq;
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;
/* # 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;
/* size of data_buf, in bytes (always a multiple of PAGE_SIZE) */
unsigned int buf_size;
/* largest possible packet size, in bytes */
unsigned int max_packet_size;
/* ringbuffer of packet descriptors in regular kernel memory */
struct hpsb_iso_packet_info *infos;
/* offset relative to (buf.kvirt + N*buf_stride) at which
the data payload begins for packet N */
int packet_data_offset;
/* # of packets in the ringbuffer */
unsigned int buf_packets;
/* 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;
/* the index of the next packet that will be transmitted
or received by the 1394 hardware */
int pkt_dma;
/* 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 +112,12 @@ struct hpsb_iso {
/* # 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,30 +125,40 @@ struct hpsb_iso {
/* 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);
int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync);
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);
/* queue a packet for transmission. 'offset' is relative to the beginning of the
DMA buffer, where the packet's data payload should already have been placed */
int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy);
/* wait until all queued packets have been transmitted to the bus */
int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
/* 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);
......@@ -139,10 +166,19 @@ 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);
/* the following are callbacks available to low-level drivers */
/* call after a packet has been transmitted to the bus (interrupt context is OK)
'cycle' is the _exact_ cycle the packet was sent on
'error' should be non-zero if some sort of error occurred when sending the packet
*/
void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
/* call after a packet has been received (interrupt context OK) */
void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
u16 cycle, u8 channel, u8 tag, u8 sy);
/* 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);
/* call to wake waiting processes after buffer space has opened up. */
void hpsb_iso_wake(struct hpsb_iso *iso);
#endif /* IEEE1394_ISO_H */
......@@ -1089,8 +1089,7 @@ static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned
static void nodemgr_remove_node(struct node_entry *ne)
{
HPSB_DEBUG("%s removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
(ne->host->node_id == ne->nodeid) ? "Host" : "Device",
HPSB_DEBUG("Device removed: Node[" NODE_BUS_FMT "] GUID[%016Lx] [%s]",
NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid,
ne->vendor_name ?: "Unknown");
......
This diff is collapsed.
......@@ -109,6 +109,7 @@ struct dma_rcv_ctx {
int ctrlClear;
int ctrlSet;
int cmdPtr;
int ctxtMatch;
};
/* DMA transmit context */
......@@ -145,7 +146,8 @@ struct ohci1394_iso_tasklet {
struct tasklet_struct tasklet;
struct list_head link;
int context;
enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE } type;
enum { OHCI_ISO_TRANSMIT, OHCI_ISO_RECEIVE,
OHCI_ISO_MULTICHANNEL_RECEIVE } type;
};
struct ti_ohci {
......@@ -187,18 +189,28 @@ struct ti_ohci {
struct dma_trm_ctx at_req_context;
/* iso receive */
struct dma_rcv_ctx ir_context;
struct ohci1394_iso_tasklet ir_tasklet;
spinlock_t IR_channel_lock;
int nb_iso_rcv_ctx;
unsigned long ir_ctx_usage; /* use test_and_set_bit() for atomicity */
unsigned long ir_multichannel_used; /* ditto */
spinlock_t IR_channel_lock;
/* iso receive (legacy API) */
u64 ir_legacy_channels; /* note: this differs from ISO_channel_usage;
it only accounts for channels listened to
by the legacy API, so that we can know when
it is safe to free the legacy API context */
struct dma_rcv_ctx ir_legacy_context;
struct ohci1394_iso_tasklet ir_legacy_tasklet;
/* 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 */
/* iso transmit (legacy API) */
struct dma_trm_ctx it_legacy_context;
struct ohci1394_iso_tasklet it_legacy_tasklet;
u64 ISO_channel_usage;
/* IEEE-1394 part follows */
......@@ -385,7 +397,7 @@ static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
/* OHCI evt_* error types, table 3-2 of the OHCI 1.1 spec. */
#define EVT_NO_STATUS 0x0 /* No event status */
#define EVT_RESERVED 0x1 /* Reserved, not used !!! */
#define EVT_RESERVED_A 0x1 /* Reserved, not used !!! */
#define EVT_LONG_PACKET 0x2 /* The revc data was longer than the buf */
#define EVT_MISSING_ACK 0x3 /* A subaction gap was detected before an ack
arrived, or recv'd ack had a parity error */
......@@ -404,6 +416,17 @@ static inline u32 reg_read(const struct ti_ohci *ohci, int offset)
16-bit host memory write */
#define EVT_BUS_RESET 0x9 /* Identifies a PHY packet in the recv buffer as
being a synthesized bus reset packet */
#define EVT_TIMEOUT 0xa /* Indicates that the asynchronous transmit response
packet expired and was not transmitted, or that an
IT DMA context experienced a skip processing overflow */
#define EVT_TCODE_ERR 0xb /* A bad tCode is associated with this packet.
The packet was flushed */
#define EVT_RESERVED_B 0xc /* Reserved, not used !!! */
#define EVT_RESERVED_C 0xd /* Reserved, not used !!! */
#define EVT_UNKNOWN 0xe /* An error condition has occurred that cannot be
represented by any other event codes defined herein. */
#define EVT_FLUSHED 0xf /* Send by the link side of output FIFO when asynchronous
packets are being flushed due to a bus reset. */
#define OHCI1394_TCODE_PHY 0xE
......@@ -416,8 +439,8 @@ int ohci1394_register_iso_tasklet(struct ti_ohci *ohci,
void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci,
struct ohci1394_iso_tasklet *tasklet);
void ohci1394_stop_context (struct ti_ohci *ohci, int reg, char *msg);
/* returns zero if successful, one if DMA context is locked up */
int ohci1394_stop_context (struct ti_ohci *ohci, int reg, char *msg);
struct ti_ohci *ohci1394_get_struct(int card_num);
#endif
This diff is collapsed.
......@@ -95,6 +95,7 @@ struct ti_lynx {
struct lynx_send_data {
pcl_t pcl_start, pcl;
struct list_head queue;
struct list_head pcl_queue; /* this queue contains at most one packet */
spinlock_t queue_lock;
dma_addr_t header_dma, data_dma;
int channel;
......@@ -514,13 +515,13 @@ static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan)
static quadlet_t lynx_csr_rom[] = {
/* bus info block offset (hex) */
_(0x04040000), /* info/CRC length, CRC 400 */
_(0x04046aaf), /* info/CRC length, CRC 400 */
_(0x31333934), /* 1394 magic number 404 */
_(0xf064a000), /* misc. settings 408 */
_(0x08002850), /* vendor ID, chip ID high 40c */
_(0x0000ffff), /* chip ID low 410 */
/* root directory */
_(0x00090000), /* directory length, CRC 414 */
_(0x00095778), /* directory length, CRC 414 */
_(0x03080028), /* vendor ID (Texas Instr.) 418 */
_(0x81000008), /* offset to textual ID 41c */
_(0x0c000200), /* node capabilities 420 */
......@@ -530,8 +531,8 @@ static quadlet_t lynx_csr_rom[] = {
_(0x81000014), /* offset to textual ID 430 */
_(0x09000000), /* node hardware version 434 */
_(0x81000018), /* offset to textual ID 438 */
/* module vendor ID textual */
_(0x00070000), /* CRC length, CRC 43c */
/* module vendor ID textual */
_(0x00070812), /* CRC length, CRC 43c */
_(0x00000000), /* 440 */
_(0x00000000), /* 444 */
_(0x54455841), /* "Texas Instruments" 448 */
......@@ -540,25 +541,25 @@ static quadlet_t lynx_csr_rom[] = {
_(0x4d454e54), /* 454 */
_(0x53000000), /* 458 */
/* node unique ID leaf */
_(0x00020000), /* CRC length, CRC 45c */
_(0x00022ead), /* CRC length, CRC 45c */
_(0x08002850), /* vendor ID, chip ID high 460 */
_(0x0000ffff), /* chip ID low 464 */
/* module dependent info */
_(0x00050000), /* CRC length, CRC 468 */
_(0x0005d837), /* CRC length, CRC 468 */
_(0x81000012), /* offset to module textual ID 46c */
_(0x81000017), /* textual descriptor 470 */
_(0x39010000), /* SRAM size 474 */
_(0x3a010000), /* AUXRAM size 478 */
_(0x3b000000), /* AUX device 47c */
/* module textual ID */
_(0x00050000), /* CRC length, CRC 480 */
_(0x000594df), /* CRC length, CRC 480 */
_(0x00000000), /* 484 */
_(0x00000000), /* 488 */
_(0x54534231), /* "TSB12LV21" 48c */
_(0x324c5632), /* 490 */
_(0x31000000), /* 494 */
/* part number */
_(0x00060000), /* CRC length, CRC 498 */
_(0x00068405), /* CRC length, CRC 498 */
_(0x00000000), /* 49c */
_(0x00000000), /* 4a0 */
_(0x39383036), /* "9806000-0001" 4a4 */
......@@ -566,14 +567,14 @@ static quadlet_t lynx_csr_rom[] = {
_(0x30303031), /* 4ac */
_(0x20000001), /* 4b0 */
/* module hardware version textual */
_(0x00050000), /* CRC length, CRC 4b4 */
_(0x00056501), /* CRC length, CRC 4b4 */
_(0x00000000), /* 4b8 */
_(0x00000000), /* 4bc */
_(0x5453424b), /* "TSBKPCITST" 4c0 */
_(0x50434954), /* 4c4 */
_(0x53540000), /* 4c8 */
/* node hardware version textual */
_(0x00050000), /* CRC length, CRC 4d0 */
_(0x0005d805), /* CRC length, CRC 4d0 */
_(0x00000000), /* 4d4 */
_(0x00000000), /* 4d8 */
_(0x54534232), /* "TSB21LV03" 4dc */
......
#ifndef IEEE1394_RAW1394_PRIVATE_H
#define IEEE1394_RAW1394_PRIVATE_H
/* header for definitions that are private to the raw1394 driver
and not visible to user-space */
#define RAW1394_DEVICE_MAJOR 171
#define RAW1394_DEVICE_NAME "raw1394"
struct iso_block_store {
atomic_t refcount;
size_t data_size;
quadlet_t data[0];
};
enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0,
RAW1394_ISO_RECV = 1,
RAW1394_ISO_XMIT = 2 };
struct file_info {
struct list_head list;
enum { opened, initialized, connected } state;
unsigned int protocol_version;
struct hpsb_host *host;
struct list_head req_pending;
struct list_head req_complete;
struct semaphore complete_sem;
spinlock_t reqlists_lock;
wait_queue_head_t poll_wait_complete;
struct list_head addr_list;
u8 *fcp_buffer;
/* old ISO API */
u64 listen_channels;
quadlet_t *iso_buffer;
size_t iso_buffer_length;
u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
/* new rawiso API */
enum raw1394_iso_state iso_state;
struct hpsb_iso *iso_handle;
};
struct arm_addr {
struct list_head addr_list; /* file_info list */
u64 start, end;
u64 arm_tag;
u8 access_rights;
u8 notification_options;
u8 client_transactions;
u64 recvb;
u16 rec_length;
u8 *addr_space_buffer; /* accessed by read/write/lock */
};
struct pending_request {
struct list_head list;
struct file_info *file_info;
struct hpsb_packet *packet;
struct iso_block_store *ibs;
quadlet_t *data;
int free_data;
struct raw1394_request req;
};
struct host_info {
struct list_head list;
struct hpsb_host *host;
struct list_head file_info_list;
};
#endif /* IEEE1394_RAW1394_PRIVATE_H */
This diff is collapsed.
#ifndef IEEE1394_RAW1394_H
#define IEEE1394_RAW1394_H
#define RAW1394_DEVICE_MAJOR 171
#define RAW1394_DEVICE_NAME "raw1394"
/* header for the raw1394 API that is exported to user-space */
#define RAW1394_KERNELAPI_VERSION 4
......@@ -94,21 +93,21 @@ struct raw1394_khost_list {
};
typedef struct arm_request {
nodeid_t destination_nodeid;
nodeid_t source_nodeid;
nodeaddr_t destination_offset;
u8 tlabel;
u8 tcode;
u_int8_t extended_transaction_code;
u_int32_t generation;
arm_length_t buffer_length;
byte_t *buffer;
__u16 destination_nodeid;
__u16 source_nodeid;
__u64 destination_offset;
__u8 tlabel;
__u8 tcode;
__u8 extended_transaction_code;
__u32 generation;
__u16 buffer_length;
__u8 *buffer;
} *arm_request_t;
typedef struct arm_response {
int response_code;
arm_length_t buffer_length;
byte_t *buffer;
__s32 response_code;
__u16 buffer_length;
__u8 *buffer;
} *arm_response_t;
typedef struct arm_request_response {
......@@ -117,33 +116,40 @@ typedef struct arm_request_response {
} *arm_request_response_t;
/* 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
#include "ieee1394-ioctl.h"
/* per-packet metadata embedded in the ringbuffer */
/* must be identical to hpsb_iso_packet_info in iso.h! */
struct raw1394_iso_packet_info {
unsigned short len;
unsigned short cycle;
unsigned char channel; /* recv only */
unsigned char tag;
unsigned char sy;
__u32 offset;
__u16 len;
__u16 cycle; /* recv only */
__u8 channel; /* recv only */
__u8 tag;
__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 {
unsigned int buf_packets;
unsigned int max_packet_size;
int channel;
int speed; /* xmit only */
int irq_interval;
/* 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;
/* 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;
};
/* argument to RAW1394_ISO_XMIT/RECV_INIT and RAW1394_ISO_GET_STATUS */
......@@ -151,99 +157,18 @@ struct raw1394_iso_status {
/* current settings */
struct raw1394_iso_config config;
/* byte offset between successive packets in the buffer */
int buf_stride;
/* byte offset of data payload within each packet */
int packet_data_offset;
/* byte offset of struct iso_packet_info within each packet */
int packet_info_offset;
/* index of next packet to fill with data (ISO transmission)
or next packet containing data recieved (ISO reception) */
unsigned int first_packet;
/* number of packets waiting to be filled with data (ISO transmission)
or containing data received (ISO reception) */
unsigned int n_packets;
__u32 n_packets;
/* approximate number of packets dropped due to overflow or
underflow of the packet buffer (a value of zero guarantees
that no packets have been dropped) */
unsigned int overflows;
};
#ifdef __KERNEL__
struct iso_block_store {
atomic_t refcount;
size_t data_size;
quadlet_t data[0];
};
__u32 overflows;
enum raw1394_iso_state { RAW1394_ISO_INACTIVE = 0,
RAW1394_ISO_RECV = 1,
RAW1394_ISO_XMIT = 2 };
struct file_info {
struct list_head list;
enum { opened, initialized, connected } state;
unsigned int protocol_version;
struct hpsb_host *host;
struct list_head req_pending;
struct list_head req_complete;
struct semaphore complete_sem;
spinlock_t reqlists_lock;
wait_queue_head_t poll_wait_complete;
struct list_head addr_list;
u8 *fcp_buffer;
/* old ISO API */
u64 listen_channels;
quadlet_t *iso_buffer;
size_t iso_buffer_length;
u8 notification; /* (busreset-notification) RAW1394_NOTIFY_OFF/ON */
/* new rawiso API */
enum raw1394_iso_state iso_state;
struct hpsb_iso *iso_handle;
};
struct arm_addr {
struct list_head addr_list; /* file_info list */
u64 start, end;
u64 arm_tag;
u8 access_rights;
u8 notification_options;
u8 client_transactions;
u64 recvb;
u16 rec_length;
u8 *addr_space_buffer; /* accessed by read/write/lock */
};
struct pending_request {
struct list_head list;
struct file_info *file_info;
struct hpsb_packet *packet;
struct iso_block_store *ibs;
quadlet_t *data;
int free_data;
struct raw1394_request req;
};
struct host_info {
struct list_head list;
struct hpsb_host *host;
struct list_head file_info_list;
/* cycle number at which next packet will be transmitted;
-1 if not known */
__s16 xmit_cycle;
};
#endif /* __KERNEL__ */
#endif /* IEEE1394_RAW1394_H */
This diff is collapsed.
This diff is collapsed.
......@@ -21,6 +21,8 @@
#ifndef _VIDEO_1394_H
#define _VIDEO_1394_H
#include "ieee1394-ioctl.h"
#define VIDEO1394_DRIVER_NAME "video1394"
#define VIDEO1394_MAX_SIZE 0x4000000
......@@ -31,18 +33,6 @@ enum {
VIDEO1394_BUFFER_READY
};
enum {
VIDEO1394_LISTEN_CHANNEL = 0,
VIDEO1394_UNLISTEN_CHANNEL,
VIDEO1394_LISTEN_QUEUE_BUFFER,
VIDEO1394_LISTEN_WAIT_BUFFER, // wait until buffer is ready
VIDEO1394_TALK_CHANNEL,
VIDEO1394_UNTALK_CHANNEL,
VIDEO1394_TALK_QUEUE_BUFFER,
VIDEO1394_TALK_WAIT_BUFFER,
VIDEO1394_LISTEN_POLL_BUFFER // return immediately with -EINTR if not ready
};
#define VIDEO1394_SYNC_FRAMES 0x00000001
#define VIDEO1394_INCLUDE_ISO_HEADERS 0x00000002
#define VIDEO1394_VARIABLE_PACKET_SIZE 0x00000004
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment