SIS1100 driver functions
To fully understand why certain functions are neccesary it is recommended that you have read the part "Driver Structure".
Content:
Overview
Opening/Closing the connection to the user client
Getting information about the hardware
VME single read/write
VME block read/write
VME list read/write
VME direct bus access
Overview
So far you can access 16 different functions in the driver. They are grouped by VME access method. Because you can not access these functions directly you need to tell the user client which function you want to call. This is done by providing the right constant determining the desired function. In Mac OS X you have four different ways how to access a function in the driver:
- one or more scalar input parameters, one or more scalar output parameters (IOConnectMethodScalarIScalarO)
- one or more scalar input parameters, one structure output parameter (IOConnectMethodScalarIStructureO)
- one or more scalar input parameters, one structure input parameter (IOConnectMethodScalarIStructureI)
- one structure input parameter, one structure output parameter (IOConnectMethodStructurIStructureO)
The following table will give you an overview over the function constants and the access method.
Connections with user client | |
---|---|
SISUserClientOpen | IOConnectMethodScalarIScalarO |
SISUserClientClose | IOConnectMethodScalarIScalarO |
Getting information | |
PLXReadLocalSpace0 | IOConnectMethodScalarIScalarO |
PLXWriteLocalSpace0 | IOConnectMethodScalarIScalarO |
PLXReadLocalSpace1 | IOConnectMethodScalarIScalarO |
PLXWriteLocalSpace1 | IOConnectMethodScalarIScalarO |
VME single read/write | |
VMERead | IOConnectMethodStructureIStructureO |
VMEWrite | IOConnectMethodStructureIStructureO |
VME block read/write | |
VMEBlockRead | IOConnectMethodStructureIStructureO |
VMEBlockWrite | IOConnectMethodStructureIStructureO |
VMEReadList | IOConnectMethodStructureIStructureO |
VMEWriteList | IOConnectMethodStructureIStructureO |
VME direct bus access | |
VMEMapVMEMemory | IOConnectMethodStructureIStructureO |
VMEMappedRead | IOConnectMethodStructureIStructureO |
VMEMappedWrite | IOConnectMethodStructureIStructureO |
VMEFlushAllMaps | IOConnectMethodScalarIScalarO |
All these methods will return a kern_return_t. So a possible function call in your application should look like this:
Code sample 1: Possible function call to the driver
|
To successfully call a function in the driver you need to include the "SISUserClientStuff.h" header file. It defines enumeration constants needed to select the right function in the user client. The header file "MacOSX_sis1100_var.h" is necessary to provide you with the right data structures you need. These files are part of the driver distribution. Additionally you need to link against the IOKit Framework provided by Apple.
Opening/Closing the connection to the user client
Opening a connection to the user client needs a bit of preparation. First you need to obtain a Mach port to communicate with the IOKit. The second step is to find the driver by matching its main class against the ones registered in the IORegistry. If we have got a match, then we can instantiate an instance of the user client (normally only one instance is allowed) and obtain a Mach port to communicate with the driver. Finally we can call the open method in the user client to do the basic initialisation.
The following code sample will show how you can complete this task.
Code sample 2: Opening the connection to the user client
io_connect_t open() |
Once you have completed opening the connection to the user client you can use the obtained dataPort to communicate with the driver. How to do this is shown in the next section.
When you are done communicating with the driver you must close the dataPort so that other applications may access the driver. The next code sample will show you how to do this.
Code sample 3: Closing the connection to the user client
int close(io_connect_t dataPort) |
Getting information about the hardware
The following four methods allow you to get some information about the installed hardware or to implement some custom protocoll you need.
Getting information
The PLXReadLocalSpace0 method allows you to read information about the SIS1100 PCI card which is stored in the first memory area of the PCI card.
Code sample 4: Getting PCI card information
int firmware_version() |
This code sample shows you how to retrieve information about the firmware version of the PCI card. The parameters are described below:
dataPort this is the mach communication port obtained by open()
PLXReadLocalSpace0 this is the method name you want to call in the driver
2 number of input parameters
1 number of output parameters
4 size of the requested data word (1, 2 and 4 allowed, input)
0x0 the offset you want to read from (input)
&value the address where you want to store the requested value (output)
The PLXReadLocalSpace1 method works the same way.
Writing data manually
For writing data manually into the cards memory you can either use PLXWriteLocalSpace0 or PLXWriteLocalSpace1. They differ only in the memory space into which they are writing.
Code sampel 5: Writing data manually
void write() |
The parameters are mainly the same as in PLXReadLocalSpace0, except that you don't have an output parameter.
VME single read/write
If you want to transfer single bytes, words or double words, then you can use the VME single read/write functions implemented by the driver.
Because some date needs to be transfered in and out of the driver you need to setup a sis1100_vme_req data structure. This structure is defined in the header file "MacOSX_sis1100_var.h" and is shown in code sample 4.
Code sample 6: The sis1100_vme_req data structure
struct sis1100_vme_req |
size stores the number of bytes you want to be transfered. Allowed values for single read/write are: 1 (byte), 2 (word) and 4 (double word).
am stores the address modifier if you want to use one
addr stores the address you want to read from or write to
data stores or returns the data you want to write or read
error returns the error code if one occurred
The following two code samples show you how to read and write data with the driver.
Code sample 7: VME single read
struct sis1100_vme_req req, answer; |
dataPort this is the data port obtained from the open()-method
VMERead this is the function index of the function in the kernel
structSize the size of the input structure
&outStructSize address where the driver can store the size of the output structure
&req address of the input structure
&answer address of the ouput structure
Code sample 8: VME single write
struct sis1100_vme_req req, answer; |
dataPort this is the data port obtained from the open()-method
VMERead this is the function index of the function in the kernel
structSize the size of the input structure
&outStructSize address where the driver can store the size of the output structure
&req address of the input structure
&answer address of the ouput structure
VME block read/write
The VME block transfer is intended to use with larger amounts of data (e.g. if you want to upload a program into a VME module). The principles are mainly the same as with the single read/write, except that the data structure is a bit different. Note that with the block transfer you can only access sequentiell addresses!
Code sample 9: data structure for block transfer
struct sis1100_vme_block_req { |
In addition to the sis1100_vme_req there are two more data fields.
fifo this data field is not used at the moment
num this integer is used to tell the driver how much blocks you want to transfer
Note that the data field is now a pointer pointing to the address where the data blocks are stored. Please be aware that the application has to take care that this pointer is pointing to a valid memory area. Otherwise the application will crash or even worse your whole system. For allocating memory you should use calloc instead of malloc, so that the memory area is contiguous. The following code samples show you how to initiate a block transfer.
Code sample 10: block read
struct sis1100_vme_block_req req, answer; |
The driver will copy the data address is pointing to and then transfer it to the VME module.
Code sample 11: block write
struct sis1100_vme_block_req req, answer; |
When the driver has read all the data blocks starting at addr it will copy the result to the location where address is pointing to.
VME list read/write
VME list read/write comes in handy when you want to transfer large amounts of data and your data is spread over several VME modules or your addresses are not sequentiel. With a VME list access you create a list of single requests which are then performed in order by the driver.
For a VME list access you will need an additional data structure besides the sis1100_vme_req structure:
Code sample 12: list access data structure
struct sis1100_vme_list_req |
num the total number of requests.
*data pointer to an array of num sis1100_vme_req
The following code sample demonstrates a VME list write access. For read access it is analogous except that you do not need to fill the data fields of the requests.
Code sample 13: VME write with list access
struct sis1100_vme_list_req lreq; |
VME direct bus access
The VME direct bus access is intended to be used for frequent VME access to a small chunk of VME memory. You have to request a memory mapping of VME memory and then the driver can directly write to this memory. The restrictions about this are that the memory area can not be larger than 64kB and you can create only 64 mappings at a time. Explicit flushing of a mapping is currently not implemented - you can only flush all mappings.
To use direct bus access you have to request a mapping of VME memory. To do this use the following data structure:
Code sample 14: data structure for map requests
struct sis1100_map_req |
am address modifier for accesses to this mapping
baseAddr the base address of the VME memory you want to be mapped
mapNum this field is filled by the driver and returns the number of the mapping. This number is later used to address the correct mapping in a request
Code sample 15: request a mapping
struct sis1100_map_req mapReq;
|
To actually write to or read from a mapping you need fill two more data structures. See code sample 18 how to perform such a access.
Code sample 16: mapped request data structure
struct sis1100_mapped_req |
mapNum the number of the memory map where the requests pointed to by *data should be written or read.
numberOfReq the total number of requests pointed to by *data.
*data pointer to user space memory where all the sis1100_simple_req are stored.
Code sample 17: simple request data structure
struct sis1100_simple_req |
offset this is an offset to the base address of the mapped VME memory region.
value here you store the value if you want to write something. Otherwise leave empty and the driver will fill this field on a read request.
To perform the request you put together a sis1100_mapped_req specifying the map number, the number of requests and a pointer pointing to an array of sis1100_simple_req.
Code sample 18: mapped request
struct sis1100_simple_req *mappedReq; struct sis1100_mapped_req mReq; IOByteCount size1 = sizeof(struct sis1100_mapped_req); IOByteCount size2 = sizeof(struct sis1100_mapped_req); int count; // allocate enough memory for the requests count = 10000; mappedReq = malloc(count*sizeof(struct sis1100_simple_req)); if(!mappedReq) { printf("malloc failed\n"); return -1; } // put the number of requests and a pointer to them into the mapped request mReq.numberOfReq = count; mReq.data = mappedReq; // set the correct map number to access the right mapping mReq.mapNum = mapNumber; // fill offsets and values for(i = 0; i < count; i++) { mappedReq[i].offset = 0x38; mappedReq[i].value = i; } kernReturn = IOConnectMethodStructureIStructureO(dataPort, VMEMappedWrite, size1, &size2, &mReq, &mReq); |