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