Jan,
Thanks for such a quick response! We've made good progress with
the RTnet Arcnet driver but have a few questions about code which
doesn't quite match up to the existing examples.
And I have little experience with designing network drivers (or
any driver!) .. therefore the questions might seem basic.
1.) The original arcnet driver does not call alloc_etherdev but
rather alloc_netdev (shown below). Can I still change this
function call like suggested in your porting documenation? What
effect will this have on the program?
BEFORE
struct net_device *alloc_arcdev(char *name)
{
struct net_device *dev;
dev = alloc_netdev(sizeof(struct arcnet_local),
name && *name ? name : "arc%d", arcdev_setup);
if(dev) {
struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
spin_lock_init(&lp->lock);
}
return dev;
}
AFTER
struct rtnet_device *alloc_arcdev(char *name)
{
struct rtnet_device *dev;
/*** RTnet ***/
dev = rt_alloc_etherdev(sizeof(struct arcnet_local)); //
etherdev?
rtdev_alloc_name(dev, "rtarc%d");
rt_rtdev_connect(dev, &RTDEV_manager);
RTNET_SET_MODULE_OWNER(dev);
dev->vers = RTDEV_VERS_2_0;
/*** RTnet ***/
if(dev) {
struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
rtdm_lock_init(&lp->lock); /*** RTnet ***/
}
return dev;
}
2.) In your example, you initialize the skb_pool in the probe
function and if the return value is less than twice the ring size
then you release the skb_pool and call a few 'cleanup' functions.
e.g. pci_free_consistent(lp->pci_dev, sizeof(*lp), lp,
lp->dma_addr);
I'm not sure how it works, but I cannot this function because
the struct arcnet_local does not have a field called 'dma_addr'.
So my question is .. is dma_addr disguised under another name in
the arcnet_local struct? and if not do I even need to call this
function?
static int __devinit com20020pci_probe(struct pci_dev *pdev, const
struct pci_device_id *id)
{
struct rtnet_device *dev;
struct arcnet_local *lp;
int ioaddr, err;
/*** RTnet ***/
cards_found++;
if (cards[cards_found] == 0)
return -ENODEV;
/*** RTnet ***/
if (pci_enable_device(pdev))
return -EIO;
dev = alloc_arcdev(device);
if (!dev)
return -ENOMEM;
lp = dev->priv;
pci_set_drvdata(pdev, dev);
// SOHARD needs PCI base addr 4
if (pdev->vendor==0x10B5) {
BUGMSG(D_NORMAL, "SOHARD\n");
ioaddr = pci_resource_start(pdev, 4);
}
else {
BUGMSG(D_NORMAL, "Contemporary Controls\n");
ioaddr = pci_resource_start(pdev, 2);
}
if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com20020-pci"))
{
BUGMSG(D_INIT, "IO region %xh-%xh already allocated.\n",
ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
err = -EBUSY;
goto out_dev;
}
/*** RTnet ***/
if (rtskb_pool_init(&lp->skb_pool, RX_RING_SIZE*2) <
RX_RING_SIZE*2) {
rtskb_pool_release(&lp->skb_pool);
---------> // pci_free_consistent(lp->pci_dev, sizeof(*lp), lp,
lp->dma_addr);
release_region(ioaddr, ARCNET_TOTAL_SIZE);
rtdev_free(dev);
return -ENOMEM;
}
/*** RTnet ***/
// Dummy access after Reset
// ARCNET controller needs this access to detect bustype
outb(0x00,ioaddr+1);
inb(ioaddr+1);
dev->base_addr = ioaddr;
dev->irq = pdev->irq;
dev->dev_addr[0] = node;
lp->card_name = "PCI COM20020";
lp->card_flags = id->driver_data;
lp->backplane = backplane;
lp->clockp = clockp & 7;
lp->clockm = clockm & 3;
lp->timeout = timeout;
lp->hw.owner = THIS_MODULE;
if (ASTATUS() == 0xFF) {
BUGMSG(D_NORMAL, "IO address %Xh was reported by PCI BIOS, "
"but seems empty!\n", ioaddr);
err = -EIO;
goto out_port;
}
if (com20020_check(dev)) {
err = -EIO;
goto out_port;
}
if ((err = com20020_found(dev, SA_SHIRQ)) != 0)
goto out_port;
return 0;
out_port:
release_region(ioaddr, ARCNET_TOTAL_SIZE);
out_dev:
rtdev_free(dev);
return err;
}
here's a snapshot of the arcnet_local struct ...
struct arcnet_local {
struct net_device_stats stats;
uint8_t config, /* current value of CONFIG register */
timeout, /* Extended timeout for COM20020 */
backplane, /* Backplane flag for COM20020 */
clockp, /* COM20020 clock divider */
clockm, /* COM20020 clock multiplier flag */
setup, /* Contents of setup1 register */
setup2, /* Contents of setup2 register */
intmask; /* current value of INTMASK register */
uint8_t default_proto[256]; /* default encap to use for each host
*/
int cur_tx, /* buffer used by current transmit, or -1 */
next_tx, /* buffer where a packet is ready to send */
cur_rx; /* current receive buffer */
int lastload_dest, /* can last loaded packet be acked? */
lasttrans_dest; /* can last TX'd packet be acked? */
int timed_out; /* need to process TX timeout and drop packet */
unsigned long last_timeout; /* time of last reported timeout */
char *card_name; /* card ident string */
int card_flags; /* special card features */
/*** RTnet ***/
struct rtskb *tx_skbuff[TX_RING_SIZE];
struct rtskb *rx_skbuff[RX_RING_SIZE];
struct rtskb_queue skb_pool;
rtdm_irq_t irq_handle;
/*** RTnet ***/
/* On preemtive and SMB a lock is needed */
rtdm_lock_t lock;
/*
* Buffer management: an ARCnet card has 4 x 512-byte buffers,
each of
* which can be used for either sending or receiving. The new
dynamic
* buffer management routines use a simple circular queue of
available
* buffers, and take them as they're needed. This way, we
simplify
* situations in which we (for example) want to pre-load a
transmit
* buffer, or start receiving while we copy a received packet to
* memory.
*
* The rules: only the interrupt handler is allowed to _add_
buffers to
* the queue; thus, this doesn't require a lock. Both the
interrupt
* handler and the transmit function will want to _remove_
buffers, so
* we need to handle the situation where they try to do it at the
same
* time.
*
* If next_buf == first_free_buf, the queue is empty. Since
there are
* only four possible buffers, the queue should never be full.
*/
atomic_t buf_lock;
int buf_queue[5];
int next_buf, first_free_buf;
/* network "reconfiguration" handling */
time_t first_recon, /* time of "first" RECON message to count
*/
last_recon; /* time of most recent RECON */
int num_recons; /* number of RECONs between first and last. */
bool network_down; /* do we think the network is down? */
bool excnak_pending; /* We just got an excesive nak interrupt */
struct {
uint16_t sequence; /* sequence number (incs with each packet)
*/
uint16_t aborted_seq;
struct Incoming incoming[256]; /* one from each address */
} rfc1201;
/* really only used by rfc1201, but we'll pretend it's not */
struct Outgoing outgoing; /* packet currently being sent */
/* hardware-specific functions */
struct {
struct module *owner;
void (*command) (struct rtnet_device * dev, int cmd);
int (*status) (struct rtnet_device * dev);
void (*intmask) (struct rtnet_device * dev, int mask);
bool (*reset) (struct rtnet_device * dev, bool really_reset);
void (*open) (struct rtnet_device * dev);
void (*close) (struct rtnet_device * dev);
void (*copy_to_card) (struct rtnet_device * dev, int bufnum, int
offset,
void *buf, int count);
void (*copy_from_card) (struct rtnet_device * dev, int bufnum,
int offset,
void *buf, int count);
} hw;
void __iomem *mem_start; /* pointer to ioremap'ed MMIO */
};
and if you're curious com20020_found looks like this ...
/* Set up the struct rtnet_device associated with this card.
Called after
* probing succeeds.
*/
int com20020_found(struct rtnet_device *dev, int shared)
{
struct arcnet_local *lp;
int ioaddr = dev->base_addr;
int retval;
/* Initialize the rest of the device structure. */
lp = dev->priv;
lp->hw.owner = THIS_MODULE;
lp->hw.command = com20020_command;
lp->hw.status = com20020_status;
lp->hw.intmask = com20020_setmask;
lp->hw.reset = com20020_reset;
lp->hw.copy_to_card = com20020_copy_to_card;
lp->hw.copy_from_card = com20020_copy_from_card;
lp->hw.close = com20020_close;
// dev->set_multicast_list = com20020_set_mc_list; /*** RTNET
***/
if (!dev->dev_addr[0])
dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this
some other way! (not written by me - Chris) */
SET_SUBADR(SUB_SETUP1);
outb(lp->setup, _XREG);
if (lp->card_flags & ARC_CAN_10MBIT)
{
SET_SUBADR(SUB_SETUP2);
outb(lp->setup2, _XREG);
/* must now write the magic "restart operation" command */
mdelay(1);
outb(0x18, _COMMAND);
}
lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) |
1;
/* Default 0x38 + register: Node ID */
SETCONF;
outb(dev->dev_addr[0], _XREG);
/*** RTnet ***/
/* reserve the irq */
/*if (request_irq(dev->irq, &arcnet_interrupt, shared,
"arcnet (COM20020)", dev)) {
BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
return -ENODEV;
}*/
if (dev->irq == 0)
return -EAGAIN;
rt_stack_connect(dev, &STACK_manager);
retval = rtdm_irq_request(&lp->irq_handle, dev->irq,
arcnet_interrupt, 0,
"rt_arcnet", dev);
if (retval)
return retval;
rtdm_irq_enable(&lp->irq_handle);
/*** RTnet ***/
dev->base_addr = ioaddr;
BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
lp->card_name, dev->dev_addr[0], dev->base_addr,
dev->irq);
if (lp->backplane)
BUGMSG(D_NORMAL, "Using backplane mode.\n");
if (lp->timeout != 3)
BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n",
lp->timeout);
BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n",
lp->setup >> 1,
clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup &
0x0F) >> 1)]);
/*** RTnet ***/
/*if (register_netdev(dev)) {
free_irq(dev->irq, dev);
return -EIO;
}*/
if (rt_register_rtnetdev(dev))
{
rtdm_irq_free(&lp->irq_handle);
return -EIO;
}
/*** RTnet ***/
return 0;
}
3.) Ok. last question. It seems like I have a problem with module
dependencies. The arcnet driver is set up to be loaded as four
separate modules: arcnet.ko (the foundation), arc-rawmode.ko
(provides rawmode encapsulation), com20020.ko (support for
chipset), and com20020-pci.ko (support for pci-board).
I've done my homework and created four rt modules that compile and
load into the kernel.. but the problem is that I cannot get them
to load with 'rtnet start'. How do I specify dependencies in the
'rtnet.conf' file? My dilemna is that if I specify com20020-pci
without any extra work.. the program will not run because it
cannot load the module without its dependencies.. BUT I cannot
load rt_arcnet.ko and rt_com20020.ko before running 'rtnet start'
because they both depend on the module rtnet!!
So I feel like I'm in a bit of a catch-22. Please tell me how to
get around this problem.
Thank you so much for your help.
Chris Lightcap
University of Florida
On Tue Jul 11 04:03:33 EDT 2006, Jan Kiszka <jan...@we...>
wrote:
> Chris Lightcap wrote:
>> Hello all,
>>
>> I'd like to implement a real-time arcnet driver to communicate
>> with
>> our mitsubishi PA-10 robot using RTAI and RTnet. I found in the
>
> You are welcome!
>
>> mailing list archive that there was some interest in this topic
>> about
>> a year ago.
>>
>> .. Has anyone made progress with a real-time arcnet driver? is it
>
> I'm personally not aware of any effort.
>
>> possible to simply modify the existing driver as explained in the
>> porting documentation? are there any additional problems I should
>> know of beforehand?
>
> I think to remember once browsing through the Linux arcnet stack
> without
> finding anything that's fundamentally different from the "normal"
> networking stack. So all the pattern of RTnet /should/ apply to
> arcnet
> as well. If you encounter something that doesn't fit on first
> sight,
> feel free to discuss it here.
>
> Please build your port on top of latest RTnet and only use RTDM
> for
> interfacing the real-time Linux services - this will help to
> integrate
> your work into the official version later.
>
> Jan
>
>
--
LIGHTCAP,CHRISTOPHER A
|