Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/ieee1394/linux1394-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6: (23 commits)
  firewire: ohci: extend initialization log message
  firewire: ohci: fix IR/IT context mask mixup
  firewire: ohci: add module parameter to activate quirk fixes
  firewire: ohci: use an ID table for quirks detection
  firewire: ohci: reorder struct fw_ohci for better cache efficiency
  firewire: ohci: remove unused dualbuffer IR code
  firewire: core: combine a bit of repeated code
  firewire: core: change type of a data buffer
  firewire: cdev: increment ABI version number
  firewire: cdev: add more flexible cycle timer ioctl
  firewire: core: rename an internal function
  firewire: core: fix an information leak
  firewire: core: increase stack size of config ROM reader
  firewire: core: don't fail device creation in case of too large config ROM blocks
  firewire: core: fix "giving up on config rom" with Panasonic AG-DV2500
  firewire: remove incomplete Bus_Time CSR support
  firewire: get_cycle_timer optimization and cleanup
  firewire: ohci: enable cycle timer fix on ALi and NEC controllers
  firewire: ohci: work around cycle timer bugs on VIA controllers
  firewire: make PCI device id constant
  ...
  • Loading branch information
Linus Torvalds committed Mar 3, 2010
2 parents 60f8a8d + 6fdb2ee commit c1dcb4b
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 552 deletions.
368 changes: 182 additions & 186 deletions drivers/firewire/core-cdev.c

Large diffs are not rendered by default.

198 changes: 132 additions & 66 deletions drivers/firewire/core-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <linux/bug.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
Expand All @@ -43,7 +44,7 @@

#include "core.h"

void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
void fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p)
{
ci->p = p + 1;
ci->end = ci->p + (p[0] >> 16);
Expand All @@ -59,9 +60,76 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value)
}
EXPORT_SYMBOL(fw_csr_iterator_next);

static const u32 *search_leaf(const u32 *directory, int search_key)
{
struct fw_csr_iterator ci;
int last_key = 0, key, value;

fw_csr_iterator_init(&ci, directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (last_key == search_key &&
key == (CSR_DESCRIPTOR | CSR_LEAF))
return ci.p - 1 + value;

last_key = key;
}

return NULL;
}

static int textual_leaf_to_string(const u32 *block, char *buf, size_t size)
{
unsigned int quadlets, i;
char c;

if (!size || !buf)
return -EINVAL;

quadlets = min(block[0] >> 16, 256U);
if (quadlets < 2)
return -ENODATA;

if (block[1] != 0 || block[2] != 0)
/* unknown language/character set */
return -ENODATA;

block += 3;
quadlets -= 2;
for (i = 0; i < quadlets * 4 && i < size - 1; i++) {
c = block[i / 4] >> (24 - 8 * (i % 4));
if (c == '\0')
break;
buf[i] = c;
}
buf[i] = '\0';

return i;
}

/**
* fw_csr_string - reads a string from the configuration ROM
* @directory: e.g. root directory or unit directory
* @key: the key of the preceding directory entry
* @buf: where to put the string
* @size: size of @buf, in bytes
*
* The string is taken from a minimal ASCII text descriptor leaf after
* the immediate entry with @key. The string is zero-terminated.
* Returns strlen(buf) or a negative error code.
*/
int fw_csr_string(const u32 *directory, int key, char *buf, size_t size)
{
const u32 *leaf = search_leaf(directory, key);
if (!leaf)
return -ENOENT;

return textual_leaf_to_string(leaf, buf, size);
}
EXPORT_SYMBOL(fw_csr_string);

static bool is_fw_unit(struct device *dev);

static int match_unit_directory(u32 *directory, u32 match_flags,
static int match_unit_directory(const u32 *directory, u32 match_flags,
const struct ieee1394_device_id *id)
{
struct fw_csr_iterator ci;
Expand Down Expand Up @@ -195,7 +263,7 @@ static ssize_t show_immediate(struct device *dev,
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
struct fw_csr_iterator ci;
u32 *dir;
const u32 *dir;
int key, value, ret = -ENOENT;

down_read(&fw_device_rwsem);
Expand Down Expand Up @@ -226,10 +294,10 @@ static ssize_t show_text_leaf(struct device *dev,
{
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
struct fw_csr_iterator ci;
u32 *dir, *block = NULL, *p, *end;
int length, key, value, last_key = 0, ret = -ENOENT;
char *b;
const u32 *dir;
size_t bufsize;
char dummy_buf[2];
int ret;

down_read(&fw_device_rwsem);

Expand All @@ -238,40 +306,23 @@ static ssize_t show_text_leaf(struct device *dev,
else
dir = fw_device(dev)->config_rom + 5;

fw_csr_iterator_init(&ci, dir);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (attr->key == last_key &&
key == (CSR_DESCRIPTOR | CSR_LEAF))
block = ci.p - 1 + value;
last_key = key;
if (buf) {
bufsize = PAGE_SIZE - 1;
} else {
buf = dummy_buf;
bufsize = 1;
}

if (block == NULL)
goto out;

length = min(block[0] >> 16, 256U);
if (length < 3)
goto out;

if (block[1] != 0 || block[2] != 0)
/* Unknown encoding. */
goto out;
ret = fw_csr_string(dir, attr->key, buf, bufsize);

if (buf == NULL) {
ret = length * 4;
goto out;
if (ret >= 0) {
/* Strip trailing whitespace and add newline. */
while (ret > 0 && isspace(buf[ret - 1]))
ret--;
strcpy(buf + ret, "\n");
ret++;
}

b = buf;
end = &block[length + 1];
for (p = &block[3]; p < end; p++, b += 4)
* (u32 *) b = (__force u32) __cpu_to_be32(*p);

/* Strip trailing whitespace and add newline. */
while (b--, (isspace(*b) || *b == '\0') && b > buf);
strcpy(b + 1, "\n");
ret = b + 2 - buf;
out:
up_read(&fw_device_rwsem);

return ret;
Expand Down Expand Up @@ -371,7 +422,7 @@ static ssize_t guid_show(struct device *dev,
return ret;
}

static int units_sprintf(char *buf, u32 *directory)
static int units_sprintf(char *buf, const u32 *directory)
{
struct fw_csr_iterator ci;
int key, value;
Expand Down Expand Up @@ -441,28 +492,29 @@ static int read_rom(struct fw_device *device,
return rcode;
}

#define READ_BIB_ROM_SIZE 256
#define READ_BIB_STACK_SIZE 16
#define MAX_CONFIG_ROM_SIZE 256

/*
* Read the bus info block, perform a speed probe, and read all of the rest of
* the config ROM. We do all this with a cached bus generation. If the bus
* generation changes under us, read_bus_info_block will fail and get retried.
* generation changes under us, read_config_rom will fail and get retried.
* It's better to start all over in this case because the node from which we
* are reading the ROM may have changed the ROM during the reset.
*/
static int read_bus_info_block(struct fw_device *device, int generation)
static int read_config_rom(struct fw_device *device, int generation)
{
u32 *rom, *stack, *old_rom, *new_rom;
const u32 *old_rom, *new_rom;
u32 *rom, *stack;
u32 sp, key;
int i, end, length, ret = -1;

rom = kmalloc(sizeof(*rom) * READ_BIB_ROM_SIZE +
sizeof(*stack) * READ_BIB_STACK_SIZE, GFP_KERNEL);
rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE +
sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL);
if (rom == NULL)
return -ENOMEM;

stack = &rom[READ_BIB_ROM_SIZE];
stack = &rom[MAX_CONFIG_ROM_SIZE];
memset(rom, 0, sizeof(*rom) * MAX_CONFIG_ROM_SIZE);

device->max_speed = SCODE_100;

Expand Down Expand Up @@ -529,40 +581,54 @@ static int read_bus_info_block(struct fw_device *device, int generation)
*/
key = stack[--sp];
i = key & 0xffffff;
if (i >= READ_BIB_ROM_SIZE)
/*
* The reference points outside the standard
* config rom area, something's fishy.
*/
if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE))
goto out;

/* Read header quadlet for the block to get the length. */
if (read_rom(device, generation, i, &rom[i]) != RCODE_COMPLETE)
goto out;
end = i + (rom[i] >> 16) + 1;
i++;
if (end > READ_BIB_ROM_SIZE)
if (end > MAX_CONFIG_ROM_SIZE) {
/*
* This block extends outside standard config
* area (and the array we're reading it
* into). That's broken, so ignore this
* device.
* This block extends outside the config ROM which is
* a firmware bug. Ignore this whole block, i.e.
* simply set a fake block length of 0.
*/
goto out;
fw_error("skipped invalid ROM block %x at %llx\n",
rom[i],
i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
rom[i] = 0;
end = i;
}
i++;

/*
* Now read in the block. If this is a directory
* block, check the entries as we read them to see if
* it references another block, and push it in that case.
*/
while (i < end) {
for (; i < end; i++) {
if (read_rom(device, generation, i, &rom[i]) !=
RCODE_COMPLETE)
goto out;
if ((key >> 30) == 3 && (rom[i] >> 30) > 1 &&
sp < READ_BIB_STACK_SIZE)
stack[sp++] = i + rom[i];
i++;

if ((key >> 30) != 3 || (rom[i] >> 30) < 2)
continue;
/*
* Offset points outside the ROM. May be a firmware
* bug or an Extended ROM entry (IEEE 1212-2001 clause
* 7.7.18). Simply overwrite this pointer here by a
* fake immediate entry so that later iterators over
* the ROM don't have to check offsets all the time.
*/
if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) {
fw_error("skipped unsupported ROM entry %x at %llx\n",
rom[i],
i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
rom[i] = 0;
continue;
}
stack[sp++] = i + rom[i];
}
if (length < i)
length = i;
Expand Down Expand Up @@ -905,7 +971,7 @@ static void fw_device_init(struct work_struct *work)
* device.
*/

if (read_bus_info_block(device, device->generation) < 0) {
if (read_config_rom(device, device->generation) < 0) {
if (device->config_rom_retries < MAX_RETRIES &&
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
device->config_rom_retries++;
Expand Down Expand Up @@ -1022,7 +1088,7 @@ enum {
};

/* Reread and compare bus info block and header of root directory */
static int reread_bus_info_block(struct fw_device *device, int generation)
static int reread_config_rom(struct fw_device *device, int generation)
{
u32 q;
int i;
Expand All @@ -1048,7 +1114,7 @@ static void fw_device_refresh(struct work_struct *work)
struct fw_card *card = device->card;
int node_id = device->node_id;

switch (reread_bus_info_block(device, device->generation)) {
switch (reread_config_rom(device, device->generation)) {
case REREAD_BIB_ERROR:
if (device->config_rom_retries < MAX_RETRIES / 2 &&
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
Expand Down Expand Up @@ -1082,7 +1148,7 @@ static void fw_device_refresh(struct work_struct *work)
*/
device_for_each_child(&device->device, NULL, shutdown_unit);

if (read_bus_info_block(device, device->generation) < 0) {
if (read_config_rom(device, device->generation) < 0) {
if (device->config_rom_retries < MAX_RETRIES &&
atomic_read(&device->state) == FW_DEVICE_INITIALIZING) {
device->config_rom_retries++;
Expand Down
17 changes: 6 additions & 11 deletions drivers/firewire/core-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,23 +921,15 @@ static void handle_registers(struct fw_card *card, struct fw_request *request,
void *payload, size_t length, void *callback_data)
{
int reg = offset & ~CSR_REGISTER_BASE;
unsigned long long bus_time;
__be32 *data = payload;
int rcode = RCODE_COMPLETE;

switch (reg) {
case CSR_CYCLE_TIME:
case CSR_BUS_TIME:
if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) {
rcode = RCODE_TYPE_ERROR;
break;
}

bus_time = card->driver->get_bus_time(card);
if (reg == CSR_CYCLE_TIME)
*data = cpu_to_be32(bus_time);
if (TCODE_IS_READ_REQUEST(tcode) && length == 4)
*data = cpu_to_be32(card->driver->get_cycle_time(card));
else
*data = cpu_to_be32(bus_time >> 25);
rcode = RCODE_TYPE_ERROR;
break;

case CSR_BROADCAST_CHANNEL:
Expand Down Expand Up @@ -968,6 +960,9 @@ static void handle_registers(struct fw_card *card, struct fw_request *request,
case CSR_BUSY_TIMEOUT:
/* FIXME: Implement this. */

case CSR_BUS_TIME:
/* Useless without initialization by the bus manager. */

default:
rcode = RCODE_ADDRESS_ERROR;
break;
Expand Down
2 changes: 1 addition & 1 deletion drivers/firewire/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct fw_card_driver {
int (*enable_phys_dma)(struct fw_card *card,
int node_id, int generation);

u64 (*get_bus_time)(struct fw_card *card);
u32 (*get_cycle_time)(struct fw_card *card);

struct fw_iso_context *
(*allocate_iso_context)(struct fw_card *card,
Expand Down
Loading

0 comments on commit c1dcb4b

Please sign in to comment.