以下为本人学习笔记,如有转载请注明出处,谢谢
1. service用法 oneshot
DEFINE_MUTEX(buzzer_mutex);
mutex_lock(&buzzer_mutex);
mutex_unlock(&buzzer_mutex);
static void WriteNumber(const char *fileName, int number)
{
FILE *fp;
fp = fopen(fileName, "w");
if (fp == NULL) {
LCD_DEBUG("open %s error, errno %d ", fileName, errno);
return;
}
fprintf(fp, "%d", number);
fclose(fp);
}
service aa /usr/bin/aa
class core
user root
group root
critical
onrestart restart aa
onrestart restart tt
#service bbd /usr/bin/bb
service bb /usr/bin/app_bbl_read
user root
group root
oneshot
2.【Shell脚本】怎样表示一个for循环
作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/
在此说一下我常用的两个结构:
1.
for i in $(seq 1 100); do
echo $i
done
2.
for (( i = 1 ; $i <= 100; i++ )) ;do
echo $i;
done
3.对于关闭抢占的内核接口
看内核接口是同步还是异步,比方说msleep就是异步接口,会休眠让出cpu
mdelay是忙等待,不会让出cpu
如果是同步接口的话,对于单核cpu,就不会出现多进程同时调用同一个接口出错的情况
如果是异步接口的话,就得具体问题具体分析,如果是仅仅避免多个进程调度同一个接口的情况的话,加信号量或者普通的锁就可以,不一定要加互斥锁,
如果是又有中间层调度接口,又有用户态上层调度接口,那么就另当别论了
spin_lock这种锁是用来多核cpu共同调用同个接口的问题,
mutex_lock这种是会休眠,同个cpu的互斥锁,如果是进程上下文就可以用,如果是中断上下文就不能用这种锁,因为这种锁会休眠,中断中是不允许休眠的
local_irq_save(flags);
local_irq_restore(flags);
spin_lock_irqsave(&iproc_bbl->lock, flags);
spin_unlock_irqrestore(&iproc_bbl->lock, flags);
4.网上大家经常碰到的不能连接问题:
请在运行-cmd输入netstat -n 查看5222端口是否在建立状态 ESTABLISHED,这个方法来源自网络,部落没有遇到过.另外,还有一个,就是清空本地本地DNS缓存,具体步骤如下:
在windows下运行运行菜单,开始->运行,输入"CMD"进行命令行窗口,然后输入 ipconfig/flushdns 按回车键
5.休眠唤醒调试
kernel/power/main.c中可以通过打印链表的信息,在休眠唤醒的时候将每个唤醒的源消耗的时间打印出来
dpm_run_callback() ----》drivers/base/power
6.中断中不可以加入打印
中断处理函数应该避免调用不可重入函数, 因为新的中断可能发生并打断正在执行任务中,如果当前任务调用了一些不可重入的函数,将会产生错误。
一些常用库函数如printf,malloc,free等都是不可重入函数,因为在函数中引用了全局变量, 这个道理因该很容易明白了吧?
例如, printf会引用全局变量stdout,malloc,free会引用全局的内存分配表。
arch/arm/kernel/debug.S:157: Error: too many positional arguments
7.sys文件节点要注意返回值
static inline ssize_t show_counter_sysfs(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct asiu_pwm_chip *asiu_pwm = dev_get_drvdata(dev);
int i, n;
long value;
n=0;
if(asiu_pwm) {
for (i=0; i<6; i++) {
value = __onepulse_pwm_counter_extend(i);
n += sprintf(buf + n, "pwm_id(%d)----0x%x
", i, value);
}
return n;
}
else
return -1;
}
static struct device_attribute sysfs_misc_list[] = {
__ATTR(onepluse_counter,S_IRUGO , show_counter_sysfs, NULL),
};
/* tp info show include tp sw version, fw version, cfg csum, hw version */
static ssize_t tp_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data;
int len = 0;
int retval;
unsigned char config_id[4];
if (NULL == rmi4_data || !f34_ctrl_base_addr)
return -1;
/* Get device config ID */
retval = synaptics_rmi4_reg_read(rmi4_data,
f34_ctrl_base_addr,
config_id, sizeof(config_id));
if (retval < 0) {
dev_err(rmi4_data->pdev,
"%s: Failed to read device config ID
", __func__);
return -1;
}
len =
scnprintf(buf + len, PAGE_SIZE, "SW:%s
",
SYNAPTICS_DSX_DRV_VERSION);
/* combine FW and CFG CSUM */
#define SPLIT_TAG "_CFGID"
len +=
scnprintf(buf + len, PAGE_SIZE, "FW:%u%s%02x%02x%02x%02x
", rmi4_data->firmware_id,
SPLIT_TAG,
config_id[0],
config_id[1],
config_id[2],
config_id[3]);
/*
len +=
scnprintf(buf + len, PAGE_SIZE,
"CFG ID:0x%02x 0x%02x 0x%02x 0x%02x
", config_id[0],
config_id[1], config_id[2], config_id[3]);
*/
/* add for chipset name */
len += scnprintf(buf + len, PAGE_SIZE, "CHIP NAME:%s
", "synaptics");
return len;
}
sys 文件节点写函数的时候,sh: write error: Bad address,这种错误一般是return没有返回正确的值,
什么值是正确的呢?那就是count,长度这些才可以,那么系统是怎么识别的呢?要取决于文件系统和echo的实现
static ssize_t test_sysfs_get_report_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int retval;
unsigned char command;
unsigned long setting;
struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
retval = sstrtoul(buf, 10, &setting);
if (retval)
return retval;
printk("zbh 111
");
if (setting != 1) {
printk("zbh %s(): ---> %d
", __func__, __LINE__);
return -EINVAL;
}
mutex_lock(&f54->status_mutex);
retval = test_check_for_idle_status();
if (retval < 0) {
printk("zbh %s(): ---> %d
", __func__, __LINE__);
goto exit;
}
if (!test_report_type_valid(f54->report_type)) {
dev_err(rmi4_data->pdev,
"%s: Invalid report type
",
__func__);
retval = -EINVAL;
printk("zbh %s(): ---> %d
", __func__, __LINE__);
goto exit;
}
test_set_interrupt(true);
command = (unsigned char)COMMAND_GET_REPORT;
retval = synaptics_rmi4_reg_write(rmi4_data,
f54->command_base_addr,
&command,
sizeof(command));
if (retval < 0) {
dev_err(rmi4_data->pdev,
"%s: Failed to write get report command
",
__func__);
goto exit;
}
/* tddi f54 test reporting + */
#ifdef F54_POLLING_GET_REPORT
retval = test_sysfs_get_report_polling();
if (retval < 0) {
dev_err(rmi4_data->pdev,
"%s: Failed to get report image
",
__func__);
printk("zbh 222
");
goto exit;
}
#else
/* tddi f54 test reporting - */
f54->status = STATUS_BUSY;
f54->report_size = 0;
f54->data_pos = 0;
hrtimer_start(&f54->watchdog,
ktime_set(GET_REPORT_TIMEOUT_S, 0),
HRTIMER_MODE_REL);
retval = count;
#endif
retval = count; // ===》如果去掉这一行的话,那么echo 1 > get_report 的话就会出现错误 sh: write error: Bad address
exit:
mutex_unlock(&f54->status_mutex);
printk("zbh %s(): retval=%d---> %d
", __func__, retval, __LINE__);
return retval;
}
因为bbl会影响打印机,1s=1000000us
Bbl的时钟是32.768kHz,
所以1000000/32768=30us左右
也就是这个时钟的精度是30us,而bbl寄存器读status状态是延时10us,太短了,至少要在一个clock完成后再读,所以改成延时35us,
8.shell脚本循环
#!/bin/sh
while true
do
let "i++"
echo "is $i"
done
9.查看这些宏CONFIG_SMP在linux内核中是否有定义
#ifndef CONFIG_SMP
要去linux3.6.5目录下.config文件
10.书写代码框架注意:
goto 和return
static int asiu_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm,
int duty_ns, int period_ns)
{
struct asiu_pwm_chip *asiu_pwm = to_asiu_pwm_chip(chip);
struct asiu_pwm_cfg *pwm_cfg;
unsigned long period_counts, dutyhi_counts;
unsigned long prescale = 0;
unsigned long long ticks;
if (pwm_cfg->enabled) {
/* Change the PWM output with the new config */
asiu_pwmc_disable(chip, pwm);
//asiu_pwmc_enable(chip, pwm);
}
#ifdef PWMC_DEBUG
dev_info(chip->dev, "%s : [pwm-%d] duty_ns = %d, period_ns = %d ",
__FUNCTION__, pwm->hwpwm, duty_ns, period_ns);
#endif
if (duty_ns == 0 && period_ns ==0) {
/* PWM stay low, duty cycle = 0% */
prescale = 0;
period_counts = 0;
dutyhi_counts = 0;
}
else if (duty_ns >= period_ns) {
/* PWM stay high, duty cycle = 100% */
prescale = 0;
period_counts = ASIU_PWM_PERIOD_MASK;
dutyhi_counts = ASIU_PWM_DUTYHI_MASK;
}
else {
ticks = (unsigned long long)period_ns * asiu_pwm->tick_hz;
do_div(ticks, NSEC_PER_SEC);
period_counts = ticks;
prescale = period_counts >> ASIU_PWM_PEROID_WIDTH;
if(prescale & ~ASIU_PWM_PRESCALE_MASK) {
dev_warn(chip->dev, "%s(%d) : period_counts = %d, prescale = 0x%x ",
__FUNCTION__, __LINE__, period_counts, prescale);
return -EINVAL;
}
ticks = (unsigned long long)duty_ns * asiu_pwm->tick_hz;
do_div(ticks, NSEC_PER_SEC);
dutyhi_counts = ticks;
}
pwm_cfg = pwm_get_chip_data(pwm);
if (!pwm_cfg) {
dev_warn(chip->dev, "fail to get pwm config data ");
return -ENOMEM;
}
pwm_cfg->prescale = prescale;
pwm_cfg->period_cnt = period_counts;
pwm_cfg->dutyhi_cnt = dutyhi_counts;
if (pwm_cfg->enabled) {
/* Change the PWM output with the new config */
//asiu_pwmc_disable(chip, pwm);
asiu_pwmc_enable(chip, pwm);
}
#ifdef PWMC_DEBUG
dev_info(chip->dev, "%s : pwm_cfg->prescale = %d, period_cnt = %d, dutyhi_cnt = %d ",
__FUNCTION__, pwm_cfg->prescale, pwm_cfg->period_cnt, pwm_cfg->dutyhi_cnt);
#endif
return 0;
}
对于如上框架:
进入config函数时先pwm_disable, 然后中间计算,最后enable
但是中间有很多return,我们做接口的目的有个宗旨:
函数是为了改变原来的状态的,如果此函数运行失败返回,那么就要还原原来的场景,不要改变原来的场景。
如上例子如果用中间用return,一开始执行了disable,那么中间如果执行失败return了,后面就不会执行enable,那么pwm状态就会变了,那么对于这种情况我们要使用goto到后面,然后再进行enable,就算失败了,也要还原之前的场景去enable,所以需要把上一次的值保存起来。
11.vim快捷键
vi -d a.c b.c,可以对比
对比过程中,如果拷贝的话,可以用快捷键dp
12.对触摸屏中断的理解
触摸屏与cpu是由一个gpio中断口线相连,所有的中断都是由tp那端抛给cpu的,
Tp通过设定各种参数,刷新频率,扫描频率,电容值(即基准点),tp通过计算触摸屏上的点来决定是否发送中断给cpu,如果计算到没点,就不发送中断给cpu,如果计算到有点,那么就会发送中断给cpu,(注意:tp只要上电启动了那么就会一直进行运算),每次触摸屏上只要有按下,不管几个手指都会产生一个中断,抬起又产生一个中断,最小单位产生两个中断,如果是电平中断,一直按着手指,会不停的产生中断,产生中断的时间和次数由tp的运算速度决定,很难去量化,按下的手指越多,tp运算时间越长,至于有时候发现log有10个finger,和10个status状态,那是驱动代码在一个中断产生时遍历了report动作10次,这些finger和status是通过tp寄存器读取到再由驱动上报内核空间,最终通过input子系统发送给用户空间
===========================================================================================
13.怎么样让编译器对某些函数不再抱怨warning: unused parameter ‘xxx’?
转自:https://segmentfault.com/q/1010000002395334
#define UNUSED_PARAMETER(x) (void)x
int main(int argc, char **argv)
{
// 这两行是为了避免编译报警告
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
}
如果可能,请务必用正常方法消除 warning,真的多余就去掉吧
方法一:
void foo(int a) {
(void)a;
// ...
}
方法二:
#ifdef __GNUC__
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#else
# define UNUSED(x) UNUSED_ ## x
#endif
void foo(int UNUSED(a)) {
// ...
}
参考:http://stackoverflow.com/a/12891181
===========================================================================================
字符串比较时,strncmp strcmp 需要注意的地方,要注意比较长度
if ((strlen(ts_keyword) != strlen(fw_ugd->ts_keyid)) ||
strcasecmp(ts_keyword, fw_ugd->ts_keyid)) {
printf("%s: check ts_keyword[%s],fw_ugd->ts_keyid[%s]
",
__FUNCTION__, ts_keyword, fw_ugd->ts_keyid);
retval = TP_FW_ERR_TS_KEYID_COMPARE;
goto error;
}
中断 IRQF_ONESHOT 与 IRQF_SHARED 不能同时使用 当多个设备共享中断时,由于IRQF_ONESHOT会关闭中断线程的中断,而线程一般执行时间会比较长,所以是不允许的 当hardirq函数为NULL时,必须声明IRQF_ONESHOT, 表示threadirq线程中关闭该中断,在某些情况下,这个标志会非常有用 例如:设备是低电平产生中断,而硬中断函数为NULL,如果不使用IRQF_ONESHOT,就会一直产生中断执行NULL函数,中断线程 得不到执行,声明IRQF_ONESHOT后,会执行完线程才使能该中断
14.devm_request_irq---- 这种架构会自己释放资源,不需要自己释放,交由devr内核链表管理
http://lxr.free-electrons.com/ident?i=devm_request_irq
15.这个网址可以查询内核源码
16.rm命令
删除除了 tt.c的所有文件,只保留tt.c不被删除
rm -rf !(tt.c)
17.查看当前项目有哪些远程仓库
$ git remote
bixiaopeng@bixiaopengtekiMacBook-Pro wirelessqa$ git remote origin
查看远程仓库
$ git remote -v
bixiaopeng@bixiaopengtekiMacBook-Pro wirelessqa$ git remote -v origin git@gitlab.***.com:xiaopeng.bxp/wirelessqa.git (fetch) origin git@gitlab.***.com:xiaopeng.bxp/wirelessqa.git (push)
0x1000 16*16*16=4096bit = 4KB
18.堆栈的限制
堆栈空间的最大值是由setrlimit系统调用确定的,也可以通过bash内建的ulimit命令来设定和查看.
例如:
查看当前可使用的最大堆栈(以KB为单位)
ulimit -s
8192 //栈的大小默认是8M
设定为最大的使用堆栈为15KB
ulimit -s 15
此时执行ls将会得到一个段错误.
ls -l /etc/
total 1040
Segmentation fault
通过用strace跟踪ls命令,将发现有如下的系统调用
getrlimit(RLIMIT_STACK, {rlim_cur=15*1024, rlim_max=15*1024}) = 0
说明当前可用的堆栈空间,已经不足以运行strace命令了.
19.Linux驱动调试中的Debugfs的使用简介
http://blog.csdn.net/wealoong/article/details/7992071
Linux DebugFS 子目录也是用debugfs_create_dir来实现
http://blog.csdn.net/superkris/article/details/8626517
mount来调试文件节点, sysfs节点调试方法
make menuconfig ----
Global build settings ------
Compile the kernel with Debug FileSystem enabled
Make kernel_menuconfig --------------
Kernel hacking --------
Debug Filesystem
mount -t debugfs none /sys/kernel/debug
20.grep 命令
grep -run "< abc >"
21.检测内存泄露的方法
Make kernel_menuconfig
Kernel hacking =======>
[*] Kernel memory leak detector
(40000) Maximum kmemleak early log entries
[*] Compile the kernel with debug info
make menuconfig
[*] Compile the kernel with Debug FileSystem enabled
[*] Compile the kernel with debug information
开机后
mount -t debugfs none /sys/kernel/debug
cd /sys/kernel/debug
cat kmemleak
make kernel_menuconfig
这个选项是说明
General setup ================>
Choose SLAB allocator (SLUB (Unqueued Allocator))
(X) SLUB (Unqueued Allocator)
[*] Enable SLUB debugging support
22.这里使用slub分配内存比slab更高效
我们在调试内核时,如果出现系统响应非常慢的情况
先看有没有死锁,用lockdep来检测
检测出来后再用ftrace来跟踪函数
如果出现kernel内存泄露,可以 用kmemleak来查看,查看前要确认内核使用哪个分配器,一般是slab或者slub
cat /proc/meminfo 查看内存泄露
23.Linux设备驱动之semaphore机制
static noinline void __down(struct semaphore *sem);
static noinline int __down_interruptible(struct semaphore *sem);
static noinline int __down_killable(struct semaphore *sem);
static noinline int __down_timeout(struct semaphore *sem, long jiffies);
static noinline void __up(struct semaphore *sem);
#define DEFINE_SEMAPHORE(name)
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
static inline void sema_init(struct semaphore *sem, int val)
{
static struct lock_class_key __key;
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}
一个核,执行了两个死循环进程,那么这两个进程是通过内核的调度算法去选择要执行哪个进程的,最终会执行arm的指令,这两个进程在单和上是永远不可能同时执行的,MMU里面会有进程id寄存器
24.获得内核函数地址的四种方法
本文以获取内核函数 sys_open()的地址为例。
1)从System.map文件中直接得到地址:
$ grep sys_open /usr/src/linux/System.map
2)使用 nm 命令:
$ nm vmlinuz | grep sys_open
3)从 /proc/kallsyms 文件获得地址:
$ cat /proc/kallsyms | grep sys_open
4)使用 kallsyms_lookup_name() 函数:
是在kernel/kallsyms.c文件中定义的,要使用它必须启用CONFIG_KALLSYMS编译内核。
kallsyms_lookup_name()接受一个字符串格式内核函数名,返回那个内核函数的地址。
kallsyms_lookup_name("sys_open");
方法一、
通过打印函数地址,可以查看函数在哪里调用
例如:
Core.c driverspwm
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
if (!pwm || period_ns == 0 || duty_ns > period_ns)
return -EINVAL;
printk("%s driverspwm Core.c----(%d) ", __func__, __LINE__);
printk("pwm->chip->ops->config=%p----(%d) ", pwm->chip->ops->config, __LINE__);
return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns);
}
终端显示如下:
[ 42.550000] pwm->chip->ops->config=c001b0c0----(378)
然后可以在
如下目录
Z:linux-3.6.5
中的System.map中找到
c001b0c0 t asiu_pwmc_config
就调用的是这个函数asiu_pwmc_config
方法二、
dump_stack()函数
25.打印语句的使用,带颜色的打印
#define RESETCOLOR " 33[0m"
#define GREEN " 33[0;32m"
#define RED " 33[0;31m"
#define LIGHT_RED " 33[1;31m"
#define YELLOW " 33[1;33m"
#define BLUE " 33[0;34m"
#define LIGHT_BLUE " 33[1;34m"
#define CYAN " 33[0;36m"
#define PURPLE " 33[0;35m"
#define LIGHT_PURPLE " 33[1;35m"
#define BROWN " 33[0;33m"
#define WHITE " 33[1;37m"
#define LIGHT_GRAY " 33[0;37m"
#define DARY_GRAY " 33[1;30m"
printf(YELLOW"** 10. set asiu_pwm while **"RESETCOLOR" ");
printf(YELLOW"**"LIGHT_RED" 4. onepluse pwm disable "YELLOW"**"RESETCOLOR" ");
RC文件延时方法:
wait /dev/zhangb 随便一个不存在的节点都可以,固定为延时5s
如何增加打印信息---灵活使用宏定义:
#include <stdio.h>
#include <stdlib.h>
#define qWiFiDebug(format, ...) printf("[WiFi] "format" File:%s, Line:%d, Function:%s
", ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__);
int main(void)
-{
| qWiFiDebug("aaaaaa -----");
|
| return 0;
|}
打印输出:
./a.out
[WiFi] aaaaaa ----- File:a.c, Line:9, Function:main
#define RESETCOLOR " 33[0m" #define GREEN " 33[0;32m" #define RED " 33[0;31m" #define LIGHT_RED " 33[1;31m" #define YELLOW " 33[1;33m" #define BLUE " 33[0;34m" #define LIGHT_BLUE " 33[1;34m" #define CYAN " 33[0;36m" #define PURPLE " 33[0;35m" #define LIGHT_PURPLE " 33[1;35m" #define BROWN " 33[0;33m" #define WHITE " 33[1;37m" #define LIGHT_GRAY " 33[0;37m" #define DARY_GRAY " 33[1;30m" #define ZBH_TRACE_DEBUG (1 << 0) #define ZBH_TRACE_INIT (1 << 1) #define ZBH_TRACE_INT (1 << 2) #define ZBH_TRACE_X_Y_COORDINATE (1 << 3) #define ZBH_TRACE_FINGER_UP (1 << 4) #ifdef __BASE_FILE_NAME__ #define printk_get_basename(x) __BASE_FILE_NAME__ #else static char *printk_get_basename(char *path) { char *p1 = path, *p2 = p1; while (*p1 != '