?? fw-device.c
字號:
/* * 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/module.h>#include <linux/wait.h>#include <linux/errno.h>#include <linux/kthread.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/idr.h>#include <linux/rwsem.h>#include <asm/semaphore.h>#include <linux/ctype.h>#include "fw-transaction.h"#include "fw-topology.h"#include "fw-device.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 int is_fw_unit(struct device *dev);static int match_unit_directory(u32 * directory, const struct fw_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) match |= FW_MATCH_VENDOR; if (key == CSR_MODEL && value == id->model) match |= FW_MATCH_MODEL; if (key == CSR_SPECIFIER_ID && value == id->specifier_id) match |= FW_MATCH_SPECIFIER_ID; if (key == CSR_VERSION && value == id->version) match |= FW_MATCH_VERSION; } return (match & id->match_flags) == id->match_flags;}static int fw_unit_match(struct device *dev, struct device_driver *drv){ struct fw_unit *unit = fw_unit(dev); struct fw_driver *driver = fw_driver(drv); int i; /* We only allow binding to fw_units. */ if (!is_fw_unit(dev)) return 0; for (i = 0; driver->id_table[i].match_flags != 0; i++) { if (match_unit_directory(unit->directory, &driver->id_table[i])) return 1; } return 0;}static int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size){ struct fw_device *device = fw_device(unit->device.parent); 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 intfw_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);struct fw_device *fw_device_get(struct fw_device *device){ get_device(&device->device); return device;}void fw_device_put(struct fw_device *device){ put_device(&device->device);}static void fw_device_release(struct device *dev){ struct fw_device *device = fw_device(dev); unsigned long flags; /* * Take the card lock so we don't set this to NULL while a * FW_NODE_UPDATED callback is being handled. */ spin_lock_irqsave(&device->card->lock, flags); device->node->data = NULL; spin_unlock_irqrestore(&device->card->lock, flags); fw_node_put(device->node); fw_card_put(device->card); kfree(device->config_rom); kfree(device);}int fw_device_enable_phys_dma(struct fw_device *device){ return device->card->driver->enable_phys_dma(device->card, device->node_id, device->generation);}EXPORT_SYMBOL(fw_device_enable_phys_dma);struct config_rom_attribute { struct device_attribute attr; u32 key;};static ssize_tshow_immediate(struct device *dev, struct device_attribute *dattr, char *buf){ struct config_rom_attribute *attr = container_of(dattr, struct config_rom_attribute, attr); struct fw_csr_iterator ci; u32 *dir; int key, value; if (is_fw_unit(dev)) dir = fw_unit(dev)->directory; 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 == key) return snprintf(buf, buf ? PAGE_SIZE : 0, "0x%06x\n", value); return -ENOENT;}#define IMMEDIATE_ATTR(name, key) \ { __ATTR(name, S_IRUGO, show_immediate, NULL), key }static ssize_tshow_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf){ 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; char *b; if (is_fw_unit(dev)) dir = fw_unit(dev)->directory; 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 (block == NULL) return -ENOENT; length = min(block[0] >> 16, 256U); if (length < 3) return -ENOENT; if (block[1] != 0 || block[2] != 0) /* Unknown encoding. */ return -ENOENT; if (buf == NULL) return length * 4; 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"); return b + 2 - buf;}#define TEXT_LEAF_ATTR(name, key) \ { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key }static struct config_rom_attribute config_rom_attributes[] = { IMMEDIATE_ATTR(vendor, CSR_VENDOR), IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION), IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID), IMMEDIATE_ATTR(version, CSR_VERSION), IMMEDIATE_ATTR(model, CSR_MODEL), TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR), TEXT_LEAF_ATTR(model_name, CSR_MODEL), TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),};static voidinit_fw_attribute_group(struct device *dev, struct device_attribute *attrs, struct fw_attribute_group *group){ struct device_attribute *attr; int i, j; for (j = 0; attrs[j].attr.name != NULL; j++) group->attrs[j] = &attrs[j].attr; for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) { attr = &config_rom_attributes[i].attr; if (attr->show(dev, attr, NULL) < 0) continue; group->attrs[j++] = &attr->attr; } BUG_ON(j >= ARRAY_SIZE(group->attrs)); group->attrs[j++] = NULL; group->groups[0] = &group->group; group->groups[1] = NULL; group->group.attrs = group->attrs; dev->groups = group->groups;}static ssize_tmodalias_show(struct device *dev, struct device_attribute *attr, char *buf){ struct fw_unit *unit = fw_unit(dev); int length; length = get_modalias(unit, buf, PAGE_SIZE); strcpy(buf + length, "\n"); return length + 1;}static ssize_trom_index_show(struct device *dev, struct device_attribute *attr, char *buf){ struct fw_device *device = fw_device(dev->parent); struct fw_unit *unit = fw_unit(dev); return snprintf(buf, PAGE_SIZE, "%d\n", (int)(unit->directory - device->config_rom));}static struct device_attribute fw_unit_attributes[] = { __ATTR_RO(modalias), __ATTR_RO(rom_index), __ATTR_NULL,};static ssize_tconfig_rom_show(struct device *dev, struct device_attribute *attr, char *buf){ struct fw_device *device = fw_device(dev); memcpy(buf, device->config_rom, device->config_rom_length * 4); return device->config_rom_length * 4;}static ssize_tguid_show(struct device *dev, struct device_attribute *attr, char *buf){ struct fw_device *device = fw_device(dev); u64 guid; guid = ((u64)device->config_rom[3] << 32) | device->config_rom[4]; return snprintf(buf, PAGE_SIZE, "0x%016llx\n", (unsigned long long)guid);}static struct device_attribute fw_device_attributes[] = { __ATTR_RO(config_rom), __ATTR_RO(guid), __ATTR_NULL,};struct read_quadlet_callback_data { struct completion done; int rcode; u32 data;};static voidcomplete_transaction(struct fw_card *card, int rcode, void *payload, size_t length, void *data){ struct read_quadlet_callback_data *callback_data = data; if (rcode == RCODE_COMPLETE) callback_data->data = be32_to_cpu(*(__be32 *)payload); callback_data->rcode = rcode; complete(&callback_data->done);}static int read_rom(struct fw_device *device, int index, u32 * data){ struct read_quadlet_callback_data callback_data; struct fw_transaction t; u64 offset; init_completion(&callback_data.done); offset = 0xfffff0000400ULL + index * 4; fw_send_request(device->card, &t, TCODE_READ_QUADLET_REQUEST, device->node_id, device->generation, device->max_speed, offset, NULL, 4, complete_transaction, &callback_data); wait_for_completion(&callback_data.done); *data = callback_data.data; return callback_data.rcode;}static int read_bus_info_block(struct fw_device *device){ static u32 rom[256]; u32 stack[16], sp, key; int i, end, length; device->max_speed = SCODE_100; /* First read the bus info block. */ for (i = 0; i < 5; i++) { if (read_rom(device, i, &rom[i]) != RCODE_COMPLETE) return -1; /*
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -