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: src/sys/dev/tws/tws_user.c,v 1.3 2007/05/09 04:16:32 mrangana Exp $ 35 */ 36 37 #include <dev/raid/tws/tws.h> 38 #include <dev/raid/tws/tws_services.h> 39 #include <dev/raid/tws/tws_hdm.h> 40 #include <dev/raid/tws/tws_user.h> 41 42 43 d_ioctl_t tws_ioctl; 44 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_reset(void *arg); 60 61 int 62 tws_ioctl(struct dev_ioctl_args *ap) 63 { 64 cdev_t dev = ap->a_head.a_dev; 65 u_long cmd = ap->a_cmd; 66 caddr_t buf = ap->a_data; 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 lockmgr(&sc->sim_lock, LK_EXCLUSIVE); 79 error = tws_bus_scan(sc); 80 lockmgr(&sc->sim_lock, LK_RELEASE); 81 break; 82 default : 83 TWS_TRACE_DEBUG(sc, "ioctl-aen", cmd, buf); 84 error = tws_ioctl_aen(sc, cmd, (void *)buf); 85 break; 86 87 } 88 return(error); 89 } 90 91 static int 92 tws_passthru(struct tws_softc *sc, void *buf) 93 { 94 struct tws_request *req; 95 struct tws_ioctl_no_data_buf *ubuf = (struct tws_ioctl_no_data_buf *)buf; 96 int error; 97 u_int16_t lun4; 98 99 if ( tws_get_state(sc) == TWS_RESET ) { 100 return(EBUSY); 101 } 102 103 do { 104 req = tws_get_request(sc, TWS_PASSTHRU_REQ); 105 if ( !req ) { 106 sc->chan = 1; 107 error = tsleep((void *)&sc->chan, 0, 108 "tws_sleep", TWS_IO_TIMEOUT*hz); 109 if ( error == EWOULDBLOCK ) { 110 return(ETIMEDOUT); 111 } 112 } else { 113 break; 114 } 115 }while(1); 116 117 req->length = ubuf->driver_pkt.buffer_length; 118 TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id); 119 if ( req->length ) { 120 req->data = kmalloc(req->length, M_TWS, M_WAITOK | M_ZERO); 121 error = copyin(ubuf->pdata, req->data, req->length); 122 } 123 req->flags = TWS_DIR_IN | TWS_DIR_OUT; 124 req->cb = tws_passthru_complete; 125 126 memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 127 sizeof(struct tws_command_apache)); 128 129 if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 130 TWS_FW_CMD_EXECUTE_SCSI ) { 131 lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000; 132 req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id; 133 } else { 134 req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id; 135 136 } 137 138 139 lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 140 req->error_code = tws_map_request(sc, req); 141 142 error = lksleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IO_TIMEOUT*hz); 143 if ( error == EWOULDBLOCK ) { 144 error = ETIMEDOUT; 145 TWS_TRACE_DEBUG(sc, "lksleep timeout", error, req->request_id); 146 tws_reset((void *)sc); 147 } 148 149 if ( req->error_code == TWS_REQ_REQUEUE ) { 150 error = EBUSY; 151 } 152 153 tws_unmap_request(sc, req); 154 155 memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache)); 156 memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache)); 157 if ( !error && req->length ) { 158 error = copyout(req->data, ubuf->pdata, req->length); 159 } 160 161 kfree(req->data, M_TWS); 162 req->state = TWS_REQ_STATE_FREE; 163 lockmgr(&sc->gen_lock, LK_RELEASE); 164 165 if ( error ) 166 TWS_TRACE_DEBUG(sc, "errored", error, 0); 167 if ( req->error_code != TWS_REQ_SUBMIT_SUCCESS ) 168 ubuf->driver_pkt.os_status = error; 169 if ( sc->chan && tws_get_state(sc) != TWS_RESET ) { 170 sc->chan = 0; 171 wakeup((void *)&sc->chan); 172 } 173 return(error); 174 } 175 176 void 177 tws_passthru_complete(struct tws_request *req) 178 { 179 struct tws_softc *sc = req->sc; 180 181 lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 182 wakeup_one(req); 183 lockmgr(&sc->gen_lock, LK_RELEASE); 184 185 } 186 187 static void 188 tws_retrive_aen(struct tws_softc *sc, u_long cmd, 189 struct tws_ioctl_packet *ubuf) 190 { 191 u_int16_t index=0; 192 struct tws_event_packet eventp, *qp; 193 194 if ( sc->aen_q.head == sc->aen_q.tail ) { 195 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 196 return; 197 } 198 199 ubuf->driver_pkt.status = 0; 200 201 /* 202 * once this flag is set cli will not display alarms 203 * needs a revisit from tools? 204 */ 205 if ( sc->aen_q.overflow ) { 206 ubuf->driver_pkt.status = TWS_AEN_OVERFLOW; 207 sc->aen_q.overflow = 0; /* reset */ 208 } 209 210 qp = (struct tws_event_packet *)sc->aen_q.q; 211 212 switch (cmd) { 213 case TWS_IOCTL_GET_FIRST_EVENT : 214 index = sc->aen_q.head; 215 break; 216 case TWS_IOCTL_GET_LAST_EVENT : 217 /* index = tail-1 */ 218 index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth; 219 break; 220 case TWS_IOCTL_GET_NEXT_EVENT : 221 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 222 index = sc->aen_q.head; 223 do { 224 if ( qp[index].sequence_id == 225 (eventp.sequence_id + 1) ) 226 break; 227 index = (index+1) % sc->aen_q.depth; 228 }while ( index != sc->aen_q.tail ); 229 if ( index == sc->aen_q.tail ) { 230 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 231 return; 232 } 233 break; 234 case TWS_IOCTL_GET_PREVIOUS_EVENT : 235 memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet)); 236 index = sc->aen_q.head; 237 do { 238 if ( qp[index].sequence_id == 239 (eventp.sequence_id - 1) ) 240 break; 241 index = (index+1) % sc->aen_q.depth; 242 }while ( index != sc->aen_q.tail ); 243 if ( index == sc->aen_q.tail ) { 244 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 245 return; 246 } 247 break; 248 default : 249 TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd); 250 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS; 251 return; 252 } 253 254 memcpy(ubuf->data_buf, &qp[index], 255 sizeof(struct tws_event_packet)); 256 qp[index].retrieved = TWS_AEN_RETRIEVED; 257 258 return; 259 260 } 261 262 static int 263 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf) 264 { 265 266 struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf; 267 struct tws_compatibility_packet cpkt; 268 struct tws_lock_packet lpkt; 269 time_t ctime; 270 271 lockmgr(&sc->gen_lock, LK_EXCLUSIVE); 272 ubuf->driver_pkt.status = 0; 273 switch(cmd) { 274 case TWS_IOCTL_GET_FIRST_EVENT : 275 case TWS_IOCTL_GET_LAST_EVENT : 276 case TWS_IOCTL_GET_NEXT_EVENT : 277 case TWS_IOCTL_GET_PREVIOUS_EVENT : 278 tws_retrive_aen(sc,cmd,ubuf); 279 break; 280 case TWS_IOCTL_GET_LOCK : 281 ctime = TWS_LOCAL_TIME; 282 memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet)); 283 if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) || 284 (lpkt.force_flag) || 285 (ctime >= sc->ioctl_lock.timeout) ) { 286 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD; 287 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000); 288 lpkt.time_remaining_msec = lpkt.timeout_msec; 289 } else { 290 lpkt.time_remaining_msec = (u_int32_t) 291 ((sc->ioctl_lock.timeout - ctime) * 1000); 292 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD; 293 294 } 295 break; 296 case TWS_IOCTL_RELEASE_LOCK : 297 if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) { 298 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD; 299 } else { 300 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE; 301 ubuf->driver_pkt.status = 0; 302 } 303 break; 304 case TWS_IOCTL_GET_COMPATIBILITY_INFO : 305 TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd); 306 307 memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING, 308 sizeof(TWS_DRIVER_VERSION_STRING)); 309 cpkt.working_srl = sc->cinfo.working_srl; 310 cpkt.working_branch = sc->cinfo.working_branch; 311 cpkt.working_build = sc->cinfo.working_build; 312 cpkt.driver_srl_high = TWS_CURRENT_FW_SRL; 313 cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH; 314 cpkt.driver_build_high = TWS_CURRENT_FW_BUILD; 315 cpkt.driver_srl_low = TWS_BASE_FW_SRL; 316 cpkt.driver_branch_low = TWS_BASE_FW_BRANCH; 317 cpkt.driver_build_low = TWS_BASE_FW_BUILD; 318 cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl; 319 cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch; 320 cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build; 321 ubuf->driver_pkt.status = 0; 322 int len = sizeof(struct tws_compatibility_packet); 323 if ( ubuf->driver_pkt.buffer_length < len ) 324 len = ubuf->driver_pkt.buffer_length; 325 memcpy(ubuf->data_buf, &cpkt, len); 326 327 break; 328 default : 329 TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 330 TWS_IOCTL_GET_COMPATIBILITY_INFO); 331 break; 332 333 } 334 lockmgr(&sc->gen_lock, LK_RELEASE); 335 return(SUCCESS); 336 337 } 338 339 void 340 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq, 341 struct tws_event_packet *aen) 342 { 343 344 struct tws_event_packet *q = (struct tws_event_packet *)cq->q; 345 volatile u_int16_t head, tail; 346 u_int8_t retr; 347 KKASSERT(lockstatus(&sc->gen_lock, curthread) != 0); 348 349 head = cq->head; 350 tail = cq->tail; 351 retr = q[tail].retrieved; 352 353 memcpy(&q[tail], aen, sizeof(struct tws_event_packet)); 354 tail = (tail+1) % cq->depth; 355 356 if ( head == tail ) { /* q is full */ 357 if ( retr != TWS_AEN_RETRIEVED ) 358 cq->overflow = 1; 359 cq->head = (head+1) % cq->depth; 360 } 361 cq->tail = tail; 362 363 } 364