Below is the code for my custom device driver “bdd” where the open() call to
/dev/phy fails with errno 3.
bdd registers both an UP producer and a converter. Its a custom driver of
type “bd”. The call to open() /dev/phy takes place in the
bdd_RegisterDevice() routine. I call the open() to /dev/phy after I spawn a
receive thread for the device and register the device with io-net.
I very much appreciate your help in looking over this. Thanks!
/*
- bdd
- Description: A custom network device driver of type “bd” that
sends/receives IP
- packets via another device driver.
- This code registers both the UP Producer and a pass-thru Converter
-
*/
// io-net functions used in the code.
#define ionetRxPackets ext->ion->tx_up_start
#define ionetRegister ext->ion->reg
#define ionetDeregister ext->ion->dereg
#define ionetAlloc ext->ion->alloc
#define ionetFree ext->ion->free
#define ionetAllocNpkt ext->ion->alloc_up_npkt
#define ionetMphys ext->ion->mphys
#define ionetTxComplete ext->ion->tx_done
#define ionetDevctl ext->ion->devctl
// Drvr entry
io_net_dll_entry_t io_net_dll_entry = {
2,
bdd_Init,
NULL
};
// Functions associated with our UP producer
io_net_registrant_funcs_t bddFuncs = {
_IO_NET_REG_NFUNCS,
NULL,
bdd_Transmit,
bdd_ReceiveComplete,
bdd_Shutdown1,
bdd_Shutdown2,
bdd_Advertise,
bdd_Devctl,
bdd_FlushTxQueue,
NULL,
};
// UP Producer register entry
io_net_registrant_t bddEntry = {
_REG_PRODUCER_UP,
“devn-bdd.so”,
“bd”,
NULL,
NULL,
&bddFuncs,
0
};
// Functions associated with our ip-bd converter
// Notice: I am using some of UP producer functions here
// so the IP stack can transmit packets directly to the UP producer
// and bypass the converter
io_net_registrant_funcs_t bdcFuncs = {
_IO_NET_REG_NFUNCS, // nfuncs,
bdc_RxUp,
bdd_Transmit,
bdd_ReceiveComplete,
bdc_Shutdown1,
bdc_Shutdown2,
NULL,
NULL,
NULL,
NULL,
};
// Converter register entry
io_net_registrant_t bdcEntry = {
_REG_CONVERTOR,
“devn-bdd.so”,
“ip”,
“bd”,
NULL,
&bdcFuncs,
0,
};
/*
- bdd_Init() – Our DLL entry point.
-
*/
int bdd_Init(void *dll_hdl, dispatch_t *dpp, io_net_self_t *ion, char
*options)
{
int ret;
dpp = dpp;
if ((ret = bdd_Detect(dll_hdl, ion, options)) != SUCCESS)
{
errno = ret;
return FAILURE;
}
return SUCCESS;
}
/*
- bdd_Detect – Detect device interfaces and register them with io-net
-
*/
int bdd_Detect( void *dll_hdl, io_net_self_t *ion, char *options )
{
int iface, totalInterfaces = 1; //hardcoded to 1 for testing
Nic_t *nic = NULL, *conv_nic = NULL;
BDD_EXT *ext = NULL;
int reg_convhdl;
uint16_t lan;
// Some prints for debugging
fprintf(stderr, “bdd: Detect\n”);
// Parse command line args
//TODO: Code missing
// First and foremost, create a device
//and register our custom converter with io-net
fflush(stdout);
if ((conv_nic = nic_create_dev(sizeof (BDD_EXT))) == NULL)
{
return ENODEV;
}
ext = (BDD_EXT *)(conv_nic->ext);
memset((char *) ext, 0, sizeof(BDD_EXT));
ext->ion = ion;
ext->dll_hdl = dll_hdl;
ext->verbose = 1; // temp verbose
bdcEntry.func_hdl = (void *) conv_nic;
if ((ion->reg(dll_hdl, &bdcEntry, &ext->reg_convhdl, &ext->cell, &lan))
!= SUCCESS)
{
fprintf(stderr, “bdd: Unable to register the converter with
io-net\n”);
return FAILURE;
}
conv_nic->lan = lan;
reg_convhdl = ext->reg_convhdl;
for (iface = 0;iface < totalBackhaulInterfaces; iface++)
{
// For each interface, do the following…
// Create a device structure and initialize it
if ((nic = nic_create_dev(sizeof (BDD_EXT))) == NULL)
{
return ENODEV;
}
ext = (BDD_EXT *)(nic->ext);
memset((char *) ext, 0, sizeof(BDD_EXT));
ext->ion = ion;
ext->dll_hdl = dll_hdl;
ext->reg_convhdl = reg_convhdl;
ext->verbose = 1; // temp verbose
// Register device to io-net and spawn an event thread
if ((bdd_RegisterDevice(nic, ion, dll_hdl)) != SUCCESS)
{
return FAILURE;
}
// Advertise the device
bdd_Advertise(ext->reg_hdl, nic);
}
return SUCCESS;
}
/*
*
- bdd_Advertise – Advertise device to io-net and upper layers
-
-
*/
int bdd_Advertise(int reg_hdl, void *func_hdl)
{
npkt_t *npkt = NULL;
net_buf_t *nb = NULL;
net_iov_t *iov = NULL;
Nic_t *nic = NULL;
BDD_EXT *ext = NULL;
io_net_msg_dl_advert_t *ap = NULL;
nic = (Nic_t *) func_hdl;
ext = (BDD_EXT *) nic->ext;
if (ext->verbose > 1)
{
fprintf(stderr, “bdd: advertise to io-net\n”);
}
nic->mtu = MAX_PACKET_SIZE;
if ((npkt = ionetAllocNpkt(sizeof(*nb) + sizeof *iov,
(void **)&nb)) == NULL)
{
return(ENOMEM);
}
if ((ap = ionetAlloc(sizeof(*ap), 0)) == NULL)
{
ionetFree(npkt);
return(ENOMEM);
}
TAILQ_INSERT_HEAD(&npkt->buffers, nb, ptrs);
iov = (net_iov_t *)(nb + 1);
nb->niov = 1;
nb->net_iov = iov;
iov->iov_base = ap;
iov->iov_len = sizeof *ap;
memset(ap, 0x00, sizeof *ap);
ap->type = _IO_NET_MSG_DL_ADVERT;
// Our interface does not support broadcast or multicast
ap->iflags = (IFF_SIMPLEX | IFF_POINTOPOINT | IFF_RUNNING);
ap->mtu_min = 0;
ap->mtu_max = nic->mtu;
ap->mtu_preferred = nic->mtu;
strcpy(ap->up_type, “bd”);
itoa(nic->lan, ap->up_type + 2, 10);
strcpy(ap->dl.sdl_data, ap->up_type);
ap->dl.sdl_len = sizeof(struct sockaddr_dl);
ap->dl.sdl_family = AF_LINK;
ap->dl.sdl_index = nic->lan;
ap->dl.sdl_type = IFT_OTHER;
// We dont have an ethernet address. Lets put something here for now
ap->dl.sdl_nlen = strlen(ap->dl.sdl_data); /* not null terminated */
ap->dl.sdl_alen = 6;
memcpy(ap->dl.sdl_data + ap->dl.sdl_nlen, “\x1\x2\x3\x4\x5\x6”, 6);
npkt->org_data = ap;
npkt->flags |= _NPKT_MSG;
npkt->iface = 0;
npkt->framelen = sizeof *ap;
//atomic_add(&ext->rxActive, 1);
// we are using ion->tx_up_start to deliver this advertise packet up
if (ionetRxPackets(ext->reg_hdl, npkt, 0, 0, ext->cell, nic->lan, 0,
nic) == SUCCESS)
{
fprintf(stdout, “Advertise: Done with RxPackets\n”);
fflush(stdout);
ionetTxComplete(ext->reg_hdl, npkt);
}
return(EOK);
}
/* bdd_RegisterDevice
*
- Create a pulse channel for communication with physical layer
- Spawn an event thread to receive pulses and fetch packet from the
physical layer
- Register the device with ionet
- and obtain a handle to /dev/phy
-
*/
int bdd_RegisterDevice(Nic_t *nic, io_net_self_t *ion, void *dll_hdl)
{
BDD_EXT *ext = (BDD_EXT *) nic->ext;
pthread_attr_t pattr;
pthread_mutexattr_t mattr;
struct sched_param param;
uint16_t lan;
DEVCTL_PULSE_MESSAGE Pulse;
int dev_ret;
fprintf(stdout, “bdd_RegisterDevice\n”);
fflush(stdout);
if (ext->verbose > 1 )
{
fprintf(stderr, “bdd: RegisterDeviceInstance\n”);
}
// Create an Event Channel to receive pulses from /dev/phy
// This code simply calls CreateChannel() and AttachChannel() and fills
up the
// Event structure with channel ID and connection IDs.
if ((fddCreateEvent(&ext->Event)) != SUCCESS)
{
fprintf(stderr, “bdd: Unable to create and attach a pulse channel
for events\n”);
return ENODEV;
}
// Setup a Mutex for this thread to use
pthread_mutexattr_init(&mattr);
mattr.flags = PTHREAD_RECURSIVE_ENABLE;
if (pthread_mutex_init(&ext->mutex, NULL) == FAILURE)
{
fprintf(stderr,“bdd: RegisterDeviceInstance: phtread_mutex_init
failed\n”);
return ENODEV;
}
// Setup thread attributes
pthread_attr_init(&pattr);
pthread_attr_setschedpolicy(&pattr, SCHED_RR);
param.sched_priority = 21;
pthread_attr_setschedparam(&pattr, ¶m);
pthread_attr_setinheritsched(&pattr, PTHREAD_EXPLICIT_SCHED);
// create the event handler thread
if (pthread_create(&ext->threadId, &pattr,(void *)bdd_EventHandler, nic)
!= SUCCESS)
{
slogf( _SLOG_SETCODE(_SLOGC_NETWORK,0), _SLOG_ERROR,
“bdd: Unable to create driver thread”);
pthread_mutex_destroy(&ext->mutex);
return ENODEV;
}
bddEntry.func_hdl = (void *) nic;
// Register the device with io-net
if (ionetRegister(dll_hdl, &bddEntry, &ext->reg_hdl, &ext->cell, &lan)
!= SUCCESS)
{
slogf( _SLOG_SETCODE(_SLOGC_NETWORK,0), _SLOG_ERROR,
“bdd: Unable to register with io-net”);
pthread_kill(ext->threadId, SIGKILL);
pthread_mutex_destroy(&ext->mutex);
return ENODEV;
}
nic->lan = lan;
if (!ext->maxPackets)
{
ext->maxPackets = MAX_PACKETS;
}
ionetDevctl( ext->reg_hdl, DCMD_IO_NET_MAX_QUEUE, &ext->maxPackets,
sizeof(ext->maxPackets), NULL );
// Get a handle to /dev/phy, our physical layer emulator
/******* This is where the open() fails ************/
if ( (ext->phy_hdl = open("/dev/phy", O_RDWR)) != SUCCESS)
{
fprintf(stderr, “bdd: Can’t open the Phy device. ret = %x Errno =
%x\n”, ext->phy_hdl, errno);
return ext->phy_hdl;
}
// Pass the Channel’s ConnectionID to /dev/phy so he can use it to send
us pulses
Pulse.connectionId = ext->Event->Connection;
if ( (devctl(ext->phy_hdl, DEVCTL_PULSE_CODE, &Pulse, sizeof(Pulse),
&dev_ret)) != SUCCESS)
{
fprintf(stderr, “bdd: Can’t send connectionId to /dev/phy\n”);
return ENODEV;
}
return SUCCESS;
} // end RegisterDeviceInstance
/* bdd_AssemblePacket
*
- Defrag packet received from the IP stack into a linear area
-
*/
int bdd_AssemblePacket( Nic_t *nic, npkt_t *npkt, char *packet, int
*packetSize )
{
char *dst;
net_iov_t *iov;
net_buf_t *buf;
int totalLen;
int i, len;
BDD_EXT *ext;
ext = (BDD_EXT *)nic->ext;
fprintf(stdout, “bdd_AssemblePacket\n”);
fflush(stdout);
// Do some basic checks on the packet size
for ( totalLen = 0, buf = TAILQ_FIRST(&npkt->buffers); buf != NULL;
buf = TAILQ_NEXT( buf, ptrs ) )
{
for ( i = 0, iov = buf->net_iov; i < buf->niov; i++, iov++ )
{
totalLen += iov->iov_len;
}
}
if ( totalLen > MAX_PACKET_SIZE )
{
return FAILURE;
}
*packetSize = npkt->framelen;
// Initialize pointer for copying the entire packet
dst = packet;
// Copy the fragments into a linear area
for ( len = 0, buf = TAILQ_FIRST( &npkt->buffers ); buf != NULL;
buf = TAILQ_NEXT( buf, ptrs ) )
{
for ( i = 0, iov = buf->net_iov; i < buf->niov; i++, iov++ )
{
memcpy( dst, iov->iov_base, iov->iov_len );
dst += iov->iov_len;
len += iov->iov_len;
}
}
return SUCCESS;
}
/*
- bdd_Transmit
- Transmit packets via our physical layer
*/
int bdd_Transmit( npkt_t *npkt, void *hdl )
{
Nic_t *nic;
BDD_EXT *ext;
DEVCTL_MESSAGE packet;
int dev_ret;
nic = (Nic_t *) hdl;
ext = (BDD_EXT *)nic->ext;
//if link is down then return error
fprintf(stdout, “bdd_Transmit\n”);
fflush(stdout);
if ( ext->linkFail == 1 )
{
errno = ENOLINK;
npkt->flags |= _NPKT_NOT_TXED;
pthread_mutex_unlock( &ext->mutex );
ionetTxComplete( ext->reg_hdl, npkt );
pthread_mutex_lock( &ext->mutex );
return(TX_DOWN_FAILED);
}
memset((char *) &packet, 0, sizeof(packet));
// Defrag the packet and assemble the data into a linear packet
if ((bdd_AssemblePacket(nic, npkt, (char*) &packet.buf,
&packet.bufSize)) != SUCCESS)
{
errno = ENOMEM;
npkt->flags |= _NPKT_NOT_TXED;
ionetTxComplete( ext->reg_hdl, npkt );
return(TX_DOWN_FAILED);
} else
{
// Send the packet to the bottom layer (/dev/phy) using devctl
if ( (devctl(ext->phy_hdl, DEVCTL_PACKET_TRANSMIT, &packet,
sizeof(packet), &dev_ret)) != SUCCESS)
{
npkt->flags |= _NPKT_NOT_TXED;
ionetTxComplete( ext->reg_hdl, npkt );
fprintf(stderr, “bdd: Can’t send packet to /dev/phy \n”);
return(TX_DOWN_FAILED);
}
// Give back the packet to io-net
pthread_mutex_unlock( &ext->mutex );
ionetTxComplete( ext->reg_hdl, npkt );
pthread_mutex_lock( &ext->mutex );
// Collect stats
ext->stats.txBytes += packet.bufSize;
ext->stats.txPackets++;
return(TX_DOWN_OK);
}
}
/*
*
- bdd_EventHandler
- This is our EventHandler,
- it will handle all incoming pulses and invoke approprite functions
-
*/
void *bdd_EventHandler(void *data)
{
Nic_t *nic = (Nic_t *)(data);
BDD_EXT *ext;
//iov_t iov;
int ret;
ext = (BDD_EXT *)(nic->ext);
while (1)
{
// fddWaitOnEvent calls MessageReceivePulse and waits for a pulse
// from /dev/phy
ret = fddWaitOnEvent(ext->Event, 0, FDD_TIMEOUT_INFINITE);
if (ret != SUCCESS)
{
if ( errno == ESRCH )
{
pthread_exit( NULL );
}
continue;
}
pthread_mutex_lock(&ext->mutex);
switch (ext->Event->EventCode)
{
case DEVCTL_PACKET_RECEIVE:
bdd_Receive(nic);
break;
}
pthread_mutex_unlock(&ext->mutex);
}
return(NULL);
}
/*
- bdd_Receive
- Receive packet from the physical layer and pass it up
-
*/
void bdd_Receive(Nic_t *nic)
{
BDD_EXT *ext = NULL;
npkt_t *npkt = NULL;
net_buf_t *nb;
net_iov_t *ni = NULL;
int dev_ret;
DEVCTL_MESSAGE packet;
ext = (BDD_EXT *)(nic->ext);
fprintf(stdout, “bdd: Bdd_receive\n”);
fflush(stdout);
// Allocate packet
if ((npkt = ionetAllocNpkt(sizeof(*nb) + sizeof *ni + MAX_PACKET_SIZE,
(void **)&nb)) == NULL)
{
return;
}
// Receive packet data from /dev/phy using devctl
memset((char *) &packet, 0, sizeof(packet));
if ( (devctl(ext->phy_hdl, DEVCTL_PACKET_RECEIVE, &packet,
sizeof(packet), &dev_ret)) != SUCCESS)
{
fprintf(stderr, “bdd: Can’t receive packet from /dev/phy \n”);
bdd_ReceiveComplete(npkt, nic, NULL);
return;
}
// Copy packet to the iov for up delivery
// ni->iov_base is the starting point for a buffer copy
memcpy((char *) ni->iov_base, (char *) &packet.buf, packet.bufSize);
// Fill in the packet structure
npkt->tot_iov = 1;
ni = (net_iov_t *)(nb + 1);
nb->niov = 1;
nb->net_iov = ni;
ni->iov_base = ni + 1;
ni->iov_len = packet.bufSize; // Actual packet size in bytes
npkt->iface = 0;
npkt->flags = _NPKT_UP;
npkt->req_complete = 0;
npkt->ref_cnt = 1;
npkt->framelen = packet.bufSize;
// Collect stats
ext->stats.rxBytes += packet.bufSize;
ext->stats.rxPackets++;
// Send the packet up
pthread_mutex_unlock( &ext->mutex );
if ((ionetRxPackets(ext->reg_convhdl, npkt, 0, 0, ext->cell, nic->lan,
0, nic)) != NULL)
{
bdd_ReceiveComplete(npkt, nic, NULL);
}
pthread_mutex_lock( &ext->mutex );
return;
}
/*
- bdd_ReceiveComplete
- our tx_done()
-
*/
int bdd_ReceiveComplete ( npkt_t *npkt, void *hdl, void *func_hdl )
{
Nic_t *nic;
BDD_EXT *ext;
int ret;
nic = (Nic_t *)hdl;
ext = (BDD_EXT *)nic->ext;
fprintf(stdout, “bdd_ReceiveComplete\n”);
fflush(stdout);
pthread_mutex_lock(&ext->mutex);
// We will simply free up the packet we created.
ret = ionetFree(npkt);
pthread_mutex_unlock(&ext->mutex);
return ret;
}
/*
*
*/
int bdd_Shutdown1( int reg_hdl, void *hdl )
{
Nic_t *nic = (Nic_t *)hdl;
BDD_EXT *ext = (BDD_EXT *)nic->ext;
if (ext->verbose > 1)
{
fprintf(stderr, “bdd: shutdown1 (hdl 0x%lx, nic 0x%lx)\n”,
(ulong_t)(hdl), (ulong_t)(nic));
}
// Destroy the event channel
fddDestroyEvent(ext->Event);
// Close the phy driver instance
close(ext->phy_hdl);
return SUCCESS;
}
int bdd_Shutdown2( int reg_hdl, void *hdl )
{
Nic_t *nic = (Nic_t *)hdl;
BDD_EXT *ext = (BDD_EXT *)nic->ext;
if (ext->verbose > 1)
{
fprintf(stderr, “bdd: shutdown2 (hdl 0x%lx, nic 0x%lx)\n”,
(ulong_t)(hdl), (ulong_t)(nic));
}
// Free up all the device resources
pthread_join(ext->threadId, NULL);
pthread_mutex_destroy(&ext->mutex);
free(nic->ext);
free(nic);
return SUCCESS;
}
int bdd_Devctl( void *hdl, int dcmd, void *data, size_t size, int *ret )
{
return SUCCESS;
}
int bdd_FlushTxQueue( int x, void *hdl )
{
return SUCCESS;
}
/* bdc_RxUp
*
- Converter code to receive packets on the way up. Useful in handling
advertise
- packets
-
*/
int bdc_RxUp( npkt_t *npkt,
void *func_hdl,
int off,
int framlen_sub,
uint16_t cell,
uint16_t endpoint,
uint16_t iface )
{
Nic_t *nic = (Nic_t *)func_hdl;
BDD_EXT *ext = (BDD_EXT *)nic->ext;
fprintf( stdout, “bdc_RxUp\n” );
fflush(stdout);
(*ext->ion->tx_up)( ext->reg_convhdl, npkt, off, framlen_sub, cell,
endpoint, iface );
return EOK;
}
/*
*/
int bdc_Shutdown1( int reg_hdl, void *hdl )
{
return SUCCESS;
}
int bdc_Shutdown2( int reg_hdl, void *hdl )
{
Nic_t *nic = (Nic_t *)hdl;
BDD_EXT *ext = (BDD_EXT *)nic->ext;
if (ext->verbose > 1)
{
fprintf(stderr, “bdc: shutdown2 (hdl 0x%lx, nic 0x%lx)\n”,
(ulong_t)(hdl), (ulong_t)(nic));
}
free(nic->ext);
free(nic);
return SUCCESS;
}