diff options
Diffstat (limited to 'drivers/ide/ide-atapi.c')
-rw-r--r-- | drivers/ide/ide-atapi.c | 257 |
1 files changed, 147 insertions, 110 deletions
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c index 4e58b9e7a58a..e96c01260598 100644 --- a/drivers/ide/ide-atapi.c +++ b/drivers/ide/ide-atapi.c | |||
@@ -3,6 +3,7 @@ | |||
3 | */ | 3 | */ |
4 | 4 | ||
5 | #include <linux/kernel.h> | 5 | #include <linux/kernel.h> |
6 | #include <linux/cdrom.h> | ||
6 | #include <linux/delay.h> | 7 | #include <linux/delay.h> |
7 | #include <linux/ide.h> | 8 | #include <linux/ide.h> |
8 | #include <scsi/scsi.h> | 9 | #include <scsi/scsi.h> |
@@ -14,6 +15,13 @@ | |||
14 | #define debug_log(fmt, args...) do {} while (0) | 15 | #define debug_log(fmt, args...) do {} while (0) |
15 | #endif | 16 | #endif |
16 | 17 | ||
18 | #define ATAPI_MIN_CDB_BYTES 12 | ||
19 | |||
20 | static inline int dev_is_idecd(ide_drive_t *drive) | ||
21 | { | ||
22 | return drive->media == ide_cdrom || drive->media == ide_optical; | ||
23 | } | ||
24 | |||
17 | /* | 25 | /* |
18 | * Check whether we can support a device, | 26 | * Check whether we can support a device, |
19 | * based on the ATAPI IDENTIFY command results. | 27 | * based on the ATAPI IDENTIFY command results. |
@@ -233,18 +241,49 @@ void ide_retry_pc(ide_drive_t *drive, struct gendisk *disk) | |||
233 | } | 241 | } |
234 | EXPORT_SYMBOL_GPL(ide_retry_pc); | 242 | EXPORT_SYMBOL_GPL(ide_retry_pc); |
235 | 243 | ||
236 | int ide_scsi_expiry(ide_drive_t *drive) | 244 | int ide_cd_expiry(ide_drive_t *drive) |
237 | { | 245 | { |
238 | struct ide_atapi_pc *pc = drive->pc; | 246 | struct request *rq = drive->hwif->rq; |
247 | unsigned long wait = 0; | ||
239 | 248 | ||
240 | debug_log("%s called for %lu at %lu\n", __func__, | 249 | debug_log("%s: rq->cmd[0]: 0x%x\n", __func__, rq->cmd[0]); |
241 | pc->scsi_cmd->serial_number, jiffies); | ||
242 | 250 | ||
243 | pc->flags |= PC_FLAG_TIMEDOUT; | 251 | /* |
252 | * Some commands are *slow* and normally take a long time to complete. | ||
253 | * Usually we can use the ATAPI "disconnect" to bypass this, but not all | ||
254 | * commands/drives support that. Let ide_timer_expiry keep polling us | ||
255 | * for these. | ||
256 | */ | ||
257 | switch (rq->cmd[0]) { | ||
258 | case GPCMD_BLANK: | ||
259 | case GPCMD_FORMAT_UNIT: | ||
260 | case GPCMD_RESERVE_RZONE_TRACK: | ||
261 | case GPCMD_CLOSE_TRACK: | ||
262 | case GPCMD_FLUSH_CACHE: | ||
263 | wait = ATAPI_WAIT_PC; | ||
264 | break; | ||
265 | default: | ||
266 | if (!(rq->cmd_flags & REQ_QUIET)) | ||
267 | printk(KERN_INFO "cmd 0x%x timed out\n", | ||
268 | rq->cmd[0]); | ||
269 | wait = 0; | ||
270 | break; | ||
271 | } | ||
272 | return wait; | ||
273 | } | ||
274 | EXPORT_SYMBOL_GPL(ide_cd_expiry); | ||
244 | 275 | ||
245 | return 0; /* we do not want the IDE subsystem to retry */ | 276 | int ide_cd_get_xferlen(struct request *rq) |
277 | { | ||
278 | if (blk_fs_request(rq)) | ||
279 | return 32768; | ||
280 | else if (blk_sense_request(rq) || blk_pc_request(rq) || | ||
281 | rq->cmd_type == REQ_TYPE_ATA_PC) | ||
282 | return rq->data_len; | ||
283 | else | ||
284 | return 0; | ||
246 | } | 285 | } |
247 | EXPORT_SYMBOL_GPL(ide_scsi_expiry); | 286 | EXPORT_SYMBOL_GPL(ide_cd_get_xferlen); |
248 | 287 | ||
249 | /* | 288 | /* |
250 | * This is the usual interrupt handler which will be called during a packet | 289 | * This is the usual interrupt handler which will be called during a packet |
@@ -255,24 +294,17 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) | |||
255 | { | 294 | { |
256 | struct ide_atapi_pc *pc = drive->pc; | 295 | struct ide_atapi_pc *pc = drive->pc; |
257 | ide_hwif_t *hwif = drive->hwif; | 296 | ide_hwif_t *hwif = drive->hwif; |
258 | struct request *rq = hwif->hwgroup->rq; | 297 | struct request *rq = hwif->rq; |
259 | const struct ide_tp_ops *tp_ops = hwif->tp_ops; | 298 | const struct ide_tp_ops *tp_ops = hwif->tp_ops; |
260 | xfer_func_t *xferfunc; | 299 | xfer_func_t *xferfunc; |
261 | ide_expiry_t *expiry; | ||
262 | unsigned int timeout, temp; | 300 | unsigned int timeout, temp; |
263 | u16 bcount; | 301 | u16 bcount; |
264 | u8 stat, ireason, scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI), dsc = 0; | 302 | u8 stat, ireason, dsc = 0; |
265 | 303 | ||
266 | debug_log("Enter %s - interrupt handler\n", __func__); | 304 | debug_log("Enter %s - interrupt handler\n", __func__); |
267 | 305 | ||
268 | if (scsi) { | 306 | timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD |
269 | timeout = ide_scsi_get_timeout(pc); | 307 | : WAIT_TAPE_CMD; |
270 | expiry = ide_scsi_expiry; | ||
271 | } else { | ||
272 | timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD | ||
273 | : WAIT_TAPE_CMD; | ||
274 | expiry = NULL; | ||
275 | } | ||
276 | 308 | ||
277 | if (pc->flags & PC_FLAG_TIMEDOUT) { | 309 | if (pc->flags & PC_FLAG_TIMEDOUT) { |
278 | drive->pc_callback(drive, 0); | 310 | drive->pc_callback(drive, 0); |
@@ -284,8 +316,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) | |||
284 | 316 | ||
285 | if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { | 317 | if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { |
286 | if (hwif->dma_ops->dma_end(drive) || | 318 | if (hwif->dma_ops->dma_end(drive) || |
287 | (drive->media == ide_tape && !scsi && (stat & ATA_ERR))) { | 319 | (drive->media == ide_tape && (stat & ATA_ERR))) { |
288 | if (drive->media == ide_floppy && !scsi) | 320 | if (drive->media == ide_floppy) |
289 | printk(KERN_ERR "%s: DMA %s error\n", | 321 | printk(KERN_ERR "%s: DMA %s error\n", |
290 | drive->name, rq_data_dir(pc->rq) | 322 | drive->name, rq_data_dir(pc->rq) |
291 | ? "write" : "read"); | 323 | ? "write" : "read"); |
@@ -307,7 +339,7 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) | |||
307 | 339 | ||
308 | local_irq_enable_in_hardirq(); | 340 | local_irq_enable_in_hardirq(); |
309 | 341 | ||
310 | if (drive->media == ide_tape && !scsi && | 342 | if (drive->media == ide_tape && |
311 | (stat & ATA_ERR) && rq->cmd[0] == REQUEST_SENSE) | 343 | (stat & ATA_ERR) && rq->cmd[0] == REQUEST_SENSE) |
312 | stat &= ~ATA_ERR; | 344 | stat &= ~ATA_ERR; |
313 | 345 | ||
@@ -315,11 +347,8 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) | |||
315 | /* Error detected */ | 347 | /* Error detected */ |
316 | debug_log("%s: I/O error\n", drive->name); | 348 | debug_log("%s: I/O error\n", drive->name); |
317 | 349 | ||
318 | if (drive->media != ide_tape || scsi) { | 350 | if (drive->media != ide_tape) |
319 | pc->rq->errors++; | 351 | pc->rq->errors++; |
320 | if (scsi) | ||
321 | goto cmd_finished; | ||
322 | } | ||
323 | 352 | ||
324 | if (rq->cmd[0] == REQUEST_SENSE) { | 353 | if (rq->cmd[0] == REQUEST_SENSE) { |
325 | printk(KERN_ERR "%s: I/O error in request sense" | 354 | printk(KERN_ERR "%s: I/O error in request sense" |
@@ -335,7 +364,6 @@ static ide_startstop_t ide_pc_intr(ide_drive_t *drive) | |||
335 | /* queued, but not started */ | 364 | /* queued, but not started */ |
336 | return ide_stopped; | 365 | return ide_stopped; |
337 | } | 366 | } |
338 | cmd_finished: | ||
339 | pc->error = 0; | 367 | pc->error = 0; |
340 | 368 | ||
341 | if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0) | 369 | if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0) |
@@ -382,25 +410,8 @@ cmd_finished: | |||
382 | "us more data than expected - " | 410 | "us more data than expected - " |
383 | "discarding data\n", | 411 | "discarding data\n", |
384 | drive->name); | 412 | drive->name); |
385 | if (scsi) | 413 | |
386 | temp = pc->buf_size - pc->xferred; | 414 | ide_pad_transfer(drive, 0, bcount); |
387 | else | ||
388 | temp = 0; | ||
389 | if (temp) { | ||
390 | if (pc->sg) | ||
391 | drive->pc_io_buffers(drive, pc, | ||
392 | temp, 0); | ||
393 | else | ||
394 | tp_ops->input_data(drive, NULL, | ||
395 | pc->cur_pos, temp); | ||
396 | printk(KERN_ERR "%s: transferred %d of " | ||
397 | "%d bytes\n", | ||
398 | drive->name, | ||
399 | temp, bcount); | ||
400 | } | ||
401 | pc->xferred += temp; | ||
402 | pc->cur_pos += temp; | ||
403 | ide_pad_transfer(drive, 0, bcount - temp); | ||
404 | goto next_irq; | 415 | goto next_irq; |
405 | } | 416 | } |
406 | debug_log("The device wants to send us more data than " | 417 | debug_log("The device wants to send us more data than " |
@@ -410,14 +421,13 @@ cmd_finished: | |||
410 | } else | 421 | } else |
411 | xferfunc = tp_ops->output_data; | 422 | xferfunc = tp_ops->output_data; |
412 | 423 | ||
413 | if ((drive->media == ide_floppy && !scsi && !pc->buf) || | 424 | if ((drive->media == ide_floppy && !pc->buf) || |
414 | (drive->media == ide_tape && !scsi && pc->bh) || | 425 | (drive->media == ide_tape && pc->bh)) { |
415 | (scsi && pc->sg)) { | ||
416 | int done = drive->pc_io_buffers(drive, pc, bcount, | 426 | int done = drive->pc_io_buffers(drive, pc, bcount, |
417 | !!(pc->flags & PC_FLAG_WRITING)); | 427 | !!(pc->flags & PC_FLAG_WRITING)); |
418 | 428 | ||
419 | /* FIXME: don't do partial completions */ | 429 | /* FIXME: don't do partial completions */ |
420 | if (drive->media == ide_floppy && !scsi) | 430 | if (drive->media == ide_floppy) |
421 | ide_end_request(drive, 1, done >> 9); | 431 | ide_end_request(drive, 1, done >> 9); |
422 | } else | 432 | } else |
423 | xferfunc(drive, NULL, pc->cur_pos, bcount); | 433 | xferfunc(drive, NULL, pc->cur_pos, bcount); |
@@ -430,7 +440,7 @@ cmd_finished: | |||
430 | rq->cmd[0], bcount); | 440 | rq->cmd[0], bcount); |
431 | next_irq: | 441 | next_irq: |
432 | /* And set the interrupt handler again */ | 442 | /* And set the interrupt handler again */ |
433 | ide_set_handler(drive, ide_pc_intr, timeout, expiry); | 443 | ide_set_handler(drive, ide_pc_intr, timeout, NULL); |
434 | return ide_started; | 444 | return ide_started; |
435 | } | 445 | } |
436 | 446 | ||
@@ -479,11 +489,12 @@ static int ide_delayed_transfer_pc(ide_drive_t *drive) | |||
479 | 489 | ||
480 | static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) | 490 | static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) |
481 | { | 491 | { |
482 | struct ide_atapi_pc *pc = drive->pc; | 492 | struct ide_atapi_pc *uninitialized_var(pc); |
483 | ide_hwif_t *hwif = drive->hwif; | 493 | ide_hwif_t *hwif = drive->hwif; |
484 | struct request *rq = hwif->hwgroup->rq; | 494 | struct request *rq = hwif->rq; |
485 | ide_expiry_t *expiry; | 495 | ide_expiry_t *expiry; |
486 | unsigned int timeout; | 496 | unsigned int timeout; |
497 | int cmd_len; | ||
487 | ide_startstop_t startstop; | 498 | ide_startstop_t startstop; |
488 | u8 ireason; | 499 | u8 ireason; |
489 | 500 | ||
@@ -493,101 +504,127 @@ static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) | |||
493 | return startstop; | 504 | return startstop; |
494 | } | 505 | } |
495 | 506 | ||
496 | ireason = ide_read_ireason(drive); | 507 | if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { |
497 | if (drive->media == ide_tape && | 508 | if (drive->dma) |
498 | (drive->dev_flags & IDE_DFLAG_SCSI) == 0) | 509 | drive->waiting_for_dma = 1; |
499 | ireason = ide_wait_ireason(drive, ireason); | ||
500 | |||
501 | if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) { | ||
502 | printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " | ||
503 | "a packet command\n", drive->name); | ||
504 | return ide_do_reset(drive); | ||
505 | } | 510 | } |
506 | 511 | ||
507 | /* | 512 | if (dev_is_idecd(drive)) { |
508 | * If necessary schedule the packet transfer to occur 'timeout' | 513 | /* ATAPI commands get padded out to 12 bytes minimum */ |
509 | * miliseconds later in ide_delayed_transfer_pc() after the device | 514 | cmd_len = COMMAND_SIZE(rq->cmd[0]); |
510 | * says it's ready for a packet. | 515 | if (cmd_len < ATAPI_MIN_CDB_BYTES) |
511 | */ | 516 | cmd_len = ATAPI_MIN_CDB_BYTES; |
512 | if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { | 517 | |
513 | timeout = drive->pc_delay; | 518 | timeout = rq->timeout; |
514 | expiry = &ide_delayed_transfer_pc; | 519 | expiry = ide_cd_expiry; |
515 | } else { | 520 | } else { |
516 | if (drive->dev_flags & IDE_DFLAG_SCSI) { | 521 | pc = drive->pc; |
517 | timeout = ide_scsi_get_timeout(pc); | 522 | |
518 | expiry = ide_scsi_expiry; | 523 | cmd_len = ATAPI_MIN_CDB_BYTES; |
524 | |||
525 | /* | ||
526 | * If necessary schedule the packet transfer to occur 'timeout' | ||
527 | * miliseconds later in ide_delayed_transfer_pc() after the | ||
528 | * device says it's ready for a packet. | ||
529 | */ | ||
530 | if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { | ||
531 | timeout = drive->pc_delay; | ||
532 | expiry = &ide_delayed_transfer_pc; | ||
519 | } else { | 533 | } else { |
520 | timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD | 534 | timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD |
521 | : WAIT_TAPE_CMD; | 535 | : WAIT_TAPE_CMD; |
522 | expiry = NULL; | 536 | expiry = NULL; |
523 | } | 537 | } |
538 | |||
539 | ireason = ide_read_ireason(drive); | ||
540 | if (drive->media == ide_tape) | ||
541 | ireason = ide_wait_ireason(drive, ireason); | ||
542 | |||
543 | if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) { | ||
544 | printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing " | ||
545 | "a packet command\n", drive->name); | ||
546 | |||
547 | return ide_do_reset(drive); | ||
548 | } | ||
524 | } | 549 | } |
525 | 550 | ||
526 | /* Set the interrupt routine */ | 551 | /* Set the interrupt routine */ |
527 | ide_set_handler(drive, ide_pc_intr, timeout, expiry); | 552 | ide_set_handler(drive, |
553 | (dev_is_idecd(drive) ? drive->irq_handler | ||
554 | : ide_pc_intr), | ||
555 | timeout, expiry); | ||
528 | 556 | ||
529 | /* Begin DMA, if necessary */ | 557 | /* Begin DMA, if necessary */ |
530 | if (pc->flags & PC_FLAG_DMA_OK) { | 558 | if (dev_is_idecd(drive)) { |
531 | pc->flags |= PC_FLAG_DMA_IN_PROGRESS; | 559 | if (drive->dma) |
532 | hwif->dma_ops->dma_start(drive); | 560 | hwif->dma_ops->dma_start(drive); |
561 | } else { | ||
562 | if (pc->flags & PC_FLAG_DMA_OK) { | ||
563 | pc->flags |= PC_FLAG_DMA_IN_PROGRESS; | ||
564 | hwif->dma_ops->dma_start(drive); | ||
565 | } | ||
533 | } | 566 | } |
534 | 567 | ||
535 | /* Send the actual packet */ | 568 | /* Send the actual packet */ |
536 | if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) | 569 | if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) |
537 | hwif->tp_ops->output_data(drive, NULL, rq->cmd, 12); | 570 | hwif->tp_ops->output_data(drive, NULL, rq->cmd, cmd_len); |
538 | 571 | ||
539 | return ide_started; | 572 | return ide_started; |
540 | } | 573 | } |
541 | 574 | ||
542 | ide_startstop_t ide_issue_pc(ide_drive_t *drive, unsigned int timeout, | 575 | ide_startstop_t ide_issue_pc(ide_drive_t *drive) |
543 | ide_expiry_t *expiry) | ||
544 | { | 576 | { |
545 | struct ide_atapi_pc *pc = drive->pc; | 577 | struct ide_atapi_pc *pc; |
546 | ide_hwif_t *hwif = drive->hwif; | 578 | ide_hwif_t *hwif = drive->hwif; |
579 | ide_expiry_t *expiry = NULL; | ||
580 | unsigned int timeout; | ||
547 | u32 tf_flags; | 581 | u32 tf_flags; |
548 | u16 bcount; | 582 | u16 bcount; |
549 | u8 scsi = !!(drive->dev_flags & IDE_DFLAG_SCSI); | ||
550 | 583 | ||
551 | /* We haven't transferred any data yet */ | 584 | if (dev_is_idecd(drive)) { |
552 | pc->xferred = 0; | 585 | tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; |
553 | pc->cur_pos = pc->buf; | 586 | bcount = ide_cd_get_xferlen(hwif->rq); |
587 | expiry = ide_cd_expiry; | ||
588 | timeout = ATAPI_WAIT_PC; | ||
554 | 589 | ||
555 | /* Request to transfer the entire buffer at once */ | 590 | if (drive->dma) |
556 | if (drive->media == ide_tape && scsi == 0) | 591 | drive->dma = !hwif->dma_ops->dma_setup(drive); |
557 | bcount = pc->req_xfer; | 592 | } else { |
558 | else | 593 | pc = drive->pc; |
559 | bcount = min(pc->req_xfer, 63 * 1024); | ||
560 | 594 | ||
561 | if (pc->flags & PC_FLAG_DMA_ERROR) { | 595 | /* We haven't transferred any data yet */ |
562 | pc->flags &= ~PC_FLAG_DMA_ERROR; | 596 | pc->xferred = 0; |
563 | ide_dma_off(drive); | 597 | pc->cur_pos = pc->buf; |
564 | } | ||
565 | 598 | ||
566 | if ((pc->flags & PC_FLAG_DMA_OK) && | 599 | tf_flags = IDE_TFLAG_OUT_DEVICE; |
567 | (drive->dev_flags & IDE_DFLAG_USING_DMA)) { | 600 | bcount = ((drive->media == ide_tape) ? |
568 | if (scsi) | 601 | pc->req_xfer : |
569 | hwif->sg_mapped = 1; | 602 | min(pc->req_xfer, 63 * 1024)); |
570 | drive->dma = !hwif->dma_ops->dma_setup(drive); | ||
571 | if (scsi) | ||
572 | hwif->sg_mapped = 0; | ||
573 | } | ||
574 | 603 | ||
575 | if (!drive->dma) | 604 | if (pc->flags & PC_FLAG_DMA_ERROR) { |
576 | pc->flags &= ~PC_FLAG_DMA_OK; | 605 | pc->flags &= ~PC_FLAG_DMA_ERROR; |
606 | ide_dma_off(drive); | ||
607 | } | ||
577 | 608 | ||
578 | if (scsi) | 609 | if ((pc->flags & PC_FLAG_DMA_OK) && |
579 | tf_flags = 0; | 610 | (drive->dev_flags & IDE_DFLAG_USING_DMA)) |
580 | else if (drive->media == ide_cdrom || drive->media == ide_optical) | 611 | drive->dma = !hwif->dma_ops->dma_setup(drive); |
581 | tf_flags = IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL; | 612 | |
582 | else | 613 | if (!drive->dma) |
583 | tf_flags = IDE_TFLAG_OUT_DEVICE; | 614 | pc->flags &= ~PC_FLAG_DMA_OK; |
615 | |||
616 | timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD | ||
617 | : WAIT_TAPE_CMD; | ||
618 | } | ||
584 | 619 | ||
585 | ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma); | 620 | ide_pktcmd_tf_load(drive, tf_flags, bcount, drive->dma); |
586 | 621 | ||
587 | /* Issue the packet command */ | 622 | /* Issue the packet command */ |
588 | if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { | 623 | if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { |
624 | if (drive->dma) | ||
625 | drive->waiting_for_dma = 0; | ||
589 | ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc, | 626 | ide_execute_command(drive, ATA_CMD_PACKET, ide_transfer_pc, |
590 | timeout, NULL); | 627 | timeout, expiry); |
591 | return ide_started; | 628 | return ide_started; |
592 | } else { | 629 | } else { |
593 | ide_execute_pkt_cmd(drive); | 630 | ide_execute_pkt_cmd(drive); |