Index: head/sbin/atacontrol/atacontrol.8 =================================================================== --- head/sbin/atacontrol/atacontrol.8 (revision 226178) +++ head/sbin/atacontrol/atacontrol.8 (revision 226179) @@ -1,379 +1,405 @@ .\" .\" Copyright (c) 2000,2001,2002 Søren Schmidt .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd February 21, 2009 +.Dd October 9, 2011 .Dt ATACONTROL 8 .Os .Sh NAME .Nm atacontrol .Nd ATA device driver control program +.Pp +This utility was +.Em deprecated +in +.Fx 9.0 . +See +.Sx NOTES . .Sh SYNOPSIS .Nm .Aq Ar command .Ar args .Pp .Nm .Ic attach .Ar channel .Nm .Ic detach .Ar channel .Nm .Ic reinit .Ar channel .Nm .Ic create .Ar type Oo Ar interleave Oc Ar disk0 ... diskN .Nm .Ic delete .Ar raid .Nm .Ic addspare .Ar raid disk .Nm .Ic rebuild .Ar raid .Nm .Ic status .Ar raid .Nm .Ic mode .Ar device .Op Ar mode .Nm .Ic info .Ar channel .Nm .Ic cap .Ar device .Nm .Ic spindown .Ar device .Op Ar seconds .Nm .Ic list .Sh DESCRIPTION The .Nm utility is a control program that provides the user access and control to the .Fx .Xr ata 4 subsystem. .Pp The .Nm utility can cause severe system crashes and loss of data if used improperly. Please exercise caution when using this command! .Pp The .Ar channel argument is the ATA channel device (e.g., ata0) on which to operate. The following commands are supported: .Bl -tag -width ".Ic addspare" .It Ic attach Attach an ATA .Ar channel . Devices on the channel are probed and attached as is done on boot. .It Ic detach Detach an ATA .Ar channel . Devices on the channel are removed from the kernel, and all outstanding transfers etc.\& are returned back to the system marked as failed. .It Ic reinit Reinitialize an ATA .Ar channel . Both devices on the channel are reset and initialized to the parameters the ATA driver has stored internally. Devices that have gone bad and no longer respond to the probe, or devices that have physically been removed, are removed from the kernel. Likewise are devices that show up during a reset, probed and attached. .It Ic create Create a .Ar type ATA RAID. The type can be .Cm RAID0 (stripe), .Cm RAID1 (mirror), .Cm RAID0+1 , .Cm SPAN or .Cm JBOD . In case the RAID has a .Cm RAID0 component, the .Ar interleave must be specified in number of sectors. The RAID will be created of the individual disks named .Bk -words .Ar disk0 ... diskN . .Ek .Pp Although the ATA driver allows for creating an ATA RAID on disks with any controller, there are restrictions. It is only possible to boot on an array if it is either located on a .Dq real ATA RAID controller like the Promise or Highpoint controllers, or if the RAID declared is of .Cm RAID1 or .Cm SPAN type; in case of a .Cm SPAN , the partition to boot must reside on the first disk in the SPAN. .It Ic delete Delete a RAID array on a RAID capable ATA controller. .It Ic addspare Add a spare disk to an existing RAID. .It Ic rebuild Rebuild a RAID1 array on a RAID capable ATA controller. .It Ic status Get the status of an ATA RAID. .It Ic mode Without the .Ar mode argument, the current transfer mode of the device are printed. If the .Ar mode argument is given, the ATA driver is asked to change the transfer mode to the one given. The ATA driver will reject modes that are not supported by the hardware. Modes are given like .Dq Li PIO3 , .Dq Li udma2 , .Dq Li udma100 , case does not matter. .Pp Currently supported modes are: .Cm BIOSPIO , PIO0 , PIO1 , PIO2 , PIO3 , PIO4 , WDMA2 , UDMA2 (alias .Cm UDMA33 ) , .Cm UDMA4 (alias .Cm UDMA66 ) , .Cm UDMA5 (alias .Cm UDMA100 ) , .Cm UDMA6 (alias .Cm UDMA133 ) , .Cm SATA150 , SATA300 , USB , USB1 , USB2 and .Cm BIOSDMA . .It Ic cap Show detailed info about the device on .Ar device . .It Ic spindown Set or report timeout after which the .Ar device will be spun down. To arm the timeout the device needs at least one more request after setting the timeout. To disable spindown, set the timeout to zero. No further actions are needed in this case. .It Ic info Show info about the attached devices on the .Ar channel . The device name and manufacture/version strings are shown. .It Ic list Show info about all attached devices on all active controllers. .El .Sh EXAMPLES To get information on devices attached to a channel, use the command line: .Pp .Dl "atacontrol info ata0" .Pp To see the devices' current access modes, use the command line: .Pp .Dl "atacontrol mode ad0" .Pp which results in the modes of the devices being displayed as a string like this: .Pp .Dl "current mode = UDMA100" .Pp You can set the mode with .Nm and a string like the above, for example: .Pp .Dl "atacontrol mode ad0 PIO4" .Pp The new modes are set as soon as the .Nm command returns. .Pp The atacontrol command can also be used to create purely software RAID arrays in systems that do NOT have a "real" hardware RAID card such as a Highpoint or Promise card. A common scenario is a 1U server such as the HP DL320 G4 or G5. These servers contain a SATA controller that has 2 channels that can contain 2 disks per channel, but the servers are wired to only place a single SATA drive on each channel. These servers do have a "pseudo" RAID BIOS but it uses a proprietary format that is not compatible with the ata driver, and thus their RAID bios must be switched off. Another common scenario would be a Promise UDMA100 controller card that did not contain the Fasttrack RAID BIOS, but did contain 2 UDMA channels. 1 disk would be attached to one channel and the other disk would be attached to the other channel. It is NOT recommended to create such arrays on a primary/secondary pair on a SINGLE channel since the throughput of the mirror would be severely compromised, the ability to rebuild the array in the event of a disk failure would be greatly complicated, and if a disk controller electronics failed it could wedge the channel and take both disks in the mirror offline. (which would defeat the purpose of having a mirror in the first place) .Pp A quick and dirty way to create such a mirrored array on a new system is to boot off the FreeBSD install CD, do a minimal scratch install, abort out of the post install questions, and at the command line issue the command: .Pp .Dl "atacontrol create RAID1 ad4 ad6" .Pp then immediately issue a reboot and boot from the installation CD again, and during the installation, you will now see "ar0" listed as a disk to install on, and install on that instead of ad4, ad6, etc. .Pp To get information about the status of a RAID array in the system use the command line: .Pp .Dl "atacontrol status ar0" .Pp A typical output showing good health on a RAID array might be as follows: .Pp .Dl "ar0: ATA RAID1 subdisks: ad4 ad6 status: READY" .Pp If a disk drive in a RAID1 array dies the system will mark the disk in a DOWN state and change the array status to DEGRADED. This can ALSO happen in rare instances due to a power fluctuation or other event causing the system to not shutdown properly. In that case the output will look like the following: .Pp .Dl "ar0: ATA RAID1 subdisks: ad4 DOWN status: DEGRADED" .Pp For a mirrored RAID1 system the server WILL ALLOW you to remove a dead SATA disk drive (if the drive is in a hot-swap tray) without freezing up the system, so you can remove the disk and while you are obtaining a replacement the server can run from the active disk. The only caveat is that if the active disk is ad6, the system most likely will NOT be able to be rebooted since most systems only support booting from the first disk drive. .Pp To deactivate the DOWN disk ad6 to allow for it to be ejected, use the following: .Pp .Dl "atacontrol detach ata3" .Pp then eject or remove the disk. Note that this only works if the 2 disks in the mirror are on separate channels (which is the standard setup for 1-U servers like the HP DL320). When the new disk drive is obtained, make sure it is blank, then shut the system down. At this point, if the system has a RAID array card like a Highpoint or Promise controller, you may then boot it into the BIOS of the card and use the manufacturers RAID array rebuild utilities to rebuild the array. .Pp If the system has a pure software array and is not using a "real" ATA RAID controller, then shut the system down, make sure that the disk that was still working is moved to the bootable position (channel 0 or whatever the BIOS allows the system to boot from) and the blank disk is placed in the secondary position, then boot the system into single-user mode and issue the command: .Pp .Dl "atacontrol addspare ar0 ad6" .Dl "atacontrol rebuild ar0" .Pp If the disk drive did NOT fail and the RAID array became unmirrored due to a software glitch or improper shutdown, then a slightly different process must be followed. Begin by issuing the detach command (this shows the detach for disk ad6, the primary master on channel 3): .Pp .Dl "atacontrol detach ata3" .Pp then reboot the system into single-user mode. (don't just init the system, reboot it so that both disks get probed) You will probably see TWO mirrored RAID arrays appear during the boot messages, ar0 and ar1. Issue the command: .Pp .Dl "atacontrol delete ar1" .Dl "atacontrol addspare ar0 ad6" .Pp Now a status command will show the array rebuilding. .Pp To spin down a disk after 30 minutes run .Pp .Dl "atacontrol spindown ad6 1800" .Dl "dd if=/dev/ad6 of=/dev/null count=1" .Pp While any IO on the disk will arm the timer, using .Xr dd 1 on the raw device will work in all cases, as when the disk is not opened at all. You can check the current setting with .Pp .Dl "atacontrol spindown ad6" .Pp You should not set a spindown timeout on a disk with .Pa / or syslog logging on it as the disk will be worn out spinning down and up all the time. .Sh SEE ALSO .Xr ata 4 +.Xr cam 4 +.Xr camcontrol 8 .Sh HISTORY The .Nm utility first appeared in .Fx 4.6 . +.Pp +.Nm +was deprecated in +.Fx 9.0 . .Sh AUTHORS .An -nosplit The .Nm utility was written by .An S\(/oren Schmidt .Aq sos@FreeBSD.org . .Pp This manual page was written by .An S\(/oren Schmidt .Aq sos@FreeBSD.org . +.Sh NOTES +The +.Nm +utility was deprecated in +.Fx 9.0 . +When +.Bd -ragged -offset indent +.Cd "options ATA_CAM" +.Ed +.Pp +is compiled into the kernel, then +.Xr camcontrol 8 +must be used instead. Index: head/sbin/atacontrol/atacontrol.c =================================================================== --- head/sbin/atacontrol/atacontrol.c (revision 226178) +++ head/sbin/atacontrol/atacontrol.c (revision 226179) @@ -1,639 +1,644 @@ /*- * Copyright (c) 2000 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include static const char * mode2str(int mode) { switch (mode & 0xff) { case ATA_PIO: return "BIOSPIO"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA0"; case ATA_UDMA1: return "UDMA1"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA44"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_DMA: return "BIOSDMA"; default: return "???"; } } static const char * satarev2str(int mode) { switch ((mode & 0xff00) >> 8) { case 0: return ""; case 1: return "SATA 1.5Gb/s"; case 2: return "SATA 3Gb/s"; case 3: return "SATA 6Gb/s"; case 0xff: return "SATA"; default: return "???"; } } static int str2mode(char *str) { if (!strcasecmp(str, "BIOSPIO")) return ATA_PIO; if (!strcasecmp(str, "PIO0")) return ATA_PIO0; if (!strcasecmp(str, "PIO1")) return ATA_PIO1; if (!strcasecmp(str, "PIO2")) return ATA_PIO2; if (!strcasecmp(str, "PIO3")) return ATA_PIO3; if (!strcasecmp(str, "PIO4")) return ATA_PIO4; if (!strcasecmp(str, "WDMA0")) return ATA_WDMA0; if (!strcasecmp(str, "WDMA1")) return ATA_WDMA1; if (!strcasecmp(str, "WDMA2")) return ATA_WDMA2; if (!strcasecmp(str, "UDMA0")) return ATA_UDMA0; if (!strcasecmp(str, "UDMA16")) return ATA_UDMA0; if (!strcasecmp(str, "UDMA1")) return ATA_UDMA1; if (!strcasecmp(str, "UDMA25")) return ATA_UDMA1; if (!strcasecmp(str, "UDMA2")) return ATA_UDMA2; if (!strcasecmp(str, "UDMA33")) return ATA_UDMA2; if (!strcasecmp(str, "UDMA3")) return ATA_UDMA3; if (!strcasecmp(str, "UDMA44")) return ATA_UDMA3; if (!strcasecmp(str, "UDMA4")) return ATA_UDMA4; if (!strcasecmp(str, "UDMA66")) return ATA_UDMA4; if (!strcasecmp(str, "UDMA5")) return ATA_UDMA5; if (!strcasecmp(str, "UDMA100")) return ATA_UDMA5; if (!strcasecmp(str, "UDMA6")) return ATA_UDMA6; if (!strcasecmp(str, "UDMA133")) return ATA_UDMA6; if (!strcasecmp(str, "BIOSDMA")) return ATA_DMA; return -1; } static void usage(void) { fprintf(stderr, "usage: atacontrol args:\n" " atacontrol list\n" " atacontrol info channel\n" " atacontrol attach channel\n" " atacontrol detach channel\n" " atacontrol reinit channel\n" " atacontrol create type [interleave] disk0 ... diskN\n" " atacontrol delete array\n" " atacontrol addspare array disk\n" " atacontrol rebuild array\n" " atacontrol status array\n" " atacontrol mode device [mode]\n" " atacontrol cap device\n" " atacontrol spindown device [seconds]\n" ); exit(EX_USAGE); } static int version(int ver) { int bit; if (ver == 0xffff) return 0; for (bit = 15; bit >= 0; bit--) if (ver & (1< ", parm->model, parm->revision); if (parm->satacapabilities && parm->satacapabilities != 0xffff) { if (parm->satacapabilities & ATA_SATA_GEN2) printf("SATA revision 2.x\n"); else if (parm->satacapabilities & ATA_SATA_GEN1) printf("SATA revision 1.x\n"); else printf("Unknown SATA revision\n"); } else printf("ATA/ATAPI revision %d\n", version(parm->version_major)); } static void cap_print(struct ata_params *parm) { u_int32_t lbasize = (u_int32_t)parm->lba_size_1 | ((u_int32_t)parm->lba_size_2 << 16); u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) | ((u_int64_t)parm->lba_size48_2 << 16) | ((u_int64_t)parm->lba_size48_3 << 32) | ((u_int64_t)parm->lba_size48_4 << 48); printf("\n"); printf("Protocol "); if (parm->satacapabilities && parm->satacapabilities != 0xffff) { if (parm->satacapabilities & ATA_SATA_GEN2) printf("SATA revision 2.x\n"); else if (parm->satacapabilities & ATA_SATA_GEN1) printf("SATA revision 1.x\n"); else printf("Unknown SATA revision\n"); } else printf("ATA/ATAPI revision %d\n", version(parm->version_major)); printf("device model %.40s\n", parm->model); printf("serial number %.20s\n", parm->serial); printf("firmware revision %.8s\n", parm->revision); printf("cylinders %d\n", parm->cylinders); printf("heads %d\n", parm->heads); printf("sectors/track %d\n", parm->sectors); if (parm->config == ATA_PROTO_CFA || (parm->support.command2 & ATA_SUPPORT_CFA)) printf("CFA supported\n"); printf("lba%ssupported ", parm->capabilities1 & ATA_SUPPORT_LBA ? " " : " not "); if (lbasize) printf("%d sectors\n", lbasize); else printf("\n"); printf("lba48%ssupported ", parm->support.command2 & ATA_SUPPORT_ADDRESS48 ? " " : " not "); if (lbasize48) printf("%ju sectors\n", (uintmax_t)lbasize48); else printf("\n"); printf("dma%ssupported\n", parm->capabilities1 & ATA_SUPPORT_DMA ? " " : " not "); printf("overlap%ssupported\n", parm->capabilities1 & ATA_SUPPORT_OVERLAP ? " " : " not "); printf("\nFeature " "Support Enable Value Vendor\n"); printf("write cache %s %s\n", parm->support.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no", parm->enabled.command1 & ATA_SUPPORT_WRITECACHE ? "yes" : "no"); printf("read ahead %s %s\n", parm->support.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no", parm->enabled.command1 & ATA_SUPPORT_LOOKAHEAD ? "yes" : "no"); if (parm->satacapabilities && parm->satacapabilities != 0xffff) { printf("Native Command Queuing (NCQ) %s %s" " %d/0x%02X\n", parm->satacapabilities & ATA_SUPPORT_NCQ ? "yes" : "no", " -", (parm->satacapabilities & ATA_SUPPORT_NCQ) ? ATA_QUEUE_LEN(parm->queue) : 0, (parm->satacapabilities & ATA_SUPPORT_NCQ) ? ATA_QUEUE_LEN(parm->queue) : 0); } printf("Tagged Command Queuing (TCQ) %s %s %d/0x%02X\n", parm->support.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no", parm->enabled.command2 & ATA_SUPPORT_QUEUED ? "yes" : "no", ATA_QUEUE_LEN(parm->queue), ATA_QUEUE_LEN(parm->queue)); printf("SMART %s %s\n", parm->support.command1 & ATA_SUPPORT_SMART ? "yes" : "no", parm->enabled.command1 & ATA_SUPPORT_SMART ? "yes" : "no"); printf("microcode download %s %s\n", parm->support.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no", parm->enabled.command2 & ATA_SUPPORT_MICROCODE ? "yes" : "no"); printf("security %s %s\n", parm->support.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no", parm->enabled.command1 & ATA_SUPPORT_SECURITY ? "yes" : "no"); printf("power management %s %s\n", parm->support.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no", parm->enabled.command1 & ATA_SUPPORT_POWERMGT ? "yes" : "no"); printf("advanced power management %s %s %d/0x%02X\n", parm->support.command2 & ATA_SUPPORT_APM ? "yes" : "no", parm->enabled.command2 & ATA_SUPPORT_APM ? "yes" : "no", parm->apm_value, parm->apm_value); printf("automatic acoustic management %s %s " "%d/0x%02X %d/0x%02X\n", parm->support.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no", parm->enabled.command2 & ATA_SUPPORT_AUTOACOUSTIC ? "yes" :"no", ATA_ACOUSTIC_CURRENT(parm->acoustic), ATA_ACOUSTIC_CURRENT(parm->acoustic), ATA_ACOUSTIC_VENDOR(parm->acoustic), ATA_ACOUSTIC_VENDOR(parm->acoustic)); } static void ata_cap_print(int fd) { struct ata_params params; if (ioctl(fd, IOCATAGPARM, ¶ms) < 0) err(1, "ioctl(IOCATAGPARM)"); cap_print(¶ms); } static void info_print(int fd, int channel, int prchan) { struct ata_ioc_devices devices; devices.channel = channel; if (ioctl(fd, IOCATADEVICES, &devices) < 0) { if (!prchan) err(1, "ioctl(IOCATADEVICES)"); return; } if (prchan) printf("ATA channel %d:\n", channel); printf("%sMaster: ", prchan ? " " : ""); if (*devices.name[0]) { printf("%4.4s ", devices.name[0]); param_print(&devices.params[0]); } else printf(" no device present\n"); printf("%sSlave: ", prchan ? " " : ""); if (*devices.name[1]) { printf("%4.4s ", devices.name[1]); param_print(&devices.params[1]); } else printf(" no device present\n"); } static void ata_spindown(int fd, const char *dev, const char *arg) { int tmo; if (arg != NULL) { tmo = strtoul(arg, NULL, 0); if (ioctl(fd, IOCATASSPINDOWN, &tmo) < 0) err(1, "ioctl(IOCATASSPINDOWN)"); } else { if (ioctl(fd, IOCATAGSPINDOWN, &tmo) < 0) err(1, "ioctl(IOCATAGSPINDOWN)"); if (tmo == 0) printf("%s: idle spin down disabled\n", dev); else printf("%s: spin down after %d seconds idle\n", dev, tmo); } } static int open_dev(const char *arg, int mode) { int disk, fd; char device[64]; if (!(sscanf(arg, "ad%d", &disk) == 1 || sscanf(arg, "acd%d", &disk) == 1 || sscanf(arg, "afd%d", &disk) == 1 || sscanf(arg, "ast%d", &disk) == 1)) { fprintf(stderr, "atacontrol: Invalid device %s\n", arg); exit(EX_USAGE); } sprintf(device, "/dev/%s", arg); if ((fd = open(device, mode)) < 0) err(1, "device not found"); return (fd); } static int ar_arg(const char *arg) { int array; if (!(sscanf(arg, "ar%d", &array) == 1)) { fprintf(stderr, "atacontrol: Invalid array %s\n", arg); exit(EX_USAGE); } return (array); } static int ata_arg(const char *arg) { int channel; if (!(sscanf(arg, "ata%d", &channel) == 1)) { fprintf(stderr, "atacontrol: Invalid channel %s\n", arg); exit(EX_USAGE); } return (channel); } int main(int argc, char **argv) { int fd, mode, channel, array; + if (feature_present("ata_cam")) { + errx(1, "\nATA_CAM option is enabled in kernel.\n" + "Please use camcontrol instead."); + } + if (argc < 2) usage(); if (!strcmp(argv[1], "mode") && (argc == 3 || argc == 4)) { fd = open_dev(argv[2], O_RDONLY); if (argc == 4) { mode = str2mode(argv[3]); if (mode == -1) errx(1, "unknown mode"); if (ioctl(fd, IOCATASMODE, &mode) < 0) warn("ioctl(IOCATASMODE)"); } if (argc == 3 || argc == 4) { if (ioctl(fd, IOCATAGMODE, &mode) < 0) err(1, "ioctl(IOCATAGMODE)"); printf("current mode = %s %s\n", mode2str(mode), satarev2str(mode)); } exit(EX_OK); } if (!strcmp(argv[1], "cap") && argc == 3) { fd = open_dev(argv[2], O_RDONLY); ata_cap_print(fd); exit(EX_OK); } if (!strcmp(argv[1], "spindown") && (argc == 3 || argc == 4)) { fd = open_dev(argv[2], O_RDONLY); ata_spindown(fd, argv[2], argv[3]); exit(EX_OK); } if ((fd = open("/dev/ata", O_RDWR)) < 0) err(1, "control device not found"); if (!strcmp(argv[1], "list") && argc == 2) { int maxchannel; if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) err(1, "ioctl(IOCATAGMAXCHANNEL)"); for (channel = 0; channel < maxchannel; channel++) info_print(fd, channel, 1); exit(EX_OK); } if (!strcmp(argv[1], "info") && argc == 3) { channel = ata_arg(argv[2]); info_print(fd, channel, 0); exit(EX_OK); } if (!strcmp(argv[1], "detach") && argc == 3) { channel = ata_arg(argv[2]); if (ioctl(fd, IOCATADETACH, &channel) < 0) err(1, "ioctl(IOCATADETACH)"); exit(EX_OK); } if (!strcmp(argv[1], "attach") && argc == 3) { channel = ata_arg(argv[2]); if (ioctl(fd, IOCATAATTACH, &channel) < 0) err(1, "ioctl(IOCATAATTACH)"); info_print(fd, channel, 0); exit(EX_OK); } if (!strcmp(argv[1], "reinit") && argc == 3) { channel = ata_arg(argv[2]); if (ioctl(fd, IOCATAREINIT, &channel) < 0) warn("ioctl(IOCATAREINIT)"); info_print(fd, channel, 0); exit(EX_OK); } if (!strcmp(argv[1], "create")) { int disk, dev, offset; struct ata_ioc_raid_config config; bzero(&config, sizeof(config)); if (argc > 2) { if (!strcasecmp(argv[2], "RAID0") || !strcasecmp(argv[2], "stripe")) config.type = AR_RAID0; if (!strcasecmp(argv[2], "RAID1") || !strcasecmp(argv[2],"mirror")) config.type = AR_RAID1; if (!strcasecmp(argv[2], "RAID0+1") || !strcasecmp(argv[2],"RAID10")) config.type = AR_RAID01; if (!strcasecmp(argv[2], "RAID5")) config.type = AR_RAID5; if (!strcasecmp(argv[2], "SPAN")) config.type = AR_SPAN; if (!strcasecmp(argv[2], "JBOD")) config.type = AR_JBOD; } if (!config.type) { fprintf(stderr, "atacontrol: Invalid RAID type %s\n", argv[2]); fprintf(stderr, "atacontrol: Valid RAID types: \n"); fprintf(stderr, " stripe | mirror | " "RAID0 | RAID1 | RAID0+1 | RAID5 | " "SPAN | JBOD\n"); exit(EX_USAGE); } if (config.type == AR_RAID0 || config.type == AR_RAID01 || config.type == AR_RAID5) { if (argc < 4 || !sscanf(argv[3], "%d", &config.interleave) == 1) { fprintf(stderr, "atacontrol: Invalid interleave %s\n", argv[3]); exit(EX_USAGE); } offset = 4; } else offset = 3; for (disk = 0; disk < 16 && (offset + disk) < argc; disk++) { if (!(sscanf(argv[offset + disk], "ad%d", &dev) == 1)) { fprintf(stderr, "atacontrol: Invalid disk %s\n", argv[offset + disk]); exit(EX_USAGE); } config.disks[disk] = dev; } if ((config.type == AR_RAID1 || config.type == AR_RAID01) && disk < 2) { fprintf(stderr, "atacontrol: At least 2 disks must be " "specified\n"); exit(EX_USAGE); } config.total_disks = disk; if (ioctl(fd, IOCATARAIDCREATE, &config) < 0) err(1, "ioctl(IOCATARAIDCREATE)"); else printf("ar%d created\n", config.lun); exit(EX_OK); } if (!strcmp(argv[1], "delete") && argc == 3) { array = ar_arg(argv[2]); if (ioctl(fd, IOCATARAIDDELETE, &array) < 0) warn("ioctl(IOCATARAIDDELETE)"); exit(EX_OK); } if (!strcmp(argv[1], "addspare") && argc == 4) { struct ata_ioc_raid_config config; config.lun = ar_arg(argv[2]); if (!(sscanf(argv[3], "ad%d", &config.disks[0]) == 1)) { fprintf(stderr, "atacontrol: Invalid disk %s\n", argv[3]); usage(); } if (ioctl(fd, IOCATARAIDADDSPARE, &config) < 0) warn("ioctl(IOCATARAIDADDSPARE)"); exit(EX_OK); } if (!strcmp(argv[1], "rebuild") && argc == 3) { array = ar_arg(argv[2]); if (ioctl(fd, IOCATARAIDREBUILD, &array) < 0) warn("ioctl(IOCATARAIDREBUILD)"); else { char device[64]; char *buffer; ssize_t len; int arfd; if (daemon(0, 1) == -1) err(1, "daemon"); nice(20); snprintf(device, sizeof(device), "/dev/ar%d", array); if ((arfd = open(device, O_RDONLY)) == -1) err(1, "open %s", device); if ((buffer = malloc(1024 * 1024)) == NULL) err(1, "malloc"); while ((len = read(arfd, buffer, 1024 * 1024)) > 0) ; if (len == -1) err(1, "read"); else fprintf(stderr, "atacontrol: ar%d rebuild completed\n", array); free(buffer); close(arfd); } exit(EX_OK); } if (!strcmp(argv[1], "status") && argc == 3) { struct ata_ioc_raid_status status; int i, lun, state; status.lun = ar_arg(argv[2]); if (ioctl(fd, IOCATARAIDSTATUS, &status) < 0) err(1, "ioctl(IOCATARAIDSTATUS)"); printf("ar%d: ATA ", status.lun); switch (status.type) { case AR_RAID0: printf("RAID0 stripesize=%d", status.interleave); break; case AR_RAID1: printf("RAID1"); break; case AR_RAID01: printf("RAID0+1 stripesize=%d", status.interleave); break; case AR_RAID5: printf("RAID5 stripesize=%d", status.interleave); break; case AR_JBOD: printf("JBOD"); break; case AR_SPAN: printf("SPAN"); break; } printf(" status: "); switch (status.status) { case AR_READY: printf("READY\n"); break; case AR_READY | AR_DEGRADED: printf("DEGRADED\n"); break; case AR_READY | AR_DEGRADED | AR_REBUILDING: printf("REBUILDING %d%% completed\n", status.progress); break; default: printf("BROKEN\n"); } printf(" subdisks:\n"); for (i = 0; i < status.total_disks; i++) { printf(" %2d ", i); lun = status.disks[i].lun; state = status.disks[i].state; if (lun < 0) printf("---- "); else printf("ad%-2d ", lun); if (state & AR_DISK_ONLINE) printf("ONLINE"); else if (state & AR_DISK_SPARE) printf("SPARE"); else if (state & AR_DISK_PRESENT) printf("OFFLINE"); else printf("MISSING"); printf("\n"); } exit(EX_OK); } usage(); exit(EX_OK); } Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 226178) +++ head/sys/dev/ata/ata-all.c (revision 226179) @@ -1,1933 +1,1936 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ATA_CAM #include #include #include #include #include #endif #ifndef ATA_CAM /* device structure */ static d_ioctl_t ata_ioctl; static struct cdevsw ata_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; #endif /* prototypes */ #ifndef ATA_CAM static void ata_boot_attach(void); static device_t ata_add_child(device_t, struct ata_device *, int); #else static void ataaction(struct cam_sim *sim, union ccb *ccb); static void atapoll(struct cam_sim *sim); #endif static void ata_conn_event(void *, int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); static void ata_interrupt_locked(void *data); #ifdef ATA_CAM static void ata_periodic_poll(void *data); #endif /* global vars */ MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer"); int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL; struct intr_config_hook *ata_delayed_attach = NULL; devclass_t ata_devclass; uma_zone_t ata_request_zone; uma_zone_t ata_composite_zone; int ata_wc = 1; int ata_setmax = 0; int ata_dma_check_80pin = 1; /* local vars */ static int ata_dma = 1; static int atapi_dma = 1; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); TUNABLE_INT("hw.ata.ata_dma", &ata_dma); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0, "ATA disk DMA mode control"); TUNABLE_INT("hw.ata.ata_dma_check_80pin", &ata_dma_check_80pin); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma_check_80pin, CTLFLAG_RW, &ata_dma_check_80pin, 1, "Check for 80pin cable before setting ATA DMA mode"); TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0, "ATAPI device DMA mode control"); TUNABLE_INT("hw.ata.wc", &ata_wc); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0, "ATA disk write caching"); TUNABLE_INT("hw.ata.setmax", &ata_setmax); SYSCTL_INT(_hw_ata, OID_AUTO, setmax, CTLFLAG_RDTUN, &ata_setmax, 0, "ATA disk set max native address"); +#ifdef ATA_CAM +FEATURE(ata_cam, "ATA devices are accessed through the cam(4) driver"); +#endif /* * newbus device interface related functions */ int ata_probe(device_t dev) { return 0; } int ata_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); int error, rid; #ifdef ATA_CAM struct cam_devq *devq; const char *res; char buf[64]; int i, mode; #endif /* check that we have a virgin channel to attach */ if (ch->r_irq) return EEXIST; /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); TASK_INIT(&ch->conntask, 0, ata_conn_event, dev); #ifdef ATA_CAM for (i = 0; i < 16; i++) { ch->user[i].mode = 0; snprintf(buf, sizeof(buf), "dev%d.mode", i); if (resource_string_value(device_get_name(dev), device_get_unit(dev), buf, &res) == 0) mode = ata_str2mode(res); else if (resource_string_value(device_get_name(dev), device_get_unit(dev), "mode", &res) == 0) mode = ata_str2mode(res); else mode = -1; if (mode >= 0) ch->user[i].mode = mode; if (ch->flags & ATA_SATA) ch->user[i].bytecount = 8192; else ch->user[i].bytecount = MAXPHYS; ch->user[i].caps = 0; ch->curr[i] = ch->user[i]; if (ch->pm_level > 0) ch->user[i].caps |= CTS_SATA_CAPS_H_PMREQ; if (ch->pm_level > 1) ch->user[i].caps |= CTS_SATA_CAPS_D_PMREQ; } callout_init(&ch->poll_callout, 1); #endif /* reset the controller HW, the channel and device(s) */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("ataatch", 1); #ifndef ATA_CAM ATA_RESET(dev); #endif ATA_LOCKING(dev, ATA_LF_UNLOCK); /* allocate DMA resources if DMA HW present*/ if (ch->dma.alloc) ch->dma.alloc(dev); /* setup interrupt delivery */ rid = ATA_IRQ_RID; ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (!ch->r_irq) { device_printf(dev, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, NULL, ata_interrupt, ch, &ch->ih))) { bus_release_resource(dev, SYS_RES_IRQ, rid, ch->r_irq); device_printf(dev, "unable to setup interrupt\n"); return error; } #ifndef ATA_CAM /* probe and attach devices on this channel unless we are in early boot */ if (!ata_delayed_attach) ata_identify(dev); return (0); #else if (ch->flags & ATA_PERIODIC_POLL) callout_reset(&ch->poll_callout, hz, ata_periodic_poll, ch); mtx_lock(&ch->state_mtx); /* Create the device queue for our SIM. */ devq = cam_simq_alloc(1); if (devq == NULL) { device_printf(dev, "Unable to allocate simq\n"); error = ENOMEM; goto err1; } /* Construct SIM entry */ ch->sim = cam_sim_alloc(ataaction, atapoll, "ata", ch, device_get_unit(dev), &ch->state_mtx, 1, 0, devq); if (ch->sim == NULL) { device_printf(dev, "unable to allocate sim\n"); cam_simq_free(devq); error = ENOMEM; goto err1; } if (xpt_bus_register(ch->sim, dev, 0) != CAM_SUCCESS) { device_printf(dev, "unable to register xpt bus\n"); error = ENXIO; goto err2; } if (xpt_create_path(&ch->path, /*periph*/NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { device_printf(dev, "unable to create path\n"); error = ENXIO; goto err3; } mtx_unlock(&ch->state_mtx); return (0); err3: xpt_bus_deregister(cam_sim_path(ch->sim)); err2: cam_sim_free(ch->sim, /*free_devq*/TRUE); ch->sim = NULL; err1: bus_release_resource(dev, SYS_RES_IRQ, rid, ch->r_irq); mtx_unlock(&ch->state_mtx); if (ch->flags & ATA_PERIODIC_POLL) callout_drain(&ch->poll_callout); return (error); #endif } int ata_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); #ifndef ATA_CAM device_t *children; int nchildren, i; #endif /* check that we have a valid channel to detach */ if (!ch->r_irq) return ENXIO; /* grap the channel lock so no new requests gets launched */ mtx_lock(&ch->state_mtx); ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); #ifdef ATA_CAM if (ch->flags & ATA_PERIODIC_POLL) callout_drain(&ch->poll_callout); #endif #ifndef ATA_CAM /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } #endif taskqueue_drain(taskqueue_thread, &ch->conntask); #ifdef ATA_CAM mtx_lock(&ch->state_mtx); xpt_async(AC_LOST_DEVICE, ch->path, NULL); xpt_free_path(ch->path); xpt_bus_deregister(cam_sim_path(ch->sim)); cam_sim_free(ch->sim, /*free_devq*/TRUE); ch->sim = NULL; mtx_unlock(&ch->state_mtx); #endif /* release resources */ bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ch->r_irq = NULL; /* free DMA resources if DMA HW present*/ if (ch->dma.free) ch->dma.free(dev); mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } static void ata_conn_event(void *context, int dummy) { device_t dev = (device_t)context; #ifdef ATA_CAM struct ata_channel *ch = device_get_softc(dev); union ccb *ccb; mtx_lock(&ch->state_mtx); if (ch->sim == NULL) { mtx_unlock(&ch->state_mtx); return; } ata_reinit(dev); if ((ccb = xpt_alloc_ccb_nowait()) == NULL) return; if (xpt_create_path(&ccb->ccb_h.path, NULL, cam_sim_path(ch->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_free_ccb(ccb); return; } xpt_rescan(ccb); mtx_unlock(&ch->state_mtx); #else ata_reinit(dev); #endif } int ata_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; #ifndef ATA_CAM device_t *children; int nchildren, i; /* check that we have a valid channel to reinit */ if (!ch || !ch->r_irq) return ENXIO; if (bootverbose) device_printf(dev, "reiniting channel ..\n"); /* poll for locking the channel */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("atarini", 1); /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); if (ch->state & ATA_STALL_QUEUE) { /* Recursive reinits and reinits during detach prohobited. */ mtx_unlock(&ch->state_mtx); return (ENXIO); } if ((request = ch->running)) callout_stop(&request->callout); ch->running = NULL; /* unconditionally grap the channel lock */ ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* reinit the children and delete any that fails */ if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { /* did any children go missing ? */ if (children[i] && device_is_attached(children[i]) && ATA_REINIT(children[i])) { /* * if we had a running request and its device matches * this child we need to inform the request that the * device is gone. */ if (request && request->dev == children[i]) { request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); /* if not timeout finish request here */ if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); request = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* if we still have a good request put it on the queue again */ if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= ATA_R_REQUEUE; ata_queue_request(request); } /* we're done release the channel for new work */ mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); /* Add new children. */ /* ata_identify(dev); */ if (bootverbose) device_printf(dev, "reinit done ..\n"); /* kick off requests on the queue */ ata_start(dev); #else xpt_freeze_simq(ch->sim, 1); if ((request = ch->running)) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; callout_stop(&request->callout); if (ch->dma.unload) ch->dma.unload(request); request->result = ERESTART; ata_cam_end_transaction(dev, request); } /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* Tell the XPT about the event */ xpt_async(AC_BUS_RESET, ch->path, NULL); xpt_release_simq(ch->sim, TRUE); #endif return(0); } int ata_suspend(device_t dev) { struct ata_channel *ch; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; #ifdef ATA_CAM if (ch->flags & ATA_PERIODIC_POLL) callout_drain(&ch->poll_callout); mtx_lock(&ch->state_mtx); xpt_freeze_simq(ch->sim, 1); while (ch->state != ATA_IDLE) msleep(ch, &ch->state_mtx, PRIBIO, "atasusp", hz/100); mtx_unlock(&ch->state_mtx); #else /* wait for the channel to be IDLE or detached before suspending */ while (ch->r_irq) { mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE) { ch->state = ATA_ACTIVE; mtx_unlock(&ch->state_mtx); break; } mtx_unlock(&ch->state_mtx); tsleep(ch, PRIBIO, "atasusp", hz/10); } ATA_LOCKING(dev, ATA_LF_UNLOCK); #endif return(0); } int ata_resume(device_t dev) { struct ata_channel *ch; int error; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; #ifdef ATA_CAM mtx_lock(&ch->state_mtx); error = ata_reinit(dev); xpt_release_simq(ch->sim, TRUE); mtx_unlock(&ch->state_mtx); if (ch->flags & ATA_PERIODIC_POLL) callout_reset(&ch->poll_callout, hz, ata_periodic_poll, ch); #else /* reinit the devices, we dont know what mode/state they are in */ error = ata_reinit(dev); /* kick off requests on the queue */ ata_start(dev); #endif return error; } void ata_interrupt(void *data) { #ifdef ATA_CAM struct ata_channel *ch = (struct ata_channel *)data; mtx_lock(&ch->state_mtx); #endif ata_interrupt_locked(data); #ifdef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } static void ata_interrupt_locked(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; #ifndef ATA_CAM mtx_lock(&ch->state_mtx); #endif do { /* ignore interrupt if its not for us */ if (ch->hw.status && !ch->hw.status(ch->dev)) break; /* do we have a running request */ if (!(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } /* * we have the HW locks, so end the transaction for this request * if it finishes immediately otherwise wait for next interrupt */ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; #ifdef ATA_CAM ata_cam_end_transaction(ch->dev, request); #else mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); #endif return; } } while (0); #ifndef ATA_CAM mtx_unlock(&ch->state_mtx); #endif } #ifdef ATA_CAM static void ata_periodic_poll(void *data) { struct ata_channel *ch = (struct ata_channel *)data; callout_reset(&ch->poll_callout, hz, ata_periodic_poll, ch); ata_interrupt(ch); } #endif void ata_print_cable(device_t dev, u_int8_t *who) { device_printf(dev, "DMA limited to UDMA33, %s found non-ATA66 cable\n", who); } int ata_check_80pin(device_t dev, int mode) { struct ata_device *atadev = device_get_softc(dev); if (!ata_dma_check_80pin) { if (bootverbose) device_printf(dev, "Skipping 80pin cable check\n"); return mode; } if (mode > ATA_UDMA2 && !(atadev->param.hwres & ATA_CABLE_ID)) { ata_print_cable(dev, "device"); mode = ATA_UDMA2; } return mode; } void ata_setmode(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); int error, mode, pmode; mode = atadev->mode; do { pmode = mode = ata_limit_mode(dev, mode, ATA_DMA_MAX); mode = ATA_SETMODE(device_get_parent(dev), atadev->unit, mode); if ((ch->flags & (ATA_CHECKS_CABLE | ATA_SATA)) == 0) mode = ata_check_80pin(dev, mode); } while (pmode != mode); /* Interate till successfull negotiation. */ error = ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_SETXFER, 0, mode); if (bootverbose) device_printf(dev, "%ssetting %s\n", (error) ? "FAILURE " : "", ata_mode2str(mode)); atadev->mode = mode; } /* * device related interfaces */ #ifndef ATA_CAM static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag, struct thread *td) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data; int *value = (int *)data; int i, nchildren, error = ENOTTY; switch (cmd) { case IOCATAGMAXCHANNEL: /* In case we have channel 0..n this will return n+1. */ *value = devclass_get_maxunit(ata_devclass); error = 0; break; case IOCATAREINIT: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = ata_reinit(device); break; case IOCATAATTACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_ATTACH(device); break; case IOCATADETACH: if (*value >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value)) || !device_is_attached(device)) return ENXIO; error = DEVICE_DETACH(device); break; case IOCATADEVICES: if (devices->channel >= devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, devices->channel)) || !device_is_attached(device)) return ENXIO; bzero(devices->name[0], 32); bzero(&devices->params[0], sizeof(struct ata_params)); bzero(devices->name[1], 32); bzero(&devices->params[1], sizeof(struct ata_params)); if (!device_get_children(device, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && device_is_attached(children[i])) { struct ata_device *atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) { /* XXX SOS PM */ strncpy(devices->name[0], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { /* XXX SOS PM */ strncpy(devices->name[1], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[1], sizeof(struct ata_params)); } } } free(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(cmd, data); } return error; } #endif int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) { struct ata_device *atadev = device_get_softc(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data; struct ata_params *params = (struct ata_params *)data; int *mode = (int *)data; struct ata_request *request; caddr_t buf; int error; switch (cmd) { case IOCATAREQUEST: if (ioc_request->count > (ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS)) { return (EFBIG); } if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) { return ENOMEM; } if (!(request = ata_alloc_request())) { free(buf, M_ATA); return ENOMEM; } request->dev = atadev->dev; if (ioc_request->flags & ATA_CMD_WRITE) { error = copyin(ioc_request->data, buf, ioc_request->count); if (error) { free(buf, M_ATA); ata_free_request(request); return error; } } if (ioc_request->flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = ioc_request->u.ata.command; request->u.ata.feature = ioc_request->u.ata.feature; request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; if (ioc_request->flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (ioc_request->flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (ioc_request->flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); if (request->flags & ATA_R_ATAPI) { bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense, sizeof(struct atapi_sense)); } else { ioc_request->u.ata.command = request->u.ata.command; ioc_request->u.ata.feature = request->u.ata.feature; ioc_request->u.ata.lba = request->u.ata.lba; ioc_request->u.ata.count = request->u.ata.count; } ioc_request->error = request->result; if (ioc_request->flags & ATA_CMD_READ) error = copyout(buf, ioc_request->data, ioc_request->count); else error = 0; free(buf, M_ATA); ata_free_request(request); return error; case IOCATAGPARM: ata_getparam(atadev, 0); bcopy(&atadev->param, params, sizeof(struct ata_params)); return 0; case IOCATASMODE: atadev->mode = *mode; ata_setmode(dev); return 0; case IOCATAGMODE: *mode = atadev->mode | (ATA_GETREV(device_get_parent(dev), atadev->unit) << 8); return 0; case IOCATASSPINDOWN: atadev->spindown = *mode; return 0; case IOCATAGSPINDOWN: *mode = atadev->spindown; return 0; default: return ENOTTY; } } #ifndef ATA_CAM static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; mtx_lock(&Giant); /* newbus suckage it needs Giant */ /* kick off probe and attach on all channels */ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) { if ((ch = devclass_get_softc(ata_devclass, ctlr))) { ata_identify(ch->dev); } } /* release the hook that got us here, we are only needed once during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } #endif /* * misc support functions */ #ifndef ATA_CAM static device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit) { device_t child; if ((child = device_add_child(parent, NULL, unit))) { device_set_softc(child, atadev); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; } return child; } #endif int ata_getparam(struct ata_device *atadev, int init) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); struct ata_request *request; const char *res; char buf[64]; u_int8_t command = 0; int error = ENOMEM, retries = 2, mode = -1; if (ch->devices & (ATA_ATA_MASTER << atadev->unit)) command = ATA_ATA_IDENTIFY; if (ch->devices & (ATA_ATAPI_MASTER << atadev->unit)) command = ATA_ATAPI_IDENTIFY; if (!command) return ENXIO; while (retries-- > 0 && error) { if (!(request = ata_alloc_request())) break; request->dev = atadev->dev; request->timeout = 1; request->retries = 0; request->u.ata.command = command; request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT); if (!bootverbose) request->flags |= ATA_R_QUIET; request->data = (void *)&atadev->param; request->bytecount = sizeof(struct ata_params); request->donecount = 0; request->transfersize = DEV_BSIZE; ata_queue_request(request); error = request->result; ata_free_request(request); } if (!error && (isprint(atadev->param.model[0]) || isprint(atadev->param.model[1]))) { struct ata_params *atacap = &atadev->param; int16_t *ptr; for (ptr = (int16_t *)atacap; ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) { *ptr = le16toh(*ptr); } if (!(!strncmp(atacap->model, "FX", 2) || !strncmp(atacap->model, "NEC", 3) || !strncmp(atacap->model, "Pioneer", 7) || !strncmp(atacap->model, "SHARP", 5))) { bswap(atacap->model, sizeof(atacap->model)); bswap(atacap->revision, sizeof(atacap->revision)); bswap(atacap->serial, sizeof(atacap->serial)); } btrim(atacap->model, sizeof(atacap->model)); bpack(atacap->model, atacap->model, sizeof(atacap->model)); btrim(atacap->revision, sizeof(atacap->revision)); bpack(atacap->revision, atacap->revision, sizeof(atacap->revision)); btrim(atacap->serial, sizeof(atacap->serial)); bpack(atacap->serial, atacap->serial, sizeof(atacap->serial)); if (bootverbose) printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n", device_get_unit(ch->dev), ata_unit2str(atadev), ata_mode2str(ata_pmode(atacap)), ata_mode2str(ata_wmode(atacap)), ata_mode2str(ata_umode(atacap)), (atacap->hwres & ATA_CABLE_ID) ? "80":"40"); if (init) { char buffer[64]; sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision); device_set_desc_copy(atadev->dev, buffer); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config != ATA_CFA_MAGIC1) && (atadev->param.config != ATA_CFA_MAGIC2)) { if (atapi_dma && (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR && ata_umode(&atadev->param) >= ATA_UDMA2) atadev->mode = ATA_DMA_MAX; } else { if (ata_dma && (ata_umode(&atadev->param) > 0 || ata_wmode(&atadev->param) > 0)) atadev->mode = ATA_DMA_MAX; } snprintf(buf, sizeof(buf), "dev%d.mode", atadev->unit); if (resource_string_value(device_get_name(ch->dev), device_get_unit(ch->dev), buf, &res) == 0) mode = ata_str2mode(res); else if (resource_string_value(device_get_name(ch->dev), device_get_unit(ch->dev), "mode", &res) == 0) mode = ata_str2mode(res); if (mode >= 0) atadev->mode = mode; } } else { if (!error) error = ENXIO; } return error; } #ifndef ATA_CAM int ata_identify(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_device *atadev; device_t *children; device_t child, master = NULL; int nchildren, i, n = ch->devices; if (bootverbose) device_printf(dev, "Identifying devices: %08x\n", ch->devices); mtx_lock(&Giant); /* Skip existing devices. */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && (atadev = device_get_softc(children[i]))) n &= ~((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << atadev->unit); } free(children, M_TEMP); } /* Create new devices. */ if (bootverbose) device_printf(dev, "New devices: %08x\n", n); if (n == 0) { mtx_unlock(&Giant); return (0); } for (i = 0; i < ATA_PM; ++i) { if (n & (((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << i))) { int unit = -1; if (!(atadev = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } atadev->unit = i; #ifdef ATA_STATIC_ID if (n & (ATA_ATA_MASTER << i)) unit = (device_get_unit(dev) << 1) + i; #endif if ((child = ata_add_child(dev, atadev, unit))) { /* * PATA slave should be identified first, to allow * device cable detection on master to work properly. */ if (i == 0 && (n & ATA_PORTMULTIPLIER) == 0 && (n & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << 1)) != 0) { master = child; continue; } if (ata_getparam(atadev, 1)) { device_delete_child(dev, child); free(atadev, M_ATA); } } else free(atadev, M_ATA); } } if (master) { atadev = device_get_softc(master); if (ata_getparam(atadev, 1)) { device_delete_child(dev, master); free(atadev, M_ATA); } } bus_generic_probe(dev); bus_generic_attach(dev); mtx_unlock(&Giant); return 0; } #endif void ata_default_registers(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* fill in the defaults from whats setup already */ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res; ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset; ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res; ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset; ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res; ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset; ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res; ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset; } void ata_modify_if_48bit(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); request->flags &= ~ATA_R_48BIT; if (((request->u.ata.lba + request->u.ata.count) >= ATA_MAX_28BIT_LBA || request->u.ata.count > 256) && atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_READ: request->u.ata.command = ATA_READ48; break; case ATA_READ_MUL: request->u.ata.command = ATA_READ_MUL48; break; case ATA_READ_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA48; break; case ATA_READ_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA_QUEUED48; break; case ATA_WRITE: request->u.ata.command = ATA_WRITE48; break; case ATA_WRITE_MUL: request->u.ata.command = ATA_WRITE_MUL48; break; case ATA_WRITE_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA48; break; case ATA_WRITE_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA_QUEUED48; break; case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } else if (atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_READ_NATIVE_MAX_ADDRESS: request->u.ata.command = ATA_READ_NATIVE_MAX_ADDRESS48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } request->flags |= ATA_R_48BIT; } } void ata_udelay(int interval) { /* for now just use DELAY, the timer/sleep subsytems are not there yet */ if (1 || interval < (1000000/hz) || ata_delayed_attach) DELAY(interval); else pause("ataslp", interval/(1000000/hz)); } char * ata_unit2str(struct ata_device *atadev) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); static char str[8]; if (ch->devices & ATA_PORTMULTIPLIER) sprintf(str, "port%d", atadev->unit); else sprintf(str, "%s", atadev->unit == ATA_MASTER ? "master" : "slave"); return str; } const char * ata_mode2str(int mode) { switch (mode) { case -1: return "UNSUPPORTED"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA16"; case ATA_UDMA1: return "UDMA25"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA40"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_SA150: return "SATA150"; case ATA_SA300: return "SATA300"; default: if (mode & ATA_DMA_MASK) return "BIOSDMA"; else return "BIOSPIO"; } } int ata_str2mode(const char *str) { if (!strcasecmp(str, "PIO0")) return (ATA_PIO0); if (!strcasecmp(str, "PIO1")) return (ATA_PIO1); if (!strcasecmp(str, "PIO2")) return (ATA_PIO2); if (!strcasecmp(str, "PIO3")) return (ATA_PIO3); if (!strcasecmp(str, "PIO4")) return (ATA_PIO4); if (!strcasecmp(str, "WDMA0")) return (ATA_WDMA0); if (!strcasecmp(str, "WDMA1")) return (ATA_WDMA1); if (!strcasecmp(str, "WDMA2")) return (ATA_WDMA2); if (!strcasecmp(str, "UDMA0")) return (ATA_UDMA0); if (!strcasecmp(str, "UDMA16")) return (ATA_UDMA0); if (!strcasecmp(str, "UDMA1")) return (ATA_UDMA1); if (!strcasecmp(str, "UDMA25")) return (ATA_UDMA1); if (!strcasecmp(str, "UDMA2")) return (ATA_UDMA2); if (!strcasecmp(str, "UDMA33")) return (ATA_UDMA2); if (!strcasecmp(str, "UDMA3")) return (ATA_UDMA3); if (!strcasecmp(str, "UDMA44")) return (ATA_UDMA3); if (!strcasecmp(str, "UDMA4")) return (ATA_UDMA4); if (!strcasecmp(str, "UDMA66")) return (ATA_UDMA4); if (!strcasecmp(str, "UDMA5")) return (ATA_UDMA5); if (!strcasecmp(str, "UDMA100")) return (ATA_UDMA5); if (!strcasecmp(str, "UDMA6")) return (ATA_UDMA6); if (!strcasecmp(str, "UDMA133")) return (ATA_UDMA6); return (-1); } const char * ata_satarev2str(int rev) { switch (rev) { case 0: return ""; case 1: return "SATA 1.5Gb/s"; case 2: return "SATA 3Gb/s"; case 3: return "SATA 6Gb/s"; case 0xff: return "SATA"; default: return "???"; } } int ata_atapi(device_t dev, int target) { struct ata_channel *ch = device_get_softc(dev); return (ch->devices & (ATA_ATAPI_MASTER << target)); } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 0x02) return ATA_PIO4; if (ap->apiomodes & 0x01) return ATA_PIO3; } if (ap->mwdmamodes & 0x04) return ATA_PIO4; if (ap->mwdmamodes & 0x02) return ATA_PIO3; if (ap->mwdmamodes & 0x01) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100) return ATA_PIO1; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000) return ATA_PIO0; return ATA_PIO0; } int ata_wmode(struct ata_params *ap) { if (ap->mwdmamodes & 0x04) return ATA_WDMA2; if (ap->mwdmamodes & 0x02) return ATA_WDMA1; if (ap->mwdmamodes & 0x01) return ATA_WDMA0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x40) return ATA_UDMA6; if (ap->udmamodes & 0x20) return ATA_UDMA5; if (ap->udmamodes & 0x10) return ATA_UDMA4; if (ap->udmamodes & 0x08) return ATA_UDMA3; if (ap->udmamodes & 0x04) return ATA_UDMA2; if (ap->udmamodes & 0x02) return ATA_UDMA1; if (ap->udmamodes & 0x01) return ATA_UDMA0; } return -1; } int ata_limit_mode(device_t dev, int mode, int maxmode) { struct ata_device *atadev = device_get_softc(dev); if (maxmode && mode > maxmode) mode = maxmode; if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0) return min(mode, ata_umode(&atadev->param)); if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0) return min(mode, ata_wmode(&atadev->param)); if (mode > ata_pmode(&atadev->param)) return min(mode, ata_pmode(&atadev->param)); return mode; } static void bswap(int8_t *buf, int len) { u_int16_t *ptr = (u_int16_t*)(buf + len); while (--ptr >= (u_int16_t*)buf) *ptr = ntohs(*ptr); } static void btrim(int8_t *buf, int len) { int8_t *ptr; for (ptr = buf; ptr < buf+len; ++ptr) if (!*ptr || *ptr == '_') *ptr = ' '; for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) *ptr = 0; } static void bpack(int8_t *src, int8_t *dst, int len) { int i, j, blank; for (i = j = blank = 0 ; i < len; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') { blank = 1; if (i == 0) continue; } dst[j++] = src[i]; } if (j < len) dst[j] = 0x00; } #ifdef ATA_CAM void ata_cam_begin_transaction(device_t dev, union ccb *ccb) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } bzero(request, sizeof(*request)); /* setup request */ request->dev = NULL; request->parent = dev; request->unit = ccb->ccb_h.target_id; if (ccb->ccb_h.func_code == XPT_ATA_IO) { request->data = ccb->ataio.data_ptr; request->bytecount = ccb->ataio.dxfer_len; request->u.ata.command = ccb->ataio.cmd.command; request->u.ata.feature = ((uint16_t)ccb->ataio.cmd.features_exp << 8) | (uint16_t)ccb->ataio.cmd.features; request->u.ata.count = ((uint16_t)ccb->ataio.cmd.sector_count_exp << 8) | (uint16_t)ccb->ataio.cmd.sector_count; if (ccb->ataio.cmd.flags & CAM_ATAIO_48BIT) { request->flags |= ATA_R_48BIT; request->u.ata.lba = ((uint64_t)ccb->ataio.cmd.lba_high_exp << 40) | ((uint64_t)ccb->ataio.cmd.lba_mid_exp << 32) | ((uint64_t)ccb->ataio.cmd.lba_low_exp << 24); } else { request->u.ata.lba = ((uint64_t)(ccb->ataio.cmd.device & 0x0f) << 24); } request->u.ata.lba |= ((uint64_t)ccb->ataio.cmd.lba_high << 16) | ((uint64_t)ccb->ataio.cmd.lba_mid << 8) | (uint64_t)ccb->ataio.cmd.lba_low; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ccb->ataio.cmd.flags & CAM_ATAIO_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } else { request->data = ccb->csio.data_ptr; request->bytecount = ccb->csio.dxfer_len; bcopy((ccb->ccb_h.flags & CAM_CDB_POINTER) ? ccb->csio.cdb_io.cdb_ptr : ccb->csio.cdb_io.cdb_bytes, request->u.atapi.ccb, ccb->csio.cdb_len); request->flags |= ATA_R_ATAPI; if (ch->curr[ccb->ccb_h.target_id].atapi == 16) request->flags |= ATA_R_ATAPI16; if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE && ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) request->flags |= ATA_R_DMA; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) request->flags |= ATA_R_READ; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) request->flags |= ATA_R_WRITE; } request->transfersize = min(request->bytecount, ch->curr[ccb->ccb_h.target_id].bytecount); request->retries = 0; request->timeout = (ccb->ccb_h.timeout + 999) / 1000; callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); request->ccb = ccb; ch->running = request; ch->state = ATA_ACTIVE; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; ata_cam_end_transaction(dev, request); return; } } static void ata_cam_request_sense(device_t dev, struct ata_request *request) { struct ata_channel *ch = device_get_softc(dev); union ccb *ccb = request->ccb; ch->requestsense = 1; bzero(request, sizeof(&request)); request->dev = NULL; request->parent = dev; request->unit = ccb->ccb_h.target_id; request->data = (void *)&ccb->csio.sense_data; request->bytecount = ccb->csio.sense_len; request->u.atapi.ccb[0] = ATAPI_REQUEST_SENSE; request->u.atapi.ccb[4] = ccb->csio.sense_len; request->flags |= ATA_R_ATAPI; if (ch->curr[ccb->ccb_h.target_id].atapi == 16) request->flags |= ATA_R_ATAPI16; if (ch->curr[ccb->ccb_h.target_id].mode >= ATA_DMA) request->flags |= ATA_R_DMA; request->flags |= ATA_R_READ; request->transfersize = min(request->bytecount, ch->curr[ccb->ccb_h.target_id].bytecount); request->retries = 0; request->timeout = (ccb->ccb_h.timeout + 999) / 1000; callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); request->ccb = ccb; ch->running = request; ch->state = ATA_ACTIVE; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; ata_cam_end_transaction(dev, request); return; } } static void ata_cam_process_sense(device_t dev, struct ata_request *request) { struct ata_channel *ch = device_get_softc(dev); union ccb *ccb = request->ccb; int fatalerr = 0; ch->requestsense = 0; if (request->flags & ATA_R_TIMEOUT) fatalerr = 1; if ((request->flags & ATA_R_TIMEOUT) == 0 && (request->status & ATA_S_ERROR) == 0 && request->result == 0) { ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } else { ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL; } ata_free_request(request); xpt_done(ccb); /* Do error recovery if needed. */ if (fatalerr) ata_reinit(dev); } void ata_cam_end_transaction(device_t dev, struct ata_request *request) { struct ata_channel *ch = device_get_softc(dev); union ccb *ccb = request->ccb; int fatalerr = 0; if (ch->requestsense) { ata_cam_process_sense(dev, request); return; } ccb->ccb_h.status &= ~CAM_STATUS_MASK; if (request->flags & ATA_R_TIMEOUT) { xpt_freeze_simq(ch->sim, 1); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT | CAM_RELEASE_SIMQ; fatalerr = 1; } else if (request->status & ATA_S_ERROR) { if (ccb->ccb_h.func_code == XPT_ATA_IO) { ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR; } else { ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR; ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } } else if (request->result == ERESTART) ccb->ccb_h.status |= CAM_REQUEUE_REQ; else if (request->result != 0) ccb->ccb_h.status |= CAM_REQ_CMP_ERR; else ccb->ccb_h.status |= CAM_REQ_CMP; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP && !(ccb->ccb_h.status & CAM_DEV_QFRZN)) { xpt_freeze_devq(ccb->ccb_h.path, 1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } if (ccb->ccb_h.func_code == XPT_ATA_IO && ((request->status & ATA_S_ERROR) || (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT))) { struct ata_res *res = &ccb->ataio.res; res->status = request->status; res->error = request->error; res->lba_low = request->u.ata.lba; res->lba_mid = request->u.ata.lba >> 8; res->lba_high = request->u.ata.lba >> 16; res->device = request->u.ata.lba >> 24; res->lba_low_exp = request->u.ata.lba >> 24; res->lba_mid_exp = request->u.ata.lba >> 32; res->lba_high_exp = request->u.ata.lba >> 40; res->sector_count = request->u.ata.count; res->sector_count_exp = request->u.ata.count >> 8; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if (ccb->ccb_h.func_code == XPT_ATA_IO) { ccb->ataio.resid = ccb->ataio.dxfer_len - request->donecount; } else { ccb->csio.resid = ccb->csio.dxfer_len - request->donecount; } } if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR && (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) ata_cam_request_sense(dev, request); else { ata_free_request(request); xpt_done(ccb); } /* Do error recovery if needed. */ if (fatalerr) ata_reinit(dev); } static int ata_check_ids(device_t dev, union ccb *ccb) { struct ata_channel *ch = device_get_softc(dev); if (ccb->ccb_h.target_id > ((ch->flags & ATA_NO_SLAVE) ? 0 : 1)) { ccb->ccb_h.status = CAM_TID_INVALID; xpt_done(ccb); return (-1); } if (ccb->ccb_h.target_lun != 0) { ccb->ccb_h.status = CAM_LUN_INVALID; xpt_done(ccb); return (-1); } return (0); } static void ataaction(struct cam_sim *sim, union ccb *ccb) { device_t dev, parent; struct ata_channel *ch; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ataaction func_code=%x\n", ccb->ccb_h.func_code)); ch = (struct ata_channel *)cam_sim_softc(sim); dev = ch->dev; switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_ATA_IO: /* Execute the requested I/O operation */ case XPT_SCSI_IO: if (ata_check_ids(dev, ccb)) return; if ((ch->devices & ((ATA_ATA_MASTER | ATA_ATAPI_MASTER) << ccb->ccb_h.target_id)) == 0) { ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; } if (ch->running) device_printf(dev, "already running!\n"); if (ccb->ccb_h.func_code == XPT_ATA_IO && (ccb->ataio.cmd.flags & CAM_ATAIO_CONTROL) && (ccb->ataio.cmd.control & ATA_A_RESET)) { struct ata_res *res = &ccb->ataio.res; bzero(res, sizeof(*res)); if (ch->devices & (ATA_ATA_MASTER << ccb->ccb_h.target_id)) { res->lba_high = 0; res->lba_mid = 0; } else { res->lba_high = 0xeb; res->lba_mid = 0x14; } ccb->ccb_h.status = CAM_REQ_CMP; break; } ata_cam_begin_transaction(dev, ccb); return; case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (ata_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; if (ch->flags & ATA_SATA) { if (cts->xport_specific.sata.valid & CTS_SATA_VALID_REVISION) d->revision = cts->xport_specific.sata.revision; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.sata.mode); } else d->mode = cts->xport_specific.sata.mode; } if (cts->xport_specific.sata.valid & CTS_SATA_VALID_BYTECOUNT) d->bytecount = min(8192, cts->xport_specific.sata.bytecount); if (cts->xport_specific.sata.valid & CTS_SATA_VALID_ATAPI) d->atapi = cts->xport_specific.sata.atapi; if (cts->xport_specific.sata.valid & CTS_SATA_VALID_CAPS) d->caps = cts->xport_specific.sata.caps; } else { if (cts->xport_specific.ata.valid & CTS_ATA_VALID_MODE) { if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { d->mode = ATA_SETMODE(ch->dev, ccb->ccb_h.target_id, cts->xport_specific.ata.mode); } else d->mode = cts->xport_specific.ata.mode; } if (cts->xport_specific.ata.valid & CTS_ATA_VALID_BYTECOUNT) d->bytecount = cts->xport_specific.ata.bytecount; if (cts->xport_specific.ata.valid & CTS_ATA_VALID_ATAPI) d->atapi = cts->xport_specific.ata.atapi; } ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; struct ata_cam_device *d; if (ata_check_ids(dev, ccb)) return; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) d = &ch->curr[ccb->ccb_h.target_id]; else d = &ch->user[ccb->ccb_h.target_id]; cts->protocol = PROTO_ATA; cts->protocol_version = PROTO_VERSION_UNSPECIFIED; if (ch->flags & ATA_SATA) { cts->transport = XPORT_SATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.sata.valid = 0; cts->xport_specific.sata.mode = d->mode; cts->xport_specific.sata.valid |= CTS_SATA_VALID_MODE; cts->xport_specific.sata.bytecount = d->bytecount; cts->xport_specific.sata.valid |= CTS_SATA_VALID_BYTECOUNT; if (cts->type == CTS_TYPE_CURRENT_SETTINGS) { cts->xport_specific.sata.revision = ATA_GETREV(dev, ccb->ccb_h.target_id); if (cts->xport_specific.sata.revision != 0xff) { cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; } cts->xport_specific.sata.caps = d->caps & CTS_SATA_CAPS_D; if (ch->pm_level) { cts->xport_specific.sata.caps |= CTS_SATA_CAPS_H_PMREQ; } cts->xport_specific.sata.caps &= ch->user[ccb->ccb_h.target_id].caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } else { cts->xport_specific.sata.revision = d->revision; cts->xport_specific.sata.valid |= CTS_SATA_VALID_REVISION; cts->xport_specific.sata.caps = d->caps; cts->xport_specific.sata.valid |= CTS_SATA_VALID_CAPS; } cts->xport_specific.sata.atapi = d->atapi; cts->xport_specific.sata.valid |= CTS_SATA_VALID_ATAPI; } else { cts->transport = XPORT_ATA; cts->transport_version = XPORT_VERSION_UNSPECIFIED; cts->xport_specific.ata.valid = 0; cts->xport_specific.ata.mode = d->mode; cts->xport_specific.ata.valid |= CTS_ATA_VALID_MODE; cts->xport_specific.ata.bytecount = d->bytecount; cts->xport_specific.ata.valid |= CTS_ATA_VALID_BYTECOUNT; cts->xport_specific.ata.atapi = d->atapi; cts->xport_specific.ata.valid |= CTS_ATA_VALID_ATAPI; } ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ ata_reinit(dev); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; parent = device_get_parent(dev); cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_SEQSCAN; cpi->hba_eng_cnt = 0; if (ch->flags & ATA_NO_SLAVE) cpi->max_target = 0; else cpi->max_target = 1; cpi->max_lun = 0; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); if (ch->flags & ATA_SATA) cpi->base_transfer_speed = 150000; else cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "ATA", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); if (ch->flags & ATA_SATA) cpi->transport = XPORT_SATA; else cpi->transport = XPORT_ATA; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; cpi->protocol = PROTO_ATA; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->maxio = ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS; if (device_get_devclass(device_get_parent(parent)) == devclass_find("pci")) { cpi->hba_vendor = pci_get_vendor(parent); cpi->hba_device = pci_get_device(parent); cpi->hba_subvendor = pci_get_subvendor(parent); cpi->hba_subdevice = pci_get_subdevice(parent); } cpi->ccb_h.status = CAM_REQ_CMP; break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void atapoll(struct cam_sim *sim) { struct ata_channel *ch = (struct ata_channel *)cam_sim_softc(sim); ata_interrupt_locked(ch); } #endif /* * module handeling */ static int ata_module_event_handler(module_t mod, int what, void *arg) { #ifndef ATA_CAM static struct cdev *atacdev; #endif switch (what) { case MOD_LOAD: #ifndef ATA_CAM /* register controlling device */ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); if (cold) { /* register boot attach to be run when interrupts are enabled */ if (!(ata_delayed_attach = (struct intr_config_hook *) malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO))) { printf("ata: malloc of delayed attach hook failed\n"); return EIO; } ata_delayed_attach->ich_func = (void*)ata_boot_attach; if (config_intrhook_establish(ata_delayed_attach) != 0) { printf("ata: config_intrhook_establish failed\n"); free(ata_delayed_attach, M_TEMP); } } #endif return 0; case MOD_UNLOAD: #ifndef ATA_CAM /* deregister controlling device */ destroy_dev(atacdev); #endif return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(ata, 1); #ifdef ATA_CAM MODULE_DEPEND(ata, cam, 1, 1, 1); #endif static void ata_init(void) { ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); ata_composite_zone = uma_zcreate("ata_composite", sizeof(struct ata_composite), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL); static void ata_uninit(void) { uma_zdestroy(ata_composite_zone); uma_zdestroy(ata_request_zone); } SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL); Index: head/usr.sbin/burncd/burncd.8 =================================================================== --- head/usr.sbin/burncd/burncd.8 (revision 226178) +++ head/usr.sbin/burncd/burncd.8 (revision 226179) @@ -1,222 +1,249 @@ .\" .\" Copyright (c) 2000,2001,2002 Søren Schmidt .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer, .\" without modification, immediately at the beginning of the file. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. The name of the author may not be used to endorse or promote products .\" derived from this software without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd December 21, 2009 +.Dd October 9, 2011 .Dt BURNCD 8 .Os .Sh NAME .Nm burncd .Nd control the ATAPI CD-R/RW driver +.Pp +This utility was +.Em deprecated +in +.Fx 9.0 . +See +.Sx NOTES . .Sh SYNOPSIS .Nm .Op Fl deFlmnpqtv .Op Fl f Ar device .Op Fl s Ar speed .Op Ar command .Op Ar command Ar .Sh DESCRIPTION The .Nm utility is used to burn CD-R/RW media using the ATAPI cd driver. .Pp Available options and operands: .Bl -tag -width XXXXXXXXXXXX .It Fl d burn the CD-R/RW in DAO (disk at once) mode. .It Fl e eject the medium when done. .It Fl f Ar device set the device to use for the burning process. .It Fl F force operation regardless of warnings. .It Fl l read a list of image files from filename. .It Fl m close disk in multisession mode (otherwise disk is closed as singlesession). .It Fl n do not write gaps between data tracks in DAO mode. .It Fl p use preemphasis on audio tracks. .It Fl q quiet, do not print progress messages. .It Fl s Ar speed set the speed of the burner device. Defaults to 4. Specify .Dq Li max to use the drive's fastest speed. .It Fl t test write, do not actually write on the media. .It Fl v verbose, print extra progress messages. .El .Pp .Ar command may be one of: .Bl -tag -width XXXXXXXXXXXX .It Cm msinfo Show the first LBA of the last track on the media and the next writeable address on the media for use with the .Xr mkisofs 8 Ns 's Pq Pa ports/sysutils/cdrtools .Fl C switch when adding additional data to ISO file systems with extra sessions. .It Cm blank Blank a CD-RW medium. This uses the fast blanking method, so data are not physically overwritten, only those areas that make the media appear blank for further usage are erased. .It Cm eject Eject the medium when done. This is equivalent to the .Fl e option. .It Cm erase Erase a CD-RW medium. This erases the entire media. Can take up to 1 hour to finish. .It Cm format Brq Cm dvd+rw | dvd-rw Formats a DVD+RW or DVD-RW media to the default max size and 2048 byte blocks. This operation can take a long time to finish. Progress reporting is done during the process. .It Cm fixate Fixate the medium so that the TOC is generated and the media can be used in an ordinary CD drive. The driver defaults to creating singlesession media (see .Fl m option). Ignored in DAO mode (see .Fl d option). .It Cm raw | audio Set the write mode to produce audio (raw mode) tracks for the following images on the command line. .It Cm data | mode1 Set the write mode to produce data (mode1) tracks for the following image files on the command line. .It Cm mode2 Set the write mode to produce data (mode2) tracks for the following image files on the command line. .It Cm XAmode1 Set the write mode to produce data (XAmode1) tracks for the following image files on the command line. .It Cm XAmode2 Set the write mode to produce data (XAmode2) tracks for the following image files on the command line. .It Cm vcd Set the write mode to produce VCD/SVCD tracks for the following image files on the command line. This automatically sets DAO .Pq Fl d and .Dq "no gaps" .Pq Fl n modes. .It Cm dvdrw Set the write mode to write a DVD+RW from the following image. DVDs only have one track. .It Ar file All other arguments are treated as filenames of images to write to the media, or in case the .Fl l option is used as files containing lists of images. .El .Pp Files whose length are not a multiple of the current media blocksize are quietly zero padded to fit the blocksize requirement. The conventional filename .Fl refers to stdin, and can only be used once. .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : .Bl -tag -width ".Ev BURNCD_SPEED" .It Ev BURNCD_SPEED The write speed to use if one is not specified with the .Fl s flag. .It Ev CDROM The CD device to use if one is not specified with the .Fl f flag. .El .Sh FILES .Bl -tag -width ".Pa /dev/acd0" .It Pa /dev/acd0 The default device, if not overridden by the .Ev CDROM environment variable or the .Fl f option. .El .Sh EXAMPLES The typical usage for burning a data CD-R: .Pp .Dl "burncd -f /dev/acd0 data file1 fixate" .Pp The typical usage for burning an audio CD-R: .Pp .Dl "burncd -f /dev/acd0 audio file1 file2 file3 fixate" .Pp The typical usage for burning an audio CD-R in DAO mode: .Pp .Dl "burncd -f /dev/acd0 -d audio file1 file2 file3" .Pp The typical usage for burning a mixed mode CD-R: .Pp .Dl "burncd -f /dev/acd0 data file1 audio file2 file3 fixate" .Pp The typical usage for burning from a compressed image file on stdin: .Pp .Dl "gunzip -c file.iso.gz | burncd -f /dev/acd0 data - fixate" .Pp In the examples above, the files burned to data CD-Rs are assumed to be ISO9660 file systems. .Xr mkisofs 8 , available in the .Fx Ports Collection, as part of the .Pa sysutils/cdrtools port, is commonly used to create ISO9660 file system images from a given directory tree. .Sh HISTORY The .Nm utility appeared in .Fx 4.0 . +.Pp +.Nm +was deprecated in +.Fx 9.0 . .Sh AUTHORS The .Nm utility and this manpage was contributed by .An S\(/oren Schmidt , Denmark .Aq sos@FreeBSD.org . .Sh BUGS Probably, please report when found. +.Sh NOTES +When +.Bd -ragged -offset indent +.Cd "options ATA_CAM" +.Ed +.Pp +is compiled into the kernel, then +.Xr cdrecord 1 , +available in the +.Fx +Ports Collection as part of the +.Pa sysutils/cdrtools +port, must be used instead. +Refer to: +.Pp +http://www.freebsd.org/doc/handbook/creating-cds.html#CDRECORD Index: head/usr.sbin/burncd/burncd.c =================================================================== --- head/usr.sbin/burncd/burncd.c (revision 226178) +++ head/usr.sbin/burncd/burncd.c (revision 226179) @@ -1,741 +1,748 @@ /*- * Copyright (c) 2000,2001,2002 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLOCKS 16 struct track_info { int file; char file_name[MAXPATHLEN + 1]; off_t file_size; int block_size; int block_type; int pregap; int addr; }; static struct track_info tracks[100]; static int quiet, verbose, saved_block_size, notracks; static volatile sig_atomic_t global_fd_for_cleanup; void add_track(char *, int, int, int); void do_DAO(int fd, int, int); void do_TAO(int fd, int, int, int); void do_format(int, int, char *); int write_file(int fd, struct track_info *); int roundup_blocks(struct track_info *); void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int); void cleanup(int); void cleanup_flush(void); void cleanup_signal(int); void usage(void); int main(int argc, char **argv) { int arg, addr, ch, fd; int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0; int nogap = 0, speed = 4 * 177, test_write = 0, force = 0; int block_size = 0, block_type = 0, cdopen = 0, dvdrw = 0; const char *dev, *env_speed; + if (feature_present("ata_cam")) { + errx(1, "\nATA_CAM option is enabled in kernel.\n" + "Install the sysutils/cdrtools port and use cdrecord instead.\n\n" + "Please refer to:\n" + "http://www.freebsd.org/doc/handbook/creating-cds.html#CDRECORD"); + } + if ((dev = getenv("CDROM")) == NULL) dev = "/dev/acd0"; env_speed = getenv("BURNCD_SPEED"); while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) { switch (ch) { case 'd': dao = 1; break; case 'e': eject = 1; break; case 'f': dev = optarg; break; case 'F': force = 1; break; case 'l': list = 1; break; case 'm': multi = 1; break; case 'n': nogap = 1; break; case 'p': preemp = 1; break; case 'q': quiet = 1; break; case 's': env_speed = optarg; break; case 't': test_write = 1; break; case 'v': verbose = 1; break; default: usage(); } } argc -= optind; argv += optind; if (env_speed == NULL) ; else if (strcasecmp("max", env_speed) == 0) speed = CDR_MAX_SPEED; else speed = atoi(env_speed) * 177; if (speed <= 0) errx(EX_USAGE, "Invalid speed: %s", env_speed); if (argc == 0) usage(); if ((fd = open(dev, O_RDWR, 0)) < 0) err(EX_NOINPUT, "open(%s)", dev); if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)"); if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0) err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)"); global_fd_for_cleanup = fd; err_set_exit(cleanup); signal(SIGHUP, cleanup_signal); signal(SIGINT, cleanup_signal); signal(SIGTERM, cleanup_signal); for (arg = 0; arg < argc; arg++) { if (!strcasecmp(argv[arg], "fixate")) { fixate = 1; continue; } if (!strcasecmp(argv[arg], "eject")) { eject = 1; break; } if (!strcasecmp(argv[arg], "msinfo")) { struct ioc_read_toc_single_entry entry; struct ioc_toc_header header; if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0) err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)"); bzero(&entry, sizeof(struct ioc_read_toc_single_entry)); entry.address_format = CD_LBA_FORMAT; entry.track = header.ending_track; if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0) err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)"); if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); fprintf(stdout, "%d,%d\n", ntohl(entry.entry.addr.lba), addr); break; } if ((!strcasecmp(argv[arg], "erase") || !strcasecmp(argv[arg], "blank")) && !test_write) { int blank, pct, last = 0; if (!strcasecmp(argv[arg], "erase")) blank = CDR_B_ALL; else blank = CDR_B_MIN; if (!quiet) fprintf(stderr, "%sing CD, please wait..\r", blank == CDR_B_ALL ? "eras" : "blank"); if (ioctl(fd, CDRIOCBLANK, &blank) < 0) err(EX_IOERR, "ioctl(CDRIOCBLANK)"); while (1) { sleep(1); if (ioctl(fd, CDRIOCGETPROGRESS, &pct) == -1) err(EX_IOERR,"ioctl(CDRIOGETPROGRESS)"); if (pct > 0 && !quiet) fprintf(stderr, "%sing CD - %d %% done \r", blank == CDR_B_ALL ? "eras" : "blank", pct); if (pct == 100 || (pct == 0 && last > 90)) break; last = pct; } if (!quiet) printf("\n"); continue; } if (!strcasecmp(argv[arg], "format") && !test_write) { if (arg + 1 < argc && (!strcasecmp(argv[arg + 1], "dvd+rw") || !strcasecmp(argv[arg + 1], "dvd-rw"))) do_format(fd, force, argv[arg + 1]); else errx(EX_NOINPUT, "format media type invalid"); arg++; continue; } if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) { block_type = CDR_DB_RAW; block_size = 2352; continue; } if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) { block_type = CDR_DB_ROM_MODE1; block_size = 2048; continue; } if (!strcasecmp(argv[arg], "mode2")) { block_type = CDR_DB_ROM_MODE2; block_size = 2336; continue; } if (!strcasecmp(argv[arg], "xamode1")) { block_type = CDR_DB_XA_MODE1; block_size = 2048; continue; } if (!strcasecmp(argv[arg], "xamode2")) { block_type = CDR_DB_XA_MODE2_F2; block_size = 2324; continue; } if (!strcasecmp(argv[arg], "vcd")) { block_type = CDR_DB_XA_MODE2_F2; block_size = 2352; dao = 1; nogap = 1; continue; } if (!strcasecmp(argv[arg], "dvdrw")) { block_type = CDR_DB_ROM_MODE1; block_size = 2048; dvdrw = 1; continue; } if (!block_size) errx(EX_NOINPUT, "no data format selected"); if (list) { char file_buf[MAXPATHLEN + 1], *eol; FILE *fp; if ((fp = fopen(argv[arg], "r")) == NULL) err(EX_NOINPUT, "fopen(%s)", argv[arg]); while (fgets(file_buf, sizeof(file_buf), fp) != NULL) { if (*file_buf == '#' || *file_buf == '\n') continue; if ((eol = strchr(file_buf, '\n'))) *eol = '\0'; add_track(file_buf, block_size, block_type, nogap); } if (feof(fp)) fclose(fp); else err(EX_IOERR, "fgets(%s)", file_buf); } else add_track(argv[arg], block_size, block_type, nogap); } if (notracks) { if (dvdrw && notracks > 1) errx(EX_USAGE, "DVD's only have 1 track"); if (ioctl(fd, CDIOCSTART, 0) < 0) err(EX_IOERR, "ioctl(CDIOCSTART)"); if (!cdopen) { if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0) err(EX_IOERR, "ioctl(CDRIOCINITWRITER)"); cdopen = 1; } if (dao) do_DAO(fd, test_write, multi); else do_TAO(fd, test_write, preemp, dvdrw); } if (!test_write && fixate && !dao && !dvdrw) { if (!quiet) fprintf(stderr, "fixating CD, please wait..\n"); if (ioctl(fd, CDRIOCFIXATE, &multi) < 0) err(EX_IOERR, "ioctl(CDRIOCFIXATE)"); } if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) { err_set_exit(NULL); err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); } if (eject) if (ioctl(fd, CDIOCEJECT) < 0) err(EX_IOERR, "ioctl(CDIOCEJECT)"); signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); close(fd); exit(EX_OK); } void add_track(char *name, int block_size, int block_type, int nogap) { struct stat sb; int file; static int done_stdin = 0; if (!strcmp(name, "-")) { if (done_stdin) { warn("skipping multiple usages of stdin"); return; } file = STDIN_FILENO; done_stdin = 1; } else if ((file = open(name, O_RDONLY, 0)) < 0) err(EX_NOINPUT, "open(%s)", name); if (fstat(file, &sb) < 0) err(EX_IOERR, "fstat(%s)", name); tracks[notracks].file = file; strncpy(tracks[notracks].file_name, name, MAXPATHLEN); if (file == STDIN_FILENO) tracks[notracks].file_size = -1; else tracks[notracks].file_size = sb.st_size; tracks[notracks].block_size = block_size; tracks[notracks].block_type = block_type; if (nogap && notracks) tracks[notracks].pregap = 0; else { if (tracks[notracks - (notracks > 0)].block_type == block_type) tracks[notracks].pregap = 150; else tracks[notracks].pregap = 255; } if (verbose) { int pad = 0; if (tracks[notracks].file_size / tracks[notracks].block_size != roundup_blocks(&tracks[notracks])) pad = 1; fprintf(stderr, "adding type 0x%02x file %s size %jd KB %d blocks %s\n", tracks[notracks].block_type, name, (intmax_t)sb.st_size/1024, roundup_blocks(&tracks[notracks]), pad ? "(0 padded)" : ""); } notracks++; } void do_DAO(int fd, int test_write, int multi) { struct cdr_cuesheet sheet; struct cdr_cue_entry cue[100]; int format = CDR_SESS_CDROM; int addr, i, j = 0; int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 }; int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1, 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 }; if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); if (verbose) fprintf(stderr, "next writeable LBA %d\n", addr); cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0, (bt2df[tracks[0].block_type] & 0xf0) | (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr); for (i = 0; i < notracks; i++) { if (bt2ctl[tracks[i].block_type] < 0 || bt2df[tracks[i].block_type] < 0) errx(EX_IOERR, "track type not supported in DAO mode"); if (tracks[i].block_type >= CDR_DB_XA_MODE1) format = CDR_SESS_CDROM_XA; if (i == 0) { addr += tracks[i].pregap; tracks[i].addr = addr; cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 0x00, addr); } else { if (tracks[i].pregap) { if (tracks[i].block_type > 0x7) { cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 0x01, i+1, 0x0, (bt2df[tracks[i].block_type] & 0xf0) | (tracks[i].block_type < 8 ? 0x01 :0x04), 0x00, addr); } else cue_ent(&cue[j++],bt2ctl[tracks[i].block_type], 0x01, i+1, 0x0, bt2df[tracks[i].block_type], 0x00, addr); } tracks[i].addr = tracks[i - 1].addr + roundup_blocks(&tracks[i - 1]); cue_ent(&cue[j++], bt2ctl[tracks[i].block_type], 0x01, i+1, 0x1, bt2df[tracks[i].block_type], 0x00, addr + tracks[i].pregap); if (tracks[i].block_type > 0x7) addr += tracks[i].pregap; } addr += roundup_blocks(&tracks[i]); } cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01, (bt2df[tracks[i - 1].block_type] & 0xf0) | (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr); sheet.len = j * 8; sheet.entries = cue; sheet.test_write = test_write; sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE; sheet.session_format = format; if (verbose) { u_int8_t *ptr = (u_int8_t *)sheet.entries; fprintf(stderr,"CUE sheet:"); for (i = 0; i < sheet.len; i++) if (i % 8) fprintf(stderr," %02x", ptr[i]); else fprintf(stderr,"\n%02x", ptr[i]); fprintf(stderr,"\n"); } if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0) err(EX_IOERR, "ioctl(CDRIOCSENDCUE)"); for (i = 0; i < notracks; i++) { if (write_file(fd, &tracks[i])) { cleanup_flush(); err(EX_IOERR, "write_file"); } } ioctl(fd, CDRIOCFLUSH); } void do_TAO(int fd, int test_write, int preemp, int dvdrw) { struct cdr_track track; int i; for (i = 0; i < notracks; i++) { track.test_write = test_write; track.datablock_type = tracks[i].block_type; track.preemp = preemp; if (ioctl(fd, CDRIOCINITTRACK, &track) < 0) err(EX_IOERR, "ioctl(CDRIOCINITTRACK)"); if (dvdrw) tracks[i].addr = 0; else if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &tracks[i].addr) < 0) err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)"); if (!quiet) fprintf(stderr, "next writeable LBA %d\n", tracks[i].addr); if (write_file(fd, &tracks[i])) { cleanup_flush(); err(EX_IOERR, "write_file"); } if (ioctl(fd, CDRIOCFLUSH) < 0) err(EX_IOERR, "ioctl(CDRIOCFLUSH)"); } } #define NTOH3B(x) ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16) void do_format(int the_fd, int force, char *type) { struct cdr_format_capacities capacities; struct cdr_format_params format_params; int count, i, pct, last = 0; if (ioctl(the_fd, CDRIOCREADFORMATCAPS, &capacities) == -1) err(EX_IOERR, "ioctl(CDRIOCREADFORMATCAPS)"); if (verbose) { fprintf(stderr, "format list entries=%zd\n", capacities.length / sizeof(struct cdr_format_capacity)); fprintf(stderr, "current format: blocks=%u type=0x%x block_size=%u\n", ntohl(capacities.blocks), capacities.type, NTOH3B(capacities.block_size)); } count = capacities.length / sizeof(struct cdr_format_capacity); if (verbose) { for (i = 0; i < count; ++i) fprintf(stderr, "format %d: blocks=%u type=0x%x param=%u\n", i, ntohl(capacities.format[i].blocks), capacities.format[i].type, NTOH3B(capacities.format[i].param)); } for (i = 0; i < count; ++i) { if (!strcasecmp(type, "dvd+rw")) { if (capacities.format[i].type == 0x26) { break; } } if (!strcasecmp(type, "dvd-rw")) { if (capacities.format[i].type == 0x0) { break; } } } if (i == count) errx(EX_IOERR, "could not find a valid format capacity"); if (!quiet) fprintf(stderr,"formatting with blocks=%u type=0x%x param=%u\n", ntohl(capacities.format[i].blocks), capacities.format[i].type, NTOH3B(capacities.format[i].param)); if (!force && capacities.type == 2) errx(EX_IOERR, "media already formatted (use -F to override)"); memset(&format_params, 0, sizeof(struct cdr_format_params)); format_params.fov = 1; format_params.immed = 1; format_params.length = ntohs(sizeof(struct cdr_format_capacity)); memcpy(&format_params.format, &capacities.format[i], sizeof(struct cdr_format_capacity)); if(ioctl(the_fd, CDRIOCFORMAT, &format_params) == -1) err(EX_IOERR, "ioctl(CDRIOCFORMAT)"); while (1) { sleep(1); if (ioctl(the_fd, CDRIOCGETPROGRESS, &pct) == -1) err(EX_IOERR, "ioctl(CDRIOGETPROGRESS)"); if (pct > 0 && !quiet) fprintf(stderr, "formatting DVD - %d %% done \r", pct); if (pct == 100 || (pct == 0 && last > 90)) break; last = pct; } if (!quiet) fprintf(stderr, "\n"); } int write_file(int fd, struct track_info *track_info) { off_t size, count, filesize; char buf[2352*BLOCKS]; static off_t tot_size = 0; filesize = track_info->file_size / 1024; if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); if (track_info->addr >= 0) lseek(fd, track_info->addr * track_info->block_size, SEEK_SET); if (verbose) fprintf(stderr, "addr = %d size = %jd blocks = %d\n", track_info->addr, (intmax_t)track_info->file_size, roundup_blocks(track_info)); if (!quiet) { if (track_info->file == STDIN_FILENO) fprintf(stderr, "writing from stdin\n"); else fprintf(stderr, "writing from file %s size %jd KB\n", track_info->file_name, (intmax_t)filesize); } size = 0; while ((count = read(track_info->file, buf, track_info->file_size == -1 ? track_info->block_size * BLOCKS : MIN((track_info->file_size - size), track_info->block_size * BLOCKS))) > 0) { int res; if (count % track_info->block_size) { /* pad file to % block_size */ bzero(&buf[count], (track_info->block_size * BLOCKS) - count); count = ((count / track_info->block_size) + 1) * track_info->block_size; } if ((res = write(fd, buf, count)) != count) { if (res == -1) { fprintf(stderr, "\n"); close(track_info->file); return errno; } else fprintf(stderr, "\nonly wrote %d of %jd" " bytes\n", res, (intmax_t)count); break; } size += count; tot_size += count; if (!quiet) { int pct; fprintf(stderr, "written this track %jd KB", (intmax_t)size/1024); if (track_info->file != STDIN_FILENO && filesize) { pct = (size / 1024) * 100 / filesize; fprintf(stderr, " (%d%%)", pct); } fprintf(stderr, " total %jd KB\r", (intmax_t)tot_size / 1024); } if (track_info->file_size != -1 && size >= track_info->file_size) break; } if (!quiet) fprintf(stderr, "\n"); close(track_info->file); return 0; } int roundup_blocks(struct track_info *track) { return ((track->file_size + track->block_size - 1) / track->block_size); } void cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx, int dataform, int scms, int lba) { cue->adr = adr; cue->ctl = ctl; cue->track = track; cue->index = idx; cue->dataform = dataform; cue->scms = scms; lba += 150; cue->min = lba / (60*75); cue->sec = (lba % (60*75)) / 75; cue->frame = (lba % (60*75)) % 75; } void cleanup(int dummy __unused) { if (ioctl(global_fd_for_cleanup, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)"); } void cleanup_flush(void) { if (ioctl(global_fd_for_cleanup, CDRIOCFLUSH) < 0) err(EX_IOERR, "ioctl(CDRIOCFLUSH)"); } void cleanup_signal(int sig) { signal(sig, SIG_IGN); ioctl(global_fd_for_cleanup, CDRIOCFLUSH); write(STDERR_FILENO, "\nAborted\n", 10); _exit(EXIT_FAILURE); } void usage(void) { fprintf(stderr, "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]" " [command file ...]\n", getprogname()); exit(EX_USAGE); }