📄 cosa.c
字号:
char idstring[COSA_MAX_ID_STRING]; if (cosa->usage > 1) printk(KERN_INFO "cosa%d: WARNING: reset requested with cosa->usage > 1 (%d). Odd things may happen.\n", cosa->num, cosa->usage); cosa->firmware_status &= ~(COSA_FW_RESET|COSA_FW_START); if (cosa_reset_and_read_id(cosa, idstring) < 0) { printk(KERN_NOTICE "cosa%d: reset failed\n", cosa->num); return -EIO; } printk(KERN_INFO "cosa%d: resetting device: %s\n", cosa->num, idstring); cosa->firmware_status |= COSA_FW_RESET; return 0;}/* High-level function to download data into COSA memory. Calls download() */static inline int cosa_download(struct cosa_data *cosa, void __user *arg){ struct cosa_download d; int i; if (cosa->usage > 1) printk(KERN_INFO "%s: WARNING: download of microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n", cosa->name, cosa->usage); if (!(cosa->firmware_status & COSA_FW_RESET)) { printk(KERN_NOTICE "%s: reset the card first (status %d).\n", cosa->name, cosa->firmware_status); return -EPERM; } if (copy_from_user(&d, arg, sizeof(d))) return -EFAULT; if (d.addr < 0 || d.addr > COSA_MAX_FIRMWARE_SIZE) return -EINVAL; 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); i = download(cosa, d.code, d.len, d.addr); if (i < 0) { printk(KERN_NOTICE "cosa%d: microcode download failed: %d\n", cosa->num, i); return -EIO; } printk(KERN_INFO "cosa%d: downloading microcode - 0x%04x bytes at 0x%04x\n", cosa->num, d.len, d.addr); cosa->firmware_status |= COSA_FW_RESET|COSA_FW_DOWNLOAD; return 0;}/* High-level function to read COSA memory. Calls readmem() */static inline int cosa_readmem(struct cosa_data *cosa, void __user *arg){ struct cosa_download d; int i; if (cosa->usage > 1) printk(KERN_INFO "cosa%d: WARNING: readmem requested with " "cosa->usage > 1 (%d). Odd things may happen.\n", cosa->num, cosa->usage); if (!(cosa->firmware_status & COSA_FW_RESET)) { printk(KERN_NOTICE "%s: reset the card first (status %d).\n", cosa->name, cosa->firmware_status); return -EPERM; } if (copy_from_user(&d, arg, sizeof(d))) return -EFAULT; /* If something fails, force the user to reset the card */ cosa->firmware_status &= ~COSA_FW_RESET; i = readmem(cosa, d.code, d.len, d.addr); if (i < 0) { printk(KERN_NOTICE "cosa%d: reading memory failed: %d\n", cosa->num, i); return -EIO; } printk(KERN_INFO "cosa%d: reading card memory - 0x%04x bytes at 0x%04x\n", cosa->num, d.len, d.addr); cosa->firmware_status |= COSA_FW_RESET; return 0;}/* High-level function to start microcode. Calls startmicrocode(). */static inline int cosa_start(struct cosa_data *cosa, int address){ int i; if (cosa->usage > 1) printk(KERN_INFO "cosa%d: WARNING: start microcode requested with cosa->usage > 1 (%d). Odd things may happen.\n", cosa->num, cosa->usage); if ((cosa->firmware_status & (COSA_FW_RESET|COSA_FW_DOWNLOAD)) != (COSA_FW_RESET|COSA_FW_DOWNLOAD)) { printk(KERN_NOTICE "%s: download the microcode and/or reset the card first (status %d).\n", cosa->name, cosa->firmware_status); return -EPERM; } cosa->firmware_status &= ~COSA_FW_RESET; if ((i=startmicrocode(cosa, address)) < 0) { printk(KERN_NOTICE "cosa%d: start microcode at 0x%04x failed: %d\n", cosa->num, address, i); return -EIO; } printk(KERN_INFO "cosa%d: starting microcode at 0x%04x\n", cosa->num, address); cosa->startaddr = address; cosa->firmware_status |= COSA_FW_START; return 0;} /* Buffer of size at least COSA_MAX_ID_STRING is expected */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;}/* Buffer of size at least COSA_MAX_ID_STRING is expected */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){ void __user *argp = (void __user *)arg; switch(cmd) { case COSAIORSET: /* Reset the device */ if (!capable(CAP_NET_ADMIN)) return -EACCES; return cosa_reset(cosa); case COSAIOSTRT: /* Start the firmware */ if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_start(cosa, arg); case COSAIODOWNLD: /* Download the firmware */ if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_download(cosa, argp); case COSAIORMEM: if (!capable(CAP_SYS_RAWIO)) return -EACCES; return cosa_readmem(cosa, argp); case COSAIORTYPE: return cosa_gettype(cosa, argp); case COSAIORIDSTR: return cosa_getidstr(cosa, argp); case COSAIONRCARDS: return nr_cards; case COSAIONRCHANS: return cosa->nchannels; case COSAIOBMSET: if (!capable(CAP_SYS_RAWIO)) return -EACCES; if (is_8bit(cosa)) return -EINVAL; if (arg != COSA_BM_OFF && arg != COSA_BM_ON) return -EINVAL; cosa->busmaster = arg; return 0; case COSAIOBMGET: return cosa->busmaster; } return -ENOIOCTLCMD;}static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){ int rv; struct channel_data *chan = dev->priv; rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data); if (rv == -ENOIOCTLCMD) { return sppp_do_ioctl(dev, ifr, cmd); } return rv;}static int cosa_chardev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct channel_data *channel = file->private_data; struct cosa_data *cosa = channel->cosa; return cosa_ioctl_common(cosa, channel, cmd, arg);}/*---------- HW layer interface ---------- *//* * 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){ struct cosa_data *cosa = chan->cosa; if (!test_and_set_bit(chan->num, &cosa->rxbitmap)) put_driver_status(cosa);}static void cosa_disable_rx(struct channel_data *chan){ struct cosa_data *cosa = chan->cosa; if (test_and_clear_bit(chan->num, &cosa->rxbitmap)) put_driver_status(cosa);}/* * 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. */static int cosa_start_tx(struct channel_data *chan, char *buf, int len){ struct cosa_data *cosa = chan->cosa; unsigned long flags;#ifdef DEBUG_DATA int i; printk(KERN_INFO "cosa%dc%d: starting tx(0x%x)", chan->cosa->num, chan->num, len); for (i=0; i<len; i++) printk(" %02x", buf[i]&0xff); printk("\n");#endif spin_lock_irqsave(&cosa->lock, flags); chan->txbuf = buf; chan->txsize = len; if (len > COSA_MTU) chan->txsize = COSA_MTU; spin_unlock_irqrestore(&cosa->lock, flags); /* Tell the firmware we are ready */ set_bit(chan->num, &cosa->txbitmap); put_driver_status(cosa); return 0;}static void put_driver_status(struct cosa_data *cosa){ unsigned long flags; int status; spin_lock_irqsave(&cosa->lock, flags); status = (cosa->rxbitmap ? DRIVER_RX_READY : 0) | (cosa->txbitmap ? DRIVER_TX_READY : 0) | (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT) &DRIVER_TXMAP_MASK : 0); if (!cosa->rxtx) { if (cosa->rxbitmap|cosa->txbitmap) { if (!cosa->enabled) { cosa_putstatus(cosa, SR_RX_INT_ENA);#ifdef DEBUG_IO debug_status_out(cosa, SR_RX_INT_ENA);#endif cosa->enabled = 1; } } else if (cosa->enabled) { cosa->enabled = 0; cosa_putstatus(cosa, 0);#ifdef DEBUG_IO debug_status_out(cosa, 0);#endif } cosa_putdata8(cosa, status);#ifdef DEBUG_IO debug_data_cmd(cosa, status);#endif } spin_unlock_irqrestore(&cosa->lock, flags);}static void put_driver_status_nolock(struct cosa_data *cosa){ int status; status = (cosa->rxbitmap ? DRIVER_RX_READY : 0) | (cosa->txbitmap ? DRIVER_TX_READY : 0) | (cosa->txbitmap? ~(cosa->txbitmap<<DRIVER_TXMAP_SHIFT) &DRIVER_TXMAP_MASK : 0); if (cosa->rxbitmap|cosa->txbitmap) { cosa_putstatus(cosa, SR_RX_INT_ENA);#ifdef DEBUG_IO debug_status_out(cosa, SR_RX_INT_ENA);#endif cosa->enabled = 1; } else { cosa_putstatus(cosa, 0);#ifdef DEBUG_IO debug_status_out(cosa, 0);#endif cosa->enabled = 0; } cosa_putdata8(cosa, status);#ifdef DEBUG_IO debug_data_cmd(cosa, status);#endif}/* * 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. */static void cosa_kick(struct cosa_data *cosa){ unsigned long flags, flags1; char *s = "(probably) IRQ"; if (test_bit(RXBIT, &cosa->rxtx)) s = "RX DMA"; if (test_bit(TXBIT, &cosa->rxtx)) s = "TX DMA"; printk(KERN_INFO "%s: %s timeout - restarting.\n", cosa->name, s); spin_lock_irqsave(&cosa->lock, flags); cosa->rxtx = 0; flags1 = claim_dma_lock(); disable_dma(cosa->dma); clear_dma_ff(cosa->dma); release_dma_lock(flags1); /* FIXME: Anything else? */ udelay(100); cosa_putstatus(cosa, 0); udelay(100); (void) cosa_getdata8(cosa); udelay(100); cosa_putdata8(cosa, 0); udelay(100); put_driver_status_nolock(cosa); spin_unlock_irqrestore(&cosa->lock, flags);}/* * 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. */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) { if (count++ < 5) printk(KERN_INFO "%s: packet spanning a 64k boundary\n", chan->name); return 0; } return 1;}/* ---------- The SRP/COSA ROM monitor functions ---------- *//* * 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 * by a single space. Monitor has to reply with a space. Now the download * begins. After the download monitor replies with "\r\n." (CR LF dot). */static int download(struct cosa_data *cosa, const char __user *microcode, int length, int address){ int i; 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 (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;#ifndef SRP_DOWNLOAD_AT_BOOT if (get_user(c, microcode)) return -23; /* ??? */#else c = *microcode;#endif if (put_wait_data(cosa, c) == -1) return -20; 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 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. * 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 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. * 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). * * This routine is not needed during the normal operation and serves * for debugging purposes only. */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; while (length--) { char c; int i; if ((i=get_wait_data(cosa)) == -1) { printk (KERN_INFO "cosa: 0x%04x bytes remaining\n", length); return -11; } c=i;#if 1 if (put_user(c, microcode)) return -23; /* ??? */#else *microcode = c;#endif 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 0 printk(KERN_DEBUG "cosa%d: readmem completed.\n", cosa->num);#endif return 0;}/* * 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){ int i=0, id=0, prev=0, curr=0; /* Reset the card ... */ cosa_putstatus(cosa, 0); cosa_getdata8(cosa); cosa_putstatus(cosa, SR_RST);#ifdef MODULE current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/2);#else udelay(5*100000);#endif /* Disable all IRQs from the card */ cosa_putstatus(cosa, 0); /* * Try to read the ID string. The card then prints out the * identification string ended by the "\n\x2e". *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -