Add tws(4), a driver for the LSI 3ware 9750 series SATA/SAS RAID controllers.
[dragonfly.git] / sys / dev / raid / 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: 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 }