Sunday May 14, 2006
In this next installment of my series of entries about the Solaris 1394 Software Framework, I'm continuing with a discussion of asynchronous request handling. (Looking for details on isochronous handling? Be patient.) But this time I'm going to be talking about handling and responding to incoming asynchronous requests.
When the 1394 Framework receives a request from the bus, it must determine to which target driver the request is intended. This is made possible by requiring target drivers to first reserve ranges within the 1394 host address space. Then, when a request arrives, the Framework can look up the target driver that owns the range and processing of the request can begin.
The other side of incoming request processing, of course, is sending a response.
The Framework provides four ways for target drivers to do this:
IEEE 1394 is based on IEEE 1212 and uses IEEE 1212 style 64-bit addressing. In the 64-bit address the high-order 10 bits identifying the bus number (0x3FF for the local bus) and the next 6 bits identify the node ID. Node ID 63 is reserved for broadcast, so this allows for up to 63 devices (0 - 62) on the bus. The remaining 48 bits are used to address locations on the specified device.
Looking at it another way, each node (device) can assign, partition, and manage its own 48 bits of address space. The significance of this for a particular node is in terms of received requests, being sent to it from other nodes on the bus. By ascribing meaning or functionality to certain addresses, a node can handle incoming asynchronous requests as defined in a standards specification or as otherwise planned. From the point of view of transmitting a request, it is relevant to know the meaning of address locations at the destination node.
Again according to IEEE 1212 (and used by IEEE 1394), some of the highest 48-bit addresses, those beginning with 0xFFFF_Fxxx_xxxx, are resered for use by various protocols, including IEEE 1394 itself.
To satisfy this need, the 48-bit space is paritition into sections:
Currently, the 1394 Software Framework supports a physical address range within the low 32 bits (0x0000_0000_0000 to 0x0000_FFFF_FFFF). Received write and read requests that have a 48bit destination offset with that range can be automatically handled by hardware to or from host memory.
In addition, IEEE 1212-1994 reserves a region of address space for control and status registers, some of which may be used by protocols. This space is near the highest end of the address space.
The 1394 Software Framework further divides the 48-bit address space as shown:
The routines for allocating and freeing 1394 address space do not themselves result in any transactions being sent over the bug and therefore are not handled like the outgoin asynchronous request commands. Instead, these routines perform the requested function and return immediately with DDI_SUCCESS or DDI_FAILURE
As with most 1394 Software Framework calls, these are called with the t1394_handle and a parameters structure as defined below.
/*
* t1394_alloc_addr_t
* is passed to t1394_alloc_addr(), when 1394 address space is being
* allocated, to describe the type of address space. The target driver
* is responsible for specifying the aa_enable, aa_type, and aa_evts
* fields described above as well as the size of the allocated block.
* Additionally, the target driver may specify backing store
* (aa_kmem_bufp), a specific address (in aa_address if aa_type is
* T1394_ADDR_FIXED), and a callback argument (in aa_arg) to be
* passed to the target in any of its callback routines.
* When it returns, t1394_alloc_addr() will return in aa_address the
* starting address of the requested block of 1394 address space and
* and address block handle (aa_hdl) used to free the address block
* in a call to t1394_free_addr().
*/
typedef struct t1394_alloc_addr {
t1394_addr_type_t aa_type; /* IN: address region */
size_t aa_length; /* IN: # bytes requested */
t1394_addr_enable_t aa_enable; /* IN: request enables */
t1394_addr_evts_t aa_evts; /* IN: event callbacks */
opaque_t aa_arg; /* IN: evt callback arg */
caddr_t aa_kmem_bufp; /* IN: backing-store buf */
uint64_t aa_address; /* IN/OUT: alloced address */
t1394_addr_handle_t aa_hdl; /* OUT: returned to target */
} t1394_alloc_addr_t;
/*
* t1394_addr_type_t
* is used in the t1394_alloc_addr_t structure, passed to
* t1394_alloc_addr(), to indicate what type of address block the
* target driver would like to allocate.
* T1394_ADDR_POSTED_WRITE indicates posted write memory, where
* incoming write requests are automatically acknowledged as complete.
* T1394_ADDR_NORMAL indicates memory, unlike the posted write area,
* where all requests regardless of type are ack_pended upon receipt
* and are subsequently responded to.
* T1394_ADDR_CSR memory range is generally used by target drivers
* that are implementing a well-defined protocol.
* And T1394_ADDR_FIXED is used to indicate to t1394_alloc_addr()
* that a specific set of addresses are needed. Unlike the other three
* types, this type of request is used to choose a specific address or
* range of addresses in 1394 address space.
*/
typedef enum {
T1394_ADDR_POSTED_WRITE = 0,
T1394_ADDR_NORMAL = 1,
T1394_ADDR_CSR = 2,
T1394_ADDR_FIXED = 3
} t1394_addr_type_t;
/*
* t1394_addr_enable_t
* is used in the t1394_alloc_addr_t structure, passed to
* t1394_alloc_addr(), to indicate what types of (incoming)
* asynchronous requests will be allowed in a given address block.
* If, for example, an address block is intended to be read-only,
* then only the T1394_ADDR_RDENBL bit should be enabled at allocation
* time. Then, when incoming requests of an inappropriate type (write
* or lock requests, in this case) arrive, the 1394 Framework can
* automatically respond to them with TYPE_ERROR in the response
* without having to notify the target driver.
*/
typedef enum {
T1394_ADDR_RDENBL = (1 << 0),
T1394_ADDR_WRENBL = (1 << 1),
T1394_ADDR_LKENBL = (1 << 2)
} t1394_addr_enable_t;
/*
* t1394_addr_evts_t
* is used in the t1394_alloc_addr_t structure, passed to
* t1394_alloc_addr(), to specify callback routines for the
* allocated address block. When a request of the appropriate type
* (read/write/lock) is received to a target driver's address
* block, the appropriate callback routine is consulted and if it is
* non-NULL it is called and passed a cmd1394_cmd_t structure used to
* describe the incoming asynch request.
*/
typedef struct t1394_addr_evts {
void (*recv_read_request)(cmd1394_cmd_t *req);
void (*recv_write_request)(cmd1394_cmd_t *req);
void (*recv_lock_request)(cmd1394_cmd_t *req);
} t1394_addr_evts_t;
Sunday July 10, 2005
OK... here's part 2 of my discussion on the Solaris 1394 Framework's interfaces for sending outgoing asynch requests. This continues my discussion of the Solaris 1394 Software Framework, and in this post I'm going to go over the functions used to allocate, free, and transmit asynch requests (read, write, or lock). In my previous entry, I described the details of the cmd1394_cmd_t structure that is used to encapsulate all the relevant information for an asynch command.
/* * Function: t1394_alloc_cmd() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * flags The flags parameter is described below * * Output(s): cmdp Pointer to the newly allocated command * * Description: t1394_alloc_cmd() allocates a command for use with the * t1394_read(), t1394_write(), or t1394_lock() interfaces * of the 1394 Software Framework. By default, t1394_alloc_cmd() * may sleep while allocating memory for the command structure. * If this is undesirable, the target may set the * T1394_ALLOC_CMD_NOSLEEP bit in the flags parameter. Also, * this call may fail because a target driver has already * allocated MAX_NUMBER_ALLOC_CMDS commands. */ int t1394_alloc_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
Using this interface, a target driver allocates one command used to issue outgoing asynchronous requests. The Framework limits the total number of commands that a target driver may have outstanding. This limit is fixed and currently defaults to 256 commands.
/* * Function: t1394_free_cmd() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * flags The flags parameter is unused (for now) * cmdp Pointer to the command to be freed * * Output(s): DDI_SUCCESS Target successfully freed command * DDI_FAILURE Target failed to free command * * Description: t1394_free_cmd() attempts to free a command that has previously * been allocated by the target driver. It is possible for * t1394_free_cmd() to fail because the command is currently * in-use by the 1394 Software Framework. */ /* ARGSUSED */ int t1394_free_cmd(t1394_handle_t t1394_hdl, uint_t flags, cmd1394_cmd_t **cmdp)
Using this interface, a target driver frees one cmd1394_cmd_t command.
/* * Function: t1394_read() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * cmd Pointer to the command to send * * Output(s): DDI_SUCCESS Target successful sent the command * DDI_FAILURE Target failed to send command * * Description: t1394_read() attempts to send an asynchronous read request * onto the 1394 bus. */ int t1394_read(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
The t1394_read() command issues an asynchronous read quadlet request of read block request to the indicated address and places the response data into the provided cmd->cmd_u.b.data_block message buffer or cmd->cmd_u.q.quadlet_data
If the CMD1394_OVERRIDE_ADDR option is not set, the 1394 Software Framework fills in the appropriate high-order 16 bits of the cmd_addr field with the 10-bit bus number and 6-bit node ID for the attached device. If the CMD1394_OVERRIDE_ADDR option is set, the Framework uses the complete 64-bit cmd_addr field as provided.
The Framework splits the read request into multiple read requests if any of the following conditions are true.
If the CMD1394_DISABLE_ADDR_INCREMENT option is not set, successive read requests are issued with the destination address advancing accordingly. If the CMD1394_DISABLE_ADDR_INCREMENT option is set, the Framework sends each read request to the same destination address.
Possible immediate errors (see 'Error Codes' in my previous blog entry):
Possible completion statuses (in cmd_result) are below. Note that if the operation fails, bytes_transferred indicates if part of the operation succeeded.
/* * Function: t1394_write() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * cmd Pointer to the command to send * * Output(s): DDI_SUCCESS Target successful sent the command * DDI_FAILURE Target failed to send command * * Description: t1394_write() attempts to send an asynchronous write request * onto the 1394 bus. */ int t1394_write(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
The t1394_write() command issues an asynchronous write quadlet request of write block request to the indicated address (based on cmd_type) and uses the provided cmd->cmd_u.b.data_block message buffer or cmd->cmd_u.q.quadlet_data field
If the CMD1394_OVERRIDE_ADDR option is not set, the 1394 Software Framework fills in the appropriate high-order 16 bits of the cmd_addr field with the 10-bit bus number and 6-bit node ID for the attached device. If the CMD1394_OVERRIDE_ADDR option is set, the Framework uses the complete 64-bit cmd_addr field as provided.
The Framework splits the write request into multiple write requests if any of the following conditions are true.
If the CMD1394_DISABLE_ADDR_INCREMENT option is not set, successive write requests are issued with the destination address advancing accordingly. If the CMD1394_DISABLE_ADDR_INCREMENT option is set, the Framework sends each write request to the same destination address.
Possible immediate errors (see 'Error Codes' in my previous blog entry):
Possible completion statuses (in cmd_result) are below. Note that if the operation fails, bytes_transferred indicates if part of the operation succeeded.
/* * Function: t1394_lock() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * cmd Pointer to the command to send * * Output(s): DDI_SUCCESS Target successful sent the command * DDI_FAILURE Target failed to send command * * Description: t1394_lock() attempts to send an asynchronous lock request * onto the 1394 bus. */ int t1394_lock(t1394_handle_t t1394_hdl, cmd1394_cmd_t *cmd)
There are several lock operations provided by the 1394 Software Framework (above and beyond the basic lock operations provided by IEEE 1394). These are described below (with pseudo-code to describe their operation).
For all lock operations, if the CMD1394_OVERRIDE_ADDR option is not set, the 1394 Software Framework fills in the appropriate high-order 16 bits of the cmd_addr field with the 10-bit bus number and 6-bit node ID for the attached device. If the CMD1394_OVERRIDE_ADDR option is set, the Framework uses the complete 64-bit cmd_addr field as provided.
new_value = (data_value & arg_value) | (old_value & ~arg_value)
if (old_value == arg_value) then new_value = data_value
new_value = old_value + data_value
if (old_value != arg_value) then new_value = old_value + data_value
new_value = (old_value != arg_value) ? old_value + data_value : data_value
new_value = old_value & data_value
new_value = old_value | data_value
new_value = old_value ^ data_value
new_value = old_value + 1
new_value = old_value - 1
new_value = old_value + data_value
new_value = old_value - data_value
if ((old_value + data_value) <= threshold_arg) then new_value = old_value + data_value
if ((old_value - data_value) >= threshold_arg) then new_value = old_value - data_value
if ((old_value + data_value) > clip_arg) then new_value = clip_arg
else new_value = old_value + data_value
if ((old_value - data_value) < clip_arg) then new_value = clip_arg
else new_value = old_value - data_value
Possible completion statuses (in cmd_result) are below.
Saturday July 09, 2005
Continuing my discussion of the Solaris 1394 Software Framework, in this post I'm going to go into some detail on the methods by which a 1394 target driver can transmit outgoing asynchronous requests and receive the corresponding responses.
To acquire a command structure for transmitting asynchronous requests and receiving responses, the 1394 target driver calls t1394_alloc_cmd(). This routine allocates and returns a pointer to a cmd1394_cmd_t structure (which I'll say more about below.) Note: This same command structure is also used for handling incoming asynchronous requests, although some fields are used differently.
A 1394 target driver can re-use a command after it has completed (see below), but it should not reissue or alter any fields for any command that is still pending. And when a target driver no longer needs a command, it will call t1394_free_cmd() to release the command structure back to the Framework. Note: 1394 target drivers are responsible for freeing all of their allocated commands before detaching (or the detach will not be allowed - see my previous blog entry.)
The command itself often takes some time to complete, since a packet must be sent over the bus to the destination node and the 1394 Framework must await the response. Therefore, the Framework gives the target driver the option to block or not block pending command completion (more on this below).
Once the command is "handed off" to the 1394 Software Framework, the target driver should not re-use or modify the same allocated command until the target driver can determine that the requested action has fully completed.
A command is "completed" when the bus transaction(s) used to perform the command have finished. The command's cmd_result field indicates either success (CMD1394_CMDSUCCESS) or failure, where a failure is indicated by an error code (See 'Error Codes' below for details).
From the IEEE 1394 protocol perspective, when a bus reset occurs all the pending and in-progress command requests are canceled. Target drivers have two options with respect to processing of any outstanding commands.
/* cmd1394_cmd: cmd1394 - common command type */
typedef struct cmd1394_cmd
{
int cmd_version;
volatile int cmd_result;
cmd1394_flags_t cmd_options;
cmd1394_cmd_type_t cmd_type;
void (*completion_callback)(struct cmd1394_cmd *);
opaque_t cmd_callback_arg;
uint64_t cmd_addr;
uint_t cmd_speed;
uint_t bus_generation;
uint_t nodeID;
uint_t broadcast;
union {
cmd1394_quadlet_t q;
cmd1394_block_t b;
cmd1394_lock32_t l32;
cmd1394_lock64_t l64;
} cmd_u;
} cmd1394_cmd_t;
/*
* cmd1394_cmd.cmd_type
* Used to select/indicate the request packet type
*/
typedef enum {
CMD1394_ASYNCH_RD_QUAD = 0,
CMD1394_ASYNCH_WR_QUAD = 1,
CMD1394_ASYNCH_RD_BLOCK = 2,
CMD1394_ASYNCH_WR_BLOCK = 3,
CMD1394_ASYNCH_LOCK_32 = 4,
CMD1394_ASYNCH_LOCK_64 = 5
} cmd1394_cmd_type_t;
/*
* cmd1394_cmd.flags
* Used to select the request's behavior, including
* how the destination address is determined, how
* a large request will be broken into smaller requests,
* whether the command should be resent after a
* bus reset has happened, etc.
*/
typedef enum {
CMD1394_CANCEL_ON_BUS_RESET = (1 << 0),
CMD1394_OVERRIDE_ADDR = (1 << 1),
CMD1394_OVERRIDE_MAX_PAYLOAD = (1 << 2),
CMD1394_DISABLE_ADDR_INCREMENT = (1 << 3),
CMD1394_BLOCKING = (1 << 4),
CMD1394_OVERRIDE_SPEED = (1 << 5)
} cmd1394_flags_t;
/* Asynchronous Command (Data Quadlet) */
typedef struct cmd1394_quadlet {
uint32_t quadlet_data;
} cmd1394_quadlet_t;
For t1394_write(), the quadlet_data field contains the 4 bytes
to be written. For t1394_read(), the quadlet_data contains the
4-byte read data from the requested address.
/* Asynchronous Command (Data Block) */
typedef struct cmd1394_block {
mblk_t *data_block;
size_t blk_length;
size_t bytes_transferred;
uint_t max_payload;
} cmd1394_block_t;
/* Asynchronous Command (Lock Cmd - 32 bit) */
typedef struct cmd1394_lock32 {
uint32_t old_value;
uint32_t data_value;
uint32_t arg_value;
uint_t num_retries;
cmd1394_lock_type_t lock_type;
} cmd1394_lock32_t;
The following structure is used for 64-bit lock requests:
/* Asynchronous Command (Lock Cmd - 64 bit) */
typedef struct cmd1394_lock64 {
uint64_t old_value;
uint64_t data_value;
uint64_t arg_value;
uint_t num_retries;
cmd1394_lock_type_t lock_type;
} cmd1394_lock64_t;
The Framework supports a large number of lock operations, each of which uses
the lock values somewhat differently. The value fields are described here
in a general way.
An error might occur when the command is initially issued due to a faulty parameter or another immediately detected error. For example, if t1394_write() is called with a NULL data_block message pointer for a block write, it will return an error status of DDI_FAILURE, and the command's cmd_result is set to CMD1394_ENULL_MBLK. If no error is found, the function return value is DDI_SUCCESS and the command's cmd_result reflects the current status of the transaction(s).
An error might also occur during the ensuing bus transaction(s). In this case, the Framework sets the command's cmd_result appropriately and the command is completed as described above.
For non-blocking commands, the command cmd_result after a target driver gets DDI_SUCCESS can be CMD1394_NOSTATUS.
Sunday June 19, 2005
Continuing my discussion of the Solaris 1394 Software Framework, in this post I'm going to go into some detail on the methods by which a 1394 target driver can register and deregister itself with the core framework. And, in the process, I will also touch on the Solaris event notification mechanisms leveraged by the framework.
All 1394 target drivers must register themselves with the framework in order to operate properly. The 1394 framework will make an association with the device driver instance for the target and the corresponding HAL driver instance (called the 'parent') for the adapter to which it is attached.
In addition, the 1394 framework will allocate resources (internally) to track the target driver state (in the target driver 'handle') and return useful information about the current state of the target device on the 1394 bus and DMA and/or interrupt properties of the parent HAL.[1]
/*
* Function: t1394_attach()
* Input(s): dip The dip given to the target driver
* in it's attach() routine
* version The version of the target driver -
* T1394_VERSION_V1
* flags The flags parameter is unused (for now)
*
* Output(s): attachinfo Used to pass info back to target,
* including bus generation, local
* node ID, dma attribute, etc.
* t1394_hdl The target "handle" to be used for
* all subsequent calls into the
* 1394 Software Framework
*
* Description: t1394_attach() registers the target (based on its dip) with
* the 1394 Software Framework. It returns the bus_generation,
* local_nodeID, iblock_cookie and other useful information to
* the target, as well as a handle (t1394_hdl) that will be used
* in all subsequent calls into this framework.
*/
/* ARGSUSED */
int
t1394_attach(
dev_info_t *dip, /* supplied by the target */
int version, /* supplied by the target */
uint_t flags, /* supplied by the target */
t1394_attachinfo_t *attachinfo, /* filled in by the framework */
t1394_handle_t *t1394_hdl) /* returned to the target */
During a target driver's attach processing, it calls t1394_attach() to register with the 1394 Software Framework. The Framework initializes any necessary internal data structures and returns a t1394_hdl which the target driver uses with all other calls into the Framework. The Framework also returns additional information in attachinfo, which is needed by some target driver implementations.
/*
* t1394_attachinfo_t
* is filled in and returned by the 1394 Framework at attach time
* (returned from the call to t1394_attach()). This structure contains
* the t1394_localinfo_t structure described above, as well as the
* iblock cookie and the attributes necessary for DMA allocations, etc.
*/
typedef struct t1394_attachinfo_s {
ddi_iblock_cookie_t iblock_cookie;
ddi_device_acc_attr_t acc_attr;
ddi_dma_attr_t dma_attr;
t1394_localinfo_t localinfo;
} t1394_attachinfo_t;
/*
* t1394_localinfo_t
* is filled in and returned by the 1394 Framework at attach time
* (in the t1394_attachinfo_t structure returned from t1394_attach())
* to provide the local host nodeID and the current bus generation.
*/
typedef struct t1394_localinfo_s {
uint_t bus_generation;
uint_t local_nodeID;
} t1394_localinfo_t;
/* * Function: t1394_detach() * Input(s): t1394_hdl The target "handle" returned by * t1394_attach() * flags The flags parameter is unused (for now) * * Output(s): DDI_SUCCESS Target successfully detached * DDI_FAILURE Target failed to detach * * Description: t1394_detach() unregisters the target from the 1394 Software * Framework. t1394_detach() can fail if the target has any * allocated commands that haven't been freed. */ /* ARGSUSED */ int t1394_detach(t1394_handle_t *t1394_hdl, uint_t flags)
The target driver calls t1394_detach() to deregister from the 1394 Software Framework. Typically the target calls this from its detach(9E) routine.
Target drivers may register callbacks for general 1394 Software Framework events by using the Solaris Event Framework. All calls to the Event Framework must be performed before the call to t1394_attach() is performed. For details about the Solaris Event Framework, see:
The following events are supported by the 1394 Framework:
void (*handler)(dev_info_t *dip, ddi_eventcookie_t cookie, void *arg,
void *impl_data);
The callback impl_data provided to the eventcalls associated with each of these events is a t1394_localinfo_t *, as described above.
Note: Within an event callback function, a target driver shouldn't invoke any procedure that blocks or sleeps. For example, an event callback function shouldn't issue any outgoing asynch request that has the CMD1394_BLOCKING flag set. (Yep, again... more on this in a later blog entry).
OK. So obviously there's more I could say here about the details of Framework's implementation for tracking and coordinating target drivers and their events, but as I've said before that I want to first go through the Framework at a high-level.
Next time, I'm going to go over the outgoing asynch interfaces: command structure allocation, command completion mechanisms (event-driver, blocking, polling), command types (read, write, lock), quadlet requests, block requests, etc. That's a ton of stuff to cover, so maybe it won't all get into a single blog entry, but anyway that's where I'm headed next.
Tuesday June 14, 2005
When I started at Sun about seven years ago, the Solaris 1394 Software Framework was my first project. A team of four, we designed, implemented, tested and putback to Solaris 8 (Update 2) in about a year and a half. Since then the code has been transitioned to others (like Alan Perry and Artem Kachitchkine), who continue to maintain and extend its functionality even today. Now, almost six years later, I finally get a chance to share this code with the world (and, hopefully, at least some interested readers.)
So I figured I'd start by giving a brief overview of the stack and the features that it provides and then talk a bit about the source files for the modules: how they're organized, what's in them, etc. Then (over the course of several posts) I'll get into the specifics of how to use each of interfaces, gotcha's for potential developers working with the source, some of the little bits of which I'm most proud, and maybe some discussion of what could be improved or extended in the existing code (and I'll be interested to hear what others think). This will not be an intro to the IEEE 1394 specification or the technology, nor will it be an introduction to writing Solaris device drivers (though I'll try to help anyone in any way I can). What follows will assume a certain familiarity with IEEE 1394 and with writing Solaris device drivers.
The Solaris 1394 Software Framework Device Driver Interface provides a set of kernel interface routines to facilitate access to devices on an IEEE 1394 bus. The interface routines are intended for use by 1394 device drivers, also referred to as target drivers.
There are two kinds of target drivers: class drivers and vendor specific drivers. Class drivers adhere to a general standard for a particular kind of device and can drive any vendor's device that adheres to the same standard. For example, a class driver for the IEEE 1394 Digital Conferencing Camera specification can drive video conferencing cameras manufactures by a variety of vendors (even though each may have a different set of features). Vendor-specific drivers are built to drive a specialized non-standard device. The 1394 Software Framework supports both kinds of target drivers.
The 1394 Software Framework provides several features to support target drivers using the IEEE 1394 bus.
There are two sides of asynchronous I/O: issuing outgoing requests and handling incoming requests.
Outgoing Requests - The 1394 Framework provides the ability to send the basic set of IEEE 1394 asynchronous requests; read, write and lock. In addition to the IEEE 1394 defined set of lock request options, the Framework lock request interface also provides a set of bit and arithmetic functions.
For asynchronous requests, the 1394 Framework automatically determines the device's destination ID, sends the request using the local host's 1394 hardware interface, tracks the status of the request until the transaction completes, and supplies response information as needed.
Target drivers choose whether to: 1) block while waiting for the transaction to complete, 2) poll on the request completion status, or 3) have the 1394 Framework call a specified callback routine when the transaction completes. Using the poll and callback mechanisms, target drivers can issue several outstanding requests and poll or be notified for each completion.
Incoming Requests - The primary role of the 1394 Framework with respect to incoming asynchronous requests is to dispatch the request to the appropriate target driver or to handle the request on behalf of the appropriate target driver.
To support this, the 1394 Framework provides an allocation mechanism that target drivers use to reserve ranges of addresses within the 48-bit local node address space. Target drivers have several options available when allocating 1394 address space including the ability to specify a kernel virtual buffer to map to the allocated space. When the 'destination_offset' of an incoming request falls within an allocated address range, the 1394 Framework fulfills the request if possible, notifies the target driver if desired, and sends the response. Target drivers may also allocate 1394 address space with the characteristic that an incoming request to that space will be handled by hardware. In this case hardware directly accesses host memory bound to that address space, and transmits the appropriate response.
Due to the potentially large volume of isochronous data and the critical isochronous timing needs, the 1394 Software Framework provides a mechanism designed to reduce call overhead and to maximize throughput.
Before starting isochronous I/O, a target driver sets up the overall sequence and structure of receive or transmit buffers, indicating other needs such as when the 1394 Framework should invoke a target driver's callback. Once isochronous I/O is started, the target driver can focus most of its time on handling the data.
The 1394 Framework mechanism for configuring isochronous I/O is the Isochronous Transfer Language (IXL). The IXL is a hardware independent set of control blocks that the target driver uses to direct isochronous DMA. The 1394 Software Framework converts the hardware independent IXL into the appropriate DMA directives for the local host 1394 interface hardware changes, the impact to the target driver is minimal or non-existent.
The 1394 Software Framework also facilitates peer to peer communication by tracking all target drivers with an interest os a particular isochronous stream, allocating a channel number and bandwidth as needed, and coordinating the target driver notification of stream starts and stops.
In addition to complying with the IEEE 1394 requirements for bus reset processing, such as cancelling pending asynchronous requests, the 1394 Framework provides several bus reset related features.
One of the most severe effects of a bus reset is the re-enumeration of all the nodes on the bus. The 1394 Software Framework assesses the post bus reset topology and determines the new node_IDs for all target driver instances. It can then reissue any uncompleted outgoing asynchronous requests on behalf of the issuing target driver, and each target driver can continue on without concern for their new node number.
As part of the topology evaluation, the 1394 Software Framework also creates a speed map to determine the maximum packet speed between any two nodes. The Framework uses the speed map to select the most efficient speed for target driver outgoing asynchronous requests.
In addition to the topology map and speed map which are part of 1394 bus manager duties, the 1394 Software Framework also contends for isochronous resource manager and bus manager. If it is bus manager, the Framework will ensure that the root is cycle master capable and optimize the gap count.
Another aspect of the Framework's topology evaluation is that it determines which devices, if any, have been removed from the bus and which ones have been added to the bus. For removed devices, the 1394 Software Framework calls into the Solaris Hotplug Framework to notify it that the device is offline. For added devices, the Framework reads the device's configuration ROM to determine the pertinent information, the Global Unique ID and often the Unit_Spec_Id and Unit_Sw_Version, and creates the Solaris "/devices" node using the Solaris Hotplug Framework interfaces.
To ensure that the Solaris 1394 Software Framework is loaded, target drivers must link with a dynamic dependency on the Framework misc module. This is done using the '-N' flag with ld:
ld -r -dy -Nmisc/s1394 -o target target1.o target2.o -o target
Solaris device entries for IEEE 1394 devices are created based on the device's global unique ID. The format of the name uses a prefix of "unit@" followed by the GUID in hexadecimal. An example /devices pathname for device A above is as follows ("tdA" is target driver A's minor name):
/devices/pci@1f,4000/firewire@4,2/unit@0800460200000016,0:tdA
1. Unit_Spec_Id, Unit_Sw_Version
2. Node_Spec_Id, Node_Sw_Version
3. Node_Vendor_Id, Node_Hw_Version
4. Module_Spec_Id, Module_Sw_Version
5. Module_Vendor_Id, Module_Hw_Version
For further information on the layout of configuration ROM and the meaning of these values, refer to IEEE 1212-1994 Section 8 and IEEE 1394-1995 Section 8.3.2.5. For specific information about configuration ROM for a particular device class, refer to the device class specification.
After parsing configuration ROM and locating one of the pairs as shown above, the Solaris 1394 Software Framework provides the information to the hotplug framework. If a driver is configured to bind to the designated pair, the /devices and /dev entries are created and the driver's attach() routine is invoked. For example, to add a driver for a video conferencing camera which adheres to the 1394 Digital Camera Draft 1.04 (note that hexidecimal letters must be in lower case):
add_drv -n -i \"firewire00a02d,000100\" tdA
Where firewire is the hardware interface, 00a02d is the Unit_Spec_ID and 000100 is the Unit_Sw_Version.
All the source and headers for the Solaris 1394 Framework can be found under:
usr/src/uts/common/io/1394
usr/src/uts/common/sys/1394
The files themselves break down this way:
* t1394.c - 1394 Target Driver Interfaces
* s1394.c - 1394 Services LAyer Initialization and Cleanup Routines
* s1394_addr.c - 1394 Address Space Routines
* s1394_asynch.c - 1394 Services Layer Asynch Communications Routines
* s1394_bus_reset.c - 1394 Services Layer Bus Reset Routines
* s1394_csr.c - 1394 Services Layer CSR and Config ROM Routines
* s1394_dev_disc.c - 1394 Services Layer Device Deiscovery Routines
* s1394_hotplug.c - 1394 Services Layer Hotplug Routines
* s1394_isoch.c - 1394 Services Layer Isoch Communications Routines
* s1394_misc.c - 1394 Services Layer Miscellaneous Routines
* nx1394.c - 1394 Services Layer Nexus Support Routines
* h1394.c - 1394 Services Layer HAL Interfaces
* t1394_errmsg.c - Utility function that targets can use to convert an
error code into a printable string.
* s1394_cmp.c - 1394 Services Layer Connection Management Procedures Support Routines
* s1394_fa.c - 1394 Services Layer Fixed Address Support Routines
(Currently used only for FCP support)
* s1394_fcp.c - 1394 Services Layer FCP Support Routines
* t1394.h
This is the primary header file for the 1394 Framework and it includes
all other header files listed below. In addition, it contains all 1394
Framework interface routine prototypes as well as all data structure and
defines beginning with the "t1394_" prefix. (n.b. This one's pretty
well-commented, if I say so myself.)
* cmd1394.h
This file contains all structures and defines for handling asynchronous
commands.
* id1394.h
This file contains all structures and defines for managing a local
isochronous DMA resource.
* ieee1394.h
This file contains general IEEE 1394 defines.
* ieee1212.h
This file contains general IEEE 1212 defines.
* ixl1394.h
This file contains all structures and defines for utilizing IXL programs.
* h1394.h
This file contains the structure and error codes used to communicate between
the HAL and the rest of the 1394 Software Framework
* s1394.h
This file contains all of the structures used (internally) by the 1394
Software Framework.
* s1394_impl.h
This file contains typedefs and defines used by all 1394 Software Framework
files.
The source for our OpenHCI-compliant HAL driver can be found in:
usr/src/uts/common/io/1394/adapters
usr/src/uts/common/sys/1394/adapters
The files here are numerous, so I will hold off saying more about this driver until some later entries.
hci1394_extern.c hci1394_misc.c hci1394.c
hci1394_ioctl.c hci1394_ohci.c hci1394.conf
hci1394_async.c hci1394_isr.c hci1394_s1394if.c
hci1394_attach.c hci1394_ixl_comp.c hci1394_tlabel.c
hci1394_buf.c hci1394_ixl_isr.c hci1394_tlist.c
hci1394_csr.c hci1394_ixl_misc.c hci1394_vendor.c
hci1394_detach.c hci1394_ixl_update.c
hci1394_isoch.c hci1394_q.c
hci1394.h hci1394_extern.h hci1394_state.h
hci1394_async.h hci1394_ioctl.h hci1394_tlabel.h
hci1394_buf.h hci1394_isoch.h hci1394_tlist.h
hci1394_csr.h hci1394_ixl.h hci1394_tnf.h
hci1394_def.h hci1394_ohci.h hci1394_vendor.h
hci1394_descriptors.h hci1394_q.h hci1394_drvinfo.h
hci1394_rio_regs.h
And the source for the existing target drivers (mentioned above) can be found in:
usr/src/uts/common/io/1394/targets/av1394
usr/src/uts/common/io/1394/targets/scsa1394
So my basic plan is to continue with some discussion of driver attach() and detach() interfaces (see t1394_attach() and t1394_detach() in the source) and basic 1394 event processing. Then to move on to talk about the asynch interfaces, the isoch interfaces, and finally some of the miscellaneous interface routines. At this point, I thought I'd change the focus from a description of the interfaces and how to use them to a more detailed examination of some specific bits of internals code.
But I'm open to suggestions too. If you've read this far and feel like you may be interested in reading more, going through the code yourself, sharing you thoughts and comments, and end up with something specific you'd like to hear about... lemme know.
(2005-06-14 09:00:00.0) Permalink
Tuesday November 30, 2004
Well... so it turns out that there's actually a Solaris bug that prevents the iPod from mounting properly (without a little coaxing). Let's just say we're working on it.
Update (01/18/2005): I have just confirmed that the workaround described below is completely unnecessary for Solaris 10 on x86. The 'pcfs' bug and workaround below only affect Solaris SPARC. If you are using Solaris SPARC, continue reading. Otherwise skip to "But wait... there's more..." below.
But if you're reading this, then you probably actually wanted to try out your iPod with Solaris 10 now. So I've included some details below on the workaround I'm using.
Problem
The iPod contains two partitions: the 1st is a small one used
by iPod firmware, the 2nd is the rest of the disk.
Originally, both are marked FAT32 and both are marked primary.
But unfortunately Solaris 'pcfs' doesn't see the 2nd partition
(which is, of course, a bug).
The 1st partition's signature is in the first sector:
# dd if=/dev/rdsk/c5t0d0p0 count=1 | od -c 1+0 records in 1+0 records out 0000000 353 X 220 M S D O S 5 . 0 002 \0 \b \0 002 0000020 002 \0 \0 \0 \0 370 \0 \0 ? \0 377 \0 \0 \0 \0 \0 0000040 _ 340 276 001 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000060 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 0000100 002 \0 ) R 6 213 250 i P o d \0 M E 0000120 F A T 3 2 3 311 216 321 274 364 .......
Now the first part of the workaround is basically to modify the "FAT32" signature to something different, e.g. replacing 'F' with 'C' will be sufficient. As a result, 'pcfs' will ignore 1st partition and will skip to the 2nd.
But wait... there's more...
The iPod's music repository is also located in a hidden directory. And, by default, Solaris 'pcfs' will not show hidden directories. However, there is an undocumented 'hidden' option to override this behavior. So your mount command will typically look something like this:
# mount -F pcfs -o hidden /dev/dsk/c5t0d0s0:1 /mnt/ipod
Update, cont'd. (01/18/2005): If you are using Solaris 10
on x86, then you need only to mount the 2nd partition with:
# mount -F pcfs -o hidden /dev/dsk/c5t0d0p2 /mnt/ipod
Nothing else, no additional hacking of the first sector, etc.
For Solaris 10 on x86, all of the rest of the hacks I described
here are unnecessary.
Summary
So basically what I did to my iPod was this:
dd if=/dev/rdsk/c5t0d0s0 of=restoreoriginal_iPodhack count=1
dd if=enable_iPodhack of=/dev/rdsk/c5t0d0s0 count=1
mount -F pcfs -o hidden /dev/dsk/c5t0d0s0:1 /mnt/ipod
dd if=restoreoriginal_iPodhack of=/dev/rdsk/c5t0d0s0 count=1
So that's it. No big deal really. Hopefully we'll get this Solaris bug fixed soon and none of us will have to go through this convoluted process just to use our iPods. But until then... I hope this workaround helps.
BTW... with the help of some gtkpod developers, the latest version (v0.85) now compiles and runs unmodified on Solaris 10. Pretty cool. Thanks guys! Try it out for yourself.
(2004-11-30 18:02:59.0) Permalink Comments [4]
Monday October 11, 2004
OK, so I got gtkpod working. Pretty good app. Nice and easy to use.
It wasn't too hard to build on Solaris 10, actually, but it didn't exactly work right out of the box either (i.e. not just 'configure; make; make install'). No big deal though. It just needed to be hacked up a little to get it running.
I started by downloading the latest release gtkpod-0.80-2 and found that I also needed to download and build libid3tag. So I grabbed libid3tag-0.15.1b and it built without any changes. Then I started to build gtkpod-0.80-2 and found that I needed to make a few changes, mostly minor.
A couple of the files ('clientserver.c' and 'file.c') make calls to flock() that really oughta be calls to fcntl() for Solaris. And in 'info.c' there's a call to 'df -k -P', where '-P' is supposedly POSIX output format. That extra argument causes problems (and is unnecessary) for Solaris, so I took it out. And in 'misc.c' the function called which() seemed to be causing me some problems (but this could just be a problem with my environment.) Not sure though, so... when in doubt, hack it out.
The only other things I had to do were also probably related to my environment. Couple minor changes in the makefiles: invalid compiler flag, some issues with duplicate/conflicting header files, and needing to add 'libnsl' and 'libsocket' in with the final link. No biggie. After all that was done, I was ready to play.
As I said above, this is a pretty good app. I didn't build it with any AAC support yet (see libmp4v2 though), so I can't listen to my handful of iTunes songs. (Plus there's that whole DRM issue, which I won't go into here.) But I am able to listen to all my MP3's (all ripped from CD's I own.)
(2004-10-11 18:07:11.0) Permalink Comments [5]
Thursday October 07, 2004
Got my iPod working with my Solaris desktop. Very cool.
Sometime around the end of July, the support for 1394 mass storage devices (CD-ROMs, DVD-ROMs, Zipdisks, and devices like the iPod - which is just a 1394 hard drive) was integrated into Solaris 10. I run the latest S10 build on my desktop, of course, so I've got the new Solaris 1394 Mass Storage driver, but most of you will have to wait for the next Solaris Express release (any day now).
The new 'scsa1394' driver implements the Serial Bus Protocol 2 (SBP-2) specification, which allows 1394 mass storages devices, like the iPod, to look and act like any other disk that you're used to using. This driver joins the existing collection of drivers know collectively as the Solaris 1394 Software Framework. The framework provides support for FireWire on SPARC and x86 systems and supports both 1394 digital video (DV) camera devices and 1394 conferencing camera ("webcam") devices, in addition to the new 1394 mass storage devices.
So, anyway, I plug my iPod into one of the FireWire ports on my system (it's immediately recognized after the hotplug), mount the drive, 'cd' and/or 'ls' to see the music files (and all the other hidden tidbits in there) and run xmms to listen to my tunes while I'm in my office at work.
Next step, I figure, is to try to get something like gtkpod working on Solaris 10 so that I can add/remove files from the iPod's database, use my playlists, and have a nicer GUI interface. But it's actually pretty cool as it is. I'll post again, though, when I get it working.
In short though, if you use Solaris and you love your iPod, stay tuned for some really cool new stuff coming to your desktop.
(2004-10-07 18:39:30.0) Permalink Comments [12]