Commit 56880b13 authored by Ben Collins's avatar Ben Collins

Merge http://linux.bkbits.net/linux-2.5

into debian.org:/usr/src/kernel/ieee1394-2.6
parents 95a08114 01bdc78f
......@@ -110,8 +110,12 @@ config IEEE1394_ETH1394
tristate "Ethernet over 1394"
depends on IEEE1394
help
Extremely Experimental! This driver is a Linux specific way to use your
IEEE1394 Host as an Ethernet type device. This is _NOT_ IP1394.
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.
config IEEE1394_DV1394
tristate "OHCI-DV I/O support"
......
......@@ -3,7 +3,8 @@
#
ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
highlevel.o csr.o nodemgr.o oui.o dma.o iso.o
highlevel.o csr.o nodemgr.o oui.o dma.o iso.o \
csr1212.o
obj-$(CONFIG_IEEE1394) += ieee1394.o
obj-$(CONFIG_IEEE1394_PCILYNX) += pcilynx.o
......
......@@ -862,14 +862,14 @@ static int stream_alloc_packet_lists(struct stream *s)
static void stream_free_packet_lists(struct stream *s)
{
struct list_head *lh, *next;
struct packet_list *packet_l, *packet_l_next;
if (s->current_packet_list != NULL)
packet_list_free(s->current_packet_list, s);
list_for_each_safe(lh, next, &s->dma_packet_lists)
packet_list_free(list_entry(lh, struct packet_list, link), s);
list_for_each_safe(lh, next, &s->free_packet_lists)
packet_list_free(list_entry(lh, struct packet_list, link), s);
list_for_each_entry_safe(packet_l, packet_l_next, &s->dma_packet_lists, link)
packet_list_free(packet_l, s);
list_for_each_entry_safe(packet_l, packet_l_next, &s->free_packet_lists, link)
packet_list_free(packet_l, s);
if (s->packet_pool != NULL)
pci_pool_destroy(s->packet_pool);
......
......@@ -23,6 +23,7 @@
#include <linux/param.h>
#include <linux/spinlock.h>
#include "csr1212.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394.h"
......@@ -35,7 +36,10 @@ static int fcp = 1;
module_param(fcp, int, 0444);
MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
static struct csr1212_keyval *node_cap = NULL;
static void add_host(struct hpsb_host *host);
static void remove_host(struct hpsb_host *host);
static void host_reset(struct hpsb_host *host);
static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 fl);
......@@ -49,10 +53,15 @@ static int lock_regs(struct hpsb_host *host, int nodeid, quadlet_t *store,
u64 addr, quadlet_t data, quadlet_t arg, int extcode, u16 fl);
static int lock64_regs(struct hpsb_host *host, int nodeid, octlet_t * store,
u64 addr, octlet_t data, octlet_t arg, int extcode, u16 fl);
static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 fl);
static u64 allocate_addr_range(u64 size, u32 alignment, void *__host);
static void release_addr_range(u64 addr, void *__host);
static struct hpsb_highlevel csr_highlevel = {
.name = "standard registers",
.add_host = add_host,
.remove_host = remove_host,
.host_reset = host_reset,
};
......@@ -71,6 +80,15 @@ static struct hpsb_address_ops reg_ops = {
.lock64 = lock64_regs,
};
static struct hpsb_address_ops config_rom_ops = {
.read = read_config_rom,
};
struct csr1212_bus_ops csr_bus_ops = {
.allocate_addr_range = allocate_addr_range,
.release_addr = release_addr_range,
};
static u16 csr_crc16(unsigned *data, int length)
{
......@@ -162,10 +180,13 @@ static inline void calculate_expire(struct csr_control *csr)
static void add_host(struct hpsb_host *host)
{
struct csr1212_keyval *root;
quadlet_t bus_info[CSR_BUS_INFO_SIZE];
hpsb_register_addrspace(&csr_highlevel, host, &reg_ops,
CSR_REGISTER_BASE,
CSR_REGISTER_BASE + CSR_CONFIG_ROM);
hpsb_register_addrspace(&csr_highlevel, host, &map_ops,
hpsb_register_addrspace(&csr_highlevel, host, &config_rom_ops,
CSR_REGISTER_BASE + CSR_CONFIG_ROM,
CSR_REGISTER_BASE + CSR_CONFIG_ROM_END);
if (fcp) {
......@@ -182,8 +203,6 @@ static void add_host(struct hpsb_host *host)
host->csr.lock = SPIN_LOCK_UNLOCKED;
host->csr.rom_size = host->driver->get_rom(host, &host->csr.rom);
host->csr.rom_version = 0;
host->csr.state = 0;
host->csr.node_ids = 0;
host->csr.split_timeout_hi = 0;
......@@ -202,42 +221,99 @@ static void add_host(struct hpsb_host *host)
host->driver->hw_csr_reg(host, 2, 0xfffffffe, ~0);
}
}
}
int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
size_t size, unsigned char rom_version)
{
unsigned long flags;
int ret;
if (host->csr.max_rec >= 9)
host->csr.max_rom = 2;
else if (host->csr.max_rec >= 5)
host->csr.max_rom = 1;
else
host->csr.max_rom = 0;
spin_lock_irqsave(&host->csr.lock, flags);
if (rom_version != host->csr.rom_version)
ret = -1;
else if (size > (CSR_CONFIG_ROM_SIZE << 2))
ret = -2;
else {
memcpy(host->csr.rom,new_rom,size);
host->csr.rom_size=size;
host->csr.rom_version++;
ret=0;
host->csr.generation = 2;
bus_info[1] = __constant_cpu_to_be32(0x31333934);
bus_info[2] = cpu_to_be32((1 << CSR_IRMC_SHIFT) |
(1 << CSR_CMC_SHIFT) |
(1 << CSR_ISC_SHIFT) |
(0 << CSR_BMC_SHIFT) |
(0 << CSR_PMC_SHIFT) |
(host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
(host->csr.max_rec << CSR_MAX_REC_SHIFT) |
(host->csr.max_rom << CSR_MAX_ROM_SHIFT) |
(host->csr.generation << CSR_GENERATION_SHIFT) |
host->csr.lnk_spd);
bus_info[3] = cpu_to_be32(host->csr.guid_hi);
bus_info[4] = cpu_to_be32(host->csr.guid_lo);
/* The hardware copy of the bus info block will be set later when a
* bus reset is issued. */
csr1212_init_local_csr(host->csr.rom, bus_info, host->csr.max_rom);
host->csr.rom->max_rom = host->csr.max_rom;
root = host->csr.rom->root_kv;
if(csr1212_attach_keyval_to_directory(root, node_cap) != CSR1212_SUCCESS) {
HPSB_ERR("Failed to attach Node Capabilities to root directory");
}
spin_unlock_irqrestore(&host->csr.lock, flags);
return ret;
host->update_config_rom = 1;
}
int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer,
size_t buffersize, size_t *rom_size, unsigned char *rom_version)
static void remove_host(struct hpsb_host *host)
{
quadlet_t bus_info[CSR_BUS_INFO_SIZE];
bus_info[1] = __constant_cpu_to_be32(0x31333934);
bus_info[2] = cpu_to_be32((0 << CSR_IRMC_SHIFT) |
(0 << CSR_CMC_SHIFT) |
(0 << CSR_ISC_SHIFT) |
(0 << CSR_BMC_SHIFT) |
(0 << CSR_PMC_SHIFT) |
(host->csr.cyc_clk_acc << CSR_CYC_CLK_ACC_SHIFT) |
(host->csr.max_rec << CSR_MAX_REC_SHIFT) |
(0 << CSR_MAX_ROM_SHIFT) |
(0 << CSR_GENERATION_SHIFT) |
host->csr.lnk_spd);
bus_info[3] = cpu_to_be32(host->csr.guid_hi);
bus_info[4] = cpu_to_be32(host->csr.guid_lo);
csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, node_cap);
csr1212_init_local_csr(host->csr.rom, bus_info, 0);
host->update_config_rom = 1;
}
int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
size_t buffersize, unsigned char rom_version)
{
unsigned long flags;
int ret;
HPSB_NOTICE("hpsb_update_config_rom() is deprecated");
spin_lock_irqsave(&host->csr.lock, flags);
*rom_version=host->csr.rom_version;
*rom_size=host->csr.rom_size;
if (buffersize < host->csr.rom_size)
if (rom_version != host->csr.generation)
ret = -1;
else if (buffersize > host->csr.rom->cache_head->size)
ret = -2;
else {
memcpy(buffer,host->csr.rom,host->csr.rom_size);
/* Just overwrite the generated ConfigROM image with new data,
* it can be regenerated later. */
memcpy(host->csr.rom->cache_head->data, new_rom, buffersize);
host->csr.rom->cache_head->len = buffersize;
if (host->driver->set_hw_config_rom)
host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data);
/* Increment the generation number to keep some sort of sync
* with the newer ConfigROM manipulation method. */
host->csr.generation++;
if (host->csr.generation > 0xf || host->csr.generation < 2)
host->csr.generation = 2;
ret=0;
}
spin_unlock_irqrestore(&host->csr.lock, flags);
......@@ -255,13 +331,7 @@ static int read_maps(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
spin_lock_irqsave(&host->csr.lock, flags);
if (csraddr < CSR_TOPOLOGY_MAP) {
if (csraddr + length > CSR_CONFIG_ROM + host->csr.rom_size) {
spin_unlock_irqrestore(&host->csr.lock, flags);
return RCODE_ADDRESS_ERROR;
}
src = ((char *)host->csr.rom) + csraddr - CSR_CONFIG_ROM;
} else if (csraddr < CSR_SPEED_MAP) {
if (csraddr < CSR_SPEED_MAP) {
src = ((char *)host->csr.topology_map) + csraddr
- CSR_TOPOLOGY_MAP;
} else {
......@@ -738,14 +808,52 @@ static int write_fcp(struct hpsb_host *host, int nodeid, int dest,
return RCODE_COMPLETE;
}
static int read_config_rom(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 fl)
{
u32 offset = addr - CSR1212_REGISTER_SPACE_BASE;
if (csr1212_read(host->csr.rom, offset, buffer, length) == CSR1212_SUCCESS)
return RCODE_COMPLETE;
else
return RCODE_ADDRESS_ERROR;
}
void init_csr(void)
static u64 allocate_addr_range(u64 size, u32 alignment, void *__host)
{
struct hpsb_host *host = (struct hpsb_host*)__host;
return hpsb_allocate_and_register_addrspace(&csr_highlevel,
host,
&config_rom_ops,
size, alignment,
CSR1212_UNITS_SPACE_BASE,
CSR1212_UNITS_SPACE_END);
}
static void release_addr_range(u64 addr, void *__host)
{
struct hpsb_host *host = (struct hpsb_host*)__host;
hpsb_unregister_addrspace(&csr_highlevel, host, addr);
}
int init_csr(void)
{
node_cap = csr1212_new_immediate(CSR1212_KV_ID_NODE_CAPABILITIES, 0x0083c0);
if (!node_cap) {
HPSB_ERR("Failed to allocate memory for Node Capabilties ConfigROM entry!");
return -ENOMEM;
}
hpsb_register_highlevel(&csr_highlevel);
return 0;
}
void cleanup_csr(void)
{
if (node_cap)
csr1212_release_keyval(node_cap);
hpsb_unregister_highlevel(&csr_highlevel);
}
......@@ -6,6 +6,8 @@
#include <linux/sched.h>
#endif
#include "csr1212.h"
#define CSR_REGISTER_BASE 0xfffff0000000ULL
/* register offsets relative to CSR_REGISTER_BASE */
......@@ -34,6 +36,27 @@
#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 Inforamation 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
#define CSR_SET_BUS_INFO_GENERATION(csr, gen) \
((csr)->bus_info_data[2] = \
cpu_to_be32((be32_to_cpu((csr)->bus_info_data[2]) & \
~(0xf << CSR_GENERATION_SHIFT)) | \
(gen) << CSR_GENERATION_SHIFT))
struct csr_control {
spinlock_t lock;
......@@ -49,17 +72,25 @@ struct csr_control {
quadlet_t channels_available_hi, channels_available_lo;
quadlet_t broadcast_channel;
quadlet_t *rom;
size_t rom_size;
unsigned char rom_version;
/* 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;
void init_csr(void);
int init_csr(void);
void cleanup_csr(void);
#endif /* _IEEE1394_CSR_H */
/*
* csr1212.c -- IEEE 1212 Control and Status Register support for Linux
*
* Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
* Steve Kinneberg <kinnebergsteve@acmsystems.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* TODO List:
* - Verify interface consistency: i.e., public functions that take a size
* parameter expect size to be in bytes.
* - Convenience functions for reading a block of data from a given offset.
*/
#ifndef __KERNEL__
#include <string.h>
#endif
#include "csr1212.h"
/* Permitted key type for each key id */
#define __I (1 << CSR1212_KV_TYPE_IMMEDIATE)
#define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET)
#define __D (1 << CSR1212_KV_TYPE_DIRECTORY)
#define __L (1 << CSR1212_KV_TYPE_LEAF)
static const u_int8_t csr1212_key_id_type_map[0x30] = {
0, /* Reserved */
__D | __L, /* Descriptor */
__I | __D | __L, /* Bus_Dependent_Info */
__I | __D | __L, /* Vendor */
__I, /* Hardware_Version */
0, 0, /* Reserved */
__D | __L, /* Module */
0, 0, 0, 0, /* Reserved */
__I, /* Node_Capabilities */
__L, /* EUI_64 */
0, 0, 0, /* Reserved */
__D, /* Unit */
__I, /* Specifier_ID */
__I, /* Version */
__I | __C | __D | __L, /* Dependent_Info */
__L, /* Unit_Location */
0, /* Reserved */
__I, /* Model */
__D, /* Instance */
__L, /* Keyword */
__D, /* Feature */
__L, /* Extended_ROM */
__I, /* Extended_Key_Specifier_ID */
__I, /* Extended_Key */
__I | __C | __D | __L, /* Extended_Data */
__L, /* Modifiable_Descriptor */
__I, /* Directory_ID */
__I, /* Revision */
};
#undef __I
#undef __C
#undef __D
#undef __L
#define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t))
#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t))
static inline void free_keyval(struct csr1212_keyval *kv)
{
if (kv->key.type == CSR1212_KV_TYPE_LEAF)
CSR1212_FREE(kv->value.leaf.data);
CSR1212_FREE(kv);
}
static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length)
{
int shift;
u_int32_t data;
u_int16_t sum, crc = 0;
for (; length; length--) {
data = CSR1212_BE32_TO_CPU(*buffer);
buffer++;
for (shift = 28; shift >= 0; shift -= 4 ) {
sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
}
crc &= 0xffff;
}
return CSR1212_CPU_TO_BE16(crc);
}
#if 0
/* Microsoft computes the CRC with the bytes in reverse order. Therefore we
* have a special version of the CRC algorithm to account for their buggy
* software. */
static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length)
{
int shift;
u_int32_t data;
u_int16_t sum, crc = 0;
for (; length; length--) {
data = CSR1212_LE32_TO_CPU(*buffer);
buffer++;
for (shift = 28; shift >= 0; shift -= 4 ) {
sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum);
}
crc &= 0xffff;
}
return CSR1212_CPU_TO_BE16(crc);
}
#endif
static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir,
struct csr1212_keyval *kv)
{
struct csr1212_dentry *pos;
for (pos = dir->value.directory.dentries_head;
pos != NULL; pos = pos->next) {
if (pos->kv == kv)
return pos;
}
return NULL;
}
static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list,
u_int32_t offset)
{
struct csr1212_keyval *kv;
for (kv = kv_list; kv != NULL; kv = kv->next) {
if (kv->offset == offset)
return kv;
}
return NULL;
}
/* Creation Routines */
struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
size_t bus_info_size, void *private)
{
struct csr1212_csr *csr;
csr = CSR1212_MALLOC(sizeof(*csr));
if (!csr)
return NULL;
csr->cache_head =
csr1212_rom_cache_malloc(CSR1212_CONFIG_ROM_SPACE_OFFSET,
CSR1212_CONFIG_ROM_SPACE_SIZE);
if (!csr->cache_head) {
CSR1212_FREE(csr);
return NULL;
}
/* The keyval key id is not used for the root node, but a valid key id
* that can be used for a directory needs to be passed to
* csr1212_new_directory(). */
csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR);
if (!csr->root_kv) {
CSR1212_FREE(csr->cache_head);
CSR1212_FREE(csr);
return NULL;
}
csr->bus_info_data = csr->cache_head->data;
csr->bus_info_len = bus_info_size;
csr->crc_len = bus_info_size;
csr->ops = ops;
csr->private = private;
csr->cache_tail = csr->cache_head;
return csr;
}
void csr1212_init_local_csr(struct csr1212_csr *csr,
const u_int32_t *bus_info_data, int max_rom)
{
csr->max_rom = max_rom;
memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len);
}
static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key)
{
struct csr1212_keyval *kv;
if (key < 0x30 && ((csr1212_key_id_type_map[key] & (1 << type)) == 0))
return NULL;
kv = CSR1212_MALLOC(sizeof(*kv));
if (!kv)
return NULL;
kv->key.type = type;
kv->key.id = key;
kv->associate = NULL;
kv->refcnt = 1;
kv->next = NULL;
kv->prev = NULL;
kv->offset = 0;
kv->valid = 0;
return kv;
}
struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value)
{
struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key);
if (!kv)
return NULL;
kv->value.immediate = value;
kv->valid = 1;
return kv;
}
struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len)
{
struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key);
if (!kv)
return NULL;
if (data_len > 0) {
kv->value.leaf.data = CSR1212_MALLOC(data_len);
if (!kv->value.leaf.data)
{
CSR1212_FREE(kv);
return NULL;
}
if (data)
memcpy(kv->value.leaf.data, data, data_len);
} else {
kv->value.leaf.data = NULL;
}
kv->value.leaf.len = bytes_to_quads(data_len);
kv->offset = 0;
kv->valid = 1;
return kv;
}
struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset)
{
struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key);
if (!kv)
return NULL;
kv->value.csr_offset = csr_offset;
kv->offset = 0;
kv->valid = 1;
return kv;
}
struct csr1212_keyval *csr1212_new_directory(u_int8_t key)
{
struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key);
if (!kv)
return NULL;
kv->value.directory.len = 0;
kv->offset = 0;
kv->value.directory.dentries_head = NULL;
kv->value.directory.dentries_tail = NULL;
kv->valid = 1;
return kv;
}
int csr1212_associate_keyval(struct csr1212_keyval *kv,
struct csr1212_keyval *associate)
{
if (!kv || !associate)
return CSR1212_EINVAL;
if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR ||
(associate->key.id != CSR1212_KV_ID_DESCRIPTOR &&
associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO &&
associate->key.id != CSR1212_KV_ID_EXTENDED_KEY &&
associate->key.id != CSR1212_KV_ID_EXTENDED_DATA &&
associate->key.id < 0x30))
return CSR1212_EINVAL;
if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID &&
associate->key.id != CSR1212_KV_ID_EXTENDED_KEY)
return CSR1212_EINVAL;
if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
associate->key.id != CSR1212_KV_ID_EXTENDED_DATA)
return CSR1212_EINVAL;
if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY &&
kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID)
return CSR1212_EINVAL;
if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA &&
kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)
return CSR1212_EINVAL;
if (kv->associate)
csr1212_release_keyval(kv->associate);
associate->refcnt++;
kv->associate = associate;
return CSR1212_SUCCESS;
}
int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
struct csr1212_keyval *kv)
{
struct csr1212_dentry *dentry;
if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY)
return CSR1212_EINVAL;
dentry = CSR1212_MALLOC(sizeof(*dentry));
if (!dentry)
return CSR1212_ENOMEM;
dentry->kv = kv;
kv->refcnt++;
dentry->next = NULL;
dentry->prev = dir->value.directory.dentries_tail;
if (!dir->value.directory.dentries_head)
dir->value.directory.dentries_head = dentry;
if (dir->value.directory.dentries_tail)
dir->value.directory.dentries_tail->next = dentry;
dir->value.directory.dentries_tail = dentry;
return CSR1212_SUCCESS;
}
struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key,
u_int32_t value)
{
struct csr1212_keyval *kvs, *kvk, *kvv;
kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec);
kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key);
kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value);
if (!kvs || !kvk || !kvv) {
if (kvs)
free_keyval(kvs);
if (kvk)
free_keyval(kvk);
if (kvv)
free_keyval(kvv);
return NULL;
}
/* Don't keep a local reference to the extended key or value. */
kvk->refcnt = 0;
kvv->refcnt = 0;
csr1212_associate_keyval(kvk, kvv);
csr1212_associate_keyval(kvs, kvk);
return kvs;
}
struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key,
const void *data, size_t data_len)
{
struct csr1212_keyval *kvs, *kvk, *kvv;
kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec);
kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key);
kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len);
if (!kvs || !kvk || !kvv) {
if (kvs)
free_keyval(kvs);
if (kvk)
free_keyval(kvk);
if (kvv)
free_keyval(kvv);
return NULL;
}
/* Don't keep a local reference to the extended key or value. */
kvk->refcnt = 0;
kvv->refcnt = 0;
csr1212_associate_keyval(kvk, kvv);
csr1212_associate_keyval(kvs, kvk);
return kvs;
}
struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id,
const void *data, size_t data_len)
{
struct csr1212_keyval *kv;
kv = csr1212_new_leaf(CSR1212_KV_ID_DESCRIPTOR, NULL,
data_len + CSR1212_DESCRIPTOR_LEAF_OVERHEAD);
if (!kv)
return NULL;
CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype);
CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id);
if (data) {
memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len);
}
return kv;
}
struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth,
u_int16_t cset,
u_int16_t language,
const void *data,
size_t data_len)
{
struct csr1212_keyval *kv;
char *lstr;
kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len +
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
if (!kv)
return NULL;
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth);
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset);
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language);
lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
/* make sure last quadlet is zeroed out */
*((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0;
/* don't copy the NUL terminator */
memcpy(lstr, data, data_len);
return kv;
}
static int csr1212_check_minimal_ascii(const char *s)
{
static const char minimal_ascii_table[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00,
};
for (; *s; s++) {
if (minimal_ascii_table[*s & 0x7F] != *s)
return -1; /* failed */
}
/* String conforms to minimal-ascii, as specified by IEEE 1212,
* par. 7.4 */
return 0;
}
struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s)
{
/* Check if string conform to minimal_ascii format */
if (csr1212_check_minimal_ascii(s))
return NULL;
/* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */
return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s));
}
struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version,
u_int8_t palette_depth,
u_int8_t color_space,
u_int16_t language,
u_int16_t hscan,
u_int16_t vscan,
u_int32_t *palette,
u_int32_t *pixels)
{
static const int pd[4] = { 0, 4, 16, 256 };
static const int cs[16] = { 4, 2 };
struct csr1212_keyval *kv;
int palette_size = pd[palette_depth] * cs[color_space];
int pixel_size = (hscan * vscan + 3) & ~0x3;
if ((palette_depth && !palette) || !pixels)
return NULL;
kv = csr1212_new_descriptor_leaf(1, 0, NULL,
palette_size + pixel_size +
CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD);
if (!kv)
return NULL;
CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version);
CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth);
CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space);
CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language);
CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan);
CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan);
if (palette_size)
memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette,
palette_size);
memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size);
return kv;
}
struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size,
u_int64_t address)
{
struct csr1212_keyval *kv;
/* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */
kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t));
if(!kv)
return NULL;
CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size);
CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address);
CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address);
return kv;
}
static int csr1212_check_keyword(const char *s)
{
for (; *s; s++) {
if (('A' <= *s) && (*s <= 'Z'))
continue;
if (('0' <= *s) && (*s <= '9'))
continue;
if (*s == '-')
continue;
return -1; /* failed */
}
/* String conforms to keyword, as specified by IEEE 1212,
* par. 7.6.5 */
return CSR1212_SUCCESS;
}
struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[])
{
struct csr1212_keyval *kv;
char *buffer;
int i, data_len = 0;
/* Check all keywords to see if they conform to restrictions:
* Only the following characters is allowed ['A'..'Z','0'..'9','-']
* Each word is zero-terminated.
* Also calculate the total length of the keywords.
*/
for (i = 0; i < strc; i++) {
if (!strv[i] || csr1212_check_keyword(strv[i])) {
return NULL;
}
data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */
}
/* IEEE 1212, par. 7.6.5 Keyword leaves */
kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len);
if (!kv)
return NULL;
buffer = (char *)kv->value.leaf.data;
/* make sure last quadlet is zeroed out */
*((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0;
/* Copy keyword(s) into leaf data buffer */
for (i = 0; i < strc; i++) {
int len = strlen(strv[i]) + 1;
memcpy(buffer, strv[i], len);
buffer += len;
}
return kv;
}
/* Destruction Routines */
void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
struct csr1212_keyval *kv)
{
struct csr1212_dentry *dentry;
if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY)
return;
dentry = csr1212_find_keyval(dir, kv);
if (!dentry)
return;
if (dentry->prev)
dentry->prev->next = dentry->next;
if (dentry->next)
dentry->next->prev = dentry->prev;
if (dir->value.directory.dentries_head == dentry)
dir->value.directory.dentries_head = dentry->next;
if (dir->value.directory.dentries_tail == dentry)
dir->value.directory.dentries_tail = dentry->prev;
CSR1212_FREE(dentry);
csr1212_release_keyval(kv);
}
void csr1212_disassociate_keyval(struct csr1212_keyval *kv)
{
if (kv->associate) {
csr1212_release_keyval(kv->associate);
}
kv->associate = NULL;
}
/* This function is used to free the memory taken by a keyval. If the given
* keyval is a directory type, then any keyvals contained in that directory
* will be destroyed as well if their respective refcnts are 0. By means of
* list manipulation, this routine will descend a directory structure in a
* non-recursive manner. */
void _csr1212_destroy_keyval(struct csr1212_keyval *kv)
{
struct csr1212_keyval *k, *a;
struct csr1212_dentry dentry;
struct csr1212_dentry *head, *tail;
dentry.kv = kv;
dentry.next = NULL;
dentry.prev = NULL;
head = &dentry;
tail = head;
while (head) {
k = head->kv;
while (k) {
k->refcnt--;
if (k->refcnt > 0)
break;
a = k->associate;
if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) {
/* If the current entry is a directory, then move all
* the entries to the destruction list. */
tail->next = k->value.directory.dentries_head;
if (k->value.directory.dentries_head)
k->value.directory.dentries_head->prev = tail;
tail = k->value.directory.dentries_tail;
}
free_keyval(k);
k = a;
}
head = head->next;
if (head) {
if (head->prev && head->prev != &dentry) {
CSR1212_FREE(head->prev);
}
head->prev = NULL;
} else if (tail != &dentry)
CSR1212_FREE(tail);
}
}
void csr1212_destroy_csr(struct csr1212_csr *csr)
{
struct csr1212_csr_rom_cache *c, *oc;
struct csr1212_cache_region *cr, *ocr;
csr1212_release_keyval(csr->root_kv);
c = csr->cache_head;
while (c) {
oc = c;
cr = c->filled_head;
while (cr) {
ocr = cr;
cr = cr->next;
CSR1212_FREE(ocr);
}
c = c->next;
CSR1212_FREE(oc);
}
CSR1212_FREE(csr);
}
/* CSR Image Creation */
static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize)
{
struct csr1212_csr_rom_cache *cache;
u_int64_t csr_addr;
if (!csr || !csr->ops->allocate_addr_range ||
!csr->ops->release_addr)
return CSR1212_ENOMEM;
/* ROM size must be a multiple of csr->max_rom */
romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1);
csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private);
if (csr_addr == ~0ULL) {
return CSR1212_ENOMEM;
}
if (csr_addr < CSR1212_REGISTER_SPACE_BASE) {
/* Invalid address returned from allocate_addr_range(). */
csr->ops->release_addr(csr_addr, csr->private);
return CSR1212_ENOMEM;
}
cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize);
if (!cache) {
csr->ops->release_addr(csr_addr, csr->private);
return CSR1212_ENOMEM;
}
cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM);
if (!cache->ext_rom) {
csr->ops->release_addr(csr_addr, csr->private);
CSR1212_FREE(cache);
return CSR1212_ENOMEM;
}
if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS)
{
csr1212_release_keyval(cache->ext_rom);
csr->ops->release_addr(csr_addr, csr->private);
CSR1212_FREE(cache);
return CSR1212_ENOMEM;
}
cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE;
cache->ext_rom->value.leaf.len = 0;
/* Add cache to tail of cache list */
cache->prev = csr->cache_tail;
csr->cache_tail->next = cache;
csr->cache_tail = cache;
return CSR1212_SUCCESS;
}
static inline void csr1212_remove_cache(struct csr1212_csr *csr,
struct csr1212_csr_rom_cache *cache)
{
if (csr->cache_head == cache)
csr->cache_head = cache->next;
if (csr->cache_tail == cache)
csr->cache_tail = cache->prev;
if (cache->prev)
cache->prev->next = cache->next;
if (cache->next)
cache->next->prev = cache->prev;
if (cache->ext_rom) {
csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom);
csr1212_release_keyval(cache->ext_rom);
}
CSR1212_FREE(cache);
}
static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir,
struct csr1212_keyval **layout_tail)
{
struct csr1212_dentry *dentry;
struct csr1212_keyval *dkv;
struct csr1212_keyval *last_extkey_spec = NULL;
struct csr1212_keyval *last_extkey = NULL;
int num_entries = 0;
for (dentry = dir->value.directory.dentries_head; dentry;
dentry = dentry->next) {
for (dkv = dentry->kv; dkv; dkv = dkv->associate) {
/* Special Case: Extended Key Specifier_ID */
if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
if (last_extkey_spec == NULL) {
last_extkey_spec = dkv;
} else if (dkv->value.immediate != last_extkey_spec->value.immediate) {
last_extkey_spec = dkv;
} else {
continue;
}
/* Special Case: Extended Key */
} else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
if (last_extkey == NULL) {
last_extkey = dkv;
} else if (dkv->value.immediate != last_extkey->value.immediate) {
last_extkey = dkv;
} else {
continue;
}
}
num_entries += 1;
switch(dkv->key.type) {
default:
case CSR1212_KV_TYPE_IMMEDIATE:
case CSR1212_KV_TYPE_CSR_OFFSET:
continue;
case CSR1212_KV_TYPE_LEAF:
case CSR1212_KV_TYPE_DIRECTORY:
/* Remove from list */
if (dkv->prev)
dkv->prev->next = dkv->next;
if (dkv->next)
dkv->next->prev = dkv->prev;
if (dkv == *layout_tail)
*layout_tail = dkv->prev;
/* Special case: Extended ROM leafs */
if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
dkv->value.leaf.len = 0; /* initialize to zero */
/* Don't add Extended ROM leafs in the layout list,
* they are handled differently. */
break;
}
/* Add to tail of list */
dkv->next = NULL;
dkv->prev = *layout_tail;
(*layout_tail)->next = dkv;
*layout_tail = dkv;
break;
}
}
}
return num_entries;
}
size_t csr1212_generate_layout_order(struct csr1212_keyval *kv)
{
struct csr1212_keyval *ltail = kv;
size_t agg_size = 0;
while(kv) {
switch(kv->key.type) {
case CSR1212_KV_TYPE_LEAF:
/* Add 1 quadlet for crc/len field */
agg_size += kv->value.leaf.len + 1;
break;
case CSR1212_KV_TYPE_DIRECTORY:
kv->value.directory.len = csr1212_generate_layout_subdir(kv, &ltail);
/* Add 1 quadlet for crc/len field */
agg_size += kv->value.directory.len + 1;
break;
}
kv = kv->next;
}
return quads_to_bytes(agg_size);
}
struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
struct csr1212_keyval *start_kv,
int start_pos)
{
struct csr1212_keyval *kv = start_kv;
struct csr1212_keyval *okv = start_kv;
int pos = start_pos;
int kv_len = 0, okv_len = 0;
cache->layout_head = kv;
while(kv && pos < cache->size) {
kv->offset = cache->offset + pos;
switch(kv->key.type) {
case CSR1212_KV_TYPE_LEAF:
kv_len = kv->value.leaf.len;
break;
case CSR1212_KV_TYPE_DIRECTORY:
kv_len = kv->value.directory.len;
break;
default:
/* Should never get here */
break;
}
pos += quads_to_bytes(kv_len + 1);
if (pos <= cache->size) {
okv = kv;
okv_len = kv_len;
kv = kv->next;
}
}
cache->layout_tail = okv;
cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1);
return kv;
}
static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir,
u_int32_t *data_buffer)
{
struct csr1212_dentry *dentry;
struct csr1212_keyval *last_extkey_spec = NULL;
struct csr1212_keyval *last_extkey = NULL;
int index = 0;
for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) {
struct csr1212_keyval *a;
for (a = dentry->kv; a; a = a->associate) {
u_int32_t value = 0;
/* Special Case: Extended Key Specifier_ID */
if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) {
if (last_extkey_spec == NULL) {
last_extkey_spec = a;
} else if (a->value.immediate != last_extkey_spec->value.immediate) {
last_extkey_spec = a;
} else {
continue;
}
/* Special Case: Extended Key */
} else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) {
if (last_extkey == NULL) {
last_extkey = a;
} else if (a->value.immediate != last_extkey->value.immediate) {
last_extkey = a;
} else {
continue;
}
}
switch(a->key.type) {
case CSR1212_KV_TYPE_IMMEDIATE:
value = a->value.immediate;
break;
case CSR1212_KV_TYPE_CSR_OFFSET:
value = a->value.csr_offset;
break;
case CSR1212_KV_TYPE_LEAF:
value = a->offset;
value -= dir->offset + quads_to_bytes(1+index);
value = bytes_to_quads(value);
break;
case CSR1212_KV_TYPE_DIRECTORY:
value = a->offset;
value -= dir->offset + quads_to_bytes(1+index);
value = bytes_to_quads(value);
break;
default:
/* Should never get here */
break; /* GDB breakpoint */
}
value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT;
value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) <<
(CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT);
data_buffer[index] = CSR1212_CPU_TO_BE32(value);
index++;
}
}
}
void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache)
{
struct csr1212_keyval *kv, *nkv;
struct csr1212_keyval_img *kvi;
for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) {
kvi = (struct csr1212_keyval_img *)
(cache->data + bytes_to_quads(kv->offset - cache->offset));
switch(kv->key.type) {
default:
case CSR1212_KV_TYPE_IMMEDIATE:
case CSR1212_KV_TYPE_CSR_OFFSET:
/* Should never get here */
break; /* GDB breakpoint */
case CSR1212_KV_TYPE_LEAF:
/* Don't copy over Extended ROM areas, they are
* already filled out! */
if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
memcpy(kvi->data, kv->value.leaf.data,
quads_to_bytes(kv->value.leaf.len));
kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len);
kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len);
break;
case CSR1212_KV_TYPE_DIRECTORY:
csr1212_generate_tree_subdir(kv, kvi->data);
kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len);
kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len);
break;
}
nkv = kv->next;
kv->prev = NULL;
kv->next = NULL;
}
}
int csr1212_generate_csr_image(struct csr1212_csr *csr)
{
struct csr1212_bus_info_block_img *bi;
struct csr1212_csr_rom_cache *cache;
struct csr1212_keyval *kv;
size_t agg_size;
int ret;
int init_offset;
if (!csr)
return CSR1212_EINVAL;
cache = csr->cache_head;
bi = (struct csr1212_bus_info_block_img*)cache->data;
bi->length = bytes_to_quads(csr->bus_info_len) - 1;
bi->crc_length = bi->length;
bi->crc = csr1212_crc16(bi->data, bi->crc_length);
agg_size = csr1212_generate_layout_order(csr->root_kv);
init_offset = csr->bus_info_len;
for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) {
if (!cache) {
/* Estimate approximate number of additional cache
* regions needed (it assumes that the cache holding
* the first 1K Config ROM space always exists). */
int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE -
(2 * sizeof(u_int32_t))) + 1;
/* Add additional cache regions, extras will be
* removed later */
for (; est_c; est_c--) {
ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE);
if (ret != CSR1212_SUCCESS)
return ret;
}
/* Need to re-layout for additional cache regions */
agg_size = csr1212_generate_layout_order(csr->root_kv);
kv = csr->root_kv;
cache = csr->cache_head;
init_offset = csr->bus_info_len;
}
kv = csr1212_generate_positions(cache, kv, init_offset);
agg_size -= cache->len;
init_offset = sizeof(u_int32_t);
}
/* Remove unused, excess cache regions */
while (cache) {
struct csr1212_csr_rom_cache *oc = cache;
cache = cache->next;
csr1212_remove_cache(csr, oc);
}
/* Go through the list backward so that when done, the correct CRC
* will be calculated for the Extended ROM areas. */
for(cache = csr->cache_tail; cache; cache = cache->prev) {
/* Only Extended ROM caches should have this set. */
if (cache->ext_rom) {
int leaf_size;
/* Make sure the Extended ROM leaf is a multiple of
* max_rom in size. */
leaf_size = (cache->len + (csr->max_rom - 1)) &
(csr->max_rom - 1);
/* Zero out the unused ROM region */
memset(cache->data + bytes_to_quads(cache->len), 0x00,
leaf_size - cache->len);
/* Subtract leaf header */
leaf_size -= sizeof(u_int32_t);
/* Update the Extended ROM leaf length */
cache->ext_rom->value.leaf.len =
bytes_to_quads(leaf_size);
} else {
/* Zero out the unused ROM region */
memset(cache->data + bytes_to_quads(cache->len), 0x00,
cache->size - cache->len);
}
/* Copy the data into the cache buffer */
csr1212_fill_cache(cache);
}
return CSR1212_SUCCESS;
}
int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len)
{
struct csr1212_csr_rom_cache *cache;
for (cache = csr->cache_head; cache; cache = cache->next) {
if (offset >= cache->offset &&
(offset + len) <= (cache->offset + cache->size)) {
memcpy(buffer,
&cache->data[bytes_to_quads(offset - cache->offset)],
len);
return CSR1212_SUCCESS;
} else if (((offset < cache->offset) &&
((offset + len) >= cache->offset)) ||
((offset >= cache->offset) &&
((offset + len) > (cache->offset + cache->size)))) {
return CSR1212_EINVAL;
}
}
return CSR1212_ENOENT;
}
/* Parse a chunk of data as a Config ROM */
static int csr1212_parse_bus_info_block(struct csr1212_csr *csr)
{
struct csr1212_bus_info_block_img *bi;
struct csr1212_cache_region *cr;
int i;
int ret;
/* IEEE 1212 says that the entire bus info block should be readable in
* a single transaction regardless of the max_rom value.
* Unfortunately, many IEEE 1394 devices do not abide by that, so the
* bus info block will be read 1 quadlet at a time. The rest of the
* ConfigROM will be read according to the max_rom field. */
for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) {
ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
sizeof(csr1212_quad_t),
&csr->cache_head->data[bytes_to_quads(i)],
csr->private);
if (ret != CSR1212_SUCCESS)
return ret;
}
bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data;
csr->crc_len = quads_to_bytes(bi->crc_length);
/* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not
* always the case, so read the rest of the crc area 1 quadlet at a time. */
for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) {
ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,
sizeof(csr1212_quad_t),
&csr->cache_head->data[bytes_to_quads(i)],
csr->private);
if (ret != CSR1212_SUCCESS)
return ret;
}
if (bytes_to_quads(csr->bus_info_len - sizeof(csr1212_quad_t)) != bi->length)
return CSR1212_EINVAL;
#if 0
/* Apparently there are too many differnt wrong implementations of the
* CRC algorithm that verifying them is moot. */
if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) &&
(csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc))
return CSR1212_EINVAL;
#endif
cr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
if (!cr)
return CSR1212_ENOMEM;
cr->next = NULL;
cr->prev = NULL;
cr->offset_start = 0;
cr->offset_end = csr->crc_len + 4;
csr->cache_head->filled_head = cr;
csr->cache_head->filled_tail = cr;
return CSR1212_SUCCESS;
}
static inline int csr1212_parse_dir_entry(struct csr1212_keyval *dir,
csr1212_quad_t ki,
u_int32_t kv_pos,
struct csr1212_csr_rom_cache *cache)
{
int ret = CSR1212_SUCCESS;
struct csr1212_keyval *k = NULL;
u_int32_t offset;
switch(CSR1212_KV_KEY_TYPE(ki)) {
case CSR1212_KV_TYPE_IMMEDIATE:
k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki),
CSR1212_KV_VAL(ki));
if (!k) {
ret = CSR1212_ENOMEM;
goto fail;
}
k->refcnt = 0; /* Don't keep local reference when parsing. */
break;
case CSR1212_KV_TYPE_CSR_OFFSET:
k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki),
CSR1212_KV_VAL(ki));
if (!k) {
ret = CSR1212_ENOMEM;
goto fail;
}
k->refcnt = 0; /* Don't keep local reference when parsing. */
break;
default:
/* Compute the offset from 0xffff f000 0000. */
offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos;
if (offset == kv_pos) {
/* Uh-oh. Can't have a relative offset of 0 for Leaves
* or Directories. The Config ROM image is most likely
* messed up, so we'll just abort here. */
ret = CSR1212_EIO;
goto fail;
}
k = csr1212_find_keyval_offset(cache->layout_head, offset);
if (k)
break; /* Found it. */
if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) {
k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki));
} else {
k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0);
}
if (!k) {
ret = CSR1212_ENOMEM;
goto fail;
}
k->refcnt = 0; /* Don't keep local reference when parsing. */
k->valid = 0; /* Contents not read yet so it's not valid. */
k->offset = offset;
k->prev = cache->layout_tail;
k->next = NULL;
if (cache->layout_tail)
cache->layout_tail->next = k;
cache->layout_tail = k;
}
ret = csr1212_attach_keyval_to_directory(dir, k);
fail:
if (ret != CSR1212_SUCCESS) {
if (k)
free_keyval(k);
}
return ret;
}
int csr1212_parse_keyval(struct csr1212_keyval *kv,
struct csr1212_csr_rom_cache *cache)
{
struct csr1212_keyval_img *kvi;
int i;
int ret = CSR1212_SUCCESS;
int kvi_len;
kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset -
cache->offset)];
kvi_len = CSR1212_BE16_TO_CPU(kvi->length);
#if 0
/* Apparently there are too many differnt wrong implementations of the
* CRC algorithm that verifying them is moot. */
if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) &&
(csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) {
ret = CSR1212_EINVAL;
goto fail;
}
#endif
switch(kv->key.type) {
case CSR1212_KV_TYPE_DIRECTORY:
for (i = 0; i < kvi_len; i++) {
csr1212_quad_t ki = kvi->data[i];
ret = csr1212_parse_dir_entry(kv, ki,
(kv->offset +
quads_to_bytes(i + 1)),
cache);
}
kv->value.directory.len = kvi_len;
break;
case CSR1212_KV_TYPE_LEAF:
if (kv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
kv->value.leaf.data = cache->data;
} else {
kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len));
if (!kv->value.leaf.data)
{
ret = CSR1212_ENOMEM;
goto fail;
}
kv->value.leaf.len = kvi_len;
memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len));
}
break;
}
kv->valid = 1;
fail:
return ret;
}
int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv)
{
struct csr1212_cache_region *cr, *ncr, *newcr = NULL;
struct csr1212_keyval_img *kvi = NULL;
struct csr1212_csr_rom_cache *cache;
int cache_index;
u_int64_t addr;
u_int32_t *cache_ptr;
u_int16_t kv_len = 0;
if (!csr || !kv)
return CSR1212_EINVAL;
/* First find which cache the data should be in (or go in if not read
* yet). */
for (cache = csr->cache_head; cache; cache = cache->next) {
if (kv->offset >= cache->offset &&
kv->offset < (cache->offset + cache->size))
break;
}
if (!cache) {
csr1212_quad_t q;
struct csr1212_csr_rom_cache *nc;
/* Only create a new cache for Extended ROM leaves. */
if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
return CSR1212_EINVAL;
if (csr->ops->bus_read(csr,
CSR1212_REGISTER_SPACE_BASE + kv->offset,
sizeof(csr1212_quad_t), &q, csr->private)) {
return CSR1212_EIO;
}
kv->value.leaf.len = quads_to_bytes(CSR1212_BE32_TO_CPU(q)>>16);
nc = csr1212_rom_cache_malloc(kv->offset, kv->value.leaf.len);
cache->next = nc;
nc->prev = cache;
csr->cache_tail = nc;
cache->filled_head =
CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
if (!cache->filled_head) {
return CSR1212_ENOMEM;
}
cache->filled_head->offset_start = 0;
cache->filled_head->offset_end = sizeof(csr1212_quad_t);
cache->filled_tail = cache->filled_head;
cache->filled_head->next = NULL;
cache->filled_head->prev = NULL;
cache->data[0] = q;
}
cache_index = kv->offset - cache->offset;
/* Now seach read portions of the cache to see if it is there. */
for (cr = cache->filled_head; cr; cr = cr->next) {
if (cache_index < cr->offset_start) {
newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
if (!newcr)
return CSR1212_ENOMEM;
newcr->offset_start = cache_index & ~(csr->max_rom - 1);
newcr->offset_end = newcr->offset_start;
newcr->next = cr;
newcr->prev = cr->prev;
cr->prev = newcr;
cr = newcr;
break;
} else if ((cache_index >= cr->offset_start) &&
(cache_index < cr->offset_end)) {
kvi = (struct csr1212_keyval_img*)
(&cache->data[bytes_to_quads(cache_index)]);
kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) +
1);
break;
} else if (cache_index == cr->offset_end)
break;
}
if (!cr) {
cr = cache->filled_tail;
newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
if (!newcr)
return CSR1212_ENOMEM;
newcr->offset_start = cache_index & ~(csr->max_rom - 1);
newcr->offset_end = newcr->offset_start;
newcr->prev = cr;
newcr->next = cr->next;
cr->next = newcr;
cr = newcr;
cache->filled_tail = newcr;
}
while(!kvi || cr->offset_end < cache_index + kv_len) {
cache_ptr = &cache->data[bytes_to_quads(cr->offset_end &
~(csr->max_rom - 1))];
addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset +
cr->offset_end) & ~(csr->max_rom - 1);
if (csr->ops->bus_read(csr, addr, csr->max_rom, cache_ptr,
csr->private)) {
if (csr->max_rom == 4)
/* We've got problems! */
return CSR1212_EIO;
/* Apperently the max_rom value was a lie, set it to
* do quadlet reads and try again. */
csr->max_rom = 4;
continue;
}
cr->offset_end += csr->max_rom - (cr->offset_end &
(csr->max_rom - 1));
if (!kvi && (cr->offset_end > cache_index)) {
kvi = (struct csr1212_keyval_img*)
(&cache->data[bytes_to_quads(cache_index)]);
kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) +
1);
}
if ((kv_len + (kv->offset - cache->offset)) > cache->size) {
/* The Leaf or Directory claims its length extends
* beyond the ConfigROM image region and thus beyond the
* end of our cache region. Therefore, we abort now
* rather than seg faulting later. */
return CSR1212_EIO;
}
ncr = cr->next;
if (ncr && (cr->offset_end >= ncr->offset_start)) {
/* consolidate region entries */
ncr->offset_start = cr->offset_start;
if (cr->prev)
cr->prev->next = cr->next;
ncr->prev = cr->prev;
if (cache->filled_head == cr)
cache->filled_head = ncr;
CSR1212_FREE(cr);
cr = ncr;
}
}
return csr1212_parse_keyval(kv, cache);
}
int csr1212_parse_csr(struct csr1212_csr *csr)
{
static const int mr_map[] = { 4, 64, 1024, 0 };
int ret;
if (!csr || !csr->ops->bus_read)
return CSR1212_EINVAL;
ret = csr1212_parse_bus_info_block(csr);
if (ret != CSR1212_SUCCESS)
return ret;
if (!csr->ops->get_max_rom)
csr->max_rom = mr_map[0]; /* default value */
else
csr->max_rom = mr_map[csr->ops->get_max_rom(csr->bus_info_data,
csr->private)];
csr->cache_head->layout_head = csr->root_kv;
csr->cache_head->layout_tail = csr->root_kv;
csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) +
csr->bus_info_len;
csr->root_kv->valid = 0;
csr1212_get_keyval(csr, csr->root_kv);
return CSR1212_SUCCESS;
}
/*
* csr1212.h -- IEEE 1212 Control and Status Register support for Linux
*
* Copyright (C) 2003 Francois Retief <fgretief@sun.ac.za>
* Steve Kinneberg <kinnebergsteve@acmsystems.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __CSR1212_H__
#define __CSR1212_H__
/* Compatibility layer */
#ifdef __KERNEL__
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#define CSR1212_MALLOC(size) kmalloc((size), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
#define CSR1212_FREE(ptr) kfree(ptr)
#define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad)
#define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad)
#define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad)
#define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad)
#define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad)
#define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad)
#define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad)
#define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad)
#define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad)
#define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad)
#define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad)
#define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad)
#include <linux/errno.h>
#define CSR1212_SUCCESS (0)
#define CSR1212_EINVAL (-EINVAL)
#define CSR1212_ENOMEM (-ENOMEM)
#define CSR1212_ENOENT (-ENOENT)
#define CSR1212_EIO (-EIO)
#define CSR1212_EBUSY (-EBUSY)
#else /* Userspace */
#include <sys/types.h>
#include <malloc.h>
#define CSR1212_MALLOC(size) malloc(size)
#define CSR1212_FREE(ptr) free(ptr)
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#include <byteswap.h>
#define CSR1212_BE16_TO_CPU(quad) bswap_16(quad)
#define CSR1212_CPU_TO_BE16(quad) bswap_16(quad)
#define CSR1212_BE32_TO_CPU(quad) bswap_32(quad)
#define CSR1212_CPU_TO_BE32(quad) bswap_32(quad)
#define CSR1212_BE64_TO_CPU(quad) bswap_64(quad)
#define CSR1212_CPU_TO_BE64(quad) bswap_64(quad)
#define CSR1212_LE16_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_LE16(quad) (quad)
#define CSR1212_LE32_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_LE32(quad) (quad)
#define CSR1212_LE64_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_LE64(quad) (quad)
#else
#define CSR1212_BE16_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_BE16(quad) (quad)
#define CSR1212_BE32_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_BE32(quad) (quad)
#define CSR1212_BE64_TO_CPU(quad) (quad)
#define CSR1212_CPU_TO_BE64(quad) (quad)
#define CSR1212_LE16_TO_CPU(quad) bswap_16(quad)
#define CSR1212_CPU_TO_LE16(quad) bswap_16(quad)
#define CSR1212_LE32_TO_CPU(quad) bswap_32(quad)
#define CSR1212_CPU_TO_LE32(quad) bswap_32(quad)
#define CSR1212_LE64_TO_CPU(quad) bswap_64(quad)
#define CSR1212_CPU_TO_LE64(quad) bswap_64(quad)
#endif
#include <errno.h>
#define CSR1212_SUCCESS (0)
#define CSR1212_EINVAL (EINVAL)
#define CSR1212_ENOMEM (ENOMEM)
#define CSR1212_ENOENT (ENOENT)
#define CSR1212_EIO (EIO)
#define CSR1212_EBUSY (EBUSY)
#endif
#define CSR1212_KV_VAL_MASK 0xffffff
#define CSR1212_KV_KEY_SHIFT 24
#define CSR1212_KV_KEY_TYPE_SHIFT 6
#define CSR1212_KV_KEY_ID_MASK 0x3f
#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* After shift */
/* CSR 1212 key types */
#define CSR1212_KV_TYPE_IMMEDIATE 0
#define CSR1212_KV_TYPE_CSR_OFFSET 1
#define CSR1212_KV_TYPE_LEAF 2
#define CSR1212_KV_TYPE_DIRECTORY 3
/* CSR 1212 key ids */
#define CSR1212_KV_ID_DESCRIPTOR 0x01
#define CSR1212_KV_ID_BUS_DEPENDENT_INFO 0x02
#define CSR1212_KV_ID_VENDOR 0x03
#define CSR1212_KV_ID_HARDWARE_VERSION 0x04
#define CSR1212_KV_ID_MODULE 0x07
#define CSR1212_KV_ID_NODE_CAPABILITIES 0x0C
#define CSR1212_KV_ID_EUI_64 0x0D
#define CSR1212_KV_ID_UNIT 0x11
#define CSR1212_KV_ID_SPECIFIER_ID 0x12
#define CSR1212_KV_ID_VERSION 0x13
#define CSR1212_KV_ID_DEPENDENT_INFO 0x14
#define CSR1212_KV_ID_UNIT_LOCATION 0x15
#define CSR1212_KV_ID_MODEL 0x17
#define CSR1212_KV_ID_INSTANCE 0x18
#define CSR1212_KV_ID_KEYWORD 0x19
#define CSR1212_KV_ID_FEATURE 0x1A
#define CSR1212_KV_ID_EXTENDED_ROM 0x1B
#define CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID 0x1C
#define CSR1212_KV_ID_EXTENDED_KEY 0x1D
#define CSR1212_KV_ID_EXTENDED_DATA 0x1E
#define CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR 0x1F
#define CSR1212_KV_ID_DIRECTORY_ID 0x20
#define CSR1212_KV_ID_REVISION 0x21
/* IEEE 1212 Address space map */
#define CSR1212_ALL_SPACE_BASE (0x000000000000ULL)
#define CSR1212_ALL_SPACE_SIZE (1ULL << 48)
#define CSR1212_ALL_SPACE_END (CSR1212_ALL_SPACE_BASE + CSR1212_ALL_SPACE_SIZE)
#define CSR1212_MEMORY_SPACE_BASE (0x000000000000ULL)
#define CSR1212_MEMORY_SPACE_SIZE ((256ULL * (1ULL << 40)) - (512ULL * (1ULL << 20)))
#define CSR1212_MEMORY_SPACE_END (CSR1212_MEMORY_SPACE_BASE + CSR1212_MEMORY_SPACE_SIZE)
#define CSR1212_PRIVATE_SPACE_BASE (0xffffe0000000ULL)
#define CSR1212_PRIVATE_SPACE_SIZE (256ULL * (1ULL << 20))
#define CSR1212_PRIVATE_SPACE_END (CSR1212_PRIVATE_SPACE_BASE + CSR1212_PRIVATE_SPACE_SIZE)
#define CSR1212_REGISTER_SPACE_BASE (0xfffff0000000ULL)
#define CSR1212_REGISTER_SPACE_SIZE (256ULL * (1ULL << 20))
#define CSR1212_REGISTER_SPACE_END (CSR1212_REGISTER_SPACE_BASE + CSR1212_REGISTER_SPACE_SIZE)
#define CSR1212_CSR_ARCH_REG_SPACE_BASE (0xfffff0000000ULL)
#define CSR1212_CSR_ARCH_REG_SPACE_SIZE (512)
#define CSR1212_CSR_ARCH_REG_SPACE_END (CSR1212_CSR_ARCH_REG_SPACE_BASE + CSR1212_CSR_ARCH_REG_SPACE_SIZE)
#define CSR1212_CSR_ARCH_REG_SPACE_OFFSET (CSR1212_CSR_ARCH_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
#define CSR1212_CSR_BUS_DEP_REG_SPACE_BASE (0xfffff0000200ULL)
#define CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE (512)
#define CSR1212_CSR_BUS_DEP_REG_SPACE_END (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE + CSR1212_CSR_BUS_DEP_REG_SPACE_SIZE)
#define CSR1212_CSR_BUS_DEP_REG_SPACE_OFFSET (CSR1212_CSR_BUS_DEP_REG_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
#define CSR1212_CONFIG_ROM_SPACE_BASE (0xfffff0000400ULL)
#define CSR1212_CONFIG_ROM_SPACE_SIZE (1024)
#define CSR1212_CONFIG_ROM_SPACE_END (CSR1212_CONFIG_ROM_SPACE_BASE + CSR1212_CONFIG_ROM_SPACE_SIZE)
#define CSR1212_CONFIG_ROM_SPACE_OFFSET (CSR1212_CONFIG_ROM_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
#define CSR1212_UNITS_SPACE_BASE (0xfffff0000800ULL)
#define CSR1212_UNITS_SPACE_SIZE ((256ULL * (1ULL << 20)) - 2048)
#define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE)
#define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE)
#define CSR1212_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t))
/* Config ROM image structures */
struct csr1212_bus_info_block_img {
u_int8_t length;
u_int8_t crc_length;
u_int16_t crc;
/* Must be last */
u_int32_t data[0]; /* older gcc can't handle [] which is standard */
};
#define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT)
#define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT)
#define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK)
#define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK)
#define CSR1212_SET_KV_KEY(quad, key) ((quad) = \
CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT)))
#define CSR1212_SET_KV_VAL(quad, val) ((quad) = \
CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val)))
#define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \
CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \
(((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \
((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT)))
typedef u_int32_t csr1212_quad_t;
struct csr1212_keyval_img {
u_int16_t length;
u_int16_t crc;
/* Must be last */
csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */
};
struct csr1212_leaf {
int len;
u_int32_t *data;
};
struct csr1212_dentry {
struct csr1212_dentry *next, *prev;
struct csr1212_keyval *kv;
};
struct csr1212_directory {
int len;
struct csr1212_dentry *dentries_head, *dentries_tail;
};
struct csr1212_keyval {
struct {
u_int8_t type;
u_int8_t id;
} key;
union {
u_int32_t immediate;
u_int32_t csr_offset;
struct csr1212_leaf leaf;
struct csr1212_directory directory;
} value;
struct csr1212_keyval *associate;
int refcnt;
/* used in generating and/or parsing CSR image */
struct csr1212_keyval *next, *prev; /* flat list of CSR elements */
u_int32_t offset; /* position in CSR from 0xffff f000 0000 */
u_int8_t valid; /* flag indicating keyval has valid data*/
};
struct csr1212_cache_region {
struct csr1212_cache_region *next, *prev;
u_int32_t offset_start; /* inclusive */
u_int32_t offset_end; /* exclusive */
};
struct csr1212_csr_rom_cache {
struct csr1212_csr_rom_cache *next, *prev;
struct csr1212_cache_region *filled_head, *filled_tail;
struct csr1212_keyval *layout_head, *layout_tail;
size_t size;
u_int32_t offset;
struct csr1212_keyval *ext_rom;
size_t len;
/* Must be last */
u_int32_t data[0]; /* older gcc can't handle [] which is standard */
};
struct csr1212_csr {
size_t bus_info_len; /* bus info block length in bytes */
size_t crc_len; /* crc length in bytes */
u_int32_t *bus_info_data; /* bus info data incl bus name and EUI */
void *private; /* private, bus specific data */
struct csr1212_bus_ops *ops;
struct csr1212_keyval *root_kv;
int max_rom; /* max bytes readable in Config ROM region */
/* Items below used for image parsing and generation */
struct csr1212_csr_rom_cache *cache_head, *cache_tail;
};
struct csr1212_bus_ops {
/* This function is used by csr1212 to read additional information
* from remote nodes when parsing a Config ROM (i.e., read Config ROM
* entries located in the Units Space. Must return 0 on success
* anything else indicates an error. */
int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr,
u_int16_t length, void *buffer, void *private);
/* This function is used by csr1212 to allocate a region in units space
* in the event that Config ROM entries don't all fit in the predefined
* 1K region. The void *private parameter is private member of struct
* csr1212_csr. */
u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment,
void *private);
/* This function is used by csr1212 to release a region in units space
* that is no longer needed. */
void (*release_addr) (u_int64_t addr, void *private);
/* This function is used by csr1212 to determine the max read request
* supported by a remote node when reading the ConfigROM space. Must
* return 0, 1, or 2 per IEEE 1212. */
int (*get_max_rom) (u_int32_t *bus_info, void *private);
};
/* Descriptor Leaf manipulation macros */
#define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24
#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff
#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t))
#define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)
#define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \
CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)
#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \
(&((kv)->value.leaf.data[1]))
#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \
((kv)->value.leaf.data[0] = \
CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \
((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT)))
#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \
((kv)->value.leaf.data[0] = \
CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \
CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \
((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK)))
/* Text Descriptor Leaf manipulation macros */
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK 0xf /* after shift */
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t))
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \
((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK)
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \
(&((kv)->value.leaf.data[2]))
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \
((kv)->value.leaf.data[1] = \
((kv)->value.leaf.data[1] & \
CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \
CSR1212_CPU_TO_BE32(((width) & \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \
((kv)->value.leaf.data[1] = \
((kv)->value.leaf.data[1] & \
CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \
CSR1212_CPU_TO_BE32(((char_set) & \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))
#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \
((kv)->value.leaf.data[1] = \
((kv)->value.leaf.data[1] & \
CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \
CSR1212_CPU_TO_BE32(((language) & \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)))
/* Icon Descriptor Leaf manipulation macros */
#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff
#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30
#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */
#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16
#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */
#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff
#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16
#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */
#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff
#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t))
#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \
CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK)
#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \
CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)
#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \
((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK)
#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \
CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK)
#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \
((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK)
#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \
(CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \
CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK)
#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \
(&((kv)->value.leaf.data[5]))
static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv)
{
static const int pd[4] = { 0, 4, 16, 256 };
static const int cs[16] = { 4, 2 };
int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)];
return &kv->value.leaf.data[5 +
(ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) /
sizeof(u_int32_t)];
}
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \
((kv)->value.leaf.data[2] = \
((kv)->value.leaf.data[2] & \
CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \
CSR1212_CPU_TO_BE32(((version) & \
CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK)))
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \
((kv)->value.leaf.data[3] = \
((kv)->value.leaf.data[3] & \
CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \
CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \
CSR1212_CPU_TO_BE32(((palette_depth) & \
CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \
CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \
((kv)->value.leaf.data[3] = \
((kv)->value.leaf.data[3] & \
CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \
CSR1212_CPU_TO_BE32(((color_space) & \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \
CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \
((kv)->value.leaf.data[3] = \
((kv)->value.leaf.data[3] & \
CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \
CSR1212_CPU_TO_BE32(((language) & \
CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK)))
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \
((kv)->value.leaf.data[4] = \
((kv)->value.leaf.data[4] & \
CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \
CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \
CSR1212_CPU_TO_BE32(((hscan) & \
CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \
CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))
#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \
((kv)->value.leaf.data[4] = \
(((kv)->value.leaf.data[4] & \
CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \
CSR1212_CPU_TO_BE32(((vscan) & \
CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK)))
/* Modifiable Descriptor Leaf manipulation macros */
#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16
#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff
#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32
#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff
#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL
#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \
CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT)
#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \
(CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \
CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \
CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]))
#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \
((kv)->value.leaf.data[0] = \
((kv)->value.leaf.data[0] & \
CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \
CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \
CSR1212_CPU_TO_BE32(((size) & \
CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \
CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))
#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \
((kv)->value.leaf.data[0] = \
((kv)->value.leaf.data[0] & \
CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \
CSR1212_CPU_TO_BE32(((addr) & \
CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK)))
#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \
((kv)->value.leaf.data[1] = \
CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK))
/* The following 2 function are for creating new Configuration ROM trees. The
* first function is used for both creating local trees and parsing remote
* trees. The second function adds pertinent information to local Configuration
* ROM trees - namely data for the bus information block. */
extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops,
size_t bus_info_size,
void *private);
extern void csr1212_init_local_csr(struct csr1212_csr *csr,
const u_int32_t *bus_info_data, int max_rom);
/* The following function destroys a Configuration ROM tree and release all
* memory taken by the tree. */
extern void csr1212_destroy_csr(struct csr1212_csr *csr);
/* The following set of functions are fore creating new keyvals for placement in
* a Configuration ROM tree. Code that creates new keyvals with these functions
* must release those keyvals with csr1212_release_keyval() when they are no
* longer needed. */
extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value);
extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data,
size_t data_len);
extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key,
u_int32_t csr_offset);
extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key);
extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec,
u_int32_t key,
u_int32_t value);
extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec,
u_int32_t key,
const void *data,
size_t data_len);
extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype,
u_int32_t specifier_id,
const void *data,
size_t data_len);
extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth,
u_int16_t cset,
u_int16_t language,
const void *data,
size_t data_len);
extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s);
extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version,
u_int8_t palette_depth,
u_int8_t color_space,
u_int16_t language,
u_int16_t hscan,
u_int16_t vscan,
u_int32_t *palette,
u_int32_t *pixels);
extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size,
u_int64_t address);
extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc,
const char *strv[]);
/* The following functions manage association between keyvals. Typically,
* Descriptor Leaves and Directories will be associated with another keyval and
* it is desirable for the Descriptor keyval to be place immediately after the
* keyval that it is associated with.*/
extern int csr1212_associate_keyval(struct csr1212_keyval *kv,
struct csr1212_keyval *associate);
extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv);
/* The following functions manage the association of a keyval and directories.
* A keyval may be attached to more than one directory. */
extern int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir,
struct csr1212_keyval *kv);
extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir,
struct csr1212_keyval *kv);
/* The following functions create a Configuration ROM image from the tree of
* keyvals provided. csr1212_generate_csr_image() creates a complete image in
* the list of caches available via csr->cache_head. The other functions are
* provided should there be a need to create a flat image without restrictions
* placed by IEEE 1212. */
extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
struct csr1212_keyval *start_kv,
int start_pos);
extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv);
extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache);
extern int csr1212_generate_csr_image(struct csr1212_csr *csr);
/* This is a convience function for reading a block of data out of one of the
* caches in the csr->cache_head list. */
extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer,
u_int32_t len);
/* The following functions are in place for parsing Configuration ROM images.
* csr1212_parse_keyval() is used should there be a need to directly parse a
* Configuration ROM directly. */
extern int csr1212_parse_keyval(struct csr1212_keyval *kv,
struct csr1212_csr_rom_cache *cache);
extern int csr1212_parse_csr(struct csr1212_csr *csr);
/* These are internal functions referenced by inline functions below. */
extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv);
extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv);
/* This function allocates a new cache which may be used for either parsing or
* generating sub-sets of Configuration ROM images. */
static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset,
size_t size)
{
struct csr1212_csr_rom_cache *cache;
cache = CSR1212_MALLOC(sizeof(struct csr1212_csr_rom_cache) + size);
if (!cache)
return NULL;
cache->next = NULL;
cache->prev = NULL;
cache->filled_head = NULL;
cache->filled_tail = NULL;
cache->layout_head = NULL;
cache->layout_tail = NULL;
cache->offset = offset;
cache->size = size;
cache->ext_rom = NULL;
return cache;
}
/* This function ensures that a keyval contains data when referencing a keyval
* created by parsing a Configuration ROM. */
static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr,
struct csr1212_keyval *kv)
{
if (!kv)
return NULL;
if (!kv->valid)
if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)
return NULL;
return kv;
}
/* This function increments the reference count for a keyval should there be a
* need for code to retain a keyval that has been parsed. */
static inline void csr1212_keep_keyval(struct csr1212_keyval *kv)
{
kv->refcnt++;
}
/* This function decrements a keyval's reference count and will destroy the
* keyval when there are no more users of the keyval. This should be called by
* any code that calls csr1212_keep_keyval() or any of the keyval creation
* routines csr1212_new_*(). */
static inline void csr1212_release_keyval(struct csr1212_keyval *kv)
{
if (kv->refcnt > 1)
kv->refcnt--;
else
_csr1212_destroy_keyval(kv);
}
/*
* This macro allows for looping over the keyval entries in a directory and it
* ensures that keyvals from remote ConfigROMs are parsed properly.
*
* _csr is a struct csr1212_csr * that points to CSR associated with dir.
* _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index).
* _dir is a struct csr1212_keyval * that points to the directory to be looped.
* _pos is a struct csr1212_dentry * that is used internally for indexing.
*
* kv will be NULL upon exit of the loop.
*/
#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \
for (csr1212_get_keyval((_csr), (_dir)), \
_pos = (_dir)->value.directory.dentries_head, \
_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL; \
(_kv) && (_pos); \
(_kv->associate == NULL) ? \
((_pos = _pos->next), \
(_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : \
NULL)) : \
(_kv = csr1212_get_keyval((_csr), _kv->associate)))
#endif /* __CSR1212_H__ */
......@@ -1801,15 +1801,12 @@ static int dv1394_open(struct inode *inode, struct file *file)
} else {
/* look up the card by ID */
struct list_head *lh;
unsigned long flags;
spin_lock_irqsave(&dv1394_cards_lock, flags);
if (!list_empty(&dv1394_cards)) {
struct video_card *p;
list_for_each(lh, &dv1394_cards) {
p = list_entry(lh, struct video_card, list);
list_for_each_entry(p, &dv1394_cards, list) {
if ((p->id) == ieee1394_file_to_instance(file)) {
video = p;
break;
......@@ -2374,7 +2371,6 @@ static void dv1394_host_reset(struct hpsb_host *host)
struct ti_ohci *ohci;
struct video_card *video = NULL;
unsigned long flags;
struct list_head *lh;
/* We only work with the OHCI-1394 driver */
if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
......@@ -2386,8 +2382,7 @@ static void dv1394_host_reset(struct hpsb_host *host)
/* find the corresponding video_cards */
spin_lock_irqsave(&dv1394_cards_lock, flags);
if (!list_empty(&dv1394_cards)) {
list_for_each(lh, &dv1394_cards) {
video = list_entry(lh, struct video_card, list);
list_for_each_entry(video, &dv1394_cards, list) {
if ((video->id >> 2) == ohci->id)
break;
}
......
......@@ -29,7 +29,6 @@
*
* TODO:
* RFC 2734 related:
* - Add Config ROM entry
* - Add MCAP. Limited Multicast exists only to 224.0.0.1 and 224.0.0.2.
*
* Non-RFC 2734 related:
......@@ -38,7 +37,6 @@
* - Convert kmalloc/kfree for link fragments to use kmem_cache_* instead
* - Stability improvements
* - Performance enhancements
* - Change hardcoded 1394 bus address region to a dynamic memory space allocation
* - Consider garbage collecting old partial datagrams after X amount of time
*/
......@@ -69,6 +67,7 @@
#include <asm/semaphore.h>
#include <net/arp.h>
#include "csr1212.h"
#include "ieee1394_types.h"
#include "ieee1394_core.h"
#include "ieee1394_transactions.h"
......@@ -89,7 +88,7 @@
#define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__)
static char version[] __devinitdata =
"$Rev: 1096 $ Ben Collins <bcollins@debian.org>";
"$Rev: 1133 $ Ben Collins <bcollins@debian.org>";
struct fragment_info {
struct list_head list;
......@@ -107,8 +106,35 @@ struct partial_datagram {
struct list_head frag_info;
};
static struct csr1212_keyval *eth1394_ud = NULL;
struct pdg_list {
struct list_head list; /* partial datagram list per node */
unsigned int sz; /* partial datagram list size per node */
spinlock_t lock; /* partial datagram lock */
};
struct eth1394_host_info {
struct hpsb_host *host;
struct net_device *dev;
};
struct eth1394_node_ref {
struct unit_directory *ud;
struct list_head list;
};
struct eth1394_node_info {
u16 maxpayload; /* Max payload */
u8 sspd; /* Max speed */
u64 fifo; /* FIFO address */
struct pdg_list pdg; /* partial RX datagram lists */
int dgl; /* Outgoing datagram label */
};
/* Our ieee1394 highlevel driver */
static const char driver_name[] = "eth1394";
#define ETH1394_DRIVER_NAME "IP/1394"
static const char driver_name[] = ETH1394_DRIVER_NAME;
static kmem_cache_t *packet_task_cache;
......@@ -188,95 +214,37 @@ static struct hpsb_highlevel eth1394_highlevel = {
};
static void eth1394_iso_shutdown(struct eth1394_priv *priv)
{
priv->bc_state = ETHER1394_BC_CLOSED;
if (priv->iso != NULL) {
if (!in_interrupt())
hpsb_iso_shutdown(priv->iso);
priv->iso = NULL;
}
}
static int ether1394_init_bc(struct net_device *dev)
/* This is called after an "ifup" */
static int ether1394_open (struct net_device *dev)
{
struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv;
int ret = 0;
/* First time sending? Need a broadcast channel for ARP and for
* listening on */
if (priv->bc_state == ETHER1394_BC_CHECK) {
quadlet_t bc;
/* Get the local copy of the broadcast channel and check its
* validity (the IRM should validate it for us) */
bc = priv->host->csr.broadcast_channel;
if ((bc & 0xc0000000) != 0xc0000000) {
/* broadcast channel not validated yet */
ETH1394_PRINT(KERN_WARNING, dev->name,
"Error BROADCAST_CHANNEL register valid "
"bit not set, can't send IP traffic\n");
eth1394_iso_shutdown(priv);
return -EAGAIN;
}
if (priv->broadcast_channel != (bc & 0x3f)) {
/* This really shouldn't be possible, but just in case
* the IEEE 1394 spec changes regarding broadcast
* channels in the future. */
eth1394_iso_shutdown(priv);
if (in_interrupt())
return -EAGAIN;
priv->broadcast_channel = bc & 0x3f;
ETH1394_PRINT(KERN_INFO, dev->name,
"Changing to broadcast channel %d...\n",
priv->broadcast_channel);
priv->iso = hpsb_iso_recv_init(priv->host, 16 * 4096,
16, priv->broadcast_channel,
HPSB_ISO_DMA_PACKET_PER_BUFFER, 1, ether1394_iso);
/* Something bad happened, don't even try */
if (priv->bc_state == ETHER1394_BC_ERROR) {
/* we'll try again */
priv->iso = hpsb_iso_recv_init(priv->host,
ETHER1394_GASP_BUFFERS * 2 *
(1 << (priv->host->csr.max_rec +
1)),
ETHER1394_GASP_BUFFERS,
priv->broadcast_channel,
HPSB_ISO_DMA_PACKET_PER_BUFFER,
1, ether1394_iso);
if (priv->iso == NULL) {
ETH1394_PRINT(KERN_ERR, dev->name,
"failed to change broadcast "
"channel\n");
return -EAGAIN;
}
}
if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) {
ETH1394_PRINT(KERN_ERR, dev->name,
"Could not start data stream reception\n");
eth1394_iso_shutdown(priv);
return -EAGAIN;
"Could not allocate isochronous receive "
"context for the broadcast channel\n");
priv->bc_state = ETHER1394_BC_ERROR;
ret = -EAGAIN;
} else {
if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
priv->bc_state = ETHER1394_BC_STOPPED;
else
priv->bc_state = ETHER1394_BC_RUNNING;
}
priv->bc_state = ETHER1394_BC_OPENED;
}
return 0;
}
/* This is called after an "ifup" */
static int ether1394_open (struct net_device *dev)
{
struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv;
unsigned long flags;
int ret;
/* Something bad happened, don't even try */
if (priv->bc_state == ETHER1394_BC_CLOSED)
return -EAGAIN;
spin_lock_irqsave(&priv->lock, flags);
ret = ether1394_init_bc(dev);
spin_unlock_irqrestore(&priv->lock, flags);
if (ret)
return ret;
......@@ -312,66 +280,227 @@ static void ether1394_tx_timeout (struct net_device *dev)
static int ether1394_change_mtu(struct net_device *dev, int new_mtu)
{
struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv;
int phy_id = NODEID_TO_NODE(priv->host->node_id);
if ((new_mtu < 68) || (new_mtu > min(ETH1394_DATA_LEN, (int)(priv->maxpayload[phy_id] -
(sizeof(union eth1394_hdr) + ETHER1394_GASP_OVERHEAD)))))
if ((new_mtu < 68) ||
(new_mtu > min(ETH1394_DATA_LEN,
(int)((1 << (priv->host->csr.max_rec + 1)) -
(sizeof(union eth1394_hdr) +
ETHER1394_GASP_OVERHEAD)))))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static inline void ether1394_register_limits(int nodeid, u16 maxpayload,
unsigned char sspd, u64 eui, u64 fifo,
struct eth1394_priv *priv)
/******************************************
* 1394 bus activity functions
******************************************/
static struct eth1394_node_ref *eth1394_find_node(struct list_head *inl,
struct unit_directory *ud)
{
if (nodeid < 0 || nodeid >= ALL_NODES) {
ETH1394_PRINT_G (KERN_ERR, "Cannot register invalid nodeid %d\n", nodeid);
return;
struct eth1394_node_ref *node;
list_for_each_entry(node, inl, list)
if (node->ud == ud)
return node;
return NULL;
}
static struct eth1394_node_ref *eth1394_find_node_guid(struct list_head *inl,
u64 guid)
{
struct eth1394_node_ref *node;
list_for_each_entry(node, inl, list)
if (node->ud->ne->guid == guid)
return node;
return NULL;
}
static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl,
nodeid_t nodeid)
{
struct eth1394_node_ref *node;
list_for_each_entry(node, inl, list) {
if (node->ud->ne->nodeid == nodeid)
return node;
}
priv->maxpayload[nodeid] = maxpayload;
priv->sspd[nodeid] = sspd;
priv->fifo[nodeid] = fifo;
priv->eui[nodeid] = eui;
return NULL;
}
static int eth1394_probe(struct device *dev)
{
struct unit_directory *ud;
struct eth1394_host_info *hi;
struct eth1394_priv *priv;
struct eth1394_node_ref *new_node;
struct eth1394_node_info *node_info;
ud = container_of(dev, struct unit_directory, device);
hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
if (!hi)
return -ENOENT;
priv->maxpayload[ALL_NODES] = min(priv->maxpayload[ALL_NODES], maxpayload);
priv->sspd[ALL_NODES] = min(priv->sspd[ALL_NODES], sspd);
new_node = kmalloc(sizeof(struct eth1394_node_ref),
in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
if (!new_node)
return -ENOMEM;
node_info = kmalloc(sizeof(struct eth1394_node_info),
in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
if (!node_info) {
kfree(new_node);
return -ENOMEM;
}
spin_lock_init(&node_info->pdg.lock);
INIT_LIST_HEAD(&node_info->pdg.list);
node_info->pdg.sz = 0;
node_info->fifo = ETHER1394_INVALID_ADDR;
ud->device.driver_data = node_info;
new_node->ud = ud;
priv = (struct eth1394_priv *)hi->dev->priv;
list_add_tail(&new_node->list, &priv->ip_node_list);
return 0;
}
static int eth1394_remove(struct device *dev)
{
struct unit_directory *ud;
struct eth1394_host_info *hi;
struct eth1394_priv *priv;
struct eth1394_node_ref *old_node;
struct eth1394_node_info *node_info;
struct list_head *lh, *n;
unsigned long flags;
ud = container_of(dev, struct unit_directory, device);
hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
if (!hi)
return -ENOENT;
priv = (struct eth1394_priv *)hi->dev->priv;
old_node = eth1394_find_node(&priv->ip_node_list, ud);
if (old_node) {
list_del(&old_node->list);
kfree(old_node);
node_info = (struct eth1394_node_info*)ud->device.driver_data;
spin_lock_irqsave(&node_info->pdg.lock, flags);
/* The partial datagram list should be empty, but we'll just
* make sure anyway... */
list_for_each_safe(lh, n, &node_info->pdg.list) {
purge_partial_datagram(lh);
}
spin_unlock_irqrestore(&node_info->pdg.lock, flags);
kfree(node_info);
ud->device.driver_data = NULL;
}
return 0;
}
static void eth1394_update(struct unit_directory *ud)
{
struct eth1394_host_info *hi;
struct eth1394_priv *priv;
struct eth1394_node_ref *node;
struct eth1394_node_info *node_info;
hi = hpsb_get_hostinfo(&eth1394_highlevel, ud->ne->host);
if (!hi)
return;
priv = (struct eth1394_priv *)hi->dev->priv;
node = eth1394_find_node(&priv->ip_node_list, ud);
if (!node) {
node = kmalloc(sizeof(struct eth1394_node_ref),
in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
if (!node)
return;
node_info = kmalloc(sizeof(struct eth1394_node_info),
in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
spin_lock_init(&node_info->pdg.lock);
INIT_LIST_HEAD(&node_info->pdg.list);
node_info->pdg.sz = 0;
ud->device.driver_data = node_info;
node->ud = ud;
priv = (struct eth1394_priv *)hi->dev->priv;
list_add_tail(&node->list, &priv->ip_node_list);
}
}
static struct ieee1394_device_id eth1394_id_table[] = {
{
.match_flags = (IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION),
.specifier_id = ETHER1394_GASP_SPECIFIER_ID,
.version = ETHER1394_GASP_VERSION,
},
{}
};
static struct hpsb_protocol_driver eth1394_proto_driver = {
.name = "IPv4 over 1394 Driver",
.id_table = eth1394_id_table,
.update = eth1394_update,
.driver = {
.name = ETH1394_DRIVER_NAME,
.bus = &ieee1394_bus_type,
.probe = eth1394_probe,
.remove = eth1394_remove,
},
};
static void ether1394_reset_priv (struct net_device *dev, int set_mtu)
{
unsigned long flags;
int i;
struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv;
struct hpsb_host *host = priv->host;
int phy_id = NODEID_TO_NODE(host->node_id);
u64 guid = *((u64*)&(host->csr.rom[3]));
u16 maxpayload = 1 << (((be32_to_cpu(host->csr.rom[2]) >> 12) & 0xf) + 1);
u64 guid = *((u64*)&(host->csr.rom->bus_info_data[3]));
u16 maxpayload = 1 << (host->csr.max_rec + 1);
int max_speed = IEEE1394_SPEED_MAX;
spin_lock_irqsave (&priv->lock, flags);
/* Clear the speed/payload/offset tables */
memset (priv->maxpayload, 0, sizeof (priv->maxpayload));
memset (priv->sspd, 0, sizeof (priv->sspd));
memset (priv->fifo, 0, sizeof (priv->fifo));
priv->sspd[ALL_NODES] = ETH1394_SPEED_DEF;
priv->maxpayload[ALL_NODES] = eth1394_speedto_maxpayload[priv->sspd[ALL_NODES]];
priv->bc_state = ETHER1394_BC_CHECK;
memset(priv->ud_list, 0, sizeof(struct node_entry*) * ALL_NODES);
priv->bc_maxpayload = 512;
/* Register our limits now */
ether1394_register_limits(phy_id, maxpayload,
host->speed_map[(phy_id << 6) + phy_id],
guid, ETHER1394_REGION_ADDR, priv);
/* Determine speed limit */
for (i = 0; i < host->node_count; i++)
if (max_speed > host->speed_map[NODEID_TO_NODE(host->node_id) *
64 + i])
max_speed = host->speed_map[NODEID_TO_NODE(host->node_id) *
64 + i];
priv->bc_sspd = max_speed;
/* We'll use our maxpayload as the default mtu */
if (set_mtu) {
dev->mtu = min(ETH1394_DATA_LEN, (int)(priv->maxpayload[phy_id] -
(sizeof(union eth1394_hdr) + ETHER1394_GASP_OVERHEAD)));
dev->mtu = min(ETH1394_DATA_LEN,
(int)(maxpayload -
(sizeof(union eth1394_hdr) +
ETHER1394_GASP_OVERHEAD)));
/* Set our hardware address while we're at it */
*(u64*)dev->dev_addr = guid;
......@@ -379,20 +508,6 @@ static void ether1394_reset_priv (struct net_device *dev, int set_mtu)
}
spin_unlock_irqrestore (&priv->lock, flags);
for (i = 0; i < ALL_NODES; i++) {
struct list_head *lh, *n;
spin_lock_irqsave(&priv->pdg[i].lock, flags);
if (!set_mtu) {
list_for_each_safe(lh, n, &priv->pdg[i].list) {
purge_partial_datagram(lh);
}
}
INIT_LIST_HEAD(&(priv->pdg[i].list));
priv->pdg[i].sz = 0;
spin_unlock_irqrestore(&priv->pdg[i].lock, flags);
}
}
/* This function is called right before register_netdev */
......@@ -432,15 +547,21 @@ static void ether1394_init_dev (struct net_device *dev)
*/
static void ether1394_add_host (struct hpsb_host *host)
{
int i;
struct host_info *hi = NULL;
struct eth1394_host_info *hi = NULL;
struct net_device *dev = NULL;
struct eth1394_priv *priv;
static int version_printed = 0;
hpsb_register_addrspace(&eth1394_highlevel, host, &addr_ops,
ETHER1394_REGION_ADDR,
ETHER1394_REGION_ADDR_END);
u64 fifo_addr;
fifo_addr = hpsb_allocate_and_register_addrspace(&eth1394_highlevel,
host,
&addr_ops,
ETHER1394_REGION_ADDR_LEN,
ETHER1394_REGION_ADDR_LEN,
-1, -1);
if (fifo_addr == ~0ULL)
goto out;
if (version_printed++ == 0)
ETH1394_PRINT_G (KERN_INFO, "%s\n", version);
......@@ -461,14 +582,11 @@ static void ether1394_add_host (struct hpsb_host *host)
priv = (struct eth1394_priv *)dev->priv;
INIT_LIST_HEAD(&priv->ip_node_list);
spin_lock_init(&priv->lock);
priv->host = host;
for (i = 0; i < ALL_NODES; i++) {
spin_lock_init(&priv->pdg[i].lock);
INIT_LIST_HEAD(&priv->pdg[i].list);
priv->pdg[i].sz = 0;
}
priv->local_fifo = fifo_addr;
hi = hpsb_create_hostinfo(&eth1394_highlevel, host, sizeof(*hi));
......@@ -496,10 +614,30 @@ static void ether1394_add_host (struct hpsb_host *host)
* be checked when the eth device is opened. */
priv->broadcast_channel = host->csr.broadcast_channel & 0x3f;
priv->iso = hpsb_iso_recv_init(host, 16 * 4096, 16, priv->broadcast_channel,
HPSB_ISO_DMA_PACKET_PER_BUFFER, 1, ether1394_iso);
priv->iso = hpsb_iso_recv_init(host, (ETHER1394_GASP_BUFFERS * 2 *
(1 << (host->csr.max_rec + 1))),
ETHER1394_GASP_BUFFERS,
priv->broadcast_channel,
HPSB_ISO_DMA_PACKET_PER_BUFFER,
1, ether1394_iso);
if (priv->iso == NULL) {
priv->bc_state = ETHER1394_BC_CLOSED;
ETH1394_PRINT(KERN_ERR, dev->name,
"Could not allocate isochronous receive context "
"for the broadcast channel\n");
priv->bc_state = ETHER1394_BC_ERROR;
} else {
if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0)
priv->bc_state = ETHER1394_BC_STOPPED;
else
priv->bc_state = ETHER1394_BC_RUNNING;
}
if (csr1212_attach_keyval_to_directory(host->csr.rom->root_kv,
eth1394_ud) != CSR1212_SUCCESS) {
ETH1394_PRINT (KERN_ERR, dev->name,
"Cannot attach IP 1394 Unit Directory to "
"Config ROM\n");
goto out;
}
return;
......@@ -515,12 +653,21 @@ static void ether1394_add_host (struct hpsb_host *host)
/* Remove a card from our list */
static void ether1394_remove_host (struct hpsb_host *host)
{
struct host_info *hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
struct eth1394_host_info *hi;
hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
if (hi != NULL) {
struct eth1394_priv *priv = (struct eth1394_priv *)hi->dev->priv;
eth1394_iso_shutdown(priv);
hpsb_unregister_addrspace(&eth1394_highlevel, host,
priv->local_fifo);
if (priv->iso != NULL)
hpsb_iso_shutdown(priv->iso);
csr1212_detach_keyval_from_directory(hi->host->csr.rom->root_kv,
eth1394_ud);
hi->host->update_config_rom = 1;
if (hi->dev) {
unregister_netdev (hi->dev);
......@@ -534,18 +681,42 @@ static void ether1394_remove_host (struct hpsb_host *host)
/* A reset has just arisen */
static void ether1394_host_reset (struct hpsb_host *host)
{
struct host_info *hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
struct eth1394_host_info *hi;
struct eth1394_priv *priv;
struct net_device *dev;
struct list_head *lh, *n;
struct eth1394_node_ref *node;
struct eth1394_node_info *node_info;
unsigned long flags;
hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
/* This can happen for hosts that we don't use */
if (hi == NULL)
return;
dev = hi->dev;
priv = (struct eth1394_priv *)dev->priv;
/* Reset our private host data, but not our mtu */
netif_stop_queue (dev);
ether1394_reset_priv (dev, 0);
list_for_each_entry(node, &priv->ip_node_list, list) {
node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
spin_lock_irqsave(&node_info->pdg.lock, flags);
list_for_each_safe(lh, n, &node_info->pdg.list) {
purge_partial_datagram(lh);
}
INIT_LIST_HEAD(&(node_info->pdg.list));
node_info->pdg.sz = 0;
spin_unlock_irqrestore(&node_info->pdg.lock, flags);
}
netif_wake_queue (dev);
}
......@@ -622,7 +793,8 @@ static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr)
static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh)
{
unsigned short type = hh->hh_type;
struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) + 6);
struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) +
(16 - ETH1394_HLEN));
struct net_device *dev = neigh->dev;
if (type == __constant_htons(ETH_P_802_3)) {
......@@ -641,7 +813,7 @@ static void ether1394_header_cache_update(struct hh_cache *hh,
struct net_device *dev,
unsigned char * haddr)
{
memcpy(((u8*)hh->hh_data) + 6, haddr, dev->addr_len);
memcpy(((u8*)hh->hh_data) + (16 - ETH1394_HLEN), haddr, dev->addr_len);
}
static int ether1394_mac_addr(struct net_device *dev, void *p)
......@@ -650,7 +822,7 @@ static int ether1394_mac_addr(struct net_device *dev, void *p)
return -EBUSY;
/* Not going to allow setting the MAC address, we really need to use
* the real one suppliled by the hardware */
* the real one supplied by the hardware */
return -EINVAL;
}
......@@ -710,31 +882,37 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb,
if (destid == (LOCAL_BUS | ALL_NODES))
dest_hw = ~0ULL; /* broadcast */
else
dest_hw = priv->eui[NODEID_TO_NODE(destid)];
dest_hw = cpu_to_be64((((u64)priv->host->csr.guid_hi) << 32) |
priv->host->csr.guid_lo);
/* If this is an ARP packet, convert it. First, we want to make
* use of some of the fields, since they tell us a little bit
* about the sending machine. */
if (ether_type == __constant_htons (ETH_P_ARP)) {
unsigned long flags;
struct eth1394_arp *arp1394 = (struct eth1394_arp*)skb->data;
struct arphdr *arp = (struct arphdr *)skb->data;
unsigned char *arp_ptr = (unsigned char *)(arp + 1);
u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 |
ntohl(arp1394->fifo_lo);
u8 host_max_rec = (be32_to_cpu(priv->host->csr.rom[2]) >>
12) & 0xf;
u8 max_rec = min(host_max_rec, (u8)(arp1394->max_rec));
u8 max_rec = min(priv->host->csr.max_rec,
(u8)(arp1394->max_rec));
u16 maxpayload = min(eth1394_speedto_maxpayload[arp1394->sspd],
(u16)(1 << (max_rec + 1)));
struct eth1394_node_ref *node;
struct eth1394_node_info *node_info;
node = eth1394_find_node_guid(&priv->ip_node_list,
be64_to_cpu(arp1394->s_uniq_id));
if (!node) {
return 0;
}
node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
/* Update our speed/payload/fifo_offset table */
spin_lock_irqsave (&priv->lock, flags);
ether1394_register_limits(NODEID_TO_NODE(srcid), maxpayload,
arp1394->sspd, arp1394->s_uniq_id,
fifo_addr, priv);
spin_unlock_irqrestore (&priv->lock, flags);
node_info->maxpayload = maxpayload;
node_info->sspd = arp1394->sspd;
node_info->fifo = fifo_addr;
/* Now that we're done with the 1394 specific stuff, we'll
* need to alter some of the data. Believe it or not, all
......@@ -743,9 +921,8 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb,
* in and the hardware address length set to 8.
*
* IMPORTANT: The code below overwrites 1394 specific data
* needed above data so keep the call to
* ether1394_register_limits() before munging the data for the
* higher level IP stack. */
* needed above so keep the munging of the data for the
* higher level IP stack last. */
arp->ar_hln = 8;
arp_ptr += arp->ar_hln; /* skip over sender unique id */
......@@ -754,9 +931,9 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb,
if (arp->ar_op == 1)
/* just set ARP req target unique ID to 0 */
memset(arp_ptr, 0, ETH1394_ALEN);
*((u64*)arp_ptr) = 0;
else
memcpy(arp_ptr, dev->dev_addr, ETH1394_ALEN);
*((u64*)arp_ptr) = *((u64*)dev->dev_addr);
}
/* Now add the ethernet header. */
......@@ -769,12 +946,9 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb,
static inline int fragment_overlap(struct list_head *frag_list, int offset, int len)
{
struct list_head *lh;
struct fragment_info *fi;
list_for_each(lh, frag_list) {
fi = list_entry(lh, struct fragment_info, list);
list_for_each_entry(fi, frag_list, list) {
if ( ! ((offset > (fi->offset + fi->len - 1)) ||
((offset + len - 1) < fi->offset)))
return 1;
......@@ -784,13 +958,11 @@ static inline int fragment_overlap(struct list_head *frag_list, int offset, int
static inline struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl)
{
struct list_head *lh;
struct partial_datagram *pd;
list_for_each(lh, pdgl) {
pd = list_entry(lh, struct partial_datagram, list);
list_for_each_entry(pd, pdgl, list) {
if (pd->dgl == dgl)
return lh;
return &pd->list;
}
return NULL;
}
......@@ -939,12 +1111,29 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid,
{
struct sk_buff *skb;
unsigned long flags;
struct eth1394_priv *priv;
struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv;
union eth1394_hdr *hdr = (union eth1394_hdr *)buf;
u16 ether_type = 0; /* initialized to clear warning */
int hdr_len;
struct unit_directory *ud = priv->ud_list[NODEID_TO_NODE(srcid)];
struct eth1394_node_info *node_info;
if (!ud) {
struct eth1394_node_ref *node;
node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid);
if (!node) {
HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid "
"lookup failure: " NODE_BUS_FMT,
NODE_BUS_ARGS(priv->host, srcid));
priv->stats.rx_dropped++;
return -1;
}
ud = node->ud;
priv = (struct eth1394_priv *)dev->priv;
priv->ud_list[NODEID_TO_NODE(srcid)] = ud;
}
node_info = (struct eth1394_node_info*)ud->device.driver_data;
/* First, did we receive a fragmented or unfragmented datagram? */
hdr->words.word1 = ntohs(hdr->words.word1);
......@@ -975,8 +1164,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid,
int dg_size;
int dgl;
int retval;
int sid = NODEID_TO_NODE(srcid);
struct pdg_list *pdg = &(priv->pdg[sid]);
struct pdg_list *pdg = &(node_info->pdg);
hdr->words.word3 = ntohs(hdr->words.word3);
/* The 4th header word is reserved so no need to do ntohs() */
......@@ -1110,8 +1298,9 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid,
static int ether1394_write(struct hpsb_host *host, int srcid, int destid,
quadlet_t *data, u64 addr, size_t len, u16 flags)
{
struct host_info *hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
struct eth1394_host_info *hi;
hi = hpsb_get_hostinfo(&eth1394_highlevel, host);
if (hi == NULL) {
ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n",
host->driver->name);
......@@ -1128,7 +1317,7 @@ static void ether1394_iso(struct hpsb_iso *iso)
{
quadlet_t *data;
char *buf;
struct host_info *hi = hpsb_get_hostinfo(&eth1394_highlevel, iso->host);
struct eth1394_host_info *hi;
struct net_device *dev;
struct eth1394_priv *priv;
unsigned int len;
......@@ -1137,6 +1326,7 @@ static void ether1394_iso(struct hpsb_iso *iso)
int i;
int nready;
hi = hpsb_get_hostinfo(&eth1394_highlevel, iso->host);
if (hi == NULL) {
ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n",
iso->host->driver->name);
......@@ -1193,7 +1383,6 @@ static inline void ether1394_arp_to_1394arp(struct sk_buff *skb,
struct net_device *dev)
{
struct eth1394_priv *priv = (struct eth1394_priv *)(dev->priv);
u16 phy_id = NODEID_TO_NODE(priv->host->node_id);
struct arphdr *arp = (struct arphdr *)skb->data;
unsigned char *arp_ptr = (unsigned char *)(arp + 1);
......@@ -1203,10 +1392,10 @@ static inline void ether1394_arp_to_1394arp(struct sk_buff *skb,
* and set hw_addr_len, max_rec, sspd, fifo_hi and fifo_lo. */
arp1394->hw_addr_len = 16;
arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN);
arp1394->max_rec = (be32_to_cpu(priv->host->csr.rom[2]) >> 12) & 0xf;
arp1394->sspd = priv->sspd[phy_id];
arp1394->fifo_hi = htons (priv->fifo[phy_id] >> 32);
arp1394->fifo_lo = htonl (priv->fifo[phy_id] & ~0x0);
arp1394->max_rec = priv->host->csr.max_rec;
arp1394->sspd = priv->host->csr.lnk_spd;
arp1394->fifo_hi = htons (priv->local_fifo >> 32);
arp1394->fifo_lo = htonl (priv->local_fifo & ~0x0);
return;
}
......@@ -1334,14 +1523,14 @@ static inline void ether1394_prep_gasp_packet(struct hpsb_packet *p,
p->data = ((quadlet_t*)skb->data) - 2;
p->data[0] = cpu_to_be32((priv->host->node_id << 16) |
ETHER1394_GASP_SPECIFIER_ID_HI);
p->data[1] = cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) |
p->data[1] = __constant_cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) |
ETHER1394_GASP_VERSION);
/* Setting the node id to ALL_NODES (not LOCAL_BUS | ALL_NODES)
* prevents hpsb_send_packet() from setting the speed to an arbitrary
* value based on packet->node_id if packet->node_id is not set. */
p->node_id = ALL_NODES;
p->speed_code = priv->sspd[ALL_NODES];
p->speed_code = priv->bc_sspd;
}
static inline void ether1394_free_packet(struct hpsb_packet *packet)
......@@ -1458,7 +1647,8 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
u16 dg_size;
u16 dgl;
struct packet_task *ptask;
struct node_entry *ne;
struct eth1394_node_ref *node;
struct eth1394_node_info *node_info = NULL;
ptask = kmem_cache_alloc(packet_task_cache, kmflags);
if (ptask == NULL) {
......@@ -1466,22 +1656,11 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
goto fail;
}
spin_lock_irqsave (&priv->lock, flags);
if (priv->bc_state == ETHER1394_BC_CLOSED) {
ETH1394_PRINT(KERN_ERR, dev->name,
"Cannot send packet, no broadcast channel available.\n");
if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) {
ret = -EAGAIN;
spin_unlock_irqrestore (&priv->lock, flags);
goto fail;
}
if ((ret = ether1394_init_bc(dev))) {
spin_unlock_irqrestore (&priv->lock, flags);
goto fail;
}
spin_unlock_irqrestore (&priv->lock, flags);
if ((skb = skb_share_check (skb, kmflags)) == NULL) {
ret = -ENOMEM;
goto fail;
......@@ -1491,28 +1670,8 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
eth = (struct eth1394hdr*)skb->data;
skb_pull(skb, ETH1394_HLEN);
ne = hpsb_guid_get_entry(be64_to_cpu(*(u64*)eth->h_dest));
if (!ne)
dest_node = LOCAL_BUS | ALL_NODES;
else
dest_node = ne->nodeid;
proto = eth->h_proto;
/* If this is an ARP packet, convert it */
if (proto == __constant_htons (ETH_P_ARP))
ether1394_arp_to_1394arp (skb, dev);
max_payload = priv->maxpayload[NODEID_TO_NODE(dest_node)];
/* This check should be unnecessary, but we'll keep it for safety for
* a while longer. */
if (max_payload < 512) {
ETH1394_PRINT(KERN_WARNING, dev->name,
"max_payload too small: %d (setting to 512)\n",
max_payload);
max_payload = 512;
}
dg_size = skb->len;
/* Set the transmission type for the packet. ARP packets and IP
* broadcast packets are sent via GASP. */
......@@ -1521,18 +1680,38 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
(proto == __constant_htons(ETH_P_IP) &&
IN_MULTICAST(__constant_ntohl(skb->nh.iph->daddr)))) {
tx_type = ETH1394_GASP;
max_payload -= ETHER1394_GASP_OVERHEAD;
dest_node = LOCAL_BUS | ALL_NODES;
max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD;
BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD));
dgl = priv->bc_dgl;
if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
priv->bc_dgl++;
} else {
tx_type = ETH1394_WRREQ;
node = eth1394_find_node_guid(&priv->ip_node_list,
be64_to_cpu(*(u64*)eth->h_dest));
if (!node) {
ret = -EAGAIN;
goto fail;
}
node_info = (struct eth1394_node_info*)node->ud->device.driver_data;
if (node_info->fifo == ETHER1394_INVALID_ADDR) {
ret = -EAGAIN;
goto fail;
}
dg_size = skb->len;
dest_node = node->ud->ne->nodeid;
max_payload = node_info->maxpayload;
BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD));
spin_lock_irqsave (&priv->lock, flags);
dgl = priv->dgl[NODEID_TO_NODE(dest_node)];
dgl = node_info->dgl;
if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF])
priv->dgl[NODEID_TO_NODE(dest_node)]++;
spin_unlock_irqrestore (&priv->lock, flags);
node_info->dgl++;
tx_type = ETH1394_WRREQ;
}
/* If this is an ARP packet, convert it */
if (proto == __constant_htons (ETH_P_ARP))
ether1394_arp_to_1394arp (skb, dev);
ptask->hdr.words.word1 = 0;
ptask->hdr.words.word2 = 0;
......@@ -1545,17 +1724,8 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev)
if (tx_type != ETH1394_GASP) {
u64 addr;
/* This test is just temporary until ConfigROM support has
* been added to eth1394. Until then, we need an ARP packet
* after a bus reset from the current destination node so that
* we can get FIFO information. */
if (priv->fifo[NODEID_TO_NODE(dest_node)] == 0ULL) {
ret = -EAGAIN;
goto fail;
}
spin_lock_irqsave(&priv->lock, flags);
addr = priv->fifo[NODEID_TO_NODE(dest_node)];
addr = node_info->fifo;
spin_unlock_irqrestore(&priv->lock, flags);
ptask->addr = addr;
......@@ -1621,7 +1791,7 @@ static int ether1394_ethtool_ioctl(struct net_device *dev, void *useraddr)
case ETHTOOL_GDRVINFO: {
struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
strcpy (info.driver, driver_name);
strcpy (info.version, "$Rev: 1096 $");
strcpy (info.version, "$Rev: 1133 $");
/* FIXME XXX provide sane businfo */
strcpy (info.bus_info, "ieee1394");
if (copy_to_user (useraddr, &info, sizeof (info)))
......@@ -1644,19 +1814,78 @@ static int ether1394_ethtool_ioctl(struct net_device *dev, void *useraddr)
static int __init ether1394_init_module (void)
{
int ret;
struct csr1212_keyval *spec_id = NULL;
struct csr1212_keyval *spec_desc = NULL;
struct csr1212_keyval *ver = NULL;
struct csr1212_keyval *ver_desc = NULL;
packet_task_cache = kmem_cache_create("packet_task", sizeof(struct packet_task),
0, 0, NULL, NULL);
eth1394_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 ((!eth1394_ud) ||
(!spec_id) ||
(!spec_desc) ||
(!ver) ||
(!ver_desc)) {
ret = -ENOMEM;
goto out;
}
ret = csr1212_associate_keyval(spec_id, spec_desc);
if (ret != CSR1212_SUCCESS)
goto out;
ret = csr1212_associate_keyval(ver, ver_desc);
if (ret != CSR1212_SUCCESS)
goto out;
ret = csr1212_attach_keyval_to_directory(eth1394_ud, spec_id);
if (ret != CSR1212_SUCCESS)
goto out;
ret = csr1212_attach_keyval_to_directory(eth1394_ud, ver);
if (ret != CSR1212_SUCCESS)
goto out;
/* Register ourselves as a highlevel driver */
hpsb_register_highlevel(&eth1394_highlevel);
return 0;
ret = hpsb_register_protocol(&eth1394_proto_driver);
out:
if ((ret != 0) && eth1394_ud) {
csr1212_release_keyval(eth1394_ud);
}
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 __exit ether1394_exit_module (void)
{
hpsb_unregister_protocol(&eth1394_proto_driver);
hpsb_unregister_highlevel(&eth1394_highlevel);
kmem_cache_destroy(packet_task_cache);
if (eth1394_ud) {
csr1212_release_keyval(eth1394_ud);
}
}
module_init(ether1394_init_module);
......
......@@ -29,8 +29,8 @@
/* 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
#define ETHER1394_REGION_ADDR 0xfffff0200000ULL
#define ETHER1394_REGION_ADDR_END (ETHER1394_REGION_ADDR + ETHER1394_REGION_ADDR_LEN)
#define ETHER1394_INVALID_ADDR ~0ULL
/* GASP identifier numbers for IPv4 over IEEE 1394 */
#define ETHER1394_GASP_SPECIFIER_ID 0x00005E
......@@ -40,37 +40,30 @@
#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* GASP header overhead */
#define ETHER1394_GASP_BUFFERS 16
/* Node set == 64 */
#define NODE_SET (ALL_NODES + 1)
enum eth1394_bc_states { ETHER1394_BC_CLOSED, ETHER1394_BC_OPENED,
ETHER1394_BC_CHECK };
enum eth1394_bc_states { ETHER1394_BC_ERROR,
ETHER1394_BC_RUNNING,
ETHER1394_BC_STOPPED };
struct pdg_list {
struct list_head list; /* partial datagram list per node */
unsigned int sz; /* partial datagram list size per node */
spinlock_t lock; /* partial datagram lock */
};
/* Private structure for our ethernet driver */
struct eth1394_priv {
struct net_device_stats stats; /* Device stats */
struct hpsb_host *host; /* The card for this dev */
u16 maxpayload[NODE_SET]; /* Max payload per node */
unsigned char sspd[NODE_SET]; /* Max speed per node */
u64 fifo[ALL_NODES]; /* FIFO offset per node */
u64 eui[ALL_NODES]; /* EUI-64 per node */
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 */
struct pdg_list pdg[ALL_NODES]; /* partial RX datagram lists */
int dgl[NODE_SET]; /* Outgoing datagram label per node */
};
struct host_info {
struct hpsb_host *host;
struct net_device *dev;
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 */
};
......
......@@ -20,6 +20,7 @@
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/bitops.h>
#include "ieee1394.h"
#include "ieee1394_types.h"
......@@ -44,7 +45,6 @@ static DECLARE_RWSEM(hl_drivers_sem);
static LIST_HEAD(hl_irqs);
static rwlock_t hl_irqs_lock = RW_LOCK_UNLOCKED;
static LIST_HEAD(addr_space);
static rwlock_t addr_space_lock = RW_LOCK_UNLOCKED;
/* addr_space list will have zero and max already included as bounds */
......@@ -56,21 +56,20 @@ static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl,
struct hpsb_host *host)
{
struct hl_host_info *hi = NULL;
struct list_head *lh;
if (!hl || !host)
return NULL;
read_lock(&hl->host_info_lock);
list_for_each (lh, &hl->host_info_list) {
hi = list_entry(lh, struct hl_host_info, list);
if (hi->host == host)
break;
hi = NULL;
list_for_each_entry(hi, &hl->host_info_list, list) {
if (hi->host == host) {
read_unlock(&hl->host_info_lock);
return hi;
}
}
read_unlock(&hl->host_info_lock);
return hi;
return NULL;
}
......@@ -188,7 +187,6 @@ unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host
void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
{
struct list_head *lh;
struct hl_host_info *hi;
void *data = NULL;
......@@ -196,8 +194,7 @@ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
return NULL;
read_lock(&hl->host_info_lock);
list_for_each (lh, &hl->host_info_list) {
hi = list_entry(lh, struct hl_host_info, list);
list_for_each_entry(hi, &hl->host_info_list, list) {
if (hi->key == key) {
data = hi->data;
break;
......@@ -211,7 +208,6 @@ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key)
struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key)
{
struct list_head *lh;
struct hl_host_info *hi;
struct hpsb_host *host = NULL;
......@@ -219,8 +215,7 @@ struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long k
return NULL;
read_lock(&hl->host_info_lock);
list_for_each (lh, &hl->host_info_list) {
hi = list_entry(lh, struct hl_host_info, list);
list_for_each_entry(hi, &hl->host_info_list, list) {
if (hi->key == key) {
host = hi->host;
break;
......@@ -237,6 +232,13 @@ static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data)
hl->add_host(host);
if (host->update_config_rom) {
if (hpsb_update_config_rom_image(host) < 0) {
HPSB_ERR("Failed to generate Configuration ROM image for host "
"%s-%d", hl->name, host->id);
}
}
return 0;
}
......@@ -261,12 +263,20 @@ void hpsb_register_highlevel(struct hpsb_highlevel *hl)
return;
}
static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host)
static void __delete_addr(struct hpsb_address_serve *as)
{
list_del(&as->host_list);
list_del(&as->hl_list);
kfree(as);
}
static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host, int update_cr)
{
unsigned long flags;
struct list_head *lh, *next;
struct hpsb_address_serve *as;
/* First, let the highlevel driver unreg */
if (hl->remove_host)
hl->remove_host(host);
......@@ -274,19 +284,24 @@ static void __unregister_host(struct hpsb_highlevel *hl, struct hpsb_host *host)
* and this particular host. */
write_lock_irqsave(&addr_space_lock, flags);
list_for_each_safe (lh, next, &hl->addr_list) {
as = list_entry(lh, struct hpsb_address_serve, addr_list);
as = list_entry(lh, struct hpsb_address_serve, hl_list);
if (as->host != host)
continue;
if (as->host == host)
__delete_addr(as);
}
write_unlock_irqrestore(&addr_space_lock, flags);
if (!list_empty(&as->addr_list)) {
list_del(&as->as_list);
list_del(&as->addr_list);
kfree(as);
/* Now update the config-rom to reflect anything removed by the
* highlevel driver. */
if (update_cr && host->update_config_rom) {
if (hpsb_update_config_rom_image(host) < 0) {
HPSB_ERR("Failed to generate Configuration ROM image for host "
"%s-%d", hl->name, host->id);
}
}
write_unlock_irqrestore(&addr_space_lock, flags);
/* And finally, remove all the host info associated between these
* two. */
hpsb_destroy_hostinfo(hl, host);
}
......@@ -294,7 +309,7 @@ static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data)
{
struct hpsb_highlevel *hl = __data;
__unregister_host(hl, host);
__unregister_host(hl, host, 1);
return 0;
}
......@@ -312,11 +327,86 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl)
nodemgr_for_each_host(hl, highlevel_for_each_host_unreg);
}
u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
struct hpsb_host *host,
struct hpsb_address_ops *ops,
u64 size, u64 alignment,
u64 start, u64 end)
{
struct hpsb_address_serve *as, *a1, *a2;
struct list_head *entry;
u64 retval = ~0ULL;
unsigned long flags;
u64 align_mask = ~(alignment - 1);
if ((alignment & 3) || (alignment > 0x800000000000ULL) ||
((hweight32(alignment >> 32) +
hweight32(alignment & 0xffffffff) != 1))) {
HPSB_ERR("%s called with invalid alignment: 0x%048llx",
__FUNCTION__, (unsigned long long)alignment);
return retval;
}
if (start == ~0ULL && end == ~0ULL) {
start = CSR1212_ALL_SPACE_BASE + 0xffff00000000ULL; /* ohci1394.c limit */
end = CSR1212_ALL_SPACE_END;
}
if (((start|end) & ~align_mask) || (start >= end) || (end > 0x1000000000000ULL)) {
HPSB_ERR("%s called with invalid addresses (start = %012Lx end = %012Lx)",
__FUNCTION__, (unsigned long long)start, (unsigned long long)end);
return retval;
}
as = (struct hpsb_address_serve *)
kmalloc(sizeof(struct hpsb_address_serve), GFP_KERNEL);
if (as == NULL) {
return retval;
}
INIT_LIST_HEAD(&as->host_list);
INIT_LIST_HEAD(&as->hl_list);
as->op = ops;
as->host = host;
write_lock_irqsave(&addr_space_lock, flags);
list_for_each(entry, &host->addr_space) {
u64 a1sa, a1ea;
u64 a2sa, a2ea;
a1 = list_entry(entry, struct hpsb_address_serve, host_list);
a2 = list_entry(entry->next, struct hpsb_address_serve, host_list);
a1sa = a1->start & align_mask;
a1ea = (a1->end + alignment -1) & align_mask;
a2sa = a2->start & align_mask;
a2ea = (a2->end + alignment -1) & align_mask;
if ((a2sa - a1ea >= size) && (a2sa - start >= size) && (end - a1ea >= size)) {
as->start = max(start, a1ea);
as->end = as->start + size;
list_add(&as->host_list, entry);
list_add_tail(&as->hl_list, &hl->addr_list);
retval = as->start;
break;
}
}
write_unlock_irqrestore(&addr_space_lock, flags);
if (retval == ~0ULL) {
kfree(as);
}
return retval;
}
int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
struct hpsb_address_ops *ops, u64 start, u64 end)
{
struct hpsb_address_serve *as;
struct list_head *entry;
struct list_head *lh;
int retval = 0;
unsigned long flags;
......@@ -331,31 +421,35 @@ int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
return 0;
}
INIT_LIST_HEAD(&as->as_list);
INIT_LIST_HEAD(&as->addr_list);
INIT_LIST_HEAD(&as->host_list);
INIT_LIST_HEAD(&as->hl_list);
as->op = ops;
as->start = start;
as->end = end;
as->host = host;
write_lock_irqsave(&addr_space_lock, flags);
entry = host->addr_space.next;
while (list_entry(entry, struct hpsb_address_serve, as_list)->end
<= start) {
if (list_entry(entry->next, struct hpsb_address_serve, as_list)
->start >= end) {
list_add(&as->as_list, entry);
list_add_tail(&as->addr_list, &hl->addr_list);
list_for_each(lh, &host->addr_space) {
struct hpsb_address_serve *as_this =
list_entry(lh, struct hpsb_address_serve, host_list);
struct hpsb_address_serve *as_next =
list_entry(lh->next, struct hpsb_address_serve, host_list);
if (as_this->end > as->start)
break;
if (as_next->start >= as->end) {
list_add(&as->host_list, lh);
list_add_tail(&as->hl_list, &hl->addr_list);
retval = 1;
break;
}
entry = entry->next;
}
write_unlock_irqrestore(&addr_space_lock, flags);
if (retval == 0) {
if (retval == 0)
kfree(as);
}
return retval;
}
......@@ -365,20 +459,15 @@ int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
{
int retval = 0;
struct hpsb_address_serve *as;
struct list_head *entry;
struct list_head *lh, *next;
unsigned long flags;
write_lock_irqsave(&addr_space_lock, flags);
entry = hl->addr_list.next;
while (entry != &hl->addr_list) {
as = list_entry(entry, struct hpsb_address_serve, addr_list);
entry = entry->next;
list_for_each_safe (lh, next, &hl->addr_list) {
as = list_entry(lh, struct hpsb_address_serve, hl_list);
if (as->start == start && as->host == host) {
list_del(&as->as_list);
list_del(&as->addr_list);
kfree(as);
__delete_addr(as);
retval = 1;
break;
}
......@@ -419,18 +508,18 @@ void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
static void init_hpsb_highlevel(struct hpsb_host *host)
{
INIT_LIST_HEAD(&dummy_zero_addr.as_list);
INIT_LIST_HEAD(&dummy_zero_addr.addr_list);
INIT_LIST_HEAD(&dummy_max_addr.as_list);
INIT_LIST_HEAD(&dummy_max_addr.addr_list);
INIT_LIST_HEAD(&dummy_zero_addr.host_list);
INIT_LIST_HEAD(&dummy_zero_addr.hl_list);
INIT_LIST_HEAD(&dummy_max_addr.host_list);
INIT_LIST_HEAD(&dummy_max_addr.hl_list);
dummy_zero_addr.op = dummy_max_addr.op = &dummy_ops;
dummy_zero_addr.start = dummy_zero_addr.end = 0;
dummy_max_addr.start = dummy_max_addr.end = ((u64) 1) << 48;
list_add_tail(&dummy_zero_addr.as_list, &host->addr_space);
list_add_tail(&dummy_max_addr.as_list, &host->addr_space);
list_add_tail(&dummy_zero_addr.host_list, &host->addr_space);
list_add_tail(&dummy_max_addr.host_list, &host->addr_space);
}
void highlevel_add_host(struct hpsb_host *host)
......@@ -445,6 +534,11 @@ void highlevel_add_host(struct hpsb_host *host)
hl->add_host(host);
}
up_read(&hl_drivers_sem);
if (host->update_config_rom) {
if (hpsb_update_config_rom_image(host) < 0)
HPSB_ERR("Failed to generate Configuration ROM image for "
"host %s-%d", hl->name, host->id);
}
}
void highlevel_remove_host(struct hpsb_host *host)
......@@ -453,7 +547,7 @@ void highlevel_remove_host(struct hpsb_host *host)
down_read(&hl_drivers_sem);
list_for_each_entry(hl, &hl_drivers, hl_list)
__unregister_host(hl, host);
__unregister_host(hl, host, 0);
up_read(&hl_drivers_sem);
}
......@@ -501,16 +595,15 @@ int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
u64 addr, unsigned int length, u16 flags)
{
struct hpsb_address_serve *as;
struct list_head *entry;
unsigned int partlength;
int rcode = RCODE_ADDRESS_ERROR;
read_lock(&addr_space_lock);
entry = host->addr_space.next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
list_for_each_entry(as, &host->addr_space, host_list) {
if (as->start > addr)
break;
while (as->start <= addr) {
if (as->end > addr) {
partlength = min(as->end - addr, (u64) length);
......@@ -529,9 +622,6 @@ int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
break;
}
}
entry = entry->next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
}
read_unlock(&addr_space_lock);
......@@ -547,16 +637,15 @@ int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
void *data, u64 addr, unsigned int length, u16 flags)
{
struct hpsb_address_serve *as;
struct list_head *entry;
unsigned int partlength;
int rcode = RCODE_ADDRESS_ERROR;
read_lock(&addr_space_lock);
entry = host->addr_space.next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
list_for_each_entry(as, &host->addr_space, host_list) {
if (as->start > addr)
break;
while (as->start <= addr) {
if (as->end > addr) {
partlength = min(as->end - addr, (u64) length);
......@@ -575,9 +664,6 @@ int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
break;
}
}
entry = entry->next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
}
read_unlock(&addr_space_lock);
......@@ -594,15 +680,14 @@ 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)
{
struct hpsb_address_serve *as;
struct list_head *entry;
int rcode = RCODE_ADDRESS_ERROR;
read_lock(&addr_space_lock);
entry = host->addr_space.next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
list_for_each_entry(as, &host->addr_space, host_list) {
if (as->start > addr)
break;
while (as->start <= addr) {
if (as->end > addr) {
if (as->op->lock) {
rcode = as->op->lock(host, nodeid, store, addr,
......@@ -613,9 +698,6 @@ int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
break;
}
entry = entry->next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
}
read_unlock(&addr_space_lock);
......@@ -627,15 +709,14 @@ 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)
{
struct hpsb_address_serve *as;
struct list_head *entry;
int rcode = RCODE_ADDRESS_ERROR;
read_lock(&addr_space_lock);
entry = host->addr_space.next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
list_for_each_entry(as, &host->addr_space, host_list) {
if (as->start > addr)
break;
while (as->start <= addr) {
if (as->end > addr) {
if (as->op->lock64) {
rcode = as->op->lock64(host, nodeid, store,
......@@ -647,9 +728,6 @@ int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
break;
}
entry = entry->next;
as = list_entry(entry, struct hpsb_address_serve, as_list);
}
read_unlock(&addr_space_lock);
......
......@@ -4,9 +4,9 @@
struct hpsb_address_serve {
struct list_head as_list; /* global list */
struct list_head host_list; /* per host list */
struct list_head addr_list; /* hpsb_highlevel list */
struct list_head hl_list; /* hpsb_highlevel list */
struct hpsb_address_ops *op;
......@@ -140,6 +140,11 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
* It returns true for successful allocation. There is no unregister function,
* all address spaces are deallocated together with the hpsb_highlevel.
*/
u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
struct hpsb_host *host,
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,
struct hpsb_address_ops *ops, u64 start, u64 end);
......
......@@ -17,14 +17,46 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "highlevel.h"
#include "nodemgr.h"
#include "csr.h"
static void delayed_reset_bus(unsigned long __reset_info)
{
struct hpsb_host *host = (struct hpsb_host*)__reset_info;
int generation = host->csr.generation + 1;
/* The generation field rolls over to 2 rather than 0 per IEEE
* 1394a-2000. */
if (generation > 0xf || generation < 2)
generation = 2;
CSR_SET_BUS_INFO_GENERATION(host->csr.rom, generation);
if (csr1212_generate_csr_image(host->csr.rom) != CSR1212_SUCCESS) {
/* CSR image creation failed, reset generation field and do not
* issue a bus reset. */
CSR_SET_BUS_INFO_GENERATION(host->csr.rom, host->csr.generation);
return;
}
host->csr.generation = generation;
host->update_config_rom = 0;
if (host->driver->set_hw_config_rom)
host->driver->set_hw_config_rom(host, host->csr.rom->bus_info_data);
host->csr.gen_timestamp[host->csr.generation] = jiffies;
hpsb_reset_bus(host, SHORT_RESET);
}
static int dummy_transmit_packet(struct hpsb_host *h, struct hpsb_packet *p)
{
return 0;
......@@ -83,6 +115,12 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
if (!h) return NULL;
memset(h, 0, sizeof(struct hpsb_host) + extra);
h->csr.rom = csr1212_create_csr(&csr_bus_ops, CSR_BUS_INFO_SIZE, h);
if (!h->csr.rom) {
kfree(h);
return NULL;
}
h->hostdata = h + 1;
h->driver = drv;
......@@ -91,6 +129,12 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
INIT_LIST_HEAD(&h->addr_space);
init_timer(&h->delayed_reset);
h->delayed_reset.function = delayed_reset_bus;
h->delayed_reset.data = (unsigned long)h;
for (i = 2; i < 16; i++)
h->csr.gen_timestamp[i] = jiffies - 60 * HZ;
for (i = 0; i < ARRAY_SIZE(h->tpool); i++)
HPSB_TPOOL_INIT(&h->tpool[i]);
......@@ -116,7 +160,14 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
memcpy(&h->device, &nodemgr_dev_template_host, sizeof(h->device));
h->device.parent = dev;
snprintf(h->device.bus_id, BUS_ID_SIZE, "fw-host%d", h->id);
h->class_dev.dev = &h->device;
h->class_dev.class = &hpsb_host_class;
snprintf(h->class_dev.class_id, BUS_ID_SIZE, "fw-host%d", h->id);
device_register(&h->device);
class_device_register(&h->class_dev);
get_device(&h->device);
return h;
}
......@@ -124,7 +175,6 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
void hpsb_add_host(struct hpsb_host *host)
{
highlevel_add_host(host);
host->driver->devctl(host, RESET_BUS, LONG_RESET);
}
void hpsb_remove_host(struct hpsb_host *host)
......@@ -134,5 +184,38 @@ void hpsb_remove_host(struct hpsb_host *host)
highlevel_remove_host(host);
class_device_unregister(&host->class_dev);
device_unregister(&host->device);
}
int hpsb_update_config_rom_image(struct hpsb_host *host)
{
unsigned long reset_time;
int next_gen = host->csr.generation + 1;
if (!host->update_config_rom)
return -EINVAL;
if (next_gen > 0xf)
next_gen = 2;
/* Stop the delayed interrupt, we're about to change the config rom and
* it would be a waste to do a bus reset twice. */
del_timer_sync(&host->delayed_reset);
/* IEEE 1394a-2000 prohibits using the same generation number
* twice in a 60 second period. */
if (jiffies - host->csr.gen_timestamp[next_gen] < 60 * HZ)
/* Wait 60 seconds from the last time this generation number was
* used. */
reset_time = (60 * HZ) + host->csr.gen_timestamp[next_gen];
else
/* Wait 1 second in case some other code wants to change the
* Config ROM in the near future. */
reset_time = jiffies + HZ;
/* This will add the timer as well as modify it */
mod_timer(&host->delayed_reset, reset_time);
return 0;
}
......@@ -10,14 +10,6 @@
#include "ieee1394_types.h"
#include "csr.h"
/* size of the array used to store config rom (in quadlets)
maximum is 0x100. About 0x40 is needed for the default
entries. So 0x80 should provide enough space for additional
directories etc.
Note: All lowlevel drivers are required to allocate at least
this amount of memory for the configuration rom!
*/
#define CSR_CONFIG_ROM_SIZE 0x100
struct hpsb_packet;
struct hpsb_iso;
......@@ -69,6 +61,10 @@ struct hpsb_host {
int id;
struct device device;
struct class_device class_dev;
int update_config_rom;
struct timer_list delayed_reset;
struct list_head addr_space;
};
......@@ -153,12 +149,10 @@ struct hpsb_host_driver {
struct module *owner;
const char *name;
/* This function must store a pointer to the configuration ROM into the
* location referenced to by pointer and return the size of the ROM. It
* may not fail. If any allocation is required, it must be done
* earlier.
*/
size_t (*get_rom) (struct hpsb_host *host, quadlet_t **pointer);
/* The hardware driver may optionally support a function that is used
* to set the hardware ConfigROM if the hardware supports handling
* reads to the ConfigROM on its own. */
void (*set_hw_config_rom) (struct hpsb_host *host, quadlet_t *config_rom);
/* This function shall implement packet transmission based on
* packet->type. It shall CRC both parts of the packet (unless
......@@ -200,24 +194,18 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
void hpsb_add_host(struct hpsb_host *host);
void hpsb_remove_host(struct hpsb_host *h);
/* updates the configuration rom of a host.
* rom_version must be the current version,
* otherwise it will fail with return value -1.
* Return value -2 indicates that the new
* rom version is too big.
* Return value 0 indicates success
*/
/* The following 2 functions are deprecated and will be removed when the
* raw1394/libraw1394 update is complete. */
int hpsb_update_config_rom(struct hpsb_host *host,
const quadlet_t *new_rom, size_t size, unsigned char rom_version);
/* reads the current version of the configuration rom of a host.
* buffersize is the size of the buffer, rom_size
* returns the size of the current rom image.
* rom_version is the version number of the fetched rom.
* return value -1 indicates, that the buffer was
* too small, 0 indicates success.
*/
int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer,
size_t buffersize, size_t *rom_size, unsigned char *rom_version);
/* Updates the configuration rom image of a host. rom_version must be the
* current version, otherwise it will fail with return value -1. If this
* host does not support config-rom-update, it will return -EINVAL.
* Return value 0 indicates success.
*/
int hpsb_update_config_rom_image(struct hpsb_host *host);
#endif /* _IEEE1394_HOSTS_H */
......@@ -515,9 +515,9 @@ int hpsb_send_packet(struct hpsb_packet *packet)
if (packet->node_id == host->node_id)
{ /* it is a local request, so handle it locally */
quadlet_t *data;
size_t size=packet->data_size+packet->header_size;
size_t size = packet->data_size + packet->header_size;
data = kmalloc(packet->header_size + packet->data_size, GFP_ATOMIC);
data = kmalloc(size, GFP_ATOMIC);
if (!data) {
HPSB_ERR("unable to allocate memory for concatenating header and data");
return -ENOMEM;
......@@ -526,12 +526,12 @@ int hpsb_send_packet(struct hpsb_packet *packet)
memcpy(data, packet->header, packet->header_size);
if (packet->data_size)
memcpy(((u8*)data)+packet->header_size, packet->data, packet->data_size);
memcpy(((u8*)data) + packet->header_size, packet->data, packet->data_size);
dump_packet("send packet local:", packet->header,
packet->header_size);
hpsb_packet_sent(host, packet, packet->expect_response?ACK_PENDING:ACK_COMPLETE);
hpsb_packet_sent(host, packet, packet->expect_response ? ACK_PENDING : ACK_COMPLETE);
hpsb_packet_received(host, data, size, 0);
kfree(data);
......@@ -935,8 +935,7 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
void abort_requests(struct hpsb_host *host)
{
unsigned long flags;
struct hpsb_packet *packet;
struct list_head *lh, *tlh;
struct hpsb_packet *packet, *packet_next;
LIST_HEAD(llist);
host->driver->devctl(host, CANCEL_REQUESTS, 0);
......@@ -946,8 +945,7 @@ void abort_requests(struct hpsb_host *host)
INIT_LIST_HEAD(&host->pending_packets);
spin_unlock_irqrestore(&host->pending_pkt_lock, flags);
list_for_each_safe(lh, tlh, &llist) {
packet = list_entry(lh, struct hpsb_packet, list);
list_for_each_entry_safe(packet, packet_next, &llist, list) {
list_del(&packet->list);
packet->state = hpsb_complete;
packet->ack_code = ACKX_ABORTED;
......@@ -959,9 +957,8 @@ void abort_timedouts(unsigned long __opaque)
{
struct hpsb_host *host = (struct hpsb_host *)__opaque;
unsigned long flags;
struct hpsb_packet *packet;
struct hpsb_packet *packet, *packet_next;
unsigned long expire;
struct list_head *lh, *tlh;
LIST_HEAD(expiredlist);
spin_lock_irqsave(&host->csr.lock, flags);
......@@ -970,8 +967,7 @@ void abort_timedouts(unsigned long __opaque)
spin_lock_irqsave(&host->pending_pkt_lock, flags);
list_for_each_safe(lh, tlh, &host->pending_packets) {
packet = list_entry(lh, struct hpsb_packet, list);
list_for_each_entry_safe(packet, packet_next, &host->pending_packets, list) {
if (time_before(packet->sendtime + expire, jiffies)) {
list_del(&packet->list);
list_add(&packet->list, &expiredlist);
......@@ -983,8 +979,7 @@ void abort_timedouts(unsigned long __opaque)
spin_unlock_irqrestore(&host->pending_pkt_lock, flags);
list_for_each_safe(lh, tlh, &expiredlist) {
packet = list_entry(lh, struct hpsb_packet, list);
list_for_each_entry_safe(packet, packet_next, &expiredlist, list) {
list_del(&packet->list);
packet->state = hpsb_complete;
packet->ack_code = ACKX_TIMEOUT;
......@@ -995,6 +990,8 @@ void abort_timedouts(unsigned long __opaque)
static int __init ieee1394_init(void)
{
int i;
devfs_mk_dir("ieee1394");
if (register_chrdev_region(IEEE1394_CORE_DEV, 256, "ieee1394")) {
......@@ -1008,8 +1005,12 @@ static int __init ieee1394_init(void)
0, 0, NULL, NULL);
bus_register(&ieee1394_bus_type);
for (i = 0; fw_bus_attrs[i]; i++)
bus_create_file(&ieee1394_bus_type, fw_bus_attrs[i]);
class_register(&hpsb_host_class);
init_csr();
if (init_csr())
return -ENOMEM;
if (!disable_nodemgr)
init_ieee1394_nodemgr();
......@@ -1021,11 +1022,16 @@ static int __init ieee1394_init(void)
static void __exit ieee1394_cleanup(void)
{
int i;
if (!disable_nodemgr)
cleanup_ieee1394_nodemgr();
cleanup_csr();
class_unregister(&hpsb_host_class);
for (i = 0; fw_bus_attrs[i]; i++)
bus_remove_file(&ieee1394_bus_type, fw_bus_attrs[i]);
bus_unregister(&ieee1394_bus_type);
kmem_cache_destroy(hpsb_packet_cache);
......@@ -1043,6 +1049,7 @@ module_exit(ieee1394_cleanup);
EXPORT_SYMBOL(hpsb_alloc_host);
EXPORT_SYMBOL(hpsb_add_host);
EXPORT_SYMBOL(hpsb_remove_host);
EXPORT_SYMBOL(hpsb_update_config_rom_image);
/** ieee1394_core.c **/
EXPORT_SYMBOL(hpsb_speedto_str);
......@@ -1081,6 +1088,7 @@ EXPORT_SYMBOL(hpsb_register_highlevel);
EXPORT_SYMBOL(hpsb_unregister_highlevel);
EXPORT_SYMBOL(hpsb_register_addrspace);
EXPORT_SYMBOL(hpsb_unregister_addrspace);
EXPORT_SYMBOL(hpsb_allocate_and_register_addrspace);
EXPORT_SYMBOL(hpsb_listen_channel);
EXPORT_SYMBOL(hpsb_unlisten_channel);
EXPORT_SYMBOL(hpsb_get_hostinfo);
......@@ -1113,7 +1121,6 @@ EXPORT_SYMBOL(nodemgr_for_each_host);
/** csr.c **/
EXPORT_SYMBOL(hpsb_update_config_rom);
EXPORT_SYMBOL(hpsb_get_config_rom);
/** dma.c **/
EXPORT_SYMBOL(dma_prog_region_init);
......@@ -1144,3 +1151,34 @@ EXPORT_SYMBOL(hpsb_iso_packet_sent);
EXPORT_SYMBOL(hpsb_iso_packet_received);
EXPORT_SYMBOL(hpsb_iso_wake);
EXPORT_SYMBOL(hpsb_iso_recv_flush);
/** csr1212.c **/
EXPORT_SYMBOL(csr1212_create_csr);
EXPORT_SYMBOL(csr1212_init_local_csr);
EXPORT_SYMBOL(csr1212_new_immediate);
EXPORT_SYMBOL(csr1212_new_leaf);
EXPORT_SYMBOL(csr1212_new_csr_offset);
EXPORT_SYMBOL(csr1212_new_directory);
EXPORT_SYMBOL(csr1212_associate_keyval);
EXPORT_SYMBOL(csr1212_attach_keyval_to_directory);
EXPORT_SYMBOL(csr1212_new_extended_immediate);
EXPORT_SYMBOL(csr1212_new_extended_leaf);
EXPORT_SYMBOL(csr1212_new_descriptor_leaf);
EXPORT_SYMBOL(csr1212_new_textual_descriptor_leaf);
EXPORT_SYMBOL(csr1212_new_string_descriptor_leaf);
EXPORT_SYMBOL(csr1212_new_icon_descriptor_leaf);
EXPORT_SYMBOL(csr1212_new_modifiable_descriptor_leaf);
EXPORT_SYMBOL(csr1212_new_keyword_leaf);
EXPORT_SYMBOL(csr1212_detach_keyval_from_directory);
EXPORT_SYMBOL(csr1212_disassociate_keyval);
EXPORT_SYMBOL(csr1212_release_keyval);
EXPORT_SYMBOL(csr1212_destroy_csr);
EXPORT_SYMBOL(csr1212_read);
EXPORT_SYMBOL(csr1212_generate_positions);
EXPORT_SYMBOL(csr1212_generate_layout_order);
EXPORT_SYMBOL(csr1212_fill_cache);
EXPORT_SYMBOL(csr1212_generate_csr_image);
EXPORT_SYMBOL(csr1212_parse_keyval);
EXPORT_SYMBOL(csr1212_parse_csr);
EXPORT_SYMBOL(_csr1212_read_keyval);
EXPORT_SYMBOL(_csr1212_destroy_keyval);
......@@ -215,5 +215,6 @@ static inline unsigned char ieee1394_file_to_instance(struct file *file)
/* Our sysfs bus entry */
extern struct bus_type ieee1394_bus_type;
extern struct class hpsb_host_class;
#endif /* _IEEE1394_CORE_H */
......@@ -18,6 +18,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
#include <asm/atomic.h>
#include "ieee1394_types.h"
......@@ -28,6 +29,15 @@
#include "csr.h"
#include "nodemgr.h"
static int ignore_drivers = 0;
module_param(ignore_drivers, int, 0444);
MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
struct nodemgr_csr_info {
struct hpsb_host *host;
nodeid_t nodeid;
unsigned int generation;
};
static char *nodemgr_find_oui_name(int oui)
......@@ -47,6 +57,37 @@ static char *nodemgr_find_oui_name(int oui)
}
static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length,
void *buffer, void *__ci)
{
struct nodemgr_csr_info *ci = (struct nodemgr_csr_info*)__ci;
int i, ret = 0;
for (i = 0; i < 3; i++) {
ret = hpsb_read(ci->host, ci->nodeid, ci->generation, addr,
buffer, length);
if (!ret)
break;
set_current_state(TASK_INTERRUPTIBLE);
if (schedule_timeout (HZ/3))
return -EINTR;
}
return ret;
}
static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci)
{
return (bus_info_data[2] >> 8) & 0x3;
}
static struct csr1212_bus_ops nodemgr_csr_ops = {
.bus_read = nodemgr_bus_read,
.get_max_rom = nodemgr_get_max_rom
};
/*
* Basically what we do here is start off retrieving the bus_info block.
* From there will fill in some info about the node, verify it is of IEEE
......@@ -69,7 +110,6 @@ static char *nodemgr_find_oui_name(int oui)
static DECLARE_MUTEX(nodemgr_serialize);
struct host_info {
struct hpsb_host *host;
struct list_head list;
......@@ -79,19 +119,108 @@ struct host_info {
char daemon_name[15];
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
static void nodemgr_resume_ne(struct node_entry *ne);
static void nodemgr_remove_ne(struct node_entry *ne);
static struct node_entry *find_entry_by_guid(u64 guid);
struct bus_type ieee1394_bus_type = {
.name = "ieee1394",
.match = nodemgr_bus_match,
.hotplug = nodemgr_hotplug,
};
static void host_cls_release(struct class_device *class_dev)
{
put_device(&container_of((class_dev), struct hpsb_host, class_dev)->device);
}
struct class hpsb_host_class = {
.name = "ieee1394_host",
.release = host_cls_release,
};
static void ne_cls_release(struct class_device *class_dev)
{
put_device(&container_of((class_dev), struct node_entry, class_dev)->device);
}
struct class nodemgr_ne_class = {
.name = "ieee1394_node",
.release = ne_cls_release,
};
static struct hpsb_highlevel nodemgr_highlevel;
static int nodemgr_driverdata_ne;
static int nodemgr_driverdata_host;
static int nodemgr_platform_data_ud;
static int nodemgr_generic_probe(struct device *dev)
{
return -ENODEV;
}
static struct device_driver nodemgr_driver_ne = {
.name = "ieee1394_node",
.bus = &ieee1394_bus_type,
.probe = nodemgr_generic_probe,
};
static struct device_driver nodemgr_driver_host = {
.name = "ieee1394_host",
.bus = &ieee1394_bus_type,
.probe = nodemgr_generic_probe,
};
static void nodemgr_release_ud(struct device *dev)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
if (ud->vendor_name_kv)
csr1212_release_keyval(ud->vendor_name_kv);
if (ud->model_name_kv)
csr1212_release_keyval(ud->model_name_kv);
kfree(ud);
}
static void nodemgr_release_ne(struct device *dev)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
if (ne->vendor_name_kv)
csr1212_release_keyval(ne->vendor_name_kv);
kfree(ne);
}
static void nodemgr_release_host(struct device *dev)
{
struct hpsb_host *host = container_of(dev, struct hpsb_host, device);
csr1212_destroy_csr(host->csr.rom);
kfree(host);
}
static struct device nodemgr_dev_template_ud = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ud,
.platform_data = &nodemgr_platform_data_ud,
};
static struct device nodemgr_dev_template_ne = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ne,
.driver = &nodemgr_driver_ne,
};
struct device nodemgr_dev_template_host = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_host,
.driver = &nodemgr_driver_host,
};
......@@ -107,6 +236,26 @@ static struct device_attribute dev_attr_##class##_##field = { \
.show = fw_show_##class##_##field, \
};
#define fw_attr_td(class, class_type, td_kv) \
static ssize_t fw_show_##class##_##td_kv (struct device *dev, char *buf)\
{ \
int len; \
class_type *class = container_of(dev, class_type, device); \
len = (class->td_kv->value.leaf.len - 2) * sizeof(quadlet_t); \
memcpy(buf, \
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(class->td_kv), \
len); \
while ((buf + len - 1) == '\0') \
len--; \
buf[len++] = '\n'; \
buf[len] = '\0'; \
return len; \
} \
static struct device_attribute dev_attr_##class##_##td_kv = { \
.attr = {.name = __stringify(td_kv), .mode = S_IRUGO }, \
.show = fw_show_##class##_##td_kv, \
};
#define fw_drv_attr(field, type, format_string) \
static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
......@@ -126,10 +275,13 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, char *buf)
struct node_entry *ne = container_of(dev, struct node_entry, device);
return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) "
"LSPD(%d) MAX_REC(%d) CYC_CLK_ACC(%d)\n", ne->busopt.irmc,
"LSPD(%d) MAX_REC(%d) MAX_ROM(%d) CYC_CLK_ACC(%d)\n",
ne->busopt.irmc,
ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
ne->busopt.max_rec, ne->busopt.cyc_clk_acc);
ne->busopt.max_rec,
ne->busopt.max_rom,
ne->busopt.cyc_clk_acc);
}
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
......@@ -159,19 +311,121 @@ static ssize_t fw_show_ne_tlabels_mask(struct device *dev, char *buf)
return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
#endif
}
static DEVICE_ATTR(tlabels_mask,S_IRUGO,fw_show_ne_tlabels_mask,NULL);
static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
static ssize_t fw_set_ignore_driver(struct device *dev, const char *buf, size_t count)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
int state = simple_strtoul(buf, NULL, 10);
if (state == 1) {
down_write(&dev->bus->subsys.rwsem);
device_release_driver(dev);
ud->ignore_driver = 1;
up_write(&dev->bus->subsys.rwsem);
} else if (!state)
ud->ignore_driver = 0;
return count;
}
static ssize_t fw_get_ignore_driver(struct device *dev, char *buf)
{
struct unit_directory *ud = container_of(dev, struct unit_directory, device);
return sprintf(buf, "%d\n", ud->ignore_driver);
}
static DEVICE_ATTR(ignore_driver, S_IWUSR | S_IRUGO, fw_get_ignore_driver, fw_set_ignore_driver);
static ssize_t fw_set_destroy_node(struct bus_type *bus, const char *buf, size_t count)
{
struct node_entry *ne;
u64 guid = (u64)simple_strtoull(buf, NULL, 16);
ne = find_entry_by_guid(guid);
if (ne == NULL || !ne->in_limbo)
return -EINVAL;
nodemgr_remove_ne(ne);
return count;
}
static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf)
{
return sprintf(buf, "You can destroy in_limbo nodes by writing their GUID to this file\n");
}
static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node);
static int nodemgr_rescan_bus_thread(void *__unused)
{
/* No userlevel access needed */
daemonize("kfwrescan");
allow_signal(SIGTERM);
bus_rescan_devices(&ieee1394_bus_type);
return 0;
}
static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count)
{
int state = simple_strtoul(buf, NULL, 10);
/* Don't wait for this, or care about errors. Root could do
* something stupid and spawn this a lot of times, but that's
* root's fault. */
if (state == 1)
kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL);
return count;
}
static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
{
return sprintf(buf, "You can force a rescan of the bus for "
"drivers by writing a 1 to this file\n");
}
static BUS_ATTR(rescan, S_IWUSR | S_IRUGO, fw_get_rescan, fw_set_rescan);
static ssize_t fw_set_ignore_drivers(struct bus_type *bus, const char *buf, size_t count)
{
int state = simple_strtoul(buf, NULL, 10);
if (state == 1)
ignore_drivers = 1;
else if (!state)
ignore_drivers = 0;
return count;
}
static ssize_t fw_get_ignore_drivers(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", ignore_drivers);
}
static BUS_ATTR(ignore_drivers, S_IWUSR | S_IRUGO, fw_get_ignore_drivers, fw_set_ignore_drivers);
struct bus_attribute *const fw_bus_attrs[] = {
&bus_attr_destroy_node,
&bus_attr_rescan,
&bus_attr_ignore_drivers,
NULL
};
fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n")
fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n")
fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n")
fw_attr(ne, struct node_entry, vendor_name, const char *, "%s\n")
fw_attr_td(ne, struct node_entry, vendor_name_kv)
fw_attr(ne, struct node_entry, vendor_oui, const char *, "%s\n")
fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n")
fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n")
fw_attr(ne, struct node_entry, guid_vendor_oui, const char *, "%s\n")
fw_attr(ne, struct node_entry, in_limbo, int, "%d\n");
static struct device_attribute *const fw_ne_attrs[] = {
&dev_attr_ne_guid,
......@@ -194,13 +448,14 @@ fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n")
fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n")
fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n")
fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n")
fw_attr(ud, struct unit_directory, vendor_name, const char *, "%s\n")
fw_attr_td(ud, struct unit_directory, vendor_name_kv)
fw_attr(ud, struct unit_directory, vendor_oui, const char *, "%s\n")
fw_attr(ud, struct unit_directory, model_name, const char *, "%s\n")
fw_attr_td(ud, struct unit_directory, model_name_kv)
static struct device_attribute *const fw_ud_attrs[] = {
&dev_attr_ud_address,
&dev_attr_ud_length,
&dev_attr_ignore_driver,
};
......@@ -358,14 +613,14 @@ static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
device_create_file(dev, &dev_attr_ud_vendor_id);
if (ud->flags & UNIT_DIRECTORY_VENDOR_TEXT)
device_create_file(dev, &dev_attr_ud_vendor_name);
if (ud->vendor_name_kv)
device_create_file(dev, &dev_attr_ud_vendor_name_kv);
}
if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
device_create_file(dev, &dev_attr_ud_model_id);
if (ud->flags & UNIT_DIRECTORY_MODEL_TEXT)
device_create_file(dev, &dev_attr_ud_model_name);
if (ud->model_name_kv)
device_create_file(dev, &dev_attr_ud_model_name_kv);
}
}
......@@ -376,14 +631,17 @@ static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
struct unit_directory *ud;
struct ieee1394_device_id *id;
if (dev->driver_data == &nodemgr_driverdata_ne ||
dev->driver_data == &nodemgr_driverdata_host ||
drv == &nodemgr_driver_ne || drv == &nodemgr_driver_host)
/* We only match unit directories, and ignore our internal drivers */
if (dev->platform_data != &nodemgr_platform_data_ud ||
drv->probe == nodemgr_generic_probe)
return 0;
ud = container_of(dev, struct unit_directory, device);
driver = container_of(drv, struct hpsb_protocol_driver, driver);
if (ud->ne->in_limbo || ud->ignore_driver)
return 0;
for (id = driver->id_table; id->match_flags != 0; id++) {
if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
id->vendor_id != ud->vendor_id)
......@@ -408,24 +666,6 @@ static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
}
static void nodemgr_release_ud(struct device *dev)
{
kfree(container_of(dev, struct unit_directory, device));
}
static void nodemgr_release_ne(struct device *dev)
{
kfree(container_of(dev, struct node_entry, device));
}
static void nodemgr_release_host(struct device *dev)
{
kfree(container_of(dev, struct hpsb_host, device));
}
static void nodemgr_remove_ud(struct unit_directory *ud)
{
struct device *dev = &ud->device;
......@@ -444,10 +684,10 @@ static void nodemgr_remove_ud(struct unit_directory *ud)
device_remove_file(dev, &dev_attr_ud_specifier_id);
device_remove_file(dev, &dev_attr_ud_version);
device_remove_file(dev, &dev_attr_ud_vendor_id);
device_remove_file(dev, &dev_attr_ud_vendor_name);
device_remove_file(dev, &dev_attr_ud_vendor_name_kv);
device_remove_file(dev, &dev_attr_ud_vendor_oui);
device_remove_file(dev, &dev_attr_ud_model_id);
device_remove_file(dev, &dev_attr_ud_model_name);
device_remove_file(dev, &dev_attr_ud_model_name_kv);
device_unregister(dev);
}
......@@ -470,15 +710,20 @@ static void nodemgr_remove_ne(struct node_entry *ne)
struct device *dev = &ne->device;
int i;
HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
nodemgr_remove_node_uds(ne);
for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
device_remove_file(dev, fw_ne_attrs[i]);
device_remove_file(dev, &dev_attr_ne_guid_vendor_oui);
device_remove_file(dev, &dev_attr_ne_vendor_name);
device_remove_file(dev, &dev_attr_ne_vendor_name_kv);
device_remove_file(dev, &dev_attr_ne_vendor_oui);
device_remove_file(dev, &dev_attr_ne_in_limbo);
class_device_unregister(&ne->class_dev);
device_unregister(dev);
}
......@@ -503,188 +748,13 @@ static void nodemgr_remove_host_dev(struct device *dev)
}
static struct device nodemgr_dev_template_ud = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ud,
};
static struct device nodemgr_dev_template_ne = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_ne,
.driver = &nodemgr_driver_ne,
.driver_data = &nodemgr_driverdata_ne,
};
struct device nodemgr_dev_template_host = {
.bus = &ieee1394_bus_type,
.release = nodemgr_release_host,
.driver = &nodemgr_driver_host,
.driver_data = &nodemgr_driverdata_host,
};
static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size);
struct bus_type ieee1394_bus_type = {
.name = "ieee1394",
.match = nodemgr_bus_match,
.hotplug = nodemgr_hotplug,
};
static int nodemgr_read_quadlet(struct hpsb_host *host,
nodeid_t nodeid, unsigned int generation,
octlet_t address, quadlet_t *quad)
{
int i;
int ret = 0;
for (i = 0; i < 3; i++) {
ret = hpsb_read(host, nodeid, generation, address, quad, 4);
if (!ret)
break;
set_current_state(TASK_INTERRUPTIBLE);
if (schedule_timeout (HZ/3))
return -1;
}
*quad = be32_to_cpu(*quad);
return ret;
}
static int nodemgr_size_text_leaf(struct hpsb_host *host,
nodeid_t nodeid, unsigned int generation,
octlet_t address)
{
quadlet_t quad;
int size = 0;
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return -1;
if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) {
/* This is the offset. */
address += 4 * CONFIG_ROM_VALUE(quad);
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return -1;
/* Now we got the size of the text descriptor leaf. */
size = CONFIG_ROM_LEAF_LENGTH(quad);
}
return size;
}
static int nodemgr_read_text_leaf(struct node_entry *ne,
octlet_t address,
quadlet_t *quadp)
{
quadlet_t quad;
int i, size, ret;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)
|| CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
return -1;
/* This is the offset. */
address += 4 * CONFIG_ROM_VALUE(quad);
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad))
return -1;
/* Now we got the size of the text descriptor leaf. */
size = CONFIG_ROM_LEAF_LENGTH(quad) - 2;
if (size <= 0)
return -1;
address += 4;
for (i = 0; i < 2; i++, address += 4, quadp++) {
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, quadp))
return -1;
}
/* Now read the text string. */
ret = -ENXIO;
for (; size > 0; size--, address += 4, quadp++) {
for (i = 0; i < 3; i++) {
ret = hpsb_node_read(ne, address, quadp, 4);
if (ret != -EAGAIN)
break;
}
if (ret)
break;
}
return ret;
}
static struct node_entry *nodemgr_scan_root_directory
(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation)
static void nodemgr_update_bus_options(struct node_entry *ne)
{
octlet_t address;
quadlet_t quad;
int length;
int code, size, total_size;
struct node_entry *ne;
address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return NULL;
if (CONFIG_ROM_BUS_INFO_LENGTH(quad) == 1) /* minimal config rom */
return NULL;
address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return NULL;
length = CONFIG_ROM_ROOT_LENGTH(quad);
address += 4;
size = 0;
total_size = sizeof(struct node_entry);
for (; length > 0; length--, address += 4) {
if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
return NULL;
code = CONFIG_ROM_KEY(quad);
if (code == CONFIG_ROM_VENDOR_ID && length > 0) {
/* Check if there is a text descriptor leaf
immediately after this. */
size = nodemgr_size_text_leaf(host, nodeid, generation,
address + 4);
if (size > 0) {
address += 4;
length--;
total_size += (size + 1) * sizeof (quadlet_t);
} else if (size < 0)
return NULL;
}
}
ne = kmalloc(total_size, GFP_KERNEL);
if (!ne)
return NULL;
memset(ne, 0, total_size);
if (size != 0) {
ne->vendor_name = (const char *) &(ne->quadlets[2]);
ne->quadlets[size] = 0;
} else {
ne->vendor_name = NULL;
}
return ne;
}
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
static const u16 mr[] = { 4, 64, 1024, 0};
#endif
quadlet_t busoptions = be32_to_cpu(ne->csr->bus_info_data[2]);
static void nodemgr_update_bus_options(struct node_entry *ne,
quadlet_t busoptions)
{
ne->busopt.irmc = (busoptions >> 31) & 1;
ne->busopt.cmc = (busoptions >> 30) & 1;
ne->busopt.isc = (busoptions >> 29) & 1;
......@@ -692,28 +762,32 @@ static void nodemgr_update_bus_options(struct node_entry *ne,
ne->busopt.pmc = (busoptions >> 27) & 1;
ne->busopt.cyc_clk_acc = (busoptions >> 16) & 0xff;
ne->busopt.max_rec = 1 << (((busoptions >> 12) & 0xf) + 1);
ne->busopt.max_rom = (busoptions >> 8) & 0x3;
ne->busopt.generation = (busoptions >> 4) & 0xf;
ne->busopt.lnkspd = busoptions & 0x7;
HPSB_VERBOSE("NodeMgr: raw=0x%08x irmc=%d cmc=%d isc=%d bmc=%d pmc=%d "
"cyc_clk_acc=%d max_rec=%d gen=%d lspd=%d",
"cyc_clk_acc=%d max_rec=%d max_rom=%d gen=%d lspd=%d",
busoptions, ne->busopt.irmc, ne->busopt.cmc,
ne->busopt.isc, ne->busopt.bmc, ne->busopt.pmc,
ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
mr[ne->busopt.max_rom],
ne->busopt.generation, ne->busopt.lnkspd);
}
static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr *csr,
struct host_info *hi, nodeid_t nodeid,
unsigned int generation)
{
struct hpsb_host *host = hi->host;
struct node_entry *ne;
ne = nodemgr_scan_root_directory (host, nodeid, generation);
ne = kmalloc(sizeof(struct node_entry), GFP_KERNEL);
if (!ne) return NULL;
memset(ne, 0, sizeof(struct node_entry));
ne->tpool = &host->tpool[nodeid & NODE_MASK];
ne->host = host;
......@@ -724,6 +798,7 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption
ne->guid = guid;
ne->guid_vendor_id = (guid >> 40) & 0xffffff;
ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id);
ne->csr = csr;
memcpy(&ne->device, &nodemgr_dev_template_ne,
sizeof(ne->device));
......@@ -731,13 +806,20 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption
snprintf(ne->device.bus_id, BUS_ID_SIZE, "%016Lx",
(unsigned long long)(ne->guid));
ne->class_dev.dev = &ne->device;
ne->class_dev.class = &nodemgr_ne_class;
snprintf(ne->class_dev.class_id, BUS_ID_SIZE, "%016Lx",
(unsigned long long)(ne->guid));
device_register(&ne->device);
class_device_register(&ne->class_dev);
get_device(&ne->device);
if (ne->guid_vendor_oui)
device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui);
nodemgr_create_ne_dev_files(ne);
nodemgr_update_bus_options(ne, busoptions);
nodemgr_update_bus_options(ne);
HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
(host->node_id == nodeid) ? "Host" : "Node",
......@@ -747,276 +829,129 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoption
}
struct guid_search_baton {
u64 guid;
struct node_entry *ne;
};
static int nodemgr_guid_search_cb(struct device *dev, void *__data)
{
struct guid_search_baton *search = __data;
struct node_entry *ne;
if (dev->driver_data != &nodemgr_driverdata_ne)
return 0;
ne = container_of(dev, struct node_entry, device);
if (ne->guid == search->guid) {
search->ne = ne;
return 1;
}
return 0;
}
static struct node_entry *find_entry_by_guid(u64 guid)
{
struct guid_search_baton search;
search.guid = guid;
search.ne = NULL;
bus_for_each_dev(&ieee1394_bus_type, NULL, &search, nodemgr_guid_search_cb);
return search.ne;
}
struct nodeid_search_baton {
nodeid_t nodeid;
struct node_entry *ne;
struct hpsb_host *host;
};
static int nodemgr_nodeid_search_cb(struct device *dev, void *__data)
{
struct nodeid_search_baton *search = __data;
struct node_entry *ne;
if (dev->driver_data != &nodemgr_driverdata_ne)
return 0;
struct class *class = &nodemgr_ne_class;
struct class_device *cdev;
struct node_entry *ne, *ret_ne = NULL;
ne = container_of(dev, struct node_entry, device);
down_read(&class->subsys.rwsem);
list_for_each_entry(cdev, &class->children, node) {
ne = container_of(cdev, struct node_entry, class_dev);
if (ne->host == search->host && ne->nodeid == search->nodeid) {
search->ne = ne;
/* Returning 1 stops the iteration */
return 1;
if (ne->guid == guid) {
ret_ne = ne;
break;
}
}
up_read(&class->subsys.rwsem);
return 0;
return ret_ne;
}
static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid)
{
struct nodeid_search_baton search;
search.nodeid = nodeid;
search.ne = NULL;
search.host = host;
bus_for_each_dev(&ieee1394_bus_type, NULL, &search, nodemgr_nodeid_search_cb);
struct class *class = &nodemgr_ne_class;
struct class_device *cdev;
struct node_entry *ne, *ret_ne = NULL;
return search.ne;
}
static struct unit_directory *nodemgr_scan_unit_directory
(struct node_entry *ne, octlet_t address)
{
struct unit_directory *ud;
quadlet_t quad;
u8 flags, todo;
int length, size, total_size, count;
int vendor_name_size, model_name_size;
down_read(&class->subsys.rwsem);
list_for_each_entry(cdev, &class->children, node) {
ne = container_of(cdev, struct node_entry, class_dev);
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad))
return NULL;
length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
address += 4;
size = 0;
total_size = sizeof (struct unit_directory);
flags = 0;
count = 0;
vendor_name_size = 0;
model_name_size = 0;
for (; length > 0; length--, address += 4) {
int code;
quadlet_t value;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
return NULL;
code = CONFIG_ROM_KEY(quad);
value = CONFIG_ROM_VALUE(quad);
todo = 0;
switch (code) {
case CONFIG_ROM_VENDOR_ID:
todo = UNIT_DIRECTORY_VENDOR_TEXT;
break;
case CONFIG_ROM_MODEL_ID:
todo = UNIT_DIRECTORY_MODEL_TEXT;
break;
case CONFIG_ROM_SPECIFIER_ID:
case CONFIG_ROM_UNIT_SW_VERSION:
if (ne->host == host && ne->nodeid == nodeid) {
ret_ne = ne;
break;
case CONFIG_ROM_DESCRIPTOR_LEAF:
case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
/* TODO: read strings... icons? */
break;
default:
/* Which types of quadlets do we want to
store? Only count immediate values and
CSR offsets for now. */
code &= CONFIG_ROM_KEY_TYPE_MASK;
if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0)
count++;
break;
}
if (todo && length > 0) {
/* Check if there is a text descriptor leaf
immediately after this. */
size = nodemgr_size_text_leaf(ne->host,
ne->nodeid,
ne->generation,
address + 4);
if (todo == UNIT_DIRECTORY_VENDOR_TEXT)
vendor_name_size = size;
else
model_name_size = size;
if (size > 0) {
address += 4;
length--;
flags |= todo;
total_size += (size + 1) * sizeof (quadlet_t);
}
else if (size < 0)
return NULL;
}
}
total_size += count * sizeof (quadlet_t);
ud = kmalloc (total_size, GFP_KERNEL);
if (ud != NULL) {
memset (ud, 0, total_size);
ud->flags = flags;
ud->length = count;
ud->vendor_name_size = vendor_name_size;
ud->model_name_size = model_name_size;
}
up_read(&class->subsys.rwsem);
return ud;
return ret_ne;
}
/* This implementation currently only scans the config rom and its
* immediate unit directories looking for software_id and
* software_version entries, in order to get driver autoloading working. */
static struct unit_directory *nodemgr_process_unit_directory
(struct host_info *hi, struct node_entry *ne, octlet_t address, unsigned int *id,
struct unit_directory *parent)
(struct host_info *hi, struct node_entry *ne, struct csr1212_keyval *ud_kv,
unsigned int *id, struct unit_directory *parent)
{
struct unit_directory *ud;
quadlet_t quad;
quadlet_t *infop;
int length;
struct unit_directory *ud_temp = NULL;
struct csr1212_dentry *dentry;
struct csr1212_keyval *kv;
u8 last_key_id = 0;
if (!(ud = nodemgr_scan_unit_directory(ne, address)))
ud = kmalloc(sizeof(struct unit_directory), GFP_KERNEL);
if (!ud)
goto unit_directory_error;
memset (ud, 0, sizeof(struct unit_directory));
ud->ne = ne;
ud->address = address;
ud->ignore_driver = ignore_drivers;
ud->address = ud_kv->offset + CSR1212_CONFIG_ROM_SPACE_BASE;
ud->ud_kv = ud_kv;
ud->id = (*id)++;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
goto unit_directory_error;
length = CONFIG_ROM_DIRECTORY_LENGTH(quad) ;
address += 4;
infop = (quadlet_t *) ud->quadlets;
for (; length > 0; length--, address += 4) {
int code;
quadlet_t value;
quadlet_t *quadp;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
goto unit_directory_error;
code = CONFIG_ROM_KEY(quad) ;
value = CONFIG_ROM_VALUE(quad);
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ud->vendor_id = value;
csr1212_for_each_dir_entry(ne->csr, kv, ud_kv, dentry) {
switch (kv->key.id) {
case CSR1212_KV_ID_VENDOR:
if (kv->key.type == CSR1212_KV_TYPE_IMMEDIATE) {
ud->vendor_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
if (ud->vendor_id)
ud->vendor_oui = nodemgr_find_oui_name(ud->vendor_id);
if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) {
length--;
address += 4;
quadp = &(ud->quadlets[ud->length]);
if (nodemgr_read_text_leaf(ne, address, quadp) == 0
&& quadp[0] == 0 && quadp[1] == 0) {
/* We only support minimal
ASCII and English. */
quadp[ud->vendor_name_size] = 0;
ud->vendor_name
= (const char *) &(quadp[2]);
}
}
break;
case CONFIG_ROM_MODEL_ID:
ud->model_id = value;
case CSR1212_KV_ID_MODEL:
ud->model_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_MODEL_ID;
if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) {
length--;
address += 4;
quadp = &(ud->quadlets[ud->length + ud->vendor_name_size + 1]);
if (nodemgr_read_text_leaf(ne, address, quadp) == 0
&& quadp[0] == 0 && quadp[1] == 0) {
/* We only support minimal
ASCII and English. */
quadp[ud->model_name_size] = 0;
ud->model_name
= (const char *) &(quadp[2]);
}
}
break;
case CONFIG_ROM_SPECIFIER_ID:
ud->specifier_id = value;
case CSR1212_KV_ID_SPECIFIER_ID:
ud->specifier_id = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_SPECIFIER_ID;
break;
case CONFIG_ROM_UNIT_SW_VERSION:
ud->version = value;
case CSR1212_KV_ID_VERSION:
ud->version = kv->value.immediate;
ud->flags |= UNIT_DIRECTORY_VERSION;
break;
case CONFIG_ROM_DESCRIPTOR_LEAF:
case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
/* TODO: read strings... icons? */
case CSR1212_KV_ID_DESCRIPTOR:
if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
switch (last_key_id) {
case CSR1212_KV_ID_VENDOR:
ud->vendor_name_kv = kv;
csr1212_keep_keyval(kv);
break;
case CONFIG_ROM_LOGICAL_UNIT_DIRECTORY:
case CSR1212_KV_ID_MODEL:
ud->model_name_kv = kv;
csr1212_keep_keyval(kv);
break;
}
} /* else if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) ... */
break;
case CSR1212_KV_ID_DEPENDENT_INFO:
if (kv->key.type == CSR1212_KV_TYPE_DIRECTORY) {
/* This should really be done in SBP2 as this is
* doing SBP2 specific parsing. */
ud->flags |= UNIT_DIRECTORY_HAS_LUN_DIRECTORY;
ud_temp = nodemgr_process_unit_directory(hi, ne, address + value * 4, id,
ud_temp = nodemgr_process_unit_directory(hi, ne, kv, id,
parent);
if (ud_temp == NULL)
......@@ -1048,18 +983,14 @@ static struct unit_directory *nodemgr_process_unit_directory
ud_temp->flags |= UNIT_DIRECTORY_VERSION;
ud_temp->version = ud->version;
}
}
break;
default:
/* Which types of quadlets do we want to
store? Only count immediate values and
CSR offsets for now. */
code &= CONFIG_ROM_KEY_TYPE_MASK;
if ((code & CONFIG_ROM_KEY_TYPE_LEAF) == 0)
*infop++ = quad;
break;
}
last_key_id = kv->key.id;
}
memcpy(&ud->device, &nodemgr_dev_template_ud,
......@@ -1091,76 +1022,51 @@ static struct unit_directory *nodemgr_process_unit_directory
static void nodemgr_process_root_directory(struct host_info *hi, struct node_entry *ne)
{
octlet_t address;
quadlet_t quad;
int length;
unsigned int ud_id = 0;
struct csr1212_dentry *dentry;
struct csr1212_keyval *kv;
u8 last_key_id = 0;
device_remove_file(&ne->device, &dev_attr_ne_vendor_oui);
address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
return;
address += 4 + CONFIG_ROM_BUS_INFO_LENGTH(quad) * 4;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
return;
length = CONFIG_ROM_ROOT_LENGTH(quad);
address += 4;
for (; length > 0; length--, address += 4) {
int code, value;
if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
address, &quad))
return;
code = CONFIG_ROM_KEY(quad);
value = CONFIG_ROM_VALUE(quad);
ne->needs_probe = 0;
switch (code) {
case CONFIG_ROM_VENDOR_ID:
ne->vendor_id = value;
csr1212_for_each_dir_entry(ne->csr, kv, ne->csr->root_kv, dentry) {
switch (kv->key.id) {
case CSR1212_KV_ID_VENDOR:
ne->vendor_id = kv->value.immediate;
if (ne->vendor_id)
ne->vendor_oui = nodemgr_find_oui_name(ne->vendor_id);
/* Now check if there is a vendor name text
string. */
if (ne->vendor_name != NULL) {
length--;
address += 4;
if (nodemgr_read_text_leaf(ne, address, ne->quadlets) != 0
|| ne->quadlets[0] != 0 || ne->quadlets[1] != 0)
/* We only support minimal
ASCII and English. */
ne->vendor_name = NULL;
else
device_create_file(&ne->device,
&dev_attr_ne_vendor_name);
}
break;
case CONFIG_ROM_NODE_CAPABILITES:
ne->capabilities = value;
case CSR1212_KV_ID_NODE_CAPABILITIES:
ne->capabilities = kv->value.immediate;
break;
case CONFIG_ROM_UNIT_DIRECTORY:
nodemgr_process_unit_directory(hi, ne, address + value * 4, &ud_id,
NULL);
case CSR1212_KV_ID_UNIT:
nodemgr_process_unit_directory(hi, ne, kv, &ud_id, NULL);
break;
case CONFIG_ROM_DESCRIPTOR_LEAF:
case CONFIG_ROM_DESCRIPTOR_DIRECTORY:
/* TODO: read strings... icons? */
case CSR1212_KV_ID_DESCRIPTOR:
if (last_key_id == CSR1212_KV_ID_VENDOR) {
if (kv->key.type == CSR1212_KV_TYPE_LEAF &&
CSR1212_DESCRIPTOR_LEAF_TYPE(kv) == 0 &&
CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) == 0 &&
CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) == 0) {
ne->vendor_name_kv = kv;
csr1212_keep_keyval(kv);
}
}
break;
}
last_key_id = kv->key.id;
}
if (ne->vendor_oui)
device_create_file(&ne->device, &dev_attr_ne_vendor_oui);
if (ne->vendor_name_kv)
device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv);
}
#ifdef CONFIG_HOTPLUG
......@@ -1176,13 +1082,14 @@ static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
if (!dev)
return -ENODEV;
/* Have to check driver_data, since on remove, driver == NULL */
if (dev->driver_data == &nodemgr_driverdata_ne ||
dev->driver_data == &nodemgr_driverdata_host)
if (dev->platform_data != &nodemgr_platform_data_ud)
return -ENODEV;
ud = container_of(dev, struct unit_directory, device);
if (ud->ne->in_limbo || ud->ignore_driver)
return -ENODEV;
scratch = buffer;
#define PUT_ENVP(fmt,val) \
......@@ -1261,7 +1168,7 @@ void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
* informed that this device just went through a bus reset, to allow
* the to take whatever actions required.
*/
static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
static void nodemgr_update_node(struct node_entry *ne, struct csr1212_csr *csr,
struct host_info *hi, nodeid_t nodeid,
unsigned int generation)
{
......@@ -1272,87 +1179,29 @@ static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
ne->nodeid = nodeid;
}
if (ne->busopt.generation != ((busoptions >> 4) & 0xf)) {
if (ne->busopt.generation != ((be32_to_cpu(csr->bus_info_data[2]) >> 4) & 0xf)) {
kfree(ne->csr->private);
csr1212_destroy_csr(ne->csr);
ne->csr = csr;
/* If the node's configrom generation has changed, we
* unregister all the unit directories. */
nodemgr_remove_node_uds(ne);
nodemgr_update_bus_options(ne, busoptions);
nodemgr_update_bus_options(ne);
/* Mark the node as new, so it gets re-probed */
ne->needs_probe = 1;
}
if (ne->in_limbo)
nodemgr_resume_ne(ne);
/* Mark the node current */
ne->generation = generation;
}
static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation,
quadlet_t *buffer, int buffer_length)
{
octlet_t addr = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
unsigned header_size;
int i;
/* IEEE P1212 says that devices should support 64byte block
* reads, aligned on 64byte boundaries. That doesn't seem to
* work though, and we are forced to doing quadlet sized
* reads. */
HPSB_VERBOSE("Initiating ConfigROM request for node " NODE_BUS_FMT,
NODE_BUS_ARGS(host, nodeid));
/*
* Must retry a few times if config rom read returns zero (how long?). Will
* not normally occur, but we should do the right thing. For example, with
* some sbp2 devices, the bridge chipset cannot return valid config rom reads
* immediately after power-on, since they need to detect the type of
* device attached (disk or CD-ROM).
*/
for (i = 0; i < 4; i++) {
if (nodemgr_read_quadlet(host, nodeid, generation,
addr, &buffer[0]) < 0) {
HPSB_ERR("ConfigROM quadlet transaction error for node "
NODE_BUS_FMT, NODE_BUS_ARGS(host, nodeid));
return -1;
}
if (buffer[0])
break;
set_current_state(TASK_INTERRUPTIBLE);
if (schedule_timeout (HZ/4))
return -1;
}
header_size = buffer[0] >> 24;
addr += 4;
if (header_size == 1) {
HPSB_INFO("Node " NODE_BUS_FMT " has a minimal ROM. "
"Vendor is %08x",
NODE_BUS_ARGS(host, nodeid), buffer[0] & 0x00ffffff);
return -1;
}
if (header_size < 4) {
HPSB_INFO("Node " NODE_BUS_FMT " has non-standard ROM "
"format (%d quads), cannot parse",
NODE_BUS_ARGS(host, nodeid), header_size);
return -1;
}
for (i = 1; i < buffer_length; i++, addr += 4) {
if (nodemgr_read_quadlet(host, nodeid, generation,
addr, &buffer[i]) < 0) {
HPSB_ERR("ConfigROM quadlet transaction "
"error for node " NODE_BUS_FMT,
NODE_BUS_ARGS(host, nodeid));
return -1;
}
}
return 0;
}
static void nodemgr_node_scan_one(struct host_info *hi,
......@@ -1360,17 +1209,32 @@ static void nodemgr_node_scan_one(struct host_info *hi,
{
struct hpsb_host *host = hi->host;
struct node_entry *ne;
quadlet_t buffer[5];
octlet_t guid;
struct csr1212_csr *csr;
struct nodemgr_csr_info *ci;
ci = kmalloc(sizeof(struct nodemgr_csr_info), GFP_KERNEL);
if (!ci)
return;
ci->host = host;
ci->nodeid = nodeid;
ci->generation = generation;
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
if (read_businfo_block (host, nodeid, generation,
buffer, sizeof(buffer) >> 2))
csr = csr1212_create_csr(&nodemgr_csr_ops, 5 * sizeof(quadlet_t), ci);
if (!csr || csr1212_parse_csr(csr) != CSR1212_SUCCESS) {
HPSB_ERR("Error parsing configrom for node " NODE_BUS_FMT,
NODE_BUS_ARGS(host, nodeid));
if (csr)
csr1212_destroy_csr(csr);
kfree(ci);
return;
}
if (buffer[1] != IEEE1394_BUSID_MAGIC) {
if (csr->bus_info_data[1] != IEEE1394_BUSID_MAGIC) {
/* This isn't a 1394 device, but we let it slide. There
* was a report of a device with broken firmware which
* reported '2394' instead of '1394', which is obviously a
......@@ -1379,176 +1243,196 @@ static void nodemgr_node_scan_one(struct host_info *hi,
* shouldn't be held responsible, so we'll allow it with a
* warning. */
HPSB_WARN("Node " NODE_BUS_FMT " has invalid busID magic [0x%08x]",
NODE_BUS_ARGS(host, nodeid), buffer[1]);
NODE_BUS_ARGS(host, nodeid), csr->bus_info_data[1]);
}
guid = ((u64)buffer[3] << 32) | buffer[4];
guid = ((u64)be32_to_cpu(csr->bus_info_data[3]) << 32) | be32_to_cpu(csr->bus_info_data[4]);
ne = find_entry_by_guid(guid);
if (ne && ne->host != host && ne->in_limbo) {
/* Must have moved this device from one host to another */
nodemgr_remove_ne(ne);
ne = NULL;
}
if (!ne)
nodemgr_create_node(guid, buffer[2], hi, nodeid, generation);
nodemgr_create_node(guid, csr, hi, nodeid, generation);
else
nodemgr_update_node(ne, buffer[2], hi, nodeid, generation);
nodemgr_update_node(ne, csr, hi, nodeid, generation);
return;
}
struct cleanup_baton {
unsigned int generation;
struct hpsb_host *host;
struct node_entry *ne;
};
static void nodemgr_node_scan(struct host_info *hi, int generation)
{
int count;
struct hpsb_host *host = hi->host;
struct selfid *sid = (struct selfid *)host->topology_map;
nodeid_t nodeid = LOCAL_BUS;
/* Scan each node on the bus */
for (count = host->selfid_count; count; count--, sid++) {
if (sid->extended)
continue;
if (!sid->link_active) {
nodeid++;
continue;
}
nodemgr_node_scan_one(hi, nodeid++, generation);
}
}
static int nodemgr_remove_node(struct device *dev, void *__data)
static void nodemgr_suspend_ud(struct unit_directory *ud)
{
struct cleanup_baton *cleanup = __data;
struct node_entry *ne;
struct device *dev;
if (dev->driver_data != &nodemgr_driverdata_ne)
return 0;
list_for_each_entry(dev, &ud->device.children, node)
nodemgr_suspend_ud(container_of(dev, struct unit_directory, device));
ne = container_of(dev, struct node_entry, device);
if (ud->device.driver) {
int ret = -1;
if (ne->host != cleanup->host)
return 0;
if (ud->device.driver->suspend)
ret = ud->device.driver->suspend(&ud->device, 0, 0);
if (ne->generation != cleanup->generation) {
cleanup->ne = ne;
return 1;
if (ret) {
dev = &ud->device;
down_write(&dev->bus->subsys.rwsem);
device_release_driver(dev);
up_write(&dev->bus->subsys.rwsem);
}
}
}
return 0;
static void nodemgr_suspend_ne(struct node_entry *ne)
{
struct device *dev;
HPSB_DEBUG("Node suspended: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
ne->in_limbo = 1;
device_create_file(&ne->device, &dev_attr_ne_in_limbo);
list_for_each_entry(dev, &ne->device.children, node)
nodemgr_suspend_ud(container_of(dev, struct unit_directory, device));
}
struct ne_cb_data_struct {
struct host_info *hi;
struct node_entry *ne;
};
static int nodemgr_probe_ne_cb(struct device *dev, void *__data)
static void nodemgr_resume_ud(struct unit_directory *ud)
{
struct ne_cb_data_struct *ne_cb_data = __data;
struct host_info *hi = ne_cb_data->hi;
struct node_entry *ne;
struct device *dev;
if (dev->driver_data != &nodemgr_driverdata_ne)
return 0;
list_for_each_entry(dev, &ud->device.children, node)
nodemgr_resume_ud(container_of(dev, struct unit_directory, device));
ne = ne_cb_data->ne = container_of(dev, struct node_entry, device);
if (ud->device.driver && ud->device.driver->resume)
ud->device.driver->resume(&ud->device, 0);
}
if (ne->host != hi->host)
return 0;
/* We can't call nodemgr_process_root_directory() here because
* that can call device_register. Since this callback is under a
* rwsem, the device_register would deadlock. So, we signal back
* to the callback, and process things there. */
static void nodemgr_resume_ne(struct node_entry *ne)
{
struct device *dev;
if (ne->needs_probe) {
ne->needs_probe = 0;
return 1;
} else {
struct list_head *lh;
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
/* Update unit_dirs with attached drivers */
list_for_each(lh, &dev->children) {
struct unit_directory *ud;
list_for_each_entry(dev, &ne->device.children, node)
nodemgr_resume_ud(container_of(dev, struct unit_directory, device));
ud = container_of(list_to_dev(lh), struct unit_directory, device);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
if (ud->device.driver) {
static void nodemgr_ud_update_pdrv(struct unit_directory *ud)
{
struct device *dev;
struct hpsb_protocol_driver *pdrv;
pdrv = container_of(ud->device.driver,
struct hpsb_protocol_driver, driver);
if (!get_device(&ud->device))
return;
list_for_each_entry(dev, &ud->device.children, node)
nodemgr_ud_update_pdrv(container_of(dev, struct unit_directory, device));
if (ud->device.driver) {
pdrv = container_of(ud->device.driver, struct hpsb_protocol_driver, driver);
if (pdrv->update)
pdrv->update(ud);
}
}
}
return 0;
put_device(&ud->device);
}
static void nodemgr_node_scan(struct host_info *hi, int generation)
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
int count;
struct hpsb_host *host = hi->host;
struct selfid *sid = (struct selfid *)host->topology_map;
nodeid_t nodeid = LOCAL_BUS;
struct device *dev, *udev;
/* Scan each node on the bus */
for (count = host->selfid_count; count; count--, sid++) {
if (sid->extended)
continue;
if (ne->host != hi->host || ne->in_limbo)
return;
if (!sid->link_active) {
nodeid++;
continue;
}
nodemgr_node_scan_one(hi, nodeid++, generation);
}
dev = get_device(&ne->device);
if (!dev)
return;
/* If "needs_probe", then this is either a new or changed node we
* rescan totally. If the generation matches for an existing node
* (one that existed prior to the bus reset) we send update calls
* down to the drivers. Otherwise, this is a dead node and we
* suspend it. */
if (ne->needs_probe)
nodemgr_process_root_directory(hi, ne);
else if (ne->generation == generation)
list_for_each_entry(udev, &dev->children, node)
nodemgr_ud_update_pdrv(container_of(udev, struct unit_directory, device));
else
nodemgr_suspend_ne(ne);
put_device(dev);
}
static void nodemgr_node_probe(struct host_info *hi, int generation)
{
struct hpsb_host *host = hi->host;
struct ne_cb_data_struct ne_cb_data;
ne_cb_data.hi = hi;
ne_cb_data.ne = NULL;
struct class *class = &nodemgr_ne_class;
struct class_device *cdev;
/* Do some processing of the nodes we've probed. This pulls them
* into the sysfs layer if needed, and can result in processing of
* unit-directories, or just updating the node and it's
* unit-directories. */
while (bus_for_each_dev(&ieee1394_bus_type, ne_cb_data.ne ? &ne_cb_data.ne->device : NULL,
&ne_cb_data, nodemgr_probe_ne_cb)) {
/* If we get in here, we've got a node that needs it's
* unit directories processed. */
struct device *dev = get_device(&ne_cb_data.ne->device);
if (dev) {
nodemgr_process_root_directory(hi, ne_cb_data.ne);
put_device(dev);
}
}
down_read(&class->subsys.rwsem);
list_for_each_entry(cdev, &class->children, node)
nodemgr_probe_ne(hi, container_of(cdev, struct node_entry, class_dev), generation);
up_read(&class->subsys.rwsem);
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
* skip the clean up for now, since we could remove nodes that
* were still on the bus. The bus reset increased hi->reset_sem,
* so there's a bus scan pending which will do the clean up
* eventually. */
if (generation == get_hpsb_generation(host)) {
struct cleanup_baton cleanup;
cleanup.generation = generation;
cleanup.host = host;
/* This will iterate until all devices that do not match
* the generation are removed. */
while (bus_for_each_dev(&ieee1394_bus_type, NULL, &cleanup,
nodemgr_remove_node)) {
struct node_entry *ne = cleanup.ne;
HPSB_DEBUG("Node removed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(host, ne->nodeid), (unsigned long long)ne->guid);
nodemgr_remove_ne(ne);
}
/* Now let's tell the bus to rescan our devices. This may
* seem like overhead, but the driver-model core will only
* scan a device for a driver when either the device is
* added, or when a new driver is added. A bus reset is a
* good reason to rescan devices that were there before.
* For example, an sbp2 device may become available for
* login, if the host that held it was just removed. */
* eventually.
*
* Now let's tell the bus to rescan our devices. This may seem
* like overhead, but the driver-model core will only scan a
* device for a driver when either the device is added, or when a
* new driver is added. A bus reset is a good reason to rescan
* devices that were there before. For example, an sbp2 device
* may become available for login, if the host that held it was
* just removed. */
if (generation == get_hpsb_generation(host))
bus_rescan_devices(&ieee1394_bus_type);
}
return;
}
......@@ -1736,32 +1620,24 @@ struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid
return ne;
}
struct for_each_host_struct {
int (*cb)(struct hpsb_host *, void *);
void *data;
};
static int nodemgr_for_each_host_cb(struct device *dev, void *__data)
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
{
struct for_each_host_struct *host_data = __data;
struct class *class = &hpsb_host_class;
struct class_device *cdev;
struct hpsb_host *host;
int error = 0;
if (dev->driver_data != &nodemgr_driverdata_host)
return 0;
host = container_of(dev, struct hpsb_host, device);
down_read(&class->subsys.rwsem);
list_for_each_entry(cdev, &class->children, node) {
host = container_of(cdev, struct hpsb_host, class_dev);
return host_data->cb(host, host_data->data);
}
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
{
struct for_each_host_struct host_data;
host_data.cb = cb;
host_data.data = __data;
if ((error = cb(host, __data)))
break;
}
up_read(&class->subsys.rwsem);
return bus_for_each_dev(&ieee1394_bus_type, NULL, &host_data, nodemgr_for_each_host_cb);
return error;
}
/* The following four convenience functions use a struct node_entry
......@@ -1887,6 +1763,7 @@ void init_ieee1394_nodemgr(void)
{
driver_register(&nodemgr_driver_host);
driver_register(&nodemgr_driver_ne);
class_register(&nodemgr_ne_class);
hpsb_register_highlevel(&nodemgr_highlevel);
}
......@@ -1895,6 +1772,7 @@ void cleanup_ieee1394_nodemgr(void)
{
hpsb_unregister_highlevel(&nodemgr_highlevel);
class_unregister(&nodemgr_ne_class);
driver_unregister(&nodemgr_driver_ne);
driver_unregister(&nodemgr_driver_host);
}
......@@ -21,50 +21,12 @@
#define _IEEE1394_NODEMGR_H
#include <linux/device.h>
#include "csr1212.h"
#include "ieee1394_core.h"
#include "ieee1394_hotplug.h"
#define CONFIG_ROM_BUS_INFO_LENGTH(q) ((q) >> 24)
#define CONFIG_ROM_BUS_CRC_LENGTH(q) (((q) >> 16) & 0xff)
#define CONFIG_ROM_BUS_CRC(q) ((q) & 0xffff)
#define CONFIG_ROM_ROOT_LENGTH(q) ((q) >> 16)
#define CONFIG_ROM_ROOT_CRC(q) ((q) & 0xffff)
#define CONFIG_ROM_DIRECTORY_LENGTH(q) ((q) >> 16)
#define CONFIG_ROM_DIRECTORY_CRC(q) ((q) & 0xffff)
#define CONFIG_ROM_LEAF_LENGTH(q) ((q) >> 16)
#define CONFIG_ROM_LEAF_CRC(q) ((q) & 0xffff)
#define CONFIG_ROM_DESCRIPTOR_TYPE(q) ((q) >> 24)
#define CONFIG_ROM_DESCRIPTOR_SPECIFIER_ID(q) ((q) & 0xffffff)
#define CONFIG_ROM_DESCRIPTOR_WIDTH(q) ((q) >> 28)
#define CONFIG_ROM_DESCRIPTOR_CHAR_SET(q) (((q) >> 16) & 0xfff)
#define CONFIG_ROM_DESCRIPTOR_LANG(q) ((q) & 0xffff)
#define CONFIG_ROM_KEY_ID_MASK 0x3f
#define CONFIG_ROM_KEY_TYPE_MASK 0xc0
#define CONFIG_ROM_KEY_TYPE_IMMEDIATE 0x00
#define CONFIG_ROM_KEY_TYPE_OFFSET 0x40
#define CONFIG_ROM_KEY_TYPE_LEAF 0x80
#define CONFIG_ROM_KEY_TYPE_DIRECTORY 0xc0
#define CONFIG_ROM_KEY(q) ((q) >> 24)
#define CONFIG_ROM_VALUE(q) ((q) & 0xffffff)
#define CONFIG_ROM_VENDOR_ID 0x03
#define CONFIG_ROM_MODEL_ID 0x17
#define CONFIG_ROM_NODE_CAPABILITES 0x0C
#define CONFIG_ROM_UNIT_DIRECTORY 0xd1
#define CONFIG_ROM_LOGICAL_UNIT_DIRECTORY 0xd4
#define CONFIG_ROM_SPECIFIER_ID 0x12
#define CONFIG_ROM_UNIT_SW_VERSION 0x13
#define CONFIG_ROM_DESCRIPTOR_LEAF 0x81
#define CONFIG_ROM_DESCRIPTOR_DIRECTORY 0xc1
/* '1' '3' '9' '4' in ASCII */
#define IEEE1394_BUSID_MAGIC 0x31333934
#define IEEE1394_BUSID_MAGIC __constant_cpu_to_be32(0x31333934)
/* This is the start of a Node entry structure. It should be a stable API
* for which to gather info from the Node Manager about devices attached
......@@ -76,6 +38,7 @@ struct bus_options {
u8 bmc; /* Bus Master Capable */
u8 pmc; /* Power Manager Capable (PNP spec) */
u8 cyc_clk_acc; /* Cycle clock accuracy */
u8 max_rom; /* Maximum block read supported in the CSR */
u8 generation; /* Incremented when configrom changes */
u8 lnkspd; /* Link speed */
u16 max_rec; /* Maximum packet size node can receive */
......@@ -86,10 +49,8 @@ struct bus_options {
#define UNIT_DIRECTORY_MODEL_ID 0x02
#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
#define UNIT_DIRECTORY_VERSION 0x08
#define UNIT_DIRECTORY_VENDOR_TEXT 0x10
#define UNIT_DIRECTORY_MODEL_TEXT 0x20
#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x40
#define UNIT_DIRECTORY_LUN_DIRECTORY 0x80
#define UNIT_DIRECTORY_HAS_LUN_DIRECTORY 0x10
#define UNIT_DIRECTORY_LUN_DIRECTORY 0x20
/*
* A unit directory corresponds to a protocol supported by the
......@@ -102,24 +63,23 @@ struct unit_directory {
u8 flags; /* Indicates which entries were read */
quadlet_t vendor_id;
const char *vendor_name;
struct csr1212_keyval *vendor_name_kv;
const char *vendor_oui;
int vendor_name_size;
quadlet_t model_id;
const char *model_name;
int model_name_size;
struct csr1212_keyval *model_name_kv;
quadlet_t specifier_id;
quadlet_t version;
unsigned int id;
int ignore_driver;
int length; /* Number of quadlets */
struct device device;
/* XXX Must be last in the struct! */
quadlet_t quadlets[0];
struct csr1212_keyval *ud_kv;
};
struct node_entry {
......@@ -135,7 +95,7 @@ struct node_entry {
/* The following is read from the config rom */
u32 vendor_id;
const char *vendor_name;
struct csr1212_keyval *vendor_name_kv;
const char *vendor_oui;
u32 capabilities;
......@@ -143,8 +103,12 @@ struct node_entry {
struct device device;
/* XXX Must be last in the struct! */
quadlet_t quadlets[0];
struct class_device class_dev;
/* Means this node is not attached anymore */
int in_limbo;
struct csr1212_csr *csr;
};
struct hpsb_protocol_driver {
......@@ -231,4 +195,7 @@ void cleanup_ieee1394_nodemgr(void);
/* The template for a host device */
extern struct device nodemgr_dev_template_host;
/* Bus attributes we export */
extern struct bus_attribute *const fw_bus_attrs[];
#endif /* _IEEE1394_NODEMGR_H */
......@@ -115,6 +115,7 @@
#include <asm/pci-bridge.h>
#endif
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
......@@ -161,7 +162,7 @@ printk(level "%s: " fmt "\n" , OHCI1394_DRIVER_NAME , ## args)
printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
static char version[] __devinitdata =
"$Rev: 1097 $ Ben Collins <bcollins@debian.org>";
"$Rev: 1131 $ Ben Collins <bcollins@debian.org>";
/* Module Parameters */
static int phys_dma = 1;
......@@ -495,8 +496,6 @@ static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg)
return ctx;
}
static void ohci_init_config_rom(struct ti_ohci *ohci);
/* Global initialization */
static void ohci_initialize(struct ti_ohci *ohci)
{
......@@ -540,9 +539,6 @@ static void ohci_initialize(struct ti_ohci *ohci)
/* Set the Config ROM mapping register */
reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus);
/* Initialize the Config ROM */
ohci_init_config_rom(ohci);
/* Now get our max packet size */
ohci->max_packet_size =
1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1);
......@@ -2211,14 +2207,12 @@ static void ohci_schedule_iso_tasklets(struct ti_ohci *ohci,
quadlet_t rx_event,
quadlet_t tx_event)
{
struct list_head *lh;
struct ohci1394_iso_tasklet *t;
unsigned long mask;
spin_lock(&ohci->iso_tasklet_list_lock);
list_for_each(lh, &ohci->iso_tasklet_list) {
t = list_entry(lh, struct ohci1394_iso_tasklet, link);
list_for_each_entry(t, &ohci->iso_tasklet_list, link) {
mask = 1 << t->context;
if (t->type == OHCI_ISO_TRANSMIT && tx_event & mask)
......@@ -3085,154 +3079,16 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
return 0;
}
static u16 ohci_crc16 (u32 *ptr, int length)
{
int shift;
u32 crc, sum, data;
crc = 0;
for (; length > 0; length--) {
data = be32_to_cpu(*ptr++);
for (shift = 28; shift >= 0; shift -= 4) {
sum = ((crc >> 12) ^ (data >> shift)) & 0x000f;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
}
crc &= 0xffff;
}
return crc;
}
/* Config ROM macro implementation influenced by NetBSD OHCI driver */
struct config_rom_unit {
u32 *start;
u32 *refer;
int length;
int refunit;
};
struct config_rom_ptr {
u32 *data;
int unitnum;
struct config_rom_unit unitdir[10];
};
#define cf_put_1quad(cr, q) (((cr)->data++)[0] = cpu_to_be32(q))
#define cf_put_4bytes(cr, b1, b2, b3, b4) \
(((cr)->data++)[0] = cpu_to_be32(((b1) << 24) | ((b2) << 16) | ((b3) << 8) | (b4)))
#define cf_put_keyval(cr, key, val) (((cr)->data++)[0] = cpu_to_be32(((key) << 24) | (val)))
static inline void cf_put_str(struct config_rom_ptr *cr, const char *str)
{
int t;
char fourb[4];
while (str[0]) {
memset(fourb, 0, 4);
for (t = 0; t < 4 && str[t]; t++)
fourb[t] = str[t];
cf_put_4bytes(cr, fourb[0], fourb[1], fourb[2], fourb[3]);
str += strlen(str) < 4 ? strlen(str) : 4;
}
return;
}
static inline void cf_put_crc16(struct config_rom_ptr *cr, int unit)
{
*cr->unitdir[unit].start =
cpu_to_be32((cr->unitdir[unit].length << 16) |
ohci_crc16(cr->unitdir[unit].start + 1,
cr->unitdir[unit].length));
}
static inline void cf_unit_begin(struct config_rom_ptr *cr, int unit)
{
if (cr->unitdir[unit].refer != NULL) {
*cr->unitdir[unit].refer |=
cpu_to_be32 (cr->data - cr->unitdir[unit].refer);
cf_put_crc16(cr, cr->unitdir[unit].refunit);
}
cr->unitnum = unit;
cr->unitdir[unit].start = cr->data++;
}
static inline void cf_put_refer(struct config_rom_ptr *cr, char key, int unit)
static void ohci_set_hw_config_rom(struct hpsb_host *host, quadlet_t *config_rom)
{
cr->unitdir[unit].refer = cr->data;
cr->unitdir[unit].refunit = cr->unitnum;
(cr->data++)[0] = cpu_to_be32(key << 24);
}
static inline void cf_unit_end(struct config_rom_ptr *cr)
{
cr->unitdir[cr->unitnum].length = cr->data -
(cr->unitdir[cr->unitnum].start + 1);
cf_put_crc16(cr, cr->unitnum);
}
/* End of NetBSD derived code. */
struct ti_ohci *ohci = host->hostdata;
static void ohci_init_config_rom(struct ti_ohci *ohci)
{
struct config_rom_ptr cr;
reg_write(ohci, OHCI1394_ConfigROMhdr, be32_to_cpu(config_rom[0]));
reg_write(ohci, OHCI1394_BusOptions, be32_to_cpu(config_rom[2]));
memset(&cr, 0, sizeof(cr));
memset(ohci->csr_config_rom_cpu, 0, OHCI_CONFIG_ROM_LEN);
cr.data = ohci->csr_config_rom_cpu;
/* Bus info block */
cf_unit_begin(&cr, 0);
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_BusID));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_BusOptions));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_GUIDHi));
cf_put_1quad(&cr, reg_read(ohci, OHCI1394_GUIDLo));
cf_unit_end(&cr);
DBGMSG(ohci->id, "GUID: %08x:%08x", reg_read(ohci, OHCI1394_GUIDHi),
reg_read(ohci, OHCI1394_GUIDLo));
/* IEEE P1212 suggests the initial ROM header CRC should only
* cover the header itself (and not the entire ROM). Since we do
* this, then we can make our bus_info_len the same as the CRC
* length. */
ohci->csr_config_rom_cpu[0] |= cpu_to_be32(
(be32_to_cpu(ohci->csr_config_rom_cpu[0]) & 0x00ff0000) << 8);
reg_write(ohci, OHCI1394_ConfigROMhdr,
be32_to_cpu(ohci->csr_config_rom_cpu[0]));
/* Root directory */
cf_unit_begin(&cr, 1);
/* Vendor ID */
cf_put_keyval(&cr, 0x03, reg_read(ohci,OHCI1394_VendorID) & 0xFFFFFF);
cf_put_refer(&cr, 0x81, 2); /* Textual description unit */
cf_put_keyval(&cr, 0x0c, 0x0083c0); /* Node capabilities */
/* NOTE: Add other unit referers here, and append at bottom */
cf_unit_end(&cr);
/* Textual description - "Linux 1394" */
cf_unit_begin(&cr, 2);
cf_put_keyval(&cr, 0, 0);
cf_put_1quad(&cr, 0);
cf_put_str(&cr, "Linux OHCI-1394");
cf_unit_end(&cr);
ohci->csr_config_rom_length = cr.data - ohci->csr_config_rom_cpu;
memcpy(ohci->csr_config_rom_cpu, config_rom, OHCI_CONFIG_ROM_LEN);
}
static size_t ohci_get_rom(struct hpsb_host *host, quadlet_t **ptr)
{
struct ti_ohci *ohci=host->hostdata;
DBGMSG(ohci->id, "request csr_rom address: %p",
ohci->csr_config_rom_cpu);
*ptr = ohci->csr_config_rom_cpu;
return ohci->csr_config_rom_length * 4;
}
static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
quadlet_t data, quadlet_t compare)
......@@ -3257,7 +3113,7 @@ static quadlet_t ohci_hw_csr_reg(struct hpsb_host *host, int reg,
static struct hpsb_host_driver ohci1394_driver = {
.owner = THIS_MODULE,
.name = OHCI1394_DRIVER_NAME,
.get_rom = ohci_get_rom,
.set_hw_config_rom = ohci_set_hw_config_rom,
.transmit_packet = ohci_transmit,
.devctl = ohci_devctl,
.isoctl = ohci_isoctl,
......@@ -3280,6 +3136,11 @@ do { \
static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
struct csr1212_keyval *root;
struct csr1212_keyval *vend_id = NULL;
struct csr1212_keyval *text = NULL;
int ret;
static int version_printed = 0;
struct hpsb_host *host;
......@@ -3458,6 +3319,45 @@ static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
ohci->init_state = OHCI_INIT_HAVE_IRQ;
ohci_initialize(ohci);
/* Setup initial root directory entries */
root = host->csr.rom->root_kv;
vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR,
reg_read(ohci, OHCI1394_GUIDHi) >> 8);
text = csr1212_new_string_descriptor_leaf("Linux 1394 - OHCI");
if (!vend_id || !text) {
if (vend_id) {
csr1212_release_keyval(vend_id);
}
if (text) {
csr1212_release_keyval(text);
}
FAIL(-ENOMEM, "Failed to allocate memory for mandatory ConfigROM entries!");
}
ret = csr1212_associate_keyval(vend_id, text);
csr1212_release_keyval(text);
if(ret != CSR1212_SUCCESS) {
csr1212_release_keyval(vend_id);
FAIL(ret, "Failed to associate text descriptor to vendor id");
}
ret = csr1212_attach_keyval_to_directory(root, vend_id);
if(ret != CSR1212_SUCCESS) {
csr1212_release_keyval(vend_id);
FAIL(ret, "Failed to attach vendor id to root directory");
}
host->update_config_rom = 1;
/* Set certain csr values */
host->csr.guid_hi = reg_read(ohci, OHCI1394_GUIDHi);
host->csr.guid_lo = reg_read(ohci, OHCI1394_GUIDLo);
host->csr.cyc_clk_acc = 100; /* how do we determine clk accuracy? */
host->csr.max_rec = (reg_read(ohci, OHCI1394_BusOptions) >> 12) & 0xf;
host->csr.lnk_spd = reg_read(ohci, OHCI1394_BusOptions) & 0x7;
/* Tell the highlevel this host is ready */
hpsb_add_host(host);
ohci->init_state = OHCI_INIT_DONE;
......@@ -3574,13 +3474,11 @@ static void ohci1394_pci_remove(struct pci_dev *pdev)
}
#ifdef CONFIG_PM
static int ohci1394_pci_resume (struct pci_dev *dev)
{
pci_enable_device(dev);
return 0;
}
#endif
#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
......@@ -3604,10 +3502,7 @@ static struct pci_driver ohci1394_pci_driver = {
.id_table = ohci1394_pci_tbl,
.probe = ohci1394_pci_probe,
.remove = ohci1394_pci_remove,
#ifdef CONFIG_PM
.resume = ohci1394_pci_resume,
#endif /* PM */
};
......
......@@ -49,6 +49,7 @@
#include <asm/io.h>
#include <asm/uaccess.h>
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
......@@ -1515,6 +1516,11 @@ static int __devinit add_card(struct pci_dev *dev,
return error; \
} while (0)
struct csr1212_keyval *root;
struct csr1212_keyval *vend_id = NULL;
struct csr1212_keyval *text = NULL;
int ret;
char irq_buf[16];
struct hpsb_host *host;
struct ti_lynx *lynx; /* shortcut to currently handled device */
......@@ -1527,8 +1533,6 @@ static int __devinit add_card(struct pci_dev *dev,
struct i2c_adapter i2c_adapter;
struct i2c_algo_bit_data i2c_adapter_data;
int got_valid_bus_info_block = 0; /* set to 1, if we were able to get a valid bus info block from serial eeprom */
error = -ENXIO;
if (pci_set_dma_mask(dev, 0xffffffff))
......@@ -1814,14 +1818,15 @@ static int __devinit add_card(struct pci_dev *dev,
if (i2c_bit_add_bus(&i2c_adapter) < 0)
{
PRINT(KERN_ERR, lynx->id, "unable to register i2c");
error = -ENXIO;
FAIL("unable to register i2c");
}
else
{
/* do i2c stuff */
unsigned char i2c_cmd = 0x10;
struct i2c_msg msg[2] = { { 0x50, 0, 1, &i2c_cmd },
{ 0x50, I2C_M_RD, 20, (unsigned char*) lynx->config_rom }
{ 0x50, I2C_M_RD, 20, (unsigned char*) lynx->bus_info_block }
};
......@@ -1858,16 +1863,16 @@ static int __devinit add_card(struct pci_dev *dev,
for (i = 0; i < 5 ; i++)
PRINTD(KERN_DEBUG, lynx->id, "Businfo block quadlet %i: %08x",
i, be32_to_cpu(lynx->config_rom[i]));
i, be32_to_cpu(lynx->bus_info_block[i]));
/* info_length, crc_length and 1394 magic number to check, if it is really a bus info block */
if (((be32_to_cpu(lynx->config_rom[0]) & 0xffff0000) == 0x04040000) &&
(lynx->config_rom[1] == __constant_cpu_to_be32(0x31333934)))
if (((be32_to_cpu(lynx->bus_info_block[0]) & 0xffff0000) == 0x04040000) &&
(lynx->bus_info_block[1] == __constant_cpu_to_be32(0x31333934)))
{
PRINT(KERN_DEBUG, lynx->id, "read a valid bus info block from");
got_valid_bus_info_block = 1;
} else {
PRINT(KERN_WARNING, lynx->id, "read something from serial eeprom, but it does not seem to be a valid bus info block");
error = -ENXIO;
FAIL("read something from serial eeprom, but it does not seem to be a valid bus info block");
}
}
......@@ -1876,29 +1881,55 @@ static int __devinit add_card(struct pci_dev *dev,
}
}
if (got_valid_bus_info_block) {
memcpy(lynx->config_rom+5,lynx_csr_rom+5,sizeof(lynx_csr_rom)-20);
} else {
PRINT(KERN_INFO, lynx->id, "since we did not get a bus info block from serial eeprom, we use a generic one with a hard coded GUID");
memcpy(lynx->config_rom,lynx_csr_rom,sizeof(lynx_csr_rom));
host->csr.guid_hi = be32_to_cpu(lynx->bus_info_block[3]);
host->csr.guid_lo = be32_to_cpu(lynx->bus_info_block[4]);
host->csr.cyc_clk_acc = (be32_to_cpu(lynx->bus_info_block[2]) >> 16) & 0xff;
host->csr.max_rec = (be32_to_cpu(lynx->bus_info_block[2]) >> 12) & 0xf;
if (!lynx->phyic.reg_1394a)
host->csr.lnk_spd = (get_phy_reg(lynx, 2) & 0xc0) >> 6;
else
host->csr.lnk_spd = be32_to_cpu(lynx->bus_info_block[2]) & 0x7;
/* Setup initial root directory entries */
root = host->csr.rom->root_kv;
vend_id = csr1212_new_immediate(CSR1212_KV_ID_VENDOR,
be32_to_cpu(lynx->bus_info_block[3]) >> 8);
text = csr1212_new_string_descriptor_leaf("Linux 1394 - PCI-Lynx");
if (!vend_id || !text) {
if (vend_id)
csr1212_release_keyval(vend_id);
if (text)
csr1212_release_keyval(text);
error = -ENOMEM;
FAIL("Failed to allocate memory for mandatory ConfigROM entries!");
}
ret = csr1212_associate_keyval(vend_id, text);
csr1212_release_keyval(text); /* no longer needed locally. */
if(ret != CSR1212_SUCCESS) {
csr1212_release_keyval(vend_id);
error = ret;
FAIL("Failed to associate text descriptor to vendor id");
}
ret = csr1212_attach_keyval_to_directory(root, vend_id);
csr1212_release_keyval(vend_id); /* no longer needed locally. */
if(ret != CSR1212_SUCCESS) {
error = ret;
FAIL("Failed to attach vendor id to root directory");
}
host->update_config_rom = 1;
hpsb_add_host(host);
lynx->state = is_host;
return 0;
return ret;
#undef FAIL
}
static size_t get_lynx_rom(struct hpsb_host *host, quadlet_t **ptr)
{
struct ti_lynx *lynx = host->hostdata;
*ptr = lynx->config_rom;
return sizeof(lynx_csr_rom);
}
static struct pci_device_id pci_table[] = {
{
.vendor = PCI_VENDOR_ID_TI,
......@@ -1919,7 +1950,7 @@ static struct pci_driver lynx_pci_driver = {
static struct hpsb_host_driver lynx_driver = {
.owner = THIS_MODULE,
.name = PCILYNX_DRIVER_NAME,
.get_rom = get_lynx_rom,
.set_hw_config_rom = NULL,
.transmit_packet = lynx_transmit,
.devctl = lynx_devctl,
.isoctl = NULL,
......
#ifndef __PCILYNX_H__
#define __PCILYNX_H__
#include <linux/config.h>
#define PCILYNX_DRIVER_NAME "pcilynx"
......@@ -50,7 +53,7 @@ struct ti_lynx {
void *local_rom;
void *local_ram;
void *aux_port;
quadlet_t config_rom[PCILYNX_CONFIG_ROM_LENGTH/4];
quadlet_t bus_info_block[5];
#ifdef CONFIG_IEEE1394_PCILYNX_PORTS
atomic_t aux_intr_seen;
......@@ -510,76 +513,4 @@ static inline void run_pcl(const struct ti_lynx *lynx, pcl_t pclid, int dmachan)
#define PCL_BIGENDIAN (1<<16)
#define PCL_ISOMODE (1<<12)
#define _(x) (__constant_cpu_to_be32(x))
static quadlet_t lynx_csr_rom[] = {
/* bus info block offset (hex) */
_(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 */
_(0x00095778), /* directory length, CRC 414 */
_(0x03080028), /* vendor ID (Texas Instr.) 418 */
_(0x81000008), /* offset to textual ID 41c */
_(0x0c000200), /* node capabilities 420 */
_(0x8d00000e), /* offset to unique ID 424 */
_(0xc7000010), /* offset to module independent info 428 */
_(0x04000000), /* module hardware version 42c */
_(0x81000014), /* offset to textual ID 430 */
_(0x09000000), /* node hardware version 434 */
_(0x81000018), /* offset to textual ID 438 */
/* module vendor ID textual */
_(0x00070812), /* CRC length, CRC 43c */
_(0x00000000), /* 440 */
_(0x00000000), /* 444 */
_(0x54455841), /* "Texas Instruments" 448 */
_(0x5320494e), /* 44c */
_(0x53545255), /* 450 */
_(0x4d454e54), /* 454 */
_(0x53000000), /* 458 */
/* node unique ID leaf */
_(0x00022ead), /* CRC length, CRC 45c */
_(0x08002850), /* vendor ID, chip ID high 460 */
_(0x0000ffff), /* chip ID low 464 */
/* module dependent info */
_(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 */
_(0x000594df), /* CRC length, CRC 480 */
_(0x00000000), /* 484 */
_(0x00000000), /* 488 */
_(0x54534231), /* "TSB12LV21" 48c */
_(0x324c5632), /* 490 */
_(0x31000000), /* 494 */
/* part number */
_(0x00068405), /* CRC length, CRC 498 */
_(0x00000000), /* 49c */
_(0x00000000), /* 4a0 */
_(0x39383036), /* "9806000-0001" 4a4 */
_(0x3030302d), /* 4a8 */
_(0x30303031), /* 4ac */
_(0x20000001), /* 4b0 */
/* module hardware version textual */
_(0x00056501), /* CRC length, CRC 4b4 */
_(0x00000000), /* 4b8 */
_(0x00000000), /* 4bc */
_(0x5453424b), /* "TSBKPCITST" 4c0 */
_(0x50434954), /* 4c4 */
_(0x53540000), /* 4c8 */
/* node hardware version textual */
_(0x0005d805), /* CRC length, CRC 4d0 */
_(0x00000000), /* 4d4 */
_(0x00000000), /* 4d8 */
_(0x54534232), /* "TSB21LV03" 4dc */
_(0x314c5630), /* 4e0 */
_(0x33000000) /* 4e4 */
};
#undef _
#endif
......@@ -7,6 +7,8 @@
#define RAW1394_DEVICE_MAJOR 171
#define RAW1394_DEVICE_NAME "raw1394"
#define RAW1394_MAX_USER_CSR_DIRS 16
struct iso_block_store {
atomic_t refcount;
size_t data_size;
......@@ -45,6 +47,12 @@ struct file_info {
/* new rawiso API */
enum raw1394_iso_state iso_state;
struct hpsb_iso *iso_handle;
/* User space's CSR1212 dynamic ConfigROM directories */
struct csr1212_keyval *csr1212_dirs[RAW1394_MAX_USER_CSR_DIRS];
/* Legacy ConfigROM update flag */
u8 cfgrom_upd;
};
struct arm_addr {
......
......@@ -43,6 +43,7 @@
#include <asm/atomic.h>
#include <linux/devfs_fs_kernel.h>
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ieee1394_core.h"
......@@ -214,15 +215,11 @@ static void add_host(struct hpsb_host *host)
static struct host_info *find_host_info(struct hpsb_host *host)
{
struct list_head *lh;
struct host_info *hi;
list_for_each(lh, &host_info_list) {
hi = list_entry(lh, struct host_info, list);
if (hi->host == host) {
list_for_each_entry(hi, &host_info_list, list)
if (hi->host == host)
return hi;
}
}
return NULL;
}
......@@ -261,7 +258,6 @@ static void remove_host(struct hpsb_host *host)
static void host_reset(struct hpsb_host *host)
{
unsigned long flags;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi;
struct pending_request *req;
......@@ -270,8 +266,7 @@ static void host_reset(struct hpsb_host *host)
hi = find_host_info(host);
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
if (fi->notification == RAW1394_NOTIFY_ON) {
req = __alloc_pending_request(SLAB_ATOMIC);
......@@ -298,10 +293,9 @@ static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data,
size_t length)
{
unsigned long flags;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi;
struct pending_request *req;
struct pending_request *req, *req_next;
struct iso_block_store *ibs = NULL;
LIST_HEAD(reqs);
......@@ -314,12 +308,9 @@ static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data,
hi = find_host_info(host);
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
if (!(fi->listen_channels & (1ULL << channel))) {
list_for_each_entry(fi, &hi->file_info_list, list) {
if (!(fi->listen_channels & (1ULL << channel)))
continue;
}
req = __alloc_pending_request(SLAB_ATOMIC);
if (!req) break;
......@@ -354,23 +345,17 @@ static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data,
}
spin_unlock_irqrestore(&host_info_lock, flags);
lh = reqs.next;
while (lh != &reqs) {
req = list_entry(lh, struct pending_request, list);
lh = lh->next;
list_for_each_entry_safe(req, req_next, &reqs, list)
queue_complete_req(req);
}
}
static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
int cts, u8 *data, size_t length)
{
unsigned long flags;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi;
struct pending_request *req;
struct pending_request *req, *req_next;
struct iso_block_store *ibs = NULL;
LIST_HEAD(reqs);
......@@ -383,12 +368,9 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
hi = find_host_info(host);
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
if (!fi->fcp_buffer) {
list_for_each_entry(fi, &hi->file_info_list, list) {
if (!fi->fcp_buffer)
continue;
}
req = __alloc_pending_request(SLAB_ATOMIC);
if (!req) break;
......@@ -423,13 +405,8 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction,
}
spin_unlock_irqrestore(&host_info_lock, flags);
lh = reqs.next;
while (lh != &reqs) {
req = list_entry(lh, struct pending_request, list);
lh = lh->next;
list_for_each_entry_safe(req, req_next, &reqs, list)
queue_complete_req(req);
}
}
......@@ -505,7 +482,6 @@ static int state_opened(struct file_info *fi, struct pending_request *req)
static int state_initialized(struct file_info *fi, struct pending_request *req)
{
struct list_head *lh;
struct host_info *hi;
struct raw1394_khost_list *khl;
......@@ -527,12 +503,9 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
req->req.misc = host_count;
req->data = (quadlet_t *)khl;
list_for_each(lh, &host_info_list) {
hi = list_entry(lh, struct host_info, list);
list_for_each_entry(hi, &host_info_list, list) {
khl->nodes = hi->host->node_count;
strcpy(khl->name, hi->host->driver->name);
khl++;
}
}
......@@ -550,23 +523,17 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
break;
case RAW1394_REQ_SET_CARD:
lh = NULL;
spin_lock_irq(&host_info_lock);
if (req->req.misc < host_count) {
lh = host_info_list.next;
while (req->req.misc--) {
lh = lh->next;
list_for_each_entry(hi, &host_info_list, list) {
if (!req->req.misc--)
break;
}
hi = list_entry(lh, struct host_info, list);
get_device(&hi->host->device); // XXX Need to handle failure case
list_add_tail(&fi->list, &hi->file_info_list);
fi->host = hi->host;
fi->state = connected;
}
spin_unlock_irq(&host_info_lock);
if (lh != NULL) {
req->req.error = RAW1394_ERROR_NONE;
req->req.generation = get_hpsb_generation(fi->host);
req->req.misc = (fi->host->node_id << 16)
......@@ -577,6 +544,7 @@ static int state_initialized(struct file_info *fi, struct pending_request *req)
} else {
req->req.error = RAW1394_ERROR_INVALID_ARG;
}
spin_unlock_irq(&host_info_lock);
req->req.length = 0;
break;
......@@ -898,7 +866,6 @@ static int arm_read (struct hpsb_host *host, int nodeid, quadlet_t *buffer,
u64 addr, size_t length, u16 flags)
{
struct pending_request *req;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi = NULL;
struct list_head *entry;
......@@ -915,8 +882,7 @@ static int arm_read (struct hpsb_host *host, int nodeid, quadlet_t *buffer,
spin_lock(&host_info_lock);
hi = find_host_info(host); /* search address-entry */
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
entry = fi->addr_list.next;
while (entry != &(fi->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1034,7 +1000,6 @@ static int arm_write (struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 flags)
{
struct pending_request *req;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi = NULL;
struct list_head *entry;
......@@ -1051,8 +1016,7 @@ static int arm_write (struct hpsb_host *host, int nodeid, int destid,
spin_lock(&host_info_lock);
hi = find_host_info(host); /* search address-entry */
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
entry = fi->addr_list.next;
while (entry != &(fi->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1161,7 +1125,6 @@ static int arm_lock (struct hpsb_host *host, int nodeid, quadlet_t *store,
u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags)
{
struct pending_request *req;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi = NULL;
struct list_head *entry;
......@@ -1187,8 +1150,7 @@ static int arm_lock (struct hpsb_host *host, int nodeid, quadlet_t *store,
spin_lock(&host_info_lock);
hi = find_host_info(host); /* search address-entry */
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
entry = fi->addr_list.next;
while (entry != &(fi->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1359,7 +1321,6 @@ static int arm_lock64 (struct hpsb_host *host, int nodeid, octlet_t *store,
u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags)
{
struct pending_request *req;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi = NULL;
struct list_head *entry;
......@@ -1394,8 +1355,7 @@ static int arm_lock64 (struct hpsb_host *host, int nodeid, octlet_t *store,
spin_lock(&host_info_lock);
hi = find_host_info(host); /* search addressentry in file_info's for host */
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
entry = fi->addr_list.next;
while (entry != &(fi->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1566,7 +1526,6 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
{
int retval;
struct arm_addr *addr;
struct list_head *lh, *lh_1, *lh_2;
struct host_info *hi;
struct file_info *fi_hlp = NULL;
struct list_head *entry;
......@@ -1630,8 +1589,7 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
same_host = 0;
another_host = 0;
/* same host with address-entry containing same addressrange ? */
list_for_each(lh, &hi->file_info_list) {
fi_hlp = list_entry(lh, struct file_info, list);
list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
entry = fi_hlp->addr_list.next;
while (entry != &(fi_hlp->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1656,11 +1614,9 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
return (-EALREADY);
}
/* another host with valid address-entry containing same addressrange */
list_for_each(lh_1, &host_info_list) {
hi = list_entry(lh_1, struct host_info, list);
list_for_each_entry(hi, &host_info_list, list) {
if (hi->host != fi->host) {
list_for_each(lh_2, &hi->file_info_list) {
fi_hlp = list_entry(lh_2, struct file_info, list);
list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
entry = fi_hlp->addr_list.next;
while (entry != &(fi_hlp->addr_list)) {
arm_addr = list_entry(entry, struct arm_addr, addr_list);
......@@ -1719,7 +1675,6 @@ static int arm_unregister(struct file_info *fi, struct pending_request *req)
int retval = 0;
struct list_head *entry;
struct arm_addr *addr = NULL;
struct list_head *lh_1, *lh_2;
struct host_info *hi;
struct file_info *fi_hlp = NULL;
struct arm_addr *arm_addr = NULL;
......@@ -1750,11 +1705,9 @@ static int arm_unregister(struct file_info *fi, struct pending_request *req)
another_host = 0;
/* another host with valid address-entry containing
same addressrange */
list_for_each(lh_1, &host_info_list) {
hi = list_entry(lh_1, struct host_info, list);
list_for_each_entry(hi, &host_info_list, list) {
if (hi->host != fi->host) {
list_for_each(lh_2, &hi->file_info_list) {
fi_hlp = list_entry(lh_2, struct file_info, list);
list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
entry = fi_hlp->addr_list.next;
while (entry != &(fi_hlp->addr_list)) {
arm_addr = list_entry(entry,
......@@ -1822,9 +1775,9 @@ static int arm_get_buf(struct file_info *fi, struct pending_request *req)
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
DBGMSG("arm_get_buf copy_to_user( %08X, %08X, %u )",
DBGMSG("arm_get_buf copy_to_user( %08X, %p, %u )",
(u32) req->req.recvb,
(u32) (arm_addr->addr_space_buffer+offset),
arm_addr->addr_space_buffer+offset,
(u32) req->req.length);
if (copy_to_user(int2ptr(req->req.recvb), arm_addr->addr_space_buffer+offset, req->req.length)) {
......@@ -1833,7 +1786,10 @@ static int arm_get_buf(struct file_info *fi, struct pending_request *req)
}
spin_unlock_irqrestore(&host_info_lock, flags);
free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
/* We have to free the request, because we
* queue no response, and therefore nobody
* will free it. */
free_pending_request(req);
return sizeof(struct raw1394_request);
} else {
DBGMSG("arm_get_buf request exceeded mapping");
......@@ -1873,8 +1829,8 @@ static int arm_set_buf(struct file_info *fi, struct pending_request *req)
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
DBGMSG("arm_set_buf copy_from_user( %08X, %08X, %u )",
(u32) (arm_addr->addr_space_buffer+offset),
DBGMSG("arm_set_buf copy_from_user( %p, %08X, %u )",
arm_addr->addr_space_buffer+offset,
(u32) req->req.sendb,
(u32) req->req.length);
......@@ -1941,22 +1897,22 @@ static int write_phypacket(struct file_info *fi, struct pending_request *req)
static int get_config_rom(struct file_info *fi, struct pending_request *req)
{
size_t return_size;
unsigned char rom_version;
int ret=sizeof(struct raw1394_request);
quadlet_t *data = kmalloc(req->req.length, SLAB_KERNEL);
int status;
if (!data) return -ENOMEM;
status = hpsb_get_config_rom(fi->host, data,
req->req.length, &return_size, &rom_version);
status = csr1212_read(fi->host->csr.rom, CSR1212_CONFIG_ROM_SPACE_OFFSET,
data, req->req.length);
if (copy_to_user(int2ptr(req->req.recvb), data,
req->req.length))
ret = -EFAULT;
if (copy_to_user(int2ptr(req->req.tag), &return_size,
sizeof(return_size)))
if (copy_to_user(int2ptr(req->req.tag), &fi->host->csr.rom->cache_head->len,
sizeof(fi->host->csr.rom->cache_head->len)))
ret = -EFAULT;
if (copy_to_user(int2ptr(req->req.address), &rom_version,
sizeof(rom_version)))
if (copy_to_user(int2ptr(req->req.address), &fi->host->csr.generation,
sizeof(fi->host->csr.generation)))
ret = -EFAULT;
if (copy_to_user(int2ptr(req->req.sendb), &status,
sizeof(status)))
......@@ -1987,8 +1943,120 @@ static int update_config_rom(struct file_info *fi, struct pending_request *req)
kfree(data);
if (ret >= 0) {
free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
fi->cfgrom_upd = 1;
}
return ret;
}
static int modify_config_rom(struct file_info *fi, struct pending_request *req)
{
struct csr1212_keyval *kv;
struct csr1212_csr_rom_cache *cache;
struct csr1212_dentry *dentry;
u32 dr;
int ret = 0;
if (req->req.misc == ~0) {
if (req->req.length == 0) return -EINVAL;
/* Find an unused slot */
for (dr = 0; dr < RAW1394_MAX_USER_CSR_DIRS && fi->csr1212_dirs[dr]; dr++);
if (dr == RAW1394_MAX_USER_CSR_DIRS) return -ENOMEM;
fi->csr1212_dirs[dr] = csr1212_new_directory(CSR1212_KV_ID_VENDOR);
if (!fi->csr1212_dirs[dr]) return -ENOMEM;
} else {
dr = req->req.misc;
if (!fi->csr1212_dirs[dr]) return -EINVAL;
/* Delete old stuff */
for (dentry = fi->csr1212_dirs[dr]->value.directory.dentries_head;
dentry; dentry = dentry->next) {
csr1212_detach_keyval_from_directory(fi->host->csr.rom->root_kv,
dentry->kv);
}
if (req->req.length == 0) {
csr1212_release_keyval(fi->csr1212_dirs[dr]);
fi->csr1212_dirs[dr] = NULL;
hpsb_update_config_rom_image(fi->host);
free_pending_request(req);
return sizeof(struct raw1394_request);
}
}
cache = csr1212_rom_cache_malloc(0, req->req.length);
if (!cache) {
csr1212_release_keyval(fi->csr1212_dirs[dr]);
fi->csr1212_dirs[dr] = NULL;
return -ENOMEM;
}
cache->filled_head = kmalloc(sizeof(struct csr1212_cache_region), GFP_KERNEL);
if (!cache->filled_head) {
csr1212_release_keyval(fi->csr1212_dirs[dr]);
fi->csr1212_dirs[dr] = NULL;
CSR1212_FREE(cache);
return -ENOMEM;
}
cache->filled_tail = cache->filled_head;
if (copy_from_user(cache->data, int2ptr(req->req.sendb),
req->req.length)) {
csr1212_release_keyval(fi->csr1212_dirs[dr]);
fi->csr1212_dirs[dr] = NULL;
CSR1212_FREE(cache);
ret= -EFAULT;
} else {
cache->len = req->req.length;
cache->filled_head->offset_start = 0;
cache->filled_head->offset_end = cache->size -1;
cache->layout_head = cache->layout_tail = fi->csr1212_dirs[dr];
ret = CSR1212_SUCCESS;
/* parse all the items */
for (kv = cache->layout_head; ret == CSR1212_SUCCESS && kv;
kv = kv->next) {
ret = csr1212_parse_keyval(kv, cache);
}
/* attach top level items to the root directory */
for (dentry = fi->csr1212_dirs[dr]->value.directory.dentries_head;
ret == CSR1212_SUCCESS && dentry; dentry = dentry->next) {
ret = csr1212_attach_keyval_to_directory(fi->host->csr.rom->root_kv,
dentry->kv);
}
if (ret == CSR1212_SUCCESS) {
ret = hpsb_update_config_rom_image(fi->host);
if (ret >= 0 && copy_to_user(int2ptr(req->req.recvb),
&dr, sizeof(dr))) {
ret = -ENOMEM;
}
}
}
kfree(cache->filled_head);
kfree(cache);
if (ret >= 0) {
/* we have to free the request, because we queue no response,
* and therefore nobody will free it */
free_pending_request(req);
return sizeof(struct raw1394_request);
} else {
for (dentry = fi->csr1212_dirs[dr]->value.directory.dentries_head;
dentry; dentry = dentry->next) {
csr1212_detach_keyval_from_directory(fi->host->csr.rom->root_kv,
dentry->kv);
}
csr1212_release_keyval(fi->csr1212_dirs[dr]);
fi->csr1212_dirs[dr] = NULL;
return ret;
}
}
static int state_connected(struct file_info *fi, struct pending_request *req)
......@@ -2049,6 +2117,9 @@ static int state_connected(struct file_info *fi, struct pending_request *req)
case RAW1394_REQ_UPDATE_ROM:
return update_config_rom(fi, req);
case RAW1394_REQ_MODIFY_ROM:
return modify_config_rom(fi, req);
}
if (req->req.generation != get_hpsb_generation(fi->host)) {
......@@ -2125,15 +2196,11 @@ static ssize_t raw1394_write(struct file *file, const char *buffer, size_t count
* completion queue (reqlists_lock must be taken) */
static inline int __rawiso_event_in_queue(struct file_info *fi)
{
struct list_head *lh;
struct pending_request *req;
list_for_each(lh, &fi->req_complete) {
req = list_entry(lh, struct pending_request, list);
if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY) {
list_for_each_entry(req, &fi->req_complete, list)
if (req->req.type == RAW1394_REQ_RAWISO_ACTIVITY)
return 1;
}
}
return 0;
}
......@@ -2167,15 +2234,14 @@ static void queue_rawiso_event(struct file_info *fi)
static void rawiso_activity_cb(struct hpsb_iso *iso)
{
unsigned long flags;
struct list_head *lh;
struct host_info *hi;
struct file_info *fi;
spin_lock_irqsave(&host_info_lock, flags);
hi = find_host_info(iso->host);
if (hi != NULL) {
list_for_each(lh, &hi->file_info_list) {
struct file_info *fi = list_entry(lh, struct file_info, list);
list_for_each_entry(fi, &hi->file_info_list, list) {
if (fi->iso_handle == iso)
queue_rawiso_event(fi);
}
......@@ -2495,11 +2561,11 @@ static int raw1394_release(struct inode *inode, struct file *file)
int retval = 0;
struct list_head *entry;
struct arm_addr *addr = NULL;
struct list_head *lh_1, *lh_2;
struct host_info *hi;
struct file_info *fi_hlp = NULL;
struct arm_addr *arm_addr = NULL;
int another_host;
int csr_mod = 0;
if (fi->iso_state != RAW1394_ISO_INACTIVE)
raw1394_iso_shutdown(fi);
......@@ -2524,11 +2590,9 @@ static int raw1394_release(struct inode *inode, struct file *file)
addr = list_entry(lh, struct arm_addr, addr_list);
/* another host with valid address-entry containing
same addressrange? */
list_for_each(lh_1, &host_info_list) {
hi = list_entry(lh_1, struct host_info, list);
list_for_each_entry(hi, &host_info_list, list) {
if (hi->host != fi->host) {
list_for_each(lh_2, &hi->file_info_list) {
fi_hlp = list_entry(lh_2, struct file_info, list);
list_for_each_entry(fi_hlp, &hi->file_info_list, list) {
entry = fi_hlp->addr_list.next;
while (entry != &(fi_hlp->addr_list)) {
arm_addr = list_entry(entry,
......@@ -2587,6 +2651,22 @@ static int raw1394_release(struct inode *inode, struct file *file)
if (!done) down_interruptible(&fi->complete_sem);
}
/* Remove any sub-trees left by user space programs */
for (i = 0; i < RAW1394_MAX_USER_CSR_DIRS; i++) {
struct csr1212_dentry *dentry;
if (!fi->csr1212_dirs[i]) continue;
for (dentry = fi->csr1212_dirs[i]->value.directory.dentries_head;
dentry; dentry = dentry->next) {
csr1212_detach_keyval_from_directory(fi->host->csr.rom->root_kv, dentry->kv);
}
csr1212_release_keyval(fi->csr1212_dirs[i]);
fi->csr1212_dirs[i] = NULL;
csr_mod = 1;
}
if ((csr_mod || fi->cfgrom_upd) && hpsb_update_config_rom_image(fi->host) < 0)
HPSB_ERR("Failed to generate Configuration ROM image for host %d", fi->host->id);
if (fi->state == connected) {
spin_lock_irq(&host_info_lock);
list_del(&fi->list);
......
......@@ -27,6 +27,7 @@
#define RAW1394_REQ_GET_ROM 203
#define RAW1394_REQ_UPDATE_ROM 204
#define RAW1394_REQ_ECHO 205
#define RAW1394_REQ_MODIFY_ROM 206
#define RAW1394_REQ_ARM_REGISTER 300
#define RAW1394_REQ_ARM_UNREGISTER 301
......
......@@ -67,6 +67,7 @@
#include "../scsi/scsi.h"
#include "../scsi/hosts.h"
#include "csr1212.h"
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "ieee1394_core.h"
......@@ -77,7 +78,7 @@
#include "sbp2.h"
static char version[] __devinitdata =
"$Rev: 1096 $ Ben Collins <bcollins@debian.org>";
"$Rev: 1144 $ Ben Collins <bcollins@debian.org>";
/*
* Module load parameter definitions
......@@ -230,9 +231,13 @@ static Scsi_Host_Template scsi_driver_template;
const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC };
static void sbp2_remove_host(struct hpsb_host *host);
static void sbp2_host_reset(struct hpsb_host *host);
static struct hpsb_highlevel sbp2_highlevel = {
.name = SBP2_DEVICE_NAME,
.remove_host = sbp2_remove_host,
.host_reset = sbp2_host_reset,
};
static struct hpsb_address_ops sbp2_ops = {
......@@ -401,7 +406,7 @@ static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i
unsigned long flags, orbs;
struct sbp2_command_info *command;
orbs = serialize_io ? 2 : SBP2_MAX_COMMAND_ORBS;
orbs = serialize_io ? 2 : SBP2_MAX_CMDS;
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
for (i = 0; i < orbs; i++) {
......@@ -468,14 +473,12 @@ static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_
static struct sbp2_command_info *sbp2util_find_command_for_orb(
struct scsi_id_instance_data *scsi_id, dma_addr_t orb)
{
struct list_head *lh;
struct sbp2_command_info *command;
unsigned long flags;
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
list_for_each(lh, &scsi_id->sbp2_command_orb_inuse) {
command = list_entry(lh, struct sbp2_command_info, list);
list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) {
if (command->command_orb_dma == orb) {
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
return (command);
......@@ -495,14 +498,12 @@ static struct sbp2_command_info *sbp2util_find_command_for_orb(
*/
static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt)
{
struct list_head *lh;
struct sbp2_command_info *command;
unsigned long flags;
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
list_for_each(lh, &scsi_id->sbp2_command_orb_inuse) {
command = list_entry(lh, struct sbp2_command_info, list);
list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) {
if (command->Current_SCpnt == SCpnt) {
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
return (command);
......@@ -543,15 +544,17 @@ static struct sbp2_command_info *sbp2util_allocate_command_orb(
/* Free our DMA's */
static void sbp2util_free_command_dma(struct sbp2_command_info *command)
{
struct scsi_id_instance_data *scsi_id =
(struct scsi_id_instance_data *)command->Current_SCpnt->device->host->hostdata[0];
struct hpsb_host *host;
host = hpsb_get_host_bykey(&sbp2_highlevel,
(unsigned long)command->Current_SCpnt->device->host);
if (!host) {
printk(KERN_ERR "%s: host == NULL\n", __FUNCTION__);
if (!scsi_id) {
printk(KERN_ERR "%s: scsi_id == NULL\n", __FUNCTION__);
return;
}
host = scsi_id->ud->ne->host;
if (command->cmd_dma) {
if (command->dma_type == CMD_DMA_SINGLE) {
pci_unmap_single(host->pdev, command->cmd_dma,
......@@ -593,13 +596,14 @@ static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_i
/*********************************************
* IEEE-1394 core driver stack related section
*********************************************/
static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud);
static int sbp2_probe(struct device *dev)
{
struct unit_directory *ud;
struct sbp2scsi_host_info *hi;
struct scsi_id_instance_data *scsi_id;
SBP2_DEBUG(__FUNCTION__);
SBP2_DEBUG("sbp2_probe");
ud = container_of(dev, struct unit_directory, device);
......@@ -608,55 +612,39 @@ static int sbp2_probe(struct device *dev)
if (ud->flags & UNIT_DIRECTORY_HAS_LUN_DIRECTORY)
return -ENODEV;
/* This will only add it if it doesn't exist */
hi = sbp2_add_host(ud->ne->host);
scsi_id = sbp2_alloc_device(ud);
if (!hi)
return -ENODEV;
if (!scsi_id)
return -ENOMEM;
return sbp2_start_ud(hi, ud);
sbp2_parse_unit_directory(scsi_id, ud);
return sbp2_start_device(scsi_id);
}
static int sbp2_remove(struct device *dev)
{
struct scsi_id_group *scsi_group;
struct list_head *lh, *next;
struct unit_directory *ud;
struct scsi_id_instance_data *scsi_id;
SBP2_DEBUG(__FUNCTION__);
SBP2_DEBUG("sbp2_remove");
ud = container_of(dev, struct unit_directory, device);
scsi_group = ud->device.driver_data;
ud->device.driver_data = NULL;
list_for_each_safe (lh, next, &scsi_group->scsi_id_list) {
scsi_id = list_entry(lh, struct scsi_id_instance_data, list);
scsi_id = ud->device.driver_data;
if (scsi_id != NULL) {
sbp2_logout_device(scsi_id);
sbp2_remove_device(scsi_id);
}
}
kfree(scsi_group);
return 0;
}
static void sbp2_update(struct unit_directory *ud)
{
struct sbp2scsi_host_info *hi;
struct scsi_id_group *scsi_group = ud->device.driver_data;
struct list_head *lh, *next;
struct scsi_id_instance_data *scsi_id;
unsigned long flags;
struct scsi_id_instance_data *scsi_id = ud->device.driver_data;
struct sbp2scsi_host_info *hi = scsi_id->hi;
SBP2_DEBUG("sbp2_update");
list_for_each_safe (lh, next, &scsi_group->scsi_id_list) {
scsi_id = list_entry(lh, struct scsi_id_instance_data, list);
hi = scsi_id->hi;
if (sbp2_reconnect_device(scsi_id)) {
......@@ -665,6 +653,8 @@ static void sbp2_update(struct unit_directory *ud)
* Ok, reconnect has failed. Perhaps we didn't
* reconnect fast enough. Try doing a regular login.
*/
sbp2_logout_device(scsi_id);
if (sbp2_login_device(scsi_id)) {
/* Login failed too, just remove the device. */
SBP2_ERR("sbp2_reconnect_device failed!");
......@@ -685,130 +675,121 @@ static void sbp2_update(struct unit_directory *ud)
/* Complete any pending commands with busy (so they get
* retried) and remove them from our queue
*/
spin_lock_irqsave(&hi->sbp2_command_lock, flags);
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
}
}
/* This functions is called by the sbp2_probe, for each new device. If the
* host_info already exists, it will return it. If not, it allocated a new
* host_info entry and a corresponding scsi_host. */
static struct sbp2scsi_host_info *sbp2_add_host(struct hpsb_host *host)
/* This functions is called by the sbp2_probe, for each new device. We now
* allocate one scsi host for each scsi_id (unit directory). */
static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud)
{
struct sbp2scsi_host_info *hi;
struct Scsi_Host *scsi_host;
struct Scsi_Host *scsi_host = NULL;
struct scsi_id_instance_data *scsi_id = NULL;
SBP2_DEBUG("sbp2_add_host");
SBP2_DEBUG("sbp2_alloc_device");
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
if (hi)
return hi;
scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL);
if (!scsi_id) {
SBP2_ERR("failed to create scsi_id");
goto failed_alloc;
}
memset(scsi_id, 0, sizeof(*scsi_id));
/* Register our host with the SCSI stack. */
scsi_host = scsi_host_alloc(&scsi_driver_template, 0);
if (!scsi_host) {
SBP2_ERR("failed to register scsi host");
return NULL;
scsi_id->ne = ud->ne;
scsi_id->ud = ud;
scsi_id->speed_code = IEEE1394_SPEED_100;
scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
atomic_set(&scsi_id->sbp2_login_complete, 0);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed);
INIT_LIST_HEAD(&scsi_id->scsi_list);
scsi_id->sbp2_command_orb_lock = SPIN_LOCK_UNLOCKED;
scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED;
ud->device.driver_data = scsi_id;
hi = hpsb_get_hostinfo(&sbp2_highlevel, ud->ne->host);
if (!hi) {
hi = hpsb_create_hostinfo(&sbp2_highlevel, ud->ne->host, sizeof(*hi));
if (!hi) {
SBP2_ERR("failed to allocate hostinfo");
goto failed_alloc;
}
SBP2_DEBUG("sbp2_alloc_device: allocated hostinfo");
hi->host = ud->ne->host;
INIT_LIST_HEAD(&hi->scsi_ids);
/* Register our sbp2 status address space... */
hpsb_register_addrspace(&sbp2_highlevel, host, &sbp2_ops,
hpsb_register_addrspace(&sbp2_highlevel, ud->ne->host, &sbp2_ops,
SBP2_STATUS_FIFO_ADDRESS,
SBP2_STATUS_FIFO_ADDRESS +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2SCSI_MAX_SCSI_IDS+1));
/* Handle data movement if physical dma is not enabled/supported
* on host controller */
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(SBP2_MAX_UDS_PER_NODE+1));
#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
hpsb_register_addrspace(&sbp2_highlevel, host, &sbp2_physdma_ops, 0x0ULL, 0xfffffffcULL);
/* Handle data movement if physical dma is not
* enabled/supportedon host controller */
hpsb_register_addrspace(&sbp2_highlevel, host, &sbp2_physdma_ops,
0x0ULL, 0xfffffffcULL);
#endif
}
hi = hpsb_create_hostinfo(&sbp2_highlevel, host, sizeof(*hi));
if (!hi) {
SBP2_ERR("failed to allocate hostinfo");
scsi_host_put(hi->scsi_host);
scsi_id->hi = hi;
list_add_tail(&scsi_id->scsi_list, &hi->scsi_ids);
/* Register our host with the SCSI stack. */
scsi_host = scsi_host_alloc(&scsi_driver_template, 0);
if (!scsi_host) {
SBP2_ERR("failed to register scsi host");
goto failed_alloc;
}
hpsb_set_hostinfo_key(&sbp2_highlevel, host, (unsigned long)scsi_host);
scsi_host->hostdata[0] = (unsigned long)scsi_id;
hi->scsi_host = scsi_host;
hi->host = host;
hi->sbp2_command_lock = SPIN_LOCK_UNLOCKED;
hi->scsi_host->max_id = SBP2SCSI_MAX_SCSI_IDS;
if (!scsi_add_host(scsi_host, &ud->device)) {
scsi_id->scsi_host = scsi_host;
return scsi_id;
}
/* XXX We need a device to pass here as the scsi-host class. Can't
* use the PCI device, since it is already bound to the ieee1394
* host. Can't use the fw-host device since it is multi-class
* enabled (scsi-host uses classdata member of the device). */
if (scsi_add_host(hi->scsi_host, NULL)) {
SBP2_ERR("failed to add scsi host");
scsi_host_put(hi->scsi_host);
hpsb_destroy_hostinfo(&sbp2_highlevel, host);
return NULL;
}
scsi_host_put(scsi_host);
return hi;
failed_alloc:
sbp2_remove_device(scsi_id);
return NULL;
}
/*
* This function is called when a host is removed.
*/
static void sbp2_remove_host(struct hpsb_host *host)
static void sbp2_host_reset(struct hpsb_host *host)
{
struct sbp2scsi_host_info *hi;
SBP2_DEBUG("sbp2_remove_host");
struct scsi_id_instance_data *scsi_id;
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
if (hi) {
scsi_remove_host(hi->scsi_host);
scsi_host_put(hi->scsi_host);
list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
scsi_block_requests(scsi_id->scsi_host);
}
}
static int sbp2_start_ud(struct sbp2scsi_host_info *hi, struct unit_directory *ud)
{
struct scsi_id_instance_data *scsi_id;
struct scsi_id_group *scsi_group;
struct list_head *lh, *next;
SBP2_DEBUG("sbp2_start_ud");
scsi_group = kmalloc(sizeof(*scsi_group), GFP_KERNEL);
if (!scsi_group) {
SBP2_ERR ("Could not allocate memory for scsi_group");
return -ENOMEM;
}
static void sbp2_remove_host(struct hpsb_host *host)
{
struct sbp2scsi_host_info *hi;
INIT_LIST_HEAD(&scsi_group->scsi_id_list);
ud->device.driver_data = scsi_group;
sbp2_parse_unit_directory(scsi_group, ud);
SBP2_DEBUG("sbp2_remove_host");
list_for_each_safe (lh, next, &scsi_group->scsi_id_list) {
scsi_id = list_entry(lh, struct scsi_id_instance_data, list);
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
scsi_id->ne = ud->ne;
scsi_id->hi = hi;
scsi_id->speed_code = IEEE1394_SPEED_100;
scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
atomic_set(&scsi_id->sbp2_login_complete, 0);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed);
scsi_id->sbp2_command_orb_lock = SPIN_LOCK_UNLOCKED;
if (hi) {
struct scsi_id_instance_data *scsi_id;
sbp2_start_device(scsi_id);
list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list) {
down_write(&scsi_id->ud->device.bus->subsys.rwsem);
device_release_driver(&scsi_id->ud->device);
up_write(&scsi_id->ud->device.bus->subsys.rwsem);
}
/* Check to see if any of our devices survived the ordeal */
if (list_empty(&scsi_group->scsi_id_list)) {
kfree(scsi_group);
return -ENODEV;
}
return 0;
}
......@@ -820,7 +801,6 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
{
struct sbp2scsi_host_info *hi = scsi_id->hi;
struct scsi_device *sdev;
int i;
SBP2_DEBUG("sbp2_start_device");
......@@ -912,7 +892,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
kfree(scsi_id);
list_del(&scsi_id->list);
list_del(&scsi_id->scsi_list);
SBP2_ERR ("Could not allocate memory for scsi_id");
......@@ -920,17 +900,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
}
SBP2_DMA_ALLOC("consistent DMA region for login ORB");
/*
* Find an empty spot to stick our scsi id instance data.
*/
for (i = 0; i < hi->scsi_host->max_id; i++) {
if (!hi->scsi_id[i]) {
hi->scsi_id[i] = scsi_id;
scsi_id->id = i;
SBP2_DEBUG("New SBP-2 device inserted, SCSI ID = %x", (unsigned int) i);
break;
}
}
SBP2_DEBUG("New SBP-2 device inserted, SCSI ID = %x", scsi_id->ud->id);
/*
* Create our command orb pool
......@@ -941,15 +911,6 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
return -ENOMEM;
}
/*
* Make sure we are not out of space
*/
if (i == hi->scsi_host->max_id) {
SBP2_ERR("No slots left for SBP-2 device");
sbp2_remove_device(scsi_id);
return -EBUSY;
}
/* Schedule a timeout here. The reason is that we may be so close
* to a bus reset, that the device is not available for logins.
* This can happen when the bus reset is caused by the host
......@@ -985,15 +946,12 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
sbp2_max_speed_and_size(scsi_id);
/* Add this device to the scsi layer now */
sdev = scsi_add_device(hi->scsi_host, 0, scsi_id->id, 0);
sdev = scsi_add_device(scsi_id->scsi_host, 0, scsi_id->ud->id, 0);
if (IS_ERR(sdev)) {
SBP2_ERR("scsi_add_device failed");
return PTR_ERR(sdev);
}
sdev->hostdata = scsi_id;
scsi_id->sdev = sdev;
return 0;
}
......@@ -1002,22 +960,24 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
*/
static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
{
struct sbp2scsi_host_info *hi = scsi_id->hi;
struct sbp2scsi_host_info *hi;
SBP2_DEBUG("sbp2_remove_device");
/* Complete any pending commands with selection timeout */
sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT);
if (!scsi_id)
return;
hi = scsi_id->hi;
/* Remove it from the scsi layer now */
if (scsi_id->sdev) {
scsi_remove_device(scsi_id->sdev);
scsi_device_put(scsi_id->sdev);
/* This will remove our scsi device aswell */
if (scsi_id->scsi_host) {
scsi_remove_host(scsi_id->scsi_host);
scsi_host_put(scsi_id->scsi_host);
}
sbp2util_remove_command_orb_pool(scsi_id);
hi->scsi_id[scsi_id->id] = NULL;
list_del(&scsi_id->scsi_list);
if (scsi_id->login_response) {
pci_free_consistent(hi->host->pdev,
......@@ -1067,9 +1027,9 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
SBP2_DMA_FREE("single query logins data");
}
SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->id);
scsi_id->ud->device.driver_data = NULL;
list_del(&scsi_id->list);
SBP2_DEBUG("SBP-2 device removed, SCSI ID = %d", scsi_id->ud->id);
kfree(scsi_id);
}
......@@ -1157,7 +1117,7 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
SBP2_DEBUG("sbp2_query_logins: reserved_resp_length initialized");
scsi_id->query_logins_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id);
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id);
scsi_id->query_logins_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) |
SBP2_STATUS_FIFO_ADDRESS_HI);
SBP2_DEBUG("sbp2_query_logins: status FIFO initialized");
......@@ -1271,7 +1231,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
SBP2_DEBUG("sbp2_login_device: passwd_resp_lengths initialized");
scsi_id->login_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id);
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id);
scsi_id->login_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) |
SBP2_STATUS_FIFO_ADDRESS_HI);
SBP2_DEBUG("sbp2_login_device: status FIFO initialized");
......@@ -1387,7 +1347,7 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id)
scsi_id->logout_orb->reserved5 = 0x0;
scsi_id->logout_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id);
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id);
scsi_id->logout_orb->status_FIFO_hi = (ORB_SET_NODE_ID(hi->host->node_id) |
SBP2_STATUS_FIFO_ADDRESS_HI);
......@@ -1447,7 +1407,7 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
scsi_id->reconnect_orb->reserved5 = 0x0;
scsi_id->reconnect_orb->status_FIFO_lo = SBP2_STATUS_FIFO_ADDRESS_LO +
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->id);
SBP2_STATUS_FIFO_ENTRY_TO_OFFSET(scsi_id->ud->id);
scsi_id->reconnect_orb->status_FIFO_hi =
(ORB_SET_NODE_ID(hi->host->node_id) | SBP2_STATUS_FIFO_ADDRESS_HI);
......@@ -1530,16 +1490,17 @@ static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id)
return(0);
}
/*
* This function is called to parse sbp2 device's config rom unit
* directory. Used to determine things like sbp2 management agent offset,
* and command set used (SCSI or RBC).
*/
static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
struct unit_directory *ud)
{
struct scsi_id_instance_data *scsi_id;
struct list_head *lh;
struct csr1212_keyval *kv;
struct csr1212_dentry *dentry;
u64 management_agent_addr;
u32 command_set_spec_id, command_set, unit_characteristics,
firmware_revision, workarounds;
......@@ -1554,29 +1515,31 @@ static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
firmware_revision = 0x0;
/* Handle different fields in the unit directory, based on keys */
for (i = 0; i < ud->length; i++) {
switch (CONFIG_ROM_KEY(ud->quadlets[i])) {
case SBP2_CSR_OFFSET_KEY:
csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) {
switch (kv->key.id) {
case CSR1212_KV_ID_DEPENDENT_INFO:
if (kv->key.type == CSR1212_KV_TYPE_CSR_OFFSET) {
/* Save off the management agent address */
management_agent_addr =
CSR_REGISTER_BASE +
(CONFIG_ROM_VALUE(ud->quadlets[i]) << 2);
CSR1212_REGISTER_SPACE_BASE +
(kv->value.csr_offset << 2);
SBP2_DEBUG("sbp2_management_agent_addr = %x",
(unsigned int) management_agent_addr);
} else
scsi_id->sbp2_device_type_and_lun = kv->value.immediate;
break;
case SBP2_COMMAND_SET_SPEC_ID_KEY:
/* Command spec organization */
command_set_spec_id
= CONFIG_ROM_VALUE(ud->quadlets[i]);
command_set_spec_id = kv->value.immediate;
SBP2_DEBUG("sbp2_command_set_spec_id = %x",
(unsigned int) command_set_spec_id);
break;
case SBP2_COMMAND_SET_KEY:
/* Command set used by sbp2 device */
command_set = CONFIG_ROM_VALUE(ud->quadlets[i]);
command_set = kv->value.immediate;
SBP2_DEBUG("sbp2_command_set = %x",
(unsigned int) command_set);
break;
......@@ -1586,35 +1549,14 @@ static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
* Unit characterisitcs (orb related stuff
* that I'm not yet paying attention to)
*/
unit_characteristics
= CONFIG_ROM_VALUE(ud->quadlets[i]);
unit_characteristics = kv->value.immediate;
SBP2_DEBUG("sbp2_unit_characteristics = %x",
(unsigned int) unit_characteristics);
break;
case SBP2_DEVICE_TYPE_AND_LUN_KEY:
/*
* Device type and lun (used for
* detemining type of sbp2 device)
*/
scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL);
if (!scsi_id) {
SBP2_ERR("Out of memory adding scsi_id, not all LUN's will be added");
break;
}
memset(scsi_id, 0, sizeof(*scsi_id));
scsi_id->sbp2_device_type_and_lun
= CONFIG_ROM_VALUE(ud->quadlets[i]);
SBP2_DEBUG("sbp2_device_type_and_lun = %x",
(unsigned int) scsi_id->sbp2_device_type_and_lun);
list_add_tail(&scsi_id->list, &scsi_group->scsi_id_list);
break;
case SBP2_FIRMWARE_REVISION_KEY:
/* Firmware revision */
firmware_revision
= CONFIG_ROM_VALUE(ud->quadlets[i]);
firmware_revision = kv->value.immediate;
if (force_inquiry_hack)
SBP2_INFO("sbp2_firmware_revision = %x",
(unsigned int) firmware_revision);
......@@ -1664,30 +1606,12 @@ static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
}
/* If this is a logical unit directory entry, process the parent
* to get the common values. */
* to get the values. */
if (ud->flags & UNIT_DIRECTORY_LUN_DIRECTORY) {
struct unit_directory *parent_ud =
container_of(ud->device.parent, struct unit_directory, device);
sbp2_parse_unit_directory(scsi_group, parent_ud);
sbp2_parse_unit_directory(scsi_id, parent_ud);
} else {
/* If our list is empty, add a base scsi_id (happens in a normal
* case where there is no logical_unit_number entry */
if (list_empty(&scsi_group->scsi_id_list)) {
scsi_id = kmalloc(sizeof(*scsi_id), GFP_KERNEL);
if (!scsi_id) {
SBP2_ERR("Out of memory adding scsi_id");
return;
}
memset(scsi_id, 0, sizeof(*scsi_id));
scsi_id->sbp2_device_type_and_lun = SBP2_DEVICE_TYPE_LUN_UNINITIALIZED;
list_add_tail(&scsi_id->list, &scsi_group->scsi_id_list);
}
/* Update the generic fields in all the LUN's */
list_for_each (lh, &scsi_group->scsi_id_list) {
scsi_id = list_entry(lh, struct scsi_id_instance_data, list);
scsi_id->sbp2_management_agent_addr = management_agent_addr;
scsi_id->sbp2_command_set_spec_id = command_set_spec_id;
scsi_id->sbp2_command_set = command_set;
......@@ -1695,7 +1619,6 @@ static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
scsi_id->sbp2_firmware_revision = firmware_revision;
scsi_id->workarounds = workarounds;
}
}
}
/*
......@@ -1727,7 +1650,7 @@ static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id)
/* Payload size is the lesser of what our speed supports and what
* our host supports. */
scsi_id->max_payload_size = min(sbp2_speedto_max_payload[scsi_id->speed_code],
(u8)(((be32_to_cpu(hi->host->csr.rom[2]) >> 12) & 0xf) - 1));
(u8)(hi->host->csr.max_rec - 1));
SBP2_ERR("Node " NODE_BUS_FMT ": Max speed [%s] - Max payload [%u]",
NODE_BUS_ARGS(hi->host, scsi_id->ne->nodeid),
......@@ -2392,10 +2315,9 @@ static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id,
static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 fl)
{
struct sbp2scsi_host_info *hi = NULL;
struct scsi_id_instance_data *scsi_id = NULL;
struct sbp2scsi_host_info *hi;
struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp;
u32 id;
unsigned long flags;
Scsi_Cmnd *SCpnt = NULL;
u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
struct sbp2_command_info *command;
......@@ -2416,18 +2338,20 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
return(RCODE_ADDRESS_ERROR);
}
spin_lock_irqsave(&hi->sbp2_command_lock, flags);
/*
* Find our scsi_id structure by looking at the status fifo address written to by
* the sbp2 device.
*/
id = SBP2_STATUS_FIFO_OFFSET_TO_ENTRY((u32)(addr - SBP2_STATUS_FIFO_ADDRESS));
scsi_id = hi->scsi_id[id];
list_for_each_entry(scsi_id_tmp, &hi->scsi_ids, scsi_list) {
if (scsi_id_tmp->ne->nodeid == nodeid && scsi_id_tmp->ud->id == id) {
scsi_id = scsi_id_tmp;
break;
}
}
if (!scsi_id) {
SBP2_ERR("scsi_id is NULL - device is gone?");
spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
return(RCODE_ADDRESS_ERROR);
}
......@@ -2515,18 +2439,9 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
}
}
spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
if (SCpnt) {
/*
* Complete the SCSI command.
*
* Only do it after we've released the sbp2_command_lock,
* as it might otherwise deadlock with the
* io_request_lock (in sbp2scsi_queuecommand).
*/
/* Complete the SCSI command. */
SBP2_DEBUG("Completing SCSI command");
sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt,
command->Current_done);
......@@ -2547,31 +2462,26 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
*/
static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
struct sbp2scsi_host_info *hi = NULL;
struct scsi_id_instance_data *scsi_id = NULL;
unsigned long flags;
struct scsi_id_instance_data *scsi_id =
(struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0];
struct sbp2scsi_host_info *hi;
SBP2_DEBUG("sbp2scsi_queuecommand");
/*
* Pull our host info and scsi id instance data from the scsi command
* If scsi_id is null, it means there is no device in this slot,
* so we should return selection timeout.
*/
hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel, (unsigned long)SCpnt->device->host);
if (!hi) {
SBP2_ERR("sbp2scsi_host_info is NULL - this is bad!");
if (!scsi_id) {
SCpnt->result = DID_NO_CONNECT << 16;
done (SCpnt);
return(0);
return 0;
}
scsi_id = hi->scsi_id[SCpnt->device->id];
hi = scsi_id->hi;
/*
* If scsi_id is null, it means there is no device in this slot,
* so we should return selection timeout.
*/
if (!scsi_id) {
if (!hi) {
SBP2_ERR("sbp2scsi_host_info is NULL - this is bad!");
SCpnt->result = DID_NO_CONNECT << 16;
done (SCpnt);
return(0);
......@@ -2612,13 +2522,11 @@ static int sbp2scsi_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
/*
* Try and send our SCSI command
*/
spin_lock_irqsave(&hi->sbp2_command_lock, flags);
if (sbp2_send_command(scsi_id, SCpnt, done)) {
SBP2_ERR("Error sending SCSI command");
sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
SCpnt, done);
}
spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
return(0);
}
......@@ -2634,7 +2542,7 @@ static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id
struct list_head *lh;
struct sbp2_command_info *command;
SBP2_DEBUG("sbp2_complete_all_commands");
SBP2_DEBUG("sbp2scsi_complete_all_commands");
while (!list_empty(&scsi_id->sbp2_command_orb_inuse)) {
SBP2_DEBUG("Found pending command to complete");
......@@ -2654,6 +2562,8 @@ static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id
}
}
scsi_unblock_requests(scsi_id->scsi_host);
return;
}
......@@ -2766,9 +2676,9 @@ static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id,
/*
* Tell scsi stack that we're done with this command
*/
spin_lock_irqsave(scsi_id->hi->scsi_host->host_lock,flags);
spin_lock_irqsave(scsi_id->scsi_host->host_lock,flags);
done (SCpnt);
spin_unlock_irqrestore(scsi_id->hi->scsi_host->host_lock,flags);
spin_unlock_irqrestore(scsi_id->scsi_host->host_lock,flags);
return;
}
......@@ -2779,11 +2689,10 @@ static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id,
*/
static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
{
struct sbp2scsi_host_info *hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel,
(unsigned long)SCpnt->device->host);
struct scsi_id_instance_data *scsi_id = hi->scsi_id[SCpnt->device->id];
struct scsi_id_instance_data *scsi_id =
(struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0];
struct sbp2scsi_host_info *hi = scsi_id->hi;
struct sbp2_command_info *command;
unsigned long flags;
SBP2_ERR("aborting sbp2 command");
print_command (SCpnt->cmnd);
......@@ -2794,7 +2703,6 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
* Right now, just return any matching command structures
* to the free pool.
*/
spin_lock_irqsave(&hi->sbp2_command_lock, flags);
command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt);
if (command) {
SBP2_DEBUG("Found command to abort");
......@@ -2819,7 +2727,6 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
*/
sbp2_agent_reset(scsi_id, 0);
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
}
return(SUCCESS);
......@@ -2830,9 +2737,8 @@ static int sbp2scsi_abort (Scsi_Cmnd *SCpnt)
*/
static int sbp2scsi_reset (Scsi_Cmnd *SCpnt)
{
struct sbp2scsi_host_info *hi = hpsb_get_hostinfo_bykey(&sbp2_highlevel,
(unsigned long)SCpnt->device->host);
struct scsi_id_instance_data *scsi_id = hi->scsi_id[SCpnt->device->id];
struct scsi_id_instance_data *scsi_id =
(struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0];
SBP2_ERR("reset requested");
......@@ -2849,24 +2755,30 @@ static const char *sbp2scsi_info (struct Scsi_Host *host)
return "SCSI emulation for IEEE-1394 SBP-2 Devices";
}
static ssize_t sbp2_sysfs_ieee1394_guid_show(struct device *dev, char *buf)
static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev, char *buf)
{
struct scsi_device *sdev;
struct scsi_id_instance_data *scsi_id;
int lun;
if (!(sdev = to_scsi_device(dev)))
return 0;
if (!(scsi_id = sdev->hostdata))
if (!(scsi_id = (struct scsi_id_instance_data *)sdev->host->hostdata[0]))
return 0;
return sprintf(buf, "%016Lx\n", (unsigned long long)scsi_id->ne->guid);
}
if (scsi_id->sbp2_device_type_and_lun == SBP2_DEVICE_TYPE_LUN_UNINITIALIZED)
lun = 0;
else
lun = ORB_SET_LUN(scsi_id->sbp2_device_type_and_lun);
static DEVICE_ATTR(ieee1394_guid, S_IRUGO, sbp2_sysfs_ieee1394_guid_show, NULL);
return sprintf(buf, "%016Lx:%d:%d\n", (unsigned long long)scsi_id->ne->guid,
scsi_id->ud->id, lun);
}
static DEVICE_ATTR(ieee1394_id, S_IRUGO, sbp2_sysfs_ieee1394_id_show, NULL);
static struct device_attribute *sbp2_sysfs_sdev_attrs[] = {
&dev_attr_ieee1394_guid,
&dev_attr_ieee1394_id,
NULL
};
......@@ -2889,8 +2801,8 @@ static Scsi_Host_Template scsi_driver_template = {
.this_id = -1,
.sg_tablesize = SG_ALL,
.use_clustering = ENABLE_CLUSTERING,
.cmd_per_lun = SBP2_MAX_CMDS_PER_LUN,
.can_queue = SBP2_MAX_SCSI_QUEUE,
.cmd_per_lun = SBP2_MAX_CMDS,
.can_queue = SBP2_MAX_CMDS,
.emulated = 1,
.sdev_attrs = sbp2_sysfs_sdev_attrs,
};
......
......@@ -195,20 +195,22 @@ struct sbp2_status_block {
* Miscellaneous SBP2 related config rom defines
*/
/*
* The status fifo address definition below is used as a status base, with a chunk
* separately assigned for each sbp2 device detected. For example, 0xfffe00000000ULL
* is used for the first sbp2 device detected, 0xfffe00000020ULL for the next sbp2
* device, and so on.
/* The status fifo address definition below is used as a base for each
* node, which a chunk seperately assigned to each unit directory in the
* node. For example, 0xfffe00000000ULL is used for the first sbp2 device
* detected on node 0, 0xfffe00000020ULL for the next sbp2 device on node
* 0, and so on.
*
* Note: We could use a single status fifo address for all sbp2 devices, and figure
* out which sbp2 device the status belongs to by looking at the source node id of
* the status write... but, using separate addresses for each sbp2 device allows for
* better code and the ability to support multiple luns within a single 1394 node.
* Note: We could use a single status fifo address for all sbp2 devices,
* and figure out which sbp2 device the status belongs to by looking at
* the source node id of the status write... but, using separate addresses
* for each sbp2 unit directory allows for better code and the ability to
* support multiple luns within a single 1394 node.
*
* Also note that we choose the address range below as it is a region specified for
* write posting, where the ohci controller will automatically send an ack_complete
* when the status is written by the sbp2 device... saving a split transaction. =)
* Also note that we choose the address range below as it is a region
* specified for write posting, where the ohci controller will
* automatically send an ack_complete when the status is written by the
* sbp2 device... saving a split transaction. =)
*/
#define SBP2_STATUS_FIFO_ADDRESS 0xfffe00000000ULL
#define SBP2_STATUS_FIFO_ADDRESS_HI 0xfffe
......@@ -261,7 +263,7 @@ struct sbp2_status_block {
*/
#define SBP2_MAX_SG_ELEMENT_LENGTH 0xf000
#define SBP2SCSI_MAX_SCSI_IDS 32 /* Max sbp2 device instances supported */
#define SBP2_MAX_UDS_PER_NODE 16 /* Maximum scsi devices per node */
#define SBP2_MAX_SECTORS 255 /* Max sectors supported */
#ifndef TYPE_SDAD
......@@ -304,11 +306,8 @@ static unchar sbp2scsi_direction_table[0x100] = {
DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN
};
/* This should be safe. If there's more than one LUN per node, we could
* saturate the tlabel's though. */
#define SBP2_MAX_CMDS_PER_LUN 8
#define SBP2_MAX_SCSI_QUEUE (SBP2_MAX_CMDS_PER_LUN * SBP2SCSI_MAX_SCSI_IDS)
#define SBP2_MAX_COMMAND_ORBS SBP2_MAX_SCSI_QUEUE
/* This should be safe */
#define SBP2_MAX_CMDS 8
/* This is the two dma types we use for cmd_dma below */
enum cmd_dma_types {
......@@ -351,9 +350,6 @@ struct sbp2scsi_host_info;
* Information needed on a per scsi id basis (one for each sbp2 device)
*/
struct scsi_id_instance_data {
/* SCSI ID */
int id;
/*
* Various sbp2 specific structures
*/
......@@ -402,50 +398,28 @@ struct scsi_id_instance_data {
struct list_head sbp2_command_orb_inuse;
struct list_head sbp2_command_orb_completed;
struct list_head list;
struct list_head scsi_list;
/* Node entry, as retrieved from NodeMgr entries */
struct node_entry *ne;
struct unit_directory *ud;
/* A backlink to our host_info */
struct sbp2scsi_host_info *hi;
/* The scsi_device associated with this scsi_id */
/* SCSI related pointers */
struct scsi_device *sdev;
struct Scsi_Host *scsi_host;
/* Device specific workarounds/brokeness */
u32 workarounds;
};
/* Describes a per-ud scsi_id group */
struct scsi_id_group {
struct list_head scsi_id_list;
};
/*
* Sbp2 host data structure (one per sbp2 host)
*/
/* Sbp2 host data structure (one per IEEE1394 host) */
struct sbp2scsi_host_info {
struct hpsb_host *host;
/*
* Spin locks for command processing
*/
spinlock_t sbp2_command_lock;
/*
* This is the scsi host we register with the scsi mid level.
* We keep a reference to it here, so we can unregister it
* when the hpsb_host is removed.
*/
struct Scsi_Host *scsi_host;
/*
* SCSI ID instance data (one for each sbp2 device instance possible)
*/
struct scsi_id_instance_data *scsi_id[SBP2SCSI_MAX_SCSI_IDS];
struct hpsb_host *host; /* IEEE1394 host */
struct list_head scsi_ids; /* List of scsi ids on this host */
};
/*
......@@ -468,15 +442,10 @@ static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_i
/*
* IEEE-1394 core driver related prototypes
*/
static struct sbp2scsi_host_info *sbp2_add_host(struct hpsb_host *host);
static void sbp2_remove_host(struct hpsb_host *host);
static int sbp2_probe(struct device *dev);
static int sbp2_remove(struct device *dev);
static void sbp2_update(struct unit_directory *ud);
static int sbp2_start_ud(struct sbp2scsi_host_info *hi,
struct unit_directory *ud);
static int sbp2_start_device(struct scsi_id_instance_data *scsi_id);
static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id);
......@@ -511,7 +480,7 @@ static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data);
static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd);
static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt);
static void sbp2_parse_unit_directory(struct scsi_id_group *scsi_group,
static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
struct unit_directory *ud);
static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id);
static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id);
......
......@@ -476,11 +476,9 @@ static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags)
static struct dma_iso_ctx *
find_ctx(struct list_head *list, int type, int channel)
{
struct list_head *lh;
list_for_each(lh, list) {
struct dma_iso_ctx *ctx;
ctx = list_entry(lh, struct dma_iso_ctx, link);
list_for_each_entry(ctx, list, link) {
if (ctx->type == type && ctx->channel == channel)
return ctx;
}
......
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