ssm2602 I2C Linux 驱动
下面是驱动程序:
/*
* 这是一个注册 I2C 字符设备
* I2C 设备的 read/write 函数不实用,这里先写一下初始化与显示的函数
* ioctl LIST_REGS GET_REG INIT_REG SETMODE
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/bcd.h>
#include <linux/list.h>
#include <linux/types.h>
//#include "i2c-dev.h"
#define SSM2602_SETMODE 0x1
#define SSM2602_LISTREGS 0x2
#define SSM2602_MAJOR 250
#define SSM2602_ADDRESS 0x1a
/*
struct i2c_msg
{
__u16 addr;
__u16 flags;
__u16 len;
__u8 *buf;
};
*/
static unsigned short normal_i2c[] = { SSM2602_ADDRESS, I2C_CLIENT_END };
//static unsigned short normal_i2c[] = { SSM2602_ADDRESS>>1, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
struct i2c_client *ssm2602_datap;
static int ssm2602_major = SSM2602_MAJOR;
static int ssm2602_attach_adapter(struct i2c_adapter *adapter);
static int ssm2602_command(struct i2c_client *client, unsigned int cmd, void *arg);
static int ssm2602_detect(struct i2c_adapter *adapter, int address, int kind);
static int ssm2602_detach_client(struct i2c_client *client);
const unsigned short Codec_I2C_Setting[4][14] = {
{0x97, 0x97, 0x79, 0x79, 0x0a, 0x08, 0x9f, 0x0a, 0x00, 0x00, 0x00, 0x7b, 0x32, 0x00},/*idle*/
{0x97, 0x97, 0x138, 0x138, 0x14, 0x00, 0x0, 0x8a, 0x00, 0x01, 0x00, 0x7b, 0x32, 0x00},/*speaker*/
{0x97, 0x97, 0x179, 0x179, 0x14, 0x00, 0x0, 0x8a, 0x00, 0x01, 0x00, 0x7b, 0x32, 0x00},/*headphone*/
{0x1B, 0x1B, 0x179, 0x179, 0x0e, 0x00, 0x0, 0x0a, 0x00, 0x00, 0x00, 0xff, 0x32, 0x00},/*bypass*/
};
#define NORMAL_CONFIG_REG_LENGTH 0x0a
#define CODEC_REG_LIV 0X00 //Left-Channel ADC Input Volume
#define CODEC_REG_RIV 0X01 //Right-Channel ADC Input Volume
#define CODEC_REG_LOV 0X02 //Left-Channel DAC Volume
#define CODEC_REG_ROV 0X03 //Right-Channel DAC Volume
#define CODEC_REG_AAP 0X04 //Analog Audio Path
#define CODEC_REG_DAP 0X05 //Digital Audio Path
#define CODEC_REG_PM 0X06 //Power Management
#define CODEC_REG_DIF 0X07 //Digital Audio I/F
#define CODEC_REG_SR 0X08 //Sampling Rate
#define CODEC_REG_ACT 0X09 //Active
#define CODEC_REG_RST 0X0f //Software Reset
#define CODEC_REG_ALC1 0X10 //ALC Control 1
#define CODEC_REG_ALC2 0X11 //ALC Control 2
#define CODEC_REG_NG 0X12 //Noise Gate
const unsigned char Codec_RegAddress[] =
{
CODEC_REG_LIV ,
CODEC_REG_RIV ,
CODEC_REG_LOV ,
CODEC_REG_ROV ,
CODEC_REG_AAP ,
CODEC_REG_DAP ,
CODEC_REG_PM ,
CODEC_REG_DIF ,
CODEC_REG_SR ,
CODEC_REG_ACT ,
CODEC_REG_RST ,
CODEC_REG_ALC1,
CODEC_REG_ALC2,
CODEC_REG_NG ,
};
//***********************************************************************
u8 ssm2602_write (struct i2c_client *client,
u8 reg,
u8 value)
{
unsigned char buf[3]={0};
struct i2c_msg msg[1];
buf[0] = (reg << 1) | ((value >> 8) & 0x0001);
buf[1] = value & 0x00ff;
msg[0].flags=0; //0 write
msg[0].addr=client->addr;
msg[0].buf=buf;
msg[0].len=2;
i2c_transfer(client->adapter, msg, 1);
return 0;
}
u8 ssm2602_read (struct i2c_client *client,
u8 reg,
u8 *buf)
{
unsigned char regs[1]={0};
struct i2c_msg msg[2];
regs[0]=reg<<1&0x00fe;
msg[0].flags=0; //0 write
msg[0].addr=client->addr;
msg[0].buf=regs;
msg[0].len=1;
msg[1].flags=1; //1 read
msg[1].addr=client->addr;
msg[1].buf=buf;
msg[1].len=2;
i2c_transfer(client->adapter, msg, 2);
return 0;
}
//***********************************************************************
void ssm2602_list_regs(struct i2c_client *client)
{
u8 buf[2],i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_read(client,Codec_RegAddress[i],buf);
printk(KERN_INFO "Address:0x%02x Value:0x%02x\n",Codec_RegAddress[i],(buf[1]<<8)|buf[0]);
}
}
void ssm2602_set_mode(struct i2c_client *client,u8 mode)
{
u8 i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_write(client,Codec_RegAddress[i],Codec_I2C_Setting[mode][i]);
}
}
//***********************************************************************
int ssm2602_open(struct inode *inode, struct file *filep)
{
filep->private_data = ssm2602_datap;
return 0;
}
int ssm2602_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int ssm2602_ioctl(struct inode *inodep, struct file *filep, unsigned
int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)filep->private_data;
switch (cmd)
{
case SSM2602_SETMODE:
printk(KERN_INFO "set mode command\n");
ssm2602_set_mode(client,(u8)arg);
break;
case SSM2602_LISTREGS:
printk(KERN_INFO "list regs command\n");
ssm2602_list_regs(client);
break;
default:
return -EINVAL;
}
return 0;
}
//***********************************************************************
static const struct file_operations ssm2602_i2c_fops =
{
.owner = THIS_MODULE,
//.llseek = ssm2602_llseek,
//.read = ssm2602_read,
//.write = ssm2602_write,
.ioctl =ssm2602_ioctl,
.open = ssm2602_open,
.release = ssm2602_release,
};
static struct i2c_driver ssm2602_driver = {
.driver = {
.name = "SSM2602",
},
.attach_adapter = ssm2602_attach_adapter,
.detach_client = ssm2602_detach_client,
.command = ssm2602_command,
};
static int ssm2602_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
return 0;
}
void ssm2602_init_client(struct i2c_client *new_client)
{
u8 i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_write(new_client,Codec_RegAddress[i],Codec_I2C_Setting[1][i]);
}
}
static int ssm2602_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_I2C))
goto exit;
if (!(ssm2602_datap = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(ssm2602_datap, 0, sizeof(struct i2c_client));
new_client = ssm2602_datap;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ssm2602_driver;
new_client->flags = 0;
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the DS1337 chip */
ssm2602_init_client(new_client);
return 0;
exit_free:
kfree(ssm2602_datap);
exit:
return err;
}
static int ssm2602_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, ssm2602_detect);
}
static int ssm2602_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client)))
return err;
return 0;
}
/*
* Driver data (common to all clients)
*/
static int __init ssm2602_init(void)
{
int res;
res = register_chrdev(ssm2602_major, "ssm2602_iic", &ssm2602_i2c_fops);
if(res)
goto out;
res = i2c_add_driver(&ssm2602_driver);
if(res)
goto out_unreg_chrdev;
return 0;
out_unreg_chrdev: unregister_chrdev(ssm2602_major,"ssm2602_iic");
out: printk(KERN_ERR "%s: Driver Initialisation failed \n", __FILE__);
return res;
}
static void __exit ssm2602_exit(void)
{
i2c_del_driver(&ssm2602_driver);
unregister_chrdev(ssm2602_major, "ssm2602_iic");
}
MODULE_AUTHOR("Hill@Ensky.tech");
MODULE_DESCRIPTION("SSM2602 IIC driver");
MODULE_LICENSE("Dual BSD/GPL");
module_init(ssm2602_init);
module_exit(ssm2602_exit);
* 这是一个注册 I2C 字符设备
* I2C 设备的 read/write 函数不实用,这里先写一下初始化与显示的函数
* ioctl LIST_REGS GET_REG INIT_REG SETMODE
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h> /* get the user-level API */
#include <linux/bcd.h>
#include <linux/list.h>
#include <linux/types.h>
//#include "i2c-dev.h"
#define SSM2602_SETMODE 0x1
#define SSM2602_LISTREGS 0x2
#define SSM2602_MAJOR 250
#define SSM2602_ADDRESS 0x1a
/*
struct i2c_msg
{
__u16 addr;
__u16 flags;
__u16 len;
__u8 *buf;
};
*/
static unsigned short normal_i2c[] = { SSM2602_ADDRESS, I2C_CLIENT_END };
//static unsigned short normal_i2c[] = { SSM2602_ADDRESS>>1, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
struct i2c_client *ssm2602_datap;
static int ssm2602_major = SSM2602_MAJOR;
static int ssm2602_attach_adapter(struct i2c_adapter *adapter);
static int ssm2602_command(struct i2c_client *client, unsigned int cmd, void *arg);
static int ssm2602_detect(struct i2c_adapter *adapter, int address, int kind);
static int ssm2602_detach_client(struct i2c_client *client);
const unsigned short Codec_I2C_Setting[4][14] = {
{0x97, 0x97, 0x79, 0x79, 0x0a, 0x08, 0x9f, 0x0a, 0x00, 0x00, 0x00, 0x7b, 0x32, 0x00},/*idle*/
{0x97, 0x97, 0x138, 0x138, 0x14, 0x00, 0x0, 0x8a, 0x00, 0x01, 0x00, 0x7b, 0x32, 0x00},/*speaker*/
{0x97, 0x97, 0x179, 0x179, 0x14, 0x00, 0x0, 0x8a, 0x00, 0x01, 0x00, 0x7b, 0x32, 0x00},/*headphone*/
{0x1B, 0x1B, 0x179, 0x179, 0x0e, 0x00, 0x0, 0x0a, 0x00, 0x00, 0x00, 0xff, 0x32, 0x00},/*bypass*/
};
#define NORMAL_CONFIG_REG_LENGTH 0x0a
#define CODEC_REG_LIV 0X00 //Left-Channel ADC Input Volume
#define CODEC_REG_RIV 0X01 //Right-Channel ADC Input Volume
#define CODEC_REG_LOV 0X02 //Left-Channel DAC Volume
#define CODEC_REG_ROV 0X03 //Right-Channel DAC Volume
#define CODEC_REG_AAP 0X04 //Analog Audio Path
#define CODEC_REG_DAP 0X05 //Digital Audio Path
#define CODEC_REG_PM 0X06 //Power Management
#define CODEC_REG_DIF 0X07 //Digital Audio I/F
#define CODEC_REG_SR 0X08 //Sampling Rate
#define CODEC_REG_ACT 0X09 //Active
#define CODEC_REG_RST 0X0f //Software Reset
#define CODEC_REG_ALC1 0X10 //ALC Control 1
#define CODEC_REG_ALC2 0X11 //ALC Control 2
#define CODEC_REG_NG 0X12 //Noise Gate
const unsigned char Codec_RegAddress[] =
{
CODEC_REG_LIV ,
CODEC_REG_RIV ,
CODEC_REG_LOV ,
CODEC_REG_ROV ,
CODEC_REG_AAP ,
CODEC_REG_DAP ,
CODEC_REG_PM ,
CODEC_REG_DIF ,
CODEC_REG_SR ,
CODEC_REG_ACT ,
CODEC_REG_RST ,
CODEC_REG_ALC1,
CODEC_REG_ALC2,
CODEC_REG_NG ,
};
//***********************************************************************
u8 ssm2602_write (struct i2c_client *client,
u8 reg,
u8 value)
{
unsigned char buf[3]={0};
struct i2c_msg msg[1];
buf[0] = (reg << 1) | ((value >> 8) & 0x0001);
buf[1] = value & 0x00ff;
msg[0].flags=0; //0 write
msg[0].addr=client->addr;
msg[0].buf=buf;
msg[0].len=2;
i2c_transfer(client->adapter, msg, 1);
return 0;
}
u8 ssm2602_read (struct i2c_client *client,
u8 reg,
u8 *buf)
{
unsigned char regs[1]={0};
struct i2c_msg msg[2];
regs[0]=reg<<1&0x00fe;
msg[0].flags=0; //0 write
msg[0].addr=client->addr;
msg[0].buf=regs;
msg[0].len=1;
msg[1].flags=1; //1 read
msg[1].addr=client->addr;
msg[1].buf=buf;
msg[1].len=2;
i2c_transfer(client->adapter, msg, 2);
return 0;
}
//***********************************************************************
void ssm2602_list_regs(struct i2c_client *client)
{
u8 buf[2],i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_read(client,Codec_RegAddress[i],buf);
printk(KERN_INFO "Address:0x%02x Value:0x%02x\n",Codec_RegAddress[i],(buf[1]<<8)|buf[0]);
}
}
void ssm2602_set_mode(struct i2c_client *client,u8 mode)
{
u8 i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_write(client,Codec_RegAddress[i],Codec_I2C_Setting[mode][i]);
}
}
//***********************************************************************
int ssm2602_open(struct inode *inode, struct file *filep)
{
filep->private_data = ssm2602_datap;
return 0;
}
int ssm2602_release(struct inode *inode, struct file *filp)
{
return 0;
}
static int ssm2602_ioctl(struct inode *inodep, struct file *filep, unsigned
int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)filep->private_data;
switch (cmd)
{
case SSM2602_SETMODE:
printk(KERN_INFO "set mode command\n");
ssm2602_set_mode(client,(u8)arg);
break;
case SSM2602_LISTREGS:
printk(KERN_INFO "list regs command\n");
ssm2602_list_regs(client);
break;
default:
return -EINVAL;
}
return 0;
}
//***********************************************************************
static const struct file_operations ssm2602_i2c_fops =
{
.owner = THIS_MODULE,
//.llseek = ssm2602_llseek,
//.read = ssm2602_read,
//.write = ssm2602_write,
.ioctl =ssm2602_ioctl,
.open = ssm2602_open,
.release = ssm2602_release,
};
static struct i2c_driver ssm2602_driver = {
.driver = {
.name = "SSM2602",
},
.attach_adapter = ssm2602_attach_adapter,
.detach_client = ssm2602_detach_client,
.command = ssm2602_command,
};
static int ssm2602_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
return 0;
}
void ssm2602_init_client(struct i2c_client *new_client)
{
u8 i;
for(i=0;i<NORMAL_CONFIG_REG_LENGTH;i++)
{
ssm2602_write(new_client,Codec_RegAddress[i],Codec_I2C_Setting[1][i]);
}
}
static int ssm2602_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *new_client;
int err = 0;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_I2C))
goto exit;
if (!(ssm2602_datap = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
memset(ssm2602_datap, 0, sizeof(struct i2c_client));
new_client = ssm2602_datap;
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &ssm2602_driver;
new_client->flags = 0;
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto exit_free;
/* Initialize the DS1337 chip */
ssm2602_init_client(new_client);
return 0;
exit_free:
kfree(ssm2602_datap);
exit:
return err;
}
static int ssm2602_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, ssm2602_detect);
}
static int ssm2602_detach_client(struct i2c_client *client)
{
int err;
if ((err = i2c_detach_client(client)))
return err;
return 0;
}
/*
* Driver data (common to all clients)
*/
static int __init ssm2602_init(void)
{
int res;
res = register_chrdev(ssm2602_major, "ssm2602_iic", &ssm2602_i2c_fops);
if(res)
goto out;
res = i2c_add_driver(&ssm2602_driver);
if(res)
goto out_unreg_chrdev;
return 0;
out_unreg_chrdev: unregister_chrdev(ssm2602_major,"ssm2602_iic");
out: printk(KERN_ERR "%s: Driver Initialisation failed \n", __FILE__);
return res;
}
static void __exit ssm2602_exit(void)
{
i2c_del_driver(&ssm2602_driver);
unregister_chrdev(ssm2602_major, "ssm2602_iic");
}
MODULE_AUTHOR("Hill@Ensky.tech");
MODULE_DESCRIPTION("SSM2602 IIC driver");
MODULE_LICENSE("Dual BSD/GPL");
module_init(ssm2602_init);
module_exit(ssm2602_exit);
下面是测试程序:
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#define SSM2602_LISTREGS 0x2
#define SSM2602_SETMODE 0x1
#define Byte unsigned char
#define UINT16 unsigned short
int main(int argc, char *argv[])
{
int fd;
int count=0;
if(argc<2)
{
printf("usage: %s command [counts]\n",argv[0]);
return -1;
}
fd=open("/tmp/ssm_i2c",O_RDWR);
/* ioctl(fd,MMCLEAR,(void *)0); */
if(strcmp(argv[1],"list_regs")==0)
ioctl(fd,SSM2602_LISTREGS,(void *)0);
else if(strcmp(argv[1], "set_mode")==0)
ioctl(fd,SSM2602_SETMODE, (void *)(unsigned long)atol(argv[2]));
close(fd);
return 0;
}
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#define SSM2602_LISTREGS 0x2
#define SSM2602_SETMODE 0x1
#define Byte unsigned char
#define UINT16 unsigned short
int main(int argc, char *argv[])
{
int fd;
int count=0;
if(argc<2)
{
printf("usage: %s command [counts]\n",argv[0]);
return -1;
}
fd=open("/tmp/ssm_i2c",O_RDWR);
/* ioctl(fd,MMCLEAR,(void *)0); */
if(strcmp(argv[1],"list_regs")==0)
ioctl(fd,SSM2602_LISTREGS,(void *)0);
else if(strcmp(argv[1], "set_mode")==0)
ioctl(fd,SSM2602_SETMODE, (void *)(unsigned long)atol(argv[2]));
close(fd);
return 0;
}