]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/dev/tws/tws_user.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / dev / tws / tws_user.c
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             mtx_lock(&sc->sim_lock);
77             error = tws_bus_scan(sc);
78             mtx_unlock(&sc->sim_lock);
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_int16_t lun4;
96
97     if ( tws_get_state(sc) != TWS_ONLINE) {
98         return(EBUSY);
99     }
100
101     do {
102         req = tws_get_request(sc, TWS_REQ_TYPE_PASSTHRU);
103         if ( !req ) {
104             sc->chan = 1;
105             error = tsleep((void *)&sc->chan,  0, 
106                                    "tws_sleep", TWS_IOCTL_TIMEOUT*hz);
107             if ( error == EWOULDBLOCK ) {
108                 return(ETIMEDOUT);
109             }
110         } else {
111             break;
112         }
113     }while(1);
114
115     req->length = ubuf->driver_pkt.buffer_length;
116     TWS_TRACE_DEBUG(sc, "datal,rid", req->length, req->request_id);
117     if ( req->length ) {
118         req->data = malloc(req->length, M_TWS, M_WAITOK | M_ZERO);
119         if ( !req->data ) {
120             TWS_TRACE_DEBUG(sc, "malloc failed", 0, req->request_id);
121             req->state = TWS_REQ_STATE_FREE;
122             ubuf->driver_pkt.os_status = ENOMEM;
123             if ( sc->chan ) {
124                 sc->chan = 0;
125                 wakeup_one((void *)&sc->chan);
126             }
127             return(ENOMEM);
128         }
129         bzero(req->data, req->length);
130         error = copyin(ubuf->pdata, req->data, req->length);
131     }
132     req->flags = TWS_DIR_IN | TWS_DIR_OUT;
133     req->cb = tws_passthru_complete;
134
135     memcpy(&req->cmd_pkt->cmd, &ubuf->cmd_pkt.cmd, 
136                               sizeof(struct tws_command_apache));
137
138     if ( GET_OPCODE(req->cmd_pkt->cmd.pkt_a.res__opcode) == 
139                                                TWS_FW_CMD_EXECUTE_SCSI ) { 
140         lun4 = req->cmd_pkt->cmd.pkt_a.lun_l4__req_id & 0xF000;
141         req->cmd_pkt->cmd.pkt_a.lun_l4__req_id = lun4 | req->request_id;
142     } else {
143         req->cmd_pkt->cmd.pkt_g.generic.request_id = (u_int8_t) req->request_id;
144
145     }
146
147     error = tws_map_request(sc, req);
148     if (error) {
149         ubuf->driver_pkt.os_status = error;
150         goto out;
151     }
152
153 //==================================================================================================
154     mtx_lock(&sc->gen_lock);
155     error = mtx_sleep(req, &sc->gen_lock, 0, "tws_passthru", TWS_IOCTL_TIMEOUT*hz);
156     mtx_unlock(&sc->gen_lock);
157     if (( req->state != TWS_REQ_STATE_COMPLETE ) && ( error == EWOULDBLOCK )) {
158             TWS_TRACE_DEBUG(sc, "msleep timeout", error, req->request_id);
159             tws_timeout((void*) req);
160     }
161
162     if ( req->error_code == TWS_REQ_RET_RESET ) {
163         error = EBUSY;
164         req->error_code = EBUSY;
165         TWS_TRACE_DEBUG(sc, "ioctl reset", error, req->request_id);
166     }
167
168     tws_unmap_request(sc, req);
169
170     memcpy(&ubuf->cmd_pkt.hdr, &req->cmd_pkt->hdr, sizeof(struct tws_command_apache));
171     memcpy(&ubuf->cmd_pkt.cmd, &req->cmd_pkt->cmd, sizeof(struct tws_command_apache));
172     if ( !error && req->length ) {
173         error = copyout(req->data, ubuf->pdata, req->length);
174     }
175 //==================================================================================================
176
177 out:
178     free(req->data, M_TWS);
179
180     if ( error ) 
181         TWS_TRACE_DEBUG(sc, "errored", error, 0);
182
183     if ( req->error_code != TWS_REQ_RET_SUBMIT_SUCCESS )
184         ubuf->driver_pkt.os_status = error;
185
186     req->state = TWS_REQ_STATE_FREE;
187
188     if ( sc->chan && (tws_get_state(sc) == TWS_ONLINE) ) {
189         sc->chan = 0;
190         wakeup_one((void *)&sc->chan);
191     }
192     return(error);
193 }
194
195 void 
196 tws_passthru_complete(struct tws_request *req)
197 {
198     req->state = TWS_REQ_STATE_COMPLETE;
199     wakeup_one(req);
200
201 }
202
203 static void 
204 tws_retrive_aen(struct tws_softc *sc, u_long cmd, 
205                             struct tws_ioctl_packet *ubuf)
206 {
207     u_int16_t index=0;
208     struct tws_event_packet eventp, *qp;
209
210     if ( sc->aen_q.head == sc->aen_q.tail ) {
211         ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
212         return;
213     }
214     
215     ubuf->driver_pkt.status = 0;
216
217     /* 
218      * once this flag is set cli will not display alarms 
219      * needs a revisit from tools?
220      */
221     if ( sc->aen_q.overflow ) {
222         ubuf->driver_pkt.status = TWS_AEN_OVERFLOW;
223         sc->aen_q.overflow = 0; /* reset */
224     }
225
226     qp = (struct tws_event_packet *)sc->aen_q.q;
227
228     switch (cmd) {
229         case TWS_IOCTL_GET_FIRST_EVENT :
230             index = sc->aen_q.head;
231             break;
232         case TWS_IOCTL_GET_LAST_EVENT :
233             /* index = tail-1 */ 
234             index = (sc->aen_q.depth + sc->aen_q.tail - 1) % sc->aen_q.depth;
235             break;
236         case TWS_IOCTL_GET_NEXT_EVENT :
237             memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
238             index = sc->aen_q.head;
239             do {
240                 if ( qp[index].sequence_id == 
241                            (eventp.sequence_id + 1) )
242                     break;
243                 index  = (index+1) % sc->aen_q.depth;
244             }while ( index != sc->aen_q.tail );
245             if ( index == sc->aen_q.tail ) {
246                 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
247                 return;
248             }
249             break;
250         case TWS_IOCTL_GET_PREVIOUS_EVENT :
251             memcpy(&eventp, ubuf->data_buf, sizeof(struct tws_event_packet));
252             index = sc->aen_q.head;
253             do {
254                 if ( qp[index].sequence_id == 
255                            (eventp.sequence_id - 1) )
256                     break;
257                 index  = (index+1) % sc->aen_q.depth;
258             }while ( index != sc->aen_q.tail );
259             if ( index == sc->aen_q.tail ) {
260                 ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
261                 return;
262             }
263             break;
264         default :
265             TWS_TRACE_DEBUG(sc, "not a valid event", sc, cmd);
266             ubuf->driver_pkt.status = TWS_AEN_NO_EVENTS;
267             return;
268     }
269
270     memcpy(ubuf->data_buf, &qp[index], 
271                            sizeof(struct tws_event_packet));
272     qp[index].retrieved = TWS_AEN_RETRIEVED;
273
274     return;
275
276 }
277
278 static int 
279 tws_ioctl_aen(struct tws_softc *sc, u_long cmd, void *buf)
280 {
281
282     struct tws_ioctl_packet *ubuf = (struct tws_ioctl_packet *)buf;
283     struct tws_compatibility_packet cpkt;
284     struct tws_lock_packet lpkt;
285     time_t ctime;
286
287     mtx_lock(&sc->gen_lock);
288     ubuf->driver_pkt.status = 0;
289     switch(cmd) {
290         case TWS_IOCTL_GET_FIRST_EVENT :
291         case TWS_IOCTL_GET_LAST_EVENT :
292         case TWS_IOCTL_GET_NEXT_EVENT :
293         case TWS_IOCTL_GET_PREVIOUS_EVENT :
294             tws_retrive_aen(sc,cmd,ubuf);
295             break;
296         case TWS_IOCTL_GET_LOCK :
297             ctime = TWS_LOCAL_TIME;
298             memcpy(&lpkt, ubuf->data_buf, sizeof(struct tws_lock_packet));
299             if ( (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) ||
300                  (lpkt.force_flag) ||
301                  (ctime >= sc->ioctl_lock.timeout) ) {
302                 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_HELD;
303                 sc->ioctl_lock.timeout = ctime + (lpkt.timeout_msec / 1000);
304                 lpkt.time_remaining_msec = lpkt.timeout_msec;
305             }  else {
306                 lpkt.time_remaining_msec = (u_int32_t)
307                           ((sc->ioctl_lock.timeout - ctime) * 1000);
308                 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_ALREADY_HELD;
309
310             }
311             break;
312         case TWS_IOCTL_RELEASE_LOCK :
313             if (sc->ioctl_lock.lock == TWS_IOCTL_LOCK_FREE) {
314                 ubuf->driver_pkt.status = TWS_IOCTL_LOCK_NOT_HELD;
315             } else {
316                 sc->ioctl_lock.lock = TWS_IOCTL_LOCK_FREE;
317                 ubuf->driver_pkt.status = 0;
318             }
319             break;
320         case TWS_IOCTL_GET_COMPATIBILITY_INFO :
321             TWS_TRACE_DEBUG(sc, "get comp info", sc, cmd);
322
323             memcpy( cpkt.driver_version, TWS_DRIVER_VERSION_STRING,
324                                          sizeof(TWS_DRIVER_VERSION_STRING));
325             cpkt.working_srl = sc->cinfo.working_srl;
326             cpkt.working_branch = sc->cinfo.working_branch;
327             cpkt.working_build = sc->cinfo.working_build;
328             cpkt.driver_srl_high = TWS_CURRENT_FW_SRL;
329             cpkt.driver_branch_high = TWS_CURRENT_FW_BRANCH;
330             cpkt.driver_build_high = TWS_CURRENT_FW_BUILD;
331             cpkt.driver_srl_low = TWS_BASE_FW_SRL;
332             cpkt.driver_branch_low = TWS_BASE_FW_BRANCH;
333             cpkt.driver_build_low = TWS_BASE_FW_BUILD;
334             cpkt.fw_on_ctlr_srl = sc->cinfo.fw_on_ctlr_srl;
335             cpkt.fw_on_ctlr_branch = sc->cinfo.fw_on_ctlr_branch;
336             cpkt.fw_on_ctlr_build = sc->cinfo.fw_on_ctlr_build;
337             ubuf->driver_pkt.status = 0;
338             int len = sizeof(struct tws_compatibility_packet);
339             if ( ubuf->driver_pkt.buffer_length < len )
340                 len = ubuf->driver_pkt.buffer_length;
341             memcpy(ubuf->data_buf, &cpkt, len);
342
343             break;
344         default :
345             TWS_TRACE_DEBUG(sc, "not valid cmd", cmd, 
346                            TWS_IOCTL_GET_COMPATIBILITY_INFO);
347             break;
348
349     }
350     mtx_unlock(&sc->gen_lock);
351     return(SUCCESS);
352
353 }
354
355 void
356 tws_circular_aenq_insert(struct tws_softc *sc, struct tws_circular_q *cq,
357 struct tws_event_packet *aen)
358 {
359
360     struct tws_event_packet *q = (struct tws_event_packet *)cq->q;
361     volatile u_int16_t head, tail;
362     u_int8_t retr;
363     mtx_assert(&sc->gen_lock, MA_OWNED);
364
365     head = cq->head;
366     tail = cq->tail;
367     retr = q[tail].retrieved;
368
369     memcpy(&q[tail], aen, sizeof(struct tws_event_packet));
370     tail = (tail+1) % cq->depth;
371
372     if ( head == tail ) { /* q is full */
373         if ( retr != TWS_AEN_RETRIEVED )
374             cq->overflow = 1;
375         cq->head = (head+1) % cq->depth;
376     }
377     cq->tail = tail;
378
379 }