[PATCH] char/isicom: Firmware loading
Firmware loading via hotplug added. Cleanup firmware old-way fields in header file. Signed-off-by: Jiri Slaby <xslaby@fi.muni.cz> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
9ac0948b20
commit
e65c1db19f
|
@ -112,6 +112,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
@ -120,7 +121,6 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -175,8 +175,6 @@ static struct tty_driver *isicom_normal;
|
|||
static struct timer_list tx;
|
||||
static char re_schedule = 1;
|
||||
|
||||
static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
|
||||
static void isicom_tx(unsigned long _data);
|
||||
static void isicom_start(struct tty_struct *tty);
|
||||
|
||||
|
@ -384,233 +382,6 @@ static inline void kill_queue(struct isi_port *port, short queue)
|
|||
unlock_card(card);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Firmware loader driver specific routines. This needs to mostly die
|
||||
* and be replaced with request_firmware.
|
||||
*/
|
||||
|
||||
static struct file_operations ISILoad_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = ISILoad_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice isiloader_device = {
|
||||
ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops
|
||||
};
|
||||
|
||||
|
||||
static inline int WaitTillCardIsFree(unsigned long base)
|
||||
{
|
||||
unsigned long count=0;
|
||||
while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000));
|
||||
if (inw(base+0xe)&0x1)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ISILoad_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int card, i, j, signature, status, portcount = 0;
|
||||
unsigned long t, base;
|
||||
u16 word_count;
|
||||
bin_frame frame;
|
||||
void __user *argp = (void __user *)arg;
|
||||
/* exec_record exec_rec; */
|
||||
|
||||
if (get_user(card, (int __user *)argp))
|
||||
return -EFAULT;
|
||||
|
||||
if (card < 0 || card >= BOARD_COUNT)
|
||||
return -ENXIO;
|
||||
|
||||
base=isi_card[card].base;
|
||||
|
||||
if (base==0)
|
||||
return -ENXIO; /* disabled or not used */
|
||||
|
||||
switch(cmd) {
|
||||
case MIOCTL_RESET_CARD:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%lx ",card+1,base);
|
||||
|
||||
inw(base+0x8);
|
||||
|
||||
for (t=jiffies+HZ/100;time_before(jiffies, t););
|
||||
|
||||
outw(0,base+0x8); /* Reset */
|
||||
|
||||
for (j=1;j<=3;j++) {
|
||||
for (t=jiffies+HZ;time_before(jiffies, t););
|
||||
printk(".");
|
||||
}
|
||||
signature=(inw(base+0x4)) & 0xff;
|
||||
if (isi_card[card].isa) {
|
||||
|
||||
if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {
|
||||
#ifdef ISICOM_DEBUG
|
||||
printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
|
||||
#endif
|
||||
printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
portcount = inw(base+0x2);
|
||||
if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) {
|
||||
#ifdef ISICOM_DEBUG
|
||||
printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
|
||||
#endif
|
||||
printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
switch(signature) {
|
||||
case 0xa5:
|
||||
case 0xbb:
|
||||
case 0xdd:
|
||||
if (isi_card[card].isa)
|
||||
isi_card[card].port_count = 8;
|
||||
else {
|
||||
if (portcount == 4)
|
||||
isi_card[card].port_count = 4;
|
||||
else
|
||||
isi_card[card].port_count = 8;
|
||||
}
|
||||
isi_card[card].shift_count = 12;
|
||||
break;
|
||||
|
||||
case 0xcc: isi_card[card].port_count = 16;
|
||||
isi_card[card].shift_count = 11;
|
||||
break;
|
||||
|
||||
default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
|
||||
#ifdef ISICOM_DEBUG
|
||||
printk("Sig=0x%x\n",signature);
|
||||
#endif
|
||||
return -EIO;
|
||||
}
|
||||
printk("-Done\n");
|
||||
return put_user(signature,(unsigned __user *)argp);
|
||||
|
||||
case MIOCTL_LOAD_FIRMWARE:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&frame, argp, sizeof(bin_frame)))
|
||||
return -EFAULT;
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
outw(0xf0,base); /* start upload sequence */
|
||||
outw(0x00,base);
|
||||
outw((frame.addr), base); /* lsb of adderess */
|
||||
|
||||
word_count=(frame.count >> 1) + frame.count % 2;
|
||||
outw(word_count, base);
|
||||
InterruptTheCard(base);
|
||||
|
||||
for (i=0;i<=0x2f;i++); /* a wee bit of delay */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
if ((status=inw(base+0x4))!=0) {
|
||||
printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
|
||||
card+1, frame.addr, frame.count, status);
|
||||
return -EIO;
|
||||
}
|
||||
outsw(base, (void *) frame.bin_data, word_count);
|
||||
|
||||
InterruptTheCard(base);
|
||||
|
||||
for (i=0;i<=0x0f;i++); /* another wee bit of delay */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
if ((status=inw(base+0x4))!=0) {
|
||||
printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case MIOCTL_READ_FIRMWARE:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&frame, argp, sizeof(bin_header)))
|
||||
return -EFAULT;
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
outw(0xf1,base); /* start download sequence */
|
||||
outw(0x00,base);
|
||||
outw((frame.addr), base); /* lsb of adderess */
|
||||
|
||||
word_count=(frame.count >> 1) + frame.count % 2;
|
||||
outw(word_count+1, base);
|
||||
InterruptTheCard(base);
|
||||
|
||||
for (i=0;i<=0xf;i++); /* a wee bit of delay */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
if ((status=inw(base+0x4))!=0) {
|
||||
printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
|
||||
card+1, frame.addr, frame.count, status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
inw(base);
|
||||
insw(base, frame.bin_data, word_count);
|
||||
InterruptTheCard(base);
|
||||
|
||||
for (i=0;i<=0x0f;i++); /* another wee bit of delay */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
if ((status=inw(base+0x4))!=0) {
|
||||
printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (copy_to_user(argp, &frame, sizeof(bin_frame)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case MIOCTL_XFER_CTRL:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (WaitTillCardIsFree(base))
|
||||
return -EIO;
|
||||
|
||||
outw(0xf2, base);
|
||||
outw(0x800, base);
|
||||
outw(0x0, base);
|
||||
outw(0x0, base);
|
||||
InterruptTheCard(base);
|
||||
outw(0x0, base+0x4); /* for ISI4608 cards */
|
||||
|
||||
isi_card[card].status |= FIRMWARE_LOADED;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
#ifdef ISICOM_DEBUG
|
||||
printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd);
|
||||
#endif
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ISICOM Driver specific routines ...
|
||||
*
|
||||
|
@ -1927,6 +1698,175 @@ end:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static inline int WaitTillCardIsFree(u16 base)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
|
||||
while (!(inw(base + 0xe) & 0x1) && count++ < 100)
|
||||
msleep(5);
|
||||
|
||||
return !(inw(base + 0xe) & 0x1);
|
||||
}
|
||||
|
||||
static int __devinit load_firmware(struct pci_dev *pdev,
|
||||
const unsigned int index, const unsigned int signature)
|
||||
{
|
||||
struct isi_board *board = pci_get_drvdata(pdev);
|
||||
const struct firmware *fw;
|
||||
unsigned long base = board->base;
|
||||
unsigned int a;
|
||||
u16 word_count, status;
|
||||
int retval = -EIO;
|
||||
char *name;
|
||||
u8 *data;
|
||||
|
||||
struct stframe {
|
||||
u16 addr;
|
||||
u16 count;
|
||||
u8 data[0];
|
||||
} *frame;
|
||||
|
||||
switch (signature) {
|
||||
case 0xa5:
|
||||
name = "isi608.bin";
|
||||
break;
|
||||
case 0xbb:
|
||||
name = "isi608em.bin";
|
||||
break;
|
||||
case 0xcc:
|
||||
name = "isi616em.bin";
|
||||
break;
|
||||
case 0xdd:
|
||||
name = "isi4608.bin";
|
||||
break;
|
||||
case 0xee:
|
||||
name = "isi4616.bin";
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unknown signature.\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
retval = request_firmware(&fw, name, &pdev->dev);
|
||||
if (retval)
|
||||
goto end;
|
||||
|
||||
for (frame = (struct stframe *)fw->data;
|
||||
frame < (struct stframe *)(fw->data + fw->size);
|
||||
frame++) {
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
outw(0xf0, base); /* start upload sequence */
|
||||
outw(0x00, base);
|
||||
outw(frame->addr, base); /* lsb of address */
|
||||
|
||||
word_count = frame->count / 2 + frame->count % 2;
|
||||
outw(word_count, base);
|
||||
InterruptTheCard(base);
|
||||
|
||||
udelay(100); /* 0x2f */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
if ((status = inw(base + 0x4)) != 0) {
|
||||
dev_warn(&pdev->dev, "Card%d rejected load header:\n"
|
||||
"Address:0x%x\nCount:0x%x\nStatus:0x%x\n",
|
||||
index + 1, frame->addr, frame->count, status);
|
||||
goto errrelfw;
|
||||
}
|
||||
outsw(base, frame->data, word_count);
|
||||
|
||||
InterruptTheCard(base);
|
||||
|
||||
udelay(50); /* 0x0f */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
if ((status = inw(base + 0x4)) != 0) {
|
||||
dev_err(&pdev->dev, "Card%d got out of sync.Card "
|
||||
"Status:0x%x\n", index + 1, status);
|
||||
goto errrelfw;
|
||||
}
|
||||
}
|
||||
|
||||
retval = -EIO;
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
outw(0xf2, base);
|
||||
outw(0x800, base);
|
||||
outw(0x0, base);
|
||||
outw(0x0, base);
|
||||
InterruptTheCard(base);
|
||||
outw(0x0, base + 0x4); /* for ISI4608 cards */
|
||||
|
||||
/* XXX: should we test it by reading it back and comparing with original like
|
||||
* in load firmware package? */
|
||||
for (frame = (struct stframe*)fw->data;
|
||||
frame < (struct stframe*)(fw->data + fw->size);
|
||||
frame++) {
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
outw(0xf1, base); /* start download sequence */
|
||||
outw(0x00, base);
|
||||
outw(frame->addr, base); /* lsb of address */
|
||||
|
||||
word_count = (frame->count >> 1) + frame->count % 2;
|
||||
outw(word_count + 1, base);
|
||||
InterruptTheCard(base);
|
||||
|
||||
udelay(50); /* 0xf */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
if ((status = inw(base + 0x4)) != 0) {
|
||||
dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
|
||||
"Address:0x%x\nCount:0x%x\nStatus: 0x%x\n",
|
||||
index + 1, frame->addr, frame->count, status);
|
||||
goto errrelfw;
|
||||
}
|
||||
|
||||
data = kmalloc(word_count * 2, GFP_KERNEL);
|
||||
inw(base);
|
||||
insw(base, data, word_count);
|
||||
InterruptTheCard(base);
|
||||
|
||||
for (a = 0; a < frame->count; a++)
|
||||
if (data[a] != frame->data[a]) {
|
||||
kfree(data);
|
||||
dev_err(&pdev->dev, "Card%d, firmware upload "
|
||||
"failed\n", index + 1);
|
||||
goto errrelfw;
|
||||
}
|
||||
kfree(data);
|
||||
|
||||
udelay(50); /* 0xf */
|
||||
|
||||
if (WaitTillCardIsFree(base))
|
||||
goto errrelfw;
|
||||
|
||||
if ((status = inw(base + 0x4)) != 0) {
|
||||
dev_err(&pdev->dev, "Card%d verify got out of sync. "
|
||||
"Card Status:0x%x\n", index + 1, status);
|
||||
goto errrelfw;
|
||||
}
|
||||
}
|
||||
|
||||
board->status |= FIRMWARE_LOADED;
|
||||
retval = 0;
|
||||
|
||||
errrelfw:
|
||||
release_firmware(fw);
|
||||
end:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insmod can set static symbols so keep these static
|
||||
*/
|
||||
|
@ -1976,6 +1916,10 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
|
|||
if (retval < 0)
|
||||
goto errunri;
|
||||
|
||||
retval = load_firmware(pdev, index, signature);
|
||||
if (retval < 0)
|
||||
goto errunri;
|
||||
|
||||
return 0;
|
||||
|
||||
errunri:
|
||||
|
@ -2048,10 +1992,6 @@ static int __devinit isicom_setup(void)
|
|||
goto errtty;
|
||||
}
|
||||
|
||||
retval = misc_register(&isiloader_device);
|
||||
if (retval < 0)
|
||||
goto errpci;
|
||||
|
||||
init_timer(&tx);
|
||||
tx.expires = jiffies + 1;
|
||||
tx.data = 0;
|
||||
|
@ -2060,8 +2000,6 @@ static int __devinit isicom_setup(void)
|
|||
add_timer(&tx);
|
||||
|
||||
return 0;
|
||||
errpci:
|
||||
pci_unregister_driver(&isicom_driver);
|
||||
errtty:
|
||||
isicom_unregister_tty_driver();
|
||||
error:
|
||||
|
|
|
@ -4,46 +4,11 @@
|
|||
/*#define ISICOM_DEBUG*/
|
||||
/*#define ISICOM_DEBUG_DTR_RTS*/
|
||||
|
||||
|
||||
/*
|
||||
* Firmware Loader definitions ...
|
||||
*/
|
||||
|
||||
#define __MultiTech ('M'<<8)
|
||||
#define MIOCTL_LOAD_FIRMWARE (__MultiTech | 0x01)
|
||||
#define MIOCTL_READ_FIRMWARE (__MultiTech | 0x02)
|
||||
#define MIOCTL_XFER_CTRL (__MultiTech | 0x03)
|
||||
#define MIOCTL_RESET_CARD (__MultiTech | 0x04)
|
||||
|
||||
#define DATA_SIZE 16
|
||||
|
||||
typedef struct {
|
||||
unsigned short exec_segment;
|
||||
unsigned short exec_addr;
|
||||
} exec_record;
|
||||
|
||||
typedef struct {
|
||||
int board; /* Board to load */
|
||||
unsigned short addr;
|
||||
unsigned short count;
|
||||
} bin_header;
|
||||
|
||||
typedef struct {
|
||||
int board; /* Board to load */
|
||||
unsigned short addr;
|
||||
unsigned short count;
|
||||
unsigned short segment;
|
||||
unsigned char bin_data[DATA_SIZE];
|
||||
} bin_frame;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#define YES 1
|
||||
#define NO 0
|
||||
|
||||
#define ISILOAD_MISC_MINOR 155 /* /dev/isctl */
|
||||
#define ISILOAD_NAME "ISILoad"
|
||||
|
||||
/*
|
||||
* ISICOM Driver definitions ...
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue