1226026Sdelphij/* 2226026Sdelphij * Copyright (c) 2010, LSI Corp. 3226026Sdelphij * All rights reserved. 4226026Sdelphij * Author : Manjunath Ranganathaiah 5226026Sdelphij * Support: freebsdraid@lsi.com 6226026Sdelphij * 7226026Sdelphij * Redistribution and use in source and binary forms, with or without 8226026Sdelphij * modification, are permitted provided that the following conditions 9226026Sdelphij * are met: 10226026Sdelphij * 11226026Sdelphij * 1. Redistributions of source code must retain the above copyright 12226026Sdelphij * notice, this list of conditions and the following disclaimer. 13226026Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 14226026Sdelphij * notice, this list of conditions and the following disclaimer in 15226026Sdelphij * the documentation and/or other materials provided with the 16226026Sdelphij * distribution. 17226026Sdelphij * 3. Neither the name of the <ORGANIZATION> nor the names of its 18226026Sdelphij * contributors may be used to endorse or promote products derived 19226026Sdelphij * from this software without specific prior written permission. 20226026Sdelphij * 21226026Sdelphij * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22226026Sdelphij * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23226026Sdelphij * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24226026Sdelphij * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25226026Sdelphij * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26226026Sdelphij * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27226026Sdelphij * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28226026Sdelphij * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29226026Sdelphij * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30226026Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31226026Sdelphij * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32226026Sdelphij * POSSIBILITY OF SUCH DAMAGE. 33226026Sdelphij * 34226026Sdelphij * $FreeBSD$ 35226026Sdelphij */ 36226026Sdelphij 37226026Sdelphij#include <dev/tws/tws.h> 38226026Sdelphij#include <dev/tws/tws_services.h> 39226026Sdelphij#include <dev/tws/tws_hdm.h> 40226026Sdelphij#include <dev/tws/tws_user.h> 41226026Sdelphij 42226026Sdelphij 43226026Sdelphijint tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags, 44226026Sdelphij d_thread_t *proc); 45226026Sdelphijvoid tws_passthru_complete(struct tws_request *req); 46226026Sdelphijextern void tws_circular_aenq_insert(struct tws_softc *sc, 47226026Sdelphij struct tws_circular_q *cq, struct tws_event_packet *aen); 48226026Sdelphij 49226026Sdelphij 50226026Sdelphijstatic int tws_passthru(struct tws_softc *sc, void *buf); 51226026Sdelphijstatic int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf); 52226026Sdelphij 53226026Sdelphijextern int tws_bus_scan(struct tws_softc *sc); 54226026Sdelphijextern struct tws_request *tws_get_request(struct tws_softc *sc, 55226026Sdelphij u_int16_t type); 56226026Sdelphijextern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); 57226026Sdelphijextern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); 58226026Sdelphijextern uint8_t tws_get_state(struct tws_softc *sc); 59226026Sdelphijextern void tws_timeout(void *arg); 60226026Sdelphij 61226026Sdelphijint 62226026Sdelphijtws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags, 63226026Sdelphij d_thread_t *proc) 64226026Sdelphij{ 65226026Sdelphij struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1); 66226026Sdelphij int error; 67226026Sdelphij 68226026Sdelphij TWS_TRACE_DEBUG(sc, "entry", sc, cmd); 69226026Sdelphij sc->stats.ioctls++; 70226026Sdelphij switch(cmd) { 71226026Sdelphij case TWS_IOCTL_FIRMWARE_PASS_THROUGH : 72226026Sdelphij error = tws_passthru(sc, (void *)buf); 73226026Sdelphij break; 74226026Sdelphij case TWS_IOCTL_SCAN_BUS : 75226026Sdelphij TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0); 76226026Sdelphij mtx_lock(&sc->sim_lock); 77226026Sdelphij error = tws_bus_scan(sc); 78226026Sdelphij mtx_unlock(&sc->sim_lock); 79226026Sdelphij break; 80226026Sdelphij default : 81226026Sdelphij TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); 82226026Sdelphij error = tws_ioctl_aen(sc, cmd, (void *)buf); 83226026Sdelphij break; 84226026Sdelphij 85226026Sdelphij } 86226026Sdelphij return(error); 87226026Sdelphij} 88226026Sdelphij 89226026Sdelphijstatic int 90226026Sdelphijtws_passthru(struct tws_softc *sc, void *buf) 91226026Sdelphij{ 92226026Sdelphij struct tws_request *req; 93226026Sdelphij struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; 94226026Sdelphij int error; 95226026Sdelphij u_int16_t lun4; 96226026Sdelphij 97241762Sdelphij 98226026Sdelphij if ( tws_get_state(sc) != TWS_ONLINE) { 99226026Sdelphij return(EBUSY); 100226026Sdelphij } 101226026Sdelphij 102241762Sdelphij //============================================================================================== 103241762Sdelphij // Get a command 104241762Sdelphij // 105226026Sdelphij do { 106226026Sdelphij req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU); 107226026Sdelphij if ( !req ) { 108263126Sdelphij error = tsleep(sc, 0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz); 109226026Sdelphij if ( error == EWOULDBLOCK ) { 110226026Sdelphij return(ETIMEDOUT); 111226026Sdelphij } 112226026Sdelphij } else { 113241762Sdelphij // Make sure we are still ready for new commands... 114241762Sdelphij if ( tws_get_state(sc) != TWS_ONLINE) { 115241762Sdelphij return(EBUSY); 116241762Sdelphij } 117226026Sdelphij break; 118226026Sdelphij } 119241762Sdelphij } while(1); 120226026Sdelphij 121241762Sdelphij req->length = (ubuf->driver_pkt.buffer_length + 511) & ~511; 122226026Sdelphij TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); 123226026Sdelphij if ( req->length ) { 124241762Sdelphij req->data = sc->ioctl_data_mem; 125241762Sdelphij req->dma_map = sc->ioctl_data_map; 126241762Sdelphij 127241762Sdelphij //========================================================================================== 128241762Sdelphij // Copy data in from user space 129241762Sdelphij // 130226026Sdelphij error = copyin(ubuf->pdata, req->data, req->length); 131226026Sdelphij } 132241762Sdelphij 133241762Sdelphij //============================================================================================== 134241762Sdelphij // Set command fields 135241762Sdelphij // 136226026Sdelphij req->flags = TWS_DIR_IN | TWS_DIR_OUT; 137226026Sdelphij req->cb = tws_passthru_complete; 138226026Sdelphij 139226026Sdelphij memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 140226026Sdelphij sizeof(struct tws_command_apache)); 141226026Sdelphij 142226026Sdelphij if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 143226026Sdelphij TWS_FW_CMD_EXECUTE_SCSI ) { 144226026Sdelphij lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; 145226026Sdelphij req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; 146226026Sdelphij } else { 147226026Sdelphij req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; 148226026Sdelphij } 149226026Sdelphij 150241762Sdelphij //============================================================================================== 151241762Sdelphij // Send command to controller 152241762Sdelphij // 153226026Sdelphij error = tws_map_request(sc, req); 154226026Sdelphij if (error) { 155226026Sdelphij ubuf->driver_pkt.os_status = error; 156241762Sdelphij goto out_data; 157226026Sdelphij } 158226026Sdelphij 159241762Sdelphij if ( req->state == TWS_REQ_STATE_COMPLETE ) { 160241762Sdelphij ubuf->driver_pkt.os_status = req->error_code; 161241762Sdelphij goto out_unmap; 162241762Sdelphij } 163241762Sdelphij 164226026Sdelphij mtx_lock(&sc->gen_lock); 165226026Sdelphij error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz); 166226026Sdelphij mtx_unlock(&sc->gen_lock); 167226026Sdelphij if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) { 168226026Sdelphij TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id); 169226026Sdelphij tws_timeout((void*) req); 170226026Sdelphij } 171226026Sdelphij 172241762Sdelphijout_unmap: 173226026Sdelphij if ( req->error_code == TWS_REQ_RET_RESET ) { 174226026Sdelphij error = EBUSY; 175226026Sdelphij req->error_code = EBUSY; 176226026Sdelphij TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id); 177226026Sdelphij } 178226026Sdelphij 179226026Sdelphij tws_unmap_request(sc, req); 180226026Sdelphij 181241762Sdelphij //============================================================================================== 182241762Sdelphij // Return command status to user space 183241762Sdelphij // 184226026Sdelphij memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); 185226026Sdelphij memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 186241762Sdelphij 187241762Sdelphijout_data: 188241762Sdelphij if ( req->length ) { 189241762Sdelphij //========================================================================================== 190241762Sdelphij // Copy data out to user space 191241762Sdelphij // 192241762Sdelphij if ( !error ) 193241762Sdelphij error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length); 194226026Sdelphij } 195226026Sdelphij 196226026Sdelphij if ( error ) 197226026Sdelphij TWS_TRACE_DEBUG(sc, "errored", error, 0); 198226026Sdelphij 199226026Sdelphij if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) 200226026Sdelphij ubuf->driver_pkt.os_status = error; 201226026Sdelphij 202241762Sdelphij //============================================================================================== 203241762Sdelphij // Free command 204241762Sdelphij // 205226026Sdelphij req->state = TWS_REQ_STATE_FREE; 206226026Sdelphij 207263126Sdelphij wakeup_one(sc); 208241762Sdelphij 209226026Sdelphij return(error); 210226026Sdelphij} 211226026Sdelphij 212226026Sdelphijvoid 213226026Sdelphijtws_passthru_complete(struct tws_request *req) 214226026Sdelphij{ 215226026Sdelphij req->state = TWS_REQ_STATE_COMPLETE; 216226026Sdelphij wakeup_one(req); 217226026Sdelphij 218226026Sdelphij} 219226026Sdelphij 220226026Sdelphijstatic void 221226026Sdelphijtws_retrive_aen(struct tws_softc *sc, u_long cmd, 222226026Sdelphij struct tws_ioctl_packet *ubuf) 223226026Sdelphij{ 224226026Sdelphij u_int16_t index=0; 225226026Sdelphij struct tws_event_packet eventp, *qp; 226226026Sdelphij 227226026Sdelphij if ( sc->aen_q.head == sc->aen_q.tail ) { 228226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 229226026Sdelphij return; 230226026Sdelphij } 231226026Sdelphij 232226026Sdelphij ubuf->driver_pkt.status = 0; 233226026Sdelphij 234226026Sdelphij /* 235226026Sdelphij * once this flag is set cli will not display alarms 236226026Sdelphij * needs a revisit from tools? 237226026Sdelphij */ 238226026Sdelphij if ( sc->aen_q.overflow ) { 239226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; 240226026Sdelphij sc->aen_q.overflow = 0; /* reset */ 241226026Sdelphij } 242226026Sdelphij 243226026Sdelphij qp = (struct tws_event_packet *)sc->aen_q.q; 244226026Sdelphij 245226026Sdelphij switch (cmd) { 246226026Sdelphij case TWS_IOCTL_GET_FIRST_EVENT : 247226026Sdelphij index = sc->aen_q.head; 248226026Sdelphij break; 249226026Sdelphij case TWS_IOCTL_GET_LAST_EVENT : 250226026Sdelphij /* index = tail-1 */ 251226026Sdelphij index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; 252226026Sdelphij break; 253226026Sdelphij case TWS_IOCTL_GET_NEXT_EVENT : 254226026Sdelphij memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 255226026Sdelphij index = sc->aen_q.head; 256226026Sdelphij do { 257226026Sdelphij if ( qp[index].sequence_id == 258226026Sdelphij (eventp.sequence_id + 1) ) 259226026Sdelphij break; 260226026Sdelphij index = (index+1) % sc->aen_q.depth; 261226026Sdelphij }while ( index != sc->aen_q.tail ); 262226026Sdelphij if ( index == sc->aen_q.tail ) { 263226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 264226026Sdelphij return; 265226026Sdelphij } 266226026Sdelphij break; 267226026Sdelphij case TWS_IOCTL_GET_PREVIOUS_EVENT : 268226026Sdelphij memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 269226026Sdelphij index = sc->aen_q.head; 270226026Sdelphij do { 271226026Sdelphij if ( qp[index].sequence_id == 272226026Sdelphij (eventp.sequence_id - 1) ) 273226026Sdelphij break; 274226026Sdelphij index = (index+1) % sc->aen_q.depth; 275226026Sdelphij }while ( index != sc->aen_q.tail ); 276226026Sdelphij if ( index == sc->aen_q.tail ) { 277226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 278226026Sdelphij return; 279226026Sdelphij } 280226026Sdelphij break; 281226026Sdelphij default : 282226026Sdelphij TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); 283226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 284226026Sdelphij return; 285226026Sdelphij } 286226026Sdelphij 287226026Sdelphij memcpy(ubuf->data_buf, &qp[index], 288226026Sdelphij sizeof(struct tws_event_packet)); 289226026Sdelphij qp[index].retrieved = TWS_AEN_RETRIEVED; 290226026Sdelphij 291226026Sdelphij return; 292226026Sdelphij 293226026Sdelphij} 294226026Sdelphij 295226026Sdelphijstatic int 296226026Sdelphijtws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) 297226026Sdelphij{ 298226026Sdelphij 299226026Sdelphij struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; 300226026Sdelphij struct tws_compatibility_packet cpkt; 301226026Sdelphij struct tws_lock_packet lpkt; 302226026Sdelphij time_t ctime; 303226026Sdelphij 304226026Sdelphij mtx_lock(&sc->gen_lock); 305226026Sdelphij ubuf->driver_pkt.status = 0; 306226026Sdelphij switch(cmd) { 307226026Sdelphij case TWS_IOCTL_GET_FIRST_EVENT : 308226026Sdelphij case TWS_IOCTL_GET_LAST_EVENT : 309226026Sdelphij case TWS_IOCTL_GET_NEXT_EVENT : 310226026Sdelphij case TWS_IOCTL_GET_PREVIOUS_EVENT : 311226026Sdelphij tws_retrive_aen(sc,cmd,ubuf); 312226026Sdelphij break; 313226026Sdelphij case TWS_IOCTL_GET_LOCK : 314226026Sdelphij ctime = TWS_LOCAL_TIME; 315226026Sdelphij memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); 316226026Sdelphij if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || 317226026Sdelphij (lpkt.force_flag) || 318226026Sdelphij (ctime >= sc->ioctl_lock.timeout) ) { 319226026Sdelphij sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; 320226026Sdelphij sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); 321226026Sdelphij lpkt.time_remaining_msec = lpkt.timeout_msec; 322226026Sdelphij } else { 323226026Sdelphij lpkt.time_remaining_msec = (u_int32_t) 324226026Sdelphij ((sc->ioctl_lock.timeout - ctime) * 1000); 325226026Sdelphij ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; 326226026Sdelphij 327226026Sdelphij } 328226026Sdelphij break; 329226026Sdelphij case TWS_IOCTL_RELEASE_LOCK : 330226026Sdelphij if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { 331226026Sdelphij ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; 332226026Sdelphij } else { 333226026Sdelphij sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; 334226026Sdelphij ubuf->driver_pkt.status = 0; 335226026Sdelphij } 336226026Sdelphij break; 337226026Sdelphij case TWS_IOCTL_GET_COMPATIBILITY_INFO : 338226026Sdelphij TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); 339226026Sdelphij 340226026Sdelphij memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, 341226026Sdelphij sizeof(TWS_DRIVER_VERSION_STRING)); 342226026Sdelphij cpkt.working_srl = sc->cinfo.working_srl; 343226026Sdelphij cpkt.working_branch = sc->cinfo.working_branch; 344226026Sdelphij cpkt.working_build = sc->cinfo.working_build; 345226026Sdelphij cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; 346226026Sdelphij cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; 347226026Sdelphij cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; 348226026Sdelphij cpkt.driver_srl_low = TWS_BASE_FW_SRL; 349226026Sdelphij cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; 350226026Sdelphij cpkt.driver_build_low = TWS_BASE_FW_BUILD; 351226026Sdelphij cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; 352226026Sdelphij cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; 353226026Sdelphij cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; 354226026Sdelphij ubuf->driver_pkt.status = 0; 355226026Sdelphij int len = sizeof(struct tws_compatibility_packet); 356226026Sdelphij if ( ubuf->driver_pkt.buffer_length < len ) 357226026Sdelphij len = ubuf->driver_pkt.buffer_length; 358226026Sdelphij memcpy(ubuf->data_buf, &cpkt, len); 359226026Sdelphij 360226026Sdelphij break; 361226026Sdelphij default : 362226026Sdelphij TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 363226026Sdelphij TWS_IOCTL_GET_COMPATIBILITY_INFO); 364226026Sdelphij break; 365226026Sdelphij 366226026Sdelphij } 367226026Sdelphij mtx_unlock(&sc->gen_lock); 368226026Sdelphij return(SUCCESS); 369226026Sdelphij 370226026Sdelphij} 371226026Sdelphij 372226026Sdelphijvoid 373226026Sdelphijtws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, 374226026Sdelphijstruct tws_event_packet *aen) 375226026Sdelphij{ 376226026Sdelphij 377226026Sdelphij struct tws_event_packet *q = (struct tws_event_packet *)cq->q; 378226026Sdelphij volatile u_int16_t head, tail; 379226026Sdelphij u_int8_t retr; 380226026Sdelphij mtx_assert(&sc->gen_lock, MA_OWNED); 381226026Sdelphij 382226026Sdelphij head = cq->head; 383226026Sdelphij tail = cq->tail; 384226026Sdelphij retr = q[tail].retrieved; 385226026Sdelphij 386226026Sdelphij memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); 387226026Sdelphij tail = (tail+1) % cq->depth; 388226026Sdelphij 389226026Sdelphij if ( head == tail ) { /* q is full */ 390226026Sdelphij if ( retr != TWS_AEN_RETRIEVED ) 391226026Sdelphij cq->overflow = 1; 392226026Sdelphij cq->head = (head+1) % cq->depth; 393226026Sdelphij } 394226026Sdelphij cq->tail = tail; 395226026Sdelphij 396226026Sdelphij} 397