| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*****************************************************************************/ | 
|  | 2 |  | 
|  | 3 | /* | 
|  | 4 | *	baycom_epp.c  -- baycom epp radio modem driver. | 
|  | 5 | * | 
|  | 6 | *	Copyright (C) 1998-2000 | 
|  | 7 | *          Thomas Sailer (sailer@ife.ee.ethz.ch) | 
|  | 8 | * | 
|  | 9 | *	This program is free software; you can redistribute it and/or modify | 
|  | 10 | *	it under the terms of the GNU General Public License as published by | 
|  | 11 | *	the Free Software Foundation; either version 2 of the License, or | 
|  | 12 | *	(at your option) any later version. | 
|  | 13 | * | 
|  | 14 | *	This program is distributed in the hope that it will be useful, | 
|  | 15 | *	but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 16 | *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 17 | *	GNU General Public License for more details. | 
|  | 18 | * | 
|  | 19 | *	You should have received a copy of the GNU General Public License | 
|  | 20 | *	along with this program; if not, write to the Free Software | 
|  | 21 | *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | 22 | * | 
|  | 23 | *  Please note that the GPL allows you to use the driver, NOT the radio. | 
|  | 24 | *  In order to use the radio, you need a license from the communications | 
|  | 25 | *  authority of your country. | 
|  | 26 | * | 
|  | 27 | * | 
|  | 28 | *  History: | 
|  | 29 | *   0.1  xx.xx.1998  Initial version by Matthias Welwarsky (dg2fef) | 
|  | 30 | *   0.2  21.04.1998  Massive rework by Thomas Sailer | 
|  | 31 | *                    Integrated FPGA EPP modem configuration routines | 
|  | 32 | *   0.3  11.05.1998  Took FPGA config out and moved it into a separate program | 
|  | 33 | *   0.4  26.07.1999  Adapted to new lowlevel parport driver interface | 
|  | 34 | *   0.5  03.08.1999  adapt to Linus' new __setup/__initcall | 
|  | 35 | *                    removed some pre-2.2 kernel compatibility cruft | 
|  | 36 | *   0.6  10.08.1999  Check if parport can do SPP and is safe to access during interrupt contexts | 
|  | 37 | *   0.7  12.02.2000  adapted to softnet driver interface | 
|  | 38 | * | 
|  | 39 | */ | 
|  | 40 |  | 
|  | 41 | /*****************************************************************************/ | 
|  | 42 |  | 
| Ralf Baechle | c4bc7ee | 2005-09-12 14:19:26 -0700 | [diff] [blame] | 43 | #include <linux/crc-ccitt.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 | #include <linux/module.h> | 
|  | 45 | #include <linux/kernel.h> | 
|  | 46 | #include <linux/init.h> | 
|  | 47 | #include <linux/string.h> | 
|  | 48 | #include <linux/workqueue.h> | 
|  | 49 | #include <linux/fs.h> | 
|  | 50 | #include <linux/parport.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 | #include <linux/if_arp.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 52 | #include <linux/hdlcdrv.h> | 
|  | 53 | #include <linux/baycom.h> | 
| Marcelo Feitoza Parisi | cd8749b | 2005-07-15 11:16:42 +0100 | [diff] [blame] | 54 | #include <linux/jiffies.h> | 
| Ralf Baechle | 8b5b467 | 2007-02-16 11:55:33 +0000 | [diff] [blame] | 55 | #include <linux/random.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | #include <net/ax25.h> | 
| Ralf Baechle | c4bc7ee | 2005-09-12 14:19:26 -0700 | [diff] [blame] | 57 | #include <asm/uaccess.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 |  | 
|  | 59 | /* --------------------------------------------------------------------- */ | 
|  | 60 |  | 
|  | 61 | #define BAYCOM_DEBUG | 
|  | 62 | #define BAYCOM_MAGIC 19730510 | 
|  | 63 |  | 
|  | 64 | /* --------------------------------------------------------------------- */ | 
|  | 65 |  | 
|  | 66 | static const char paranoia_str[] = KERN_ERR | 
|  | 67 | "baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; | 
|  | 68 |  | 
|  | 69 | static const char bc_drvname[] = "baycom_epp"; | 
|  | 70 | static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" | 
|  | 71 | KERN_INFO "baycom_epp: version 0.7 compiled " __TIME__ " " __DATE__ "\n"; | 
|  | 72 |  | 
|  | 73 | /* --------------------------------------------------------------------- */ | 
|  | 74 |  | 
|  | 75 | #define NR_PORTS 4 | 
|  | 76 |  | 
|  | 77 | static struct net_device *baycom_device[NR_PORTS]; | 
|  | 78 |  | 
|  | 79 | /* --------------------------------------------------------------------- */ | 
|  | 80 |  | 
|  | 81 | /* EPP status register */ | 
|  | 82 | #define EPP_DCDBIT      0x80 | 
|  | 83 | #define EPP_PTTBIT      0x08 | 
|  | 84 | #define EPP_NREF        0x01 | 
|  | 85 | #define EPP_NRAEF       0x02 | 
|  | 86 | #define EPP_NRHF        0x04 | 
|  | 87 | #define EPP_NTHF        0x20 | 
|  | 88 | #define EPP_NTAEF       0x10 | 
|  | 89 | #define EPP_NTEF        EPP_PTTBIT | 
|  | 90 |  | 
|  | 91 | /* EPP control register */ | 
|  | 92 | #define EPP_TX_FIFO_ENABLE 0x10 | 
|  | 93 | #define EPP_RX_FIFO_ENABLE 0x08 | 
|  | 94 | #define EPP_MODEM_ENABLE   0x20 | 
|  | 95 | #define EPP_LEDS           0xC0 | 
|  | 96 | #define EPP_IRQ_ENABLE     0x10 | 
|  | 97 |  | 
|  | 98 | /* LPT registers */ | 
|  | 99 | #define LPTREG_ECONTROL       0x402 | 
|  | 100 | #define LPTREG_CONFIGB        0x401 | 
|  | 101 | #define LPTREG_CONFIGA        0x400 | 
|  | 102 | #define LPTREG_EPPDATA        0x004 | 
|  | 103 | #define LPTREG_EPPADDR        0x003 | 
|  | 104 | #define LPTREG_CONTROL        0x002 | 
|  | 105 | #define LPTREG_STATUS         0x001 | 
|  | 106 | #define LPTREG_DATA           0x000 | 
|  | 107 |  | 
|  | 108 | /* LPT control register */ | 
|  | 109 | #define LPTCTRL_PROGRAM       0x04   /* 0 to reprogram */ | 
|  | 110 | #define LPTCTRL_WRITE         0x01 | 
|  | 111 | #define LPTCTRL_ADDRSTB       0x08 | 
|  | 112 | #define LPTCTRL_DATASTB       0x02 | 
|  | 113 | #define LPTCTRL_INTEN         0x10 | 
|  | 114 |  | 
|  | 115 | /* LPT status register */ | 
|  | 116 | #define LPTSTAT_SHIFT_NINTR   6 | 
|  | 117 | #define LPTSTAT_WAIT          0x80 | 
|  | 118 | #define LPTSTAT_NINTR         (1<<LPTSTAT_SHIFT_NINTR) | 
|  | 119 | #define LPTSTAT_PE            0x20 | 
|  | 120 | #define LPTSTAT_DONE          0x10 | 
|  | 121 | #define LPTSTAT_NERROR        0x08 | 
|  | 122 | #define LPTSTAT_EPPTIMEOUT    0x01 | 
|  | 123 |  | 
|  | 124 | /* LPT data register */ | 
|  | 125 | #define LPTDATA_SHIFT_TDI     0 | 
|  | 126 | #define LPTDATA_SHIFT_TMS     2 | 
|  | 127 | #define LPTDATA_TDI           (1<<LPTDATA_SHIFT_TDI) | 
|  | 128 | #define LPTDATA_TCK           0x02 | 
|  | 129 | #define LPTDATA_TMS           (1<<LPTDATA_SHIFT_TMS) | 
|  | 130 | #define LPTDATA_INITBIAS      0x80 | 
|  | 131 |  | 
|  | 132 |  | 
|  | 133 | /* EPP modem config/status bits */ | 
|  | 134 | #define EPP_DCDBIT            0x80 | 
|  | 135 | #define EPP_PTTBIT            0x08 | 
|  | 136 | #define EPP_RXEBIT            0x01 | 
|  | 137 | #define EPP_RXAEBIT           0x02 | 
|  | 138 | #define EPP_RXHFULL           0x04 | 
|  | 139 |  | 
|  | 140 | #define EPP_NTHF              0x20 | 
|  | 141 | #define EPP_NTAEF             0x10 | 
|  | 142 | #define EPP_NTEF              EPP_PTTBIT | 
|  | 143 |  | 
|  | 144 | #define EPP_TX_FIFO_ENABLE    0x10 | 
|  | 145 | #define EPP_RX_FIFO_ENABLE    0x08 | 
|  | 146 | #define EPP_MODEM_ENABLE      0x20 | 
|  | 147 | #define EPP_LEDS              0xC0 | 
|  | 148 | #define EPP_IRQ_ENABLE        0x10 | 
|  | 149 |  | 
|  | 150 | /* Xilinx 4k JTAG instructions */ | 
|  | 151 | #define XC4K_IRLENGTH   3 | 
|  | 152 | #define XC4K_EXTEST     0 | 
|  | 153 | #define XC4K_PRELOAD    1 | 
|  | 154 | #define XC4K_CONFIGURE  5 | 
|  | 155 | #define XC4K_BYPASS     7 | 
|  | 156 |  | 
|  | 157 | #define EPP_CONVENTIONAL  0 | 
|  | 158 | #define EPP_FPGA          1 | 
|  | 159 | #define EPP_FPGAEXTSTATUS 2 | 
|  | 160 |  | 
|  | 161 | #define TXBUFFER_SIZE     ((HDLCDRV_MAXFLEN*6/5)+8) | 
|  | 162 |  | 
|  | 163 | /* ---------------------------------------------------------------------- */ | 
|  | 164 | /* | 
|  | 165 | * Information that need to be kept for each board. | 
|  | 166 | */ | 
|  | 167 |  | 
|  | 168 | struct baycom_state { | 
|  | 169 | int magic; | 
|  | 170 |  | 
|  | 171 | struct pardevice *pdev; | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 172 | struct net_device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 173 | unsigned int work_running; | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 174 | struct delayed_work run_work; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 175 | unsigned int modem; | 
|  | 176 | unsigned int bitrate; | 
|  | 177 | unsigned char stat; | 
|  | 178 |  | 
|  | 179 | struct { | 
|  | 180 | unsigned int intclk; | 
|  | 181 | unsigned int fclk; | 
|  | 182 | unsigned int bps; | 
|  | 183 | unsigned int extmodem; | 
|  | 184 | unsigned int loopback; | 
|  | 185 | } cfg; | 
|  | 186 |  | 
|  | 187 | struct hdlcdrv_channel_params ch_params; | 
|  | 188 |  | 
|  | 189 | struct { | 
|  | 190 | unsigned int bitbuf, bitstream, numbits, state; | 
|  | 191 | unsigned char *bufptr; | 
|  | 192 | int bufcnt; | 
|  | 193 | unsigned char buf[TXBUFFER_SIZE]; | 
|  | 194 | } hdlcrx; | 
|  | 195 |  | 
|  | 196 | struct { | 
|  | 197 | int calibrate; | 
|  | 198 | int slotcnt; | 
|  | 199 | int flags; | 
|  | 200 | enum { tx_idle = 0, tx_keyup, tx_data, tx_tail } state; | 
|  | 201 | unsigned char *bufptr; | 
|  | 202 | int bufcnt; | 
|  | 203 | unsigned char buf[TXBUFFER_SIZE]; | 
|  | 204 | } hdlctx; | 
|  | 205 |  | 
|  | 206 | struct net_device_stats stats; | 
|  | 207 | unsigned int ptt_keyed; | 
|  | 208 | struct sk_buff *skb;  /* next transmit packet  */ | 
|  | 209 |  | 
|  | 210 | #ifdef BAYCOM_DEBUG | 
|  | 211 | struct debug_vals { | 
|  | 212 | unsigned long last_jiffies; | 
|  | 213 | unsigned cur_intcnt; | 
|  | 214 | unsigned last_intcnt; | 
|  | 215 | int cur_pllcorr; | 
|  | 216 | int last_pllcorr; | 
|  | 217 | unsigned int mod_cycles; | 
|  | 218 | unsigned int demod_cycles; | 
|  | 219 | } debug_vals; | 
|  | 220 | #endif /* BAYCOM_DEBUG */ | 
|  | 221 | }; | 
|  | 222 |  | 
|  | 223 | /* --------------------------------------------------------------------- */ | 
|  | 224 |  | 
|  | 225 | #define KISS_VERBOSE | 
|  | 226 |  | 
|  | 227 | /* --------------------------------------------------------------------- */ | 
|  | 228 |  | 
|  | 229 | #define PARAM_TXDELAY   1 | 
|  | 230 | #define PARAM_PERSIST   2 | 
|  | 231 | #define PARAM_SLOTTIME  3 | 
|  | 232 | #define PARAM_TXTAIL    4 | 
|  | 233 | #define PARAM_FULLDUP   5 | 
|  | 234 | #define PARAM_HARDWARE  6 | 
|  | 235 | #define PARAM_RETURN    255 | 
|  | 236 |  | 
|  | 237 | /* --------------------------------------------------------------------- */ | 
|  | 238 | /* | 
|  | 239 | * the CRC routines are stolen from WAMPES | 
|  | 240 | * by Dieter Deyke | 
|  | 241 | */ | 
|  | 242 |  | 
|  | 243 |  | 
|  | 244 | /*---------------------------------------------------------------------------*/ | 
|  | 245 |  | 
|  | 246 | #if 0 | 
|  | 247 | static inline void append_crc_ccitt(unsigned char *buffer, int len) | 
|  | 248 | { | 
|  | 249 | unsigned int crc = 0xffff; | 
|  | 250 |  | 
|  | 251 | for (;len>0;len--) | 
|  | 252 | crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; | 
|  | 253 | crc ^= 0xffff; | 
|  | 254 | *buffer++ = crc; | 
|  | 255 | *buffer++ = crc >> 8; | 
|  | 256 | } | 
|  | 257 | #endif | 
|  | 258 |  | 
|  | 259 | /*---------------------------------------------------------------------------*/ | 
|  | 260 |  | 
|  | 261 | static inline int check_crc_ccitt(const unsigned char *buf, int cnt) | 
|  | 262 | { | 
|  | 263 | return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; | 
|  | 264 | } | 
|  | 265 |  | 
|  | 266 | /*---------------------------------------------------------------------------*/ | 
|  | 267 |  | 
|  | 268 | static inline int calc_crc_ccitt(const unsigned char *buf, int cnt) | 
|  | 269 | { | 
|  | 270 | return (crc_ccitt(0xffff, buf, cnt) ^ 0xffff) & 0xffff; | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | /* ---------------------------------------------------------------------- */ | 
|  | 274 |  | 
|  | 275 | #define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) | 
|  | 276 |  | 
|  | 277 | /* --------------------------------------------------------------------- */ | 
|  | 278 |  | 
|  | 279 | static inline void baycom_int_freq(struct baycom_state *bc) | 
|  | 280 | { | 
|  | 281 | #ifdef BAYCOM_DEBUG | 
|  | 282 | unsigned long cur_jiffies = jiffies; | 
|  | 283 | /* | 
|  | 284 | * measure the interrupt frequency | 
|  | 285 | */ | 
|  | 286 | bc->debug_vals.cur_intcnt++; | 
| Marcelo Feitoza Parisi | cd8749b | 2005-07-15 11:16:42 +0100 | [diff] [blame] | 287 | if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 288 | bc->debug_vals.last_jiffies = cur_jiffies; | 
|  | 289 | bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; | 
|  | 290 | bc->debug_vals.cur_intcnt = 0; | 
|  | 291 | bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; | 
|  | 292 | bc->debug_vals.cur_pllcorr = 0; | 
|  | 293 | } | 
|  | 294 | #endif /* BAYCOM_DEBUG */ | 
|  | 295 | } | 
|  | 296 |  | 
|  | 297 | /* ---------------------------------------------------------------------- */ | 
|  | 298 | /* | 
|  | 299 | *    eppconfig_path should be setable  via /proc/sys. | 
|  | 300 | */ | 
|  | 301 |  | 
|  | 302 | static char eppconfig_path[256] = "/usr/sbin/eppfpga"; | 
|  | 303 |  | 
|  | 304 | static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; | 
|  | 305 |  | 
|  | 306 | /* eppconfig: called during ifconfig up to configure the modem */ | 
|  | 307 | static int eppconfig(struct baycom_state *bc) | 
|  | 308 | { | 
|  | 309 | char modearg[256]; | 
|  | 310 | char portarg[16]; | 
|  | 311 | char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, | 
|  | 312 | NULL }; | 
|  | 313 |  | 
|  | 314 | /* set up arguments */ | 
|  | 315 | sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", | 
|  | 316 | bc->cfg.intclk ? "int" : "ext", | 
|  | 317 | bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, | 
|  | 318 | (bc->cfg.fclk + 8 * bc->cfg.bps) / (16 * bc->cfg.bps), | 
|  | 319 | bc->cfg.loopback ? ",loopback" : ""); | 
|  | 320 | sprintf(portarg, "%ld", bc->pdev->port->base); | 
|  | 321 | printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); | 
|  | 322 |  | 
|  | 323 | return call_usermodehelper(eppconfig_path, argv, envp, 1); | 
|  | 324 | } | 
|  | 325 |  | 
|  | 326 | /* ---------------------------------------------------------------------- */ | 
|  | 327 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 328 | static void epp_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 329 | { | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 | /* ---------------------------------------------------------------------- */ | 
|  | 333 |  | 
|  | 334 | static inline void do_kiss_params(struct baycom_state *bc, | 
|  | 335 | unsigned char *data, unsigned long len) | 
|  | 336 | { | 
|  | 337 |  | 
|  | 338 | #ifdef KISS_VERBOSE | 
|  | 339 | #define PKP(a,b) printk(KERN_INFO "baycomm_epp: channel params: " a "\n", b) | 
|  | 340 | #else /* KISS_VERBOSE */ | 
|  | 341 | #define PKP(a,b) | 
|  | 342 | #endif /* KISS_VERBOSE */ | 
|  | 343 |  | 
|  | 344 | if (len < 2) | 
|  | 345 | return; | 
|  | 346 | switch(data[0]) { | 
|  | 347 | case PARAM_TXDELAY: | 
|  | 348 | bc->ch_params.tx_delay = data[1]; | 
|  | 349 | PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); | 
|  | 350 | break; | 
|  | 351 | case PARAM_PERSIST: | 
|  | 352 | bc->ch_params.ppersist = data[1]; | 
|  | 353 | PKP("p persistence = %u", bc->ch_params.ppersist); | 
|  | 354 | break; | 
|  | 355 | case PARAM_SLOTTIME: | 
|  | 356 | bc->ch_params.slottime = data[1]; | 
|  | 357 | PKP("slot time = %ums", bc->ch_params.slottime); | 
|  | 358 | break; | 
|  | 359 | case PARAM_TXTAIL: | 
|  | 360 | bc->ch_params.tx_tail = data[1]; | 
|  | 361 | PKP("TX tail = %ums", bc->ch_params.tx_tail); | 
|  | 362 | break; | 
|  | 363 | case PARAM_FULLDUP: | 
|  | 364 | bc->ch_params.fulldup = !!data[1]; | 
|  | 365 | PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); | 
|  | 366 | break; | 
|  | 367 | default: | 
|  | 368 | break; | 
|  | 369 | } | 
|  | 370 | #undef PKP | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 | /* --------------------------------------------------------------------- */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 |  | 
|  | 375 | static void encode_hdlc(struct baycom_state *bc) | 
|  | 376 | { | 
|  | 377 | struct sk_buff *skb; | 
|  | 378 | unsigned char *wp, *bp; | 
|  | 379 | int pkt_len; | 
|  | 380 | unsigned bitstream, notbitstream, bitbuf, numbit, crc; | 
|  | 381 | unsigned char crcarr[2]; | 
| Adrian Bunk | 0fd56f6 | 2005-06-02 14:04:00 -0700 | [diff] [blame] | 382 | int j; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 |  | 
|  | 384 | if (bc->hdlctx.bufcnt > 0) | 
|  | 385 | return; | 
|  | 386 | skb = bc->skb; | 
|  | 387 | if (!skb) | 
|  | 388 | return; | 
|  | 389 | bc->skb = NULL; | 
|  | 390 | pkt_len = skb->len-1; /* strip KISS byte */ | 
|  | 391 | wp = bc->hdlctx.buf; | 
|  | 392 | bp = skb->data+1; | 
|  | 393 | crc = calc_crc_ccitt(bp, pkt_len); | 
|  | 394 | crcarr[0] = crc; | 
|  | 395 | crcarr[1] = crc >> 8; | 
|  | 396 | *wp++ = 0x7e; | 
|  | 397 | bitstream = bitbuf = numbit = 0; | 
|  | 398 | while (pkt_len > -2) { | 
|  | 399 | bitstream >>= 8; | 
|  | 400 | bitstream |= ((unsigned int)*bp) << 8; | 
|  | 401 | bitbuf |= ((unsigned int)*bp) << numbit; | 
|  | 402 | notbitstream = ~bitstream; | 
|  | 403 | bp++; | 
|  | 404 | pkt_len--; | 
|  | 405 | if (!pkt_len) | 
|  | 406 | bp = crcarr; | 
| Adrian Bunk | 0fd56f6 | 2005-06-02 14:04:00 -0700 | [diff] [blame] | 407 | for (j = 0; j < 8; j++) | 
|  | 408 | if (unlikely(!(notbitstream & (0x1f0 << j)))) { | 
|  | 409 | bitstream &= ~(0x100 << j); | 
|  | 410 | bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | | 
|  | 411 | ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); | 
|  | 412 | numbit++; | 
|  | 413 | notbitstream = ~bitstream; | 
|  | 414 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 415 | numbit += 8; | 
|  | 416 | while (numbit >= 8) { | 
|  | 417 | *wp++ = bitbuf; | 
|  | 418 | bitbuf >>= 8; | 
|  | 419 | numbit -= 8; | 
|  | 420 | } | 
|  | 421 | } | 
|  | 422 | bitbuf |= 0x7e7e << numbit; | 
|  | 423 | numbit += 16; | 
|  | 424 | while (numbit >= 8) { | 
|  | 425 | *wp++ = bitbuf; | 
|  | 426 | bitbuf >>= 8; | 
|  | 427 | numbit -= 8; | 
|  | 428 | } | 
|  | 429 | bc->hdlctx.bufptr = bc->hdlctx.buf; | 
|  | 430 | bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; | 
|  | 431 | dev_kfree_skb(skb); | 
|  | 432 | bc->stats.tx_packets++; | 
|  | 433 | } | 
|  | 434 |  | 
|  | 435 | /* ---------------------------------------------------------------------- */ | 
|  | 436 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 437 | static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) | 
|  | 438 | { | 
|  | 439 | struct parport *pp = bc->pdev->port; | 
|  | 440 | unsigned char tmp[128]; | 
|  | 441 | int i, j; | 
|  | 442 |  | 
|  | 443 | if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) | 
|  | 444 | bc->hdlctx.state = tx_idle; | 
|  | 445 | if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { | 
|  | 446 | if (bc->hdlctx.bufcnt <= 0) | 
|  | 447 | encode_hdlc(bc); | 
|  | 448 | if (bc->hdlctx.bufcnt <= 0) | 
|  | 449 | return 0; | 
|  | 450 | if (!bc->ch_params.fulldup) { | 
|  | 451 | if (!(stat & EPP_DCDBIT)) { | 
|  | 452 | bc->hdlctx.slotcnt = bc->ch_params.slottime; | 
|  | 453 | return 0; | 
|  | 454 | } | 
|  | 455 | if ((--bc->hdlctx.slotcnt) > 0) | 
|  | 456 | return 0; | 
|  | 457 | bc->hdlctx.slotcnt = bc->ch_params.slottime; | 
| Ralf Baechle | 8b5b467 | 2007-02-16 11:55:33 +0000 | [diff] [blame] | 458 | if ((random32() % 256) > bc->ch_params.ppersist) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 459 | return 0; | 
|  | 460 | } | 
|  | 461 | } | 
|  | 462 | if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { | 
|  | 463 | bc->hdlctx.state = tx_keyup; | 
|  | 464 | bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); | 
|  | 465 | bc->ptt_keyed++; | 
|  | 466 | } | 
|  | 467 | while (cnt > 0) { | 
|  | 468 | switch (bc->hdlctx.state) { | 
|  | 469 | case tx_keyup: | 
|  | 470 | i = min_t(int, cnt, bc->hdlctx.flags); | 
|  | 471 | cnt -= i; | 
|  | 472 | bc->hdlctx.flags -= i; | 
|  | 473 | if (bc->hdlctx.flags <= 0) | 
|  | 474 | bc->hdlctx.state = tx_data; | 
|  | 475 | memset(tmp, 0x7e, sizeof(tmp)); | 
|  | 476 | while (i > 0) { | 
|  | 477 | j = (i > sizeof(tmp)) ? sizeof(tmp) : i; | 
|  | 478 | if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) | 
|  | 479 | return -1; | 
|  | 480 | i -= j; | 
|  | 481 | } | 
|  | 482 | break; | 
|  | 483 |  | 
|  | 484 | case tx_data: | 
|  | 485 | if (bc->hdlctx.bufcnt <= 0) { | 
|  | 486 | encode_hdlc(bc); | 
|  | 487 | if (bc->hdlctx.bufcnt <= 0) { | 
|  | 488 | bc->hdlctx.state = tx_tail; | 
|  | 489 | bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); | 
|  | 490 | break; | 
|  | 491 | } | 
|  | 492 | } | 
|  | 493 | i = min_t(int, cnt, bc->hdlctx.bufcnt); | 
|  | 494 | bc->hdlctx.bufcnt -= i; | 
|  | 495 | cnt -= i; | 
|  | 496 | if (i != pp->ops->epp_write_data(pp, bc->hdlctx.bufptr, i, 0)) | 
|  | 497 | return -1; | 
|  | 498 | bc->hdlctx.bufptr += i; | 
|  | 499 | break; | 
|  | 500 |  | 
|  | 501 | case tx_tail: | 
|  | 502 | encode_hdlc(bc); | 
|  | 503 | if (bc->hdlctx.bufcnt > 0) { | 
|  | 504 | bc->hdlctx.state = tx_data; | 
|  | 505 | break; | 
|  | 506 | } | 
|  | 507 | i = min_t(int, cnt, bc->hdlctx.flags); | 
|  | 508 | if (i) { | 
|  | 509 | cnt -= i; | 
|  | 510 | bc->hdlctx.flags -= i; | 
|  | 511 | memset(tmp, 0x7e, sizeof(tmp)); | 
|  | 512 | while (i > 0) { | 
|  | 513 | j = (i > sizeof(tmp)) ? sizeof(tmp) : i; | 
|  | 514 | if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) | 
|  | 515 | return -1; | 
|  | 516 | i -= j; | 
|  | 517 | } | 
|  | 518 | break; | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | default:  /* fall through */ | 
|  | 522 | if (bc->hdlctx.calibrate <= 0) | 
|  | 523 | return 0; | 
|  | 524 | i = min_t(int, cnt, bc->hdlctx.calibrate); | 
|  | 525 | cnt -= i; | 
|  | 526 | bc->hdlctx.calibrate -= i; | 
|  | 527 | memset(tmp, 0, sizeof(tmp)); | 
|  | 528 | while (i > 0) { | 
|  | 529 | j = (i > sizeof(tmp)) ? sizeof(tmp) : i; | 
|  | 530 | if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) | 
|  | 531 | return -1; | 
|  | 532 | i -= j; | 
|  | 533 | } | 
|  | 534 | break; | 
|  | 535 | } | 
|  | 536 | } | 
|  | 537 | return 0; | 
|  | 538 | } | 
|  | 539 |  | 
|  | 540 | /* ---------------------------------------------------------------------- */ | 
|  | 541 |  | 
|  | 542 | static void do_rxpacket(struct net_device *dev) | 
|  | 543 | { | 
|  | 544 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 545 | struct sk_buff *skb; | 
|  | 546 | unsigned char *cp; | 
|  | 547 | unsigned pktlen; | 
|  | 548 |  | 
|  | 549 | if (bc->hdlcrx.bufcnt < 4) | 
|  | 550 | return; | 
|  | 551 | if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) | 
|  | 552 | return; | 
|  | 553 | pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ | 
|  | 554 | if (!(skb = dev_alloc_skb(pktlen))) { | 
|  | 555 | printk("%s: memory squeeze, dropping packet\n", dev->name); | 
|  | 556 | bc->stats.rx_dropped++; | 
|  | 557 | return; | 
|  | 558 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 559 | cp = skb_put(skb, pktlen); | 
|  | 560 | *cp++ = 0; /* KISS kludge */ | 
|  | 561 | memcpy(cp, bc->hdlcrx.buf, pktlen - 1); | 
| Arnaldo Carvalho de Melo | 56cb515 | 2005-04-24 18:53:06 -0700 | [diff] [blame] | 562 | skb->protocol = ax25_type_trans(skb, dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 563 | netif_rx(skb); | 
|  | 564 | dev->last_rx = jiffies; | 
|  | 565 | bc->stats.rx_packets++; | 
|  | 566 | } | 
|  | 567 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 568 | static int receive(struct net_device *dev, int cnt) | 
|  | 569 | { | 
|  | 570 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 571 | struct parport *pp = bc->pdev->port; | 
|  | 572 | unsigned int bitbuf, notbitstream, bitstream, numbits, state; | 
|  | 573 | unsigned char tmp[128]; | 
|  | 574 | unsigned char *cp; | 
|  | 575 | int cnt2, ret = 0; | 
| Adrian Bunk | 0fd56f6 | 2005-06-02 14:04:00 -0700 | [diff] [blame] | 576 | int j; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 577 |  | 
|  | 578 | numbits = bc->hdlcrx.numbits; | 
|  | 579 | state = bc->hdlcrx.state; | 
|  | 580 | bitstream = bc->hdlcrx.bitstream; | 
|  | 581 | bitbuf = bc->hdlcrx.bitbuf; | 
|  | 582 | while (cnt > 0) { | 
|  | 583 | cnt2 = (cnt > sizeof(tmp)) ? sizeof(tmp) : cnt; | 
|  | 584 | cnt -= cnt2; | 
|  | 585 | if (cnt2 != pp->ops->epp_read_data(pp, tmp, cnt2, 0)) { | 
|  | 586 | ret = -1; | 
|  | 587 | break; | 
|  | 588 | } | 
|  | 589 | cp = tmp; | 
|  | 590 | for (; cnt2 > 0; cnt2--, cp++) { | 
|  | 591 | bitstream >>= 8; | 
|  | 592 | bitstream |= (*cp) << 8; | 
|  | 593 | bitbuf >>= 8; | 
|  | 594 | bitbuf |= (*cp) << 8; | 
|  | 595 | numbits += 8; | 
|  | 596 | notbitstream = ~bitstream; | 
| Adrian Bunk | 0fd56f6 | 2005-06-02 14:04:00 -0700 | [diff] [blame] | 597 | for (j = 0; j < 8; j++) { | 
|  | 598 |  | 
|  | 599 | /* flag or abort */ | 
|  | 600 | if (unlikely(!(notbitstream & (0x0fc << j)))) { | 
|  | 601 |  | 
|  | 602 | /* abort received */ | 
|  | 603 | if (!(notbitstream & (0x1fc << j))) | 
|  | 604 | state = 0; | 
|  | 605 |  | 
|  | 606 | /* not flag received */ | 
|  | 607 | else if (!(bitstream & (0x1fe << j)) != (0x0fc << j)) { | 
|  | 608 | if (state) | 
|  | 609 | do_rxpacket(dev); | 
|  | 610 | bc->hdlcrx.bufcnt = 0; | 
|  | 611 | bc->hdlcrx.bufptr = bc->hdlcrx.buf; | 
|  | 612 | state = 1; | 
|  | 613 | numbits = 7-j; | 
|  | 614 | } | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | /* stuffed bit */ | 
|  | 618 | else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) { | 
|  | 619 | numbits--; | 
|  | 620 | bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); | 
|  | 621 | } | 
|  | 622 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 623 | while (state && numbits >= 8) { | 
|  | 624 | if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { | 
|  | 625 | state = 0; | 
|  | 626 | } else { | 
|  | 627 | *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); | 
|  | 628 | bc->hdlcrx.bufcnt++; | 
|  | 629 | numbits -= 8; | 
|  | 630 | } | 
|  | 631 | } | 
|  | 632 | } | 
|  | 633 | } | 
|  | 634 | bc->hdlcrx.numbits = numbits; | 
|  | 635 | bc->hdlcrx.state = state; | 
|  | 636 | bc->hdlcrx.bitstream = bitstream; | 
|  | 637 | bc->hdlcrx.bitbuf = bitbuf; | 
|  | 638 | return ret; | 
|  | 639 | } | 
|  | 640 |  | 
|  | 641 | /* --------------------------------------------------------------------- */ | 
|  | 642 |  | 
|  | 643 | #ifdef __i386__ | 
|  | 644 | #include <asm/msr.h> | 
|  | 645 | #define GETTICK(x)                                                \ | 
|  | 646 | ({                                                                \ | 
|  | 647 | if (cpu_has_tsc)                                          \ | 
|  | 648 | rdtscl(x);                                        \ | 
|  | 649 | }) | 
|  | 650 | #else /* __i386__ */ | 
|  | 651 | #define GETTICK(x) | 
|  | 652 | #endif /* __i386__ */ | 
|  | 653 |  | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 654 | static void epp_bh(struct work_struct *work) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 655 | { | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 656 | struct net_device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 657 | struct baycom_state *bc; | 
|  | 658 | struct parport *pp; | 
|  | 659 | unsigned char stat; | 
|  | 660 | unsigned char tmp[2]; | 
|  | 661 | unsigned int time1 = 0, time2 = 0, time3 = 0; | 
|  | 662 | int cnt, cnt2; | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 663 |  | 
|  | 664 | bc = container_of(work, struct baycom_state, run_work.work); | 
|  | 665 | dev = bc->dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 666 | if (!bc->work_running) | 
|  | 667 | return; | 
|  | 668 | baycom_int_freq(bc); | 
|  | 669 | pp = bc->pdev->port; | 
|  | 670 | /* update status */ | 
|  | 671 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 672 | goto epptimeout; | 
|  | 673 | bc->stat = stat; | 
|  | 674 | bc->debug_vals.last_pllcorr = stat; | 
|  | 675 | GETTICK(time1); | 
|  | 676 | if (bc->modem == EPP_FPGAEXTSTATUS) { | 
|  | 677 | /* get input count */ | 
|  | 678 | tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1; | 
|  | 679 | if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) | 
|  | 680 | goto epptimeout; | 
|  | 681 | if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) | 
|  | 682 | goto epptimeout; | 
|  | 683 | cnt = tmp[0] | (tmp[1] << 8); | 
|  | 684 | cnt &= 0x7fff; | 
|  | 685 | /* get output count */ | 
|  | 686 | tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2; | 
|  | 687 | if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) | 
|  | 688 | goto epptimeout; | 
|  | 689 | if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) | 
|  | 690 | goto epptimeout; | 
|  | 691 | cnt2 = tmp[0] | (tmp[1] << 8); | 
|  | 692 | cnt2 = 16384 - (cnt2 & 0x7fff); | 
|  | 693 | /* return to normal */ | 
|  | 694 | tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; | 
|  | 695 | if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) | 
|  | 696 | goto epptimeout; | 
|  | 697 | if (transmit(bc, cnt2, stat)) | 
|  | 698 | goto epptimeout; | 
|  | 699 | GETTICK(time2); | 
|  | 700 | if (receive(dev, cnt)) | 
|  | 701 | goto epptimeout; | 
|  | 702 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 703 | goto epptimeout; | 
|  | 704 | bc->stat = stat; | 
|  | 705 | } else { | 
|  | 706 | /* try to tx */ | 
|  | 707 | switch (stat & (EPP_NTAEF|EPP_NTHF)) { | 
|  | 708 | case EPP_NTHF: | 
|  | 709 | cnt = 2048 - 256; | 
|  | 710 | break; | 
|  | 711 |  | 
|  | 712 | case EPP_NTAEF: | 
|  | 713 | cnt = 2048 - 1793; | 
|  | 714 | break; | 
|  | 715 |  | 
|  | 716 | case 0: | 
|  | 717 | cnt = 0; | 
|  | 718 | break; | 
|  | 719 |  | 
|  | 720 | default: | 
|  | 721 | cnt = 2048 - 1025; | 
|  | 722 | break; | 
|  | 723 | } | 
|  | 724 | if (transmit(bc, cnt, stat)) | 
|  | 725 | goto epptimeout; | 
|  | 726 | GETTICK(time2); | 
|  | 727 | /* do receiver */ | 
|  | 728 | while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { | 
|  | 729 | switch (stat & (EPP_NRAEF|EPP_NRHF)) { | 
|  | 730 | case EPP_NRAEF: | 
|  | 731 | cnt = 1025; | 
|  | 732 | break; | 
|  | 733 |  | 
|  | 734 | case 0: | 
|  | 735 | cnt = 1793; | 
|  | 736 | break; | 
|  | 737 |  | 
|  | 738 | default: | 
|  | 739 | cnt = 256; | 
|  | 740 | break; | 
|  | 741 | } | 
|  | 742 | if (receive(dev, cnt)) | 
|  | 743 | goto epptimeout; | 
|  | 744 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 745 | goto epptimeout; | 
|  | 746 | } | 
|  | 747 | cnt = 0; | 
|  | 748 | if (bc->bitrate < 50000) | 
|  | 749 | cnt = 256; | 
|  | 750 | else if (bc->bitrate < 100000) | 
|  | 751 | cnt = 128; | 
|  | 752 | while (cnt > 0 && stat & EPP_NREF) { | 
|  | 753 | if (receive(dev, 1)) | 
|  | 754 | goto epptimeout; | 
|  | 755 | cnt--; | 
|  | 756 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 757 | goto epptimeout; | 
|  | 758 | } | 
|  | 759 | } | 
|  | 760 | GETTICK(time3); | 
|  | 761 | #ifdef BAYCOM_DEBUG | 
|  | 762 | bc->debug_vals.mod_cycles = time2 - time1; | 
|  | 763 | bc->debug_vals.demod_cycles = time3 - time2; | 
|  | 764 | #endif /* BAYCOM_DEBUG */ | 
|  | 765 | schedule_delayed_work(&bc->run_work, 1); | 
|  | 766 | if (!bc->skb) | 
|  | 767 | netif_wake_queue(dev); | 
|  | 768 | return; | 
|  | 769 | epptimeout: | 
|  | 770 | printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); | 
|  | 771 | } | 
|  | 772 |  | 
|  | 773 | /* ---------------------------------------------------------------------- */ | 
|  | 774 | /* | 
|  | 775 | * ===================== network driver interface ========================= | 
|  | 776 | */ | 
|  | 777 |  | 
|  | 778 | static int baycom_send_packet(struct sk_buff *skb, struct net_device *dev) | 
|  | 779 | { | 
|  | 780 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 781 |  | 
|  | 782 | if (skb->data[0] != 0) { | 
|  | 783 | do_kiss_params(bc, skb->data, skb->len); | 
|  | 784 | dev_kfree_skb(skb); | 
|  | 785 | return 0; | 
|  | 786 | } | 
|  | 787 | if (bc->skb) | 
|  | 788 | return -1; | 
|  | 789 | /* strip KISS byte */ | 
|  | 790 | if (skb->len >= HDLCDRV_MAXFLEN+1 || skb->len < 3) { | 
|  | 791 | dev_kfree_skb(skb); | 
|  | 792 | return 0; | 
|  | 793 | } | 
|  | 794 | netif_stop_queue(dev); | 
|  | 795 | bc->skb = skb; | 
|  | 796 | return 0; | 
|  | 797 | } | 
|  | 798 |  | 
|  | 799 | /* --------------------------------------------------------------------- */ | 
|  | 800 |  | 
|  | 801 | static int baycom_set_mac_address(struct net_device *dev, void *addr) | 
|  | 802 | { | 
|  | 803 | struct sockaddr *sa = (struct sockaddr *)addr; | 
|  | 804 |  | 
|  | 805 | /* addr is an AX.25 shifted ASCII mac address */ | 
|  | 806 | memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); | 
|  | 807 | return 0; | 
|  | 808 | } | 
|  | 809 |  | 
|  | 810 | /* --------------------------------------------------------------------- */ | 
|  | 811 |  | 
|  | 812 | static struct net_device_stats *baycom_get_stats(struct net_device *dev) | 
|  | 813 | { | 
|  | 814 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 815 |  | 
|  | 816 | /* | 
|  | 817 | * Get the current statistics.  This may be called with the | 
|  | 818 | * card open or closed. | 
|  | 819 | */ | 
|  | 820 | return &bc->stats; | 
|  | 821 | } | 
|  | 822 |  | 
|  | 823 | /* --------------------------------------------------------------------- */ | 
|  | 824 |  | 
|  | 825 | static void epp_wakeup(void *handle) | 
|  | 826 | { | 
|  | 827 | struct net_device *dev = (struct net_device *)handle; | 
|  | 828 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 829 |  | 
|  | 830 | printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); | 
|  | 831 | if (!parport_claim(bc->pdev)) | 
|  | 832 | printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); | 
|  | 833 | } | 
|  | 834 |  | 
|  | 835 | /* --------------------------------------------------------------------- */ | 
|  | 836 |  | 
|  | 837 | /* | 
|  | 838 | * Open/initialize the board. This is called (in the current kernel) | 
|  | 839 | * sometime after booting when the 'ifconfig' program is run. | 
|  | 840 | * | 
|  | 841 | * This routine should set everything up anew at each open, even | 
|  | 842 | * registers that "should" only need to be set once at boot, so that | 
|  | 843 | * there is non-reboot way to recover if something goes wrong. | 
|  | 844 | */ | 
|  | 845 |  | 
|  | 846 | static int epp_open(struct net_device *dev) | 
|  | 847 | { | 
|  | 848 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 849 | struct parport *pp = parport_find_base(dev->base_addr); | 
|  | 850 | unsigned int i, j; | 
|  | 851 | unsigned char tmp[128]; | 
|  | 852 | unsigned char stat; | 
|  | 853 | unsigned long tstart; | 
|  | 854 |  | 
|  | 855 | if (!pp) { | 
|  | 856 | printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); | 
|  | 857 | return -ENXIO; | 
|  | 858 | } | 
|  | 859 | #if 0 | 
|  | 860 | if (pp->irq < 0) { | 
|  | 861 | printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); | 
|  | 862 | parport_put_port(pp); | 
|  | 863 | return -ENXIO; | 
|  | 864 | } | 
|  | 865 | #endif | 
|  | 866 | if ((~pp->modes) & (PARPORT_MODE_TRISTATE | PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { | 
|  | 867 | printk(KERN_ERR "%s: parport at 0x%lx cannot be used\n", | 
|  | 868 | bc_drvname, pp->base); | 
|  | 869 | parport_put_port(pp); | 
|  | 870 | return -EIO; | 
|  | 871 | } | 
|  | 872 | memset(&bc->modem, 0, sizeof(bc->modem)); | 
|  | 873 | bc->pdev = parport_register_device(pp, dev->name, NULL, epp_wakeup, | 
|  | 874 | epp_interrupt, PARPORT_DEV_EXCL, dev); | 
|  | 875 | parport_put_port(pp); | 
|  | 876 | if (!bc->pdev) { | 
|  | 877 | printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); | 
|  | 878 | return -ENXIO; | 
|  | 879 | } | 
|  | 880 | if (parport_claim(bc->pdev)) { | 
|  | 881 | printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); | 
|  | 882 | parport_unregister_device(bc->pdev); | 
|  | 883 | return -EBUSY; | 
|  | 884 | } | 
|  | 885 | dev->irq = /*pp->irq*/ 0; | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 886 | INIT_DELAYED_WORK(&bc->run_work, epp_bh); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 887 | bc->work_running = 1; | 
|  | 888 | bc->modem = EPP_CONVENTIONAL; | 
|  | 889 | if (eppconfig(bc)) | 
|  | 890 | printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); | 
|  | 891 | else | 
|  | 892 | bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; | 
|  | 893 | parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ | 
|  | 894 | /* reset the modem */ | 
|  | 895 | tmp[0] = 0; | 
|  | 896 | tmp[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; | 
|  | 897 | if (pp->ops->epp_write_addr(pp, tmp, 2, 0) != 2) | 
|  | 898 | goto epptimeout; | 
|  | 899 | /* autoprobe baud rate */ | 
|  | 900 | tstart = jiffies; | 
|  | 901 | i = 0; | 
| Marcelo Feitoza Parisi | ff5688a | 2006-01-09 18:37:15 -0800 | [diff] [blame] | 902 | while (time_before(jiffies, tstart + HZ/3)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 903 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 904 | goto epptimeout; | 
|  | 905 | if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { | 
|  | 906 | schedule(); | 
|  | 907 | continue; | 
|  | 908 | } | 
|  | 909 | if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) | 
|  | 910 | goto epptimeout; | 
|  | 911 | if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) | 
|  | 912 | goto epptimeout; | 
|  | 913 | i += 256; | 
|  | 914 | } | 
|  | 915 | for (j = 0; j < 256; j++) { | 
|  | 916 | if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) | 
|  | 917 | goto epptimeout; | 
|  | 918 | if (!(stat & EPP_NREF)) | 
|  | 919 | break; | 
|  | 920 | if (pp->ops->epp_read_data(pp, tmp, 1, 0) != 1) | 
|  | 921 | goto epptimeout; | 
|  | 922 | i++; | 
|  | 923 | } | 
|  | 924 | tstart = jiffies - tstart; | 
|  | 925 | bc->bitrate = i * (8 * HZ) / tstart; | 
|  | 926 | j = 1; | 
|  | 927 | i = bc->bitrate >> 3; | 
|  | 928 | while (j < 7 && i > 150) { | 
|  | 929 | j++; | 
|  | 930 | i >>= 1; | 
|  | 931 | } | 
|  | 932 | printk(KERN_INFO "%s: autoprobed bitrate: %d  int divider: %d  int rate: %d\n", | 
|  | 933 | bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); | 
|  | 934 | tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/; | 
|  | 935 | if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) | 
|  | 936 | goto epptimeout; | 
|  | 937 | /* | 
|  | 938 | * initialise hdlc variables | 
|  | 939 | */ | 
|  | 940 | bc->hdlcrx.state = 0; | 
|  | 941 | bc->hdlcrx.numbits = 0; | 
|  | 942 | bc->hdlctx.state = tx_idle; | 
|  | 943 | bc->hdlctx.bufcnt = 0; | 
|  | 944 | bc->hdlctx.slotcnt = bc->ch_params.slottime; | 
|  | 945 | bc->hdlctx.calibrate = 0; | 
|  | 946 | /* start the bottom half stuff */ | 
|  | 947 | schedule_delayed_work(&bc->run_work, 1); | 
|  | 948 | netif_start_queue(dev); | 
|  | 949 | return 0; | 
|  | 950 |  | 
|  | 951 | epptimeout: | 
|  | 952 | printk(KERN_ERR "%s: epp timeout during bitrate probe\n", bc_drvname); | 
|  | 953 | parport_write_control(pp, 0); /* reset the adapter */ | 
|  | 954 | parport_release(bc->pdev); | 
|  | 955 | parport_unregister_device(bc->pdev); | 
|  | 956 | return -EIO; | 
|  | 957 | } | 
|  | 958 |  | 
|  | 959 | /* --------------------------------------------------------------------- */ | 
|  | 960 |  | 
|  | 961 | static int epp_close(struct net_device *dev) | 
|  | 962 | { | 
|  | 963 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 964 | struct parport *pp = bc->pdev->port; | 
|  | 965 | unsigned char tmp[1]; | 
|  | 966 |  | 
|  | 967 | bc->work_running = 0; | 
|  | 968 | flush_scheduled_work(); | 
|  | 969 | bc->stat = EPP_DCDBIT; | 
|  | 970 | tmp[0] = 0; | 
|  | 971 | pp->ops->epp_write_addr(pp, tmp, 1, 0); | 
|  | 972 | parport_write_control(pp, 0); /* reset the adapter */ | 
|  | 973 | parport_release(bc->pdev); | 
|  | 974 | parport_unregister_device(bc->pdev); | 
|  | 975 | if (bc->skb) | 
|  | 976 | dev_kfree_skb(bc->skb); | 
|  | 977 | bc->skb = NULL; | 
|  | 978 | printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", | 
|  | 979 | bc_drvname, dev->base_addr, dev->irq); | 
|  | 980 | return 0; | 
|  | 981 | } | 
|  | 982 |  | 
|  | 983 | /* --------------------------------------------------------------------- */ | 
|  | 984 |  | 
|  | 985 | static int baycom_setmode(struct baycom_state *bc, const char *modestr) | 
|  | 986 | { | 
|  | 987 | const char *cp; | 
|  | 988 |  | 
|  | 989 | if (strstr(modestr,"intclk")) | 
|  | 990 | bc->cfg.intclk = 1; | 
|  | 991 | if (strstr(modestr,"extclk")) | 
|  | 992 | bc->cfg.intclk = 0; | 
|  | 993 | if (strstr(modestr,"intmodem")) | 
|  | 994 | bc->cfg.extmodem = 0; | 
|  | 995 | if (strstr(modestr,"extmodem")) | 
|  | 996 | bc->cfg.extmodem = 1; | 
|  | 997 | if (strstr(modestr,"noloopback")) | 
|  | 998 | bc->cfg.loopback = 0; | 
|  | 999 | if (strstr(modestr,"loopback")) | 
|  | 1000 | bc->cfg.loopback = 1; | 
|  | 1001 | if ((cp = strstr(modestr,"fclk="))) { | 
|  | 1002 | bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0); | 
|  | 1003 | if (bc->cfg.fclk < 1000000) | 
|  | 1004 | bc->cfg.fclk = 1000000; | 
|  | 1005 | if (bc->cfg.fclk > 25000000) | 
|  | 1006 | bc->cfg.fclk = 25000000; | 
|  | 1007 | } | 
|  | 1008 | if ((cp = strstr(modestr,"bps="))) { | 
|  | 1009 | bc->cfg.bps = simple_strtoul(cp+4, NULL, 0); | 
|  | 1010 | if (bc->cfg.bps < 1000) | 
|  | 1011 | bc->cfg.bps = 1000; | 
|  | 1012 | if (bc->cfg.bps > 1500000) | 
|  | 1013 | bc->cfg.bps = 1500000; | 
|  | 1014 | } | 
|  | 1015 | return 0; | 
|  | 1016 | } | 
|  | 1017 |  | 
|  | 1018 | /* --------------------------------------------------------------------- */ | 
|  | 1019 |  | 
|  | 1020 | static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 
|  | 1021 | { | 
|  | 1022 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 1023 | struct hdlcdrv_ioctl hi; | 
|  | 1024 |  | 
|  | 1025 | if (cmd != SIOCDEVPRIVATE) | 
|  | 1026 | return -ENOIOCTLCMD; | 
|  | 1027 |  | 
|  | 1028 | if (copy_from_user(&hi, ifr->ifr_data, sizeof(hi))) | 
|  | 1029 | return -EFAULT; | 
|  | 1030 | switch (hi.cmd) { | 
|  | 1031 | default: | 
|  | 1032 | return -ENOIOCTLCMD; | 
|  | 1033 |  | 
|  | 1034 | case HDLCDRVCTL_GETCHANNELPAR: | 
|  | 1035 | hi.data.cp.tx_delay = bc->ch_params.tx_delay; | 
|  | 1036 | hi.data.cp.tx_tail = bc->ch_params.tx_tail; | 
|  | 1037 | hi.data.cp.slottime = bc->ch_params.slottime; | 
|  | 1038 | hi.data.cp.ppersist = bc->ch_params.ppersist; | 
|  | 1039 | hi.data.cp.fulldup = bc->ch_params.fulldup; | 
|  | 1040 | break; | 
|  | 1041 |  | 
|  | 1042 | case HDLCDRVCTL_SETCHANNELPAR: | 
|  | 1043 | if (!capable(CAP_NET_ADMIN)) | 
|  | 1044 | return -EACCES; | 
|  | 1045 | bc->ch_params.tx_delay = hi.data.cp.tx_delay; | 
|  | 1046 | bc->ch_params.tx_tail = hi.data.cp.tx_tail; | 
|  | 1047 | bc->ch_params.slottime = hi.data.cp.slottime; | 
|  | 1048 | bc->ch_params.ppersist = hi.data.cp.ppersist; | 
|  | 1049 | bc->ch_params.fulldup = hi.data.cp.fulldup; | 
|  | 1050 | bc->hdlctx.slotcnt = 1; | 
|  | 1051 | return 0; | 
|  | 1052 |  | 
|  | 1053 | case HDLCDRVCTL_GETMODEMPAR: | 
|  | 1054 | hi.data.mp.iobase = dev->base_addr; | 
|  | 1055 | hi.data.mp.irq = dev->irq; | 
|  | 1056 | hi.data.mp.dma = dev->dma; | 
|  | 1057 | hi.data.mp.dma2 = 0; | 
|  | 1058 | hi.data.mp.seriobase = 0; | 
|  | 1059 | hi.data.mp.pariobase = 0; | 
|  | 1060 | hi.data.mp.midiiobase = 0; | 
|  | 1061 | break; | 
|  | 1062 |  | 
|  | 1063 | case HDLCDRVCTL_SETMODEMPAR: | 
|  | 1064 | if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) | 
|  | 1065 | return -EACCES; | 
|  | 1066 | dev->base_addr = hi.data.mp.iobase; | 
|  | 1067 | dev->irq = /*hi.data.mp.irq*/0; | 
|  | 1068 | dev->dma = /*hi.data.mp.dma*/0; | 
|  | 1069 | return 0; | 
|  | 1070 |  | 
|  | 1071 | case HDLCDRVCTL_GETSTAT: | 
|  | 1072 | hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); | 
|  | 1073 | hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); | 
|  | 1074 | hi.data.cs.ptt_keyed = bc->ptt_keyed; | 
|  | 1075 | hi.data.cs.tx_packets = bc->stats.tx_packets; | 
|  | 1076 | hi.data.cs.tx_errors = bc->stats.tx_errors; | 
|  | 1077 | hi.data.cs.rx_packets = bc->stats.rx_packets; | 
|  | 1078 | hi.data.cs.rx_errors = bc->stats.rx_errors; | 
|  | 1079 | break; | 
|  | 1080 |  | 
|  | 1081 | case HDLCDRVCTL_OLDGETSTAT: | 
|  | 1082 | hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); | 
|  | 1083 | hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); | 
|  | 1084 | hi.data.ocs.ptt_keyed = bc->ptt_keyed; | 
|  | 1085 | break; | 
|  | 1086 |  | 
|  | 1087 | case HDLCDRVCTL_CALIBRATE: | 
|  | 1088 | if (!capable(CAP_SYS_RAWIO)) | 
|  | 1089 | return -EACCES; | 
|  | 1090 | bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; | 
|  | 1091 | return 0; | 
|  | 1092 |  | 
|  | 1093 | case HDLCDRVCTL_DRIVERNAME: | 
|  | 1094 | strncpy(hi.data.drivername, "baycom_epp", sizeof(hi.data.drivername)); | 
|  | 1095 | break; | 
|  | 1096 |  | 
|  | 1097 | case HDLCDRVCTL_GETMODE: | 
|  | 1098 | sprintf(hi.data.modename, "%sclk,%smodem,fclk=%d,bps=%d%s", | 
|  | 1099 | bc->cfg.intclk ? "int" : "ext", | 
|  | 1100 | bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, | 
|  | 1101 | bc->cfg.loopback ? ",loopback" : ""); | 
|  | 1102 | break; | 
|  | 1103 |  | 
|  | 1104 | case HDLCDRVCTL_SETMODE: | 
|  | 1105 | if (!capable(CAP_NET_ADMIN) || netif_running(dev)) | 
|  | 1106 | return -EACCES; | 
|  | 1107 | hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; | 
|  | 1108 | return baycom_setmode(bc, hi.data.modename); | 
|  | 1109 |  | 
|  | 1110 | case HDLCDRVCTL_MODELIST: | 
|  | 1111 | strncpy(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x", | 
|  | 1112 | sizeof(hi.data.modename)); | 
|  | 1113 | break; | 
|  | 1114 |  | 
|  | 1115 | case HDLCDRVCTL_MODEMPARMASK: | 
|  | 1116 | return HDLCDRV_PARMASK_IOBASE; | 
|  | 1117 |  | 
|  | 1118 | } | 
|  | 1119 | if (copy_to_user(ifr->ifr_data, &hi, sizeof(hi))) | 
|  | 1120 | return -EFAULT; | 
|  | 1121 | return 0; | 
|  | 1122 | } | 
|  | 1123 |  | 
|  | 1124 | /* --------------------------------------------------------------------- */ | 
|  | 1125 |  | 
|  | 1126 | /* | 
|  | 1127 | * Check for a network adaptor of this type, and return '0' if one exists. | 
|  | 1128 | * If dev->base_addr == 0, probe all likely locations. | 
|  | 1129 | * If dev->base_addr == 1, always return failure. | 
|  | 1130 | * If dev->base_addr == 2, allocate space for the device and return success | 
|  | 1131 | * (detachable devices only). | 
|  | 1132 | */ | 
|  | 1133 | static void baycom_probe(struct net_device *dev) | 
|  | 1134 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1135 | const struct hdlcdrv_channel_params dflt_ch_params = { | 
|  | 1136 | 20, 2, 10, 40, 0 | 
|  | 1137 | }; | 
|  | 1138 | struct baycom_state *bc; | 
|  | 1139 |  | 
|  | 1140 | /* | 
|  | 1141 | * not a real probe! only initialize data structures | 
|  | 1142 | */ | 
|  | 1143 | bc = netdev_priv(dev); | 
|  | 1144 | /* | 
|  | 1145 | * initialize the baycom_state struct | 
|  | 1146 | */ | 
|  | 1147 | bc->ch_params = dflt_ch_params; | 
|  | 1148 | bc->ptt_keyed = 0; | 
|  | 1149 |  | 
|  | 1150 | /* | 
|  | 1151 | * initialize the device struct | 
|  | 1152 | */ | 
|  | 1153 | dev->open = epp_open; | 
|  | 1154 | dev->stop = epp_close; | 
|  | 1155 | dev->do_ioctl = baycom_ioctl; | 
|  | 1156 | dev->hard_start_xmit = baycom_send_packet; | 
|  | 1157 | dev->get_stats = baycom_get_stats; | 
|  | 1158 |  | 
|  | 1159 | /* Fill in the fields of the device structure */ | 
|  | 1160 | bc->skb = NULL; | 
|  | 1161 |  | 
| Ralf Baechle | 6f74998 | 2005-09-12 14:21:01 -0700 | [diff] [blame] | 1162 | dev->hard_header = ax25_hard_header; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1163 | dev->rebuild_header = ax25_rebuild_header; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1164 | dev->set_mac_address = baycom_set_mac_address; | 
|  | 1165 |  | 
|  | 1166 | dev->type = ARPHRD_AX25;           /* AF_AX25 device */ | 
|  | 1167 | dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; | 
|  | 1168 | dev->mtu = AX25_DEF_PACLEN;        /* eth_mtu is the default */ | 
|  | 1169 | dev->addr_len = AX25_ADDR_LEN;     /* sizeof an ax.25 address */ | 
| Ralf Baechle | 15b1c0e | 2006-12-07 15:47:08 -0800 | [diff] [blame] | 1170 | memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); | 
| Ralf Baechle | f654c85 | 2006-12-10 13:46:45 -0800 | [diff] [blame] | 1171 | memcpy(dev->dev_addr, &null_ax25_address, AX25_ADDR_LEN); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1172 | dev->tx_queue_len = 16; | 
|  | 1173 |  | 
|  | 1174 | /* New style flags */ | 
|  | 1175 | dev->flags = 0; | 
|  | 1176 | } | 
|  | 1177 |  | 
|  | 1178 | /* --------------------------------------------------------------------- */ | 
|  | 1179 |  | 
|  | 1180 | /* | 
|  | 1181 | * command line settable parameters | 
|  | 1182 | */ | 
|  | 1183 | static const char *mode[NR_PORTS] = { "", }; | 
|  | 1184 | static int iobase[NR_PORTS] = { 0x378, }; | 
|  | 1185 |  | 
|  | 1186 | module_param_array(mode, charp, NULL, 0); | 
|  | 1187 | MODULE_PARM_DESC(mode, "baycom operating mode"); | 
|  | 1188 | module_param_array(iobase, int, NULL, 0); | 
|  | 1189 | MODULE_PARM_DESC(iobase, "baycom io base address"); | 
|  | 1190 |  | 
|  | 1191 | MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); | 
|  | 1192 | MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); | 
|  | 1193 | MODULE_LICENSE("GPL"); | 
|  | 1194 |  | 
|  | 1195 | /* --------------------------------------------------------------------- */ | 
|  | 1196 |  | 
|  | 1197 | static void __init baycom_epp_dev_setup(struct net_device *dev) | 
|  | 1198 | { | 
|  | 1199 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 1200 |  | 
|  | 1201 | /* | 
|  | 1202 | * initialize part of the baycom_state struct | 
|  | 1203 | */ | 
| David Howells | c402895 | 2006-11-22 14:57:56 +0000 | [diff] [blame] | 1204 | bc->dev = dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1205 | bc->magic = BAYCOM_MAGIC; | 
|  | 1206 | bc->cfg.fclk = 19666600; | 
|  | 1207 | bc->cfg.bps = 9600; | 
|  | 1208 | /* | 
|  | 1209 | * initialize part of the device struct | 
|  | 1210 | */ | 
|  | 1211 | baycom_probe(dev); | 
|  | 1212 | } | 
|  | 1213 |  | 
|  | 1214 | static int __init init_baycomepp(void) | 
|  | 1215 | { | 
|  | 1216 | int i, found = 0; | 
|  | 1217 | char set_hw = 1; | 
|  | 1218 |  | 
|  | 1219 | printk(bc_drvinfo); | 
|  | 1220 | /* | 
|  | 1221 | * register net devices | 
|  | 1222 | */ | 
|  | 1223 | for (i = 0; i < NR_PORTS; i++) { | 
|  | 1224 | struct net_device *dev; | 
|  | 1225 |  | 
|  | 1226 | dev = alloc_netdev(sizeof(struct baycom_state), "bce%d", | 
|  | 1227 | baycom_epp_dev_setup); | 
|  | 1228 |  | 
|  | 1229 | if (!dev) { | 
|  | 1230 | printk(KERN_WARNING "bce%d : out of memory\n", i); | 
|  | 1231 | return found ? 0 : -ENOMEM; | 
|  | 1232 | } | 
|  | 1233 |  | 
|  | 1234 | sprintf(dev->name, "bce%d", i); | 
|  | 1235 | dev->base_addr = iobase[i]; | 
|  | 1236 |  | 
|  | 1237 | if (!mode[i]) | 
|  | 1238 | set_hw = 0; | 
|  | 1239 | if (!set_hw) | 
|  | 1240 | iobase[i] = 0; | 
|  | 1241 |  | 
|  | 1242 | if (register_netdev(dev)) { | 
|  | 1243 | printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, dev->name); | 
|  | 1244 | free_netdev(dev); | 
|  | 1245 | break; | 
|  | 1246 | } | 
|  | 1247 | if (set_hw && baycom_setmode(netdev_priv(dev), mode[i])) | 
|  | 1248 | set_hw = 0; | 
|  | 1249 | baycom_device[i] = dev; | 
|  | 1250 | found++; | 
|  | 1251 | } | 
|  | 1252 |  | 
|  | 1253 | return found ? 0 : -ENXIO; | 
|  | 1254 | } | 
|  | 1255 |  | 
|  | 1256 | static void __exit cleanup_baycomepp(void) | 
|  | 1257 | { | 
|  | 1258 | int i; | 
|  | 1259 |  | 
|  | 1260 | for(i = 0; i < NR_PORTS; i++) { | 
|  | 1261 | struct net_device *dev = baycom_device[i]; | 
|  | 1262 |  | 
|  | 1263 | if (dev) { | 
|  | 1264 | struct baycom_state *bc = netdev_priv(dev); | 
|  | 1265 | if (bc->magic == BAYCOM_MAGIC) { | 
|  | 1266 | unregister_netdev(dev); | 
|  | 1267 | free_netdev(dev); | 
|  | 1268 | } else | 
|  | 1269 | printk(paranoia_str, "cleanup_module"); | 
|  | 1270 | } | 
|  | 1271 | } | 
|  | 1272 | } | 
|  | 1273 |  | 
|  | 1274 | module_init(init_baycomepp); | 
|  | 1275 | module_exit(cleanup_baycomepp); | 
|  | 1276 |  | 
|  | 1277 | /* --------------------------------------------------------------------- */ | 
|  | 1278 |  | 
|  | 1279 | #ifndef MODULE | 
|  | 1280 |  | 
|  | 1281 | /* | 
|  | 1282 | * format: baycom_epp=io,mode | 
|  | 1283 | * mode: fpga config options | 
|  | 1284 | */ | 
|  | 1285 |  | 
|  | 1286 | static int __init baycom_epp_setup(char *str) | 
|  | 1287 | { | 
|  | 1288 | static unsigned __initdata nr_dev = 0; | 
|  | 1289 | int ints[2]; | 
|  | 1290 |  | 
|  | 1291 | if (nr_dev >= NR_PORTS) | 
|  | 1292 | return 0; | 
|  | 1293 | str = get_options(str, 2, ints); | 
|  | 1294 | if (ints[0] < 1) | 
|  | 1295 | return 0; | 
|  | 1296 | mode[nr_dev] = str; | 
|  | 1297 | iobase[nr_dev] = ints[1]; | 
|  | 1298 | nr_dev++; | 
|  | 1299 | return 1; | 
|  | 1300 | } | 
|  | 1301 |  | 
|  | 1302 | __setup("baycom_epp=", baycom_epp_setup); | 
|  | 1303 |  | 
|  | 1304 | #endif /* MODULE */ | 
|  | 1305 | /* --------------------------------------------------------------------- */ |