ALSA: dice: split subaddress check from category check

Before allocating an instance of sound card, ALSA dice driver checks
chip_ID_hi in Bus information block of Config ROM, then also checks
subaddresses. The former operation reads cache of Config ROM in Linux
FireWire subsystem, while the latter operation sends read transaction.
The latter can be merged into initialization of transaction system.

This commit splits these two operations to reduce needless transactions
in probe processing.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2015-12-31 13:58:11 +09:00 committed by Takashi Iwai
parent d8c5ed752e
commit 4a47a87def
2 changed files with 78 additions and 84 deletions

View File

@ -331,39 +331,60 @@ int snd_dice_transaction_reinit(struct snd_dice *dice)
return register_notification_address(dice, false);
}
int snd_dice_transaction_init(struct snd_dice *dice)
static int get_subaddrs(struct snd_dice *dice)
{
struct fw_address_handler *handler = &dice->notification_handler;
static const int min_values[10] = {
10, 0x64 / 4,
10, 0x18 / 4,
10, 0x18 / 4,
0, 0,
0, 0,
};
__be32 *pointers;
__be32 version;
u32 data;
unsigned int i;
int err;
/* Use the same way which dice_interface_check() does. */
pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
GFP_KERNEL);
if (pointers == NULL)
return -ENOMEM;
/* Get offsets for sub-addresses */
/*
* Check that the sub address spaces exist and are located inside the
* private address space. The minimum values are chosen so that all
* minimally required registers are included.
*/
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
DICE_PRIVATE_SPACE,
pointers, sizeof(__be32) * 10, 0);
DICE_PRIVATE_SPACE, pointers,
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
if (err < 0)
goto end;
/* Allocation callback in address space over host controller */
handler->length = 4;
handler->address_callback = dice_notification;
handler->callback_data = dice;
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
if (err < 0) {
handler->callback_data = NULL;
goto end;
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
data = be32_to_cpu(pointers[i]);
if (data < min_values[i] || data >= 0x40000) {
err = -ENODEV;
goto end;
}
}
/* Register the address space */
err = register_notification_address(dice, true);
if (err < 0) {
fw_core_remove_address_handler(handler);
handler->callback_data = NULL;
/*
* Check that the implemented DICE driver specification major version
* number matches.
*/
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
DICE_PRIVATE_SPACE +
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
&version, sizeof(version), 0);
if (err < 0)
goto end;
if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
dev_err(&dice->unit->device,
"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
err = -ENODEV;
goto end;
}
@ -380,3 +401,32 @@ end:
kfree(pointers);
return err;
}
int snd_dice_transaction_init(struct snd_dice *dice)
{
struct fw_address_handler *handler = &dice->notification_handler;
int err;
err = get_subaddrs(dice);
if (err < 0)
return err;
/* Allocation callback in address space over host controller */
handler->length = 4;
handler->address_callback = dice_notification;
handler->callback_data = dice;
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
if (err < 0) {
handler->callback_data = NULL;
return err;
}
/* Register the address space */
err = register_notification_address(dice, true);
if (err < 0) {
fw_core_remove_address_handler(handler);
handler->callback_data = NULL;
}
return err;
}

View File

@ -18,27 +18,12 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10
static int dice_interface_check(struct fw_unit *unit)
static int check_dice_category(struct fw_unit *unit)
{
static const int min_values[10] = {
10, 0x64 / 4,
10, 0x18 / 4,
10, 0x18 / 4,
0, 0,
0, 0,
};
struct fw_device *device = fw_parent_device(unit);
struct fw_csr_iterator it;
int key, val, vendor = -1, model = -1, err;
unsigned int category, i;
__be32 *pointers;
u32 value;
__be32 version;
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
GFP_KERNEL);
if (pointers == NULL)
return -ENOMEM;
int key, val, vendor = -1, model = -1;
unsigned int category;
/*
* Check that GUID and unit directory are constructed according to DICE
@ -64,51 +49,10 @@ static int dice_interface_check(struct fw_unit *unit)
else
category = DICE_CATEGORY_ID;
if (device->config_rom[3] != ((vendor << 8) | category) ||
device->config_rom[4] >> 22 != model) {
err = -ENODEV;
goto end;
}
device->config_rom[4] >> 22 != model)
return -ENODEV;
/*
* Check that the sub address spaces exist and are located inside the
* private address space. The minimum values are chosen so that all
* minimally required registers are included.
*/
err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
DICE_PRIVATE_SPACE, pointers,
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
if (err < 0) {
err = -ENODEV;
goto end;
}
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
value = be32_to_cpu(pointers[i]);
if (value < min_values[i] || value >= 0x40000) {
err = -ENODEV;
goto end;
}
}
/*
* Check that the implemented DICE driver specification major version
* number matches.
*/
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
DICE_PRIVATE_SPACE +
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
&version, 4, 0);
if (err < 0) {
err = -ENODEV;
goto end;
}
if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
dev_err(&unit->device,
"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
err = -ENODEV;
goto end;
}
end:
return err;
return 0;
}
static int highest_supported_mode_rate(struct snd_dice *dice,
@ -254,9 +198,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
struct snd_dice *dice;
int err;
err = dice_interface_check(unit);
err = check_dice_category(unit);
if (err < 0)
goto end;
return -ENODEV;
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
sizeof(*dice), &card);