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