xref: /freebsd/sys/dev/tws/tws_user.c (revision 57718be8fa0bd5edc11ab9a72e68cc71982939a6)
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                                                     d_thread_t *proc);
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                                                     d_thread_t *proc)
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