xref: /OK3568_Linux_fs/kernel/drivers/net/hamradio/baycom_ser_hdx.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*****************************************************************************/
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun /*
5*4882a593Smuzhiyun  *	baycom_ser_hdx.c  -- baycom ser12 halfduplex radio modem driver.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *	Copyright (C) 1996-2000  Thomas Sailer (sailer@ife.ee.ethz.ch)
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *  Please note that the GPL allows you to use the driver, NOT the radio.
10*4882a593Smuzhiyun  *  In order to use the radio, you need a license from the communications
11*4882a593Smuzhiyun  *  authority of your country.
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  *  Supported modems
14*4882a593Smuzhiyun  *
15*4882a593Smuzhiyun  *  ser12:  This is a very simple 1200 baud AFSK modem. The modem consists only
16*4882a593Smuzhiyun  *          of a modulator/demodulator chip, usually a TI TCM3105. The computer
17*4882a593Smuzhiyun  *          is responsible for regenerating the receiver bit clock, as well as
18*4882a593Smuzhiyun  *          for handling the HDLC protocol. The modem connects to a serial port,
19*4882a593Smuzhiyun  *          hence the name. Since the serial port is not used as an async serial
20*4882a593Smuzhiyun  *          port, the kernel driver for serial ports cannot be used, and this
21*4882a593Smuzhiyun  *          driver only supports standard serial hardware (8250, 16450, 16550A)
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  *  Command line options (insmod command line)
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  *  mode     ser12    hardware DCD
26*4882a593Smuzhiyun  *           ser12*   software DCD
27*4882a593Smuzhiyun  *           ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
28*4882a593Smuzhiyun  *                    mutes audio input to the modem
29*4882a593Smuzhiyun  *           ser12+   hardware DCD, inverted signal at DCD pin
30*4882a593Smuzhiyun  *  iobase   base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8
31*4882a593Smuzhiyun  *  irq      interrupt line of the port; common values are 4,3
32*4882a593Smuzhiyun  *
33*4882a593Smuzhiyun  *  History:
34*4882a593Smuzhiyun  *   0.1  26.06.1996  Adapted from baycom.c and made network driver interface
35*4882a593Smuzhiyun  *        18.10.1996  Changed to new user space access routines (copy_{to,from}_user)
36*4882a593Smuzhiyun  *   0.3  26.04.1997  init code/data tagged
37*4882a593Smuzhiyun  *   0.4  08.07.1997  alternative ser12 decoding algorithm (uses delta CTS ints)
38*4882a593Smuzhiyun  *   0.5  11.11.1997  ser12/par96 split into separate files
39*4882a593Smuzhiyun  *   0.6  14.04.1998  cleanups
40*4882a593Smuzhiyun  *   0.7  03.08.1999  adapt to Linus' new __setup/__initcall
41*4882a593Smuzhiyun  *   0.8  10.08.1999  use module_init/module_exit
42*4882a593Smuzhiyun  *   0.9  12.02.2000  adapted to softnet driver interface
43*4882a593Smuzhiyun  *   0.10 03.07.2000  fix interface name handling
44*4882a593Smuzhiyun  */
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun /*****************************************************************************/
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun #include <linux/capability.h>
49*4882a593Smuzhiyun #include <linux/module.h>
50*4882a593Smuzhiyun #include <linux/ioport.h>
51*4882a593Smuzhiyun #include <linux/string.h>
52*4882a593Smuzhiyun #include <linux/init.h>
53*4882a593Smuzhiyun #include <linux/interrupt.h>
54*4882a593Smuzhiyun #include <linux/uaccess.h>
55*4882a593Smuzhiyun #include <asm/io.h>
56*4882a593Smuzhiyun #include <linux/hdlcdrv.h>
57*4882a593Smuzhiyun #include <linux/baycom.h>
58*4882a593Smuzhiyun #include <linux/jiffies.h>
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun #define BAYCOM_DEBUG
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun static const char bc_drvname[] = "baycom_ser_hdx";
67*4882a593Smuzhiyun static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
68*4882a593Smuzhiyun "baycom_ser_hdx: version 0.10\n";
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #define NR_PORTS 4
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static struct net_device *baycom_device[NR_PORTS];
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun #define RBR(iobase) (iobase+0)
79*4882a593Smuzhiyun #define THR(iobase) (iobase+0)
80*4882a593Smuzhiyun #define IER(iobase) (iobase+1)
81*4882a593Smuzhiyun #define IIR(iobase) (iobase+2)
82*4882a593Smuzhiyun #define FCR(iobase) (iobase+2)
83*4882a593Smuzhiyun #define LCR(iobase) (iobase+3)
84*4882a593Smuzhiyun #define MCR(iobase) (iobase+4)
85*4882a593Smuzhiyun #define LSR(iobase) (iobase+5)
86*4882a593Smuzhiyun #define MSR(iobase) (iobase+6)
87*4882a593Smuzhiyun #define SCR(iobase) (iobase+7)
88*4882a593Smuzhiyun #define DLL(iobase) (iobase+0)
89*4882a593Smuzhiyun #define DLM(iobase) (iobase+1)
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun #define SER12_EXTENT 8
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun /* ---------------------------------------------------------------------- */
94*4882a593Smuzhiyun /*
95*4882a593Smuzhiyun  * Information that need to be kept for each board.
96*4882a593Smuzhiyun  */
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun struct baycom_state {
99*4882a593Smuzhiyun 	struct hdlcdrv_state hdrv;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	int opt_dcd;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	struct modem_state {
104*4882a593Smuzhiyun 		short arb_divider;
105*4882a593Smuzhiyun 		unsigned char flags;
106*4882a593Smuzhiyun 		unsigned int shreg;
107*4882a593Smuzhiyun 		struct modem_state_ser12 {
108*4882a593Smuzhiyun 			unsigned char tx_bit;
109*4882a593Smuzhiyun 			int dcd_sum0, dcd_sum1, dcd_sum2;
110*4882a593Smuzhiyun 			unsigned char last_sample;
111*4882a593Smuzhiyun 			unsigned char last_rxbit;
112*4882a593Smuzhiyun 			unsigned int dcd_shreg;
113*4882a593Smuzhiyun 			unsigned int dcd_time;
114*4882a593Smuzhiyun 			unsigned int bit_pll;
115*4882a593Smuzhiyun 			unsigned char interm_sample;
116*4882a593Smuzhiyun 		} ser12;
117*4882a593Smuzhiyun 	} modem;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
120*4882a593Smuzhiyun 	struct debug_vals {
121*4882a593Smuzhiyun 		unsigned long last_jiffies;
122*4882a593Smuzhiyun 		unsigned cur_intcnt;
123*4882a593Smuzhiyun 		unsigned last_intcnt;
124*4882a593Smuzhiyun 		int cur_pllcorr;
125*4882a593Smuzhiyun 		int last_pllcorr;
126*4882a593Smuzhiyun 	} debug_vals;
127*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
128*4882a593Smuzhiyun };
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
131*4882a593Smuzhiyun 
baycom_int_freq(struct baycom_state * bc)132*4882a593Smuzhiyun static inline void baycom_int_freq(struct baycom_state *bc)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
135*4882a593Smuzhiyun 	unsigned long cur_jiffies = jiffies;
136*4882a593Smuzhiyun 	/*
137*4882a593Smuzhiyun 	 * measure the interrupt frequency
138*4882a593Smuzhiyun 	 */
139*4882a593Smuzhiyun 	bc->debug_vals.cur_intcnt++;
140*4882a593Smuzhiyun 	if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) {
141*4882a593Smuzhiyun 		bc->debug_vals.last_jiffies = cur_jiffies;
142*4882a593Smuzhiyun 		bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt;
143*4882a593Smuzhiyun 		bc->debug_vals.cur_intcnt = 0;
144*4882a593Smuzhiyun 		bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr;
145*4882a593Smuzhiyun 		bc->debug_vals.cur_pllcorr = 0;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun  * ===================== SER12 specific routines =========================
153*4882a593Smuzhiyun  */
154*4882a593Smuzhiyun 
ser12_set_divisor(struct net_device * dev,unsigned char divisor)155*4882a593Smuzhiyun static inline void ser12_set_divisor(struct net_device *dev,
156*4882a593Smuzhiyun 				     unsigned char divisor)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	outb(0x81, LCR(dev->base_addr));	/* DLAB = 1 */
159*4882a593Smuzhiyun 	outb(divisor, DLL(dev->base_addr));
160*4882a593Smuzhiyun 	outb(0, DLM(dev->base_addr));
161*4882a593Smuzhiyun 	outb(0x01, LCR(dev->base_addr));	/* word length = 6 */
162*4882a593Smuzhiyun 	/*
163*4882a593Smuzhiyun 	 * make sure the next interrupt is generated;
164*4882a593Smuzhiyun 	 * 0 must be used to power the modem; the modem draws its
165*4882a593Smuzhiyun 	 * power from the TxD line
166*4882a593Smuzhiyun 	 */
167*4882a593Smuzhiyun 	outb(0x00, THR(dev->base_addr));
168*4882a593Smuzhiyun 	/*
169*4882a593Smuzhiyun 	 * it is important not to set the divider while transmitting;
170*4882a593Smuzhiyun 	 * this reportedly makes some UARTs generating interrupts
171*4882a593Smuzhiyun 	 * in the hundredthousands per second region
172*4882a593Smuzhiyun 	 * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno)
173*4882a593Smuzhiyun 	 */
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun /*
179*4882a593Smuzhiyun  * must call the TX arbitrator every 10ms
180*4882a593Smuzhiyun  */
181*4882a593Smuzhiyun #define SER12_ARB_DIVIDER(bc)  (bc->opt_dcd ? 24 : 36)
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun #define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240)
184*4882a593Smuzhiyun 
ser12_tx(struct net_device * dev,struct baycom_state * bc)185*4882a593Smuzhiyun static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	/* one interrupt per channel bit */
188*4882a593Smuzhiyun 	ser12_set_divisor(dev, 12);
189*4882a593Smuzhiyun 	/*
190*4882a593Smuzhiyun 	 * first output the last bit (!) then call HDLC transmitter,
191*4882a593Smuzhiyun 	 * since this may take quite long
192*4882a593Smuzhiyun 	 */
193*4882a593Smuzhiyun 	outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr));
194*4882a593Smuzhiyun 	if (bc->modem.shreg <= 1)
195*4882a593Smuzhiyun 		bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv);
196*4882a593Smuzhiyun 	bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^
197*4882a593Smuzhiyun 				   (bc->modem.shreg & 1));
198*4882a593Smuzhiyun 	bc->modem.shreg >>= 1;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
202*4882a593Smuzhiyun 
ser12_rx(struct net_device * dev,struct baycom_state * bc)203*4882a593Smuzhiyun static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	unsigned char cur_s;
206*4882a593Smuzhiyun 	/*
207*4882a593Smuzhiyun 	 * do demodulator
208*4882a593Smuzhiyun 	 */
209*4882a593Smuzhiyun 	cur_s = inb(MSR(dev->base_addr)) & 0x10;	/* the CTS line */
210*4882a593Smuzhiyun 	hdlcdrv_channelbit(&bc->hdrv, cur_s);
211*4882a593Smuzhiyun 	bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) |
212*4882a593Smuzhiyun 		(cur_s != bc->modem.ser12.last_sample);
213*4882a593Smuzhiyun 	bc->modem.ser12.last_sample = cur_s;
214*4882a593Smuzhiyun 	if(bc->modem.ser12.dcd_shreg & 1) {
215*4882a593Smuzhiyun 		if (!bc->opt_dcd) {
216*4882a593Smuzhiyun 			unsigned int dcdspos, dcdsneg;
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 			dcdspos = dcdsneg = 0;
219*4882a593Smuzhiyun 			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
220*4882a593Smuzhiyun 			if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
221*4882a593Smuzhiyun 				dcdspos += 2;
222*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
223*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
224*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
227*4882a593Smuzhiyun 		} else
228*4882a593Smuzhiyun 			bc->modem.ser12.dcd_sum0--;
229*4882a593Smuzhiyun 	}
230*4882a593Smuzhiyun 	if(!bc->modem.ser12.dcd_time) {
231*4882a593Smuzhiyun 		hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
232*4882a593Smuzhiyun 					   bc->modem.ser12.dcd_sum1 +
233*4882a593Smuzhiyun 					   bc->modem.ser12.dcd_sum2) < 0);
234*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
235*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
236*4882a593Smuzhiyun 		/* offset to ensure DCD off on silent input */
237*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum0 = 2;
238*4882a593Smuzhiyun 		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 	bc->modem.ser12.dcd_time--;
241*4882a593Smuzhiyun 	if (!bc->opt_dcd) {
242*4882a593Smuzhiyun 		/*
243*4882a593Smuzhiyun 		 * PLL code for the improved software DCD algorithm
244*4882a593Smuzhiyun 		 */
245*4882a593Smuzhiyun 		if (bc->modem.ser12.interm_sample) {
246*4882a593Smuzhiyun 			/*
247*4882a593Smuzhiyun 			 * intermediate sample; set timing correction to normal
248*4882a593Smuzhiyun 			 */
249*4882a593Smuzhiyun 			ser12_set_divisor(dev, 4);
250*4882a593Smuzhiyun 		} else {
251*4882a593Smuzhiyun 			/*
252*4882a593Smuzhiyun 			 * do PLL correction and call HDLC receiver
253*4882a593Smuzhiyun 			 */
254*4882a593Smuzhiyun 			switch (bc->modem.ser12.dcd_shreg & 7) {
255*4882a593Smuzhiyun 			case 1: /* transition too late */
256*4882a593Smuzhiyun 				ser12_set_divisor(dev, 5);
257*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
258*4882a593Smuzhiyun 				bc->debug_vals.cur_pllcorr++;
259*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
260*4882a593Smuzhiyun 				break;
261*4882a593Smuzhiyun 			case 4:	/* transition too early */
262*4882a593Smuzhiyun 				ser12_set_divisor(dev, 3);
263*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
264*4882a593Smuzhiyun 				bc->debug_vals.cur_pllcorr--;
265*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
266*4882a593Smuzhiyun 				break;
267*4882a593Smuzhiyun 			default:
268*4882a593Smuzhiyun 				ser12_set_divisor(dev, 4);
269*4882a593Smuzhiyun 				break;
270*4882a593Smuzhiyun 			}
271*4882a593Smuzhiyun 			bc->modem.shreg >>= 1;
272*4882a593Smuzhiyun 			if (bc->modem.ser12.last_sample ==
273*4882a593Smuzhiyun 			    bc->modem.ser12.last_rxbit)
274*4882a593Smuzhiyun 				bc->modem.shreg |= 0x10000;
275*4882a593Smuzhiyun 			bc->modem.ser12.last_rxbit =
276*4882a593Smuzhiyun 				bc->modem.ser12.last_sample;
277*4882a593Smuzhiyun 		}
278*4882a593Smuzhiyun 		if (++bc->modem.ser12.interm_sample >= 3)
279*4882a593Smuzhiyun 			bc->modem.ser12.interm_sample = 0;
280*4882a593Smuzhiyun 		/*
281*4882a593Smuzhiyun 		 * DCD stuff
282*4882a593Smuzhiyun 		 */
283*4882a593Smuzhiyun 		if (bc->modem.ser12.dcd_shreg & 1) {
284*4882a593Smuzhiyun 			unsigned int dcdspos, dcdsneg;
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 			dcdspos = dcdsneg = 0;
287*4882a593Smuzhiyun 			dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1);
288*4882a593Smuzhiyun 			dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe))
289*4882a593Smuzhiyun 				<< 1;
290*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1);
291*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1);
292*4882a593Smuzhiyun 			dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1);
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 			bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg;
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 	} else {
297*4882a593Smuzhiyun 		/*
298*4882a593Smuzhiyun 		 * PLL algorithm for the hardware squelch DCD algorithm
299*4882a593Smuzhiyun 		 */
300*4882a593Smuzhiyun 		if (bc->modem.ser12.interm_sample) {
301*4882a593Smuzhiyun 			/*
302*4882a593Smuzhiyun 			 * intermediate sample; set timing correction to normal
303*4882a593Smuzhiyun 			 */
304*4882a593Smuzhiyun 			ser12_set_divisor(dev, 6);
305*4882a593Smuzhiyun 		} else {
306*4882a593Smuzhiyun 			/*
307*4882a593Smuzhiyun 			 * do PLL correction and call HDLC receiver
308*4882a593Smuzhiyun 			 */
309*4882a593Smuzhiyun 			switch (bc->modem.ser12.dcd_shreg & 3) {
310*4882a593Smuzhiyun 			case 1: /* transition too late */
311*4882a593Smuzhiyun 				ser12_set_divisor(dev, 7);
312*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
313*4882a593Smuzhiyun 				bc->debug_vals.cur_pllcorr++;
314*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
315*4882a593Smuzhiyun 				break;
316*4882a593Smuzhiyun 			case 2:	/* transition too early */
317*4882a593Smuzhiyun 				ser12_set_divisor(dev, 5);
318*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
319*4882a593Smuzhiyun 				bc->debug_vals.cur_pllcorr--;
320*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
321*4882a593Smuzhiyun 				break;
322*4882a593Smuzhiyun 			default:
323*4882a593Smuzhiyun 				ser12_set_divisor(dev, 6);
324*4882a593Smuzhiyun 				break;
325*4882a593Smuzhiyun 			}
326*4882a593Smuzhiyun 			bc->modem.shreg >>= 1;
327*4882a593Smuzhiyun 			if (bc->modem.ser12.last_sample ==
328*4882a593Smuzhiyun 			    bc->modem.ser12.last_rxbit)
329*4882a593Smuzhiyun 				bc->modem.shreg |= 0x10000;
330*4882a593Smuzhiyun 			bc->modem.ser12.last_rxbit =
331*4882a593Smuzhiyun 				bc->modem.ser12.last_sample;
332*4882a593Smuzhiyun 		}
333*4882a593Smuzhiyun 		bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample;
334*4882a593Smuzhiyun 		/*
335*4882a593Smuzhiyun 		 * DCD stuff
336*4882a593Smuzhiyun 		 */
337*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1);
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 	outb(0x0d, MCR(dev->base_addr));		/* transmitter off */
340*4882a593Smuzhiyun 	if (bc->modem.shreg & 1) {
341*4882a593Smuzhiyun 		hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1);
342*4882a593Smuzhiyun 		bc->modem.shreg = 0x10000;
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 	if(!bc->modem.ser12.dcd_time) {
345*4882a593Smuzhiyun 		if (bc->opt_dcd & 1)
346*4882a593Smuzhiyun 			hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80));
347*4882a593Smuzhiyun 		else
348*4882a593Smuzhiyun 			hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 +
349*4882a593Smuzhiyun 						   bc->modem.ser12.dcd_sum1 +
350*4882a593Smuzhiyun 						   bc->modem.ser12.dcd_sum2) < 0);
351*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1;
352*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0;
353*4882a593Smuzhiyun 		/* offset to ensure DCD off on silent input */
354*4882a593Smuzhiyun 		bc->modem.ser12.dcd_sum0 = 2;
355*4882a593Smuzhiyun 		bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc);
356*4882a593Smuzhiyun 	}
357*4882a593Smuzhiyun 	bc->modem.ser12.dcd_time--;
358*4882a593Smuzhiyun }
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
361*4882a593Smuzhiyun 
ser12_interrupt(int irq,void * dev_id)362*4882a593Smuzhiyun static irqreturn_t ser12_interrupt(int irq, void *dev_id)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	struct net_device *dev = (struct net_device *)dev_id;
365*4882a593Smuzhiyun 	struct baycom_state *bc = netdev_priv(dev);
366*4882a593Smuzhiyun 	unsigned char iir;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC)
369*4882a593Smuzhiyun 		return IRQ_NONE;
370*4882a593Smuzhiyun 	/* fast way out */
371*4882a593Smuzhiyun 	if ((iir = inb(IIR(dev->base_addr))) & 1)
372*4882a593Smuzhiyun 		return IRQ_NONE;
373*4882a593Smuzhiyun 	baycom_int_freq(bc);
374*4882a593Smuzhiyun 	do {
375*4882a593Smuzhiyun 		switch (iir & 6) {
376*4882a593Smuzhiyun 		case 6:
377*4882a593Smuzhiyun 			inb(LSR(dev->base_addr));
378*4882a593Smuzhiyun 			break;
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 		case 4:
381*4882a593Smuzhiyun 			inb(RBR(dev->base_addr));
382*4882a593Smuzhiyun 			break;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 		case 2:
385*4882a593Smuzhiyun 			/*
386*4882a593Smuzhiyun 			 * check if transmitter active
387*4882a593Smuzhiyun 			 */
388*4882a593Smuzhiyun 			if (hdlcdrv_ptt(&bc->hdrv))
389*4882a593Smuzhiyun 				ser12_tx(dev, bc);
390*4882a593Smuzhiyun 			else {
391*4882a593Smuzhiyun 				ser12_rx(dev, bc);
392*4882a593Smuzhiyun 				bc->modem.arb_divider--;
393*4882a593Smuzhiyun 			}
394*4882a593Smuzhiyun 			outb(0x00, THR(dev->base_addr));
395*4882a593Smuzhiyun 			break;
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 		default:
398*4882a593Smuzhiyun 			inb(MSR(dev->base_addr));
399*4882a593Smuzhiyun 			break;
400*4882a593Smuzhiyun 		}
401*4882a593Smuzhiyun 		iir = inb(IIR(dev->base_addr));
402*4882a593Smuzhiyun 	} while (!(iir & 1));
403*4882a593Smuzhiyun 	if (bc->modem.arb_divider <= 0) {
404*4882a593Smuzhiyun 		bc->modem.arb_divider = SER12_ARB_DIVIDER(bc);
405*4882a593Smuzhiyun 		local_irq_enable();
406*4882a593Smuzhiyun 		hdlcdrv_arbitrate(dev, &bc->hdrv);
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 	local_irq_enable();
409*4882a593Smuzhiyun 	hdlcdrv_transmitter(dev, &bc->hdrv);
410*4882a593Smuzhiyun 	hdlcdrv_receiver(dev, &bc->hdrv);
411*4882a593Smuzhiyun 	local_irq_disable();
412*4882a593Smuzhiyun 	return IRQ_HANDLED;
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
416*4882a593Smuzhiyun 
417*4882a593Smuzhiyun enum uart { c_uart_unknown, c_uart_8250,
418*4882a593Smuzhiyun 	    c_uart_16450, c_uart_16550, c_uart_16550A};
419*4882a593Smuzhiyun static const char *uart_str[] = {
420*4882a593Smuzhiyun 	"unknown", "8250", "16450", "16550", "16550A"
421*4882a593Smuzhiyun };
422*4882a593Smuzhiyun 
ser12_check_uart(unsigned int iobase)423*4882a593Smuzhiyun static enum uart ser12_check_uart(unsigned int iobase)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun 	unsigned char b1,b2,b3;
426*4882a593Smuzhiyun 	enum uart u;
427*4882a593Smuzhiyun 	enum uart uart_tab[] =
428*4882a593Smuzhiyun 		{ c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	b1 = inb(MCR(iobase));
431*4882a593Smuzhiyun 	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
432*4882a593Smuzhiyun 	b2 = inb(MSR(iobase));
433*4882a593Smuzhiyun 	outb(0x1a, MCR(iobase));
434*4882a593Smuzhiyun 	b3 = inb(MSR(iobase)) & 0xf0;
435*4882a593Smuzhiyun 	outb(b1, MCR(iobase));			/* restore old values */
436*4882a593Smuzhiyun 	outb(b2, MSR(iobase));
437*4882a593Smuzhiyun 	if (b3 != 0x90)
438*4882a593Smuzhiyun 		return c_uart_unknown;
439*4882a593Smuzhiyun 	inb(RBR(iobase));
440*4882a593Smuzhiyun 	inb(RBR(iobase));
441*4882a593Smuzhiyun 	outb(0x01, FCR(iobase));		/* enable FIFOs */
442*4882a593Smuzhiyun 	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
443*4882a593Smuzhiyun 	if (u == c_uart_16450) {
444*4882a593Smuzhiyun 		outb(0x5a, SCR(iobase));
445*4882a593Smuzhiyun 		b1 = inb(SCR(iobase));
446*4882a593Smuzhiyun 		outb(0xa5, SCR(iobase));
447*4882a593Smuzhiyun 		b2 = inb(SCR(iobase));
448*4882a593Smuzhiyun 		if ((b1 != 0x5a) || (b2 != 0xa5))
449*4882a593Smuzhiyun 			u = c_uart_8250;
450*4882a593Smuzhiyun 	}
451*4882a593Smuzhiyun 	return u;
452*4882a593Smuzhiyun }
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
455*4882a593Smuzhiyun 
ser12_open(struct net_device * dev)456*4882a593Smuzhiyun static int ser12_open(struct net_device *dev)
457*4882a593Smuzhiyun {
458*4882a593Smuzhiyun 	struct baycom_state *bc = netdev_priv(dev);
459*4882a593Smuzhiyun 	enum uart u;
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	if (!dev || !bc)
462*4882a593Smuzhiyun 		return -ENXIO;
463*4882a593Smuzhiyun 	if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT ||
464*4882a593Smuzhiyun 	    dev->irq < 2 || dev->irq > 15)
465*4882a593Smuzhiyun 		return -ENXIO;
466*4882a593Smuzhiyun 	if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12"))
467*4882a593Smuzhiyun 		return -EACCES;
468*4882a593Smuzhiyun 	memset(&bc->modem, 0, sizeof(bc->modem));
469*4882a593Smuzhiyun 	bc->hdrv.par.bitrate = 1200;
470*4882a593Smuzhiyun 	if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) {
471*4882a593Smuzhiyun 		release_region(dev->base_addr, SER12_EXTENT);
472*4882a593Smuzhiyun 		return -EIO;
473*4882a593Smuzhiyun 	}
474*4882a593Smuzhiyun 	outb(0, FCR(dev->base_addr));  /* disable FIFOs */
475*4882a593Smuzhiyun 	outb(0x0d, MCR(dev->base_addr));
476*4882a593Smuzhiyun 	outb(0, IER(dev->base_addr));
477*4882a593Smuzhiyun 	if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED,
478*4882a593Smuzhiyun 			"baycom_ser12", dev)) {
479*4882a593Smuzhiyun 		release_region(dev->base_addr, SER12_EXTENT);
480*4882a593Smuzhiyun 		return -EBUSY;
481*4882a593Smuzhiyun 	}
482*4882a593Smuzhiyun 	/*
483*4882a593Smuzhiyun 	 * enable transmitter empty interrupt
484*4882a593Smuzhiyun 	 */
485*4882a593Smuzhiyun 	outb(2, IER(dev->base_addr));
486*4882a593Smuzhiyun 	/*
487*4882a593Smuzhiyun 	 * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that
488*4882a593Smuzhiyun 	 * we get exactly (hopefully) 2 or 3 interrupts per radio symbol,
489*4882a593Smuzhiyun 	 * depending on the usage of the software DCD routine
490*4882a593Smuzhiyun 	 */
491*4882a593Smuzhiyun 	ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4);
492*4882a593Smuzhiyun 	printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n",
493*4882a593Smuzhiyun 	       bc_drvname, dev->base_addr, dev->irq, uart_str[u]);
494*4882a593Smuzhiyun 	return 0;
495*4882a593Smuzhiyun }
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
498*4882a593Smuzhiyun 
ser12_close(struct net_device * dev)499*4882a593Smuzhiyun static int ser12_close(struct net_device *dev)
500*4882a593Smuzhiyun {
501*4882a593Smuzhiyun 	struct baycom_state *bc = netdev_priv(dev);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	if (!dev || !bc)
504*4882a593Smuzhiyun 		return -EINVAL;
505*4882a593Smuzhiyun 	/*
506*4882a593Smuzhiyun 	 * disable interrupts
507*4882a593Smuzhiyun 	 */
508*4882a593Smuzhiyun 	outb(0, IER(dev->base_addr));
509*4882a593Smuzhiyun 	outb(1, MCR(dev->base_addr));
510*4882a593Smuzhiyun 	free_irq(dev->irq, dev);
511*4882a593Smuzhiyun 	release_region(dev->base_addr, SER12_EXTENT);
512*4882a593Smuzhiyun 	printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n",
513*4882a593Smuzhiyun 	       bc_drvname, dev->base_addr, dev->irq);
514*4882a593Smuzhiyun 	return 0;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
518*4882a593Smuzhiyun /*
519*4882a593Smuzhiyun  * ===================== hdlcdrv driver interface =========================
520*4882a593Smuzhiyun  */
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
525*4882a593Smuzhiyun 			struct hdlcdrv_ioctl *hi, int cmd);
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun static const struct hdlcdrv_ops ser12_ops = {
530*4882a593Smuzhiyun 	.drvname = bc_drvname,
531*4882a593Smuzhiyun 	.drvinfo = bc_drvinfo,
532*4882a593Smuzhiyun 	.open    = ser12_open,
533*4882a593Smuzhiyun 	.close   = ser12_close,
534*4882a593Smuzhiyun 	.ioctl   = baycom_ioctl,
535*4882a593Smuzhiyun };
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
538*4882a593Smuzhiyun 
baycom_setmode(struct baycom_state * bc,const char * modestr)539*4882a593Smuzhiyun static int baycom_setmode(struct baycom_state *bc, const char *modestr)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun 	if (strchr(modestr, '*'))
542*4882a593Smuzhiyun 		bc->opt_dcd = 0;
543*4882a593Smuzhiyun 	else if (strchr(modestr, '+'))
544*4882a593Smuzhiyun 		bc->opt_dcd = -1;
545*4882a593Smuzhiyun 	else if (strchr(modestr, '@'))
546*4882a593Smuzhiyun 		bc->opt_dcd = -2;
547*4882a593Smuzhiyun 	else
548*4882a593Smuzhiyun 		bc->opt_dcd = 1;
549*4882a593Smuzhiyun 	return 0;
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
553*4882a593Smuzhiyun 
baycom_ioctl(struct net_device * dev,struct ifreq * ifr,struct hdlcdrv_ioctl * hi,int cmd)554*4882a593Smuzhiyun static int baycom_ioctl(struct net_device *dev, struct ifreq *ifr,
555*4882a593Smuzhiyun 			struct hdlcdrv_ioctl *hi, int cmd)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun 	struct baycom_state *bc;
558*4882a593Smuzhiyun 	struct baycom_ioctl bi;
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	if (!dev)
561*4882a593Smuzhiyun 		return -EINVAL;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	bc = netdev_priv(dev);
564*4882a593Smuzhiyun 	BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC);
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	if (cmd != SIOCDEVPRIVATE)
567*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
568*4882a593Smuzhiyun 	switch (hi->cmd) {
569*4882a593Smuzhiyun 	default:
570*4882a593Smuzhiyun 		break;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	case HDLCDRVCTL_GETMODE:
573*4882a593Smuzhiyun 		strcpy(hi->data.modename, "ser12");
574*4882a593Smuzhiyun 		if (bc->opt_dcd <= 0)
575*4882a593Smuzhiyun 			strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+");
576*4882a593Smuzhiyun 		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
577*4882a593Smuzhiyun 			return -EFAULT;
578*4882a593Smuzhiyun 		return 0;
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	case HDLCDRVCTL_SETMODE:
581*4882a593Smuzhiyun 		if (netif_running(dev) || !capable(CAP_NET_ADMIN))
582*4882a593Smuzhiyun 			return -EACCES;
583*4882a593Smuzhiyun 		hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
584*4882a593Smuzhiyun 		return baycom_setmode(bc, hi->data.modename);
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	case HDLCDRVCTL_MODELIST:
587*4882a593Smuzhiyun 		strcpy(hi->data.modename, "ser12");
588*4882a593Smuzhiyun 		if (copy_to_user(ifr->ifr_data, hi, sizeof(struct hdlcdrv_ioctl)))
589*4882a593Smuzhiyun 			return -EFAULT;
590*4882a593Smuzhiyun 		return 0;
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 	case HDLCDRVCTL_MODEMPARMASK:
593*4882a593Smuzhiyun 		return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	}
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 	if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
598*4882a593Smuzhiyun 		return -EFAULT;
599*4882a593Smuzhiyun 	switch (bi.cmd) {
600*4882a593Smuzhiyun 	default:
601*4882a593Smuzhiyun 		return -ENOIOCTLCMD;
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun #ifdef BAYCOM_DEBUG
604*4882a593Smuzhiyun 	case BAYCOMCTL_GETDEBUG:
605*4882a593Smuzhiyun 		bi.data.dbg.debug1 = bc->hdrv.ptt_keyed;
606*4882a593Smuzhiyun 		bi.data.dbg.debug2 = bc->debug_vals.last_intcnt;
607*4882a593Smuzhiyun 		bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr;
608*4882a593Smuzhiyun 		break;
609*4882a593Smuzhiyun #endif /* BAYCOM_DEBUG */
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	}
612*4882a593Smuzhiyun 	if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
613*4882a593Smuzhiyun 		return -EFAULT;
614*4882a593Smuzhiyun 	return 0;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun /*
621*4882a593Smuzhiyun  * command line settable parameters
622*4882a593Smuzhiyun  */
623*4882a593Smuzhiyun static char *mode[NR_PORTS] = { "ser12*", };
624*4882a593Smuzhiyun static int iobase[NR_PORTS] = { 0x3f8, };
625*4882a593Smuzhiyun static int irq[NR_PORTS] = { 4, };
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun module_param_array(mode, charp, NULL, 0);
628*4882a593Smuzhiyun MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD");
629*4882a593Smuzhiyun module_param_hw_array(iobase, int, ioport, NULL, 0);
630*4882a593Smuzhiyun MODULE_PARM_DESC(iobase, "baycom io base address");
631*4882a593Smuzhiyun module_param_hw_array(irq, int, irq, NULL, 0);
632*4882a593Smuzhiyun MODULE_PARM_DESC(irq, "baycom irq number");
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
635*4882a593Smuzhiyun MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
636*4882a593Smuzhiyun MODULE_LICENSE("GPL");
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
639*4882a593Smuzhiyun 
init_baycomserhdx(void)640*4882a593Smuzhiyun static int __init init_baycomserhdx(void)
641*4882a593Smuzhiyun {
642*4882a593Smuzhiyun 	int i, found = 0;
643*4882a593Smuzhiyun 	char set_hw = 1;
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun 	printk(bc_drvinfo);
646*4882a593Smuzhiyun 	/*
647*4882a593Smuzhiyun 	 * register net devices
648*4882a593Smuzhiyun 	 */
649*4882a593Smuzhiyun 	for (i = 0; i < NR_PORTS; i++) {
650*4882a593Smuzhiyun 		struct net_device *dev;
651*4882a593Smuzhiyun 		struct baycom_state *bc;
652*4882a593Smuzhiyun 		char ifname[IFNAMSIZ];
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun 		sprintf(ifname, "bcsh%d", i);
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 		if (!mode[i])
657*4882a593Smuzhiyun 			set_hw = 0;
658*4882a593Smuzhiyun 		if (!set_hw)
659*4882a593Smuzhiyun 			iobase[i] = irq[i] = 0;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 		dev = hdlcdrv_register(&ser12_ops,
662*4882a593Smuzhiyun 				       sizeof(struct baycom_state),
663*4882a593Smuzhiyun 				       ifname, iobase[i], irq[i], 0);
664*4882a593Smuzhiyun 		if (IS_ERR(dev))
665*4882a593Smuzhiyun 			break;
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 		bc = netdev_priv(dev);
668*4882a593Smuzhiyun 		if (set_hw && baycom_setmode(bc, mode[i]))
669*4882a593Smuzhiyun 			set_hw = 0;
670*4882a593Smuzhiyun 		found++;
671*4882a593Smuzhiyun 		baycom_device[i] = dev;
672*4882a593Smuzhiyun 	}
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	if (!found)
675*4882a593Smuzhiyun 		return -ENXIO;
676*4882a593Smuzhiyun 	return 0;
677*4882a593Smuzhiyun }
678*4882a593Smuzhiyun 
cleanup_baycomserhdx(void)679*4882a593Smuzhiyun static void __exit cleanup_baycomserhdx(void)
680*4882a593Smuzhiyun {
681*4882a593Smuzhiyun 	int i;
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	for(i = 0; i < NR_PORTS; i++) {
684*4882a593Smuzhiyun 		struct net_device *dev = baycom_device[i];
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 		if (dev)
687*4882a593Smuzhiyun 			hdlcdrv_unregister(dev);
688*4882a593Smuzhiyun 	}
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun module_init(init_baycomserhdx);
692*4882a593Smuzhiyun module_exit(cleanup_baycomserhdx);
693*4882a593Smuzhiyun 
694*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun #ifndef MODULE
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun /*
699*4882a593Smuzhiyun  * format: baycom_ser_hdx=io,irq,mode
700*4882a593Smuzhiyun  * mode: ser12    hardware DCD
701*4882a593Smuzhiyun  *       ser12*   software DCD
702*4882a593Smuzhiyun  *       ser12@   hardware/software DCD, i.e. no explicit DCD signal but hardware
703*4882a593Smuzhiyun  *                mutes audio input to the modem
704*4882a593Smuzhiyun  *       ser12+   hardware DCD, inverted signal at DCD pin
705*4882a593Smuzhiyun  */
706*4882a593Smuzhiyun 
baycom_ser_hdx_setup(char * str)707*4882a593Smuzhiyun static int __init baycom_ser_hdx_setup(char *str)
708*4882a593Smuzhiyun {
709*4882a593Smuzhiyun         static unsigned nr_dev;
710*4882a593Smuzhiyun 	int ints[3];
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun         if (nr_dev >= NR_PORTS)
713*4882a593Smuzhiyun                 return 0;
714*4882a593Smuzhiyun 	str = get_options(str, 3, ints);
715*4882a593Smuzhiyun 	if (ints[0] < 2)
716*4882a593Smuzhiyun 		return 0;
717*4882a593Smuzhiyun 	mode[nr_dev] = str;
718*4882a593Smuzhiyun 	iobase[nr_dev] = ints[1];
719*4882a593Smuzhiyun 	irq[nr_dev] = ints[2];
720*4882a593Smuzhiyun 	nr_dev++;
721*4882a593Smuzhiyun 	return 1;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun __setup("baycom_ser_hdx=", baycom_ser_hdx_setup);
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun #endif /* MODULE */
727*4882a593Smuzhiyun /* --------------------------------------------------------------------- */
728