Date: Sun, 7 Aug 2011 09:53:31 +0200 From: Stefan Richter Subject: firewire: core: resume remote ports when starting a host, fix DCT6200 recognition After initializing an IEEE 1394 host, broadcast a resume packet. This makes remote nodes visible which suspended their ports while the host was down. Motorola DCT6200 cable receiver was affected, perhaps other devices too. http://marc.info/?t=113202715800001 http://marc.info/?t=130576061800004 This is a forward-port from the former ieee1394 driver. http://git.kernel.org/linus/48622b7bde008387218a416586e9d072b385f1ae Signed-off-by: Stefan Richter --- drivers/firewire/core-card.c | 7 +++ drivers/firewire/core-transaction.c | 59 +++++++++++++++++++++------- drivers/firewire/core.h | 1 + include/linux/firewire.h | 1 + 4 files changed, 53 insertions(+), 15 deletions(-) Index: b/drivers/firewire/core-card.c =================================================================== --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -502,6 +502,13 @@ static void bm_work(struct work_struct * if (local_id == irm_id) allocate_broadcast_channel(card, generation); + /* + * Some devices suspend their ports while being connected to a + * powered-down card. + */ + if (!card->resume_packet_sent) + card->resume_packet_sent = + fw_send_resume_packet(card, local_id, generation) == 0; out: fw_node_put(root_node); out_put_card: Index: b/drivers/firewire/core-transaction.c =================================================================== --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -434,16 +434,18 @@ int fw_run_transaction(struct fw_card *c } EXPORT_SYMBOL(fw_run_transaction); -static DEFINE_MUTEX(phy_config_mutex); -static DECLARE_COMPLETION(phy_config_done); +static DEFINE_MUTEX(phy_packet_mutex); +static DECLARE_COMPLETION(phy_packet_done); +static int phy_packet_status; static void transmit_phy_packet_callback(struct fw_packet *packet, struct fw_card *card, int status) { - complete(&phy_config_done); + phy_packet_status = status; + complete(&phy_packet_done); } -static struct fw_packet phy_config_packet = { +static struct fw_packet phy_packet = { .header_length = 12, .header[0] = TCODE_LINK_INTERNAL << 4, .payload_length = 0, @@ -451,10 +453,41 @@ static struct fw_packet phy_config_packe .callback = transmit_phy_packet_callback, }; +static int send_phy_packet(struct fw_card *card, int generation, u32 data) +{ + long timeout = DIV_ROUND_UP(HZ, 10); + int ret; + + mutex_lock(&phy_packet_mutex); + + phy_packet.header[1] = data; + phy_packet.header[2] = ~data; + phy_packet.generation = generation; + phy_packet.ack = 0; + INIT_COMPLETION(phy_packet_done); + + card->driver->send_request(card, &phy_packet); + timeout = wait_for_completion_timeout(&phy_packet_done, timeout); + if (timeout == 0) + ret = -EIO; + else switch (phy_packet_status) { + case ACK_COMPLETE: + case ACK_PENDING: + case RCODE_NO_ACK: + ret = 0; + break; + default: + ret = -EIO; + } + + mutex_unlock(&phy_packet_mutex); + + return ret; +} + void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count) { - long timeout = DIV_ROUND_UP(HZ, 10); u32 data = PHY_IDENTIFIER(PHY_PACKET_CONFIG); if (node_id != FW_PHY_CONFIG_NO_NODE_ID) @@ -471,17 +504,13 @@ void fw_send_phy_config(struct fw_card * } data |= PHY_CONFIG_GAP_COUNT(gap_count); - mutex_lock(&phy_config_mutex); - - phy_config_packet.header[1] = data; - phy_config_packet.header[2] = ~data; - phy_config_packet.generation = generation; - INIT_COMPLETION(phy_config_done); - - card->driver->send_request(card, &phy_config_packet); - wait_for_completion_timeout(&phy_config_done, timeout); + send_phy_packet(card, generation, data); +} - mutex_unlock(&phy_config_mutex); +int fw_send_resume_packet(struct fw_card *card, int node_id, int generation) +{ + return send_phy_packet(card, generation, + (node_id & 0x3f) << 24 | 0xf << 18); } static struct fw_address_handler *lookup_overlapping_address_handler( Index: b/drivers/firewire/core.h =================================================================== --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -236,6 +236,7 @@ void fw_fill_response(struct fw_packet * #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); +int fw_send_resume_packet(struct fw_card *card, int node_id, int generation); static inline bool is_ping_packet(u32 *data) { Index: b/include/linux/firewire.h =================================================================== --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -127,6 +127,7 @@ struct fw_card { int bm_generation; int bm_node_id; bool bm_abdicate; + bool resume_packet_sent; bool priority_budget_implemented; /* controller feature */ bool broadcast_channel_auto_allocated; /* controller feature */