diff options
-rw-r--r-- | drivers/firewire/.kunitconfig | 4 | ||||
-rw-r--r-- | drivers/firewire/Kconfig | 16 | ||||
-rw-r--r-- | drivers/firewire/Makefile | 3 | ||||
-rw-r--r-- | drivers/firewire/core-cdev.c | 252 | ||||
-rw-r--r-- | drivers/firewire/core-device.c | 2 | ||||
-rw-r--r-- | drivers/firewire/core-topology.c | 2 | ||||
-rw-r--r-- | drivers/firewire/core-transaction.c | 93 | ||||
-rw-r--r-- | drivers/firewire/core.h | 7 | ||||
-rw-r--r-- | drivers/firewire/net.c | 6 | ||||
-rw-r--r-- | drivers/firewire/ohci.c | 191 | ||||
-rw-r--r-- | drivers/firewire/uapi-test.c | 89 | ||||
-rw-r--r-- | include/linux/firewire.h | 82 | ||||
-rw-r--r-- | include/uapi/linux/firewire-cdev.h | 178 |
13 files changed, 694 insertions, 231 deletions
diff --git a/drivers/firewire/.kunitconfig b/drivers/firewire/.kunitconfig new file mode 100644 index 000000000000..1599e069395f --- /dev/null +++ b/drivers/firewire/.kunitconfig @@ -0,0 +1,4 @@ +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_FIREWIRE=y +CONFIG_FIREWIRE_KUNIT_UAPI_TEST=y diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index ec00a6f70da8..0a6596b027db 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -18,6 +18,22 @@ config FIREWIRE To compile this driver as a module, say M here: the module will be called firewire-core. +config FIREWIRE_KUNIT_UAPI_TEST + tristate "KUnit tests for layout of structure in UAPI" if !KUNIT_ALL_TESTS + depends on FIREWIRE && KUNIT + default KUNIT_ALL_TESTS + help + This builds the KUnit tests whether structures exposed to user + space have expected layout. + + KUnit tests run during boot and output the results to the debug + log in TAP format (https://testanything.org/). Only useful for + kernel devs running KUnit test harness and are not for inclusion + into a production build. + + For more information on KUnit and unit tests in general, refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + config FIREWIRE_OHCI tristate "OHCI-1394 controllers" depends on PCI && FIREWIRE && MMU diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile index e58c8c794778..b24b2879ac34 100644 --- a/drivers/firewire/Makefile +++ b/drivers/firewire/Makefile @@ -15,3 +15,6 @@ obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o obj-$(CONFIG_FIREWIRE_NOSY) += nosy.o obj-$(CONFIG_PROVIDE_OHCI1394_DMA_INIT) += init_ohci1394_dma.o + +firewire-uapi-test-objs += uapi-test.o +obj-$(CONFIG_FIREWIRE_KUNIT_UAPI_TEST) += firewire-uapi-test.o diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2c16ee8fd842..6274b86eb943 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -43,6 +43,7 @@ #define FW_CDEV_VERSION_EVENT_REQUEST2 4 #define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 #define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW 5 +#define FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP 6 struct client { u32 version; @@ -169,7 +170,10 @@ struct outbound_transaction_event { struct event event; struct client *client; struct outbound_transaction_resource r; - struct fw_cdev_event_response response; + union { + struct fw_cdev_event_response without_tstamp; + struct fw_cdev_event_response2 with_tstamp; + } rsp; }; struct inbound_transaction_event { @@ -177,6 +181,7 @@ struct inbound_transaction_event { union { struct fw_cdev_event_request request; struct fw_cdev_event_request2 request2; + struct fw_cdev_event_request3 with_tstamp; } req; }; @@ -199,12 +204,18 @@ struct outbound_phy_packet_event { struct event event; struct client *client; struct fw_packet p; - struct fw_cdev_event_phy_packet phy_packet; + union { + struct fw_cdev_event_phy_packet without_tstamp; + struct fw_cdev_event_phy_packet2 with_tstamp; + } phy_packet; }; struct inbound_phy_packet_event { struct event event; - struct fw_cdev_event_phy_packet phy_packet; + union { + struct fw_cdev_event_phy_packet without_tstamp; + struct fw_cdev_event_phy_packet2 with_tstamp; + } phy_packet; }; #ifdef CONFIG_COMPAT @@ -534,41 +545,64 @@ static void release_transaction(struct client *client, { } -static void complete_transaction(struct fw_card *card, int rcode, - void *payload, size_t length, void *data) +static void complete_transaction(struct fw_card *card, int rcode, u32 request_tstamp, + u32 response_tstamp, void *payload, size_t length, void *data) { struct outbound_transaction_event *e = data; - struct fw_cdev_event_response *rsp = &e->response; struct client *client = e->client; unsigned long flags; - if (length < rsp->length) - rsp->length = length; - if (rcode == RCODE_COMPLETE) - memcpy(rsp->data, payload, rsp->length); - spin_lock_irqsave(&client->lock, flags); idr_remove(&client->resource_idr, e->r.resource.handle); if (client->in_shutdown) wake_up(&client->tx_flush_wait); spin_unlock_irqrestore(&client->lock, flags); - rsp->type = FW_CDEV_EVENT_RESPONSE; - rsp->rcode = rcode; + switch (e->rsp.without_tstamp.type) { + case FW_CDEV_EVENT_RESPONSE: + { + struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; + + if (length < rsp->length) + rsp->length = length; + if (rcode == RCODE_COMPLETE) + memcpy(rsp->data, payload, rsp->length); + + rsp->rcode = rcode; + + // In the case that sizeof(*rsp) doesn't align with the position of the + // data, and the read is short, preserve an extra copy of the data + // to stay compatible with a pre-2.6.27 bug. Since the bug is harmless + // for short reads and some apps depended on it, this is both safe + // and prudent for compatibility. + if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) + queue_event(client, &e->event, rsp, sizeof(*rsp), rsp->data, rsp->length); + else + queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); - /* - * In the case that sizeof(*rsp) doesn't align with the position of the - * data, and the read is short, preserve an extra copy of the data - * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless - * for short reads and some apps depended on it, this is both safe - * and prudent for compatibility. - */ - if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) - queue_event(client, &e->event, rsp, sizeof(*rsp), - rsp->data, rsp->length); - else - queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, - NULL, 0); + break; + } + case FW_CDEV_EVENT_RESPONSE2: + { + struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; + + if (length < rsp->length) + rsp->length = length; + if (rcode == RCODE_COMPLETE) + memcpy(rsp->data, payload, rsp->length); + + rsp->rcode = rcode; + rsp->request_tstamp = request_tstamp; + rsp->response_tstamp = response_tstamp; + + queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); + + break; + default: + WARN_ON(1); + break; + } + } /* Drop the idr's reference */ client_put(client); @@ -579,6 +613,7 @@ static int init_request(struct client *client, int destination_id, int speed) { struct outbound_transaction_event *e; + void *payload; int ret; if (request->tcode != TCODE_STREAM_DATA && @@ -592,14 +627,25 @@ static int init_request(struct client *client, e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); if (e == NULL) return -ENOMEM; - e->client = client; - e->response.length = request->length; - e->response.closure = request->closure; - if (request->data && - copy_from_user(e->response.data, - u64_to_uptr(request->data), request->length)) { + if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { + struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; + + rsp->type = FW_CDEV_EVENT_RESPONSE; + rsp->length = request->length; + rsp->closure = request->closure; + payload = rsp->data; + } else { + struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; + + rsp->type = FW_CDEV_EVENT_RESPONSE2; + rsp->length = request->length; + rsp->closure = request->closure; + payload = rsp->data; + } + + if (request->data && copy_from_user(payload, u64_to_uptr(request->data), request->length)) { ret = -EFAULT; goto failed; } @@ -609,10 +655,9 @@ static int init_request(struct client *client, if (ret < 0) goto failed; - fw_send_request(client->device->card, &e->r.transaction, - request->tcode, destination_id, request->generation, - speed, request->offset, e->response.data, - request->length, complete_transaction, e); + fw_send_request_with_tstamp(client->device->card, &e->r.transaction, request->tcode, + destination_id, request->generation, speed, request->offset, + payload, request->length, complete_transaction, e); return 0; failed: @@ -708,7 +753,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request, req->handle = r->resource.handle; req->closure = handler->closure; event_size0 = sizeof(*req); - } else { + } else if (handler->client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { struct fw_cdev_event_request2 *req = &e->req.request2; req->type = FW_CDEV_EVENT_REQUEST2; @@ -722,6 +767,21 @@ static void handle_request(struct fw_card *card, struct fw_request *request, req->handle = r->resource.handle; req->closure = handler->closure; event_size0 = sizeof(*req); + } else { + struct fw_cdev_event_request3 *req = &e->req.with_tstamp; + + req->type = FW_CDEV_EVENT_REQUEST3; + req->tcode = tcode; + req->offset = offset; + req->source_node_id = source; + req->destination_node_id = destination; + req->card = card->index; + req->generation = generation; + req->length = length; + req->handle = r->resource.handle; + req->closure = handler->closure; + req->tstamp = fw_request_get_timestamp(request); + event_size0 = sizeof(*req); } queue_event(handler->client, &e->event, @@ -1495,26 +1555,61 @@ static void outbound_phy_packet_callback(struct fw_packet *packet, { struct outbound_phy_packet_event *e = container_of(packet, struct outbound_phy_packet_event, p); - struct client *e_client; + struct client *e_client = e->client; + u32 rcode; switch (status) { - /* expected: */ - case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; - /* should never happen with PHY packets: */ - case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; + // expected: + case ACK_COMPLETE: + rcode = RCODE_COMPLETE; + break; + // should never happen with PHY packets: + case ACK_PENDING: + rcode = RCODE_COMPLETE; + break; case ACK_BUSY_X: case ACK_BUSY_A: - case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; - case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; - case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; - /* stale generation; cancelled; on certain controllers: no ack */ - default: e->phy_packet.rcode = status; break; + case ACK_BUSY_B: + rcode = RCODE_BUSY; + break; + case ACK_DATA_ERROR: + rcode = RCODE_DATA_ERROR; + break; + case ACK_TYPE_ERROR: + rcode = RCODE_TYPE_ERROR; + break; + // stale generation; cancelled; on certain controllers: no ack + default: + rcode = status; + break; + } + + switch (e->phy_packet.without_tstamp.type) { + case FW_CDEV_EVENT_PHY_PACKET_SENT: + { + struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + + pp->rcode = rcode; + pp->data[0] = packet->timestamp; + queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, + NULL, 0); + break; + } + case FW_CDEV_EVENT_PHY_PACKET_SENT2: + { + struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + + pp->rcode = rcode; + pp->tstamp = packet->timestamp; + queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, + NULL, 0); + break; + } + default: + WARN_ON(1); + break; } - e->phy_packet.data[0] = packet->timestamp; - e_client = e->client; - queue_event(e->client, &e->event, &e->phy_packet, - sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); client_put(e_client); } @@ -1528,7 +1623,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) if (!client->device->is_local) return -ENOSYS; - e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); + e = kzalloc(sizeof(*e) + sizeof(a->data), GFP_KERNEL); if (e == NULL) return -ENOMEM; @@ -1541,10 +1636,24 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) e->p.header[2] = a->data[1]; e->p.header_length = 12; e->p.callback = outbound_phy_packet_callback; - e->phy_packet.closure = a->closure; - e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; - if (is_ping_packet(a->data)) - e->phy_packet.length = 4; + + if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { + struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + + pp->closure = a->closure; + pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT; + if (is_ping_packet(a->data)) + pp->length = 4; + } else { + struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + + pp->closure = a->closure; + pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT2; + // Keep the data field so that application can match the response event to the + // request. + pp->length = sizeof(a->data); + memcpy(pp->data, a->data, sizeof(a->data)); + } card->driver->send_request(card, &e->p); @@ -1583,14 +1692,29 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) if (e == NULL) break; - e->phy_packet.closure = client->phy_receiver_closure; - e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; - e->phy_packet.rcode = RCODE_COMPLETE; - e->phy_packet.length = 8; - e->phy_packet.data[0] = p->header[1]; - e->phy_packet.data[1] = p->header[2]; - queue_event(client, &e->event, - &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); + if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { + struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + + pp->closure = client->phy_receiver_closure; + pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; + pp->rcode = RCODE_COMPLETE; + pp->length = 8; + pp->data[0] = p->header[1]; + pp->data[1] = p->header[2]; + queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); + } else { + struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + + pp = &e->phy_packet.with_tstamp; + pp->closure = client->phy_receiver_closure; + pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED2; + pp->rcode = RCODE_COMPLETE; + pp->length = 8; + pp->tstamp = p->timestamp; + pp->data[0] = p->header[1]; + pp->data[1] = p->header[2]; + queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); + } } spin_unlock_irqrestore(&card->lock, flags); diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index aa597cda0d88..a3104e35412c 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1211,7 +1211,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) * without actually having a link. */ create: - device = kzalloc(sizeof(*device), GFP_ATOMIC); + device = kzalloc(sizeof(*device), GFP_KERNEL); if (device == NULL) break; diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index f40c81534381..88466b663482 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -101,7 +101,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) { struct fw_node *node; - node = kzalloc(struct_size(node, ports, port_count), GFP_ATOMIC); + node = kzalloc(struct_size(node, ports, port_count), GFP_KERNEL); if (node == NULL) return NULL; diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c index a9f70c96323e..130b95aca629 100644 --- a/drivers/firewire/core-transaction.c +++ b/drivers/firewire/core-transaction.c @@ -70,8 +70,8 @@ static int try_cancel_split_timeout(struct fw_transaction *t) return 1; } -static int close_transaction(struct fw_transaction *transaction, - struct fw_card *card, int rcode) +static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode, + u32 response_tstamp) { struct fw_transaction *t = NULL, *iter; unsigned long flags; @@ -92,7 +92,12 @@ static int close_transaction(struct fw_transaction *transaction, spin_unlock_irqrestore(&card->lock, flags); if (t) { - t->callback(card, rcode, NULL, 0, t->callback_data); + if (!t->with_tstamp) { + t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data); + } else { + t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp, + NULL, 0, t->callback_data); + } return 0; } @@ -107,6 +112,8 @@ static int close_transaction(struct fw_transaction *transaction, int fw_cancel_transaction(struct fw_card *card, struct fw_transaction *transaction) { + u32 tstamp; + /* * Cancel the packet transmission if it's still queued. That * will call the packet transmission callback which cancels @@ -121,7 +128,17 @@ int fw_cancel_transaction(struct fw_card *card, * if the transaction is still pending and remove it in that case. */ - return close_transaction(transaction, card, RCODE_CANCELLED); + if (transaction->packet.ack == 0) { + // The timestamp is reused since it was just read now. + tstamp = transaction->packet.timestamp; + } else { + u32 curr_cycle_time = 0; + + (void)fw_card_read_cycle_time(card, &curr_cycle_time); + tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time); + } + + return close_transaction(transaction, card, RCODE_CANCELLED, tstamp); } EXPORT_SYMBOL(fw_cancel_transaction); @@ -140,7 +157,12 @@ static void split_transaction_timeout_callback(struct timer_list *timer) card->tlabel_mask &= ~(1ULL << t->tlabel); spin_unlock_irqrestore(&card->lock, flags); - t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); + if (!t->with_tstamp) { + t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, t->callback_data); + } else { + t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp, + t->split_timeout_cycle, NULL, 0, t->callback_data); + } } static void start_split_transaction_timeout(struct fw_transaction *t, @@ -162,6 +184,8 @@ static void start_split_transaction_timeout(struct fw_transaction *t, spin_unlock_irqrestore(&card->lock, flags); } +static u32 compute_split_timeout_timestamp(struct fw_card *card, u32 request_timestamp); + static void transmit_complete_callback(struct fw_packet *packet, struct fw_card *card, int status) { @@ -170,28 +194,32 @@ static void transmit_complete_callback(struct fw_packet *packet, switch (status) { case ACK_COMPLETE: - close_transaction(t, card, RCODE_COMPLETE); + close_transaction(t, card, RCODE_COMPLETE, packet->timestamp); break; case ACK_PENDING: + { + t->split_timeout_cycle = + compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff; start_split_transaction_timeout(t, card); break; + } case ACK_BUSY_X: case ACK_BUSY_A: case ACK_BUSY_B: - close_transaction(t, card, RCODE_BUSY); + close_transaction(t, card, RCODE_BUSY, packet->timestamp); break; case ACK_DATA_ERROR: - close_transaction(t, card, RCODE_DATA_ERROR); + close_transaction(t, card, RCODE_DATA_ERROR, packet->timestamp); break; case ACK_TYPE_ERROR: - close_transaction(t, card, RCODE_TYPE_ERROR); + close_transaction(t, card, RCODE_TYPE_ERROR, packet->timestamp); break; default: /* * In this case the ack is really a juju specific * rcode, so just forward that to the callback. */ - close_transaction(t, card, status); + close_transaction(t, card, status, packet->timestamp); break; } } @@ -288,7 +316,8 @@ static int allocate_tlabel(struct fw_card *card) } /** - * fw_send_request() - submit a request packet for transmission + * __fw_send_request() - submit a request packet for transmission to generate callback for response + * subaction with or without time stamp. * @card: interface to send the request at * @t: transaction instance to which the request belongs * @tcode: transaction code @@ -298,7 +327,9 @@ static int allocate_tlabel(struct fw_card *card) * @offset: 48bit wide offset into destination's address space * @payload: data payload for the request subaction * @length: length of the payload, in bytes - * @callback: function to be called when the transaction is completed + * @callback: union of two functions whether to receive time stamp or not for response + * subaction. + * @with_tstamp: Whether to receive time stamp or not for response subaction. * @callback_data: data to be passed to the transaction completion callback * * Submit a request packet into the asynchronous request transmission queue. @@ -335,10 +366,10 @@ static int allocate_tlabel(struct fw_card *card) * transaction completion and hence execution of @callback may happen even * before fw_send_request() returns. */ -void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, - int destination_id, int generation, int speed, - unsigned long long offset, void *payload, size_t length, - fw_transaction_callback_t callback, void *callback_data) +void __fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, + int destination_id, int generation, int speed, unsigned long long offset, + void *payload, size_t length, union fw_transaction_callback callback, + bool with_tstamp, void *callback_data) { unsigned long flags; int tlabel; @@ -353,7 +384,19 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, tlabel = allocate_tlabel(card); if (tlabel < 0) { spin_unlock_irqrestore(&card->lock, flags); - callback(card, RCODE_SEND_ERROR, NULL, 0, callback_data); + if (!with_tstamp) { + callback.without_tstamp(card, RCODE_SEND_ERROR, NULL, 0, callback_data); + } else { + // Timestamping on behalf of hardware. + u32 curr_cycle_time = 0; + u32 tstamp; + + (void)fw_card_read_cycle_time(card, &curr_cycle_time); + tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time); + + callback.with_tstamp(card, RCODE_SEND_ERROR, tstamp, tstamp, NULL, 0, + callback_data); + } return; } @@ -361,13 +404,12 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, t->tlabel = tlabel; t->card = card; t->is_split_transaction = false; - timer_setup(&t->split_timeout_timer, - split_transaction_timeout_callback, 0); + timer_setup(&t->split_timeout_timer, split_transaction_timeout_callback, 0); t->callback = callback; + t->with_tstamp = with_tstamp; t->callback_data = callback_data; - fw_fill_request(&t->packet, tcode, t->tlabel, - destination_id, card->node_id, generation, + fw_fill_request(&t->packet, tcode, t->tlabel, destination_id, card->node_id, generation, speed, offset, payload, length); t->packet.callback = transmit_complete_callback; @@ -377,7 +419,7 @@ void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, card->driver->send_request(card, &t->packet); } -EXPORT_SYMBOL(fw_send_request); +EXPORT_SYMBOL_GPL(__fw_send_request); struct transaction_callback_data { struct completion done; @@ -1047,7 +1089,12 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p) */ card->driver->cancel_packet(card, &t->packet); - t->callback(card, rcode, data, data_length, t->callback_data); + if (!t->with_tstamp) { + t->callback.without_tstamp(card, rcode, data, data_length, t->callback_data); + } else { + t->callback.with_tstamp(card, rcode, t->packet.timestamp, p->timestamp, data, + data_length, t->callback_data); + } } EXPORT_SYMBOL(fw_core_handle_response); diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index eafa4eaae737..2a05f411328f 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -247,6 +247,13 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header, void fw_request_get(struct fw_request *request); void fw_request_put(struct fw_request *request); +// Convert the value of IEEE 1394 CYCLE_TIME register to the format of timeStamp field in +// descriptors of 1394 OHCI. +static inline u32 cycle_time_to_ohci_tstamp(u32 tstamp) +{ + return (tstamp & 0x0ffff000) >> 12; +} + #define FW_PHY_CONFIG_NO_NODE_ID -1 #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 void fw_send_phy_config(struct fw_card *card, diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index 538bd677c254..7a4d1a478e33 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -479,7 +479,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net, struct sk_buff *skb, u16 source_node_id, bool is_broadcast, u16 ether_type) { - int status; + int status, len; switch (ether_type) { case ETH_P_ARP: @@ -533,13 +533,15 @@ static int fwnet_finish_incoming_packet(struct net_device *net, } skb->protocol = protocol; } + + len = skb->len; status = netif_rx(skb); if (status == NET_RX_DROP) { net->stats.rx_errors++; net->stats.rx_dropped++; } else { net->stats.rx_packets++; - net->stats.rx_bytes += skb->len; + net->stats.rx_bytes += len; } return 0; diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c index 17c9d825188b..7e88fd489741 100644 --- a/drivers/firewire/ohci.c +++ b/drivers/firewire/ohci.c @@ -677,6 +677,9 @@ static void ar_context_release(struct ar_context *ctx) struct device *dev = ctx->ohci->card.device; unsigned int i; + if (!ctx->buffer) + return; + vunmap(ctx->buffer); for (i = 0; i < AR_BUFFERS; i++) { @@ -1105,8 +1108,7 @@ static int context_add_buffer(struct context *ctx) if (ctx->total_allocation >= 16*1024*1024) return -ENOMEM; - desc = dma_alloc_coherent(ctx->ohci->card.device, PAGE_SIZE, - &bus_addr, GFP_ATOMIC); + desc = dmam_alloc_coherent(ctx->ohci->card.device, PAGE_SIZE, &bus_addr, GFP_ATOMIC); if (!desc) return -ENOMEM; @@ -1165,10 +1167,10 @@ static void context_release(struct context *ctx) struct fw_card *card = &ctx->ohci->card; struct descriptor_buffer *desc, *tmp; - list_for_each_entry_safe(desc, tmp, &ctx->buffer_list, list) - dma_free_coherent(card->device, PAGE_SIZE, desc, - desc->buffer_bus - - ((void *)&desc->buffer - (void *)desc)); + list_for_each_entry_safe(desc, tmp, &ctx->buffer_list, list) { + dmam_free_coherent(card->device, PAGE_SIZE, desc, + desc->buffer_bus - ((void *)&desc->buffer - (void *)desc)); + } } /* Must be called with ohci->lock held */ @@ -1623,6 +1625,8 @@ static void handle_local_request(struct context *ctx, struct fw_packet *packet) } } +static u32 get_cycle_time(struct fw_ohci *ohci); + static void at_context_transmit(struct context *ctx, struct fw_packet *packet) { unsigned long flags; @@ -1633,6 +1637,10 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet) if (HEADER_GET_DESTINATION(packet->header[0]) == ctx->ohci->node_id && ctx->ohci->generation == packet->generation) { spin_unlock_irqrestore(&ctx->ohci->lock, flags); + + // Timestamping on behalf of the hardware. + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); + handle_local_request(ctx, packet); return; } @@ -1640,9 +1648,12 @@ static void at_context_transmit(struct context *ctx, struct fw_packet *packet) ret = at_context_queue_packet(ctx, packet); spin_unlock_irqrestore(&ctx->ohci->lock, flags); - if (ret < 0) - packet->callback(packet, &ctx->ohci->card, packet->ack); + if (ret < 0) { + // Timestamping on behalf of the hardware. + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); + packet->callback(packet, &ctx->ohci->card, packet->ack); + } } static void detect_dead_context(struct fw_ohci *ohci, @@ -2044,8 +2055,7 @@ static void bus_reset_work(struct work_struct *work) spin_unlock_irq(&ohci->lock); if (free_rom) - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - free_rom, free_rom_bus); + dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, free_rom, free_rom_bus); log_selfids(ohci, generation, self_id_count); @@ -2377,10 +2387,8 @@ static int ohci_enable(struct fw_card *card, */ if (config_rom) { - ohci->next_config_rom = - dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, - &ohci->next_config_rom_bus, - GFP_KERNEL); + ohci->next_config_rom = dmam_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, + &ohci->next_config_rom_bus, GFP_KERNEL); if (ohci->next_config_rom == NULL) return -ENOMEM; @@ -2472,9 +2480,8 @@ static int ohci_set_config_rom(struct fw_card *card, * ohci->next_config_rom to NULL (see bus_reset_work). */ - next_config_rom = - dma_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, - &next_config_rom_bus, GFP_KERNEL); + next_config_rom = dmam_alloc_coherent(ohci->card.device, CONFIG_ROM_SIZE, + &next_config_rom_bus, GFP_KERNEL); if (next_config_rom == NULL) return -ENOMEM; @@ -2507,9 +2514,10 @@ static int ohci_set_config_rom(struct fw_card *card, spin_unlock_irq(&ohci->lock); /* If we didn't use the DMA allocation, delete it. */ - if (next_config_rom != NULL) - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - next_config_rom, next_config_rom_bus); + if (next_config_rom != NULL) { + dmam_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, next_config_rom, + next_config_rom_bus); + } /* * Now initiate a bus reset to have the changes take @@ -2557,6 +2565,10 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) log_ar_at_event(ohci, 'T', packet->speed, packet->header, 0x20); driver_data->packet = NULL; packet->ack = RCODE_CANCELLED; + + // Timestamping on behalf of the hardware. + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci)); + packet->callback(packet, &ohci->card, packet->ack); ret = 0; out: @@ -3544,6 +3556,19 @@ static inline void pmac_ohci_on(struct pci_dev *dev) {} static inline void pmac_ohci_off(struct pci_dev *dev) {} #endif /* CONFIG_PPC_PMAC */ +static void release_ohci(struct device *dev, void *data) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct fw_ohci *ohci = pci_get_drvdata(pdev); + + pmac_ohci_off(pdev); + + ar_context_release(&ohci->ar_response_ctx); + ar_context_release(&ohci->ar_request_ctx); + + dev_notice(dev, "removed fw-ohci device\n"); +} + static int pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) { @@ -3558,25 +3583,22 @@ static int pci_probe(struct pci_dev *dev, return -ENOSYS; } - ohci = kzalloc(sizeof(*ohci), GFP_KERNEL); - if (ohci == NULL) { - err = -ENOMEM; - goto fail; - } - + ohci = devres_alloc(release_ohci, sizeof(*ohci), GFP_KERNEL); + if (ohci == NULL) + return -ENOMEM; fw_card_initialize(&ohci->card, &ohci_driver, &dev->dev); - + pci_set_drvdata(dev, ohci); pmac_ohci_on(dev); + devres_add(&dev->dev, ohci); - err = pci_enable_device(dev); + err = pcim_enable_device(dev); if (err) { dev_err(&dev->dev, "failed to enable OHCI hardware\n"); - goto fail_free; + return err; } pci_set_master(dev); pci_write_config_dword(dev, OHCI1394_PCI_HCI_Control, 0); - pci_set_drvdata(dev, ohci); spin_lock_init(&ohci->lock); mutex_init(&ohci->phy_reg_mutex); @@ -3586,22 +3608,15 @@ static int pci_probe(struct pci_dev *dev, if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM) || pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE) { ohci_err(ohci, "invalid MMIO resource\n"); - err = -ENXIO; - goto fail_disable; + return -ENXIO; } - err = pci_request_region(dev, 0, ohci_driver_name); + err = pcim_iomap_regions(dev, 1 << 0, ohci_driver_name); if (err) { - ohci_err(ohci, "MMIO resource unavailable\n"); - goto fail_disable; - } - - ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE); - if (ohci->registers == NULL) { - ohci_err(ohci, "failed to remap registers\n"); - err = -ENXIO; - goto fail_iomem; + ohci_err(ohci, "request and map MMIO resource unavailable\n"); + return -ENXIO; } + ohci->registers = pcim_iomap_table(dev)[0]; for (i = 0; i < ARRAY_SIZE(ohci_quirks); i++) if ((ohci_quirks[i].vendor == dev->vendor) && @@ -3622,34 +3637,30 @@ static int pci_probe(struct pci_dev *dev, */ BUILD_BUG_ON(AR_BUFFERS * sizeof(struct descriptor) > PAGE_SIZE/4); BUILD_BUG_ON(SELF_ID_BUF_SIZE > PAGE_SIZE/2); - ohci->misc_buffer = dma_alloc_coherent(ohci->card.device, - PAGE_SIZE, - &ohci->misc_buffer_bus, - GFP_KERNEL); - if (!ohci->misc_buffer) { - err = -ENOMEM; - goto fail_iounmap; - } + ohci->misc_buffer = dmam_alloc_coherent(&dev->dev, PAGE_SIZE, &ohci->misc_buffer_bus, + GFP_KERNEL); + if (!ohci->misc_buffer) + return -ENOMEM; err = ar_context_init(&ohci->ar_request_ctx, ohci, 0, OHCI1394_AsReqRcvContextControlSet); if (err < 0) - goto fail_misc_buf; + return err; err = ar_context_init(&ohci->ar_response_ctx, ohci, PAGE_SIZE/4, OHCI1394_AsRspRcvContextControlSet); if (err < 0) - goto fail_arreq_ctx; + return err; err = context_init(&ohci->at_request_ctx, ohci, OHCI1394_AsReqTrContextControlSet, handle_at_packet); if (err < 0) - goto fail_arrsp_ctx; + return err; err = context_init(&ohci->at_response_ctx, ohci, OHCI1394_AsRspTrContextControlSet, handle_at_packet); if (err < 0) - goto fail_atreq_ctx; + return err; reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); ohci->ir_context_channels = ~0ULL; @@ -3658,7 +3669,9 @@ static int pci_probe(struct pci_dev *dev, ohci->ir_context_mask = ohci->ir_context_support; ohci->n_ir = hweight32(ohci->ir_context_mask); size = sizeof(struct iso_context) * ohci->n_ir; - ohci->ir_context_list = kzalloc(size, GFP_KERNEL); + ohci->ir_context_list = devm_kzalloc(&dev->dev, size, GFP_KERNEL); + if (!ohci->ir_context_list) + return -ENOMEM; reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); ohci->it_context_support = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); @@ -3671,12 +3684,9 @@ static int pci_probe(struct pci_dev *dev, ohci->it_context_mask = ohci->it_context_support; ohci->n_it = hweight32(ohci->it_context_mask); size = sizeof(struct iso_context) * ohci->n_it; - ohci->it_context_list = kzalloc(size, GFP_KERNEL); - - if (ohci->it_context_list == NULL || ohci->ir_context_list == NULL) { - err = -ENOMEM; - goto fail_contexts; - } + ohci->it_context_list = devm_kzalloc(&dev->dev, size, GFP_KERNEL); + if (!ohci->it_context_list) + return -ENOMEM; ohci->self_id = ohci->misc_buffer + PAGE_SIZE/2; ohci->self_id_bus = ohci->misc_buffer_bus + PAGE_SIZE/2; @@ -3689,17 +3699,16 @@ static int pci_probe(struct pci_dev *dev, if (!(ohci->quirks & QUIRK_NO_MSI)) pci_enable_msi(dev); - if (request_irq(dev->irq, irq_handler, - pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, - ohci_driver_name, ohci)) { + err = devm_request_irq(&dev->dev, dev->irq, irq_handler, + pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED, ohci_driver_name, ohci); + if (err < 0) { ohci_err(ohci, "failed to allocate interrupt %d\n", dev->irq); - err = -EIO; goto fail_msi; } err = fw_card_add(&ohci->card, max_receive, link_speed, guid); if (err) - goto fail_irq; + goto fail_msi; version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff; ohci_notice(ohci, @@ -3712,33 +3721,9 @@ static int pci_probe(struct pci_dev *dev, return 0; - fail_irq: - free_irq(dev->irq, ohci); fail_msi: pci_disable_msi(dev); - fail_contexts: - kfree(ohci->ir_context_list); - kfree(ohci->it_context_list); - context_release(&ohci->at_response_ctx); - fail_atreq_ctx: - context_release(&ohci->at_request_ctx); - fail_arrsp_ctx: - ar_context_release(&ohci->ar_response_ctx); - fail_arreq_ctx: - ar_context_release(&ohci->ar_request_ctx); - fail_misc_buf: - dma_free_coherent(ohci->card.device, PAGE_SIZE, - ohci->misc_buffer, ohci->misc_buffer_bus); - fail_iounmap: - pci_iounmap(dev, ohci->registers); - fail_iomem: - pci_release_region(dev, 0); - fail_disable: - pci_disable_device(dev); - fail_free: - kfree(ohci); - pmac_ohci_off(dev); - fail: + return err; } @@ -3763,30 +3748,10 @@ static void pci_remove(struct pci_dev *dev) */ software_reset(ohci); - free_irq(dev->irq, ohci); - - if (ohci->next_config_rom && ohci->next_config_rom != ohci->config_rom) - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - ohci->next_config_rom, ohci->next_config_rom_bus); - if (ohci->config_rom) - dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE, - ohci->config_rom, ohci->config_rom_bus); - ar_context_release(&ohci->ar_request_ctx); - ar_context_release(&ohci->ar_response_ctx); - dma_free_coherent(ohci->card.device, PAGE_SIZE, - ohci->misc_buffer, ohci->misc_buffer_bus); - context_release(&ohci->at_request_ctx); - context_release(&ohci->at_response_ctx); - kfree(ohci->it_context_list); - kfree(ohci->ir_context_list); + pci_disable_msi(dev); - pci_iounmap(dev, ohci->registers); - pci_release_region(dev, 0); - pci_disable_device(dev); - kfree(ohci); - pmac_ohci_off(dev); - dev_notice(&dev->dev, "removed fw-ohci device\n"); + dev_notice(&dev->dev, "removing fw-ohci device\n"); } #ifdef CONFIG_PM diff --git a/drivers/firewire/uapi-test.c b/drivers/firewire/uapi-test.c new file mode 100644 index 000000000000..2fcbede4fab1 --- /dev/null +++ b/drivers/firewire/uapi-test.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// uapi_test.c - An application of Kunit to check layout of structures exposed to user space for +// FireWire subsystem. +// +// Copyright (c) 2023 Takashi Sakamoto + +#include <kunit/test.h> +#include <linux/firewire-cdev.h> + +// Known issue added at v2.6.27 kernel. +static void structure_layout_event_response(struct kunit *test) +{ +#if defined(CONFIG_X86_32) + // 4 bytes alignment for aggregate type including 8 bytes storage types. + KUNIT_EXPECT_EQ(test, 20, sizeof(struct fw_cdev_event_response)); +#else + // 8 bytes alignment for aggregate type including 8 bytes storage types. + KUNIT_EXPECT_EQ(test, 24, sizeof(struct fw_cdev_event_response)); +#endif + + KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_response, closure)); + KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_response, type)); + KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_response, rcode)); + KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_response, length)); + KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_response, data)); +} + +// Added at v6.5. +static void structure_layout_event_request3(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, 56, sizeof(struct fw_cdev_event_request3)); + + KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_request3, closure)); + KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_request3, type)); + KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_request3, tcode)); + KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_request3, offset)); + KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_request3, source_node_id)); + KUNIT_EXPECT_EQ(test, 28, offsetof(struct fw_cdev_event_request3, destination_node_id)); + KUNIT_EXPECT_EQ(test, 32, offsetof(struct fw_cdev_event_request3, card)); + KUNIT_EXPECT_EQ(test, 36, offsetof(struct fw_cdev_event_request3, generation)); + KUNIT_EXPECT_EQ(test, 40, offsetof(struct fw_cdev_event_request3, handle)); + KUNIT_EXPECT_EQ(test, 44, offsetof(struct fw_cdev_event_request3, length)); + KUNIT_EXPECT_EQ(test, 48, offsetof(struct fw_cdev_event_request3, tstamp)); + KUNIT_EXPECT_EQ(test, 56, offsetof(struct fw_cdev_event_request3, data)); +} + +// Added at v6.5. +static void structure_layout_event_response2(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, 32, sizeof(struct fw_cdev_event_response2)); + + KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_response2, closure)); + KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_response2, type)); + KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_response2, rcode)); + KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_response2, length)); + KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_response2, request_tstamp)); + KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_response2, response_tstamp)); + KUNIT_EXPECT_EQ(test, 32, offsetof(struct fw_cdev_event_response2, data)); +} + +// Added at v6.5. +static void structure_layout_event_phy_packet2(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, 24, sizeof(struct fw_cdev_event_phy_packet2)); + + KUNIT_EXPECT_EQ(test, 0, offsetof(struct fw_cdev_event_phy_packet2, closure)); + KUNIT_EXPECT_EQ(test, 8, offsetof(struct fw_cdev_event_phy_packet2, type)); + KUNIT_EXPECT_EQ(test, 12, offsetof(struct fw_cdev_event_phy_packet2, rcode)); + KUNIT_EXPECT_EQ(test, 16, offsetof(struct fw_cdev_event_phy_packet2, length)); + KUNIT_EXPECT_EQ(test, 20, offsetof(struct fw_cdev_event_phy_packet2, tstamp)); + KUNIT_EXPECT_EQ(test, 24, offsetof(struct fw_cdev_event_phy_packet2, data)); +} + +static struct kunit_case structure_layout_test_cases[] = { + KUNIT_CASE(structure_layout_event_response), + KUNIT_CASE(structure_layout_event_request3), + KUNIT_CASE(structure_layout_event_response2), + KUNIT_CASE(structure_layout_event_phy_packet2), + {} +}; + +static struct kunit_suite structure_layout_test_suite = { + .name = "firewire-uapi-structure-layout", + .test_cases = structure_layout_test_cases, +}; +kunit_test_suite(structure_layout_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/firewire.h b/include/linux/firewire.h index efb6e2cf2034..bd3fc75d4f14 100644 --- a/include/linux/firewire.h +++ b/include/linux/firewire.h @@ -261,6 +261,15 @@ typedef void (*fw_packet_callback_t)(struct fw_packet *packet, typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, void *data, size_t length, void *callback_data); +typedef void (*fw_transaction_callback_with_tstamp_t)(struct fw_card *card, int rcode, + u32 request_tstamp, u32 response_tstamp, void *data, + size_t length, void *callback_data); + +union fw_transaction_callback { + fw_transaction_callback_t without_tstamp; + fw_transaction_callback_with_tstamp_t with_tstamp; +}; + /* * This callback handles an inbound request subaction. It is called in * RCU read-side context, therefore must not sleep. @@ -312,6 +321,7 @@ struct fw_transaction { struct fw_card *card; bool is_split_transaction; struct timer_list split_timeout_timer; + u32 split_timeout_cycle; struct fw_packet packet; @@ -319,7 +329,8 @@ struct fw_transaction { * The data passed to the callback is valid only during the * callback. */ - fw_transaction_callback_t callback; + union fw_transaction_callback callback; + bool with_tstamp; void *callback_data; }; @@ -345,10 +356,71 @@ void fw_send_response(struct fw_card *card, struct fw_request *request, int rcode); int fw_get_request_speed(struct fw_request *request); u32 fw_request_get_timestamp(const struct fw_request *request); -void fw_send_request(struct fw_card *card, struct fw_transaction *t, - int tcode, int destination_id, int generation, int speed, - unsigned long long offset, void *payload, size_t length, - fw_transaction_callback_t callback, void *callback_data); + +void __fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, + int destination_id, int generation, int speed, unsigned long long offset, + void *payload, size_t length, union fw_transaction_callback callback, + bool with_tstamp, void *callback_data); + +/** + * fw_send_request() - submit a request packet for transmission to generate callback for response + * subaction without time stamp. + * @card: interface to send the request at + * @t: transaction instance to which the request belongs + * @tcode: transaction code + * @destination_id: destination node ID, consisting of bus_ID and phy_ID + * @generation: bus generation in which request and response are valid + * @speed: transmission speed + * @offset: 48bit wide offset into destination's address space + * @payload: data payload for the request subaction + * @length: length of the payload, in bytes + * @callback: function to be called when the transaction is completed + * @callback_data: data to be passed to the transaction completion callback + * + * A variation of __fw_send_request() to generate callback for response subaction without time + * stamp. + */ +static inline void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode, + int destination_id, int generation, int speed, + unsigned long long offset, void *payload, size_t length, + fw_transaction_callback_t callback, void *callback_data) +{ + union fw_transaction_callback cb = { + .without_tstamp = callback, + }; + __fw_send_request(card, t, tcode, destination_id, generation, speed, offset, payload, + length, cb, false, callback_data); +} + +/** + * fw_send_request_with_tstamp() - submit a request packet for transmission to generate callback for + * response with time stamp. + * @card: interface to send the request at + * @t: transaction instance to which the request belongs + * @tcode: transaction code + * @destination_id: destination node ID, consisting of bus_ID and phy_ID + * @generation: bus generation in which request and response are valid + * @speed: transmission speed + * @offset: 48bit wide offset into destination's address space + * @payload: data payload for the request subaction + * @length: length of the payload, in bytes + * @callback: function to be called when the transaction is completed + * @callback_data: data to be passed to the transaction completion callback + * + * A variation of __fw_send_request() to generate callback for response subaction with time stamp. + */ +static inline void fw_send_request_with_tstamp(struct fw_card *card, struct fw_transaction *t, + int tcode, int destination_id, int generation, int speed, unsigned long long offset, + void *payload, size_t length, fw_transaction_callback_with_tstamp_t callback, + void *callback_data) +{ + union fw_transaction_callback cb = { + .with_tstamp = callback, + }; + __fw_send_request(card, t, tcode, destination_id, generation, speed, offset, payload, + length, cb, true, callback_data); +} + int fw_cancel_transaction(struct fw_card *card, struct fw_transaction *transaction); int fw_run_transaction(struct fw_card *card, int tcode, int destination_id, diff --git a/include/uapi/linux/firewire-cdev.h b/include/uapi/linux/firewire-cdev.h index 92be3ea3c6e0..1f2c9469f921 100644 --- a/include/uapi/linux/firewire-cdev.h +++ b/include/uapi/linux/firewire-cdev.h @@ -46,6 +46,12 @@ #define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08 #define FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL 0x09 +/* available since kernel version 6.5 */ +#define FW_CDEV_EVENT_REQUEST3 0x0a +#define FW_CDEV_EVENT_RESPONSE2 0x0b +#define FW_CDEV_EVENT_PHY_PACKET_SENT2 0x0c +#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 0x0d + /** * struct fw_cdev_event_common - Common part of all fw_cdev_event_* types * @closure: For arbitrary use by userspace @@ -103,6 +109,32 @@ struct fw_cdev_event_bus_reset { * @length: Data length, i.e. the response's payload size in bytes * @data: Payload data, if any * + * This event is sent instead of &fw_cdev_event_response if the kernel or the client implements + * ABI version <= 5. It has the lack of time stamp field comparing to &fw_cdev_event_response2. + */ +struct fw_cdev_event_response { + __u64 closure; + __u32 type; + __u32 rcode; + __u32 length; + __u32 data[]; +}; + +/** + * struct fw_cdev_event_response2 - Sent when a response packet was received + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_REQUEST + * or %FW_CDEV_IOC_SEND_BROADCAST_REQUEST + * or %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl + * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_RESPONSE + * @rcode: Response code returned by the remote node + * @length: Data length, i.e. the response's payload size in bytes + * @request_tstamp: The time stamp of isochronous cycle at which the request was sent. + * @response_tstamp: The time stamp of isochronous cycle at which the response was sent. + * @padding: Padding to keep the size of structure as multiples of 8 in various architectures + * since 4 byte alignment is used for 8 byte of object type in System V ABI for i386 + * architecture. + * @data: Payload data, if any + * * This event is sent when the stack receives a response to an outgoing request * sent by %FW_CDEV_IOC_SEND_REQUEST ioctl. The payload data for responses * carrying data (read and lock responses) follows immediately and can be @@ -112,12 +144,21 @@ struct fw_cdev_event_bus_reset { * involve response packets. This includes unified write transactions, * broadcast write transactions, and transmission of asynchronous stream * packets. @rcode indicates success or failure of such transmissions. + * + * The value of @request_tstamp expresses the isochronous cycle at which the request was sent to + * initiate the transaction. The value of @response_tstamp expresses the isochronous cycle at which + * the response arrived to complete the transaction. Each value is unsigned 16 bit integer + * containing three low order bits of second field and all 13 bits of cycle field in format of + * CYCLE_TIMER register. */ -struct fw_cdev_event_response { +struct fw_cdev_event_response2 { __u64 closure; __u32 type; __u32 rcode; __u32 length; + __u32 request_tstamp; + __u32 response_tstamp; + __u32 padding; __u32 data[]; }; @@ -159,6 +200,41 @@ struct fw_cdev_event_request { * @length: Data length, i.e. the request's payload size in bytes * @data: Incoming data, if any * + * This event is sent instead of &fw_cdev_event_request3 if the kernel or the client implements + * ABI version <= 5. It has the lack of time stamp field comparing to &fw_cdev_event_request3. + */ +struct fw_cdev_event_request2 { + __u64 closure; + __u32 type; + __u32 tcode; + __u64 offset; + __u32 source_node_id; + __u32 destination_node_id; + __u32 card; + __u32 generation; + __u32 handle; + __u32 length; + __u32 data[]; +}; + +/** + * struct fw_cdev_event_request3 - Sent on incoming request to an address region + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_ALLOCATE ioctl + * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_REQUEST2 + * @tcode: Transaction code of the incoming request + * @offset: The offset into the 48-bit per-node address space + * @source_node_id: Sender node ID + * @destination_node_id: Destination node ID + * @card: The index of the card from which the request came + * @generation: Bus generation in which the request is valid + * @handle: Reference to the kernel-side pending request + * @length: Data length, i.e. the request's payload size in bytes + * @tstamp: The time stamp of isochronous cycle at which the request arrived. + * @padding: Padding to keep the size of structure as multiples of 8 in various architectures + * since 4 byte alignment is used for 8 byte of object type in System V ABI for i386 + * architecture. + * @data: Incoming data, if any + * * This event is sent when the stack receives an incoming request to an address * region registered using the %FW_CDEV_IOC_ALLOCATE ioctl. The request is * guaranteed to be completely contained in the specified region. Userspace is @@ -191,10 +267,14 @@ struct fw_cdev_event_request { * sent. * * If the client subsequently needs to initiate requests to the sender node of - * an &fw_cdev_event_request2, it needs to use a device file with matching + * an &fw_cdev_event_request3, it needs to use a device file with matching * card index, node ID, and generation for outbound requests. + * + * @tstamp is isochronous cycle at which the request arrived. It is 16 bit integer value and the + * higher 3 bits expresses three low order bits of second field in the format of CYCLE_TIME + * register and the rest 13 bits expresses cycle field. */ -struct fw_cdev_event_request2 { +struct fw_cdev_event_request3 { __u64 closure; __u32 type; __u32 tcode; @@ -205,6 +285,8 @@ struct fw_cdev_event_request2 { __u32 generation; __u32 handle; __u32 length; + __u32 tstamp; + __u32 padding; __u32 data[]; }; @@ -341,20 +423,59 @@ struct fw_cdev_event_iso_resource { * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT or %..._RECEIVED * @rcode: %RCODE_..., indicates success or failure of transmission * @length: Data length in bytes + * @data: Incoming data for %FW_CDEV_IOC_RECEIVE_PHY_PACKETS. For %FW_CDEV_IOC_SEND_PHY_PACKET + * the field has the same data in the request, thus the length of 8 bytes. + * + * This event is sent instead of &fw_cdev_event_phy_packet2 if the kernel or + * the client implements ABI version <= 5. It has the lack of time stamp field comparing to + * &fw_cdev_event_phy_packet2. + */ +struct fw_cdev_event_phy_packet { + __u64 closure; + __u32 type; + __u32 rcode; + __u32 length; + __u32 data[]; +}; + +/** + * struct fw_cdev_event_phy_packet2 - A PHY packet was transmitted or received with time stamp. + * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_PHY_PACKET + * or %FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl + * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT2 or %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 + * @rcode: %RCODE_..., indicates success or failure of transmission + * @length: Data length in bytes + * @tstamp: For %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, the time stamp of isochronous cycle at + * which the packet arrived. For %FW_CDEV_EVENT_PHY_PACKET_SENT2 and non-ping packet, + * the time stamp of isochronous cycle at which the packet was sent. For ping packet, + * the tick count for round-trip time measured by 1394 OHCI controller. + * The time stamp of isochronous cycle at which either the response was sent for + * %FW_CDEV_EVENT_PHY_PACKET_SENT2 or the request arrived for + * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2. * @data: Incoming data * - * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty, - * except in case of a ping packet: Then, @length is 4, and @data[0] is the - * ping time in 49.152MHz clocks if @rcode is %RCODE_COMPLETE. + * If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT2, @length is 8 and @data consists of the two PHY + * packet quadlets to be sent, in host byte order, * - * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data - * consists of the two PHY packet quadlets, in host byte order. + * If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, @length is 8 and @data consists of the two PHY + * packet quadlets, in host byte order. + * + * For %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2, the @tstamp is the isochronous cycle at which the + * packet arrived. It is 16 bit integer value and the higher 3 bits expresses three low order bits + * of second field and the rest 13 bits expresses cycle field in the format of CYCLE_TIME register. + * + * For %FW_CDEV_EVENT_PHY_PACKET_SENT2, the @tstamp has different meanings whether to sent the + * packet for ping or not. If it's not for ping, the @tstamp is the isochronous cycle at which the + * packet was sent, and use the same format as the case of %FW_CDEV_EVENT_PHY_PACKET_SENT2. If it's + * for ping, the @tstamp is for round-trip time measured by 1394 OHCI controller with 42.195 MHz + * resolution. */ -struct fw_cdev_event_phy_packet { +struct fw_cdev_event_phy_packet2 { __u64 closure; __u32 type; __u32 rcode; __u32 length; + __u32 tstamp; __u32 data[]; }; @@ -375,6 +496,11 @@ struct fw_cdev_event_phy_packet { * %FW_CDEV_EVENT_PHY_PACKET_SENT or * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED * + * @request3: Valid if @common.type == %FW_CDEV_EVENT_REQUEST3 + * @response2: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE2 + * @phy_packet2: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT2 or + * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 + * * Convenience union for userspace use. Events could be read(2) into an * appropriately aligned char buffer and then cast to this union for further * processing. Note that for a request, response or iso_interrupt event, @@ -393,6 +519,9 @@ union fw_cdev_event { struct fw_cdev_event_iso_interrupt_mc iso_interrupt_mc; /* added in 2.6.36 */ struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ + struct fw_cdev_event_request3 request3; /* added in 6.5 */ + struct fw_cdev_event_response2 response2; /* added in 6.5 */ + struct fw_cdev_event_phy_packet2 phy_packet2; /* added in 6.5 */ }; /* available since kernel version 2.6.22 */ @@ -457,6 +586,11 @@ union fw_cdev_event { * 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to * avoid dropping data * - added %FW_CDEV_IOC_FLUSH_ISO + * 6 (6.5) - added some event for subactions of asynchronous transaction with time stamp + * - %FW_CDEV_EVENT_REQUEST3 + * - %FW_CDEV_EVENT_RESPONSE2 + * - %FW_CDEV_EVENT_PHY_PACKET_SENT2 + * - %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 */ /** @@ -502,11 +636,11 @@ struct fw_cdev_get_info { * @data: Userspace pointer to payload * @generation: The bus generation where packet is valid * - * Send a request to the device. This ioctl implements all outgoing requests. - * Both quadlet and block request specify the payload as a pointer to the data - * in the @data field. Once the transaction completes, the kernel writes an - * &fw_cdev_event_response event back. The @closure field is passed back to - * user space in the response event. + * Send a request to the device. This ioctl implements all outgoing requests. Both quadlet and + * block request specify the payload as a pointer to the data in the @data field. Once the + * transaction completes, the kernel writes either &fw_cdev_event_response event or + * &fw_cdev_event_response event back. The @closure field is passed back to user space in the + * response event. */ struct fw_cdev_send_request { __u32 tcode; @@ -989,10 +1123,9 @@ struct fw_cdev_allocate_iso_resource { * @generation: The bus generation where packet is valid * @speed: Speed to transmit at * - * The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet - * to every device which is listening to the specified channel. The kernel - * writes an &fw_cdev_event_response event which indicates success or failure of - * the transmission. + * The %FW_CDEV_IOC_SEND_STREAM_PACKET ioctl sends an asynchronous stream packet to every device + * which is listening to the specified channel. The kernel writes either &fw_cdev_event_response + * event or &fw_cdev_event_response2 event which indicates success or failure of the transmission. */ struct fw_cdev_send_stream_packet { __u32 length; @@ -1011,8 +1144,8 @@ struct fw_cdev_send_stream_packet { * @data: First and second quadlet of the PHY packet * @generation: The bus generation where packet is valid * - * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes - * on the same card as this device. After transmission, an + * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes on the same card as this + * device. After transmission, either %FW_CDEV_EVENT_PHY_PACKET_SENT event or * %FW_CDEV_EVENT_PHY_PACKET_SENT event is generated. * * The payload @data\[\] shall be specified in host byte order. Usually, @@ -1031,8 +1164,9 @@ struct fw_cdev_send_phy_packet { * struct fw_cdev_receive_phy_packets - start reception of PHY packets * @closure: Passed back to userspace in phy packet events * - * This ioctl activates issuing of %FW_CDEV_EVENT_PHY_PACKET_RECEIVED due to - * incoming PHY packets from any node on the same bus as the device. + * This ioctl activates issuing of either %FW_CDEV_EVENT_PHY_PACKET_RECEIVED or + * %FW_CDEV_EVENT_PHY_PACKET_RECEIVED2 due to incoming PHY packets from any node on the same bus + * as the device. * * The ioctl is only permitted on device files which represent a local node. */ |