The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/wds/wd7000.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1994 Ludd, University of Lule}, Sweden.
    3  * Copyright (c) 2000 Sergey A. Babkin
    4  * All rights reserved.
    5  *
    6  * Written by Olof Johansson (offe@ludd.luth.se) 1995.
    7  * Based on code written by Theo de Raadt (deraadt@fsa.ca).
    8  * Resurrected, ported to CAM and generally cleaned up by Sergey Babkin
    9  * <babkin@bellatlantic.net> or <babkin@users.sourceforge.net>.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. All advertising materials mentioning features or use of this software
   20  *    must display the following acknowledgement:
   21  *     This product includes software developed at Ludd, University of Lule}
   22  *     and by the FreeBSD project.
   23  * 4. The name of the author may not be used to endorse or promote products
   24  *    derived from this software without specific prior written permission
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   36  *
   37  */
   38 
   39 #include <sys/cdefs.h>
   40 __FBSDID("$FreeBSD: src/sys/dev/wds/wd7000.c,v 1.8.4.1 2005/01/30 01:00:12 imp Exp $");
   41 
   42 /* All bugs are subject to removal without further notice */
   43 
   44 /*
   45  * offe 01/07/95
   46  * 
   47  * This version of the driver _still_ doesn't implement scatter/gather for the
   48  * WD7000-FASST2. This is due to the fact that my controller doesn't seem to
   49  * support it. That, and the lack of documentation makes it impossible for me
   50  * to implement it. What I've done instead is allocated a local buffer,
   51  * contiguous buffer big enough to handle the requests. I haven't seen any
   52  * read/write bigger than 64k, so I allocate a buffer of 64+16k. The data
   53  * that needs to be DMA'd to/from the controller is copied to/from that
   54  * buffer before/after the command is sent to the card.
   55  * 
   56  * SB 03/30/00
   57  * 
   58  * An intermediate buffer is needed anyway to make sure that the buffer is
   59  * located under 16MB, otherwise it's out of reach of ISA cards. I've added
   60  * optimizations to allocate space in buffer in fragments.
   61  */
   62 
   63 /*
   64  * Jumpers: (see The Ref(TM) for more info)
   65  * W1/W2 - interrupt selection:
   66  *  W1 (1-2) IRQ3, (3-4) IRQ4, (5-6) IRQ5, (7-8) IRQ7, (9-10) IRQ9
   67  *  W2 (21-22) IRQ10, (19-20) IRQ11, (17-18) IRQ12, (15-16) IRQ14, (13-14) IRQ15
   68  *
   69  * W2 - DRQ/DACK selection, DRQ and DACK must be the same:
   70  *  (5-6) DRQ5 (11-12) DACK5
   71  *  (3-4) DRQ6 (9-10) DACK6
   72  *  (1-2) DRQ7 (7-8) DACK7
   73  *
   74  * W3 - I/O address selection: open pair of pins (OFF) means 1, jumpered (ON) means 0
   75  *  pair (1-2) is bit 3, ..., pair (9-10) is bit 7. All the other bits are equal
   76  *  to the value 0x300. In bitwise representation that would be:
   77  *   0 0 1 1 (9-10) (7-8) (5-6) (3-4) (1-2) 0 0 0
   78  *  For example, address 0x3C0, bitwise 1111000000 will be represented as:
   79  *   (9-10) OFF, (7-8) OFF, (5-6) ON, (3-4) ON, (1-2) ON
   80  * 
   81  * W4 - BIOS address: open pair of pins (OFF) means 1, jumpered (ON) means 0
   82  *  pair (1-2) is bit 13, ..., pair (7-8) is bit 16. All the other bits are
   83  *  equal to the value 0xC0000. In bitwise representation that would be:
   84  *   1 1 0 (7-8) (5-6) (3-4) (1-2) 0 0000 0000 0000
   85  *  For example, address 0xD8000 will be represented as:
   86  *   (7-8) OFF, (5-6) OFF, (3-4) ON, (1-2) ON
   87  *
   88  * W98 (on newer cards) - BIOS enabled; on older cards just remove the BIOS
   89  * chip to disable it
   90  * W99 (on newer cards) - ROM size (1-2) OFF, (3-4) ON
   91  *
   92  * W5 - terminator power
   93  *  ON - host supplies term. power
   94  *  OFF - target supplies term. power
   95  *
   96  * W6, W9 - floppy support (a bit cryptic):
   97  *  W6 ON, W9 ON - disabled
   98  *  W6 OFF, W9 ON - enabled with HardCard only
   99  *  W6 OFF, W9 OFF - enabled with no hardCard or Combo
  100  *
  101  * Default: I/O 0x350, IRQ15, DMA6
  102  */
  103 
  104 /*
  105  * debugging levels: 
  106  * 0 - disabled 
  107  * 1 - print debugging messages 
  108  * 2 - collect  debugging messages in an internal log buffer which can be 
  109  *     printed later by calling wds_printlog from DDB 
  110  *
  111  * Both kind of logs are heavy and interact significantly with the timing 
  112  * of commands, so the observed problems may become invisible if debug 
  113  * logging is enabled.
  114  * 
  115  * The light-weight logging facility may be enabled by defining
  116  * WDS_ENABLE_SMALLOG as 1. It has very little overhead and allows observing 
  117  * the traces of various race conditions without affectiong them but the log is
  118  * quite terse. The small log can be printer from DDB by calling
  119  * wds_printsmallog.
  120  */
  121 #ifndef WDS_DEBUG
  122 #define WDS_DEBUG 0
  123 #endif
  124 
  125 #ifndef WDS_ENABLE_SMALLOG 
  126 #define WDS_ENABLE_SMALLOG 0
  127 #endif
  128 
  129 #include <sys/types.h>
  130 #include <sys/param.h>
  131 #include <sys/systm.h>
  132 #include <sys/errno.h>
  133 #include <sys/kernel.h>
  134 #include <sys/assym.h>
  135 
  136 #include <sys/bio.h>
  137 #include <sys/buf.h>
  138 
  139 #include <cam/cam.h>
  140 #include <cam/cam_ccb.h>
  141 #include <cam/cam_sim.h>
  142 #include <cam/cam_xpt_sim.h>
  143 #include <cam/cam_debug.h>
  144 #include <cam/scsi/scsi_all.h>
  145 #include <cam/scsi/scsi_message.h>
  146 
  147 #include <machine/clock.h>
  148 
  149 #include <vm/vm.h>
  150 #include <vm/vm_param.h>
  151 #include <vm/pmap.h>
  152 
  153 #include <sys/module.h>
  154 #include <sys/bus.h>
  155 #include <machine/bus.h>
  156 #include <machine/resource.h>
  157 #include <sys/rman.h>
  158 
  159 #include <isa/isavar.h>
  160 #include <isa/pnpvar.h>
  161 
  162 #define WDSTOPHYS(wp, a)        ( ((u_long)a) - ((u_long)wp->dx) + ((u_long)wp->dx_p) )
  163 #define WDSTOVIRT(wp, a)        ( ((char *)a) - ((char*)wp->dx_p) + ((char *)wp->dx) )
  164 
  165 /* 0x10000 (64k) should be enough. But just to be sure... */
  166 #define BUFSIZ          0x12000
  167 /* buffer fragment size, no more than 32 frags per buffer */
  168 #define FRAGSIZ         0x1000
  169 
  170 
  171 /* WD7000 registers */
  172 #define WDS_STAT                0       /* read */
  173 #define WDS_IRQSTAT             1       /* read */
  174 
  175 #define WDS_CMD                 0       /* write */
  176 #define WDS_IRQACK              1       /* write */
  177 #define WDS_HCR                 2       /* write */
  178 
  179 #define WDS_NPORTS              4 /* number of ports used */
  180 
  181 /* WDS_STAT (read) defs */
  182 #define WDS_IRQ                 0x80
  183 #define WDS_RDY                 0x40
  184 #define WDS_REJ                 0x20
  185 #define WDS_INIT                0x10
  186 
  187 /* WDS_IRQSTAT (read) defs */
  188 #define WDSI_MASK               0xc0
  189 #define WDSI_ERR                0x00
  190 #define WDSI_MFREE              0x80
  191 #define WDSI_MSVC               0xc0
  192 
  193 /* WDS_CMD (write) defs */
  194 #define WDSC_NOOP               0x00
  195 #define WDSC_INIT               0x01
  196 #define WDSC_DISUNSOL           0x02 /* disable unsolicited ints */
  197 #define WDSC_ENAUNSOL           0x03 /* enable unsolicited ints */
  198 #define WDSC_IRQMFREE           0x04 /* interrupt on free RQM */
  199 #define WDSC_SCSIRESETSOFT      0x05 /* soft reset */
  200 #define WDSC_SCSIRESETHARD      0x06 /* hard reset ack */
  201 #define WDSC_MSTART(m)          (0x80 + (m)) /* start mailbox */
  202 #define WDSC_MMSTART(m)         (0xc0 + (m)) /* start all mailboxes */
  203 
  204 /* WDS_HCR (write) defs */
  205 #define WDSH_IRQEN              0x08
  206 #define WDSH_DRQEN              0x04
  207 #define WDSH_SCSIRESET          0x02
  208 #define WDSH_ASCRESET           0x01
  209 
  210 struct wds_cmd {
  211         u_int8_t        cmd;
  212         u_int8_t        targ;
  213         u_int8_t        scb[12];
  214         u_int8_t        stat;
  215         u_int8_t        venderr;
  216         u_int8_t        len[3];
  217         u_int8_t        data[3];
  218         u_int8_t        next[3];
  219         u_int8_t        write;
  220         u_int8_t        xx[6];
  221 };
  222 
  223 struct wds_req {
  224         struct     wds_cmd cmd;
  225         union      ccb *ccb;
  226         enum {
  227                 WR_DONE = 0x01,
  228                 WR_SENSE = 0x02
  229         } flags;
  230         u_int8_t  *buf;         /* address of linear data buffer */
  231         u_int32_t  mask;        /* mask of allocated fragments */
  232         u_int8_t        ombn;
  233         u_int8_t        id;     /* number of request */
  234 };
  235 
  236 #define WDSX_SCSICMD            0x00
  237 #define WDSX_OPEN_RCVBUF        0x80
  238 #define WDSX_RCV_CMD            0x81
  239 #define WDSX_RCV_DATA           0x82
  240 #define WDSX_RCV_DATASTAT       0x83
  241 #define WDSX_SND_DATA           0x84
  242 #define WDSX_SND_DATASTAT       0x85
  243 #define WDSX_SND_CMDSTAT        0x86
  244 #define WDSX_READINIT           0x88
  245 #define WDSX_READSCSIID         0x89
  246 #define WDSX_SETUNSOLIRQMASK    0x8a
  247 #define WDSX_GETUNSOLIRQMASK    0x8b
  248 #define WDSX_GETFIRMREV         0x8c
  249 #define WDSX_EXECDIAG           0x8d
  250 #define WDSX_SETEXECPARM        0x8e
  251 #define WDSX_GETEXECPARM        0x8f
  252 
  253 struct wds_mb {
  254         u_int8_t        stat;
  255         u_int8_t        addr[3];
  256 };
  257 /* ICMB status value */
  258 #define ICMB_OK                 0x01
  259 #define ICMB_OKERR              0x02
  260 #define ICMB_ETIME              0x04
  261 #define ICMB_ERESET             0x05
  262 #define ICMB_ETARCMD            0x06
  263 #define ICMB_ERESEL             0x80
  264 #define ICMB_ESEL               0x81
  265 #define ICMB_EABORT             0x82
  266 #define ICMB_ESRESET            0x83
  267 #define ICMB_EHRESET            0x84
  268 
  269 struct wds_setup {
  270         u_int8_t        cmd;
  271         u_int8_t        scsi_id;
  272         u_int8_t        buson_t;
  273         u_int8_t        busoff_t;
  274         u_int8_t        xx;
  275         u_int8_t        mbaddr[3];
  276         u_int8_t        nomb;
  277         u_int8_t        nimb;
  278 };
  279 
  280 /* the code depends on equality of these parameters */
  281 #define MAXSIMUL        8
  282 #define WDS_NOMB        MAXSIMUL
  283 #define WDS_NIMB        MAXSIMUL
  284 
  285 static int      fragsiz;
  286 static int      nfrags;
  287 
  288 /* structure for data exchange with controller */
  289 
  290 struct wdsdx {
  291         struct wds_req  req[MAXSIMUL];
  292         struct wds_mb   ombs[MAXSIMUL];
  293         struct wds_mb   imbs[MAXSIMUL];
  294         u_int8_t        data[BUFSIZ];
  295 };
  296 
  297 /* structure softc */
  298 
  299 struct wds {
  300         device_t         dev;
  301         int              unit;
  302         int              addr;
  303         int              drq;
  304         struct cam_sim  *sim;   /* SIM descriptor for this card */
  305         struct cam_path *path;  /* wildcard path for this card */
  306         char             want_wdsr;     /* resource shortage flag */
  307         u_int32_t        data_free;
  308         u_int32_t        wdsr_free;
  309         struct wdsdx    *dx;
  310         struct wdsdx    *dx_p; /* physical address */
  311         struct resource *port_r;
  312         int              port_rid;
  313         struct resource *drq_r;
  314         int              drq_rid;
  315         struct resource *intr_r;
  316         int              intr_rid;
  317         void            *intr_cookie;
  318         bus_dma_tag_t    bustag;
  319         bus_dmamap_t     busmap;
  320 };
  321 
  322 #define ccb_wdsr        spriv_ptr1      /* for wds request */
  323 
  324 static int      wds_probe(device_t dev);
  325 static int      wds_attach(device_t dev);
  326 static void     wds_intr(struct wds *wp);
  327 
  328 static void     wds_action(struct cam_sim * sim, union ccb * ccb);
  329 static void     wds_poll(struct cam_sim * sim);
  330 
  331 static int      wds_preinit(struct wds *wp);
  332 static int      wds_init(struct wds *wp);
  333 
  334 static void     wds_alloc_callback(void *arg, bus_dma_segment_t *seg,  
  335          int nseg, int error);
  336 static void     wds_free_resources(struct wds *wp);
  337 
  338 static struct wds_req *wdsr_alloc(struct wds *wp);
  339 
  340 static void     wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio);
  341 static void     wdsr_ccb_done(struct wds *wp, struct wds_req *r, 
  342                               union ccb *ccb, u_int32_t status);
  343 
  344 static void     wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat);
  345 static int      wds_runsense(struct wds *wp, struct wds_req *r);
  346 static int      wds_getvers(struct wds *wp);
  347 
  348 static int      wds_cmd(int base, u_int8_t * p, int l);
  349 static void     wds_wait(int reg, int mask, int val);
  350 
  351 static struct wds_req *cmdtovirt(struct wds *wp, u_int32_t phys);
  352 
  353 static u_int32_t frag_alloc(struct wds *wp, int size, u_int8_t **res, 
  354                             u_int32_t *maskp);
  355 static void     frag_free(struct wds *wp, u_int32_t mask);
  356 
  357 void            wds_print(void);
  358 
  359 #if WDS_ENABLE_SMALLOG==1
  360 static __inline void   smallog(char c);
  361 void    wds_printsmallog(void);
  362 #endif /* SMALLOG */
  363 
  364 /* SCSI ID of the adapter itself */
  365 #ifndef WDS_HBA_ID
  366 #define WDS_HBA_ID 7
  367 #endif
  368 
  369 #if WDS_DEBUG == 2
  370 #define LOGLINESIZ      81
  371 #define NLOGLINES       300
  372 #define DBX     wds_nextlog(), LOGLINESIZ,
  373 #define DBG     snprintf
  374 
  375 static char     wds_log[NLOGLINES][LOGLINESIZ];
  376 static int      logwrite = 0, logread = 0;
  377 static char    *wds_nextlog(void);
  378 void            wds_printlog(void);
  379 
  380 #elif WDS_DEBUG != 0
  381 #define DBX
  382 #define DBG     printf
  383 #else
  384 #define DBX
  385 #define DBG     if(0) printf
  386 #endif
  387 
  388 /* the table of supported bus methods */
  389 static device_method_t wds_isa_methods[] = {
  390         DEVMETHOD(device_probe,         wds_probe),
  391         DEVMETHOD(device_attach,        wds_attach),
  392         { 0, 0 }
  393 };
  394 
  395 static driver_t wds_isa_driver = {
  396         "wds",
  397         wds_isa_methods,
  398         sizeof(struct wds),
  399 };
  400 
  401 static devclass_t wds_devclass;
  402 
  403 DRIVER_MODULE(wds, isa, wds_isa_driver, wds_devclass, 0, 0);
  404 
  405 #if WDS_ENABLE_SMALLOG==1
  406 #define SMALLOGSIZ      512
  407 static char      wds_smallog[SMALLOGSIZ];
  408 static char     *wds_smallogp = wds_smallog;
  409 static char      wds_smallogover = 0;
  410 
  411 static __inline void
  412 smallog(char c)
  413 {
  414         *wds_smallogp = c;
  415         if (++wds_smallogp == &wds_smallog[SMALLOGSIZ]) {
  416                 wds_smallogp = wds_smallog;
  417                 wds_smallogover = 1;
  418         }
  419 }
  420 
  421 #define smallog2(a, b)  (smallog(a), smallog(b))
  422 #define smallog3(a, b, c)       (smallog(a), smallog(b), smallog(c))
  423 #define smallog4(a, b, c, d)    (smallog(a),smallog(b),smallog(c),smallog(d))
  424 
  425 void 
  426 wds_printsmallog(void)
  427 {
  428         int      i;
  429         char    *p;
  430 
  431         printf("wds: ");
  432         p = wds_smallogover ? wds_smallogp : wds_smallog;
  433         i = 0;
  434         do {
  435                 printf("%c", *p);
  436                 if (++p == &wds_smallog[SMALLOGSIZ])
  437                         p = wds_smallog;
  438                 if (++i == 70) {
  439                         i = 0;
  440                         printf("\nwds: ");
  441                 }
  442         } while (p != wds_smallogp);
  443         printf("\n");
  444 }
  445 #else
  446 #define smallog(a)
  447 #define smallog2(a, b)
  448 #define smallog3(a, b, c)
  449 #define smallog4(a, b, c, d)
  450 #endif                          /* SMALLOG */
  451 
  452 static int
  453 wds_probe(device_t dev)
  454 {
  455         struct  wds *wp;
  456         int     error = 0;
  457         int     irq;
  458 
  459         /* No pnp support */
  460         if (isa_get_vendorid(dev))
  461                 return (ENXIO);
  462 
  463         wp = (struct wds *) device_get_softc(dev);
  464         wp->unit = device_get_unit(dev);
  465         wp->dev = dev;
  466 
  467         wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
  468         if (wp->addr == 0 || wp->addr <0x300
  469          || wp->addr > 0x3f8 || wp->addr & 0x7) {
  470                 device_printf(dev, "invalid port address 0x%x\n", wp->addr);
  471                 return (ENXIO);
  472         }
  473 
  474         if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0)
  475                 return (ENXIO);
  476 
  477         /* get the DRQ */
  478         wp->drq = bus_get_resource_start(dev, SYS_RES_DRQ, 0 /*rid*/);
  479         if (wp->drq < 5 || wp->drq > 7) {
  480                 device_printf(dev, "invalid DRQ %d\n", wp->drq);
  481                 return (ENXIO);
  482         }
  483 
  484         /* get the IRQ */
  485         irq = bus_get_resource_start(dev, SYS_RES_IRQ, 0 /*rid*/);
  486         if (irq < 3) {
  487                 device_printf(dev, "invalid IRQ %d\n", irq);
  488                 return (ENXIO);
  489         }
  490 
  491         wp->port_rid = 0;
  492         wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
  493                                         /*start*/ 0, /*end*/ ~0,
  494                                         /*count*/ 0, RF_ACTIVE);
  495         if (wp->port_r == NULL)
  496                 return (ENXIO);
  497 
  498         error = wds_preinit(wp);
  499 
  500         /*
  501          * We cannot hold resources between probe and
  502          * attach as we may never be attached.
  503          */
  504         wds_free_resources(wp);
  505 
  506         return (error);
  507 }
  508 
  509 static int
  510 wds_attach(device_t dev)
  511 {
  512         struct  wds *wp;
  513         struct  cam_devq *devq;
  514         struct  cam_sim *sim;
  515         struct  cam_path *pathp;
  516         int     i;
  517         int     error = 0;
  518 
  519         wp = (struct wds *)device_get_softc(dev);
  520 
  521         wp->port_rid = 0;
  522         wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT,  &wp->port_rid,
  523                                         /*start*/ 0, /*end*/ ~0,
  524                                         /*count*/ 0, RF_ACTIVE);
  525         if (wp->port_r == NULL)
  526                 return (ENXIO);
  527 
  528         /* We must now release resources on error. */
  529 
  530         wp->drq_rid = 0;
  531         wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ,  &wp->drq_rid,
  532                                        /*start*/ 0, /*end*/ ~0,
  533                                        /*count*/ 0, RF_ACTIVE);
  534         if (wp->drq_r == NULL)
  535                 goto bad;
  536 
  537         wp->intr_rid = 0;
  538         wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ,  &wp->intr_rid,
  539                                         /*start*/ 0, /*end*/ ~0,
  540                                         /*count*/ 0, RF_ACTIVE);
  541         if (wp->intr_r == NULL)
  542                 goto bad;
  543         error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY,
  544                                (driver_intr_t *)wds_intr, (void *)wp,
  545                                &wp->intr_cookie);
  546         if (error)
  547                 goto bad;
  548 
  549         /* now create the memory buffer */
  550         error = bus_dma_tag_create(NULL, /*alignment*/4,
  551                                    /*boundary*/0,
  552                                    /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
  553                                    /*highaddr*/ BUS_SPACE_MAXADDR,
  554                                    /*filter*/ NULL, /*filterarg*/ NULL,
  555                                    /*maxsize*/ sizeof(* wp->dx),
  556                                    /*nsegments*/ 1,
  557                                    /*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0,
  558                                    /*lockfunc*/busdma_lock_mutex,
  559                                    /*lockarg*/&Giant,
  560                                    &wp->bustag);
  561         if (error)
  562                 goto bad;
  563 
  564         error = bus_dmamem_alloc(wp->bustag, (void **)&wp->dx,
  565                                  /*flags*/ 0, &wp->busmap);
  566         if (error)
  567                 goto bad;
  568             
  569         bus_dmamap_load(wp->bustag, wp->busmap, (void *)wp->dx,
  570                         sizeof(* wp->dx), wds_alloc_callback,
  571                         (void *)&wp->dx_p, /*flags*/0);
  572 
  573         /* initialize the wds_req structures on this unit */
  574         for(i=0; i<MAXSIMUL; i++)  {
  575                 wp->dx->req[i].id = i;
  576                 wp->wdsr_free |= 1<<i;
  577         }
  578 
  579         /* initialize the memory buffer allocation for this unit */
  580         if (BUFSIZ / FRAGSIZ > 32) {
  581                 fragsiz = (BUFSIZ / 32) & ~0x01; /* keep it word-aligned */
  582                 device_printf(dev, "data buffer fragment size too small.  "
  583                               "BUFSIZE / FRAGSIZE must be <= 32\n");
  584         } else
  585                 fragsiz = FRAGSIZ & ~0x01; /* keep it word-aligned */
  586 
  587         wp->data_free = 0;
  588         nfrags = 0;
  589         for (i = fragsiz; i <= BUFSIZ; i += fragsiz) {
  590                 nfrags++;
  591                 wp->data_free = (wp->data_free << 1) | 1;
  592         }
  593 
  594         /* complete the hardware initialization */
  595         if (wds_init(wp) != 0)
  596                 goto bad;
  597 
  598         if (wds_getvers(wp) == -1)
  599                 device_printf(dev, "getvers failed\n");
  600         device_printf(dev, "using %d bytes / %d frags for dma buffer\n",
  601                       BUFSIZ, nfrags);
  602 
  603         devq = cam_simq_alloc(MAXSIMUL);
  604         if (devq == NULL)
  605                 goto bad;
  606 
  607         sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp,
  608                             wp->unit, 1, 1, devq);
  609         if (sim == NULL) {
  610                 cam_simq_free(devq);
  611                 goto bad;
  612         }
  613         wp->sim = sim;
  614 
  615         if (xpt_bus_register(sim, 0) != CAM_SUCCESS) {
  616                 cam_sim_free(sim, /* free_devq */ TRUE);
  617                 goto bad;
  618         }
  619         if (xpt_create_path(&pathp, /* periph */ NULL,
  620                             cam_sim_path(sim), CAM_TARGET_WILDCARD,
  621                             CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
  622                 xpt_bus_deregister(cam_sim_path(sim));
  623                 cam_sim_free(sim, /* free_devq */ TRUE);
  624                 goto bad;
  625         }
  626         wp->path = pathp;
  627 
  628         return (0);
  629 
  630 bad:
  631         wds_free_resources(wp);
  632         if (error)  
  633                 return (error);
  634         else /* exact error is unknown */
  635                 return (ENXIO);
  636 }
  637 
  638 /* callback to save the physical address */
  639 static void     
  640 wds_alloc_callback(void *arg, bus_dma_segment_t *seg,  int nseg, int error)
  641 {
  642         *(bus_addr_t *)arg = seg[0].ds_addr;
  643 }
  644 
  645 static void     
  646 wds_free_resources(struct wds *wp)
  647 {
  648         /* check every resource and free if not zero */
  649             
  650         /* interrupt handler */
  651         if (wp->intr_r) {
  652                 bus_teardown_intr(wp->dev, wp->intr_r, wp->intr_cookie);
  653                 bus_release_resource(wp->dev, SYS_RES_IRQ, wp->intr_rid,
  654                                      wp->intr_r);
  655                 wp->intr_r = 0;
  656         }
  657 
  658         /* all kinds of memory maps we could have allocated */
  659         if (wp->dx_p) {
  660                 bus_dmamap_unload(wp->bustag, wp->busmap);
  661                 wp->dx_p = 0;
  662         }
  663         if (wp->dx) { /* wp->busmap may be legitimately equal to 0 */
  664                 /* the map will also be freed */
  665                 bus_dmamem_free(wp->bustag, wp->dx, wp->busmap);
  666                 wp->dx = 0;
  667         }
  668         if (wp->bustag) {
  669                 bus_dma_tag_destroy(wp->bustag);
  670                 wp->bustag = 0;
  671         }
  672         /* release all the bus resources */
  673         if (wp->drq_r) {
  674                 bus_release_resource(wp->dev, SYS_RES_DRQ,
  675                                      wp->drq_rid, wp->drq_r);
  676                 wp->drq_r = 0;
  677         }
  678         if (wp->port_r) {
  679                 bus_release_resource(wp->dev, SYS_RES_IOPORT,
  680                                      wp->port_rid, wp->port_r);
  681                 wp->port_r = 0;
  682         }
  683 }
  684 
  685 /* allocate contiguous fragments from the buffer */
  686 static u_int32_t
  687 frag_alloc(struct wds *wp, int size, u_int8_t **res, u_int32_t *maskp)
  688 {
  689         int     i;
  690         u_int32_t       mask;
  691         u_int32_t       free;
  692 
  693         if (size > fragsiz * nfrags)
  694                 return (CAM_REQ_TOO_BIG);
  695 
  696         mask = 1;               /* always allocate at least 1 fragment */
  697         for (i = fragsiz; i < size; i += fragsiz)
  698                 mask = (mask << 1) | 1;
  699 
  700         free = wp->data_free;
  701         if(free != 0) {
  702                 i = ffs(free)-1; /* ffs counts bits from 1 */
  703                 for (mask <<= i; i < nfrags; i++) {
  704                         if ((free & mask) == mask) {
  705                                 wp->data_free &= ~mask; /* mark frags as busy */
  706                                 *maskp = mask;
  707                                 *res = &wp->dx->data[fragsiz * i];
  708                                 DBG(DBX "wds%d: allocated buffer mask=0x%x\n",
  709                                         wp->unit, mask);
  710                                 return (CAM_REQ_CMP);
  711                         }
  712                         if (mask & 0x80000000)
  713                                 break;
  714 
  715                         mask <<= 1;
  716                 }
  717         }
  718         return (CAM_REQUEUE_REQ);       /* no free memory now, try later */
  719 }
  720 
  721 static void
  722 frag_free(struct wds *wp, u_int32_t mask)
  723 {
  724         wp->data_free |= mask;  /* mark frags as free */
  725         DBG(DBX "wds%d: freed buffer mask=0x%x\n", wp->unit, mask);
  726 }
  727 
  728 static struct wds_req *
  729 wdsr_alloc(struct wds *wp)
  730 {
  731         struct  wds_req *r;
  732         int     x;
  733         int     i;
  734 
  735         r = NULL;
  736         x = splcam();
  737 
  738         /* anyway most of the time only 1 or 2 commands will
  739          * be active because SCSI disconnect is not supported
  740          * by hardware, so the search should be fast enough
  741          */
  742         i = ffs(wp->wdsr_free) - 1;
  743         if(i < 0) {
  744                 splx(x);
  745                 return (NULL);
  746         }
  747         wp->wdsr_free &= ~ (1<<i);
  748         r = &wp->dx->req[i];
  749         r->flags = 0;   /* reset all flags */
  750         r->ombn = i;            /* luckily we have one omb per wdsr */
  751         wp->dx->ombs[i].stat = 1;
  752 
  753         r->mask = 0;
  754         splx(x);
  755         smallog3('r', i + '', r->ombn + '');
  756         return (r);
  757 }
  758 
  759 static void
  760 wds_intr(struct wds *wp)
  761 {
  762         struct   wds_req *rp;
  763         struct   wds_mb *in;
  764         u_int8_t stat;
  765         u_int8_t c;
  766         int      addr = wp->addr;
  767 
  768         DBG(DBX "wds%d: interrupt [\n", wp->unit);
  769         smallog('[');
  770 
  771         if (inb(addr + WDS_STAT) & WDS_IRQ) {
  772                 c = inb(addr + WDS_IRQSTAT);
  773                 if ((c & WDSI_MASK) == WDSI_MSVC) {
  774                         c = c & ~WDSI_MASK;
  775                         in = &wp->dx->imbs[c];
  776 
  777                         rp = cmdtovirt(wp, scsi_3btoul(in->addr));
  778                         stat = in->stat;
  779 
  780                         if (rp != NULL)
  781                                 wds_done(wp, rp, stat);
  782                         else
  783                                 device_printf(wp->dev,
  784                                               "got weird command address %p"
  785                                               "from controller\n", rp);
  786 
  787                         in->stat = 0;
  788                 } else
  789                         device_printf(wp->dev,
  790                                       "weird interrupt, irqstat=0x%x\n", c);
  791                 outb(addr + WDS_IRQACK, 0);
  792         } else {
  793                 smallog('?');
  794         }
  795         smallog(']');
  796         DBG(DBX "wds%d: ]\n", wp->unit);
  797 }
  798 
  799 static void
  800 wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat)
  801 {
  802         struct  ccb_hdr *ccb_h;
  803         struct  ccb_scsiio *csio;
  804         int     status;
  805 
  806         smallog('d');
  807 
  808         if (r->flags & WR_DONE) {
  809                 device_printf(wp->dev,
  810                                 "request %d reported done twice\n", r->id);
  811                 smallog2('x', r->id + '');
  812                 return;
  813         }
  814 
  815         smallog(r->id + '');
  816         ccb_h = &r->ccb->ccb_h;
  817         csio = &r->ccb->csio;
  818         status = CAM_REQ_CMP_ERR;
  819 
  820         DBG(DBX "wds%d: %s stat=0x%x c->stat=0x%x c->venderr=0x%x\n", wp->unit,
  821             r->flags & WR_SENSE ? "(sense)" : "", 
  822                 stat, r->cmd.stat, r->cmd.venderr);
  823 
  824         if (r->flags & WR_SENSE) {
  825                 if (stat == ICMB_OK || (stat == ICMB_OKERR && r->cmd.stat == 0)) {
  826                         DBG(DBX "wds%d: sense 0x%x\n", wp->unit, r->buf[0]);
  827                         /* it has the same size now but for future */
  828                         bcopy(r->buf, &csio->sense_data,
  829                               sizeof(struct scsi_sense_data) > csio->sense_len ?
  830                               csio->sense_len : sizeof(struct scsi_sense_data));
  831                         if (sizeof(struct scsi_sense_data) >= csio->sense_len)
  832                                 csio->sense_resid = 0;
  833                         else
  834                                 csio->sense_resid =
  835                                         csio->sense_len
  836                                       - sizeof(struct scsi_sense_data);
  837                         status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR;
  838                 } else {
  839                         status = CAM_AUTOSENSE_FAIL;
  840                 }
  841         } else {
  842                 switch (stat) {
  843                 case ICMB_OK:
  844                         if (ccb_h) {
  845                                 csio->resid = 0;
  846                                 csio->scsi_status = r->cmd.stat;
  847                                 status = CAM_REQ_CMP;
  848                         }
  849                         break;
  850                 case ICMB_OKERR:
  851                         if (ccb_h) {
  852                                 csio->scsi_status = r->cmd.stat;
  853                                 if (r->cmd.stat) {
  854                                         if (ccb_h->flags & CAM_DIS_AUTOSENSE)
  855                                                 status = CAM_SCSI_STATUS_ERROR;
  856                                         else {
  857                                                 if ( wds_runsense(wp, r) == CAM_REQ_CMP )
  858                                                         return;
  859                                                 /* in case of error continue with freeing of CCB */
  860                                         }
  861                                 } else {
  862                                         csio->resid = 0;
  863                                         status = CAM_REQ_CMP;
  864                                 }
  865                         }
  866                         break;
  867                 case ICMB_ETIME:
  868                         if (ccb_h)
  869                                 status = CAM_SEL_TIMEOUT;
  870                         break;
  871                 case ICMB_ERESET:
  872                 case ICMB_ETARCMD:
  873                 case ICMB_ERESEL:
  874                 case ICMB_ESEL:
  875                 case ICMB_EABORT:
  876                 case ICMB_ESRESET:
  877                 case ICMB_EHRESET:
  878                         if (ccb_h)
  879                                 status = CAM_REQ_CMP_ERR;
  880                         break;
  881                 }
  882 
  883                 if (ccb_h && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) {
  884                         /* we accept only virtual addresses in wds_action() */
  885                         bcopy(r->buf, csio->data_ptr, csio->dxfer_len);
  886                 }
  887         }
  888 
  889         r->flags |= WR_DONE;
  890         wp->dx->ombs[r->ombn].stat = 0;
  891 
  892         if (ccb_h) {
  893                 wdsr_ccb_done(wp, r, r->ccb, status);
  894                 smallog3('-', ccb_h->target_id + '', ccb_h->target_lun + '');
  895         } else {
  896                 frag_free(wp, r->mask);
  897                 if (wp->want_wdsr) {
  898                         wp->want_wdsr = 0;
  899                         xpt_release_simq(wp->sim, /* run queue */ 1);
  900                 }
  901                 wp->wdsr_free |= (1 << r->id);
  902         }
  903 
  904         DBG(DBX "wds%d: request %p done\n", wp->unit, r);
  905 }
  906 
  907 /* command returned bad status, request sense */
  908 
  909 static int
  910 wds_runsense(struct wds *wp, struct wds_req *r)
  911 {
  912         u_int8_t          c;
  913         struct  ccb_hdr *ccb_h;
  914 
  915         ccb_h = &r->ccb->ccb_h;
  916 
  917         r->flags |= WR_SENSE;
  918         scsi_ulto3b(WDSTOPHYS(wp, &r->cmd),
  919          wp->dx->ombs[r->ombn].addr);
  920         bzero(&r->cmd, sizeof r->cmd);
  921         r->cmd.cmd = WDSX_SCSICMD;
  922         r->cmd.targ = (ccb_h->target_id << 5) |
  923                 ccb_h->target_lun;
  924 
  925         scsi_ulto3b(0, r->cmd.next);
  926 
  927         r->cmd.scb[0] = REQUEST_SENSE;
  928         r->cmd.scb[1] = ccb_h->target_lun << 5;
  929         r->cmd.scb[4] = sizeof(struct scsi_sense_data);
  930         r->cmd.scb[5] = 0;
  931         scsi_ulto3b(WDSTOPHYS(wp, r->buf), r->cmd.data);
  932         scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len);
  933         r->cmd.write = 0x80;
  934 
  935         outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
  936 
  937         wp->dx->ombs[r->ombn].stat = 1;
  938         c = WDSC_MSTART(r->ombn);
  939 
  940         if (wds_cmd(wp->addr, &c, sizeof c) != 0) {
  941                 device_printf(wp->dev, "unable to start outgoing sense mbox\n");
  942                 wp->dx->ombs[r->ombn].stat = 0;
  943                 wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL);
  944                 return CAM_AUTOSENSE_FAIL;
  945         } else {
  946                 DBG(DBX "wds%d: enqueued status cmd 0x%x, r=%p\n",
  947                         wp->unit, r->cmd.scb[0] & 0xFF, r);
  948                 /* don't free CCB yet */
  949                 smallog3('*', ccb_h->target_id + '',
  950                          ccb_h->target_lun + '');
  951                 return CAM_REQ_CMP;
  952         }
  953 }
  954 
  955 static int
  956 wds_getvers(struct wds *wp)
  957 {
  958         struct   wds_req *r;
  959         int      base;
  960         u_int8_t c;
  961         int      i;
  962 
  963         base = wp->addr;
  964 
  965         r = wdsr_alloc(wp);
  966         if (!r) {
  967                 device_printf(wp->dev, "no request slot available!\n");
  968                 return (-1);
  969         }
  970         r->flags &= ~WR_DONE;
  971 
  972         r->ccb = NULL;
  973 
  974         scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
  975 
  976         bzero(&r->cmd, sizeof r->cmd);
  977         r->cmd.cmd = WDSX_GETFIRMREV;
  978 
  979         outb(base + WDS_HCR, WDSH_DRQEN);
  980 
  981         c = WDSC_MSTART(r->ombn);
  982         if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) {
  983                 device_printf(wp->dev, "version request failed\n");
  984                 wp->wdsr_free |= (1 << r->id);
  985                 wp->dx->ombs[r->ombn].stat = 0;
  986                 return (-1);
  987         }
  988         while (1) {
  989                 i = 0;
  990                 while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) {
  991                         DELAY(9000);
  992                         if (++i == 100) {
  993                                 device_printf(wp->dev, "getvers timeout\n");
  994                                 return (-1);
  995                         }
  996                 }
  997                 wds_intr(wp);
  998                 if (r->flags & WR_DONE) {
  999                         device_printf(wp->dev, "firmware version %d.%02d\n",
 1000                                r->cmd.targ, r->cmd.scb[0]);
 1001                         wp->wdsr_free |= (1 << r->id);
 1002                         return (0);
 1003                 }
 1004         }
 1005 }
 1006 
 1007 static void
 1008 wdsr_ccb_done(struct wds *wp, struct wds_req *r,
 1009               union ccb *ccb, u_int32_t status)
 1010 {
 1011         ccb->ccb_h.ccb_wdsr = 0;
 1012 
 1013         if (r != NULL) {
 1014                 /* To implement timeouts we would need to know how to abort the
 1015                  * command on controller, and this is a great mystery.
 1016                  * So for now we just pass the responsibility for timeouts
 1017                  * to the controlles itself, it does that reasonably good.
 1018                  */
 1019                 /* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */
 1020                 /* we're about to free a hcb, so the shortage has ended */
 1021                 frag_free(wp, r->mask);
 1022                 if (wp->want_wdsr && status != CAM_REQUEUE_REQ) {
 1023                         wp->want_wdsr = 0;
 1024                         status |= CAM_RELEASE_SIMQ;
 1025                         smallog('R');
 1026                 }
 1027                 wp->wdsr_free |= (1 << r->id);
 1028         }
 1029         ccb->ccb_h.status =
 1030             status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
 1031         xpt_done(ccb);
 1032 }
 1033 
 1034 static void
 1035 wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
 1036 {
 1037         int      unit = cam_sim_unit(sim);
 1038         struct   wds *wp;
 1039         struct   ccb_hdr *ccb_h;
 1040         struct   wds_req *r;
 1041         int      base;
 1042         u_int8_t c;
 1043         int      error;
 1044         int      n;
 1045 
 1046         wp = (struct wds *)cam_sim_softc(sim);
 1047         ccb_h = &csio->ccb_h;
 1048 
 1049         DBG(DBX "wds%d: cmd TARG=%d LUN=%d\n", unit, ccb_h->target_id,
 1050             ccb_h->target_lun);
 1051 
 1052         if (ccb_h->target_id > 7 || ccb_h->target_id == WDS_HBA_ID) {
 1053                 ccb_h->status = CAM_TID_INVALID;
 1054                 xpt_done((union ccb *) csio);
 1055                 return;
 1056         }
 1057         if (ccb_h->target_lun > 7) {
 1058                 ccb_h->status = CAM_LUN_INVALID;
 1059                 xpt_done((union ccb *) csio);
 1060                 return;
 1061         }
 1062         if (csio->dxfer_len > BUFSIZ) {
 1063                 ccb_h->status = CAM_REQ_TOO_BIG;
 1064                 xpt_done((union ccb *) csio);
 1065                 return;
 1066         }
 1067         if (ccb_h->flags & (CAM_CDB_PHYS | CAM_SCATTER_VALID | CAM_DATA_PHYS)) {
 1068                 /* don't support these */
 1069                 ccb_h->status = CAM_REQ_INVALID;
 1070                 xpt_done((union ccb *) csio);
 1071                 return;
 1072         }
 1073         base = wp->addr;
 1074 
 1075         /*
 1076          * this check is mostly for debugging purposes,
 1077          * "can't happen" normally.
 1078          */
 1079         if(wp->want_wdsr) {
 1080                 DBG(DBX "wds%d: someone already waits for buffer\n", unit);
 1081                 smallog('b');
 1082                 n = xpt_freeze_simq(sim, /* count */ 1);
 1083                 smallog(''+n);
 1084                 ccb_h->status = CAM_REQUEUE_REQ;
 1085                 xpt_done((union ccb *) csio);
 1086                 return;
 1087         }
 1088 
 1089         r = wdsr_alloc(wp);
 1090         if (r == NULL) {
 1091                 device_printf(wp->dev, "no request slot available!\n");
 1092                 wp->want_wdsr = 1;
 1093                 n = xpt_freeze_simq(sim, /* count */ 1);
 1094                 smallog2('f', ''+n);
 1095                 ccb_h->status = CAM_REQUEUE_REQ;
 1096                 xpt_done((union ccb *) csio);
 1097                 return;
 1098         }
 1099 
 1100         ccb_h->ccb_wdsr = (void *) r;
 1101         r->ccb = (union ccb *) csio;
 1102 
 1103         switch (error = frag_alloc(wp, csio->dxfer_len, &r->buf, &r->mask)) {
 1104         case CAM_REQ_CMP:
 1105                 break;
 1106         case CAM_REQUEUE_REQ:
 1107                 DBG(DBX "wds%d: no data buffer available\n", unit);
 1108                 wp->want_wdsr = 1;
 1109                 n = xpt_freeze_simq(sim, /* count */ 1);
 1110                 smallog2('f', ''+n);
 1111                 wdsr_ccb_done(wp, r, r->ccb, CAM_REQUEUE_REQ);
 1112                 return;
 1113         default:
 1114                 DBG(DBX "wds%d: request is too big\n", unit);
 1115                 wdsr_ccb_done(wp, r, r->ccb, error);
 1116                 break;
 1117         }
 1118 
 1119         ccb_h->status |= CAM_SIM_QUEUED;
 1120         r->flags &= ~WR_DONE;
 1121 
 1122         scsi_ulto3b(WDSTOPHYS(wp, &r->cmd), wp->dx->ombs[r->ombn].addr);
 1123 
 1124         bzero(&r->cmd, sizeof r->cmd);
 1125         r->cmd.cmd = WDSX_SCSICMD;
 1126         r->cmd.targ = (ccb_h->target_id << 5) | ccb_h->target_lun;
 1127 
 1128         if (ccb_h->flags & CAM_CDB_POINTER)
 1129                 bcopy(csio->cdb_io.cdb_ptr, &r->cmd.scb,
 1130                       csio->cdb_len < 12 ? csio->cdb_len : 12);
 1131         else
 1132                 bcopy(csio->cdb_io.cdb_bytes, &r->cmd.scb,
 1133                       csio->cdb_len < 12 ? csio->cdb_len : 12);
 1134 
 1135         scsi_ulto3b(csio->dxfer_len, r->cmd.len);
 1136 
 1137         if (csio->dxfer_len > 0
 1138          && (ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_OUT) {
 1139                 /* we already rejected physical or scattered addresses */
 1140                 bcopy(csio->data_ptr, r->buf, csio->dxfer_len);
 1141         }
 1142         scsi_ulto3b(csio->dxfer_len ? WDSTOPHYS(wp, r->buf) : 0, r->cmd.data);
 1143 
 1144         if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN)
 1145                 r->cmd.write = 0x80;
 1146         else
 1147                 r->cmd.write = 0x00;
 1148 
 1149         scsi_ulto3b(0, r->cmd.next);
 1150 
 1151         outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
 1152 
 1153         c = WDSC_MSTART(r->ombn);
 1154 
 1155         if (wds_cmd(base, &c, sizeof c) != 0) {
 1156                 device_printf(wp->dev, "unable to start outgoing mbox\n");
 1157                 wp->dx->ombs[r->ombn].stat = 0;
 1158                 wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL);
 1159                 return;
 1160         }
 1161         DBG(DBX "wds%d: enqueued cmd 0x%x, r=%p\n", unit,
 1162             r->cmd.scb[0] & 0xFF, r);
 1163 
 1164         smallog3('+', ccb_h->target_id + '', ccb_h->target_lun + '');
 1165 }
 1166 
 1167 static void
 1168 wds_action(struct cam_sim * sim, union ccb * ccb)
 1169 {
 1170         int     unit = cam_sim_unit(sim);
 1171         int     s;
 1172 
 1173         DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code);
 1174         switch (ccb->ccb_h.func_code) {
 1175         case XPT_SCSI_IO:
 1176                 s = splcam();
 1177                 DBG(DBX "wds%d: SCSI IO entered\n", unit);
 1178                 wds_scsi_io(sim, &ccb->csio);
 1179                 DBG(DBX "wds%d: SCSI IO returned\n", unit);
 1180                 splx(s);
 1181                 break;
 1182         case XPT_RESET_BUS:
 1183                 /* how to do it right ? */
 1184                 printf("wds%d: reset\n", unit);
 1185                 ccb->ccb_h.status = CAM_REQ_CMP;
 1186                 xpt_done(ccb);
 1187                 break;
 1188         case XPT_ABORT:
 1189                 ccb->ccb_h.status = CAM_UA_ABORT;
 1190                 xpt_done(ccb);
 1191                 break;
 1192         case XPT_CALC_GEOMETRY:
 1193         {
 1194                 struct    ccb_calc_geometry *ccg;
 1195                 u_int32_t size_mb;
 1196                 u_int32_t secs_per_cylinder;
 1197 
 1198                 ccg = &ccb->ccg;
 1199                 size_mb = ccg->volume_size
 1200                         / ((1024L * 1024L) / ccg->block_size);
 1201 
 1202                 ccg->heads = 64;
 1203                 ccg->secs_per_track = 16;
 1204                 secs_per_cylinder = ccg->heads * ccg->secs_per_track;
 1205                 ccg->cylinders = ccg->volume_size / secs_per_cylinder;
 1206                 ccb->ccb_h.status = CAM_REQ_CMP;
 1207                 xpt_done(ccb);
 1208                 break;
 1209         }
 1210         case XPT_PATH_INQ:      /* Path routing inquiry */
 1211         {
 1212                 struct ccb_pathinq *cpi = &ccb->cpi;
 1213 
 1214                 cpi->version_num = 1;   /* XXX??? */
 1215                 cpi->hba_inquiry = 0;   /* nothing fancy */
 1216                 cpi->target_sprt = 0;
 1217                 cpi->hba_misc = 0;
 1218                 cpi->hba_eng_cnt = 0;
 1219                 cpi->max_target = 7;
 1220                 cpi->max_lun = 7;
 1221                 cpi->initiator_id = WDS_HBA_ID;
 1222                 cpi->hba_misc = 0;
 1223                 cpi->bus_id = cam_sim_bus(sim);
 1224                 cpi->base_transfer_speed = 3300;
 1225                 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
 1226                 strncpy(cpi->hba_vid, "WD/FDC", HBA_IDLEN);
 1227                 strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
 1228                 cpi->unit_number = cam_sim_unit(sim);
 1229                 cpi->ccb_h.status = CAM_REQ_CMP;
 1230                 xpt_done(ccb);
 1231                 break;
 1232         }
 1233         default:
 1234                 ccb->ccb_h.status = CAM_REQ_INVALID;
 1235                 xpt_done(ccb);
 1236                 break;
 1237         }
 1238 }
 1239 
 1240 static void
 1241 wds_poll(struct cam_sim * sim)
 1242 {
 1243         wds_intr((struct wds *)cam_sim_softc(sim));
 1244 }
 1245 
 1246 /* part of initialization done in probe() */
 1247 /* returns 0 if OK, ENXIO if bad */
 1248 
 1249 static int
 1250 wds_preinit(struct wds *wp)
 1251 {
 1252         int     base;
 1253         int     i;
 1254 
 1255         base = wp->addr;
 1256 
 1257         /*
 1258          * Sending a command causes the CMDRDY bit to clear.
 1259          */
 1260         outb(base + WDS_CMD, WDSC_NOOP);
 1261         if (inb(base + WDS_STAT) & WDS_RDY)
 1262                 return (ENXIO);
 1263 
 1264         /*
 1265          * the controller exists. reset and init.
 1266          */
 1267         outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
 1268         DELAY(30);
 1269         outb(base + WDS_HCR, 0);
 1270 
 1271         if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
 1272                 for (i = 0; i < 10; i++) {
 1273                         if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
 1274                                 break;
 1275                         DELAY(40000);
 1276                 }
 1277                 if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
 1278                         /* probe timeout */
 1279                         return (ENXIO);
 1280         }
 1281 
 1282         return (0);
 1283 }
 1284 
 1285 /* part of initialization done in attach() */
 1286 /* returns 0 if OK, 1 if bad */
 1287 
 1288 static int
 1289 wds_init(struct wds *wp)
 1290 {
 1291         struct  wds_setup init;
 1292         int     base;
 1293         int     i;
 1294         struct  wds_cmd  wc;
 1295 
 1296         base = wp->addr;
 1297 
 1298         outb(base + WDS_HCR, WDSH_DRQEN);
 1299 
 1300         isa_dmacascade(wp->drq);
 1301 
 1302         if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
 1303                 for (i = 0; i < 10; i++) {
 1304                         if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
 1305                                 break;
 1306                         DELAY(40000);
 1307                 }
 1308                 if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
 1309                         /* probe timeout */
 1310                         return (1);
 1311         }
 1312         bzero(&init, sizeof init);
 1313         init.cmd = WDSC_INIT;
 1314         init.scsi_id = WDS_HBA_ID;
 1315         init.buson_t = 24;
 1316         init.busoff_t = 48;
 1317         scsi_ulto3b(WDSTOPHYS(wp, &wp->dx->ombs), init.mbaddr); 
 1318         init.xx = 0;
 1319         init.nomb = WDS_NOMB;
 1320         init.nimb = WDS_NIMB;
 1321 
 1322         wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
 1323         if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) {
 1324                 device_printf(wp->dev, "wds_cmd init failed\n");
 1325                 return (1);
 1326         }
 1327         wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT);
 1328 
 1329         wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
 1330 
 1331         bzero(&wc, sizeof wc);
 1332         wc.cmd = WDSC_DISUNSOL;
 1333         if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) {
 1334                 device_printf(wp->dev, "wds_cmd init2 failed\n");
 1335                 return (1);
 1336         }
 1337         return (0);
 1338 }
 1339 
 1340 static int
 1341 wds_cmd(int base, u_int8_t * p, int l)
 1342 {
 1343         int     s = splcam();
 1344 
 1345         while (l--) {
 1346                 do {
 1347                         outb(base + WDS_CMD, *p);
 1348                         wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
 1349                 } while (inb(base + WDS_STAT) & WDS_REJ);
 1350                 p++;
 1351         }
 1352 
 1353         wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
 1354 
 1355         splx(s);
 1356 
 1357         return (0);
 1358 }
 1359 
 1360 static void
 1361 wds_wait(int reg, int mask, int val)
 1362 {
 1363         while ((inb(reg) & mask) != val)
 1364                 ;
 1365 }
 1366 
 1367 static struct wds_req *
 1368 cmdtovirt(struct wds *wp, u_int32_t phys)
 1369 {
 1370         char    *a;
 1371 
 1372         a = WDSTOVIRT(wp, (uintptr_t)phys);
 1373         if( a < (char *)&wp->dx->req[0] || a>= (char *)&wp->dx->req[MAXSIMUL]) {
 1374                 device_printf(wp->dev, "weird phys address 0x%x\n", phys);
 1375                 return (NULL);
 1376         }
 1377         a -= (int)offsetof(struct wds_req, cmd); /* convert cmd to request */
 1378         return ((struct wds_req *)a);
 1379 }
 1380 
 1381 /* for debugging, print out all the data about the status of devices */
 1382 void
 1383 wds_print(void)
 1384 {
 1385         int     unit;
 1386         int     i;
 1387         struct  wds_req *r;
 1388         struct  wds     *wp;
 1389 
 1390         for (unit = 0; unit < devclass_get_maxunit(wds_devclass); unit++) {
 1391                 wp = (struct wds *) devclass_get_device(wds_devclass, unit);
 1392                 if (wp == NULL)
 1393                         continue;
 1394                 printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n",
 1395                        unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff,
 1396                        (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no",
 1397                        inb(wp->addr + WDS_IRQSTAT) & 0xff);
 1398                 for (i = 0; i < MAXSIMUL; i++) {
 1399                         r = &wp->dx->req[i];
 1400                         if( wp->wdsr_free & (1 << r->id) ) {
 1401                                 printf("req=%d flg=0x%x ombn=%d ombstat=%d "
 1402                                        "mask=0x%x targ=%d lun=%d cmd=0x%x\n",
 1403                                        i, r->flags, r->ombn,
 1404                                        wp->dx->ombs[r->ombn].stat,
 1405                                        r->mask, r->cmd.targ >> 5,
 1406                                        r->cmd.targ & 7, r->cmd.scb[0]);
 1407                         }
 1408                 }
 1409         }
 1410 }
 1411 
 1412 #if WDS_DEBUG == 2
 1413 /* create circular log buffer */
 1414 static char    *
 1415 wds_nextlog(void)
 1416 {
 1417         int     n = logwrite;
 1418 
 1419         if (++logwrite >= NLOGLINES)
 1420                 logwrite = 0;
 1421         if (logread == logwrite)
 1422                 if (++logread >= NLOGLINES)
 1423                         logread = 0;
 1424         return (wds_log[n]);
 1425 }
 1426 
 1427 void
 1428 wds_printlog(void)
 1429 {
 1430         /* print the circular buffer */
 1431         int     i;
 1432 
 1433         for (i = logread; i != logwrite;) {
 1434                 printf("%s", wds_log[i]);
 1435                 if (i == NLOGLINES)
 1436                         i = 0;
 1437                 else
 1438                         i++;
 1439         }
 1440 }
 1441 #endif /* WDS_DEBUG */

Cache object: bbb9fe78e5b38cde5ee4aafe0cba0f0b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.