/*
* Device probing and sysfs code.
*
* Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/idr.h>
#include <linux/jiffies.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/rwsem.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/workqueue.h>
#include <asm/atomic.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include "core.h"
void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 * p)
{
ci->p = p + 1;
ci->end = ci->p + (p[0] >> 16);
}
EXPORT_SYMBOL(fw_csr_iterator_init);
int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value)
{
*key = *ci->p >> 24;
*value = *ci->p & 0xffffff;
return ci->p++ < ci->end;
}
EXPORT_SYMBOL(fw_csr_iterator_next);
static bool is_fw_unit(struct device *dev);
static int match_unit_directory(u32 *directory, u32 match_flags,
const struct ieee1394_device_id *id)
{
struct fw_csr_iterator ci;
int key, value, match;
match = 0;
fw_csr_iterator_init(&ci, directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (key == CSR_VENDOR && value == id->vendor_id)
match |= IEEE1394_MATCH_VENDOR_ID;
if (key == CSR_MODEL && value == id->model_id)
match |= IEEE1394_MATCH_MODEL_ID;
if (key == CSR_SPECIFIER_ID && value == id->specifier_id)
match |= IEEE1394_MATCH_SPECIFIER_ID;
if (key == CSR_VERSION && value == id->version)
match |= IEEE1394_MATCH_VERSION;
}
return (match & match_flags) == match_flags;
}
static int fw_unit_match(struct device *dev, struct device_driver *drv)
{
struct fw_unit *unit = fw_unit(dev);
struct fw_device *device;
const struct ieee1394_device_id *id;
/* We only allow binding to fw_units. */
if (!is_fw_unit(dev))
return 0;
device = fw_parent_device(unit);
id = container_of(drv, struct fw_driver, driver)->id_table;
for (; id->match_flags != 0; id++) {
if (match_unit_directory(unit->directory, id->match_flags, id))
return 1;
/* Also check vendor ID in the root directory. */
if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
match_unit_directory(&device->config_rom[5],
IEEE1394_MATCH_VENDOR_ID, id) &&
match_unit_directory(unit->directory, id->match_flags
& ~IEEE1394_MATCH_VENDOR_ID, id))
return 1;
}
return 0;
}
static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size)
{
struct fw_device *device = fw_parent_device(unit);
struct fw_csr_iterator ci;
int key, value;
int vendor = 0;
int model = 0;
int specifier_id = 0;
int version = 0;
fw_csr_iterator_init(&ci, &device->config_rom[5]);
while (fw_csr_iterator_next(&ci, &key, &value)) {
switch (key) {
case CSR_VENDOR:
vendor = value;
break;
case CSR_MODEL:
model = value;
break;
}
}
fw_csr_iterator_init(&ci, unit->directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
switch (key) {
case CSR_SPECIFIER_ID:
specifier_id = value;
break;
case CSR_VERSION:
version = value;
break;
}
}
return snprintf(buffer, buffer_size,
"ieee1394:ven%08Xmo%08Xsp%08Xver%08X",
vendor, model, specifier_id, version);
}
static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct fw_unit *unit = fw_unit(dev);
char modalias[64];
get_modalias(unit, modalias, sizeof(modalias));
if (add_uevent_var(env, "MODALIAS=%s", modalias))
return -ENOMEM;
return 0;
}
struct bus_type fw_bus_type = {
.name = "firewire",
.match = fw_unit_match,
};
EXPORT_SYMBOL(fw_bus_type);
int fw_device_enable_phys_dma(struct fw_device *device)
{
int generation = device->generation;
/* device->node_id, accessed below, must not be older than generation */
smp_rmb();
return device->card->driver->enable_phys_dma(device->card,
device->node_id,
generation);
}
EXPORT_SYMBOL(fw_device_enable_phys_dma);
struct config_rom_attribute {
struct device_attribute attr;
|