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 * $FreeBSD$ 37 */ 38 39 #include <dev/tws/tws.h> 40 #include <dev/tws/tws_services.h> 41 #include <dev/tws/tws_hdm.h> 42 #include <dev/tws/tws_user.h> 43 44 45 int tws_ioctl(struct cdev *dev, long unsigned int cmd, caddr_t buf, int flags, 46 struct thread *td); 47 void tws_passthru_complete(struct tws_request *req); 48 extern void tws_circular_aenq_insert(struct tws_softc *sc, 49 struct tws_circular_q *cq, struct tws_event_packet *aen); 50 51 52 static int tws_passthru(struct tws_softc *sc, void *buf); 53 static int tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf); 54 55 extern int tws_bus_scan(struct tws_softc *sc); 56 extern struct tws_request *tws_get_request(struct tws_softc *sc, 57 u_int16_t type); 58 extern int32_t tws_map_request(struct tws_softc *sc, struct tws_request *req); 59 extern void tws_unmap_request(struct tws_softc *sc, struct tws_request *req); 60 extern uint8_t tws_get_state(struct tws_softc *sc); 61 extern void tws_timeout(void *arg); 62 63 int 64 tws_ioctl(struct cdev *dev, u_long cmd, caddr_t buf, int flags, 65 struct thread *td) 66 { 67 struct tws_softc *sc = (struct tws_softc *)(dev->si_drv1); 68 int error; 69 70 TWS_TRACE_DEBUG(sc, "entry", sc, cmd); 71 sc->stats.ioctls++; 72 switch(cmd) { 73 case TWS_IOCTL_FIRMWARE_PASS_THROUGH : 74 error = tws_passthru(sc, (void *)buf); 75 break; 76 case TWS_IOCTL_SCAN_BUS : 77 TWS_TRACE_DEBUG(sc, "scan-bus", 0, 0); 78 error = tws_bus_scan(sc); 79 break; 80 default : 81 TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); 82 error = tws_ioctl_aen(sc, cmd, (void *)buf); 83 break; 84 85 } 86 return(error); 87 } 88 89 static int 90 tws_passthru(struct tws_softc *sc, void *buf) 91 { 92 struct tws_request *req; 93 struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; 94 int error; 95 u_int32_t buffer_length; 96 u_int16_t lun4; 97 98 buffer_length = roundup2(ubuf->driver_pkt.buffer_length, 512); 99 if ( buffer_length > TWS_MAX_IO_SIZE ) { 100 return(EINVAL); 101 } 102 if ( tws_get_state(sc) != TWS_ONLINE) { 103 return(EBUSY); 104 } 105 106 //============================================================================================== 107 // Get a command 108 // 109 do { 110 req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU); 111 if ( !req ) { 112 error = tsleep(sc, 0, "tws_sleep", TWS_IOCTL_TIMEOUT*hz); 113 if ( error == EWOULDBLOCK ) { 114 return(ETIMEDOUT); 115 } 116 } else { 117 // Make sure we are still ready for new commands... 118 if ( tws_get_state(sc) != TWS_ONLINE) { 119 return(EBUSY); 120 } 121 break; 122 } 123 } while(1); 124 125 req->length = buffer_length; 126 TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); 127 if ( req->length ) { 128 req->data = sc->ioctl_data_mem; 129 req->dma_map = sc->ioctl_data_map; 130 131 //========================================================================================== 132 // Copy data in from user space 133 // 134 error = copyin(ubuf->pdata, req->data, req->length); 135 } 136 137 //============================================================================================== 138 // Set command fields 139 // 140 req->flags = TWS_DIR_IN | TWS_DIR_OUT; 141 req->cb = tws_passthru_complete; 142 143 memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 144 sizeof(struct tws_command_apache)); 145 146 if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 147 TWS_FW_CMD_EXECUTE_SCSI ) { 148 lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; 149 req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; 150 } else { 151 req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; 152 } 153 154 //============================================================================================== 155 // Send command to controller 156 // 157 error = tws_map_request(sc, req); 158 if (error) { 159 ubuf->driver_pkt.os_status = error; 160 goto out_data; 161 } 162 163 if ( req->state == TWS_REQ_STATE_COMPLETE ) { 164 ubuf->driver_pkt.os_status = req->error_code; 165 goto out_unmap; 166 } 167 168 mtx_lock(&sc->gen_lock); 169 error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz); 170 mtx_unlock(&sc->gen_lock); 171 if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) { 172 TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id); 173 tws_timeout((void*) req); 174 } 175 176 out_unmap: 177 if ( req->error_code == TWS_REQ_RET_RESET ) { 178 error = EBUSY; 179 req->error_code = EBUSY; 180 TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id); 181 } 182 183 tws_unmap_request(sc, req); 184 185 //============================================================================================== 186 // Return command status to user space 187 // 188 memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); 189 memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 190 191 out_data: 192 if ( req->length ) { 193 //========================================================================================== 194 // Copy data out to user space 195 // 196 if ( !error ) 197 error = copyout(req->data, ubuf->pdata, ubuf->driver_pkt.buffer_length); 198 } 199 200 if ( error ) 201 TWS_TRACE_DEBUG(sc, "errored", error, 0); 202 203 if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS ) 204 ubuf->driver_pkt.os_status = error; 205 206 //============================================================================================== 207 // Free command 208 // 209 req->state = TWS_REQ_STATE_FREE; 210 211 wakeup_one(sc); 212 213 return(error); 214 } 215 216 void 217 tws_passthru_complete(struct tws_request *req) 218 { 219 req->state = TWS_REQ_STATE_COMPLETE; 220 wakeup_one(req); 221 222 } 223 224 static void 225 tws_retrive_aen(struct tws_softc *sc, u_long cmd, 226 struct tws_ioctl_packet *ubuf) 227 { 228 u_int16_t index=0; 229 struct tws_event_packet eventp, *qp; 230 231 if ( sc->aen_q.head == sc->aen_q.tail ) { 232 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 233 return; 234 } 235 236 ubuf->driver_pkt.status = 0; 237 238 /* 239 * once this flag is set cli will not display alarms 240 * needs a revisit from tools? 241 */ 242 if ( sc->aen_q.overflow ) { 243 ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; 244 sc->aen_q.overflow = 0; /* reset */ 245 } 246 247 qp = (struct tws_event_packet *)sc->aen_q.q; 248 249 switch (cmd) { 250 case TWS_IOCTL_GET_FIRST_EVENT : 251 index = sc->aen_q.head; 252 break; 253 case TWS_IOCTL_GET_LAST_EVENT : 254 /* index = tail-1 */ 255 index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; 256 break; 257 case TWS_IOCTL_GET_NEXT_EVENT : 258 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 259 index = sc->aen_q.head; 260 do { 261 if ( qp[index].sequence_id == 262 (eventp.sequence_id + 1) ) 263 break; 264 index = (index+1) % sc->aen_q.depth; 265 }while ( index != sc->aen_q.tail ); 266 if ( index == sc->aen_q.tail ) { 267 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 268 return; 269 } 270 break; 271 case TWS_IOCTL_GET_PREVIOUS_EVENT : 272 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 273 index = sc->aen_q.head; 274 do { 275 if ( qp[index].sequence_id == 276 (eventp.sequence_id - 1) ) 277 break; 278 index = (index+1) % sc->aen_q.depth; 279 }while ( index != sc->aen_q.tail ); 280 if ( index == sc->aen_q.tail ) { 281 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 282 return; 283 } 284 break; 285 default : 286 TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); 287 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 288 return; 289 } 290 291 memcpy(ubuf->data_buf, &qp[index], 292 sizeof(struct tws_event_packet)); 293 qp[index].retrieved = TWS_AEN_RETRIEVED; 294 295 return; 296 297 } 298 299 static int 300 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) 301 { 302 303 struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; 304 struct tws_compatibility_packet cpkt; 305 struct tws_lock_packet lpkt; 306 time_t ctime; 307 308 mtx_lock(&sc->gen_lock); 309 ubuf->driver_pkt.status = 0; 310 switch(cmd) { 311 case TWS_IOCTL_GET_FIRST_EVENT : 312 case TWS_IOCTL_GET_LAST_EVENT : 313 case TWS_IOCTL_GET_NEXT_EVENT : 314 case TWS_IOCTL_GET_PREVIOUS_EVENT : 315 tws_retrive_aen(sc,cmd,ubuf); 316 break; 317 case TWS_IOCTL_GET_LOCK : 318 ctime = TWS_LOCAL_TIME; 319 memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); 320 if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || 321 (lpkt.force_flag) || 322 (ctime >= sc->ioctl_lock.timeout) ) { 323 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; 324 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); 325 lpkt.time_remaining_msec = lpkt.timeout_msec; 326 } else { 327 lpkt.time_remaining_msec = (u_int32_t) 328 ((sc->ioctl_lock.timeout - ctime) * 1000); 329 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; 330 331 } 332 break; 333 case TWS_IOCTL_RELEASE_LOCK : 334 if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { 335 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; 336 } else { 337 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; 338 ubuf->driver_pkt.status = 0; 339 } 340 break; 341 case TWS_IOCTL_GET_COMPATIBILITY_INFO : 342 TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); 343 344 memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, 345 sizeof(TWS_DRIVER_VERSION_STRING)); 346 cpkt.working_srl = sc->cinfo.working_srl; 347 cpkt.working_branch = sc->cinfo.working_branch; 348 cpkt.working_build = sc->cinfo.working_build; 349 cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; 350 cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; 351 cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; 352 cpkt.driver_srl_low = TWS_BASE_FW_SRL; 353 cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; 354 cpkt.driver_build_low = TWS_BASE_FW_BUILD; 355 cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; 356 cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; 357 cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; 358 ubuf->driver_pkt.status = 0; 359 int len = sizeof(struct tws_compatibility_packet); 360 if ( ubuf->driver_pkt.buffer_length < len ) 361 len = ubuf->driver_pkt.buffer_length; 362 memcpy(ubuf->data_buf, &cpkt, len); 363 364 break; 365 default : 366 TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 367 TWS_IOCTL_GET_COMPATIBILITY_INFO); 368 break; 369 370 } 371 mtx_unlock(&sc->gen_lock); 372 return(SUCCESS); 373 374 } 375 376 void 377 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, 378 struct tws_event_packet *aen) 379 { 380 381 struct tws_event_packet *q = (struct tws_event_packet *)cq->q; 382 volatile u_int16_t head, tail; 383 u_int8_t retr; 384 mtx_assert(&sc->gen_lock, MA_OWNED); 385 386 head = cq->head; 387 tail = cq->tail; 388 retr = q[tail].retrieved; 389 390 memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); 391 tail = (tail+1) % cq->depth; 392 393 if ( head == tail ) { /* q is full */ 394 if ( retr != TWS_AEN_RETRIEVED ) 395 cq->overflow = 1; 396 cq->head = (head+1) % cq->depth; 397 } 398 cq->tail = tail; 399 400 } 401