(原创文章,转载注明出处,谢谢)
dm9000_txDaemon 任务:
void dm9000_txDaemon (void *arg)
{
dm9000_softc_t *sc = (dm9000_softc_t *)arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
struct mbuf *m;
rtems_event_set events;
for (;;)
{
/* turn on TX interrupt, then wait for one */
rtems_bsdnet_event_receive(
START_TRANSMIT_EVENT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events);
/* Send packets till queue is empty */
for (;;)
{
/* Get the next mbuf chain to transmit. */
IF_DEQUEUE(&ifp->if_snd, m);
if (!m)
break;
dm9000_sendpacket(sc, m);
}
ifp->if_flags &= ~IFF_OACTIVE;
}
}
/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
unsigned int length = 0;
rtems_interval tmo;
/*
整个发送任务没有什么好解释的,但我想解释一下这一段代码:
蓝色的代码,很显然,数据被Copy了两次。一次是从mbuf中拷贝到packet中,第二次是由packet拷贝到dm9000中。由于协议包的数据分布在mbuf所定义的链表中,不是一个连续的包,必须组合以后才可以输出到dm9000中。但是有没有别的好办法,可以减少一次Copy过程呢?办法是有的,那就是想办法,把两个不连续的指针所指向的数据接合起来。这个问题其实比较复杂,要分三种情况,DM9000总线分为三种工作模式:8 bit 、16bit 、32bit。三种情况下的操作都不一样。 详情请看下面的注释:
*/
#if 0
struct mbuf *l;
static uint8_t packet[2000];
/* copy the mbuf chain into the transmit buffer */
l = m;
while (l != NULL) {
memcpy(((char *)packet + length), /* offset into pkt for mbuf */
(char *)mtod(l, void *), /* cast to void */
l->m_len); /* length of this mbuf */
length += l->m_len; /* update offset */
l = l->m_next; /* get next mbuf, if any */
}
/* free the mbuf chain we just copied */
m_freem(m);
DM9000_DMP_PACKET("eth_send", packet, length);
/* Move data to DM9000 TX RAM */
DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
/* push the data to the TX-fifo */
(sc->outblk)(packet, length);
#else
/* Move data to DM9000 TX RAM */
DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
/* 依据DM9000的工作模式
oubmbuf指针可能指向三个函数: dm9000_outmbuf_8bit、 dm9000_outmbuf_16bit、 dm9000_outmbuf_32bit ,
*/
length = (sc->outmbuf)(m);
m_freem(m);
#endif
/* Set TX length to DM9000 */
DM9000_iow(DM9000_TXPLL, length & 0xff);
DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
/* Issue TX polling command */
DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
/* wait for end of transmission */
tmo = rtems_clock_get_ticks_since_boot() + 5 * rtems_clock_get_ticks_per_second();
while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
!(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
if (rtems_clock_get_ticks_since_boot() >= tmo)
{
printf("transmission timeout/n");
break;
}
}
DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
DM9000_DBG("transmit done/n/n");
}
/*
这三个函数即是将mbuf输出到dm9000中的拷贝过程。其中8bit比较简单,因为一个字节copy不需要考虑一些特殊问题。
但是16bit 就没那么幸运了,至少要考虑以下情况:
假设mbuf链表是由5个元素组成,依次所包含的数据长度是:
14 21 56 0 0
可以看到,14个字节,刚好可以组成7个双字(16bit)输出到dm9000里。
下面21只能组成10个双字,还余下一个字节,需要和下面的数据块组成双字输出到DM9000里。
本来56个字节自己刚好能组成28个双字的,现在只能组成27个,因为上面借去了一个。余下最后一个字节,单独的输出到DM9000里。
更特殊一点的情况:
14 21 0 56 0
当21向后面一个借字节时,发现是空的。只能再往下借了。
32Bit 的就更加复杂了,但是分析过程如同16bit。我没有编写相关的代码,图省事,就Copy了两次。主要是DM9000在mini2440上的工作模式,我配置成了16Bit。
*/
static uint32_t dm9000_outmbuf_8bit(struct mbuf *m)
{
struct mbuf *l;
uint32_t length = 0;
l = m;
while (l != NULL)
{
if (l->m_len > 0)
dm9000_outblk_8bit(mtod(l, void *), l->m_len);
length += l->m_len; /* update offset */
l = l->m_next; /* get next mbuf, if any */
}
return (length);
}
static uint32_t dm9000_outmbuf_16bit(struct mbuf *m)
{
uint32_t offset = 0;
uint32_t len;
uint8_t data[2];
uint8_t *ptr;
uint32_t length = 0;
struct mbuf *l;
l = m;
while (l != NULL)
{
if (l->m_len > 0)
{
ptr = ((uint8_t *)mtod(l, void *));
len = l->m_len - offset;
if (offset == 1)
{
data[1] = *ptr;
dm9000_outblk_16bit(data, 2);
}
dm9000_outblk_16bit((ptr + offset), (len & 0xFFFFFFFE));
offset = len & 0x1;
if (offset)
{
data[0] = *(ptr + l->m_len - 1);
}
}
length += l->m_len;
l = l->m_next;
}
if (offset)
dm9000_outblk_16bit(data, 1);
return (length);
}
static uint32_t dm9000_outmbuf_32bit(struct mbuf *m)
{
struct mbuf *l;
uint32_t length = 0;
uint8_t packet[2000];
l = m;
while (l != NULL) {
memcpy(((char *)packet + length), /* offset into pkt for mbuf */
(char *)mtod(l, void *), /* cast to void */
l->m_len); /* length of this mbuf */
length += l->m_len; /* update offset */
l = l->m_next; /* get next mbuf, if any */
}
dm9000_outblk_32bit(packet, length);
return (length);
}
dm9000_rxDaemon 函数:
/* dm9000 reader task */
void dm9000_rxDaemon(void *arg)
{
dm9000_softc_t *sc = (dm9000_softc_t *)arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
struct mbuf *m;
struct ether_header *eh;
rtems_event_set events;
uint8_t rxbyte;
uint16_t RxStatus, RxLen = 0;
/* Input packet handling loop */
for (;;) {
rtems_bsdnet_event_receive(
START_RECEIVE_EVENT,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events);
for (;;) {
/*申请mbuf*/
/* get an mbuf this packet */
MGETHDR(m, M_WAIT, MT_DATA);
/* now get a cluster pointed to by the mbuf */
/* since an mbuf by itself is too small */
MCLGET(m, M_WAIT);
/* set the type of mbuf to ifp (ethernet I/F) */
m->m_pkthdr.rcvif = ifp;
m->m_nextpkt = 0;
DM9000_ior(DM9000_MRCMDX); /* Dummy read */
/* Get most updated data,
only look at bits 0:1, See application notes DM9000 */
rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
/* Status check: this byte must be 0 or 1 */
if (rxbyte > DM9000_PKT_RDY) {
DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */
DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */
printf("DM9000 error: status check fail: 0x%x/n",
rxbyte);
m_freem(m);
/*错误恢复*/
rtems_task_wake_after(2 * rtems_clock_get_ticks_per_second());
dm9000_init_hw(sc);
break;
}
if (rxbyte != DM9000_PKT_RDY)
{
m_freem(m);
break; /* No packet received, ignore */
}
DM9000_DBG("receiving packet/n");
DM9000_outb(DM9000_MWCMD, DM9000_IO);
/* A packet ready now & Get status/length */
(sc->rx_status)(&RxStatus, &RxLen);
DM9000_DBG("rx status: 0x%04x rx len: %d/n", RxStatus, RxLen);
if ((RxStatus & 0xbf00) || (RxLen < 0x40)
|| (RxLen > DM9000_PKT_MAX)) {
if (RxStatus & 0x100) {
printf("rx fifo error/n");
}
if (RxStatus & 0x200) {
printf("rx crc error/n");
}
if (RxStatus & 0x8000) {
printf("rx length error/n");
}
if (RxLen > DM9000_PKT_MAX) {
printf("rx length too big/n");
/*dm9000_reset();*/
/*错误恢复*/
dm9000_init_hw(sc);
}
m_freem(m);
} else {
/* Move data from DM9000 */
/* Read received packet from RX SRAM */
(sc->inblk)(m->m_ext.ext_buf, RxLen); /*接收数据包*/
DM9000_DMP_PACKET("eth_rx", rdptr, RxLen);
DM9000_DBG("passing packet to upper layer/n");
/* set the length of the mbuf */
m->m_len = RxLen - (sizeof(struct ether_header) + 4);/*移动到数据头位置*/
m->m_pkthdr.len = m->m_len;
/* strip off the ethernet header from the mbuf */
/* but save the pointer to it */
eh = mtod (m, struct ether_header *);
m->m_data += sizeof(struct ether_header);
ether_input(ifp, eh, m);/*输出到以太网协议栈内*/
}
}
} /* for (;;) */
}
dm9000_ioctl 函数:
/* Driver ioctl handler */
static int
dm9000_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
{
dm9000_softc_t *sc = ifp->if_softc;
int error = 0;
switch (command) {
case SIOCGIFADDR:
case SIOCSIFADDR:
ether_ioctl (ifp, command, data); /*定式,设置获取IF的地址。让协议栈去做 */
break;
case SIOCSIFFLAGS:
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))/*启动停止驱动*/
{
case IFF_RUNNING:
dm9000_stop (sc);
break;
case IFF_UP:
dm9000_init (sc);
break;
case IFF_UP | IFF_RUNNING:
dm9000_stop (sc);
dm9000_init (sc);
break;
default:
break;
} /* switch (if_flags) */
break;
case SIO_RTEMS_SHOW_STATS:/*打印驱动的一些统计数据,这里我偷懒了,没有统计任何数据,其实,大家可以把中断的次数、接收到的数据包、发送的数据包等等都统计下来,可以用作调试*/
dm9000_stats (sc);
break;
/*
* FIXME: All sorts of multicast commands need to be added here!
*/
default:
error = EINVAL;
break;
} /* switch (command) */
return error;
}
驱动程序中断相关函数
驱动程序中断相关函数相对比较简单,实在没啥可讲的。就贴出来,占一些篇幅吧。
static void dm9000_isr_on(const rtems_irq_connect_data *unused)
{
rEXTINT0 = 0x12222222;
/*Initialize the interrupt, mini2440 eint7*/
/*eint4_7 use irq mode*/
rINTMOD &= (~BIT_EINT4_7);
/*enable eint7, io -> eint7*/
rGPFCON = ((rGPFCON & 0x3FFF) | (0x2 << 14));
/*enable eint7*/
rEINTMASK &= (~0x80);
/*enable eint4-7*/
rINTMSK &= (~BIT_EINT4_7);
return;
}
static void dm9000_isr_off(const rtems_irq_connect_data *unused)
{
/* disable all various TX/RX interrupts */
rINTMSK |= BIT_EINT4_7;
rEINTMASK |= 0x80;
return;
}
/* Tests to see if dm9000 interrupts are enabled, and
* returns non-0 if so.
* If interrupt is not enabled, returns 0.
*/
static int dm9000_isr_is_on(const rtems_irq_connect_data *irq)
{
return ((rINTMSK & BIT_EINT4_7) == 0)? 1:0; /* any interrupts enabled? */
}
/* interrupt handler */
rtems_isr dm9000_isr (rtems_vector_number v)
{
int status;
#if !defined(CONFIG_NET_POLL_CONTROLLER)
rEINTMASK |= 0x80;
#endif
DM9000_iow(DM9000_IMR, IMR_PAR);
status = DM9000_ior(DM9000_ISR);
DM9000_iow(DM9000_ISR, status);
if ((status & ISR_PRS))
{
rtems_event_send (softc.rxDaemonTid, START_RECEIVE_EVENT);
}
DM9000_iow(DM9000_IMR, IMR_PAR | IMR_PRM);
#if !defined(CONFIG_NET_POLL_CONTROLLER)
rEINTPEND = 0x80;
ClearPending(BIT_EINT4_7);
rEINTMASK &= ~0x80;
#endif
}
至此,关键的地方我们都说到了,下一篇,我们总结一下这个驱动。并点评一下要点。
(原创文章,转载注明出处,谢谢)