]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Sealevel Systems 4021 driver. | |
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 | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | * | |
9 | * (c) Copyright 1999, 2001 Alan Cox | |
10 | * (c) Copyright 2001 Red Hat Inc. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/mm.h> | |
17 | #include <linux/net.h> | |
18 | #include <linux/skbuff.h> | |
19 | #include <linux/netdevice.h> | |
20 | #include <linux/if_arp.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/ioport.h> | |
23 | #include <linux/init.h> | |
24 | #include <net/arp.h> | |
25 | ||
26 | #include <asm/io.h> | |
27 | #include <asm/dma.h> | |
28 | #include <asm/byteorder.h> | |
29 | #include <net/syncppp.h> | |
30 | #include "z85230.h" | |
31 | ||
32 | ||
33 | struct slvl_device | |
34 | { | |
35 | void *if_ptr; /* General purpose pointer (used by SPPP) */ | |
36 | struct z8530_channel *chan; | |
37 | struct ppp_device pppdev; | |
38 | int channel; | |
39 | }; | |
40 | ||
41 | ||
42 | struct slvl_board | |
43 | { | |
44 | struct slvl_device *dev[2]; | |
45 | struct z8530_dev board; | |
46 | int iobase; | |
47 | }; | |
48 | ||
49 | /* | |
50 | * Network driver support routines | |
51 | */ | |
52 | ||
53 | /* | |
54 | * Frame receive. Simple for our card as we do sync ppp and there | |
55 | * is no funny garbage involved | |
56 | */ | |
57 | ||
58 | static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb) | |
59 | { | |
60 | /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ | |
61 | skb_trim(skb, skb->len-2); | |
62 | skb->protocol=htons(ETH_P_WAN_PPP); | |
63 | skb->mac.raw=skb->data; | |
64 | skb->dev=c->netdevice; | |
65 | /* | |
66 | * Send it to the PPP layer. We don't have time to process | |
67 | * it right now. | |
68 | */ | |
69 | netif_rx(skb); | |
70 | c->netdevice->last_rx = jiffies; | |
71 | } | |
72 | ||
73 | /* | |
74 | * We've been placed in the UP state | |
75 | */ | |
76 | ||
77 | static int sealevel_open(struct net_device *d) | |
78 | { | |
79 | struct slvl_device *slvl=d->priv; | |
80 | int err = -1; | |
81 | int unit = slvl->channel; | |
82 | ||
83 | /* | |
84 | * Link layer up. | |
85 | */ | |
86 | ||
87 | switch(unit) | |
88 | { | |
89 | case 0: | |
90 | err=z8530_sync_dma_open(d, slvl->chan); | |
91 | break; | |
92 | case 1: | |
93 | err=z8530_sync_open(d, slvl->chan); | |
94 | break; | |
95 | } | |
96 | ||
97 | if(err) | |
98 | return err; | |
99 | /* | |
100 | * Begin PPP | |
101 | */ | |
102 | err=sppp_open(d); | |
103 | if(err) | |
104 | { | |
105 | switch(unit) | |
106 | { | |
107 | case 0: | |
108 | z8530_sync_dma_close(d, slvl->chan); | |
109 | break; | |
110 | case 1: | |
111 | z8530_sync_close(d, slvl->chan); | |
112 | break; | |
113 | } | |
114 | return err; | |
115 | } | |
116 | ||
117 | slvl->chan->rx_function=sealevel_input; | |
118 | ||
119 | /* | |
120 | * Go go go | |
121 | */ | |
122 | netif_start_queue(d); | |
123 | return 0; | |
124 | } | |
125 | ||
126 | static int sealevel_close(struct net_device *d) | |
127 | { | |
128 | struct slvl_device *slvl=d->priv; | |
129 | int unit = slvl->channel; | |
130 | ||
131 | /* | |
132 | * Discard new frames | |
133 | */ | |
134 | ||
135 | slvl->chan->rx_function=z8530_null_rx; | |
136 | ||
137 | /* | |
138 | * PPP off | |
139 | */ | |
140 | sppp_close(d); | |
141 | /* | |
142 | * Link layer down | |
143 | */ | |
144 | ||
145 | netif_stop_queue(d); | |
146 | ||
147 | switch(unit) | |
148 | { | |
149 | case 0: | |
150 | z8530_sync_dma_close(d, slvl->chan); | |
151 | break; | |
152 | case 1: | |
153 | z8530_sync_close(d, slvl->chan); | |
154 | break; | |
155 | } | |
156 | return 0; | |
157 | } | |
158 | ||
159 | static int sealevel_ioctl(struct net_device *d, struct ifreq *ifr, int cmd) | |
160 | { | |
161 | /* struct slvl_device *slvl=d->priv; | |
162 | z8530_ioctl(d,&slvl->sync.chanA,ifr,cmd) */ | |
163 | return sppp_do_ioctl(d, ifr,cmd); | |
164 | } | |
165 | ||
166 | static struct net_device_stats *sealevel_get_stats(struct net_device *d) | |
167 | { | |
168 | struct slvl_device *slvl=d->priv; | |
169 | if(slvl) | |
170 | return z8530_get_stats(slvl->chan); | |
171 | else | |
172 | return NULL; | |
173 | } | |
174 | ||
175 | /* | |
176 | * Passed PPP frames, fire them downwind. | |
177 | */ | |
178 | ||
179 | static int sealevel_queue_xmit(struct sk_buff *skb, struct net_device *d) | |
180 | { | |
181 | struct slvl_device *slvl=d->priv; | |
182 | return z8530_queue_xmit(slvl->chan, skb); | |
183 | } | |
184 | ||
185 | static int sealevel_neigh_setup(struct neighbour *n) | |
186 | { | |
187 | if (n->nud_state == NUD_NONE) { | |
188 | n->ops = &arp_broken_ops; | |
189 | n->output = n->ops->output; | |
190 | } | |
191 | return 0; | |
192 | } | |
193 | ||
194 | static int sealevel_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p) | |
195 | { | |
196 | if (p->tbl->family == AF_INET) { | |
197 | p->neigh_setup = sealevel_neigh_setup; | |
198 | p->ucast_probes = 0; | |
199 | p->mcast_probes = 0; | |
200 | } | |
201 | return 0; | |
202 | } | |
203 | ||
204 | static int sealevel_attach(struct net_device *dev) | |
205 | { | |
206 | struct slvl_device *sv = dev->priv; | |
207 | sppp_attach(&sv->pppdev); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | static void sealevel_detach(struct net_device *dev) | |
212 | { | |
213 | sppp_detach(dev); | |
214 | } | |
215 | ||
216 | static void slvl_setup(struct net_device *d) | |
217 | { | |
218 | d->open = sealevel_open; | |
219 | d->stop = sealevel_close; | |
220 | d->init = sealevel_attach; | |
221 | d->uninit = sealevel_detach; | |
222 | d->hard_start_xmit = sealevel_queue_xmit; | |
223 | d->get_stats = sealevel_get_stats; | |
224 | d->set_multicast_list = NULL; | |
225 | d->do_ioctl = sealevel_ioctl; | |
226 | d->neigh_setup = sealevel_neigh_setup_dev; | |
227 | d->set_mac_address = NULL; | |
228 | ||
229 | } | |
230 | ||
231 | static inline struct slvl_device *slvl_alloc(int iobase, int irq) | |
232 | { | |
233 | struct net_device *d; | |
234 | struct slvl_device *sv; | |
235 | ||
236 | d = alloc_netdev(sizeof(struct slvl_device), "hdlc%d", | |
237 | slvl_setup); | |
238 | ||
239 | if (!d) | |
240 | return NULL; | |
241 | ||
242 | sv = d->priv; | |
243 | sv->if_ptr = &sv->pppdev; | |
244 | sv->pppdev.dev = d; | |
245 | d->base_addr = iobase; | |
246 | d->irq = irq; | |
247 | ||
248 | return sv; | |
249 | } | |
250 | ||
251 | ||
252 | /* | |
253 | * Allocate and setup Sealevel board. | |
254 | */ | |
255 | ||
256 | static __init struct slvl_board *slvl_init(int iobase, int irq, | |
257 | int txdma, int rxdma, int slow) | |
258 | { | |
259 | struct z8530_dev *dev; | |
260 | struct slvl_board *b; | |
261 | ||
262 | /* | |
263 | * Get the needed I/O space | |
264 | */ | |
265 | ||
266 | if(!request_region(iobase, 8, "Sealevel 4021")) | |
267 | { | |
268 | printk(KERN_WARNING "sealevel: I/O 0x%X already in use.\n", iobase); | |
269 | return NULL; | |
270 | } | |
271 | ||
272 | b = kmalloc(sizeof(struct slvl_board), GFP_KERNEL); | |
273 | if(!b) | |
274 | goto fail3; | |
275 | ||
276 | memset(b, 0, sizeof(*b)); | |
277 | if (!(b->dev[0]= slvl_alloc(iobase, irq))) | |
278 | goto fail2; | |
279 | ||
280 | b->dev[0]->chan = &b->board.chanA; | |
281 | b->dev[0]->channel = 0; | |
282 | ||
283 | if (!(b->dev[1] = slvl_alloc(iobase, irq))) | |
284 | goto fail1_0; | |
285 | ||
286 | b->dev[1]->chan = &b->board.chanB; | |
287 | b->dev[1]->channel = 1; | |
288 | ||
289 | dev = &b->board; | |
290 | ||
291 | /* | |
292 | * Stuff in the I/O addressing | |
293 | */ | |
294 | ||
295 | dev->active = 0; | |
296 | ||
297 | b->iobase = iobase; | |
298 | ||
299 | /* | |
300 | * Select 8530 delays for the old board | |
301 | */ | |
302 | ||
303 | if(slow) | |
304 | iobase |= Z8530_PORT_SLEEP; | |
305 | ||
306 | dev->chanA.ctrlio=iobase+1; | |
307 | dev->chanA.dataio=iobase; | |
308 | dev->chanB.ctrlio=iobase+3; | |
309 | dev->chanB.dataio=iobase+2; | |
310 | ||
311 | dev->chanA.irqs=&z8530_nop; | |
312 | dev->chanB.irqs=&z8530_nop; | |
313 | ||
314 | /* | |
315 | * Assert DTR enable DMA | |
316 | */ | |
317 | ||
318 | outb(3|(1<<7), b->iobase+4); | |
319 | ||
320 | ||
321 | /* We want a fast IRQ for this device. Actually we'd like an even faster | |
322 | IRQ ;) - This is one driver RtLinux is made for */ | |
323 | ||
324 | if(request_irq(irq, &z8530_interrupt, SA_INTERRUPT, "SeaLevel", dev)<0) | |
325 | { | |
326 | printk(KERN_WARNING "sealevel: IRQ %d already in use.\n", irq); | |
327 | goto fail1_1; | |
328 | } | |
329 | ||
330 | dev->irq=irq; | |
331 | dev->chanA.private=&b->dev[0]; | |
332 | dev->chanB.private=&b->dev[1]; | |
333 | dev->chanA.netdevice=b->dev[0]->pppdev.dev; | |
334 | dev->chanB.netdevice=b->dev[1]->pppdev.dev; | |
335 | dev->chanA.dev=dev; | |
336 | dev->chanB.dev=dev; | |
337 | ||
338 | dev->chanA.txdma=3; | |
339 | dev->chanA.rxdma=1; | |
340 | if(request_dma(dev->chanA.txdma, "SeaLevel (TX)")!=0) | |
341 | goto fail; | |
342 | ||
343 | if(request_dma(dev->chanA.rxdma, "SeaLevel (RX)")!=0) | |
344 | goto dmafail; | |
345 | ||
346 | disable_irq(irq); | |
347 | ||
348 | /* | |
349 | * Begin normal initialise | |
350 | */ | |
351 | ||
352 | if(z8530_init(dev)!=0) | |
353 | { | |
354 | printk(KERN_ERR "Z8530 series device not found.\n"); | |
355 | enable_irq(irq); | |
356 | goto dmafail2; | |
357 | } | |
358 | if(dev->type==Z85C30) | |
359 | { | |
360 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream); | |
361 | z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream); | |
362 | } | |
363 | else | |
364 | { | |
365 | z8530_channel_load(&dev->chanA, z8530_hdlc_kilostream_85230); | |
366 | z8530_channel_load(&dev->chanB, z8530_hdlc_kilostream_85230); | |
367 | } | |
368 | ||
369 | /* | |
370 | * Now we can take the IRQ | |
371 | */ | |
372 | ||
373 | enable_irq(irq); | |
374 | ||
375 | if (register_netdev(b->dev[0]->pppdev.dev)) | |
376 | goto dmafail2; | |
377 | ||
378 | if (register_netdev(b->dev[1]->pppdev.dev)) | |
379 | goto fail_unit; | |
380 | ||
381 | z8530_describe(dev, "I/O", iobase); | |
382 | dev->active=1; | |
383 | return b; | |
384 | ||
385 | fail_unit: | |
386 | unregister_netdev(b->dev[0]->pppdev.dev); | |
387 | ||
388 | dmafail2: | |
389 | free_dma(dev->chanA.rxdma); | |
390 | dmafail: | |
391 | free_dma(dev->chanA.txdma); | |
392 | fail: | |
393 | free_irq(irq, dev); | |
394 | fail1_1: | |
395 | free_netdev(b->dev[1]->pppdev.dev); | |
396 | fail1_0: | |
397 | free_netdev(b->dev[0]->pppdev.dev); | |
398 | fail2: | |
399 | kfree(b); | |
400 | fail3: | |
401 | release_region(iobase,8); | |
402 | return NULL; | |
403 | } | |
404 | ||
405 | static void __exit slvl_shutdown(struct slvl_board *b) | |
406 | { | |
407 | int u; | |
408 | ||
409 | z8530_shutdown(&b->board); | |
410 | ||
411 | for(u=0; u<2; u++) | |
412 | { | |
413 | struct net_device *d = b->dev[u]->pppdev.dev; | |
414 | unregister_netdev(d); | |
415 | free_netdev(d); | |
416 | } | |
417 | ||
418 | free_irq(b->board.irq, &b->board); | |
419 | free_dma(b->board.chanA.rxdma); | |
420 | free_dma(b->board.chanA.txdma); | |
421 | /* DMA off on the card, drop DTR */ | |
422 | outb(0, b->iobase); | |
423 | release_region(b->iobase, 8); | |
424 | kfree(b); | |
425 | } | |
426 | ||
427 | ||
428 | static int io=0x238; | |
429 | static int txdma=1; | |
430 | static int rxdma=3; | |
431 | static int irq=5; | |
432 | static int slow=0; | |
433 | ||
434 | module_param(io, int, 0); | |
435 | MODULE_PARM_DESC(io, "The I/O base of the Sealevel card"); | |
436 | module_param(txdma, int, 0); | |
437 | MODULE_PARM_DESC(txdma, "Transmit DMA channel"); | |
438 | module_param(rxdma, int, 0); | |
439 | MODULE_PARM_DESC(rxdma, "Receive DMA channel"); | |
440 | module_param(irq, int, 0); | |
441 | MODULE_PARM_DESC(irq, "The interrupt line setting for the SeaLevel card"); | |
442 | module_param(slow, bool, 0); | |
443 | MODULE_PARM_DESC(slow, "Set this for an older Sealevel card such as the 4012"); | |
444 | ||
445 | MODULE_AUTHOR("Alan Cox"); | |
446 | MODULE_LICENSE("GPL"); | |
447 | MODULE_DESCRIPTION("Modular driver for the SeaLevel 4021"); | |
448 | ||
449 | static struct slvl_board *slvl_unit; | |
450 | ||
451 | static int __init slvl_init_module(void) | |
452 | { | |
453 | #ifdef MODULE | |
454 | printk(KERN_INFO "SeaLevel Z85230 Synchronous Driver v 0.02.\n"); | |
455 | printk(KERN_INFO "(c) Copyright 1998, Building Number Three Ltd.\n"); | |
456 | #endif | |
457 | slvl_unit = slvl_init(io, irq, txdma, rxdma, slow); | |
458 | ||
459 | return slvl_unit ? 0 : -ENODEV; | |
460 | } | |
461 | ||
462 | static void __exit slvl_cleanup_module(void) | |
463 | { | |
464 | if(slvl_unit) | |
465 | slvl_shutdown(slvl_unit); | |
466 | } | |
467 | ||
468 | module_init(slvl_init_module); | |
469 | module_exit(slvl_cleanup_module); |