lwip: handle UNDI stacks which need to be polled
authorH. Peter Anvin <hpa@linux.intel.com>
Fri, 22 Apr 2011 22:57:33 +0000 (15:57 -0700)
committerH. Peter Anvin <hpa@linux.intel.com>
Fri, 22 Apr 2011 22:57:33 +0000 (15:57 -0700)
If the UNDI stack reports either IRQ 0 or does NOT report the NDIS IRQ
supported flag, then poll the interrupt routine from the idle thread
instead.

This is somewhat limited; we really should have a chain of idle poll
routines to support things like serial console.

Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
com32/include/syslinux/pxe_api.h
core/fs/pxe/isr.c
core/fs/pxe/pxe.h
core/include/thread.h
core/lwip/src/netif/undiif.c
core/thread/idle_thread.c

index 27166b0..9324329 100644 (file)
@@ -359,7 +359,24 @@ typedef struct s_PXENV_UNDI_GET_IFACE_INFO {
     uint32_t LinkSpeed;
     uint32_t ServiceFlags;
     uint32_t Reserved[4];
-} __packed t_PXENV_UNDI_GET_NDIS_INFO;
+} __packed t_PXENV_UNDI_GET_IFACE_INFO;
+#define PXE_UNDI_IFACE_FLAG_BCAST      0x00000001
+#define PXE_UNDI_IFACE_FLAG_MCAST      0x00000002
+#define PXE_UNDI_IFACE_FLAG_GROUP      0x00000004
+#define PXE_UNDI_IFACE_FLAG_PROMISC    0x00000008
+#define PXE_UNDI_IFACE_FLAG_SOFTMAC    0x00000010
+#define PXE_UNDI_IFACE_FLAG_STATS      0x00000020
+#define PXE_UNDI_IFACE_FLAG_DIAGS      0x00000040
+#define PXE_UNDI_IFACE_FLAG_LOOPBACK   0x00000080
+#define PXE_UNDI_IFACE_FLAG_RCVCHAIN   0x00000100
+#define PXE_UNDI_IFACE_FLAG_IBMSRCRT   0x00000200
+#define PXE_UNDI_IFACE_FLAG_RESET      0x00000400
+#define PXE_UNDI_IFACE_FLAG_OPEN       0x00000800
+#define PXE_UNDI_IFACE_FLAG_IRQ                0x00001000
+#define PXE_UNDI_IFACE_FLAG_SRCRT      0x00002000
+#define PXE_UNDI_IFACE_FLAG_GDTVIRT    0x00004000
+#define PXE_UNDI_IFACE_FLAG_MULTI      0x00008000
+#define PXE_UNDI_IFACE_FLAG_LKFISZ     0x00010000
 
 typedef struct s_PXENV_UNDI_GET_STATE {
 #define PXE_UNDI_GET_STATE_STARTED 1
index 680c7ef..ade3b0d 100644 (file)
@@ -18,32 +18,35 @@ bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old)
 {
     far_ptr_t *entry;
     unsigned int vec;
-  
+
     if (irq < 8)
        vec = irq + 0x08;
     else if (irq < 16)
        vec = (irq - 8) + 0x70;
     else
        return false;
+
     entry = (far_ptr_t *)(vec << 2);
     *old = *entry;
     entry->ptr = (uint32_t)isr;
     return true;
 }
 
-bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
+static bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old)
 {
     far_ptr_t *entry;
     unsigned int vec;
-   
+
+    if (!irq)
+       return true;            /* Nothing to uninstall */
+
     if (irq < 8)
        vec = irq + 0x08;
     else if (irq < 16)
        vec = (irq - 8) + 0x70;
     else
        return false;
-  
+
     entry = (far_ptr_t *)(vec << 2);
 
     if (entry->ptr != (uint32_t)isr)
@@ -62,47 +65,57 @@ static void pxe_poll_wakeups(void)
        last_jiffies = now;
        __thread_process_timeouts();
     }
-   
+
     if (pxe_irq_pending) {
        pxe_irq_pending = 0;
        sem_up(&pxe_receive_thread_sem);
     }
 }
 
+static bool pxe_isr_poll(void)
+{
+    static __lowmem t_PXENV_UNDI_ISR isr;
+
+    isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
+    pxe_call(PXENV_UNDI_ISR, &isr);
+
+    return isr.FuncFlag == PXENV_UNDI_ISR_OUT_OURS;
+}
+
 static void pxe_process_irq(void)
 {
     static __lowmem t_PXENV_UNDI_ISR isr;
 
-    uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */        
+    uint16_t func = PXENV_UNDI_ISR_IN_PROCESS; /* First time */
     bool done = false;
 
     while (!done) {
         memset(&isr, 0, sizeof isr);
         isr.FuncFlag = func;
         func = PXENV_UNDI_ISR_IN_GET_NEXT; /* Next time */
-    
+
         pxe_call(PXENV_UNDI_ISR, &isr);
-    
+
         switch (isr.FuncFlag) {
         case PXENV_UNDI_ISR_OUT_DONE:
            done = true;
            break;
-    
+
         case PXENV_UNDI_ISR_OUT_TRANSMIT:
            /* Transmit complete - nothing for us to do */
            break;
-    
+
         case PXENV_UNDI_ISR_OUT_RECEIVE:
            undiif_input(&isr);
            break;
-       
+
         case PXENV_UNDI_ISR_OUT_BUSY:
-       /* ISR busy, this should not happen */
+       /* ISR busy, this should not happen */
            done = true;
            break;
-       
+
         default:
-       /* Invalid return code, this should not happen */
+       /* Invalid return code, this should not happen */
            done = true;
            break;
         }
@@ -115,10 +128,16 @@ static void pxe_receive_thread(void *dummy)
 
     for (;;) {
        sem_down(&pxe_receive_thread_sem, 0);
-       pxe_process_irq();
+       if (pxe_irq_vector || pxe_isr_poll())
+           pxe_process_irq();
     }
 }
 
+static void pxe_do_isr_poll(void)
+{
+    sem_up(&pxe_receive_thread_sem);
+}
+
 void pxe_init_isr(void)
 {
     start_idle_thread();
@@ -132,6 +151,11 @@ void pxe_init_isr(void)
     pxe_thread = start_thread("pxe receive", 16384, -20,
                              pxe_receive_thread, NULL);
     core_pm_hook = __schedule;
+
+    if (!pxe_irq_vector) {
+       /* No IRQ vector, need to poll */
+       idle_hook = pxe_do_isr_poll;
+    }
 }
 
 
@@ -139,10 +163,11 @@ void pxe_cleanup_isr(void)
 {
     static __lowmem struct s_PXENV_UNDI_CLOSE undi_close;
 
+    idle_hook = NULL;
     sched_hook_func = NULL;
     core_pm_hook = core_pm_null_hook;
     kill_thread(pxe_thread);
+
     memset(&undi_close, 0, sizeof(undi_close));
     pxe_call(PXENV_UNDI_CLOSE, &undi_close);
     uninstall_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
index d30e783..0aa3c5a 100644 (file)
@@ -245,7 +245,6 @@ extern far_ptr_t pxe_irq_chain;
 void pxe_init_isr(void);
 void pxe_cleanup_isr(void);
 bool install_irq_vector(uint8_t irq, void (*isr)(void), far_ptr_t *old);
-bool uninstall_irq_vector(uint8_t irq, void (*isr), far_ptr_t *old);
 
 /* pxe.c */
 bool ip_ok(uint32_t);
index b283424..7c83b5a 100644 (file)
@@ -95,4 +95,6 @@ void kill_thread(struct thread *);
 void start_idle_thread(void);
 void test_thread(void);
 
+extern void (*idle_hook)(void);
+
 #endif /* _THREAD_H */
index f21eb0b..4e78693 100644 (file)
@@ -224,15 +224,21 @@ static void
 low_level_init(struct netif *netif)
 {
   static __lowmem t_PXENV_UNDI_GET_INFORMATION undi_info;
+  static __lowmem t_PXENV_UNDI_GET_IFACE_INFO undi_iface;
   static __lowmem t_PXENV_UNDI_OPEN undi_open;
   int i;
   /* struct undiif *undiif = netif->state; */
 
   pxe_call(PXENV_UNDI_GET_INFORMATION, &undi_info);
+  pxe_call(PXENV_UNDI_GET_IFACE_INFO, &undi_iface);
 
-  dprintf("UNDI: baseio %04x int %d MTU %d type %d\n",
-        undi_info.BaseIo, undi_info.IntNumber, undi_info.MaxTranUnit,
-        undi_info.HwType);
+  dprintf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
+         undi_info.BaseIo, undi_info.IntNumber, undi_info.MaxTranUnit,
+         undi_info.HwType, undi_iface.IfaceType, undi_iface.ServiceFlags);
+
+  pxe_irq_vector = undi_info.IntNumber;
+  if (!(undi_iface.ServiceFlags & PXE_UNDI_IFACE_FLAG_IRQ))
+    pxe_irq_vector = 0;        /* Interrupts not supported */
 
   /* MAC_type and MAC_len should always match what is returned by
    * PXENV_UNDI_GET_INFORMATION.  At the moment the both seem to be
@@ -272,8 +278,8 @@ low_level_init(struct netif *netif)
     netif->flags |= NETIF_FLAG_ETHARP;
 
   /* Install the interrupt vector */
-  pxe_irq_vector = undi_info.IntNumber;
-  install_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
+  if (pxe_irq_vector)
+    install_irq_vector(pxe_irq_vector, pxe_isr, &pxe_irq_chain);
 
   /* Open the UNDI stack - you'd think the BC would have done this... */
   undi_open.PktFilter = 0x0003;        /* FLTR_DIRECTED | FLTR_BRDCST */
index b51a462..e2f8b57 100644 (file)
@@ -2,6 +2,8 @@
 #include <limits.h>
 #include <sys/cpu.h>
 
+void (*idle_hook)(void);
+
 static void idle_thread_func(void *dummy)
 {
     (void)dummy;
@@ -9,7 +11,10 @@ static void idle_thread_func(void *dummy)
 
     for (;;) {
        thread_yield();
-       asm volatile("hlt");
+       if (idle_hook)
+           idle_hook();
+       else
+           asm volatile("hlt");
     }
 }