You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
559 lines
18 KiB
559 lines
18 KiB
/*
|
|
* InsydeFlash Linux Driver
|
|
* File: isfl_drv.c
|
|
*
|
|
* Copyright (C) 2005 - 2015 Insyde Software Corp.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/version.h>
|
|
#include <asm/uaccess.h>
|
|
#include <asm/page.h>
|
|
#include <asm/io.h>
|
|
|
|
typedef struct _PageAlloc {
|
|
unsigned long addr;
|
|
unsigned long PhyAddr;
|
|
unsigned int order;
|
|
}PageAlloc_t;
|
|
|
|
#include "isfl.h"
|
|
|
|
#define MAX_8042_LOOPS 100000
|
|
|
|
#define ISFL_VERSION "0.0.05"
|
|
#define DRIVER_AUTHOR "Howard Ho <howard.ho@insydesw.com.tw>"
|
|
#define DRIVER_DESC "InsydeFlash tool for Linux"
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
static dev_t first;
|
|
static struct cdev c_dev;
|
|
static struct class *isfl_class;
|
|
#endif
|
|
|
|
static int Device_Open = 0;
|
|
static unsigned char *isfl_mem_buff;
|
|
static unsigned char *g_ppbyMemoryBuffer[MAX_MEMORY_BLOCK_COUNT] = {0};
|
|
static PageAlloc_t g_ppbyMemoryBuffer2[MAX_MEMORY_BLOCK_COUNT] = {{0,0}};
|
|
static int isfl_open(struct inode *, struct file *);
|
|
static int isfl_release(struct inode *, struct file *);
|
|
static ssize_t isfl_read(struct file *, char *, size_t, loff_t *);
|
|
static ssize_t isfl_write(struct file *file, const char *buff, size_t length, loff_t *offset);
|
|
//Flash will close kbc except show dialog
|
|
static void enable_a20_kbc(void);
|
|
static void disable_a20_kbc(void);
|
|
//static int empty_8042(void);
|
|
static inline void io_delay(void);
|
|
|
|
//Use this function to get the message from AP
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
static long isfl_ioctl(struct file *file, unsigned int num, unsigned long arg)
|
|
#else
|
|
static int isfl_ioctl(struct inode *inode, struct file *file, unsigned int num, unsigned long arg)
|
|
#endif
|
|
{
|
|
unsigned long isfl_phys_addr; // Assgin to SW SMI
|
|
struct _isfl *isfl;
|
|
int isfl_size = sizeof( struct _isfl );
|
|
unsigned char buf[isfl_size];
|
|
struct _mblock *pBlock;
|
|
int iBlockStructureSize = sizeof( struct _mblock );
|
|
unsigned char *pbyBlockBuffer;
|
|
unsigned char *ch;
|
|
int i;
|
|
unsigned long mbuf_index;
|
|
unsigned char *vir_addr = 0;
|
|
|
|
switch ( num )
|
|
{
|
|
case IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY:
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
get_user( buf[i], ( ch + i ) );
|
|
}
|
|
isfl = (struct _isfl *)buf;
|
|
|
|
mbuf_index = isfl->mbuf_paddr; // use mbuf_paddr to store mbuf_index to backward compatible
|
|
|
|
// Allocate memory buffer
|
|
g_ppbyMemoryBuffer2[mbuf_index].order = get_order( isfl->mbuf_size );
|
|
g_ppbyMemoryBuffer2[mbuf_index].addr = __get_free_pages( GFP_DMA32, g_ppbyMemoryBuffer2[mbuf_index].order );
|
|
g_ppbyMemoryBuffer[mbuf_index] = (unsigned char *)g_ppbyMemoryBuffer2[mbuf_index].addr;
|
|
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
|
|
{
|
|
//printk( KERN_INFO "isfl ioctl IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY allocate physical memory failed\n" );
|
|
return -1;
|
|
}
|
|
memset( g_ppbyMemoryBuffer[mbuf_index], 0, isfl->mbuf_size );
|
|
|
|
g_ppbyMemoryBuffer2[mbuf_index].PhyAddr = virt_to_phys( g_ppbyMemoryBuffer[mbuf_index] );
|
|
isfl->mbuf_paddr = g_ppbyMemoryBuffer2[mbuf_index].PhyAddr;
|
|
//printk( KERN_INFO "isfl alloc id=%d, PhyAddr=%x\n", mbuf_index, g_ppbyMemoryBuffer2[mbuf_index].PhyAddr );
|
|
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
put_user( buf[i], ( ch + i ) );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_FREE_MULTI_BLOCK_MEMORY:
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
get_user( buf[i], ( ch + i ) );
|
|
}
|
|
isfl = (struct _isfl *)buf;
|
|
|
|
mbuf_index = -1;
|
|
isfl_phys_addr = isfl->mbuf_paddr;
|
|
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
|
|
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
|
|
mbuf_index = i;
|
|
if (-1 == mbuf_index)
|
|
return -1;
|
|
|
|
// Free Memory
|
|
if ( g_ppbyMemoryBuffer[mbuf_index] != NULL )
|
|
{
|
|
free_pages( g_ppbyMemoryBuffer2[mbuf_index].addr, g_ppbyMemoryBuffer2[mbuf_index].order );
|
|
g_ppbyMemoryBuffer2[mbuf_index].addr = 0;
|
|
g_ppbyMemoryBuffer2[mbuf_index].order = 0;
|
|
g_ppbyMemoryBuffer[mbuf_index] = NULL;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_WRITE_TO_PHYSICAL:
|
|
ch = (unsigned char *)arg;
|
|
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
|
|
if ( pbyBlockBuffer == NULL )
|
|
{
|
|
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with NULL parameter\n" );
|
|
return -1;
|
|
}
|
|
for ( i = 0; i < iBlockStructureSize; i++ )
|
|
{
|
|
get_user( pbyBlockBuffer[i], ( ch + i ) );
|
|
}
|
|
pBlock = (struct _mblock *)pbyBlockBuffer;
|
|
isfl_phys_addr = pBlock->mbuf_PhyAddr;
|
|
//printk( KERN_INFO "isfl write PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
|
|
//printk( KERN_INFO "isfl write len=%x\n", pBlock->mbuf_size);
|
|
//printk( KERN_INFO "isfl write PhyAddr=%x\n", isfl_phys_addr );
|
|
|
|
mbuf_index = -1;
|
|
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
|
|
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
|
|
mbuf_index = i;
|
|
if (-1 == mbuf_index)
|
|
{
|
|
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with wrong physcial address\n" );
|
|
return -1;
|
|
}
|
|
|
|
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
|
|
{
|
|
//printk( KERN_INFO "isfl ioctl IOCTL_WRITE_TO_PHYSICAL with physcial address didn't allocate\n" );
|
|
return -1;
|
|
}
|
|
|
|
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
|
|
{
|
|
g_ppbyMemoryBuffer[mbuf_index][i] = pBlock->pbyBuffer[i];
|
|
}
|
|
|
|
kfree( pbyBlockBuffer );
|
|
break;
|
|
|
|
case IOCTL_READ_FROME_PHYSICAL:
|
|
ch = (unsigned char *)arg;
|
|
pbyBlockBuffer = kmalloc( iBlockStructureSize, GFP_DMA );
|
|
if ( pbyBlockBuffer == NULL )
|
|
{
|
|
return -1;
|
|
}
|
|
for ( i = 0; i < iBlockStructureSize; i++ )
|
|
{
|
|
get_user( pbyBlockBuffer[i], ( ch + i ) );
|
|
}
|
|
pBlock = (struct _mblock *)pbyBlockBuffer;
|
|
isfl_phys_addr = pBlock->mbuf_PhyAddr;
|
|
//printk( KERN_INFO "isfl read PhyAddr=%x\n", pBlock->mbuf_PhyAddr);
|
|
//printk( KERN_INFO "isfl read len=%x\n", pBlock->mbuf_size);
|
|
//printk( KERN_INFO "isfl read PhyAddr=%x\n", isfl_phys_addr );
|
|
|
|
mbuf_index = -1;
|
|
for (i = 0; i < MAX_MEMORY_BLOCK_COUNT; i++)
|
|
if (isfl_phys_addr == g_ppbyMemoryBuffer2[i].PhyAddr)
|
|
mbuf_index = i;
|
|
if (-1 == mbuf_index)
|
|
return -1;
|
|
|
|
if ( g_ppbyMemoryBuffer[mbuf_index] == NULL )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
for ( i = 0; i < pBlock->mbuf_size && i < ISFB_FLASH_SIZE; i++ )
|
|
{
|
|
pBlock->pbyBuffer[i] = g_ppbyMemoryBuffer[mbuf_index][i];
|
|
}
|
|
|
|
for ( i = 0; i < iBlockStructureSize; i++ )
|
|
{
|
|
put_user( pbyBlockBuffer[i], ( ch + i ) );
|
|
}
|
|
kfree( pbyBlockBuffer );
|
|
break;
|
|
|
|
case IOCTL_ALLOCATE_MEMORY:
|
|
//printk( KERN_INFO "IOCTL_ALLOCATE_MEMORY\n" );
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
get_user( buf[i], ( ch + i ) );
|
|
//printk( KERN_INFO "%02X ", buf[i] );
|
|
}
|
|
//printk( KERN_INFO "\n" );
|
|
isfl = (struct _isfl *)buf;
|
|
|
|
/* Allocate a memory buffer */
|
|
//printk( KERN_INFO "Allocate Memory size: %ld\n", isfl->mbuf_size );
|
|
|
|
isfl_mem_buff = kmalloc( isfl->mbuf_size, GFP_DMA ); //20090605 - Keep OS allocate under 16M
|
|
if ( isfl_mem_buff == NULL )
|
|
{
|
|
return -1;
|
|
}
|
|
memset( isfl_mem_buff, 0, isfl->mbuf_size );
|
|
|
|
isfl_phys_addr = virt_to_phys( isfl_mem_buff );
|
|
//printk( KERN_INFO "isfl: virt: 0x%p phys: 0x%08lx\n", isfl_mem_buff, isfl_phys_addr );
|
|
isfl->mbuf_paddr = isfl_phys_addr;
|
|
//printk( KERN_INFO "Memory physical addr: 0x%08lx\n", isfl->mbuf_paddr );
|
|
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
//printk( KERN_INFO "%02X ", buf[i] );
|
|
put_user( buf[i], ( ch + i ) );
|
|
}
|
|
//printk( KERN_INFO "\n" );
|
|
break;
|
|
|
|
case IOCTL_FREE_MEMORY:
|
|
//printk( KERN_INFO "IOCTL_FREE_MEMORY\n" );
|
|
//kfree( isfl_mem_buff );
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
get_user( buf[i], ( ch + i ) );
|
|
//printk( KERN_INFO "%02X ", buf[i] );
|
|
}
|
|
|
|
isfl = (struct _isfl *)buf;
|
|
isfl_phys_addr = isfl->mbuf_paddr;
|
|
|
|
vir_addr = (unsigned char*)phys_to_virt (isfl_phys_addr);
|
|
if ( vir_addr == NULL )
|
|
{
|
|
//printk( KERN_INFO "isfl ioctl IOCTL_READ_FROME_PHYSICAL with failure from phys_to_virt\n" );
|
|
return -1;
|
|
}
|
|
|
|
kfree (vir_addr);
|
|
break;
|
|
|
|
case IOCTL_GENERATE_SMI:
|
|
//printk( "IOCTL_GENERATE_SMI\n" );
|
|
|
|
//printk( "size of (struct _isfl): %d\n", isfl_size );
|
|
|
|
/* Get SMI initial data */
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
get_user( buf[i], ( ch + i ) );
|
|
//printk( "%02X ", buf[i] );
|
|
}
|
|
//printk( "\n" );
|
|
isfl = (struct _isfl *)buf;
|
|
//printk( "SMI IO port: 0x%lX\n", isfl->smi_io );
|
|
//printk( "Bufer Size: %ld\n", isfl->mbuf_size );
|
|
break;
|
|
|
|
case IOCTL_GET_SMI_STATUS:
|
|
//printk( "IOCTL_GET_SMI_STATUS\n" );
|
|
|
|
ch = (unsigned char *)arg;
|
|
for ( i = 0; i < isfl_size; i++ )
|
|
{
|
|
//printk( "%02X ", buf[i] );
|
|
put_user( buf[i], ( ch + i ) );
|
|
}
|
|
//printk( "\n" );
|
|
break;
|
|
|
|
case IOCTL_ENABLE_KBC:
|
|
enable_a20_kbc();
|
|
break;
|
|
|
|
case IOCTL_DISABLE_KBC:
|
|
disable_a20_kbc();
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
static DEFINE_MUTEX( isflash_mutex );
|
|
static long isfl_ioctl_unlock(struct file *fp, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret;
|
|
mutex_lock( &isflash_mutex );
|
|
ret = isfl_ioctl( fp, cmd, arg );
|
|
mutex_unlock( &isflash_mutex );
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static struct file_operations fops = {
|
|
.read = isfl_read,
|
|
.write = isfl_write,
|
|
.open = isfl_open,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
.unlocked_ioctl = isfl_ioctl_unlock,
|
|
.compat_ioctl = isfl_ioctl_unlock,
|
|
#else
|
|
.ioctl = isfl_ioctl,
|
|
#endif
|
|
.release = isfl_release
|
|
};
|
|
|
|
//Driver initialization
|
|
static int __init init_isfl(void)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
first = MKDEV(DEVICE_MAJOR_NUM, 0);
|
|
if (register_chrdev_region(first, 1, DEVICE_NAME) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
if ((isfl_class = class_create(THIS_MODULE, DEVICE_NAME)) == NULL)
|
|
{
|
|
unregister_chrdev_region(first, 1);
|
|
return -1;
|
|
}
|
|
if (device_create(isfl_class, NULL, first, NULL, "isfl") == NULL)
|
|
{
|
|
class_destroy(isfl_class);
|
|
unregister_chrdev_region(first, 1);
|
|
return -1;
|
|
}
|
|
cdev_init(&c_dev, &fops);
|
|
if (cdev_add(&c_dev, first, 1) == -1)
|
|
{
|
|
device_destroy(isfl_class, first);
|
|
class_destroy(isfl_class);
|
|
unregister_chrdev_region(first, 1);
|
|
return -1;
|
|
}
|
|
#else
|
|
int ret = register_chrdev(DEVICE_MAJOR_NUM, DEVICE_NAME, &fops);
|
|
if ( ret < 0 )
|
|
{
|
|
//printk( KERN_ALERT "Registering char device %s failed with %d\n", DEVICE_NAME, ret );
|
|
return ret;
|
|
}
|
|
#endif
|
|
//printk ("<0> IOCTL_ALLOCATE_MEMORY = %lx\n", IOCTL_ALLOCATE_MEMORY );
|
|
//printk ("<0> IOCTL_FREE_MEMORY = %lx\n", IOCTL_FREE_MEMORY );
|
|
//printk ("<0> IOCTL_WRITE_MEMORY = %lx\n", IOCTL_WRITE_MEMORY );
|
|
//printk ("<0> IOCTL_READ_MEMORY = %lx\n", IOCTL_READ_MEMORY );
|
|
//printk ("<0> IOCTL_GENERATE_SMI = %lx\n", IOCTL_GENERATE_SMI );
|
|
//printk ("<0> IOCTL_GET_SMI_STATUS = %lx\n", IOCTL_GET_SMI_STATUS );
|
|
//printk ("<0> IOCTL_DISABLE_KBC = %lx\n", IOCTL_DISABLE_KBC );
|
|
//printk ("<0> IOCTL_ENABLE_KBC = %lx\n", IOCTL_ENABLE_KBC );
|
|
//printk ("<0> IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_ALLOCATE_MULTI_BLOCK_MEMORY);
|
|
//printk ("<0> IOCTL_FREE_MULTI_BLOCK_MEMORY = %lx\n", IOCTL_FREE_MULTI_BLOCK_MEMORY );
|
|
//printk ("<0> IOCTL_COPY_BUFFER_TO_MEMORY = %lx\n", IOCTL_COPY_BUFFER_TO_MEMORY );
|
|
//printk ("<0> = %d\n", MAJOR(first));
|
|
return 0;
|
|
}
|
|
|
|
static void __exit cleanup_isfl(void)
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 36 )
|
|
cdev_del(&c_dev);
|
|
device_destroy(isfl_class, first);
|
|
class_destroy(isfl_class);
|
|
unregister_chrdev_region(first, 1);
|
|
#else
|
|
unregister_chrdev( DEVICE_MAJOR_NUM, DEVICE_NAME );
|
|
#endif
|
|
}
|
|
|
|
static int isfl_open(struct inode *inode, struct file *file)
|
|
{
|
|
if ( Device_Open )
|
|
{
|
|
return -EBUSY;
|
|
}
|
|
|
|
Device_Open++;
|
|
try_module_get( THIS_MODULE );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int isfl_release(struct inode *inode, struct file *file)
|
|
{
|
|
Device_Open--;
|
|
|
|
module_put( THIS_MODULE );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t isfl_read(struct file *filp, char *buff, size_t len, loff_t *offset)
|
|
{
|
|
int bytes_read = 0;
|
|
|
|
//printk( KERN_INFO "isfl_read\n" );
|
|
|
|
if ( isfl_mem_buff == 0 )
|
|
{
|
|
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
|
|
return -EFAULT;
|
|
}
|
|
|
|
while ( len )
|
|
{
|
|
put_user( isfl_mem_buff[bytes_read++], buff++ );
|
|
len--;
|
|
}
|
|
|
|
//printk( KERN_INFO "isfl_read: read %d bytes, left %d\n", bytes_read, len );
|
|
return bytes_read;
|
|
}
|
|
|
|
static ssize_t isfl_write(struct file *file, const char *buff, size_t len, loff_t *offset)
|
|
{
|
|
int i;
|
|
|
|
//printk( KERN_INFO "isfl_write\n" );
|
|
|
|
if ( isfl_mem_buff == 0 )
|
|
{
|
|
//printk( KERN_ERR "Memory buffer in kernel space that maybe not be allocated!\n" );
|
|
return -EFAULT;
|
|
}
|
|
|
|
//printk( KERN_INFO "isfl_write: len: %d\n", len );
|
|
|
|
for ( i = 0; i < len && i < ISFB_FLASH_SIZE; i++ )
|
|
{
|
|
get_user( *( isfl_mem_buff + i ), buff + i );
|
|
}
|
|
#if 0
|
|
for ( i = 0; i < len; i++ )
|
|
{
|
|
if ( ( i + 1 ) % 8 )
|
|
{
|
|
//printk( "%02x ", isfl_mem_buff[i] );
|
|
}
|
|
else if ( ( i + 1 ) %16 )
|
|
{
|
|
//printk( "%02x ", isfl_mem_buff[i] );
|
|
}
|
|
else
|
|
{
|
|
//printk( "%02x\n", isfl_mem_buff[i] );
|
|
}
|
|
}
|
|
//printk( "\n" );
|
|
#endif
|
|
return i;
|
|
}
|
|
|
|
static int empty_8042(void)
|
|
{
|
|
u8 status;
|
|
int loops = MAX_8042_LOOPS;
|
|
|
|
while ( loops-- )
|
|
{
|
|
io_delay();
|
|
|
|
status = inb( 0x64 );
|
|
if ( status & 1 )
|
|
{
|
|
/* Read and discard input data */
|
|
io_delay();
|
|
(void)inb( 0x60 );
|
|
}
|
|
else if ( !( status & 2 ) )
|
|
{
|
|
/* Buffers empty, finished! */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void enable_a20_kbc(void)
|
|
{
|
|
empty_8042();
|
|
|
|
outb( 0x60, 0x64 ); // Command write
|
|
empty_8042();
|
|
|
|
outb( 0x47, 0x60 ); // A20 on
|
|
empty_8042();
|
|
}
|
|
|
|
static void disable_a20_kbc(void)
|
|
{
|
|
empty_8042();
|
|
|
|
outb( 0x60, 0x64 ); // Command write
|
|
empty_8042();
|
|
|
|
outb( 0x74, 0x60 ); // A20 on
|
|
empty_8042();
|
|
}
|
|
|
|
static inline void io_delay(void)
|
|
{
|
|
const u16 DELAY_PORT = 0x80;
|
|
asm volatile( "outb %%al,%0" : : "dN" (DELAY_PORT) );
|
|
}
|
|
|
|
module_init( init_isfl );
|
|
module_exit( cleanup_isfl );
|
|
|
|
MODULE_AUTHOR( DRIVER_AUTHOR );
|
|
MODULE_DESCRIPTION( DRIVER_DESC );
|
|
MODULE_VERSION( ISFL_VERSION );
|
|
MODULE_SUPPORTED_DEVICE( "Insyde EFI BIOS Flash" );
|
|
MODULE_LICENSE( "GPL" );
|