xref: /linux/drivers/net/hamradio/yam.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
13f5296f0SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*****************************************************************************/
31da177e4SLinus Torvalds 
41da177e4SLinus Torvalds /*
51da177e4SLinus Torvalds  *    yam.c  -- YAM radio modem driver.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *      Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr)
81da177e4SLinus Torvalds  *      Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch)
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  Please note that the GPL allows you to use the driver, NOT the radio.
111da177e4SLinus Torvalds  *  In order to use the radio, you need a license from the communications
121da177e4SLinus Torvalds  *  authority of your country.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  History:
151da177e4SLinus Torvalds  *   0.0 F1OAT 06.06.98  Begin of work with baycom.c source code V 0.3
161da177e4SLinus Torvalds  *   0.1 F1OAT 07.06.98  Add timer polling routine for channel arbitration
171da177e4SLinus Torvalds  *   0.2 F6FBB 08.06.98  Added delay after FPGA programming
181da177e4SLinus Torvalds  *   0.3 F6FBB 29.07.98  Delayed PTT implementation for dupmode=2
1925985edcSLucas De Marchi  *   0.4 F6FBB 30.07.98  Added TxTail, Slottime and Persistence
201da177e4SLinus Torvalds  *   0.5 F6FBB 01.08.98  Shared IRQs, /proc/net and network statistics
211da177e4SLinus Torvalds  *   0.6 F6FBB 25.08.98  Added 1200Bds format
221da177e4SLinus Torvalds  *   0.7 F6FBB 12.09.98  Added to the kernel configuration
231da177e4SLinus Torvalds  *   0.8 F6FBB 14.10.98  Fixed slottime/persistence timing bug
241da177e4SLinus Torvalds  *       OK1ZIA 2.09.01  Fixed "kfree_skb on hard IRQ"
251da177e4SLinus Torvalds  *                       using dev_kfree_skb_any(). (important in 2.4 kernel)
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds /*****************************************************************************/
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <linux/module.h>
311da177e4SLinus Torvalds #include <linux/types.h>
321da177e4SLinus Torvalds #include <linux/net.h>
331da177e4SLinus Torvalds #include <linux/in.h>
341da177e4SLinus Torvalds #include <linux/if.h>
351da177e4SLinus Torvalds #include <linux/slab.h>
361da177e4SLinus Torvalds #include <linux/errno.h>
371da177e4SLinus Torvalds #include <linux/bitops.h>
388b5b4671SRalf Baechle #include <linux/random.h>
391da177e4SLinus Torvalds #include <asm/io.h>
401da177e4SLinus Torvalds #include <linux/interrupt.h>
411da177e4SLinus Torvalds #include <linux/ioport.h>
42a7a5eb9dSJaswinder Singh Rajput #include <linux/firmware.h>
43a7a5eb9dSJaswinder Singh Rajput #include <linux/platform_device.h>
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #include <linux/netdevice.h>
461da177e4SLinus Torvalds #include <linux/if_arp.h>
471da177e4SLinus Torvalds #include <linux/etherdevice.h>
481da177e4SLinus Torvalds #include <linux/skbuff.h>
491da177e4SLinus Torvalds #include <net/ax25.h>
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #include <linux/kernel.h>
521da177e4SLinus Torvalds #include <linux/proc_fs.h>
531da177e4SLinus Torvalds #include <linux/seq_file.h>
54457c4cbcSEric W. Biederman #include <net/net_namespace.h>
551da177e4SLinus Torvalds 
567c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
571da177e4SLinus Torvalds #include <linux/init.h>
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds #include <linux/yam.h>
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds static const char yam_drvname[] = "yam";
64afa8c78bSAndi Kleen static const char yam_drvinfo[] __initconst = KERN_INFO \
65eb33ae24SHannes Eder 	"YAM driver version 0.8 by F1OAT/F6FBB\n";
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
681da177e4SLinus Torvalds 
69a7a5eb9dSJaswinder Singh Rajput #define FIRMWARE_9600	"yam/9600.bin"
70a7a5eb9dSJaswinder Singh Rajput #define FIRMWARE_1200	"yam/1200.bin"
71a7a5eb9dSJaswinder Singh Rajput 
721da177e4SLinus Torvalds #define YAM_9600	1
731da177e4SLinus Torvalds #define YAM_1200	2
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #define NR_PORTS	4
761da177e4SLinus Torvalds #define YAM_MAGIC	0xF10A7654
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /* Transmitter states */
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds #define TX_OFF		0
811da177e4SLinus Torvalds #define TX_HEAD		1
821da177e4SLinus Torvalds #define TX_DATA		2
831da177e4SLinus Torvalds #define TX_CRC1		3
841da177e4SLinus Torvalds #define TX_CRC2		4
851da177e4SLinus Torvalds #define TX_TAIL		5
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds #define YAM_MAX_FRAME	1024
881da177e4SLinus Torvalds 
891da177e4SLinus Torvalds #define DEFAULT_BITRATE	9600			/* bps */
901da177e4SLinus Torvalds #define DEFAULT_HOLDD	10			/* sec */
911da177e4SLinus Torvalds #define DEFAULT_TXD	300			/* ms */
921da177e4SLinus Torvalds #define DEFAULT_TXTAIL	10			/* ms */
931da177e4SLinus Torvalds #define DEFAULT_SLOT	100			/* ms */
941da177e4SLinus Torvalds #define DEFAULT_PERS	64			/* 0->255 */
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds struct yam_port {
971da177e4SLinus Torvalds 	int magic;
981da177e4SLinus Torvalds 	int bitrate;
991da177e4SLinus Torvalds 	int baudrate;
1001da177e4SLinus Torvalds 	int iobase;
1011da177e4SLinus Torvalds 	int irq;
1021da177e4SLinus Torvalds 	int dupmode;
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 	struct net_device *dev;
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	int nb_rxint;
1071da177e4SLinus Torvalds 	int nb_mdint;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	/* Parameters section */
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	int txd;				/* tx delay */
1121da177e4SLinus Torvalds 	int holdd;				/* duplex ptt delay */
1131da177e4SLinus Torvalds 	int txtail;				/* txtail delay */
1141da177e4SLinus Torvalds 	int slot;				/* slottime */
1151da177e4SLinus Torvalds 	int pers;				/* persistence */
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	/* Tx section */
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	int tx_state;
1201da177e4SLinus Torvalds 	int tx_count;
1211da177e4SLinus Torvalds 	int slotcnt;
1221da177e4SLinus Torvalds 	unsigned char tx_buf[YAM_MAX_FRAME];
1231da177e4SLinus Torvalds 	int tx_len;
1241da177e4SLinus Torvalds 	int tx_crcl, tx_crch;
1251da177e4SLinus Torvalds 	struct sk_buff_head send_queue;		/* Packets awaiting transmission */
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	/* Rx section */
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	int dcd;
1301da177e4SLinus Torvalds 	unsigned char rx_buf[YAM_MAX_FRAME];
1311da177e4SLinus Torvalds 	int rx_len;
1321da177e4SLinus Torvalds 	int rx_crcl, rx_crch;
1331da177e4SLinus Torvalds };
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds struct yam_mcs {
1361da177e4SLinus Torvalds 	unsigned char bits[YAM_FPGA_SIZE];
1371da177e4SLinus Torvalds 	int bitrate;
1381da177e4SLinus Torvalds 	struct yam_mcs *next;
1391da177e4SLinus Torvalds };
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds static struct net_device *yam_devs[NR_PORTS];
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static struct yam_mcs *yam_data;
1441da177e4SLinus Torvalds 
1451d27e3e2SKees Cook static DEFINE_TIMER(yam_timer, NULL);
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds #define RBR(iobase)	(iobase+0)
1501da177e4SLinus Torvalds #define THR(iobase)	(iobase+0)
1511da177e4SLinus Torvalds #define IER(iobase)	(iobase+1)
1521da177e4SLinus Torvalds #define IIR(iobase)	(iobase+2)
1531da177e4SLinus Torvalds #define FCR(iobase)	(iobase+2)
1541da177e4SLinus Torvalds #define LCR(iobase)	(iobase+3)
1551da177e4SLinus Torvalds #define MCR(iobase)	(iobase+4)
1561da177e4SLinus Torvalds #define LSR(iobase)	(iobase+5)
1571da177e4SLinus Torvalds #define MSR(iobase)	(iobase+6)
1581da177e4SLinus Torvalds #define SCR(iobase)	(iobase+7)
1591da177e4SLinus Torvalds #define DLL(iobase)	(iobase+0)
1601da177e4SLinus Torvalds #define DLM(iobase)	(iobase+1)
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds #define YAM_EXTENT	8
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds /* Interrupt Identification Register Bit Masks */
1651da177e4SLinus Torvalds #define IIR_NOPEND	1
1661da177e4SLinus Torvalds #define IIR_MSR		0
1671da177e4SLinus Torvalds #define IIR_TX		2
1681da177e4SLinus Torvalds #define IIR_RX		4
1691da177e4SLinus Torvalds #define IIR_LSR		6
1701da177e4SLinus Torvalds #define IIR_TIMEOUT	12			/* Fifo mode only */
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds #define IIR_MASK	0x0F
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds /* Interrupt Enable Register Bit Masks */
1751da177e4SLinus Torvalds #define IER_RX		1			/* enable rx interrupt */
1761da177e4SLinus Torvalds #define IER_TX		2			/* enable tx interrupt */
1771da177e4SLinus Torvalds #define IER_LSR		4			/* enable line status interrupts */
1781da177e4SLinus Torvalds #define IER_MSR		8			/* enable modem status interrupts */
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds /* Modem Control Register Bit Masks */
1811da177e4SLinus Torvalds #define MCR_DTR		0x01			/* DTR output */
1821da177e4SLinus Torvalds #define MCR_RTS		0x02			/* RTS output */
1831da177e4SLinus Torvalds #define MCR_OUT1	0x04			/* OUT1 output (not accessible in RS232) */
1841da177e4SLinus Torvalds #define MCR_OUT2	0x08			/* Master Interrupt enable (must be set on PCs) */
1851da177e4SLinus Torvalds #define MCR_LOOP	0x10			/* Loopback enable */
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds /* Modem Status Register Bit Masks */
1881da177e4SLinus Torvalds #define MSR_DCTS	0x01			/* Delta CTS input */
1891da177e4SLinus Torvalds #define MSR_DDSR	0x02			/* Delta DSR */
1901da177e4SLinus Torvalds #define MSR_DRIN	0x04			/* Delta RI */
1911da177e4SLinus Torvalds #define MSR_DDCD	0x08			/* Delta DCD */
1921da177e4SLinus Torvalds #define MSR_CTS		0x10			/* CTS input */
1931da177e4SLinus Torvalds #define MSR_DSR		0x20			/* DSR input */
1941da177e4SLinus Torvalds #define MSR_RING	0x40			/* RI  input */
1951da177e4SLinus Torvalds #define MSR_DCD		0x80			/* DCD input */
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds /* line status register bit mask */
1981da177e4SLinus Torvalds #define LSR_RXC		0x01
1991da177e4SLinus Torvalds #define LSR_OE		0x02
2001da177e4SLinus Torvalds #define LSR_PE		0x04
2011da177e4SLinus Torvalds #define LSR_FE		0x08
2021da177e4SLinus Torvalds #define LSR_BREAK	0x10
2031da177e4SLinus Torvalds #define LSR_THRE	0x20
2041da177e4SLinus Torvalds #define LSR_TSRE	0x40
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds /* Line Control Register Bit Masks */
2071da177e4SLinus Torvalds #define LCR_DLAB	0x80
2081da177e4SLinus Torvalds #define LCR_BREAK	0x40
2091da177e4SLinus Torvalds #define LCR_PZERO	0x28
2101da177e4SLinus Torvalds #define LCR_PEVEN	0x18
2111da177e4SLinus Torvalds #define LCR_PODD	0x08
2121da177e4SLinus Torvalds #define LCR_STOP1	0x00
2131da177e4SLinus Torvalds #define LCR_STOP2	0x04
2141da177e4SLinus Torvalds #define LCR_BIT5	0x00
2151da177e4SLinus Torvalds #define LCR_BIT6	0x02
2161da177e4SLinus Torvalds #define LCR_BIT7	0x01
2171da177e4SLinus Torvalds #define LCR_BIT8	0x03
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds /* YAM Modem <-> UART Port mapping */
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds #define TX_RDY		MSR_DCTS		/* transmitter ready to send */
2221da177e4SLinus Torvalds #define RX_DCD		MSR_DCD			/* carrier detect */
2231da177e4SLinus Torvalds #define RX_FLAG		MSR_RING		/* hdlc flag received */
2241da177e4SLinus Torvalds #define FPGA_DONE	MSR_DSR			/* FPGA is configured */
2251da177e4SLinus Torvalds #define PTT_ON		(MCR_RTS|MCR_OUT2)	/* activate PTT */
2261da177e4SLinus Torvalds #define PTT_OFF		(MCR_DTR|MCR_OUT2)	/* release PTT */
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds #define ENABLE_RXINT	IER_RX			/* enable uart rx interrupt during rx */
2291da177e4SLinus Torvalds #define ENABLE_TXINT	IER_MSR			/* enable uart ms interrupt during tx */
2301da177e4SLinus Torvalds #define ENABLE_RTXINT	(IER_RX|IER_MSR)	/* full duplex operations */
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds /*************************************************************************
2341da177e4SLinus Torvalds * CRC Tables
2351da177e4SLinus Torvalds ************************************************************************/
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds static const unsigned char chktabl[256] =
2381da177e4SLinus Torvalds {0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e,
2391da177e4SLinus Torvalds  0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64,
2401da177e4SLinus Torvalds  0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e,
2411da177e4SLinus Torvalds  0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50,
2421da177e4SLinus Torvalds  0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e,
2431da177e4SLinus Torvalds  0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44,
2441da177e4SLinus Torvalds  0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e,
2451da177e4SLinus Torvalds  0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38,
2461da177e4SLinus Torvalds  0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e,
2471da177e4SLinus Torvalds  0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24,
2481da177e4SLinus Torvalds  0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e,
2491da177e4SLinus Torvalds  0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10,
2501da177e4SLinus Torvalds  0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e,
2511da177e4SLinus Torvalds  0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04,
2521da177e4SLinus Torvalds  0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e,
2531da177e4SLinus Torvalds  0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9,
2541da177e4SLinus Torvalds  0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1,
2551da177e4SLinus Torvalds  0x78};
2561da177e4SLinus Torvalds static const unsigned char chktabh[256] =
2571da177e4SLinus Torvalds {0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9,
2581da177e4SLinus Torvalds  0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb,
2591da177e4SLinus Torvalds  0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb,
2601da177e4SLinus Torvalds  0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f,
2611da177e4SLinus Torvalds  0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed,
2621da177e4SLinus Torvalds  0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf,
2631da177e4SLinus Torvalds  0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef,
2641da177e4SLinus Torvalds  0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07,
2651da177e4SLinus Torvalds  0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1,
2661da177e4SLinus Torvalds  0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3,
2671da177e4SLinus Torvalds  0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3,
2681da177e4SLinus Torvalds  0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87,
2691da177e4SLinus Torvalds  0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5,
2701da177e4SLinus Torvalds  0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7,
2711da177e4SLinus Torvalds  0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7,
2721da177e4SLinus Torvalds  0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f,
2731da177e4SLinus Torvalds  0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e,
2741da177e4SLinus Torvalds  0x0f};
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds /*************************************************************************
2771da177e4SLinus Torvalds * FPGA functions
2781da177e4SLinus Torvalds ************************************************************************/
2791da177e4SLinus Torvalds 
delay(int ms)2801da177e4SLinus Torvalds static void delay(int ms)
2811da177e4SLinus Torvalds {
2821da177e4SLinus Torvalds 	unsigned long timeout = jiffies + ((ms * HZ) / 1000);
2831da177e4SLinus Torvalds 	while (time_before(jiffies, timeout))
2841da177e4SLinus Torvalds 		cpu_relax();
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds /*
2881da177e4SLinus Torvalds  * reset FPGA
2891da177e4SLinus Torvalds  */
2901da177e4SLinus Torvalds 
fpga_reset(int iobase)2911da177e4SLinus Torvalds static void fpga_reset(int iobase)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	outb(0, IER(iobase));
2941da177e4SLinus Torvalds 	outb(LCR_DLAB | LCR_BIT5, LCR(iobase));
2951da177e4SLinus Torvalds 	outb(1, DLL(iobase));
2961da177e4SLinus Torvalds 	outb(0, DLM(iobase));
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	outb(LCR_BIT5, LCR(iobase));
2991da177e4SLinus Torvalds 	inb(LSR(iobase));
3001da177e4SLinus Torvalds 	inb(MSR(iobase));
3011da177e4SLinus Torvalds 	/* turn off FPGA supply voltage */
3021da177e4SLinus Torvalds 	outb(MCR_OUT1 | MCR_OUT2, MCR(iobase));
3031da177e4SLinus Torvalds 	delay(100);
3041da177e4SLinus Torvalds 	/* turn on FPGA supply voltage again */
3051da177e4SLinus Torvalds 	outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase));
3061da177e4SLinus Torvalds 	delay(100);
3071da177e4SLinus Torvalds }
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds /*
3101da177e4SLinus Torvalds  * send one byte to FPGA
3111da177e4SLinus Torvalds  */
3121da177e4SLinus Torvalds 
fpga_write(int iobase,unsigned char wrd)3131da177e4SLinus Torvalds static int fpga_write(int iobase, unsigned char wrd)
3141da177e4SLinus Torvalds {
3151da177e4SLinus Torvalds 	unsigned char bit;
3161da177e4SLinus Torvalds 	int k;
3171da177e4SLinus Torvalds 	unsigned long timeout = jiffies + HZ / 10;
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	for (k = 0; k < 8; k++) {
3201da177e4SLinus Torvalds 		bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR;
3211da177e4SLinus Torvalds 		outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase));
3221da177e4SLinus Torvalds 		wrd <<= 1;
3231da177e4SLinus Torvalds 		outb(0xfc, THR(iobase));
3241da177e4SLinus Torvalds 		while ((inb(LSR(iobase)) & LSR_TSRE) == 0)
3251da177e4SLinus Torvalds 			if (time_after(jiffies, timeout))
3261da177e4SLinus Torvalds 				return -1;
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds 	return 0;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
332a7a5eb9dSJaswinder Singh Rajput /*
333a7a5eb9dSJaswinder Singh Rajput  * predef should be 0 for loading user defined mcs
334a7a5eb9dSJaswinder Singh Rajput  * predef should be YAM_1200 for loading predef 1200 mcs
335a7a5eb9dSJaswinder Singh Rajput  * predef should be YAM_9600 for loading predef 9600 mcs
336a7a5eb9dSJaswinder Singh Rajput  */
add_mcs(unsigned char * bits,int bitrate,unsigned int predef)337a7a5eb9dSJaswinder Singh Rajput static unsigned char *add_mcs(unsigned char *bits, int bitrate,
338a7a5eb9dSJaswinder Singh Rajput 			      unsigned int predef)
3391da177e4SLinus Torvalds {
340a7a5eb9dSJaswinder Singh Rajput 	const char *fw_name[2] = {FIRMWARE_9600, FIRMWARE_1200};
341a7a5eb9dSJaswinder Singh Rajput 	const struct firmware *fw;
342a7a5eb9dSJaswinder Singh Rajput 	struct platform_device *pdev;
3431da177e4SLinus Torvalds 	struct yam_mcs *p;
344a7a5eb9dSJaswinder Singh Rajput 	int err;
345a7a5eb9dSJaswinder Singh Rajput 
346a7a5eb9dSJaswinder Singh Rajput 	switch (predef) {
347a7a5eb9dSJaswinder Singh Rajput 	case 0:
348a7a5eb9dSJaswinder Singh Rajput 		fw = NULL;
349a7a5eb9dSJaswinder Singh Rajput 		break;
350a7a5eb9dSJaswinder Singh Rajput 	case YAM_1200:
351a7a5eb9dSJaswinder Singh Rajput 	case YAM_9600:
352a7a5eb9dSJaswinder Singh Rajput 		predef--;
353a7a5eb9dSJaswinder Singh Rajput 		pdev = platform_device_register_simple("yam", 0, NULL, 0);
354a7a5eb9dSJaswinder Singh Rajput 		if (IS_ERR(pdev)) {
355a7a5eb9dSJaswinder Singh Rajput 			printk(KERN_ERR "yam: Failed to register firmware\n");
356a7a5eb9dSJaswinder Singh Rajput 			return NULL;
357a7a5eb9dSJaswinder Singh Rajput 		}
358a7a5eb9dSJaswinder Singh Rajput 		err = request_firmware(&fw, fw_name[predef], &pdev->dev);
359a7a5eb9dSJaswinder Singh Rajput 		platform_device_unregister(pdev);
360a7a5eb9dSJaswinder Singh Rajput 		if (err) {
361a7a5eb9dSJaswinder Singh Rajput 			printk(KERN_ERR "Failed to load firmware \"%s\"\n",
362a7a5eb9dSJaswinder Singh Rajput 			       fw_name[predef]);
363a7a5eb9dSJaswinder Singh Rajput 			return NULL;
364a7a5eb9dSJaswinder Singh Rajput 		}
365a7a5eb9dSJaswinder Singh Rajput 		if (fw->size != YAM_FPGA_SIZE) {
366a7a5eb9dSJaswinder Singh Rajput 			printk(KERN_ERR "Bogus length %zu in firmware \"%s\"\n",
367a7a5eb9dSJaswinder Singh Rajput 			       fw->size, fw_name[predef]);
368a7a5eb9dSJaswinder Singh Rajput 			release_firmware(fw);
369a7a5eb9dSJaswinder Singh Rajput 			return NULL;
370a7a5eb9dSJaswinder Singh Rajput 		}
371a7a5eb9dSJaswinder Singh Rajput 		bits = (unsigned char *)fw->data;
372a7a5eb9dSJaswinder Singh Rajput 		break;
373a7a5eb9dSJaswinder Singh Rajput 	default:
374a7a5eb9dSJaswinder Singh Rajput 		printk(KERN_ERR "yam: Invalid predef number %u\n", predef);
375a7a5eb9dSJaswinder Singh Rajput 		return NULL;
376a7a5eb9dSJaswinder Singh Rajput 	}
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds 	/* If it already exists, replace the bit data */
3791da177e4SLinus Torvalds 	p = yam_data;
3801da177e4SLinus Torvalds 	while (p) {
3811da177e4SLinus Torvalds 		if (p->bitrate == bitrate) {
3821da177e4SLinus Torvalds 			memcpy(p->bits, bits, YAM_FPGA_SIZE);
38326877c79SJesper Juhl 			goto out;
3841da177e4SLinus Torvalds 		}
3851da177e4SLinus Torvalds 		p = p->next;
3861da177e4SLinus Torvalds 	}
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	/* Allocate a new mcs */
3891da177e4SLinus Torvalds 	if ((p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL)) == NULL) {
390a7a5eb9dSJaswinder Singh Rajput 		release_firmware(fw);
3911da177e4SLinus Torvalds 		return NULL;
3921da177e4SLinus Torvalds 	}
3931da177e4SLinus Torvalds 	memcpy(p->bits, bits, YAM_FPGA_SIZE);
3941da177e4SLinus Torvalds 	p->bitrate = bitrate;
3951da177e4SLinus Torvalds 	p->next = yam_data;
3961da177e4SLinus Torvalds 	yam_data = p;
39726877c79SJesper Juhl  out:
398a7a5eb9dSJaswinder Singh Rajput 	release_firmware(fw);
3991da177e4SLinus Torvalds 	return p->bits;
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
get_mcs(int bitrate)4021da177e4SLinus Torvalds static unsigned char *get_mcs(int bitrate)
4031da177e4SLinus Torvalds {
4041da177e4SLinus Torvalds 	struct yam_mcs *p;
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds 	p = yam_data;
4071da177e4SLinus Torvalds 	while (p) {
4081da177e4SLinus Torvalds 		if (p->bitrate == bitrate)
4091da177e4SLinus Torvalds 			return p->bits;
4101da177e4SLinus Torvalds 		p = p->next;
4111da177e4SLinus Torvalds 	}
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds 	/* Load predefined mcs data */
4141da177e4SLinus Torvalds 	switch (bitrate) {
4151da177e4SLinus Torvalds 	case 1200:
416a7a5eb9dSJaswinder Singh Rajput 		/* setting predef as YAM_1200 for loading predef 1200 mcs */
417a7a5eb9dSJaswinder Singh Rajput 		return add_mcs(NULL, bitrate, YAM_1200);
4181da177e4SLinus Torvalds 	default:
419a7a5eb9dSJaswinder Singh Rajput 		/* setting predef as YAM_9600 for loading predef 9600 mcs */
420a7a5eb9dSJaswinder Singh Rajput 		return add_mcs(NULL, bitrate, YAM_9600);
4211da177e4SLinus Torvalds 	}
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds /*
4251da177e4SLinus Torvalds  * download bitstream to FPGA
4261da177e4SLinus Torvalds  * data is contained in bits[] array in yam1200.h resp. yam9600.h
4271da177e4SLinus Torvalds  */
4281da177e4SLinus Torvalds 
fpga_download(int iobase,int bitrate)4291da177e4SLinus Torvalds static int fpga_download(int iobase, int bitrate)
4301da177e4SLinus Torvalds {
4311da177e4SLinus Torvalds 	int i, rc;
4321da177e4SLinus Torvalds 	unsigned char *pbits;
4331da177e4SLinus Torvalds 
4341da177e4SLinus Torvalds 	pbits = get_mcs(bitrate);
4351da177e4SLinus Torvalds 	if (pbits == NULL)
4361da177e4SLinus Torvalds 		return -1;
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds 	fpga_reset(iobase);
4391da177e4SLinus Torvalds 	for (i = 0; i < YAM_FPGA_SIZE; i++) {
4401da177e4SLinus Torvalds 		if (fpga_write(iobase, pbits[i])) {
4411da177e4SLinus Torvalds 			printk(KERN_ERR "yam: error in write cycle\n");
4421da177e4SLinus Torvalds 			return -1;			/* write... */
4431da177e4SLinus Torvalds 		}
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	fpga_write(iobase, 0xFF);
4471da177e4SLinus Torvalds 	rc = inb(MSR(iobase));		/* check DONE signal */
4481da177e4SLinus Torvalds 
4491da177e4SLinus Torvalds 	/* Needed for some hardwares */
4501da177e4SLinus Torvalds 	delay(50);
4511da177e4SLinus Torvalds 
4521da177e4SLinus Torvalds 	return (rc & MSR_DSR) ? 0 : -1;
4531da177e4SLinus Torvalds }
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 
4561da177e4SLinus Torvalds /************************************************************************
4571da177e4SLinus Torvalds * Serial port init
4581da177e4SLinus Torvalds ************************************************************************/
4591da177e4SLinus Torvalds 
yam_set_uart(struct net_device * dev)4601da177e4SLinus Torvalds static void yam_set_uart(struct net_device *dev)
4611da177e4SLinus Torvalds {
4621da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
4631da177e4SLinus Torvalds 	int divisor = 115200 / yp->baudrate;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	outb(0, IER(dev->base_addr));
4661da177e4SLinus Torvalds 	outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr));
4671da177e4SLinus Torvalds 	outb(divisor, DLL(dev->base_addr));
4681da177e4SLinus Torvalds 	outb(0, DLM(dev->base_addr));
4691da177e4SLinus Torvalds 	outb(LCR_BIT8, LCR(dev->base_addr));
4701da177e4SLinus Torvalds 	outb(PTT_OFF, MCR(dev->base_addr));
4711da177e4SLinus Torvalds 	outb(0x00, FCR(dev->base_addr));
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds 	/* Flush pending irq */
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	inb(RBR(dev->base_addr));
4761da177e4SLinus Torvalds 	inb(MSR(dev->base_addr));
4771da177e4SLinus Torvalds 
4781da177e4SLinus Torvalds 	/* Enable rx irq */
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds 	outb(ENABLE_RTXINT, IER(dev->base_addr));
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds enum uart {
4871da177e4SLinus Torvalds 	c_uart_unknown, c_uart_8250,
4881da177e4SLinus Torvalds 	c_uart_16450, c_uart_16550, c_uart_16550A
4891da177e4SLinus Torvalds };
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds static const char *uart_str[] =
4921da177e4SLinus Torvalds {"unknown", "8250", "16450", "16550", "16550A"};
4931da177e4SLinus Torvalds 
yam_check_uart(unsigned int iobase)4941da177e4SLinus Torvalds static enum uart yam_check_uart(unsigned int iobase)
4951da177e4SLinus Torvalds {
4961da177e4SLinus Torvalds 	unsigned char b1, b2, b3;
4971da177e4SLinus Torvalds 	enum uart u;
4981da177e4SLinus Torvalds 	enum uart uart_tab[] =
4991da177e4SLinus Torvalds 	{c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A};
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	b1 = inb(MCR(iobase));
5021da177e4SLinus Torvalds 	outb(b1 | 0x10, MCR(iobase));	/* loopback mode */
5031da177e4SLinus Torvalds 	b2 = inb(MSR(iobase));
5041da177e4SLinus Torvalds 	outb(0x1a, MCR(iobase));
5051da177e4SLinus Torvalds 	b3 = inb(MSR(iobase)) & 0xf0;
5061da177e4SLinus Torvalds 	outb(b1, MCR(iobase));		/* restore old values */
5071da177e4SLinus Torvalds 	outb(b2, MSR(iobase));
5081da177e4SLinus Torvalds 	if (b3 != 0x90)
5091da177e4SLinus Torvalds 		return c_uart_unknown;
5101da177e4SLinus Torvalds 	inb(RBR(iobase));
5111da177e4SLinus Torvalds 	inb(RBR(iobase));
5121da177e4SLinus Torvalds 	outb(0x01, FCR(iobase));	/* enable FIFOs */
5131da177e4SLinus Torvalds 	u = uart_tab[(inb(IIR(iobase)) >> 6) & 3];
5141da177e4SLinus Torvalds 	if (u == c_uart_16450) {
5151da177e4SLinus Torvalds 		outb(0x5a, SCR(iobase));
5161da177e4SLinus Torvalds 		b1 = inb(SCR(iobase));
5171da177e4SLinus Torvalds 		outb(0xa5, SCR(iobase));
5181da177e4SLinus Torvalds 		b2 = inb(SCR(iobase));
5191da177e4SLinus Torvalds 		if ((b1 != 0x5a) || (b2 != 0xa5))
5201da177e4SLinus Torvalds 			u = c_uart_8250;
5211da177e4SLinus Torvalds 	}
5221da177e4SLinus Torvalds 	return u;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds /******************************************************************************
5261da177e4SLinus Torvalds * Rx Section
5271da177e4SLinus Torvalds ******************************************************************************/
yam_rx_flag(struct net_device * dev,struct yam_port * yp)5281da177e4SLinus Torvalds static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) {
5311da177e4SLinus Torvalds 		int pkt_len = yp->rx_len - 2 + 1;	/* -CRC + kiss */
5321da177e4SLinus Torvalds 		struct sk_buff *skb;
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 		if ((yp->rx_crch & yp->rx_crcl) != 0xFF) {
5351da177e4SLinus Torvalds 			/* Bad crc */
5361da177e4SLinus Torvalds 		} else {
5371da177e4SLinus Torvalds 			if (!(skb = dev_alloc_skb(pkt_len))) {
5381da177e4SLinus Torvalds 				printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name);
5393c94acb7SStephen Hemminger 				++dev->stats.rx_dropped;
5401da177e4SLinus Torvalds 			} else {
5411da177e4SLinus Torvalds 				unsigned char *cp;
5421da177e4SLinus Torvalds 				cp = skb_put(skb, pkt_len);
5431da177e4SLinus Torvalds 				*cp++ = 0;		/* KISS kludge */
5441da177e4SLinus Torvalds 				memcpy(cp, yp->rx_buf, pkt_len - 1);
54556cb5156SArnaldo Carvalho de Melo 				skb->protocol = ax25_type_trans(skb, dev);
5461da177e4SLinus Torvalds 				netif_rx(skb);
5473c94acb7SStephen Hemminger 				++dev->stats.rx_packets;
5481da177e4SLinus Torvalds 			}
5491da177e4SLinus Torvalds 		}
5501da177e4SLinus Torvalds 	}
5511da177e4SLinus Torvalds 	yp->rx_len = 0;
5521da177e4SLinus Torvalds 	yp->rx_crcl = 0x21;
5531da177e4SLinus Torvalds 	yp->rx_crch = 0xf3;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
yam_rx_byte(struct net_device * dev,struct yam_port * yp,unsigned char rxb)5561da177e4SLinus Torvalds static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb)
5571da177e4SLinus Torvalds {
5581da177e4SLinus Torvalds 	if (yp->rx_len < YAM_MAX_FRAME) {
5591da177e4SLinus Torvalds 		unsigned char c = yp->rx_crcl;
5601da177e4SLinus Torvalds 		yp->rx_crcl = (chktabl[c] ^ yp->rx_crch);
5611da177e4SLinus Torvalds 		yp->rx_crch = (chktabh[c] ^ rxb);
5621da177e4SLinus Torvalds 		yp->rx_buf[yp->rx_len++] = rxb;
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds /********************************************************************************
5671da177e4SLinus Torvalds * TX Section
5681da177e4SLinus Torvalds ********************************************************************************/
5691da177e4SLinus Torvalds 
ptt_on(struct net_device * dev)5701da177e4SLinus Torvalds static void ptt_on(struct net_device *dev)
5711da177e4SLinus Torvalds {
5721da177e4SLinus Torvalds 	outb(PTT_ON, MCR(dev->base_addr));
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
ptt_off(struct net_device * dev)5751da177e4SLinus Torvalds static void ptt_off(struct net_device *dev)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	outb(PTT_OFF, MCR(dev->base_addr));
5781da177e4SLinus Torvalds }
5791da177e4SLinus Torvalds 
yam_send_packet(struct sk_buff * skb,struct net_device * dev)58036e4d64aSStephen Hemminger static netdev_tx_t yam_send_packet(struct sk_buff *skb,
58136e4d64aSStephen Hemminger 					 struct net_device *dev)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
5841da177e4SLinus Torvalds 
5851d5da757SEric W. Biederman 	if (skb->protocol == htons(ETH_P_IP))
5861d5da757SEric W. Biederman 		return ax25_ip_xmit(skb);
5871d5da757SEric W. Biederman 
5881da177e4SLinus Torvalds 	skb_queue_tail(&yp->send_queue, skb);
589860e9538SFlorian Westphal 	netif_trans_update(dev);
5906ed10654SPatrick McHardy 	return NETDEV_TX_OK;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
yam_start_tx(struct net_device * dev,struct yam_port * yp)5931da177e4SLinus Torvalds static void yam_start_tx(struct net_device *dev, struct yam_port *yp)
5941da177e4SLinus Torvalds {
5951da177e4SLinus Torvalds 	if ((yp->tx_state == TX_TAIL) || (yp->txd == 0))
5961da177e4SLinus Torvalds 		yp->tx_count = 1;
5971da177e4SLinus Torvalds 	else
5981da177e4SLinus Torvalds 		yp->tx_count = (yp->bitrate * yp->txd) / 8000;
5991da177e4SLinus Torvalds 	yp->tx_state = TX_HEAD;
6001da177e4SLinus Torvalds 	ptt_on(dev);
6011da177e4SLinus Torvalds }
6021da177e4SLinus Torvalds 
yam_arbitrate(struct net_device * dev)6031da177e4SLinus Torvalds static void yam_arbitrate(struct net_device *dev)
6041da177e4SLinus Torvalds {
6051da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds 	if (yp->magic != YAM_MAGIC || yp->tx_state != TX_OFF ||
6081da177e4SLinus Torvalds 	    skb_queue_empty(&yp->send_queue))
6091da177e4SLinus Torvalds 		return;
6101da177e4SLinus Torvalds 	/* tx_state is TX_OFF and there is data to send */
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	if (yp->dupmode) {
6131da177e4SLinus Torvalds 		/* Full duplex mode, don't wait */
6141da177e4SLinus Torvalds 		yam_start_tx(dev, yp);
6151da177e4SLinus Torvalds 		return;
6161da177e4SLinus Torvalds 	}
6171da177e4SLinus Torvalds 	if (yp->dcd) {
6181da177e4SLinus Torvalds 		/* DCD on, wait slotime ... */
6191da177e4SLinus Torvalds 		yp->slotcnt = yp->slot / 10;
6201da177e4SLinus Torvalds 		return;
6211da177e4SLinus Torvalds 	}
6221da177e4SLinus Torvalds 	/* Is slottime passed ? */
6231da177e4SLinus Torvalds 	if ((--yp->slotcnt) > 0)
6241da177e4SLinus Torvalds 		return;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	yp->slotcnt = yp->slot / 10;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	/* is random > persist ? */
629*7e3cf084SJason A. Donenfeld 	if (get_random_u8() > yp->pers)
6301da177e4SLinus Torvalds 		return;
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 	yam_start_tx(dev, yp);
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds 
yam_dotimer(struct timer_list * unused)635f6fd8918SKees Cook static void yam_dotimer(struct timer_list *unused)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	int i;
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	for (i = 0; i < NR_PORTS; i++) {
6401da177e4SLinus Torvalds 		struct net_device *dev = yam_devs[i];
6411da177e4SLinus Torvalds 		if (dev && netif_running(dev))
6421da177e4SLinus Torvalds 			yam_arbitrate(dev);
6431da177e4SLinus Torvalds 	}
6441da177e4SLinus Torvalds 	yam_timer.expires = jiffies + HZ / 100;
6451da177e4SLinus Torvalds 	add_timer(&yam_timer);
6461da177e4SLinus Torvalds }
6471da177e4SLinus Torvalds 
yam_tx_byte(struct net_device * dev,struct yam_port * yp)6481da177e4SLinus Torvalds static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	struct sk_buff *skb;
6511da177e4SLinus Torvalds 	unsigned char b, temp;
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 	switch (yp->tx_state) {
6541da177e4SLinus Torvalds 	case TX_OFF:
6551da177e4SLinus Torvalds 		break;
6561da177e4SLinus Torvalds 	case TX_HEAD:
6571da177e4SLinus Torvalds 		if (--yp->tx_count <= 0) {
6581da177e4SLinus Torvalds 			if (!(skb = skb_dequeue(&yp->send_queue))) {
6591da177e4SLinus Torvalds 				ptt_off(dev);
6601da177e4SLinus Torvalds 				yp->tx_state = TX_OFF;
6611da177e4SLinus Torvalds 				break;
6621da177e4SLinus Torvalds 			}
6631da177e4SLinus Torvalds 			yp->tx_state = TX_DATA;
6641da177e4SLinus Torvalds 			if (skb->data[0] != 0) {
6651da177e4SLinus Torvalds /*                              do_kiss_params(s, skb->data, skb->len); */
6661da177e4SLinus Torvalds 				dev_kfree_skb_any(skb);
6671da177e4SLinus Torvalds 				break;
6681da177e4SLinus Torvalds 			}
6691da177e4SLinus Torvalds 			yp->tx_len = skb->len - 1;	/* strip KISS byte */
6701da177e4SLinus Torvalds 			if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) {
6711da177e4SLinus Torvalds 				dev_kfree_skb_any(skb);
6721da177e4SLinus Torvalds 				break;
6731da177e4SLinus Torvalds 			}
67455404bcaSAndrew Morton 			skb_copy_from_linear_data_offset(skb, 1,
675d626f62bSArnaldo Carvalho de Melo 							 yp->tx_buf,
676d626f62bSArnaldo Carvalho de Melo 							 yp->tx_len);
6771da177e4SLinus Torvalds 			dev_kfree_skb_any(skb);
6781da177e4SLinus Torvalds 			yp->tx_count = 0;
6791da177e4SLinus Torvalds 			yp->tx_crcl = 0x21;
6801da177e4SLinus Torvalds 			yp->tx_crch = 0xf3;
6811da177e4SLinus Torvalds 			yp->tx_state = TX_DATA;
6821da177e4SLinus Torvalds 		}
6831da177e4SLinus Torvalds 		break;
6841da177e4SLinus Torvalds 	case TX_DATA:
6851da177e4SLinus Torvalds 		b = yp->tx_buf[yp->tx_count++];
6861da177e4SLinus Torvalds 		outb(b, THR(dev->base_addr));
6871da177e4SLinus Torvalds 		temp = yp->tx_crcl;
6881da177e4SLinus Torvalds 		yp->tx_crcl = chktabl[temp] ^ yp->tx_crch;
6891da177e4SLinus Torvalds 		yp->tx_crch = chktabh[temp] ^ b;
6901da177e4SLinus Torvalds 		if (yp->tx_count >= yp->tx_len) {
6911da177e4SLinus Torvalds 			yp->tx_state = TX_CRC1;
6921da177e4SLinus Torvalds 		}
6931da177e4SLinus Torvalds 		break;
6941da177e4SLinus Torvalds 	case TX_CRC1:
6951da177e4SLinus Torvalds 		yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch;
6961da177e4SLinus Torvalds 		yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff;
6971da177e4SLinus Torvalds 		outb(yp->tx_crcl, THR(dev->base_addr));
6981da177e4SLinus Torvalds 		yp->tx_state = TX_CRC2;
6991da177e4SLinus Torvalds 		break;
7001da177e4SLinus Torvalds 	case TX_CRC2:
7011da177e4SLinus Torvalds 		outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr));
7021da177e4SLinus Torvalds 		if (skb_queue_empty(&yp->send_queue)) {
7031da177e4SLinus Torvalds 			yp->tx_count = (yp->bitrate * yp->txtail) / 8000;
7041da177e4SLinus Torvalds 			if (yp->dupmode == 2)
7051da177e4SLinus Torvalds 				yp->tx_count += (yp->bitrate * yp->holdd) / 8;
7061da177e4SLinus Torvalds 			if (yp->tx_count == 0)
7071da177e4SLinus Torvalds 				yp->tx_count = 1;
7081da177e4SLinus Torvalds 			yp->tx_state = TX_TAIL;
7091da177e4SLinus Torvalds 		} else {
7101da177e4SLinus Torvalds 			yp->tx_count = 1;
7111da177e4SLinus Torvalds 			yp->tx_state = TX_HEAD;
7121da177e4SLinus Torvalds 		}
7133c94acb7SStephen Hemminger 		++dev->stats.tx_packets;
7141da177e4SLinus Torvalds 		break;
7151da177e4SLinus Torvalds 	case TX_TAIL:
7161da177e4SLinus Torvalds 		if (--yp->tx_count <= 0) {
7171da177e4SLinus Torvalds 			yp->tx_state = TX_OFF;
7181da177e4SLinus Torvalds 			ptt_off(dev);
7191da177e4SLinus Torvalds 		}
7201da177e4SLinus Torvalds 		break;
7211da177e4SLinus Torvalds 	}
7221da177e4SLinus Torvalds }
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds /***********************************************************************************
7251da177e4SLinus Torvalds * ISR routine
7261da177e4SLinus Torvalds ************************************************************************************/
7271da177e4SLinus Torvalds 
yam_interrupt(int irq,void * dev_id)7287d12e780SDavid Howells static irqreturn_t yam_interrupt(int irq, void *dev_id)
7291da177e4SLinus Torvalds {
7301da177e4SLinus Torvalds 	struct net_device *dev;
7311da177e4SLinus Torvalds 	struct yam_port *yp;
7321da177e4SLinus Torvalds 	unsigned char iir;
7331da177e4SLinus Torvalds 	int counter = 100;
7341da177e4SLinus Torvalds 	int i;
7351da177e4SLinus Torvalds 	int handled = 0;
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds 	for (i = 0; i < NR_PORTS; i++) {
7381da177e4SLinus Torvalds 		dev = yam_devs[i];
7391da177e4SLinus Torvalds 		yp = netdev_priv(dev);
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds 		if (!netif_running(dev))
7421da177e4SLinus Torvalds 			continue;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 		while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) {
7451da177e4SLinus Torvalds 			unsigned char msr = inb(MSR(dev->base_addr));
7461da177e4SLinus Torvalds 			unsigned char lsr = inb(LSR(dev->base_addr));
7471da177e4SLinus Torvalds 			unsigned char rxb;
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds 			handled = 1;
7501da177e4SLinus Torvalds 
7511da177e4SLinus Torvalds 			if (lsr & LSR_OE)
7523c94acb7SStephen Hemminger 				++dev->stats.rx_fifo_errors;
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 			yp->dcd = (msr & RX_DCD) ? 1 : 0;
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 			if (--counter <= 0) {
7571da177e4SLinus Torvalds 				printk(KERN_ERR "%s: too many irq iir=%d\n",
7581da177e4SLinus Torvalds 						dev->name, iir);
7591da177e4SLinus Torvalds 				goto out;
7601da177e4SLinus Torvalds 			}
7611da177e4SLinus Torvalds 			if (msr & TX_RDY) {
7621da177e4SLinus Torvalds 				++yp->nb_mdint;
7631da177e4SLinus Torvalds 				yam_tx_byte(dev, yp);
7641da177e4SLinus Torvalds 			}
7651da177e4SLinus Torvalds 			if (lsr & LSR_RXC) {
7661da177e4SLinus Torvalds 				++yp->nb_rxint;
7671da177e4SLinus Torvalds 				rxb = inb(RBR(dev->base_addr));
7681da177e4SLinus Torvalds 				if (msr & RX_FLAG)
7691da177e4SLinus Torvalds 					yam_rx_flag(dev, yp);
7701da177e4SLinus Torvalds 				else
7711da177e4SLinus Torvalds 					yam_rx_byte(dev, yp, rxb);
7721da177e4SLinus Torvalds 			}
7731da177e4SLinus Torvalds 		}
7741da177e4SLinus Torvalds 	}
7751da177e4SLinus Torvalds out:
7761da177e4SLinus Torvalds 	return IRQ_RETVAL(handled);
7771da177e4SLinus Torvalds }
7781da177e4SLinus Torvalds 
7791da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS
7801da177e4SLinus Torvalds 
yam_seq_start(struct seq_file * seq,loff_t * pos)7811da177e4SLinus Torvalds static void *yam_seq_start(struct seq_file *seq, loff_t *pos)
7821da177e4SLinus Torvalds {
7831da177e4SLinus Torvalds 	return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL;
7841da177e4SLinus Torvalds }
7851da177e4SLinus Torvalds 
yam_seq_next(struct seq_file * seq,void * v,loff_t * pos)7861da177e4SLinus Torvalds static void *yam_seq_next(struct seq_file *seq, void *v, loff_t *pos)
7871da177e4SLinus Torvalds {
7881da177e4SLinus Torvalds 	++*pos;
7891da177e4SLinus Torvalds 	return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL;
7901da177e4SLinus Torvalds }
7911da177e4SLinus Torvalds 
yam_seq_stop(struct seq_file * seq,void * v)7921da177e4SLinus Torvalds static void yam_seq_stop(struct seq_file *seq, void *v)
7931da177e4SLinus Torvalds {
7941da177e4SLinus Torvalds }
7951da177e4SLinus Torvalds 
yam_seq_show(struct seq_file * seq,void * v)7961da177e4SLinus Torvalds static int yam_seq_show(struct seq_file *seq, void *v)
7971da177e4SLinus Torvalds {
7981da177e4SLinus Torvalds 	struct net_device *dev = v;
7991da177e4SLinus Torvalds 	const struct yam_port *yp = netdev_priv(dev);
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	seq_printf(seq, "Device %s\n", dev->name);
8021da177e4SLinus Torvalds 	seq_printf(seq, "  Up       %d\n", netif_running(dev));
8031da177e4SLinus Torvalds 	seq_printf(seq, "  Speed    %u\n", yp->bitrate);
8041da177e4SLinus Torvalds 	seq_printf(seq, "  IoBase   0x%x\n", yp->iobase);
8051da177e4SLinus Torvalds 	seq_printf(seq, "  BaudRate %u\n", yp->baudrate);
8061da177e4SLinus Torvalds 	seq_printf(seq, "  IRQ      %u\n", yp->irq);
8071da177e4SLinus Torvalds 	seq_printf(seq, "  TxState  %u\n", yp->tx_state);
8081da177e4SLinus Torvalds 	seq_printf(seq, "  Duplex   %u\n", yp->dupmode);
8091da177e4SLinus Torvalds 	seq_printf(seq, "  HoldDly  %u\n", yp->holdd);
8101da177e4SLinus Torvalds 	seq_printf(seq, "  TxDelay  %u\n", yp->txd);
8111da177e4SLinus Torvalds 	seq_printf(seq, "  TxTail   %u\n", yp->txtail);
8121da177e4SLinus Torvalds 	seq_printf(seq, "  SlotTime %u\n", yp->slot);
8131da177e4SLinus Torvalds 	seq_printf(seq, "  Persist  %u\n", yp->pers);
8143c94acb7SStephen Hemminger 	seq_printf(seq, "  TxFrames %lu\n", dev->stats.tx_packets);
8153c94acb7SStephen Hemminger 	seq_printf(seq, "  RxFrames %lu\n", dev->stats.rx_packets);
8161da177e4SLinus Torvalds 	seq_printf(seq, "  TxInt    %u\n", yp->nb_mdint);
8171da177e4SLinus Torvalds 	seq_printf(seq, "  RxInt    %u\n", yp->nb_rxint);
8183c94acb7SStephen Hemminger 	seq_printf(seq, "  RxOver   %lu\n", dev->stats.rx_fifo_errors);
8191da177e4SLinus Torvalds 	seq_printf(seq, "\n");
8201da177e4SLinus Torvalds 	return 0;
8211da177e4SLinus Torvalds }
8221da177e4SLinus Torvalds 
8234101dec9SJan Engelhardt static const struct seq_operations yam_seqops = {
8241da177e4SLinus Torvalds 	.start = yam_seq_start,
8251da177e4SLinus Torvalds 	.next = yam_seq_next,
8261da177e4SLinus Torvalds 	.stop = yam_seq_stop,
8271da177e4SLinus Torvalds 	.show = yam_seq_show,
8281da177e4SLinus Torvalds };
8291da177e4SLinus Torvalds #endif
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 
8321da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
8331da177e4SLinus Torvalds 
yam_open(struct net_device * dev)8341da177e4SLinus Torvalds static int yam_open(struct net_device *dev)
8351da177e4SLinus Torvalds {
8361da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
8371da177e4SLinus Torvalds 	enum uart u;
8381da177e4SLinus Torvalds 	int i;
8391da177e4SLinus Torvalds 	int ret=0;
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 	printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq);
8421da177e4SLinus Torvalds 
843429a22caSColin Ian King 	if (!yp->bitrate)
8441da177e4SLinus Torvalds 		return -ENXIO;
8451da177e4SLinus Torvalds 	if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT ||
8461da177e4SLinus Torvalds 		dev->irq < 2 || dev->irq > 15) {
8471da177e4SLinus Torvalds 		return -ENXIO;
8481da177e4SLinus Torvalds 	}
8491da177e4SLinus Torvalds 	if (!request_region(dev->base_addr, YAM_EXTENT, dev->name))
8501da177e4SLinus Torvalds 	{
8511da177e4SLinus Torvalds 		printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr);
8521da177e4SLinus Torvalds 		return -EACCES;
8531da177e4SLinus Torvalds 	}
8541da177e4SLinus Torvalds 	if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) {
8551da177e4SLinus Torvalds 		printk(KERN_ERR "%s: cannot find uart type\n", dev->name);
8561da177e4SLinus Torvalds 		ret = -EIO;
8571da177e4SLinus Torvalds 		goto out_release_base;
8581da177e4SLinus Torvalds 	}
8591da177e4SLinus Torvalds 	if (fpga_download(dev->base_addr, yp->bitrate)) {
8601da177e4SLinus Torvalds 		printk(KERN_ERR "%s: cannot init FPGA\n", dev->name);
8611da177e4SLinus Torvalds 		ret = -EIO;
8621da177e4SLinus Torvalds 		goto out_release_base;
8631da177e4SLinus Torvalds 	}
8641da177e4SLinus Torvalds 	outb(0, IER(dev->base_addr));
865bfdd56b2SMichael Opdenacker 	if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) {
8661da177e4SLinus Torvalds 		printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq);
8671da177e4SLinus Torvalds 		ret = -EBUSY;
8681da177e4SLinus Torvalds 		goto out_release_base;
8691da177e4SLinus Torvalds 	}
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	yam_set_uart(dev);
8721da177e4SLinus Torvalds 
8731da177e4SLinus Torvalds 	netif_start_queue(dev);
8741da177e4SLinus Torvalds 
8751da177e4SLinus Torvalds 	yp->slotcnt = yp->slot / 10;
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds 	/* Reset overruns for all ports - FPGA programming makes overruns */
8781da177e4SLinus Torvalds 	for (i = 0; i < NR_PORTS; i++) {
8799b329f18SHannes Eder 		struct net_device *yam_dev = yam_devs[i];
8803c94acb7SStephen Hemminger 
8819b329f18SHannes Eder 		inb(LSR(yam_dev->base_addr));
8829b329f18SHannes Eder 		yam_dev->stats.rx_fifo_errors = 0;
8831da177e4SLinus Torvalds 	}
8841da177e4SLinus Torvalds 
8851da177e4SLinus Torvalds 	printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq,
8861da177e4SLinus Torvalds 		   uart_str[u]);
8871da177e4SLinus Torvalds 	return 0;
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds out_release_base:
8901da177e4SLinus Torvalds 	release_region(dev->base_addr, YAM_EXTENT);
8911da177e4SLinus Torvalds 	return ret;
8921da177e4SLinus Torvalds }
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
8951da177e4SLinus Torvalds 
yam_close(struct net_device * dev)8961da177e4SLinus Torvalds static int yam_close(struct net_device *dev)
8971da177e4SLinus Torvalds {
8981da177e4SLinus Torvalds 	struct sk_buff *skb;
8991da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds 	if (!dev)
9021da177e4SLinus Torvalds 		return -EINVAL;
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds 	/*
9051da177e4SLinus Torvalds 	 * disable interrupts
9061da177e4SLinus Torvalds 	 */
9071da177e4SLinus Torvalds 	outb(0, IER(dev->base_addr));
9081da177e4SLinus Torvalds 	outb(1, MCR(dev->base_addr));
9091da177e4SLinus Torvalds 	/* Remove IRQ handler if last */
9101da177e4SLinus Torvalds 	free_irq(dev->irq,dev);
9111da177e4SLinus Torvalds 	release_region(dev->base_addr, YAM_EXTENT);
9121da177e4SLinus Torvalds 	netif_stop_queue(dev);
9131da177e4SLinus Torvalds 	while ((skb = skb_dequeue(&yp->send_queue)))
9141da177e4SLinus Torvalds 		dev_kfree_skb(skb);
9151da177e4SLinus Torvalds 
9161da177e4SLinus Torvalds 	printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n",
9171da177e4SLinus Torvalds 		   yam_drvname, dev->base_addr, dev->irq);
9181da177e4SLinus Torvalds 	return 0;
9191da177e4SLinus Torvalds }
9201da177e4SLinus Torvalds 
9211da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
9221da177e4SLinus Torvalds 
yam_siocdevprivate(struct net_device * dev,struct ifreq * ifr,void __user * data,int cmd)92325ec92fbSArnd Bergmann static int yam_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd)
9241da177e4SLinus Torvalds {
9251da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
9261da177e4SLinus Torvalds 	struct yamdrv_ioctl_cfg yi;
9271da177e4SLinus Torvalds 	struct yamdrv_ioctl_mcs *ym;
9281da177e4SLinus Torvalds 	int ioctl_cmd;
9291da177e4SLinus Torvalds 
93025ec92fbSArnd Bergmann 	if (copy_from_user(&ioctl_cmd, data, sizeof(int)))
9311da177e4SLinus Torvalds 		return -EFAULT;
9321da177e4SLinus Torvalds 
9331da177e4SLinus Torvalds 	if (yp->magic != YAM_MAGIC)
9341da177e4SLinus Torvalds 		return -EINVAL;
9351da177e4SLinus Torvalds 
9361da177e4SLinus Torvalds 	if (!capable(CAP_NET_ADMIN))
9371da177e4SLinus Torvalds 		return -EPERM;
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 	if (cmd != SIOCDEVPRIVATE)
9401da177e4SLinus Torvalds 		return -EINVAL;
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds 	switch (ioctl_cmd) {
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	case SIOCYAMRESERVED:
9451da177e4SLinus Torvalds 		return -EINVAL;			/* unused */
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 	case SIOCYAMSMCS:
9481da177e4SLinus Torvalds 		if (netif_running(dev))
9491da177e4SLinus Torvalds 			return -EINVAL;		/* Cannot change this parameter when up */
95025ec92fbSArnd Bergmann 		ym = memdup_user(data, sizeof(struct yamdrv_ioctl_mcs));
951871ff2ebSGeliang Tang 		if (IS_ERR(ym))
952871ff2ebSGeliang Tang 			return PTR_ERR(ym);
95329eb3154SHangyu Hua 		if (ym->cmd != SIOCYAMSMCS || ym->bitrate > YAM_MAXBITRATE) {
9541da177e4SLinus Torvalds 			kfree(ym);
9551da177e4SLinus Torvalds 			return -EINVAL;
9561da177e4SLinus Torvalds 		}
957a7a5eb9dSJaswinder Singh Rajput 		/* setting predef as 0 for loading userdefined mcs data */
958a7a5eb9dSJaswinder Singh Rajput 		add_mcs(ym->bits, ym->bitrate, 0);
9591da177e4SLinus Torvalds 		kfree(ym);
9601da177e4SLinus Torvalds 		break;
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds 	case SIOCYAMSCFG:
9631da177e4SLinus Torvalds 		if (!capable(CAP_SYS_RAWIO))
9641da177e4SLinus Torvalds 			return -EPERM;
96525ec92fbSArnd Bergmann 		if (copy_from_user(&yi, data, sizeof(struct yamdrv_ioctl_cfg)))
9661da177e4SLinus Torvalds 			return -EFAULT;
9671da177e4SLinus Torvalds 
9680781168eSWenwen Wang 		if (yi.cmd != SIOCYAMSCFG)
9690781168eSWenwen Wang 			return -EINVAL;
9701da177e4SLinus Torvalds 		if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev))
9711da177e4SLinus Torvalds 			return -EINVAL;		/* Cannot change this parameter when up */
9721da177e4SLinus Torvalds 		if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev))
9731da177e4SLinus Torvalds 			return -EINVAL;		/* Cannot change this parameter when up */
9741da177e4SLinus Torvalds 		if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev))
9751da177e4SLinus Torvalds 			return -EINVAL;		/* Cannot change this parameter when up */
9761da177e4SLinus Torvalds 		if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev))
9771da177e4SLinus Torvalds 			return -EINVAL;		/* Cannot change this parameter when up */
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_IOBASE) {
9801da177e4SLinus Torvalds 			yp->iobase = yi.cfg.iobase;
9811da177e4SLinus Torvalds 			dev->base_addr = yi.cfg.iobase;
9821da177e4SLinus Torvalds 		}
9831da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_IRQ) {
9841da177e4SLinus Torvalds 			if (yi.cfg.irq > 15)
9851da177e4SLinus Torvalds 				return -EINVAL;
9861da177e4SLinus Torvalds 			yp->irq = yi.cfg.irq;
9871da177e4SLinus Torvalds 			dev->irq = yi.cfg.irq;
9881da177e4SLinus Torvalds 		}
9891da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_BITRATE) {
9901da177e4SLinus Torvalds 			if (yi.cfg.bitrate > YAM_MAXBITRATE)
9911da177e4SLinus Torvalds 				return -EINVAL;
9921da177e4SLinus Torvalds 			yp->bitrate = yi.cfg.bitrate;
9931da177e4SLinus Torvalds 		}
9941da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_BAUDRATE) {
9951da177e4SLinus Torvalds 			if (yi.cfg.baudrate > YAM_MAXBAUDRATE)
9961da177e4SLinus Torvalds 				return -EINVAL;
9971da177e4SLinus Torvalds 			yp->baudrate = yi.cfg.baudrate;
9981da177e4SLinus Torvalds 		}
9991da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_MODE) {
10001da177e4SLinus Torvalds 			if (yi.cfg.mode > YAM_MAXMODE)
10011da177e4SLinus Torvalds 				return -EINVAL;
10021da177e4SLinus Torvalds 			yp->dupmode = yi.cfg.mode;
10031da177e4SLinus Torvalds 		}
10041da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_HOLDDLY) {
10051da177e4SLinus Torvalds 			if (yi.cfg.holddly > YAM_MAXHOLDDLY)
10061da177e4SLinus Torvalds 				return -EINVAL;
10071da177e4SLinus Torvalds 			yp->holdd = yi.cfg.holddly;
10081da177e4SLinus Torvalds 		}
10091da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_TXDELAY) {
10101da177e4SLinus Torvalds 			if (yi.cfg.txdelay > YAM_MAXTXDELAY)
10111da177e4SLinus Torvalds 				return -EINVAL;
10121da177e4SLinus Torvalds 			yp->txd = yi.cfg.txdelay;
10131da177e4SLinus Torvalds 		}
10141da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_TXTAIL) {
10151da177e4SLinus Torvalds 			if (yi.cfg.txtail > YAM_MAXTXTAIL)
10161da177e4SLinus Torvalds 				return -EINVAL;
10171da177e4SLinus Torvalds 			yp->txtail = yi.cfg.txtail;
10181da177e4SLinus Torvalds 		}
10191da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_PERSIST) {
10201da177e4SLinus Torvalds 			if (yi.cfg.persist > YAM_MAXPERSIST)
10211da177e4SLinus Torvalds 				return -EINVAL;
10221da177e4SLinus Torvalds 			yp->pers = yi.cfg.persist;
10231da177e4SLinus Torvalds 		}
10241da177e4SLinus Torvalds 		if (yi.cfg.mask & YAM_SLOTTIME) {
10251da177e4SLinus Torvalds 			if (yi.cfg.slottime > YAM_MAXSLOTTIME)
10261da177e4SLinus Torvalds 				return -EINVAL;
10271da177e4SLinus Torvalds 			yp->slot = yi.cfg.slottime;
10281da177e4SLinus Torvalds 			yp->slotcnt = yp->slot / 10;
10291da177e4SLinus Torvalds 		}
10301da177e4SLinus Torvalds 		break;
10311da177e4SLinus Torvalds 
10321da177e4SLinus Torvalds 	case SIOCYAMGCFG:
10338e3fbf87SSalva Peiró 		memset(&yi, 0, sizeof(yi));
10341da177e4SLinus Torvalds 		yi.cfg.mask = 0xffffffff;
10351da177e4SLinus Torvalds 		yi.cfg.iobase = yp->iobase;
10361da177e4SLinus Torvalds 		yi.cfg.irq = yp->irq;
10371da177e4SLinus Torvalds 		yi.cfg.bitrate = yp->bitrate;
10381da177e4SLinus Torvalds 		yi.cfg.baudrate = yp->baudrate;
10391da177e4SLinus Torvalds 		yi.cfg.mode = yp->dupmode;
10401da177e4SLinus Torvalds 		yi.cfg.txdelay = yp->txd;
10411da177e4SLinus Torvalds 		yi.cfg.holddly = yp->holdd;
10421da177e4SLinus Torvalds 		yi.cfg.txtail = yp->txtail;
10431da177e4SLinus Torvalds 		yi.cfg.persist = yp->pers;
10441da177e4SLinus Torvalds 		yi.cfg.slottime = yp->slot;
104525ec92fbSArnd Bergmann 		if (copy_to_user(data, &yi, sizeof(struct yamdrv_ioctl_cfg)))
10461da177e4SLinus Torvalds 			return -EFAULT;
10471da177e4SLinus Torvalds 		break;
10481da177e4SLinus Torvalds 
10491da177e4SLinus Torvalds 	default:
10501da177e4SLinus Torvalds 		return -EINVAL;
10511da177e4SLinus Torvalds 
10521da177e4SLinus Torvalds 	}
10531da177e4SLinus Torvalds 
10541da177e4SLinus Torvalds 	return 0;
10551da177e4SLinus Torvalds }
10561da177e4SLinus Torvalds 
10571da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
10581da177e4SLinus Torvalds 
yam_set_mac_address(struct net_device * dev,void * addr)10591da177e4SLinus Torvalds static int yam_set_mac_address(struct net_device *dev, void *addr)
10601da177e4SLinus Torvalds {
10611da177e4SLinus Torvalds 	struct sockaddr *sa = (struct sockaddr *) addr;
10621da177e4SLinus Torvalds 
10631da177e4SLinus Torvalds 	/* addr is an AX.25 shifted ASCII mac address */
1064ea52a0b5SJakub Kicinski 	dev_addr_set(dev, sa->sa_data);
10651da177e4SLinus Torvalds 	return 0;
10661da177e4SLinus Torvalds }
10671da177e4SLinus Torvalds 
10681da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
10691da177e4SLinus Torvalds 
10703f75f748SStephen Hemminger static const struct net_device_ops yam_netdev_ops = {
10713f75f748SStephen Hemminger 	.ndo_open	     = yam_open,
10723f75f748SStephen Hemminger 	.ndo_stop	     = yam_close,
10733f75f748SStephen Hemminger 	.ndo_start_xmit      = yam_send_packet,
107425ec92fbSArnd Bergmann 	.ndo_siocdevprivate  = yam_siocdevprivate,
10753f75f748SStephen Hemminger 	.ndo_set_mac_address = yam_set_mac_address,
10763f75f748SStephen Hemminger };
10773f75f748SStephen Hemminger 
yam_setup(struct net_device * dev)10781da177e4SLinus Torvalds static void yam_setup(struct net_device *dev)
10791da177e4SLinus Torvalds {
10801da177e4SLinus Torvalds 	struct yam_port *yp = netdev_priv(dev);
10811da177e4SLinus Torvalds 
10821da177e4SLinus Torvalds 	yp->magic = YAM_MAGIC;
10831da177e4SLinus Torvalds 	yp->bitrate = DEFAULT_BITRATE;
10841da177e4SLinus Torvalds 	yp->baudrate = DEFAULT_BITRATE * 2;
10851da177e4SLinus Torvalds 	yp->iobase = 0;
10861da177e4SLinus Torvalds 	yp->irq = 0;
10871da177e4SLinus Torvalds 	yp->dupmode = 0;
10881da177e4SLinus Torvalds 	yp->holdd = DEFAULT_HOLDD;
10891da177e4SLinus Torvalds 	yp->txd = DEFAULT_TXD;
10901da177e4SLinus Torvalds 	yp->txtail = DEFAULT_TXTAIL;
10911da177e4SLinus Torvalds 	yp->slot = DEFAULT_SLOT;
10921da177e4SLinus Torvalds 	yp->pers = DEFAULT_PERS;
10931da177e4SLinus Torvalds 	yp->dev = dev;
10941da177e4SLinus Torvalds 
10951da177e4SLinus Torvalds 	dev->base_addr = yp->iobase;
10961da177e4SLinus Torvalds 	dev->irq = yp->irq;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 	skb_queue_head_init(&yp->send_queue);
10991da177e4SLinus Torvalds 
11003f75f748SStephen Hemminger 	dev->netdev_ops = &yam_netdev_ops;
11013b04dddeSStephen Hemminger 	dev->header_ops = &ax25_header_ops;
11021da177e4SLinus Torvalds 
1103c4bc7ee2SRalf Baechle 	dev->type = ARPHRD_AX25;
1104c4bc7ee2SRalf Baechle 	dev->hard_header_len = AX25_MAX_HEADER_LEN;
1105c4bc7ee2SRalf Baechle 	dev->mtu = AX25_MTU;
1106c4bc7ee2SRalf Baechle 	dev->addr_len = AX25_ADDR_LEN;
110715b1c0e8SRalf Baechle 	memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
110820c3d9e4SJakub Kicinski 	dev_addr_set(dev, (u8 *)&ax25_defaddr);
11091da177e4SLinus Torvalds }
11101da177e4SLinus Torvalds 
yam_init_driver(void)11111da177e4SLinus Torvalds static int __init yam_init_driver(void)
11121da177e4SLinus Torvalds {
11131da177e4SLinus Torvalds 	struct net_device *dev;
11141da177e4SLinus Torvalds 	int i, err;
11151da177e4SLinus Torvalds 	char name[IFNAMSIZ];
11161da177e4SLinus Torvalds 
11171da177e4SLinus Torvalds 	printk(yam_drvinfo);
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	for (i = 0; i < NR_PORTS; i++) {
11201da177e4SLinus Torvalds 		sprintf(name, "yam%d", i);
11211da177e4SLinus Torvalds 
11221da177e4SLinus Torvalds 		dev = alloc_netdev(sizeof(struct yam_port), name,
1123c835a677STom Gundersen 				   NET_NAME_UNKNOWN, yam_setup);
11241da177e4SLinus Torvalds 		if (!dev) {
112589dc0be6SJulia Lawall 			pr_err("yam: cannot allocate net device\n");
11261da177e4SLinus Torvalds 			err = -ENOMEM;
11271da177e4SLinus Torvalds 			goto error;
11281da177e4SLinus Torvalds 		}
11291da177e4SLinus Torvalds 
11301da177e4SLinus Torvalds 		err = register_netdev(dev);
11311da177e4SLinus Torvalds 		if (err) {
11321da177e4SLinus Torvalds 			printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name);
113398749b71SWang Hai 			free_netdev(dev);
11341da177e4SLinus Torvalds 			goto error;
11351da177e4SLinus Torvalds 		}
11361da177e4SLinus Torvalds 		yam_devs[i] = dev;
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds 	}
11391da177e4SLinus Torvalds 
1140f6fd8918SKees Cook 	timer_setup(&yam_timer, yam_dotimer, 0);
11411da177e4SLinus Torvalds 	yam_timer.expires = jiffies + HZ / 100;
11421da177e4SLinus Torvalds 	add_timer(&yam_timer);
11431da177e4SLinus Torvalds 
1144fddda2b7SChristoph Hellwig 	proc_create_seq("yam", 0444, init_net.proc_net, &yam_seqops);
11451da177e4SLinus Torvalds 	return 0;
11461da177e4SLinus Torvalds  error:
11471da177e4SLinus Torvalds 	while (--i >= 0) {
11481da177e4SLinus Torvalds 		unregister_netdev(yam_devs[i]);
11491da177e4SLinus Torvalds 		free_netdev(yam_devs[i]);
11501da177e4SLinus Torvalds 	}
11511da177e4SLinus Torvalds 	return err;
11521da177e4SLinus Torvalds }
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
11551da177e4SLinus Torvalds 
yam_cleanup_driver(void)11561da177e4SLinus Torvalds static void __exit yam_cleanup_driver(void)
11571da177e4SLinus Torvalds {
11581da177e4SLinus Torvalds 	struct yam_mcs *p;
11591da177e4SLinus Torvalds 	int i;
11601da177e4SLinus Torvalds 
1161d3be267dSJulia Lawall 	del_timer_sync(&yam_timer);
11621da177e4SLinus Torvalds 	for (i = 0; i < NR_PORTS; i++) {
11631da177e4SLinus Torvalds 		struct net_device *dev = yam_devs[i];
11641da177e4SLinus Torvalds 		if (dev) {
11651da177e4SLinus Torvalds 			unregister_netdev(dev);
11661da177e4SLinus Torvalds 			free_netdev(dev);
11671da177e4SLinus Torvalds 		}
11681da177e4SLinus Torvalds 	}
11691da177e4SLinus Torvalds 
11701da177e4SLinus Torvalds 	while (yam_data) {
11711da177e4SLinus Torvalds 		p = yam_data;
11721da177e4SLinus Torvalds 		yam_data = yam_data->next;
11731da177e4SLinus Torvalds 		kfree(p);
11741da177e4SLinus Torvalds 	}
11751da177e4SLinus Torvalds 
1176ece31ffdSGao feng 	remove_proc_entry("yam", init_net.proc_net);
11771da177e4SLinus Torvalds }
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");
11821da177e4SLinus Torvalds MODULE_DESCRIPTION("Yam amateur radio modem driver");
11831da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1184a7a5eb9dSJaswinder Singh Rajput MODULE_FIRMWARE(FIRMWARE_1200);
1185a7a5eb9dSJaswinder Singh Rajput MODULE_FIRMWARE(FIRMWARE_9600);
11861da177e4SLinus Torvalds 
11871da177e4SLinus Torvalds module_init(yam_init_driver);
11881da177e4SLinus Torvalds module_exit(yam_cleanup_driver);
11891da177e4SLinus Torvalds 
11901da177e4SLinus Torvalds /* --------------------------------------------------------------------- */
11911da177e4SLinus Torvalds 
1192