Use PROCH macro for unit process.
[AROS.git] / workbench / devs / diskimage / device / unit.c
blob4a639fa9a9bcc4a6ffd0effdd506d3c912a0d5f3
1 /* Copyright 2007-2012 Fredrik Wikstrom. All rights reserved.
2 **
3 ** Redistribution and use in source and binary forms, with or without
4 ** modification, are permitted provided that the following conditions
5 ** are met:
6 **
7 ** 1. Redistributions of source code must retain the above copyright
8 ** notice, this list of conditions and the following disclaimer.
9 **
10 ** 2. Redistributions in binary form must reproduce the above copyright
11 ** notice, this list of conditions and the following disclaimer in the
12 ** documentation and/or other materials provided with the distribution.
14 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
15 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 ** POSSIBILITY OF SUCH DAMAGE.
27 #include "diskimage_device.h"
28 #include <libraries/iffparse.h>
30 #ifdef __AROS__
31 #include <expat.h>
32 #else
33 #include <proto/expat.h>
34 #endif
36 #include <SDI_stdarg.h>
38 struct ChangeInt {
39 struct MinNode ci_Node;
40 struct Interrupt *ci_Interrupt;
43 static void ReadUnitPrefs (struct DiskImageUnit *unit);
44 static void WriteUnitPrefs (struct DiskImageUnit *unit, BOOL envarc);
45 static inline struct IOExtTD *GetIOMsg (struct DiskImageUnit *unit);
46 static void Cleanup (struct DiskImageUnit *unit);
47 static LONG TDGeometry (struct DiskImageUnit *unit, struct IOStdReq *io);
48 static LONG TDRead (struct DiskImageUnit *unit, struct IOStdReq *io);
49 static LONG TDWrite (struct DiskImageUnit *unit, struct IOStdReq *io);
50 static void InsertDisk (struct DiskImageUnit *unit, BPTR dir, CONST_STRPTR filename,
51 struct DiskImagePlugin *plugin, STRPTR fullpath, ULONG fullpath_size);
52 static void RemoveDisk (struct DiskImageUnit *unit);
53 static void DiskChange (struct DiskImageUnit *unit);
55 #ifdef __AROS__
56 AROS_PROCH(UnitProcEntry, argstr, arglen, SysBase)
58 AROS_PROCFUNC_INIT
59 #else
60 int UnitProcEntry (void) {
61 #endif
62 struct Process *proc;
63 struct DiskImageMsg *msg;
64 struct DiskImageUnit *unit;
65 struct DeathMessage *dm;
67 dbug(("UnitProcEntry()\n"));
69 proc = (struct Process *)FindTask(NULL);
70 WaitPort(&proc->pr_MsgPort);
71 msg = (struct DiskImageMsg *)GetMsg(&proc->pr_MsgPort);
72 if (!msg->dim_Unit || msg->dim_Command != DICMD_STARTUP) {
73 return RETURN_FAIL;
76 unit = msg->dim_Unit;
77 dm = unit->DeathMsg;
79 dm->dm_ReturnCode = UnitProcMain(unit);
80 dm->dm_Result2 = IoErr();
82 Forbid();
83 ReplyMsg(&dm->dm_Msg);
84 return dm->dm_ReturnCode;
85 #ifdef __AROS__
86 AROS_PROCFUNC_EXIT
87 #endif
90 int UnitProcMain (struct DiskImageUnit *unit) {
91 struct DiskImageBase *libBase = unit->LibBase;
92 struct Library *SysBase = libBase->SysBase;
93 struct Library *UtilityBase = libBase->UtilityBase;
94 struct IOExtTD *iotd;
95 struct DiskImageMsg *msg;
96 struct TagItem *ti, *tstate;
97 LONG err;
98 ULONG sigmask;
99 CONST_STRPTR filename, plugin_name;
100 struct ChangeInt *handler;
101 BOOL disk_change = FALSE;
103 dbug(("UnitProcMain()\n"));
105 unit->IOPort = CreateMsgPort();
106 unit->MsgPort = CreateMsgPort();
107 if (!unit->IOPort || !unit->MsgPort) {
108 Cleanup(unit);
109 return RETURN_FAIL;
112 unit->Prefs = AllocPrefsDictionary();
113 if (!unit->Prefs) {
114 Cleanup(unit);
115 return RETURN_FAIL;
118 dbug(("replying to start msg\n"));
119 ReplyMsg(&unit->DiskImageMsg->dim_Msg);
121 #ifdef __AROS__
122 ObtainSemaphore(libBase->PluginSemaphore);
123 if (IsListEmpty(libBase->Plugins)) {
124 InitLocaleInfo(SysBase, &libBase->LocaleInfo, "diskimagedevice.catalog");
125 LoadPlugins(libBase);
127 ReleaseSemaphore(libBase->PluginSemaphore);
128 #endif
130 ReadUnitPrefs(unit);
131 unit->DeviceType = DictGetIntegerForKey(unit->Prefs, "DeviceType", DG_DIRECT_ACCESS);
132 unit->Flags = DictGetIntegerForKey(unit->Prefs, "Flags", DGF_REMOVABLE);
133 filename = DictGetStringForKey(unit->Prefs, "DiskImageFile", NULL);
134 plugin_name = DictGetStringForKey(unit->Prefs, "Plugin", NULL);
135 if (filename) {
136 struct DiskImagePlugin *plugin = NULL;
137 ObtainSemaphoreShared(libBase->PluginSemaphore);
138 if (plugin_name) {
139 plugin = (struct DiskImagePlugin *)FindName(libBase->Plugins, plugin_name);
141 if (!plugin_name || plugin) {
142 APTR window;
143 window = SetProcWindow((APTR)-1);
144 InsertDisk(unit, ZERO, filename, plugin, NULL, 0);
145 SetProcWindow(window);
147 ReleaseSemaphore(libBase->PluginSemaphore);
150 sigmask = (1UL << unit->IOPort->mp_SigBit)|(1UL << unit->MsgPort->mp_SigBit);
151 dbug(("entering main loop\n"));
152 for (;;) {
153 Wait(sigmask);
155 while ((msg = (struct DiskImageMsg *)GetMsg(unit->MsgPort))) {
156 switch (msg->dim_Command) {
157 case DICMD_DIE:
158 Cleanup(unit);
159 return RETURN_OK;
161 case DICMD_TAGLIST:
162 if ((tstate = msg->dim_Tags)) {
163 BPTR curr_dir = ZERO;
164 struct DiskImagePlugin *plugin = NULL;
166 unit->Error = NO_ERROR;
167 unit->ErrorString = NULL;
169 ObtainSemaphoreShared(libBase->PluginSemaphore);
170 while (!unit->Error && (ti = NextTagItem(&tstate))) {
171 switch (ti->ti_Tag) {
172 case DITAG_Error:
173 unit->ErrorPtr = (LONG *)ti->ti_Data;
174 if (unit->ErrorPtr) {
175 *unit->ErrorPtr = NO_ERROR;
177 break;
179 case DITAG_ErrorString:
180 unit->ErrorString = (STRPTR)ti->ti_Data;
181 if (unit->ErrorString && unit->ErrorStringLength) {
182 unit->ErrorString[0] = 0;
184 break;
186 case DITAG_ErrorStringLength:
187 unit->ErrorStringLength = ti->ti_Data;
188 if (unit->ErrorString && unit->ErrorStringLength) {
189 unit->ErrorString[0] = 0;
191 break;
193 case DITAG_Screen:
194 unit->Screen = (struct Screen *)ti->ti_Data;
195 break;
197 case DITAG_Password:
198 unit->Password = (CONST_STRPTR)ti->ti_Data;
199 break;
201 case DITAG_CurrentDir:
202 curr_dir = (BPTR)ti->ti_Data;
203 break;
205 case DITAG_Plugin:
206 if (!ti->ti_Data) break;
207 plugin = (void *)FindName(libBase->Plugins, (STRPTR)ti->ti_Data);
208 if (!plugin) {
209 SetDiskImageError(NULL, unit, ERROR_OBJECT_NOT_FOUND, 0);
210 break;
212 break;
214 case DITAG_Filename: {
215 APTR image_data = unit->ImageData;
216 TEXT fullpath[512];
217 RemoveDisk(unit);
218 filename = (CONST_STRPTR)ti->ti_Data;
219 if (filename) {
220 InsertDisk(unit, curr_dir, filename, plugin, fullpath, sizeof(fullpath));
222 if (image_data || unit->ImageData) {
223 disk_change = TRUE;
224 if (unit->ImageData) {
225 DictSetObjectForKey(unit->Prefs,
226 AllocPrefsString(fullpath),
227 "DiskImageFile");
228 } else {
229 DictRemoveObjForKey(unit->Prefs,
230 "DiskImageFile");
232 if (unit->ImageData && plugin) {
233 DictSetObjectForKey(unit->Prefs,
234 AllocPrefsString(plugin->Node.ln_Name),
235 "Plugin");
236 } else {
237 DictRemoveObjForKey(unit->Prefs,
238 "Plugin");
240 WriteUnitPrefs(unit, TRUE);
242 break;
245 case DITAG_WriteProtect:
246 unit->WriteProtect = ti->ti_Data ? TRUE : FALSE;
247 break;
249 case DITAG_GetImageName:
250 if (!unit->Name) {
251 *(STRPTR *)ti->ti_Data = NULL;
252 break;
254 *(STRPTR *)ti->ti_Data = ASPrintf("%s", unit->Name);
255 if (!ti->ti_Data) {
256 SetDiskImageError(NULL, unit, ERROR_NO_FREE_STORE, 0);
257 break;
259 break;
261 case DITAG_GetWriteProtect:
262 *(BOOL *)ti->ti_Data = unit->WriteProtect;
263 break;
265 case DITAG_DiskImageType:
266 if (unit->ImageData)
267 *(ULONG *)ti->ti_Data = DITYPE_RAW;
268 else
269 *(ULONG *)ti->ti_Data = DITYPE_NONE;
270 break;
272 case DITAG_SetDeviceType:
273 if (unit->DeviceType != ti->ti_Data) {
274 unit->DeviceType = ti->ti_Data;
275 DictSetObjectForKey(unit->Prefs,
276 AllocPrefsInteger(unit->DeviceType),
277 "DeviceType");
278 WriteUnitPrefs(unit, TRUE);
280 break;
282 case DITAG_GetDeviceType:
283 *(UBYTE *)ti->ti_Data = unit->DeviceType;
284 break;
286 case DITAG_SetFlags:
287 if (unit->Flags != ti->ti_Data) {
288 unit->Flags = ti->ti_Data;
289 DictSetObjectForKey(unit->Prefs,
290 AllocPrefsInteger(unit->Flags),
291 "Flags");
292 WriteUnitPrefs(unit, TRUE);
294 break;
296 case DITAG_GetFlags:
297 *(UBYTE *)ti->ti_Data = unit->Flags;
298 break;
299 } /* switch */
300 } /* while */
301 ReleaseSemaphore(libBase->PluginSemaphore);
303 unit->Screen = NULL;
304 unit->Password = NULL;
305 } /* if */
306 break;
307 } /* switch */
308 ReplyMsg(&msg->dim_Msg);
310 if (disk_change) {
311 DiskChange(unit);
312 disk_change = FALSE;
315 while ((iotd = GetIOMsg(unit))) {
316 switch (iotd->iotd_Req.io_Command) {
317 case ETD_READ:
318 if (iotd->iotd_Count < unit->ChangeCnt) {
319 err = TDERR_DiskChanged;
320 break;
322 case CMD_READ:
323 iotd->iotd_Req.io_Actual = 0;
324 err = TDRead(unit, &iotd->iotd_Req);
325 break;
327 case ETD_WRITE:
328 case ETD_FORMAT:
329 if (iotd->iotd_Count < unit->ChangeCnt) {
330 err = TDERR_DiskChanged;
331 break;
333 case CMD_WRITE:
334 case TD_FORMAT:
335 iotd->iotd_Req.io_Actual = 0;
336 err = TDWrite(unit, &iotd->iotd_Req);
337 break;
339 case NSCMD_ETD_READ64:
340 if (iotd->iotd_Count < unit->ChangeCnt) {
341 err = TDERR_DiskChanged;
342 break;
344 case NSCMD_TD_READ64:
345 case TD_READ64:
346 err = TDRead(unit, &iotd->iotd_Req);
347 break;
349 case NSCMD_ETD_WRITE64:
350 case NSCMD_ETD_FORMAT64:
351 if (iotd->iotd_Count < unit->ChangeCnt) {
352 err = TDERR_DiskChanged;
353 break;
355 case NSCMD_TD_WRITE64:
356 case NSCMD_TD_FORMAT64:
357 case TD_WRITE64:
358 case TD_FORMAT64:
359 err = TDWrite(unit, &iotd->iotd_Req);
360 break;
362 case TD_CHANGENUM:
363 err = IOERR_SUCCESS;
364 iotd->iotd_Req.io_Actual = unit->ChangeCnt;
365 break;
367 case TD_CHANGESTATE:
368 err = IOERR_SUCCESS;
369 iotd->iotd_Req.io_Actual = unit->ImageData ? 0 : 1;
370 break;
372 case TD_PROTSTATUS:
373 err = IOERR_SUCCESS;
374 iotd->iotd_Req.io_Actual = unit->WriteProtect;
375 break;
377 case TD_GETGEOMETRY:
378 err = TDGeometry(unit, &iotd->iotd_Req);
379 break;
381 case TD_EJECT:
382 err = IOERR_SUCCESS;
383 if (unit->ImageData) {
384 RemoveDisk(unit);
385 DiskChange(unit);
387 break;
389 case TD_REMOVE:
390 err = IOERR_SUCCESS;
391 unit->ObsoleteChangeInt = (struct Interrupt *)iotd->iotd_Req.io_Data;
392 break;
394 case TD_ADDCHANGEINT:
395 handler = AllocMem(sizeof(*handler), MEMF_CLEAR);
396 if (handler) {
397 iotd->iotd_Req.io_Error = 0;
398 handler->ci_Interrupt = (struct Interrupt *)iotd->iotd_Req.io_Data;
399 AddTail(unit->ChangeInts, (struct Node *)&handler->ci_Node);
400 } else {
401 iotd->iotd_Req.io_Error = TDERR_NoMem;
403 iotd = NULL;
404 break;
406 case TD_REMCHANGEINT:
407 err = IOERR_SUCCESS;
408 handler = (struct ChangeInt *)unit->ChangeInts->lh_Head;
409 while (handler->ci_Node.mln_Succ) {
410 if (handler->ci_Interrupt == (struct Interrupt *)iotd->iotd_Req.io_Data) {
411 Remove((struct Node *)&handler->ci_Node);
412 FreeMem(handler, sizeof(*handler));
413 break;
415 handler = (struct ChangeInt *)handler->ci_Node.mln_Succ;
417 break;
419 case HD_SCSICMD:
420 err = DoSCSICmd(&iotd->iotd_Req, (struct SCSICmd *)iotd->iotd_Req.io_Data);
421 break;
423 default:
424 err = IOERR_NOCMD;
425 break;
428 if (iotd) {
429 iotd->iotd_Req.io_Error = err;
430 ReplyMsg(&iotd->iotd_Req.io_Message);
436 static void ReadUnitPrefs (struct DiskImageUnit *unit) {
437 TEXT filename[64];
438 SNPrintf(filename, sizeof(filename), "ENV:DiskImage/unit_%ld.xml", unit->UnitNum);
439 if (!ReadPrefs(unit->Prefs, filename)) {
440 SNPrintf(filename, sizeof(filename), "ENVARC:DiskImage/unit_%ld.xml", unit->UnitNum);
441 ReadPrefs(unit->Prefs, filename);
445 static void WriteUnitPrefs (struct DiskImageUnit *unit, BOOL envarc) {
446 TEXT filename[64];
447 if (envarc) {
448 UnLock(CreateDir("ENVARC:DiskImage"));
449 SNPrintf(filename, sizeof(filename), "ENVARC:DiskImage/unit_%ld.xml", unit->UnitNum);
450 WritePrefs(unit->Prefs, filename);
452 UnLock(CreateDir("ENV:DiskImage"));
453 SNPrintf(filename, sizeof(filename), "ENV:DiskImage/unit_%ld.xml", unit->UnitNum);
454 WritePrefs(unit->Prefs, filename);
457 static inline struct IOExtTD *GetIOMsg (struct DiskImageUnit *unit) {
458 struct Library *SysBase = unit->LibBase->SysBase;
459 struct IOExtTD *iotd;
460 ObtainSemaphore(unit->IOSemaphore);
461 iotd = (struct IOExtTD *)GetMsg(unit->IOPort);
462 ReleaseSemaphore(unit->IOSemaphore);
463 return iotd;
466 static void Cleanup (struct DiskImageUnit *unit) {
467 struct Library *SysBase = unit->LibBase->SysBase;
468 RemoveDisk(unit);
469 FreePrefsObject(unit->Prefs);
470 DeleteMsgPort(unit->IOPort);
471 DeleteMsgPort(unit->MsgPort);
472 unit->IOPort = unit->MsgPort = NULL;
475 LONG DOS2IOErr (APTR Self, LONG error) {
476 LONG ret;
477 switch (error) {
478 case ERROR_SEEK_ERROR:
479 ret = TDERR_SeekError;
480 break;
481 case ERROR_DISK_WRITE_PROTECTED:
482 case ERROR_WRITE_PROTECTED:
483 ret = TDERR_WriteProt;
484 break;
485 case ERROR_NO_DISK:
486 ret = TDERR_DiskChanged;
487 break;
488 case ERROR_NO_FREE_STORE:
489 ret = TDERR_NoMem;
490 break;
491 default:
492 ret = TDERR_NotSpecified;
493 break;
495 return ret;
498 static LONG TDGeometry (struct DiskImageUnit *unit, struct IOStdReq *io) {
499 struct DiskImagePlugin *plugin = unit->Plugin;
500 struct DriveGeometry *dg = (struct DriveGeometry *)io->io_Data;
501 LONG error;
503 io->io_Actual = 0;
504 if (io->io_Length < sizeof(struct DriveGeometry)) {
505 return IOERR_BADLENGTH;
508 memset(dg, 0, io->io_Length);
509 dg->dg_SectorSize = 512;
510 dg->dg_CylSectors = 1;
511 dg->dg_Heads = 1;
512 dg->dg_TrackSectors = 1;
513 dg->dg_BufMemType = MEMF_ANY;
514 dg->dg_DeviceType = unit->DeviceType;
515 dg->dg_Flags = unit->Flags;
517 if (!unit->ImageData || !plugin) {
518 io->io_Actual = sizeof(struct DriveGeometry);
519 return IOERR_SUCCESS;
522 error = Plugin_Geometry(plugin, unit->ImageData, dg);
523 if (error == IOERR_SUCCESS) {
524 io->io_Actual = sizeof(struct DriveGeometry);
526 return error;
529 static LONG TDRead (struct DiskImageUnit *unit, struct IOStdReq *io) {
530 struct DiskImagePlugin *plugin = unit->Plugin;
531 if (!unit->ImageData || !plugin) {
532 return TDERR_DiskChanged;
534 return Plugin_Read(plugin, unit->ImageData, io);
537 static LONG TDWrite (struct DiskImageUnit *unit, struct IOStdReq *io) {
538 struct DiskImagePlugin *plugin = unit->Plugin;
539 if (!unit->ImageData || !plugin) {
540 return TDERR_DiskChanged;
542 if (unit->WriteProtect || !plugin->plugin_Write) {
543 return TDERR_WriteProt;
545 return Plugin_Write(plugin, unit->ImageData, io);
548 static void InsertDisk (struct DiskImageUnit *unit, BPTR dir, CONST_STRPTR filename,
549 struct DiskImagePlugin *plugin, STRPTR fullpath, ULONG fullpath_size)
551 struct Library *DOSBase = unit->LibBase->DOSBase;
552 BPTR curr_dir, file;
553 curr_dir = CurrentDir(dir);
554 unit->Name = ASPrintf("%s", FilePart(filename));
555 file = Open(filename, MODE_OLDFILE);
556 if (unit->Name && file) {
557 if (fullpath && fullpath_size) {
558 NameFromFH(file, fullpath, fullpath_size);
560 if (plugin) {
561 unit->Plugin = plugin;
562 unit->ImageData = Plugin_OpenImage(plugin, unit, file, filename);
563 } else {
564 unit->ImageData = OpenImage(NULL, unit, file, filename);
566 } else {
567 const LONG error = !file ? IoErr() : ERROR_NO_FREE_STORE;
568 SetDiskImageError(NULL, unit, error, 0);
570 if (!unit->Plugin) {
571 Close(file);
573 if (!unit->ImageData) {
574 RemoveDisk(unit);
576 CurrentDir(curr_dir);
579 static void RemoveDisk (struct DiskImageUnit *unit) {
580 struct Library *SysBase = unit->LibBase->SysBase;
582 if (unit->Plugin && unit->ImageData) {
583 Plugin_CloseImage(unit->Plugin, unit->ImageData);
585 unit->ImageData =
586 unit->Plugin = NULL;
588 RemoveTempFile(NULL, unit);
590 FreeVec(unit->Name);
591 unit->Name = NULL;
594 static void DiskChange (struct DiskImageUnit *unit) {
595 struct DiskImageBase *libBase = unit->LibBase;
596 struct Library *SysBase = libBase->SysBase;
597 struct Library *UtilityBase = libBase->UtilityBase;
598 struct ChangeInt *handler;
599 struct Hook *hook;
601 unit->ChangeCnt++;
603 if (unit->ObsoleteChangeInt) {
604 Cause(unit->ObsoleteChangeInt);
607 handler = (struct ChangeInt *)unit->ChangeInts->lh_Head;
608 while (handler->ci_Node.mln_Succ) {
609 Cause(handler->ci_Interrupt);
610 handler = (struct ChangeInt *)handler->ci_Node.mln_Succ;
613 ObtainSemaphoreShared(libBase->DiskChangeSemaphore);
614 hook = (struct Hook *)libBase->DiskChangeHooks->lh_Head;
615 while (hook->h_MinNode.mln_Succ) {
616 CallHookPkt(hook, &unit->UnitNum, hook->h_Data);
617 hook = (struct Hook *)hook->h_MinNode.mln_Succ;
619 ReleaseSemaphore(libBase->DiskChangeSemaphore);
622 void SetDiskImageErrorA (APTR Self, struct DiskImageUnit *unit, LONG error,
623 LONG error_string, CONST_APTR error_args)
625 struct DiskImageBase *libBase = unit->LibBase;
626 if (error != NO_ERROR) {
627 unit->Error = error;
628 if (unit->ErrorPtr) {
629 *unit->ErrorPtr = error;
632 if (unit->ErrorString && unit->ErrorStringLength) {
633 if (error_string != NO_ERROR_STRING) {
634 VSNPrintf(unit->ErrorString, unit->ErrorStringLength,
635 GetString(&libBase->LocaleInfo, error_string), error_args);
636 } else if (error != NO_ERROR) {
637 struct Library *DOSBase = libBase->DOSBase;
638 Fault(error, NULL, unit->ErrorString, unit->ErrorStringLength);
643 VARARGS68K void SetDiskImageError (APTR Self, struct DiskImageUnit *unit, LONG error,
644 LONG error_string, ...)
646 VA_LIST args;
647 VA_START(args, error_string);
648 SetDiskImageErrorA(Self, unit, error, error_string, VA_ARG(args, CONST_APTR));
649 VA_END(args);