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: releng/11.0/sys/dev/tws/tws_user.c 276715 2015-01-05 19:49:20Z jhb $ 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, 44276715Sjhb struct thread *td); 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, 63276715Sjhb struct thread *td) 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 error = tws_bus_scan(sc); 77226026Sdelphij break; 78226026Sdelphij default : 79226026Sdelphij TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); 80226026Sdelphij error = tws_ioctl_aen(sc, cmd, (void *)buf); 81226026Sdelphij break; 82226026Sdelphij 83226026Sdelphij } 84226026Sdelphij return(error); 85226026Sdelphij} 86226026Sdelphij 87226026Sdelphijstatic int 88226026Sdelphijtws_passthru(struct tws_softc *sc, void *buf) 89226026Sdelphij{ 90226026Sdelphij struct tws_request *req; 91226026Sdelphij struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; 92226026Sdelphij int error; 93226026Sdelphij u_int16_t lun4; 94226026Sdelphij 95241753Sdelphij 96226026Sdelphij if ( tws_get_state(sc) != TWS_ONLINE) { 97226026Sdelphij return(EBUSY); 98226026Sdelphij } 99226026Sdelphij 100241753Sdelphij //============================================================================================== 101241753Sdelphij // Get a command 102241753Sdelphij // 103226026Sdelphij do { 104226026Sdelphij req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU); 105226026Sdelphij if ( !req ) { 106262572Sdelphij error = tsleep(sc, 0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz); 107226026Sdelphij if ( error == EWOULDBLOCK ) { 108226026Sdelphij return(ETIMEDOUT); 109226026Sdelphij } 110226026Sdelphij } else { 111241753Sdelphij // Make sure we are still ready for new commands... 112241753Sdelphij if ( tws_get_state(sc) != TWS_ONLINE) { 113241753Sdelphij return(EBUSY); 114241753Sdelphij } 115226026Sdelphij break; 116226026Sdelphij } 117241753Sdelphij } while(1); 118226026Sdelphij 119241753Sdelphij req->length = (ubuf->driver_pkt.buffer_length + 511) & ~511; 120226026Sdelphij TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); 121226026Sdelphij if ( req->length ) { 122241753Sdelphij req->data = sc->ioctl_data_mem; 123241753Sdelphij req->dma_map = sc->ioctl_data_map; 124241753Sdelphij 125241753Sdelphij //========================================================================================== 126241753Sdelphij // Copy data in from user space 127241753Sdelphij // 128226026Sdelphij error = copyin(ubuf->pdata, req->data, req->length); 129226026Sdelphij } 130241753Sdelphij 131241753Sdelphij //============================================================================================== 132241753Sdelphij // Set command fields 133241753Sdelphij // 134226026Sdelphij req->flags = TWS_DIR_IN | TWS_DIR_OUT; 135226026Sdelphij req->cb = tws_passthru_complete; 136226026Sdelphij 137226026Sdelphij memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 138226026Sdelphij sizeof(struct tws_command_apache)); 139226026Sdelphij 140226026Sdelphij if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 141226026Sdelphij TWS_FW_CMD_EXECUTE_SCSI ) { 142226026Sdelphij lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; 143226026Sdelphij req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; 144226026Sdelphij } else { 145226026Sdelphij req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; 146226026Sdelphij } 147226026Sdelphij 148241753Sdelphij //============================================================================================== 149241753Sdelphij // Send command to controller 150241753Sdelphij // 151226026Sdelphij error = tws_map_request(sc, req); 152226026Sdelphij if (error) { 153226026Sdelphij ubuf->driver_pkt.os_status = error; 154241753Sdelphij goto out_data; 155226026Sdelphij } 156226026Sdelphij 157241753Sdelphij if ( req->state == TWS_REQ_STATE_COMPLETE ) { 158241753Sdelphij ubuf->driver_pkt.os_status = req->error_code; 159241753Sdelphij goto out_unmap; 160241753Sdelphij } 161241753Sdelphij 162226026Sdelphij mtx_lock(&sc->gen_lock); 163226026Sdelphij error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz); 164226026Sdelphij mtx_unlock(&sc->gen_lock); 165226026Sdelphij if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) { 166226026Sdelphij TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id); 167226026Sdelphij tws_timeout((void*) req); 168226026Sdelphij } 169226026Sdelphij 170241753Sdelphijout_unmap: 171226026Sdelphij if ( req->error_code == TWS_REQ_RET_RESET ) { 172226026Sdelphij error = EBUSY; 173226026Sdelphij req->error_code = EBUSY; 174226026Sdelphij TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id); 175226026Sdelphij } 176226026Sdelphij 177226026Sdelphij tws_unmap_request(sc, req); 178226026Sdelphij 179241753Sdelphij //============================================================================================== 180241753Sdelphij // Return command status to user space 181241753Sdelphij // 182226026Sdelphij memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); 183226026Sdelphij memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 184241753Sdelphij 185241753Sdelphijout_data: 186241753Sdelphij if ( req->length ) { 187241753Sdelphij //========================================================================================== 188241753Sdelphij // Copy data out to user space 189241753Sdelphij // 190241753Sdelphij if ( !error ) 191241753Sdelphij error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length); 192226026Sdelphij } 193226026Sdelphij 194226026Sdelphij if ( error ) 195226026Sdelphij TWS_TRACE_DEBUG(sc, "errored", error, 0); 196226026Sdelphij 197226026Sdelphij if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) 198226026Sdelphij ubuf->driver_pkt.os_status = error; 199226026Sdelphij 200241753Sdelphij //============================================================================================== 201241753Sdelphij // Free command 202241753Sdelphij // 203226026Sdelphij req->state = TWS_REQ_STATE_FREE; 204226026Sdelphij 205262572Sdelphij wakeup_one(sc); 206241753Sdelphij 207226026Sdelphij return(error); 208226026Sdelphij} 209226026Sdelphij 210226026Sdelphijvoid 211226026Sdelphijtws_passthru_complete(struct tws_request *req) 212226026Sdelphij{ 213226026Sdelphij req->state = TWS_REQ_STATE_COMPLETE; 214226026Sdelphij wakeup_one(req); 215226026Sdelphij 216226026Sdelphij} 217226026Sdelphij 218226026Sdelphijstatic void 219226026Sdelphijtws_retrive_aen(struct tws_softc *sc, u_long cmd, 220226026Sdelphij struct tws_ioctl_packet *ubuf) 221226026Sdelphij{ 222226026Sdelphij u_int16_t index=0; 223226026Sdelphij struct tws_event_packet eventp, *qp; 224226026Sdelphij 225226026Sdelphij if ( sc->aen_q.head == sc->aen_q.tail ) { 226226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 227226026Sdelphij return; 228226026Sdelphij } 229226026Sdelphij 230226026Sdelphij ubuf->driver_pkt.status = 0; 231226026Sdelphij 232226026Sdelphij /* 233226026Sdelphij * once this flag is set cli will not display alarms 234226026Sdelphij * needs a revisit from tools? 235226026Sdelphij */ 236226026Sdelphij if ( sc->aen_q.overflow ) { 237226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; 238226026Sdelphij sc->aen_q.overflow = 0; /* reset */ 239226026Sdelphij } 240226026Sdelphij 241226026Sdelphij qp = (struct tws_event_packet *)sc->aen_q.q; 242226026Sdelphij 243226026Sdelphij switch (cmd) { 244226026Sdelphij case TWS_IOCTL_GET_FIRST_EVENT : 245226026Sdelphij index = sc->aen_q.head; 246226026Sdelphij break; 247226026Sdelphij case TWS_IOCTL_GET_LAST_EVENT : 248226026Sdelphij /* index = tail-1 */ 249226026Sdelphij index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; 250226026Sdelphij break; 251226026Sdelphij case TWS_IOCTL_GET_NEXT_EVENT : 252226026Sdelphij memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 253226026Sdelphij index = sc->aen_q.head; 254226026Sdelphij do { 255226026Sdelphij if ( qp[index].sequence_id == 256226026Sdelphij (eventp.sequence_id + 1) ) 257226026Sdelphij break; 258226026Sdelphij index = (index+1) % sc->aen_q.depth; 259226026Sdelphij }while ( index != sc->aen_q.tail ); 260226026Sdelphij if ( index == sc->aen_q.tail ) { 261226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 262226026Sdelphij return; 263226026Sdelphij } 264226026Sdelphij break; 265226026Sdelphij case TWS_IOCTL_GET_PREVIOUS_EVENT : 266226026Sdelphij memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 267226026Sdelphij index = sc->aen_q.head; 268226026Sdelphij do { 269226026Sdelphij if ( qp[index].sequence_id == 270226026Sdelphij (eventp.sequence_id - 1) ) 271226026Sdelphij break; 272226026Sdelphij index = (index+1) % sc->aen_q.depth; 273226026Sdelphij }while ( index != sc->aen_q.tail ); 274226026Sdelphij if ( index == sc->aen_q.tail ) { 275226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 276226026Sdelphij return; 277226026Sdelphij } 278226026Sdelphij break; 279226026Sdelphij default : 280226026Sdelphij TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); 281226026Sdelphij ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 282226026Sdelphij return; 283226026Sdelphij } 284226026Sdelphij 285226026Sdelphij memcpy(ubuf->data_buf, &qp[index], 286226026Sdelphij sizeof(struct tws_event_packet)); 287226026Sdelphij qp[index].retrieved = TWS_AEN_RETRIEVED; 288226026Sdelphij 289226026Sdelphij return; 290226026Sdelphij 291226026Sdelphij} 292226026Sdelphij 293226026Sdelphijstatic int 294226026Sdelphijtws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) 295226026Sdelphij{ 296226026Sdelphij 297226026Sdelphij struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; 298226026Sdelphij struct tws_compatibility_packet cpkt; 299226026Sdelphij struct tws_lock_packet lpkt; 300226026Sdelphij time_t ctime; 301226026Sdelphij 302226026Sdelphij mtx_lock(&sc->gen_lock); 303226026Sdelphij ubuf->driver_pkt.status = 0; 304226026Sdelphij switch(cmd) { 305226026Sdelphij case TWS_IOCTL_GET_FIRST_EVENT : 306226026Sdelphij case TWS_IOCTL_GET_LAST_EVENT : 307226026Sdelphij case TWS_IOCTL_GET_NEXT_EVENT : 308226026Sdelphij case TWS_IOCTL_GET_PREVIOUS_EVENT : 309226026Sdelphij tws_retrive_aen(sc,cmd,ubuf); 310226026Sdelphij break; 311226026Sdelphij case TWS_IOCTL_GET_LOCK : 312226026Sdelphij ctime = TWS_LOCAL_TIME; 313226026Sdelphij memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); 314226026Sdelphij if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || 315226026Sdelphij (lpkt.force_flag) || 316226026Sdelphij (ctime >= sc->ioctl_lock.timeout) ) { 317226026Sdelphij sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; 318226026Sdelphij sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); 319226026Sdelphij lpkt.time_remaining_msec = lpkt.timeout_msec; 320226026Sdelphij } else { 321226026Sdelphij lpkt.time_remaining_msec = (u_int32_t) 322226026Sdelphij ((sc->ioctl_lock.timeout - ctime) * 1000); 323226026Sdelphij ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; 324226026Sdelphij 325226026Sdelphij } 326226026Sdelphij break; 327226026Sdelphij case TWS_IOCTL_RELEASE_LOCK : 328226026Sdelphij if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { 329226026Sdelphij ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; 330226026Sdelphij } else { 331226026Sdelphij sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; 332226026Sdelphij ubuf->driver_pkt.status = 0; 333226026Sdelphij } 334226026Sdelphij break; 335226026Sdelphij case TWS_IOCTL_GET_COMPATIBILITY_INFO : 336226026Sdelphij TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); 337226026Sdelphij 338226026Sdelphij memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, 339226026Sdelphij sizeof(TWS_DRIVER_VERSION_STRING)); 340226026Sdelphij cpkt.working_srl = sc->cinfo.working_srl; 341226026Sdelphij cpkt.working_branch = sc->cinfo.working_branch; 342226026Sdelphij cpkt.working_build = sc->cinfo.working_build; 343226026Sdelphij cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; 344226026Sdelphij cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; 345226026Sdelphij cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; 346226026Sdelphij cpkt.driver_srl_low = TWS_BASE_FW_SRL; 347226026Sdelphij cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; 348226026Sdelphij cpkt.driver_build_low = TWS_BASE_FW_BUILD; 349226026Sdelphij cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; 350226026Sdelphij cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; 351226026Sdelphij cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; 352226026Sdelphij ubuf->driver_pkt.status = 0; 353226026Sdelphij int len = sizeof(struct tws_compatibility_packet); 354226026Sdelphij if ( ubuf->driver_pkt.buffer_length < len ) 355226026Sdelphij len = ubuf->driver_pkt.buffer_length; 356226026Sdelphij memcpy(ubuf->data_buf, &cpkt, len); 357226026Sdelphij 358226026Sdelphij break; 359226026Sdelphij default : 360226026Sdelphij TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 361226026Sdelphij TWS_IOCTL_GET_COMPATIBILITY_INFO); 362226026Sdelphij break; 363226026Sdelphij 364226026Sdelphij } 365226026Sdelphij mtx_unlock(&sc->gen_lock); 366226026Sdelphij return(SUCCESS); 367226026Sdelphij 368226026Sdelphij} 369226026Sdelphij 370226026Sdelphijvoid 371226026Sdelphijtws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, 372226026Sdelphijstruct tws_event_packet *aen) 373226026Sdelphij{ 374226026Sdelphij 375226026Sdelphij struct tws_event_packet *q = (struct tws_event_packet *)cq->q; 376226026Sdelphij volatile u_int16_t head, tail; 377226026Sdelphij u_int8_t retr; 378226026Sdelphij mtx_assert(&sc->gen_lock, MA_OWNED); 379226026Sdelphij 380226026Sdelphij head = cq->head; 381226026Sdelphij tail = cq->tail; 382226026Sdelphij retr = q[tail].retrieved; 383226026Sdelphij 384226026Sdelphij memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); 385226026Sdelphij tail = (tail+1) % cq->depth; 386226026Sdelphij 387226026Sdelphij if ( head == tail ) { /* q is full */ 388226026Sdelphij if ( retr != TWS_AEN_RETRIEVED ) 389226026Sdelphij cq->overflow = 1; 390226026Sdelphij cq->head = (head+1) % cq->depth; 391226026Sdelphij } 392226026Sdelphij cq->tail = tail; 393226026Sdelphij 394226026Sdelphij} 395