405 lines
9.4 KiB
C
405 lines
9.4 KiB
C
/*
|
|
* Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
|
|
*
|
|
* This software may be freely used, copied, modified, and distributed
|
|
* provided that the above copyright notice is preserved in all copies of the
|
|
* software.
|
|
*/
|
|
|
|
/* -*-C-*-
|
|
*
|
|
* $Revision$
|
|
* $Date$
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "adp.h"
|
|
#include "hsys.h"
|
|
#include "rxtx.h"
|
|
#include "drivers.h"
|
|
#include "buffers.h"
|
|
#include "devclnt.h"
|
|
#include "adperr.h"
|
|
#include "devsw.h"
|
|
#include "hostchan.h"
|
|
#include "logging.h"
|
|
|
|
/*
|
|
* TODO: this should be adjustable - it could be done by defining
|
|
* a reason code for DevSW_Ioctl. It could even be a
|
|
* per-devicechannel parameter.
|
|
*/
|
|
static const unsigned int allocsize = ADP_BUFFER_MIN_SIZE;
|
|
|
|
#define illegalDevChanID(type) ((type) >= DC_NUM_CHANNELS)
|
|
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
* Function: initialise_read
|
|
* Purpose: Set up a read request for another packet
|
|
*
|
|
* Params:
|
|
* In/Out: ds State structure to be initialised
|
|
*
|
|
* Returns:
|
|
* OK: 0
|
|
* Error: -1
|
|
*/
|
|
static int initialise_read(DevSWState *ds)
|
|
{
|
|
struct data_packet *dp;
|
|
|
|
/*
|
|
* try to claim the structure that will
|
|
* eventually hold the new packet.
|
|
*/
|
|
if ((ds->ds_nextreadpacket = DevSW_AllocatePacket(allocsize)) == NULL)
|
|
return -1;
|
|
|
|
/*
|
|
* Calls into the device driver use the DriverCall structure: use
|
|
* the buffer we have just allocated, and declare its size. We
|
|
* are also obliged to clear the driver's context pointer.
|
|
*/
|
|
dp = &ds->ds_activeread.dc_packet;
|
|
dp->buf_len = allocsize;
|
|
dp->data = ds->ds_nextreadpacket->pk_buffer;
|
|
|
|
ds->ds_activeread.dc_context = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Function: initialise_write
|
|
* Purpose: Set up a write request for another packet
|
|
*
|
|
* Params:
|
|
* Input: packet The packet to be written
|
|
*
|
|
* type The type of the packet
|
|
*
|
|
* In/Out: dc The structure to be intialised
|
|
*
|
|
* Returns: Nothing
|
|
*/
|
|
static void initialise_write(DriverCall *dc, Packet *packet, DevChanID type)
|
|
{
|
|
struct data_packet *dp = &dc->dc_packet;
|
|
|
|
dp->len = packet->pk_length;
|
|
dp->data = packet->pk_buffer;
|
|
dp->type = type;
|
|
|
|
/*
|
|
* we are required to clear the state structure for the driver
|
|
*/
|
|
dc->dc_context = NULL;
|
|
}
|
|
|
|
/*
|
|
* Function: enqueue_packet
|
|
* Purpose: move a newly read packet onto the appropriate queue
|
|
* of read packets
|
|
*
|
|
* Params:
|
|
* In/Out: ds State structure with new packet
|
|
*
|
|
* Returns: Nothing
|
|
*/
|
|
static void enqueue_packet(DevSWState *ds)
|
|
{
|
|
struct data_packet *dp = &ds->ds_activeread.dc_packet;
|
|
Packet *packet = ds->ds_nextreadpacket;
|
|
|
|
/*
|
|
* transfer the length
|
|
*/
|
|
packet->pk_length = dp->len;
|
|
|
|
/*
|
|
* take this packet out of the incoming slot
|
|
*/
|
|
ds->ds_nextreadpacket = NULL;
|
|
|
|
/*
|
|
* try to put it on the correct input queue
|
|
*/
|
|
if (illegalDevChanID(dp->type))
|
|
{
|
|
/* this shouldn't happen */
|
|
WARN("Illegal type for Rx packet");
|
|
DevSW_FreePacket(packet);
|
|
}
|
|
else
|
|
Adp_addToQueue(&ds->ds_readqueue[dp->type], packet);
|
|
}
|
|
|
|
/*
|
|
* Function: flush_packet
|
|
* Purpose: Send a packet to the device driver
|
|
*
|
|
* Params:
|
|
* Input: device The device to be written to
|
|
*
|
|
* In/Out: dc Describes the packet to be sent
|
|
*
|
|
* Returns: Nothing
|
|
*
|
|
* Post-conditions: If the whole packet was accepted by the device
|
|
* driver, then dc->dc_packet.data will be
|
|
* set to NULL.
|
|
*/
|
|
static void flush_packet(const DeviceDescr *device, DriverCall *dc)
|
|
{
|
|
if (device->DeviceWrite(dc) > 0)
|
|
/*
|
|
* the whole packet was swallowed
|
|
*/
|
|
dc->dc_packet.data = NULL;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
|
|
/*
|
|
* These are the externally visible functions. They are documented in
|
|
* devsw.h
|
|
*/
|
|
Packet *DevSW_AllocatePacket(const unsigned int length)
|
|
{
|
|
Packet *pk;
|
|
|
|
if ((pk = malloc(sizeof(*pk))) == NULL)
|
|
{
|
|
WARN("malloc failure");
|
|
return NULL;
|
|
}
|
|
|
|
if ((pk->pk_buffer = malloc(length+CHAN_HEADER_SIZE)) == NULL)
|
|
{
|
|
WARN("malloc failure");
|
|
free(pk);
|
|
return NULL;
|
|
}
|
|
|
|
return pk;
|
|
}
|
|
|
|
void DevSW_FreePacket(Packet *pk)
|
|
{
|
|
free(pk->pk_buffer);
|
|
free(pk);
|
|
}
|
|
|
|
AdpErrs DevSW_Open(DeviceDescr *device, const char *name, const char *arg,
|
|
const DevChanID type)
|
|
{
|
|
DevSWState *ds;
|
|
|
|
/*
|
|
* is this the very first open call for this driver?
|
|
*/
|
|
if ((ds = (DevSWState *)(device->SwitcherState)) == NULL)
|
|
{
|
|
/*
|
|
* yes, it is: initialise state
|
|
*/
|
|
if ((ds = malloc(sizeof(*ds))) == NULL)
|
|
/* give up */
|
|
return adp_malloc_failure;
|
|
|
|
(void)memset(ds, 0, sizeof(*ds));
|
|
device->SwitcherState = (void *)ds;
|
|
}
|
|
|
|
/*
|
|
* check that we haven't already been opened for this type
|
|
*/
|
|
if ((ds->ds_opendevchans & (1 << type)) != 0)
|
|
return adp_device_already_open;
|
|
|
|
/*
|
|
* if no opens have been done for this device, then do it now
|
|
*/
|
|
if (ds->ds_opendevchans == 0)
|
|
if (device->DeviceOpen(name, arg) < 0)
|
|
return adp_device_open_failed;
|
|
|
|
/*
|
|
* open has finished
|
|
*/
|
|
ds->ds_opendevchans |= (1 << type);
|
|
return adp_ok;
|
|
}
|
|
|
|
AdpErrs DevSW_Match(const DeviceDescr *device, const char *name,
|
|
const char *arg)
|
|
{
|
|
return (device->DeviceMatch(name, arg) == -1) ? adp_failed : adp_ok;
|
|
}
|
|
|
|
AdpErrs DevSW_Close(const DeviceDescr *device, const DevChanID type)
|
|
{
|
|
DevSWState *ds = (DevSWState *)(device->SwitcherState);
|
|
Packet *pk;
|
|
|
|
if ((ds->ds_opendevchans & (1 << type)) == 0)
|
|
return adp_device_not_open;
|
|
|
|
ds->ds_opendevchans &= ~(1 << type);
|
|
|
|
/*
|
|
* if this is the last close for this channel, then inform the driver
|
|
*/
|
|
if (ds->ds_opendevchans == 0)
|
|
device->DeviceClose();
|
|
|
|
/*
|
|
* release all packets of the appropriate type
|
|
*/
|
|
for (pk = Adp_removeFromQueue(&(ds->ds_readqueue[type]));
|
|
pk != NULL;
|
|
pk = Adp_removeFromQueue(&(ds->ds_readqueue[type])))
|
|
DevSW_FreePacket(pk);
|
|
|
|
/* Free memory */
|
|
free ((char *) device->SwitcherState);
|
|
device->SwitcherState = 0x0;
|
|
|
|
/* that's all */
|
|
return adp_ok;
|
|
}
|
|
|
|
AdpErrs DevSW_Read(const DeviceDescr *device, const DevChanID type,
|
|
Packet **packet, bool block)
|
|
{
|
|
int read_err;
|
|
DevSWState *ds = device->SwitcherState;
|
|
|
|
/*
|
|
* To try to get information out of the device driver as
|
|
* quickly as possible, we try and read more packets, even
|
|
* if a completed packet is already available.
|
|
*/
|
|
|
|
/*
|
|
* have we got a packet currently pending?
|
|
*/
|
|
if (ds->ds_nextreadpacket == NULL)
|
|
/*
|
|
* no - set things up
|
|
*/
|
|
if (initialise_read(ds) < 0) {
|
|
/*
|
|
* we failed to initialise the next packet, but can
|
|
* still return a packet that has already arrived.
|
|
*/
|
|
*packet = Adp_removeFromQueue(&ds->ds_readqueue[type]);
|
|
return adp_ok;
|
|
}
|
|
read_err = device->DeviceRead(&ds->ds_activeread, block);
|
|
switch (read_err) {
|
|
case 1:
|
|
/*
|
|
* driver has pulled in a complete packet, queue it up
|
|
*/
|
|
#ifdef RET_DEBUG
|
|
printf("got a complete packet\n");
|
|
#endif
|
|
enqueue_packet(ds);
|
|
*packet = Adp_removeFromQueue(&ds->ds_readqueue[type]);
|
|
return adp_ok;
|
|
case 0:
|
|
/*
|
|
* OK, return the head of the read queue for the given type
|
|
*/
|
|
/* enqueue_packet(ds); */
|
|
*packet = Adp_removeFromQueue(&ds->ds_readqueue[type]);
|
|
return adp_ok;
|
|
case -1:
|
|
#ifdef RET_DEBUG
|
|
printf("got a bad packet\n");
|
|
#endif
|
|
/* bad packet */
|
|
*packet = NULL;
|
|
return adp_bad_packet;
|
|
default:
|
|
panic("DevSW_Read: bad read status %d", read_err);
|
|
}
|
|
return 0; /* get rid of a potential compiler warning */
|
|
}
|
|
|
|
|
|
AdpErrs DevSW_FlushPendingWrite(const DeviceDescr *device)
|
|
{
|
|
struct DriverCall *dc;
|
|
struct data_packet *dp;
|
|
|
|
dc = &((DevSWState *)(device->SwitcherState))->ds_activewrite;
|
|
dp = &dc->dc_packet;
|
|
|
|
/*
|
|
* try to flush any packet that is still being written
|
|
*/
|
|
if (dp->data != NULL)
|
|
{
|
|
flush_packet(device, dc);
|
|
|
|
/* see if it has gone */
|
|
if (dp->data != NULL)
|
|
return adp_write_busy;
|
|
else
|
|
return adp_ok;
|
|
}
|
|
else
|
|
return adp_ok;
|
|
}
|
|
|
|
|
|
AdpErrs DevSW_Write(const DeviceDescr *device, Packet *packet, DevChanID type)
|
|
{
|
|
struct DriverCall *dc;
|
|
struct data_packet *dp;
|
|
|
|
dc = &((DevSWState *)(device->SwitcherState))->ds_activewrite;
|
|
dp = &dc->dc_packet;
|
|
|
|
if (illegalDevChanID(type))
|
|
return adp_illegal_args;
|
|
|
|
/*
|
|
* try to flush any packet that is still being written
|
|
*/
|
|
if (DevSW_FlushPendingWrite(device) != adp_ok)
|
|
return adp_write_busy;
|
|
|
|
/*
|
|
* we can take this packet - set things up, then try to get rid of it
|
|
*/
|
|
initialise_write(dc, packet, type);
|
|
flush_packet(device, dc);
|
|
|
|
return adp_ok;
|
|
}
|
|
|
|
AdpErrs DevSW_Ioctl(const DeviceDescr *device, const int opcode, void *args)
|
|
{
|
|
return (device->DeviceIoctl(opcode, args) < 0) ? adp_failed : adp_ok;
|
|
}
|
|
|
|
bool DevSW_WriteFinished(const DeviceDescr *device)
|
|
{
|
|
struct DriverCall *dc;
|
|
struct data_packet *dp;
|
|
|
|
dc = &((DevSWState *)(device->SwitcherState))->ds_activewrite;
|
|
dp = &dc->dc_packet;
|
|
|
|
return (dp == NULL || dp->data == NULL);
|
|
}
|
|
|
|
/* EOF devsw.c */
|