2 * Copyright (c) 2000 Michael Smith
3 * Copyright (c) 2000 BSDi
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * Copyright (c) 2002 Eric Moore
28 * Copyright (c) 2002 LSI Logic Corporation
29 * All rights reserved.
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 3. The party using or redistributing the source code and binary forms
40 * agrees to the disclaimer below and the terms and conditions set forth
43 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * $FreeBSD: src/sys/dev/amr/amr_cam.c,v 1.1.2.3 2002/11/11 13:19:10 emoore Exp $
56 * $DragonFly: src/sys/dev/raid/amr/amr_cam.c,v 1.11 2008/05/18 20:30:23 pavalos Exp $
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/malloc.h>
62 #include <sys/kernel.h>
64 #include "amr_compat.h"
67 #include <sys/devicestat.h>
71 #include <bus/cam/cam.h>
72 #include <bus/cam/cam_ccb.h>
73 #include <bus/cam/cam_sim.h>
74 #include <bus/cam/cam_xpt.h>
75 #include <bus/cam/cam_xpt_sim.h>
76 #include <bus/cam/cam_debug.h>
77 #include <bus/cam/scsi/scsi_all.h>
78 #include <bus/cam/scsi/scsi_message.h>
83 static void amr_cam_action(struct cam_sim
*sim
, union ccb
*ccb
);
84 static void amr_cam_poll(struct cam_sim
*sim
);
85 static void amr_cam_complete(struct amr_command
*ac
);
86 static void amr_cam_complete_extcdb(struct amr_command
*ac
);
89 /********************************************************************************
90 * Enqueue/dequeue functions
93 amr_enqueue_ccb(struct amr_softc
*sc
, union ccb
*ccb
)
96 TAILQ_INSERT_TAIL(&sc
->amr_cam_ccbq
, &ccb
->ccb_h
, sim_links
.tqe
);
101 amr_requeue_ccb(struct amr_softc
*sc
, union ccb
*ccb
)
104 TAILQ_INSERT_HEAD(&sc
->amr_cam_ccbq
, &ccb
->ccb_h
, sim_links
.tqe
);
108 static __inline
union ccb
*
109 amr_dequeue_ccb(struct amr_softc
*sc
)
114 if ((ccb
= (union ccb
*)TAILQ_FIRST(&sc
->amr_cam_ccbq
)) != NULL
)
115 TAILQ_REMOVE(&sc
->amr_cam_ccbq
, &ccb
->ccb_h
, sim_links
.tqe
);
120 /********************************************************************************
121 * Attach our 'real' SCSI channels to CAM
124 amr_cam_attach(struct amr_softc
*sc
)
126 struct cam_devq
*devq
;
129 /* initialise the ccb queue */
130 TAILQ_INIT(&sc
->amr_cam_ccbq
);
133 * Allocate a devq for all our channels combined. This should
134 * allow for the maximum number of SCSI commands we will accept
137 if ((devq
= cam_simq_alloc(AMR_MAX_SCSI_CMDS
)) == NULL
)
141 * Iterate over our channels, registering them with CAM
143 for (chn
= 0; chn
< sc
->amr_maxchan
; chn
++) {
146 if ((sc
->amr_cam_sim
[chn
] = cam_sim_alloc(amr_cam_action
,
150 device_get_unit(sc
->amr_dev
),
155 device_printf(sc
->amr_dev
, "CAM SIM attach failed\n");
159 /* register the bus ID so we can get it later */
160 if (xpt_bus_register(sc
->amr_cam_sim
[chn
], chn
)) {
161 device_printf(sc
->amr_dev
, "CAM XPT bus registration failed\n");
165 cam_simq_release(devq
);
167 * XXX we should scan the config and work out which devices are actually
173 /********************************************************************************
174 * Disconnect ourselves from CAM
177 amr_cam_detach(struct amr_softc
*sc
)
182 * If a sim was allocated for a channel, free it
184 for (chn
= 0; chn
< sc
->amr_maxchan
; chn
++) {
185 if (sc
->amr_cam_sim
[chn
] != NULL
) {
186 xpt_bus_deregister(cam_sim_path(sc
->amr_cam_sim
[chn
]));
187 cam_sim_free(sc
->amr_cam_sim
[chn
]);
192 /********************************************************************************
193 ********************************************************************************
194 CAM passthrough interface
195 ********************************************************************************
196 ********************************************************************************/
198 /********************************************************************************
199 * Handle a request for action from CAM
202 amr_cam_action(struct cam_sim
*sim
, union ccb
*ccb
)
204 struct amr_softc
*sc
= cam_sim_softc(sim
);
206 switch(ccb
->ccb_h
.func_code
) {
209 * Perform SCSI I/O to a physical device.
213 struct ccb_hdr
*ccbh
= &ccb
->ccb_h
;
214 struct ccb_scsiio
*csio
= &ccb
->csio
;
216 /* Validate the CCB */
217 ccbh
->status
= CAM_REQ_INPROG
;
219 /* check the CDB length */
220 if (csio
->cdb_len
> AMR_MAX_EXTCDB_LEN
)
221 ccbh
->status
= CAM_REQ_CMP_ERR
;
223 if ((csio
->cdb_len
> AMR_MAX_CDB_LEN
) && (sc
->support_ext_cdb
== 0 ))
224 ccbh
->status
= CAM_REQ_CMP_ERR
;
226 /* check that the CDB pointer is not to a physical address */
227 if ((ccbh
->flags
& CAM_CDB_POINTER
) && (ccbh
->flags
& CAM_CDB_PHYS
))
228 ccbh
->status
= CAM_REQ_CMP_ERR
;
230 /* if there is data transfer, it must be to/from a virtual address */
231 if ((ccbh
->flags
& CAM_DIR_MASK
) != CAM_DIR_NONE
) {
232 if (ccbh
->flags
& CAM_DATA_PHYS
) /* we can't map it */
233 ccbh
->status
= CAM_REQ_CMP_ERR
;
234 if (ccbh
->flags
& CAM_SCATTER_VALID
) /* we want to do the s/g setup */
235 ccbh
->status
= CAM_REQ_CMP_ERR
;
239 * If the command is to a LUN other than 0, fail it.
240 * This is probably incorrect, but during testing the firmware did not
241 * seem to respect the LUN field, and thus devices appear echoed.
243 if (csio
->ccb_h
.target_lun
!= 0)
244 ccbh
->status
= CAM_REQ_CMP_ERR
;
246 /* if we're happy with the request, queue it for attention */
247 if (ccbh
->status
== CAM_REQ_INPROG
) {
249 /* save the channel number in the ccb */
250 csio
->ccb_h
.sim_priv
.entries
[0].field
= cam_sim_bus(sim
);
252 amr_enqueue_ccb(sc
, ccb
);
259 case XPT_CALC_GEOMETRY
:
261 struct ccb_calc_geometry
*ccg
= &ccb
->ccg
;
262 u_int32_t size_in_mb
;
263 u_int32_t secs_per_cylinder
;
265 size_in_mb
= ccg
->volume_size
/ ((1024L * 1024L) / ccg
->block_size
);
267 if (size_in_mb
> 1024) {
269 ccg
->secs_per_track
= 63;
272 ccg
->secs_per_track
= 32;
274 secs_per_cylinder
= ccg
->heads
* ccg
->secs_per_track
;
275 ccg
->cylinders
= ccg
->volume_size
/ secs_per_cylinder
;
276 ccb
->ccb_h
.status
= CAM_REQ_CMP
;
281 * Return path stats. Some of these should probably be
286 struct ccb_pathinq
*cpi
= & ccb
->cpi
;
288 debug(3, "XPT_PATH_INQ");
289 cpi
->version_num
= 1; /* XXX??? */
290 cpi
->hba_inquiry
= PI_SDTR_ABLE
|PI_TAG_ABLE
|PI_WIDE_16
;
291 cpi
->target_sprt
= 0;
292 cpi
->hba_misc
= PIM_NOBUSRESET
;
293 cpi
->hba_eng_cnt
= 0;
294 cpi
->max_target
= AMR_MAX_TARGETS
;
295 cpi
->max_lun
= 0 /* AMR_MAX_LUNS*/;
296 cpi
->initiator_id
= 7; /* XXX variable? */
297 strncpy(cpi
->sim_vid
, "FreeBSD", SIM_IDLEN
);
298 strncpy(cpi
->hba_vid
, "LSI", HBA_IDLEN
);
299 strncpy(cpi
->dev_name
, cam_sim_name(sim
), DEV_IDLEN
);
300 cpi
->unit_number
= cam_sim_unit(sim
);
301 cpi
->bus_id
= cam_sim_bus(sim
);
302 cpi
->base_transfer_speed
= 132 * 1024; /* XXX get from controller? */
303 cpi
->transport
= XPORT_SPI
;
304 cpi
->transport_version
= 2;
305 cpi
->protocol
= PROTO_SCSI
;
306 cpi
->protocol_version
= SCSI_REV_2
;
307 cpi
->ccb_h
.status
= CAM_REQ_CMP
;
314 struct ccb_pathinq
*cpi
= & ccb
->cpi
;
316 debug(1, "XPT_RESET_BUS");
317 cpi
->ccb_h
.status
= CAM_REQ_CMP
;
323 debug(1, "XPT_RESET_DEV");
324 ccb
->ccb_h
.status
= CAM_REQ_CMP
;
328 case XPT_GET_TRAN_SETTINGS
:
330 struct ccb_trans_settings
*cts
= &(ccb
->cts
);
332 debug(3, "XPT_GET_TRAN_SETTINGS");
334 struct ccb_trans_settings_scsi
*scsi
= &cts
->proto_specific
.scsi
;
335 struct ccb_trans_settings_spi
*spi
= &cts
->xport_specific
.spi
;
337 cts
->protocol
= PROTO_SCSI
;
338 cts
->protocol_version
= SCSI_REV_2
;
339 cts
->transport
= XPORT_SPI
;
340 cts
->transport_version
= 2;
342 if (cts
->type
== CTS_TYPE_USER_SETTINGS
) {
343 ccb
->ccb_h
.status
= CAM_FUNC_NOTAVAIL
;
347 spi
->flags
= CTS_SPI_FLAGS_DISC_ENB
;
348 spi
->bus_width
= MSG_EXT_WDTR_BUS_32_BIT
;
349 spi
->sync_period
= 6; /* 40MHz how wide is this bus? */
350 spi
->sync_offset
= 31; /* How to extract this from board? */
352 spi
->valid
= CTS_SPI_VALID_SYNC_RATE
353 | CTS_SPI_VALID_SYNC_OFFSET
354 | CTS_SPI_VALID_BUS_WIDTH
355 | CTS_SPI_VALID_DISC
;
356 scsi
->valid
= CTS_SCSI_VALID_TQ
;
357 ccb
->ccb_h
.status
= CAM_REQ_CMP
;
361 case XPT_SET_TRAN_SETTINGS
:
362 debug(3, "XPT_SET_TRAN_SETTINGS");
363 ccb
->ccb_h
.status
= CAM_FUNC_NOTAVAIL
;
368 * Reject anything else as unsupported.
371 /* we can't do this */
372 ccb
->ccb_h
.status
= CAM_REQ_INVALID
;
378 /********************************************************************************
379 * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI command.
382 amr_cam_command(struct amr_softc
*sc
, struct amr_command
**acp
)
384 struct amr_command
*ac
;
385 struct amr_passthrough
*ap
;
386 struct amr_ext_passthrough
*aep
;
387 struct ccb_scsiio
*csio
;
388 int bus
, target
, error
;
395 /* check to see if there is a ccb for us to work with */
396 if ((csio
= (struct ccb_scsiio
*)amr_dequeue_ccb(sc
)) == NULL
)
399 /* get bus/target, XXX validate against protected devices? */
400 bus
= csio
->ccb_h
.sim_priv
.entries
[0].field
;
401 target
= csio
->ccb_h
.target_id
;
404 * Build a passthrough command.
407 /* construct passthrough */
408 if (sc
->support_ext_cdb
) {
409 aep
= kmalloc(sizeof(*aep
), M_DEVBUF
, M_INTWAIT
| M_ZERO
);
412 aep
->ap_request_sense_length
= 14;
413 aep
->ap_islogical
= 0;
414 aep
->ap_channel
= bus
;
415 aep
->ap_scsi_id
= target
;
416 aep
->ap_logical_drive_no
= csio
->ccb_h
.target_lun
;
417 aep
->ap_cdb_length
= csio
->cdb_len
;
418 aep
->ap_data_transfer_length
= csio
->dxfer_len
;
419 if (csio
->ccb_h
.flags
& CAM_CDB_POINTER
) {
420 bcopy(csio
->cdb_io
.cdb_ptr
, aep
->ap_cdb
, csio
->cdb_len
);
422 bcopy(csio
->cdb_io
.cdb_bytes
, aep
->ap_cdb
, csio
->cdb_len
);
424 /* we leave the data s/g list and s/g count to the map routine later */
426 debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep
->ap_cdb
[0], aep
->ap_cdb_length
, csio
->dxfer_len
,
427 aep
->ap_channel
, aep
->ap_scsi_id
, aep
->ap_logical_drive_no
);
430 ap
= kmalloc(sizeof(*ap
), M_DEVBUF
, M_INTWAIT
| M_ZERO
);
433 ap
->ap_request_sense_length
= 14;
434 ap
->ap_islogical
= 0;
435 ap
->ap_channel
= bus
;
436 ap
->ap_scsi_id
= target
;
437 ap
->ap_logical_drive_no
= csio
->ccb_h
.target_lun
;
438 ap
->ap_cdb_length
= csio
->cdb_len
;
439 ap
->ap_data_transfer_length
= csio
->dxfer_len
;
440 if (csio
->ccb_h
.flags
& CAM_CDB_POINTER
) {
441 bcopy(csio
->cdb_io
.cdb_ptr
, ap
->ap_cdb
, csio
->cdb_len
);
443 bcopy(csio
->cdb_io
.cdb_bytes
, ap
->ap_cdb
, csio
->cdb_len
);
445 /* we leave the data s/g list and s/g count to the map routine later */
447 debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap
->ap_cdb
[0], ap
->ap_cdb_length
, csio
->dxfer_len
,
448 ap
->ap_channel
, ap
->ap_scsi_id
, ap
->ap_logical_drive_no
);
451 /* construct command */
452 if ((ac
= amr_alloccmd(sc
)) == NULL
) {
457 ac
->ac_flags
|= AMR_CMD_DATAOUT
;
459 ac
->ac_ccb_data
= csio
->data_ptr
;
460 ac
->ac_ccb_length
= csio
->dxfer_len
;
461 if ((csio
->ccb_h
.flags
& CAM_DIR_MASK
) == CAM_DIR_IN
)
462 ac
->ac_flags
|= AMR_CMD_CCB_DATAIN
;
463 if ((csio
->ccb_h
.flags
& CAM_DIR_MASK
) == CAM_DIR_OUT
)
464 ac
->ac_flags
|= AMR_CMD_CCB_DATAOUT
;
466 ac
->ac_private
= csio
;
467 if ( sc
->support_ext_cdb
) {
469 ac
->ac_length
= sizeof(*aep
);
470 ac
->ac_complete
= amr_cam_complete_extcdb
;
471 ac
->ac_mailbox
.mb_command
= AMR_CMD_EXTPASS
;
474 ac
->ac_length
= sizeof(*ap
);
475 ac
->ac_complete
= amr_cam_complete
;
476 ac
->ac_mailbox
.mb_command
= AMR_CMD_PASS
;
486 kfree(aep
, M_DEVBUF
);
487 if (csio
!= NULL
) /* put it back and try again later */
488 amr_requeue_ccb(sc
, (union ccb
*)csio
);
494 /********************************************************************************
495 * Check for interrupt status
498 amr_cam_poll(struct cam_sim
*sim
)
500 amr_done(cam_sim_softc(sim
));
503 /********************************************************************************
504 * Handle completion of a command submitted via CAM.
507 amr_cam_complete(struct amr_command
*ac
)
509 struct amr_passthrough
*ap
= (struct amr_passthrough
*)ac
->ac_data
;
510 struct ccb_scsiio
*csio
= (struct ccb_scsiio
*)ac
->ac_private
;
511 struct scsi_inquiry_data
*inq
= (struct scsi_inquiry_data
*)csio
->data_ptr
;
513 /* XXX note that we're ignoring ac->ac_status - good idea? */
515 debug(1, "status 0x%x scsi_status 0x%x", ac
->ac_status
, ap
->ap_scsi_status
);
518 * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
520 * If the configuration provides a mechanism to mark a disk a "not managed", we
521 * could add handling for that to allow disks to be selectively visible.
524 if ((ap
->ap_cdb
[0] == INQUIRY
) && (SID_TYPE(inq
) == T_DIRECT
)) {
525 bzero(csio
->data_ptr
, csio
->dxfer_len
);
526 if (ap
->ap_scsi_status
== 0xf0) {
527 csio
->ccb_h
.status
= CAM_SCSI_STATUS_ERROR
;
529 csio
->ccb_h
.status
= CAM_DEV_NOT_THERE
;
533 /* handle passthrough SCSI status */
534 switch(ap
->ap_scsi_status
) {
535 case 0: /* completed OK */
536 csio
->ccb_h
.status
= CAM_REQ_CMP
;
540 csio
->ccb_h
.status
= CAM_SCSI_STATUS_ERROR
;
541 csio
->scsi_status
= SCSI_STATUS_CHECK_COND
;
542 bcopy(ap
->ap_request_sense_area
, &csio
->sense_data
, AMR_MAX_REQ_SENSE_LEN
);
543 csio
->sense_len
= AMR_MAX_REQ_SENSE_LEN
;
544 csio
->ccb_h
.status
|= CAM_AUTOSNS_VALID
;
548 csio
->ccb_h
.status
= CAM_SCSI_BUSY
;
554 csio
->ccb_h
.status
= CAM_REQ_CMP_ERR
;
559 if ((csio
->ccb_h
.flags
& CAM_DIR_MASK
) != CAM_DIR_NONE
)
560 debug(2, "%*D\n", imin(csio
->dxfer_len
, 16), csio
->data_ptr
, " ");
561 xpt_done((union ccb
*)csio
);
565 /********************************************************************************
566 * Handle completion of a command submitted via CAM.
567 * Completion for extended cdb
570 amr_cam_complete_extcdb(struct amr_command
*ac
)
572 struct amr_ext_passthrough
*aep
= (struct amr_ext_passthrough
*)ac
->ac_data
;
573 struct ccb_scsiio
*csio
= (struct ccb_scsiio
*)ac
->ac_private
;
574 struct scsi_inquiry_data
*inq
= (struct scsi_inquiry_data
*)csio
->data_ptr
;
576 /* XXX note that we're ignoring ac->ac_status - good idea? */
578 debug(1, "status 0x%x scsi_status 0x%x", ac
->ac_status
, aep
->ap_scsi_status
);
581 * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
583 * If the configuration provides a mechanism to mark a disk a "not managed", we
584 * could add handling for that to allow disks to be selectively visible.
587 if ((aep
->ap_cdb
[0] == INQUIRY
) && (SID_TYPE(inq
) == T_DIRECT
)) {
588 bzero(csio
->data_ptr
, csio
->dxfer_len
);
589 if (aep
->ap_scsi_status
== 0xf0) {
590 csio
->ccb_h
.status
= CAM_SCSI_STATUS_ERROR
;
592 csio
->ccb_h
.status
= CAM_DEV_NOT_THERE
;
596 /* handle passthrough SCSI status */
597 switch(aep
->ap_scsi_status
) {
598 case 0: /* completed OK */
599 csio
->ccb_h
.status
= CAM_REQ_CMP
;
603 csio
->ccb_h
.status
= CAM_SCSI_STATUS_ERROR
;
604 csio
->scsi_status
= SCSI_STATUS_CHECK_COND
;
605 bcopy(aep
->ap_request_sense_area
, &csio
->sense_data
, AMR_MAX_REQ_SENSE_LEN
);
606 csio
->sense_len
= AMR_MAX_REQ_SENSE_LEN
;
607 csio
->ccb_h
.status
|= CAM_AUTOSNS_VALID
;
611 csio
->ccb_h
.status
= CAM_SCSI_BUSY
;
617 csio
->ccb_h
.status
= CAM_REQ_CMP_ERR
;
621 kfree(aep
, M_DEVBUF
);
622 if ((csio
->ccb_h
.flags
& CAM_DIR_MASK
) != CAM_DIR_NONE
)
623 debug(2, "%*D\n", imin(csio
->dxfer_len
, 16), csio
->data_ptr
, " ");
624 xpt_done((union ccb
*)csio
);