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