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

firewire: Implement basic isochronous receive functionality.

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>

authored by

Kristian Høgsberg and committed by
Stefan Richter
295e3feb 30200739

+181 -30
+19 -4
drivers/firewire/fw-device-cdev.c
··· 406 406 if (copy_from_user(&request, arg, sizeof request)) 407 407 return -EFAULT; 408 408 409 + if (request.type > FW_ISO_CONTEXT_RECEIVE) 410 + return -EINVAL; 411 + 409 412 client->iso_context = fw_iso_context_create(client->device->card, 410 - FW_ISO_CONTEXT_TRANSMIT, 413 + request.type, 414 + request.header_size, 411 415 iso_callback, client); 412 416 if (IS_ERR(client->iso_context)) 413 417 return PTR_ERR(client->iso_context); ··· 423 419 { 424 420 struct fw_cdev_queue_iso request; 425 421 struct fw_cdev_iso_packet __user *p, *end, *next; 426 - unsigned long payload, payload_end; 422 + unsigned long payload, payload_end, header_length; 427 423 int count; 428 424 struct { 429 425 struct fw_iso_packet packet; ··· 460 456 while (p < end) { 461 457 if (__copy_from_user(&u.packet, p, sizeof *p)) 462 458 return -EFAULT; 459 + 460 + if (client->iso_context->type == FW_ISO_CONTEXT_TRANSMIT) { 461 + header_length = u.packet.header_length; 462 + } else { 463 + /* We require that header_length is a multiple of 464 + * the fixed header size, ctx->header_size */ 465 + if (u.packet.header_length % client->iso_context->header_size != 0) 466 + return -EINVAL; 467 + header_length = 0; 468 + } 469 + 463 470 next = (struct fw_cdev_iso_packet __user *) 464 - &p->header[u.packet.header_length / 4]; 471 + &p->header[header_length / 4]; 465 472 if (next > end) 466 473 return -EINVAL; 467 474 if (__copy_from_user 468 - (u.packet.header, p->header, u.packet.header_length)) 475 + (u.packet.header, p->header, header_length)) 469 476 return -EFAULT; 470 477 if (u.packet.skip && 471 478 u.packet.header_length + u.packet.payload_length > 0)
+5
drivers/firewire/fw-device-cdev.h
··· 125 125 __u32 length; 126 126 }; 127 127 128 + #define FW_CDEV_ISO_CONTEXT_TRANSMIT 0 129 + #define FW_CDEV_ISO_CONTEXT_RECEIVE 1 130 + 128 131 struct fw_cdev_create_iso_context { 132 + __u32 type; 133 + __u32 header_size; 129 134 __u32 handle; 130 135 }; 131 136
+4 -3
drivers/firewire/fw-iso.c
··· 105 105 buffer->pages = NULL; 106 106 } 107 107 108 - struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, 109 - fw_iso_callback_t callback, 110 - void *callback_data) 108 + struct fw_iso_context * 109 + fw_iso_context_create(struct fw_card *card, int type, size_t header_size, 110 + fw_iso_callback_t callback, void *callback_data) 111 111 { 112 112 struct fw_iso_context *ctx; 113 113 ··· 117 117 118 118 ctx->card = card; 119 119 ctx->type = type; 120 + ctx->header_size = header_size; 120 121 ctx->callback = callback; 121 122 ctx->callback_data = callback_data; 122 123
+149 -19
drivers/firewire/fw-ohci.c
··· 45 45 #define descriptor_irq_error (1 << 4) 46 46 #define descriptor_irq_always (3 << 4) 47 47 #define descriptor_branch_always (3 << 2) 48 + #define descriptor_wait (3 << 0) 48 49 49 50 struct descriptor { 50 51 __le16 req_count; ··· 54 53 __le32 branch_address; 55 54 __le16 res_count; 56 55 __le16 transfer_status; 56 + } __attribute__((aligned(16))); 57 + 58 + struct db_descriptor { 59 + __le16 first_size; 60 + __le16 control; 61 + __le16 second_req_count; 62 + __le16 first_req_count; 63 + __le32 branch_address; 64 + __le16 second_res_count; 65 + __le16 first_res_count; 66 + __le32 reserved0; 67 + __le32 first_buffer; 68 + __le32 second_buffer; 69 + __le32 reserved1; 57 70 } __attribute__((aligned(16))); 58 71 59 72 #define control_set(regs) (regs) ··· 186 171 return container_of(card, struct fw_ohci, card); 187 172 } 188 173 189 - #define CONTEXT_CYCLE_MATCH_ENABLE 0x80000000 174 + #define IT_CONTEXT_CYCLE_MATCH_ENABLE 0x80000000 175 + #define IR_CONTEXT_BUFFER_FILL 0x80000000 176 + #define IR_CONTEXT_ISOCH_HEADER 0x40000000 177 + #define IR_CONTEXT_CYCLE_MATCH_ENABLE 0x20000000 178 + #define IR_CONTEXT_MULTI_CHANNEL_MODE 0x10000000 179 + #define IR_CONTEXT_DUAL_BUFFER_MODE 0x08000000 190 180 191 181 #define CONTEXT_RUN 0x8000 192 182 #define CONTEXT_WAKE 0x1000 ··· 538 518 return d; 539 519 } 540 520 541 - static void context_run(struct context *ctx, u32 cycle_match) 521 + static void context_run(struct context *ctx, u32 extra) 542 522 { 543 523 struct fw_ohci *ohci = ctx->ohci; 544 524 545 525 reg_write(ohci, command_ptr(ctx->regs), 546 526 le32_to_cpu(ctx->tail_descriptor_last->branch_address)); 547 527 reg_write(ohci, control_clear(ctx->regs), ~0); 548 - reg_write(ohci, control_set(ctx->regs), CONTEXT_RUN | cycle_match); 528 + reg_write(ohci, control_set(ctx->regs), CONTEXT_RUN | extra); 549 529 flush_writes(ohci); 550 530 } 551 531 ··· 1260 1240 return retval; 1261 1241 } 1262 1242 1263 - static void ir_context_tasklet(unsigned long data) 1243 + static int handle_ir_packet(struct context *context, 1244 + struct descriptor *d, 1245 + struct descriptor *last) 1264 1246 { 1265 - struct iso_context *ctx = (struct iso_context *)data; 1247 + struct iso_context *ctx = 1248 + container_of(context, struct iso_context, context); 1249 + struct db_descriptor *db = (struct db_descriptor *) d; 1250 + 1251 + if (db->first_res_count > 0 && db->second_res_count > 0) 1252 + /* This descriptor isn't done yet, stop iteration. */ 1253 + return 0; 1266 1254 1267 - (void)ctx; 1255 + if (le16_to_cpu(db->control) & descriptor_irq_always) 1256 + /* FIXME: we should pass payload address here. */ 1257 + ctx->base.callback(&ctx->base, 1258 + 0, 0, 1259 + ctx->base.callback_data); 1260 + 1261 + return 1; 1268 1262 } 1269 1263 1270 1264 #define ISO_BUFFER_SIZE (64 * 1024) ··· 1308 1274 struct fw_ohci *ohci = fw_ohci(card); 1309 1275 struct iso_context *ctx, *list; 1310 1276 descriptor_callback_t callback; 1311 - u32 *mask; 1277 + u32 *mask, regs; 1312 1278 unsigned long flags; 1313 1279 int index, retval; 1314 1280 ··· 1317 1283 list = ohci->it_context_list; 1318 1284 callback = handle_it_packet; 1319 1285 } else { 1320 - return ERR_PTR(-EINVAL); 1286 + mask = &ohci->ir_context_mask; 1287 + list = ohci->ir_context_list; 1288 + callback = handle_ir_packet; 1321 1289 } 1322 1290 1323 1291 spin_lock_irqsave(&ohci->lock, flags); ··· 1331 1295 if (index < 0) 1332 1296 return ERR_PTR(-EBUSY); 1333 1297 1298 + if (type == FW_ISO_CONTEXT_TRANSMIT) 1299 + regs = OHCI1394_IsoXmitContextBase(index); 1300 + else 1301 + regs = OHCI1394_IsoRcvContextBase(index); 1302 + 1334 1303 ctx = &list[index]; 1335 1304 memset(ctx, 0, sizeof *ctx); 1336 1305 retval = context_init(&ctx->context, ohci, ISO_BUFFER_SIZE, 1337 - OHCI1394_IsoXmitContextBase(index), callback); 1306 + regs, callback); 1338 1307 if (retval < 0) { 1339 1308 spin_lock_irqsave(&ohci->lock, flags); 1340 1309 *mask |= 1 << index; ··· 1357 1316 u32 cycle_match = 0; 1358 1317 int index; 1359 1318 1360 - index = ctx - ohci->it_context_list; 1361 - if (cycle > 0) 1362 - cycle_match = CONTEXT_CYCLE_MATCH_ENABLE | 1363 - (cycle & 0x7fff) << 16; 1319 + if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) { 1320 + index = ctx - ohci->it_context_list; 1321 + if (cycle > 0) 1322 + cycle_match = IT_CONTEXT_CYCLE_MATCH_ENABLE | 1323 + (cycle & 0x7fff) << 16; 1324 + 1325 + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 1 << index); 1326 + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); 1327 + context_run(&ctx->context, cycle_match); 1328 + } else { 1329 + index = ctx - ohci->ir_context_list; 1364 1330 1365 - reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1 << index); 1366 - context_run(&ctx->context, cycle_match); 1331 + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 1 << index); 1332 + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index); 1333 + reg_write(ohci, context_match(ctx->context.regs), 1334 + 0xf0000000 | ctx->base.channel); 1335 + context_run(&ctx->context, IR_CONTEXT_DUAL_BUFFER_MODE); 1336 + } 1367 1337 1368 1338 return 0; 1369 1339 } ··· 1407 1355 } 1408 1356 1409 1357 static int 1410 - ohci_queue_iso(struct fw_iso_context *base, 1411 - struct fw_iso_packet *packet, 1412 - struct fw_iso_buffer *buffer, 1413 - unsigned long payload) 1358 + ohci_queue_iso_transmit(struct fw_iso_context *base, 1359 + struct fw_iso_packet *packet, 1360 + struct fw_iso_buffer *buffer, 1361 + unsigned long payload) 1414 1362 { 1415 1363 struct iso_context *ctx = container_of(base, struct iso_context, base); 1416 1364 struct descriptor *d, *last, *pd; ··· 1501 1449 context_append(&ctx->context, d, z, header_z); 1502 1450 1503 1451 return 0; 1452 + } 1453 + 1454 + static int 1455 + ohci_queue_iso_receive(struct fw_iso_context *base, 1456 + struct fw_iso_packet *packet, 1457 + struct fw_iso_buffer *buffer, 1458 + unsigned long payload) 1459 + { 1460 + struct iso_context *ctx = container_of(base, struct iso_context, base); 1461 + struct db_descriptor *db = NULL; 1462 + struct descriptor *d; 1463 + struct fw_iso_packet *p; 1464 + dma_addr_t d_bus, page_bus; 1465 + u32 z, header_z, length, rest; 1466 + int page, offset; 1467 + 1468 + /* FIXME: Cycle lost behavior should be configurable: lose 1469 + * packet, retransmit or terminate.. */ 1470 + 1471 + p = packet; 1472 + z = 2; 1473 + 1474 + /* Get header size in number of descriptors. */ 1475 + header_z = DIV_ROUND_UP(p->header_length, sizeof *d); 1476 + page = payload >> PAGE_SHIFT; 1477 + offset = payload & ~PAGE_MASK; 1478 + rest = p->payload_length; 1479 + 1480 + /* FIXME: OHCI 1.0 doesn't support dual buffer receive */ 1481 + /* FIXME: handle descriptor_wait */ 1482 + /* FIXME: make packet-per-buffer/dual-buffer a context option */ 1483 + while (rest > 0) { 1484 + d = context_get_descriptors(&ctx->context, 1485 + z + header_z, &d_bus); 1486 + if (d == NULL) 1487 + return -ENOMEM; 1488 + 1489 + db = (struct db_descriptor *) d; 1490 + db->control = cpu_to_le16(descriptor_status | 1491 + descriptor_branch_always); 1492 + db->first_size = cpu_to_le16(ctx->base.header_size); 1493 + db->first_req_count = cpu_to_le16(p->header_length); 1494 + db->second_req_count = cpu_to_le16(p->payload_length); 1495 + db->first_res_count = cpu_to_le16(db->first_req_count); 1496 + db->second_res_count = cpu_to_le16(db->second_req_count); 1497 + 1498 + db->first_buffer = cpu_to_le32(d_bus + sizeof *db); 1499 + 1500 + if (offset + rest < PAGE_SIZE) 1501 + length = rest; 1502 + else 1503 + length = PAGE_SIZE - offset; 1504 + 1505 + page_bus = page_private(buffer->pages[page]); 1506 + db->second_buffer = cpu_to_le32(page_bus + offset); 1507 + 1508 + context_append(&ctx->context, d, z, header_z); 1509 + offset = (offset + length) & ~PAGE_MASK; 1510 + rest -= length; 1511 + page++; 1512 + } 1513 + 1514 + if (p->interrupt) 1515 + db->control |= cpu_to_le16(descriptor_irq_always); 1516 + 1517 + return 0; 1518 + } 1519 + 1520 + static int 1521 + ohci_queue_iso(struct fw_iso_context *base, 1522 + struct fw_iso_packet *packet, 1523 + struct fw_iso_buffer *buffer, 1524 + unsigned long payload) 1525 + { 1526 + if (base->type == FW_ISO_CONTEXT_TRANSMIT) 1527 + return ohci_queue_iso_transmit(base, packet, buffer, payload); 1528 + else 1529 + return ohci_queue_iso_receive(base, packet, buffer, payload); 1504 1530 } 1505 1531 1506 1532 static const struct fw_card_driver ohci_driver = {
+1
drivers/firewire/fw-ohci.h
··· 103 103 #define OHCI1394_IsoXmitCommandPtr(n) (0x20C + 16 * (n)) 104 104 105 105 /* Isochronous receive registers */ 106 + #define OHCI1394_IsoRcvContextBase(n) (0x400 + 32 * (n)) 106 107 #define OHCI1394_IsoRcvContextControlSet(n) (0x400 + 32 * (n)) 107 108 #define OHCI1394_IsoRcvContextControlClear(n) (0x404 + 32 * (n)) 108 109 #define OHCI1394_IsoRcvCommandPtr(n) (0x40C + 32 * (n))
+3 -4
drivers/firewire/fw-transaction.h
··· 354 354 int type; 355 355 int channel; 356 356 int speed; 357 + size_t header_size; 357 358 fw_iso_callback_t callback; 358 359 void *callback_data; 359 360 }; ··· 370 369 fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); 371 370 372 371 struct fw_iso_context * 373 - fw_iso_context_create(struct fw_card *card, int type, 374 - fw_iso_callback_t callback, 375 - void *callback_data); 376 - 372 + fw_iso_context_create(struct fw_card *card, int type, size_t header_size, 373 + fw_iso_callback_t callback, void *callback_data); 377 374 378 375 void 379 376 fw_iso_context_destroy(struct fw_iso_context *ctx);