Commit | Line | Data |
---|---|---|
50ee11fe BB |
1 | /* Copyright (C) 2007-2008 One Stop Systems |
2 | * Copyright (C) 2003-2006 SBE, Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
e6e4d05d JP |
15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
16 | ||
50ee11fe BB |
17 | #include <linux/types.h> |
18 | #include <linux/netdevice.h> | |
45296236 | 19 | #include <linux/module.h> |
50ee11fe BB |
20 | #include <linux/hdlc.h> |
21 | #include <linux/if_arp.h> | |
22 | #include <linux/init.h> | |
23 | #include <asm/uaccess.h> | |
24 | #include <linux/rtnetlink.h> | |
25 | #include <linux/skbuff.h> | |
26 | #include "pmcc4_sysdep.h" | |
27 | #include "sbecom_inline_linux.h" | |
28 | #include "libsbew.h" | |
29 | #include "pmcc4.h" | |
30 | #include "pmcc4_ioctls.h" | |
31 | #include "pmcc4_private.h" | |
32 | #include "sbeproc.h" | |
33 | ||
34 | /***************************************************************************************** | |
35 | * Error out early if we have compiler trouble. | |
36 | * | |
37 | * (This section is included from the kernel's init/main.c as a friendly | |
38 | * spiderman recommendation...) | |
39 | * | |
40 | * Versions of gcc older than that listed below may actually compile and link | |
41 | * okay, but the end product can have subtle run time bugs. To avoid associated | |
42 | * bogus bug reports, we flatly refuse to compile with a gcc that is known to be | |
43 | * too old from the very beginning. | |
44 | */ | |
45 | #if (__GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 2) | |
46 | #error Sorry, your GCC is too old. It builds incorrect kernels. | |
47 | #endif | |
48 | ||
49 | #if __GNUC__ == 4 && __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ == 0 | |
50 | #warning gcc-4.1.0 is known to miscompile the kernel. A different compiler version is recommended. | |
51 | #endif | |
52 | ||
53 | /*****************************************************************************************/ | |
54 | ||
55 | #ifdef SBE_INCLUDE_SYMBOLS | |
56 | #define STATIC | |
57 | #else | |
58 | #define STATIC static | |
59 | #endif | |
60 | ||
61 | #define CHANNAME "hdlc" | |
62 | ||
63 | /*******************************************************************/ | |
64 | /* forward references */ | |
65 | status_t c4_chan_work_init (mpi_t *, mch_t *); | |
66 | void musycc_wq_chan_restart (void *); | |
67 | status_t __init c4_init (ci_t *, u_char *, u_char *); | |
68 | status_t __init c4_init2 (ci_t *); | |
69 | ci_t *__init c4_new (void *); | |
70 | int __init c4hw_attach_all (void); | |
71 | void __init hdw_sn_get (hdw_info_t *, int); | |
72 | ||
73 | #ifdef CONFIG_SBE_PMCC4_NCOMM | |
74 | irqreturn_t c4_ebus_intr_th_handler (void *); | |
75 | ||
76 | #endif | |
77 | int c4_frame_rw (ci_t *, struct sbecom_port_param *); | |
78 | status_t c4_get_port (ci_t *, int); | |
79 | int c4_loop_port (ci_t *, int, u_int8_t); | |
80 | int c4_musycc_rw (ci_t *, struct c4_musycc_param *); | |
81 | int c4_new_chan (ci_t *, int, int, void *); | |
82 | status_t c4_set_port (ci_t *, int); | |
83 | int c4_pld_rw (ci_t *, struct sbecom_port_param *); | |
84 | void cleanup_devs (void); | |
85 | void cleanup_ioremap (void); | |
86 | status_t musycc_chan_down (ci_t *, int); | |
87 | irqreturn_t musycc_intr_th_handler (void *); | |
88 | int musycc_start_xmit (ci_t *, int, void *); | |
89 | ||
90 | extern char pmcc4_OSSI_release[]; | |
91 | extern ci_t *CI; | |
92 | extern struct s_hdw_info hdw_info[]; | |
93 | ||
94 | #if defined(CONFIG_SBE_HDLC_V7) || defined(CONFIG_SBE_WAN256T3_HDLC_V7) || \ | |
95 | defined(CONFIG_SBE_HDLC_V7_MODULE) || defined(CONFIG_SBE_WAN256T3_HDLC_V7_MODULE) | |
96 | #define _v7_hdlc_ 1 | |
97 | #else | |
98 | #define _v7_hdlc_ 0 | |
99 | #endif | |
100 | ||
101 | #if _v7_hdlc_ | |
102 | #define V7(x) (x ## _v7) | |
103 | extern int hdlc_netif_rx_v7 (hdlc_device *, struct sk_buff *); | |
104 | extern int register_hdlc_device_v7 (hdlc_device *); | |
105 | extern int unregister_hdlc_device_v7 (hdlc_device *); | |
106 | ||
107 | #else | |
108 | #define V7(x) x | |
109 | #endif | |
110 | ||
111 | int error_flag; /* module load error reporting */ | |
b8b73994 | 112 | int cxt1e1_log_level = LOG_ERROR; |
50ee11fe | 113 | int log_level_default = LOG_ERROR; |
b8b73994 | 114 | module_param(cxt1e1_log_level, int, 0444); |
50ee11fe | 115 | |
c694ed85 | 116 | int cxt1e1_max_mru = MUSYCC_MRU; |
50ee11fe | 117 | int max_mru_default = MUSYCC_MRU; |
c694ed85 | 118 | module_param(cxt1e1_max_mru, int, 0444); |
50ee11fe | 119 | |
c694ed85 | 120 | int cxt1e1_max_mtu = MUSYCC_MTU; |
50ee11fe | 121 | int max_mtu_default = MUSYCC_MTU; |
c694ed85 | 122 | module_param(cxt1e1_max_mtu, int, 0444); |
50ee11fe BB |
123 | |
124 | int max_txdesc_used = MUSYCC_TXDESC_MIN; | |
125 | int max_txdesc_default = MUSYCC_TXDESC_MIN; | |
126 | module_param(max_txdesc_used, int, 0444); | |
127 | ||
128 | int max_rxdesc_used = MUSYCC_RXDESC_MIN; | |
129 | int max_rxdesc_default = MUSYCC_RXDESC_MIN; | |
130 | module_param(max_rxdesc_used, int, 0444); | |
131 | ||
132 | /****************************************************************************/ | |
133 | /****************************************************************************/ | |
134 | /****************************************************************************/ | |
135 | ||
136 | void * | |
137 | getuserbychan (int channum) | |
138 | { | |
139 | mch_t *ch; | |
140 | ||
141 | ch = c4_find_chan (channum); | |
142 | return ch ? ch->user : 0; | |
143 | } | |
144 | ||
145 | ||
50ee11fe BB |
146 | char * |
147 | get_hdlc_name (hdlc_device * hdlc) | |
148 | { | |
149 | struct c4_priv *priv = hdlc->priv; | |
150 | struct net_device *dev = getuserbychan (priv->channum); | |
151 | ||
152 | return dev->name; | |
153 | } | |
50ee11fe BB |
154 | |
155 | ||
156 | static status_t | |
157 | mkret (int bsd) | |
158 | { | |
159 | if (bsd > 0) | |
160 | return -bsd; | |
161 | else | |
162 | return bsd; | |
163 | } | |
164 | ||
165 | /***************************************************************************/ | |
50ee11fe BB |
166 | #include <linux/workqueue.h> |
167 | ||
168 | /*** | |
169 | * One workqueue (wq) per port (since musycc allows simultaneous group | |
170 | * commands), with individual data for each channel: | |
171 | * | |
172 | * mpi_t -> struct workqueue_struct *wq_port; (dynamically allocated using | |
173 | * create_workqueue()) | |
174 | * | |
175 | * With work structure (work) statically allocated for each channel: | |
176 | * | |
177 | * mch_t -> struct work_struct ch_work; (statically allocated using ???) | |
178 | * | |
179 | ***/ | |
180 | ||
181 | ||
182 | /* | |
183 | * Called by the start transmit routine when a channel TX_ENABLE is to be | |
184 | * issued. This queues the transmission start request among other channels | |
185 | * within a port's group. | |
186 | */ | |
187 | void | |
188 | c4_wk_chan_restart (mch_t * ch) | |
189 | { | |
190 | mpi_t *pi = ch->up; | |
191 | ||
192 | #ifdef RLD_RESTART_DEBUG | |
694a9807 JP |
193 | pr_info(">> %s: queueing Port %d Chan %d, mch_t @ %p\n", |
194 | __func__, pi->portnum, ch->channum, ch); | |
50ee11fe BB |
195 | #endif |
196 | ||
197 | /* create new entry w/in workqueue for this channel and let'er rip */ | |
198 | ||
199 | /** queue_work (struct workqueue_struct *queue, | |
200 | ** struct work_struct *work); | |
201 | **/ | |
202 | queue_work (pi->wq_port, &ch->ch_work); | |
203 | } | |
204 | ||
205 | status_t | |
206 | c4_wk_chan_init (mpi_t * pi, mch_t * ch) | |
207 | { | |
208 | /* | |
209 | * this will be used to restart a stopped channel | |
210 | */ | |
211 | ||
212 | /** INIT_WORK (struct work_struct *work, | |
213 | ** void (*function)(void *), | |
214 | ** void *data); | |
215 | **/ | |
216 | INIT_WORK(&ch->ch_work, (void *)musycc_wq_chan_restart); | |
217 | return 0; /* success */ | |
218 | } | |
219 | ||
220 | status_t | |
221 | c4_wq_port_init (mpi_t * pi) | |
222 | { | |
223 | ||
224 | char name[16], *np; /* NOTE: name of the queue limited by system | |
225 | * to 10 characters */ | |
226 | ||
227 | if (pi->wq_port) | |
228 | return 0; /* already initialized */ | |
229 | ||
230 | np = name; | |
231 | memset (name, 0, 16); | |
232 | sprintf (np, "%s%d", pi->up->devname, pi->portnum); /* IE pmcc4-01) */ | |
233 | ||
234 | #ifdef RLD_RESTART_DEBUG | |
694a9807 JP |
235 | pr_info(">> %s: creating workqueue <%s> for Port %d.\n", |
236 | __func__, name, pi->portnum); /* RLD DEBUG */ | |
50ee11fe BB |
237 | #endif |
238 | if (!(pi->wq_port = create_singlethread_workqueue (name))) | |
239 | return ENOMEM; | |
240 | return 0; /* success */ | |
241 | } | |
242 | ||
243 | void | |
244 | c4_wq_port_cleanup (mpi_t * pi) | |
245 | { | |
246 | /* | |
247 | * PORT POINT: cannot call this if WQ is statically allocated w/in | |
248 | * structure since it calls kfree(wq); | |
249 | */ | |
250 | if (pi->wq_port) | |
251 | { | |
252 | destroy_workqueue (pi->wq_port); /* this also calls | |
253 | * flush_workqueue() */ | |
254 | pi->wq_port = 0; | |
255 | } | |
256 | } | |
50ee11fe BB |
257 | |
258 | /***************************************************************************/ | |
259 | ||
260 | irqreturn_t | |
261 | c4_linux_interrupt (int irq, void *dev_instance) | |
262 | { | |
263 | struct net_device *ndev = dev_instance; | |
264 | ||
265 | return musycc_intr_th_handler(netdev_priv(ndev)); | |
266 | } | |
267 | ||
268 | ||
269 | #ifdef CONFIG_SBE_PMCC4_NCOMM | |
270 | irqreturn_t | |
271 | c4_ebus_interrupt (int irq, void *dev_instance) | |
272 | { | |
273 | struct net_device *ndev = dev_instance; | |
274 | ||
275 | return c4_ebus_intr_th_handler(netdev_priv(ndev)); | |
276 | } | |
277 | #endif | |
278 | ||
279 | ||
280 | static int | |
281 | void_open (struct net_device * ndev) | |
282 | { | |
694a9807 | 283 | pr_info("%s: trying to open master device !\n", ndev->name); |
50ee11fe BB |
284 | return -1; |
285 | } | |
286 | ||
287 | ||
50ee11fe BB |
288 | STATIC int |
289 | chan_open (struct net_device * ndev) | |
290 | { | |
291 | hdlc_device *hdlc = dev_to_hdlc (ndev); | |
292 | const struct c4_priv *priv = hdlc->priv; | |
293 | int ret; | |
294 | ||
295 | if ((ret = hdlc_open (ndev))) | |
296 | { | |
694a9807 | 297 | pr_info("hdlc_open failure, err %d.\n", ret); |
50ee11fe BB |
298 | return ret; |
299 | } | |
300 | if ((ret = c4_chan_up (priv->ci, priv->channum))) | |
301 | return -ret; | |
302 | try_module_get (THIS_MODULE); | |
303 | netif_start_queue (ndev); | |
304 | return 0; /* no error = success */ | |
305 | } | |
50ee11fe | 306 | |
50ee11fe | 307 | |
50ee11fe BB |
308 | STATIC int |
309 | chan_close (struct net_device * ndev) | |
310 | { | |
311 | hdlc_device *hdlc = dev_to_hdlc (ndev); | |
312 | const struct c4_priv *priv = hdlc->priv; | |
313 | ||
314 | netif_stop_queue (ndev); | |
315 | musycc_chan_down ((ci_t *) 0, priv->channum); | |
316 | hdlc_close (ndev); | |
317 | module_put (THIS_MODULE); | |
318 | return 0; | |
319 | } | |
50ee11fe BB |
320 | |
321 | ||
50ee11fe BB |
322 | STATIC int |
323 | chan_dev_ioctl (struct net_device * dev, struct ifreq * ifr, int cmd) | |
324 | { | |
325 | return hdlc_ioctl (dev, ifr, cmd); | |
326 | } | |
327 | ||
328 | ||
329 | STATIC int | |
50ee11fe | 330 | chan_attach_noop (struct net_device * ndev, unsigned short foo_1, unsigned short foo_2) |
50ee11fe BB |
331 | { |
332 | return 0; /* our driver has nothing to do here, show's | |
333 | * over, go home */ | |
334 | } | |
50ee11fe BB |
335 | |
336 | ||
337 | STATIC struct net_device_stats * | |
338 | chan_get_stats (struct net_device * ndev) | |
339 | { | |
340 | mch_t *ch; | |
341 | struct net_device_stats *nstats; | |
342 | struct sbecom_chan_stats *stats; | |
343 | int channum; | |
344 | ||
50ee11fe BB |
345 | { |
346 | struct c4_priv *priv; | |
347 | ||
348 | priv = (struct c4_priv *) dev_to_hdlc (ndev)->priv; | |
349 | channum = priv->channum; | |
350 | } | |
50ee11fe BB |
351 | |
352 | ch = c4_find_chan (channum); | |
353 | if (ch == NULL) | |
354 | return NULL; | |
355 | ||
356 | nstats = &ndev->stats; | |
357 | stats = &ch->s; | |
358 | ||
359 | memset (nstats, 0, sizeof (struct net_device_stats)); | |
360 | nstats->rx_packets = stats->rx_packets; | |
361 | nstats->tx_packets = stats->tx_packets; | |
362 | nstats->rx_bytes = stats->rx_bytes; | |
363 | nstats->tx_bytes = stats->tx_bytes; | |
364 | nstats->rx_errors = stats->rx_length_errors + | |
365 | stats->rx_over_errors + | |
366 | stats->rx_crc_errors + | |
367 | stats->rx_frame_errors + | |
368 | stats->rx_fifo_errors + | |
369 | stats->rx_missed_errors; | |
370 | nstats->tx_errors = stats->tx_dropped + | |
371 | stats->tx_aborted_errors + | |
372 | stats->tx_fifo_errors; | |
373 | nstats->rx_dropped = stats->rx_dropped; | |
374 | nstats->tx_dropped = stats->tx_dropped; | |
375 | ||
376 | nstats->rx_length_errors = stats->rx_length_errors; | |
377 | nstats->rx_over_errors = stats->rx_over_errors; | |
378 | nstats->rx_crc_errors = stats->rx_crc_errors; | |
379 | nstats->rx_frame_errors = stats->rx_frame_errors; | |
380 | nstats->rx_fifo_errors = stats->rx_fifo_errors; | |
381 | nstats->rx_missed_errors = stats->rx_missed_errors; | |
382 | ||
383 | nstats->tx_aborted_errors = stats->tx_aborted_errors; | |
384 | nstats->tx_fifo_errors = stats->tx_fifo_errors; | |
385 | ||
386 | return nstats; | |
387 | } | |
388 | ||
389 | ||
390 | static ci_t * | |
391 | get_ci_by_dev (struct net_device * ndev) | |
392 | { | |
393 | return (ci_t *)(netdev_priv(ndev)); | |
394 | } | |
395 | ||
396 | ||
50ee11fe BB |
397 | STATIC int |
398 | c4_linux_xmit (struct sk_buff * skb, struct net_device * ndev) | |
399 | { | |
400 | const struct c4_priv *priv; | |
401 | int rval; | |
402 | ||
50ee11fe BB |
403 | hdlc_device *hdlc = dev_to_hdlc (ndev); |
404 | ||
405 | priv = hdlc->priv; | |
50ee11fe BB |
406 | |
407 | rval = musycc_start_xmit (priv->ci, priv->channum, skb); | |
a47bf245 | 408 | return rval; |
50ee11fe | 409 | } |
50ee11fe BB |
410 | |
411 | static const struct net_device_ops chan_ops = { | |
412 | .ndo_open = chan_open, | |
413 | .ndo_stop = chan_close, | |
414 | .ndo_start_xmit = c4_linux_xmit, | |
415 | .ndo_do_ioctl = chan_dev_ioctl, | |
416 | .ndo_get_stats = chan_get_stats, | |
417 | }; | |
418 | ||
419 | STATIC struct net_device * | |
420 | create_chan (struct net_device * ndev, ci_t * ci, | |
421 | struct sbecom_chan_param * cp) | |
422 | { | |
423 | hdlc_device *hdlc; | |
424 | struct net_device *dev; | |
425 | hdw_info_t *hi; | |
426 | int ret; | |
427 | ||
428 | if (c4_find_chan (cp->channum)) | |
429 | return 0; /* channel already exists */ | |
430 | ||
431 | { | |
432 | struct c4_priv *priv; | |
433 | ||
434 | /* allocate then fill in private data structure */ | |
435 | priv = OS_kmalloc (sizeof (struct c4_priv)); | |
436 | if (!priv) | |
437 | { | |
e6e4d05d | 438 | pr_warning("%s: no memory for net_device !\n", ci->devname); |
50ee11fe BB |
439 | return 0; |
440 | } | |
441 | dev = alloc_hdlcdev (priv); | |
442 | if (!dev) | |
443 | { | |
e6e4d05d | 444 | pr_warning("%s: no memory for hdlc_device !\n", ci->devname); |
50ee11fe BB |
445 | OS_kfree (priv); |
446 | return 0; | |
447 | } | |
448 | priv->ci = ci; | |
449 | priv->channum = cp->channum; | |
450 | } | |
451 | ||
452 | hdlc = dev_to_hdlc (dev); | |
453 | ||
454 | dev->base_addr = 0; /* not I/O mapped */ | |
455 | dev->irq = ndev->irq; | |
456 | dev->type = ARPHRD_RAWHDLC; | |
457 | *dev->name = 0; /* default ifconfig name = "hdlc" */ | |
458 | ||
459 | hi = (hdw_info_t *) ci->hdw_info; | |
460 | if (hi->mfg_info_sts == EEPROM_OK) | |
461 | { | |
462 | switch (hi->promfmt) | |
463 | { | |
464 | case PROM_FORMAT_TYPE1: | |
465 | memcpy (dev->dev_addr, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); | |
466 | break; | |
467 | case PROM_FORMAT_TYPE2: | |
468 | memcpy (dev->dev_addr, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); | |
469 | break; | |
470 | default: | |
471 | memset (dev->dev_addr, 0, 6); | |
472 | break; | |
473 | } | |
474 | } else | |
475 | { | |
476 | memset (dev->dev_addr, 0, 6); | |
477 | } | |
478 | ||
479 | hdlc->xmit = c4_linux_xmit; | |
480 | ||
481 | dev->netdev_ops = &chan_ops; | |
482 | /* | |
483 | * The native hdlc stack calls this 'attach' routine during | |
484 | * hdlc_raw_ioctl(), passing parameters for line encoding and parity. | |
485 | * Since hdlc_raw_ioctl() stack does not interrogate whether an 'attach' | |
486 | * routine is actually registered or not, we supply a dummy routine which | |
487 | * does nothing (since encoding and parity are setup for our driver via a | |
488 | * special configuration application). | |
489 | */ | |
490 | ||
491 | hdlc->attach = chan_attach_noop; | |
492 | ||
493 | rtnl_unlock (); /* needed due to Ioctl calling sequence */ | |
494 | ret = register_hdlc_device (dev); | |
495 | /* NOTE: <stats> setting must occur AFTER registration in order to "take" */ | |
496 | dev->tx_queue_len = MAX_DEFAULT_IFQLEN; | |
497 | ||
498 | rtnl_lock (); /* needed due to Ioctl calling sequence */ | |
499 | if (ret) | |
500 | { | |
b8b73994 | 501 | if (cxt1e1_log_level >= LOG_WARN) |
694a9807 | 502 | pr_info("%s: create_chan[%d] registration error = %d.\n", |
50ee11fe BB |
503 | ci->devname, cp->channum, ret); |
504 | free_netdev (dev); /* cleanup */ | |
505 | return 0; /* failed to register */ | |
506 | } | |
507 | return dev; | |
508 | } | |
509 | ||
510 | ||
511 | /* the idea here is to get port information and pass it back (using pointer) */ | |
512 | STATIC status_t | |
513 | do_get_port (struct net_device * ndev, void *data) | |
514 | { | |
515 | int ret; | |
516 | ci_t *ci; /* ci stands for card information */ | |
517 | struct sbecom_port_param pp;/* copy data to kernel land */ | |
518 | ||
519 | if (copy_from_user (&pp, data, sizeof (struct sbecom_port_param))) | |
520 | return -EFAULT; | |
521 | if (pp.portnum >= MUSYCC_NPORTS) | |
522 | return -EFAULT; | |
523 | ci = get_ci_by_dev (ndev); | |
524 | if (!ci) | |
525 | return -EINVAL; /* get card info */ | |
526 | ||
527 | ret = mkret (c4_get_port (ci, pp.portnum)); | |
528 | if (ret) | |
529 | return ret; | |
530 | if (copy_to_user (data, &ci->port[pp.portnum].p, | |
531 | sizeof (struct sbecom_port_param))) | |
532 | return -EFAULT; | |
533 | return 0; | |
534 | } | |
535 | ||
536 | /* this function copys the user data and then calls the real action function */ | |
537 | STATIC status_t | |
538 | do_set_port (struct net_device * ndev, void *data) | |
539 | { | |
540 | ci_t *ci; /* ci stands for card information */ | |
541 | struct sbecom_port_param pp;/* copy data to kernel land */ | |
542 | ||
543 | if (copy_from_user (&pp, data, sizeof (struct sbecom_port_param))) | |
544 | return -EFAULT; | |
545 | if (pp.portnum >= MUSYCC_NPORTS) | |
546 | return -EFAULT; | |
547 | ci = get_ci_by_dev (ndev); | |
548 | if (!ci) | |
549 | return -EINVAL; /* get card info */ | |
550 | ||
551 | if (pp.portnum >= ci->max_port) /* sanity check */ | |
3ec6080e | 552 | return -ENXIO; |
50ee11fe BB |
553 | |
554 | memcpy (&ci->port[pp.portnum].p, &pp, sizeof (struct sbecom_port_param)); | |
555 | return mkret (c4_set_port (ci, pp.portnum)); | |
556 | } | |
557 | ||
558 | /* work the port loopback mode as per directed */ | |
559 | STATIC status_t | |
560 | do_port_loop (struct net_device * ndev, void *data) | |
561 | { | |
562 | struct sbecom_port_param pp; | |
563 | ci_t *ci; | |
564 | ||
565 | if (copy_from_user (&pp, data, sizeof (struct sbecom_port_param))) | |
566 | return -EFAULT; | |
567 | ci = get_ci_by_dev (ndev); | |
568 | if (!ci) | |
569 | return -EINVAL; | |
570 | return mkret (c4_loop_port (ci, pp.portnum, pp.port_mode)); | |
571 | } | |
572 | ||
573 | /* set the specified register with the given value / or just read it */ | |
574 | STATIC status_t | |
575 | do_framer_rw (struct net_device * ndev, void *data) | |
576 | { | |
577 | struct sbecom_port_param pp; | |
578 | ci_t *ci; | |
579 | int ret; | |
580 | ||
581 | if (copy_from_user (&pp, data, sizeof (struct sbecom_port_param))) | |
582 | return -EFAULT; | |
583 | ci = get_ci_by_dev (ndev); | |
584 | if (!ci) | |
585 | return -EINVAL; | |
586 | ret = mkret (c4_frame_rw (ci, &pp)); | |
587 | if (ret) | |
588 | return ret; | |
589 | if (copy_to_user (data, &pp, sizeof (struct sbecom_port_param))) | |
590 | return -EFAULT; | |
591 | return 0; | |
592 | } | |
593 | ||
594 | /* set the specified register with the given value / or just read it */ | |
595 | STATIC status_t | |
596 | do_pld_rw (struct net_device * ndev, void *data) | |
597 | { | |
598 | struct sbecom_port_param pp; | |
599 | ci_t *ci; | |
600 | int ret; | |
601 | ||
602 | if (copy_from_user (&pp, data, sizeof (struct sbecom_port_param))) | |
603 | return -EFAULT; | |
604 | ci = get_ci_by_dev (ndev); | |
605 | if (!ci) | |
606 | return -EINVAL; | |
607 | ret = mkret (c4_pld_rw (ci, &pp)); | |
608 | if (ret) | |
609 | return ret; | |
610 | if (copy_to_user (data, &pp, sizeof (struct sbecom_port_param))) | |
611 | return -EFAULT; | |
612 | return 0; | |
613 | } | |
614 | ||
615 | /* set the specified register with the given value / or just read it */ | |
616 | STATIC status_t | |
617 | do_musycc_rw (struct net_device * ndev, void *data) | |
618 | { | |
619 | struct c4_musycc_param mp; | |
620 | ci_t *ci; | |
621 | int ret; | |
622 | ||
623 | if (copy_from_user (&mp, data, sizeof (struct c4_musycc_param))) | |
624 | return -EFAULT; | |
625 | ci = get_ci_by_dev (ndev); | |
626 | if (!ci) | |
627 | return -EINVAL; | |
628 | ret = mkret (c4_musycc_rw (ci, &mp)); | |
629 | if (ret) | |
630 | return ret; | |
631 | if (copy_to_user (data, &mp, sizeof (struct c4_musycc_param))) | |
632 | return -EFAULT; | |
633 | return 0; | |
634 | } | |
635 | ||
636 | STATIC status_t | |
637 | do_get_chan (struct net_device * ndev, void *data) | |
638 | { | |
639 | struct sbecom_chan_param cp; | |
640 | int ret; | |
641 | ||
642 | if (copy_from_user (&cp, data, | |
643 | sizeof (struct sbecom_chan_param))) | |
644 | return -EFAULT; | |
645 | ||
646 | if ((ret = mkret (c4_get_chan (cp.channum, &cp)))) | |
647 | return ret; | |
648 | ||
649 | if (copy_to_user (data, &cp, sizeof (struct sbecom_chan_param))) | |
650 | return -EFAULT; | |
651 | return 0; | |
652 | } | |
653 | ||
654 | STATIC status_t | |
655 | do_set_chan (struct net_device * ndev, void *data) | |
656 | { | |
657 | struct sbecom_chan_param cp; | |
658 | int ret; | |
659 | ci_t *ci; | |
660 | ||
661 | if (copy_from_user (&cp, data, sizeof (struct sbecom_chan_param))) | |
662 | return -EFAULT; | |
663 | ci = get_ci_by_dev (ndev); | |
664 | if (!ci) | |
665 | return -EINVAL; | |
666 | switch (ret = mkret (c4_set_chan (cp.channum, &cp))) | |
667 | { | |
668 | case 0: | |
669 | return 0; | |
670 | default: | |
671 | return ret; | |
672 | } | |
673 | } | |
674 | ||
675 | STATIC status_t | |
676 | do_create_chan (struct net_device * ndev, void *data) | |
677 | { | |
678 | ci_t *ci; | |
679 | struct net_device *dev; | |
680 | struct sbecom_chan_param cp; | |
681 | int ret; | |
682 | ||
683 | if (copy_from_user (&cp, data, sizeof (struct sbecom_chan_param))) | |
684 | return -EFAULT; | |
685 | ci = get_ci_by_dev (ndev); | |
686 | if (!ci) | |
687 | return -EINVAL; | |
688 | dev = create_chan (ndev, ci, &cp); | |
689 | if (!dev) | |
690 | return -EBUSY; | |
691 | ret = mkret (c4_new_chan (ci, cp.port, cp.channum, dev)); | |
692 | if (ret) | |
693 | { | |
50ee11fe BB |
694 | rtnl_unlock (); /* needed due to Ioctl calling sequence */ |
695 | unregister_hdlc_device (dev); | |
696 | rtnl_lock (); /* needed due to Ioctl calling sequence */ | |
697 | free_netdev (dev); | |
50ee11fe BB |
698 | } |
699 | return ret; | |
700 | } | |
701 | ||
702 | STATIC status_t | |
703 | do_get_chan_stats (struct net_device * ndev, void *data) | |
704 | { | |
705 | struct c4_chan_stats_wrap ccs; | |
706 | int ret; | |
707 | ||
708 | if (copy_from_user (&ccs, data, | |
709 | sizeof (struct c4_chan_stats_wrap))) | |
710 | return -EFAULT; | |
711 | switch (ret = mkret (c4_get_chan_stats (ccs.channum, &ccs.stats))) | |
712 | { | |
713 | case 0: | |
714 | break; | |
715 | default: | |
716 | return ret; | |
717 | } | |
718 | if (copy_to_user (data, &ccs, | |
719 | sizeof (struct c4_chan_stats_wrap))) | |
720 | return -EFAULT; | |
721 | return 0; | |
722 | } | |
723 | STATIC status_t | |
724 | do_set_loglevel (struct net_device * ndev, void *data) | |
725 | { | |
b8b73994 | 726 | unsigned int cxt1e1_log_level; |
50ee11fe | 727 | |
b8b73994 | 728 | if (copy_from_user (&cxt1e1_log_level, data, sizeof (int))) |
50ee11fe | 729 | return -EFAULT; |
b8b73994 | 730 | sbecom_set_loglevel (cxt1e1_log_level); |
50ee11fe BB |
731 | return 0; |
732 | } | |
733 | ||
734 | STATIC status_t | |
735 | do_deluser (struct net_device * ndev, int lockit) | |
736 | { | |
737 | if (ndev->flags & IFF_UP) | |
738 | return -EBUSY; | |
739 | ||
740 | { | |
741 | ci_t *ci; | |
742 | mch_t *ch; | |
743 | const struct c4_priv *priv; | |
744 | int channum; | |
745 | ||
50ee11fe | 746 | priv = (struct c4_priv *) dev_to_hdlc (ndev)->priv; |
50ee11fe BB |
747 | ci = priv->ci; |
748 | channum = priv->channum; | |
749 | ||
750 | ch = c4_find_chan (channum); | |
751 | if (ch == NULL) | |
752 | return -ENOENT; | |
753 | ch->user = 0; /* will be freed, below */ | |
754 | } | |
755 | ||
50ee11fe BB |
756 | if (lockit) |
757 | rtnl_unlock (); /* needed if Ioctl calling sequence */ | |
758 | unregister_hdlc_device (ndev); | |
759 | if (lockit) | |
760 | rtnl_lock (); /* needed if Ioctl calling sequence */ | |
761 | free_netdev (ndev); | |
50ee11fe BB |
762 | return 0; |
763 | } | |
764 | ||
765 | int | |
766 | do_del_chan (struct net_device * musycc_dev, void *data) | |
767 | { | |
768 | struct sbecom_chan_param cp; | |
769 | char buf[sizeof (CHANNAME) + 3]; | |
770 | struct net_device *dev; | |
771 | int ret; | |
772 | ||
773 | if (copy_from_user (&cp, data, | |
774 | sizeof (struct sbecom_chan_param))) | |
775 | return -EFAULT; | |
776 | sprintf (buf, CHANNAME "%d", cp.channum); | |
777 | if (!(dev = dev_get_by_name (&init_net, buf))) | |
778 | return -ENOENT; | |
779 | dev_put (dev); | |
780 | ret = do_deluser (dev, 1); | |
781 | if (ret) | |
782 | return ret; | |
783 | return c4_del_chan (cp.channum); | |
784 | } | |
785 | int c4_reset_board (void *); | |
786 | ||
787 | int | |
788 | do_reset (struct net_device * musycc_dev, void *data) | |
789 | { | |
790 | const struct c4_priv *priv; | |
791 | int i; | |
792 | ||
793 | for (i = 0; i < 128; i++) | |
794 | { | |
795 | struct net_device *ndev; | |
796 | char buf[sizeof (CHANNAME) + 3]; | |
797 | ||
798 | sprintf (buf, CHANNAME "%d", i); | |
799 | if (!(ndev = dev_get_by_name(&init_net, buf))) | |
800 | continue; | |
801 | priv = dev_to_hdlc (ndev)->priv; | |
802 | ||
803 | if ((unsigned long) (priv->ci) == | |
804 | (unsigned long) (netdev_priv(musycc_dev))) | |
805 | { | |
806 | ndev->flags &= ~IFF_UP; | |
807 | dev_put (ndev); | |
808 | netif_stop_queue (ndev); | |
809 | do_deluser (ndev, 1); | |
810 | } else | |
811 | dev_put (ndev); | |
812 | } | |
813 | return 0; | |
814 | } | |
815 | ||
816 | int | |
817 | do_reset_chan_stats (struct net_device * musycc_dev, void *data) | |
818 | { | |
819 | struct sbecom_chan_param cp; | |
820 | ||
821 | if (copy_from_user (&cp, data, | |
822 | sizeof (struct sbecom_chan_param))) | |
823 | return -EFAULT; | |
824 | return mkret (c4_del_chan_stats (cp.channum)); | |
825 | } | |
826 | ||
827 | STATIC status_t | |
828 | c4_ioctl (struct net_device * ndev, struct ifreq * ifr, int cmd) | |
829 | { | |
830 | ci_t *ci; | |
831 | void *data; | |
832 | int iocmd, iolen; | |
833 | status_t ret; | |
834 | static struct data | |
835 | { | |
836 | union | |
837 | { | |
838 | u_int8_t c; | |
839 | u_int32_t i; | |
840 | struct sbe_brd_info bip; | |
841 | struct sbe_drv_info dip; | |
842 | struct sbe_iid_info iip; | |
843 | struct sbe_brd_addr bap; | |
844 | struct sbecom_chan_stats stats; | |
845 | struct sbecom_chan_param param; | |
846 | struct temux_card_stats cards; | |
847 | struct sbecom_card_param cardp; | |
848 | struct sbecom_framer_param frp; | |
849 | } u; | |
850 | } arg; | |
851 | ||
852 | ||
853 | if (!capable (CAP_SYS_ADMIN)) | |
854 | return -EPERM; | |
855 | if (cmd != SIOCDEVPRIVATE + 15) | |
856 | return -EINVAL; | |
857 | if (!(ci = get_ci_by_dev (ndev))) | |
858 | return -EINVAL; | |
859 | if (ci->state != C_RUNNING) | |
860 | return -ENODEV; | |
861 | if (copy_from_user (&iocmd, ifr->ifr_data, sizeof (iocmd))) | |
862 | return -EFAULT; | |
863 | #if 0 | |
864 | if (copy_from_user (&len, ifr->ifr_data + sizeof (iocmd), sizeof (len))) | |
865 | return -EFAULT; | |
866 | #endif | |
867 | ||
868 | #if 0 | |
694a9807 | 869 | pr_info("c4_ioctl: iocmd %x, dir %x type %x nr %x iolen %d.\n", iocmd, |
50ee11fe BB |
870 | _IOC_DIR (iocmd), _IOC_TYPE (iocmd), _IOC_NR (iocmd), |
871 | _IOC_SIZE (iocmd)); | |
872 | #endif | |
873 | iolen = _IOC_SIZE (iocmd); | |
874 | data = ifr->ifr_data + sizeof (iocmd); | |
875 | if (copy_from_user (&arg, data, iolen)) | |
876 | return -EFAULT; | |
877 | ||
878 | ret = 0; | |
879 | switch (iocmd) | |
880 | { | |
881 | case SBE_IOC_PORT_GET: | |
694a9807 | 882 | //pr_info(">> SBE_IOC_PORT_GET Ioctl...\n"); |
50ee11fe BB |
883 | ret = do_get_port (ndev, data); |
884 | break; | |
885 | case SBE_IOC_PORT_SET: | |
694a9807 | 886 | //pr_info(">> SBE_IOC_PORT_SET Ioctl...\n"); |
50ee11fe BB |
887 | ret = do_set_port (ndev, data); |
888 | break; | |
889 | case SBE_IOC_CHAN_GET: | |
694a9807 | 890 | //pr_info(">> SBE_IOC_CHAN_GET Ioctl...\n"); |
50ee11fe BB |
891 | ret = do_get_chan (ndev, data); |
892 | break; | |
893 | case SBE_IOC_CHAN_SET: | |
694a9807 | 894 | //pr_info(">> SBE_IOC_CHAN_SET Ioctl...\n"); |
50ee11fe BB |
895 | ret = do_set_chan (ndev, data); |
896 | break; | |
897 | case C4_DEL_CHAN: | |
694a9807 | 898 | //pr_info(">> C4_DEL_CHAN Ioctl...\n"); |
50ee11fe BB |
899 | ret = do_del_chan (ndev, data); |
900 | break; | |
901 | case SBE_IOC_CHAN_NEW: | |
902 | ret = do_create_chan (ndev, data); | |
903 | break; | |
904 | case SBE_IOC_CHAN_GET_STAT: | |
905 | ret = do_get_chan_stats (ndev, data); | |
906 | break; | |
907 | case SBE_IOC_LOGLEVEL: | |
908 | ret = do_set_loglevel (ndev, data); | |
909 | break; | |
910 | case SBE_IOC_RESET_DEV: | |
911 | ret = do_reset (ndev, data); | |
912 | break; | |
913 | case SBE_IOC_CHAN_DEL_STAT: | |
914 | ret = do_reset_chan_stats (ndev, data); | |
915 | break; | |
916 | case C4_LOOP_PORT: | |
917 | ret = do_port_loop (ndev, data); | |
918 | break; | |
919 | case C4_RW_FRMR: | |
920 | ret = do_framer_rw (ndev, data); | |
921 | break; | |
922 | case C4_RW_MSYC: | |
923 | ret = do_musycc_rw (ndev, data); | |
924 | break; | |
925 | case C4_RW_PLD: | |
926 | ret = do_pld_rw (ndev, data); | |
927 | break; | |
928 | case SBE_IOC_IID_GET: | |
929 | ret = (iolen == sizeof (struct sbe_iid_info)) ? c4_get_iidinfo (ci, &arg.u.iip) : -EFAULT; | |
930 | if (ret == 0) /* no error, copy data */ | |
931 | if (copy_to_user (data, &arg, iolen)) | |
932 | return -EFAULT; | |
933 | break; | |
934 | default: | |
694a9807 | 935 | //pr_info(">> c4_ioctl: EINVAL - unknown iocmd <%x>\n", iocmd); |
50ee11fe BB |
936 | ret = -EINVAL; |
937 | break; | |
938 | } | |
939 | return mkret (ret); | |
940 | } | |
941 | ||
942 | static const struct net_device_ops c4_ops = { | |
943 | .ndo_open = void_open, | |
944 | .ndo_start_xmit = c4_linux_xmit, | |
945 | .ndo_do_ioctl = c4_ioctl, | |
946 | }; | |
947 | ||
948 | static void c4_setup(struct net_device *dev) | |
949 | { | |
950 | dev->type = ARPHRD_VOID; | |
951 | dev->netdev_ops = &c4_ops; | |
952 | } | |
953 | ||
954 | struct net_device *__init | |
955 | c4_add_dev (hdw_info_t * hi, int brdno, unsigned long f0, unsigned long f1, | |
956 | int irq0, int irq1) | |
957 | { | |
958 | struct net_device *ndev; | |
959 | ci_t *ci; | |
960 | ||
961 | ndev = alloc_netdev(sizeof(ci_t), SBE_IFACETMPL, c4_setup); | |
962 | if (!ndev) | |
963 | { | |
e6e4d05d | 964 | pr_warning("%s: no memory for struct net_device !\n", hi->devname); |
50ee11fe BB |
965 | error_flag = ENOMEM; |
966 | return 0; | |
967 | } | |
968 | ci = (ci_t *)(netdev_priv(ndev)); | |
969 | ndev->irq = irq0; | |
970 | ||
971 | ci->hdw_info = hi; | |
972 | ci->state = C_INIT; /* mark as hardware not available */ | |
973 | ci->next = c4_list; | |
974 | c4_list = ci; | |
975 | ci->brdno = ci->next ? ci->next->brdno + 1 : 0; | |
976 | ||
977 | if (CI == 0) | |
978 | CI = ci; /* DEBUG, only board 0 usage */ | |
979 | ||
980 | strcpy (ci->devname, hi->devname); | |
981 | ci->release = &pmcc4_OSSI_release[0]; | |
982 | ||
983 | /* tasklet */ | |
984 | #if defined(SBE_ISR_TASKLET) | |
985 | tasklet_init (&ci->ci_musycc_isr_tasklet, | |
986 | (void (*) (unsigned long)) musycc_intr_bh_tasklet, | |
987 | (unsigned long) ci); | |
988 | ||
989 | if (atomic_read (&ci->ci_musycc_isr_tasklet.count) == 0) | |
990 | tasklet_disable_nosync (&ci->ci_musycc_isr_tasklet); | |
991 | #elif defined(SBE_ISR_IMMEDIATE) | |
992 | ci->ci_musycc_isr_tq.routine = (void *) (unsigned long) musycc_intr_bh_tasklet; | |
993 | ci->ci_musycc_isr_tq.data = ci; | |
994 | #endif | |
995 | ||
996 | ||
997 | if (register_netdev (ndev) || | |
998 | (c4_init (ci, (u_char *) f0, (u_char *) f1) != SBE_DRVR_SUCCESS)) | |
999 | { | |
1000 | OS_kfree (netdev_priv(ndev)); | |
1001 | OS_kfree (ndev); | |
1002 | error_flag = ENODEV; | |
1003 | return 0; | |
1004 | } | |
1005 | /************************************************************* | |
1006 | * int request_irq(unsigned int irq, | |
1007 | * void (*handler)(int, void *, struct pt_regs *), | |
1008 | * unsigned long flags, const char *dev_name, void *dev_id); | |
1009 | * wherein: | |
1010 | * irq -> The interrupt number that is being requested. | |
1011 | * handler -> Pointer to handling function being installed. | |
1012 | * flags -> A bit mask of options related to interrupt management. | |
1013 | * dev_name -> String used in /proc/interrupts to show owner of interrupt. | |
1014 | * dev_id -> Pointer (for shared interrupt lines) to point to its own | |
1015 | * private data area (to identify which device is interrupting). | |
1016 | * | |
1017 | * extern void free_irq(unsigned int irq, void *dev_id); | |
1018 | **************************************************************/ | |
1019 | ||
1020 | if (request_irq (irq0, &c4_linux_interrupt, | |
50ee11fe | 1021 | IRQF_SHARED, |
50ee11fe BB |
1022 | ndev->name, ndev)) |
1023 | { | |
e6e4d05d | 1024 | pr_warning("%s: MUSYCC could not get irq: %d\n", ndev->name, irq0); |
50ee11fe BB |
1025 | unregister_netdev (ndev); |
1026 | OS_kfree (netdev_priv(ndev)); | |
1027 | OS_kfree (ndev); | |
1028 | error_flag = EIO; | |
1029 | return 0; | |
1030 | } | |
1031 | #ifdef CONFIG_SBE_PMCC4_NCOMM | |
1032 | if (request_irq (irq1, &c4_ebus_interrupt, IRQF_SHARED, ndev->name, ndev)) | |
1033 | { | |
e6e4d05d | 1034 | pr_warning("%s: EBUS could not get irq: %d\n", hi->devname, irq1); |
50ee11fe BB |
1035 | unregister_netdev (ndev); |
1036 | free_irq (irq0, ndev); | |
d87d909a | 1037 | OS_kfree (netdev_priv(ndev)); |
50ee11fe BB |
1038 | OS_kfree (ndev); |
1039 | error_flag = EIO; | |
1040 | return 0; | |
1041 | } | |
1042 | #endif | |
1043 | ||
1044 | /* setup board identification information */ | |
1045 | ||
1046 | { | |
1047 | u_int32_t tmp; | |
1048 | ||
1049 | hdw_sn_get (hi, brdno); /* also sets PROM format type (promfmt) | |
1050 | * for later usage */ | |
1051 | ||
1052 | switch (hi->promfmt) | |
1053 | { | |
1054 | case PROM_FORMAT_TYPE1: | |
1055 | memcpy (ndev->dev_addr, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); | |
1056 | memcpy (&tmp, (FLD_TYPE1 *) (hi->mfg_info.pft1.Id), 4); /* unaligned data | |
1057 | * acquisition */ | |
1058 | ci->brd_id = cpu_to_be32 (tmp); | |
1059 | break; | |
1060 | case PROM_FORMAT_TYPE2: | |
1061 | memcpy (ndev->dev_addr, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); | |
1062 | memcpy (&tmp, (FLD_TYPE2 *) (hi->mfg_info.pft2.Id), 4); /* unaligned data | |
1063 | * acquisition */ | |
1064 | ci->brd_id = cpu_to_be32 (tmp); | |
1065 | break; | |
1066 | default: | |
1067 | ci->brd_id = 0; | |
1068 | memset (ndev->dev_addr, 0, 6); | |
1069 | break; | |
1070 | } | |
1071 | ||
1072 | #if 1 | |
1073 | sbeid_set_hdwbid (ci); /* requires bid to be preset */ | |
1074 | #else | |
1075 | sbeid_set_bdtype (ci); /* requires hdw_bid to be preset */ | |
1076 | #endif | |
1077 | ||
1078 | } | |
1079 | ||
1080 | #ifdef CONFIG_PROC_FS | |
1081 | sbecom_proc_brd_init (ci); | |
1082 | #endif | |
1083 | #if defined(SBE_ISR_TASKLET) | |
1084 | tasklet_enable (&ci->ci_musycc_isr_tasklet); | |
1085 | #endif | |
1086 | ||
1087 | ||
1088 | if ((error_flag = c4_init2 (ci)) != SBE_DRVR_SUCCESS) | |
1089 | { | |
1090 | #ifdef CONFIG_PROC_FS | |
1091 | sbecom_proc_brd_cleanup (ci); | |
1092 | #endif | |
1093 | unregister_netdev (ndev); | |
1094 | free_irq (irq1, ndev); | |
1095 | free_irq (irq0, ndev); | |
1096 | OS_kfree (netdev_priv(ndev)); | |
1097 | OS_kfree (ndev); | |
1098 | return 0; /* failure, error_flag is set */ | |
1099 | } | |
1100 | return ndev; | |
1101 | } | |
1102 | ||
1103 | STATIC int __init | |
1104 | c4_mod_init (void) | |
1105 | { | |
1106 | int rtn; | |
1107 | ||
e6e4d05d | 1108 | pr_warning("%s\n", pmcc4_OSSI_release); |
50ee11fe BB |
1109 | if ((rtn = c4hw_attach_all ())) |
1110 | return -rtn; /* installation failure - see system log */ | |
1111 | ||
1112 | /* housekeeping notifications */ | |
b8b73994 GKH |
1113 | if (cxt1e1_log_level != log_level_default) |
1114 | pr_info("NOTE: driver parameter <cxt1e1_log_level> changed from default %d to %d.\n", | |
1115 | log_level_default, cxt1e1_log_level); | |
c694ed85 BB |
1116 | if (cxt1e1_max_mru != max_mru_default) |
1117 | pr_info("NOTE: driver parameter <cxt1e1_max_mru> changed from default %d to %d.\n", | |
1118 | max_mru_default, cxt1e1_max_mru); | |
1119 | if (cxt1e1_max_mtu != max_mtu_default) | |
1120 | pr_info("NOTE: driver parameter <cxt1e1_max_mtu> changed from default %d to %d.\n", | |
1121 | max_mtu_default, cxt1e1_max_mtu); | |
50ee11fe BB |
1122 | if (max_rxdesc_used != max_rxdesc_default) |
1123 | { | |
1124 | if (max_rxdesc_used > 2000) | |
1125 | max_rxdesc_used = 2000; /* out-of-bounds reset */ | |
e6e4d05d JP |
1126 | pr_info("NOTE: driver parameter <max_rxdesc_used> changed from default %d to %d.\n", |
1127 | max_rxdesc_default, max_rxdesc_used); | |
50ee11fe BB |
1128 | } |
1129 | if (max_txdesc_used != max_txdesc_default) | |
1130 | { | |
1131 | if (max_txdesc_used > 1000) | |
1132 | max_txdesc_used = 1000; /* out-of-bounds reset */ | |
e6e4d05d JP |
1133 | pr_info("NOTE: driver parameter <max_txdesc_used> changed from default %d to %d.\n", |
1134 | max_txdesc_default, max_txdesc_used); | |
50ee11fe BB |
1135 | } |
1136 | return 0; /* installation success */ | |
1137 | } | |
1138 | ||
1139 | ||
1140 | /* | |
1141 | * find any still allocated hdlc registrations and unregister via call to | |
1142 | * do_deluser() | |
1143 | */ | |
1144 | ||
1145 | STATIC void __exit | |
1146 | cleanup_hdlc (void) | |
1147 | { | |
1148 | hdw_info_t *hi; | |
1149 | ci_t *ci; | |
1150 | struct net_device *ndev; | |
1151 | int i, j, k; | |
1152 | ||
1153 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
1154 | { | |
1155 | if (hi->ndev) /* a board has been attached */ | |
1156 | { | |
1157 | ci = (ci_t *)(netdev_priv(hi->ndev)); | |
1158 | for (j = 0; j < ci->max_port; j++) | |
1159 | for (k = 0; k < MUSYCC_NCHANS; k++) | |
1160 | if ((ndev = ci->port[j].chan[k]->user)) | |
1161 | { | |
1162 | do_deluser (ndev, 0); | |
1163 | } | |
1164 | } | |
1165 | } | |
1166 | } | |
1167 | ||
1168 | ||
1169 | STATIC void __exit | |
1170 | c4_mod_remove (void) | |
1171 | { | |
21aac2c9 DN |
1172 | cleanup_hdlc(); /* delete any missed channels */ |
1173 | cleanup_devs(); | |
1174 | c4_cleanup(); | |
1175 | cleanup_ioremap(); | |
1176 | pr_info("SBE - driver removed.\n"); | |
50ee11fe BB |
1177 | } |
1178 | ||
1179 | module_init (c4_mod_init); | |
1180 | module_exit (c4_mod_remove); | |
1181 | ||
50ee11fe BB |
1182 | MODULE_AUTHOR ("SBE Technical Services <support@sbei.com>"); |
1183 | MODULE_DESCRIPTION ("wanPCI-CxT1E1 Generic HDLC WAN Driver module"); | |
1184 | #ifdef MODULE_LICENSE | |
1185 | MODULE_LICENSE ("GPL"); | |
1186 | #endif | |
1187 | ||
1188 | /*** End-of-File ***/ |