18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device probing and sysfs code. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2006 Kristian Hoegsberg <krh@bitplanet.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bug.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/firewire.h> 148c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h> 158c2ecf20Sopenharmony_ci#include <linux/idr.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/kobject.h> 188c2ecf20Sopenharmony_ci#include <linux/list.h> 198c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci#include <linux/random.h> 238c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 268c2ecf20Sopenharmony_ci#include <linux/string.h> 278c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/atomic.h> 308c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "core.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_civoid fw_csr_iterator_init(struct fw_csr_iterator *ci, const u32 *p) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci ci->p = p + 1; 378c2ecf20Sopenharmony_ci ci->end = ci->p + (p[0] >> 16); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_csr_iterator_init); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci *key = *ci->p >> 24; 448c2ecf20Sopenharmony_ci *value = *ci->p & 0xffffff; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return ci->p++ < ci->end; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_csr_iterator_next); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const u32 *search_leaf(const u32 *directory, int search_key) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 538c2ecf20Sopenharmony_ci int last_key = 0, key, value; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 568c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 578c2ecf20Sopenharmony_ci if (last_key == search_key && 588c2ecf20Sopenharmony_ci key == (CSR_DESCRIPTOR | CSR_LEAF)) 598c2ecf20Sopenharmony_ci return ci.p - 1 + value; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci last_key = key; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return NULL; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int textual_leaf_to_string(const u32 *block, char *buf, size_t size) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci unsigned int quadlets, i; 708c2ecf20Sopenharmony_ci char c; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!size || !buf) 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci quadlets = min(block[0] >> 16, 256U); 768c2ecf20Sopenharmony_ci if (quadlets < 2) 778c2ecf20Sopenharmony_ci return -ENODATA; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (block[1] != 0 || block[2] != 0) 808c2ecf20Sopenharmony_ci /* unknown language/character set */ 818c2ecf20Sopenharmony_ci return -ENODATA; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci block += 3; 848c2ecf20Sopenharmony_ci quadlets -= 2; 858c2ecf20Sopenharmony_ci for (i = 0; i < quadlets * 4 && i < size - 1; i++) { 868c2ecf20Sopenharmony_ci c = block[i / 4] >> (24 - 8 * (i % 4)); 878c2ecf20Sopenharmony_ci if (c == '\0') 888c2ecf20Sopenharmony_ci break; 898c2ecf20Sopenharmony_ci buf[i] = c; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci buf[i] = '\0'; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return i; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/** 978c2ecf20Sopenharmony_ci * fw_csr_string() - reads a string from the configuration ROM 988c2ecf20Sopenharmony_ci * @directory: e.g. root directory or unit directory 998c2ecf20Sopenharmony_ci * @key: the key of the preceding directory entry 1008c2ecf20Sopenharmony_ci * @buf: where to put the string 1018c2ecf20Sopenharmony_ci * @size: size of @buf, in bytes 1028c2ecf20Sopenharmony_ci * 1038c2ecf20Sopenharmony_ci * The string is taken from a minimal ASCII text descriptor leaf just after the entry with the 1048c2ecf20Sopenharmony_ci * @key. The string is zero-terminated. An overlong string is silently truncated such that it 1058c2ecf20Sopenharmony_ci * and the zero byte fit into @size. 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * Returns strlen(buf) or a negative error code. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ciint fw_csr_string(const u32 *directory, int key, char *buf, size_t size) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const u32 *leaf = search_leaf(directory, key); 1128c2ecf20Sopenharmony_ci if (!leaf) 1138c2ecf20Sopenharmony_ci return -ENOENT; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return textual_leaf_to_string(leaf, buf, size); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_csr_string); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void get_ids(const u32 *directory, int *id) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 1228c2ecf20Sopenharmony_ci int key, value; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 1258c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 1268c2ecf20Sopenharmony_ci switch (key) { 1278c2ecf20Sopenharmony_ci case CSR_VENDOR: id[0] = value; break; 1288c2ecf20Sopenharmony_ci case CSR_MODEL: id[1] = value; break; 1298c2ecf20Sopenharmony_ci case CSR_SPECIFIER_ID: id[2] = value; break; 1308c2ecf20Sopenharmony_ci case CSR_VERSION: id[3] = value; break; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void get_modalias_ids(struct fw_unit *unit, int *id) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci get_ids(&fw_parent_device(unit)->config_rom[5], id); 1388c2ecf20Sopenharmony_ci get_ids(unit->directory, id); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic bool match_ids(const struct ieee1394_device_id *id_table, int *id) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int match = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (id[0] == id_table->vendor_id) 1468c2ecf20Sopenharmony_ci match |= IEEE1394_MATCH_VENDOR_ID; 1478c2ecf20Sopenharmony_ci if (id[1] == id_table->model_id) 1488c2ecf20Sopenharmony_ci match |= IEEE1394_MATCH_MODEL_ID; 1498c2ecf20Sopenharmony_ci if (id[2] == id_table->specifier_id) 1508c2ecf20Sopenharmony_ci match |= IEEE1394_MATCH_SPECIFIER_ID; 1518c2ecf20Sopenharmony_ci if (id[3] == id_table->version) 1528c2ecf20Sopenharmony_ci match |= IEEE1394_MATCH_VERSION; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return (match & id_table->match_flags) == id_table->match_flags; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const struct ieee1394_device_id *unit_match(struct device *dev, 1588c2ecf20Sopenharmony_ci struct device_driver *drv) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci const struct ieee1394_device_id *id_table = 1618c2ecf20Sopenharmony_ci container_of(drv, struct fw_driver, driver)->id_table; 1628c2ecf20Sopenharmony_ci int id[] = {0, 0, 0, 0}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci get_modalias_ids(fw_unit(dev), id); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (; id_table->match_flags != 0; id_table++) 1678c2ecf20Sopenharmony_ci if (match_ids(id_table, id)) 1688c2ecf20Sopenharmony_ci return id_table; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return NULL; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic bool is_fw_unit(struct device *dev); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int fw_unit_match(struct device *dev, struct device_driver *drv) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci /* We only allow binding to fw_units. */ 1788c2ecf20Sopenharmony_ci return is_fw_unit(dev) && unit_match(dev, drv) != NULL; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int fw_unit_probe(struct device *dev) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct fw_driver *driver = 1848c2ecf20Sopenharmony_ci container_of(dev->driver, struct fw_driver, driver); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return driver->probe(fw_unit(dev), unit_match(dev, dev->driver)); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int fw_unit_remove(struct device *dev) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct fw_driver *driver = 1928c2ecf20Sopenharmony_ci container_of(dev->driver, struct fw_driver, driver); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return driver->remove(fw_unit(dev)), 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int get_modalias(struct fw_unit *unit, char *buffer, size_t buffer_size) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci int id[] = {0, 0, 0, 0}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci get_modalias_ids(unit, id); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return snprintf(buffer, buffer_size, 2048c2ecf20Sopenharmony_ci "ieee1394:ven%08Xmo%08Xsp%08Xver%08X", 2058c2ecf20Sopenharmony_ci id[0], id[1], id[2], id[3]); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 2118c2ecf20Sopenharmony_ci char modalias[64]; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci get_modalias(unit, modalias, sizeof(modalias)); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (add_uevent_var(env, "MODALIAS=%s", modalias)) 2168c2ecf20Sopenharmony_ci return -ENOMEM; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistruct bus_type fw_bus_type = { 2228c2ecf20Sopenharmony_ci .name = "firewire", 2238c2ecf20Sopenharmony_ci .match = fw_unit_match, 2248c2ecf20Sopenharmony_ci .probe = fw_unit_probe, 2258c2ecf20Sopenharmony_ci .remove = fw_unit_remove, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_bus_type); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciint fw_device_enable_phys_dma(struct fw_device *device) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci int generation = device->generation; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* device->node_id, accessed below, must not be older than generation */ 2348c2ecf20Sopenharmony_ci smp_rmb(); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return device->card->driver->enable_phys_dma(device->card, 2378c2ecf20Sopenharmony_ci device->node_id, 2388c2ecf20Sopenharmony_ci generation); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_device_enable_phys_dma); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistruct config_rom_attribute { 2438c2ecf20Sopenharmony_ci struct device_attribute attr; 2448c2ecf20Sopenharmony_ci u32 key; 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic ssize_t show_immediate(struct device *dev, 2488c2ecf20Sopenharmony_ci struct device_attribute *dattr, char *buf) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct config_rom_attribute *attr = 2518c2ecf20Sopenharmony_ci container_of(dattr, struct config_rom_attribute, attr); 2528c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 2538c2ecf20Sopenharmony_ci const u32 *dir; 2548c2ecf20Sopenharmony_ci int key, value, ret = -ENOENT; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (is_fw_unit(dev)) 2598c2ecf20Sopenharmony_ci dir = fw_unit(dev)->directory; 2608c2ecf20Sopenharmony_ci else 2618c2ecf20Sopenharmony_ci dir = fw_device(dev)->config_rom + 5; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, dir); 2648c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) 2658c2ecf20Sopenharmony_ci if (attr->key == key) { 2668c2ecf20Sopenharmony_ci ret = snprintf(buf, buf ? PAGE_SIZE : 0, 2678c2ecf20Sopenharmony_ci "0x%06x\n", value); 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return ret; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#define IMMEDIATE_ATTR(name, key) \ 2778c2ecf20Sopenharmony_ci { __ATTR(name, S_IRUGO, show_immediate, NULL), key } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic ssize_t show_text_leaf(struct device *dev, 2808c2ecf20Sopenharmony_ci struct device_attribute *dattr, char *buf) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct config_rom_attribute *attr = 2838c2ecf20Sopenharmony_ci container_of(dattr, struct config_rom_attribute, attr); 2848c2ecf20Sopenharmony_ci const u32 *dir; 2858c2ecf20Sopenharmony_ci size_t bufsize; 2868c2ecf20Sopenharmony_ci char dummy_buf[2]; 2878c2ecf20Sopenharmony_ci int ret; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (is_fw_unit(dev)) 2928c2ecf20Sopenharmony_ci dir = fw_unit(dev)->directory; 2938c2ecf20Sopenharmony_ci else 2948c2ecf20Sopenharmony_ci dir = fw_device(dev)->config_rom + 5; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (buf) { 2978c2ecf20Sopenharmony_ci bufsize = PAGE_SIZE - 1; 2988c2ecf20Sopenharmony_ci } else { 2998c2ecf20Sopenharmony_ci buf = dummy_buf; 3008c2ecf20Sopenharmony_ci bufsize = 1; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = fw_csr_string(dir, attr->key, buf, bufsize); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (ret >= 0) { 3068c2ecf20Sopenharmony_ci /* Strip trailing whitespace and add newline. */ 3078c2ecf20Sopenharmony_ci while (ret > 0 && isspace(buf[ret - 1])) 3088c2ecf20Sopenharmony_ci ret--; 3098c2ecf20Sopenharmony_ci strcpy(buf + ret, "\n"); 3108c2ecf20Sopenharmony_ci ret++; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return ret; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci#define TEXT_LEAF_ATTR(name, key) \ 3198c2ecf20Sopenharmony_ci { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct config_rom_attribute config_rom_attributes[] = { 3228c2ecf20Sopenharmony_ci IMMEDIATE_ATTR(vendor, CSR_VENDOR), 3238c2ecf20Sopenharmony_ci IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION), 3248c2ecf20Sopenharmony_ci IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID), 3258c2ecf20Sopenharmony_ci IMMEDIATE_ATTR(version, CSR_VERSION), 3268c2ecf20Sopenharmony_ci IMMEDIATE_ATTR(model, CSR_MODEL), 3278c2ecf20Sopenharmony_ci TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR), 3288c2ecf20Sopenharmony_ci TEXT_LEAF_ATTR(model_name, CSR_MODEL), 3298c2ecf20Sopenharmony_ci TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION), 3308c2ecf20Sopenharmony_ci}; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void init_fw_attribute_group(struct device *dev, 3338c2ecf20Sopenharmony_ci struct device_attribute *attrs, 3348c2ecf20Sopenharmony_ci struct fw_attribute_group *group) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct device_attribute *attr; 3378c2ecf20Sopenharmony_ci int i, j; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (j = 0; attrs[j].attr.name != NULL; j++) 3408c2ecf20Sopenharmony_ci group->attrs[j] = &attrs[j].attr; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) { 3438c2ecf20Sopenharmony_ci attr = &config_rom_attributes[i].attr; 3448c2ecf20Sopenharmony_ci if (attr->show(dev, attr, NULL) < 0) 3458c2ecf20Sopenharmony_ci continue; 3468c2ecf20Sopenharmony_ci group->attrs[j++] = &attr->attr; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci group->attrs[j] = NULL; 3508c2ecf20Sopenharmony_ci group->groups[0] = &group->group; 3518c2ecf20Sopenharmony_ci group->groups[1] = NULL; 3528c2ecf20Sopenharmony_ci group->group.attrs = group->attrs; 3538c2ecf20Sopenharmony_ci dev->groups = (const struct attribute_group **) group->groups; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, 3578c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 3608c2ecf20Sopenharmony_ci int length; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci length = get_modalias(unit, buf, PAGE_SIZE); 3638c2ecf20Sopenharmony_ci strcpy(buf + length, "\n"); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci return length + 1; 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic ssize_t rom_index_show(struct device *dev, 3698c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev->parent); 3728c2ecf20Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", 3758c2ecf20Sopenharmony_ci (int)(unit->directory - device->config_rom)); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic struct device_attribute fw_unit_attributes[] = { 3798c2ecf20Sopenharmony_ci __ATTR_RO(modalias), 3808c2ecf20Sopenharmony_ci __ATTR_RO(rom_index), 3818c2ecf20Sopenharmony_ci __ATTR_NULL, 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic ssize_t config_rom_show(struct device *dev, 3858c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev); 3888c2ecf20Sopenharmony_ci size_t length; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 3918c2ecf20Sopenharmony_ci length = device->config_rom_length * 4; 3928c2ecf20Sopenharmony_ci memcpy(buf, device->config_rom, length); 3938c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return length; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic ssize_t guid_show(struct device *dev, 3998c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev); 4028c2ecf20Sopenharmony_ci int ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 4058c2ecf20Sopenharmony_ci ret = snprintf(buf, PAGE_SIZE, "0x%08x%08x\n", 4068c2ecf20Sopenharmony_ci device->config_rom[3], device->config_rom[4]); 4078c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return ret; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic ssize_t is_local_show(struct device *dev, 4138c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", device->is_local); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int units_sprintf(char *buf, const u32 *directory) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 4238c2ecf20Sopenharmony_ci int key, value; 4248c2ecf20Sopenharmony_ci int specifier_id = 0; 4258c2ecf20Sopenharmony_ci int version = 0; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, directory); 4288c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 4298c2ecf20Sopenharmony_ci switch (key) { 4308c2ecf20Sopenharmony_ci case CSR_SPECIFIER_ID: 4318c2ecf20Sopenharmony_ci specifier_id = value; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case CSR_VERSION: 4348c2ecf20Sopenharmony_ci version = value; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return sprintf(buf, "0x%06x:0x%06x ", specifier_id, version); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic ssize_t units_show(struct device *dev, 4438c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev); 4468c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 4478c2ecf20Sopenharmony_ci int key, value, i = 0; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 4508c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, &device->config_rom[5]); 4518c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 4528c2ecf20Sopenharmony_ci if (key != (CSR_UNIT | CSR_DIRECTORY)) 4538c2ecf20Sopenharmony_ci continue; 4548c2ecf20Sopenharmony_ci i += units_sprintf(&buf[i], ci.p + value - 1); 4558c2ecf20Sopenharmony_ci if (i >= PAGE_SIZE - (8 + 1 + 8 + 1)) 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (i) 4618c2ecf20Sopenharmony_ci buf[i - 1] = '\n'; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return i; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic struct device_attribute fw_device_attributes[] = { 4678c2ecf20Sopenharmony_ci __ATTR_RO(config_rom), 4688c2ecf20Sopenharmony_ci __ATTR_RO(guid), 4698c2ecf20Sopenharmony_ci __ATTR_RO(is_local), 4708c2ecf20Sopenharmony_ci __ATTR_RO(units), 4718c2ecf20Sopenharmony_ci __ATTR_NULL, 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int read_rom(struct fw_device *device, 4758c2ecf20Sopenharmony_ci int generation, int index, u32 *data) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci u64 offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4; 4788c2ecf20Sopenharmony_ci int i, rcode; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* device->node_id, accessed below, must not be older than generation */ 4818c2ecf20Sopenharmony_ci smp_rmb(); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci for (i = 10; i < 100; i += 10) { 4848c2ecf20Sopenharmony_ci rcode = fw_run_transaction(device->card, 4858c2ecf20Sopenharmony_ci TCODE_READ_QUADLET_REQUEST, device->node_id, 4868c2ecf20Sopenharmony_ci generation, device->max_speed, offset, data, 4); 4878c2ecf20Sopenharmony_ci if (rcode != RCODE_BUSY) 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci msleep(i); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci be32_to_cpus(data); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return rcode; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci#define MAX_CONFIG_ROM_SIZE 256 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* 4998c2ecf20Sopenharmony_ci * Read the bus info block, perform a speed probe, and read all of the rest of 5008c2ecf20Sopenharmony_ci * the config ROM. We do all this with a cached bus generation. If the bus 5018c2ecf20Sopenharmony_ci * generation changes under us, read_config_rom will fail and get retried. 5028c2ecf20Sopenharmony_ci * It's better to start all over in this case because the node from which we 5038c2ecf20Sopenharmony_ci * are reading the ROM may have changed the ROM during the reset. 5048c2ecf20Sopenharmony_ci * Returns either a result code or a negative error code. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic int read_config_rom(struct fw_device *device, int generation) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct fw_card *card = device->card; 5098c2ecf20Sopenharmony_ci const u32 *old_rom, *new_rom; 5108c2ecf20Sopenharmony_ci u32 *rom, *stack; 5118c2ecf20Sopenharmony_ci u32 sp, key; 5128c2ecf20Sopenharmony_ci int i, end, length, ret; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE + 5158c2ecf20Sopenharmony_ci sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL); 5168c2ecf20Sopenharmony_ci if (rom == NULL) 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci stack = &rom[MAX_CONFIG_ROM_SIZE]; 5208c2ecf20Sopenharmony_ci memset(rom, 0, sizeof(*rom) * MAX_CONFIG_ROM_SIZE); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci device->max_speed = SCODE_100; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* First read the bus info block. */ 5258c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 5268c2ecf20Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 5278c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) 5288c2ecf20Sopenharmony_ci goto out; 5298c2ecf20Sopenharmony_ci /* 5308c2ecf20Sopenharmony_ci * As per IEEE1212 7.2, during initialization, devices can 5318c2ecf20Sopenharmony_ci * reply with a 0 for the first quadlet of the config 5328c2ecf20Sopenharmony_ci * rom to indicate that they are booting (for example, 5338c2ecf20Sopenharmony_ci * if the firmware is on the disk of a external 5348c2ecf20Sopenharmony_ci * harddisk). In that case we just fail, and the 5358c2ecf20Sopenharmony_ci * retry mechanism will try again later. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci if (i == 0 && rom[i] == 0) { 5388c2ecf20Sopenharmony_ci ret = RCODE_BUSY; 5398c2ecf20Sopenharmony_ci goto out; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci device->max_speed = device->node->max_speed; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * Determine the speed of 5478c2ecf20Sopenharmony_ci * - devices with link speed less than PHY speed, 5488c2ecf20Sopenharmony_ci * - devices with 1394b PHY (unless only connected to 1394a PHYs), 5498c2ecf20Sopenharmony_ci * - all devices if there are 1394b repeaters. 5508c2ecf20Sopenharmony_ci * Note, we cannot use the bus info block's link_spd as starting point 5518c2ecf20Sopenharmony_ci * because some buggy firmwares set it lower than necessary and because 5528c2ecf20Sopenharmony_ci * 1394-1995 nodes do not have the field. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci if ((rom[2] & 0x7) < device->max_speed || 5558c2ecf20Sopenharmony_ci device->max_speed == SCODE_BETA || 5568c2ecf20Sopenharmony_ci card->beta_repeaters_present) { 5578c2ecf20Sopenharmony_ci u32 dummy; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* for S1600 and S3200 */ 5608c2ecf20Sopenharmony_ci if (device->max_speed == SCODE_BETA) 5618c2ecf20Sopenharmony_ci device->max_speed = card->link_speed; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci while (device->max_speed > SCODE_100) { 5648c2ecf20Sopenharmony_ci if (read_rom(device, generation, 0, &dummy) == 5658c2ecf20Sopenharmony_ci RCODE_COMPLETE) 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci device->max_speed--; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * Now parse the config rom. The config rom is a recursive 5738c2ecf20Sopenharmony_ci * directory structure so we parse it using a stack of 5748c2ecf20Sopenharmony_ci * references to the blocks that make up the structure. We 5758c2ecf20Sopenharmony_ci * push a reference to the root directory on the stack to 5768c2ecf20Sopenharmony_ci * start things off. 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci length = i; 5798c2ecf20Sopenharmony_ci sp = 0; 5808c2ecf20Sopenharmony_ci stack[sp++] = 0xc0000005; 5818c2ecf20Sopenharmony_ci while (sp > 0) { 5828c2ecf20Sopenharmony_ci /* 5838c2ecf20Sopenharmony_ci * Pop the next block reference of the stack. The 5848c2ecf20Sopenharmony_ci * lower 24 bits is the offset into the config rom, 5858c2ecf20Sopenharmony_ci * the upper 8 bits are the type of the reference the 5868c2ecf20Sopenharmony_ci * block. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci key = stack[--sp]; 5898c2ecf20Sopenharmony_ci i = key & 0xffffff; 5908c2ecf20Sopenharmony_ci if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) { 5918c2ecf20Sopenharmony_ci ret = -ENXIO; 5928c2ecf20Sopenharmony_ci goto out; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Read header quadlet for the block to get the length. */ 5968c2ecf20Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 5978c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) 5988c2ecf20Sopenharmony_ci goto out; 5998c2ecf20Sopenharmony_ci end = i + (rom[i] >> 16) + 1; 6008c2ecf20Sopenharmony_ci if (end > MAX_CONFIG_ROM_SIZE) { 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * This block extends outside the config ROM which is 6038c2ecf20Sopenharmony_ci * a firmware bug. Ignore this whole block, i.e. 6048c2ecf20Sopenharmony_ci * simply set a fake block length of 0. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci fw_err(card, "skipped invalid ROM block %x at %llx\n", 6078c2ecf20Sopenharmony_ci rom[i], 6088c2ecf20Sopenharmony_ci i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); 6098c2ecf20Sopenharmony_ci rom[i] = 0; 6108c2ecf20Sopenharmony_ci end = i; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci i++; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* 6158c2ecf20Sopenharmony_ci * Now read in the block. If this is a directory 6168c2ecf20Sopenharmony_ci * block, check the entries as we read them to see if 6178c2ecf20Sopenharmony_ci * it references another block, and push it in that case. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci for (; i < end; i++) { 6208c2ecf20Sopenharmony_ci ret = read_rom(device, generation, i, &rom[i]); 6218c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) 6228c2ecf20Sopenharmony_ci goto out; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if ((key >> 30) != 3 || (rom[i] >> 30) < 2) 6258c2ecf20Sopenharmony_ci continue; 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * Offset points outside the ROM. May be a firmware 6288c2ecf20Sopenharmony_ci * bug or an Extended ROM entry (IEEE 1212-2001 clause 6298c2ecf20Sopenharmony_ci * 7.7.18). Simply overwrite this pointer here by a 6308c2ecf20Sopenharmony_ci * fake immediate entry so that later iterators over 6318c2ecf20Sopenharmony_ci * the ROM don't have to check offsets all the time. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) { 6348c2ecf20Sopenharmony_ci fw_err(card, 6358c2ecf20Sopenharmony_ci "skipped unsupported ROM entry %x at %llx\n", 6368c2ecf20Sopenharmony_ci rom[i], 6378c2ecf20Sopenharmony_ci i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM); 6388c2ecf20Sopenharmony_ci rom[i] = 0; 6398c2ecf20Sopenharmony_ci continue; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci stack[sp++] = i + rom[i]; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci if (length < i) 6448c2ecf20Sopenharmony_ci length = i; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci old_rom = device->config_rom; 6488c2ecf20Sopenharmony_ci new_rom = kmemdup(rom, length * 4, GFP_KERNEL); 6498c2ecf20Sopenharmony_ci if (new_rom == NULL) { 6508c2ecf20Sopenharmony_ci ret = -ENOMEM; 6518c2ecf20Sopenharmony_ci goto out; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci down_write(&fw_device_rwsem); 6558c2ecf20Sopenharmony_ci device->config_rom = new_rom; 6568c2ecf20Sopenharmony_ci device->config_rom_length = length; 6578c2ecf20Sopenharmony_ci up_write(&fw_device_rwsem); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci kfree(old_rom); 6608c2ecf20Sopenharmony_ci ret = RCODE_COMPLETE; 6618c2ecf20Sopenharmony_ci device->max_rec = rom[2] >> 12 & 0xf; 6628c2ecf20Sopenharmony_ci device->cmc = rom[2] >> 30 & 1; 6638c2ecf20Sopenharmony_ci device->irmc = rom[2] >> 31 & 1; 6648c2ecf20Sopenharmony_ci out: 6658c2ecf20Sopenharmony_ci kfree(rom); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic void fw_unit_release(struct device *dev) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci fw_device_put(fw_parent_device(unit)); 6758c2ecf20Sopenharmony_ci kfree(unit); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic struct device_type fw_unit_type = { 6798c2ecf20Sopenharmony_ci .uevent = fw_unit_uevent, 6808c2ecf20Sopenharmony_ci .release = fw_unit_release, 6818c2ecf20Sopenharmony_ci}; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic bool is_fw_unit(struct device *dev) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci return dev->type == &fw_unit_type; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic void create_units(struct fw_device *device) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct fw_csr_iterator ci; 6918c2ecf20Sopenharmony_ci struct fw_unit *unit; 6928c2ecf20Sopenharmony_ci int key, value, i; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci i = 0; 6958c2ecf20Sopenharmony_ci fw_csr_iterator_init(&ci, &device->config_rom[5]); 6968c2ecf20Sopenharmony_ci while (fw_csr_iterator_next(&ci, &key, &value)) { 6978c2ecf20Sopenharmony_ci if (key != (CSR_UNIT | CSR_DIRECTORY)) 6988c2ecf20Sopenharmony_ci continue; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * Get the address of the unit directory and try to 7028c2ecf20Sopenharmony_ci * match the drivers id_tables against it. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci unit = kzalloc(sizeof(*unit), GFP_KERNEL); 7058c2ecf20Sopenharmony_ci if (unit == NULL) 7068c2ecf20Sopenharmony_ci continue; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci unit->directory = ci.p + value - 1; 7098c2ecf20Sopenharmony_ci unit->device.bus = &fw_bus_type; 7108c2ecf20Sopenharmony_ci unit->device.type = &fw_unit_type; 7118c2ecf20Sopenharmony_ci unit->device.parent = &device->device; 7128c2ecf20Sopenharmony_ci dev_set_name(&unit->device, "%s.%d", dev_name(&device->device), i++); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(unit->attribute_group.attrs) < 7158c2ecf20Sopenharmony_ci ARRAY_SIZE(fw_unit_attributes) + 7168c2ecf20Sopenharmony_ci ARRAY_SIZE(config_rom_attributes)); 7178c2ecf20Sopenharmony_ci init_fw_attribute_group(&unit->device, 7188c2ecf20Sopenharmony_ci fw_unit_attributes, 7198c2ecf20Sopenharmony_ci &unit->attribute_group); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci fw_device_get(device); 7228c2ecf20Sopenharmony_ci if (device_register(&unit->device) < 0) { 7238c2ecf20Sopenharmony_ci put_device(&unit->device); 7248c2ecf20Sopenharmony_ci continue; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int shutdown_unit(struct device *device, void *data) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci device_unregister(device); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci/* 7378c2ecf20Sopenharmony_ci * fw_device_rwsem acts as dual purpose mutex: 7388c2ecf20Sopenharmony_ci * - serializes accesses to fw_device_idr, 7398c2ecf20Sopenharmony_ci * - serializes accesses to fw_device.config_rom/.config_rom_length and 7408c2ecf20Sopenharmony_ci * fw_unit.directory, unless those accesses happen at safe occasions 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ciDECLARE_RWSEM(fw_device_rwsem); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ciDEFINE_IDR(fw_device_idr); 7458c2ecf20Sopenharmony_ciint fw_cdev_major; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistruct fw_device *fw_device_get_by_devt(dev_t devt) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct fw_device *device; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 7528c2ecf20Sopenharmony_ci device = idr_find(&fw_device_idr, MINOR(devt)); 7538c2ecf20Sopenharmony_ci if (device) 7548c2ecf20Sopenharmony_ci fw_device_get(device); 7558c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return device; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistruct workqueue_struct *fw_workqueue; 7618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fw_workqueue); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic void fw_schedule_device_work(struct fw_device *device, 7648c2ecf20Sopenharmony_ci unsigned long delay) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci queue_delayed_work(fw_workqueue, &device->work, delay); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/* 7708c2ecf20Sopenharmony_ci * These defines control the retry behavior for reading the config 7718c2ecf20Sopenharmony_ci * rom. It shouldn't be necessary to tweak these; if the device 7728c2ecf20Sopenharmony_ci * doesn't respond to a config rom read within 10 seconds, it's not 7738c2ecf20Sopenharmony_ci * going to respond at all. As for the initial delay, a lot of 7748c2ecf20Sopenharmony_ci * devices will be able to respond within half a second after bus 7758c2ecf20Sopenharmony_ci * reset. On the other hand, it's not really worth being more 7768c2ecf20Sopenharmony_ci * aggressive than that, since it scales pretty well; if 10 devices 7778c2ecf20Sopenharmony_ci * are plugged in, they're all getting read within one second. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci#define MAX_RETRIES 10 7818c2ecf20Sopenharmony_ci#define RETRY_DELAY (3 * HZ) 7828c2ecf20Sopenharmony_ci#define INITIAL_DELAY (HZ / 2) 7838c2ecf20Sopenharmony_ci#define SHUTDOWN_DELAY (2 * HZ) 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void fw_device_shutdown(struct work_struct *work) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct fw_device *device = 7888c2ecf20Sopenharmony_ci container_of(work, struct fw_device, work.work); 7898c2ecf20Sopenharmony_ci int minor = MINOR(device->device.devt); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (time_before64(get_jiffies_64(), 7928c2ecf20Sopenharmony_ci device->card->reset_jiffies + SHUTDOWN_DELAY) 7938c2ecf20Sopenharmony_ci && !list_empty(&device->card->link)) { 7948c2ecf20Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 7958c2ecf20Sopenharmony_ci return; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&device->state, 7998c2ecf20Sopenharmony_ci FW_DEVICE_GONE, 8008c2ecf20Sopenharmony_ci FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE) 8018c2ecf20Sopenharmony_ci return; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci fw_device_cdev_remove(device); 8048c2ecf20Sopenharmony_ci device_for_each_child(&device->device, NULL, shutdown_unit); 8058c2ecf20Sopenharmony_ci device_unregister(&device->device); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci down_write(&fw_device_rwsem); 8088c2ecf20Sopenharmony_ci idr_remove(&fw_device_idr, minor); 8098c2ecf20Sopenharmony_ci up_write(&fw_device_rwsem); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci fw_device_put(device); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic void fw_device_release(struct device *dev) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct fw_device *device = fw_device(dev); 8178c2ecf20Sopenharmony_ci struct fw_card *card = device->card; 8188c2ecf20Sopenharmony_ci unsigned long flags; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* 8218c2ecf20Sopenharmony_ci * Take the card lock so we don't set this to NULL while a 8228c2ecf20Sopenharmony_ci * FW_NODE_UPDATED callback is being handled or while the 8238c2ecf20Sopenharmony_ci * bus manager work looks at this node. 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 8268c2ecf20Sopenharmony_ci device->node->data = NULL; 8278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci fw_node_put(device->node); 8308c2ecf20Sopenharmony_ci kfree(device->config_rom); 8318c2ecf20Sopenharmony_ci kfree(device); 8328c2ecf20Sopenharmony_ci fw_card_put(card); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic struct device_type fw_device_type = { 8368c2ecf20Sopenharmony_ci .release = fw_device_release, 8378c2ecf20Sopenharmony_ci}; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic bool is_fw_device(struct device *dev) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci return dev->type == &fw_device_type; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic int update_unit(struct device *dev, void *data) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct fw_unit *unit = fw_unit(dev); 8478c2ecf20Sopenharmony_ci struct fw_driver *driver = (struct fw_driver *)dev->driver; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (is_fw_unit(dev) && driver != NULL && driver->update != NULL) { 8508c2ecf20Sopenharmony_ci device_lock(dev); 8518c2ecf20Sopenharmony_ci driver->update(unit); 8528c2ecf20Sopenharmony_ci device_unlock(dev); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic void fw_device_update(struct work_struct *work) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci struct fw_device *device = 8618c2ecf20Sopenharmony_ci container_of(work, struct fw_device, work.work); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci fw_device_cdev_update(device); 8648c2ecf20Sopenharmony_ci device_for_each_child(&device->device, NULL, update_unit); 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* 8688c2ecf20Sopenharmony_ci * If a device was pending for deletion because its node went away but its 8698c2ecf20Sopenharmony_ci * bus info block and root directory header matches that of a newly discovered 8708c2ecf20Sopenharmony_ci * device, revive the existing fw_device. 8718c2ecf20Sopenharmony_ci * The newly allocated fw_device becomes obsolete instead. 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_cistatic int lookup_existing_device(struct device *dev, void *data) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct fw_device *old = fw_device(dev); 8768c2ecf20Sopenharmony_ci struct fw_device *new = data; 8778c2ecf20Sopenharmony_ci struct fw_card *card = new->card; 8788c2ecf20Sopenharmony_ci int match = 0; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (!is_fw_device(dev)) 8818c2ecf20Sopenharmony_ci return 0; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); /* serialize config_rom access */ 8848c2ecf20Sopenharmony_ci spin_lock_irq(&card->lock); /* serialize node access */ 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && 8878c2ecf20Sopenharmony_ci atomic_cmpxchg(&old->state, 8888c2ecf20Sopenharmony_ci FW_DEVICE_GONE, 8898c2ecf20Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { 8908c2ecf20Sopenharmony_ci struct fw_node *current_node = new->node; 8918c2ecf20Sopenharmony_ci struct fw_node *obsolete_node = old->node; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci new->node = obsolete_node; 8948c2ecf20Sopenharmony_ci new->node->data = new; 8958c2ecf20Sopenharmony_ci old->node = current_node; 8968c2ecf20Sopenharmony_ci old->node->data = old; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci old->max_speed = new->max_speed; 8998c2ecf20Sopenharmony_ci old->node_id = current_node->node_id; 9008c2ecf20Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 9018c2ecf20Sopenharmony_ci old->generation = card->generation; 9028c2ecf20Sopenharmony_ci old->config_rom_retries = 0; 9038c2ecf20Sopenharmony_ci fw_notice(card, "rediscovered device %s\n", dev_name(dev)); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci old->workfn = fw_device_update; 9068c2ecf20Sopenharmony_ci fw_schedule_device_work(old, 0); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (current_node == card->root_node) 9098c2ecf20Sopenharmony_ci fw_schedule_bm_work(card, 0); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci match = 1; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci spin_unlock_irq(&card->lock); 9158c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return match; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cienum { BC_UNKNOWN = 0, BC_UNIMPLEMENTED, BC_IMPLEMENTED, }; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic void set_broadcast_channel(struct fw_device *device, int generation) 9238c2ecf20Sopenharmony_ci{ 9248c2ecf20Sopenharmony_ci struct fw_card *card = device->card; 9258c2ecf20Sopenharmony_ci __be32 data; 9268c2ecf20Sopenharmony_ci int rcode; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!card->broadcast_channel_allocated) 9298c2ecf20Sopenharmony_ci return; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * The Broadcast_Channel Valid bit is required by nodes which want to 9338c2ecf20Sopenharmony_ci * transmit on this channel. Such transmissions are practically 9348c2ecf20Sopenharmony_ci * exclusive to IP over 1394 (RFC 2734). IP capable nodes are required 9358c2ecf20Sopenharmony_ci * to be IRM capable and have a max_rec of 8 or more. We use this fact 9368c2ecf20Sopenharmony_ci * to narrow down to which nodes we send Broadcast_Channel updates. 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci if (!device->irmc || device->max_rec < 8) 9398c2ecf20Sopenharmony_ci return; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* 9428c2ecf20Sopenharmony_ci * Some 1394-1995 nodes crash if this 1394a-2000 register is written. 9438c2ecf20Sopenharmony_ci * Perform a read test first. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci if (device->bc_implemented == BC_UNKNOWN) { 9468c2ecf20Sopenharmony_ci rcode = fw_run_transaction(card, TCODE_READ_QUADLET_REQUEST, 9478c2ecf20Sopenharmony_ci device->node_id, generation, device->max_speed, 9488c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL, 9498c2ecf20Sopenharmony_ci &data, 4); 9508c2ecf20Sopenharmony_ci switch (rcode) { 9518c2ecf20Sopenharmony_ci case RCODE_COMPLETE: 9528c2ecf20Sopenharmony_ci if (data & cpu_to_be32(1 << 31)) { 9538c2ecf20Sopenharmony_ci device->bc_implemented = BC_IMPLEMENTED; 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci fallthrough; /* to case address error */ 9578c2ecf20Sopenharmony_ci case RCODE_ADDRESS_ERROR: 9588c2ecf20Sopenharmony_ci device->bc_implemented = BC_UNIMPLEMENTED; 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (device->bc_implemented == BC_IMPLEMENTED) { 9638c2ecf20Sopenharmony_ci data = cpu_to_be32(BROADCAST_CHANNEL_INITIAL | 9648c2ecf20Sopenharmony_ci BROADCAST_CHANNEL_VALID); 9658c2ecf20Sopenharmony_ci fw_run_transaction(card, TCODE_WRITE_QUADLET_REQUEST, 9668c2ecf20Sopenharmony_ci device->node_id, generation, device->max_speed, 9678c2ecf20Sopenharmony_ci CSR_REGISTER_BASE + CSR_BROADCAST_CHANNEL, 9688c2ecf20Sopenharmony_ci &data, 4); 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ciint fw_device_set_broadcast_channel(struct device *dev, void *gen) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci if (is_fw_device(dev)) 9758c2ecf20Sopenharmony_ci set_broadcast_channel(fw_device(dev), (long)gen); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void fw_device_init(struct work_struct *work) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct fw_device *device = 9838c2ecf20Sopenharmony_ci container_of(work, struct fw_device, work.work); 9848c2ecf20Sopenharmony_ci struct fw_card *card = device->card; 9858c2ecf20Sopenharmony_ci struct device *revived_dev; 9868c2ecf20Sopenharmony_ci int minor, ret; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci /* 9898c2ecf20Sopenharmony_ci * All failure paths here set node->data to NULL, so that we 9908c2ecf20Sopenharmony_ci * don't try to do device_for_each_child() on a kfree()'d 9918c2ecf20Sopenharmony_ci * device. 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci ret = read_config_rom(device, device->generation); 9958c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) { 9968c2ecf20Sopenharmony_ci if (device->config_rom_retries < MAX_RETRIES && 9978c2ecf20Sopenharmony_ci atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { 9988c2ecf20Sopenharmony_ci device->config_rom_retries++; 9998c2ecf20Sopenharmony_ci fw_schedule_device_work(device, RETRY_DELAY); 10008c2ecf20Sopenharmony_ci } else { 10018c2ecf20Sopenharmony_ci if (device->node->link_on) 10028c2ecf20Sopenharmony_ci fw_notice(card, "giving up on node %x: reading config rom failed: %s\n", 10038c2ecf20Sopenharmony_ci device->node_id, 10048c2ecf20Sopenharmony_ci fw_rcode_string(ret)); 10058c2ecf20Sopenharmony_ci if (device->node == card->root_node) 10068c2ecf20Sopenharmony_ci fw_schedule_bm_work(card, 0); 10078c2ecf20Sopenharmony_ci fw_device_release(&device->device); 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci return; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci revived_dev = device_find_child(card->device, 10138c2ecf20Sopenharmony_ci device, lookup_existing_device); 10148c2ecf20Sopenharmony_ci if (revived_dev) { 10158c2ecf20Sopenharmony_ci put_device(revived_dev); 10168c2ecf20Sopenharmony_ci fw_device_release(&device->device); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci return; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci device_initialize(&device->device); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci fw_device_get(device); 10248c2ecf20Sopenharmony_ci down_write(&fw_device_rwsem); 10258c2ecf20Sopenharmony_ci minor = idr_alloc(&fw_device_idr, device, 0, 1 << MINORBITS, 10268c2ecf20Sopenharmony_ci GFP_KERNEL); 10278c2ecf20Sopenharmony_ci up_write(&fw_device_rwsem); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci if (minor < 0) 10308c2ecf20Sopenharmony_ci goto error; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci device->device.bus = &fw_bus_type; 10338c2ecf20Sopenharmony_ci device->device.type = &fw_device_type; 10348c2ecf20Sopenharmony_ci device->device.parent = card->device; 10358c2ecf20Sopenharmony_ci device->device.devt = MKDEV(fw_cdev_major, minor); 10368c2ecf20Sopenharmony_ci dev_set_name(&device->device, "fw%d", minor); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(device->attribute_group.attrs) < 10398c2ecf20Sopenharmony_ci ARRAY_SIZE(fw_device_attributes) + 10408c2ecf20Sopenharmony_ci ARRAY_SIZE(config_rom_attributes)); 10418c2ecf20Sopenharmony_ci init_fw_attribute_group(&device->device, 10428c2ecf20Sopenharmony_ci fw_device_attributes, 10438c2ecf20Sopenharmony_ci &device->attribute_group); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (device_add(&device->device)) { 10468c2ecf20Sopenharmony_ci fw_err(card, "failed to add device\n"); 10478c2ecf20Sopenharmony_ci goto error_with_cdev; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci create_units(device); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* 10538c2ecf20Sopenharmony_ci * Transition the device to running state. If it got pulled 10548c2ecf20Sopenharmony_ci * out from under us while we did the initialization work, we 10558c2ecf20Sopenharmony_ci * have to shut down the device again here. Normally, though, 10568c2ecf20Sopenharmony_ci * fw_node_event will be responsible for shutting it down when 10578c2ecf20Sopenharmony_ci * necessary. We have to use the atomic cmpxchg here to avoid 10588c2ecf20Sopenharmony_ci * racing with the FW_NODE_DESTROYED case in 10598c2ecf20Sopenharmony_ci * fw_node_event(). 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&device->state, 10628c2ecf20Sopenharmony_ci FW_DEVICE_INITIALIZING, 10638c2ecf20Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { 10648c2ecf20Sopenharmony_ci device->workfn = fw_device_shutdown; 10658c2ecf20Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 10668c2ecf20Sopenharmony_ci } else { 10678c2ecf20Sopenharmony_ci fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n", 10688c2ecf20Sopenharmony_ci dev_name(&device->device), 10698c2ecf20Sopenharmony_ci device->config_rom[3], device->config_rom[4], 10708c2ecf20Sopenharmony_ci 1 << device->max_speed); 10718c2ecf20Sopenharmony_ci device->config_rom_retries = 0; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci set_broadcast_channel(device, device->generation); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci add_device_randomness(&device->config_rom[3], 8); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* 10798c2ecf20Sopenharmony_ci * Reschedule the IRM work if we just finished reading the 10808c2ecf20Sopenharmony_ci * root node config rom. If this races with a bus reset we 10818c2ecf20Sopenharmony_ci * just end up running the IRM work a couple of extra times - 10828c2ecf20Sopenharmony_ci * pretty harmless. 10838c2ecf20Sopenharmony_ci */ 10848c2ecf20Sopenharmony_ci if (device->node == card->root_node) 10858c2ecf20Sopenharmony_ci fw_schedule_bm_work(card, 0); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci return; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci error_with_cdev: 10908c2ecf20Sopenharmony_ci down_write(&fw_device_rwsem); 10918c2ecf20Sopenharmony_ci idr_remove(&fw_device_idr, minor); 10928c2ecf20Sopenharmony_ci up_write(&fw_device_rwsem); 10938c2ecf20Sopenharmony_ci error: 10948c2ecf20Sopenharmony_ci fw_device_put(device); /* fw_device_idr's reference */ 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci put_device(&device->device); /* our reference */ 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci/* Reread and compare bus info block and header of root directory */ 11008c2ecf20Sopenharmony_cistatic int reread_config_rom(struct fw_device *device, int generation, 11018c2ecf20Sopenharmony_ci bool *changed) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci u32 q; 11048c2ecf20Sopenharmony_ci int i, rcode; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 11078c2ecf20Sopenharmony_ci rcode = read_rom(device, generation, i, &q); 11088c2ecf20Sopenharmony_ci if (rcode != RCODE_COMPLETE) 11098c2ecf20Sopenharmony_ci return rcode; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (i == 0 && q == 0) 11128c2ecf20Sopenharmony_ci /* inaccessible (see read_config_rom); retry later */ 11138c2ecf20Sopenharmony_ci return RCODE_BUSY; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (q != device->config_rom[i]) { 11168c2ecf20Sopenharmony_ci *changed = true; 11178c2ecf20Sopenharmony_ci return RCODE_COMPLETE; 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci *changed = false; 11228c2ecf20Sopenharmony_ci return RCODE_COMPLETE; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic void fw_device_refresh(struct work_struct *work) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct fw_device *device = 11288c2ecf20Sopenharmony_ci container_of(work, struct fw_device, work.work); 11298c2ecf20Sopenharmony_ci struct fw_card *card = device->card; 11308c2ecf20Sopenharmony_ci int ret, node_id = device->node_id; 11318c2ecf20Sopenharmony_ci bool changed; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ret = reread_config_rom(device, device->generation, &changed); 11348c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) 11358c2ecf20Sopenharmony_ci goto failed_config_rom; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (!changed) { 11388c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&device->state, 11398c2ecf20Sopenharmony_ci FW_DEVICE_INITIALIZING, 11408c2ecf20Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) 11418c2ecf20Sopenharmony_ci goto gone; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci fw_device_update(work); 11448c2ecf20Sopenharmony_ci device->config_rom_retries = 0; 11458c2ecf20Sopenharmony_ci goto out; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* 11498c2ecf20Sopenharmony_ci * Something changed. We keep things simple and don't investigate 11508c2ecf20Sopenharmony_ci * further. We just destroy all previous units and create new ones. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci device_for_each_child(&device->device, NULL, shutdown_unit); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ret = read_config_rom(device, device->generation); 11558c2ecf20Sopenharmony_ci if (ret != RCODE_COMPLETE) 11568c2ecf20Sopenharmony_ci goto failed_config_rom; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci fw_device_cdev_update(device); 11598c2ecf20Sopenharmony_ci create_units(device); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Userspace may want to re-read attributes. */ 11628c2ecf20Sopenharmony_ci kobject_uevent(&device->device.kobj, KOBJ_CHANGE); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&device->state, 11658c2ecf20Sopenharmony_ci FW_DEVICE_INITIALIZING, 11668c2ecf20Sopenharmony_ci FW_DEVICE_RUNNING) == FW_DEVICE_GONE) 11678c2ecf20Sopenharmony_ci goto gone; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci fw_notice(card, "refreshed device %s\n", dev_name(&device->device)); 11708c2ecf20Sopenharmony_ci device->config_rom_retries = 0; 11718c2ecf20Sopenharmony_ci goto out; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci failed_config_rom: 11748c2ecf20Sopenharmony_ci if (device->config_rom_retries < MAX_RETRIES && 11758c2ecf20Sopenharmony_ci atomic_read(&device->state) == FW_DEVICE_INITIALIZING) { 11768c2ecf20Sopenharmony_ci device->config_rom_retries++; 11778c2ecf20Sopenharmony_ci fw_schedule_device_work(device, RETRY_DELAY); 11788c2ecf20Sopenharmony_ci return; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci fw_notice(card, "giving up on refresh of device %s: %s\n", 11828c2ecf20Sopenharmony_ci dev_name(&device->device), fw_rcode_string(ret)); 11838c2ecf20Sopenharmony_ci gone: 11848c2ecf20Sopenharmony_ci atomic_set(&device->state, FW_DEVICE_GONE); 11858c2ecf20Sopenharmony_ci device->workfn = fw_device_shutdown; 11868c2ecf20Sopenharmony_ci fw_schedule_device_work(device, SHUTDOWN_DELAY); 11878c2ecf20Sopenharmony_ci out: 11888c2ecf20Sopenharmony_ci if (node_id == card->root_node->node_id) 11898c2ecf20Sopenharmony_ci fw_schedule_bm_work(card, 0); 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic void fw_device_workfn(struct work_struct *work) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct fw_device *device = container_of(to_delayed_work(work), 11958c2ecf20Sopenharmony_ci struct fw_device, work); 11968c2ecf20Sopenharmony_ci device->workfn(work); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_civoid fw_node_event(struct fw_card *card, struct fw_node *node, int event) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci struct fw_device *device; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci switch (event) { 12048c2ecf20Sopenharmony_ci case FW_NODE_CREATED: 12058c2ecf20Sopenharmony_ci /* 12068c2ecf20Sopenharmony_ci * Attempt to scan the node, regardless whether its self ID has 12078c2ecf20Sopenharmony_ci * the L (link active) flag set or not. Some broken devices 12088c2ecf20Sopenharmony_ci * send L=0 but have an up-and-running link; others send L=1 12098c2ecf20Sopenharmony_ci * without actually having a link. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_ci create: 12128c2ecf20Sopenharmony_ci device = kzalloc(sizeof(*device), GFP_ATOMIC); 12138c2ecf20Sopenharmony_ci if (device == NULL) 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* 12178c2ecf20Sopenharmony_ci * Do minimal initialization of the device here, the 12188c2ecf20Sopenharmony_ci * rest will happen in fw_device_init(). 12198c2ecf20Sopenharmony_ci * 12208c2ecf20Sopenharmony_ci * Attention: A lot of things, even fw_device_get(), 12218c2ecf20Sopenharmony_ci * cannot be done before fw_device_init() finished! 12228c2ecf20Sopenharmony_ci * You can basically just check device->state and 12238c2ecf20Sopenharmony_ci * schedule work until then, but only while holding 12248c2ecf20Sopenharmony_ci * card->lock. 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_ci atomic_set(&device->state, FW_DEVICE_INITIALIZING); 12278c2ecf20Sopenharmony_ci device->card = fw_card_get(card); 12288c2ecf20Sopenharmony_ci device->node = fw_node_get(node); 12298c2ecf20Sopenharmony_ci device->node_id = node->node_id; 12308c2ecf20Sopenharmony_ci device->generation = card->generation; 12318c2ecf20Sopenharmony_ci device->is_local = node == card->local_node; 12328c2ecf20Sopenharmony_ci mutex_init(&device->client_list_mutex); 12338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&device->client_list); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* 12368c2ecf20Sopenharmony_ci * Set the node data to point back to this device so 12378c2ecf20Sopenharmony_ci * FW_NODE_UPDATED callbacks can update the node_id 12388c2ecf20Sopenharmony_ci * and generation for the device. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci node->data = device; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* 12438c2ecf20Sopenharmony_ci * Many devices are slow to respond after bus resets, 12448c2ecf20Sopenharmony_ci * especially if they are bus powered and go through 12458c2ecf20Sopenharmony_ci * power-up after getting plugged in. We schedule the 12468c2ecf20Sopenharmony_ci * first config rom scan half a second after bus reset. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_ci device->workfn = fw_device_init; 12498c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&device->work, fw_device_workfn); 12508c2ecf20Sopenharmony_ci fw_schedule_device_work(device, INITIAL_DELAY); 12518c2ecf20Sopenharmony_ci break; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci case FW_NODE_INITIATED_RESET: 12548c2ecf20Sopenharmony_ci case FW_NODE_LINK_ON: 12558c2ecf20Sopenharmony_ci device = node->data; 12568c2ecf20Sopenharmony_ci if (device == NULL) 12578c2ecf20Sopenharmony_ci goto create; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci device->node_id = node->node_id; 12608c2ecf20Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 12618c2ecf20Sopenharmony_ci device->generation = card->generation; 12628c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&device->state, 12638c2ecf20Sopenharmony_ci FW_DEVICE_RUNNING, 12648c2ecf20Sopenharmony_ci FW_DEVICE_INITIALIZING) == FW_DEVICE_RUNNING) { 12658c2ecf20Sopenharmony_ci device->workfn = fw_device_refresh; 12668c2ecf20Sopenharmony_ci fw_schedule_device_work(device, 12678c2ecf20Sopenharmony_ci device->is_local ? 0 : INITIAL_DELAY); 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci break; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci case FW_NODE_UPDATED: 12728c2ecf20Sopenharmony_ci device = node->data; 12738c2ecf20Sopenharmony_ci if (device == NULL) 12748c2ecf20Sopenharmony_ci break; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci device->node_id = node->node_id; 12778c2ecf20Sopenharmony_ci smp_wmb(); /* update node_id before generation */ 12788c2ecf20Sopenharmony_ci device->generation = card->generation; 12798c2ecf20Sopenharmony_ci if (atomic_read(&device->state) == FW_DEVICE_RUNNING) { 12808c2ecf20Sopenharmony_ci device->workfn = fw_device_update; 12818c2ecf20Sopenharmony_ci fw_schedule_device_work(device, 0); 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci break; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci case FW_NODE_DESTROYED: 12868c2ecf20Sopenharmony_ci case FW_NODE_LINK_OFF: 12878c2ecf20Sopenharmony_ci if (!node->data) 12888c2ecf20Sopenharmony_ci break; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci /* 12918c2ecf20Sopenharmony_ci * Destroy the device associated with the node. There 12928c2ecf20Sopenharmony_ci * are two cases here: either the device is fully 12938c2ecf20Sopenharmony_ci * initialized (FW_DEVICE_RUNNING) or we're in the 12948c2ecf20Sopenharmony_ci * process of reading its config rom 12958c2ecf20Sopenharmony_ci * (FW_DEVICE_INITIALIZING). If it is fully 12968c2ecf20Sopenharmony_ci * initialized we can reuse device->work to schedule a 12978c2ecf20Sopenharmony_ci * full fw_device_shutdown(). If not, there's work 12988c2ecf20Sopenharmony_ci * scheduled to read it's config rom, and we just put 12998c2ecf20Sopenharmony_ci * the device in shutdown state to have that code fail 13008c2ecf20Sopenharmony_ci * to create the device. 13018c2ecf20Sopenharmony_ci */ 13028c2ecf20Sopenharmony_ci device = node->data; 13038c2ecf20Sopenharmony_ci if (atomic_xchg(&device->state, 13048c2ecf20Sopenharmony_ci FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { 13058c2ecf20Sopenharmony_ci device->workfn = fw_device_shutdown; 13068c2ecf20Sopenharmony_ci fw_schedule_device_work(device, 13078c2ecf20Sopenharmony_ci list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci} 1312