18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SDLA An implementation of a driver for the Sangoma S502/S508 series 48c2ecf20Sopenharmony_ci * multi-protocol PC interface card. Initial offering is with 58c2ecf20Sopenharmony_ci * the DLCI driver, providing Frame Relay support for linux. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Global definitions for the Frame relay interface. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Version: @(#)sdla.c 0.30 12 Sep 1996 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Credits: Sangoma Technologies, for the use of 2 cards for an extended 128c2ecf20Sopenharmony_ci * period of time. 138c2ecf20Sopenharmony_ci * David Mandelstam <dm@sangoma.com> for getting me started on 148c2ecf20Sopenharmony_ci * this project, and incentive to complete it. 158c2ecf20Sopenharmony_ci * Gene Kozen <74604.152@compuserve.com> for providing me with 168c2ecf20Sopenharmony_ci * important information about the cards. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Author: Mike McLagan <mike.mclagan@linux.org> 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Changes: 218c2ecf20Sopenharmony_ci * 0.15 Mike McLagan Improved error handling, packet dropping 228c2ecf20Sopenharmony_ci * 0.20 Mike McLagan New transmit/receive flags for config 238c2ecf20Sopenharmony_ci * If in FR mode, don't accept packets from 248c2ecf20Sopenharmony_ci * non DLCI devices. 258c2ecf20Sopenharmony_ci * 0.25 Mike McLagan Fixed problem with rejecting packets 268c2ecf20Sopenharmony_ci * from non DLCI devices. 278c2ecf20Sopenharmony_ci * 0.30 Mike McLagan Fixed kernel panic when used with modified 288c2ecf20Sopenharmony_ci * ifconfig 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/module.h> 348c2ecf20Sopenharmony_ci#include <linux/kernel.h> 358c2ecf20Sopenharmony_ci#include <linux/types.h> 368c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 378c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 388c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 398c2ecf20Sopenharmony_ci#include <linux/ioport.h> 408c2ecf20Sopenharmony_ci#include <linux/in.h> 418c2ecf20Sopenharmony_ci#include <linux/slab.h> 428c2ecf20Sopenharmony_ci#include <linux/string.h> 438c2ecf20Sopenharmony_ci#include <linux/timer.h> 448c2ecf20Sopenharmony_ci#include <linux/errno.h> 458c2ecf20Sopenharmony_ci#include <linux/init.h> 468c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 478c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 488c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 498c2ecf20Sopenharmony_ci#include <linux/if_frad.h> 508c2ecf20Sopenharmony_ci#include <linux/sdla.h> 518c2ecf20Sopenharmony_ci#include <linux/bitops.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <asm/io.h> 548c2ecf20Sopenharmony_ci#include <asm/dma.h> 558c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const char* version = "SDLA driver v0.30, 12 Sep 1996, mike.mclagan@linux.org"; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic unsigned int valid_port[] = { 0x250, 0x270, 0x280, 0x300, 0x350, 0x360, 0x380, 0x390}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic unsigned int valid_mem[] = { 628c2ecf20Sopenharmony_ci 0xA0000, 0xA2000, 0xA4000, 0xA6000, 0xA8000, 0xAA000, 0xAC000, 0xAE000, 638c2ecf20Sopenharmony_ci 0xB0000, 0xB2000, 0xB4000, 0xB6000, 0xB8000, 0xBA000, 0xBC000, 0xBE000, 648c2ecf20Sopenharmony_ci 0xC0000, 0xC2000, 0xC4000, 0xC6000, 0xC8000, 0xCA000, 0xCC000, 0xCE000, 658c2ecf20Sopenharmony_ci 0xD0000, 0xD2000, 0xD4000, 0xD6000, 0xD8000, 0xDA000, 0xDC000, 0xDE000, 668c2ecf20Sopenharmony_ci 0xE0000, 0xE2000, 0xE4000, 0xE6000, 0xE8000, 0xEA000, 0xEC000, 0xEE000}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sdla_lock); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/********************************************************* 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * these are the core routines that access the card itself 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci *********************************************************/ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define SDLA_WINDOW(dev,addr) outb((((addr) >> 13) & 0x1F), (dev)->base_addr + SDLA_REG_Z80_WINDOW) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void __sdla_read(struct net_device *dev, int addr, void *buf, short len) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci char *temp; 818c2ecf20Sopenharmony_ci const void *base; 828c2ecf20Sopenharmony_ci int offset, bytes; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci temp = buf; 858c2ecf20Sopenharmony_ci while(len) 868c2ecf20Sopenharmony_ci { 878c2ecf20Sopenharmony_ci offset = addr & SDLA_ADDR_MASK; 888c2ecf20Sopenharmony_ci bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len; 898c2ecf20Sopenharmony_ci base = (const void *) (dev->mem_start + offset); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 928c2ecf20Sopenharmony_ci memcpy(temp, base, bytes); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci addr += bytes; 958c2ecf20Sopenharmony_ci temp += bytes; 968c2ecf20Sopenharmony_ci len -= bytes; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void sdla_read(struct net_device *dev, int addr, void *buf, short len) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci unsigned long flags; 1038c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 1048c2ecf20Sopenharmony_ci __sdla_read(dev, addr, buf, len); 1058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void __sdla_write(struct net_device *dev, int addr, 1098c2ecf20Sopenharmony_ci const void *buf, short len) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci const char *temp; 1128c2ecf20Sopenharmony_ci void *base; 1138c2ecf20Sopenharmony_ci int offset, bytes; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci temp = buf; 1168c2ecf20Sopenharmony_ci while(len) 1178c2ecf20Sopenharmony_ci { 1188c2ecf20Sopenharmony_ci offset = addr & SDLA_ADDR_MASK; 1198c2ecf20Sopenharmony_ci bytes = offset + len > SDLA_WINDOW_SIZE ? SDLA_WINDOW_SIZE - offset : len; 1208c2ecf20Sopenharmony_ci base = (void *) (dev->mem_start + offset); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 1238c2ecf20Sopenharmony_ci memcpy(base, temp, bytes); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci addr += bytes; 1268c2ecf20Sopenharmony_ci temp += bytes; 1278c2ecf20Sopenharmony_ci len -= bytes; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void sdla_write(struct net_device *dev, int addr, 1328c2ecf20Sopenharmony_ci const void *buf, short len) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci unsigned long flags; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 1378c2ecf20Sopenharmony_ci __sdla_write(dev, addr, buf, len); 1388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void sdla_clear(struct net_device *dev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci unsigned long flags; 1458c2ecf20Sopenharmony_ci char *base; 1468c2ecf20Sopenharmony_ci int len, addr, bytes; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci len = 65536; 1498c2ecf20Sopenharmony_ci addr = 0; 1508c2ecf20Sopenharmony_ci bytes = SDLA_WINDOW_SIZE; 1518c2ecf20Sopenharmony_ci base = (void *) dev->mem_start; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 1548c2ecf20Sopenharmony_ci while(len) 1558c2ecf20Sopenharmony_ci { 1568c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 1578c2ecf20Sopenharmony_ci memset(base, 0, bytes); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci addr += bytes; 1608c2ecf20Sopenharmony_ci len -= bytes; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic char sdla_byte(struct net_device *dev, int addr) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci unsigned long flags; 1698c2ecf20Sopenharmony_ci char byte, *temp; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci temp = (void *) (dev->mem_start + (addr & SDLA_ADDR_MASK)); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 1748c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 1758c2ecf20Sopenharmony_ci byte = *temp; 1768c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return byte; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void sdla_stop(struct net_device *dev) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct frad_local *flp; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 1868c2ecf20Sopenharmony_ci switch(flp->type) 1878c2ecf20Sopenharmony_ci { 1888c2ecf20Sopenharmony_ci case SDLA_S502A: 1898c2ecf20Sopenharmony_ci outb(SDLA_S502A_HALT, dev->base_addr + SDLA_REG_CONTROL); 1908c2ecf20Sopenharmony_ci flp->state = SDLA_HALT; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case SDLA_S502E: 1938c2ecf20Sopenharmony_ci outb(SDLA_HALT, dev->base_addr + SDLA_REG_Z80_CONTROL); 1948c2ecf20Sopenharmony_ci outb(SDLA_S502E_ENABLE, dev->base_addr + SDLA_REG_CONTROL); 1958c2ecf20Sopenharmony_ci flp->state = SDLA_S502E_ENABLE; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci case SDLA_S507: 1988c2ecf20Sopenharmony_ci flp->state &= ~SDLA_CPUEN; 1998c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case SDLA_S508: 2028c2ecf20Sopenharmony_ci flp->state &= ~SDLA_CPUEN; 2038c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void sdla_start(struct net_device *dev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct frad_local *flp; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 2138c2ecf20Sopenharmony_ci switch(flp->type) 2148c2ecf20Sopenharmony_ci { 2158c2ecf20Sopenharmony_ci case SDLA_S502A: 2168c2ecf20Sopenharmony_ci outb(SDLA_S502A_NMI, dev->base_addr + SDLA_REG_CONTROL); 2178c2ecf20Sopenharmony_ci outb(SDLA_S502A_START, dev->base_addr + SDLA_REG_CONTROL); 2188c2ecf20Sopenharmony_ci flp->state = SDLA_S502A_START; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case SDLA_S502E: 2218c2ecf20Sopenharmony_ci outb(SDLA_S502E_CPUEN, dev->base_addr + SDLA_REG_Z80_CONTROL); 2228c2ecf20Sopenharmony_ci outb(0x00, dev->base_addr + SDLA_REG_CONTROL); 2238c2ecf20Sopenharmony_ci flp->state = 0; 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci case SDLA_S507: 2268c2ecf20Sopenharmony_ci flp->state |= SDLA_CPUEN; 2278c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case SDLA_S508: 2308c2ecf20Sopenharmony_ci flp->state |= SDLA_CPUEN; 2318c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/**************************************************** 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * this is used for the S502A/E cards to determine 2398c2ecf20Sopenharmony_ci * the speed of the onboard CPU. Calibration is 2408c2ecf20Sopenharmony_ci * necessary for the Frame Relay code uploaded 2418c2ecf20Sopenharmony_ci * later. Incorrect results cause timing problems 2428c2ecf20Sopenharmony_ci * with link checks & status messages 2438c2ecf20Sopenharmony_ci * 2448c2ecf20Sopenharmony_ci ***************************************************/ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int sdla_z80_poll(struct net_device *dev, int z80_addr, int jiffs, char resp1, char resp2) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci unsigned long start, done, now; 2498c2ecf20Sopenharmony_ci char resp, *temp; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci start = now = jiffies; 2528c2ecf20Sopenharmony_ci done = jiffies + jiffs; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci temp = (void *)dev->mem_start; 2558c2ecf20Sopenharmony_ci temp += z80_addr & SDLA_ADDR_MASK; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci resp = ~resp1; 2588c2ecf20Sopenharmony_ci while (time_before(jiffies, done) && (resp != resp1) && (!resp2 || (resp != resp2))) 2598c2ecf20Sopenharmony_ci { 2608c2ecf20Sopenharmony_ci if (jiffies != now) 2618c2ecf20Sopenharmony_ci { 2628c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, z80_addr); 2638c2ecf20Sopenharmony_ci now = jiffies; 2648c2ecf20Sopenharmony_ci resp = *temp; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci return time_before(jiffies, done) ? jiffies - start : -1; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* constants for Z80 CPU speed */ 2718c2ecf20Sopenharmony_ci#define Z80_READY '1' /* Z80 is ready to begin */ 2728c2ecf20Sopenharmony_ci#define LOADER_READY '2' /* driver is ready to begin */ 2738c2ecf20Sopenharmony_ci#define Z80_SCC_OK '3' /* SCC is on board */ 2748c2ecf20Sopenharmony_ci#define Z80_SCC_BAD '4' /* SCC was not found */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int sdla_cpuspeed(struct net_device *dev, struct ifreq *ifr) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int jiffs; 2798c2ecf20Sopenharmony_ci char data; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci sdla_start(dev); 2828c2ecf20Sopenharmony_ci if (sdla_z80_poll(dev, 0, 3*HZ, Z80_READY, 0) < 0) 2838c2ecf20Sopenharmony_ci return -EIO; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci data = LOADER_READY; 2868c2ecf20Sopenharmony_ci sdla_write(dev, 0, &data, 1); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if ((jiffs = sdla_z80_poll(dev, 0, 8*HZ, Z80_SCC_OK, Z80_SCC_BAD)) < 0) 2898c2ecf20Sopenharmony_ci return -EIO; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci sdla_stop(dev); 2928c2ecf20Sopenharmony_ci sdla_read(dev, 0, &data, 1); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (data == Z80_SCC_BAD) 2958c2ecf20Sopenharmony_ci { 2968c2ecf20Sopenharmony_ci printk("%s: SCC bad\n", dev->name); 2978c2ecf20Sopenharmony_ci return -EIO; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (data != Z80_SCC_OK) 3018c2ecf20Sopenharmony_ci return -EINVAL; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (jiffs < 165) 3048c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_16M; 3058c2ecf20Sopenharmony_ci else if (jiffs < 220) 3068c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_10M; 3078c2ecf20Sopenharmony_ci else if (jiffs < 258) 3088c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_8M; 3098c2ecf20Sopenharmony_ci else if (jiffs < 357) 3108c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_7M; 3118c2ecf20Sopenharmony_ci else if (jiffs < 467) 3128c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_5M; 3138c2ecf20Sopenharmony_ci else 3148c2ecf20Sopenharmony_ci ifr->ifr_mtu = SDLA_CPU_3M; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/************************************************ 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * Direct interaction with the Frame Relay code 3228c2ecf20Sopenharmony_ci * starts here. 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci ************************************************/ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistruct _dlci_stat 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci short dlci; 3298c2ecf20Sopenharmony_ci char flags; 3308c2ecf20Sopenharmony_ci} __packed; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistruct _frad_stat 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci char flags; 3358c2ecf20Sopenharmony_ci struct _dlci_stat dlcis[SDLA_MAX_DLCI]; 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void sdla_errors(struct net_device *dev, int cmd, int dlci, int ret, int len, void *data) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct _dlci_stat *pstatus; 3418c2ecf20Sopenharmony_ci short *pdlci; 3428c2ecf20Sopenharmony_ci int i; 3438c2ecf20Sopenharmony_ci char *state, line[30]; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci switch (ret) 3468c2ecf20Sopenharmony_ci { 3478c2ecf20Sopenharmony_ci case SDLA_RET_MODEM: 3488c2ecf20Sopenharmony_ci state = data; 3498c2ecf20Sopenharmony_ci if (*state & SDLA_MODEM_DCD_LOW) 3508c2ecf20Sopenharmony_ci netdev_info(dev, "Modem DCD unexpectedly low!\n"); 3518c2ecf20Sopenharmony_ci if (*state & SDLA_MODEM_CTS_LOW) 3528c2ecf20Sopenharmony_ci netdev_info(dev, "Modem CTS unexpectedly low!\n"); 3538c2ecf20Sopenharmony_ci /* I should probably do something about this! */ 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci case SDLA_RET_CHANNEL_OFF: 3578c2ecf20Sopenharmony_ci netdev_info(dev, "Channel became inoperative!\n"); 3588c2ecf20Sopenharmony_ci /* same here */ 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci case SDLA_RET_CHANNEL_ON: 3628c2ecf20Sopenharmony_ci netdev_info(dev, "Channel became operative!\n"); 3638c2ecf20Sopenharmony_ci /* same here */ 3648c2ecf20Sopenharmony_ci break; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci case SDLA_RET_DLCI_STATUS: 3678c2ecf20Sopenharmony_ci netdev_info(dev, "Status change reported by Access Node\n"); 3688c2ecf20Sopenharmony_ci len /= sizeof(struct _dlci_stat); 3698c2ecf20Sopenharmony_ci for(pstatus = data, i=0;i < len;i++,pstatus++) 3708c2ecf20Sopenharmony_ci { 3718c2ecf20Sopenharmony_ci if (pstatus->flags & SDLA_DLCI_NEW) 3728c2ecf20Sopenharmony_ci state = "new"; 3738c2ecf20Sopenharmony_ci else if (pstatus->flags & SDLA_DLCI_DELETED) 3748c2ecf20Sopenharmony_ci state = "deleted"; 3758c2ecf20Sopenharmony_ci else if (pstatus->flags & SDLA_DLCI_ACTIVE) 3768c2ecf20Sopenharmony_ci state = "active"; 3778c2ecf20Sopenharmony_ci else 3788c2ecf20Sopenharmony_ci { 3798c2ecf20Sopenharmony_ci sprintf(line, "unknown status: %02X", pstatus->flags); 3808c2ecf20Sopenharmony_ci state = line; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci netdev_info(dev, "DLCI %i: %s\n", 3838c2ecf20Sopenharmony_ci pstatus->dlci, state); 3848c2ecf20Sopenharmony_ci /* same here */ 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci case SDLA_RET_DLCI_UNKNOWN: 3898c2ecf20Sopenharmony_ci netdev_info(dev, "Received unknown DLCIs:"); 3908c2ecf20Sopenharmony_ci len /= sizeof(short); 3918c2ecf20Sopenharmony_ci for(pdlci = data,i=0;i < len;i++,pdlci++) 3928c2ecf20Sopenharmony_ci pr_cont(" %i", *pdlci); 3938c2ecf20Sopenharmony_ci pr_cont("\n"); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case SDLA_RET_TIMEOUT: 3978c2ecf20Sopenharmony_ci netdev_err(dev, "Command timed out!\n"); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci case SDLA_RET_BUF_OVERSIZE: 4018c2ecf20Sopenharmony_ci netdev_info(dev, "Bc/CIR overflow, acceptable size is %i\n", 4028c2ecf20Sopenharmony_ci len); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci case SDLA_RET_BUF_TOO_BIG: 4068c2ecf20Sopenharmony_ci netdev_info(dev, "Buffer size over specified max of %i\n", 4078c2ecf20Sopenharmony_ci len); 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci case SDLA_RET_CHANNEL_INACTIVE: 4118c2ecf20Sopenharmony_ci case SDLA_RET_DLCI_INACTIVE: 4128c2ecf20Sopenharmony_ci case SDLA_RET_CIR_OVERFLOW: 4138c2ecf20Sopenharmony_ci case SDLA_RET_NO_BUFS: 4148c2ecf20Sopenharmony_ci if (cmd == SDLA_INFORMATION_WRITE) 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci fallthrough; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci default: 4198c2ecf20Sopenharmony_ci netdev_dbg(dev, "Cmd 0x%02X generated return code 0x%02X\n", 4208c2ecf20Sopenharmony_ci cmd, ret); 4218c2ecf20Sopenharmony_ci /* Further processing could be done here */ 4228c2ecf20Sopenharmony_ci break; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int sdla_cmd(struct net_device *dev, int cmd, short dlci, short flags, 4278c2ecf20Sopenharmony_ci void *inbuf, short inlen, void *outbuf, short *outlen) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci static struct _frad_stat status; 4308c2ecf20Sopenharmony_ci struct frad_local *flp; 4318c2ecf20Sopenharmony_ci struct sdla_cmd *cmd_buf; 4328c2ecf20Sopenharmony_ci unsigned long pflags; 4338c2ecf20Sopenharmony_ci unsigned long jiffs; 4348c2ecf20Sopenharmony_ci int ret, waiting, len; 4358c2ecf20Sopenharmony_ci long window; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 4388c2ecf20Sopenharmony_ci window = flp->type == SDLA_S508 ? SDLA_508_CMD_BUF : SDLA_502_CMD_BUF; 4398c2ecf20Sopenharmony_ci cmd_buf = (struct sdla_cmd *)(dev->mem_start + (window & SDLA_ADDR_MASK)); 4408c2ecf20Sopenharmony_ci ret = 0; 4418c2ecf20Sopenharmony_ci len = 0; 4428c2ecf20Sopenharmony_ci jiffs = jiffies + HZ; /* 1 second is plenty */ 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, pflags); 4458c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, window); 4468c2ecf20Sopenharmony_ci cmd_buf->cmd = cmd; 4478c2ecf20Sopenharmony_ci cmd_buf->dlci = dlci; 4488c2ecf20Sopenharmony_ci cmd_buf->flags = flags; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (inbuf) 4518c2ecf20Sopenharmony_ci memcpy(cmd_buf->data, inbuf, inlen); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci cmd_buf->length = inlen; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci cmd_buf->opp_flag = 1; 4568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, pflags); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci waiting = 1; 4598c2ecf20Sopenharmony_ci len = 0; 4608c2ecf20Sopenharmony_ci while (waiting && time_before_eq(jiffies, jiffs)) 4618c2ecf20Sopenharmony_ci { 4628c2ecf20Sopenharmony_ci if (waiting++ % 3) 4638c2ecf20Sopenharmony_ci { 4648c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, pflags); 4658c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, window); 4668c2ecf20Sopenharmony_ci waiting = ((volatile int)(cmd_buf->opp_flag)); 4678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, pflags); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (!waiting) 4728c2ecf20Sopenharmony_ci { 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, pflags); 4758c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, window); 4768c2ecf20Sopenharmony_ci ret = cmd_buf->retval; 4778c2ecf20Sopenharmony_ci len = cmd_buf->length; 4788c2ecf20Sopenharmony_ci if (outbuf && outlen) 4798c2ecf20Sopenharmony_ci { 4808c2ecf20Sopenharmony_ci *outlen = *outlen >= len ? len : *outlen; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (*outlen) 4838c2ecf20Sopenharmony_ci memcpy(outbuf, cmd_buf->data, *outlen); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* This is a local copy that's used for error handling */ 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci memcpy(&status, cmd_buf->data, len > sizeof(status) ? sizeof(status) : len); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, pflags); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci else 4938c2ecf20Sopenharmony_ci ret = SDLA_RET_TIMEOUT; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (ret != SDLA_RET_OK) 4968c2ecf20Sopenharmony_ci sdla_errors(dev, cmd, dlci, ret, len, &status); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci return ret; 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci/*********************************************** 5028c2ecf20Sopenharmony_ci * 5038c2ecf20Sopenharmony_ci * these functions are called by the DLCI driver 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci ***********************************************/ 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int sdla_reconfig(struct net_device *dev); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic int sdla_activate(struct net_device *slave, struct net_device *master) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct frad_local *flp; 5128c2ecf20Sopenharmony_ci int i; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci flp = netdev_priv(slave); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 5178c2ecf20Sopenharmony_ci if (flp->master[i] == master) 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 5218c2ecf20Sopenharmony_ci return -ENODEV; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci flp->dlci[i] = abs(flp->dlci[i]); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE)) 5268c2ecf20Sopenharmony_ci sdla_cmd(slave, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int sdla_deactivate(struct net_device *slave, struct net_device *master) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct frad_local *flp; 5348c2ecf20Sopenharmony_ci int i; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci flp = netdev_priv(slave); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 5398c2ecf20Sopenharmony_ci if (flp->master[i] == master) 5408c2ecf20Sopenharmony_ci break; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 5438c2ecf20Sopenharmony_ci return -ENODEV; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci flp->dlci[i] = -abs(flp->dlci[i]); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (netif_running(slave) && (flp->config.station == FRAD_STATION_NODE)) 5488c2ecf20Sopenharmony_ci sdla_cmd(slave, SDLA_DEACTIVATE_DLCI, 0, 0, &flp->dlci[i], sizeof(short), NULL, NULL); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return 0; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int sdla_assoc(struct net_device *slave, struct net_device *master) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct frad_local *flp; 5568c2ecf20Sopenharmony_ci int i; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (master->type != ARPHRD_DLCI) 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci flp = netdev_priv(slave); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 5648c2ecf20Sopenharmony_ci { 5658c2ecf20Sopenharmony_ci if (!flp->master[i]) 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci if (abs(flp->dlci[i]) == *(short *)(master->dev_addr)) 5688c2ecf20Sopenharmony_ci return -EADDRINUSE; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 5728c2ecf20Sopenharmony_ci return -EMLINK; /* #### Alan: Comments on this ?? */ 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci flp->master[i] = master; 5768c2ecf20Sopenharmony_ci flp->dlci[i] = -*(short *)(master->dev_addr); 5778c2ecf20Sopenharmony_ci master->mtu = slave->mtu; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (netif_running(slave)) { 5808c2ecf20Sopenharmony_ci if (flp->config.station == FRAD_STATION_CPE) 5818c2ecf20Sopenharmony_ci sdla_reconfig(slave); 5828c2ecf20Sopenharmony_ci else 5838c2ecf20Sopenharmony_ci sdla_cmd(slave, SDLA_ADD_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int sdla_deassoc(struct net_device *slave, struct net_device *master) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct frad_local *flp; 5928c2ecf20Sopenharmony_ci int i; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci flp = netdev_priv(slave); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 5978c2ecf20Sopenharmony_ci if (flp->master[i] == master) 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 6018c2ecf20Sopenharmony_ci return -ENODEV; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci flp->master[i] = NULL; 6048c2ecf20Sopenharmony_ci flp->dlci[i] = 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (netif_running(slave)) { 6088c2ecf20Sopenharmony_ci if (flp->config.station == FRAD_STATION_CPE) 6098c2ecf20Sopenharmony_ci sdla_reconfig(slave); 6108c2ecf20Sopenharmony_ci else 6118c2ecf20Sopenharmony_ci sdla_cmd(slave, SDLA_DELETE_DLCI, 0, 0, master->dev_addr, sizeof(short), NULL, NULL); 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int sdla_dlci_conf(struct net_device *slave, struct net_device *master, int get) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct frad_local *flp; 6208c2ecf20Sopenharmony_ci struct dlci_local *dlp; 6218c2ecf20Sopenharmony_ci int i; 6228c2ecf20Sopenharmony_ci short len, ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci flp = netdev_priv(slave); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 6278c2ecf20Sopenharmony_ci if (flp->master[i] == master) 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 6318c2ecf20Sopenharmony_ci return -ENODEV; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci dlp = netdev_priv(master); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ret = SDLA_RET_OK; 6368c2ecf20Sopenharmony_ci len = sizeof(struct dlci_conf); 6378c2ecf20Sopenharmony_ci if (netif_running(slave)) { 6388c2ecf20Sopenharmony_ci if (get) 6398c2ecf20Sopenharmony_ci ret = sdla_cmd(slave, SDLA_READ_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, 6408c2ecf20Sopenharmony_ci NULL, 0, &dlp->config, &len); 6418c2ecf20Sopenharmony_ci else 6428c2ecf20Sopenharmony_ci ret = sdla_cmd(slave, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, 6438c2ecf20Sopenharmony_ci &dlp->config, sizeof(struct dlci_conf) - 4 * sizeof(short), NULL, NULL); 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci return ret == SDLA_RET_OK ? 0 : -EIO; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/************************** 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci * now for the Linux driver 6528c2ecf20Sopenharmony_ci * 6538c2ecf20Sopenharmony_ci **************************/ 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* NOTE: the DLCI driver deals with freeing the SKB!! */ 6568c2ecf20Sopenharmony_cistatic netdev_tx_t sdla_transmit(struct sk_buff *skb, 6578c2ecf20Sopenharmony_ci struct net_device *dev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct frad_local *flp; 6608c2ecf20Sopenharmony_ci int ret, addr, accept, i; 6618c2ecf20Sopenharmony_ci short size; 6628c2ecf20Sopenharmony_ci unsigned long flags; 6638c2ecf20Sopenharmony_ci struct buf_entry *pbuf; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 6668c2ecf20Sopenharmony_ci ret = 0; 6678c2ecf20Sopenharmony_ci accept = 1; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci netif_stop_queue(dev); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* 6728c2ecf20Sopenharmony_ci * stupid GateD insists on setting up the multicast router thru us 6738c2ecf20Sopenharmony_ci * and we're ill equipped to handle a non Frame Relay packet at this 6748c2ecf20Sopenharmony_ci * time! 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci accept = 1; 6788c2ecf20Sopenharmony_ci switch (dev->type) 6798c2ecf20Sopenharmony_ci { 6808c2ecf20Sopenharmony_ci case ARPHRD_FRAD: 6818c2ecf20Sopenharmony_ci if (skb->dev->type != ARPHRD_DLCI) 6828c2ecf20Sopenharmony_ci { 6838c2ecf20Sopenharmony_ci netdev_warn(dev, "Non DLCI device, type %i, tried to send on FRAD module\n", 6848c2ecf20Sopenharmony_ci skb->dev->type); 6858c2ecf20Sopenharmony_ci accept = 0; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci default: 6898c2ecf20Sopenharmony_ci netdev_warn(dev, "unknown firmware type 0x%04X\n", 6908c2ecf20Sopenharmony_ci dev->type); 6918c2ecf20Sopenharmony_ci accept = 0; 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci if (accept) 6958c2ecf20Sopenharmony_ci { 6968c2ecf20Sopenharmony_ci /* this is frame specific, but till there's a PPP module, it's the default */ 6978c2ecf20Sopenharmony_ci switch (flp->type) 6988c2ecf20Sopenharmony_ci { 6998c2ecf20Sopenharmony_ci case SDLA_S502A: 7008c2ecf20Sopenharmony_ci case SDLA_S502E: 7018c2ecf20Sopenharmony_ci ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, skb->data, skb->len, NULL, NULL); 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case SDLA_S508: 7048c2ecf20Sopenharmony_ci size = sizeof(addr); 7058c2ecf20Sopenharmony_ci ret = sdla_cmd(dev, SDLA_INFORMATION_WRITE, *(short *)(skb->dev->dev_addr), 0, NULL, skb->len, &addr, &size); 7068c2ecf20Sopenharmony_ci if (ret == SDLA_RET_OK) 7078c2ecf20Sopenharmony_ci { 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 7108c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 7118c2ecf20Sopenharmony_ci pbuf = (void *)(dev->mem_start + (addr & SDLA_ADDR_MASK)); 7128c2ecf20Sopenharmony_ci __sdla_write(dev, pbuf->buf_addr, skb->data, skb->len); 7138c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, addr); 7148c2ecf20Sopenharmony_ci pbuf->opp_flag = 1; 7158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci break; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci switch (ret) 7218c2ecf20Sopenharmony_ci { 7228c2ecf20Sopenharmony_ci case SDLA_RET_OK: 7238c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci case SDLA_RET_CIR_OVERFLOW: 7278c2ecf20Sopenharmony_ci case SDLA_RET_BUF_OVERSIZE: 7288c2ecf20Sopenharmony_ci case SDLA_RET_NO_BUFS: 7298c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 7308c2ecf20Sopenharmony_ci break; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci default: 7338c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7388c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 7398c2ecf20Sopenharmony_ci { 7408c2ecf20Sopenharmony_ci if(flp->master[i]!=NULL) 7418c2ecf20Sopenharmony_ci netif_wake_queue(flp->master[i]); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 7458c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic void sdla_receive(struct net_device *dev) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct net_device *master; 7518c2ecf20Sopenharmony_ci struct frad_local *flp; 7528c2ecf20Sopenharmony_ci struct dlci_local *dlp; 7538c2ecf20Sopenharmony_ci struct sk_buff *skb; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci struct sdla_cmd *cmd; 7568c2ecf20Sopenharmony_ci struct buf_info *pbufi; 7578c2ecf20Sopenharmony_ci struct buf_entry *pbuf; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci unsigned long flags; 7608c2ecf20Sopenharmony_ci int i=0, received, success, addr, buf_base, buf_top; 7618c2ecf20Sopenharmony_ci short dlci, len, len2, split; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 7648c2ecf20Sopenharmony_ci success = 1; 7658c2ecf20Sopenharmony_ci received = addr = buf_top = buf_base = 0; 7668c2ecf20Sopenharmony_ci len = dlci = 0; 7678c2ecf20Sopenharmony_ci skb = NULL; 7688c2ecf20Sopenharmony_ci master = NULL; 7698c2ecf20Sopenharmony_ci cmd = NULL; 7708c2ecf20Sopenharmony_ci pbufi = NULL; 7718c2ecf20Sopenharmony_ci pbuf = NULL; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci spin_lock_irqsave(&sdla_lock, flags); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci switch (flp->type) 7768c2ecf20Sopenharmony_ci { 7778c2ecf20Sopenharmony_ci case SDLA_S502A: 7788c2ecf20Sopenharmony_ci case SDLA_S502E: 7798c2ecf20Sopenharmony_ci cmd = (void *) (dev->mem_start + (SDLA_502_RCV_BUF & SDLA_ADDR_MASK)); 7808c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, SDLA_502_RCV_BUF); 7818c2ecf20Sopenharmony_ci success = cmd->opp_flag; 7828c2ecf20Sopenharmony_ci if (!success) 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci dlci = cmd->dlci; 7868c2ecf20Sopenharmony_ci len = cmd->length; 7878c2ecf20Sopenharmony_ci break; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci case SDLA_S508: 7908c2ecf20Sopenharmony_ci pbufi = (void *) (dev->mem_start + (SDLA_508_RXBUF_INFO & SDLA_ADDR_MASK)); 7918c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO); 7928c2ecf20Sopenharmony_ci pbuf = (void *) (dev->mem_start + ((pbufi->rse_base + flp->buffer * sizeof(struct buf_entry)) & SDLA_ADDR_MASK)); 7938c2ecf20Sopenharmony_ci success = pbuf->opp_flag; 7948c2ecf20Sopenharmony_ci if (!success) 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci buf_top = pbufi->buf_top; 7988c2ecf20Sopenharmony_ci buf_base = pbufi->buf_base; 7998c2ecf20Sopenharmony_ci dlci = pbuf->dlci; 8008c2ecf20Sopenharmony_ci len = pbuf->length; 8018c2ecf20Sopenharmony_ci addr = pbuf->buf_addr; 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* common code, find the DLCI and get the SKB */ 8068c2ecf20Sopenharmony_ci if (success) 8078c2ecf20Sopenharmony_ci { 8088c2ecf20Sopenharmony_ci for (i=0;i<CONFIG_DLCI_MAX;i++) 8098c2ecf20Sopenharmony_ci if (flp->dlci[i] == dlci) 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci if (i == CONFIG_DLCI_MAX) 8138c2ecf20Sopenharmony_ci { 8148c2ecf20Sopenharmony_ci netdev_notice(dev, "Received packet from invalid DLCI %i, ignoring\n", 8158c2ecf20Sopenharmony_ci dlci); 8168c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 8178c2ecf20Sopenharmony_ci success = 0; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (success) 8228c2ecf20Sopenharmony_ci { 8238c2ecf20Sopenharmony_ci master = flp->master[i]; 8248c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len + sizeof(struct frhdr)); 8258c2ecf20Sopenharmony_ci if (skb == NULL) 8268c2ecf20Sopenharmony_ci { 8278c2ecf20Sopenharmony_ci netdev_notice(dev, "Memory squeeze, dropping packet\n"); 8288c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 8298c2ecf20Sopenharmony_ci success = 0; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci else 8328c2ecf20Sopenharmony_ci skb_reserve(skb, sizeof(struct frhdr)); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* pick up the data */ 8368c2ecf20Sopenharmony_ci switch (flp->type) 8378c2ecf20Sopenharmony_ci { 8388c2ecf20Sopenharmony_ci case SDLA_S502A: 8398c2ecf20Sopenharmony_ci case SDLA_S502E: 8408c2ecf20Sopenharmony_ci if (success) 8418c2ecf20Sopenharmony_ci __sdla_read(dev, SDLA_502_RCV_BUF + SDLA_502_DATA_OFS, skb_put(skb,len), len); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, SDLA_502_RCV_BUF); 8448c2ecf20Sopenharmony_ci cmd->opp_flag = 0; 8458c2ecf20Sopenharmony_ci break; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci case SDLA_S508: 8488c2ecf20Sopenharmony_ci if (success) 8498c2ecf20Sopenharmony_ci { 8508c2ecf20Sopenharmony_ci /* is this buffer split off the end of the internal ring buffer */ 8518c2ecf20Sopenharmony_ci split = addr + len > buf_top + 1 ? len - (buf_top - addr + 1) : 0; 8528c2ecf20Sopenharmony_ci len2 = len - split; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci __sdla_read(dev, addr, skb_put(skb, len2), len2); 8558c2ecf20Sopenharmony_ci if (split) 8568c2ecf20Sopenharmony_ci __sdla_read(dev, buf_base, skb_put(skb, split), split); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* increment the buffer we're looking at */ 8608c2ecf20Sopenharmony_ci SDLA_WINDOW(dev, SDLA_508_RXBUF_INFO); 8618c2ecf20Sopenharmony_ci flp->buffer = (flp->buffer + 1) % pbufi->rse_num; 8628c2ecf20Sopenharmony_ci pbuf->opp_flag = 0; 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (success) 8678c2ecf20Sopenharmony_ci { 8688c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 8698c2ecf20Sopenharmony_ci dlp = netdev_priv(master); 8708c2ecf20Sopenharmony_ci (*dlp->receive)(skb, master); 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sdla_lock, flags); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic irqreturn_t sdla_isr(int dummy, void *dev_id) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct net_device *dev; 8798c2ecf20Sopenharmony_ci struct frad_local *flp; 8808c2ecf20Sopenharmony_ci char byte; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci dev = dev_id; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (!flp->initialized) 8878c2ecf20Sopenharmony_ci { 8888c2ecf20Sopenharmony_ci netdev_warn(dev, "irq %d for uninitialized device\n", dev->irq); 8898c2ecf20Sopenharmony_ci return IRQ_NONE; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci byte = sdla_byte(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE); 8938c2ecf20Sopenharmony_ci switch (byte) 8948c2ecf20Sopenharmony_ci { 8958c2ecf20Sopenharmony_ci case SDLA_INTR_RX: 8968c2ecf20Sopenharmony_ci sdla_receive(dev); 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* the command will get an error return, which is processed above */ 9008c2ecf20Sopenharmony_ci case SDLA_INTR_MODEM: 9018c2ecf20Sopenharmony_ci case SDLA_INTR_STATUS: 9028c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_READ_DLC_STATUS, 0, 0, NULL, 0, NULL, NULL); 9038c2ecf20Sopenharmony_ci break; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci case SDLA_INTR_TX: 9068c2ecf20Sopenharmony_ci case SDLA_INTR_COMPLETE: 9078c2ecf20Sopenharmony_ci case SDLA_INTR_TIMER: 9088c2ecf20Sopenharmony_ci netdev_warn(dev, "invalid irq flag 0x%02X\n", byte); 9098c2ecf20Sopenharmony_ci break; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* the S502E requires a manual acknowledgement of the interrupt */ 9138c2ecf20Sopenharmony_ci if (flp->type == SDLA_S502E) 9148c2ecf20Sopenharmony_ci { 9158c2ecf20Sopenharmony_ci flp->state &= ~SDLA_S502E_INTACK; 9168c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 9178c2ecf20Sopenharmony_ci flp->state |= SDLA_S502E_INTACK; 9188c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* this clears the byte, informing the Z80 we're done */ 9228c2ecf20Sopenharmony_ci byte = 0; 9238c2ecf20Sopenharmony_ci sdla_write(dev, flp->type == SDLA_S508 ? SDLA_508_IRQ_INTERFACE : SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte)); 9248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic void sdla_poll(struct timer_list *t) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct frad_local *flp = from_timer(flp, t, timer); 9308c2ecf20Sopenharmony_ci struct net_device *dev = flp->dev; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci if (sdla_byte(dev, SDLA_502_RCV_BUF)) 9338c2ecf20Sopenharmony_ci sdla_receive(dev); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci flp->timer.expires = 1; 9368c2ecf20Sopenharmony_ci add_timer(&flp->timer); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic int sdla_close(struct net_device *dev) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci struct frad_local *flp; 9428c2ecf20Sopenharmony_ci struct intr_info intr; 9438c2ecf20Sopenharmony_ci int len, i; 9448c2ecf20Sopenharmony_ci short dlcis[CONFIG_DLCI_MAX]; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci len = 0; 9498c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 9508c2ecf20Sopenharmony_ci if (flp->dlci[i]) 9518c2ecf20Sopenharmony_ci dlcis[len++] = abs(flp->dlci[i]); 9528c2ecf20Sopenharmony_ci len *= 2; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (flp->config.station == FRAD_STATION_NODE) 9558c2ecf20Sopenharmony_ci { 9568c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 9578c2ecf20Sopenharmony_ci if (flp->dlci[i] > 0) 9588c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_DEACTIVATE_DLCI, 0, 0, dlcis, len, NULL, NULL); 9598c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_DELETE_DLCI, 0, 0, &flp->dlci[i], sizeof(flp->dlci[i]), NULL, NULL); 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci memset(&intr, 0, sizeof(intr)); 9638c2ecf20Sopenharmony_ci /* let's start up the reception */ 9648c2ecf20Sopenharmony_ci switch(flp->type) 9658c2ecf20Sopenharmony_ci { 9668c2ecf20Sopenharmony_ci case SDLA_S502A: 9678c2ecf20Sopenharmony_ci del_timer(&flp->timer); 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci case SDLA_S502E: 9718c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL); 9728c2ecf20Sopenharmony_ci flp->state &= ~SDLA_S502E_INTACK; 9738c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 9748c2ecf20Sopenharmony_ci break; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci case SDLA_S507: 9778c2ecf20Sopenharmony_ci break; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci case SDLA_S508: 9808c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL); 9818c2ecf20Sopenharmony_ci flp->state &= ~SDLA_S508_INTEN; 9828c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 9838c2ecf20Sopenharmony_ci break; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistruct conf_data { 9948c2ecf20Sopenharmony_ci struct frad_conf config; 9958c2ecf20Sopenharmony_ci short dlci[CONFIG_DLCI_MAX]; 9968c2ecf20Sopenharmony_ci}; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic int sdla_open(struct net_device *dev) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct frad_local *flp; 10018c2ecf20Sopenharmony_ci struct dlci_local *dlp; 10028c2ecf20Sopenharmony_ci struct conf_data data; 10038c2ecf20Sopenharmony_ci struct intr_info intr; 10048c2ecf20Sopenharmony_ci int len, i; 10058c2ecf20Sopenharmony_ci char byte; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (!flp->initialized) 10108c2ecf20Sopenharmony_ci return -EPERM; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (!flp->configured) 10138c2ecf20Sopenharmony_ci return -EPERM; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* time to send in the configuration */ 10168c2ecf20Sopenharmony_ci len = 0; 10178c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 10188c2ecf20Sopenharmony_ci if (flp->dlci[i]) 10198c2ecf20Sopenharmony_ci data.dlci[len++] = abs(flp->dlci[i]); 10208c2ecf20Sopenharmony_ci len *= 2; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci memcpy(&data.config, &flp->config, sizeof(struct frad_conf)); 10238c2ecf20Sopenharmony_ci len += sizeof(struct frad_conf); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL); 10268c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (flp->type == SDLA_S508) 10298c2ecf20Sopenharmony_ci flp->buffer = 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* let's start up the reception */ 10348c2ecf20Sopenharmony_ci memset(&intr, 0, sizeof(intr)); 10358c2ecf20Sopenharmony_ci switch(flp->type) 10368c2ecf20Sopenharmony_ci { 10378c2ecf20Sopenharmony_ci case SDLA_S502A: 10388c2ecf20Sopenharmony_ci flp->timer.expires = 1; 10398c2ecf20Sopenharmony_ci add_timer(&flp->timer); 10408c2ecf20Sopenharmony_ci break; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci case SDLA_S502E: 10438c2ecf20Sopenharmony_ci flp->state |= SDLA_S502E_ENABLE; 10448c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 10458c2ecf20Sopenharmony_ci flp->state |= SDLA_S502E_INTACK; 10468c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 10478c2ecf20Sopenharmony_ci byte = 0; 10488c2ecf20Sopenharmony_ci sdla_write(dev, SDLA_502_IRQ_INTERFACE, &byte, sizeof(byte)); 10498c2ecf20Sopenharmony_ci intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM; 10508c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(char) + sizeof(short), NULL, NULL); 10518c2ecf20Sopenharmony_ci break; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci case SDLA_S507: 10548c2ecf20Sopenharmony_ci break; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci case SDLA_S508: 10578c2ecf20Sopenharmony_ci flp->state |= SDLA_S508_INTEN; 10588c2ecf20Sopenharmony_ci outb(flp->state, dev->base_addr + SDLA_REG_CONTROL); 10598c2ecf20Sopenharmony_ci byte = 0; 10608c2ecf20Sopenharmony_ci sdla_write(dev, SDLA_508_IRQ_INTERFACE, &byte, sizeof(byte)); 10618c2ecf20Sopenharmony_ci intr.flags = SDLA_INTR_RX | SDLA_INTR_STATUS | SDLA_INTR_MODEM; 10628c2ecf20Sopenharmony_ci intr.irq = dev->irq; 10638c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_IRQ_TRIGGER, 0, 0, &intr, sizeof(struct intr_info), NULL, NULL); 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (flp->config.station == FRAD_STATION_CPE) 10688c2ecf20Sopenharmony_ci { 10698c2ecf20Sopenharmony_ci byte = SDLA_ICS_STATUS_ENQ; 10708c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_ISSUE_IN_CHANNEL_SIGNAL, 0, 0, &byte, sizeof(byte), NULL, NULL); 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci else 10738c2ecf20Sopenharmony_ci { 10748c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_ADD_DLCI, 0, 0, data.dlci, len - sizeof(struct frad_conf), NULL, NULL); 10758c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 10768c2ecf20Sopenharmony_ci if (flp->dlci[i] > 0) 10778c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_ACTIVATE_DLCI, 0, 0, &flp->dlci[i], 2*sizeof(flp->dlci[i]), NULL, NULL); 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* configure any specific DLCI settings */ 10818c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 10828c2ecf20Sopenharmony_ci if (flp->dlci[i]) 10838c2ecf20Sopenharmony_ci { 10848c2ecf20Sopenharmony_ci dlp = netdev_priv(flp->master[i]); 10858c2ecf20Sopenharmony_ci if (dlp->configured) 10868c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, abs(flp->dlci[i]), 0, &dlp->config, sizeof(struct dlci_conf), NULL, NULL); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci netif_start_queue(dev); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return 0; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic int sdla_config(struct net_device *dev, struct frad_conf __user *conf, int get) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct frad_local *flp; 10978c2ecf20Sopenharmony_ci struct conf_data data; 10988c2ecf20Sopenharmony_ci int i; 10998c2ecf20Sopenharmony_ci short size; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (dev->type == 0xFFFF) 11028c2ecf20Sopenharmony_ci return -EUNATCH; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (!get) 11078c2ecf20Sopenharmony_ci { 11088c2ecf20Sopenharmony_ci if (netif_running(dev)) 11098c2ecf20Sopenharmony_ci return -EBUSY; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if(copy_from_user(&data.config, conf, sizeof(struct frad_conf))) 11128c2ecf20Sopenharmony_ci return -EFAULT; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci if (data.config.station & ~FRAD_STATION_NODE) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci if (data.config.flags & ~FRAD_VALID_FLAGS) 11188c2ecf20Sopenharmony_ci return -EINVAL; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if ((data.config.kbaud < 0) || 11218c2ecf20Sopenharmony_ci ((data.config.kbaud > 128) && (flp->type != SDLA_S508))) 11228c2ecf20Sopenharmony_ci return -EINVAL; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (data.config.clocking & ~(FRAD_CLOCK_INT | SDLA_S508_PORT_RS232)) 11258c2ecf20Sopenharmony_ci return -EINVAL; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if ((data.config.mtu < 0) || (data.config.mtu > SDLA_MAX_MTU)) 11288c2ecf20Sopenharmony_ci return -EINVAL; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if ((data.config.T391 < 5) || (data.config.T391 > 30)) 11318c2ecf20Sopenharmony_ci return -EINVAL; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci if ((data.config.T392 < 5) || (data.config.T392 > 30)) 11348c2ecf20Sopenharmony_ci return -EINVAL; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if ((data.config.N391 < 1) || (data.config.N391 > 255)) 11378c2ecf20Sopenharmony_ci return -EINVAL; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if ((data.config.N392 < 1) || (data.config.N392 > 10)) 11408c2ecf20Sopenharmony_ci return -EINVAL; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if ((data.config.N393 < 1) || (data.config.N393 > 10)) 11438c2ecf20Sopenharmony_ci return -EINVAL; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci memcpy(&flp->config, &data.config, sizeof(struct frad_conf)); 11468c2ecf20Sopenharmony_ci flp->config.flags |= SDLA_DIRECT_RECV; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (flp->type == SDLA_S508) 11498c2ecf20Sopenharmony_ci flp->config.flags |= SDLA_TX70_RX30; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci if (dev->mtu != flp->config.mtu) 11528c2ecf20Sopenharmony_ci { 11538c2ecf20Sopenharmony_ci /* this is required to change the MTU */ 11548c2ecf20Sopenharmony_ci dev->mtu = flp->config.mtu; 11558c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 11568c2ecf20Sopenharmony_ci if (flp->master[i]) 11578c2ecf20Sopenharmony_ci flp->master[i]->mtu = flp->config.mtu; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci flp->config.mtu += sizeof(struct frhdr); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* off to the races! */ 11638c2ecf20Sopenharmony_ci if (!flp->configured) 11648c2ecf20Sopenharmony_ci sdla_start(dev); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci flp->configured = 1; 11678c2ecf20Sopenharmony_ci } 11688c2ecf20Sopenharmony_ci else 11698c2ecf20Sopenharmony_ci { 11708c2ecf20Sopenharmony_ci /* no sense reading if the CPU isn't started */ 11718c2ecf20Sopenharmony_ci if (netif_running(dev)) 11728c2ecf20Sopenharmony_ci { 11738c2ecf20Sopenharmony_ci size = sizeof(data); 11748c2ecf20Sopenharmony_ci if (sdla_cmd(dev, SDLA_READ_DLCI_CONFIGURATION, 0, 0, NULL, 0, &data, &size) != SDLA_RET_OK) 11758c2ecf20Sopenharmony_ci return -EIO; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci else 11788c2ecf20Sopenharmony_ci if (flp->configured) 11798c2ecf20Sopenharmony_ci memcpy(&data.config, &flp->config, sizeof(struct frad_conf)); 11808c2ecf20Sopenharmony_ci else 11818c2ecf20Sopenharmony_ci memset(&data.config, 0, sizeof(struct frad_conf)); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci memcpy(&flp->config, &data.config, sizeof(struct frad_conf)); 11848c2ecf20Sopenharmony_ci data.config.flags &= FRAD_VALID_FLAGS; 11858c2ecf20Sopenharmony_ci data.config.mtu -= data.config.mtu > sizeof(struct frhdr) ? sizeof(struct frhdr) : data.config.mtu; 11868c2ecf20Sopenharmony_ci return copy_to_user(conf, &data.config, sizeof(struct frad_conf))?-EFAULT:0; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci return 0; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic int sdla_xfer(struct net_device *dev, struct sdla_mem __user *info, int read) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct sdla_mem mem; 11958c2ecf20Sopenharmony_ci char *temp; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if(copy_from_user(&mem, info, sizeof(mem))) 11988c2ecf20Sopenharmony_ci return -EFAULT; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (read) 12018c2ecf20Sopenharmony_ci { 12028c2ecf20Sopenharmony_ci temp = kzalloc(mem.len, GFP_KERNEL); 12038c2ecf20Sopenharmony_ci if (!temp) 12048c2ecf20Sopenharmony_ci return -ENOMEM; 12058c2ecf20Sopenharmony_ci sdla_read(dev, mem.addr, temp, mem.len); 12068c2ecf20Sopenharmony_ci if(copy_to_user(mem.data, temp, mem.len)) 12078c2ecf20Sopenharmony_ci { 12088c2ecf20Sopenharmony_ci kfree(temp); 12098c2ecf20Sopenharmony_ci return -EFAULT; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci kfree(temp); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci else 12148c2ecf20Sopenharmony_ci { 12158c2ecf20Sopenharmony_ci temp = memdup_user(mem.data, mem.len); 12168c2ecf20Sopenharmony_ci if (IS_ERR(temp)) 12178c2ecf20Sopenharmony_ci return PTR_ERR(temp); 12188c2ecf20Sopenharmony_ci sdla_write(dev, mem.addr, temp, mem.len); 12198c2ecf20Sopenharmony_ci kfree(temp); 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci return 0; 12228c2ecf20Sopenharmony_ci} 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cistatic int sdla_reconfig(struct net_device *dev) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct frad_local *flp; 12278c2ecf20Sopenharmony_ci struct conf_data data; 12288c2ecf20Sopenharmony_ci int i, len; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci len = 0; 12338c2ecf20Sopenharmony_ci for(i=0;i<CONFIG_DLCI_MAX;i++) 12348c2ecf20Sopenharmony_ci if (flp->dlci[i]) 12358c2ecf20Sopenharmony_ci data.dlci[len++] = flp->dlci[i]; 12368c2ecf20Sopenharmony_ci len *= 2; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci memcpy(&data, &flp->config, sizeof(struct frad_conf)); 12398c2ecf20Sopenharmony_ci len += sizeof(struct frad_conf); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_DISABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL); 12428c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_SET_DLCI_CONFIGURATION, 0, 0, &data, len, NULL, NULL); 12438c2ecf20Sopenharmony_ci sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic int sdla_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci struct frad_local *flp; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if(!capable(CAP_NET_ADMIN)) 12538c2ecf20Sopenharmony_ci return -EPERM; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci if (!flp->initialized) 12588c2ecf20Sopenharmony_ci return -EINVAL; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci switch (cmd) 12618c2ecf20Sopenharmony_ci { 12628c2ecf20Sopenharmony_ci case FRAD_GET_CONF: 12638c2ecf20Sopenharmony_ci case FRAD_SET_CONF: 12648c2ecf20Sopenharmony_ci return sdla_config(dev, ifr->ifr_data, cmd == FRAD_GET_CONF); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci case SDLA_IDENTIFY: 12678c2ecf20Sopenharmony_ci ifr->ifr_flags = flp->type; 12688c2ecf20Sopenharmony_ci break; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci case SDLA_CPUSPEED: 12718c2ecf20Sopenharmony_ci return sdla_cpuspeed(dev, ifr); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci/* ========================================================== 12748c2ecf20Sopenharmony_ciNOTE: This is rather a useless action right now, as the 12758c2ecf20Sopenharmony_ci current driver does not support protocols other than 12768c2ecf20Sopenharmony_ci FR. However, Sangoma has modules for a number of 12778c2ecf20Sopenharmony_ci other protocols in the works. 12788c2ecf20Sopenharmony_ci============================================================*/ 12798c2ecf20Sopenharmony_ci case SDLA_PROTOCOL: 12808c2ecf20Sopenharmony_ci if (flp->configured) 12818c2ecf20Sopenharmony_ci return -EALREADY; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci switch (ifr->ifr_flags) 12848c2ecf20Sopenharmony_ci { 12858c2ecf20Sopenharmony_ci case ARPHRD_FRAD: 12868c2ecf20Sopenharmony_ci dev->type = ifr->ifr_flags; 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci default: 12898c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci break; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci case SDLA_CLEARMEM: 12948c2ecf20Sopenharmony_ci sdla_clear(dev); 12958c2ecf20Sopenharmony_ci break; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci case SDLA_WRITEMEM: 12988c2ecf20Sopenharmony_ci case SDLA_READMEM: 12998c2ecf20Sopenharmony_ci if(!capable(CAP_SYS_RAWIO)) 13008c2ecf20Sopenharmony_ci return -EPERM; 13018c2ecf20Sopenharmony_ci return sdla_xfer(dev, ifr->ifr_data, cmd == SDLA_READMEM); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci case SDLA_START: 13048c2ecf20Sopenharmony_ci sdla_start(dev); 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci case SDLA_STOP: 13088c2ecf20Sopenharmony_ci sdla_stop(dev); 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci default: 13128c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci return 0; 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic int sdla_change_mtu(struct net_device *dev, int new_mtu) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci if (netif_running(dev)) 13208c2ecf20Sopenharmony_ci return -EBUSY; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* for now, you can't change the MTU! */ 13238c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 13248c2ecf20Sopenharmony_ci} 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cistatic int sdla_set_config(struct net_device *dev, struct ifmap *map) 13278c2ecf20Sopenharmony_ci{ 13288c2ecf20Sopenharmony_ci struct frad_local *flp; 13298c2ecf20Sopenharmony_ci int i; 13308c2ecf20Sopenharmony_ci char byte; 13318c2ecf20Sopenharmony_ci unsigned base; 13328c2ecf20Sopenharmony_ci int err = -EINVAL; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci flp = netdev_priv(dev); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (flp->initialized) 13378c2ecf20Sopenharmony_ci return -EINVAL; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci for(i=0; i < ARRAY_SIZE(valid_port); i++) 13408c2ecf20Sopenharmony_ci if (valid_port[i] == map->base_addr) 13418c2ecf20Sopenharmony_ci break; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(valid_port)) 13448c2ecf20Sopenharmony_ci return -EINVAL; 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci if (!request_region(map->base_addr, SDLA_IO_EXTENTS, dev->name)){ 13478c2ecf20Sopenharmony_ci pr_warn("io-port 0x%04lx in use\n", dev->base_addr); 13488c2ecf20Sopenharmony_ci return -EINVAL; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci base = map->base_addr; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* test for card types, S502A, S502E, S507, S508 */ 13538c2ecf20Sopenharmony_ci /* these tests shut down the card completely, so clear the state */ 13548c2ecf20Sopenharmony_ci flp->type = SDLA_UNKNOWN; 13558c2ecf20Sopenharmony_ci flp->state = 0; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci for(i=1;i<SDLA_IO_EXTENTS;i++) 13588c2ecf20Sopenharmony_ci if (inb(base + i) != 0xFF) 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (i == SDLA_IO_EXTENTS) { 13628c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_Z80_CONTROL); 13638c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x08) { 13648c2ecf20Sopenharmony_ci outb(SDLA_S502E_INTACK, base + SDLA_REG_CONTROL); 13658c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S502_STS) & 0x0F) == 0x0C) { 13668c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_CONTROL); 13678c2ecf20Sopenharmony_ci flp->type = SDLA_S502E; 13688c2ecf20Sopenharmony_ci goto got_type; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci for(byte=inb(base),i=0;i<SDLA_IO_EXTENTS;i++) 13748c2ecf20Sopenharmony_ci if (inb(base + i) != byte) 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (i == SDLA_IO_EXTENTS) { 13788c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_CONTROL); 13798c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x30) { 13808c2ecf20Sopenharmony_ci outb(SDLA_S507_ENABLE, base + SDLA_REG_CONTROL); 13818c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S502_STS) & 0x7E) == 0x32) { 13828c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_CONTROL); 13838c2ecf20Sopenharmony_ci flp->type = SDLA_S507; 13848c2ecf20Sopenharmony_ci goto got_type; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_CONTROL); 13908c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x00) { 13918c2ecf20Sopenharmony_ci outb(SDLA_S508_INTEN, base + SDLA_REG_CONTROL); 13928c2ecf20Sopenharmony_ci if ((inb(base + SDLA_S508_STS) & 0x3F) == 0x10) { 13938c2ecf20Sopenharmony_ci outb(SDLA_HALT, base + SDLA_REG_CONTROL); 13948c2ecf20Sopenharmony_ci flp->type = SDLA_S508; 13958c2ecf20Sopenharmony_ci goto got_type; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci outb(SDLA_S502A_HALT, base + SDLA_REG_CONTROL); 14008c2ecf20Sopenharmony_ci if (inb(base + SDLA_S502_STS) == 0x40) { 14018c2ecf20Sopenharmony_ci outb(SDLA_S502A_START, base + SDLA_REG_CONTROL); 14028c2ecf20Sopenharmony_ci if (inb(base + SDLA_S502_STS) == 0x40) { 14038c2ecf20Sopenharmony_ci outb(SDLA_S502A_INTEN, base + SDLA_REG_CONTROL); 14048c2ecf20Sopenharmony_ci if (inb(base + SDLA_S502_STS) == 0x44) { 14058c2ecf20Sopenharmony_ci outb(SDLA_S502A_START, base + SDLA_REG_CONTROL); 14068c2ecf20Sopenharmony_ci flp->type = SDLA_S502A; 14078c2ecf20Sopenharmony_ci goto got_type; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci netdev_notice(dev, "Unknown card type\n"); 14138c2ecf20Sopenharmony_ci err = -ENODEV; 14148c2ecf20Sopenharmony_ci goto fail; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cigot_type: 14178c2ecf20Sopenharmony_ci switch(base) { 14188c2ecf20Sopenharmony_ci case 0x270: 14198c2ecf20Sopenharmony_ci case 0x280: 14208c2ecf20Sopenharmony_ci case 0x380: 14218c2ecf20Sopenharmony_ci case 0x390: 14228c2ecf20Sopenharmony_ci if (flp->type != SDLA_S508 && flp->type != SDLA_S507) 14238c2ecf20Sopenharmony_ci goto fail; 14248c2ecf20Sopenharmony_ci } 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci switch (map->irq) { 14278c2ecf20Sopenharmony_ci case 2: 14288c2ecf20Sopenharmony_ci if (flp->type != SDLA_S502E) 14298c2ecf20Sopenharmony_ci goto fail; 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci case 10: 14338c2ecf20Sopenharmony_ci case 11: 14348c2ecf20Sopenharmony_ci case 12: 14358c2ecf20Sopenharmony_ci case 15: 14368c2ecf20Sopenharmony_ci case 4: 14378c2ecf20Sopenharmony_ci if (flp->type != SDLA_S508 && flp->type != SDLA_S507) 14388c2ecf20Sopenharmony_ci goto fail; 14398c2ecf20Sopenharmony_ci break; 14408c2ecf20Sopenharmony_ci case 3: 14418c2ecf20Sopenharmony_ci case 5: 14428c2ecf20Sopenharmony_ci case 7: 14438c2ecf20Sopenharmony_ci if (flp->type == SDLA_S502A) 14448c2ecf20Sopenharmony_ci goto fail; 14458c2ecf20Sopenharmony_ci break; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci default: 14488c2ecf20Sopenharmony_ci goto fail; 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci err = -EAGAIN; 14528c2ecf20Sopenharmony_ci if (request_irq(dev->irq, sdla_isr, 0, dev->name, dev)) 14538c2ecf20Sopenharmony_ci goto fail; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (flp->type == SDLA_S507) { 14568c2ecf20Sopenharmony_ci switch(dev->irq) { 14578c2ecf20Sopenharmony_ci case 3: 14588c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ3; 14598c2ecf20Sopenharmony_ci break; 14608c2ecf20Sopenharmony_ci case 4: 14618c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ4; 14628c2ecf20Sopenharmony_ci break; 14638c2ecf20Sopenharmony_ci case 5: 14648c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ5; 14658c2ecf20Sopenharmony_ci break; 14668c2ecf20Sopenharmony_ci case 7: 14678c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ7; 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci case 10: 14708c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ10; 14718c2ecf20Sopenharmony_ci break; 14728c2ecf20Sopenharmony_ci case 11: 14738c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ11; 14748c2ecf20Sopenharmony_ci break; 14758c2ecf20Sopenharmony_ci case 12: 14768c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ12; 14778c2ecf20Sopenharmony_ci break; 14788c2ecf20Sopenharmony_ci case 15: 14798c2ecf20Sopenharmony_ci flp->state = SDLA_S507_IRQ15; 14808c2ecf20Sopenharmony_ci break; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci for(i=0; i < ARRAY_SIZE(valid_mem); i++) 14858c2ecf20Sopenharmony_ci if (valid_mem[i] == map->mem_start) 14868c2ecf20Sopenharmony_ci break; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci err = -EINVAL; 14898c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(valid_mem)) 14908c2ecf20Sopenharmony_ci goto fail2; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if (flp->type == SDLA_S502A && (map->mem_start & 0xF000) >> 12 == 0x0E) 14938c2ecf20Sopenharmony_ci goto fail2; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (flp->type != SDLA_S507 && map->mem_start >> 16 == 0x0B) 14968c2ecf20Sopenharmony_ci goto fail2; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (flp->type == SDLA_S507 && map->mem_start >> 16 == 0x0D) 14998c2ecf20Sopenharmony_ci goto fail2; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci byte = flp->type != SDLA_S508 ? SDLA_8K_WINDOW : 0; 15028c2ecf20Sopenharmony_ci byte |= (map->mem_start & 0xF000) >> (12 + (flp->type == SDLA_S508 ? 1 : 0)); 15038c2ecf20Sopenharmony_ci switch(flp->type) { 15048c2ecf20Sopenharmony_ci case SDLA_S502A: 15058c2ecf20Sopenharmony_ci case SDLA_S502E: 15068c2ecf20Sopenharmony_ci switch (map->mem_start >> 16) { 15078c2ecf20Sopenharmony_ci case 0x0A: 15088c2ecf20Sopenharmony_ci byte |= SDLA_S502_SEG_A; 15098c2ecf20Sopenharmony_ci break; 15108c2ecf20Sopenharmony_ci case 0x0C: 15118c2ecf20Sopenharmony_ci byte |= SDLA_S502_SEG_C; 15128c2ecf20Sopenharmony_ci break; 15138c2ecf20Sopenharmony_ci case 0x0D: 15148c2ecf20Sopenharmony_ci byte |= SDLA_S502_SEG_D; 15158c2ecf20Sopenharmony_ci break; 15168c2ecf20Sopenharmony_ci case 0x0E: 15178c2ecf20Sopenharmony_ci byte |= SDLA_S502_SEG_E; 15188c2ecf20Sopenharmony_ci break; 15198c2ecf20Sopenharmony_ci } 15208c2ecf20Sopenharmony_ci break; 15218c2ecf20Sopenharmony_ci case SDLA_S507: 15228c2ecf20Sopenharmony_ci switch (map->mem_start >> 16) { 15238c2ecf20Sopenharmony_ci case 0x0A: 15248c2ecf20Sopenharmony_ci byte |= SDLA_S507_SEG_A; 15258c2ecf20Sopenharmony_ci break; 15268c2ecf20Sopenharmony_ci case 0x0B: 15278c2ecf20Sopenharmony_ci byte |= SDLA_S507_SEG_B; 15288c2ecf20Sopenharmony_ci break; 15298c2ecf20Sopenharmony_ci case 0x0C: 15308c2ecf20Sopenharmony_ci byte |= SDLA_S507_SEG_C; 15318c2ecf20Sopenharmony_ci break; 15328c2ecf20Sopenharmony_ci case 0x0E: 15338c2ecf20Sopenharmony_ci byte |= SDLA_S507_SEG_E; 15348c2ecf20Sopenharmony_ci break; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci break; 15378c2ecf20Sopenharmony_ci case SDLA_S508: 15388c2ecf20Sopenharmony_ci switch (map->mem_start >> 16) { 15398c2ecf20Sopenharmony_ci case 0x0A: 15408c2ecf20Sopenharmony_ci byte |= SDLA_S508_SEG_A; 15418c2ecf20Sopenharmony_ci break; 15428c2ecf20Sopenharmony_ci case 0x0C: 15438c2ecf20Sopenharmony_ci byte |= SDLA_S508_SEG_C; 15448c2ecf20Sopenharmony_ci break; 15458c2ecf20Sopenharmony_ci case 0x0D: 15468c2ecf20Sopenharmony_ci byte |= SDLA_S508_SEG_D; 15478c2ecf20Sopenharmony_ci break; 15488c2ecf20Sopenharmony_ci case 0x0E: 15498c2ecf20Sopenharmony_ci byte |= SDLA_S508_SEG_E; 15508c2ecf20Sopenharmony_ci break; 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci break; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci /* set the memory bits, and enable access */ 15568c2ecf20Sopenharmony_ci outb(byte, base + SDLA_REG_PC_WINDOW); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci switch(flp->type) 15598c2ecf20Sopenharmony_ci { 15608c2ecf20Sopenharmony_ci case SDLA_S502E: 15618c2ecf20Sopenharmony_ci flp->state = SDLA_S502E_ENABLE; 15628c2ecf20Sopenharmony_ci break; 15638c2ecf20Sopenharmony_ci case SDLA_S507: 15648c2ecf20Sopenharmony_ci flp->state |= SDLA_MEMEN; 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case SDLA_S508: 15678c2ecf20Sopenharmony_ci flp->state = SDLA_MEMEN; 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci outb(flp->state, base + SDLA_REG_CONTROL); 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci dev->irq = map->irq; 15738c2ecf20Sopenharmony_ci dev->base_addr = base; 15748c2ecf20Sopenharmony_ci dev->mem_start = map->mem_start; 15758c2ecf20Sopenharmony_ci dev->mem_end = dev->mem_start + 0x2000; 15768c2ecf20Sopenharmony_ci flp->initialized = 1; 15778c2ecf20Sopenharmony_ci return 0; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_cifail2: 15808c2ecf20Sopenharmony_ci free_irq(map->irq, dev); 15818c2ecf20Sopenharmony_cifail: 15828c2ecf20Sopenharmony_ci release_region(base, SDLA_IO_EXTENTS); 15838c2ecf20Sopenharmony_ci return err; 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic const struct net_device_ops sdla_netdev_ops = { 15878c2ecf20Sopenharmony_ci .ndo_open = sdla_open, 15888c2ecf20Sopenharmony_ci .ndo_stop = sdla_close, 15898c2ecf20Sopenharmony_ci .ndo_do_ioctl = sdla_ioctl, 15908c2ecf20Sopenharmony_ci .ndo_set_config = sdla_set_config, 15918c2ecf20Sopenharmony_ci .ndo_start_xmit = sdla_transmit, 15928c2ecf20Sopenharmony_ci .ndo_change_mtu = sdla_change_mtu, 15938c2ecf20Sopenharmony_ci}; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cistatic void setup_sdla(struct net_device *dev) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct frad_local *flp = netdev_priv(dev); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci netdev_boot_setup_check(dev); 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci dev->netdev_ops = &sdla_netdev_ops; 16028c2ecf20Sopenharmony_ci dev->flags = 0; 16038c2ecf20Sopenharmony_ci dev->type = 0xFFFF; 16048c2ecf20Sopenharmony_ci dev->hard_header_len = 0; 16058c2ecf20Sopenharmony_ci dev->addr_len = 0; 16068c2ecf20Sopenharmony_ci dev->mtu = SDLA_MAX_MTU; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci flp->activate = sdla_activate; 16098c2ecf20Sopenharmony_ci flp->deactivate = sdla_deactivate; 16108c2ecf20Sopenharmony_ci flp->assoc = sdla_assoc; 16118c2ecf20Sopenharmony_ci flp->deassoc = sdla_deassoc; 16128c2ecf20Sopenharmony_ci flp->dlci_conf = sdla_dlci_conf; 16138c2ecf20Sopenharmony_ci flp->dev = dev; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci timer_setup(&flp->timer, sdla_poll, 0); 16168c2ecf20Sopenharmony_ci flp->timer.expires = 1; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic struct net_device *sdla; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_cistatic int __init init_sdla(void) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci int err; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci printk("%s.\n", version); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci sdla = alloc_netdev(sizeof(struct frad_local), "sdla0", 16288c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, setup_sdla); 16298c2ecf20Sopenharmony_ci if (!sdla) 16308c2ecf20Sopenharmony_ci return -ENOMEM; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci err = register_netdev(sdla); 16338c2ecf20Sopenharmony_ci if (err) 16348c2ecf20Sopenharmony_ci free_netdev(sdla); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci return err; 16378c2ecf20Sopenharmony_ci} 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_cistatic void __exit exit_sdla(void) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct frad_local *flp = netdev_priv(sdla); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci unregister_netdev(sdla); 16448c2ecf20Sopenharmony_ci if (flp->initialized) { 16458c2ecf20Sopenharmony_ci free_irq(sdla->irq, sdla); 16468c2ecf20Sopenharmony_ci release_region(sdla->base_addr, SDLA_IO_EXTENTS); 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci del_timer_sync(&flp->timer); 16498c2ecf20Sopenharmony_ci free_netdev(sdla); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_cimodule_init(init_sdla); 16558c2ecf20Sopenharmony_cimodule_exit(exit_sdla); 1656