Commit 66fa12c5 authored by Stefan Richter's avatar Stefan Richter

ieee1394: remove the old IEEE 1394 driver stack

The drivers
  - ohci1394 (controller driver)
  - ieee1394 (core)
  - dv1394, raw1394, video1394 (userspace ABI)
  - eth1394, sbp2 (protocol drivers)
are replaced by
  - firewire-ohci (controller driver)
  - firewire-core (core and userspace ABI)
  - firewire-net, firewire-sbp2 (protocol drivers)
which are more featureful, better performing, and more secure than the older
drivers; all with a smaller and more modern code base.

The driver firedtv in drivers/media/dvb/firewire/ contains backends to both
ieee1394 and firewire-core.  Its ieee1394 backend code can be removed in an
independent commit; firedtv as-is builds and works fine without ieee1394.

The driver pcilynx (an incomplete controller driver) is deleted without
replacement since PCILynx cards are extremely rare.  Owners of these cards
use them with the stand-alone bus sniffer driver nosy instead.

The drivers nosy and init_ohci1394_dma which do not interact with either of
the two IEEE 1394 stacks are not affected by the ieee1394 subsystem removal.

There are still some issues with the newer firewire subsystem compared to
the older one:
  - The rare and quirky controllers ALi M52xx, Apple UniNorth v1, NVIDIA
    NForce2 are even less well supported by firewire-ohci than by ohci1394.
    I am looking into the M52xx issue.
  - The experimental firewire-net is reportedly less stable than its
    experimental cousin eth1394.
  - Audio playback of a certain group of audio devices (ones based on DICE
    chipset with EAP; supported by prerelease FFADO code) does not work yet.
    This issue is still under investigation.
  - There were some ieee1394 based out-of-the-mainline drivers.  Of them,
    only lisight, an audio driver for iSight webcams, seems still useful.
    Work is underway to reimplement it on top of firewire-core.

All these remainig issues are minor; they should not stand in the way of
overall better user experience of IEEE 1394 on Linux, together with a
reduction in support efforts and maintenance burden.  The coexistence of two
IEEE 1394 kernel driver stacks in the mainline since 2.6.22 shall end now,
as announced earlier this year.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 1ef5b816
What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire)
Contact: linux1394-devel@lists.sourceforge.net
Description:
New application development should use raw1394 + userspace libraries
instead, notably libiec61883 which is functionally equivalent.
Users:
ffmpeg/libavformat (used by a variety of media players)
dvgrab v1.x (replaced by dvgrab2 on top of raw1394 and resp. libraries)
What: dv1394 (a.k.a. "OHCI-DV I/O support" for FireWire)
Date: May 2010 (scheduled), finally removed in kernel v2.6.37
Contact: linux1394-devel@lists.sourceforge.net
Description:
/dev/dv1394/* were character device files, one for each FireWire
controller and for NTSC and PAL respectively, from which DV data
could be received by read() or transmitted by write(). A few
ioctl()s allowed limited control.
This special-purpose interface has been superseded by libraw1394 +
libiec61883 which are functionally equivalent, support HDV, and
transparently work on top of the newer firewire kernel drivers.
Users:
ffmpeg/libavformat (if configured for DV1394)
What: raw1394 (a.k.a. "Raw IEEE1394 I/O support" for FireWire)
Date: May 2010 (scheduled), finally removed in kernel v2.6.37
Contact: linux1394-devel@lists.sourceforge.net
Description:
/dev/raw1394 was a character device file that allowed low-level
access to FireWire buses. Its major drawbacks were its inability
to implement sensible device security policies, and its low level
of abstraction that required userspace clients do duplicate much
of the kernel's ieee1394 core functionality.
Replaced by /dev/fw*, i.e. the <linux/firewire-cdev.h> ABI of
firewire-core.
Users:
libraw1394 (works with firewire-cdev too, transparent to library ABI
users)
What: legacy isochronous ABI of raw1394 (1st generation iso ABI)
Date: June 2007 (scheduled), removed in kernel v2.6.23
Contact: linux1394-devel@lists.sourceforge.net
Description:
The two request types RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN have
been deprecated for quite some time. They are very inefficient as they
come with high interrupt load and several layers of callbacks for each
packet. Because of these deficiencies, the video1394 and dv1394 drivers
and the 3rd-generation isochronous ABI in raw1394 (rawiso) were created.
Users:
libraw1394 users via the long deprecated API raw1394_iso_write,
raw1394_start_iso_write, raw1394_start_iso_rcv, raw1394_stop_iso_rcv
libdc1394, which optionally uses these old libraw1394 calls
alternatively to the more efficient video1394 ABI
What: video1394 (a.k.a. "OHCI-1394 Video support" for FireWire)
Date: May 2010 (scheduled), finally removed in kernel v2.6.37
Contact: linux1394-devel@lists.sourceforge.net
Description:
/dev/video1394/* were character device files, one for each FireWire
controller, which were used for isochronous I/O. It was added as an
alternative to raw1394's isochronous I/O functionality which had
performance issues in its first generation. Any video1394 user had
to use raw1394 + libraw1394 too because video1394 did not provide
asynchronous I/O for device discovery and configuration.
Replaced by /dev/fw*, i.e. the <linux/firewire-cdev.h> ABI of
firewire-core.
Users:
libdc1394 (works with firewire-cdev too, transparent to library ABI
users)
......@@ -530,16 +530,6 @@ Who: Thomas Gleixner <tglx@linutronix.de>
----------------------------
What: old ieee1394 subsystem (CONFIG_IEEE1394)
When: 2.6.37
Files: drivers/ieee1394/ except init_ohci1394_dma.c
Why: superseded by drivers/firewire/ (CONFIG_FIREWIRE) which offers more
features, better performance, and better security, all with smaller
and more modern code base
Who: Stefan Richter <stefanr@s5r6.in-berlin.de>
----------------------------
What: The acpi_sleep=s4_nonvs command line option
When: 2.6.37
Files: arch/x86/kernel/acpi/sleep.c
......
......@@ -51,7 +51,6 @@ obj-y += net/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
obj-y += ieee1394/
obj-$(CONFIG_UIO) += uio/
obj-y += cdrom/
obj-y += auxdisplay/
......
......@@ -3,9 +3,6 @@ menu "IEEE 1394 (FireWire) support"
# firewire-core does not depend on PCI but is
# not useful without PCI controller driver
comment "You can enable one or both FireWire driver stacks."
comment "The newer stack is recommended."
config FIREWIRE
tristate "FireWire driver stack"
select CRC_ITU_T
......@@ -64,8 +61,6 @@ config FIREWIRE_NET
To compile this driver as a module, say M here: The module will be
called firewire-net.
source "drivers/ieee1394/Kconfig"
config FIREWIRE_NOSY
tristate "Nosy - a FireWire traffic sniffer for PCILynx cards"
depends on PCI
......
config IEEE1394
tristate "Legacy alternative FireWire driver stack"
depends on PCI || BROKEN
help
IEEE 1394 describes a high performance serial bus, which is also
known as FireWire(tm) or i.Link(tm) and is used for connecting all
sorts of devices (most notably digital video cameras) to your
computer.
If you have FireWire hardware and want to use it, say Y here. This
is the core support only, you will also need to select a driver for
your IEEE 1394 adapter.
To compile this driver as a module, say M here: the module will be
called ieee1394.
NOTE:
ieee1394 is superseded by the newer firewire-core driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
config IEEE1394_OHCI1394
tristate "OHCI-1394 controllers"
depends on PCI && IEEE1394
help
Enable this driver if you have an IEEE 1394 controller based on the
OHCI-1394 specification. The current driver is only tested with OHCI
chipsets made by Texas Instruments and NEC. Most third-party vendors
use one of these chipsets. It should work with any OHCI-1394
compliant card, however.
To compile this driver as a module, say M here: the module will be
called ohci1394.
NOTE:
ohci1394 is superseded by the newer firewire-ohci driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
If you want to install firewire-ohci and ohci1394 together, you
should configure them only as modules and blacklist the driver(s)
which you don't want to have auto-loaded. Add either
blacklist ohci1394
blacklist video1394
blacklist dv1394
or
blacklist firewire-ohci
to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf
depending on your distribution.
comment "PCILynx controller requires I2C"
depends on IEEE1394 && I2C=n
config IEEE1394_PCILYNX
tristate "PCILynx controller"
depends on PCI && IEEE1394 && I2C
select I2C_ALGOBIT
help
Say Y here if you have an IEEE-1394 controller with the Texas
Instruments PCILynx chip. Note: this driver is written for revision
2 of this chip and may not work with revision 0.
To compile this driver as a module, say M here: the module will be
called pcilynx.
Only some old and now very rare PCI and CardBus cards and
PowerMacs G3 B&W contain the PCILynx controller. Therefore
almost everybody can say N here.
comment "SBP-2 support (for storage devices) requires SCSI"
depends on IEEE1394 && SCSI=n
config IEEE1394_SBP2
tristate "Storage devices (SBP-2 protocol)"
depends on IEEE1394 && SCSI
help
This option enables you to use SBP-2 devices connected to an IEEE
1394 bus. SBP-2 devices include storage devices like harddisks and
DVD drives, also some other FireWire devices like scanners.
You should also enable support for disks, CD-ROMs, etc. in the SCSI
configuration section.
To compile this driver as a module, say M here: the module will be
called sbp2.
NOTE:
sbp2 is superseded by the newer firewire-sbp2 driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
config IEEE1394_SBP2_PHYS_DMA
bool "Enable replacement for physical DMA in SBP2"
depends on IEEE1394_SBP2 && VIRT_TO_BUS && EXPERIMENTAL
help
This builds sbp2 for use with non-OHCI host adapters which do not
support physical DMA or for when ohci1394 is run with phys_dma=0.
Physical DMA is data movement without assistance of the drivers'
interrupt handlers. This option includes the interrupt handlers
that are required in absence of this hardware feature.
This option is buggy and currently broken on some architectures.
If unsure, say N.
config IEEE1394_ETH1394_ROM_ENTRY
depends on IEEE1394
bool
default n
config IEEE1394_ETH1394
tristate "IP networking over 1394 (experimental)"
depends on IEEE1394 && EXPERIMENTAL && INET
select IEEE1394_ETH1394_ROM_ENTRY
help
This driver implements a functional majority of RFC 2734: IPv4 over
1394. It will provide IP connectivity with implementations of RFC
2734 found on other operating systems. It will not communicate with
older versions of this driver found in stock kernels prior to 2.6.3.
This driver is still considered experimental. It does not yet support
MCAP, therefore multicast support is significantly limited.
The module is called eth1394 although it does not emulate Ethernet.
NOTE:
eth1394 is superseded by the newer firewire-net driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
config IEEE1394_RAWIO
tristate "raw1394 userspace interface"
depends on IEEE1394
help
This option adds support for the raw1394 device file which enables
direct communication of user programs with IEEE 1394 devices
(isochronous and asynchronous). Almost all application programs
which access FireWire require this option.
To compile this driver as a module, say M here: the module will be
called raw1394.
NOTE:
raw1394 is superseded by the newer firewire-core driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
config IEEE1394_VIDEO1394
tristate "video1394 userspace interface"
depends on IEEE1394 && IEEE1394_OHCI1394
help
This option adds support for the video1394 device files which enable
isochronous communication of user programs with IEEE 1394 devices,
especially video capture or export. This interface is used by all
libdc1394 based programs and by several other programs, in addition to
the raw1394 interface. It is generally not required for DV capture.
To compile this driver as a module, say M here: the module will be
called video1394.
NOTE:
video1394 is superseded by the newer firewire-core driver. See
http://ieee1394.wiki.kernel.org/index.php/Juju_Migration for
further information on how to switch to the new FireWire drivers.
config IEEE1394_DV1394
tristate "dv1394 userspace interface (deprecated)"
depends on IEEE1394 && IEEE1394_OHCI1394
help
The dv1394 driver is unsupported and may be removed from Linux in a
future release. Its functionality is now provided by either
raw1394 or firewire-core together with libraries such as libiec61883.
config IEEE1394_VERBOSEDEBUG
bool "Excessive debugging output"
depends on IEEE1394
help
If you say Y here, you will get very verbose debugging logs from the
ieee1394 drivers, including sent and received packet headers. This
will quickly result in large amounts of data sent to the system log.
Say Y if you really need the debugging output. Everyone else says N.
#
# Makefile for the Linux IEEE 1394 implementation
#
ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
highlevel.o csr.o nodemgr.o dma.o iso.o \
csr1212.o config_roms.o
obj-$(CONFIG_IEEE1394) += ieee1394.o
obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o
obj-$(CONFIG_IEEE1394_OHCI1394) += ohci1394.o
obj-$(CONFIG_IEEE1394_VIDEO1394) += video1394.o
obj-$(CONFIG_IEEE1394_RAWIO) += raw1394.o
obj-$(CONFIG_IEEE1394_SBP2) += sbp2.o
obj-$(CONFIG_IEEE1394_DV1394) += dv1394.o
obj-$(CONFIG_IEEE1394_ETH1394) += eth1394.o
/*
* IEEE 1394 for Linux
*
* ConfigROM entries
*
* Copyright (C) 2004 Ben Collins
*
* This code is licensed under the GPL. See the file COPYING in the root
* directory of the kernel sources for details.
*/
#include <linux/types.h>
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
#include "csr.h"
#include "config_roms.h"
struct hpsb_config_rom_entry {
const char *name;
/* Base initialization, called at module load */
int (*init)(void);
/* Cleanup called at module exit */
void (*cleanup)(void);
/* The flag added to host->config_roms */
unsigned int flag;
};
/* The default host entry. This must succeed. */
int hpsb_default_host_entry(struct hpsb_host *host)
{
struct csr1212_keyval *root;
struct csr1212_keyval *vend_id = NULL;
struct csr1212_keyval *text = NULL;
char csr_name[128];
int ret;
sprintf(csr_name, "Linux - %s", host->driver->name);
root = host->csr.rom->root_kv;
vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR, host->csr.guid_hi >> 8);
text = csr1212_new_string_descriptor_leaf(csr_name);
if (!vend_id || !text) {
if (vend_id)
csr1212_release_keyval(vend_id);
if (text)
csr1212_release_keyval(text);
csr1212_destroy_csr(host->csr.rom);
return -ENOMEM;
}
csr1212_associate_keyval(vend_id, text);
csr1212_release_keyval(text);
ret = csr1212_attach_keyval_to_directory(root, vend_id);
csr1212_release_keyval(vend_id);
if (ret != CSR1212_SUCCESS) {
csr1212_destroy_csr(host->csr.rom);
return -ENOMEM;
}
host->update_config_rom = 1;
return 0;
}
#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
#include "eth1394.h"
static struct csr1212_keyval *ip1394_ud;
static int config_rom_ip1394_init(void)
{
struct csr1212_keyval *spec_id = NULL;
struct csr1212_keyval *spec_desc = NULL;
struct csr1212_keyval *ver = NULL;
struct csr1212_keyval *ver_desc = NULL;
int ret = -ENOMEM;
ip1394_ud = csr1212_new_directory(CSR1212_KV_ID_UNIT);
spec_id = csr1212_new_immediate(CSR1212_KV_ID_SPECIFIER_ID,
ETHER1394_GASP_SPECIFIER_ID);
spec_desc = csr1212_new_string_descriptor_leaf("IANA");
ver = csr1212_new_immediate(CSR1212_KV_ID_VERSION,
ETHER1394_GASP_VERSION);
ver_desc = csr1212_new_string_descriptor_leaf("IPv4");
if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc)
goto ip1394_fail;
csr1212_associate_keyval(spec_id, spec_desc);
csr1212_associate_keyval(ver, ver_desc);
if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id)
== CSR1212_SUCCESS &&
csr1212_attach_keyval_to_directory(ip1394_ud, ver)
== CSR1212_SUCCESS)
ret = 0;
ip1394_fail:
if (ret && ip1394_ud) {
csr1212_release_keyval(ip1394_ud);
ip1394_ud = NULL;
}
if (spec_id)
csr1212_release_keyval(spec_id);
if (spec_desc)
csr1212_release_keyval(spec_desc);
if (ver)
csr1212_release_keyval(ver);
if (ver_desc)
csr1212_release_keyval(ver_desc);
return ret;
}
static void config_rom_ip1394_cleanup(void)
{
if (ip1394_ud) {
csr1212_release_keyval(ip1394_ud);
ip1394_ud = NULL;
}
}
int hpsb_config_rom_ip1394_add(struct hpsb_host *host)
{
if (!ip1394_ud)
return -ENODEV;
if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv,
ip1394_ud) != CSR1212_SUCCESS)
return -ENOMEM;
host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394;
host->update_config_rom = 1;
return 0;
}
EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add);
void hpsb_config_rom_ip1394_remove(struct hpsb_host *host)
{
csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud);
host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394;
host->update_config_rom = 1;
}
EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove);
static struct hpsb_config_rom_entry ip1394_entry = {
.name = "ip1394",
.init = config_rom_ip1394_init,
.cleanup = config_rom_ip1394_cleanup,
.flag = HPSB_CONFIG_ROM_ENTRY_IP1394,
};
#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */
static struct hpsb_config_rom_entry *const config_rom_entries[] = {
#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
&ip1394_entry,
#endif
};
/* Initialize all config roms */
int hpsb_init_config_roms(void)
{
int i, error = 0;
for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
if (config_rom_entries[i]->init()) {
HPSB_ERR("Failed to initialize config rom entry `%s'",
config_rom_entries[i]->name);
error = -1;
}
return error;
}
/* Cleanup all config roms */
void hpsb_cleanup_config_roms(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++)
config_rom_entries[i]->cleanup();
}
#ifndef _IEEE1394_CONFIG_ROMS_H
#define _IEEE1394_CONFIG_ROMS_H
struct hpsb_host;
int hpsb_default_host_entry(struct hpsb_host *host);
int hpsb_init_config_roms(void);
void hpsb_cleanup_config_roms(void);
/* List of flags to check if a host contains a certain extra config rom
* entry. Available in the host->config_roms member. */
#define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001
#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY
int hpsb_config_rom_ip1394_add(struct hpsb_host *host);
void hpsb_config_rom_ip1394_remove(struct hpsb_host *host);
#endif
#endif /* _IEEE1394_CONFIG_ROMS_H */
This diff is collapsed.
#ifndef _IEEE1394_CSR_H
#define _IEEE1394_CSR_H
#include <linux/spinlock_types.h>
#include "csr1212.h"
#include "ieee1394_types.h"
#define CSR_REGISTER_BASE 0xfffff0000000ULL
/* register offsets relative to CSR_REGISTER_BASE */
#define CSR_STATE_CLEAR 0x0
#define CSR_STATE_SET 0x4
#define CSR_NODE_IDS 0x8
#define CSR_RESET_START 0xc
#define CSR_SPLIT_TIMEOUT_HI 0x18
#define CSR_SPLIT_TIMEOUT_LO 0x1c
#define CSR_CYCLE_TIME 0x200
#define CSR_BUS_TIME 0x204
#define CSR_BUSY_TIMEOUT 0x210
#define CSR_BUS_MANAGER_ID 0x21c
#define CSR_BANDWIDTH_AVAILABLE 0x220
#define CSR_CHANNELS_AVAILABLE 0x224
#define CSR_CHANNELS_AVAILABLE_HI 0x224
#define CSR_CHANNELS_AVAILABLE_LO 0x228
#define CSR_BROADCAST_CHANNEL 0x234
#define CSR_CONFIG_ROM 0x400
#define CSR_CONFIG_ROM_END 0x800
#define CSR_FCP_COMMAND 0xB00
#define CSR_FCP_RESPONSE 0xD00
#define CSR_FCP_END 0xF00
#define CSR_TOPOLOGY_MAP 0x1000
#define CSR_TOPOLOGY_MAP_END 0x1400
#define CSR_SPEED_MAP 0x2000
#define CSR_SPEED_MAP_END 0x3000
/* IEEE 1394 bus specific Configuration ROM Key IDs */
#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30)
/* IEEE 1394 Bus Information Block specifics */
#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t))
#define CSR_IRMC_SHIFT 31
#define CSR_CMC_SHIFT 30
#define CSR_ISC_SHIFT 29
#define CSR_BMC_SHIFT 28
#define CSR_PMC_SHIFT 27
#define CSR_CYC_CLK_ACC_SHIFT 16
#define CSR_MAX_REC_SHIFT 12
#define CSR_MAX_ROM_SHIFT 8
#define CSR_GENERATION_SHIFT 4
static inline void csr_set_bus_info_generation(struct csr1212_csr *csr, u8 gen)
{
csr->bus_info_data[2] &= ~cpu_to_be32(0xf << CSR_GENERATION_SHIFT);
csr->bus_info_data[2] |= cpu_to_be32((u32)gen << CSR_GENERATION_SHIFT);
}
struct csr_control {
spinlock_t lock;
quadlet_t state;
quadlet_t node_ids;
quadlet_t split_timeout_hi, split_timeout_lo;
unsigned long expire; /* Calculated from split_timeout */
quadlet_t cycle_time;
quadlet_t bus_time;
quadlet_t bus_manager_id;
quadlet_t bandwidth_available;
quadlet_t channels_available_hi, channels_available_lo;
quadlet_t broadcast_channel;
/* Bus Info */
quadlet_t guid_hi, guid_lo;
u8 cyc_clk_acc;
u8 max_rec;
u8 max_rom;
u8 generation; /* Only use values between 0x2 and 0xf */
u8 lnk_spd;
unsigned long gen_timestamp[16];
struct csr1212_csr *rom;
quadlet_t topology_map[256];
quadlet_t speed_map[1024];
};
extern struct csr1212_bus_ops csr_bus_ops;
int init_csr(void);
void cleanup_csr(void);
/* hpsb_update_config_rom() is deprecated */
struct hpsb_host;
int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
size_t size, unsigned char rom_version);
#endif /* _IEEE1394_CSR_H */
This diff is collapsed.
This diff is collapsed.
/*
* DMA region bookkeeping routines
*
* 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 <linux/mm.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/scatterlist.h>
#include "dma.h"
/* dma_prog_region */
void dma_prog_region_init(struct dma_prog_region *prog)
{
prog->kvirt = NULL;
prog->dev = NULL;
prog->n_pages = 0;
prog->bus_addr = 0;
}
int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
struct pci_dev *dev)
{
/* round up to page size */
n_bytes = PAGE_ALIGN(n_bytes);
prog->n_pages = n_bytes >> PAGE_SHIFT;
prog->kvirt = pci_alloc_consistent(dev, n_bytes, &prog->bus_addr);
if (!prog->kvirt) {
printk(KERN_ERR
"dma_prog_region_alloc: pci_alloc_consistent() failed\n");
dma_prog_region_free(prog);
return -ENOMEM;
}
prog->dev = dev;
return 0;
}
void dma_prog_region_free(struct dma_prog_region *prog)
{
if (prog->kvirt) {
pci_free_consistent(prog->dev, prog->n_pages << PAGE_SHIFT,
prog->kvirt, prog->bus_addr);
}
prog->kvirt = NULL;
prog->dev = NULL;
prog->n_pages = 0;
prog->bus_addr = 0;
}
/* dma_region */
/**
* dma_region_init - clear out all fields but do not allocate anything
*/
void dma_region_init(struct dma_region *dma)
{
dma->kvirt = NULL;
dma->dev = NULL;
dma->n_pages = 0;
dma->n_dma_pages = 0;
dma->sglist = NULL;
}
/**
* dma_region_alloc - allocate the buffer and map it to the IOMMU
*/
int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
struct pci_dev *dev, int direction)
{
unsigned int i;
/* round up to page size */
n_bytes = PAGE_ALIGN(n_bytes);
dma->n_pages = n_bytes >> PAGE_SHIFT;
dma->kvirt = vmalloc_32(n_bytes);
if (!dma->kvirt) {
printk(KERN_ERR "dma_region_alloc: vmalloc_32() failed\n");
goto err;
}
/* Clear the ram out, no junk to the user */
memset(dma->kvirt, 0, n_bytes);
/* allocate scatter/gather list */
dma->sglist = vmalloc(dma->n_pages * sizeof(*dma->sglist));
if (!dma->sglist) {
printk(KERN_ERR "dma_region_alloc: vmalloc(sglist) failed\n");
goto err;
}
sg_init_table(dma->sglist, dma->n_pages);
/* fill scatter/gather list with pages */
for (i = 0; i < dma->n_pages; i++) {
unsigned long va =
(unsigned long)dma->kvirt + (i << PAGE_SHIFT);
sg_set_page(&dma->sglist[i], vmalloc_to_page((void *)va),
PAGE_SIZE, 0);
}
/* map sglist to the IOMMU */
dma->n_dma_pages =
pci_map_sg(dev, dma->sglist, dma->n_pages, direction);
if (dma->n_dma_pages == 0) {
printk(KERN_ERR "dma_region_alloc: pci_map_sg() failed\n");
goto err;
}
dma->dev = dev;
dma->direction = direction;
return 0;
err:
dma_region_free(dma);
return -ENOMEM;
}
/**
* dma_region_free - unmap and free the buffer
*/
void dma_region_free(struct dma_region *dma)
{
if (dma->n_dma_pages) {
pci_unmap_sg(dma->dev, dma->sglist, dma->n_pages,
dma->direction);
dma->n_dma_pages = 0;
dma->dev = NULL;
}
vfree(dma->sglist);
dma->sglist = NULL;
vfree(dma->kvirt);
dma->kvirt = NULL;
dma->n_pages = 0;
}
/* 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 int start, unsigned long *rem)
{
int i;
unsigned long off = offset;
for (i = start; i < dma->n_dma_pages; i++) {
if (off < sg_dma_len(&dma->sglist[i])) {
*rem = off;
break;
}
off -= sg_dma_len(&dma->sglist[i]);
}
BUG_ON(i >= dma->n_dma_pages);
return i;
}
/**
* dma_region_offset_to_bus - get bus address of an offset within a DMA region
*
* Returns the DMA bus address of the byte with the given @offset relative to
* the beginning of the @dma.
*/
dma_addr_t dma_region_offset_to_bus(struct dma_region * dma,
unsigned long offset)
{
unsigned long rem = 0;
struct scatterlist *sg =
&dma->sglist[dma_region_find(dma, offset, 0, &rem)];
return sg_dma_address(sg) + rem;
}
/**
* dma_region_sync_for_cpu - sync the CPU's view of the buffer
*/
void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
unsigned long len)
{
int first, last;
unsigned long rem = 0;
if (!len)
len = 1;
first = dma_region_find(dma, offset, 0, &rem);
last = dma_region_find(dma, rem + len - 1, first, &rem);
pci_dma_sync_sg_for_cpu(dma->dev, &dma->sglist[first], last - first + 1,
dma->direction);
}
/**
* dma_region_sync_for_device - sync the IO bus' view of the buffer
*/
void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
unsigned long len)
{
int first, last;
unsigned long rem = 0;
if (!len)
len = 1;
first = dma_region_find(dma, offset, 0, &rem);
last = dma_region_find(dma, rem + len - 1, first, &rem);
pci_dma_sync_sg_for_device(dma->dev, &dma->sglist[first],
last - first + 1, dma->direction);
}
#ifdef CONFIG_MMU
static int dma_region_pagefault(struct vm_area_struct *vma,
struct vm_fault *vmf)
{
struct dma_region *dma = (struct dma_region *)vma->vm_private_data;
if (!dma->kvirt)
return VM_FAULT_SIGBUS;
if (vmf->pgoff >= dma->n_pages)
return VM_FAULT_SIGBUS;
vmf->page = vmalloc_to_page(dma->kvirt + (vmf->pgoff << PAGE_SHIFT));
get_page(vmf->page);
return 0;
}
static const struct vm_operations_struct dma_region_vm_ops = {
.fault = dma_region_pagefault,
};
/**
* dma_region_mmap - map the buffer into a user space process
*/
int dma_region_mmap(struct dma_region *dma, struct file *file,
struct vm_area_struct *vma)
{
unsigned long size;
if (!dma->kvirt)
return -EINVAL;
/* must be page-aligned (XXX: comment is wrong, we could allow pgoff) */
if (vma->vm_pgoff != 0)
return -EINVAL;
/* check the length */
size = vma->vm_end - vma->vm_start;
if (size > (dma->n_pages << PAGE_SHIFT))
return -EINVAL;
vma->vm_ops = &dma_region_vm_ops;
vma->vm_private_data = dma;
vma->vm_file = file;
vma->vm_flags |= VM_RESERVED | VM_ALWAYSDUMP;
return 0;
}
#else /* CONFIG_MMU */
int dma_region_mmap(struct dma_region *dma, struct file *file,
struct vm_area_struct *vma)
{
return -EINVAL;
}
#endif /* CONFIG_MMU */
/*
* DMA region bookkeeping routines
*
* 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_DMA_H
#define IEEE1394_DMA_H
#include <asm/types.h>
struct file;
struct pci_dev;
struct scatterlist;
struct vm_area_struct;
/**
* struct dma_prog_region - small contiguous DMA buffer
* @kvirt: kernel virtual address
* @dev: PCI device
* @n_pages: number of kernel pages
* @bus_addr: base bus address
*
* a small, physically contiguous DMA buffer with random-access, synchronous
* usage characteristics
*/
struct dma_prog_region {
unsigned char *kvirt;
struct pci_dev *dev;
unsigned int n_pages;
dma_addr_t bus_addr;
};
/* clear out all fields but do not allocate any memory */
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);
void dma_prog_region_free(struct dma_prog_region *prog);
static inline dma_addr_t dma_prog_region_offset_to_bus(
struct dma_prog_region *prog, unsigned long offset)
{
return prog->bus_addr + offset;
}
/**
* struct dma_region - large non-contiguous DMA buffer
* @virt: kernel virtual address
* @dev: PCI device
* @n_pages: number of kernel pages
* @n_dma_pages: number of IOMMU pages
* @sglist: IOMMU mapping
* @direction: PCI_DMA_TODEVICE, etc.
*
* a large, non-physically-contiguous DMA buffer with streaming, asynchronous
* usage characteristics
*/
struct dma_region {
unsigned char *kvirt;
struct pci_dev *dev;
unsigned int n_pages;
unsigned int n_dma_pages;
struct scatterlist *sglist;
int direction;
};
void dma_region_init(struct dma_region *dma);
int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
struct pci_dev *dev, int direction);
void dma_region_free(struct dma_region *dma);
void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
unsigned long len);
void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
unsigned long len);
int dma_region_mmap(struct dma_region *dma, struct file *file,
struct vm_area_struct *vma);
dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
unsigned long offset);
/**
* dma_region_i - macro to index into a DMA region (or dma_prog_region)
*/
#define dma_region_i(_dma, _type, _index) \
( ((_type*) ((_dma)->kvirt)) + (_index) )
#endif /* IEEE1394_DMA_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* eth1394.h -- Ethernet driver for Linux IEEE-1394 Subsystem
*
* Copyright (C) 2000 Bonin Franck <boninf@free.fr>
* (C) 2001 Ben Collins <bcollins@debian.org>
*
* Mainly based on work by Emanuel Pirker and Andreas E. Bombe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __ETH1394_H
#define __ETH1394_H
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <asm/byteorder.h>
#include "ieee1394.h"
#include "ieee1394_types.h"
/* Register for incoming packets. This is 4096 bytes, which supports up to
* S3200 (per Table 16-3 of IEEE 1394b-2002). */
#define ETHER1394_REGION_ADDR_LEN 4096
/* GASP identifier numbers for IPv4 over IEEE 1394 */
#define ETHER1394_GASP_SPECIFIER_ID 0x00005E
#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff)
#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff)
#define ETHER1394_GASP_VERSION 1
#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */
#define ETHER1394_GASP_BUFFERS 16
#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */
enum eth1394_bc_states { ETHER1394_BC_ERROR,
ETHER1394_BC_RUNNING,
ETHER1394_BC_STOPPED };
/* Private structure for our ethernet driver */
struct eth1394_priv {
struct hpsb_host *host; /* The card for this dev */
u16 bc_maxpayload; /* Max broadcast payload */
u8 bc_sspd; /* Max broadcast speed */
u64 local_fifo; /* Local FIFO Address */
spinlock_t lock; /* Private lock */
int broadcast_channel; /* Async stream Broadcast Channel */
enum eth1394_bc_states bc_state; /* broadcast channel state */
struct hpsb_iso *iso; /* Async stream recv handle */
int bc_dgl; /* Outgoing broadcast datagram label */
struct list_head ip_node_list; /* List of IP capable nodes */
struct unit_directory *ud_list[ALL_NODES]; /* Cached unit dir list */
struct work_struct wake; /* Wake up after xmit failure */
struct net_device *wake_dev; /* Stupid backlink for .wake */
nodeid_t wake_node; /* Destination of failed xmit */
};
/* Define a fake hardware header format for the networking core. Note that
* header size cannot exceed 16 bytes as that is the size of the header cache.
* Also, we do not need the source address in the header so we omit it and
* keep the header to under 16 bytes */
#define ETH1394_ALEN (8)
#define ETH1394_HLEN (10)
struct eth1394hdr {
unsigned char h_dest[ETH1394_ALEN]; /* destination eth1394 addr */
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb)
{
return (struct eth1394hdr *)skb_mac_header(skb);
}
typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type;
/* IP1394 headers */
/* Unfragmented */
#if defined __BIG_ENDIAN_BITFIELD
struct eth1394_uf_hdr {
u16 lf:2;
u16 res:14;
__be16 ether_type; /* Ethernet packet type */
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD
struct eth1394_uf_hdr {
u16 res:14;
u16 lf:2;
__be16 ether_type;
} __attribute__((packed));
#else
#error Unknown bit field type
#endif
/* First fragment */
#if defined __BIG_ENDIAN_BITFIELD
struct eth1394_ff_hdr {
u16 lf:2;
u16 res1:2;
u16 dg_size:12; /* Datagram size */
__be16 ether_type; /* Ethernet packet type */
u16 dgl; /* Datagram label */
u16 res2;
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD
struct eth1394_ff_hdr {
u16 dg_size:12;
u16 res1:2;
u16 lf:2;
__be16 ether_type;
u16 dgl;
u16 res2;
} __attribute__((packed));
#else
#error Unknown bit field type
#endif
/* XXX: Subsequent fragments, including last */
#if defined __BIG_ENDIAN_BITFIELD
struct eth1394_sf_hdr {
u16 lf:2;
u16 res1:2;
u16 dg_size:12; /* Datagram size */
u16 res2:4;
u16 fg_off:12; /* Fragment offset */
u16 dgl; /* Datagram label */
u16 res3;
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD
struct eth1394_sf_hdr {
u16 dg_size:12;
u16 res1:2;
u16 lf:2;
u16 fg_off:12;
u16 res2:4;
u16 dgl;
u16 res3;
} __attribute__((packed));
#else
#error Unknown bit field type
#endif
#if defined __BIG_ENDIAN_BITFIELD
struct eth1394_common_hdr {
u16 lf:2;
u16 pad1:14;
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD
struct eth1394_common_hdr {
u16 pad1:14;
u16 lf:2;
} __attribute__((packed));
#else
#error Unknown bit field type
#endif
struct eth1394_hdr_words {
u16 word1;
u16 word2;
u16 word3;
u16 word4;
};
union eth1394_hdr {
struct eth1394_common_hdr common;
struct eth1394_uf_hdr uf;
struct eth1394_ff_hdr ff;
struct eth1394_sf_hdr sf;
struct eth1394_hdr_words words;
};
/* End of IP1394 headers */
/* Fragment types */
#define ETH1394_HDR_LF_UF 0 /* unfragmented */
#define ETH1394_HDR_LF_FF 1 /* first fragment */
#define ETH1394_HDR_LF_LF 2 /* last fragment */
#define ETH1394_HDR_LF_IF 3 /* interior fragment */
#define IP1394_HW_ADDR_LEN 16 /* As per RFC */
/* Our arp packet (ARPHRD_IEEE1394) */
struct eth1394_arp {
u16 hw_type; /* 0x0018 */
u16 proto_type; /* 0x0806 */
u8 hw_addr_len; /* 16 */
u8 ip_addr_len; /* 4 */
u16 opcode; /* ARP Opcode */
/* Above is exactly the same format as struct arphdr */
__be64 s_uniq_id; /* Sender's 64bit EUI */
u8 max_rec; /* Sender's max packet size */
u8 sspd; /* Sender's max speed */
__be16 fifo_hi; /* hi 16bits of sender's FIFO addr */
__be32 fifo_lo; /* lo 32bits of sender's FIFO addr */
u32 sip; /* Sender's IP Address */
u32 tip; /* IP Address of requested hw addr */
};
/* Network timeout */
#define ETHER1394_TIMEOUT 100000
/* This is our task struct. It's used for the packet complete callback. */
struct packet_task {
struct sk_buff *skb;
int outstanding_pkts;
eth1394_tx_type tx_type;
int max_payload;
struct hpsb_packet *packet;
struct eth1394_priv *priv;
union eth1394_hdr hdr;
u64 addr;
u16 dest_node;
};
#endif /* __ETH1394_H */
This diff is collapsed.
#ifndef IEEE1394_HIGHLEVEL_H
#define IEEE1394_HIGHLEVEL_H
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/types.h>
struct module;
#include "ieee1394_types.h"
struct hpsb_host;
/* internal to ieee1394 core */
struct hpsb_address_serve {
struct list_head host_list; /* per host list */
struct list_head hl_list; /* hpsb_highlevel list */
const struct hpsb_address_ops *op;
struct hpsb_host *host;
u64 start; /* first address handled, quadlet aligned */
u64 end; /* first address behind, quadlet aligned */
};
/* Only the following structures are of interest to actual highlevel drivers. */
struct hpsb_highlevel {
const char *name;
/* Any of the following pointers can legally be NULL. */
/* New host initialized. Will also be called during
* hpsb_register_highlevel for all hosts already installed. */
void (*add_host)(struct hpsb_host *host);
/* Host about to be removed. Will also be called during
* hpsb_unregister_highlevel once for each host. */
void (*remove_host)(struct hpsb_host *host);
/* Host experienced bus reset with possible configuration changes.
* Note that this one may occur during interrupt/bottom half handling.
* You can not expect to be able to do stock hpsb_reads. */
void (*host_reset)(struct hpsb_host *host);
/* A write request was received on either the FCP_COMMAND (direction =
* 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
* contains the cts field (first byte of data). */
void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction,
int cts, u8 *data, size_t length);
/* These are initialized by the subsystem when the
* hpsb_higlevel is registered. */
struct list_head hl_list;
struct list_head irq_list;
struct list_head addr_list;
struct list_head host_info_list;
rwlock_t host_info_lock;
};
struct hpsb_address_ops {
/*
* Null function pointers will make the respective operation complete
* with RCODE_TYPE_ERROR. Makes for easy to implement read-only
* registers (just leave everything but read NULL).
*
* All functions shall return appropriate IEEE 1394 rcodes.
*/
/* These functions have to implement block reads for themselves.
*
* These functions either return a response code or a negative number.
* In the first case a response will be generated. In the latter case,
* no response will be sent and the driver which handled the request
* will send the response itself. */
int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 flags);
int (*write)(struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 flags);
/* Lock transactions: write results of ext_tcode operation into
* *store. */
int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store,
u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
u16 flags);
int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store,
u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
u16 flags);
};
void highlevel_add_host(struct hpsb_host *host);
void highlevel_remove_host(struct hpsb_host *host);
void highlevel_host_reset(struct hpsb_host *host);
int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
unsigned int length, u16 flags);
int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
u64 addr, unsigned int length, u16 flags);
int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
u16 flags);
int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
u16 flags);
void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
void *data, size_t length);
/**
* hpsb_init_highlevel - initialize a struct hpsb_highlevel
*
* This is only necessary if hpsb_get_hostinfo_bykey can be called
* before hpsb_register_highlevel.
*/
static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl)
{
rwlock_init(&hl->host_info_lock);
INIT_LIST_HEAD(&hl->host_info_list);
}
void hpsb_register_highlevel(struct hpsb_highlevel *hl);
void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
struct hpsb_host *host,
const struct hpsb_address_ops *ops,
u64 size, u64 alignment,
u64 start, u64 end);
int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
const struct hpsb_address_ops *ops,
u64 start, u64 end);
int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
u64 start);
void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
size_t data_size);
void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
unsigned long key);
void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
void *data);
#endif /* IEEE1394_HIGHLEVEL_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef _IEEE1394_HOTPLUG_H
#define _IEEE1394_HOTPLUG_H
/* Unit spec id and sw version entry for some protocols */
#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define AVC_SW_VERSION_ENTRY 0x00010001
#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define CAMERA_SW_VERSION_ENTRY 0x00000100
/* /include/linux/mod_devicetable.h defines:
* IEEE1394_MATCH_VENDOR_ID
* IEEE1394_MATCH_MODEL_ID
* IEEE1394_MATCH_SPECIFIER_ID
* IEEE1394_MATCH_VERSION
* struct ieee1394_device_id
*/
#include <linux/mod_devicetable.h>
#endif /* _IEEE1394_HOTPLUG_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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