This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / wan / comx.c
1 /*
2  * Device driver framework for the COMX line of synchronous serial boards
3  * 
4  * for Linux kernel 2.2.X / 2.4.X
5  *
6  * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
7  *                    Peter Bajan <bajan.peter@synergon.hu>,
8  * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
9  * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
10  *
11  * Copyright (C) 1995-1999 ITConsult-Pro Co.
12  *
13  * Contributors:
14  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.85)
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version
19  * 2 of the License, or (at your option) any later version.
20  *
21  * Version 0.80 (99/06/11):
22  *              - clean up source code (playing a bit of indent)
23  *              - port back to kernel, add support for non-module versions
24  *              - add support for board resets when channel protocol is down
25  *              - reset the device structure after protocol exit
26  *                the syncppp driver needs it
27  *              - add support for /proc/comx/protocols and 
28  *                /proc/comx/boardtypes
29  *
30  * Version 0.81 (99/06/21):
31  *              - comment out the board reset support code, the locomx
32  *                driver seems not buggy now
33  *              - printk() levels fixed
34  *
35  * Version 0.82 (99/07/08):
36  *              - Handle stats correctly if the lowlevel driver is
37  *                is not a comx one (locomx - z85230)
38  *
39  * Version 0.83 (99/07/15):
40  *              - reset line_status when interface is down
41  *
42  * Version 0.84 (99/12/01):
43  *              - comx_status should not check for IFF_UP (to report
44  *                line status from dev->open())
45  *
46  * Version 0.85 (00/08/15):
47  *              - resource release on failure in comx_mkdir
48  *              - fix return value on failure at comx_write_proc
49  *
50  * Changed      (00/10/29, Henner Eisen):
51  *              - comx_rx() / comxlapb_data_indication() return status.
52  */
53
54 #define VERSION "0.85"
55
56 #include <linux/config.h>
57 #include <linux/module.h>
58
59 #include <linux/types.h>
60 #include <linux/jiffies.h>
61 #include <linux/netdevice.h>
62 #include <linux/proc_fs.h>
63 #include <asm/uaccess.h>
64 #include <linux/ctype.h>
65 #include <linux/init.h>
66 #include <linux/smp_lock.h>
67
68 #ifdef CONFIG_KMOD
69 #include <linux/kmod.h>
70 #endif
71
72 #ifndef CONFIG_PROC_FS
73 #error For now, COMX really needs the /proc filesystem
74 #endif
75
76 #include <net/syncppp.h>
77 #include "comx.h"
78
79 MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
80 MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
81 MODULE_LICENSE("GPL");
82
83 static struct comx_hardware *comx_channels = NULL;
84 static struct comx_protocol *comx_lines = NULL;
85
86 static int comx_mkdir(struct inode *, struct dentry *, int);
87 static int comx_rmdir(struct inode *, struct dentry *);
88 static struct dentry *comx_lookup(struct inode *, struct dentry *, struct nameidata *);
89
90 static struct inode_operations comx_root_inode_ops = {
91         .lookup = comx_lookup,
92         .mkdir = comx_mkdir,
93         .rmdir = comx_rmdir,
94 };
95
96 static int comx_delete_dentry(struct dentry *dentry);
97 static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
98         int size, struct proc_dir_entry *dir);
99
100 static struct dentry_operations comx_dentry_operations = {
101         .d_delete       = comx_delete_dentry,
102 };
103
104
105 static struct proc_dir_entry * comx_root_dir;
106
107 struct comx_debugflags_struct   comx_debugflags[] = {
108         { "comx_rx",            DEBUG_COMX_RX           },
109         { "comx_tx",            DEBUG_COMX_TX           },
110         { "hw_tx",              DEBUG_HW_TX             },
111         { "hw_rx",              DEBUG_HW_RX             },
112         { "hdlc_keepalive",     DEBUG_HDLC_KEEPALIVE    },
113         { "comxppp",            DEBUG_COMX_PPP          },
114         { "comxlapb",           DEBUG_COMX_LAPB         },
115         { "dlci",               DEBUG_COMX_DLCI         },
116         { NULL,                 0                       } 
117 };
118
119
120 int comx_debug(struct net_device *dev, char *fmt, ...)
121 {
122         struct comx_channel *ch = dev->priv;
123         char *page,*str;
124         va_list args;
125         int len;
126
127         if (!ch->debug_area) return 0;
128
129         if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
130
131         va_start(args, fmt);
132         len = vsprintf(str = page, fmt, args);
133         va_end(args);
134
135         if (len >= PAGE_SIZE) {
136                 printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
137                 free_page((unsigned long)page);
138                 return -EINVAL;
139         }
140
141         while (len) {
142                 int to_copy;
143                 int free = (ch->debug_start - ch->debug_end + ch->debug_size) 
144                         % ch->debug_size;
145
146                 to_copy = min_t(int, free ? free : ch->debug_size, 
147                               min_t(int, ch->debug_size - ch->debug_end, len));
148                 memcpy(ch->debug_area + ch->debug_end, str, to_copy);
149                 str += to_copy;
150                 len -= to_copy;
151                 ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
152                 if (ch->debug_start == ch->debug_end) // Full ? push start away
153                         ch->debug_start = (ch->debug_start + len + 1) % 
154                                         ch->debug_size;
155                 ch->debug_file->size = (ch->debug_end - ch->debug_start +
156                                         ch->debug_size) % ch->debug_size;
157         } 
158
159         free_page((unsigned long)page);
160         return 0;
161 }
162
163 int comx_debug_skb(struct net_device *dev, struct sk_buff *skb, char *msg)
164 {
165         struct comx_channel *ch = dev->priv;
166
167         if (!ch->debug_area) return 0;
168         if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
169         if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
170
171         return comx_debug_bytes(dev, skb->data, skb->len, msg);
172 }
173
174 int comx_debug_bytes(struct net_device *dev, unsigned char *bytes, int len, 
175                 char *msg)
176 {
177         int pos = 0;
178         struct comx_channel *ch = dev->priv;
179
180         if (!ch->debug_area) return 0;
181
182         comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
183
184         while (pos != len) {
185                 char line[80];
186                 int i = 0;
187
188                 memset(line, 0, 80);
189                 sprintf(line,"%04d ", pos);
190                 do {
191                         sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
192                         sprintf(line + 60 + (pos % 16), "%c", 
193                                 isprint(bytes[pos]) ? bytes[pos] : '.');
194                         pos++;
195                 } while (pos != len && pos % 16);
196
197                 while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
198                 line[77] = '\n';
199                 line[78] = 0;
200         
201                 comx_debug(dev, "%s", line);
202         }
203         comx_debug(dev, "\n");
204         return 0;
205 }
206
207 static void comx_loadavg_timerfun(unsigned long d)
208 {
209         struct net_device *dev = (struct net_device *)d;
210         struct comx_channel *ch = dev->priv;
211
212         ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
213         ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = 
214                 ch->current_stats->tx_bytes;
215
216         ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
217
218         mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
219 }
220
221 #if 0
222 static void comx_reset_timerfun(unsigned long d)
223
224         struct net_device *dev = (struct net_device *)d;
225         struct comx_channel *ch = dev->priv;
226
227         if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
228                 if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
229                         ch->HW_reset(dev);
230                 }
231         }
232
233         mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
234 }
235 #endif                                            
236
237 static int comx_open(struct net_device *dev)
238 {
239         struct comx_channel *ch = dev->priv;
240         struct proc_dir_entry *comxdir = ch->procdir->subdir;
241         int ret=0;
242
243         if (!ch->protocol || !ch->hardware) return -ENODEV;
244
245         if ((ret = ch->HW_open(dev))) return ret;
246         if ((ret = ch->LINE_open(dev))) { 
247                 ch->HW_close(dev); 
248                 return ret; 
249         };
250
251         for (; comxdir ; comxdir = comxdir->next) {
252                 if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
253                    strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
254                         comxdir->mode = S_IFREG | 0444;
255         }
256
257 #if 0
258         ch->reset_pending = 1;
259         ch->reset_timeout = 30;
260         ch->reset_timer.function = comx_reset_timerfun;
261         ch->reset_timer.data = (unsigned long)dev;
262         ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
263         add_timer(&ch->reset_timer);
264 #endif
265
266         return 0;
267 }
268
269 static int comx_close(struct net_device *dev)
270 {
271         struct comx_channel *ch = dev->priv;
272         struct proc_dir_entry *comxdir = ch->procdir->subdir;
273         int ret = -ENODEV;
274
275         if (test_and_clear_bit(0, &ch->lineup_pending)) {
276                 del_timer(&ch->lineup_timer);
277         }
278
279 #if 0   
280         del_timer(&ch->reset_timer);
281 #endif
282
283         if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
284                 ret = ch->LINE_close(dev);
285         }
286
287         if (ret) return ret;
288
289         if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
290                 ret = ch->HW_close(dev);
291         }
292         
293         ch->line_status=0;
294
295         for (; comxdir ; comxdir = comxdir->next) {
296                 if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
297                     strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
298                         comxdir->mode = S_IFREG | 0644;
299         }
300
301         return ret;
302 }
303
304 void comx_status(struct net_device *dev, int status)
305 {
306         struct comx_channel *ch = dev->priv;
307
308 #if 0
309         if(status & (PROTO_UP | PROTO_LOOP)) {
310                 clear_bit(0,&ch->reset_pending);
311         }
312 #endif
313
314         printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
315                     dev->name, status & LINE_UP ? "UP" : "DOWN", 
316                     status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? 
317                     "UP" : "DOWN");
318         
319         ch->line_status = status;
320 }
321
322 static int comx_xmit(struct sk_buff *skb, struct net_device *dev)
323 {
324         struct comx_channel *ch = dev->priv;
325         int rc;
326
327         if (skb->len > dev->mtu + dev->hard_header_len) {
328                 printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
329                 (int)skb->len, dev->mtu);
330         }
331         
332         if (ch->debug_flags & DEBUG_COMX_TX) {
333                 comx_debug_skb(dev, skb, "comx_xmit skb");
334         }
335         
336         rc=ch->LINE_xmit(skb, dev);
337 //      if (!rc) dev_kfree_skb(skb);
338
339         return rc;
340 }
341
342 static int comx_header(struct sk_buff *skb, struct net_device *dev, 
343         unsigned short type, void *daddr, void *saddr, unsigned len) 
344 {
345         struct comx_channel *ch = dev->priv;
346
347         if (ch->LINE_header) {
348                 return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
349         } else {
350                 return 0;
351         }
352 }
353
354 static int comx_rebuild_header(struct sk_buff *skb) 
355 {
356         struct net_device *dev = skb->dev;
357         struct comx_channel *ch = dev->priv;
358
359         if (ch->LINE_rebuild_header) {
360                 return(ch->LINE_rebuild_header(skb));
361         } else {
362                 return 0;
363         }
364 }
365
366 int comx_rx(struct net_device *dev, struct sk_buff *skb)
367 {
368         struct comx_channel *ch = dev->priv;
369
370         if (ch->debug_flags & DEBUG_COMX_RX) {
371                 comx_debug_skb(dev, skb, "comx_rx skb");
372         }
373         if (skb) {
374                 netif_rx(skb);
375                 dev->last_rx = jiffies;
376         }
377         return 0;
378 }
379
380 static struct net_device_stats *comx_stats(struct net_device *dev)
381 {
382         struct comx_channel *ch = dev->priv;
383
384         return ch->current_stats;
385 }
386
387 void comx_lineup_func(unsigned long d)
388 {
389         struct net_device *dev = (struct net_device *)d;
390         struct comx_channel *ch = dev->priv;
391
392         del_timer(&ch->lineup_timer);
393         clear_bit(0, &ch->lineup_pending);
394
395         if (ch->LINE_status) {
396                 ch->LINE_status(dev, ch->line_status |= LINE_UP);
397         }
398 }
399
400 #define LOADAVG(avg, off) (int) \
401         ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
402         % ch->loadavg_size + off] -  ch->avg_bytes[(ch->loadavg_counter - 1 \
403                 - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
404                 % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
405
406 static int comx_statistics(struct net_device *dev, char *page)
407 {
408         struct comx_channel *ch = dev->priv;
409         int len = 0;
410         int tmp;
411         int i = 0;
412         char tmpstr[20];
413         int tmpstrlen = 0;
414
415         len += sprintf(page + len, "Interface administrative status is %s, "
416                 "modem status is %s, protocol is %s\n", 
417                 dev->flags & IFF_UP ? "UP" : "DOWN",
418                 ch->line_status & LINE_UP ? "UP" : "DOWN",
419                 ch->line_status & PROTO_LOOP ? "LOOP" : 
420                 ch->line_status & PROTO_UP ? "UP" : "DOWN");
421         len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
422                 "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? 
423                 ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", netif_running(dev));
424         len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
425                 LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
426         tmpstr[0] = 0;
427         for (i=0; i != 3; i++) {
428                 char tf;
429
430                 tf = ch->loadavg[i] % 60 == 0 && 
431                         ch->loadavg[i] / 60 > 0 ? 'm' : 's';
432                 tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", 
433                         ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, 
434                         i == 2 ? ")\n" : "/");
435         }
436         len += sprintf(page + len, 
437                 "%s              (output): %d / %d / %d bits/s (%s", tmpstr, 
438                 LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), 
439                 LOADAVG(2, ch->loadavg_size), tmpstr);
440
441         len += sprintf(page + len, "Debug flags: ");
442         tmp = len; i = 0;
443         while (comx_debugflags[i].name) {
444                 if (ch->debug_flags & comx_debugflags[i].value) 
445                         len += sprintf(page + len, "%s ", 
446                                 comx_debugflags[i].name);
447                 i++;
448         }
449         len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
450
451         len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
452                 "aborts: %lu\n           buffer overrun: %lu, pbuffer overrun: %lu\n"
453                 "TX errors: underrun: %lu\n",
454                 ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, 
455                 ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, 
456                 ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
457                 ch->current_stats->tx_fifo_errors);
458
459         if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
460                 len += ch->LINE_statistics(dev, page + len);
461         } else {
462                 len += sprintf(page+len, "Line status: driver not initialized\n");
463         }
464         if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
465                 len += ch->HW_statistics(dev, page + len);
466         } else {
467                 len += sprintf(page+len, "Board status: driver not initialized\n");
468         }
469
470         return len;
471 }
472
473 static int comx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
474 {
475         struct comx_channel *ch = dev->priv;
476
477         if (ch->LINE_ioctl) {
478                 return(ch->LINE_ioctl(dev, ifr, cmd));
479         }
480         return -EINVAL;
481 }
482
483 static void comx_reset_dev(struct net_device *dev)
484 {
485         dev->open = comx_open;
486         dev->stop = comx_close;
487         dev->hard_start_xmit = comx_xmit;
488         dev->hard_header = comx_header;
489         dev->rebuild_header = comx_rebuild_header;
490         dev->get_stats = comx_stats;
491         dev->do_ioctl = comx_ioctl;
492         dev->change_mtu = NULL;
493         dev->tx_queue_len = 20;
494         dev->flags = IFF_NOARP;
495 }
496
497 static int comx_init_dev(struct net_device *dev)
498 {
499         struct comx_channel *ch;
500
501         if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
502                 return -ENOMEM;
503         }
504         memset(ch, 0, sizeof(struct comx_channel));
505
506         ch->loadavg[0] = 5;
507         ch->loadavg[1] = 300;
508         ch->loadavg[2] = 900;
509         ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; 
510         if ((ch->avg_bytes = kmalloc(ch->loadavg_size * 
511                 sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
512                 kfree(ch);
513                 return -ENOMEM;
514         }
515
516         memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
517         ch->loadavg_counter = 0;
518         ch->loadavg_timer.function = comx_loadavg_timerfun;
519         ch->loadavg_timer.data = (unsigned long)dev;
520         ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
521         add_timer(&ch->loadavg_timer);
522
523         dev->priv = (void *)ch;
524         ch->dev = dev;
525         ch->line_status &= ~LINE_UP;
526
527         ch->current_stats = &ch->stats;
528
529         comx_reset_dev(dev);
530         return 0;
531 }
532
533 static int comx_read_proc(char *page, char **start, off_t off, int count, 
534         int *eof, void *data)
535 {
536         struct proc_dir_entry *file = (struct proc_dir_entry *)data;
537         struct net_device *dev = file->parent->data;
538         struct comx_channel *ch = dev->priv;
539         int len = 0;
540
541         if (strcmp(file->name, FILENAME_STATUS) == 0) {
542                 len = comx_statistics(dev, page);
543         } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
544                 len = sprintf(page, "%s\n", ch->hardware ? 
545                         ch->hardware->name : HWNAME_NONE);
546         } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
547                 len = sprintf(page, "%s\n", ch->protocol ? 
548                         ch->protocol->name : PROTONAME_NONE);
549         } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
550                 len = sprintf(page, "%01d\n", ch->lineup_delay);
551         }
552
553         if (off >= len) {
554                 *eof = 1;
555                 return 0;
556         }
557
558         *start = page + off;
559         if (count >= len - off) {
560                 *eof = 1;
561         }
562         return min_t(int, count, len - off);
563 }
564
565
566 static int comx_root_read_proc(char *page, char **start, off_t off, int count, 
567         int *eof, void *data)
568 {
569         struct proc_dir_entry *file = (struct proc_dir_entry *)data;
570         struct comx_hardware *hw;
571         struct comx_protocol *line;
572
573         int len = 0;
574
575         if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
576                 for(hw=comx_channels;hw;hw=hw->next) 
577                         len+=sprintf(page+len, "%s\n", hw->name);
578         } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
579                 for(line=comx_lines;line;line=line->next)
580                         len+=sprintf(page+len, "%s\n", line->name);
581         }
582
583         if (off >= len) {
584                 *eof = 1;
585                 return 0;
586         }
587
588         *start = page + off;
589         if (count >= len - off) {
590                 *eof = 1;
591         }
592         return min_t(int, count, len - off);
593 }
594
595
596
597 static int comx_write_proc(struct file *file, const char *buffer, u_long count,
598         void *data)
599 {
600         struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
601         struct net_device *dev = (struct net_device *)entry->parent->data;
602         struct comx_channel *ch = dev->priv;
603         char *page;
604         struct comx_hardware *hw = comx_channels;
605         struct comx_protocol *line = comx_lines;
606         int ret=0;
607
608         if (count > PAGE_SIZE) {
609                 printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
610                 return -ENOSPC;
611         }
612
613         if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
614
615         if(copy_from_user(page, buffer, count))
616         {
617                 count = -EFAULT;
618                 goto out;
619         }
620
621         if (page[count-1] == '\n')
622                 page[count-1] = '\0';
623         else if (count < PAGE_SIZE)
624                 page[count] = '\0';
625         else if (page[count]) {
626                 count = -EINVAL;
627                 goto out;
628         }
629
630         if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
631                 int i;
632                 int ret = 0;
633
634                 if ((i = simple_strtoul(page, NULL, 10)) != 0) {
635                         unsigned long flags;
636
637                         save_flags(flags); cli();
638                         if (ch->debug_area) kfree(ch->debug_area);
639                         if ((ch->debug_area = kmalloc(ch->debug_size = i, 
640                                 GFP_KERNEL)) == NULL) {
641                                 ret = -ENOMEM;
642                         }
643                         ch->debug_start = ch->debug_end = 0;
644                         restore_flags(flags);
645                         free_page((unsigned long)page);
646                         return ret ? ret : count;
647                 }
648                 
649                 if (*page != '+' && *page != '-') {
650                         free_page((unsigned long)page);
651                         return -EINVAL;
652                 }
653                 while (comx_debugflags[i].value && 
654                         strncmp(comx_debugflags[i].name, page + 1, 
655                         strlen(comx_debugflags[i].name))) {
656                         i++;
657                 }
658         
659                 if (comx_debugflags[i].value == 0) {
660                         printk(KERN_ERR "Invalid debug option\n");
661                         free_page((unsigned long)page);
662                         return -EINVAL;
663                 }
664                 if (*page == '+') {
665                         ch->debug_flags |= comx_debugflags[i].value;
666                 } else {
667                         ch->debug_flags &= ~comx_debugflags[i].value;
668                 }
669         } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
670                 if(strlen(page)>10) {
671                         free_page((unsigned long)page);
672                         return -EINVAL;
673                 }
674                 while (hw) { 
675                         if (strcmp(hw->name, page) == 0) {
676                                 break;
677                         } else {
678                                 hw = hw->next;
679                         }
680                 }
681 #ifdef CONFIG_KMOD
682                 if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
683                         request_module("comx-hw-%s",page);
684                 }               
685                 hw=comx_channels;
686                 while (hw) {
687                         if (comx_strcasecmp(hw->name, page) == 0) {
688                                 break;
689                         } else {
690                                 hw = hw->next;
691                         }
692                 }
693 #endif
694
695                 if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw)  {
696                         free_page((unsigned long)page);
697                         return -ENODEV;
698                 }
699                 if (ch->init_status & HW_OPEN) {
700                         free_page((unsigned long)page);
701                         return -EBUSY;
702                 }
703                 if (ch->hardware && ch->hardware->hw_exit && 
704                    (ret=ch->hardware->hw_exit(dev))) {
705                         free_page((unsigned long)page);
706                         return ret;
707                 }
708                 ch->hardware = hw;
709                 entry->size = strlen(page) + 1;
710                 if (hw && hw->hw_init) hw->hw_init(dev);
711         } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
712                 if(strlen(page)>10) {
713                         free_page((unsigned long)page);
714                         return -EINVAL;
715                 }
716                 while (line) {
717                         if (comx_strcasecmp(line->name, page) == 0) {
718                                 break;
719                         } else {
720                                 line = line->next;
721                         }
722                 }
723 #ifdef CONFIG_KMOD
724                 if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
725                         request_module("comx-proto-%s",page);
726                 }               
727                 line=comx_lines;
728                 while (line) {
729                         if (comx_strcasecmp(line->name, page) == 0) {
730                                 break;
731                         } else {
732                                 line = line->next;
733                         }
734                 }
735 #endif
736                 
737                 if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
738                         free_page((unsigned long)page);
739                         return -ENODEV;
740                 }
741                 
742                 if (ch->init_status & LINE_OPEN) {
743                         free_page((unsigned long)page);
744                         return -EBUSY;
745                 }
746                 
747                 if (ch->protocol && ch->protocol->line_exit && 
748                     (ret=ch->protocol->line_exit(dev))) {
749                         free_page((unsigned long)page);
750                         return ret;
751                 }
752                 ch->protocol = line;
753                 entry->size = strlen(page) + 1;
754                 comx_reset_dev(dev);
755                 if (line && line->line_init) line->line_init(dev);
756         } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
757                 int i;
758
759                 if ((i = simple_strtoul(page, NULL, 10)) != 0) {
760                         if (i >=0 && i < 10) { 
761                                 ch->lineup_delay = i; 
762                         } else {
763                                 printk(KERN_ERR "comx: invalid lineup_delay value\n");
764                         }
765                 }
766         }
767 out:
768         free_page((unsigned long)page);
769         return count;
770 }
771
772 static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
773 {
774         struct proc_dir_entry *new_dir, *debug_file;
775         struct net_device *dev;
776         struct comx_channel *ch;
777         int ret = -EIO;
778
779         if ((dev = kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) {
780                 return -ENOMEM;
781         }
782         memset(dev, 0, sizeof(struct net_device));
783
784         lock_kernel();
785         if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, 
786                 comx_root_dir)) == NULL) {
787                 goto cleanup_dev;
788         }
789
790         new_dir->nlink = 2;
791         new_dir->data = NULL; // ide jon majd a struct dev
792
793         /* Ezek kellenek */
794         if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, 
795             strlen(HWNAME_NONE) + 1, new_dir)) {
796                 goto cleanup_new_dir;
797         }
798         if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, 
799             strlen(PROTONAME_NONE) + 1, new_dir)) {
800                 goto cleanup_filename_hardware;
801         }
802         if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
803                 goto cleanup_filename_protocol;
804         }
805         if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
806                 goto cleanup_filename_status;
807         }
808
809         if ((debug_file = create_proc_entry(FILENAME_DEBUG, 
810             S_IFREG | 0644, new_dir)) == NULL) {
811                 goto cleanup_filename_lineupdelay;
812         }
813         debug_file->data = (void *)debug_file; 
814         debug_file->read_proc = NULL; // see below
815         debug_file->write_proc = &comx_write_proc;
816         debug_file->nlink = 1;
817
818         strcpy(dev->name, (char *)new_dir->name);
819         dev->init = comx_init_dev;
820
821         if (register_netdevice(dev)) {
822                 goto cleanup_filename_debug;
823         }
824         ch = dev->priv;
825         if((ch->if_ptr = (void *)kmalloc(sizeof(struct ppp_device), 
826                                  GFP_KERNEL)) == NULL) {
827                 goto cleanup_register;
828         }
829         memset(ch->if_ptr, 0, sizeof(struct ppp_device));
830         ch->debug_file = debug_file; 
831         ch->procdir = new_dir;
832         new_dir->data = dev;
833
834         ch->debug_start = ch->debug_end = 0;
835         if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, 
836             GFP_KERNEL)) == NULL) {
837                 ret = -ENOMEM;
838                 goto cleanup_if_ptr;
839         }
840
841         ch->lineup_delay = DEFAULT_LINEUP_DELAY;
842
843         MOD_INC_USE_COUNT;
844         unlock_kernel();
845         return 0;
846 cleanup_if_ptr:
847         kfree(ch->if_ptr);
848 cleanup_register:
849         unregister_netdevice(dev);
850 cleanup_filename_debug:
851         remove_proc_entry(FILENAME_DEBUG, new_dir);
852 cleanup_filename_lineupdelay:
853         remove_proc_entry(FILENAME_LINEUPDELAY, new_dir);
854 cleanup_filename_status:
855         remove_proc_entry(FILENAME_STATUS, new_dir);
856 cleanup_filename_protocol:
857         remove_proc_entry(FILENAME_PROTOCOL, new_dir);
858 cleanup_filename_hardware:
859         remove_proc_entry(FILENAME_HARDWARE, new_dir);
860 cleanup_new_dir:
861         remove_proc_entry(dentry->d_name.name, comx_root_dir);
862 cleanup_dev:
863         kfree(dev);
864         unlock_kernel();
865         return ret;
866 }
867
868 static int comx_rmdir(struct inode *dir, struct dentry *dentry)
869 {
870         struct proc_dir_entry *entry = PDE(dentry->d_inode);
871         struct net_device *dev;
872         struct comx_channel *ch;
873         int ret;
874
875         lock_kernel();
876         dev = entry->data;
877         ch = dev->priv;
878         if (dev->flags & IFF_UP) {
879                 printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
880                 unlock_kernel();
881                 return -EBUSY;
882         }
883
884         if (ch->protocol && ch->protocol->line_exit && 
885             (ret=ch->protocol->line_exit(dev))) {
886                 unlock_kernel();
887                 return ret;
888         }
889         if (ch->hardware && ch->hardware->hw_exit && 
890            (ret=ch->hardware->hw_exit(dev))) { 
891                 if(ch->protocol && ch->protocol->line_init) {
892                         ch->protocol->line_init(dev);
893                 }
894                 unlock_kernel();
895                 return ret;
896         }
897         ch->protocol = NULL;
898         ch->hardware = NULL;
899
900         del_timer(&ch->loadavg_timer);
901         kfree(ch->avg_bytes);
902
903         unregister_netdev(dev);
904         if (ch->debug_area) {
905                 kfree(ch->debug_area);
906         }
907         if (dev->priv) {
908                 kfree(dev->priv);
909         }
910         free_netdev(dev);
911
912         remove_proc_entry(FILENAME_DEBUG, entry);
913         remove_proc_entry(FILENAME_LINEUPDELAY, entry);
914         remove_proc_entry(FILENAME_STATUS, entry);
915         remove_proc_entry(FILENAME_HARDWARE, entry);
916         remove_proc_entry(FILENAME_PROTOCOL, entry);
917         remove_proc_entry(dentry->d_name.name, comx_root_dir);
918
919         MOD_DEC_USE_COUNT;
920         unlock_kernel();
921         return 0;
922 }
923
924 static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
925 {
926         struct proc_dir_entry *de;
927         struct inode *inode = NULL;
928
929         lock_kernel();
930         if ((de = PDE(dir)) != NULL) {
931                 for (de = de->subdir ; de ; de = de->next) {
932                         if ((de->namelen == dentry->d_name.len) &&
933                             (memcmp(dentry->d_name.name, de->name, 
934                             de->namelen) == 0)) {
935                                 if ((inode = proc_get_inode(dir->i_sb, 
936                                     de->low_ino, de)) == NULL) { 
937                                         printk(KERN_ERR "COMX: lookup error\n"); 
938                                         unlock_kernel();
939                                         return ERR_PTR(-EINVAL); 
940                                 }
941                                 break;
942                         }
943                 }
944         }
945         unlock_kernel();
946         dentry->d_op = &comx_dentry_operations;
947         d_add(dentry, inode);
948         return NULL;
949 }
950
951 int comx_strcasecmp(const char *cs, const char *ct)
952 {
953         register signed char __res;
954
955         while (1) {
956                 if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
957                         break;
958                 }
959         }
960         return __res;
961 }
962
963 static int comx_delete_dentry(struct dentry *dentry)
964 {
965         return 1;
966 }
967
968 static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
969         int size, struct proc_dir_entry *dir)
970 {
971         struct proc_dir_entry *new_file;
972
973         if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
974                 new_file->data = (void *)new_file;
975                 new_file->read_proc = &comx_read_proc;
976                 new_file->write_proc = &comx_write_proc;
977                 new_file->size = size;
978                 new_file->nlink = 1;
979         }
980         return(new_file);
981 }
982
983 int comx_register_hardware(struct comx_hardware *comx_hw)
984 {
985         struct comx_hardware *hw = comx_channels;
986
987         if (!hw) {
988                 comx_channels = comx_hw;
989         } else {
990                 while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
991                         hw = hw->next;
992                 }
993                 if (strcmp(comx_hw->name, hw->name) == 0) {
994                         return -1;
995                 }
996                 hw->next = comx_hw;
997         }
998
999         printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
1000         return 0;
1001 }
1002
1003 int comx_unregister_hardware(char *name)
1004 {
1005         struct comx_hardware *hw = comx_channels;
1006
1007         if (!hw) {
1008                 return -1;
1009         }
1010
1011         if (strcmp(hw->name, name) == 0) {
1012                 comx_channels = comx_channels->next;
1013                 return 0;
1014         }
1015
1016         while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
1017                 hw = hw->next;
1018         }
1019
1020         if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
1021                 hw->next = hw->next->next;
1022                 return 0;
1023         }
1024         return -1;
1025 }
1026
1027 int comx_register_protocol(struct comx_protocol *comx_line)
1028 {
1029         struct comx_protocol *pr = comx_lines;
1030
1031         if (!pr) {
1032                 comx_lines = comx_line;
1033         } else {
1034                 while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
1035                         pr = pr->next;
1036                 }
1037                 if (strcmp(comx_line->name, pr->name) == 0) {
1038                         return -1;
1039                 }
1040                 pr->next = comx_line;
1041         }
1042
1043         printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
1044         return 0;
1045 }
1046
1047 int comx_unregister_protocol(char *name)
1048 {
1049         struct comx_protocol *pr = comx_lines;
1050
1051         if (!pr) {
1052                 return -1;
1053         }
1054
1055         if (strcmp(pr->name, name) == 0) {
1056                 comx_lines = comx_lines->next;
1057                 return 0;
1058         }
1059
1060         while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
1061                 pr = pr->next;
1062         }
1063
1064         if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
1065                 pr->next = pr->next->next;
1066                 return 0;
1067         }
1068         return -1;
1069 }
1070
1071 static int __init comx_init(void)
1072 {
1073         struct proc_dir_entry *new_file;
1074
1075         comx_root_dir = create_proc_entry("comx", 
1076                 S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, &proc_root);
1077         if (!comx_root_dir)
1078                 return -ENOMEM;
1079         comx_root_dir->proc_iops = &comx_root_inode_ops;
1080
1081         if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, 
1082            S_IFREG | 0444, comx_root_dir)) == NULL) {
1083                 return -ENOMEM;
1084         }
1085         
1086         new_file->data = new_file;
1087         new_file->read_proc = &comx_root_read_proc;
1088         new_file->write_proc = NULL;
1089         new_file->nlink = 1;
1090
1091         if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, 
1092            S_IFREG | 0444, comx_root_dir)) == NULL) {
1093                 return -ENOMEM;
1094         }
1095         
1096         new_file->data = new_file;
1097         new_file->read_proc = &comx_root_read_proc;
1098         new_file->write_proc = NULL;
1099         new_file->nlink = 1;
1100
1101
1102         printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n", 
1103                 VERSION);
1104         return 0;
1105 }
1106
1107 static void __exit comx_exit(void)
1108 {
1109         remove_proc_entry(FILENAME_HARDWARELIST, comx_root_dir);
1110         remove_proc_entry(FILENAME_PROTOCOLLIST, comx_root_dir);
1111         remove_proc_entry(comx_root_dir->name, &proc_root);
1112 }
1113
1114 module_init(comx_init);
1115 module_exit(comx_exit);
1116
1117 EXPORT_SYMBOL(comx_register_hardware);
1118 EXPORT_SYMBOL(comx_unregister_hardware);
1119 EXPORT_SYMBOL(comx_register_protocol);
1120 EXPORT_SYMBOL(comx_unregister_protocol);
1121 EXPORT_SYMBOL(comx_debug_skb);
1122 EXPORT_SYMBOL(comx_debug_bytes);
1123 EXPORT_SYMBOL(comx_debug);
1124 EXPORT_SYMBOL(comx_lineup_func);
1125 EXPORT_SYMBOL(comx_status);
1126 EXPORT_SYMBOL(comx_rx);
1127 EXPORT_SYMBOL(comx_strcasecmp);
1128 EXPORT_SYMBOL(comx_root_dir);