struct sakway_msg_t{
#define SAKWAY_I2C_MSG_WRITE 0
#define SAKWAY_I2C_MSG_READ 1
int rw_flag; // 读写标志位,可由上面宏来替代
short chip_addr; // 片上内存地址(默认单字节)
unsigned char wmsg; // 写入的内容
unsigned char index; // 指定获取GPIO输入状态的GPIO下标,如下表
unsigned char * rmsg; // 读取的内容
};
/* input
* index 0 1 2 3 4
* gpio PA04 PA12 PC24 PC25 PD02
*/
#define SAKWAY_CMD_DRV_TEMP 0 // 温度传感器 -- 0x92
#define SAKWAY_CMD_DRV_MCUG 1 // 单片机G传感器 -- 0x34
#define SAKWAY_CMD_DRV_MCUF 2 // 单片机F传感器 -- 0x38
#define SAKWAY_CMD_GPIO_GET 3 // 获取5个Gpio的输入状态,由结构体里的rmag来得值
#define SAKWAY_CMD_GPIO_SET 4 // 设置1个Gpio的输出状态,由结构体里的wmsg来传值
#define I2C_TEMP_ADDR 0x48 // 内部温度传感器的设备地址
#define I2C_MCUG_ADDR 0x34 // GD单片机的I2c设备地址
#define I2C_MCUF_ADDR 0x38 // FM单片机的I2c设备地址
typedef struct sakway_t SAKWAY;
struct sakway_hw_t{
unsigned int io_in[5]; // 输入GPIO
unsigned int io_out; // 输出GPIO
unsigned int io[0];
int i2c_id; // 指定要操作的I2c器件
#define SAKWAY_I2C_ID_CPLD 0u
#define SAKWAY_I2C_ID_MCUG 1u
#define SAKWAY_I2C_ID_MCUF 2u
struct i2c_client *i2c_clit[3]; // 三个I2c设备
struct i2c_adapter *i2c_adper; // 适配器
struct i2c_driver *i2c_drv; // 这个没用上,可删
};
struct sakway_t{
SAKWAY *this;
struct sakway_hw_t *hw;
struct mutex lock; // 锁
unsigned int majoy_id;
};
// I2c写操作
static int __sakway_write_reg(SAKWAY* this, short reg_addr,unsigned char reg_value){
char write_buf[4];
int status;
struct i2c_msg msg =
{
this->hw->i2c_clit[this->hw->i2c_id]->addr, 0, 2, write_buf
};
memset(write_buf, 0, 4);
write_buf[0] = reg_addr&0x00ff;
write_buf[1] = reg_value;
msg.len = 2;
mutex_lock(&(this->lock));
status = i2c_transfer(this->hw->i2c_adper, &msg, 1);
mutex_unlock(&(this->lock));
return (status == 1)? 0 : -1;
}
// I2c读操作
static int __sakway_read_reg(SAKWAY* this, short reg_addr,unsigned char *reg_value){
char buf[4];
int status;
struct i2c_msg msg[2] = {
{
this->hw->i2c_clit[this->hw->i2c_id]->addr, 0, 2, buf
},
{
this->hw->i2c_clit[this->hw->i2c_id]->addr, I2C_M_RD, 1, reg_value
}
};
memset(buf, 0, 4);
buf[0] = reg_addr&0x00ff;
msg[0].len = 1;
mutex_lock(&(this->lock));
status = i2c_transfer(this->hw->i2c_adper, &msg, 2);
mutex_unlock(&(this->lock));
return (status == 2)? 0 : -1;
}
// 将所需的I2c的描述和设备地址填入下列
static struct i2c_board_info __initdata sakway_board_info[] = {
{
I2C_BOARD_INFO("TEMP-i2c", I2C_TEMP_ADDR),
.irq = -1,
},
{
I2C_BOARD_INFO("MCUG-i2c", I2C_MCUG_ADDR),
.irq = -1,
},
{
I2C_BOARD_INFO("MCUF-i2c", I2C_MCUF_ADDR),
.irq = -1,
},
{},
};
// 分配I2c设备和GPIO,设备构造
static int __sakway_hw_init(SAKWAY* this){
int ret , _i;
struct i2c_client* client;
struct i2c_adapter* adapter;
printk(DRV_NAME "\t init sakway adapter. \n");
adapter = i2c_get_adapter(0);
if (!adapter) {
ret = -ENXIO;
printk(DRV_NAME "\terror: %d : init i2c adapter failed.\n", ret);
return ret;
}
strlcpy(adapter->name, "sakway-i2c",sizeof(adapter->name));
printk(DRV_NAME "\tadd %s adapter successful.\n", adapter->name);
printk(DRV_NAME "\tinit i2c client. \n");
for(_i = 0; _i < ARRAY_SIZE(this->hw->i2c_clit); _i ++){
client = i2c_new_device(adapter, &sakway_board_info[_i]);
printk(DRV_NAME "\tclient address is %d bits\n", client->flags & I2C_CLIENT_TEN ? 10 : 7);
if (!client) {
ret = -ENXIO;
printk(DRV_NAME "\terror: %d : init i2c client failed.\n", ret);
return ret;
}
printk(DRV_NAME "\tadd %s client successful.\n", client->name);
printk(DRV_NAME "\tinfo:\n");
printk(DRV_NAME "\tclient addr: %x\n", client->addr);
printk(DRV_NAME "\tclient name: %s\n", client->name);
printk(DRV_NAME "\tadapter name: %s\n", client->adapter->name);
this->hw->i2c_clit[_i] = client;
}
this->hw->i2c_adper = adapter;
this->hw->io_in[0] = 32*0 + 4; // PA04
this->hw->io_in[1] = 32*0 + 12; // PA12
this->hw->io_in[2] = 32*2 + 24; // PC24
this->hw->io_in[3] = 32*2 + 25; // PC25
this->hw->io_in[4] = 32*3 + 2; // PD02
this->hw->io_out = 32*3 + 1; // PD01
return 0;
}
// 设备析构
static int
__sakway_remove(SAKWAY *this){
int _i ;
printk(DRV_NAME "\tRelease I2c adapter.\n");
i2c_put_adapter(this->hw->i2c_adper);
printk(DRV_NAME "\tRelease I2c client.\n");
for (_i = 0; _i < ARRAY_SIZE(Sakway->hw->i2c_clit); _i ++) {
i2c_unregister_device(this->hw->i2c_clit[_i]);
}
return 0;
}
// 初始化该设备进行的内存分配
static SAKWAY* sakway_new_dev(void){
SAKWAY* dev = (SAKWAY*)kmalloc(sizeof(SAKWAY),GFP_ATOMIC);
dev->hw = (struct sakway_hw_t*)kmalloc(sizeof(struct sakway_hw_t),GFP_ATOMIC);
return dev;
}
// 没用上
static size_t
sakway_driver_write(struct file* filp,
const char __user* buffer,
size_t size, loff_t* f_pos)
{
return 0;
}
// 没用上
static size_t
sakway_driver_read(struct file* filp,
const char __user* buffer,
size_t size, loff_t* f_pos)
{
return 0;
}
// ioctl 的实现
static int
sakway_driver_ioctl( struct file* filp,
unsigned int cmd,
unsigned long arg)
{
struct sakway_msg_t _msg,* _m;
switch(cmd){
case SAKWAY_CMD_DRV_TEMP:
case SAKWAY_CMD_DRV_MCUG:
case SAKWAY_CMD_DRV_MCUF: // I2c设备
if( cmd < ARRAY_SIZE(Sakway->hw->i2c_clit) ){
Sakway->hw->i2c_id = cmd; // 指明操作那个I2c设备
}
_m = (struct sakway_msg_t *)arg; // 类型转换,获取内容
copy_from_user(&_msg, _m, sizeof(struct sakway_msg_t)); // 拷贝到内核
if(_msg.rw_flag == SAKWAY_I2C_MSG_READ){ // 读
__sakway_read_reg(Sakway, _msg.chip_addr, _msg.rmsg);
}
else{ // 写
__sakway_write_reg(Sakway, _msg.chip_addr, _msg.wmsg);
}
break;
case SAKWAY_CMD_GPIO_GET: // gpio状态的获取
_m = (struct sakway_msg_t *)arg;
copy_from_user(&_msg, _m, sizeof(struct sakway_msg_t));
if(_msg.index < ARRAY_SIZE(Sakway->hw->io_in)){ // 防止越界
mutex_lock(&(Sakway->lock));
*(_msg.rmsg) = gpio_get_value(Sakway->hw->io_in[_msg.index]); // 指明哪个Gpio口
mutex_unlock(&(Sakway->lock));
}
break;
case SAKWAY_CMD_GPIO_SET: // 设置gpio口的状态
_m = (struct sakway_msg_t *)arg;
copy_from_user(&_msg, _m, sizeof(struct sakway_msg_t));
mutex_lock(&(Sakway->lock));
gpio_set_value(Sakway->hw->io_out, _msg.wmsg);
mutex_unlock(&(Sakway->lock));
break;
default:
// pr_info(DRV_NAME "ioctl cmd failed \n");
break;
}
return 0;
}
// 打开
static int
sakway_driver_open(struct inode* node, struct file* filp)
{
return 0;
}
static struct file_operations sakway_fops =
{
.owner = THIS_MODULE,
.open = sakway_driver_open,
.read = sakway_driver_read,
.write = sakway_driver_write,
.unlocked_ioctl = sakway_driver_ioctl,
};
static struct miscdevice sakway_miscdev =
{
.name = DRV_NAME,
.fops = &sakway_fops,
.nodename = DRV_NAME,
};
static const char *Sakway_led_lable[]={ // 给gpio的描述
"PA04","PA12","PC24", "PC25", "PD02","PD01"
};
static void __init sakway_driver_init(void)
{
int ret,i;
printk(DRV_NAME "\tApply mem for Sakway.\n");
Sakway = sakway_new_dev(); // 分配内存空间
if (!Sakway) {
ret = -ENOMEM;
printk(DRV_NAME "\Sakway new device failed!\n");
kfree(Sakway);
return ret;
}
ret = misc_register(&sakway_miscdev); // 设备的注册
if (ret < 0 ) {
printk(DRV_NAME "\tRegister Char Driver Failed. ret = %d\n", ret);
kfree(Sakway);
return ret;
}
printk(DRV_NAME "\tgenerated a driver nod : /dev/Sakway \n");
printk(DRV_NAME "\tInit hardware...\n");
ret = __sakway_hw_init(Sakway); // I2c的初始化
if (ret < 0) {
kfree(Sakway);
return ret;
}
for (i= 0; i < ARRAY_SIZE(Sakway->hw->io_in); i ++) { // 输入gpio的初始化
ret = gpio_request(Sakway->hw->io_in[i], Sakway_led_lable[i]); //申请输入gpio
if (ret < 0) {
kfree(Sakway);
misc_deregister(&sakway_miscdev);
printk(DRV_NAME "\trequest gpio %d for Sakway failed, ret = %d\n", Sakway->hw->io_in[i], ret);
return ret;
} else {
printk(DRV_NAME "\trequest gpio %d for Sakway set succussful, ret = %d\n", Sakway->hw->io_in[i], ret);
}
gpio_direction_input(Sakway->hw->io_in[i]); // 配置为输入
}
ret = gpio_request(Sakway->hw->io_out, "PD01"); // 申请输出gpio
if (ret < 0) {
kfree(Sakway);
misc_deregister(&sakway_miscdev);
printk(DRV_NAME "\trequest gpio %d for Sakway failed, ret = %d\n", Sakway->hw->io_in[i], ret);
return ret;
} else {
printk(DRV_NAME "\trequest gpio %d for Sakway set succussful, ret = %d\n", Sakway->hw->io_in[i], ret);
}
gpio_direction_output(Sakway->hw->io_out, 0); // 配置为输出
printk(DRV_NAME "\tInit mutex...\n");
mutex_init(&(Sakway->lock)); // 初始化锁
printk(DRV_NAME "\tInit module...\n");
}
module_init(sakway_driver_init);
static void __exit sakway_driver_exit(void)
{
int i;
printk(DRV_NAME "\tRelease GPIOs.\n");
for (i = 0; i < ARRAY_SIZE(Sakway->hw->io_in); i ++)
gpio_free(Sakway->hw->io_in[i]); // 释放gpio
gpio_free(Sakway->hw->io_out);
printk(DRV_NAME "\tRelease Driver nod.\n");
misc_deregister(&sakway_miscdev);
printk(DRV_NAME "\tRelease the Sakway space.\n");
__sakway_remove(Sakway);
kfree(Sakway);
}
module_exit(sakway_driver_exit);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
obj-y += sakway.o
obj-y += sakway/
重新编译内核并烧写进开发板即可,上电运行后即可在系统中的/dev下看到Sakway设备。
在外单独编译该驱动,先确保主机上有相关编译工具链(这里是mips-linux-gnu-)和halley2的kernel源码,新建并打开文件夹下的Makefile文件,并记得更改kernel路径成自己主机上Halley2的kernel的路径
obj-m += sakway.o
CC=mips-linux-gnu-gcc
MODULE_NAME =sakway
AR=mips-linux-gnu-ar
DEBFLAGS = -O2
obj-m := $(MODULE_NAME).o
# 该kernel目录文件夹路径,得更改成与主机上的halley2的kernel目录
KERNELDIR ?= /path/to/kernel/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm $(MODULE_NAME).mod.* $(MODULE_NAME).ko $(MODULE_NAME).o Module.symvers modules.order .$(MODULE_NAME).*
在主机上使用make编译该驱动,不出意外的话会在当前文件夹下生成sakway.ko文件
然后在将该ko文件放入开发板的系统里,使用insmod 来加载该ko文件。
加载成功后,会在/dev/出现Sakway设备。
#include "sakway.h"
int main(int argc, char **argv){
int fd;
unsigned char buf;
struct sakway_msg_t msg = {
SAKWAY_I2C_MSG_READ, // 配置为读
0x00, // 片上地址0x00
0,0, // wmsg为0,index为0
&buf // 写
};
fd = open("/dev/Sakway", O_RDWR);
if(fd < 0){
printf("open error ... \n");
exit(0);
}
ioctl(fd, SAKWAY_CMD_DRV_TEMP, &msg); // 读取温度传感器
printf("temp read : %02x \n", buf); // 打印获取的值
ioctl(fd, SAKWAY_CMD_DRV_MCUG, &msg); // 读取MCUG
printf("MCUG read : %02x \n", buf); // 打印获取的值
ioctl(fd, SAKWAY_CMD_DRV_MCUF, &msg); // 读取MCUF
printf("MCUF read : %02x \n", buf); // 打印获取的值
msg.index = 3; // 获取PC25的状态
ioctl(fd, SAKWAY_CMD_GPIO_GET, &msg);
printf("PC25 read: %02x \n", buf);
msg.wmsg = 1;
ioctl(fd, SAKWAY_CMD_GPIO_SET, &msg); // 设置PD01为高电平
close(fd);
return 0;
}
参考文章