There are 4 classes of device drivers that Portfolio OS supports:
I'll describe each class below.
Common for most drivers is the device creation process. Please read Items, tag_args, signals and i/o architecture basics before proceeding.
Creating of the named device steps :
You are in privileged main, but not in SVC mode. You can use SWI here, but do not access HW!!!
Create KERNELNODE+DRIVERNODE Item with a minimum of following tags - that will be your actual driver work:
TAG_ITEM_PRI (should be less than real time - pri>=1)
TAG_ITEM_NAME - the name of your driver. Not the name of your named device!!
CREATEDRIVER_TAG_MAXCMDS, minimum 3 driver commands : driver_write, driver_read, driver_status
CREATEDRIVER_TAG_CMDTABLE , driver_cmdtable array of function pointers for the driver commands.
CREATEDRIVER_TAG_INIT - driver_init function – do all the HW init stuff here.
CREATEDRIVER_TAG_ABORTIO - driver_abortio function
Create KERNELNODE+DEVICENOTE Item with a minimum of following tags - that will be your named device interface:
TAG_ITEM_PRI (should be much less than real time, pri >=128)
TAG_ITEM_NAME - the name of your named device. Used in “OpenNamedDevice()” call.
CREATEDEVICE_TAG_DRVR - the item of your device driver – result of the previous step
CREATEDEVICE_TAG_INIT - dev_init function of your named device – clear your reference counters, no HW work here
CREATEDEVICE_TAG_OPEN - dev_open function of your named device – increment usage counter, and tell HW that it is open if counter was 0
CREATEDEVICE_TAG_CLOSE - dev_close function of your named device – decrement usage counter, and tell HW that it is closed if counter reached 0
Optionally obtain parent's id for the signal loop
AllocSignal with mask 0 or what ever parent may send us
enter the loop, with WaitSignal, and exit the loop if “SIGF_ABORT” was received
clean after yourself.
Function overview :
Item driver_init(struct Driver * drv); – called in SVC context, do what you want with HW, and return dev→dev.n_Item or error.
void driver_abortio(struct IOReq * ior); – called in SVC context, let's you know that the SW finished this IORequest, and disposing them. For async devices – drop what you're doing and don't send signals back.
int32 driver_write(struct IOReq * ior); – called in SVC context, when DoIO is is called with ioinfo.ioi_Command == CMD_WRITE. Return value is passed as DoIO return value. ior→io_Info contains your ioinfo parameters. Typically ioi_Send has the buffer details. For async io use io_Callback when ready, but you have to track your IOReqs!!!
int32 driver_read(struct IOReq * ior); – called in SVC context, when DoIO is is called with ioinfo.ioi_Command == CMD_READ. Return value is passed as DoIO return value. ior→io_Info contains your ioinfo parameters. Typically ioi_Send has the buffer details. For async io use io_Callback when ready, but you have to track your IOReqs!!!
int32 driver_status(struct IOReq * ior); – called in SVC context, when DoIO is is called with ioinfo.ioi_Command == CMD_STATUS. Return value is passed as DoIO return value. ior→io_Info contains your ioinfo parameters. Typically the status is returned. No async statuses are supported. Use CMD_READ for anything async.
int32 dev_init(struct Device * dev); – called in SVC context, during the device creation call. It is intended for your named device init – software init : clear dev→dev_OpenCnt ; set dev→MaxUnitNum to something meaningful (0 - if you only have 1 unit). Think “/dev/tty0 , /dev/tty1…. /dev/ttyN , where N is dev→MaxUnitNum. You must return your item dev→dev.n_Item . Optionally you can do HW init here.
int32 dev_open(struct Device * dev); – called in SVC context, when OpenNamedDevice(“name”, unitNum) or FindAndOpenDevice is called. Do your house keeping, by incrementing dev→dev_OpenCnt . If it was the first open, take care of notifying your HW if needed. you must return your item dev→dev.n_Item or error. Unfortunately unitNum is not passed as of PF2.5 .. Eh???
void dev_close(struct Device * dev); – called in SVC context, when your named device item is closed. Do your house keeping, by decrementing dev→dev_OpenCnt . If it was the last close (i.e. count now is 0), take care of notifying your HW if needed.
driver_cmdtable - is an array of function pointers to your driver_write as [0], driver_read as [1], driver_status as [2]. You can add your own custom commands, taking care of indicating the size in CREATEDRIVER_TAG_MAXCMDS
All functions in SVC content must use non-swi style of Folio syscalls.. You cannot use SWI !!!!. See sample project's kernel_calls.s as example of how to access kernel syscalls.
Device classes:
Loadable named device - basically privileged aif (3do flags 2), but has to be loaded by the user app.
OS is not aware of their existence until loaded.
OS Loadable named device - privileged daemonized aif (3do flags 2+4). Place it in System/Daemons. Loaded by the Operator Folio prior to shell.
CPort driver - CPort header followed by privileged aif (3do flags 2). EventBroker loads it as “$boot/System/Drivers/cport%x.rom” where %x is first 1-2 bytes of the device id on the PBus wire.
Device supplied device driver - only seen on XBus. CDRom or any other XBus devices can provide it's own driver. See 256kb NVRam extension as potential example. It has XBus header followed by privileged aif (3do flags 2).
CPort driver header :
typedef struct{
uint32 headerchecksum;
uint32 filesize;
uint32 familycode;
uint32 familyversion;
uint32 imagechecksum;
uint32 pad[3];
} cport_hdr_t;
NOTE : All privileged aifs must be signed by appropriate key (depending on flag 4).