• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

looking for Device Driver development resources

jyeandle

Member
New to Mac programming, although I have a lot of C experience in windows/linux/embedded system environments. I'm using Codewarrior 8 to write a driver in C on the mac. And I'm looking for resources on programming drivers for the mac (targeting system 7). I'm building a serial to wifi adapter based on the esp8266. The communication between the mac and the adapter is based on a modified slip protocol, using unused slip escape sequences to handle wifi setup/connection/etc. I have the firmware on the esp written and working correctly, and it's usable on the mac using a simple program I wrote that outputs the setup commands over serial then starts the interslip driver to handle ip traffic. But that approach is kludgy as hell, and doesn't allow for any feedback from the adapter once the traffic has been transferred over to the interslip driver, since the interslip driver doesn't understand any of the custom escape sequences and just drops those packets. 

So, looking to roll my own driver. But, documentation on driver writing is sparse, and actual working code examples are even harder to come by. I've poured through Inside Macintosh I,II,III and Inside Macintosh: Devices, and while it was helpful in learning how drivers are structured, and how the various calls work, it was less than helpful in learning the actual mechanics of writing one.

Right now I've gotten my driver to respond to control and status calls, and in response to control calls send commands over serial to the adapter. Where I'm bogging down is getting a response from the adapter.

Say, for example, I do the control call to my driver to enable the connection to the adapter. It successfully opens the serial port drivers, sets the handshake and buffers, and sends the command to the adapter to enable it. The adapter responds by scanning for access points and sending a list of APs in range. To capture that, and any ongoing communication from the adapter, the driver starts a timer task loop, which checks for data coming in over the serial line using SerGetBuf, and if there's data it reads it in and processes it. The idea was to have it process adapter related information and store that into a struct which can be read by status calls to the driver, and IP traffic gets put into a packet queue to be read out as needed through prime calls.

But, it's not working out that way. SerGetBuf correctly sees incoming data, but PBread hangs the computer. It doesn't matter if it's immediate, synchronous, or asynchronous. PBread in the timer task hangs the computer every time. And I've found NOTHING anywhere in inside macintosh which clues me in to why that would be. I've thought about replacing the timer task with a VBL task, but IM: Processes makes it clear you can't do any memory allocation at interrupt time, and my routine to process the incoming data does. So, is there any reason why PBread hangs the computer when called in a timer task? And if thats an insolvable hurdle, is there a better way to continuously receive and process data from the serial port within a device driver context?

Also, and somewhat related, I'm somewhat confused by completion routines. IM has almost no information as to what exactly goes into a completion routine. What is passed to it? What is it expected to do? All I'm really clear on is that when it's done with whatever it needs to do it's supposed to call IODone.

 

jyeandle

Member
Rewrote my code to use a VBL task instead of a Timer Task, and now it's not hanging on every read from the serial port. But, next question:

Is there an elegeant way to pass a pointer to a completion routine? According to Inside Macintosh, a completion routine gets called with the ioResult in D0, and a pointer to the ParamBlockRec in A0. All well and good. But I'd like to access a different structure full of driver specific variables. Should I use a global variable, or is there some slick way to pass an additional pointer that I'm not thinking of?

 

Crutch

Well-known member
I believe the ParamBlockRec contains the driverRefNum. Using that I believe you can get the device control entry by calling GetDCtlEntry. I believe that has a handle called dCtlStorage you can set to point to private storage. From IM:Drivers:


Writing Open and Close Routines


You must provide both an open routine and a close routine for your device driver. The open routine should allocate any private storage your driver requires and place a handle to this storage in the dCtlStorage field of the device control entry. After allocating memory, the open routine should perform any other preparation required by your driver.

If your open routine installs an interrupt handler, you may want to store a pointer to the device control entry in private storage where it will be available for the interrupt handler. The section "Handling Asynchronous I/O" on page 1-37 discusses interrupt handling in more detail.

 

jyeandle

Member
I somehow had missed GetDCtlEntry, though I did figure out another solution that works. I just put the ParamBlockRec that I passed to the PBread as the first listing in the struct that is the dCtlStorage field. So, the pointer that gets passed to the completion routine for the ParamBlockRec is the same address as dCtlStorage, and only needs to be typecast to my storage struct to give me full access to the data I want.

 
Top