source: trunk/src/os2ahci/ioctl.c@ 87

Last change on this file since 87 was 87, checked in by markus, 14 years ago

changed copyright headers according to contract; removed evaluation message

File size: 11.2 KB
Line 
1/******************************************************************************
2 * ioctl.c - Generic IOCTL command processing
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28#include "atapi.h"
29#include "ioctl.h"
30
31#include <scsi.h>
32
33#pragma pack(1)
34
35/* -------------------------- macros and constants ------------------------- */
36
37/* ------------------------ typedefs and structures ------------------------ */
38
39/* Memory area for IOCTLs which send IORBs downstream; currently only
40 * OS2AHCI_IOCTL_PASSTHROUGH falls into this category, thus we're simply
41 * reusing the IORB_ADAPTER_PASSTHRU structure for now. If this ever changes,
42 * we'll need to define a union to cover all IORB types in question.
43 */
44typedef struct {
45 IORB_ADAPTER_PASSTHRU iorb; /* IORB */
46 SCSI_STATUS_BLOCK ssb; /* SCSI status block */
47 UCHAR sense[ATAPI_SENSE_LEN]; /* sense buffer */
48 SCATGATENTRY sg_lst[AHCI_MAX_SG / 2]; /* scatter/gather list */
49 ULONG sg_cnt; /* number of S/G elements */
50 UCHAR lh[16]; /* lock handle for VMLock() */
51} IOCTL_CONTEXT;
52
53/* -------------------------- function prototypes -------------------------- */
54
55static LIN lin(void _far *p);
56
57IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb);
58
59/* ------------------------ global/static variables ------------------------ */
60
61/* ----------------------------- start of code ----------------------------- */
62
63/******************************************************************************
64 * Return device list to allow the ring 3 application to figure out which
65 * adapter/port/device combinations are available.
66 */
67USHORT ioctl_get_devlist(RP_GENIOCTL _far *ioctl)
68{
69 OS2AHCI_DEVLIST _far *devlst = (OS2AHCI_DEVLIST _far *) ioctl->DataPacket;
70 USHORT maxcnt = 0;
71 USHORT cnt = 0;
72 USHORT a;
73 USHORT p;
74 USHORT d;
75
76 /* verify addressability of parm buffer (number of devlst elements) */
77 if (DevHelp_VerifyAccess((SEL) ((ULONG) ioctl->ParmPacket >> 16),
78 sizeof(USHORT),
79 (USHORT) (ULONG) ioctl->ParmPacket,
80 VERIFY_READONLY) != 0) {
81 return(STDON | STERR | 0x05);
82 }
83
84 maxcnt = *((USHORT _far *) ioctl->ParmPacket);
85
86 /* verify addressability of return buffer (OS2AHCI_DEVLIST) */
87 if (DevHelp_VerifyAccess((SEL) ((ULONG) devlst >> 16),
88 offsetof(OS2AHCI_DEVLIST, devs) +
89 sizeof(devlst->devs) * maxcnt,
90 (USHORT) (ULONG) devlst,
91 VERIFY_READWRITE) != 0) {
92 return(STDON | STERR | 0x05);
93 }
94
95 /* fill-in device list */
96 for (a = 0; a < ad_info_cnt; a++) {
97 AD_INFO *ai = ad_infos + a;
98
99 for (p = 0; p <= ai->port_max; p++) {
100 P_INFO *pi = ai->ports + p;
101
102 for (d = 0; d <= pi->dev_max; d++) {
103 if (pi->devs[d].present) {
104 /* add this device to the device list */
105 if (cnt >= maxcnt) {
106 /* not enough room in devlst */
107 goto ioctl_get_device_done;
108 }
109
110 devlst->devs[cnt].adapter = a;
111 devlst->devs[cnt].port = p;
112 devlst->devs[cnt].device = d;
113 devlst->devs[cnt].type = pi->devs[d].dev_type;
114 devlst->devs[cnt].ncq_max = pi->devs[d].ncq_max;
115
116 if (pi->devs[d].lba48) devlst->devs[cnt].flags |= DF_LBA48;
117 if (pi->devs[d].atapi) devlst->devs[cnt].flags |= DF_ATAPI;
118 if (pi->devs[d].atapi_16) devlst->devs[cnt].flags |= DF_ATAPI_16;
119 if (pi->devs[d].removable) devlst->devs[cnt].flags |= DF_REMOVABLE;
120 cnt++;
121 }
122 }
123 }
124 }
125
126ioctl_get_device_done:
127 devlst->cnt = cnt;
128 return(STDON);
129}
130
131/******************************************************************************
132 * Adapter passthrough IOCTL. This IOCTL covers both ATA and ATAPI passthrough
133 * requests.
134 */
135USHORT ioctl_passthrough(RP_GENIOCTL _far *ioctl)
136{
137 OS2AHCI_PASSTHROUGH _far *req = (OS2AHCI_PASSTHROUGH _far *) ioctl->ParmPacket;
138 char _far *sense_buf = (char _far *) ioctl->DataPacket;
139 IOCTL_CONTEXT *ic;
140 USHORT ret;
141 USHORT a;
142 USHORT p;
143 USHORT d;
144
145 /* verify addressability of parm buffer (OS2AHCI_PASSTHROUGH) */
146 if (DevHelp_VerifyAccess((SEL) ((ULONG) req >> 16),
147 sizeof(OS2AHCI_PASSTHROUGH),
148 (USHORT) (ULONG) req,
149 VERIFY_READWRITE) != 0) {
150 return(STDON | STERR | 0x05);
151 }
152
153 /* verify addressability of data buffer (sense data) */
154 if (req->sense_len > 0) {
155 if (DevHelp_VerifyAccess((SEL) ((ULONG) sense_buf >> 16),
156 req->sense_len,
157 (USHORT) (ULONG) sense_buf,
158 VERIFY_READWRITE) != 0) {
159 return(STDON | STERR | 0x05);
160 }
161 }
162
163 /* Verify basic request parameters such as adapter/port/device, size of
164 * DMA buffer (the S/G list can't have more than AHCI_MAX_SG / 2 entries), ...
165 */
166 a = req->adapter;
167 p = req->port;
168 d = req->device;
169 if (a >= ad_info_cnt || p > ad_infos[a].port_max ||
170 d > ad_infos[a].ports[p].dev_max || !ad_infos[a].ports[p].devs[d].present) {
171 return(STDON | STERR | ERROR_I24_BAD_UNIT);
172 }
173 if ((req->buflen + 4095) / 4096 + 1 > AHCI_MAX_SG / 2) {
174 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
175 }
176
177 /* allocate IOCTL context data */
178 if ((ic = malloc(sizeof(*ic))) == NULL) {
179 return(STDON | STERR | ERROR_I24_GEN_FAILURE);
180 }
181 memset(ic, 0x00, sizeof(*ic));
182
183 /* lock DMA transfer buffer into memory and construct S/G list */
184 if (req->buflen > 0) {
185 if (DevHelp_VMLock(VMDHL_LONG | !((req->flags & PT_WRITE) ? VMDHL_WRITE : 0),
186 req->buf, req->buflen, lin(ic->sg_lst), lin(&ic->lh),
187 &ic->sg_cnt) != 0) {
188 /* couldn't lock buffer and/or produce a S/G list */
189 free(ic);
190 return(STDON | STERR | ERROR_I24_INVALID_PARAMETER);
191 }
192 }
193
194 /* fill in adapter passthrough fields */
195 ic->iorb.iorbh.Length = sizeof(ic->iorb);
196 ic->iorb.iorbh.UnitHandle = iorb_unit(a, p, d);
197 ic->iorb.iorbh.CommandCode = IOCC_ADAPTER_PASSTHRU;
198 ic->iorb.iorbh.CommandModifier = (req->flags & PT_ATAPI) ? IOCM_EXECUTE_CDB
199 : IOCM_EXECUTE_ATA;
200 ic->iorb.iorbh.RequestControl = IORB_ASYNC_POST;
201 ic->iorb.iorbh.Timeout = req->timeout;
202 ic->iorb.iorbh.NotifyAddress = ioctl_wakeup;
203
204 ic->iorb.cSGList = ic->sg_cnt;
205 ic->iorb.pSGList = ic->sg_lst;
206 DevHelp_VirtToPhys(ic->sg_lst, &ic->iorb.ppSGLIST);
207
208 ic->iorb.ControllerCmdLen = req->cmdlen;
209 ic->iorb.pControllerCmd = req->cmd.cdb;
210 ic->iorb.Flags = (req->flags & PT_WRITE) ? 0 : PT_DIRECTION_IN;
211
212 if (req->sense_len > 0) {
213 /* initialize SCSI status block to allow getting sense data */
214 ic->iorb.iorbh.pStatusBlock = (BYTE *) &ic->ssb;
215 ic->iorb.iorbh.StatusBlockLen = sizeof(ic->ssb);
216 ic->ssb.SenseData = (SCSI_REQSENSE_DATA _far *) ic->sense;
217 ic->ssb.ReqSenseLen = sizeof(ic->sense);
218 ic->iorb.iorbh.RequestControl |= IORB_REQ_STATUSBLOCK;
219 }
220
221 /* send IORB on its way */
222 add_entry(&ic->iorb.iorbh);
223
224 /* Wait for IORB completion. On SMP kernels, ProcBlock will release
225 * all spinlocks currently held, in addition to re-enabling interrupts,
226 * so we can use spinlocks to protect the time between checking the
227 * IORB_DONE flag and calling ProcBlock.
228 *
229 * However, if OS2AHCI_SMP is not defined, we're emulating spinlocks
230 * via disabling interrupts and there's a sanity check in the emulation
231 * code to catch recursive spinlocks, thus we need to reset the emulated
232 * spinlock status before calling ProcBlock. Otherwise, the driver will
233 * will get caught in the sanity checks when processing the next IORB or
234 * interrupt while we're waiting.
235 */
236 spin_lock(drv_lock);
237 while (!(ic->iorb.iorbh.Status & IORB_DONE)) {
238# ifndef OS2AHCI_SMP
239 drv_lock = 0;
240# endif
241 DevHelp_ProcBlock((ULONG) (void _far *) &ic->iorb.iorbh, 30000, 1);
242 spin_lock(drv_lock);
243 }
244 spin_unlock(drv_lock);
245
246 ret = STDON;
247
248 /* map IORB error codes to device driver error codes */
249 if (ic->iorb.iorbh.Status & IORB_ERROR) {
250 ret |= STERR;
251
252 switch (ic->iorb.iorbh.ErrorCode) {
253
254 case IOERR_UNIT_NOT_READY:
255 ret |= ERROR_I24_NOT_READY;
256 break;
257
258 case IOERR_MEDIA_CHANGED:
259 ret |= ERROR_I24_DISK_CHANGE;
260 break;
261
262 case IOERR_MEDIA:
263 case IOERR_MEDIA_NOT_FORMATTED:
264 ret |= ERROR_I24_CRC;
265 break;
266
267 case IOERR_CMD_SYNTAX:
268 case IOERR_CMD_NOT_SUPPORTED:
269 ret |= ERROR_I24_BAD_COMMAND;
270 break;
271
272 case IOERR_MEDIA_WRITE_PROTECT:
273 ret |= ERROR_I24_WRITE_PROTECT;
274 break;
275
276 case IOERR_CMD_ABORTED:
277 ret |= ERROR_I24_CHAR_CALL_INTERRUPTED;
278 break;
279
280 case IOERR_RBA_ADDRESSING_ERROR:
281 ret |= ERROR_I24_SEEK;
282 break;
283
284 case IOERR_RBA_LIMIT:
285 ret |= ERROR_I24_SECTOR_NOT_FOUND;
286 break;
287
288 case IOERR_CMD_SGLIST_BAD:
289 ret |= ERROR_I24_INVALID_PARAMETER;
290 break;
291
292 case IOERR_DEVICE_NONSPECIFIC:
293 case IOERR_ADAPTER_TIMEOUT:
294 case IOERR_ADAPTER_DEVICEBUSCHECK:
295 case IOERR_CMD_ADD_SOFTWARE_FAILURE:
296 case IOERR_CMD_SW_RESOURCE:
297 default:
298 ret |= ERROR_I24_GEN_FAILURE;
299 break;
300 }
301
302 /* copy sense information, if there is any */
303 if ((ic->iorb.iorbh.Status & IORB_STATUSBLOCK_AVAIL) &&
304 (ic->ssb.Flags | STATUS_SENSEDATA_VALID)) {
305 memcpy(sense_buf, ic->ssb.SenseData,
306 min(ic->ssb.ReqSenseLen, req->sense_len));
307 }
308 }
309
310 free(ic);
311 if (req->buflen > 0) {
312 DevHelp_VMUnLock(lin(ic->lh));
313 }
314 return(ret);
315}
316
317/******************************************************************************
318 * Get linear address for specified virtual address.
319 */
320static LIN lin(void _far *p)
321{
322 LIN l;
323
324 if (DevHelp_VirtToLin((SEL) ((ULONG) p >> 16), (USHORT) (ULONG) p, &l) != 0) {
325 return(0);
326 }
327
328 return(l);
329}
330
331/******************************************************************************
332 * IORB notification routine; used to wake up the sleeping application thread
333 * when the IOCTL IORB is complete.
334 */
335IORBH _far * _far _cdecl ioctl_wakeup(IORBH _far *iorb)
336{
337 USHORT awake_count;
338
339 DevHelp_ProcRun((ULONG) iorb, &awake_count);
340}
341
Note: See TracBrowser for help on using the repository browser.