1114902Sscottl/*- 2119418Sobrien * Written by: David Jeffery 3114902Sscottl * Copyright (c) 2002 Adaptec Inc. 4114902Sscottl * All rights reserved. 5114902Sscottl * 6114902Sscottl * Redistribution and use in source and binary forms, with or without 7114902Sscottl * modification, are permitted provided that the following conditions 8114902Sscottl * are met: 9114902Sscottl * 1. Redistributions of source code must retain the above copyright 10114902Sscottl * notice, this list of conditions and the following disclaimer. 11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright 12114902Sscottl * notice, this list of conditions and the following disclaimer in the 13114902Sscottl * documentation and/or other materials provided with the distribution. 14114902Sscottl * 15114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16114902Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17114902Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18114902Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19114902Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20114902Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21114902Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22114902Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23114902Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24114902Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25114902Sscottl * SUCH DAMAGE. 26114902Sscottl */ 27114902Sscottl 28119418Sobrien#include <sys/cdefs.h> 29119418Sobrien__FBSDID("$FreeBSD$"); 30114902Sscottl 31152919Sscottl#include <dev/ips/ipsreg.h> 32114902Sscottl#include <dev/ips/ips.h> 33114902Sscottl#include <dev/ips/ips_disk.h> 34114902Sscottl#include <sys/stat.h> 35114902Sscottl 36114902Sscottlstatic int ipsd_probe(device_t dev); 37114902Sscottlstatic int ipsd_attach(device_t dev); 38114902Sscottlstatic int ipsd_detach(device_t dev); 39114902Sscottl 40141062Sscottlstatic int ipsd_dump(void *arg, void *virtual, vm_offset_t physical, 41141062Sscottl off_t offset, size_t length); 42141062Sscottlstatic void ipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, 43141062Sscottl int error); 44141062Sscottlstatic void ipsd_dump_block_complete(ips_command_t *command); 45141062Sscottl 46114902Sscottlstatic disk_open_t ipsd_open; 47114902Sscottlstatic disk_close_t ipsd_close; 48114902Sscottlstatic disk_strategy_t ipsd_strategy; 49114902Sscottl 50114902Sscottlstatic device_method_t ipsd_methods[] = { 51114902Sscottl DEVMETHOD(device_probe, ipsd_probe), 52114902Sscottl DEVMETHOD(device_attach, ipsd_attach), 53114902Sscottl DEVMETHOD(device_detach, ipsd_detach), 54114902Sscottl { 0, 0 } 55114902Sscottl}; 56114902Sscottl 57114902Sscottlstatic driver_t ipsd_driver = { 58114902Sscottl "ipsd", 59114902Sscottl ipsd_methods, 60114902Sscottl sizeof(ipsdisk_softc_t) 61114902Sscottl}; 62114902Sscottl 63114902Sscottlstatic devclass_t ipsd_devclass; 64114902SscottlDRIVER_MODULE(ipsd, ips, ipsd_driver, ipsd_devclass, 0, 0); 65114902Sscottl 66114902Sscottl/* handle opening of disk device. It must set up all 67114902Sscottl information about the geometry and size of the disk */ 68114902Sscottlstatic int ipsd_open(struct disk *dp) 69114902Sscottl{ 70114902Sscottl ipsdisk_softc_t *dsc = dp->d_drv1; 71114902Sscottl 72114902Sscottl dsc->state |= IPS_DEV_OPEN; 73114902Sscottl DEVICE_PRINTF(2, dsc->dev, "I'm open\n"); 74114902Sscottl return 0; 75114902Sscottl} 76114902Sscottl 77114902Sscottlstatic int ipsd_close(struct disk *dp) 78114902Sscottl{ 79114902Sscottl ipsdisk_softc_t *dsc = dp->d_drv1; 80114902Sscottl dsc->state &= ~IPS_DEV_OPEN; 81114902Sscottl DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n"); 82114902Sscottl return 0; 83114902Sscottl} 84114902Sscottl 85114902Sscottl/* ipsd_finish is called to clean up and return a completed IO request */ 86114902Sscottlvoid ipsd_finish(struct bio *iobuf) 87114902Sscottl{ 88126364Sscottl ipsdisk_softc_t *dsc; 89126364Sscottl dsc = iobuf->bio_disk->d_drv1; 90126364Sscottl 91114902Sscottl if (iobuf->bio_flags & BIO_ERROR) { 92114902Sscottl ipsdisk_softc_t *dsc; 93114902Sscottl dsc = iobuf->bio_disk->d_drv1; 94114902Sscottl device_printf(dsc->dev, "iobuf error %d\n", iobuf->bio_error); 95114902Sscottl } else 96114902Sscottl iobuf->bio_resid = 0; 97114902Sscottl 98114902Sscottl biodone(iobuf); 99126364Sscottl ips_start_io_request(dsc->sc); 100114902Sscottl} 101114902Sscottl 102114902Sscottl 103114902Sscottlstatic void ipsd_strategy(struct bio *iobuf) 104114902Sscottl{ 105114902Sscottl ipsdisk_softc_t *dsc; 106114902Sscottl 107114902Sscottl dsc = iobuf->bio_disk->d_drv1; 108114902Sscottl DEVICE_PRINTF(8,dsc->dev,"in strategy\n"); 109116931Speter iobuf->bio_driver1 = (void *)(uintptr_t)dsc->sc->drives[dsc->disk_number].drivenum; 110126364Sscottl mtx_lock(&dsc->sc->queue_mtx); 111140923Sscottl bioq_insert_tail(&dsc->sc->queue, iobuf); 112140923Sscottl ips_start_io_request(dsc->sc); 113126364Sscottl mtx_unlock(&dsc->sc->queue_mtx); 114114902Sscottl} 115114902Sscottl 116114902Sscottlstatic int ipsd_probe(device_t dev) 117114902Sscottl{ 118114902Sscottl DEVICE_PRINTF(2,dev, "in probe\n"); 119114902Sscottl device_set_desc(dev, "Logical Drive"); 120114902Sscottl return 0; 121114902Sscottl} 122114902Sscottl 123114902Sscottlstatic int ipsd_attach(device_t dev) 124114902Sscottl{ 125114902Sscottl device_t adapter; 126114902Sscottl ipsdisk_softc_t *dsc; 127114902Sscottl u_int totalsectors; 128114902Sscottl 129114902Sscottl DEVICE_PRINTF(2,dev, "in attach\n"); 130114902Sscottl 131114902Sscottl dsc = (ipsdisk_softc_t *)device_get_softc(dev); 132114902Sscottl bzero(dsc, sizeof(ipsdisk_softc_t)); 133114902Sscottl adapter = device_get_parent(dev); 134114902Sscottl dsc->dev = dev; 135114902Sscottl dsc->sc = device_get_softc(adapter); 136114902Sscottl dsc->unit = device_get_unit(dev); 137116931Speter dsc->disk_number = (uintptr_t) device_get_ivars(dev); 138125975Sphk dsc->ipsd_disk = disk_alloc(); 139125975Sphk dsc->ipsd_disk->d_drv1 = dsc; 140125975Sphk dsc->ipsd_disk->d_name = "ipsd"; 141125975Sphk dsc->ipsd_disk->d_maxsize = IPS_MAX_IO_SIZE; 142125975Sphk dsc->ipsd_disk->d_open = ipsd_open; 143125975Sphk dsc->ipsd_disk->d_close = ipsd_close; 144125975Sphk dsc->ipsd_disk->d_strategy = ipsd_strategy; 145141062Sscottl dsc->ipsd_disk->d_dump = ipsd_dump; 146114902Sscottl 147114902Sscottl totalsectors = dsc->sc->drives[dsc->disk_number].sector_count; 148114902Sscottl if ((totalsectors > 0x400000) && 149114902Sscottl ((dsc->sc->adapter_info.miscflags & 0x8) == 0)) { 150125975Sphk dsc->ipsd_disk->d_fwheads = IPS_NORM_HEADS; 151125975Sphk dsc->ipsd_disk->d_fwsectors = IPS_NORM_SECTORS; 152114902Sscottl } else { 153125975Sphk dsc->ipsd_disk->d_fwheads = IPS_COMP_HEADS; 154125975Sphk dsc->ipsd_disk->d_fwsectors = IPS_COMP_SECTORS; 155114902Sscottl } 156125975Sphk dsc->ipsd_disk->d_sectorsize = IPS_BLKSIZE; 157125975Sphk dsc->ipsd_disk->d_mediasize = (off_t)totalsectors * IPS_BLKSIZE; 158125975Sphk dsc->ipsd_disk->d_unit = dsc->unit; 159140923Sscottl dsc->ipsd_disk->d_flags = 0; 160125975Sphk disk_create(dsc->ipsd_disk, DISK_VERSION); 161114902Sscottl 162114902Sscottl device_printf(dev, "Logical Drive (%dMB)\n", 163114902Sscottl dsc->sc->drives[dsc->disk_number].sector_count >> 11); 164114902Sscottl return 0; 165114902Sscottl} 166114902Sscottl 167114902Sscottlstatic int ipsd_detach(device_t dev) 168114902Sscottl{ 169114902Sscottl ipsdisk_softc_t *dsc; 170114902Sscottl 171114902Sscottl DEVICE_PRINTF(2, dev,"in detach\n"); 172114902Sscottl dsc = (ipsdisk_softc_t *)device_get_softc(dev); 173114902Sscottl if(dsc->state & IPS_DEV_OPEN) 174114902Sscottl return (EBUSY); 175125975Sphk disk_destroy(dsc->ipsd_disk); 176114902Sscottl return 0; 177114902Sscottl} 178141062Sscottl 179141062Sscottlstatic int 180141062Sscottlipsd_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, 181141062Sscottl size_t length) 182141062Sscottl{ 183141062Sscottl ipsdisk_softc_t *dsc; 184141062Sscottl ips_softc_t *sc; 185141062Sscottl ips_command_t *command; 186141062Sscottl ips_io_cmd *command_struct; 187141062Sscottl struct disk *dp; 188141062Sscottl void *va; 189141062Sscottl off_t off; 190141062Sscottl size_t len; 191141062Sscottl int error = 0; 192141062Sscottl 193141062Sscottl dp = arg; 194141062Sscottl dsc = dp->d_drv1; 195141062Sscottl 196141062Sscottl if (dsc == NULL) 197141062Sscottl return (EINVAL); 198198329Sbrueffer sc = dsc->sc; 199141062Sscottl 200141062Sscottl if (ips_get_free_cmd(sc, &command, 0) != 0) { 201141062Sscottl printf("ipsd: failed to get cmd for dump\n"); 202141062Sscottl return (ENOMEM); 203141062Sscottl } 204141062Sscottl 205141062Sscottl command->data_dmatag = sc->sg_dmatag; 206141062Sscottl command->callback = ipsd_dump_block_complete; 207141062Sscottl 208141062Sscottl command_struct = (ips_io_cmd *)command->command_buffer; 209141062Sscottl command_struct->id = command->id; 210141062Sscottl command_struct->drivenum= sc->drives[dsc->disk_number].drivenum; 211141062Sscottl 212141062Sscottl off = offset; 213141062Sscottl va = virtual; 214141062Sscottl 215141062Sscottl while (length > 0) { 216141062Sscottl len = 217141062Sscottl (length > IPS_MAX_IO_SIZE) ? IPS_MAX_IO_SIZE : length; 218141062Sscottl 219141062Sscottl command_struct->lba = off / IPS_BLKSIZE; 220141062Sscottl 221141062Sscottl if (bus_dmamap_load(command->data_dmatag, command->data_dmamap, 222141062Sscottl va, len, ipsd_dump_map_sg, command, BUS_DMA_NOWAIT) != 0) { 223141062Sscottl error = EIO; 224141062Sscottl break; 225141062Sscottl } 226150535Sscottl if (COMMAND_ERROR(command)) { 227141062Sscottl error = EIO; 228141062Sscottl break; 229141062Sscottl } 230141062Sscottl 231141062Sscottl length -= len; 232141062Sscottl off += len; 233141062Sscottl va = (uint8_t *)va + len; 234141062Sscottl } 235141062Sscottl 236141062Sscottl ips_insert_free_cmd(command->sc, command); 237141062Sscottl return (error); 238141062Sscottl} 239141062Sscottl 240141062Sscottlstatic void 241141062Sscottlipsd_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 242141062Sscottl{ 243141062Sscottl ips_softc_t *sc; 244141062Sscottl ips_command_t *command; 245141062Sscottl ips_sg_element_t *sg_list; 246141062Sscottl ips_io_cmd *command_struct; 247141062Sscottl int i, length; 248141062Sscottl 249141062Sscottl command = (ips_command_t *)arg; 250141062Sscottl sc = command->sc; 251141062Sscottl length = 0; 252141062Sscottl 253141062Sscottl if (error) { 254141062Sscottl printf("ipsd_dump_map_sg: error %d\n", error); 255150535Sscottl ips_set_error(command, error); 256141062Sscottl return; 257141062Sscottl } 258141062Sscottl 259141062Sscottl command_struct = (ips_io_cmd *)command->command_buffer; 260141062Sscottl 261141062Sscottl if (nsegs != 1) { 262141062Sscottl command_struct->segnum = nsegs; 263141062Sscottl sg_list = (ips_sg_element_t *)((uint8_t *) 264141062Sscottl command->command_buffer + IPS_COMMAND_LEN); 265141062Sscottl for (i = 0; i < nsegs; i++) { 266141062Sscottl sg_list[i].addr = segs[i].ds_addr; 267141062Sscottl sg_list[i].len = segs[i].ds_len; 268141062Sscottl length += segs[i].ds_len; 269141062Sscottl } 270141062Sscottl command_struct->buffaddr = 271141062Sscottl (uint32_t)command->command_phys_addr + IPS_COMMAND_LEN; 272141062Sscottl command_struct->command = IPS_SG_WRITE_CMD; 273141062Sscottl } else { 274141062Sscottl command_struct->buffaddr = segs[0].ds_addr; 275141062Sscottl length = segs[0].ds_len; 276141062Sscottl command_struct->segnum = 0; 277141062Sscottl command_struct->command = IPS_WRITE_CMD; 278141062Sscottl } 279141062Sscottl 280141062Sscottl length = (length + IPS_BLKSIZE - 1) / IPS_BLKSIZE; 281141062Sscottl command_struct->length = length; 282141062Sscottl bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, 283141062Sscottl BUS_DMASYNC_PREWRITE); 284141062Sscottl bus_dmamap_sync(command->data_dmatag, command->data_dmamap, 285141062Sscottl BUS_DMASYNC_PREWRITE); 286141062Sscottl 287141062Sscottl sc->ips_issue_cmd(command); 288141062Sscottl sc->ips_poll_cmd(command); 289141062Sscottl return; 290141062Sscottl} 291141062Sscottl 292141062Sscottlstatic void 293141062Sscottlipsd_dump_block_complete(ips_command_t *command) 294141062Sscottl{ 295141062Sscottl 296150535Sscottl if (COMMAND_ERROR(command)) 297141062Sscottl printf("ipsd_dump completion error= 0x%x\n", 298141062Sscottl command->status.value); 299141062Sscottl 300141062Sscottl bus_dmamap_sync(command->data_dmatag, command->data_dmamap, 301141062Sscottl BUS_DMASYNC_POSTWRITE); 302141062Sscottl bus_dmamap_unload(command->data_dmatag, command->data_dmamap); 303141062Sscottl} 304