Loading drivers/net/wan/cosa.c +260 −233 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-or-later /* $Id: cosa.c,v 1.31 2000/03/08 17:47:16 kas Exp $ */ /* * Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> /* Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> * Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl> */ /* * The driver for the SRP and COSA synchronous serial cards. /* The driver for the SRP and COSA synchronous serial cards. * * HARDWARE INFO * Loading Loading @@ -124,9 +122,9 @@ struct channel_data { }; /* cosa->firmware_status bits */ #define COSA_FW_RESET (1<<0) /* Is the ROM monitor active? */ #define COSA_FW_DOWNLOAD (1<<1) /* Is the microcode downloaded? */ #define COSA_FW_START (1<<2) /* Is the microcode running? */ #define COSA_FW_RESET BIT(0) /* Is the ROM monitor active? */ #define COSA_FW_DOWNLOAD BIT(1) /* Is the microcode downloaded? */ #define COSA_FW_START BIT(2) /* Is the microcode running? */ struct cosa_data { int num; /* Card number */ Loading @@ -152,28 +150,25 @@ struct cosa_data { char *type; /* card type */ }; /* * Define this if you want all the possible ports to be autoprobed. /* Define this if you want all the possible ports to be autoprobed. * It is here but it probably is not a good idea to use this. */ /* #define COSA_ISA_AUTOPROBE 1*/ /* * Character device major number. 117 was allocated for us. /* Character device major number. 117 was allocated for us. * The value of 0 means to allocate a first free one. */ static DEFINE_MUTEX(cosa_chardev_mutex); static int cosa_major = 117; /* * Encoding of the minor numbers: /* Encoding of the minor numbers: * The lowest CARD_MINOR_BITS bits means the channel on the single card, * the highest bits means the card number. */ #define CARD_MINOR_BITS 4 /* How many bits in minor number are reserved * for the single card */ /* * The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" * for the single card */ /* The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" * macro doesn't like anything other than the raw number as an argument :-( */ #define MAX_CARDS 16 Loading @@ -184,8 +179,7 @@ static int cosa_major = 117; #define DRIVER_TXMAP_SHIFT 2 #define DRIVER_TXMAP_MASK 0x0c /* FIXME: 0xfc for 8-channel version */ /* * for cosa->rxtx - indicates whether either transmit or receive is /* for cosa->rxtx - indicates whether either transmit or receive is * in progress. These values are mean number of the bit. */ #define TXBIT 0 Loading Loading @@ -244,14 +238,14 @@ MODULE_LICENSE("GPL"); #define cosa_inw inw #endif #define is_8bit(cosa) (!(cosa->datareg & 0x08)) #define is_8bit(cosa) (!((cosa)->datareg & 0x08)) #define cosa_getstatus(cosa) (cosa_inb(cosa->statusreg)) #define cosa_putstatus(cosa, stat) (cosa_outb(stat, cosa->statusreg)) #define cosa_getdata16(cosa) (cosa_inw(cosa->datareg)) #define cosa_getdata8(cosa) (cosa_inb(cosa->datareg)) #define cosa_putdata16(cosa, dt) (cosa_outw(dt, cosa->datareg)) #define cosa_putdata8(cosa, dt) (cosa_outb(dt, cosa->datareg)) #define cosa_getstatus(cosa) (cosa_inb((cosa)->statusreg)) #define cosa_putstatus(cosa, stat) (cosa_outb(stat, (cosa)->statusreg)) #define cosa_getdata16(cosa) (cosa_inw((cosa)->datareg)) #define cosa_getdata8(cosa) (cosa_inb((cosa)->datareg)) #define cosa_putdata16(cosa, dt) (cosa_outw(dt, (cosa)->datareg)) #define cosa_putdata8(cosa, dt) (cosa_outb(dt, (cosa)->datareg)) /* Initialization stuff */ static int cosa_probe(int ioaddr, int irq, int dma); Loading Loading @@ -355,7 +349,8 @@ static int __init cosa_init(void) goto out; } } else { if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { cosa_major = register_chrdev(0, "cosa", &cosa_fops); if (!cosa_major) { pr_warn("unable to register chardev\n"); err = -EIO; goto out; Loading Loading @@ -438,7 +433,8 @@ static int cosa_probe(int base, int irq, int dma) return -1; } /* I/O address should be between 0x100 and 0x3ff and should be * multiple of 8. */ * multiple of 8. */ if (base < 0x100 || base > 0x3ff || base & 0x7) { pr_info("invalid I/O address 0x%x\n", base); return -1; Loading @@ -449,7 +445,8 @@ static int cosa_probe(int base, int irq, int dma) return -1; } /* and finally, on 16-bit COSA DMA should be 4-7 and * I/O base should not be multiple of 0x10 */ * I/O base should not be multiple of 0x10 */ if (((base & 0x8) && dma < 4) || (!(base & 0x8) && dma > 3)) { pr_info("8/16 bit base and DMA mismatch (base=0x%x, dma=%d)\n", base, dma); Loading @@ -471,11 +468,11 @@ static int cosa_probe(int base, int irq, int dma) } /* Test the validity of identification string */ if (!strncmp(cosa->id_string, "SRP", 3)) if (!strncmp(cosa->id_string, "SRP", 3)) { cosa->type = "srp"; else if (!strncmp(cosa->id_string, "COSA", 4)) } else if (!strncmp(cosa->id_string, "COSA", 4)) { cosa->type = is_8bit(cosa) ? "cosa8" : "cosa16"; else { } else { /* Print a warning only if we are not autoprobing */ #ifndef COSA_ISA_AUTOPROBE pr_info("valid signature not found at 0x%x\n", base); Loading @@ -495,8 +492,7 @@ static int cosa_probe(int base, int irq, int dma) unsigned long irqs; /* pr_info("IRQ autoprobe\n"); */ irqs = probe_irq_on(); /* * Enable interrupt on tx buffer empty (it sure is) /* Enable interrupt on tx buffer empty (it sure is) * really sure ? * FIXME: When this code is not used as module, we should * probably call udelay() instead of the interruptible sleep. Loading Loading @@ -563,7 +559,8 @@ static int cosa_probe(int base, int irq, int dma) sema_init(&chan->wsem, 1); /* Register the network interface */ if (!(chan->netdev = alloc_hdlcdev(chan))) { chan->netdev = alloc_hdlcdev(chan); if (!chan->netdev) { pr_warn("%s: alloc_hdlcdev failed\n", chan->name); err = -ENOMEM; goto err_hdlcdev; Loading Loading @@ -608,7 +605,6 @@ static int cosa_probe(int base, int irq, int dma) return err; } /*---------- network device ---------- */ static int cosa_net_attach(struct net_device *dev, unsigned short encoding, Loading Loading @@ -714,13 +710,12 @@ static int cosa_net_close(struct net_device *dev) static char *cosa_net_setup_rx(struct channel_data *chan, int size) { /* * We can safely fall back to non-dma-able memory, because we have /* We can safely fall back to non-dma-able memory, because we have * the cosa->bouncebuf pre-allocated. */ kfree_skb(chan->rx_skb); chan->rx_skb = dev_alloc_skb(size); if (chan->rx_skb == NULL) { if (!chan->rx_skb) { pr_notice("%s: Memory squeeze, dropping packet\n", chan->name); chan->netdev->stats.rx_dropped++; return NULL; Loading Loading @@ -784,7 +779,7 @@ static ssize_t cosa_read(struct file *file, return -ERESTARTSYS; chan->rxdata = kmalloc(COSA_MTU, GFP_DMA | GFP_KERNEL); if (chan->rxdata == NULL) { if (!chan->rxdata) { mutex_unlock(&chan->rlock); return -ENOMEM; } Loading Loading @@ -840,7 +835,6 @@ static int chrdev_rx_done(struct channel_data *chan) return 1; } static ssize_t cosa_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { Loading @@ -863,7 +857,7 @@ static ssize_t cosa_write(struct file *file, /* Allocate the buffer */ kbuf = kmalloc(count, GFP_KERNEL | GFP_DMA); if (kbuf == NULL) { if (!kbuf) { up(&chan->wsem); return -ENOMEM; } Loading Loading @@ -927,15 +921,15 @@ static int cosa_open(struct inode *inode, struct file *file) int ret = 0; mutex_lock(&cosa_chardev_mutex); if ((n=iminor(file_inode(file))>>CARD_MINOR_BITS) >= nr_cards) { n = iminor(file_inode(file)) >> CARD_MINOR_BITS; if (n >= nr_cards) { ret = -ENODEV; goto out; } cosa = cosa_cards + n; if ((n=iminor(file_inode(file)) & ((1<<CARD_MINOR_BITS)-1)) >= cosa->nchannels) { n = iminor(file_inode(file)) & ((1 << CARD_MINOR_BITS) - 1); if (n >= cosa->nchannels) { ret = -ENODEV; goto out; } Loading Loading @@ -988,16 +982,15 @@ static int cosa_fasync(struct inode *inode, struct file *file, int on) } #endif /* ---------- Ioctls ---------- */ /* * Ioctl subroutines can safely be made inline, because they are called /* Ioctl subroutines can safely be made inline, because they are called * only from cosa_ioctl(). */ static inline int cosa_reset(struct cosa_data *cosa) { char idstring[COSA_MAX_ID_STRING]; if (cosa->usage > 1) pr_info("cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n", cosa->num, cosa->usage); Loading Loading @@ -1034,7 +1027,6 @@ static inline int cosa_download(struct cosa_data *cosa, void __user *arg) if (d.len < 0 || d.len > COSA_MAX_FIRMWARE_SIZE) return -EINVAL; /* If something fails, force the user to reset the card */ cosa->firmware_status &= ~(COSA_FW_RESET | COSA_FW_DOWNLOAD); Loading Loading @@ -1098,7 +1090,8 @@ static inline int cosa_start(struct cosa_data *cosa, int address) return -EPERM; } cosa->firmware_status &= ~COSA_FW_RESET; if ((i=startmicrocode(cosa, address)) < 0) { i = startmicrocode(cosa, address); if (i < 0) { pr_notice("cosa%d: start microcode at 0x%04x failed: %d\n", cosa->num, address, i); return -EIO; Loading @@ -1113,6 +1106,7 @@ static inline int cosa_start(struct cosa_data *cosa, int address) static inline int cosa_getidstr(struct cosa_data *cosa, char __user *string) { int l = strlen(cosa->id_string) + 1; if (copy_to_user(string, cosa->id_string, l)) return -EFAULT; return l; Loading @@ -1122,15 +1116,18 @@ static inline int cosa_getidstr(struct cosa_data *cosa, char __user *string) static inline int cosa_gettype(struct cosa_data *cosa, char __user *string) { int l = strlen(cosa->type) + 1; if (copy_to_user(string, cosa->type, l)) return -EFAULT; return l; } static int cosa_ioctl_common(struct cosa_data *cosa, struct channel_data *channel, unsigned int cmd, unsigned long arg) struct channel_data *channel, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; switch (cmd) { case COSAIORSET: /* Reset the device */ if (!capable(CAP_NET_ADMIN)) Loading Loading @@ -1176,6 +1173,7 @@ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int rv; struct channel_data *chan = dev_to_chan(dev); rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data); if (rv != -ENOIOCTLCMD) Loading @@ -1197,11 +1195,9 @@ static long cosa_chardev_ioctl(struct file *file, unsigned int cmd, return ret; } /*---------- HW layer interface ---------- */ /* * The higher layer can bind itself to the HW layer by setting the callbacks /* The higher layer can bind itself to the HW layer by setting the callbacks * in the channel_data structure and by using these routines. */ static void cosa_enable_rx(struct channel_data *chan) Loading @@ -1220,8 +1216,7 @@ static void cosa_disable_rx(struct channel_data *chan) put_driver_status(cosa); } /* * FIXME: This routine probably should check for cosa_start_tx() called when /* FIXME: This routine probably should check for cosa_start_tx() called when * the previous transmit is still unfinished. In this case the non-zero * return value should indicate to the caller that the queuing(sp?) up * the transmit has failed. Loading Loading @@ -1316,8 +1311,7 @@ static void put_driver_status_nolock(struct cosa_data *cosa) #endif } /* * The "kickme" function: When the DMA times out, this is called to /* The "kickme" function: When the DMA times out, this is called to * clean up the driver status. * FIXME: Preliminary support, the interface is probably wrong. */ Loading Loading @@ -1352,8 +1346,7 @@ static void cosa_kick(struct cosa_data *cosa) spin_unlock_irqrestore(&cosa->lock, flags); } /* * Check if the whole buffer is DMA-able. It means it is below the 16M of /* Check if the whole buffer is DMA-able. It means it is below the 16M of * physical memory and doesn't span the 64k boundary. For now it seems * SKB's never do this, but we'll check this anyway. */ Loading @@ -1361,6 +1354,7 @@ static int cosa_dma_able(struct channel_data *chan, char *buf, int len) { static int count; unsigned long b = (unsigned long)buf; if (b + len >= MAX_DMA_ADDRESS) return 0; if ((b ^ (b + len)) & 0x10000) { Loading @@ -1372,11 +1366,9 @@ static int cosa_dma_able(struct channel_data *chan, char *buf, int len) return 1; } /* ---------- The SRP/COSA ROM monitor functions ---------- */ /* * Downloading SRP microcode: say "w" to SRP monitor, it answers by "w=", /* Downloading SRP microcode: say "w" to SRP monitor, it answers by "w=", * drivers need to say 4-digit hex number meaning start address of the microcode * separated by a single space. Monitor replies by saying " =". Now driver * has to write 4-digit hex number meaning the last byte address ended Loading @@ -1387,18 +1379,27 @@ static int download(struct cosa_data *cosa, const char __user *microcode, int le { int i; if (put_wait_data(cosa, 'w') == -1) return -1; if (put_wait_data(cosa, 'w') == -1) return -1; if ((i=get_wait_data(cosa)) != 'w') { printk("dnld: 0x%04x\n",i); return -2;} if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -10; if (get_wait_data(cosa) != ' ') return -11; if (get_wait_data(cosa) != '=') return -12; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -10; if (get_wait_data(cosa) != ' ') return -11; if (get_wait_data(cosa) != '=') return -12; if (puthexnumber(cosa, address+length-1) < 0) return -13; if (put_wait_data(cosa, ' ') == -1) return -18; if (get_wait_data(cosa) != ' ') return -19; if (puthexnumber(cosa, address + length - 1) < 0) return -13; if (put_wait_data(cosa, ' ') == -1) return -18; if (get_wait_data(cosa) != ' ') return -19; while (length--) { char c; Loading @@ -1413,43 +1414,53 @@ static int download(struct cosa_data *cosa, const char __user *microcode, int le microcode++; } if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; #if 0 printk(KERN_DEBUG "cosa%d: download completed.\n", cosa->num); #endif return 0; } /* * Starting microcode is done via the "g" command of the SRP monitor. /* Starting microcode is done via the "g" command of the SRP monitor. * The chat should be the following: "g" "g=" "<addr><CR>" * "<CR><CR><LF><CR><LF>". */ static int startmicrocode(struct cosa_data *cosa, int address) { if (put_wait_data(cosa, 'g') == -1) return -1; if (get_wait_data(cosa) != 'g') return -2; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, '\r') == -1) return -5; if (get_wait_data(cosa) != '\r') return -6; if (get_wait_data(cosa) != '\r') return -7; if (get_wait_data(cosa) != '\n') return -8; if (get_wait_data(cosa) != '\r') return -9; if (get_wait_data(cosa) != '\n') return -10; if (put_wait_data(cosa, 'g') == -1) return -1; if (get_wait_data(cosa) != 'g') return -2; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, '\r') == -1) return -5; if (get_wait_data(cosa) != '\r') return -6; if (get_wait_data(cosa) != '\r') return -7; if (get_wait_data(cosa) != '\n') return -8; if (get_wait_data(cosa) != '\r') return -9; if (get_wait_data(cosa) != '\n') return -10; #if 0 printk(KERN_DEBUG "cosa%d: microcode started\n", cosa->num); #endif return 0; } /* * Reading memory is done via the "r" command of the SRP monitor. /* Reading memory is done via the "r" command of the SRP monitor. * The chat is the following "r" "r=" "<addr> " " =" "<last_byte> " " " * Then driver can read the data and the conversation is finished * by SRP monitor sending "<CR><LF>." (dot at the end). Loading @@ -1459,23 +1470,35 @@ static int startmicrocode(struct cosa_data *cosa, int address) */ static int readmem(struct cosa_data *cosa, char __user *microcode, int length, int address) { if (put_wait_data(cosa, 'r') == -1) return -1; if ((get_wait_data(cosa)) != 'r') return -2; if ((get_wait_data(cosa)) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -5; if (get_wait_data(cosa) != ' ') return -6; if (get_wait_data(cosa) != '=') return -7; if (puthexnumber(cosa, address+length-1) < 0) return -8; if (put_wait_data(cosa, ' ') == -1) return -9; if (get_wait_data(cosa) != ' ') return -10; if (put_wait_data(cosa, 'r') == -1) return -1; if ((get_wait_data(cosa)) != 'r') return -2; if ((get_wait_data(cosa)) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -5; if (get_wait_data(cosa) != ' ') return -6; if (get_wait_data(cosa) != '=') return -7; if (puthexnumber(cosa, address + length - 1) < 0) return -8; if (put_wait_data(cosa, ' ') == -1) return -9; if (get_wait_data(cosa) != ' ') return -10; while (length--) { char c; int i; if ((i=get_wait_data(cosa)) == -1) { i = get_wait_data(cosa); if (i == -1) { pr_info("0x%04x bytes remaining\n", length); return -11; } Loading @@ -1489,17 +1512,19 @@ static int readmem(struct cosa_data *cosa, char __user *microcode, int length, i microcode++; } if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; #if 0 printk(KERN_DEBUG "cosa%d: readmem completed.\n", cosa->num); #endif return 0; } /* * This function resets the device and reads the initial prompt /* This function resets the device and reads the initial prompt * of the device's ROM monitor. */ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) Loading @@ -1514,8 +1539,7 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) /* Disable all IRQs from the card */ cosa_putstatus(cosa, 0); /* * Try to read the ID string. The card then prints out the /* Try to read the ID string. The card then prints out the * identification string ended by the "\n\x2e". * * The following loop is indexed through i (instead of id) Loading @@ -1523,9 +1547,10 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) * the port returns '\r', '\n' or '\x2e' permanently. */ for (i = 0; i < COSA_MAX_ID_STRING - 1; i++, prev = curr) { if ((curr = get_wait_data(cosa)) == -1) { curr = get_wait_data(cosa); if (curr == -1) return -1; } curr &= 0xff; if (curr != '\r' && curr != '\n' && curr != 0x2e) idstring[id++] = curr; Loading @@ -1537,11 +1562,9 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) return id; } /* ---------- Auxiliary routines for COSA/SRP monitor ---------- */ /* * This routine gets the data byte from the card waiting for the SR_RX_RDY /* This routine gets the data byte from the card waiting for the SR_RX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware. */ Loading @@ -1553,6 +1576,7 @@ static int get_wait_data(struct cosa_data *cosa) /* read data and return them */ if (cosa_getstatus(cosa) & SR_RX_RDY) { short r; r = cosa_getdata8(cosa); #if 0 pr_info("get_wait_data returning after %d retries\n", Loading @@ -1568,14 +1592,14 @@ static int get_wait_data(struct cosa_data *cosa) return -1; } /* * This routine puts the data byte to the card waiting for the SR_TX_RDY /* This routine puts the data byte to the card waiting for the SR_TX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware). */ static int put_wait_data(struct cosa_data *cosa, int data) { int retries = 1000; while (--retries) { /* read data and return them */ if (cosa_getstatus(cosa) & SR_TX_RDY) { Loading @@ -1595,8 +1619,7 @@ static int put_wait_data(struct cosa_data *cosa, int data) return -1; } /* * The following routine puts the hexadecimal number into the SRP monitor /* The following routine puts the hexadecimal number into the SRP monitor * and verifies the proper echo of the sent bytes. Returns 0 on success, * negative number on failure (-1,-3,-5,-7) means that put_wait_data() failed, * (-2,-4,-6,-8) means that reading echo failed. Loading @@ -1623,11 +1646,9 @@ static int puthexnumber(struct cosa_data *cosa, int number) return 0; } /* ---------- Interrupt routines ---------- */ /* * There are three types of interrupt: /* There are three types of interrupt: * At the beginning of transmit - this handled is in tx_interrupt(), * at the beginning of receive - it is in rx_interrupt() and * at the end of transmit/receive - it is the eot_interrupt() function. Loading @@ -1641,8 +1662,7 @@ static int puthexnumber(struct cosa_data *cosa, int number) * It's time to use the bottom half :-( */ /* * Transmit interrupt routine - called when COSA is willing to obtain /* Transmit interrupt routine - called when COSA is willing to obtain * data from the OS. The most tricky part of the routine is selection * of channel we (OS) want to send packet for. For SRP we should probably * use the round-robin approach. The newer COSA firmwares have a simple Loading @@ -1668,6 +1688,7 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) if (!test_bit(IRQBIT, &cosa->rxtx)) { /* flow control, see the comment above */ int i = 0; if (!cosa->txbitmap) { pr_warn("%s: No channel wants data in TX IRQ. Expect DMA timeout.\n", cosa->name); Loading @@ -1683,7 +1704,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) cosa->txchan = 0; if (!(cosa->txbitmap & (1 << cosa->txchan))) continue; if (~status & (1 << (cosa->txchan+DRIVER_TXMAP_SHIFT))) if (~status & (1 << (cosa->txchan + DRIVER_TXMAP_SHIFT))) break; /* in second pass, accept first ready-to-TX channel */ if (i > cosa->nchannels) { Loading @@ -1699,7 +1721,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) cosa->txsize = cosa->chan[cosa->txchan].txsize; if (cosa_dma_able(cosa->chan + cosa->txchan, cosa->chan[cosa->txchan].txbuf, cosa->txsize)) { cosa->chan[cosa->txchan].txbuf, cosa->txsize)) { cosa->txbuf = cosa->chan[cosa->txchan].txbuf; } else { memcpy(cosa->bouncebuf, cosa->chan[cosa->txchan].txbuf, Loading Loading @@ -1739,8 +1762,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) | (cosa->txsize & 0x1fff)); #ifdef DEBUG_IO debug_status_out(cosa, SR_TX_INT_ENA); debug_data_out(cosa, ((cosa->txchan<<13) & 0xe000) | (cosa->txsize & 0x1fff)); debug_data_out(cosa, ((cosa->txchan << 13) & 0xe000) | (cosa->txsize & 0x1fff)); debug_data_in(cosa, cosa_getdata8(cosa)); debug_status_out(cosa, 0); #else Loading @@ -1752,11 +1775,13 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) if (cosa->busmaster) { unsigned long addr = virt_to_bus(cosa->txbuf); int count = 0; pr_info("busmaster IRQ\n"); while (!(cosa_getstatus(cosa) & SR_TX_RDY)) { count++; udelay(10); if (count > 1000) break; if (count > 1000) break; } pr_info("status %x\n", cosa_getstatus(cosa)); pr_info("ready after %d loops\n", count); Loading @@ -1765,7 +1790,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) count = 0; while (!(cosa_getstatus(cosa) & SR_TX_RDY)) { count++; if (count > 1000) break; if (count > 1000) break; udelay(10); } pr_info("ready after %d loops\n", count); Loading Loading @@ -1859,11 +1885,11 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status) disable_dma(cosa->dma); clear_dma_ff(cosa->dma); set_dma_mode(cosa->dma, DMA_MODE_READ); if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) { if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) set_dma_addr(cosa->dma, virt_to_bus(cosa->rxbuf)); } else { else set_dma_addr(cosa->dma, virt_to_bus(cosa->bouncebuf)); } set_dma_count(cosa->dma, (cosa->rxsize & 0x1fff)); enable_dma(cosa->dma); release_dma_lock(flags); Loading @@ -1882,6 +1908,7 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status) static inline void eot_interrupt(struct cosa_data *cosa, int status) { unsigned long flags, flags1; spin_lock_irqsave(&cosa->lock, flags); flags1 = claim_dma_lock(); disable_dma(cosa->dma); Loading @@ -1889,6 +1916,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) release_dma_lock(flags1); if (test_bit(TXBIT, &cosa->rxtx)) { struct channel_data *chan = cosa->chan + cosa->txchan; if (chan->tx_done) if (chan->tx_done(chan, cosa->txsize)) clear_bit(chan->num, &cosa->txbitmap); Loading @@ -1896,6 +1924,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) #ifdef DEBUG_DATA { int i; pr_info("cosa%dc%d: done rx(0x%x)", cosa->num, cosa->rxchan->num, cosa->rxsize); for (i = 0; i < cosa->rxsize; i++) Loading @@ -1914,8 +1943,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) } else { pr_notice("cosa%d: unexpected EOT interrupt\n", cosa->num); } /* * Clear the RXBIT, TXBIT and IRQBIT (the latest should be /* Clear the RXBIT, TXBIT and IRQBIT (the latest should be * cleared anyway). We should do it as soon as possible * so that we can tell the COSA we are done and to give it a time * for recovery. Loading Loading @@ -1968,10 +1996,8 @@ static irqreturn_t cosa_interrupt(int irq, void *cosa_) return IRQ_HANDLED; } /* ---------- I/O debugging routines ---------- */ /* * These routines can be used to monitor COSA/SRP I/O and to printk() /* These routines can be used to monitor COSA/SRP I/O and to printk() * the data being transferred on the data and status I/O port in a * readable way. */ Loading @@ -1980,6 +2006,7 @@ static irqreturn_t cosa_interrupt(int irq, void *cosa_) static void debug_status_in(struct cosa_data *cosa, int status) { char *s; switch (status & SR_CMD_FROM_SRP_MASK) { case SR_UP_REQUEST: s = "RX_REQ"; Loading Loading
drivers/net/wan/cosa.c +260 −233 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-or-later /* $Id: cosa.c,v 1.31 2000/03/08 17:47:16 kas Exp $ */ /* * Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> /* Copyright (C) 1995-1997 Jan "Yenya" Kasprzak <kas@fi.muni.cz> * Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl> */ /* * The driver for the SRP and COSA synchronous serial cards. /* The driver for the SRP and COSA synchronous serial cards. * * HARDWARE INFO * Loading Loading @@ -124,9 +122,9 @@ struct channel_data { }; /* cosa->firmware_status bits */ #define COSA_FW_RESET (1<<0) /* Is the ROM monitor active? */ #define COSA_FW_DOWNLOAD (1<<1) /* Is the microcode downloaded? */ #define COSA_FW_START (1<<2) /* Is the microcode running? */ #define COSA_FW_RESET BIT(0) /* Is the ROM monitor active? */ #define COSA_FW_DOWNLOAD BIT(1) /* Is the microcode downloaded? */ #define COSA_FW_START BIT(2) /* Is the microcode running? */ struct cosa_data { int num; /* Card number */ Loading @@ -152,28 +150,25 @@ struct cosa_data { char *type; /* card type */ }; /* * Define this if you want all the possible ports to be autoprobed. /* Define this if you want all the possible ports to be autoprobed. * It is here but it probably is not a good idea to use this. */ /* #define COSA_ISA_AUTOPROBE 1*/ /* * Character device major number. 117 was allocated for us. /* Character device major number. 117 was allocated for us. * The value of 0 means to allocate a first free one. */ static DEFINE_MUTEX(cosa_chardev_mutex); static int cosa_major = 117; /* * Encoding of the minor numbers: /* Encoding of the minor numbers: * The lowest CARD_MINOR_BITS bits means the channel on the single card, * the highest bits means the card number. */ #define CARD_MINOR_BITS 4 /* How many bits in minor number are reserved * for the single card */ /* * The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" * for the single card */ /* The following depends on CARD_MINOR_BITS. Unfortunately, the "MODULE_STRING" * macro doesn't like anything other than the raw number as an argument :-( */ #define MAX_CARDS 16 Loading @@ -184,8 +179,7 @@ static int cosa_major = 117; #define DRIVER_TXMAP_SHIFT 2 #define DRIVER_TXMAP_MASK 0x0c /* FIXME: 0xfc for 8-channel version */ /* * for cosa->rxtx - indicates whether either transmit or receive is /* for cosa->rxtx - indicates whether either transmit or receive is * in progress. These values are mean number of the bit. */ #define TXBIT 0 Loading Loading @@ -244,14 +238,14 @@ MODULE_LICENSE("GPL"); #define cosa_inw inw #endif #define is_8bit(cosa) (!(cosa->datareg & 0x08)) #define is_8bit(cosa) (!((cosa)->datareg & 0x08)) #define cosa_getstatus(cosa) (cosa_inb(cosa->statusreg)) #define cosa_putstatus(cosa, stat) (cosa_outb(stat, cosa->statusreg)) #define cosa_getdata16(cosa) (cosa_inw(cosa->datareg)) #define cosa_getdata8(cosa) (cosa_inb(cosa->datareg)) #define cosa_putdata16(cosa, dt) (cosa_outw(dt, cosa->datareg)) #define cosa_putdata8(cosa, dt) (cosa_outb(dt, cosa->datareg)) #define cosa_getstatus(cosa) (cosa_inb((cosa)->statusreg)) #define cosa_putstatus(cosa, stat) (cosa_outb(stat, (cosa)->statusreg)) #define cosa_getdata16(cosa) (cosa_inw((cosa)->datareg)) #define cosa_getdata8(cosa) (cosa_inb((cosa)->datareg)) #define cosa_putdata16(cosa, dt) (cosa_outw(dt, (cosa)->datareg)) #define cosa_putdata8(cosa, dt) (cosa_outb(dt, (cosa)->datareg)) /* Initialization stuff */ static int cosa_probe(int ioaddr, int irq, int dma); Loading Loading @@ -355,7 +349,8 @@ static int __init cosa_init(void) goto out; } } else { if (!(cosa_major=register_chrdev(0, "cosa", &cosa_fops))) { cosa_major = register_chrdev(0, "cosa", &cosa_fops); if (!cosa_major) { pr_warn("unable to register chardev\n"); err = -EIO; goto out; Loading Loading @@ -438,7 +433,8 @@ static int cosa_probe(int base, int irq, int dma) return -1; } /* I/O address should be between 0x100 and 0x3ff and should be * multiple of 8. */ * multiple of 8. */ if (base < 0x100 || base > 0x3ff || base & 0x7) { pr_info("invalid I/O address 0x%x\n", base); return -1; Loading @@ -449,7 +445,8 @@ static int cosa_probe(int base, int irq, int dma) return -1; } /* and finally, on 16-bit COSA DMA should be 4-7 and * I/O base should not be multiple of 0x10 */ * I/O base should not be multiple of 0x10 */ if (((base & 0x8) && dma < 4) || (!(base & 0x8) && dma > 3)) { pr_info("8/16 bit base and DMA mismatch (base=0x%x, dma=%d)\n", base, dma); Loading @@ -471,11 +468,11 @@ static int cosa_probe(int base, int irq, int dma) } /* Test the validity of identification string */ if (!strncmp(cosa->id_string, "SRP", 3)) if (!strncmp(cosa->id_string, "SRP", 3)) { cosa->type = "srp"; else if (!strncmp(cosa->id_string, "COSA", 4)) } else if (!strncmp(cosa->id_string, "COSA", 4)) { cosa->type = is_8bit(cosa) ? "cosa8" : "cosa16"; else { } else { /* Print a warning only if we are not autoprobing */ #ifndef COSA_ISA_AUTOPROBE pr_info("valid signature not found at 0x%x\n", base); Loading @@ -495,8 +492,7 @@ static int cosa_probe(int base, int irq, int dma) unsigned long irqs; /* pr_info("IRQ autoprobe\n"); */ irqs = probe_irq_on(); /* * Enable interrupt on tx buffer empty (it sure is) /* Enable interrupt on tx buffer empty (it sure is) * really sure ? * FIXME: When this code is not used as module, we should * probably call udelay() instead of the interruptible sleep. Loading Loading @@ -563,7 +559,8 @@ static int cosa_probe(int base, int irq, int dma) sema_init(&chan->wsem, 1); /* Register the network interface */ if (!(chan->netdev = alloc_hdlcdev(chan))) { chan->netdev = alloc_hdlcdev(chan); if (!chan->netdev) { pr_warn("%s: alloc_hdlcdev failed\n", chan->name); err = -ENOMEM; goto err_hdlcdev; Loading Loading @@ -608,7 +605,6 @@ static int cosa_probe(int base, int irq, int dma) return err; } /*---------- network device ---------- */ static int cosa_net_attach(struct net_device *dev, unsigned short encoding, Loading Loading @@ -714,13 +710,12 @@ static int cosa_net_close(struct net_device *dev) static char *cosa_net_setup_rx(struct channel_data *chan, int size) { /* * We can safely fall back to non-dma-able memory, because we have /* We can safely fall back to non-dma-able memory, because we have * the cosa->bouncebuf pre-allocated. */ kfree_skb(chan->rx_skb); chan->rx_skb = dev_alloc_skb(size); if (chan->rx_skb == NULL) { if (!chan->rx_skb) { pr_notice("%s: Memory squeeze, dropping packet\n", chan->name); chan->netdev->stats.rx_dropped++; return NULL; Loading Loading @@ -784,7 +779,7 @@ static ssize_t cosa_read(struct file *file, return -ERESTARTSYS; chan->rxdata = kmalloc(COSA_MTU, GFP_DMA | GFP_KERNEL); if (chan->rxdata == NULL) { if (!chan->rxdata) { mutex_unlock(&chan->rlock); return -ENOMEM; } Loading Loading @@ -840,7 +835,6 @@ static int chrdev_rx_done(struct channel_data *chan) return 1; } static ssize_t cosa_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { Loading @@ -863,7 +857,7 @@ static ssize_t cosa_write(struct file *file, /* Allocate the buffer */ kbuf = kmalloc(count, GFP_KERNEL | GFP_DMA); if (kbuf == NULL) { if (!kbuf) { up(&chan->wsem); return -ENOMEM; } Loading Loading @@ -927,15 +921,15 @@ static int cosa_open(struct inode *inode, struct file *file) int ret = 0; mutex_lock(&cosa_chardev_mutex); if ((n=iminor(file_inode(file))>>CARD_MINOR_BITS) >= nr_cards) { n = iminor(file_inode(file)) >> CARD_MINOR_BITS; if (n >= nr_cards) { ret = -ENODEV; goto out; } cosa = cosa_cards + n; if ((n=iminor(file_inode(file)) & ((1<<CARD_MINOR_BITS)-1)) >= cosa->nchannels) { n = iminor(file_inode(file)) & ((1 << CARD_MINOR_BITS) - 1); if (n >= cosa->nchannels) { ret = -ENODEV; goto out; } Loading Loading @@ -988,16 +982,15 @@ static int cosa_fasync(struct inode *inode, struct file *file, int on) } #endif /* ---------- Ioctls ---------- */ /* * Ioctl subroutines can safely be made inline, because they are called /* Ioctl subroutines can safely be made inline, because they are called * only from cosa_ioctl(). */ static inline int cosa_reset(struct cosa_data *cosa) { char idstring[COSA_MAX_ID_STRING]; if (cosa->usage > 1) pr_info("cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n", cosa->num, cosa->usage); Loading Loading @@ -1034,7 +1027,6 @@ static inline int cosa_download(struct cosa_data *cosa, void __user *arg) if (d.len < 0 || d.len > COSA_MAX_FIRMWARE_SIZE) return -EINVAL; /* If something fails, force the user to reset the card */ cosa->firmware_status &= ~(COSA_FW_RESET | COSA_FW_DOWNLOAD); Loading Loading @@ -1098,7 +1090,8 @@ static inline int cosa_start(struct cosa_data *cosa, int address) return -EPERM; } cosa->firmware_status &= ~COSA_FW_RESET; if ((i=startmicrocode(cosa, address)) < 0) { i = startmicrocode(cosa, address); if (i < 0) { pr_notice("cosa%d: start microcode at 0x%04x failed: %d\n", cosa->num, address, i); return -EIO; Loading @@ -1113,6 +1106,7 @@ static inline int cosa_start(struct cosa_data *cosa, int address) static inline int cosa_getidstr(struct cosa_data *cosa, char __user *string) { int l = strlen(cosa->id_string) + 1; if (copy_to_user(string, cosa->id_string, l)) return -EFAULT; return l; Loading @@ -1122,15 +1116,18 @@ static inline int cosa_getidstr(struct cosa_data *cosa, char __user *string) static inline int cosa_gettype(struct cosa_data *cosa, char __user *string) { int l = strlen(cosa->type) + 1; if (copy_to_user(string, cosa->type, l)) return -EFAULT; return l; } static int cosa_ioctl_common(struct cosa_data *cosa, struct channel_data *channel, unsigned int cmd, unsigned long arg) struct channel_data *channel, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; switch (cmd) { case COSAIORSET: /* Reset the device */ if (!capable(CAP_NET_ADMIN)) Loading Loading @@ -1176,6 +1173,7 @@ static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { int rv; struct channel_data *chan = dev_to_chan(dev); rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data); if (rv != -ENOIOCTLCMD) Loading @@ -1197,11 +1195,9 @@ static long cosa_chardev_ioctl(struct file *file, unsigned int cmd, return ret; } /*---------- HW layer interface ---------- */ /* * The higher layer can bind itself to the HW layer by setting the callbacks /* The higher layer can bind itself to the HW layer by setting the callbacks * in the channel_data structure and by using these routines. */ static void cosa_enable_rx(struct channel_data *chan) Loading @@ -1220,8 +1216,7 @@ static void cosa_disable_rx(struct channel_data *chan) put_driver_status(cosa); } /* * FIXME: This routine probably should check for cosa_start_tx() called when /* FIXME: This routine probably should check for cosa_start_tx() called when * the previous transmit is still unfinished. In this case the non-zero * return value should indicate to the caller that the queuing(sp?) up * the transmit has failed. Loading Loading @@ -1316,8 +1311,7 @@ static void put_driver_status_nolock(struct cosa_data *cosa) #endif } /* * The "kickme" function: When the DMA times out, this is called to /* The "kickme" function: When the DMA times out, this is called to * clean up the driver status. * FIXME: Preliminary support, the interface is probably wrong. */ Loading Loading @@ -1352,8 +1346,7 @@ static void cosa_kick(struct cosa_data *cosa) spin_unlock_irqrestore(&cosa->lock, flags); } /* * Check if the whole buffer is DMA-able. It means it is below the 16M of /* Check if the whole buffer is DMA-able. It means it is below the 16M of * physical memory and doesn't span the 64k boundary. For now it seems * SKB's never do this, but we'll check this anyway. */ Loading @@ -1361,6 +1354,7 @@ static int cosa_dma_able(struct channel_data *chan, char *buf, int len) { static int count; unsigned long b = (unsigned long)buf; if (b + len >= MAX_DMA_ADDRESS) return 0; if ((b ^ (b + len)) & 0x10000) { Loading @@ -1372,11 +1366,9 @@ static int cosa_dma_able(struct channel_data *chan, char *buf, int len) return 1; } /* ---------- The SRP/COSA ROM monitor functions ---------- */ /* * Downloading SRP microcode: say "w" to SRP monitor, it answers by "w=", /* Downloading SRP microcode: say "w" to SRP monitor, it answers by "w=", * drivers need to say 4-digit hex number meaning start address of the microcode * separated by a single space. Monitor replies by saying " =". Now driver * has to write 4-digit hex number meaning the last byte address ended Loading @@ -1387,18 +1379,27 @@ static int download(struct cosa_data *cosa, const char __user *microcode, int le { int i; if (put_wait_data(cosa, 'w') == -1) return -1; if (put_wait_data(cosa, 'w') == -1) return -1; if ((i=get_wait_data(cosa)) != 'w') { printk("dnld: 0x%04x\n",i); return -2;} if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -10; if (get_wait_data(cosa) != ' ') return -11; if (get_wait_data(cosa) != '=') return -12; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -10; if (get_wait_data(cosa) != ' ') return -11; if (get_wait_data(cosa) != '=') return -12; if (puthexnumber(cosa, address+length-1) < 0) return -13; if (put_wait_data(cosa, ' ') == -1) return -18; if (get_wait_data(cosa) != ' ') return -19; if (puthexnumber(cosa, address + length - 1) < 0) return -13; if (put_wait_data(cosa, ' ') == -1) return -18; if (get_wait_data(cosa) != ' ') return -19; while (length--) { char c; Loading @@ -1413,43 +1414,53 @@ static int download(struct cosa_data *cosa, const char __user *microcode, int le microcode++; } if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; #if 0 printk(KERN_DEBUG "cosa%d: download completed.\n", cosa->num); #endif return 0; } /* * Starting microcode is done via the "g" command of the SRP monitor. /* Starting microcode is done via the "g" command of the SRP monitor. * The chat should be the following: "g" "g=" "<addr><CR>" * "<CR><CR><LF><CR><LF>". */ static int startmicrocode(struct cosa_data *cosa, int address) { if (put_wait_data(cosa, 'g') == -1) return -1; if (get_wait_data(cosa) != 'g') return -2; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, '\r') == -1) return -5; if (get_wait_data(cosa) != '\r') return -6; if (get_wait_data(cosa) != '\r') return -7; if (get_wait_data(cosa) != '\n') return -8; if (get_wait_data(cosa) != '\r') return -9; if (get_wait_data(cosa) != '\n') return -10; if (put_wait_data(cosa, 'g') == -1) return -1; if (get_wait_data(cosa) != 'g') return -2; if (get_wait_data(cosa) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, '\r') == -1) return -5; if (get_wait_data(cosa) != '\r') return -6; if (get_wait_data(cosa) != '\r') return -7; if (get_wait_data(cosa) != '\n') return -8; if (get_wait_data(cosa) != '\r') return -9; if (get_wait_data(cosa) != '\n') return -10; #if 0 printk(KERN_DEBUG "cosa%d: microcode started\n", cosa->num); #endif return 0; } /* * Reading memory is done via the "r" command of the SRP monitor. /* Reading memory is done via the "r" command of the SRP monitor. * The chat is the following "r" "r=" "<addr> " " =" "<last_byte> " " " * Then driver can read the data and the conversation is finished * by SRP monitor sending "<CR><LF>." (dot at the end). Loading @@ -1459,23 +1470,35 @@ static int startmicrocode(struct cosa_data *cosa, int address) */ static int readmem(struct cosa_data *cosa, char __user *microcode, int length, int address) { if (put_wait_data(cosa, 'r') == -1) return -1; if ((get_wait_data(cosa)) != 'r') return -2; if ((get_wait_data(cosa)) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -5; if (get_wait_data(cosa) != ' ') return -6; if (get_wait_data(cosa) != '=') return -7; if (puthexnumber(cosa, address+length-1) < 0) return -8; if (put_wait_data(cosa, ' ') == -1) return -9; if (get_wait_data(cosa) != ' ') return -10; if (put_wait_data(cosa, 'r') == -1) return -1; if ((get_wait_data(cosa)) != 'r') return -2; if ((get_wait_data(cosa)) != '=') return -3; if (puthexnumber(cosa, address) < 0) return -4; if (put_wait_data(cosa, ' ') == -1) return -5; if (get_wait_data(cosa) != ' ') return -6; if (get_wait_data(cosa) != '=') return -7; if (puthexnumber(cosa, address + length - 1) < 0) return -8; if (put_wait_data(cosa, ' ') == -1) return -9; if (get_wait_data(cosa) != ' ') return -10; while (length--) { char c; int i; if ((i=get_wait_data(cosa)) == -1) { i = get_wait_data(cosa); if (i == -1) { pr_info("0x%04x bytes remaining\n", length); return -11; } Loading @@ -1489,17 +1512,19 @@ static int readmem(struct cosa_data *cosa, char __user *microcode, int length, i microcode++; } if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; if (get_wait_data(cosa) != '\r') return -21; if (get_wait_data(cosa) != '\n') return -22; if (get_wait_data(cosa) != '.') return -23; #if 0 printk(KERN_DEBUG "cosa%d: readmem completed.\n", cosa->num); #endif return 0; } /* * This function resets the device and reads the initial prompt /* This function resets the device and reads the initial prompt * of the device's ROM monitor. */ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) Loading @@ -1514,8 +1539,7 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) /* Disable all IRQs from the card */ cosa_putstatus(cosa, 0); /* * Try to read the ID string. The card then prints out the /* Try to read the ID string. The card then prints out the * identification string ended by the "\n\x2e". * * The following loop is indexed through i (instead of id) Loading @@ -1523,9 +1547,10 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) * the port returns '\r', '\n' or '\x2e' permanently. */ for (i = 0; i < COSA_MAX_ID_STRING - 1; i++, prev = curr) { if ((curr = get_wait_data(cosa)) == -1) { curr = get_wait_data(cosa); if (curr == -1) return -1; } curr &= 0xff; if (curr != '\r' && curr != '\n' && curr != 0x2e) idstring[id++] = curr; Loading @@ -1537,11 +1562,9 @@ static int cosa_reset_and_read_id(struct cosa_data *cosa, char *idstring) return id; } /* ---------- Auxiliary routines for COSA/SRP monitor ---------- */ /* * This routine gets the data byte from the card waiting for the SR_RX_RDY /* This routine gets the data byte from the card waiting for the SR_RX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware. */ Loading @@ -1553,6 +1576,7 @@ static int get_wait_data(struct cosa_data *cosa) /* read data and return them */ if (cosa_getstatus(cosa) & SR_RX_RDY) { short r; r = cosa_getdata8(cosa); #if 0 pr_info("get_wait_data returning after %d retries\n", Loading @@ -1568,14 +1592,14 @@ static int get_wait_data(struct cosa_data *cosa) return -1; } /* * This routine puts the data byte to the card waiting for the SR_TX_RDY /* This routine puts the data byte to the card waiting for the SR_TX_RDY * bit to be set in a loop. It should be used in the exceptional cases * only (for example when resetting the card or downloading the firmware). */ static int put_wait_data(struct cosa_data *cosa, int data) { int retries = 1000; while (--retries) { /* read data and return them */ if (cosa_getstatus(cosa) & SR_TX_RDY) { Loading @@ -1595,8 +1619,7 @@ static int put_wait_data(struct cosa_data *cosa, int data) return -1; } /* * The following routine puts the hexadecimal number into the SRP monitor /* The following routine puts the hexadecimal number into the SRP monitor * and verifies the proper echo of the sent bytes. Returns 0 on success, * negative number on failure (-1,-3,-5,-7) means that put_wait_data() failed, * (-2,-4,-6,-8) means that reading echo failed. Loading @@ -1623,11 +1646,9 @@ static int puthexnumber(struct cosa_data *cosa, int number) return 0; } /* ---------- Interrupt routines ---------- */ /* * There are three types of interrupt: /* There are three types of interrupt: * At the beginning of transmit - this handled is in tx_interrupt(), * at the beginning of receive - it is in rx_interrupt() and * at the end of transmit/receive - it is the eot_interrupt() function. Loading @@ -1641,8 +1662,7 @@ static int puthexnumber(struct cosa_data *cosa, int number) * It's time to use the bottom half :-( */ /* * Transmit interrupt routine - called when COSA is willing to obtain /* Transmit interrupt routine - called when COSA is willing to obtain * data from the OS. The most tricky part of the routine is selection * of channel we (OS) want to send packet for. For SRP we should probably * use the round-robin approach. The newer COSA firmwares have a simple Loading @@ -1668,6 +1688,7 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) if (!test_bit(IRQBIT, &cosa->rxtx)) { /* flow control, see the comment above */ int i = 0; if (!cosa->txbitmap) { pr_warn("%s: No channel wants data in TX IRQ. Expect DMA timeout.\n", cosa->name); Loading @@ -1683,7 +1704,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) cosa->txchan = 0; if (!(cosa->txbitmap & (1 << cosa->txchan))) continue; if (~status & (1 << (cosa->txchan+DRIVER_TXMAP_SHIFT))) if (~status & (1 << (cosa->txchan + DRIVER_TXMAP_SHIFT))) break; /* in second pass, accept first ready-to-TX channel */ if (i > cosa->nchannels) { Loading @@ -1699,7 +1721,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) cosa->txsize = cosa->chan[cosa->txchan].txsize; if (cosa_dma_able(cosa->chan + cosa->txchan, cosa->chan[cosa->txchan].txbuf, cosa->txsize)) { cosa->chan[cosa->txchan].txbuf, cosa->txsize)) { cosa->txbuf = cosa->chan[cosa->txchan].txbuf; } else { memcpy(cosa->bouncebuf, cosa->chan[cosa->txchan].txbuf, Loading Loading @@ -1739,8 +1762,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) | (cosa->txsize & 0x1fff)); #ifdef DEBUG_IO debug_status_out(cosa, SR_TX_INT_ENA); debug_data_out(cosa, ((cosa->txchan<<13) & 0xe000) | (cosa->txsize & 0x1fff)); debug_data_out(cosa, ((cosa->txchan << 13) & 0xe000) | (cosa->txsize & 0x1fff)); debug_data_in(cosa, cosa_getdata8(cosa)); debug_status_out(cosa, 0); #else Loading @@ -1752,11 +1775,13 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) if (cosa->busmaster) { unsigned long addr = virt_to_bus(cosa->txbuf); int count = 0; pr_info("busmaster IRQ\n"); while (!(cosa_getstatus(cosa) & SR_TX_RDY)) { count++; udelay(10); if (count > 1000) break; if (count > 1000) break; } pr_info("status %x\n", cosa_getstatus(cosa)); pr_info("ready after %d loops\n", count); Loading @@ -1765,7 +1790,8 @@ static inline void tx_interrupt(struct cosa_data *cosa, int status) count = 0; while (!(cosa_getstatus(cosa) & SR_TX_RDY)) { count++; if (count > 1000) break; if (count > 1000) break; udelay(10); } pr_info("ready after %d loops\n", count); Loading Loading @@ -1859,11 +1885,11 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status) disable_dma(cosa->dma); clear_dma_ff(cosa->dma); set_dma_mode(cosa->dma, DMA_MODE_READ); if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) { if (cosa_dma_able(cosa->rxchan, cosa->rxbuf, cosa->rxsize & 0x1fff)) set_dma_addr(cosa->dma, virt_to_bus(cosa->rxbuf)); } else { else set_dma_addr(cosa->dma, virt_to_bus(cosa->bouncebuf)); } set_dma_count(cosa->dma, (cosa->rxsize & 0x1fff)); enable_dma(cosa->dma); release_dma_lock(flags); Loading @@ -1882,6 +1908,7 @@ static inline void rx_interrupt(struct cosa_data *cosa, int status) static inline void eot_interrupt(struct cosa_data *cosa, int status) { unsigned long flags, flags1; spin_lock_irqsave(&cosa->lock, flags); flags1 = claim_dma_lock(); disable_dma(cosa->dma); Loading @@ -1889,6 +1916,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) release_dma_lock(flags1); if (test_bit(TXBIT, &cosa->rxtx)) { struct channel_data *chan = cosa->chan + cosa->txchan; if (chan->tx_done) if (chan->tx_done(chan, cosa->txsize)) clear_bit(chan->num, &cosa->txbitmap); Loading @@ -1896,6 +1924,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) #ifdef DEBUG_DATA { int i; pr_info("cosa%dc%d: done rx(0x%x)", cosa->num, cosa->rxchan->num, cosa->rxsize); for (i = 0; i < cosa->rxsize; i++) Loading @@ -1914,8 +1943,7 @@ static inline void eot_interrupt(struct cosa_data *cosa, int status) } else { pr_notice("cosa%d: unexpected EOT interrupt\n", cosa->num); } /* * Clear the RXBIT, TXBIT and IRQBIT (the latest should be /* Clear the RXBIT, TXBIT and IRQBIT (the latest should be * cleared anyway). We should do it as soon as possible * so that we can tell the COSA we are done and to give it a time * for recovery. Loading Loading @@ -1968,10 +1996,8 @@ static irqreturn_t cosa_interrupt(int irq, void *cosa_) return IRQ_HANDLED; } /* ---------- I/O debugging routines ---------- */ /* * These routines can be used to monitor COSA/SRP I/O and to printk() /* These routines can be used to monitor COSA/SRP I/O and to printk() * the data being transferred on the data and status I/O port in a * readable way. */ Loading @@ -1980,6 +2006,7 @@ static irqreturn_t cosa_interrupt(int irq, void *cosa_) static void debug_status_in(struct cosa_data *cosa, int status) { char *s; switch (status & SR_CMD_FROM_SRP_MASK) { case SR_UP_REQUEST: s = "RX_REQ"; Loading