1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2010, LSI Corp. 5 * All rights reserved. 6 * Author : Manjunath Ranganathaiah 7 * Support: freebsdraid@lsi.com 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of the <ORGANIZATION> nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include <dev/tws/tws.h> 38 #include <dev/tws/tws_services.h> 39 #include <dev/tws/tws_hdm.h> 40 #include <dev/tws/tws_user.h> 41 42 int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags, 43 struct thread *td); 44 void tws_passthru_complete(struct tws_request *req); 45 extern void tws_circular_aenq_insert(struct tws_softc *sc, 46 struct tws_circular_q *cq, struct tws_event_packet *aen); 47 48 static int tws_passthru(struct tws_softc *sc, void *buf); 49 static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf); 50 51 extern int tws_bus_scan(struct tws_softc *sc); 52 extern struct tws_request *tws_get_request(struct tws_softc *sc, 53 u_int16_t type); 54 extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); 55 extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); 56 extern uint8_t tws_get_state(struct tws_softc *sc); 57 extern void tws_timeout(void *arg); 58 59 int 60 tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags, 61 struct thread *td) 62 { 63 struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1); 64 int error; 65 66 TWS_TRACE_DEBUG(sc, "entry", sc, cmd); 67 sc->stats.ioctls++; 68 switch(cmd) { 69 case TWS_IOCTL_FIRMWARE_PASS_THROUGH : 70 error = tws_passthru(sc, (void *)buf); 71 break; 72 case TWS_IOCTL_SCAN_BUS : 73 TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0); 74 error = tws_bus_scan(sc); 75 break; 76 default : 77 TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); 78 error = tws_ioctl_aen(sc, cmd, (void *)buf); 79 break; 80 } 81 return(error); 82 } 83 84 static int 85 tws_passthru(struct tws_softc *sc, void *buf) 86 { 87 struct tws_request *req; 88 struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; 89 int error; 90 u_int32_t buffer_length; 91 u_int16_t lun4; 92 93 buffer_length = roundup2(ubuf->driver_pkt.buffer_length, 512); 94 if ( buffer_length > TWS_MAX_IO_SIZE ) { 95 return(EINVAL); 96 } 97 if ( tws_get_state(sc) != TWS_ONLINE) { 98 return(EBUSY); 99 } 100 101 //============================================================================================== 102 // Get a command 103 // 104 do { 105 req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU); 106 if ( !req ) { 107 error = tsleep(sc, 0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz); 108 if ( error == EWOULDBLOCK ) { 109 return(ETIMEDOUT); 110 } 111 } else { 112 // Make sure we are still ready for new commands... 113 if ( tws_get_state(sc) != TWS_ONLINE) { 114 return(EBUSY); 115 } 116 break; 117 } 118 } while(1); 119 120 req->length = buffer_length; 121 TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); 122 if ( req->length ) { 123 req->data = sc->ioctl_data_mem; 124 req->dma_map = sc->ioctl_data_map; 125 126 //========================================================================================== 127 // Copy data in from user space 128 // 129 error = copyin(ubuf->pdata, req->data, req->length); 130 } 131 132 //============================================================================================== 133 // Set command fields 134 // 135 req->flags = TWS_DIR_IN | TWS_DIR_OUT; 136 req->cb = tws_passthru_complete; 137 138 memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 139 sizeof(struct tws_command_apache)); 140 141 if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 142 TWS_FW_CMD_EXECUTE_SCSI ) { 143 lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; 144 req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; 145 } else { 146 req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; 147 } 148 149 //============================================================================================== 150 // Send command to controller 151 // 152 error = tws_map_request(sc, req); 153 if (error) { 154 ubuf->driver_pkt.os_status = error; 155 goto out_data; 156 } 157 158 if ( req->state == TWS_REQ_STATE_COMPLETE ) { 159 ubuf->driver_pkt.os_status = req->error_code; 160 goto out_unmap; 161 } 162 163 mtx_lock(&sc->gen_lock); 164 error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz); 165 mtx_unlock(&sc->gen_lock); 166 if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) { 167 TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id); 168 tws_timeout((void*) req); 169 } 170 171 out_unmap: 172 if ( req->error_code == TWS_REQ_RET_RESET ) { 173 error = EBUSY; 174 req->error_code = EBUSY; 175 TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id); 176 } 177 178 tws_unmap_request(sc, req); 179 180 //============================================================================================== 181 // Return command status to user space 182 // 183 memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); 184 memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 185 186 out_data: 187 if ( req->length ) { 188 //========================================================================================== 189 // Copy data out to user space 190 // 191 if ( !error ) 192 error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length); 193 } 194 195 if ( error ) 196 TWS_TRACE_DEBUG(sc, "errored", error, 0); 197 198 if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) 199 ubuf->driver_pkt.os_status = error; 200 201 //============================================================================================== 202 // Free command 203 // 204 req->state = TWS_REQ_STATE_FREE; 205 206 wakeup_one(sc); 207 208 return(error); 209 } 210 211 void 212 tws_passthru_complete(struct tws_request *req) 213 { 214 req->state = TWS_REQ_STATE_COMPLETE; 215 wakeup_one(req); 216 217 } 218 219 static void 220 tws_retrive_aen(struct tws_softc *sc, u_long cmd, 221 struct tws_ioctl_packet *ubuf) 222 { 223 u_int16_t index=0; 224 struct tws_event_packet eventp, *qp; 225 226 if ( sc->aen_q.head == sc->aen_q.tail ) { 227 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 228 return; 229 } 230 231 ubuf->driver_pkt.status = 0; 232 233 /* 234 * once this flag is set cli will not display alarms 235 * needs a revisit from tools? 236 */ 237 if ( sc->aen_q.overflow ) { 238 ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; 239 sc->aen_q.overflow = 0; /* reset */ 240 } 241 242 qp = (struct tws_event_packet *)sc->aen_q.q; 243 244 switch (cmd) { 245 case TWS_IOCTL_GET_FIRST_EVENT : 246 index = sc->aen_q.head; 247 break; 248 case TWS_IOCTL_GET_LAST_EVENT : 249 /* index = tail-1 */ 250 index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; 251 break; 252 case TWS_IOCTL_GET_NEXT_EVENT : 253 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 254 index = sc->aen_q.head; 255 do { 256 if ( qp[index].sequence_id == 257 (eventp.sequence_id + 1) ) 258 break; 259 index = (index+1) % sc->aen_q.depth; 260 }while ( index != sc->aen_q.tail ); 261 if ( index == sc->aen_q.tail ) { 262 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 263 return; 264 } 265 break; 266 case TWS_IOCTL_GET_PREVIOUS_EVENT : 267 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 268 index = sc->aen_q.head; 269 do { 270 if ( qp[index].sequence_id == 271 (eventp.sequence_id - 1) ) 272 break; 273 index = (index+1) % sc->aen_q.depth; 274 }while ( index != sc->aen_q.tail ); 275 if ( index == sc->aen_q.tail ) { 276 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 277 return; 278 } 279 break; 280 default : 281 TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); 282 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 283 return; 284 } 285 286 memcpy(ubuf->data_buf, &qp[index], 287 sizeof(struct tws_event_packet)); 288 qp[index].retrieved = TWS_AEN_RETRIEVED; 289 290 return; 291 292 } 293 294 static int 295 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) 296 { 297 298 struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; 299 struct tws_compatibility_packet cpkt; 300 struct tws_lock_packet lpkt; 301 time_t ctime; 302 303 mtx_lock(&sc->gen_lock); 304 ubuf->driver_pkt.status = 0; 305 switch(cmd) { 306 case TWS_IOCTL_GET_FIRST_EVENT : 307 case TWS_IOCTL_GET_LAST_EVENT : 308 case TWS_IOCTL_GET_NEXT_EVENT : 309 case TWS_IOCTL_GET_PREVIOUS_EVENT : 310 tws_retrive_aen(sc,cmd,ubuf); 311 break; 312 case TWS_IOCTL_GET_LOCK : 313 ctime = TWS_LOCAL_TIME; 314 memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); 315 if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || 316 (lpkt.force_flag) || 317 (ctime >= sc->ioctl_lock.timeout) ) { 318 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; 319 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); 320 lpkt.time_remaining_msec = lpkt.timeout_msec; 321 } else { 322 lpkt.time_remaining_msec = (u_int32_t) 323 ((sc->ioctl_lock.timeout - ctime) * 1000); 324 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; 325 } 326 break; 327 case TWS_IOCTL_RELEASE_LOCK : 328 if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { 329 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; 330 } else { 331 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; 332 ubuf->driver_pkt.status = 0; 333 } 334 break; 335 case TWS_IOCTL_GET_COMPATIBILITY_INFO : 336 TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); 337 338 memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, 339 sizeof(TWS_DRIVER_VERSION_STRING)); 340 cpkt.working_srl = sc->cinfo.working_srl; 341 cpkt.working_branch = sc->cinfo.working_branch; 342 cpkt.working_build = sc->cinfo.working_build; 343 cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; 344 cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; 345 cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; 346 cpkt.driver_srl_low = TWS_BASE_FW_SRL; 347 cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; 348 cpkt.driver_build_low = TWS_BASE_FW_BUILD; 349 cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; 350 cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; 351 cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; 352 ubuf->driver_pkt.status = 0; 353 int len = sizeof(struct tws_compatibility_packet); 354 if ( ubuf->driver_pkt.buffer_length < len ) 355 len = ubuf->driver_pkt.buffer_length; 356 memcpy(ubuf->data_buf, &cpkt, len); 357 358 break; 359 default : 360 TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 361 TWS_IOCTL_GET_COMPATIBILITY_INFO); 362 break; 363 } 364 mtx_unlock(&sc->gen_lock); 365 return(SUCCESS); 366 367 } 368 369 void 370 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, 371 struct tws_event_packet *aen) 372 { 373 374 struct tws_event_packet *q = (struct tws_event_packet *)cq->q; 375 volatile u_int16_t head, tail; 376 u_int8_t retr; 377 mtx_assert(&sc->gen_lock, MA_OWNED); 378 379 head = cq->head; 380 tail = cq->tail; 381 retr = q[tail].retrieved; 382 383 memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); 384 tail = (tail+1) % cq->depth; 385 386 if ( head == tail ) { /* q is full */ 387 if ( retr != TWS_AEN_RETRIEVED ) 388 cq->overflow = 1; 389 cq->head = (head+1) % cq->depth; 390 } 391 cq->tail = tail; 392 393 } 394