MTK I2C驅動程式碼分析
I2C匯流排(I2C bus,Inter-IC bus)是一個雙向的兩線連續匯流排,提供積體電路(ICs)之間的通訊線路。I2C匯流排是一種序列擴充套件技術,最早由Philips公司推出。Philips公司推出的I2C匯流排採用一條資料線(SDA),加一條時鐘線(SCL)來完成資料的傳輸及外圍器件的擴充套件;對各個節點的定址是軟定址方式,節省了片選線,標準的定址位元組SLAM為7位,可以定址127個單元。
I2C匯流排有三種資料傳輸速度:標準,快速模式和高速模式。標準的是100Kbps,快速模式為400Kbps,高速模式支援快至3.4Mbps的速度。
I2C位傳輸
資料傳輸:SCL為高電平時,SDA線若保持穩定,那麼SDA上是在傳輸資料bit;若SDA發生跳變,則用來表示一個會話的開始或結束(後面講)
資料改變:SCL為低電平時,SDA線才能改變傳輸的bit
I2C開始和結束訊號
開始訊號:SCL為高電平時,SDA由高電平向低電平跳變,開始傳送資料。
結束訊號:SCL為高電平時,SDA由低電平向高電平跳變,結束傳送資料。
I2C應答訊號
Master每傳送完8bit資料後等待Slave的ACK。
即在第9個clock,若從IC發ACK,SDA會被拉低。
若沒有ACK,SDA會被置高,這會引起Master發生RESTART或STOP流程,如下所示:
SCL is a clock signal that is driven by the master. SDA is a bi-directional data signal that can be driven by either the master or the slave.
I2C架構圖:
程式碼分析:
kernel-3.10\drivers\misc\mediatek\i2c\mt6735\I2c.c
(注:所有的i2c驅動,在該模組載入前就已經載入,呼叫i2c_register_board_info函式載入的board_info資訊,已經加入__i2c_board_list連結串列)。
裝置驅動以qn8027為例。
第1607~1623行,在device tree上找到AP_DMA結點,見kernel-3.10/arch/arm64/boot/dts/mt6735m.dtsi。
DT的相關知識請參考:http://blog.csdn.net/21cnbao/article/details/8457546
第1624行執行platform_driver_register(&mt_i2c_driver);
第1508行,初始化i2c->wait等待對列。
第1510行,註冊下降沿中斷,中斷處理函式為mt_i2c_irq。該函式讀取I2C中斷狀態,喚醒等待對列i2c->wait佇列。
第1521行,呼叫i2c_add_numbered_adapter(&i2c->adap);該函式會呼叫到__i2c_add_numbered_adapter,其程式碼如下:
第1090行,分配ID號,IDR機制具體描述如下:
IDR機制在Linux核心中指的是整數ID管理機制。實質上來講,這就是一種將一個整數ID號和一個指標關聯在一起的機制。這個機制最早在03年2月加入核心,當時作為POSIX定時器的一個補丁。現在,核心中很多地方都可以找到它的身影。
IDR機制適用在那些需要把某個整數和特定指標關聯在一起的地方。例如,在IIC匯流排中,每個裝置都有自己的地址,要想在總線上找到特定的裝置,就必須要先發送裝置的地址。當介面卡要訪問總線上的IIC裝置時,首先要知道它們的ID號,同時要在核心中建立一個用於描述該裝置的結構體,和驅動程式。將ID號和裝置結構體結合起來,如果使用陣列進行索引,一旦ID號很大,則用陣列索引會佔據大量記憶體空間。這顯然不可能。或者用連結串列,但是,如果匯流排中實際存在的裝置很多,則連結串列的查詢效率會很低。此時,IDR機制應運而生。該機制內部採用紅黑樹實現,可以很方便的將整數和指標關聯起來,並且具有很高的搜尋效率。
第1096行呼叫i2c_register_adapter註冊adapter,主要程式碼如下:
第1013行,註冊&adap->dev.
第1020行,呼叫class_compat_create_link,程式碼如下:
即建立i2c_adapter_compat_class->kobj目錄下指向&adap->dev,&adap->dev->kobj目錄下指向adap->dev.parent目錄的軟連結。
第1062行,因其他驅動模組已經載入,即所有其他模組用到的i2c匯流排,例如如果用到i2c4,則根據i2c_register_board_info函式,
__i2c_first_dynamic_bus_num會為5. 故會呼叫到i2c_scan_static_board_info函式,該函式程式碼如下:
第948行,呼叫i2c_new_device(adap, &info);根據info資料,註冊新的client裝置。
回到i2c_register_adapter函式,第1066行,呼叫bus_for_each_drv對i2c bus上的驅動進行遍歷,對找到驅動,執行__process_new_adapter(drv,adap),該函式呼叫
i2c_do_add_adapter(to_i2c_driver(drv), adap); 再呼叫 i2c_detect(adap, driver);因driver->detect || address_list為空,會直接退出。
driver->attach_adapter即qn8027_driver中沒有賦值,故不會呼叫到。
回到mt_i2c_probe函式,第1529行,呼叫of_i2c_register_devices,
注:系統整合的模組會走該流程,如ncp1854。
node->full_name = /bus/[email protected]/[email protected]
request_module("%s%s", I2C_MODULE_PREFIX, info.type);即請求載入 i2c:ncp1854。
而外部加的模組則在之前的i2c_scan_static_board_info函式已作了處理。
第67行,讓linux系統的使用者空間呼叫/sbin/modprobe函式載入名為i2c:I2Cx模組。
kernel-3.10/include/linux/mod_devicetable.h:
#define I2C_MODULE_PREFIX "i2c:"
第69行,呼叫i2c_new_device(adap, &info);根據info資料,註冊新的client裝置。
kernel-3.10\drivers\i2c\I2c-core.c
postcore_initcall(i2c_init);
第1433行,註冊i2c_bus_type匯流排。
第1437行, register a compatibility class。
第1443,呼叫i2c_add_driver新增一個dummy_driver驅動。該函式呼叫i2c_register_driver,其程式碼如下:
第1327行呼叫driver_register(&driver->driver);向i2c bus註冊i2c驅動。
第1343行呼叫i2c_for_each_dev,
第1811行對driver->detect || !address_list進行判斷,如I2C驅動這兩個成員變數沒有賦值,則直接退出。
i2c_register_board_info函式解析:
生成i2c_devinfo結構體,賦值為busnum,*info,將其加入__i2c_board_list連結串列。
小結:裝置驅動程式通過i2c_register_board_info函式註冊i2c_board_info資料,i2c_scan_static_board_info函式會根據這些資料生成對應的client裝置,裝置驅動提供介面給應用層,並且在載入時呼叫i2c_add_driver完成與對應的client配對,而Client與i2c_adapter關聯在一起。資料最終由mt_i2c處理。I2c_adapter與mt_i2c、platform_device關係見下圖:
I2c資料傳送程式碼流程:
kernel-3.10\drivers\i2c\I2c-core.c
該函式定義並初始化i2c_msg訊息結構體後,第1593行呼叫i2c_transfer進行資料傳送。i2c_transfer進行相應的判斷及互斥操作後呼叫__i2c_transfer函式,該函式最終會呼叫
adap->algo->master_xfer(adap, msgs, num);
第1159行通過i2c_get_adapdata找到關聯的mt_i2c。
第1163行呼叫mt_i2c_do_transfer函式,該函式再會呼叫到mt_i2c_start_xfer函式。
第947行,呼叫_i2c_translate_msg,將msg轉換為mt_i2c。
第960行,呼叫_i2c_transfer_interface進行傳輸。該函式最後會呼叫_i2c_deal_result函式來處理傳輸結果。
tmo = wait_event_timeout(i2c->wait,atomic_read(&i2c->trans_stop), tmo);即中斷模式下,當前程序加入i2c->wait等待對列進行休眠。
喚醒時機:a. 發生中斷,正常喚醒,
b. 超時喚醒。
QN8027.C驅動程式碼示例:
/*****************************************************************************
Copyright(c) 2012 xxxx Inc. All Rights Reserved
File name : xxxx-qn8027.c
Description : NM326 host interface
History :
----------------------------------------------------------------------
2015/02/28 dlj initial
*******************************************************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/kobject.h>
#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/err.h>
#include "../inc/fm_ioctl.h"
#include "xxxx-qn8027.h"
static QN8027_CONF qn8027_conf_setting;
static unsigned char load_setting_flag = 0;
static unsigned char power_up_flag = 0;
static int qn8027_reg = -1;
static struct class *qn8027_class;
static struct i2c_client *qn8027_client;
static struct mutex qn8027_i2c_lock;
static struct i2c_board_info __initdata i2c_qn8027={ I2C_BOARD_INFO(QN8027_DEV_NAME, QN8027_I2C_SLAVE_ADDR)};
static int qn8027_save_conf(void)
{
struct file *fp;
mm_segment_t fs;
loff_t pos = 0;
int ret = 0;
fp = filp_open(QN8027_CONF_FILE, O_RDWR|O_CREAT, 0644);
if(IS_ERR(fp))
{
QN8027_ERR("create %s file failed!\n", QN8027_CONF_FILE);
ret = -1;
}
else
{
fs = get_fs();
set_fs(KERNEL_DS);
vfs_write(fp, (char *)&qn8027_conf_setting, sizeof(qn8027_conf_setting), &pos);
set_fs(fs);
filp_close(fp, NULL);
}
return ret;
}
static int qn8027_load_conf(void)
{
struct file *fp;
mm_segment_t fs;
loff_t pos = 0;
int ret = 0;
fp = filp_open(QN8027_CONF_FILE, O_RDONLY, 0644);
if(IS_ERR(fp))
{
QN8027_ERR("read %s file failed!\n", QN8027_CONF_FILE);
ret = -1;
}
else
{
fs = get_fs();
set_fs(KERNEL_DS);
vfs_read(fp, (char *)&qn8027_conf_setting, sizeof(qn8027_conf_setting), &pos);
set_fs(fs);
filp_close(fp, NULL);
}
return ret;
}
static int qn8027_i2c_write(u8 reg, u8 writedata)
{
u8 databuf[2] = {0};
int ret = 0;
databuf[0] = reg;
databuf[1] = writedata;
mutex_lock(&qn8027_i2c_lock);
QN8027_LOG("%s: write value(0x%x) to reg(0x%x) \n", __FUNCTION__, (int)writedata, (int)reg);
ret = i2c_master_send(qn8027_client, databuf, 0x2);
if(ret < 0)
{
QN8027_LOG("qn8027_i2c_write send data failed!!!\n");
}
mutex_unlock(&qn8027_i2c_lock);
return ret;
}
static int qn8027_i2c_read(u8 reg, u8 *readdata)
{
u8 databuf = 0;
int ret = 0;
databuf = reg;
mutex_lock(&qn8027_i2c_lock);
qn8027_client->ext_flag = I2C_WR_FLAG;
ret = i2c_master_send(qn8027_client, &databuf, (1<<8 | 1));
if(ret < 0)
{
QN8027_LOG("qn8027_i2c_write send data failed!!!\n");
}
qn8027_client->ext_flag = 0;
mutex_unlock(&qn8027_i2c_lock);
QN8027_LOG("qn8027_i2c_read: databuf = 0x%x\n", databuf);
*readdata = databuf;
return ret;
}
void QNF_SetRegBit(u8 reg, u8 bitMask, u8 data_val)
{
u8 temp;
qn8027_i2c_read(reg, &temp);
temp &= (u8)(~bitMask);
temp |= (data_val & bitMask);
qn8027_i2c_write(reg, temp);
}
static int qn8027_open(struct inode *inode, struct file *file)
{
QN8027_FUN();
return 0;
}
static int qn8027_release(struct inode *inode, struct file *file)
{
QN8027_FUN();
return 0;
}
static long qn8027_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
void __user *data;
char strbuf[QN8027_BUFSIZE] = {0};
QN8027_FUN();
QN8027_LOG("qn8027_ioctl: cmd = %u\n", cmd);
QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_POWERUP = %lu\n", QN8027_IOCTL_POWERUP);
QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_SETCHENNEL = %lu\n", QN8027_IOCTL_SETCHENNEL);
QN8027_LOG("qn8027_ioctl: QN8027_IOCTL_GETINFO = %lu\n", QN8027_IOCTL_GETINFO);
switch (cmd) {
case QN8027_IOCTL_POWERUP:
QN8027_LOG("qn8027_ioctl: arg = %ld\n", arg);
if(arg == QN8027_POWERON)
{
if(power_up_flag == 0)
{
gpio_set_value(QN8027_POWER_ENABLE_PIN, 1);
QN_ChipInitialization();
QND_TuneToCH(qn8027_conf_setting.channel);
power_up_flag = 1;
}
}
else if(arg == QN8027_POWEROFF)
{
if(power_up_flag == 1)
{
QNF_SetRegBit(SYSTEM, R_TXRX_MASK, 0);
gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);
power_up_flag = 0;
}
}
break;
case QN8027_IOCTL_SETCHENNEL:
if(arg<7600)
arg=7600;
if(arg>10800)
arg= 10800;
qn8027_conf_setting.channel = arg;
QN8027_LOG("qn8027_ioctl: arg = %ld\n", arg);
QND_TuneToCH(arg);
if(qn8027_save_conf())
{
QN8027_ERR("qn8027_save_conf failed\n");
}
break;
case QN8027_IOCTL_GETINFO:
data = (void __user *) arg;
if(data == NULL)
{
ret = -EINVAL;
break;
}
if(load_setting_flag == 0 )
{
if(qn8027_load_conf())
{
QN8027_ERR("qn8027_load_conf failed\n");
}
}
load_setting_flag = 1;
snprintf(strbuf, QN8027_BUFSIZE, "CH=%ld\n", qn8027_conf_setting.channel);
if(copy_to_user(data, strbuf, sizeof(strbuf)))
{
ret = -EFAULT;
}
break;
case QN8027_IOCTL_GETPOWERUP_STATUS:
data = (void __user *) arg;
if(data == NULL)
{
ret = -EINVAL;
break;
}
snprintf(strbuf, QN8027_BUFSIZE, "%d\n", power_up_flag);
if(copy_to_user(data, strbuf, sizeof(strbuf)))
{
ret = -EFAULT;
}
break;
default:
QN8027_ERR("cmd not support!\n");
ret = -EPERM;
}
return ret;
}
void QND_TuneToCH(int freq)
{
u8 tStep;
u8 tCh;
int f;
if(freq<7600)
freq=7600;
if(freq>10800)
freq= 10800;
QN8027_LOG("QND_TuneToCH: ch = %ld\n", qn8027_conf_setting.channel);
f = FREQ2CHREG(freq);
// set to reg: CH
tCh = (u8) f;
qn8027_i2c_write(0x01, tCh);
// set to reg: CH_STEP
qn8027_i2c_read(0x00, &tStep);
tStep &= ~0x03;
tStep |= ((u8) (f >> 8) & 0x03);
qn8027_i2c_write(0x00, tStep);
}
void QN_ChipInitialization(void)
{
QN8027_FUN();
qn8027_i2c_write(0x00,0x80);// reset all registers to the default value
mdelay(200);
qn8027_i2c_write(0x03,0x3F);
qn8027_i2c_write(0x04,0x33);//set 12Mhz clock
qn8027_i2c_write(0x00,0x41);
qn8027_i2c_write(0x00,0x01);
mdelay(200);
qn8027_i2c_write(0x18,0xE4);
qn8027_i2c_write(0x1B,0xF0);
qn8027_i2c_write(0x01,0x00);
qn8027_i2c_write(0x02,0xB9);
qn8027_i2c_write(0x00,0x22);//send
}
/******************************************************************************/
static ssize_t qn8027_store_reg_addr(struct device* dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int write_data;
if (!dev)
{
QN8027_ERR("dev is null!!\n");
return 0;
}
if (!strncmp(buf, "reg=",4) && (1 == sscanf(buf+4, "%d", &qn8027_reg))) {
QN8027_LOG("qn8027_reg = 0x%x\n", qn8027_reg);
}
if (!strncmp(buf, "val=", 4)&&(1==sscanf(buf+4, "%d", &write_data))) {
QN8027_LOG("write_data = 0x%x\n", write_data);
qn8027_i2c_write((u8)qn8027_reg, (u8)write_data);
}
return count;
}
/******************************************************************************/
static ssize_t qn8027_show_reg_value(struct device* dev,
struct device_attribute *attr, char *buf)
{
ssize_t res = 0;
u8 value = 0;
QN8027_LOG("reg is: 0x%x\n", qn8027_reg);
qn8027_i2c_read((u8)qn8027_reg, &value);
res = snprintf(buf, PAGE_SIZE, "0x%x\n", (int)value);
return res;
}
/******************************************************************************/
static ssize_t qn8027_store_gpio_value(struct device* dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int value;
if (!dev)
{
QN8027_ERR("dev is null!!\n");
return 0;
}
QN8027_LOG("qn8027_init_test now!!!\n");
if (!strncmp(buf, "val=",4) && (1 == sscanf(buf+4, "%d", &value))) {
QN8027_LOG("val = %d\n", value);
}
if(value == 0 || value == 1)
{
gpio_set_value(QN8027_POWER_ENABLE_PIN, value);
}
return count;
}
/******************************************************************************/
static ssize_t qn8027_show_gpio_value(struct device* dev,
struct device_attribute *attr, char *buf)
{
ssize_t res = 0;
int value = 0;
value = gpio_get_value(QN8027_POWER_ENABLE_PIN);
QN8027_LOG("val = %d\n", value);
res = snprintf(buf, PAGE_SIZE, "%d\n", value);
return res;
}
DEVICE_ATTR(reg, S_IWUSR | S_IWGRP | S_IRUGO, qn8027_show_reg_value, qn8027_store_reg_addr);
DEVICE_ATTR(gpio_val, S_IWUSR | S_IWGRP | S_IRUGO, qn8027_show_gpio_value, qn8027_store_gpio_value);
static struct device_attribute *qn8027_attr_list[] = {
&dev_attr_reg,
&dev_attr_gpio_val,
};
static int xxxx_create_attr(struct device *dev)
{
int idx, err = 0;
int num = (int)(sizeof(qn8027_attr_list)/sizeof(qn8027_attr_list[0]));
if (!dev)
{
return -EINVAL;
}
for (idx = 0; idx < num; idx++)
{
if ((err = device_create_file(dev, qn8027_attr_list[idx])))
{
QN8027_ERR("device_create_file\n");
break;
}
}
return err;
}
static struct file_operations qn8027_fops = {
.owner = THIS_MODULE,
.open = qn8027_open,
.release = qn8027_release,
.unlocked_ioctl = qn8027_ioctl,
};
static int qn8027_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct device *qn8027_dev;
QN8027_FUN();
qn8027_client = client;
// 1. register character device
ret = register_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME, &qn8027_fops);
if(ret)
{
QN8027_ERR("<xxxx> register_chrdev(QN8027_DEV) failed\n");
goto error;
}
// 2. class create
qn8027_class = class_create(THIS_MODULE, QN8027_DEV_NAME);
if(IS_ERR(qn8027_class))
{
QN8027_ERR("<xxxx> class create failed\n");
goto error;
}
// 3. device create
qn8027_dev = device_create(qn8027_class, NULL, MKDEV(QN8027_DEV_MAJOR, QN8027_DEV_MINOR), NULL, QN8027_DEV_NAME);
if (xxxx_create_attr(qn8027_dev))
{
class_destroy(qn8027_class);
goto error;
}
mutex_init(&qn8027_i2c_lock);
QNF_SetRegBit(SYSTEM, R_TXRX_MASK, 0);
gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);
qn8027_conf_setting.channel = QN8027_DEFCH;
gpio_set_value(QN8027_POWER_ENABLE_PIN, 0);
return 0;
error:
if (ret == 0)
{
unregister_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME);
}
return -1;
}
static int qn8027_remove(struct i2c_client *client)
{
unregister_chrdev(QN8027_DEV_MAJOR, QN8027_DEV_NAME);
device_destroy(qn8027_class, MKDEV(QN8027_DEV_MAJOR, QN8027_DEV_MINOR));
class_destroy(qn8027_class);
return 0;
}
static const struct i2c_device_id qn8027_ids[] = {
{ QN8027_DEV_NAME, 0, },
{ /* LIST END */ }
};
static struct i2c_driver qn8027_driver = {
.driver = {
.name = QN8027_DEV_NAME,
},
.probe = qn8027_probe,
.remove = qn8027_remove,
.id_table = qn8027_ids,
};
/*----------------------------------------------------------------------------*/
static int __init qn8027_init(void)
{
QN8027_FUN();
i2c_register_board_info(1, &i2c_qn8027, 1);
if(i2c_add_driver(&qn8027_driver)!=0)
{
QN8027_ERR("qn8027_driver initialization failed!!\n");
return -1;
}
else
{
QN8027_LOG("qn8027_driver initialization succeed!!\n");
}
return 0;
}
/*----------------------------------------------------------------------------*/
static void __exit qn8027_exit(void)
{
QN8027_FUN();
}
/*----------------------------------------------------------------------------*/
module_init(qn8027_init);
module_exit(qn8027_exit);
/*----------------------------------------------------------------------------*/
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("QN8027 driver");
MODULE_AUTHOR("[email protected]");
xxxx-qn8027.h
/*****************************************************************************
Copyright(c) 2012 XXXX Inc. All Rights Reserved
File name : xxxx-qn8027.h
Description : NM326 host interface
History :
----------------------------------------------------------------------
2015/02/28 dlj initial
*******************************************************************************/
#ifndef XXXX_QN8027_H
#define XXXX_QN8027_H
#define QN8027_BUFSIZE 32
#define QN8027_CONF_FILE "/data/qn8027.setting"
#define QN8027_DEV_MAJOR 228
#define QN8027_DEV_MINOR 0
#define QN8027_DEV_NAME "QN8027"
#define QN8027_I2C_SLAVE_ADDR 0x2C
#define QN8027_POWER_ENABLE_PIN 5
#define R_TXRX_MASK 0x20
#define SYSTEM 0x00
#define CH 0x01
#define CH_STEP 0x00
#define RDSD0 0x08
#define PAG_CAL 0x1f
#define CID2 0x06
#define RDSEN 0x80
#define TXREQ 0x20
#define CH_CH 0x03
#define RDSTXRDY 0x04
#define TX_FDEV 0x11 // FDEV on datasheet
typedef struct
{
unsigned long channel;
}QN8027_CONF;
/*----------------------------------------------------------------------------*/
#define QN8027_TAG "[QN8027] "
#define QN8027_FUN(f) printk(KERN_ERR QN8027_TAG"%s\n", __FUNCTION__)
#define QN8027_ERR(fmt, args...) printk(KERN_ERR QN8027_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
#define QN8027_LOG(fmt, args...) printk(KERN_ERR QN8027_TAG fmt, ##args)
#define QN8027_DEFCH 8800
#define FREQ2CHREG(freq) ((freq-7600)/5)
typedef enum
{
QN8027_POWEROFF = 0,
QN8027_POWERON = 1,
}QN8027_POWER_TYPE;
#define QN8027_IOC_MAGIC 0xf6
#define QN8027_IOCTL_POWERUP _IOWR(QN8027_IOC_MAGIC, 0, int32_t)
#define QN8027_IOCTL_SETCHENNEL _IOWR(QN8027_IOC_MAGIC, 1, int32_t)
#define QN8027_IOCTL_GETINFO _IOWR(QN8027_IOC_MAGIC, 2, int32_t)
#define QN8027_IOCTL_GETPOWERUP_STATUS _IOWR(QN8027_IOC_MAGIC, 3, int32_t)
//#define QN8027_IOCTL_POWERUP 1
//#define QN8027_IOCTL_SETCHENNEL 2
//#define QN8027_IOCTL_GETINFO 3
void QND_TuneToCH(int freq);
void QN_ChipInitialization(void);
#endif //XXXX_QN8027_H
參考檔案:
相關推薦
MTK I2C驅動程式碼分析
I2C匯流排(I2C bus,Inter-IC bus)是一個雙向的兩線連續匯流排,提供積體電路(ICs)之間的通訊線路。I2C匯流排是一種序列擴充套件技術,最早由Philips公司推出。Philips公司推出的I2C匯流排採用一條資料線(SDA),加一條時鐘線(SC
MTK UART驅動程式碼分析
首先參考網上的一些資料,給出UART驅動的整體描述與框架, 在 linux 系統中,tty 表示各種終端。終端通常都跟硬體相對應。比如對應於輸入裝置鍵盤 滑鼠,輸出裝置顯示器的控制終端和串列埠終端。 最上面的使用者空間會有很多對底層硬體的操作,像 read,write
linux驅動由淺入深系列:usb子系統之四(android平臺滑鼠驅動程式碼分析)
android上的usb口是支援OTG(on the go)的,USB OTG既可以作為Host又可以作為Device,我們本文來看一下android手機作為Host連線滑鼠的情況。OTG是如何做到既可以做Host又可以作為Device的呢 標準usb接頭中有四根線:vbu
Linux ASoC音訊驅動架構 及 Machine驅動程式碼分析
【軟體框架】 在對要做的事情一無所知的時候,從全域性看看系統的拓撲圖對我們認識新事物有很大的幫助。Audio 部分的驅動程式框架如下圖所示: 這幅圖明顯地分為 3 級。 上方藍色系的 ALSA Kernel 整體屬於Linux Kernel,是原生
嵌入式Linux——IIC驅動(2):i2c驅動框架分析
簡介: 本文主要介紹i2c匯流排框架,即對i2c的各個層次(i2c匯流排,i2c核心,i2c裝置)進行分析。同時我也會結合程式對框架進行說明。所以本文將分為兩部分,第一部分對i2c的框架進行介紹,而第二部分就是結合程式碼分析。 核心:linux-2.6.2
linux i2c子系統程式碼分析9---i2c裝置的註冊方法
/** * i2c_register_board_info - statically declare I2C devices * @busnum: identifies the bus to which these devices belong * @info: vector of i2c device
I2C EEPROM驅動例項分析
上篇分析了Linux Kernel中的I2C驅動框架,本篇舉一個具體的I2C裝置驅動(eeprom)來對I2C裝置驅動有個實際的認識。 s3c24xx系列集成了一個基於I2C的eeprom裝置at24cxx系列。at24cxx系列晶片包含at24c01
MTK 驅動(38)---MTK 待機問題分析
mtk 平臺待機問題分析: 1 待機問題分類 1)無法待機: 1. 需要確認 APK 是否有 partial_wakelock 2. kernel 是否持有 wakeup source 不釋放. 2)可以待機,但是喚醒頻繁. 1. 是否有開啟資料連線,
Linux I2C驅動分析與實現--例子
通過上篇《Linux I2C驅動分析與實現(二)》,我們對Linux子系統已經不陌生,那麼如何實現I2C驅動呢? 編寫客戶驅動的方法 在核心中有兩種方式的i2c客戶驅動的編寫方法,一種叫legacy傳統方式,另一種是newstyle方式. 前 一種legacy是一種舊式的方法,在2.
Linux裝置驅動程式架構分析之一個I2C驅動例項
作者:劉昊昱 核心版本:3.10.1 編寫一個I2C裝置驅動程式的工作可分為兩部分,一是定義和註冊I2C裝置,即i2c_client;二是定義和註冊I2C裝置驅動,即i2c_driver。下面我們就以mini2440的I2C裝置at24c08 EEPROM為例,介紹如
MTK平臺camera驅動架構分析
MTK6580 AndroidO(android8.1)版本camera 驅動分析 CAMERA驅動整個框架分為:三個部分hal部分邏輯呼叫,kernel層的通用驅動sensorlist.c 和具體IC的驅動xxxx_mipi_raw.c 這裡主要介紹kernel部分和HAL層部分。
linux驅動學習(八) i2c驅動架構(史上最全) davinc dm368 i2c驅動分析
預備知識 在閱讀本文最好先熟悉一種i2c裝置的驅動程式,並且瀏覽一下i2c-core.c以及晶片提供商的提供的i2c匯流排驅動(i2c-davinci.c)。標題黨請見諒! 其實i2c介面非常的簡單,即使用51單片的gpio來模擬i2c,編寫一個e2prom或者其他i2c介
FPGA音訊編解碼驅動及I2C寫入程式碼
FPGA音訊編解碼驅動及I2C寫入程式碼 使用音訊編解碼晶片為WM8731,其通過I2C對WM8731進行暫存器寫入,將需要寫入的資料放入例化的ROM塊中,通過狀態機控制資料的寫入;通過對50M和24M的時鐘分頻提供WM8731的主時鐘和位寫入時鐘,資料沒有進行
Intel 82599 ixgbe & ixgbevf CNA 卡驅動分析03——部分功能程式碼分析
I/O Operations and Activities 使用SR-IOV 的根本原因就是使得虛擬機器中的一個驅動可以直接訪問PCI進行I/O操作,並能夠在虛擬機器之間共享裝置。Intel VF 驅動了解自己執行在一個虛擬化的環境中擁有優先的PCI資源。 可用的資源包括基本的接收和
linux驅動由淺入深系列:camera驅動之二(基於高通平臺的V4L2結構及程式碼分析)
在上一篇文章中介紹了camera的基礎知識和相關概念,我們一起來了解一下驅動相關的程式碼結構。本文以高通+android平臺為示例,首先看一下整體框圖:這張圖是從整體上來看的1,圖中最下面的是kernel層的驅動,其中按照V4L2架構實現了camera sensor等驅動,向
Linux下I2C驅動分析(一)
最近在做一個基於全志A33晶片的android移植時發現嵌入式裝置很多都用到了I2C匯流排通訊,比如說攝像頭,G-sensor,觸控式螢幕等,為此我覺得很好的理解I2C裝置驅動在今後的嵌入式開發中是非常有好處的,而目前我也是處於學習階段,便將這些學習的過程給
i2c驅動程式(2) i2c_driver probe被呼叫的流程分析
(本文為個人的筆記 難免有錯 望各位高人賜教指正 謝謝!) i2c驅動程式i2c_driver probe被呼叫的流程分析 step1 i2c_add_driver(&at24_driver); step2 i2c_register_driver(T
jni呼叫kernel驅動檔案程式碼分析
轉自: https://www.cnblogs.com/armlinux/archive/2012/01/14/2396768.html 重溫了一下程式碼流程,留個腳印,以備後用
i2c驅動程式全面分析,從adapter驅動程式到裝置驅動程式
開發板 :mini2440 核心版本:linux2.6.32.2 驅動程式參考:韋東山老師畢業班i2c 內容概括: 1、adapter client 簡介2、adapter 驅動框架 2.1 裝置側2.2 驅動側 2.2.1 probe
nvdimm 驅動的分析
list smb 通過 虛擬 數據一致性 簡介 而在 異常 .mm nvdimm 驅動的分析 1.驅動的簡介 nvdimm在內核中的驅動,是基於一個典型的字符類型的設備驅動模型去實現的。因此,同樣需要實現設備驅動的幾個典型操作:open/release/mmap/ioctl