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