Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firewire: core: use union for callback of transaction completion

In 1394 OHCI, the OUTPUT_LAST descriptor of Asynchronous Transmit (AT)
request context has timeStamp field, in which 1394 OHCI controller
record the isochronous cycle when the packet was sent for the request
subaction. Additionally, for the case of split transaction in IEEE 1394,
Asynchronous Receive (AT) request context is used for response subaction
to finish the transaction. The trailer quadlet of descriptor in the
context has timeStamp field, in which 1394 OHCI controller records the
isochronous cycle when the packet arrived.

Current implementation of 1394 OHCI controller driver stores values of
both fields to internal structure as time stamp, while Linux FireWire
subsystem provides no way to access to it. When using asynchronous
transaction service provided by the subsystem, callback function is passed
to kernel API. The prototype of callback function has the lack of argument
for the values.

This commit adds a new callback function for the purpose. It has an
additional argument to point to the constant array with two elements. For
backward compatibility to kernel space, a new union is also adds to wrap
two different prototype of callback function. The fw_transaction structure
has the union as a member and a boolean flag to express which function
callback is available.

The core function is changed to handle the two cases; with or without
time stamp. For the error path to process transaction, the isochronous
cycle is computed by current value of CYCLE_TIMER register in 1394 OHCI
controller. Especially for the case of timeout of split transaction, the
expected isochronous cycle is computed.

Link: https://lore.kernel.org/r/20230529113406.986289-6-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>

+80 -15
+46 -12
drivers/firewire/core-transaction.c
··· 70 70 return 1; 71 71 } 72 72 73 - static int close_transaction(struct fw_transaction *transaction, 74 - struct fw_card *card, int rcode) 73 + static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode, 74 + u32 response_tstamp) 75 75 { 76 76 struct fw_transaction *t = NULL, *iter; 77 77 unsigned long flags; ··· 92 92 spin_unlock_irqrestore(&card->lock, flags); 93 93 94 94 if (t) { 95 - t->callback(card, rcode, NULL, 0, t->callback_data); 95 + if (!t->with_tstamp) { 96 + t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data); 97 + } else { 98 + t->callback.with_tstamp(card, rcode, t->packet.timestamp, response_tstamp, 99 + NULL, 0, t->callback_data); 100 + } 96 101 return 0; 97 102 } 98 103 ··· 112 107 int fw_cancel_transaction(struct fw_card *card, 113 108 struct fw_transaction *transaction) 114 109 { 110 + u32 tstamp; 111 + 115 112 /* 116 113 * Cancel the packet transmission if it's still queued. That 117 114 * will call the packet transmission callback which cancels ··· 128 121 * if the transaction is still pending and remove it in that case. 129 122 */ 130 123 131 - return close_transaction(transaction, card, RCODE_CANCELLED); 124 + if (transaction->packet.ack == 0) { 125 + // The timestamp is reused since it was just read now. 126 + tstamp = transaction->packet.timestamp; 127 + } else { 128 + u32 curr_cycle_time = 0; 129 + 130 + (void)fw_card_read_cycle_time(card, &curr_cycle_time); 131 + tstamp = cycle_time_to_ohci_tstamp(curr_cycle_time); 132 + } 133 + 134 + return close_transaction(transaction, card, RCODE_CANCELLED, tstamp); 132 135 } 133 136 EXPORT_SYMBOL(fw_cancel_transaction); 134 137 ··· 157 140 card->tlabel_mask &= ~(1ULL << t->tlabel); 158 141 spin_unlock_irqrestore(&card->lock, flags); 159 142 160 - t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); 143 + if (!t->with_tstamp) { 144 + t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0, t->callback_data); 145 + } else { 146 + t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp, 147 + t->split_timeout_cycle, NULL, 0, t->callback_data); 148 + } 161 149 } 162 150 163 151 static void start_split_transaction_timeout(struct fw_transaction *t, ··· 184 162 spin_unlock_irqrestore(&card->lock, flags); 185 163 } 186 164 165 + static u32 compute_split_timeout_timestamp(struct fw_card *card, u32 request_timestamp); 166 + 187 167 static void transmit_complete_callback(struct fw_packet *packet, 188 168 struct fw_card *card, int status) 189 169 { ··· 194 170 195 171 switch (status) { 196 172 case ACK_COMPLETE: 197 - close_transaction(t, card, RCODE_COMPLETE); 173 + close_transaction(t, card, RCODE_COMPLETE, packet->timestamp); 198 174 break; 199 175 case ACK_PENDING: 176 + { 177 + t->split_timeout_cycle = 178 + compute_split_timeout_timestamp(card, packet->timestamp) & 0xffff; 200 179 start_split_transaction_timeout(t, card); 201 180 break; 181 + } 202 182 case ACK_BUSY_X: 203 183 case ACK_BUSY_A: 204 184 case ACK_BUSY_B: 205 - close_transaction(t, card, RCODE_BUSY); 185 + close_transaction(t, card, RCODE_BUSY, packet->timestamp); 206 186 break; 207 187 case ACK_DATA_ERROR: 208 - close_transaction(t, card, RCODE_DATA_ERROR); 188 + close_transaction(t, card, RCODE_DATA_ERROR, packet->timestamp); 209 189 break; 210 190 case ACK_TYPE_ERROR: 211 - close_transaction(t, card, RCODE_TYPE_ERROR); 191 + close_transaction(t, card, RCODE_TYPE_ERROR, packet->timestamp); 212 192 break; 213 193 default: 214 194 /* 215 195 * In this case the ack is really a juju specific 216 196 * rcode, so just forward that to the callback. 217 197 */ 218 - close_transaction(t, card, status); 198 + close_transaction(t, card, status, packet->timestamp); 219 199 break; 220 200 } 221 201 } ··· 391 363 t->is_split_transaction = false; 392 364 timer_setup(&t->split_timeout_timer, 393 365 split_transaction_timeout_callback, 0); 394 - t->callback = callback; 366 + t->callback.without_tstamp = callback; 367 + t->with_tstamp = false; 395 368 t->callback_data = callback_data; 396 369 397 370 fw_fill_request(&t->packet, tcode, t->tlabel, ··· 1076 1047 */ 1077 1048 card->driver->cancel_packet(card, &t->packet); 1078 1049 1079 - t->callback(card, rcode, data, data_length, t->callback_data); 1050 + if (!t->with_tstamp) { 1051 + t->callback.without_tstamp(card, rcode, data, data_length, t->callback_data); 1052 + } else { 1053 + t->callback.with_tstamp(card, rcode, t->packet.timestamp, p->timestamp, data, 1054 + data_length, t->callback_data); 1055 + } 1080 1056 } 1081 1057 EXPORT_SYMBOL(fw_core_handle_response); 1082 1058
+7
drivers/firewire/core.h
··· 247 247 void fw_request_get(struct fw_request *request); 248 248 void fw_request_put(struct fw_request *request); 249 249 250 + // Convert the value of IEEE 1394 CYCLE_TIME register to the format of timeStamp field in 251 + // descriptors of 1394 OHCI. 252 + static inline u32 cycle_time_to_ohci_tstamp(u32 tstamp) 253 + { 254 + return (tstamp & 0x0ffff000) >> 12; 255 + } 256 + 250 257 #define FW_PHY_CONFIG_NO_NODE_ID -1 251 258 #define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1 252 259 void fw_send_phy_config(struct fw_card *card,
+15 -2
drivers/firewire/ohci.c
··· 1623 1623 } 1624 1624 } 1625 1625 1626 + static u32 get_cycle_time(struct fw_ohci *ohci); 1627 + 1626 1628 static void at_context_transmit(struct context *ctx, struct fw_packet *packet) 1627 1629 { 1628 1630 unsigned long flags; ··· 1635 1633 if (HEADER_GET_DESTINATION(packet->header[0]) == ctx->ohci->node_id && 1636 1634 ctx->ohci->generation == packet->generation) { 1637 1635 spin_unlock_irqrestore(&ctx->ohci->lock, flags); 1636 + 1637 + // Timestamping on behalf of the hardware. 1638 + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); 1639 + 1638 1640 handle_local_request(ctx, packet); 1639 1641 return; 1640 1642 } ··· 1646 1640 ret = at_context_queue_packet(ctx, packet); 1647 1641 spin_unlock_irqrestore(&ctx->ohci->lock, flags); 1648 1642 1649 - if (ret < 0) 1650 - packet->callback(packet, &ctx->ohci->card, packet->ack); 1643 + if (ret < 0) { 1644 + // Timestamping on behalf of the hardware. 1645 + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ctx->ohci)); 1651 1646 1647 + packet->callback(packet, &ctx->ohci->card, packet->ack); 1648 + } 1652 1649 } 1653 1650 1654 1651 static void detect_dead_context(struct fw_ohci *ohci, ··· 2566 2557 log_ar_at_event(ohci, 'T', packet->speed, packet->header, 0x20); 2567 2558 driver_data->packet = NULL; 2568 2559 packet->ack = RCODE_CANCELLED; 2560 + 2561 + // Timestamping on behalf of the hardware. 2562 + packet->timestamp = cycle_time_to_ohci_tstamp(get_cycle_time(ohci)); 2563 + 2569 2564 packet->callback(packet, &ohci->card, packet->ack); 2570 2565 ret = 0; 2571 2566 out:
+12 -1
include/linux/firewire.h
··· 261 261 typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, 262 262 void *data, size_t length, 263 263 void *callback_data); 264 + typedef void (*fw_transaction_callback_with_tstamp_t)(struct fw_card *card, int rcode, 265 + u32 request_tstamp, u32 response_tstamp, void *data, 266 + size_t length, void *callback_data); 267 + 268 + union fw_transaction_callback { 269 + fw_transaction_callback_t without_tstamp; 270 + fw_transaction_callback_with_tstamp_t with_tstamp; 271 + }; 272 + 264 273 /* 265 274 * This callback handles an inbound request subaction. It is called in 266 275 * RCU read-side context, therefore must not sleep. ··· 321 312 struct fw_card *card; 322 313 bool is_split_transaction; 323 314 struct timer_list split_timeout_timer; 315 + u32 split_timeout_cycle; 324 316 325 317 struct fw_packet packet; 326 318 ··· 329 319 * The data passed to the callback is valid only during the 330 320 * callback. 331 321 */ 332 - fw_transaction_callback_t callback; 322 + union fw_transaction_callback callback; 323 + bool with_tstamp; 333 324 void *callback_data; 334 325 }; 335 326