03864d0ba4e7ac1489b2cb9c0f7e814052256b89
[people/xl0/gpxe.git] / src / arch / i386 / drivers / net / undi.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <string.h>
20 #include <pxe.h>
21 #include <realmode.h>
22 #include <pic8259.h>
23 #include <biosint.h>
24 #include <gpxe/pkbuff.h>
25 #include <gpxe/netdevice.h>
26 #include <gpxe/if_ether.h>
27 #include <gpxe/ethernet.h>
28
29 /** @file
30  *
31  * UNDI network device driver
32  *
33  */
34
35 static void undi_close ( struct net_device *netdev );
36
37 /*****************************************************************************
38  *
39  * UNDI interrupt service routine
40  *
41  *****************************************************************************
42  */
43
44 /**
45  * UNDI interrupt service routine
46  *
47  * The UNDI ISR simply increments a counter (@c trigger_count) and
48  * exits.
49  */
50 extern void undi_isr ( void );
51
52 /** Dummy chain vector */
53 static struct segoff undi_isr_dummy_chain;
54
55 /** IRQ trigger count */
56 static volatile uint16_t __text16 ( trigger_count ) = 0;
57 #define trigger_count __use_text16 ( trigger_count )
58
59 /**
60  * Hook UNDI interrupt service routine
61  *
62  * @v irq               IRQ number
63  *
64  * The UNDI ISR specifically does @b not chain to the previous
65  * interrupt handler.  BIOSes seem to install somewhat perverse
66  * default interrupt handlers; some do nothing other than an iret (and
67  * so will cause a screaming interrupt if there really is another
68  * interrupting device) and some disable the interrupt at the PIC (and
69  * so will bring our own interrupts to a shuddering halt).
70  */
71 static void undi_hook_isr ( unsigned int irq ) {
72         __asm__ __volatile__ ( TEXT16_CODE ( "\nundi_isr:\n\t"
73                                              "incl %%cs:%c0\n\t"
74                                              "iret\n\t" )
75                                : : "p" ( & __from_text16 ( trigger_count ) ) );
76
77         hook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
78                               &undi_isr_dummy_chain );
79
80 }
81
82 /**
83  * Unhook UNDI interrupt service routine
84  *
85  * @v irq               IRQ number
86  */
87 static void undi_unhook_isr ( unsigned int irq ) {
88         unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( unsigned int ) undi_isr ),
89                                 &undi_isr_dummy_chain );
90 }
91
92 /**
93  * Test to see if UNDI ISR has been triggered
94  *
95  * @ret triggered       ISR has been triggered since last check
96  */
97 static int undi_isr_triggered ( void ) {
98         static unsigned int last_trigger_count = 0;
99         unsigned int this_trigger_count;
100
101         /* Read trigger_count.  Do this only once; it is volatile */
102         this_trigger_count = trigger_count;
103
104         if ( this_trigger_count == last_trigger_count ) {
105                 /* Not triggered */
106                 return 0;
107         } else {
108                 /* Triggered */
109                 last_trigger_count = this_trigger_count;
110                 return 1;
111         }
112 }
113
114 /*****************************************************************************
115  *
116  * UNDI network device interface
117  *
118  *****************************************************************************
119  */
120
121 /** Maximum length of a packet transmitted via the UNDI API */
122 #define UNDI_PKB_LEN 1514
123
124 /** A packet transmitted via the UNDI API */
125 struct undi_packet {
126         uint8_t bytes[UNDI_PKB_LEN];
127 };
128
129 /** UNDI packet buffer */
130 static struct undi_packet __data16 ( undi_pkb );
131 #define undi_pkb __use_data16 ( undi_pkb )
132
133 /** UNDI transmit buffer descriptor */
134 static struct s_PXENV_UNDI_TBD __data16 ( undi_tbd );
135 #define undi_tbd __use_data16 ( undi_tbd )
136
137 /**
138  * Transmit packet
139  *
140  * @v netdev            Network device
141  * @v pkb               Packet buffer
142  * @ret rc              Return status code
143  */
144 static int undi_transmit ( struct net_device *netdev, struct pk_buff *pkb ) {
145         struct pxe_device *pxe = netdev->priv;
146         struct s_PXENV_UNDI_TRANSMIT undi_transmit;
147         size_t len = pkb_len ( pkb );
148         int rc;
149
150         /* Copy packet to UNDI packet buffer */
151         if ( len > sizeof ( undi_pkb ) )
152                 len = sizeof ( undi_pkb );
153         memcpy ( &undi_pkb, pkb->data, len );
154
155         /* Create PXENV_UNDI_TRANSMIT data structure */
156         memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
157         undi_transmit.DestAddr.segment = rm_ds;
158         undi_transmit.DestAddr.offset
159                 = ( ( unsigned ) & __from_data16 ( undi_tbd ) );
160         undi_transmit.TBD.segment = rm_ds;
161         undi_transmit.TBD.offset
162                 = ( ( unsigned ) & __from_data16 ( undi_tbd ) );
163
164         /* Create PXENV_UNDI_TBD data structure */
165         undi_tbd.ImmedLength = len;
166         undi_tbd.Xmit.segment = rm_ds;
167         undi_tbd.Xmit.offset 
168                 = ( ( unsigned ) & __from_data16 ( undi_pkb ) );
169
170         /* Issue PXE API call */
171         if ( ( rc = pxe_call ( pxe, PXENV_UNDI_TRANSMIT, &undi_transmit,
172                                sizeof ( undi_transmit ) ) ) != 0 ) {
173                 DBG ( "UNDI_TRANSMIT failed: %s\n", strerror ( rc ) );
174         }
175
176         /* Free packet buffer and return */
177         free_pkb ( pkb );
178         return rc;
179 }
180
181 /** 
182  * Poll for received packets
183  *
184  * @v netdev    Network device
185  *
186  * Fun, fun, fun.  UNDI drivers don't use polling; they use
187  * interrupts.  We therefore cheat and pretend that an interrupt has
188  * occurred every time undi_poll() is called.  This isn't too much of
189  * a hack; PCI devices share IRQs and so the first thing that a proper
190  * ISR should do is call PXENV_UNDI_ISR to determine whether or not
191  * the UNDI NIC generated the interrupt; there is no harm done by
192  * spurious calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
193  * handling them any more rapidly than the usual rate of undi_poll()
194  * being called even if we did implement a full ISR.  So it should
195  * work.  Ha!
196  *
197  * Addendum (21/10/03).  Some cards don't play nicely with this trick,
198  * so instead of doing it the easy way we have to go to all the hassle
199  * of installing a genuine interrupt service routine and dealing with
200  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
201  */
202 static void undi_poll ( struct net_device *netdev ) {
203         struct pxe_device *pxe = netdev->priv;
204         struct s_PXENV_UNDI_ISR undi_isr;
205         struct pk_buff *pkb = NULL;
206         size_t len;
207         size_t frag_len;
208         int rc;
209
210         /* Do nothing unless ISR has been triggered */
211         if ( ! undi_isr_triggered() )
212                 return;
213
214         /* See if this was our interrupt */
215         memset ( &undi_isr, 0, sizeof ( undi_isr ) );
216         undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_START;
217         if ( ( rc = pxe_call ( pxe, PXENV_UNDI_ISR, &undi_isr,
218                                sizeof ( undi_isr ) ) ) != 0 ) {
219                 DBG ( "UNDI_ISR (START) failed: %s\n", strerror ( rc ) );
220                 return;
221         }
222         if ( undi_isr.FuncFlag != PXENV_UNDI_ISR_OUT_OURS )
223                 return;
224
225         /* Send EOI */
226         send_eoi ( pxe->irq );
227
228         /* Run through the ISR loop */
229         undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
230         while ( 1 ) {
231                 if ( ( rc = pxe_call ( pxe, PXENV_UNDI_ISR, &undi_isr,
232                                        sizeof ( undi_isr ) ) ) != 0 ) {
233                         DBG ( "UNDI_ISR (PROCESS/GET_NEXT) failed: %s\n",
234                               strerror ( rc ) );
235                         break;
236                 }
237                 switch ( undi_isr.FuncFlag ) {
238                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
239                         /* We don't care about transmit completions */
240                         break;
241                 case PXENV_UNDI_ISR_OUT_RECEIVE:
242                         /* Packet fragment received */
243                         len = undi_isr.FrameLength;
244                         frag_len = undi_isr.BufferLength;
245                         if ( ! pkb )
246                                 pkb = alloc_pkb ( len );
247                         if ( ! pkb ) {
248                                 DBG ( "UNDI could not allocate %zd bytes for "
249                                       "receive buffer\n", len );
250                                 break;
251                         }
252                         if ( frag_len > pkb_available ( pkb ) ) {
253                                 DBG ( "UNDI fragment too large\n" );
254                                 frag_len = pkb_available ( pkb );
255                         }
256                         copy_from_real ( pkb_put ( pkb, frag_len ),
257                                          undi_isr.Frame.segment,
258                                          undi_isr.Frame.offset, frag_len );
259                         if ( pkb_len ( pkb ) == len ) {
260                                 netdev_rx ( netdev, pkb );
261                                 pkb = NULL;
262                         }
263                         break;
264                 case PXENV_UNDI_ISR_OUT_DONE:
265                         /* Processing complete */
266                         goto done;
267                 default:
268                         /* Should never happen */
269                         DBG ( "UNDI ISR returned invalid FuncFlag %04x\n",
270                               undi_isr.FuncFlag );
271                         goto done;
272                 }
273                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
274         }
275
276  done:
277         if ( pkb ) {
278                 DBG ( "UNDI returned incomplete packet\n" );
279                 netdev_rx ( netdev, pkb );
280         }
281 }
282
283 /**
284  * Open NIC
285  *
286  * @v netdev            Net device
287  * @ret rc              Return status code
288  */
289 static int undi_open ( struct net_device *netdev ) {
290         struct pxe_device *pxe = netdev->priv;
291         struct s_PXENV_UNDI_SET_STATION_ADDRESS set_address;
292         struct s_PXENV_UNDI_OPEN open;
293         int rc;
294
295         /* Hook interrupt service routine and enable interrupt */
296         undi_hook_isr ( pxe->irq );
297         enable_irq ( pxe->irq );
298
299         /* Set station address.  Required for some PXE stacks; will
300          * spuriously fail on others.  Ignore failures.  We only ever
301          * use it to set the MAC address to the card's permanent value
302          * anyway.
303          */
304         memcpy ( set_address.StationAddress, netdev->ll_addr,
305                  sizeof ( set_address.StationAddress ) );
306         if ( ( rc = pxe_call ( pxe, PXENV_UNDI_SET_STATION_ADDRESS,
307                                &set_address, sizeof ( set_address ) ) ) != 0 ){
308                 DBG ( "UNDI_SET_STATION_ADDRESS failed: %s\n",
309                       strerror ( rc ) );
310         }
311
312         /* Open NIC */
313         memset ( &open, 0, sizeof ( open ) );
314         open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST );
315         if ( ( rc = pxe_call ( pxe, PXENV_UNDI_OPEN, &open,
316                                sizeof ( open ) ) ) != 0 ) {
317                 DBG ( "UNDI_OPEN failed: %s\n", strerror ( rc ) );
318                 goto err;
319         }
320
321         return 0;
322
323  err:
324         undi_close ( netdev );
325         return rc;
326 }
327
328 /**
329  * Close NIC
330  *
331  * @v netdev            Net device
332  */
333 static void undi_close ( struct net_device *netdev ) {
334         struct pxe_device *pxe = netdev->priv;
335         struct s_PXENV_UNDI_CLOSE close;
336         int rc;
337
338         /* Close NIC */
339         if ( ( rc = pxe_call ( pxe, PXENV_UNDI_CLOSE, &close,
340                                sizeof ( close ) ) ) != 0 ) {
341                 DBG ( "UNDI_CLOSE failed: %s\n", strerror ( rc ) );
342         }
343
344         /* Disable interrupt and unhook ISR */
345         disable_irq ( pxe->irq );
346         undi_unhook_isr ( pxe->irq );
347 }
348
349 /**
350  * Probe PXE device
351  *
352  * @v pxe               PXE device
353  * @ret rc              Return status code
354  */
355 int undi_probe ( struct pxe_device *pxe ) {
356         struct net_device *netdev;
357         int rc;
358
359         /* Allocate net device */
360         netdev = alloc_etherdev ( 0 );
361         if ( ! netdev ) {
362                 rc = -ENOMEM;
363                 goto err;
364         }
365         netdev->priv = pxe;
366         pxe_set_drvdata ( pxe, netdev );
367
368         /* Fill in NIC information */
369         memcpy ( netdev->ll_addr, pxe->hwaddr, ETH_ALEN );
370
371         /* Point to NIC specific routines */
372         netdev->open     = undi_open;
373         netdev->close    = undi_close;
374         netdev->transmit = undi_transmit;
375         netdev->poll     = undi_poll;
376
377         /* Register network device */
378         if ( ( rc = register_netdev ( netdev ) ) != 0 )
379                 goto err;
380
381         return 0;
382
383  err:
384         free_netdev ( netdev );
385         pxe_set_drvdata ( pxe, NULL );
386         return rc;
387 }
388
389 /**
390  * Remove PXE device
391  *
392  * @v pxe               PXE device
393  */
394 void undi_remove ( struct pxe_device *pxe ) {
395         struct net_device *netdev = pxe_get_drvdata ( pxe );
396
397         unregister_netdev ( netdev );
398         free_netdev ( netdev );
399 }