diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index b1deb5214bd4..d929eb6fef6a 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -30,7 +30,7 @@ * polynomial, but we need the ITU-T (or CCITT) polynomial (0x1021). * The implementation below works on an array of host-endian u32 * words, assuming they'll be transmited msb first. */ -static u16 +u16 crc16_itu_t(const u32 *buffer, size_t length) { int shift, i; diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 9e8a8f909303..a9e13468f60c 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -813,8 +813,10 @@ handle_local_request(struct context *ctx, struct fw_packet *packet) u64 offset; u32 csr; - packet->ack = ACK_PENDING; - packet->callback(packet, &ctx->ohci->card, packet->ack); + if (ctx == &ctx->ohci->at_request_ctx) { + packet->ack = ACK_PENDING; + packet->callback(packet, &ctx->ohci->card, packet->ack); + } offset = ((unsigned long long) @@ -839,6 +841,11 @@ handle_local_request(struct context *ctx, struct fw_packet *packet) fw_core_handle_response(&ctx->ohci->card, packet); break; } + + if (ctx == &ctx->ohci->at_response_ctx) { + packet->ack = ACK_COMPLETE; + packet->callback(packet, &ctx->ohci->card, packet->ack); + } } static void diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index 36c9be75b025..7923463fdbf3 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c @@ -163,11 +163,12 @@ static void update_hop_count(struct fw_node *node) * internally consistent. On succcess this funtions returns the * fw_node corresponding to the local card otherwise NULL. */ -static struct fw_node *build_tree(struct fw_card *card) +static struct fw_node *build_tree(struct fw_card *card, + u32 *sid, int self_id_count) { struct fw_node *node, *child, *local_node; struct list_head stack, *h; - u32 *sid, *next_sid, *end, q; + u32 *next_sid, *end, q; int i, port_count, child_port_count, phy_id, parent_count, stack_depth; int gap_count, topology_type; @@ -175,8 +176,7 @@ static struct fw_node *build_tree(struct fw_card *card) node = NULL; INIT_LIST_HEAD(&stack); stack_depth = 0; - sid = card->self_ids; - end = sid + card->self_id_count; + end = sid + self_id_count; phy_id = 0; card->irm_node = NULL; gap_count = self_id_gap_count(*sid); @@ -460,6 +460,20 @@ update_tree(struct fw_card *card, struct fw_node *root) } } +static void +update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count) +{ + int node_count; + u32 crc; + + card->topology_map[1]++; + node_count = (card->root_node->node_id & 0x3f) + 1; + card->topology_map[2] = (node_count << 16) | self_id_count; + crc = crc16_itu_t(card->topology_map + 1, self_id_count + 2); + card->topology_map[0] = ((self_id_count + 2) << 16) | crc; + memcpy(&card->topology_map[3], self_ids, self_id_count * 4); +} + void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, @@ -479,13 +493,13 @@ fw_core_handle_bus_reset(struct fw_card *card, card->bm_retries = 0; card->node_id = node_id; - card->self_id_count = self_id_count; card->generation = generation; - memcpy(card->self_ids, self_ids, self_id_count * 4); card->reset_jiffies = jiffies; schedule_delayed_work(&card->work, 0); - local_node = build_tree(card); + local_node = build_tree(card, self_ids, self_id_count); + + update_topology_map(card, self_ids, self_id_count); card->color++; diff --git a/drivers/firewire/fw-topology.h b/drivers/firewire/fw-topology.h index f2a575e05ae1..913bfe160882 100644 --- a/drivers/firewire/fw-topology.h +++ b/drivers/firewire/fw-topology.h @@ -88,4 +88,7 @@ fw_node_put(struct fw_node *node) void fw_destroy_nodes(struct fw_card *card); +u16 +crc16_itu_t(const u32 *buffer, size_t length); + #endif /* __fw_topology_h */ diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 3052698c13a6..38b286ed744f 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c @@ -140,7 +140,7 @@ transmit_complete_callback(struct fw_packet *packet, static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, - int node_id, int generation, int speed, + int node_id, int source_id, int generation, int speed, unsigned long long offset, void *payload, size_t length) { int ext_tcode; @@ -157,7 +157,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, header_tcode(tcode) | header_destination(node_id); packet->header[1] = - header_offset_high(offset >> 32) | header_source(0); + header_offset_high(offset >> 32) | header_source(source_id); packet->header[2] = offset; @@ -241,7 +241,7 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, fw_transaction_callback_t callback, void *callback_data) { unsigned long flags; - int tlabel; + int tlabel, source; /* Bump the flush timer up 100ms first of all so we * don't race with a flush timer callback. */ @@ -253,6 +253,7 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, spin_lock_irqsave(&card->lock, flags); + source = card->node_id; tlabel = card->current_tlabel; if (card->tlabel_mask & (1 << tlabel)) { spin_unlock_irqrestore(&card->lock, flags); @@ -274,7 +275,8 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, t->callback_data = callback_data; fw_fill_request(&t->packet, tcode, t->tlabel, - node_id, generation, speed, offset, payload, length); + node_id, source, generation, + speed, offset, payload, length); t->packet.callback = transmit_complete_callback; card->driver->send_request(card, &t->packet); @@ -716,6 +718,44 @@ fw_core_handle_response(struct fw_card *card, struct fw_packet *p) } EXPORT_SYMBOL(fw_core_handle_response); +const struct fw_address_region topology_map_region = + { .start = 0xfffff0001000ull, .end = 0xfffff0001400ull, }; + +static void +handle_topology_map(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, int speed, + unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + int i, start, end; + u32 *map; + + if (!TCODE_IS_READ_REQUEST(tcode)) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + + if ((offset & 3) > 0 || (length & 3) > 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + start = (offset - topology_map_region.start) / 4; + end = start + length / 4; + map = payload; + + for (i = 0; i < length / 4; i++) + map[i] = cpu_to_be32(card->topology_map[start + i]); + + fw_send_response(card, request, RCODE_COMPLETE); +} + +static struct fw_address_handler topology_map = { + .length = 0x400, + .address_callback = handle_topology_map, +}; + MODULE_AUTHOR("Kristian Hoegsberg "); MODULE_DESCRIPTION("Core IEEE1394 transaction logic"); MODULE_LICENSE("GPL"); @@ -767,6 +807,10 @@ static int __init fw_core_init(void) return fw_cdev_major; } + retval = fw_core_add_address_handler(&topology_map, + &topology_map_region); + BUG_ON(retval < 0); + /* Add the vendor textual descriptor. */ retval = fw_core_add_descriptor(&vendor_id_descriptor); BUG_ON(retval < 0); diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index e7301b83f91e..a661afb9d68f 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h @@ -285,9 +285,10 @@ struct fw_card { int link_speed; int config_rom_generation; - /* We need to store up to 4 self ID for a maximum of 63 devices. */ + /* We need to store up to 4 self ID for a maximum of 63 + * devices plus 3 words for the topology map header. */ int self_id_count; - u32 self_ids[252]; + u32 topology_map[252 + 3]; spinlock_t lock; /* Take this lock when handling the lists in * this struct. */