2.1 驱动源代码
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <asm/arch/irqs.h>
#define DEVICE "TEST_IIC"
#define DATA_LEN 6
int major = 233;
int minor = 0;
void *R_GPECON,*R_GPEUP,*R_IICCON,*R_IICSTAT,*R_IICADD,*R_IICDS;
int IIC_open(struct inode *, struct file *);
int IIC_release(struct inode *, struct file *);
ssize_t IIC_read(struct file *file, char* buf, size_t count, loff_t *f_pos);
unsigned int IIC_poll(struct file* file, poll_table* wait);
irqreturn_t interrupt_handle( int irq, void* dev_id, struct pt_regs* regs );
static void address_map(void)
{
#define IIC_BASE (0x54000000)
#define IIC_GPECON ( IIC_BASE + 0x40 )
#define IIC_GPEUP ( IIC_BASE + 0x48 )
#define IIC_CON ( IIC_BASE + 0x0 )
#define IIC_STAT ( IIC_BASE + 0x4 )
#define IIC_ADDR ( IIC_BASE + 0x8 )
#define IIC_DS ( IIC_BASE + 0xC )
R_GPECON = ioremap(IIC_GPECON,4);
R_GPEUP = ioremap(IIC_GPEUP ,4);
R_IICCON = ioremap(IIC_CON ,4);
R_IICSTAT = ioremap(IIC_STAT ,4);
R_IICADD = ioremap(IIC_ADDR ,4);
R_IICDS = ioremap(IIC_DS ,4);
}
static void address_unmap(void)
{
iounmap( R_GPECON );
iounmap( R_GPEUP );
iounmap( R_IICCON );
iounmap( R_IICSTAT );
iounmap( R_IICADD );
iounmap( R_IICDS );
}
static struct file_operations fops = {
owner : THIS_MODULE,
read: IIC_read,
open: IIC_open,
release: IIC_release,
poll: IIC_poll,
};
struct IIC_dev{
wait_queue_head_t rq; // 读取等待队列
uint8_t *buffer;
uint32_t size;
uint32_t index;
struct semaphore sem;
struct cdev cdev;
};
struct IIC_dev *my_dev;
static int __init IIC_init(void)
{
// 1. 分配主设备号
dev_t devno = MKDEV( major, minor );
int ret = register_chrdev_region( devno, 1, DEVICE );
if( ret < 0 )
{
printk(KERN_DEBUG "register major number failed with %d/n", ret);
return ret;
}
printk(KERN_DEBUG "%s:register major number OK/n",DEVICE);
// 2. 注册设备
my_dev = kmalloc(sizeof(struct IIC_dev), GFP_KERNEL);
memset( my_dev, 0, sizeof(struct IIC_dev) );
cdev_init( &my_dev->cdev, &fops );
my_dev->cdev.ops = &fops;
my_dev->cdev.owner = THIS_MODULE;
ret = cdev_add( &my_dev->cdev, devno, 1 );
if( ret < 0 )
{
printk(KERN_DEBUG "register device failed with %d/n", ret);
return ret;
}
printk(KERN_DEBUG "%s:register device OK/n",DEVICE);
// 3. 分配本驱动要使用的内存
my_dev->index = 0;
my_dev->size = 128;
my_dev->buffer = kmalloc( my_dev->size, GFP_KERNEL );
if( NULL == my_dev->buffer )
{
printk(KERN_DEBUG "kmalloc failed/n");
return -ENOMEM;
}
printk(KERN_DEBUG "%s:kmalloc buffer OK/n",DEVICE);
// 4. 初始化信号量
init_MUTEX( &(my_dev->sem) );
printk(KERN_DEBUG "%s:init semaphore OK/n",DEVICE);
// 5. 初始化等待队列头
init_waitqueue_head(&my_dev->rq);
// 6. Remap IIC 寄存器
address_map();
// 7. 设置 s3c2410 IIC
unsigned int tmp = ioread32( R_GPEUP );
tmp |= 0xc000; //Pull-up disable
iowrite32( tmp, R_GPEUP );
tmp = ioread32( R_GPECON );
tmp |= 0xa0000000; //GPE15:IICSDA , GPE14:IICSCL
iowrite32( tmp, R_GPECON );
return 0;
}
static void __exit IIC_exit(void)
{
dev_t devno = MKDEV( major, minor );
// 以相反的顺序清除
address_unmap();
kfree( my_dev->buffer );
cdev_del( &my_dev->cdev );
kfree( my_dev );
printk(KERN_DEBUG "%s:kfree OK/n",DEVICE);
unregister_chrdev_region( devno, 1 );
printk(KERN_DEBUG "%s:unregister device OK/n",DEVICE);
}
static void set_slave_recv_mode(void)
{
iowrite8( 0xE2, R_IICCON ); // 使能ACK,使能中断
iowrite8( 0xAA, R_IICADD ); // 从器件地址
iowrite8( 0x10, R_IICSTAT); // 设置从器件接收模式
barrier(); // 强制写入寄存器
}
int IIC_open(struct inode *inode, struct file *file)
{
struct IIC_dev *dev = container_of(inode->i_cdev, struct IIC_dev, cdev);
file->private_data = dev;
if( down_interruptible(&dev->sem) )
return -ERESTARTSYS;
set_slave_recv_mode();
int ret = request_irq( IRQ_IIC, interrupt_handle,
SA_INTERRUPT, DEVICE, (void*)dev );
if( ret )
{
printk( KERN_INFO "I2C: can't get assigned irq %d/n", IRQ_IIC );
}
return 0;
}
int IIC_read(struct file *file, char* buf, size_t count, loff_t *f_pos)
{
struct IIC_dev *dev = file->private_data;
size_t val = DATA_LEN;
while( dev->index < val )
{
if( file->f_flags & O_NONBLOCK )
return -EAGAIN;
// 在这里准备睡眠,等待条件为真
if( wait_event_interruptible(dev->rq, (dev->index >= val)) )
return -ERESTARTSYS; // 返回非0表示被信号中断
}
if( copy_to_user(buf, dev->buffer, val) )
return -EFAULT;
memset( dev->buffer, 0, dev->size );
dev->index = 0;
set_slave_recv_mode();
return val;
}
int IIC_release(struct inode *inode, struct file *file)
{
struct IIC_dev *dev = file->private_data;
iowrite8( 0x0, R_IICCON );
iowrite8( 0x0, R_IICADD );
iowrite8( 0x0, R_IICSTAT);
barrier(); // 强制写入寄存器
memset( dev->buffer, 0, dev->size );
dev->index = 0;
free_irq( IRQ_IIC, NULL );
up(&dev->sem);
return 0;
}
unsigned int IIC_poll(struct file* file, poll_table* wait)
{
struct IIC_dev *dev = file->private_data;
unsigned int mask = 0, val = DATA_LEN;
poll_wait(file,&dev->rq,wait);
if( dev->index >= val )
mask |= POLLIN | POLLRDNORM;
return mask;
}
irqreturn_t interrupt_handle( int irq, void* dev_id, struct pt_regs* regs )
{
struct IIC_dev *dev = dev_id;
int val = DATA_LEN;
uint8_t ch = ioread8( R_IICDS );
if( dev->index == 0 && ch == 0xAA )
goto ret;
dev->buffer[dev->index++] = ch;
if( dev->index >= val )
{
wake_up_interruptible( &dev->rq );
// 直接退出 Slave Receiver 模式
return IRQ_HANDLED;
}
ret:
iowrite8( 0xEF, R_IICCON );
return IRQ_HANDLED;
}
module_init(IIC_init);
module_exit(IIC_exit);
MODULE_AUTHOR("kf701.ye AT gmail.com");
MODULE_DESCRIPTION("Study");
MODULE_SUPPORTED_DEVICE(DEVICE);
MODULE_LICENSE("GPL");