WIP build diskimage device with %build_module.
[AROS.git] / workbench / devs / diskimage / device / unit.c
blob09d86624bd40361e1e77d33622378c5f927bb095
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 <proto/expat_au.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_UFH3(LONG, UnitProcEntry,
57 AROS_UFHA(STRPTR, argstr, A0),
58 AROS_UFHA(ULONG, arglen, D0),
59 AROS_UFHA(struct Library *, SysBase, A6)
62 AROS_USERFUNC_INIT
63 #else
64 int UnitProcEntry (void) {
65 #endif
66 struct Process *proc;
67 struct DiskImageMsg *msg;
68 struct DiskImageUnit *unit;
69 struct DeathMessage *dm;
71 dbug(("UnitProcEntry()\n"));
73 proc = (struct Process *)FindTask(NULL);
74 WaitPort(&proc->pr_MsgPort);
75 msg = (struct DiskImageMsg *)GetMsg(&proc->pr_MsgPort);
76 if (!msg->dim_Unit || msg->dim_Command != DICMD_STARTUP) {
77 return RETURN_FAIL;
80 unit = msg->dim_Unit;
81 dm = unit->DeathMsg;
83 dm->dm_ReturnCode = UnitProcMain(unit);
84 dm->dm_Result2 = IoErr();
86 Forbid();
87 ReplyMsg(&dm->dm_Msg);
88 return dm->dm_ReturnCode;
89 #ifdef __AROS__
90 AROS_USERFUNC_EXIT
91 #endif
94 int UnitProcMain (struct DiskImageUnit *unit) {
95 struct DiskImageBase *libBase = unit->LibBase;
96 struct Library *SysBase = libBase->SysBase;
97 struct Library *UtilityBase = libBase->UtilityBase;
98 struct IOExtTD *iotd;
99 struct DiskImageMsg *msg;
100 struct TagItem *ti, *tstate;
101 LONG err;
102 ULONG sigmask;
103 CONST_STRPTR filename, plugin_name;
104 struct ChangeInt *handler;
105 BOOL disk_change = FALSE;
107 dbug(("UnitProcMain()\n"));
109 unit->IOPort = CreateMsgPort();
110 unit->MsgPort = CreateMsgPort();
111 if (!unit->IOPort || !unit->MsgPort) {
112 Cleanup(unit);
113 return RETURN_FAIL;
116 unit->Prefs = AllocPrefsDictionary();
117 if (!unit->Prefs) {
118 Cleanup(unit);
119 return RETURN_FAIL;
122 dbug(("replying to start msg\n"));
123 ReplyMsg(&unit->DiskImageMsg->dim_Msg);
125 #ifdef __AROS__
126 ObtainSemaphore(libBase->PluginSemaphore);
127 if (IsListEmpty(libBase->Plugins)) {
128 InitLocaleInfo(SysBase, &libBase->LocaleInfo, "diskimagedevice.catalog");
129 LoadPlugins(libBase);
131 ReleaseSemaphore(libBase->PluginSemaphore);
132 #endif
134 ReadUnitPrefs(unit);
135 unit->DeviceType = DictGetIntegerForKey(unit->Prefs, "DeviceType", DG_DIRECT_ACCESS);
136 unit->Flags = DictGetIntegerForKey(unit->Prefs, "Flags", DGF_REMOVABLE);
137 filename = DictGetStringForKey(unit->Prefs, "DiskImageFile", NULL);
138 plugin_name = DictGetStringForKey(unit->Prefs, "Plugin", NULL);
139 if (filename) {
140 struct DiskImagePlugin *plugin = NULL;
141 ObtainSemaphoreShared(libBase->PluginSemaphore);
142 if (plugin_name) {
143 plugin = (struct DiskImagePlugin *)FindName(libBase->Plugins, plugin_name);
145 if (!plugin_name || plugin) {
146 APTR window;
147 window = SetProcWindow((APTR)-1);
148 InsertDisk(unit, ZERO, filename, plugin, NULL, 0);
149 SetProcWindow(window);
151 ReleaseSemaphore(libBase->PluginSemaphore);
154 sigmask = (1UL << unit->IOPort->mp_SigBit)|(1UL << unit->MsgPort->mp_SigBit);
155 dbug(("entering main loop\n"));
156 for (;;) {
157 Wait(sigmask);
159 while ((msg = (struct DiskImageMsg *)GetMsg(unit->MsgPort))) {
160 switch (msg->dim_Command) {
161 case DICMD_DIE:
162 Cleanup(unit);
163 return RETURN_OK;
165 case DICMD_TAGLIST:
166 if ((tstate = msg->dim_Tags)) {
167 BPTR curr_dir = ZERO;
168 struct DiskImagePlugin *plugin = NULL;
170 unit->Error = NO_ERROR;
171 unit->ErrorString = NULL;
173 ObtainSemaphoreShared(libBase->PluginSemaphore);
174 while (!unit->Error && (ti = NextTagItem(&tstate))) {
175 switch (ti->ti_Tag) {
176 case DITAG_Error:
177 unit->ErrorPtr = (LONG *)ti->ti_Data;
178 if (unit->ErrorPtr) {
179 *unit->ErrorPtr = NO_ERROR;
181 break;
183 case DITAG_ErrorString:
184 unit->ErrorString = (STRPTR)ti->ti_Data;
185 if (unit->ErrorString && unit->ErrorStringLength) {
186 unit->ErrorString[0] = 0;
188 break;
190 case DITAG_ErrorStringLength:
191 unit->ErrorStringLength = ti->ti_Data;
192 if (unit->ErrorString && unit->ErrorStringLength) {
193 unit->ErrorString[0] = 0;
195 break;
197 case DITAG_Screen:
198 unit->Screen = (struct Screen *)ti->ti_Data;
199 break;
201 case DITAG_Password:
202 unit->Password = (CONST_STRPTR)ti->ti_Data;
203 break;
205 case DITAG_CurrentDir:
206 curr_dir = (BPTR)ti->ti_Data;
207 break;
209 case DITAG_Plugin:
210 if (!ti->ti_Data) break;
211 plugin = (void *)FindName(libBase->Plugins, (STRPTR)ti->ti_Data);
212 if (!plugin) {
213 SetDiskImageError(NULL, unit, ERROR_OBJECT_NOT_FOUND, 0);
214 break;
216 break;
218 case DITAG_Filename: {
219 APTR image_data = unit->ImageData;
220 TEXT fullpath[512];
221 RemoveDisk(unit);
222 filename = (CONST_STRPTR)ti->ti_Data;
223 if (filename) {
224 InsertDisk(unit, curr_dir, filename, plugin, fullpath, sizeof(fullpath));
226 if (image_data || unit->ImageData) {
227 disk_change = TRUE;
228 if (unit->ImageData) {
229 DictSetObjectForKey(unit->Prefs,
230 AllocPrefsString(fullpath),
231 "DiskImageFile");
232 } else {
233 DictRemoveObjForKey(unit->Prefs,
234 "DiskImageFile");
236 if (unit->ImageData && plugin) {
237 DictSetObjectForKey(unit->Prefs,
238 AllocPrefsString(plugin->Node.ln_Name),
239 "Plugin");
240 } else {
241 DictRemoveObjForKey(unit->Prefs,
242 "Plugin");
244 WriteUnitPrefs(unit, TRUE);
246 break;
249 case DITAG_WriteProtect:
250 unit->WriteProtect = ti->ti_Data ? TRUE : FALSE;
251 break;
253 case DITAG_GetImageName:
254 if (!unit->Name) {
255 *(STRPTR *)ti->ti_Data = NULL;
256 break;
258 *(STRPTR *)ti->ti_Data = ASPrintf("%s", unit->Name);
259 if (!ti->ti_Data) {
260 SetDiskImageError(NULL, unit, ERROR_NO_FREE_STORE, 0);
261 break;
263 break;
265 case DITAG_GetWriteProtect:
266 *(BOOL *)ti->ti_Data = unit->WriteProtect;
267 break;
269 case DITAG_DiskImageType:
270 if (unit->ImageData)
271 *(ULONG *)ti->ti_Data = DITYPE_RAW;
272 else
273 *(ULONG *)ti->ti_Data = DITYPE_NONE;
274 break;
276 case DITAG_SetDeviceType:
277 if (unit->DeviceType != ti->ti_Data) {
278 unit->DeviceType = ti->ti_Data;
279 DictSetObjectForKey(unit->Prefs,
280 AllocPrefsInteger(unit->DeviceType),
281 "DeviceType");
282 WriteUnitPrefs(unit, TRUE);
284 break;
286 case DITAG_GetDeviceType:
287 *(UBYTE *)ti->ti_Data = unit->DeviceType;
288 break;
290 case DITAG_SetFlags:
291 if (unit->Flags != ti->ti_Data) {
292 unit->Flags = ti->ti_Data;
293 DictSetObjectForKey(unit->Prefs,
294 AllocPrefsInteger(unit->Flags),
295 "Flags");
296 WriteUnitPrefs(unit, TRUE);
298 break;
300 case DITAG_GetFlags:
301 *(UBYTE *)ti->ti_Data = unit->Flags;
302 break;
303 } /* switch */
304 } /* while */
305 ReleaseSemaphore(libBase->PluginSemaphore);
307 unit->Screen = NULL;
308 unit->Password = NULL;
309 } /* if */
310 break;
311 } /* switch */
312 ReplyMsg(&msg->dim_Msg);
314 if (disk_change) {
315 DiskChange(unit);
316 disk_change = FALSE;
319 while ((iotd = GetIOMsg(unit))) {
320 switch (iotd->iotd_Req.io_Command) {
321 case ETD_READ:
322 if (iotd->iotd_Count < unit->ChangeCnt) {
323 err = TDERR_DiskChanged;
324 break;
326 case CMD_READ:
327 iotd->iotd_Req.io_Actual = 0;
328 err = TDRead(unit, &iotd->iotd_Req);
329 break;
331 case ETD_WRITE:
332 case ETD_FORMAT:
333 if (iotd->iotd_Count < unit->ChangeCnt) {
334 err = TDERR_DiskChanged;
335 break;
337 case CMD_WRITE:
338 case TD_FORMAT:
339 iotd->iotd_Req.io_Actual = 0;
340 err = TDWrite(unit, &iotd->iotd_Req);
341 break;
343 case NSCMD_ETD_READ64:
344 if (iotd->iotd_Count < unit->ChangeCnt) {
345 err = TDERR_DiskChanged;
346 break;
348 case NSCMD_TD_READ64:
349 case TD_READ64:
350 err = TDRead(unit, &iotd->iotd_Req);
351 break;
353 case NSCMD_ETD_WRITE64:
354 case NSCMD_ETD_FORMAT64:
355 if (iotd->iotd_Count < unit->ChangeCnt) {
356 err = TDERR_DiskChanged;
357 break;
359 case NSCMD_TD_WRITE64:
360 case NSCMD_TD_FORMAT64:
361 case TD_WRITE64:
362 case TD_FORMAT64:
363 err = TDWrite(unit, &iotd->iotd_Req);
364 break;
366 case TD_CHANGENUM:
367 err = IOERR_SUCCESS;
368 iotd->iotd_Req.io_Actual = unit->ChangeCnt;
369 break;
371 case TD_CHANGESTATE:
372 err = IOERR_SUCCESS;
373 iotd->iotd_Req.io_Actual = unit->ImageData ? 0 : 1;
374 break;
376 case TD_PROTSTATUS:
377 err = IOERR_SUCCESS;
378 iotd->iotd_Req.io_Actual = unit->WriteProtect;
379 break;
381 case TD_GETGEOMETRY:
382 err = TDGeometry(unit, &iotd->iotd_Req);
383 break;
385 case TD_EJECT:
386 err = IOERR_SUCCESS;
387 if (unit->ImageData) {
388 RemoveDisk(unit);
389 DiskChange(unit);
391 break;
393 case TD_REMOVE:
394 err = IOERR_SUCCESS;
395 unit->ObsoleteChangeInt = (struct Interrupt *)iotd->iotd_Req.io_Data;
396 break;
398 case TD_ADDCHANGEINT:
399 handler = AllocMem(sizeof(*handler), MEMF_CLEAR);
400 if (handler) {
401 iotd->iotd_Req.io_Error = 0;
402 handler->ci_Interrupt = (struct Interrupt *)iotd->iotd_Req.io_Data;
403 AddTail(unit->ChangeInts, (struct Node *)&handler->ci_Node);
404 } else {
405 iotd->iotd_Req.io_Error = TDERR_NoMem;
407 iotd = NULL;
408 break;
410 case TD_REMCHANGEINT:
411 err = IOERR_SUCCESS;
412 handler = (struct ChangeInt *)unit->ChangeInts->lh_Head;
413 while (handler->ci_Node.mln_Succ) {
414 if (handler->ci_Interrupt == (struct Interrupt *)iotd->iotd_Req.io_Data) {
415 Remove((struct Node *)&handler->ci_Node);
416 FreeMem(handler, sizeof(*handler));
417 break;
419 handler = (struct ChangeInt *)handler->ci_Node.mln_Succ;
421 break;
423 case HD_SCSICMD:
424 err = DoSCSICmd(&iotd->iotd_Req, (struct SCSICmd *)iotd->iotd_Req.io_Data);
425 break;
427 default:
428 err = IOERR_NOCMD;
429 break;
432 if (iotd) {
433 iotd->iotd_Req.io_Error = err;
434 ReplyMsg(&iotd->iotd_Req.io_Message);
440 static void ReadUnitPrefs (struct DiskImageUnit *unit) {
441 TEXT filename[64];
442 SNPrintf(filename, sizeof(filename), "ENV:DiskImage/unit_%ld.xml", unit->UnitNum);
443 if (!ReadPrefs(unit->Prefs, filename)) {
444 SNPrintf(filename, sizeof(filename), "ENVARC:DiskImage/unit_%ld.xml", unit->UnitNum);
445 ReadPrefs(unit->Prefs, filename);
449 static void WriteUnitPrefs (struct DiskImageUnit *unit, BOOL envarc) {
450 TEXT filename[64];
451 if (envarc) {
452 UnLock(CreateDir("ENVARC:DiskImage"));
453 SNPrintf(filename, sizeof(filename), "ENVARC:DiskImage/unit_%ld.xml", unit->UnitNum);
454 WritePrefs(unit->Prefs, filename);
456 UnLock(CreateDir("ENV:DiskImage"));
457 SNPrintf(filename, sizeof(filename), "ENV:DiskImage/unit_%ld.xml", unit->UnitNum);
458 WritePrefs(unit->Prefs, filename);
461 static inline struct IOExtTD *GetIOMsg (struct DiskImageUnit *unit) {
462 struct Library *SysBase = unit->LibBase->SysBase;
463 struct IOExtTD *iotd;
464 ObtainSemaphore(unit->IOSemaphore);
465 iotd = (struct IOExtTD *)GetMsg(unit->IOPort);
466 ReleaseSemaphore(unit->IOSemaphore);
467 return iotd;
470 static void Cleanup (struct DiskImageUnit *unit) {
471 struct Library *SysBase = unit->LibBase->SysBase;
472 RemoveDisk(unit);
473 FreePrefsObject(unit->Prefs);
474 DeleteMsgPort(unit->IOPort);
475 DeleteMsgPort(unit->MsgPort);
476 unit->IOPort = unit->MsgPort = NULL;
479 LONG DOS2IOErr (APTR Self, LONG error) {
480 LONG ret;
481 switch (error) {
482 case ERROR_SEEK_ERROR:
483 ret = TDERR_SeekError;
484 break;
485 case ERROR_DISK_WRITE_PROTECTED:
486 case ERROR_WRITE_PROTECTED:
487 ret = TDERR_WriteProt;
488 break;
489 case ERROR_NO_DISK:
490 ret = TDERR_DiskChanged;
491 break;
492 case ERROR_NO_FREE_STORE:
493 ret = TDERR_NoMem;
494 break;
495 default:
496 ret = TDERR_NotSpecified;
497 break;
499 return ret;
502 static LONG TDGeometry (struct DiskImageUnit *unit, struct IOStdReq *io) {
503 struct DiskImagePlugin *plugin = unit->Plugin;
504 struct DriveGeometry *dg = (struct DriveGeometry *)io->io_Data;
505 LONG error;
507 io->io_Actual = 0;
508 if (io->io_Length < sizeof(struct DriveGeometry)) {
509 return IOERR_BADLENGTH;
512 memset(dg, 0, io->io_Length);
513 dg->dg_SectorSize = 512;
514 dg->dg_CylSectors = 1;
515 dg->dg_Heads = 1;
516 dg->dg_TrackSectors = 1;
517 dg->dg_BufMemType = MEMF_ANY;
518 dg->dg_DeviceType = unit->DeviceType;
519 dg->dg_Flags = unit->Flags;
521 if (!unit->ImageData || !plugin) {
522 io->io_Actual = sizeof(struct DriveGeometry);
523 return IOERR_SUCCESS;
526 error = Plugin_Geometry(plugin, unit->ImageData, dg);
527 if (error == IOERR_SUCCESS) {
528 io->io_Actual = sizeof(struct DriveGeometry);
530 return error;
533 static LONG TDRead (struct DiskImageUnit *unit, struct IOStdReq *io) {
534 struct DiskImagePlugin *plugin = unit->Plugin;
535 if (!unit->ImageData || !plugin) {
536 return TDERR_DiskChanged;
538 return Plugin_Read(plugin, unit->ImageData, io);
541 static LONG TDWrite (struct DiskImageUnit *unit, struct IOStdReq *io) {
542 struct DiskImagePlugin *plugin = unit->Plugin;
543 if (!unit->ImageData || !plugin) {
544 return TDERR_DiskChanged;
546 if (unit->WriteProtect || !plugin->plugin_Write) {
547 return TDERR_WriteProt;
549 return Plugin_Write(plugin, unit->ImageData, io);
552 static void InsertDisk (struct DiskImageUnit *unit, BPTR dir, CONST_STRPTR filename,
553 struct DiskImagePlugin *plugin, STRPTR fullpath, ULONG fullpath_size)
555 struct Library *DOSBase = unit->LibBase->DOSBase;
556 BPTR curr_dir, file;
557 curr_dir = CurrentDir(dir);
558 unit->Name = ASPrintf("%s", FilePart(filename));
559 file = Open(filename, MODE_OLDFILE);
560 if (unit->Name && file) {
561 if (fullpath && fullpath_size) {
562 NameFromFH(file, fullpath, fullpath_size);
564 if (plugin) {
565 unit->Plugin = plugin;
566 unit->ImageData = Plugin_OpenImage(plugin, unit, file, filename);
567 } else {
568 unit->ImageData = OpenImage(NULL, unit, file, filename);
570 } else {
571 const LONG error = !file ? IoErr() : ERROR_NO_FREE_STORE;
572 SetDiskImageError(NULL, unit, error, 0);
574 if (!unit->Plugin) {
575 Close(file);
577 if (!unit->ImageData) {
578 RemoveDisk(unit);
580 CurrentDir(curr_dir);
583 static void RemoveDisk (struct DiskImageUnit *unit) {
584 struct Library *SysBase = unit->LibBase->SysBase;
586 if (unit->Plugin && unit->ImageData) {
587 Plugin_CloseImage(unit->Plugin, unit->ImageData);
589 unit->ImageData =
590 unit->Plugin = NULL;
592 RemoveTempFile(NULL, unit);
594 FreeVec(unit->Name);
595 unit->Name = NULL;
598 static void DiskChange (struct DiskImageUnit *unit) {
599 struct DiskImageBase *libBase = unit->LibBase;
600 struct Library *SysBase = libBase->SysBase;
601 struct Library *UtilityBase = libBase->UtilityBase;
602 struct ChangeInt *handler;
603 struct Hook *hook;
605 unit->ChangeCnt++;
607 if (unit->ObsoleteChangeInt) {
608 Cause(unit->ObsoleteChangeInt);
611 handler = (struct ChangeInt *)unit->ChangeInts->lh_Head;
612 while (handler->ci_Node.mln_Succ) {
613 Cause(handler->ci_Interrupt);
614 handler = (struct ChangeInt *)handler->ci_Node.mln_Succ;
617 ObtainSemaphoreShared(libBase->DiskChangeSemaphore);
618 hook = (struct Hook *)libBase->DiskChangeHooks->lh_Head;
619 while (hook->h_MinNode.mln_Succ) {
620 CallHookPkt(hook, &unit->UnitNum, hook->h_Data);
621 hook = (struct Hook *)hook->h_MinNode.mln_Succ;
623 ReleaseSemaphore(libBase->DiskChangeSemaphore);
626 void SetDiskImageErrorA (APTR Self, struct DiskImageUnit *unit, LONG error,
627 LONG error_string, CONST_APTR error_args)
629 struct DiskImageBase *libBase = unit->LibBase;
630 if (error != NO_ERROR) {
631 unit->Error = error;
632 if (unit->ErrorPtr) {
633 *unit->ErrorPtr = error;
636 if (unit->ErrorString && unit->ErrorStringLength) {
637 if (error_string != NO_ERROR_STRING) {
638 VSNPrintf(unit->ErrorString, unit->ErrorStringLength,
639 GetString(&libBase->LocaleInfo, error_string), error_args);
640 } else if (error != NO_ERROR) {
641 struct Library *DOSBase = libBase->DOSBase;
642 Fault(error, NULL, unit->ErrorString, unit->ErrorStringLength);
647 VARARGS68K void SetDiskImageError (APTR Self, struct DiskImageUnit *unit, LONG error,
648 LONG error_string, ...)
650 VA_LIST args;
651 VA_START(args, error_string);
652 SetDiskImageErrorA(Self, unit, error, error_string, VA_ARG(args, CONST_APTR));
653 VA_END(args);