1 | /* $NetBSD: linux_sg.c,v 1.13 2008/03/21 21:54:58 ad Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2004 Soren S. Jorvang. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions, and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 | * SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include <sys/cdefs.h> |
29 | __KERNEL_RCSID(0, "$NetBSD: linux_sg.c,v 1.13 2008/03/21 21:54:58 ad Exp $" ); |
30 | |
31 | #include <sys/param.h> |
32 | #include <sys/systm.h> |
33 | #include <sys/ioctl.h> |
34 | #include <sys/file.h> |
35 | #include <sys/filedesc.h> |
36 | #include <sys/mount.h> |
37 | #include <sys/proc.h> |
38 | #include <sys/device.h> |
39 | |
40 | #include <sys/scsiio.h> |
41 | #include <dev/scsipi/scsipi_all.h> |
42 | #include <dev/scsipi/scsiconf.h> |
43 | |
44 | #include <sys/syscallargs.h> |
45 | |
46 | #include <compat/linux/common/linux_types.h> |
47 | #include <compat/linux/common/linux_ioctl.h> |
48 | #include <compat/linux/common/linux_signal.h> |
49 | #include <compat/linux/common/linux_sg.h> |
50 | #include <compat/linux/common/linux_ipc.h> |
51 | #include <compat/linux/common/linux_sem.h> |
52 | |
53 | #include <compat/linux/linux_syscallargs.h> |
54 | |
55 | int linux_sg_version = 30125; |
56 | |
57 | #ifdef LINUX_SG_DEBUG |
58 | #define DPRINTF(a) printf a |
59 | #else |
60 | #define DPRINTF(a) |
61 | #endif |
62 | |
63 | #ifdef LINUX_SG_DEBUG |
64 | static void dump_sg_io(struct linux_sg_io_hdr *); |
65 | static void dump_scsireq(struct scsireq *); |
66 | #endif |
67 | |
68 | static int bsd_to_linux_host_status(int); |
69 | static int bsd_to_linux_driver_status(int); |
70 | |
71 | int |
72 | linux_ioctl_sg(struct lwp *l, const struct linux_sys_ioctl_args *uap, |
73 | register_t *retval) |
74 | { |
75 | file_t *fp; |
76 | u_long com = SCARG(uap, com); |
77 | int error = 0; |
78 | int (*ioctlf)(file_t *, u_long, void *); |
79 | struct linux_sg_io_hdr lreq; |
80 | struct scsireq req; |
81 | |
82 | if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) |
83 | return EBADF; |
84 | |
85 | ioctlf = fp->f_ops->fo_ioctl; |
86 | |
87 | *retval = 0; |
88 | DPRINTF(("Command = %lx\n" , com)); |
89 | switch (com) { |
90 | case LINUX_SG_GET_VERSION_NUM: { |
91 | error = copyout(&version, SCARG(uap, data), |
92 | sizeof(linux_sg_version)); |
93 | break; |
94 | } |
95 | case LINUX_SG_IO: |
96 | error = copyin(SCARG(uap, data), &lreq, sizeof(lreq)); |
97 | if (error) { |
98 | DPRINTF(("failed to copy in request data %d\n" , error)); |
99 | break; |
100 | } |
101 | |
102 | #ifdef LINUX_SG_DEBUG |
103 | dump_sg_io(&lreq); |
104 | #endif |
105 | (void)memset(&req, 0, sizeof(req)); |
106 | switch (lreq.dxfer_direction) { |
107 | case SG_DXFER_TO_DEV: |
108 | req.flags = SCCMD_WRITE; |
109 | break; |
110 | case SG_DXFER_FROM_DEV: |
111 | req.flags = SCCMD_READ; |
112 | break; |
113 | default: |
114 | DPRINTF(("unknown direction %d\n" , |
115 | lreq.dxfer_direction)); |
116 | error = EINVAL; |
117 | goto done; |
118 | } |
119 | if (lreq.iovec_count != 0) { |
120 | /* XXX: Not yet */ |
121 | error = EOPNOTSUPP; |
122 | DPRINTF(("scatter/gather not supported\n" )); |
123 | break; |
124 | } |
125 | |
126 | if (lreq.cmd_len > sizeof(req.cmd)) { |
127 | DPRINTF(("invalid command length %d\n" , lreq.cmd_len)); |
128 | error = EINVAL; |
129 | break; |
130 | } |
131 | |
132 | error = copyin(lreq.cmdp, req.cmd, lreq.cmd_len); |
133 | if (error) { |
134 | DPRINTF(("failed to copy in cmd data %d\n" , error)); |
135 | break; |
136 | } |
137 | |
138 | req.timeout = lreq.timeout; |
139 | req.cmdlen = lreq.cmd_len; |
140 | req.datalen = lreq.dxfer_len; |
141 | req.databuf = lreq.dxferp; |
142 | |
143 | error = ioctlf(fp, SCIOCCOMMAND, &req); |
144 | if (error) { |
145 | DPRINTF(("SCIOCCOMMAND failed %d\n" , error)); |
146 | break; |
147 | } |
148 | #ifdef LINUX_SG_DEBUG |
149 | dump_scsireq(&req); |
150 | #endif |
151 | if (req.senselen_used) { |
152 | if (req.senselen > lreq.mx_sb_len) |
153 | req.senselen = lreq.mx_sb_len; |
154 | lreq.sb_len_wr = req.senselen; |
155 | error = copyout(req.sense, lreq.sbp, req.senselen); |
156 | if (error) { |
157 | DPRINTF(("sense copyout failed %d\n" , error)); |
158 | break; |
159 | } |
160 | } else { |
161 | lreq.sb_len_wr = 0; |
162 | } |
163 | |
164 | lreq.status = req.status; |
165 | lreq.masked_status = 0; /* XXX */ |
166 | lreq.host_status = bsd_to_linux_host_status(req.retsts); |
167 | lreq.sb_len_wr = req.datalen_used; |
168 | lreq.driver_status = bsd_to_linux_driver_status(req.error); |
169 | lreq.resid = req.datalen - req.datalen_used; |
170 | lreq.duration = req.timeout; /* XXX */ |
171 | lreq.info = 0; /* XXX */ |
172 | error = copyout(&lreq, SCARG(uap, data), sizeof(lreq)); |
173 | if (error) { |
174 | DPRINTF(("failed to copy out req data %d\n" , error)); |
175 | } |
176 | break; |
177 | case LINUX_SG_EMULATED_HOST: |
178 | case LINUX_SG_SET_TRANSFORM: |
179 | case LINUX_SG_GET_TRANSFORM: |
180 | case LINUX_SG_GET_NUM_WAITING: |
181 | case LINUX_SG_SCSI_RESET: |
182 | case LINUX_SG_GET_REQUEST_TABLE: |
183 | case LINUX_SG_SET_KEEP_ORPHAN: |
184 | case LINUX_SG_GET_KEEP_ORPHAN: |
185 | case LINUX_SG_GET_ACCESS_COUNT: |
186 | case LINUX_SG_SET_FORCE_LOW_DMA: |
187 | case LINUX_SG_GET_LOW_DMA: |
188 | case LINUX_SG_GET_SG_TABLESIZE: |
189 | case LINUX_SG_GET_SCSI_ID: |
190 | case LINUX_SG_SET_FORCE_PACK_ID: |
191 | case LINUX_SG_GET_PACK_ID: |
192 | case LINUX_SG_SET_RESERVED_SIZE: |
193 | case LINUX_SG_GET_RESERVED_SIZE: |
194 | error = ENODEV; |
195 | break; |
196 | |
197 | /* version 2 interfaces */ |
198 | case LINUX_SG_SET_TIMEOUT: |
199 | break; |
200 | case LINUX_SG_GET_TIMEOUT: |
201 | /* ioctl returns value..., grr. */ |
202 | *retval = 60; |
203 | break; |
204 | case LINUX_SG_GET_COMMAND_Q: |
205 | case LINUX_SG_SET_COMMAND_Q: |
206 | case LINUX_SG_SET_DEBUG: |
207 | case LINUX_SG_NEXT_CMD_LEN: |
208 | error = ENODEV; |
209 | break; |
210 | } |
211 | |
212 | done: |
213 | fd_putfile(SCARG(uap, fd)); |
214 | |
215 | DPRINTF(("Return=%d\n" , error)); |
216 | return error; |
217 | } |
218 | |
219 | static int |
220 | bsd_to_linux_driver_status(int bs) |
221 | { |
222 | switch (bs) { |
223 | default: |
224 | case XS_NOERROR: |
225 | return 0; |
226 | case XS_SENSE: |
227 | case XS_SHORTSENSE: |
228 | return LINUX_DRIVER_SENSE; |
229 | case XS_RESOURCE_SHORTAGE: |
230 | return LINUX_DRIVER_SOFT; |
231 | case XS_DRIVER_STUFFUP: |
232 | return LINUX_DRIVER_ERROR; |
233 | case XS_SELTIMEOUT: |
234 | case XS_TIMEOUT: |
235 | return LINUX_DRIVER_TIMEOUT; |
236 | case XS_BUSY: |
237 | return LINUX_DRIVER_BUSY; |
238 | case XS_RESET: |
239 | return LINUX_SUGGEST_ABORT; |
240 | case XS_REQUEUE: |
241 | return LINUX_SUGGEST_RETRY; |
242 | } |
243 | } |
244 | |
245 | static int |
246 | bsd_to_linux_host_status(int bs) |
247 | { |
248 | switch (bs) { |
249 | case SCCMD_OK: |
250 | case SCCMD_SENSE: |
251 | return LINUX_DID_OK; |
252 | case SCCMD_TIMEOUT: |
253 | return LINUX_DID_TIME_OUT; |
254 | case SCCMD_BUSY: |
255 | return LINUX_DID_BUS_BUSY; |
256 | case SCCMD_UNKNOWN: |
257 | default: |
258 | return LINUX_DID_ERROR; |
259 | } |
260 | } |
261 | |
262 | |
263 | |
264 | #ifdef LINUX_SG_DEBUG |
265 | static void |
266 | dump_sg_io(struct linux_sg_io_hdr *lr) |
267 | { |
268 | printf("linuxreq [interface_id=%x, dxfer_direction=%d, cmd_len=%d, " |
269 | "mx_sb_len=%d, iovec_count=%d, dxfer_len=%d, dxferp=%p, " |
270 | "cmdp=%p, sbp=%p, timeout=%u, flags=%d, pack_id=%d, " |
271 | "usr_ptr=%p, status=%u, masked_status=%u, sb_len_wr=%u, " |
272 | "host_status=%u, driver_status=%u, resid=%d, duration=%u, " |
273 | "info=%u]\n" , |
274 | lr->interface_id, lr->dxfer_direction, lr->cmd_len, |
275 | lr->mx_sb_len, lr->iovec_count, lr->dxfer_len, lr->dxferp, |
276 | lr->cmdp, lr->sbp, lr->timeout, lr->flags, lr->pack_id, |
277 | lr->usr_ptr, lr->status, lr->masked_status, lr->sb_len_wr, |
278 | lr->host_status, lr->driver_status, lr->resid, lr->duration, |
279 | lr->info); |
280 | } |
281 | |
282 | static void |
283 | dump_scsireq(struct scsireq *br) |
284 | { |
285 | int i; |
286 | printf("bsdreq [flags=%lx, timeout=%lu, cmd=[ " , |
287 | br->flags, br->timeout); |
288 | for (i = 0; i < sizeof(br->cmd) / sizeof(br->cmd[0]); i++) |
289 | printf("%.2u " , br->cmd[i]); |
290 | printf("], cmdlen=%u, databuf=%p, datalen=%lu, datalen_used=%lu, " |
291 | "sense=[ " , |
292 | br->cmdlen, br->databuf, br->datalen, br->datalen_used); |
293 | for (i = 0; i < sizeof(br->sense) / sizeof(br->sense[0]); i++) |
294 | printf("%.2u " , br->sense[i]); |
295 | printf("], senselen=%u, senselen_used=%u, status=%u, retsts=%u, " |
296 | "error=%d]\n" , |
297 | br->senselen, br->senselen_used, br->status, br->retsts, br->error); |
298 | } |
299 | #endif |
300 | |