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

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