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 | 146 | char * |
ee1803cf | 147 | get_hdlc_name (hdlc_device *hdlc) |
50ee11fe BB |
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 | |
ee1803cf | 188 | c4_wk_chan_restart (mch_t *ch) |
50ee11fe BB |
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 | |
ee1803cf | 206 | c4_wk_chan_init (mpi_t *pi, mch_t *ch) |
50ee11fe BB |
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 | |
ee1803cf | 221 | c4_wq_port_init (mpi_t *pi) |
50ee11fe BB |
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 | |
ee1803cf | 244 | c4_wq_port_cleanup (mpi_t *pi) |
50ee11fe BB |
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 | |
ee1803cf | 281 | void_open (struct net_device *ndev) |
50ee11fe | 282 | { |
694a9807 | 283 | pr_info("%s: trying to open master device !\n", ndev->name); |
50ee11fe BB |
284 | return -1; |
285 | } | |
286 | ||
287 | ||
50ee11fe | 288 | STATIC int |
ee1803cf | 289 | chan_open (struct net_device *ndev) |
50ee11fe BB |
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 | 308 | STATIC int |
ee1803cf | 309 | chan_close (struct net_device *ndev) |
50ee11fe BB |
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 | 322 | STATIC int |
ee1803cf | 323 | chan_dev_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) |
50ee11fe BB |
324 | { |
325 | return hdlc_ioctl (dev, ifr, cmd); | |
326 | } | |
327 | ||
328 | ||
329 | STATIC int | |
ee1803cf | 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 * | |
ee1803cf | 338 | chan_get_stats (struct net_device *ndev) |
50ee11fe BB |
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 * | |
ee1803cf | 391 | get_ci_by_dev (struct net_device *ndev) |
50ee11fe BB |
392 | { |
393 | return (ci_t *)(netdev_priv(ndev)); | |
394 | } | |
395 | ||
396 | ||
50ee11fe | 397 | STATIC int |
ee1803cf | 398 | c4_linux_xmit (struct sk_buff *skb, struct net_device *ndev) |
50ee11fe BB |
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 * | |
ee1803cf DG |
420 | create_chan (struct net_device *ndev, ci_t *ci, |
421 | struct sbecom_chan_param *cp) | |
50ee11fe BB |
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 | |
ee1803cf | 513 | do_get_port (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 538 | do_set_port (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 560 | do_port_loop (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 575 | do_framer_rw (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 596 | do_pld_rw (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 617 | do_musycc_rw (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 637 | do_get_chan (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 655 | do_set_chan (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 676 | do_create_chan (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 703 | do_get_chan_stats (struct net_device *ndev, void *data) |
50ee11fe BB |
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 | |
ee1803cf | 724 | do_set_loglevel (struct net_device *ndev, void *data) |
50ee11fe | 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 | |
ee1803cf | 735 | do_deluser (struct net_device *ndev, int lockit) |
50ee11fe BB |
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 | |
ee1803cf | 766 | do_del_chan (struct net_device *musycc_dev, void *data) |
50ee11fe BB |
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; | |
96a8d14e DC |
776 | if (cp.channum > 999) |
777 | return -EINVAL; | |
778 | snprintf (buf, sizeof(buf), CHANNAME "%d", cp.channum); | |
50ee11fe BB |
779 | if (!(dev = dev_get_by_name (&init_net, buf))) |
780 | return -ENOENT; | |
781 | dev_put (dev); | |
782 | ret = do_deluser (dev, 1); | |
783 | if (ret) | |
784 | return ret; | |
785 | return c4_del_chan (cp.channum); | |
786 | } | |
787 | int c4_reset_board (void *); | |
788 | ||
789 | int | |
ee1803cf | 790 | do_reset (struct net_device *musycc_dev, void *data) |
50ee11fe BB |
791 | { |
792 | const struct c4_priv *priv; | |
793 | int i; | |
794 | ||
795 | for (i = 0; i < 128; i++) | |
796 | { | |
797 | struct net_device *ndev; | |
798 | char buf[sizeof (CHANNAME) + 3]; | |
799 | ||
800 | sprintf (buf, CHANNAME "%d", i); | |
801 | if (!(ndev = dev_get_by_name(&init_net, buf))) | |
802 | continue; | |
803 | priv = dev_to_hdlc (ndev)->priv; | |
804 | ||
805 | if ((unsigned long) (priv->ci) == | |
806 | (unsigned long) (netdev_priv(musycc_dev))) | |
807 | { | |
808 | ndev->flags &= ~IFF_UP; | |
809 | dev_put (ndev); | |
810 | netif_stop_queue (ndev); | |
811 | do_deluser (ndev, 1); | |
812 | } else | |
813 | dev_put (ndev); | |
814 | } | |
815 | return 0; | |
816 | } | |
817 | ||
818 | int | |
ee1803cf | 819 | do_reset_chan_stats (struct net_device *musycc_dev, void *data) |
50ee11fe BB |
820 | { |
821 | struct sbecom_chan_param cp; | |
822 | ||
823 | if (copy_from_user (&cp, data, | |
824 | sizeof (struct sbecom_chan_param))) | |
825 | return -EFAULT; | |
826 | return mkret (c4_del_chan_stats (cp.channum)); | |
827 | } | |
828 | ||
829 | STATIC status_t | |
ee1803cf | 830 | c4_ioctl (struct net_device *ndev, struct ifreq *ifr, int cmd) |
50ee11fe BB |
831 | { |
832 | ci_t *ci; | |
833 | void *data; | |
834 | int iocmd, iolen; | |
835 | status_t ret; | |
836 | static struct data | |
837 | { | |
838 | union | |
839 | { | |
840 | u_int8_t c; | |
841 | u_int32_t i; | |
842 | struct sbe_brd_info bip; | |
843 | struct sbe_drv_info dip; | |
844 | struct sbe_iid_info iip; | |
845 | struct sbe_brd_addr bap; | |
846 | struct sbecom_chan_stats stats; | |
847 | struct sbecom_chan_param param; | |
848 | struct temux_card_stats cards; | |
849 | struct sbecom_card_param cardp; | |
850 | struct sbecom_framer_param frp; | |
851 | } u; | |
852 | } arg; | |
853 | ||
854 | ||
855 | if (!capable (CAP_SYS_ADMIN)) | |
856 | return -EPERM; | |
857 | if (cmd != SIOCDEVPRIVATE + 15) | |
858 | return -EINVAL; | |
859 | if (!(ci = get_ci_by_dev (ndev))) | |
860 | return -EINVAL; | |
861 | if (ci->state != C_RUNNING) | |
862 | return -ENODEV; | |
863 | if (copy_from_user (&iocmd, ifr->ifr_data, sizeof (iocmd))) | |
864 | return -EFAULT; | |
865 | #if 0 | |
866 | if (copy_from_user (&len, ifr->ifr_data + sizeof (iocmd), sizeof (len))) | |
867 | return -EFAULT; | |
868 | #endif | |
869 | ||
870 | #if 0 | |
694a9807 | 871 | pr_info("c4_ioctl: iocmd %x, dir %x type %x nr %x iolen %d.\n", iocmd, |
50ee11fe BB |
872 | _IOC_DIR (iocmd), _IOC_TYPE (iocmd), _IOC_NR (iocmd), |
873 | _IOC_SIZE (iocmd)); | |
874 | #endif | |
875 | iolen = _IOC_SIZE (iocmd); | |
876 | data = ifr->ifr_data + sizeof (iocmd); | |
877 | if (copy_from_user (&arg, data, iolen)) | |
878 | return -EFAULT; | |
879 | ||
880 | ret = 0; | |
881 | switch (iocmd) | |
882 | { | |
883 | case SBE_IOC_PORT_GET: | |
694a9807 | 884 | //pr_info(">> SBE_IOC_PORT_GET Ioctl...\n"); |
50ee11fe BB |
885 | ret = do_get_port (ndev, data); |
886 | break; | |
887 | case SBE_IOC_PORT_SET: | |
694a9807 | 888 | //pr_info(">> SBE_IOC_PORT_SET Ioctl...\n"); |
50ee11fe BB |
889 | ret = do_set_port (ndev, data); |
890 | break; | |
891 | case SBE_IOC_CHAN_GET: | |
694a9807 | 892 | //pr_info(">> SBE_IOC_CHAN_GET Ioctl...\n"); |
50ee11fe BB |
893 | ret = do_get_chan (ndev, data); |
894 | break; | |
895 | case SBE_IOC_CHAN_SET: | |
694a9807 | 896 | //pr_info(">> SBE_IOC_CHAN_SET Ioctl...\n"); |
50ee11fe BB |
897 | ret = do_set_chan (ndev, data); |
898 | break; | |
899 | case C4_DEL_CHAN: | |
694a9807 | 900 | //pr_info(">> C4_DEL_CHAN Ioctl...\n"); |
50ee11fe BB |
901 | ret = do_del_chan (ndev, data); |
902 | break; | |
903 | case SBE_IOC_CHAN_NEW: | |
904 | ret = do_create_chan (ndev, data); | |
905 | break; | |
906 | case SBE_IOC_CHAN_GET_STAT: | |
907 | ret = do_get_chan_stats (ndev, data); | |
908 | break; | |
909 | case SBE_IOC_LOGLEVEL: | |
910 | ret = do_set_loglevel (ndev, data); | |
911 | break; | |
912 | case SBE_IOC_RESET_DEV: | |
913 | ret = do_reset (ndev, data); | |
914 | break; | |
915 | case SBE_IOC_CHAN_DEL_STAT: | |
916 | ret = do_reset_chan_stats (ndev, data); | |
917 | break; | |
918 | case C4_LOOP_PORT: | |
919 | ret = do_port_loop (ndev, data); | |
920 | break; | |
921 | case C4_RW_FRMR: | |
922 | ret = do_framer_rw (ndev, data); | |
923 | break; | |
924 | case C4_RW_MSYC: | |
925 | ret = do_musycc_rw (ndev, data); | |
926 | break; | |
927 | case C4_RW_PLD: | |
928 | ret = do_pld_rw (ndev, data); | |
929 | break; | |
930 | case SBE_IOC_IID_GET: | |
931 | ret = (iolen == sizeof (struct sbe_iid_info)) ? c4_get_iidinfo (ci, &arg.u.iip) : -EFAULT; | |
932 | if (ret == 0) /* no error, copy data */ | |
933 | if (copy_to_user (data, &arg, iolen)) | |
934 | return -EFAULT; | |
935 | break; | |
936 | default: | |
694a9807 | 937 | //pr_info(">> c4_ioctl: EINVAL - unknown iocmd <%x>\n", iocmd); |
50ee11fe BB |
938 | ret = -EINVAL; |
939 | break; | |
940 | } | |
941 | return mkret (ret); | |
942 | } | |
943 | ||
944 | static const struct net_device_ops c4_ops = { | |
945 | .ndo_open = void_open, | |
946 | .ndo_start_xmit = c4_linux_xmit, | |
947 | .ndo_do_ioctl = c4_ioctl, | |
948 | }; | |
949 | ||
950 | static void c4_setup(struct net_device *dev) | |
951 | { | |
952 | dev->type = ARPHRD_VOID; | |
953 | dev->netdev_ops = &c4_ops; | |
954 | } | |
955 | ||
956 | struct net_device *__init | |
ee1803cf | 957 | c4_add_dev (hdw_info_t *hi, int brdno, unsigned long f0, unsigned long f1, |
50ee11fe BB |
958 | int irq0, int irq1) |
959 | { | |
960 | struct net_device *ndev; | |
961 | ci_t *ci; | |
962 | ||
963 | ndev = alloc_netdev(sizeof(ci_t), SBE_IFACETMPL, c4_setup); | |
964 | if (!ndev) | |
965 | { | |
e6e4d05d | 966 | pr_warning("%s: no memory for struct net_device !\n", hi->devname); |
50ee11fe BB |
967 | error_flag = ENOMEM; |
968 | return 0; | |
969 | } | |
970 | ci = (ci_t *)(netdev_priv(ndev)); | |
971 | ndev->irq = irq0; | |
972 | ||
973 | ci->hdw_info = hi; | |
974 | ci->state = C_INIT; /* mark as hardware not available */ | |
975 | ci->next = c4_list; | |
976 | c4_list = ci; | |
977 | ci->brdno = ci->next ? ci->next->brdno + 1 : 0; | |
978 | ||
979 | if (CI == 0) | |
980 | CI = ci; /* DEBUG, only board 0 usage */ | |
981 | ||
982 | strcpy (ci->devname, hi->devname); | |
983 | ci->release = &pmcc4_OSSI_release[0]; | |
984 | ||
985 | /* tasklet */ | |
986 | #if defined(SBE_ISR_TASKLET) | |
987 | tasklet_init (&ci->ci_musycc_isr_tasklet, | |
988 | (void (*) (unsigned long)) musycc_intr_bh_tasklet, | |
989 | (unsigned long) ci); | |
990 | ||
991 | if (atomic_read (&ci->ci_musycc_isr_tasklet.count) == 0) | |
992 | tasklet_disable_nosync (&ci->ci_musycc_isr_tasklet); | |
993 | #elif defined(SBE_ISR_IMMEDIATE) | |
994 | ci->ci_musycc_isr_tq.routine = (void *) (unsigned long) musycc_intr_bh_tasklet; | |
995 | ci->ci_musycc_isr_tq.data = ci; | |
996 | #endif | |
997 | ||
998 | ||
999 | if (register_netdev (ndev) || | |
1000 | (c4_init (ci, (u_char *) f0, (u_char *) f1) != SBE_DRVR_SUCCESS)) | |
1001 | { | |
1002 | OS_kfree (netdev_priv(ndev)); | |
1003 | OS_kfree (ndev); | |
1004 | error_flag = ENODEV; | |
1005 | return 0; | |
1006 | } | |
1007 | /************************************************************* | |
1008 | * int request_irq(unsigned int irq, | |
1009 | * void (*handler)(int, void *, struct pt_regs *), | |
1010 | * unsigned long flags, const char *dev_name, void *dev_id); | |
1011 | * wherein: | |
1012 | * irq -> The interrupt number that is being requested. | |
1013 | * handler -> Pointer to handling function being installed. | |
1014 | * flags -> A bit mask of options related to interrupt management. | |
1015 | * dev_name -> String used in /proc/interrupts to show owner of interrupt. | |
1016 | * dev_id -> Pointer (for shared interrupt lines) to point to its own | |
1017 | * private data area (to identify which device is interrupting). | |
1018 | * | |
1019 | * extern void free_irq(unsigned int irq, void *dev_id); | |
1020 | **************************************************************/ | |
1021 | ||
1022 | if (request_irq (irq0, &c4_linux_interrupt, | |
50ee11fe | 1023 | IRQF_SHARED, |
50ee11fe BB |
1024 | ndev->name, ndev)) |
1025 | { | |
e6e4d05d | 1026 | pr_warning("%s: MUSYCC could not get irq: %d\n", ndev->name, irq0); |
50ee11fe BB |
1027 | unregister_netdev (ndev); |
1028 | OS_kfree (netdev_priv(ndev)); | |
1029 | OS_kfree (ndev); | |
1030 | error_flag = EIO; | |
1031 | return 0; | |
1032 | } | |
1033 | #ifdef CONFIG_SBE_PMCC4_NCOMM | |
1034 | if (request_irq (irq1, &c4_ebus_interrupt, IRQF_SHARED, ndev->name, ndev)) | |
1035 | { | |
e6e4d05d | 1036 | pr_warning("%s: EBUS could not get irq: %d\n", hi->devname, irq1); |
50ee11fe BB |
1037 | unregister_netdev (ndev); |
1038 | free_irq (irq0, ndev); | |
d87d909a | 1039 | OS_kfree (netdev_priv(ndev)); |
50ee11fe BB |
1040 | OS_kfree (ndev); |
1041 | error_flag = EIO; | |
1042 | return 0; | |
1043 | } | |
1044 | #endif | |
1045 | ||
1046 | /* setup board identification information */ | |
1047 | ||
1048 | { | |
1049 | u_int32_t tmp; | |
1050 | ||
1051 | hdw_sn_get (hi, brdno); /* also sets PROM format type (promfmt) | |
1052 | * for later usage */ | |
1053 | ||
1054 | switch (hi->promfmt) | |
1055 | { | |
1056 | case PROM_FORMAT_TYPE1: | |
1057 | memcpy (ndev->dev_addr, (FLD_TYPE1 *) (hi->mfg_info.pft1.Serial), 6); | |
1058 | memcpy (&tmp, (FLD_TYPE1 *) (hi->mfg_info.pft1.Id), 4); /* unaligned data | |
1059 | * acquisition */ | |
1060 | ci->brd_id = cpu_to_be32 (tmp); | |
1061 | break; | |
1062 | case PROM_FORMAT_TYPE2: | |
1063 | memcpy (ndev->dev_addr, (FLD_TYPE2 *) (hi->mfg_info.pft2.Serial), 6); | |
1064 | memcpy (&tmp, (FLD_TYPE2 *) (hi->mfg_info.pft2.Id), 4); /* unaligned data | |
1065 | * acquisition */ | |
1066 | ci->brd_id = cpu_to_be32 (tmp); | |
1067 | break; | |
1068 | default: | |
1069 | ci->brd_id = 0; | |
1070 | memset (ndev->dev_addr, 0, 6); | |
1071 | break; | |
1072 | } | |
1073 | ||
1074 | #if 1 | |
1075 | sbeid_set_hdwbid (ci); /* requires bid to be preset */ | |
1076 | #else | |
1077 | sbeid_set_bdtype (ci); /* requires hdw_bid to be preset */ | |
1078 | #endif | |
1079 | ||
1080 | } | |
1081 | ||
1082 | #ifdef CONFIG_PROC_FS | |
1083 | sbecom_proc_brd_init (ci); | |
1084 | #endif | |
1085 | #if defined(SBE_ISR_TASKLET) | |
1086 | tasklet_enable (&ci->ci_musycc_isr_tasklet); | |
1087 | #endif | |
1088 | ||
1089 | ||
1090 | if ((error_flag = c4_init2 (ci)) != SBE_DRVR_SUCCESS) | |
1091 | { | |
1092 | #ifdef CONFIG_PROC_FS | |
1093 | sbecom_proc_brd_cleanup (ci); | |
1094 | #endif | |
1095 | unregister_netdev (ndev); | |
1096 | free_irq (irq1, ndev); | |
1097 | free_irq (irq0, ndev); | |
1098 | OS_kfree (netdev_priv(ndev)); | |
1099 | OS_kfree (ndev); | |
1100 | return 0; /* failure, error_flag is set */ | |
1101 | } | |
1102 | return ndev; | |
1103 | } | |
1104 | ||
1105 | STATIC int __init | |
1106 | c4_mod_init (void) | |
1107 | { | |
1108 | int rtn; | |
1109 | ||
e6e4d05d | 1110 | pr_warning("%s\n", pmcc4_OSSI_release); |
50ee11fe BB |
1111 | if ((rtn = c4hw_attach_all ())) |
1112 | return -rtn; /* installation failure - see system log */ | |
1113 | ||
1114 | /* housekeeping notifications */ | |
b8b73994 GKH |
1115 | if (cxt1e1_log_level != log_level_default) |
1116 | pr_info("NOTE: driver parameter <cxt1e1_log_level> changed from default %d to %d.\n", | |
1117 | log_level_default, cxt1e1_log_level); | |
c694ed85 BB |
1118 | if (cxt1e1_max_mru != max_mru_default) |
1119 | pr_info("NOTE: driver parameter <cxt1e1_max_mru> changed from default %d to %d.\n", | |
1120 | max_mru_default, cxt1e1_max_mru); | |
1121 | if (cxt1e1_max_mtu != max_mtu_default) | |
1122 | pr_info("NOTE: driver parameter <cxt1e1_max_mtu> changed from default %d to %d.\n", | |
1123 | max_mtu_default, cxt1e1_max_mtu); | |
50ee11fe BB |
1124 | if (max_rxdesc_used != max_rxdesc_default) |
1125 | { | |
1126 | if (max_rxdesc_used > 2000) | |
1127 | max_rxdesc_used = 2000; /* out-of-bounds reset */ | |
e6e4d05d JP |
1128 | pr_info("NOTE: driver parameter <max_rxdesc_used> changed from default %d to %d.\n", |
1129 | max_rxdesc_default, max_rxdesc_used); | |
50ee11fe BB |
1130 | } |
1131 | if (max_txdesc_used != max_txdesc_default) | |
1132 | { | |
1133 | if (max_txdesc_used > 1000) | |
1134 | max_txdesc_used = 1000; /* out-of-bounds reset */ | |
e6e4d05d JP |
1135 | pr_info("NOTE: driver parameter <max_txdesc_used> changed from default %d to %d.\n", |
1136 | max_txdesc_default, max_txdesc_used); | |
50ee11fe BB |
1137 | } |
1138 | return 0; /* installation success */ | |
1139 | } | |
1140 | ||
1141 | ||
1142 | /* | |
1143 | * find any still allocated hdlc registrations and unregister via call to | |
1144 | * do_deluser() | |
1145 | */ | |
1146 | ||
1147 | STATIC void __exit | |
1148 | cleanup_hdlc (void) | |
1149 | { | |
1150 | hdw_info_t *hi; | |
1151 | ci_t *ci; | |
1152 | struct net_device *ndev; | |
1153 | int i, j, k; | |
1154 | ||
1155 | for (i = 0, hi = hdw_info; i < MAX_BOARDS; i++, hi++) | |
1156 | { | |
1157 | if (hi->ndev) /* a board has been attached */ | |
1158 | { | |
1159 | ci = (ci_t *)(netdev_priv(hi->ndev)); | |
1160 | for (j = 0; j < ci->max_port; j++) | |
1161 | for (k = 0; k < MUSYCC_NCHANS; k++) | |
1162 | if ((ndev = ci->port[j].chan[k]->user)) | |
1163 | { | |
1164 | do_deluser (ndev, 0); | |
1165 | } | |
1166 | } | |
1167 | } | |
1168 | } | |
1169 | ||
1170 | ||
1171 | STATIC void __exit | |
1172 | c4_mod_remove (void) | |
1173 | { | |
21aac2c9 DN |
1174 | cleanup_hdlc(); /* delete any missed channels */ |
1175 | cleanup_devs(); | |
1176 | c4_cleanup(); | |
1177 | cleanup_ioremap(); | |
1178 | pr_info("SBE - driver removed.\n"); | |
50ee11fe BB |
1179 | } |
1180 | ||
1181 | module_init (c4_mod_init); | |
1182 | module_exit (c4_mod_remove); | |
1183 | ||
50ee11fe BB |
1184 | MODULE_AUTHOR ("SBE Technical Services <support@sbei.com>"); |
1185 | MODULE_DESCRIPTION ("wanPCI-CxT1E1 Generic HDLC WAN Driver module"); | |
1186 | #ifdef MODULE_LICENSE | |
1187 | MODULE_LICENSE ("GPL"); | |
1188 | #endif | |
1189 | ||
1190 | /*** End-of-File ***/ |