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