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
tws_ioctl(struct dev_ioctl_args * ap)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
tws_passthru(struct tws_softc * sc,void * buf)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(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
tws_passthru_complete(struct tws_request * req)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
tws_retrive_aen(struct tws_softc * sc,u_long cmd,struct tws_ioctl_packet * ubuf)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
tws_ioctl_aen(struct tws_softc * sc,u_long cmd,void * buf)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
tws_circular_aenq_insert(struct tws_softc * sc,struct tws_circular_q * cq,struct tws_event_packet * aen)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