📄 fw-transaction.c
字号:
* deallocated region, payload is set to NULL. */void fw_core_remove_address_handler(struct fw_address_handler *handler){ unsigned long flags; spin_lock_irqsave(&address_handler_lock, flags); list_del(&handler->link); spin_unlock_irqrestore(&address_handler_lock, flags);}EXPORT_SYMBOL(fw_core_remove_address_handler);struct fw_request { struct fw_packet response; u32 request_header[4]; int ack; u32 length; u32 data[0];};static voidfree_response_callback(struct fw_packet *packet, struct fw_card *card, int status){ struct fw_request *request; request = container_of(packet, struct fw_request, response); kfree(request);}voidfw_fill_response(struct fw_packet *response, u32 *request_header, int rcode, void *payload, size_t length){ int tcode, tlabel, extended_tcode, source, destination; tcode = HEADER_GET_TCODE(request_header[0]); tlabel = HEADER_GET_TLABEL(request_header[0]); source = HEADER_GET_DESTINATION(request_header[0]); destination = HEADER_GET_SOURCE(request_header[1]); extended_tcode = HEADER_GET_EXTENDED_TCODE(request_header[3]); response->header[0] = HEADER_RETRY(RETRY_1) | HEADER_TLABEL(tlabel) | HEADER_DESTINATION(destination); response->header[1] = HEADER_SOURCE(source) | HEADER_RCODE(rcode); response->header[2] = 0; switch (tcode) { case TCODE_WRITE_QUADLET_REQUEST: case TCODE_WRITE_BLOCK_REQUEST: response->header[0] |= HEADER_TCODE(TCODE_WRITE_RESPONSE); response->header_length = 12; response->payload_length = 0; break; case TCODE_READ_QUADLET_REQUEST: response->header[0] |= HEADER_TCODE(TCODE_READ_QUADLET_RESPONSE); if (payload != NULL) response->header[3] = *(u32 *)payload; else response->header[3] = 0; response->header_length = 16; response->payload_length = 0; break; case TCODE_READ_BLOCK_REQUEST: case TCODE_LOCK_REQUEST: response->header[0] |= HEADER_TCODE(tcode + 2); response->header[3] = HEADER_DATA_LENGTH(length) | HEADER_EXTENDED_TCODE(extended_tcode); response->header_length = 16; response->payload = payload; response->payload_length = length; break; default: BUG(); return; }}EXPORT_SYMBOL(fw_fill_response);static struct fw_request *allocate_request(struct fw_packet *p){ struct fw_request *request; u32 *data, length; int request_tcode, t; request_tcode = HEADER_GET_TCODE(p->header[0]); switch (request_tcode) { case TCODE_WRITE_QUADLET_REQUEST: data = &p->header[3]; length = 4; break; case TCODE_WRITE_BLOCK_REQUEST: case TCODE_LOCK_REQUEST: data = p->payload; length = HEADER_GET_DATA_LENGTH(p->header[3]); break; case TCODE_READ_QUADLET_REQUEST: data = NULL; length = 4; break; case TCODE_READ_BLOCK_REQUEST: data = NULL; length = HEADER_GET_DATA_LENGTH(p->header[3]); break; default: BUG(); return NULL; } request = kmalloc(sizeof(*request) + length, GFP_ATOMIC); if (request == NULL) return NULL; t = (p->timestamp & 0x1fff) + 4000; if (t >= 8000) t = (p->timestamp & ~0x1fff) + 0x2000 + t - 8000; else t = (p->timestamp & ~0x1fff) + t; request->response.speed = p->speed; request->response.timestamp = t; request->response.generation = p->generation; request->response.ack = 0; request->response.callback = free_response_callback; request->ack = p->ack; request->length = length; if (data) memcpy(request->data, data, length); memcpy(request->request_header, p->header, sizeof(p->header)); return request;}voidfw_send_response(struct fw_card *card, struct fw_request *request, int rcode){ /* * Broadcast packets are reported as ACK_COMPLETE, so this * check is sufficient to ensure we don't send response to * broadcast packets or posted writes. */ if (request->ack != ACK_PENDING) { kfree(request); return; } if (rcode == RCODE_COMPLETE) fw_fill_response(&request->response, request->request_header, rcode, request->data, request->length); else fw_fill_response(&request->response, request->request_header, rcode, NULL, 0); card->driver->send_response(card, &request->response);}EXPORT_SYMBOL(fw_send_response);voidfw_core_handle_request(struct fw_card *card, struct fw_packet *p){ struct fw_address_handler *handler; struct fw_request *request; unsigned long long offset; unsigned long flags; int tcode, destination, source; if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; request = allocate_request(p); if (request == NULL) { /* FIXME: send statically allocated busy packet. */ return; } offset = ((unsigned long long) HEADER_GET_OFFSET_HIGH(p->header[1]) << 32) | p->header[2]; tcode = HEADER_GET_TCODE(p->header[0]); destination = HEADER_GET_DESTINATION(p->header[0]); source = HEADER_GET_SOURCE(p->header[0]); spin_lock_irqsave(&address_handler_lock, flags); handler = lookup_enclosing_address_handler(&address_handler_list, offset, request->length); spin_unlock_irqrestore(&address_handler_lock, flags); /* * FIXME: lookup the fw_node corresponding to the sender of * this request and pass that to the address handler instead * of the node ID. We may also want to move the address * allocations to fw_node so we only do this callback if the * upper layers registered it for this node. */ if (handler == NULL) fw_send_response(card, request, RCODE_ADDRESS_ERROR); else handler->address_callback(card, request, tcode, destination, source, p->generation, p->speed, offset, request->data, request->length, handler->callback_data);}EXPORT_SYMBOL(fw_core_handle_request);voidfw_core_handle_response(struct fw_card *card, struct fw_packet *p){ struct fw_transaction *t; unsigned long flags; u32 *data; size_t data_length; int tcode, tlabel, destination, source, rcode; tcode = HEADER_GET_TCODE(p->header[0]); tlabel = HEADER_GET_TLABEL(p->header[0]); destination = HEADER_GET_DESTINATION(p->header[0]); source = HEADER_GET_SOURCE(p->header[1]); rcode = HEADER_GET_RCODE(p->header[1]); spin_lock_irqsave(&card->lock, flags); list_for_each_entry(t, &card->transaction_list, link) { if (t->node_id == source && t->tlabel == tlabel) { list_del(&t->link); card->tlabel_mask &= ~(1 << t->tlabel); break; } } spin_unlock_irqrestore(&card->lock, flags); if (&t->link == &card->transaction_list) { fw_notify("Unsolicited response (source %x, tlabel %x)\n", source, tlabel); return; } /* * FIXME: sanity check packet, is length correct, does tcodes * and addresses match. */ switch (tcode) { case TCODE_READ_QUADLET_RESPONSE: data = (u32 *) &p->header[3]; data_length = 4; break; case TCODE_WRITE_RESPONSE: data = NULL; data_length = 0; break; case TCODE_READ_BLOCK_RESPONSE: case TCODE_LOCK_RESPONSE: data = p->payload; data_length = HEADER_GET_DATA_LENGTH(p->header[3]); break; default: /* Should never happen, this is just to shut up gcc. */ data = NULL; data_length = 0; break; } t->callback(card, rcode, data, data_length, t->callback_data);}EXPORT_SYMBOL(fw_core_handle_response);static const struct fw_address_region topology_map_region = { .start = 0xfffff0001000ull, .end = 0xfffff0001400ull, };static voidhandle_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 = 0x200, .address_callback = handle_topology_map,};static const struct fw_address_region registers_region = { .start = 0xfffff0000000ull, .end = 0xfffff0000400ull, };static voidhandle_registers(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 reg = offset - CSR_REGISTER_BASE; unsigned long long bus_time; __be32 *data = payload; switch (reg) { case CSR_CYCLE_TIME: case CSR_BUS_TIME: if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) { fw_send_response(card, request, RCODE_TYPE_ERROR); break; } bus_time = card->driver->get_bus_time(card); if (reg == CSR_CYCLE_TIME) *data = cpu_to_be32(bus_time); else *data = cpu_to_be32(bus_time >> 25); fw_send_response(card, request, RCODE_COMPLETE); break; case CSR_BUS_MANAGER_ID: case CSR_BANDWIDTH_AVAILABLE: case CSR_CHANNELS_AVAILABLE_HI: case CSR_CHANNELS_AVAILABLE_LO: /* * FIXME: these are handled by the OHCI hardware and * the stack never sees these request. If we add * support for a new type of controller that doesn't * handle this in hardware we need to deal with these * transactions. */ BUG(); break; case CSR_BUSY_TIMEOUT: /* FIXME: Implement this. */ default: fw_send_response(card, request, RCODE_ADDRESS_ERROR); break; }}static struct fw_address_handler registers = { .length = 0x400, .address_callback = handle_registers,};MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");MODULE_DESCRIPTION("Core IEEE1394 transaction logic");MODULE_LICENSE("GPL");static const u32 vendor_textual_descriptor[] = { /* textual descriptor leaf () */ 0x00060000, 0x00000000, 0x00000000, 0x4c696e75, /* L i n u */ 0x78204669, /* x F i */ 0x72657769, /* r e w i */ 0x72650000, /* r e */};static const u32 model_textual_descriptor[] = { /* model descriptor leaf () */ 0x00030000, 0x00000000, 0x00000000, 0x4a756a75, /* J u j u */};static struct fw_descriptor vendor_id_descriptor = { .length = ARRAY_SIZE(vendor_textual_descriptor), .immediate = 0x03d00d1e, .key = 0x81000000, .data = vendor_textual_descriptor,};static struct fw_descriptor model_id_descriptor = { .length = ARRAY_SIZE(model_textual_descriptor), .immediate = 0x17000001, .key = 0x81000000, .data = model_textual_descriptor,};static int __init fw_core_init(void){ int retval; retval = bus_register(&fw_bus_type); if (retval < 0) return retval; fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops); if (fw_cdev_major < 0) { bus_unregister(&fw_bus_type); return fw_cdev_major; } retval = fw_core_add_address_handler(&topology_map, &topology_map_region); BUG_ON(retval < 0); retval = fw_core_add_address_handler(®isters, ®isters_region); BUG_ON(retval < 0); /* Add the vendor textual descriptor. */ retval = fw_core_add_descriptor(&vendor_id_descriptor); BUG_ON(retval < 0); retval = fw_core_add_descriptor(&model_id_descriptor); BUG_ON(retval < 0); return 0;}static void __exit fw_core_cleanup(void){ unregister_chrdev(fw_cdev_major, "firewire"); bus_unregister(&fw_bus_type);}module_init(fw_core_init);module_exit(fw_core_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -