m50x_device.c
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include<linux/i2c.h>
static struct i2c_client *m50x_client;
static int i2c_bus_num=1;
static struct i2c_board_info m50x_boardinfo=
{
I2C_BOARD_INFO("m50x",0x50),
.platform_data = &i2c_bus_num,
};
static int __init m50x_dev_init(void)
{
struct i2c_adapter *adapter;
int ret=0;
//get i2c adapter
adapter = i2c_get_adapter(*((int *)m50x_boardinfo.platform_data));
if(NULL == adapter)
{
printk("failed to get adapter\n");
return -ENODEV;
}
//register i2c device
m50x_client = i2c_new_device(adapter,&m50x_boardinfo);
if(NULL == m50x_client)
{
printk("failed to register i2c device\n\n");
ret = -EINVAL;
goto err_add_device;
}
m50x_client->adapter=adapter;
return 0;
err_add_device:
i2c_put_adapter(adapter);
return ret;
}
static void __exit m50x_dev_exit(void)
{
//unregister i2c device
i2c_unregister_device(m50x_client);
//release i2c adapater
i2c_put_adapter(m50x_client->adapter);
}
module_init(m50x_dev_init);
module_exit(m50x_dev_exit);
MODULE_AUTHOR("TOMMY");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PERSONAL");
m50x_driver.c
/****************
*******author:tommy
*******time:2018-11-25
*******description:此驱动是某公司
*******的PSAM卡的I2C驱动
************************/
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/i2c.h>
#include<linux/interrupt.h>
#include<linux/bcd.h>
#include<linux/workqueue.h>
#include<linux/slab.h>
#include<linux/types.h>
#include<linux/device.h>
#define BUF_SIZE 256
static dev_t dev_id; //设备号
static struct class *card_class;//设备类
struct m50x
{
struct cdev cdev; //字符设备
struct i2c_client *client; //I2C外设结构体,从机
struct semaphore sem; //并发控制用的信号量
wait_queue_head_t r_wq; //读等待队列
wait_queue_head_t w_wq; //写等待队列
unsigned char buf[BUF_SIZE];//缓冲区
unsigned char flag; //读写标志
};
static ssize_t m50x_read(struct file *file,char _user *buf,size_t count,loff_t *ofs)
{
struct m50x *chip = file->private_data;//通过私有数据获得
int ret=0;
if(down_interruptible(&chip->sem))//若返回值非0,获取信号量失败,说明临界资源已经被占用
{
return -EAGAIN;
}
if(!chip->flag)//若不可读
{
up(&chip->sem);//释放信号量
//应用空间若以非阻塞方式访问,则返回错误
if(file->f_flags & NONBLOCK)
{
return -EAGAIN;
}
else//若以阻塞方式访问内核空间
{
//将读等待队列挂起,进入睡眠
if(wait_event_interruptible(chip->r_wq,chip->flag))
{
return -ERESTARTSYS;
}
//若获取信号量失败
if(down_interruptible(&chip->sem))
{
return -ERESTARTSYS;
}
}
}
if(count > BUF_SIZE)
count=BUF_SIZE;
//ret为I2C主控器接收从机的数据长度(字节数)
ret=i2c_master_recv(chip->client,chip->buf,count);
if(ret > 0)
{
//若拷贝数据到用户空间失败
if(copy_to_user(buf,chip->buf,count))
{
up(&chip->sem);//释放信号量
return -EFAULT;
}
else
{
chip->flag=0;//置为可写标志
wake_up_interruptible(&chip->w_wq);//唤醒写等待队列
}
}
up(&chip->sem);
return ret ? count : -1;
}
static int m50x_write(struct file *file,char _user *buf,size_t count,loff_t *ops)
{
struct m50x *chip=file->private_data;
int ret=0;
if(down_interruptible(&chip->sem))
{
return -ERESTARTSYS;
}
if(chip->flag)//不可写
{
up(&chip->sem);//释放信号量
if(file->f_flags & NONBLOCK)
{
return -EAGAIN;
}
else
{
//让写等待队列进入睡眠
if(wait_event_interruptible(chip->w_wq,chip->flag == 0))
{
return -ERESTARTSYS;
}
if(down_interruptible(&chip->sem))//获取信号量
{
return -ERESTARTSYS;
}
}
}
if(count > BUF_SIZE)
{
count = BUF_SIZE;
}
if(copy_from_user(chip->buf,buf,count))
{
up(&chip->sem);
return -EFAULT;
}
else
{
//ret为I2C主控器向从机发送的数据长度(字节数)
ret=i2c_master_send(chip->client,chip->buf,count);
if(ret)
{
chip->flag=1;//可读
wake_up_interruptible(&chip->r_wq);
}
}
up(&chip->sem);
return ret ? count:-1
}
static unsigned int m50x_poll(struct file *file,struct poll_table_struct *wait)
{
struct m50x *chip;
unsigned int mask =0;
int ret=0;
if(down_interruptible(&chip->sem))
{
return -ERESTERSYS;
}
poll_wait(file,&chip->r_wq,wait);
poll_wait(file,&chip->w_wq,wait);
if(chip->flag)//可读
{
mask |=POLLIN | POLLRDNORM;
}
else//可写
{
mask |= POLLOUT | POLLWRNORM;
}
up(&chip->sem);
return mask;
}
static int m50x_open(struct inode *inode,struct file *file)
{
struct m50x *chip = container_of(inode->i_cdev,struct m50x,cdev);
file->private_data = chip;
//初始化等待队列
init_waitqueue_head(&chip->r_wq);
init_waitqueue_head(&chip->w_wq);
memset(chip->buf,0,BUF_SIZE);
chip->flag=0;
return 0;
}
static int m50x_release(struct inode *inode,struct file *file)
{
return 0;
}
static const struct file_operations m50x_fops=
.owner = THIS_MODULE,
.read = m50x_read,
.write = m50x_write,
.poll = m50x_poll,
.release= m50x_release,
};
static int m50x_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
struct m50x *chip;
int ret=0;
//申请设备结构体的动态内存空间
chip=kzalloc(sizeof(struct m50x),GFP_KERNEL);
if(!chip)
{
return -ENOMH;
goto alloc_err;
}
//把m50x结构体数据作为I2C从设备的私有数据,便于以后调用
i2c_set_clientdata(client,chip);
//对chip结构体变量的成员进行初始化
chip->client = client;
sema_init(&chip->sem,1);//初始化信号量,必须在注册前初始化,以免产生竞态
//动态申请设备号,让内核自动分配设备号
ret = alloc_chrdev_region(&dev_id,0,1,DEVICE_NAME);
if(ret)
{
printk("allocate dev id failed!\n ");
goto alloc_dev_id_err;
}
//字符设备初始化,将字符设备成员与函数操作集进行绑定
cdev_init(&chip->cdev,&m50x_fops);
chip->cdev.owner=THIS_MODULE;
//完成设备注册,将设备与设备号绑定
ret=cdev_add(&chip->cdev,dev_id,1);
if(ret)
{
printk("cdev add err!\n");
goto cdev_add_err;
}
//创建一个类,存放在sysfs目录下,sysfs/class/
card_class=class_create(THIS_MODULE,DEVICE_NAME);
if(NULL==card_class)
{
printk("class create failed!\n");
ret=-EBUSY;
goto class_err;
}
//创建设备节点成功,存放在/dev/目录下
device_create(card_class,NULL,dev_id,NULL,DEVICE_NAME);
printk(DEVICE_NAME" initalized!\n");
return(0);
alloc_err:
return(ret);
alloc_dev_id_err:
kfree(chip);
cdev_add_err:
unregister_chrdev_region(dev_id,1);
class_err:
cdev_del(&chip->cdev);
}
static int m50x_remove(struct i2c_client *client,const struct i2c_device_id *id)
{
struct m50x *chip = i2c_get_clientdata(client); //获得外设的数据
device_destroy(card_class,dev_id); //将设备号从设备类中删除
class_destroy(card_class); //删除设备类
cdev_del(&chip->cdev); //删除字符设备
unregister_chrdev_region(dev_id,1); //注销设备号
kfree(chip); //释放动态内存
printk(DEVICE_NAME" destroyed!");
return 0;
}
static const struct i2c_device_id m50x_id[]=
{
{"m50x",0},
{}
};
MODULE_DEVICE_TABLE(i2c,m50x_id);
static struct i2c_driver m50x_driver=
{
.driver=
{
.name = "m50x_card",
.owner=THIS_MODULE,
},
.probe=m50x_probe,
.remove=m50x_remove,
.id_table=m50x_id,
};
//入口函数
static __init int m50x_drv_init(void)
{
return i2c_add_driver(&m50x_driver);
}
//出口函数
static __exit void m50x_drv_exit(void)
{
i2c_del_driver(&m50x_driver);
}
module_init(m50x_drv_init);
module_init(m50x_drv_exit);
MODULE_AUTHOR("TOMMY");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LINUX_WORKER");
makefile
obj-o += m50x
PWD := $(shell pwd)
KERDIR := ${PWD}/../
make -C ${KERDIR} -M ${PWD}
RKImageMaker.exe -RK330A Image\MiniLoaderAll.bin D:\Image\update_old.img D:\Image\update_%FILENAME% -os_type:androidos